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, as well as the entry points of
extensions required by the Android CDD. 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
Android 9 and higher support the Vulkan API version 1.1. Android 7 to Android 9 support the Vulkan API version 1.0. For more information about the Vulkan 1.1 API, see the Vulkan 1.1 API spec.
Vulkan 1.1 support 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.
Implementing Vulkan 1.1
Android devices should support Vulkan 1.1 if they:
- Launch with Android 10.
- Support a 64-bit ABI.
- Aren't low memory.
Other devices can optionally support Vulkan 1.1.
To implement Vulkan 1.1:
- Add a Vulkan driver that supports Vulkan 1.1 plus the additional Android 1.1 CDD requirements, or update the existing Vulkan 1.0 driver.
- Ensure that
PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x401000)
returnstrue
by adding a rule such as the following to an appropriatedevice.mk
file: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
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 onlyVK_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 first call to vkGetSwapChainInfoWSI(..
VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)
. 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 = VkSwapChainCreateInfoWSI::imageUsageFlags .flags = 0 .sharingMode = VkSwapChainCreateInfoWSI::sharingMode .queueFamilyCount = VkSwapChainCreateInfoWSI::queueFamilyCount .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
In Android 8.0 and higher, the platform provides a
VkSwapchainImageCreateInfo
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.
Acquiring 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 VkSemaphore
and
VkFence
into the same pending state as
vkQueueSignalSemaphore
and vkQueueSubmit
respectively,
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.
Releasing 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:
- Khronos Vulkan Conformance tests
in the
CtsDeqpTestCases
module, which include functional API tests for Vulkan 1.0 and 1.1. - The
CtsGraphicsTestCases
module, which tests that the device is configured correctly for 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.
If the feature flag value is at least 0x07E30301
but less than
0x07E40301
, this means that the device claims to pass all of the
Android 10 Vulkan dEQP tests but isn't guaranteed to pass Vulkan dEQP tests
that were added for Android 11.
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.