Implement Vulkan

Vulkan is a low-overhead, cross-platform API for high-performance 3D graphics. Like OpenGL ES (GLES), Vulkan provides tools for creating high-quality, real-time graphics in apps. Advantages of using Vulkan include reductions in CPU overhead and support for the SPIR-V Binary Intermediate language.

To implement Vulkan successfully, a device must include:

  • The Vulkan loader, provided by Android.
  • A Vulkan driver, provided by SoCs such as GPU IHVs, that implements the Vulkan API. To support Vulkan functionality, the Android device needs Vulkan-capable GPU hardware and the associated driver. The GPU must also support GLES 3.1 and higher. Consult your SoC vendor to request driver support.

If a device includes a Vulkan driver, the device needs to declare FEATURE_VULKAN_HARDWARE_LEVEL and FEATURE_VULKAN_HARDWARE_VERSION system features, with versions that accurately reflect the capabilities of the device. This helps ensure that the device is in compliance with the Compatibility Definition Document (CDD).

Vulkan loader

The Vulkan loader platform/frameworks/native/vulkan is the primary interface between Vulkan apps and a device's Vulkan driver. The Vulkan loader is installed at /system/lib[64]/libvulkan.so. The loader provides the core Vulkan API entry points, the entry points of extensions required by the Android CDD, and many additional optional extensions. Window System Integration (WSI) extensions are exported by the loader and primarily implemented in the loader rather than in the driver. The loader also supports enumerating and loading layers that can expose additional extensions and intercept core API calls on their way to the driver.

The NDK includes a stub libvulkan.so library for linking. The library exports the same symbols as the loader. Apps call the functions exported from the real libvulkan.so library to enter trampoline functions in the loader, which dispatch to the appropriate layer or driver based on their first argument. The vkGet*ProcAddr() call returns the function pointers to which the trampolines dispatch (that is, it calls directly into the core API code). Calling through the function pointers, rather than the exported symbols, is more efficient as it skips the trampoline and dispatch.

Driver enumeration and loading

When the system image is built, Android expects the system to know which GPUs are available. The loader uses the existing HAL mechanism in hardware.h to discover and load the driver. Preferred paths for 32-bit and 64-bit Vulkan drivers are:

/vendor/lib/hw/vulkan.<ro.hardware.vulkan>.so
/vendor/lib/hw/vulkan.<ro.product.platform>.so
/vendor/lib64/hw/vulkan.<ro.hardware.vulkan>.so
/vendor/lib64/hw/vulkan.<ro.product.platform>.so

In Android 7.0 and higher, the Vulkan hw_module_t derivative wraps a single hw_module_t struct; only one driver is supported and the constant string HWVULKAN_DEVICE_0 is passed to open().

The Vulkan hw_device_t derivative corresponds to a single driver that can support multiple physical devices. The hw_device_t structure can extend to export vkGetGlobalExtensionProperties(), vkCreateInstance(), and vkGetInstanceProcAddr() functions. The loader can find all other VkInstance(), VkPhysicalDevice(), and vkGetDeviceProcAddr() functions by calling the hw_device_t structure's vkGetInstanceProcAddr().

Layer discovery and loading

The Vulkan loader supports enumerating and loading layers that can expose additional extensions and intercept core API calls on their way to the driver. Android doesn't include layers on the system image; however, apps may include layers in their APK.

When using layers, keep in mind that Android's security model and policies differ significantly from other platforms. In particular, Android doesn't allow loading external code into a nondebuggable process on production (nonrooted) devices, nor does it allow external code to inspect or control the process's memory, state, and so on. This includes a prohibition on saving core dumps, API traces, and so on to disk for later inspection. Only layers delivered as part of nondebuggable apps are enabled on production devices, and drivers must not provide functionality that violates these policies.

Use cases for layers include:

  • Development-time layers — Validation layers and shims for tracing/profiling/debugging tools shouldn't be installed on the system image of production devices. Validation layers and shims for tracing/profiling/debugging tools should be updatable without a system image. Developers who want to use one of these layers during development can modify the app package, for example, by adding a file to their native libraries directory. IHV and OEM engineers who want to diagnose failures in shipping unmodifiable apps are assumed to have access to nonproduction (rooted) builds of the system image, unless those apps are debuggable. For more information see Vulkan validation layers on Android.
  • Utility layers — These layers expose extensions, such as a layer that implements a memory manager for device memory. Developers choose layers, and versions of those layers, to use in their app; different apps using the same layer may still use different versions. Developers choose which of these layers to ship in their app package.
  • Injected (implicit) layers — Includes layers such as frame rate, social network, and game launcher overlays provided by the user or some other app without the app's knowledge or consent. These violate Android's security policies and aren't supported.

For nondebuggable apps, the loader searches for layers only in the app's native library directory and attempts to load any library with a name matching a particular pattern (for example, libVKLayer_foo.so).

For debuggable apps, the loader searches for layers in /data/local/debug/vulkan and attempts to load any library matching a particular pattern.

Android enables layers to be ported with build-environment changes between Android and other platforms. For details on the interface between layers and the loader, see Architecture of the Vulkan Loader Interfaces. The Khronos-maintained validation layers are hosted in Vulkan Validation Layers.

Vulkan API versions and capabilities

The following table lists Vulkan API versions for several Android releases.
Android Version Vulkan Version
Android 13 Vulkan 1.3
Android 9 Vulkan 1.1
Android 7 Vulkan 1.0

Vulkan 1.3 functionality overview

Vulkan 1.3 canonizes a number of previously optional extensions into the Vulkan core functionality. Much of this functionality is included with the intent of increasing control and granularity over the Vulkan programming interface. Single-pass render pass instances no longer need render pass objects or framebuffers. The total number of pipeline state objects can be reduced, and synchronization within the API is overhauled. Vulkan 1.3 has the same hardware requirements as Vulkan 1.2, 1.1, and 1.0, with most of the implementation in the SoC-specific graphics driver, not in the framework.

The most important Vulkan 1.3 features for Android are:

  • Support for single-pass render pass instances
  • Support for immediately terminating a shader invocation
  • Finer granularity over pipeline creation, sharing, and control

Vulkan 1.3 also includes several smaller features and API usability enhancements. All changes made to the core Vulkan API with minor revision 1.3 can be found at Core Revisions (Vulkan 1.3).

Vulkan 1.2 functionality overview

Vulkan 1.2 adds a number of features and extensions that simplifies the API surface. This includes a unified Memory Model and additional information that can be queried from a device driver. Vulkan 1.2 has the same hardware requirements as Vulkan 1.0 and 1.1; all of the implementation is in the SoC-specific graphics driver, not the framework.

The most important Vulkan 1.2 feature for Android is support for 8-bit storage.

Vulkan 1.2 also includes several smaller features and API usability enhancements. All changes made to the core Vulkan API with minor revision 1.2 can be found at Core Revisions (Vulkan 1.2).

Vulkan 1.1 functionality overview

Vulkan 1.1 includes support for memory/synchronization interop, which enables OEMs to support Vulkan 1.1 on devices. Additionally, memory/synchronization interop enables developers to determine whether Vulkan 1.1 is supported on a device, and use it effectively when it is. Vulkan 1.1 has the same hardware requirements as Vulkan 1.0, but most of the implementation is in the SOC-specific graphics driver, not in the framework.

The most important Vulkan 1.1 features for Android are:

  • Support for importing and exporting memory buffers and synchronization objects from outside Vulkan (for interop with camera, codecs, and GLES)
  • Support for YCbCr formats

Vulkan 1.1 also includes several smaller features and API usability enhancements. All changes made to the core Vulkan API with minor revision 1.1 can be found at Core Revisions (Vulkan 1.1).

Choose Vulkan support

Android devices should support the most advanced Vulkan feature set available, provided they support a 64-bit ABI and are not low memory.

Devices that launch with Android 13 and above should support Vulkan 1.3.

Devices that launch through Android 10 should support Vulkan 1.1.

Other devices can optionally support Vulkan 1.3, 1.2, and 1.1.

Support a Vulkan version

An Android device supports a Vulkan version if the following conditions are satisfied:

  1. Add a Vulkan driver that supports the Vulkan version of interest (this must be one of Vulkan version 1.3, 1.1, or 1.0) alongside the additional CDD requirements of the Android version. Alternatively, update an existing Vulkan driver of a lower Vulkan version number.
  2. For Vulkan 1.3 or 1.1, Ensure that the system feature returned by the package manager returns true for the correct vulkan version.
    • For Vulkan 1.3 the feature is PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x403000).
    • For Vulkan 1.1 the feature is PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x401000).
    The package manager will return true for Vulkan 1.3 and Vulkan 1.1 by adding a rule, shown as follows, to an appropriate device.mk file.
    • Add the following for Vulkan 1.3:
      PRODUCT_COPY_FILES += frameworks/native/data/etc/android.hardware.vulkan.version-1_3.xml:
      $(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.vulkan.version.xml
      
    • Add the following for Vulkan 1.1:
      PRODUCT_COPY_FILES += frameworks/native/data/etc/android.hardware.vulkan.version-1_1.xml:
      $(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.vulkan.version.xml
      

Android Baseline profile (ABP)

We encourage all Android devices to conform to the latest Android Baseline 2022 profile as outlined in the Android Baseline profile guide.

Any device that supports Android 14 or higher and the Vulkan API, must fulfill all functionality defined in the Android Baseline 2021 profile. The full list of required functionality is enumerated in the Vulkan profile json file, but a key subset of the required functionality includes:

  • Compressed textures through ASTC and ETC.
  • Variable colorspaces through VK_EXT_swapchain_colorspace.
  • Sample shading and multisample interpolation through sampleRateShading.

Window system integration (WSI)

In libvulkan.so, the driver implements the following window system integration (WSI) extensions:

  • VK_KHR_surface
  • VK_KHR_android_surface
  • VK_KHR_swapchain
  • VK_KHR_driver_properties, implemented for Vulkan 1.1 in Android 10 only
  • VK_GOOGLE_display_timing, implemented for any Vulkan version in Android 10

The VkSurfaceKHR and VkSwapchainKHR objects and all interactions with ANativeWindow are handled by the platform and aren't exposed to drivers. The WSI implementation relies on the VK_ANDROID_native_buffer extension, which must be supported by the driver; this extension is used only by the WSI implementation and isn't exposed to apps.

Gralloc usage flags

Vulkan implementations may need swapchain buffers to be allocated with implementation-defined private Gralloc usage flags. When creating a swapchain, Android asks the driver to translate the requested format and image usage flags into Gralloc usage flags by calling:

typedef enum VkSwapchainImageUsageFlagBitsANDROID {
    VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID = 0x00000001,
    VK_SWAPCHAIN_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkSwapchainImageUsageFlagBitsANDROID;
typedef VkFlags VkSwapchainImageUsageFlagsANDROID;

VkResult VKAPI vkGetSwapchainGrallocUsage2ANDROID(
    VkDevice                          device,
    VkFormat                          format,
    VkImageUsageFlags                 imageUsage,
    VkSwapchainImageUsageFlagsANDROID swapchainUsage,
    uint64_t*                         grallocConsumerUsage,
    uint64_t*                         grallocProducerUsage
);

The format and imageUsage parameters are taken from the VkSwapchainCreateInfoKHR structure. The driver should fill *grallocConsumerUsage and *grallocProducerUsage with the Gralloc usage flags required for the format and usage. The usage flags returned by the driver are combined with the usage flags requested by the swapchain consumer when allocating buffers.

Android 7.x calls an earlier version of VkSwapchainImageUsageFlagsANDROID(), named vkGetSwapchainGrallocUsageANDROID(). Android 8.0 and higher deprecates vkGetSwapchainGrallocUsageANDROID() but still calls vkGetSwapchainGrallocUsageANDROID() if vkGetSwapchainGrallocUsage2ANDROID() isn't provided by the driver:

VkResult VKAPI vkGetSwapchainGrallocUsageANDROID(
    VkDevice            device,
    VkFormat            format,
    VkImageUsageFlags   imageUsage,
    int*                grallocUsage
);

vkGetSwapchainGrallocUsageANDROID() doesn't support swapchain usage flags or extended Gralloc usage flags.

Gralloc-backed images

VkNativeBufferANDROID is a vkCreateImage extension structure for creating an image backed by a Gralloc buffer. VkNativeBufferANDROID is provided to vkCreateImage() in the VkImageCreateInfo structure chain. Calls to vkCreateImage() with VkNativeBufferANDROID happen during the call to vkCreateSwapchainKHR. The WSI implementation allocates the number of native buffers requested for the swapchain, then creates a VkImage for each one:

typedef struct {
    VkStructureType             sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID
    const void*                 pNext;

    // Buffer handle and stride returned from gralloc alloc()
    buffer_handle_t             handle;
    int                         stride;

    // Gralloc format and usage requested when the buffer was allocated.
    int                         format;
    int                         usage;
    // Beginning in Android 8.0, the usage field above is deprecated and the
    // usage2 struct below was added. The usage field is still filled in for
    // compatibility with Android 7.0 drivers. Drivers for Android 8.0
    // should prefer the usage2 struct, especially if the
    // android.hardware.graphics.allocator HAL uses the extended usage bits.
    struct {
        uint64_t                consumer;
        uint64_t                producer;
    } usage2;
} VkNativeBufferANDROID;

When creating a Gralloc-backed image, VkImageCreateInfo has the following data:

  .sType               = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
  .pNext               = the above VkNativeBufferANDROID structure
  .imageType           = VK_IMAGE_TYPE_2D
  .format              = a VkFormat matching the format requested for the gralloc buffer
  .extent              = the 2D dimensions requested for the gralloc buffer
  .mipLevels           = 1
  .arraySize           = 1
  .samples             = 1
  .tiling              = VK_IMAGE_TILING_OPTIMAL
  .usage               = VkSwapchainCreateInfoKHR::imageUsage
  .flags               = 0
  .sharingMode         = VkSwapchainCreateInfoKHR::imageSharingMode
  .queueFamilyCount    = VkSwapchainCreateInfoKHR::queueFamilyIndexCount
  .pQueueFamilyIndices = VkSwapchainCreateInfoKHR::pQueueFamilyIndices

In Android 8.0 and higher, the platform provides a VkSwapchainImageCreateInfoKHR extension structure in the VkImageCreateInfo chain provided to vkCreateImage when any swapchain image usage flags are required for the swapchain. The extension structure contains the swapchain image usage flags:

typedef struct {
    VkStructureType                        sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID
    const void*                            pNext;

    VkSwapchainImageUsageFlagsANDROID      usage;
} VkSwapchainImageCreateInfoANDROID;

In Android 10 and higher, the platform supports VK_KHR_swapchain v70, so the Vulkan app is able to create a VkImage backed by swapchain memory. The app first calls vkCreateImage with a VkImageSwapchainCreateInfoKHR structure chained to the VkImageCreateInfo structure. Then the app calls vkBindImageMemory2(KHR) with a VkBindImageMemorySwapchainInfoKHR structure chained to the VkBindImageMemoryInfo structure. The imageIndex specified in the VkBindImageMemorySwapchainInfoKHR structure must be a valid swapchain image index. Meanwhile, the platform provides a VkNativeBufferANDROID extension structure with the corresponding Gralloc buffer information to the VkBindImageMemoryInfo chain, so the driver knows which Gralloc buffer to bind the VkImage with.

Acquire images

vkAcquireImageANDROID acquires ownership of a swapchain image and imports an externally signaled native fence into both an existing VkSemaphore object and an existing VkFence object:

VkResult VKAPI vkAcquireImageANDROID(
    VkDevice            device,
    VkImage             image,
    int                 nativeFenceFd,
    VkSemaphore         semaphore,
    VkFence             fence
);

vkAcquireImageANDROID() is called during vkAcquireNextImageKHR to import a native fence into the VkSemaphore and VkFence objects provided by the app (however, both semaphore and fence objects are optional in this call). The driver may also use this opportunity to recognize and handle any external changes to the Gralloc buffer state; many drivers won't need to do anything here. This call puts the VkSemaphore and VkFence into the same pending state as if signaled by vkQueueSubmit, so queues can wait on the semaphore and the app can wait on the fence.

Both objects become signaled when the underlying native fence signals; if the native fence has already signaled, then the semaphore is in the signaled state when this function returns. The driver takes ownership of the fence file descriptor and closes the fence file descriptor when no longer needed. The driver must do so even if neither a semaphore or fence object is provided, or even if vkAcquireImageANDROID fails and returns an error. If fenceFd is -1, it's as if the native fence was already signaled.

Release images

vkQueueSignalReleaseImageANDROID prepares a swapchain image for external use, creates a native fence, and schedules the native fence to be signaled after the input semaphores have signaled:

VkResult VKAPI vkQueueSignalReleaseImageANDROID(
    VkQueue             queue,
    uint32_t            waitSemaphoreCount,
    const VkSemaphore*  pWaitSemaphores,
    VkImage             image,
    int*                pNativeFenceFd
);

vkQueuePresentKHR() calls vkQueueSignalReleaseImageANDROID() on the provided queue. The driver must produce a native fence that doesn't signal until all waitSemaphoreCount semaphores in pWaitSemaphores signal, and any additional work required to prepare image for presentation completes.

If the wait semaphores (if any) already signaled, and queue is already idle, the driver can set *pNativeFenceFd to -1 instead of an actual native fence file descriptor, indicating that there's nothing to wait for. The caller owns and closes the file descriptor returned in *pNativeFenceFd.

Many drivers can ignore the image parameter, but some may need to prepare CPU-side data structures associated with a Gralloc buffer for use by external image consumers. Preparing buffer contents for use by external consumers should be done asynchronously as part of transitioning the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR.

If the image was created with VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID, then the driver must allow vkQueueSignalReleaseImageANDROID() to be called repeatedly without intervening calls to vkAcquireImageANDROID().

Shared presentable image support

Some devices can share ownership of a single image between the display pipeline and the Vulkan implementation to minimize latency. In Android 9 and higher, the loader conditionally advertises the VK_KHR_shared_presentable_image extension based on the driver's response to a call to vkGetPhysicalDeviceProperties2.

If the driver doesn't support either Vulkan 1.1 or the VK_KHR_physical_device_properties2 extension, the loader doesn't advertise support for shared presentable images. Otherwise, the loader queries the driver capabilities by calling vkGetPhysicalDeviceProperties2() and including the following structure in the VkPhysicalDeviceProperties2::pNext chain:

typedef struct {
    VkStructureType sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID
    const void*     pNext;
    VkBool32        sharedImage;
} VkPhysicalDevicePresentationPropertiesANDROID;

If the driver can share ownership of an image with the display system, it sets the sharedImage member to VK_TRUE.

Validation

OEMs can test their Vulkan implementation using CTS, which includes the following:

  • Khronos Vulkan Conformance tests in the CtsDeqpTestCases module, which include functional API tests for Vulkan 1.0, 1.1, 1.2, and 1.3.
  • The CtsGraphicsTestCases module, which tests that the device is configured correctly for the Vulkan capabilities it supports.

Vulkan feature flag

A device that supports Android 11 or higher and that supports the Vulkan API is required to expose a feature flag, android.software.vulkan.deqp.level. The value of this feature flag is a date, encoded as an integer value. It specifies the date associated with the Vulkan dEQP tests that the device claims to pass.

A date of the form YYYY-MM-DD is encoded as a 32-bit integer as follows:

  • Bits 0-15 store the year
  • Bits 16-23 store the month
  • Bits 24-31 store the day

The minimum allowed value for the feature flag is 0x07E30301, which corresponds to the date 2019-03-01, which is the date associated with the Vulkan dEQP tests for Android 10. If the feature flag is at least this value, the device claims to pass all of the Android 10 Vulkan dEQP tests.

Value 0x07E40301 corresponds to the date 2020-03-01, which is the date associated with the Vulkan dEQP tests for Android 11. If the feature flag is at least this value, the device claims to pass all of the Android 11 Vulkan dEQP tests.

Value 0x07E60301 corresponds to the date 2022-03-01, which is the date associated with the Vulkan dEQP tests for Android 13. If the feature flag is at least this value, the device claims to pass all of the Android 13 Vulkan dEQP tests.

A device that exposes a specific feature flag (i.e 0x07E30301, 0x07E40301, 0x07E60301) claims to pass all Android Vulkan dEQP tests of that feature flag (Android 10, Android 11, Android 13 respectively). This device may pass Vulkan dEQP tests from a later Android release.

Vulkan dEQP forms part of Android CTS. From Android 11, the dEQP test runner component of CTS is aware of the android.software.vulkan.deqp.level feature flag, and skips any Vulkan dEQP tests that - according to this feature flag - the device doesn't claim to support. Such tests are reported as trivially passing.