Building the latest greatest for Android AArch64 (with Vulkan teaser)

Let's say you got a 64-bit ARM device running Android. For instance, the Tegra X1-based NVIDIA Shield TV. Now, let's say you are also interested in the latest greatest content from the dev branch, for example to try out some upcoming Vulkan enablers from here and here, and want to see all this running on the big screen with Android TV. How do we get Qt, or at least the basic modules like QtGui, QtQuick, etc. up and running on there?

nv_shield_2017 Our test device.

In this little guide we are going to build qtbase for Android targeting AArch64 and will deploy some examples to the Android TV device. To make it more interesting, we will do this from Windows.

Pre-requisites

The Qt documentation and wiki pages document the process fairly well. One thing to note is that a sufficient MinGW toolchain is easily obtainable by installing the official 32-bit MinGW package from Qt 5.8. Visual Studio is not sufficient as of today.

Once MinGW, Perl, git, Java, Ant, the Android SDK, and the 32-bit Android NDK are installed, open a Qt MinGW command prompt and set some environment variables:


set PATH=c:\android\tools;c:\android\platform-tools;
  c:\android\android-ndk-r13b;c:\android\qtbase\bin;
  C:\Program Files\Java\jdk1.8.0_121\bin;
  c:\android\ant\bin;%PATH%
set ANDROID_API_VERSION=android-24
set ANDROID_SDK_ROOT=c:\android
set ANDROID_BUILD_TOOLS_REVISION=25.0.2

Adapt the paths as necessary. Here we assume that the Android SDK is in c:\android, the NDK in android-ndk-r13b, qtbase/dev is checked out to c:\android\qtbase, etc.

The Shield TV has Android 7.0 and the API level is 24. This is great for trying out Vulkan in particular since the level 24 NDK comes with the Vulkan headers, unlike level 23.

Build qtbase

Now the fun part: configure. Note that architecture.


configure -developer-build -release -platform win32-g++
  -xplatform android-g++ -android-arch arm64-v8a
  -android-ndk c:/android/android-ndk-r13b -android-sdk c:/android
  -android-ndk-host windows -android-ndk-platform android-24
  -android-toolchain-version 4.9 -opensource -confirm-license
  -nomake tests -nomake examples -v

Once this succeeds, check the output to see if the necessary features (Vulkan in this case) are enabled.

Then build with mingw32-make -j8 or similar.

Deploying

To get androiddeployqt, check out the qttools repo, go to src/androiddeployqt and do qmake and mingw32-make. The result is a host (x86) build of the tool in qtbase/bin.

For general information on androiddeployqt usage, check the documentation.

Here we will also rely on Ant. This means that Ant must either be in the PATH, as shown above, or the location must be provided to androiddeployqt via the --ant parameter.

Now, Qt 5.8.0 and earlier have a small issue with AArch64 Android deployments. Therefore, grab the patch from Gerrit and apply on top of your qtbase tree if it is not there already. (it may or may not have made its way to the dev branch via merges yet)

After this one can simply go to a Qt application, for instance qtbase/examples/opengl/qopenglwidget and do:


qmake
mingw32-make install INSTALL_ROOT=bld
androiddeployqt --output bld
adb install -r bld/bin/QtApp-debug.apk

Launching

Now that a Qt application is installed, let's launch it.

Except that it does not show up in the Android TV launcher.

One easy workaround could be to adb shell and do something like the following:


am start -n org.qtproject.example.qopenglwidget/org.qtproject.qt5.android.bindings.QtActivity

Then again, it would be nice to get something like this:

nv_shield_qopenglwidget_launcher

Therefore, let's edit bld/AndroidManifest.xml:

<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<!--<category android:name="android.intent.category.LAUNCHER"/>-->
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>

and reinstall by running ant debug install. Changing the category name does the trick.

Note that rerunning androiddeployqt overwrites the manifest file. A more reusable alternative would be to make a copy of the template, change it, and use ANDROID_PACKAGE_SOURCE_DIR.

The result

Widget applications, including OpenGL, run fairly well:

nv_shield_qopenglwidget

Or something more exciting:

qqvk_shield_1

No, really. That clear to green is actually done via Vulkan.

qt_vk_android_texture

And finally, the hellovulkantexture example using QVulkanWindow! (yeah, colors are a bit bad on these photos)

adb logcat is your friend, as usual. Let's get some proof that our textured quad is indeed drawn via Vulkan:


qt.vulkan: Vulkan init (libvulkan.so)
vulkan  : searching for layers in '/data/app/org.qtproject.example.hellovulkantexture-2/lib/arm64'
...
qt.vulkan: Supported Vulkan instance layers: QVector()
qt.vulkan: Supported Vulkan instance extensions: QVector(QVulkanExtension("VK_KHR_surface" 25), QVulkanExtension("VK_KHR_android_surface" 6), QVulkanExtension("VK_EXT_debug_report" 2))
qt.vulkan: Enabling Vulkan instance layers: ()
qt.vulkan: Enabling Vulkan instance extensions: ("VK_EXT_debug_report", "VK_KHR_surface", "VK_KHR_android_surface")
qt.vulkan: QVulkanWindow init
qt.vulkan: 1 physical devices
qt.vulkan: Physical device [0]: name 'NVIDIA Tegra X1' version 361.0.0
qt.vulkan: Using physical device [0]
qt.vulkan: queue family 0: flags=0xf count=16
qt.vulkan: Supported device layers: QVector()
qt.vulkan: Enabling device layers: QVector()
qt.vulkan: Supported device extensions: QVector(QVulkanExtension("VK_KHR_swapchain" 68), QVulkanExtension("VK_KHR_sampler_mirror_clamp_to_edge" 1), QVulkanExtension("VK_NV_dedicated_allocation" 1), QVulkanExtension("VK_NV_glsl_shader" 1))
qt.vulkan: Enabling device extensions: QVector(VK_KHR_swapchain)
qt.vulkan: memtype 0: flags=0x1
qt.vulkan: memtype 1: flags=0x1
qt.vulkan: memtype 2: flags=0x7
qt.vulkan: memtype 3: flags=0xb
qt.vulkan: Picked memtype 2 for host visible memory
qt.vulkan: Picked memtype 0 for device local memory
initResources
uniform buffer offset alignment is 256
qt.vulkan: Creating new swap chain of 2 buffers, size 1920x1080
qt.vulkan: Actual swap chain buffer count: 2
qt.vulkan: Allocating 8847360 bytes for depth-stencil
initSwapChainResources
...

Should you need validation layers, follow the instructions from the Android Vulkan docs and rebuild and redeploy the package after copying the libVkLayer* to the right location.

That's all for now. Have fun experimenting. The basic Vulkan enablers, including QVulkanWindow are currently scheduled for Qt 5.10, with support for Windows, Linux/X11, and Android. (the list may grow later on)


Blog Topics:

Comments