實施 Vulkan

Vulkan是用於高性能 3D 圖形的低開銷、跨平台 API。與OpenGL ES (GLES)一樣,Vulkan 提供了用於在應用程序中創建高質量實時圖形的工具。使用 Vulkan 的優勢包括減少 CPU 開銷和支持SPIR-V 二進制中間語言。

要成功實施 Vulkan,設備必須包括:

  • Android 提供的 Vulkan 加載程序。
  • 由 GPU IHV 等 SoC 提供的 Vulkan 驅動程序,用於實現Vulkan API 。為了支持 Vulkan 功能,Android 設備需要支持 Vulkan 的 GPU 硬件和相關的驅動程序。 GPU 還必須支持 GLES 3.1 及更高版本。請諮詢您的 SoC 供應商以請求驅動程序支持。

如果設備包含 Vulkan 驅動程序,則設備需要聲明FEATURE_VULKAN_HARDWARE_LEVELFEATURE_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庫導出的函數,以在加載程序中輸入蹦床函數,這些函數根據它們的第一個參數分派到適當的層或驅動程序。 vkGet*ProcAddr()調用返回蹦床分派的函數指針(即,它直接調用核心 API 代碼)。通過函數指針而不是導出的符號調用更有效,因為它跳過了蹦床和調度。

驅動程序枚舉和加載

構建系統映像時,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 不允許將外部代碼加載到生產(非根)設備上的不可調試進程中,也不允許外部代碼檢查或控制進程的內存、狀態等。這包括禁止將核心轉儲、API 跟踪等保存到磁盤以供以後檢查。只有作為不可調試應用程序的一部分交付的層才能在生產設備上啟用,並且驅動程序不得提供違反這些策略的功能。

層的用例包括:

  • 開發時層——用於跟踪/分析/調試工具的驗證層和墊片不應安裝在生產設備的系統映像上。用於跟踪/分析/調試工具的驗證層和墊片應該可以在沒有系統映像的情況下進行更新。想要在開發期間使用這些層之一的開發人員可以修改應用程序包,例如,通過將文件添加到其本機庫目錄。 IHV 和 OEM 工程師希望診斷無法修改的應用程序的交付失敗,假定他們有權訪問系統映像的非生產(根)構建,除非這些應用程序是可調試的。有關詳細信息,請參閱Android 上的 Vulkan 驗證層
  • 實用層——這些層暴露了擴展,例如為設備內存實現內存管理器的層。開發人員選擇要在他們的應用程序中使用的層和這些層的版本;使用同一層的不同應用程序可能仍使用不同的版本。開發人員選擇在他們的應用程序包中發布這些層中的哪一個。
  • 注入(隱式)層- 包括用戶或其他應用程序在未經應用程序知情或同意的情況下提供的幀速率、社交網絡和遊戲啟動器覆蓋等層。這些違反了 Android 的安全政策並且不受支持。

對於不可調試的應用程序,加載器僅在應用程序的本機庫目錄中搜索層,並嘗試加載名稱與特定模式匹配的任何庫(例如libVKLayer_foo.so )。

對於可調試的應用程序,加載器在/data/local/debug/vulkan中搜索層並嘗試加載與特定模式匹配的任何庫。

Android 使層能夠隨著 Android 和其他平台之間的構建環境變化進行移植。有關層和加載器之間接口的詳細信息,請參閱Vulkan 加載器接口的體系結構。 Khronos 維護的驗證層託管在Vulkan 驗證層中。

Vulkan API 版本和功能

Android 9 及更高版本支持 Vulkan API 1.1 版。 Android 7 到 Android 9 支持 Vulkan API 1.0 版。有關 Vulkan 1.1 API 的更多信息,請參閱Vulkan 1.1 API 規範

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

Android 設備應支持 Vulkan 1.1,如果它們:

  • 使用 Android 10 啟動。
  • 支持 64 位 ABI。
  • 不是內存不足。

其他設備可以選擇支持 Vulkan 1.1。

要實施 Vulkan 1.1:

  1. 添加支持 Vulkan 1.1 的 Vulkan 驅動程序以及額外的 Android 1.1 CDD 要求,或更新現有的 Vulkan 1.0 驅動程序。
  2. 通過將如下規則添加到適當的device.mk文件中,確保PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x401000)返回true
    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
    

窗口系統集成 (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 版本

VkSurfaceKHRVkSwapchainKHR對像以及與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
);

formatimageUsage參數取自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是一個vkCreateImage擴展結構,用於創建由 Gralloc 緩衝區支持的圖像。 VkNativeBufferANDROID提供給VkImageCreateInfo結構鏈中的vkCreateImage() 。在調用 vkCreateSwapchainKHR 期間使用VkNativeBufferANDROID調用vkCreateSwapchainKHR vkCreateImage() 。 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 及更高版本中,當交換鏈需要任何交換鏈圖像使用標誌時,平台在提供給vkCreateImageVkImageCreateInfo鏈中提供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 。然後應用程序調用vkBindImageMemory2(KHR)並使用鏈接到VkBindImageMemorySwapchainInfoKHR結構的VkBindImageMemoryInfo結構。 VkBindImageMemorySwapchainInfoKHR結構中指定的imageIndex必須是有效的交換鏈圖像索引。同時,平台向VkBindImageMemoryInfo鏈提供了一個帶有對應Gralloc緩衝區信息的VkNativeBufferANDROID擴展結構,這樣驅動就知道要綁定VkImage到哪個Gralloc緩衝區。

獲取圖像

vkAcquireImageANDROID獲得交換鏈圖像的所有權,並將外部信號原生柵欄導入現有的VkSemaphore對象和現有的VkFence對象:

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

在 vkAcquireNextImageKHR 期間調用vkAcquireNextImageKHR vkAcquireImageANDROID()將原生柵欄導入應用程序提供的VkSemaphoreVkFence對象(但是,在此調用中,信號量和柵欄對像都是可選的)。驅動程序也可以利用這個機會來識別和處理對 Gralloc 緩衝區狀態的任何外部更改;許多司機在這裡不需要做任何事情。此調用將VkSemaphoreVkFence置於與vkQueueSubmit發出信號的相同掛起狀態,因此隊列可以在信號量上等待,應用程序可以在柵欄上等待。

當底層的原生柵欄發出信號時,兩個對像都會發出信號;如果本機柵欄已經發出信號,則當此函數返回時,信號量處於已發出信號狀態。驅動程序獲得柵欄文件描述符的所有權,並在不再需要時關閉柵欄文件描述符。即使沒有提供信號量或柵欄對象,或者即使vkAcquireImageANDROID失敗並返回錯誤,驅動程序也必須這樣做。如果fenceFd為-1,就好像本機柵欄已經發出信號。

發布圖像

vkQueueSignalReleaseImageANDROID準備一個交換鏈圖像以供外部使用,創建一個原生柵欄,並在輸入信號量發出信號後安排原生柵欄發出信號:

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

vkQueuePresentKHR() vkQueueSignalReleaseImageANDROID() 。驅動程序必須生成一個本機柵欄,直到 pWaitSemaphore 中的所有waitSemaphoreCount信號量pWaitSemaphores信號,並且為呈現準備image所需的任何其他工作完成時才發出信號。

如果等待信號量(如果有)已經發出信號,並且queue已經空閒,則驅動程序可以將*pNativeFenceFd設置為-1而不是實際的本機柵欄文件描述符,表明沒有什麼可等待的。調用者擁有並關閉*pNativeFenceFd返回的文件描述符。

許多驅動程序可以忽略圖像參數,但有些驅動程序可能需要準備與 Gralloc 緩衝區關聯的 CPU 端數據結構,以供外部圖像使用者使用。作為將圖像轉換為VK_IMAGE_LAYOUT_PRESENT_SRC_KHR的一部分,應異步完成供外部消費者使用的緩衝區內容的準備工作。

如果圖像是使用VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID創建的,那麼驅動程序必須允許重複調用 vkQueueSignalReleaseImageANDROID( 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 實施,其中包括:

  • CtsDeqpTestCases模塊中的Khronos Vulkan 一致性測試,其中包括 Vulkan 1.0 和 1.1 的功能 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 測試。

如果功能標誌值至少為0x07E30301但小於0x07E40301 ,這意味著設備聲稱通過了所有 Android 10 Vulkan dEQP 測試,但不能保證通過為 Android 11 添加的 Vulkan dEQP 測試。

Vulkan dEQP 是 Android CTS 的一部分。從 Android 11 開始,CTS 的 dEQP 測試運行程序組件知道android.software.vulkan.deqp.level功能標誌,並跳過任何 Vulkan dEQP 測試 - 根據此功能標誌 - 設備不聲稱支持。此類測試被報告為微不足道地通過。