[VULKAN] 2. Instance 초기화

2023. 1. 10. 01:00개발얘기/vulkan

 

오늘은 Instance 초기화validation layer를 한번에 본다

DirectX12를 공부할때 사실 장치 초기화하는 부분은 솔직히 대충봤다.

재미없기 때문이다.

하지만 이번엔 다를것이다. 

 


VKInstance

가장 먼저 인스턴스를 생성해 응용 프로그램과 Vulkan 라이브러리를 연결한다. 

 

Vulkan은 물리적 장치, 메모리 유형, 커맨드 버퍼 큐와 확장판을 공개하는데, 이러한 방식으로 실제 하드웨어에 근접하게 제어할 수 있는 많은 제어권을 제공한다.

하드웨어는 자신의 GPU 아키텍처에 맞춰 Vulkan 표준을 구현한 드라이버를 제공한다. 이 드라이버는 고수준의 기능을 응용 프로그램에 제공하여 장치와 교환할 수 있게 한다. 응용프로그램은 초기화 단계에서 로더와 통신하여 Vulkan 드라이버를 활성화 하고 API를 로딩한다.

 

VKInstance는 유효성 검증 또는 디버깅을 위해 활성화할 레이어와 확장판의 이름을 지정하며, VKInstanceCreateInfo 구조체로 설정값을 넘긴다.

typedef struct VkInstanceCreateInfo {
    VkStructureType             sType;
    const void*                 pNext;
    VkInstanceCreateFlags       flags;
    const VkApplicationInfo*    pApplicationInfo;
    uint32_t                    enabledLayerCount;
    const char* const*          ppEnabledLayerNames;
    uint32_t                    enabledExtensionCount;
    const char* const*          ppEnabledExtensionNames;
} VkInstanceCreateInfo;
  • sType : 반드시 VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO로 지정되야 한다고 문서에 나와있다.
  • pNext : Insatance를 확장하는 구조체의 포인터
  • enabledLayerCount, enabledLayerNames :  디버깅 기능을 제공할 오류와 유효성 검증 레이어
  • enalbedextensionCount, enabledExtensionNames : 장치별 확장판

vulkan은 대다수의 구조체에서  sType으로 유형을 명시적으로 지정해야 한다. 또한 대다수의 구조체는 생성할때 이처럼  ~~CreateInfo로 끝나는 구조체를 인수로 전달한다.

Instance를 생성하는 과정은 저 안에 들어갈 인수들을 하나하나 셋팅하는 과정이라고 보면 되겠다.

 


VKApplicationInfo

pApplicationInfo은 응용프로그램의 지정정보를 가지고 있다. null로 들어갈 수도 있고 설정은 일단 간단하다. 

 VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "VKSetting";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;

엔진의 이름과 버전까지 설정가능하다. 아직 엔진 이름도 안정했는데..

apiVersion는 드라이버가 사용 가능한 버전 번호를 지정한다. 0으로 넣으면 이 값을 무시하고, 0이 아니면 드라이버에서 지원하는 버전이 아닐경우 오류를 보고한다. 개발문서에 있는대로 인코딩 되어야 한다. 

 


Layer

enabledLayerCount와 enabledLayerNames을 세팅해야 한다. 정확히 뭐하는 변수인지 애매한 이름이다.

 

레이어는 일반적으로 드라이버에 들어갈 파라미터가 올바른지를 확인한다. 기존 Vulkan api에 연결되고, 지정된 레이어에 연결된 Vulkan 명령 체인에 삽입된다.

 

Validation layer

레이어의 중점은 선택적 사용이다. 사소한 실수 하나도 매우 치명적인 구조에서, 모든 오류 검사를 하는건 오버헤드가 크다. 그래서  vulkan 함수 호출에 연결되는 선택적 구성요소인 validation layer를 도입했다. 튜토리얼에 진짜 elegant system 이라고 써있다.

 

아래는 validation layer 의 일반적인 작업 목록이다.

  • 오용 감지를 위해 사양에 대한 매개변수 값 확인
  • 리소스 누수를 찾기 위한 개체 생성 및 소멸 추적
  • 호출이 발생한 스레드를 추적하여 스레드 안전성 확인
  • 모든 호출 및 해당 매개변수를 표준 출력에 기록
  • 프로파일링 및 재생을 위한 vulkan 호출 추적

많긴 하다..

 

아무튼 유효성 검사는 디버깅시에 유용하게 잘 쓰고 런타임에는 최소화 하는게 좋다. 이는 커맨드 버퍼를 만들고 제출하는것을 매우 효율적으로 만들어준다.

 

validation layer는 이름을 명시하여 활성화 해야 한다. (정적 문자열 배열) . VK_LAYER_KHRONOS_validtaion은 SDK에 포함된 유용한 표준 유효성 검사다.

 

const std::vector<const char*> validationLayers = {
    "VK_LAYER_KHRONOS_validation"
};

 

아래 함수는 현재 사용 가능한 레이어를 모두 보여준다. 보통 다 해당 함수를 두번 호출해서 첫번째 호출엔 갯수만 받고 컨테이너 크기를 할당한 후 두번째 호출에서 데이터를 받아오는 식이다. 위의 validationLayers에 있는게 이 함수로 받아온 availableLayers중에 없다면, 잘못된 이름을 넣었을.. 거다.

VkResult vkEnumerateInstanceLayerProperties(
    uint32_t*                                   pPropertyCount,
    VkLayerProperties*                          pProperties);

 

이전의 vulkan은 유효성 검증 단계에서 Instance별 레이어와 device 단위 레이어를 구분해서 제공했다고 한다. 하지만 이제 그러지 않는다. Instance validation layer가 모든 vulkan 호출에 적용된다. 후에 논리 device의 레이어를 지정할 때 Intance때 한거랑 동일하게 지정하면 된다. 

 


디버깅 메시지 콜백 파트는 패스 하도록 하겠다. 근데 튜토리얼 코드대로 돌리니까 이런 로그가 나왔다.

 

validation layer: loader_get_json: Failed to open JSON file E:\Program Files (x86)\Epic Games\Launcher\Portal\Extras\Overlay\EOSOverlayVkLayer-Win32.json
validation layer: loader_get_json: Failed to open JSON file E:\Program Files (x86)\Epic Games\Launcher\Portal\Extras\Overlay\EOSOverlayVkLayer-Win64.json
validation layer: windows_get_device_registry_files: GUID for 21 is not SoftwareComponent skipping
validation layer: loader_get_json: Failed to open JSON file C:\Program Files\Rockstar Games\Social Club\SocialClubVulkanLayer.json
validation layer: Searching for ICD drivers named .\nvoglv64.dll
validation layer: Build ICD instance extension list
validation layer: Loading layer library C:\VulkanSDK\1.3.236.0\Bin\.\VkLayer_khronos_validation.dll
validation layer: Loading layer library C:\Program Files\Bandicam\.\bdcamvk64.dll
validation layer: Loading layer library C:\WINDOWS\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_f52c4b8723f8dd33\.\nvoglv64.dll
validation layer: Build ICD instance extension list
validation layer: loader_phys_dev_ext_gpa: Adding unknown physical function vkGetPhysicalDeviceOpticalFlowImageFormatsNV to internal store at index 0
validation layer: loader_phys_dev_ext_gpa: Driver C:\WINDOWS\System32\DriverStore\FileRepository\nv_dispig.inf_amd64_f52c4b8723f8dd33\.\nvoglv64.dll returned ptr 00007FFD4766B522 for vkGetPhysicalDeviceOpticalFlowImageFormatsNV

 

상상도 못한 로그로 굉장히 당황스럽다.

저 익숙한 게임런처들의 이름은 왜 뜨는걸까?

난 공부할려고 저거 다 지웠는데..

아무튼 공통점은 이전에 지운 프로그램들이 어딘가 쓰레기같이 흔적이 남아있는.. 그 무언가.. 아무튼 그거다.

 

일단 레지스트리 편집기로 가본다.

HKEY_LOCAL_MACHINE\SOFTWARE\Khronos\Vulkan\ImplicitLayers 그리고 ExplicitLayers

역시나 저기 뜬 애들이 vulkan 레지스트리에 남아있는걸 확인할 수 있다.

자기들만의 vulkan layer를 따로 만들었나보다..라고 추측해본다.

 

 

그리고 HKEY_CURRENT_USER\SOFTWARE\Khronos\Vulkan\ImplicitLayers

대충 지워도 되겠지 싶어서 얘가 여기 있으면 안되겠다 싶은건 다 지웠다.

그랬더니 loader_get_json 으로 시작하는건 다 지워졌다. 

근데 애초에 레지스트리에 저게 왜 남아있었을가.. 왜 저 폴더에 있을까..

 

그리고 windows_get_device_registry_files: GUID for 21 is not SoftwareComponent skipping <= 이건 말의 뜻을 헤아리기도 힘들다.


Extensions

Vulkan은 플랫폼에 의존하지 않는 API 이므로 윈도우 시스템과 인터페이스 하려면 확장판이 필요하다. 먼저 쿼리된 이후 함수 포인터에 동적으로 링크된다.

 

인스턴스 기반, 장치 기반 두가지 유형으로 분류된다.

  • 인스턴스 기반 : 어떤 장치와도 독립적인 전역 기능으로, 어떤 VKDevice 없이도 액세스 할 수 있다.
  • 장치 기반 : 장치에 특별히 한정돼 있어 특수 기능을 조작하고 응용프로그램에 제공하려면 유효한 핸들이 필요하다.

GLFW는 GLFW창에 대한 Vukan 표면을 생성하기 위해 필요한 확장판 목록들을 반환하는 편리한 함수를 가지고 있다. 목록에 항상 VK_KHR_surface가 포함된다. 

const char ** 	glfwGetRequiredInstanceExtensions (uint32_t *count)

- 시스템에서 Vulkan을 사용할 수 없는 경우 null 을 반환하고 GLFW_API_UNAVAILABLE 오류를 생성한다.

- Vulkan을 사용할 수 있지만 창 표면 생성을 위한 확장 집합이 없는 경우 null을 반환한다. 

 

아래 함수를 통해 현재 지원되는 확장판 목록을 확인할수 있다. 

VkResult vkEnumerateInstanceExtensionProperties(
    const char*                                 pLayerName,
    uint32_t*                                   pPropertyCount,
    VkExtensionProperties*                      pProperties);

 

한번 튜토리얼 코드대로 적용 가능한 확장판을 로그로 확인해보자.

나는 확장판 부자다.

MAC 사용자라면 저 마지막의 VK_KHR_portability_enumeration 저걸 똑똑히 기억해야 한다.

MAC 적용 내용은 후에 추가할건데 이건 중요한 복선이 될것이다.

 

 


이제 진짜 생성이다. 

실패한다면 저 반환값으로 구글링 하면 된다..

if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
    throw std::runtime_error("failed to create instance!");
}

 

 

다음장은 물리적 장치/논리적 장치 초기화다.

정말 지루하기 짝이 없는 파트이지만

좀만 더 참으면 더 지루한 파트가 남아있다.

화이팅

 


참조

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

 

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

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