[VULKAN] 3. Device 초기화

2023. 1. 16. 00:06개발얘기/vulkan

 

약속 갔다와서 피곤하고

게임이 너무 하고 싶지만

이악물고 시작한다.

 


 

오늘은 장치 초기화를 다룬다. 인스턴스 생성 후 대기열과 장치를 통해 상호작용 할 수 있다. Vulkan은 물리적 장치와 논리적 장치 개념을 분리한다. 물리적 장치는 시스템의 단일 구성 요소이며, 함께 작동하는 여러 구성 요소 일 수 있다. 논리적 장치는 기본적으로 물리적 장치와의 인터페이스이다.


물리적 장치(Physical Device)

물리적 장치는 시스템이 제출된 작업을 수행하는데 도움이 되도록 함께 작동하는 다른 하드웨어 부품과 단일 GPU로 구성될 수 있든 단일 개체다. vulkan은 높은 호환성으로 다양한 플랫폼에서 실행될 수 있다. 데스크탑, 노트북, 스마트폰같은 각 장치는 다른 구성과 다른 성능, 또는 다른 그래픽 하드웨어를 포함할 수 있다. 또한 컴퓨터에 두개 이상의 그래픽 카드가 설치되있을 수 있다. 

그래서 우리는 필요한 기능을 지원하는 그래픽 카드를 찾아 선택해야 한다. 여러 개의 그래픽 카드를 선택하고 동시에 사용할 수도 있다. 

 

먼저 시스템에서 가능한 물리적 장치의 개수와 정보를 알아낸다. Intance를 생성할 때와 같은 방법이다.

VkResult vkEnumeratePhysicalDevices(
    VkInstance                                  instance,
    uint32_t*                                   pPhysicalDeviceCount,
    VkPhysicalDevice*                           pPhysicalDevices);

그리고 우리는 얻어낸 목록이 수행하려는 작업에 적합한 지를 확인해야 한다.

 

이름, 유형 및 지원되는  vulkan 버전과 같은 기본 장치 속성은 다음을 통해 쿼리할 수 있다.

VkPhysicalDeviceProperties deviceProperties;
vkGetPhysicalDeviceProperties(device, &deviceProperties);
typedef struct VkPhysicalDeviceProperties {
    uint32_t                            apiVersion;
    uint32_t                            driverVersion;
    uint32_t                            vendorID;
    uint32_t                            deviceID;
    VkPhysicalDeviceType                deviceType;
    char                                deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE];
    uint8_t                             pipelineCacheUUID[VK_UUID_SIZE];
    VkPhysicalDeviceLimits              limits;
    VkPhysicalDeviceSparseProperties    sparseProperties;
} VkPhysicalDeviceProperties;
  • driverVersion : 드라이버 공급업체 지정 버전
  • vendorID 물리적 장치의 공급업체 고유 식별자
  • deviceID : 공급업체에서 제공하는 장치 중 물리적 장치의 고유 식별다
  • deviceType : 장치 유형
  • pipelineCacheUUID : 장치 고유 식별자
  • limits : 장치별 제한을 지정하는 vkPhysicalDeviceLimits 구조

 

프로그램은 지정된 개체와 연결된 Vulkan  버전을 초과하는 기능을 사용해선 안된다. 

튜토리얼 에서는 multimap 자료구조로 각 장치별로 점수를 매겨 선정하는 방식이다.

 

텍스처 압축, 멀티 뷰포트 렌더링과 같은 선택적 기능에 대한 지원은 다음으로 쿼리할 수 있다.

typedef struct VkPhysicalDeviceFeatures {
    VkBool32    robustBufferAccess;
    VkBool32    fullDrawIndexUint32;
    VkBool32    imageCubeArray;
    VkBool32    independentBlend;
    VkBool32    geometryShader;
    VkBool32    tessellationShader;
    VkBool32    sampleRateShading;
    VkBool32    dualSrcBlend;
    VkBool32    logicOp;
    VkBool32    multiDrawIndirect;
    VkBool32    drawIndirectFirstInstance;
    VkBool32    depthClamp;
    VkBool32    depthBiasClamp;
    VkBool32    fillModeNonSolid;
    VkBool32    depthBounds;
    VkBool32    wideLines;
    VkBool32    largePoints;
    VkBool32    alphaToOne;
    VkBool32    multiViewport;
    VkBool32    samplerAnisotropy;

  	//.. 등등
} VkPhysicalDeviceFeatures;

 

물리적 장치의 메모리 속성도 확인 가능하다. 단일 물리적 장치는 서로 다른 메모리 유형을 가질 수 있으며, 이들 유형은 해당 속성에 따라 다르게 작동하게 된다. 응용 프로그램의 논리 또는 자원 유형에 따라 더 효율적으로 자원을 할당하기 위해, 메모리 특성을 아는 것은 중요하다.

typedef struct VkPhysicalDeviceMemoryProperties {
    uint32_t        memoryTypeCount;
    VkMemoryType    memoryTypes[VK_MAX_MEMORY_TYPES];
    uint32_t        memoryHeapCount;
    VkMemoryHeap    memoryHeaps[VK_MAX_MEMORY_HEAPS];
} VkPhysicalDeviceMemoryProperties;

void vkGetPhysicalDeviceMemoryProperties(
    VkPhysicalDevice                            physicalDevice,
    VkPhysicalDeviceMemoryProperties*           pMemoryProperties);
  • memoryHeaps~ : 할당할 수 있는 메모리 힙을 설명하는 VKMemoryHeap의 배열과 유효한 요소 수
  • memoryType~ : memoryHeaps에 의해 지정된 힙에서 할당된 메모리에 엑세스하는 데 사용될 수 있는 메모리 유형(VkMemoryType)의 배열

 


큐 패밀리

구글 검색하면 대기열 제품군이라고 나오는데.. 더 적당한 번역이 생각나지 않는다.

 

큐는 물리적 장치와 응용 프로그램이 통신하는 수단이다. 응용프로그램은 큐에 제출되는 명령 버퍼의 형태로 전달하고, 물리적 장치에서 읽고 비동기 식으로 처리된다. 물리적 장치에는 동일한 유형의 큐가 여러 개 있을 수 있다.

 

큐 패밀리는 속성이 동일한 큐의 집합을 말한다. 각 큐 패밀리는 명령의 하위 집합만 허용한다 . 

휴 책보면서 파워포인트로 열심히 그렸다

 

물리적 장치의 큐와 큐 속성 정보를 쿼리할 수 있다. 쿼리된 큐중 그래픽스 사용 가능 큐를 검색하고 나중에 사용할 수 있도록 큐 패밀리 색인을 응용 프로그램에 저장한다.

typedef struct VkQueueFamilyProperties {
    VkQueueFlags    queueFlags;
    uint32_t        queueCount;
    uint32_t        timestampValidBits;
    VkExtent3D      minImageTransferGranularity;
} VkQueueFamilyProperties;

vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
  • queueFlag : queueFamilies의 기능을 나타내는  bit mask.
  • queueCount : 큐의 갯수. 각 큐 패밀리는 최소 하나의  큐를 지원해야 한다.
  • minImageTransferGranularity : 이미지 전송 작업에 지원되는 이미지 데이터의 XYZ영역에 대한 제한. 자세한 부분은 참조

 

vkQueueFlagBits로 알수 있는 queue의 기능들은 다음과 같다. (몇개 더있는데 일단 지금은 패스)

  • VK_QUEUE_GRAPHICS_BIT  : 그래픽 작업 지원
  • VK_QUEUE_COMPUTE_BIT : 컴퓨팅 작업 지원
  • VK_QUEUE_TRANSFER_BIT : 대기열 전송작업 지원
  • VK_QUEUE_SPARSE_BINDING_BIT : 스파스 메모리 관리 작업 지원

 


논리적 장치

물리적 장치 선택 후 논리적 장치와 인터페이스 되도록 설정해야 한다. 동일한 물리적 장치에서 여러 논리적 장치를 생성할 수도 있다고 한다.

 

사용 가능한 queue Family를 알아냈으므로 이제 생성할 queue도 지정해야 한다. 

typedef struct VkDeviceQueueCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkDeviceQueueCreateFlags    flags;
    uint32_t                    queueFamilyIndex;
    uint32_t                    queueCount;
    const float*                pQueuePriorities;
} VkDeviceQueueCreateInfo;
  • queueCount : 단일 queue Family에 대해 원하는 대기열 수를 설정한다. 다중 스레드에서 모든 명령 버퍼를 생성한 다음 오버헤드가 낮은 단일 호출로 기본 스레드에서 한번에 모두 제출할 수 있기 때문에, 실제로는 둘 이상의 queue는 필요하지 않다.
  • pQueuePriorities : 0과 1 사이의 부동 소수점 숫자를 사용하여 명령 버퍼 실행 일정에 영향을 미치도록 대기열에 우선순위를 할당 할 수 있다. 대기열이 하나일때도 필요하다.

 

논리 장치의 생성은 VkDeviceCreateInfo로 설정한다.

이전의 vulkan에서는 인스턴스와 장치별 유효성 검사 계층을 구분했지만 지금은 그렇지 않다. vkDeviceCreateInfo에서 enableLayerCount와 ppEnabledLayerNames는 무시된다. 하지만 이전 구현과 호환되도록 설정하는게 좋다.

 

다음으로 논리적 장치를 생성한다.

VkResult vkCreateDevice(
    VkPhysicalDevice                            physicalDevice,
    const VkDeviceCreateInfo*                   pCreateInfo,
    const VkAllocationCallbacks*                pAllocator,
    VkDevice*                                   pDevice);
  • physicalDevic : 생성될 논리적 장치가 지정하는 실제 물리적 장치
  • pAllocator : 어떤 방식으로 호스트 메모리 할당을 제어할 것인지 지정한다

 

큐는 논리적 장치와 함께 자동으로 생성되지만 queue와 인터페이스할 핸들은 따로 설정해야 한다.

아래 함수로 각 queue familiy에 대한 큐 핸들을 가져올 수 있다. 

void vkGetDeviceQueue(
    VkDevice                                    device,
    uint32_t                                    queueFamilyIndex,
    uint32_t                                    queueIndex,
    VkQueue*                                    pQueue);

 

마지막으로 책에 요약된대로 정리를 한번 해본다.

 


 

이번꺼는 책이 큰 도움이 됬다. 튜토리얼은 개념설명이 잘 안나와있다..

 

 

 


출처

https://stackoverflow.com/questions/52015730/vulkan-queue-families

https://registry.khronos.org/vulkan/

 

처음 만나는Vulkan - 파르민더 싱

'개발얘기 > vulkan' 카테고리의 다른 글

[VULKAN]4. Window Surface  (0) 2023.01.17
[VULKAN] 2. Instance 초기화  (2) 2023.01.10
[VULKAN] 1. GLFW 초기화  (0) 2023.01.09
[VULKAN] 0. 시작하기  (2) 2023.01.08