주의 : 초심자 튜토리얼은 아닙니다. 그러므로, 실제 API 호출 용례를 알고자 한다면, 샘플이나 튜토리얼을 찾아서 확인해 보세요.

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

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


 

Descriptor

 

벌칸은 SPIR-V 라는 플랫폼 독립적인 중간언어를 사용합니다. 하지만 이것은 바이너리 파일이므로 사용자가 직접 작성할 수 없습니다. 

그래서 다른 셰이딩/컴퓨팅 언어들을 통해 프로그램을 작성한 다음에 이를 SPIR-V 로 컨버팅하게 됩니다. 이를 위한 언어들은 glsl, hlsl, opencl, cg 등이 될 수 있습니다. 예전부터 널리 사용해 오던 언어들이죠.

그런데 크로노스 그룹에서 정식으로 제공하고 있는 "glslangValidator.exe" 라는 프로그램이 glsl 과 hlsl 만을 지원합니다. 하지만 hlsl 에 대한 지원은 미비합니다( 나중에 언급하겠습니다 ). 크로노스 그룹에서 만든 언어이니 당연한 거겠죠.

벌칸에서는 셰이더에 뭔가를 바인딩하기 위해서 디스크립터( Descriptor )라는 것을 사용합니다. 그 중 버텍스 관련 디스크립터들에 대해서 살펴 보겠습니다.

출처 : [ Vertex input description ], Vulkan Tutorial.

버텍스 셰이더에서 사용하는 입력에 레이아웃이라는 것을 지정하는 것을 보실 수 있습니다. 버텍스 버퍼의 내부 메모리 구성이 어떻게 되어 있는지를 알려주는 거죠.

그러므로 네이티브 코드 단에서는 버텍스 입력을 제공하기 위해서 다음과 같이 클래스를 선언합니다.

출처 : [ Vertex input description ], Vulkan Tutorial.

하나의 버텍스 데이터 구조를 만들기 위해서 두 가지 작업을 하고 있는 것을 알 수 있습니다.

  • 버텍스 버퍼의 메모리 정보( 각 버텍스의 크기( stride ) )를 알려주기 위해 바인딩 디스크립터( VkVertexInputBindingDescription )를 생성.
  • 각 버텍스를 구성하고 있는 속성( attribute )들에 대한 정보( VkVertexInputAttributeDescription )를 생성.

 

SPIR-V Toolchain

 

위의 예제는 기본적으로 셰이더의 내용을 프로그램 작성자가 다 알고 있다는 가정하에서 이루어졌습니다. 하지만 실제 개발과정에서는, 저런 레이아웃의 순서가 바뀔수도 있고, 레이아웃의 순서가 어쨌든지 간에 자동화해서 데이터를 입력해 줘야 할 수도 있습니다. 셰이더 작성자와 코드 작성자가 완전히 다를 수도 있고, 셰이더 언어가 다양할 수도 있습니다.

맘 편하게 position = 0, normal = 1, texcoord = 2 라는 식으로 정할 수는 없다는 겁니다. 물론 하려면 할수도 있겠지만, 그 만큼 셰이더가 프로그램에 종속적이 될 수밖에 없겠죠.

그렇기 때문에 이런 것들( spir-v 변환부터 c++ 리플렉션까지 )을 자동으로 처리해 줄 수 있는 툴들이 필요합니다. 다행히도 크로노스에서는 이와 관련한 기본 툴체인을 제공하고 있습니다[ 1 ].

glsl 을 사용하고 있다고 가정했을 때 spir-v 로의 변환 및 리플렉션 과정은 다음과 같습니다.

  • Code Generation( glslangValidator ) : glsl 에서 spir-v 로 변환합니다. 
  • Optimization( spirv-opt ) : spir-v 파일의 크기를 줄입니다.
  • Validation( spirv-val ) : spir-v 코드가 올바른지 검사합니다. 이 과정에서 인간이 읽어들일 수 있는 포맷으로 변경하기 위해 디스어셈블러나 어셈블러를 사용해서 디버깅하기도 합니다( spirv-dis, spirv-as ).
  • Reflection ( spirv-cross ) : spir-v 파일로부터 리플렉션 정보를 추출합니다.

 

HLSL to SPIR-V

 

glsl 을 사용하고 있다면 그냥 기본 툴체인을 사용하면 되겠지만, hlsl 을 사용하고 있다면 일단 hlsl 에서 glsl 로 변환하는 과정부터 거쳐야 합니다. 그리고 layout 이라는 키워드 자체가 glsl 에서만 지원하는 것이므로 이와 관련한 변환 작업도 필요합니다.

사실 glslang 으로도 HLSL 을 SPIR-V 로 변환하는 것은 가능하지만 지원하지 않는 기능들이 많습니다. Khronos 의 [ HLSL FAQ ] 에서는 glslang 의 한계에 대해서 이야기하고 있습니다. 

일반적으로 어떤 툴체인을 사용하는지 검색해 봤는데, 다음과 같은 식으로 하고 있는 걸 발견했습니다.

  • Unity 3D 같은 경우에는 HLSLcc( hlslcc( James-Hones ) 라는 오픈소스의 변형 )를 사용해 spir-v 를 바로 생성하는 것으로 보입니다.
  • UE4 같은 경우에는 hlslcc( 원본인지 자체 변종인지는 모르겠습니다 )를 사용하여 hlsl 을 glsl 로 변환한 다음에 glslangValidator 를 사용해 spir-v 를 생성합니다.
  • DXC( DirectX Shader Compiler )는 hlsl 을 직접 spir-v 로 변환하고 있는 것( SPIR-V CodeGen )으로 보입니다.

 

나가며

 

저같은 경우에는 HLSL 을 선호하고 툴체인이 길어지는 것을 별로 좋아하지 않기 때문에 dxc 를 사용해 보려고 합니다. 

[ HLSL to SPIR-V ] 시리즈에서는 DXC 를 사용해서 hlsl 파일로부터 spir-v 를 생성하고 C++ 에서 리플렉션하는 과정까지를 다뤄 볼 계획입니다.

추가 : 참고로 위키에 따르면 SPIR-V CodeGen 은 MS 가 아니라 google/shaderc 팀에서 주로 관리한다고 하는군요.

 

참고자료

 

[ 1 ] SPIR-V Toolchain, LUNARG.

+ Recent posts