Get started with Vulkan on Android

Vulkan is the primary low-level graphics API on Android. Vulkan provides optimal performance for games that implement their own game engine and renderer.

To successfully implement Vulkan in your game engine you must:

  • Identify which Android devices to use with Vulkan
  • Understand the tradeoffs of supporting older Android devices
  • Add Vulkan to your Android build target
  • Choose a shader compiler to create SPIR-V for Vulkan
  • Determine the available Vulkan API version at runtime
  • Learn how to optimize your Vulkan rendering operations with Vulkan profiles, frame pacing, and pre-rotation
  • Select graphics tools for debugging and performance analysis

Choose minimum device specifications for Vulkan

Vulkan is available on Android beginning with Android 7.0 (API level 24). Not all Android devices running Android 7.0 or higher support Vulkan. You need to determine which Vulkan-capable Android devices your game supports.

Recommendations

Use the following specifications as minimum requirements for Vulkan support:

  • Device is running Android 10.0 (API level 29) or higher
  • Device supports Vulkan API version 1.1 or higher
  • Device has hardware capabilities and features compatible with the 2022 Android Baseline profile

Older device support

If your game is designed to run on a wide range of devices with varying levels of graphics capabilities, you may need to support devices older than those recommended in Choose minimum devices specifications for Vulkan above. Before building support for older devices, evaluate whether Vulkan provides benefits to your game. Games that have lots of draw calls and that use OpenGL ES can see significant driver overhead due to the high cost of making draw calls within OpenGL ES. These games can become CPU bound from spending large portions of their frame time in the graphics driver. The games can also see significant reductions in CPU and power use by switching from OpenGL ES to Vulkan. This is especially applicable if your game has complex scenes that can't effectively use instancing to reduce draw calls. When targeting older devices, include OpenGL ES rendering support as a fallback, as some devices in your target device list may have Vulkan implementations that can't run your game reliably.

You may not want to support older Vulkan-capable devices because they lack performance and features or have stability issues.

Performance and Features

Older Vulkan-capable Android devices may not have the rendering performance or hardware support for features needed to run your game. This is especially likely if your game has high-fidelity graphics and Vulkan is the only API you are targeting on Android. Many older devices are limited to version 1.0.3 of the Vulkan API and are often missing widely used Vulkan extensions available on more modern hardware.

Stability

Older Android devices could be using out-of-date Vulkan drivers. These driver versions might include bugs that can affect the stability of your game. Working around driver bugs can involve significant amounts of testing and engineering time.

Add Vulkan to your project

To add Vulkan to your project you need to:

  • Include Vulkan API headers
  • Compile shader code to SPIR-V
  • Call the Vulkan API at runtime

Include Vulkan API headers

Your game needs to include the Vulkan API header files to compile code that uses Vulkan. You can find a copy of the Vulkan headers in the Android NDK or packaged in Vulkan SDK releases. Any particular NDK version only includes Vulkan headers available at the time of the NDK release. If you use Vulkan headers from the NDK, use NDK version 25 or higher, which includes header files that support Vulkan version 1.3. The Vulkan SDK has the most current version of the headers.

Compile shader code to SPIR-V

The Vulkan API expects shader programs to be provided in the SPIR-V binary intermediate format. This convention is different from OpenGL ES, where you could submit source code written in the OpenGL Shading Language (GLSL) as text strings. Use a shader compiler to take code written in a shader language such as GLSL or the High-level Shader Language (HLSL) and compile it into SPIR-V modules for use with Vulkan.

The shaderc compiler can be used to compile shader programs written in GLSL into SPIR-V. If your game uses HLSL, the DirectXShaderCompiler supports SPIR-V output. Typically, you compile shader programs offline as part of the asset build process for your game and include the SPIR-V modules as part of your runtime assets.

Call the Vulkan API at runtime

To call the Vulkan API, your game needs to obtain function pointers to Vulkan API calls. The most straightforward way to do this is to link against the libvulkan.so shared library, which is included in the Android NDK. Linking against the library has two shortcomings: additional function dispatch overhead and limitations on which Vulkan API function pointers are automatically resolved.

When you call a Vulkan API function, control passes through a dispatch table managed by a construct called the Vulkan loader. Android uses its own Vulkan loader implementation and not the LunarG loader. This loader system is part of the layer architecture of the Vulkan API. Linking to the system library at build time results in an additional dispatch level for a given API call. While the overhead is small, it can be noticeable for games that perform high volumes of Vulkan calls.

The system library generally only resolves pointers to Vulkan functions that are considered part of the core API. Vulkan has a large number of extensions, which define additional Vulkan functions, many of which are not automatically resolved by the system library. You need to manually resolve pointers to these Vulkan functions before using them.

To mitigate these issues, dynamically resolve pointers to all Vulkan functions you intend to use at runtime. One way to accomplish this is by using an open source meta-loader library such as volk. The AGDKTunnel sample game integrates volk for this purpose. If you are using a meta-loader library, don't link against the libvulkan.so shared library in your build scripts.

Determine the available Vulkan API version

Android supports the following Vulkan API versions:

  • 1.0.3
  • 1.1
  • 1.3

The highest Vulkan API version number available on a given device is determined by Android version and Vulkan driver support.

Android version

Platform support for a Vulkan API version is dependent on a minimum Android version (API level):

  • 1.3 — Android 13.0 (API level 33) and higher
  • 1.1 — Android 10.0 (API level 29) and higher
  • 1.0.3 — Android 7.0 (API level 24) and higher

Vulkan driver support

Android platform support for a Vulkan API version does not guarantee the API version is supported by the device's Vulkan driver. A device running Android 13 might only support version 1.1 of the Vulkan API.

When initializing Vulkan, don't request an API version greater than:

An example of determining the highest supported Vulkan API version follows:

// Minimum Android API levels for Vulkan 1.3/1.1 version support
static constexpr int kMinimum_vk13_api_level = 33;
static constexpr int kMinimum_vk11_api_level = 29;

uint32_t GetHighestSupportedVulkanVersion(VkPhysicalDevice physical_device) {
  uint32_t instance_api_version = 0;
  vkEnumerateInstanceVersion(&instance_api_version);

  VkPhysicalDeviceProperties device_properties;
  vkGetPhysicalDeviceProperties(physical_device, &device_properties);

  // Instance and device versions don't have to match, use the lowest version
  // number for API support if they don't.
  const uint32_t driver_api_version =
      (instance_api_version < device_properties.apiVersion) ?
      instance_api_version : device_properties.apiVersion;

  const int device_api_level = android_get_device_api_level();
  if (device_api_level >= kMinimum_vk13_api_level &&
      driver_api_version >= VK_API_VERSION_1_3) {
    return VK_API_VERSION_1_3;
  } else if (device_api_level >= kMinimum_vk11_api_level &&
             driver_api_version >= VK_API_VERSION_1_1) {
    return VK_API_VERSION_1_1;
  }
  return VK_API_VERSION_1_0;
}

Determine Vulkan profile compatibility

Vulkan profiles are JSON files that define a set of required features, extensions, capabilities, and minimum parameter limits that a Vulkan device must support to be compatible with the profile. To determine if a device is compatible with a specific Vulkan profile, such as the 2022 Android Baseline profile, use the open source Vulkan Profiles API library. You can also parse the profile JSON file yourself and query device capabilities using the relevant Vulkan APIs to determine profile compatibility.

Implement frame pacing

Proper frame pacing is an essential part of delivering a high-quality gameplay experience. The Android Game Development Kit includes the Frame Pacing library to help your game achieve optimal frame pacing. For more implementation details, see Integrate Android Frame Pacing into your Vulkan renderer.

Implement pre-rotation

Android devices can display in multiple orientations. The device orientation can be different from the orientation of the render surface. Unlike OpenGL ES on Android, Vulkan does not handle discrepancies between the two. To understand how the orientation process works and the optimal method of handling orientation differences when using Vulkan, see Handle device rotation with Vulkan pre-rotation.

Troubleshoot and profile Vulkan rendering

Multiple tools are available to help you diagnose rendering issues and performance problems with Vulkan rendering code.

Vulkan validation layers

Vulkan validation layers are runtime libraries that can be enabled to inspect your calls to the Vulkan API and provide warnings or errors about incorrect or nonoptimal use. These validation layers are not active by default, as the validation process adds runtime overhead and impacts the performance of your game. For information on how to use validation layers with your game, see Vulkan validation layers on Android.

Frame capture tools

Use frame capture tools to record and replay the Vulkan API calls made during a game frame. These tools let you:

  • View information about and visualizations of active graphic resources
  • See the sequence of API calls made by your game and see the API parameters
  • Explore the state of the graphics pipeline at the time of a draw call
  • Visualize the results of rendering up to a specific draw call in the frame

Use the open source RenderDoc tool to capture frames from games running on Android. RenderDoc supports frame capture of both Vulkan and OpenGL ES.

The Android GPU Inspector (AGI) can also be used to capture Vulkan frames.

Performance analysis tools

Use performance analysis tools to investigate rendering issues in your game that cause suboptimal frame rates. Individual GPU vendors provide tools designed to profile your game and provide performance data specific to their GPU architectures. The performance characteristics and bottlenecks of your game can vary significantly when rendering on GPUs from different vendors or even on different GPU generations from the same vendor.

You can also use the Android GPU Inspector to gather and analyze performance data. Unlike the vendor tools, Android GPU Inspector is compatible with multiple GPUs from different vendors. However, Android GPU Inspector does not support older Android devices and may not be compatible with all new devices.

Sample code

The following sample code projects use Vulkan, implement pre-rotation, and demonstrate integration of validation layers: