Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

Vulkan 구현

Vulkan은 고성능 3D 그래픽을 지원하며 오버헤드가 낮은 크로스 플랫폼 API입니다. OpenGL ES(GLES)와 마찬가지로 Vulkan은 앱에서 고품질의 실시간 그래픽을 만들 수 있는 도구를 제공합니다. Vulkan을 사용하면 CPU 오버헤드를 줄이고 SPIR-V 바이너리 중간 언어를 지원하는 등의 이점을 얻을 수 있습니다.

Vulkan을 구현하려면 기기에 다음이 포함되어야 합니다.

  • Android 기반의 Vulkan 로더
  • Vulkan API를 구현하며 GPU IHV와 같은 SoC에서 제공하는 Vulkan 드라이버. 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에 필요한 확장 프로그램의 진입점을 제공합니다. Window System Integration(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 파생 코드가 단일 구조를 래핑합니다. 하나의 드라이버만 지원되며 상수 문자열인 HWVULKAN_DEVICE_0open()으로 전달됩니다.

Vulkan hw_device_t 파생은 여러 물리적 기기를 지원할 수 있는 단일 드라이버에 해당합니다. hw_device_t 구조를 확장하여 vkGetGlobalExtensionProperties(), vkCreateInstance(), vkGetInstanceProcAddr() 함수를 내보낼 수 있습니다. 로더는 hw_device_t 구조의 vkGetInstanceProcAddr()을 호출하여 다른 모든 VkInstance(), VkPhysicalDevice(), vkGetDeviceProcAddr() 함수를 찾을 수 있습니다.

레이어 검색 및 로드

Vulkan 로더는 추가 확장 프로그램을 노출하고 드라이버에 전달되는 핵심 API 호출을 가로챌 수 있는 레이어를 열거하고 로드할 수 있습니다. Android의 시스템 이미지에는 레이어가 포함되지 않지만 앱의 APK에는 레이어가 포함될 수 있습니다.

레이어를 사용할 때 Android의 보안 모델과 정책은 다른 플랫폼과 크게 다르다는 점에 유의하세요. 특히 Android는 루팅되지 않은 프로덕션 기기의 디버깅할 수 없는 프로세스로 외부 코드를 로드하거나 외부 코드가 프로세스의 메모리, 상태 등을 검사 또는 제어하는 것을 허용하지 않습니다. 또한 나중에 검사할 수 있도록 코어 덤프, API 추적 등을 디스크에 저장하는 것도 금지됩니다. 디버깅할 수 없는 앱의 일부로 제공되는 레이어만 프로덕션 기기에서 사용되며 드라이버는 이러한 정책을 위반하는 기능을 제공해서는 안 됩니다.

레이어의 사용 사례는 다음과 같습니다.

  • 개발 시간 레이어 - 프로덕션 기기의 시스템 이미지에 추적/프로파일링/디버깅 도구의 유효성 검사 레이어와 심(shim)을 설치해서는 안 됩니다. 추적/프로파일링/디버깅 도구의 유효성 검사 레이어와 심(shim)은 시스템 이미지 없이도 업데이트할 수 있어야 합니다. 예를 들어 개발 중에 이러한 레이어 중 하나를 사용하려는 개발자는 기본 라이브러리 디렉터리에 파일을 추가하여 앱 패키지를 수정할 수 있습니다. 수정 불가능한 앱을 제공할 수 없는 오류를 진단하려는 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의 기능은 다음과 같습니다.

  • 카메라, 코덱, GLES 관련 상호 운용을 위해 Vulkan 외부의 메모리 버퍼 및 동기화 객체 가져오기 및 내보내기 지원
  • 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과 Android 1.1의 추가적인 CDD 요구사항을 지원하는 Vulkan 드라이버를 추가하거나 기존 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
        

Window System Integration(WSI)

libvulkan.so에서 드라이버는 다음과 같은 Window System Integration(WSI) 확장 프로그램을 구현합니다.

  • VK_KHR_surface
  • VK_KHR_android_surface
  • VK_KHR_swapchain
  • Android 10에서만 Vulkan 1.1용으로 구현되는 VK_KHR_driver_properties
  • Android 10에서 모든 Vulkan 버전용으로 구현되는 VK_GOOGLE_display_timing

VkSurfaceKHR 객체와 VkSwapchainKHR 객체, 그리고 ANativeWindow와의 모든 상호작용은 플랫폼에 의해 처리되며 드라이버에 노출되지 않습니다. WSI 구현은 드라이버에서 지원해야 하는 VK_ANDROID_native_buffer 확장 프로그램을 사용합니다. 이 확장 프로그램은 WSI 구현에서만 사용되며 앱에 노출되지 않습니다.

Gralloc 사용 플래그

Vulkan을 구현하려면 구현이 정의된 비공개 Gralloc 사용 플래그를 통해 swapchain 버퍼를 할당해야 할 수 있습니다. swapchain을 만들 때 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를 채워야 합니다. 드라이버에 의해 반환된 사용 플래그는 버퍼를 할당할 때 swapchain 소비자가 요청한 사용 플래그와 결합됩니다.

Android 7.x에서는 이름이 vkGetSwapchainGrallocUsageANDROID()VkSwapchainImageUsageFlagsANDROID()의 이전 버전을 호출합니다. Android 8.0 이상에서는 vkGetSwapchainGrallocUsageANDROID()를 지원하지 않지만 드라이버에서 vkGetSwapchainGrallocUsage2ANDROID()를 제공하지 않는 경우 vkGetSwapchainGrallocUsageANDROID()를 계속 호출합니다.

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

vkGetSwapchainGrallocUsageANDROID()는 swapchain 사용 플래그나 확장된 Gralloc 사용 플래그를 지원하지 않습니다.

Gralloc 지원 이미지

VkNativeBufferANDROID는 Gralloc 버퍼에서 지원하는 이미지를 생성하기 위한 vkCreateImage 확장 프로그램 구조입니다. VkNativeBufferANDROIDVkImageCreateInfo 구조 체인의 vkCreateImage()에 제공됩니다. vkGetSwapChainInfoWSI(.. VK_SWAP_CHAIN_INFO_TYPE_IMAGES_WSI ..)가 처음으로 호출되는 동안 VkNativeBufferANDROID를 통해 vkCreateImage()가 호출됩니다. WSI 구현은 swapchain에 대해 요청된 기본 버퍼 수를 할당한 다음 각각에 해당하는 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               = VkSwapChainCreateInfoWSI::imageUsageFlags
      .flags               = 0
      .sharingMode         = VkSwapChainCreateInfoWSI::sharingMode
      .queueFamilyCount    = VkSwapChainCreateInfoWSI::queueFamilyCount
      .pQueueFamilyIndices = VkSwapChainCreateInfoWSI::pQueueFamilyIndices
    

Android 8.0 이상에서 플랫폼은 swapchain에 swapchain 이미지 사용 플래그가 필요한 경우 vkCreateImage에 제공되는 VkImageCreateInfo 체인의 VkSwapchainImageCreateInfo 확장 구조를 제공합니다. 확장 구조에는 swapchain 이미지 사용 플래그가 포함되어 있습니다.

    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 앱에서 swapchain 메모리가 지원하는 VkImage를 만들 수 있습니다. Vulkan 앱은 먼저 VkImageSwapchainCreateInfoKHR 구조가 VkImageCreateInfo 구조에 연결된 vkCreateImage를 호출합니다. 그런 다음 VkBindImageMemorySwapchainInfoKHR 구조가 VkBindImageMemoryInfo 구조에 연결된 vkBindImageMemory2(KHR)를 호출합니다. VkBindImageMemorySwapchainInfoKHR 구조에 지정된 imageIndex는 유효한 swapchain 이미지 색인이어야 합니다. 한편 플랫폼은 해당하는 Gralloc 버퍼 정보가 있는 VkNativeBufferANDROID 확장 구조를 VkBindImageMemoryInfo 체인에 제공하므로 드라이버는 VkImage를 바인딩할 Gralloc 버퍼를 식별할 수 있습니다.

이미지 획득

vkAcquireImageANDROID는 swapchain 이미지의 소유권을 획득하고 외부에서 신호를 받는 네이티브 펜스를 기존의 VkSemaphore 객체와 VkFence 객체로 가져옵니다.

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

앱에서 제공하는 VkSemaphoreVkFence 객체로 네이티브 펜스를 가져오기 위해 vkAcquireNextImageKHR이 실행되는 동안 vkAcquireImageANDROID()가 호출됩니다. 하지만 세마포어와 펜스 객체는 이 호출에서 선택사항입니다. 드라이버는 이 기회를 사용하여 Gralloc 버퍼 상태의 모든 외부 변경사항을 인식하고 처리할 수도 있습니다. 이때 드라이버의 대다수는 아무것도 처리할 필요가 없습니다. 이 호출은 VkSemaphoreVkFence를 각각 vkQueueSignalSemaphorevkQueueSubmit과 동일한 대기 상태로 전환하므로 대기열은 세마포어를 기다릴 수 있으며 앱은 펜스를 기다릴 수 있습니다.

두 객체는 기본 네이티브 펜스가 신호를 보낼 때 신호를 받습니다. 네이티브 펜스가 이미 신호를 보낸 경우 세마포어는 이 함수가 반환될 때 신호를 받은 상태가 됩니다. 드라이버는 펜스 파일 설명자의 소유권을 가져오며 펜스 파일 설명자가 더 이상 필요하지 않을 때 이를 닫습니다. 드라이버는 세마포어와 펜스 객체가 모두 제공되지 않거나 vkAcquireImageANDROID가 실패하여 오류를 반환하더라도 동일하게 작동해야 합니다. fenceFd가 -1이면 네이티브 펜스가 이미 신호를 받은 것으로 간주됩니다.

이미지 릴리스

vkQueueSignalReleaseImageANDROID는 외부 사용을 위한 swapchain 이미지를 준비하고, 네이티브 펜스를 만들고, 입력 세마포어가 신호를 보낸 후 이 신호를 받을 네이티브 펜스를 예약합니다.

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

vkQueuePresentKHR()은 제공된 대기열에 있는 vkQueueSignalReleaseImageANDROID()를 호출합니다. 드라이버는 pWaitSemaphores의 모든 waitSemaphoreCount 세마포어가 신호를 보내고 image의 표시를 위해 필요한 모든 추가 작업이 완료될 때까지 신호를 보내지 않는 네이티브 펜스를 만들어야 합니다.

대기 상태인 세마포어가 이미 신호를 보냈으며 queue가 이미 유휴 상태인 경우, 드라이버는 *pNativeFenceFd를 실제 네이티브 펜스 파일 설명자 대신 -1로 설정하여 기다릴 대상이 없음을 나타낼 수 있습니다. 호출자는 *pNativeFenceFd에서 반환된 파일 설명자를 소유하고 이를 닫습니다.

드라이버의 대다수는 이미지 매개변수를 무시할 수 있지만 일부는 외부 이미지 소비자가 사용할 수 있도록 Gralloc 버퍼와 연결된 CPU 측 데이터 구조를 준비해야 할 수 있습니다. 외부 소비자가 사용할 수 있도록 버퍼 콘텐츠를 준비하는 작업은 이미지를 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR로 전환하는 과정에서 비동기적으로 처리되어야 합니다.

이미지가 VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID를 통해 생성된 경우 드라이버는 vkAcquireImageANDROID()에 대한 호출에 개입할 필요 없이 vkQueueSignalReleaseImageANDROID()가 반복적으로 호출될 수 있도록 해야 합니다.

공유된 표시 가능 이미지 지원

일부 기기에서는 디스플레이 파이프라인과 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 구현을 테스트할 수 있습니다.

  • Vulkan 1.0 및 1.1용 기능적 API 테스트가 포함된 CtsDeqpTestCases 모듈에서 이루어지는 Khronos Vulkan 적합성 테스트
  • 기기가 지원하는 Vulkan 기능에 적합하게 구성되었는지 기기를 테스트하는 CtsGraphicsTestCases 모듈