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

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

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



DirectX Shader Compiler 프로젝트를 빌드하고 나면 엄청나게 많은 executable 및 library 들이 생깁니다. 


하지만 우리가 그것을 다 사용할 것이 아니기 때문에 필요한 것만 가져다가 사용해야 합니다. 


일단 "dxc.exe" 프로젝트만 빌드해서 executable 을 뽑아 봤는데, LLVM table 관련 executable 을 배제하면, 필수적인 바이너리의 목록은 다음과 같습니다.


  • dxc.exe : 커맨드라인 프로그램.

  • dxc.pdf : 디버깅할 거라면 필요함.

  • dxcompiler.dll : 실제 구현이 포함되어 있는 동적 라이브러리.

  • dxcompiler.pdb : 디버깅할 거라면 필요함.


저는 이것만 복사해서 제가 원하는 디렉토리에 복사했습니다.



이제 vertex shader 를 하나 만들어서 테스트해 보겠습니다. 옵션에 대한 자세한 정보는 [ Supported Command-line Options ] 에서 확인하실 수 있습니다.



다음과 같이 파워셸에서 테스트해 봤습니다.


PS C:\GIt\HairDresser\Distribution\Binaries\ThirdParty\dxc> .\dxc.exe -spirv -Fc -T vs_5_0 -E mainVS C:\GIt\HairDresser\Distribution\Binaries\ThirdParty\dxc\test.hlsl -Fo test.vertex


  • -spirv : SPIR-V 를 생성.
  • -E : 엔트리 포인트.
  • -Fo : 아웃풋( SPIR-V )


혹시나 해서 dxcompiler.dll 을 지워 봤는데 에러가 발생하더군요.


dxc failed : error code 0x8007007e


다른 spir-v 관련 옵션들도 잘 동작하는지 테스트해 봤습니다.


PS C:\GIt\HairDresser\Distribution\Binaries\ThirdParty\dxc> .\dxc.exe -spirv -T vs_5_0 -E mainVS C:\GIt\HairDresser\Distribution\Binaries\ThirdParty\dxc\test.hlsl -Fo test.vertex -Fc test_dis.vertex


  • -Fc : SPIR-V 디스어셈블리 생성.


다음과 같이 정상적으로 디스어셈블리가 생성되었습니다. 내용이 정확한지는 아직 모릅니다. 시리즈를 진행하면서 의미를 파악해 볼 계획입니다.



단지 "Location" 과 같은 키워드가 있는 것으로 봐서는 따로 리플렉션 도구를 이용하지 않아도 이것을 파싱함으로써 정보를 얻을 수도 있겠다는 생각이 들긴 합니다.


참고 자료


[ 1 ] HLSL to SPIR-V Feature Mapping Manual, DirectXShaderCompiler.

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

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

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


 

문제점

 

[ 2. DXC 빌드 ] 에서 "dxc.exe" 를 빌드하는 과정에 대해서 살펴 봤습니다.

 

그 이후에 어떤 옵션들이 있는지 확인하기 위해서 코드를 한 번 살펴 봤습니다. 그런데 ENABLE_SPIRV_CODEGEN 이라는 preprocess definition 이 하나 보이더군요.

 

이게 비활성화되어 있었습니다.

 

프로젝트 구성에서 추가를 할까 했는데 그냥 [ 1 ] 의 가이드라인을 따라 정상적으로 빌드해 보기로 했습니다. 

 

CMAKE 솔루션 재생성

 

일단 [ 2. DXC 빌드 ] 과정까지 거쳤기 때문에 TAEF 를 다시 설치하거나 할 필요는 없습니다. 단지 프로젝트를 다시 만들어줘야 할 뿐입니다.

 

HLSL console 을 띄운 다음에 다음과 같이 "-spirv" 옵션만 추가해서 "hctbuild.cmd" 를 실행해 주면 됩니다.

 

 

이러면 열심히 CMAKE 프로젝트를 만들고 빌드합니다.

 

"LLVM.sln" 솔루션을 열어 봤더니 새로운 프로젝트들이 추가된 것 같더군요.

 

 

Trouble Shooting

 

역시나 이것도 한 번에 빌드가 안 되는군요.

 

 

에러가 4 개인 것처럼 보이지만 사실 SPIRV-Tools.lib 를 못 찾는 건 그냥 결과입니다. 근본적인 문제는 첫 번째 에러에 있습니다.

 

18>Traceback (most recent call last):

18>  File "C:/GIt/DirectXShaderCompiler/external/SPIRV-Tools/utils/update_build_version.py", line 148, in <module>

18>    main()

18>  File "C:/GIt/DirectXShaderCompiler/external/SPIRV-Tools/utils/update_build_version.py", line 134, in main

18>    software_version = deduce_software_version(sys.argv[1])

18>  File "C:/GIt/DirectXShaderCompiler/external/SPIRV-Tools/utils/update_build_version.py", line 90, in deduce_software_version

18>    for line in f.readlines():

18>UnicodeDecodeError: 'cp949' codec can't decode byte 0xe2 in position 846: illegal multibyte sequence

18>C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Microsoft.CppCommon.targets(209,5): error MSB6006: "cmd.exe" exited with code 1.

 

원인을 찾아 봤는데 "update_build_version.py" 파일의 90 라인에서 읽고 있는 뭔가가 "cp949" 코덱으로 인코딩되어 있었기 때문입니다. 

 

 

그러므로 89 라인의 open() 함수에서 "UTF-8" 로 인코딩을 바꿔주고 이제 사용하지 않는 옵션인 "U" 를 제거하고 "t" 로 바꿔 줍니다.

 

 

이제 빌드가 잘 됩니다.

 

참고자료

 

[ 1 ] SPIR-V CodeGen, DirectXShaderCompiler.

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

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

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



DirectX Shader Compiler( DXC ) 는 High-Level Shader Language( HLSL ) 와 DirectX Intermdiate Language( DXIL ) 관련 컴파일러 및 툴체인을 포함하고 있는 프로젝트입니다.


예전에는 DXSDK 와 Windows 10 SDK 에 "fxc.exe" 라는 실행파일을 배포했었는데, 이제 따로 DXC 라는 이름으로 컴파일러를 배포하고 있더군요. [  DXC 프로젝트 위키 ] 를 보면 다중 플랫폼에 대응하고 개발자들의 열정페이를 촉진하기 위해서 만들어진 것 같습니다.


어쨌든 개발자의 요청 때문인지 HLSL 을 표준 셰이더 언어처럼 만들고자 하는 MS 의 야심때문인지는 모르겠지만, 2018 년 초부터 [ SPIR-V CodeGen ] 이라는 툴을 지원하기 시작했습니다. 따로 툴이 존재하는 것은 아니고 "dxc.exe" 의 파라미터를 통해서 실행됩니다.



이 "dxc.exe" 는 Windows 10 SDK 에 포함되어 있습니다.



즉 배포한 프로그램이 설치된 OS 에는 이 바이너리가 없을 수도 있습니다.


만약 개발자 머신에서만 dxc 를 사용하고, 배포된 프로그램에서는 dxc 가 필요하지 않다면 상관이 없습니다만, 배포된 프로그램에서도 이를 사용해 컴파일을 할 필요가 있다면 같이 배포해 줘야 합니다.


게다가 이 바이너리가 Windows SDK 와 함께 배포되므로 버그 수정과 같은 최신 변경사항에는 대응할 수 없습니다. 디버깅도 불가능하죠. 그러므로 직접 빌드해서 사용하는 것이 여러모로 좋다고 생각합니다.


아래에서는 Visual Studio 2017 을 사용해서 프로젝트를 빌드하는 과정에 대해서 살펴 보도록 하겠습니다. 다른 환경을 사용해서 빌드할 수도 있는데, 그것에 대해 궁금한 점이 있다면 [ 2 ] 를 참조하시기 바랍니다.


GitHub 클론


GitHub 나 GitHub Desktop 에 대한 기본 이해는 있다고 가정하고 진행하겠습니다.


먼저 [ 프로젝트 사이트 ] 로 가서 주소를 복사합니다.




바로 "Open in Desktop" 을 해도 되지만 가끔 실패하는 경우가 있어서 저는 그냥 GitHub Desktop 을 열어서 클론을 했습니다.



파이썬 설치


[ 파이썬 3.x ] 이상을 설치해야 합니다. 중요한 건 반드시 Path 에 파이썬 경로가 포함되어야 한다는 겁니다. 아래 그림에 빨간색 네모로 표시했습니다. 잊지 말아 주세요.



HLSL console 만들기


뭔지는 모르겠지만 HCT 라는 유틸이 있습니다. 제 생각에는 [ Hardware Certification Tool ] 의 머리글자인 것 같습니다.


어쨌든 "$(GitProjectRoot)/utils/hct/hctshortcut.js" 를 윈도우즈 탐색기에서 더블클릭해서 실행하면 바탕화면에 "HLSL console" 이라는 바로가기가 만들어집니다.


이것의 정체를 까 보면 다음과 같습니다.


C:\Windows\System32\cmd.exe /k C:\GIt\DirectXShaderCompiler\utils\hct\hctstart.cmd C:\GIt\DirectXShaderCompiler C:\GIt\hlsl.bin


hctstart 라는 커맨드를 실행하는데, 소스가 있는 폴더인 "DirectXShaderCompiler" 와 "hlsl.bin" 라는 폴더를 인자로 넣습니다.


실행해 보면 "HLSL_SRC_DIR" 과 "HLSL_BLD_DIR" 로 설정됨을 알 수 있습니다.



TAEF 설치

이제 TAEF 라는 것을 설치해야 합니다.


[ Test Authoring and Execution Framework( TAEF ) ] 는 MS 에서 하드웨어 관련 자동화 테스트를 하는 프레임워크인 것 같은데 이것을 설치해야 합니다. 


"$(GitProjectRoot)/utils/hct/hctgettaef.py" 를 실행해서 설치할 수가 있습니다.


아까 만든 "HLSL console" 을 실행해서 다음과 같이 입력합니다. 별다른 반응없이 종료되더군요. 이 작업은 처음 실행했을 때 1 번만 수행하면 된다고 합니다.



방법 1 : hctbuild 를 사용해 CMAKE 솔루션 생성


이제 그 상태에서 바로 "hctbuild" 명령을 실행합니다. 추가 : 이렇게 하면 단순하게 DX 만을 위한 컴파일러가 됩니다. "hctbuild -spirv" 를 입력해야 합니다. 자세한 사항은 [ 3. SPIR-V CodeGen 빌드 ] 에서 확인하시기 바랍니다.



한참을 기다리면, CMAKE 솔루션을 생성하고 빌드까지 합니다. 빌드 경로는 위의 "HLSL_BLD_DIR" 경로입니다. 이것을 고치려면 프로젝트 폴더의 "CMakeSettings.json" 에서 "buildRoot" 항목을 수정하시면 됩니다. 물론 구성별로 전부 수정해야 합니다. 


저같은 경우에는 "x86-Debug" 구성과 "x86-Release" 구성은 아예 지워버렸습니다. 필요하지 않기 때문입니다.



방법 2 : 직접 CMAKE 솔루션 생성


만약 "hctbuild" 를 사용하지 않겠다면, 직접 솔루션을 생성할 수도 있습니다( 직접 해 봤는데 추천하지는 않습니다. CMake 를 여는 데 사용했던 솔루션과 실제 CMAKE 솔루션 사이의 관계에 대한 혼동이 오기 때문입니다. ).


 Visual Studio 2017 은 CMake 에 대한 지원을 내장하고 있습니다. 그러므로 단순하게 프로젝트 폴더의 CMakeList.txt 를 읽어 들임으로써 솔루션이 생성됩니다.




이렇게 솔루션을 생성하면 "hctbuild" 를 생성한 것과 크게 다를 건 없습니다. 이 역시 "CMakeSettings.json" 의 "buildRoot" 에 프로젝트를 빌드합니다.


솔루션 열기


앞에서 언급했듯이 "buildRoot" 에 솔루션이 생성됩니다. 



"LLVM.sln" 을 더블 클릭해서 솔루션을 열 수도 있고, HLSL console 에서 "hctvs" 명령을 입력해 솔루션을 열 수도 있습니다. 일단 솔루션이 생성되고 나면 일반 Visual Studio 솔루션들과 다를 것이 없습니다.


Trouble Shooting


안타깝게도 현재 버전에서는 hctbuild 로 빌드에 성공하지 못하더군요. 솔루션을 열어서 다시 빌드해야 합니다.


86>c:\git\directxshadercompiler\tools\clang\lib\sema\semaexpr.cpp(12689): error C2220: warning treated as error - no 'object' file generated

86>c:\git\directxshadercompiler\tools\clang\lib\sema\semaexpr.cpp(12689): warning C4819: The file contains a character that cannot be represented in the current code page (949). Save the file in Unicode format to prevent data loss


코드페이지가 맞지 않아서라는데... 한글이 있는 것도 아니고 난감하네요.


"File >> Save As" 를 한 다음에 "Save" 옆의 드롭다운을 클릭해서 "Save with encoding" 을 클릭한 다음, "Advanced Save Option" 에서 "Unicode ( UTF-8 with signature) - Codepage 65001" 을 선택하시면 됩니다. 처음에 기본값으로 "without signature" 가 선택되어 있는데, 스크롤을 제일 위쪽으로 올리면 "with signature" 가 나옵니다.




빌드에 성공하면 "$(buildRoot)\$(Configuration)\bin" 에 "dxc.exe" 및 관련 dll 들이 생성되어 있는 것을 확인하실 수 있습니다.



참고 자료


[ 1 ] SPIR-V CodeGen, DirectXShaderCompiler.


[ 2 ] Building Sources, DirectXShaderCompiler.

주의 : 초심자 튜토리얼은 아닙니다. 그러므로, 실제 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.

주의 : 답이 틀릴 수도 있습니다. 그냥 정리하는 용도로 올립니다. 혹시라도 도움이 필요한 분이 있다면 도움이 되었으면 좋겠네요.

주의 : 특별한 경우가 아니라면, 귀찮아서 문제는 안 옮깁니다.

경고 : 숙제하려고 베끼는 데 사용하지 마십시오. 본인의 미래를 망칠 뿐입니다. 나중에 저를 원망하지 마세요.

부탁 : 문제 풀이가 잘못되었으면 지적해 주셨으면 좋겠습니다.



( a )


m 에 대해 작용하는 중력 가속도는 g 이며 이를 힘으로 나타내면 F = mg 입니다.


그러므로 다음과 같이 g 를 계산할 수 있습니다.


식 1.

( b )


gG 를 이용해서 지구의 질량을 구해야 한다면 식 1 을 사용할 수 있습니다.


식 2.

+ Recent posts