[그래픽스]Shader 컴파일타임(컴파일시점)에 대한 고찰

윤태웅·2022년 7월 25일
0

그래픽스

목록 보기
6/13

개요


최근 재밌게 하고 있는 게임인 몬스터헌터 라이즈라는 게임은 초기 1회 실행 시 위와같은 화면이 뜨면서 게임에 쓰이는 Shader들을 컴파일한다. 그래픽스를 배우기 전에는 '아 로딩 개기네..'라며 그냥 지나쳤겠지만, 그래픽스를 배우니 이런것도 보이는 것 같다. 눈여겨 볼 점은 Shader를 게임 빌드할 때 컴파일 한것이 아니라 플레이어가 최초로 게임을 실행할 때 한번만 컴파일하게 처리한 것이다! 아마도 한번 컴파일된 Shader들은 로컬파일에 저장되는 방식인것 같다. 근데, Shader를 게임 빌드할때 같이 Binary로 묶는 방식은 왜 하지 않는 것일까?라는 의문이 들어서 이 글을 작성한다.

Shader 컴파일 방식

Shader컴파일 방식은 2가지가 있다. 런타임과 빌드타임 컴파일이다. 마이크로소프트 문서(링크)를 보면 HLSL에서 런타임과 빌드타임 컴파일을 어떻게 하는지에 대한 가이드도 나와있다. 이 두 방식을 비교해보겠다.

런타임 컴파일

UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    flags |= D3DCOMPILE_DEBUG;
#endif
    // Prefer higher CS shader profile when possible as CS 5.0 provides better performance on 11-class hardware.
    LPCSTR profile = ( device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_11_0 ) ? "cs_5_0" : "cs_4_0";
    const D3D_SHADER_MACRO defines[] = 
    {
        "EXAMPLE_DEFINE", "1",
        NULL, NULL
    };
    LPCSTR  entryPoint = "VS";
    ID3DBlob* shaderBlob = nullptr;
    ID3DBlob* errorBlob = nullptr;
 HRESULT hr = D3DCompileFromFile( srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
                                     entryPoint, profile,
                                     flags, 0, &shaderBlob, &errorBlob );

위 코드는 DirectX11에서 런타임에 shader를 컴파일하는 코드이다. 간단히 요약하자면 Shader의 디렉터리 위치와 진입점을 인자로 주면 잠시 쓰레드를 Block하고 컴파일 작업을 완료한후에 쓰레드를 다시 진행시킨다. 당연히 로딩시간을 잡아먹는다.

빌드타임 컴파일

#include "CompileShaders\PS.hlsl.h // 빌드타임에 PS.hlsl이 컴파일되고 g_psshader변수로 바이트코드 접근가능(visual studio property설정 필)
ComPtr<ID3D11PixelShader> m_pPixelShader;
hr = pDevice->CreatePixelShader(g_psshader, sizeof(g_psshader), nullptr, &m_pPixelShader);//g_psshader는 

위 코드는 DirectX11에서 빌드타임에 shader를 컴파일하는 코드이다. 프로젝트 세팅에서 빌드설정을 입력해주면(진입점,feature level등등) 헤더파일을 include함으로써 빌드과정에 Shader컴파일을 포함시킨다.

런타임 vs 빌드타임

이 두개의 방식은 얼핏 보면 빌드타임 컴파일 방식이 우월해 보인다.
런타임에 컴파일: (게임 사용자가 프로그램 실행 시 긴 로딩을 겪어야함)+(Shader코드의 컴파일 에러를 런타임에 잡아야됨)
빌드타임에 컴파일: (게임 사용자는 미리 컴파일된 Shader로 바로 게임을 즐김)

작성자도 처음에는 왜 런타임 컴파일이 존재하는거지? 라는 의문이 들 정도였다.
나같은 사람이 미국에도 존재했는지 레딧에 이런 글이 올라왔다. 레딧 글
요약하자면 런타임 컴파일의 존재 의의는 최종 Product를 배포할 때
사용자의 GPU다양성에 맞춘 컴파일을 하기위해서 Compile타임을 런타임에 맞출 필요가 있다는 것이다.
GPU는 x86 CPU들과는 다르게 명령어 셋이 회사마다 다르고 또 같은 회사의 라인업마다도 다르다.
내 수준으로 이정도 low level의 사정은 잘 모르겠지만, 아마도 렌더링 작업에 특화해서 하드웨어를 더 갈구기 위해 명령어 셋이 중구난방한것인것 같기도 하다(ex/n사의 RT코어)

결론

끔찍한 로딩시간과 디버깅에서의 단점에도 불구하고 런타임 Shader컴파일을 사용하는 이유는
사용자 하드웨어(GPU)의 다양성 때문.
별개로, 그냥 그래픽스 공부하고자 비주얼 스튜디오로 DirectX프로젝트를 진행하는 경우에는
빌드타임 Shader 컴파일을 사용하는게 더 좋아보인다.

0개의 댓글