Реализация Вулкана

Vulkan — это малозатратный кроссплатформенный API для высокопроизводительной 3D-графики. Как и OpenGL ES (GLES) , Vulkan предоставляет инструменты для создания высококачественной графики в реальном времени в приложениях. Преимущества использования Vulkan включают снижение нагрузки на ЦП и поддержку языка SPIR-V Binary Intermediate .

Для успешной реализации Vulkan устройство должно включать в себя:

  • Загрузчик Vulkan, предоставленный Android.
  • Драйвер Vulkan, предоставляемый такими SoC, как GPU IHV, который реализует Vulkan API . Для поддержки функций Vulkan устройству Android требуется аппаратное обеспечение графического процессора с поддержкой Vulkan и соответствующий драйвер. Графический процессор также должен поддерживать GLES 3.1 и выше. Обратитесь к поставщику SoC, чтобы запросить поддержку драйверов.

Если устройство включает драйвер Vulkan, на устройстве необходимо объявить системные функции FEATURE_VULKAN_HARDWARE_LEVEL и FEATURE_VULKAN_HARDWARE_VERSION с версиями, точно отражающими возможности устройства. Это помогает гарантировать, что устройство соответствует документу определения совместимости (CDD).

Вулкан загрузчик

Платформа загрузчика 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 ожидает, что система знает, какие графические процессоры доступны. Загрузчик использует существующий механизм HAL в hardware.h для обнаружения и загрузки драйвера. Предпочтительные пути для 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() . Загрузчик может найти все остальные VkInstance() , VkPhysicalDevice() и vkGetDeviceProcAddr() , вызвав hw_device_t vkGetInstanceProcAddr() структуры hw_device_t.

Обнаружение и загрузка слоев

Загрузчик Vulkan поддерживает перечисление и загрузку слоев, которые могут предоставлять дополнительные расширения и перехватывать основные вызовы API на пути к драйверу. Android не включает слои в образ системы; однако приложения могут включать слои в свои APK.

При использовании слоев помните, что модель и политики безопасности Android значительно отличаются от других платформ. В частности, Android не позволяет загружать внешний код в неотлаживаемый процесс на рабочих (без рута) устройствах, а также не позволяет внешнему коду проверять или контролировать память, состояние и т. д. процесса. Это включает в себя запрет на сохранение дампов ядра, трассировок API и т. д. на диск для последующей проверки. На производственных устройствах включены только слои, поставляемые как часть неотлаживаемых приложений, и драйверы не должны предоставлять функции, нарушающие эти политики.

Примеры использования слоев включают:

  • Слои времени разработки. Слои проверки и прокладки для инструментов трассировки, профилирования и отладки не следует устанавливать в системный образ рабочих устройств. Слои проверки и прокладки для инструментов трассировки/профилирования/отладки должны обновляться без образа системы. Разработчики, которые хотят использовать один из этих слоев во время разработки, могут изменить пакет приложения, например, добавив файл в каталог собственных библиотек. Предполагается, что инженеры IHV и OEM, которые хотят диагностировать сбои при доставке неизменяемых приложений, имеют доступ к непроизводственной (с корневым доступом) сборке образа системы, если эти приложения не поддаются отладке. Дополнительные сведения см. в разделе Слои проверки Vulkan на Android .
  • Служебные уровни — эти уровни предоставляют расширения, такие как слой, который реализует диспетчер памяти для памяти устройства. Разработчики выбирают слои и версии этих слоев для использования в своем приложении; разные приложения, использующие один и тот же слой, могут по-прежнему использовать разные версии. Разработчики выбирают, какой из этих слоев будет поставляться в пакете их приложения.
  • Внедренные (неявные) слои — включают такие слои, как частота кадров, социальные сети и оверлеи для запуска игр, предоставляемые пользователем или каким-либо другим приложением без ведома или согласия приложения. Они нарушают политики безопасности 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. Дополнительные сведения об API Vulkan 1.1 см. в спецификации API Vulkan 1.1 .

Обзор поддержки Vulkan 1.1

Vulkan 1.1 включает поддержку взаимодействия памяти и синхронизации, что позволяет OEM-производителям поддерживать Vulkan 1.1 на устройствах. Кроме того, взаимодействие памяти/синхронизации позволяет разработчикам определить, поддерживается ли Vulkan 1.1 на устройстве, и эффективно использовать его, если это так. Vulkan 1.1 имеет те же требования к оборудованию, что и Vulkan 1.0, но большая часть реализации находится в графическом драйвере SOC, а не в фреймворке.

Наиболее важные функции Vulkan 1.1 для Android:

  • Поддержка импорта и экспорта буферов памяти и объектов синхронизации из-за пределов Vulkan (для взаимодействия с камерой, кодеками и GLES).
  • Поддержка форматов YCbCr

Vulkan 1.1 также включает в себя несколько небольших функций и улучшений удобства использования API.

Внедрение Вулкана 1.1

Устройства Android должны поддерживать Vulkan 1.1, если они:

  • Запуск с Android 10.
  • Поддержка 64-битного ABI.
  • Не мало памяти.

Другие устройства могут дополнительно поддерживать Vulkan 1.1.

Чтобы внедрить Вулкан 1.1:

  1. Добавьте драйвер Vulkan, который поддерживает Vulkan 1.1 и дополнительные требования CDD для Android 1.1, или обновите существующий драйвер Vulkan 1.0.
  2. Убедитесь, что PackageManager#hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, 0x401000) возвращает значение true , добавив следующее правило в соответствующий файл device.mk :
    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 драйвер реализует следующие расширения Windows System Integration (WSI):

  • VK_KHR_surface
  • VK_KHR_android_surface
  • VK_KHR_swapchain
  • VK_KHR_driver_properties , реализовано только для Vulkan 1.1 в Android 10.
  • VK_GOOGLE_display_timing , реализованный для любой версии Vulkan в Android 10.

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 . Драйвер должен заполнить *grallocConsumerUsage и *grallocProducerUsage флагами использования Gralloc, необходимыми для формата и использования. Флаги использования, возвращаемые драйвером, объединяются с флагами использования, запрошенными потребителем цепи обмена при выделении буферов.

Android 7.x вызывает более раннюю версию VkSwapchainImageUsageFlagsANDROID() с именем vkGetSwapchainGrallocUsageANDROID() . Android 8.0 и более поздние версии устарели vkGetSwapchainGrallocUsageANDROID() но по-прежнему вызывают vkGetSwapchainGrallocUsageANDROID() , если vkGetSwapchainGrallocUsage2ANDROID() не предоставляется драйвером:

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

vkGetSwapchainGrallocUsageANDROID() не поддерживает флаги использования цепочки обмена или расширенные флаги использования Gralloc.

Изображения, поддерживаемые Gralloc

VkNativeBufferANDROID — это структура расширения vkCreateImage для создания образа, поддерживаемого буфером Gralloc. VkNativeBufferANDROID предоставляется vkCreateImage() в цепочке структур VkImageCreateInfo . Вызовы vkCreateImage() с VkNativeBufferANDROID происходят во время вызова vkCreateSwapchainKHR . Реализация 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 и более поздних версиях платформа предоставляет структуру расширения VkSwapchainImageCreateInfoKHR в цепочке VkImageCreateInfo , предоставляемой vkCreateImage , когда для цепочки обмена требуются какие-либо флаги использования образа цепочки обмена. Структура расширения содержит флаги использования образа цепочки обмена:

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 , поддерживаемый памятью цепочки обмена. Приложение сначала вызывает vkCreateImage со структурой VkImageSwapchainCreateInfoKHR , связанной со структурой VkImageCreateInfo . Затем приложение вызывает vkBindImageMemory2(KHR) со структурой VkBindImageMemorySwapchainInfoKHR , связанной со структурой VkBindImageMemoryInfo . imageIndex указанный в структуре VkBindImageMemorySwapchainInfoKHR , должен быть допустимым индексом изображения цепи обмена. Тем временем платформа предоставляет структуру расширения VkNativeBufferANDROID с соответствующей информацией о буфере VkBindImageMemoryInfo в цепочку VkBindImageMemoryInfo, поэтому драйвер знает, с каким буфером Gralloc связать VkImage .

Получение изображений

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() в предоставленной очереди. Драйвер должен создать собственное ограждение, которое не подает сигнал до тех пор, пока все семафоры waitSemaphoreCount в pWaitSemaphores не подадут сигнал, и не будет завершена любая дополнительная работа, необходимая для подготовки image к представлению.

Если семафоры ожидания (если они есть) уже просигнализированы, а queue уже простаивает, драйвер может установить *pNativeFenceFd в -1 вместо фактического собственного файлового дескриптора ограждения, указывая, что ждать нечего. Вызывающий объект владеет и закрывает файловый дескриптор, возвращенный в *pNativeFenceFd .

Многие драйверы могут игнорировать параметр изображения, но некоторым может потребоваться подготовить структуры данных на стороне ЦП, связанные с буфером Gralloc, для использования внешними потребителями изображений. Подготовка содержимого буфера для использования внешними потребителями должна выполняться асинхронно в рамках перехода изображения на VK_IMAGE_LAYOUT_PRESENT_SRC_KHR .

Если образ был создан с помощью VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID , то драйвер должен разрешить vkQueueSignalReleaseImageANDROID() без промежуточных вызовов vkAcquireImageANDROID() .

Поддержка общего презентабельного изображения

Некоторые устройства могут совместно использовать одно изображение между конвейером отображения и реализацией Vulkan, чтобы минимизировать задержку. В Android 9 и выше загрузчик условно рекламирует расширение VK_KHR_shared_presentable_image на основе ответа драйвера на вызов vkGetPhysicalDeviceProperties2 .

Если драйвер не поддерживает ни 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-производители могут протестировать свою реализацию Vulkan с помощью CTS, которая включает в себя:

  • Тесты соответствия Khronos Vulkan в модуле CtsDeqpTestCases , которые включают функциональные тесты API для Vulkan 1.0 и 1.1.
  • Модуль CtsGraphicsTestCases , который проверяет правильность настройки устройства для поддерживаемых им возможностей Vulkan.

Флаг функции Вулкана

Устройство, поддерживающее Android 11 или выше и поддерживающее Vulkan API, должно предоставлять флаг функции android.software.vulkan.deqp.level . Значение этого флага функции представляет собой дату, закодированную как целочисленное значение. В нем указывается дата, связанная с тестами Vulkan dEQP, которые, как утверждает устройство, проходят.

Дата в форме ГГГГ-ММ-ДД кодируется как 32-битное целое число следующим образом:

  • Биты 0-15 хранят год
  • Биты 16-23 хранят месяц
  • Биты 24-31 хранят день

Минимальное допустимое значение для флага функции — 0x07E30301 , что соответствует дате 2019-03-01, которая является датой, связанной с тестами Vulkan dEQP для Android 10. Если флаг функции имеет по крайней мере это значение, устройство утверждает, что пройти все тесты Android 10 Vulkan dEQP.

Значение 0x07E40301 соответствует дате 2020-03-01, которая является датой, связанной с тестами Vulkan dEQP для Android 11. Если флаг функции равен хотя бы этому значению, устройство утверждает, что прошло все тесты Android 11 Vulkan dEQP.

Если значение флага функции не меньше 0x07E30301 , но меньше 0x07E40301 , это означает, что устройство утверждает, что прошло все тесты Vulkan dEQP для Android 10, но не гарантирует прохождения тестов Vulkan dEQP, которые были добавлены для Android 11.

Vulkan dEQP является частью Android CTS. Начиная с Android 11, компонент запуска тестов dEQP в CTS знает о флаге функции android.software.vulkan.deqp.level и пропускает любые тесты dEQP Vulkan, которые, согласно этому флагу функции, устройство не поддерживает. О таких тестах сообщается как о тривиальном прохождении.