주의 : 이 문서는 초심자 튜토리얼이 아닙니다. 기본 개념 정도는 안다고 가정합니다. 초심자는 [ Vulkan Tutorial ] 이나 [ Vulkan Samples Tutorial ] 을 보면서 같이 보시기 바랍니다.

주의 : 완전히 이해하고 작성한 글이 아니므로 잘못된 내용이 포함되어 있을 수 있습니다.

주의 : 이상하면 참고자료를 참조하세요.

정보 : 본문의 소스 코드는 Vulkan C++ exmaples and demos 를 기반으로 하고 있습니다. 



Extension Naming Rules


Khronos 는, OpenGL 도 그렇고 Vulkan 도 그렇고, 익스텐션이라는 개념을 사용해서 API 의 확장성을 보장합니다.


익스텐션 이름의 규칙은 다음과 같습니다.


VK_<author>_<name>


<author> 는 다음과 같은 규칙을 가집니다.


  • KHR : Khronos 익스텐션.
  • KHX : Khronos 의 실험적( experimental ) 익스텐션. Vulkan 1.1 에서는 모두 KHR 로 승격되었고 더 이상 사용되지 않음.
  • NV : Nvidia 익스텐션.
  • NVX : Nvidia 의 실험적 익스텐션.
  • VALVE : Valve 익스텐션.
  • EXT : 여러명의 제작자( 사 )가 개발한 경우의 익스텐션.
  • ANDROID : Android 용으로 Google 에서 개발된 익스텐션.


익스텐션에 대한 모든 명세는 [ 1 ] 에 포함되어 있습니다.


Emumerating Extensions for Layer


vkCreateInstance() 호출을 통해 명시적으로 로더를 로드하기 위해서는 현재 머신에서 어떤 레이어 익스텐션을 지원하고 있는지 알 필요가 있습니다. 


특정 레이어를 위해서 필요한 익스텐션들은 json 파일에서 찾아보실 수 있습니다. 예를 들어 "VKLayer_core_validationr.json" 파일에는 지원하고 있는 익스텐션의 리스트가 있습니다.



예를 들어 "VK_LAYER_LUNARG_core_validation" 레이어를 생성하기로 했다면, vkCreateInstance() 를 호출할 때 "instance_extensions" 필드에 있는 "VK_EXT_debug_report" 라는 익스텐션을 추가해 줘야 합니다. 그리고 vkCreateDevice() 를 호출할 때는 "device_extensions" 에 있는 "VK_EXT_debug_marker" 와 "VK_EXT_validation_cache" 라는 익스텐션을 추가해 줘야 합니다. 물론 사용하지 않으려면 넣지 않아도 상관없습니다.


그런데 이런 걸 외우고 다닐 수도 없고, 하드코딩하는 것도 좀 찝찝합니다. 그래서 Vulkan 은 vkEnumerateInstanceExtensionProperties() 와 vkEnumerateDeviceExtensionsProperties() 라는 함수를 지원하고 있습니다.


이 함수들은 다음과 같은 시그너쳐를 가집니다.



그런데 실제 property 개수를 모르기 때문에 처음에는 pProperties 를 nullptr 로 넘겨 개수만 받고 그 다음에 실제 array 를 넘겨서 데이터를 받습니다. 그 예가 아래에 나와 있습니다.



하지만 위의 코드에서는 첫 번째 파라미터인 pLayerName 을 nullptr 로 넘겼기 때문에 레이어 사용여부와는 상관없이 모든 레이어의 익스텐션이 반환됩니다. 그러므로 특정 레이어를 위한 익스텐션만을 뽑아 내고자 한다면 레이어의 이름을 공급해 줘야 합니다.


예를 들어 보죠. 아까 살펴 본 json 파일에서 레이어의 "name" 필드 값이 "VK_LAYER_LUNARG_core_validation" 였기 때문에 이것을 넘겨 보도록 하겠습니다.



이러면 "VK_EXT_debug_report" 라는 결과가 출력됩니다. json 의 내용과 같죠?


하지만 생각해 보면 이것도 좀 삽질이라는 느낌이 들겁니다. 레이어가 한 두 개도 아니고 이렇게 하는 것은 불합리합니다. 그러므로 모든 레이어를 돌면서 이 정보를 추출해 낼 수가 있습니다.



그런데 1.0 초반부터 레이어에 대한 디바이스 익스텐션은 없어진 것으로 보입니다. Vulkan spec 에서는 다음과 같이 이야기하고 있습니다.


30.1.1. Device Layer Deprecation


Previous version of this specification distinguished between instance and device layers. Instance layers were only able to intercept commands that operate on VkInstance and VkPhysicalDevice, except they were not able to intercept vkCreateDevice. Device layers were enabled for indivisual devices when they were created, and could only intercept commands operating on that device or its child objects.


Device-only layers are now deprecated, and this specification no longer distinguishes between instance and device layers. Layers are enabled during instance creation, and are able to intercept all commands operating on that instance or any of its child objects. At the time of deprecation there were no known device-only layers and no compelling reason to create one.


In order to maintain compatibility with implementations released prior to device-layer deprecation, applications should still enumerate and enable device layers. The behavior of vkEnumerateDeviceLayerProperties and valid usage of the ppEnabledLayernames member of VkDeviceCreateInfo maximizes compatibility with applications written to work with the previous requirements.


요약하자면 이제는 디바이스에다가 레이어 익스텐션을 넣을 필요가 없다는 겁니다. 그런 경우가 있다면 호환성을 유지하기 위해서라는 겁니다. 하지만 이제 시간이 꽤 지났기 때문에 그런 호환성조차 유지할 필요가 없을 것으로 보입니다.


Get functionality for extension


"base/VulkanDebug.cpp" 의 debug 에서는 다음과 같이 함수를 가지고 와서 호출합니다. vkGetInstanceProcAddr() 을 사용하여 함수포인터를 획득하는 것을 보실 수 있습니다.



정리


json 파일은 인스턴스 익스텐션 정보와 디바이스 익스텐션 정보를 포함합니다. 인스턴스와 디바이스를 생성할 때 각각의 익스텐션 정보를 추가하면 그 레이어의 기능을 이용할 수 있습니다. 하지만 디바이스 익스텐션은 이제 지원하지 않으므로 그냥 전부 인스턴스 익스텐션인 것처럼 생각하고 사용하시면 됩니다.


vkGetInstanceProcAddr() 를 호출하여 특정 함수에 대한 포인터를 얻어와 실행하는 것이 가능합니다.


참고자료


[ 1 ] Vulkan 1.1.89 - A Specification (with all registered Vulkan extensions), The Khronos Vulkan Working Group.


[ 2 ] Architecture of the Vulkan Loader Interfaces, LUANR XCHANGE.

+ Recent posts