D3D에서 셰이더 프로그램은 먼저 이식성이 있는 바이트 코드로 컴파일되어야 한다. 그래야 그래픽 드라이버는 그 바이트 코드를 다시 시스템의 GPU에 맞게 최적의 네이티브 명령들로 컴파일한다.
1. 런타임 컴파일
실행 시점에서 D3DCompilerFromFile() 함수를 이용해 셰이더를 컴파일할 수 있다.
https://docs.microsoft.com/en-us/windows/win32/api/d3dcompiler/nf-d3dcompiler-d3dcompilefromfile
D3DCompileFromFile function (d3dcompiler.h) - Win32 apps
Compiles Microsoft High Level Shader Language (HLSL) code into bytecode for a given target.
docs.microsoft.com
1. pFileName: 컴파일할 HLSL 소스 코드를 담은 .hlsl 파일의 이름.
2. pDefine: 고급 옵션으로, 이 책에서는 사용하지 않는다. SDK 문서화 참고.
3. pInclude: 고급 옵션으로, 이 책에서는 사용하지 않는다. SDK 문서화 참고.
4. pEntrypoint: 셰이더 프로그램의 진입점 함수 이름.
5. pTarget: 사용할 셰이더 프로그램의 종류와 대상 버전을 나타내는 문자열. 이 책의 예제는 5.0과 5.1을 사용한다.
6. Flags1: 세이더 코드의 세부적인 컴파일 방식을 제어하는 플래그. 이 책에서는 두 가지만 사용한다.
(a) D3DCOMPILE_DEBUG: 셰이더를 디버그 모드에서 컴파일한다.
(b) D3DCOMPILE_SKIP_OPTIMIZATION: 최적화를 생략한다(디버그에 유용).
7. Flags2: 고급 옵션으로, 이 책에서는 사용하지 않는다. SDK 문서화 참고.
8. ppCode: 컴파일된 셰이더 목적 바이트 코드를 담은 ID3DBlob 구조체의 포인터를 이 매개변수를 통해 돌려준다.
9. ppErrorMsgs: 컴파일 오류가 발생한 경우 오류 메시지를 담은 ID3DBlob 구조체의 포인터를 이 매개변수를 통해 돌려준다.
ID3DBlob은 범용 메모리 버퍼를 나타내는 형식으로 다음 두 메서드를 제공한다.
(a) GetbufferPointer(): 버퍼를 가리키는 void* 를 돌려준다.
(b) GetBufferSize(): 버퍼의 크기(byte 개수)를 돌려준다.
// 셰이더 프로그램 컴파일 보조 함수(d3dUtil.h/.cpp)
ComPtr<ID3DBlob> D3DUtil::CompileShader(
const std::wstring& file,
const std::string& entrypoint,
const std::string& target)
{
// 디버그 모드에서는 디버깅 관련 플래그들을 사용한다.
UINT compileFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
HRESULT hr = S_OK;
ComPtr<ID3DBlob> byteCode = nullptr;
ComPtr<ID3DBlob> errors = nullptr;
hr = D3DCompileFromFile(
file.c_str(),
nullptr,
D3D_COMPILE_STANDARD_FILE_INCLUDE,
entrypoint.c_str(),
target.c_str(),
compileFlags,
0,
byteCode.GetAddressOf(),
errors.GetAddressOf());
if (errors != nullptr)
{
OutputDebugStringA((char*)errors->GetBufferPointer());
}
ThrowIfFailed(hr);
return byteCode;
}
다음과 같이 호출한다.
ComPtr<ID3DBlob> mvsByteCode = nullptr;
ComPtr<ID3DBlob> mpsByteCode = nullptr;
mvsByteCode = D3DUtil::CompileShader(L"color.hlsl", nullptr, "VS", "vs_5_0");
mpsByteCode = D3DUtil::CompileShader(L"color.hlsl", nullptr, "PS", "ps_5_0");
2. 오프라인 컴파일
오프라인 컴파일을 하는 이유
1. 셰이더를 오프라인에서 컴파일하면 게임의 적재(loading) 시간이 빨라진다.
2. 셰이더 컴파일 오류들은 실행 시점이 아니라 빌드 과정에서 일찍 점검하는 것이 편하다.
3. Windows 8 스토어 앱은 반드시 오프라인 컴파일을 사용해야 한다.
컴파일된 셰이더를 담는 파일의 확장자로는 .cso(compiled shader object)를 사용하는 것이 관례이다. 오프라인에서 셰이더를 컴파일할 때에는 DirectX에 포함된 FXC 도구를 사용한다.
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=bloodsoda&logNo=220872161984
[DirectX 11] 쉐이더(HLSL) 컴파일 하기
fxc.exe를 HLSL 파일들과 같은 경로에 둔다. 그 폴더에서 경로표시줄에 cmd를 치고 엔터를 누른다. 그...
blog.naver.com
다음과 같이 .cso 파일 생성을 완료했다면 로드하면 된다.
// 적재 함수
ComPtr<ID3DBlob> D3DUtil::LoadBinary(const std::wstring file)
{
std::ifstream fin(file, std::ios::binary);
fin.seekg(0, std::ios_base::end);
std::ifstream::pos_type size = (int)fin.tellg();
fin.seekg(0, std::ios_base::beg);
ComPtr<ID3DBlob> blob;
ThrowIfFailed(D3DCreateBlob(size, blob.GetAddressOf()));
fin.read((char*)blob->GetBufferPointer(), size);
fin.close();
return blob;
}
'DirectX' 카테고리의 다른 글
6.9 파이프라인 상태 객체 (0) | 2022.01.03 |
---|---|
6.8 래스터화기 상태 (0) | 2022.01.03 |
6.6 루트 서명과 서술자 테이블 (0) | 2021.12.31 |
6.5 상수 버퍼(Constant buffer) (0) | 2021.12.26 |
6.4 픽셀 셰이더(Pixel Shader) (0) | 2021.12.26 |