Vulkan 是一种低开销、跨平台 API,适用于高性能 3D 图形。与 OpenGL ES (GLES) 类似,Vulkan 提供工具,用于在应用中创建高质量的实时图形。使用 Vulkan 的优势包括降低 CPU 开销以及支持 SPIR-V 二进制中间语言。
要成功实现 Vulkan,设备必须包含
- Android 提供的 Vulkan 加载程序。
- Vulkan 驱动程序由 GPU IHV 等 SoC 提供,用于实现 Vulkan API。为了支持 Vulkan 功能,Android 设备需要支持 Vulkan 的 GPU 硬件和相关的驱动程序。GPU 还必须支持 GLES 3.1 及更高版本。请咨询您的 SoC 供应商以请求驱动程序支持。
如果设备包含 Vulkan 驱动程序,则该设备需要声明 FEATURE_VULKAN_HARDWARE_LEVEL
和 FEATURE_VULKAN_HARDWARE_VERSION
系统功能,并使用准确反映设备功能的版本。这有助于确保设备符合兼容性定义文档 (CDD) 的要求。
Vulkan 加载程序
Vulkan 加载程序 platform/frameworks/native/vulkan
是 Vulkan 应用和设备 Vulkan 驱动程序之间的主要接口。Vulkan 加载程序安装在 /system/lib[64]/libvulkan.so
。加载程序提供核心 Vulkan API 入口点、Android CDD 要求的扩展程序的入口点以及许多其他可选扩展程序。窗口系统集成 (WSI) 扩展程序由加载程序导出,并且主要在加载程序而不是驱动程序中实现。加载程序还支持枚举和加载层,这些层可以公开其他扩展程序并拦截到达驱动程序途中的核心 API 调用。
NDK 包含用于链接的存根 libvulkan.so
库。该库导出与加载程序相同的符号。应用调用从真实的 libvulkan.so
库导出的函数以进入加载程序中的 trampoline 函数,这些函数根据它们的第一个参数调度到相应的层或驱动程序。vkGet*ProcAddr()
调用返回 trampoline 调度的函数指针(即,它直接调用核心 API 代码)。通过函数指针而不是导出的符号进行调用效率更高,因为它跳过了 trampoline 和调度。
驱动程序枚举和加载
构建系统映像时,Android 希望系统知道哪些 GPU 可用。加载程序使用 hardware.h
中的现有 HAL 机制来发现和加载驱动程序。32 位和 64 位 Vulkan 驱动程序的首选路径是
/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
在 Android 7.0 及更高版本中,Vulkan hw_module_t
派生类封装了单个 hw_module_t
结构;仅支持一个驱动程序,并且常量字符串 HWVULKAN_DEVICE_0
传递给 open()
。
Vulkan hw_device_t
派生类对应于可以支持多个物理设备的单个驱动程序。hw_device_t
结构可以扩展为导出 vkGetGlobalExtensionProperties()
、vkCreateInstance()
和 vkGetInstanceProcAddr()
函数。加载程序可以通过调用 hw_device_t
结构的 vkGetInstanceProcAddr()
找到所有其他 VkInstance()
、VkPhysicalDevice()
和 vkGetDeviceProcAddr()
函数。
层发现和加载
Vulkan 加载程序支持枚举和加载层,这些层可以公开其他扩展程序并拦截到达驱动程序途中的核心 API 调用。Android 系统映像中不包含层;但是,应用可以在其 APK 中包含层。
使用层时,请记住 Android 的安全模型和政策与其他平台显着不同。特别是,Android 不允许在生产(非 root)设备上将外部代码加载到不可调试的进程中,也不允许外部代码检查或控制进程的内存、状态等。这包括禁止将核心转储、API 跟踪等保存到磁盘以供以后检查。只有作为不可调试应用一部分交付的层才会在生产设备上启用,并且驱动程序不得提供违反这些政策的功能。
层的用例包括
- 开发时层 - 用于跟踪/分析/调试工具的验证层和 shim 不应安装在生产设备的系统映像上。用于跟踪/分析/调试工具的验证层和 shim 应该是可更新的,而无需系统映像。想要在开发期间使用其中一个层的开发者可以修改应用软件包,例如,通过将文件添加到其原生库目录。想要诊断不可修改的已发布应用中的故障的 IHV 和 OEM 工程师假定可以访问系统映像的非生产(root)版本,除非这些应用是可调试的。有关更多信息,请参阅 Android 上的 Vulkan 验证层。
- 实用程序层 - 这些层公开扩展程序,例如,实现设备内存内存管理器的层。开发者选择要在其应用中使用的层以及这些层的版本;使用同一层的不同应用可能仍使用不同的版本。开发者选择要在其应用软件包中发布的这些层。
- 注入(隐式)层 - 包括用户或其他应用在应用不知情或未经其同意的情况下提供的帧速率、社交网络和游戏启动器叠加层。这些层违反了 Android 的安全政策,并且不受支持。
对于不可调试的应用,加载程序仅在应用的本地库目录中搜索层,并尝试加载名称与特定模式匹配的任何库(例如,libVKLayer_foo.so
)。
对于可调试的应用,加载程序在 /data/local/debug/vulkan
中搜索层,并尝试加载与特定模式匹配的任何库。
Android 允许在 Android 和其他平台之间通过构建环境更改来移植层。有关层和加载程序之间接口的详细信息,请参阅 Vulkan 加载程序接口架构。Khronos 维护的验证层托管在 Vulkan Validation Layers 中。
Vulkan API 版本和功能
下表列出了几个 Android 版本的 Vulkan API 版本。Android 版本 | Vulkan 版本 |
---|---|
Android 13 | Vulkan 1.3 |
Android 9 | Vulkan 1.1 |
Android 7 | Vulkan 1.0 |
Vulkan 1.3 功能概述
Vulkan 1.3 将许多以前可选的扩展程序规范化为 Vulkan 核心功能。此功能的大部分包含旨在增强对 Vulkan 编程接口的控制和粒度。单通道渲染通道实例不再需要渲染通道对象或帧缓冲区。可以减少管道状态对象的总数,并且 API 内的同步得到了彻底改造。Vulkan 1.3 具有与 Vulkan 1.2、1.1 和 1.0 相同的硬件要求,大部分实现都在 SoC 特定的图形驱动程序中,而不是框架中。
Android 最重要的 Vulkan 1.3 功能是
- 支持单通道渲染通道实例
- 支持立即终止着色器调用
- 对管道创建、共享和控制进行更精细的粒度控制
Vulkan 1.3 还包括几个较小的功能和 API 可用性增强功能。可以在 核心修订(Vulkan 1.3) 中找到对核心 Vulkan API 进行的所有修订 1.3 次要修订。
Vulkan 1.2 功能概述
Vulkan 1.2 添加了许多功能和扩展程序,简化了 API 表面。这包括统一的 内存模型 以及可以从设备驱动程序查询的其他信息。Vulkan 1.2 具有与 Vulkan 1.0 和 1.1 相同的硬件要求;所有实现都在 SoC 特定的图形驱动程序中,而不是框架中。
Android 最重要的 Vulkan 1.2 功能是支持 8 位存储。
Vulkan 1.2 还包括几个较小的功能和 API 可用性增强功能。可以在 核心修订(Vulkan 1.2) 中找到对核心 Vulkan API 进行的所有修订 1.2 次要修订。
Vulkan 1.1 功能概述
Vulkan 1.1 包括对内存/同步互操作的支持,这使 OEM 能够在设备上支持 Vulkan 1.1。此外,内存/同步互操作使开发者能够确定设备上是否支持 Vulkan 1.1,并在支持时有效地使用它。Vulkan 1.1 具有与 Vulkan 1.0 相同的硬件要求,但大部分实现都在 SOC 特定的图形驱动程序中,而不是框架中。
Android 最重要的 Vulkan 1.1 功能是
- 支持从 Vulkan 外部导入和导出内存缓冲区和同步对象(用于与相机、编解码器和 GLES 互操作)
- 支持 YCbCr 格式
Vulkan 1.1 还包括几个较小的功能和 API 可用性增强功能。可以在 核心修订(Vulkan 1.1) 中找到对核心 Vulkan API 进行的所有修订 1.1 次要修订。
选择 Vulkan 支持
Android 设备应支持可用的最先进的 Vulkan 功能集,前提是它们支持 64 位 ABI 且不是低内存设备。
搭载 Android 13 及更高版本的设备应支持 Vulkan 1.3。
通过 Android 10 发布的设备应支持 Vulkan 1.1。
其他设备可以选择性地支持 Vulkan 1.3、1.2 和 1.1。
支持 Vulkan 版本
如果满足以下条件,则 Android 设备支持 Vulkan 版本
- 添加一个支持感兴趣的 Vulkan 版本的 Vulkan 驱动程序(这必须是 Vulkan 1.3、1.1 或 1.0 版本之一),以及 Android 版本的其他 CDD 要求。或者,更新较低 Vulkan 版本号的现有 Vulkan 驱动程序。
- 对于 Vulkan 1.3 或 1.1,请确保软件包管理器返回的系统功能为正确的 Vulkan 版本返回
true
。- 对于 Vulkan 1.3,该功能是
PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x403000)
。 - 对于 Vulkan 1.1,该功能是
PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x401000)
。
device.mk
文件中添加如下所示的规则,为 Vulkan 1.3 和 Vulkan 1.1 返回true
。- 为 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
- 为 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
- 对于 Vulkan 1.3,该功能是
Android 基线配置文件 (ABP)
我们鼓励所有 Android 设备都符合最新的 Android Baseline 2022 配置文件,如 Android Baseline 配置文件指南中所述。
任何支持 Android 14 或更高版本以及 Vulkan API 的设备,都必须满足 Android Baseline 2021 配置文件中定义的所有功能。所需功能的完整列表在 Vulkan 配置文件 json
文件中列出,但所需功能的关键子集包括
- 通过 ASTC 和 ETC 压缩纹理。
- 通过
VK_EXT_swapchain_colorspace
可变颜色空间。 - 通过
sampleRateShading
样本着色和多样本插值。
窗口系统集成 (WSI)
在 libvulkan.so
中,驱动程序实现了以下窗口系统集成 (WSI) 扩展程序
VK_KHR_surface
VK_KHR_android_surface
VK_KHR_swapchain
VK_KHR_driver_properties
,在 Android 10 中仅为 Vulkan 1.1 实现VK_GOOGLE_display_timing
,在 Android 10 中为任何 Vulkan 版本实现
VkSurfaceKHR
和 VkSwapchainKHR
对象以及与 ANativeWindow
的所有交互均由平台处理,并且不向驱动程序公开。WSI 实现依赖于 VK_ANDROID_native_buffer
扩展程序,该扩展程序必须由驱动程序支持;此扩展程序仅由 WSI 实现使用,不向应用公开。
Gralloc 用法标记
Vulkan 实现可能需要使用实现定义的私有 Gralloc 用法标记来分配交换链缓冲区。创建交换链时,Android 通过调用以下函数要求驱动程序将请求的格式和图像用法标记转换为 Gralloc 用法标记
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 );
format
和 imageUsage
参数取自 VkSwapchainCreateInfoKHR
结构。驱动程序应使用格式和用法所需的 Gralloc 用法标记填充 *grallocConsumerUsage
和 *grallocProducerUsage
。驱动程序返回的用法标记与交换链使用者在分配缓冲区时请求的用法标记组合在一起。
Android 7.x 调用早期版本的 VkSwapchainImageUsageFlagsANDROID()
,名为 vkGetSwapchainGrallocUsageANDROID()
。Android 8.0 及更高版本弃用了 vkGetSwapchainGrallocUsageANDROID()
,但如果驱动程序未提供 vkGetSwapchainGrallocUsage2ANDROID()
,则仍会调用 vkGetSwapchainGrallocUsageANDROID()
VkResult VKAPI vkGetSwapchainGrallocUsageANDROID( VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage );
vkGetSwapchainGrallocUsageANDROID()
不支持交换链用法标记或扩展的 Gralloc 用法标记。
Gralloc 支持的图像
VkNativeBufferANDROID
是用于创建由 Gralloc 缓冲区支持的图像的 vkCreateImage
扩展结构。VkNativeBufferANDROID
在 VkImageCreateInfo
结构链中提供给 vkCreateImage()
。在调用 vkCreateSwapchainKHR
期间会发生对 vkCreateImage()
和 VkNativeBufferANDROID
的调用。WSI 实现分配为交换链请求的本机缓冲区的数量,然后为每个缓冲区创建一个 VkImage
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;
创建 Gralloc 支持的图像时,VkImageCreateInfo
具有以下数据
.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
在 Android 8.0 及更高版本中,当交换链需要任何交换链图像用法标记时,平台在提供给 vkCreateImage
的 VkImageCreateInfo
链中提供 VkSwapchainImageCreateInfoKHR
扩展结构。扩展结构包含交换链图像用法标记
typedef struct { VkStructureType sType; // must be VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID const void* pNext; VkSwapchainImageUsageFlagsANDROID usage; } VkSwapchainImageCreateInfoANDROID;
在 Android 10 及更高版本中,平台支持 VK_KHR_swapchain
v70,因此 Vulkan 应用能够创建由交换链内存支持的 VkImage
。应用首先使用链接到 VkImageCreateInfo
结构的 VkImageSwapchainCreateInfoKHR
结构调用 vkCreateImage
。然后,应用使用链接到 VkBindImageMemoryInfo
结构的 VkBindImageMemorySwapchainInfoKHR
结构调用 vkBindImageMemory2(KHR)
。VkBindImageMemorySwapchainInfoKHR
结构中指定的 imageIndex
必须是有效的交换链图像索引。同时,平台在 VkBindImageMemoryInfo
链中提供带有相应 Gralloc 缓冲区信息的 VkNativeBufferANDROID
扩展结构,以便驱动程序知道要将 VkImage
绑定到哪个 Gralloc 缓冲区。
获取图像
vkAcquireImageANDROID
获取交换链图像的所有权,并将外部发信号的本机栅栏导入到现有的 VkSemaphore
对象和现有的 VkFence
对象中
VkResult VKAPI vkAcquireImageANDROID( VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence );
vkAcquireImageANDROID()
在 vkAcquireNextImageKHR
期间调用,以将本机栅栏导入到应用提供的 VkSemaphore
和 VkFence
对象中(但是,在此调用中,信号量和栅栏对象都是可选的)。驱动程序还可以利用此机会识别和处理对 Gralloc 缓冲区状态的任何外部更改;许多驱动程序无需在此处执行任何操作。此调用将 VkSemaphore
和 VkFence
置于与 vkQueueSubmit
发信号时相同的挂起状态,因此队列可以等待信号量,并且应用可以等待栅栏。
当底层本机栅栏发信号时,这两个对象都变为已发信号;如果本机栅栏已发信号,则当此函数返回时,信号量处于已发信号状态。驱动程序取得栅栏文件描述符的所有权,并在不再需要栅栏文件描述符时关闭栅栏文件描述符。即使未提供信号量或栅栏对象,或者即使 vkAcquireImageANDROID
失败并返回错误,驱动程序也必须这样做。如果 fenceFd
为 -1,则就像本机栅栏已发信号一样。
释放图像
vkQueueSignalReleaseImageANDROID
准备交换链图像以供外部使用,创建本机栅栏,并计划在输入信号量已发信号后发出本机栅栏信号
VkResult VKAPI vkQueueSignalReleaseImageANDROID( VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd );
vkQueuePresentKHR()
在提供的队列上调用 vkQueueSignalReleaseImageANDROID()
。驱动程序必须生成一个本机栅栏,该栅栏在 pWaitSemaphores
中的所有 waitSemaphoreCount
信号量发信号之后,以及准备 image
以进行演示所需的任何其他工作完成后,才会发信号。
如果等待信号量(如果有)已发信号,并且 queue
已空闲,则驱动程序可以将 *pNativeFenceFd
设置为 -1
而不是实际的本机栅栏文件描述符,指示无需等待任何内容。调用者拥有并关闭在 *pNativeFenceFd
中返回的文件描述符。
许多驱动程序可以忽略 image 参数,但有些驱动程序可能需要准备与 Gralloc 缓冲区关联的 CPU 端数据结构,以供外部图像使用者使用。为外部使用者使用准备缓冲区内容应作为将图像转换为 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
的一部分异步完成。
如果图像是使用 VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID
创建的,则驱动程序必须允许重复调用 vkQueueSignalReleaseImageANDROID()
,而无需中间调用 vkAcquireImageANDROID()
。
共享可呈现图像支持
某些设备可以在显示管道和 Vulkan 实现之间共享单个图像的所有权,以最大限度地减少延迟。在 Android 9 及更高版本中,加载程序根据驱动程序对 vkGetPhysicalDeviceProperties2
调用的响应有条件地播发 VK_KHR_shared_presentable_image
扩展程序。
如果驱动程序不支持 Vulkan 1.1 或 VK_KHR_physical_device_properties2
扩展程序,则加载程序不会播发对共享可呈现图像的支持。否则,加载程序通过调用 vkGetPhysicalDeviceProperties2()
并将以下结构包含在 VkPhysicalDeviceProperties2::pNext
链中来查询驱动程序功能
typedef struct { VkStructureType sType; // must be VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID const void* pNext; VkBool32 sharedImage; } VkPhysicalDevicePresentationPropertiesANDROID;
如果驱动程序可以与显示系统共享图像的所有权,则它会将 sharedImage
成员设置为 VK_TRUE
。
验证
OEM 可以使用 CTS 测试其 Vulkan 实现,其中包括以下内容
- Khronos Vulkan 一致性测试,位于
CtsDeqpTestCases
模块中,其中包括针对 Vulkan 1.0、1.1、1.2 和 1.3 的功能性 API 测试。 CtsGraphicsTestCases
模块,用于测试设备针对其支持的 Vulkan 功能是否配置正确。
Vulkan 功能标记
支持 Android 11 或更高版本并且支持 Vulkan API 的设备需要公开一个功能标记 android.software.vulkan.deqp.level
。此功能标记的值是一个日期,编码为整数值。它指定与设备声称通过的 Vulkan dEQP 测试相关的日期。
YYYY-MM-DD 形式的日期编码为 32 位整数,如下所示
- 位 0-15 存储年份
- 位 16-23 存储月份
- 位 24-31 存储日期
功能标记的最小允许值为 0x07E30301
,对应于日期 2019-03-01,这是与 Android 10 的 Vulkan dEQP 测试相关的日期。如果功能标记至少为此值,则设备声称通过了所有 Android 10 Vulkan dEQP 测试。
值 0x07E40301
对应于日期 2020-03-01,这是与 Android 11 的 Vulkan dEQP 测试相关的日期。如果功能标记至少为此值,则设备声称通过了所有 Android 11 Vulkan dEQP 测试。
值 0x07E60301
对应于日期 2022-03-01,这是与 Android 13 的 Vulkan dEQP 测试相关的日期。如果功能标记至少为此值,则设备声称通过了所有 Android 13 Vulkan dEQP 测试。
公开特定功能标记(即 0x07E30301
、0x07E40301
、0x07E60301
)的设备声称通过了该功能标记的所有 Android Vulkan dEQP 测试(分别为 Android 10、Android 11、Android 13)。此设备可能通过更高版本 Android 版本的 Vulkan dEQP 测试。
Vulkan dEQP 构成 Android CTS 的一部分。从 Android 11 开始,CTS 的 dEQP 测试运行程序组件知道 android.software.vulkan.deqp.level
功能标记,并跳过任何 Vulkan dEQP 测试,根据此功能标记,设备声称不支持这些测试。此类测试报告为轻松通过。