ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • jEngine Library 빌드 관련
    Graphics/Renderer Lab 2023. 12. 29. 22:55

    Library 빌드 관련해서 겪은 이슈들을 정리합니다. 추가 사항이 있는 경우 계속 이 문서를 업데이트 할 예정입니다.
     

    최초 작성 : 2023-12-29
    마지막 수정 : 2024-03-23
    최재호

     

    목차

    1. ShaderConductor [Deprecated]
    2. DirectXTex
    3. xxHash, CityHash

    4. DirectX12 Agility SDK


    1. ShaderConductor [Deprecated]

    Updated 2024-01-11 : Shader Language 를 HLSL, DXC 로 DX12 와 Vulkan 쉐이더 컴파일에 문제 없습니다. 추후 Metal 이나 다른 API로 포팅이 필요한 경우 다시 사용할 예정입니다.
     
    ShaderConductor 는 hlsl 을 사용하여 Vulkan, DirectX12 의 shader cross compile 을 지원합니다.
    하지만 ShaderConductor 는 2년 전 업데이트를 멈춘 상태입니다. 때문에 Raytracing 에 사용하기 위해 Bindless resources 기능을 사용하려고 하면 쉐이더 컴파일 에러가 발생합니다.
    ShaderConductor Git 링크:  https://github.com/microsoft/ShaderConductor

    // SM6.6 으로 컴파일 한 상황
    
    // Issue 1 : SM6.6 의 HLSL Dynamic Resource 기능 중 하나인 ResourceDescriptorHeap 사용 불가
    error: use of undeclared identifier 'ResourceDescriptorHeap'
    
    // Issue 2 : Bindless resources 기능을 사용하기 위해 Unbounded range 설정 시 Validation error 발생
    RWTexture2D<float4> RenderTarget[] : register(u0);   // 이렇게 선언 시, 아래 처럼 에러남.
    ----
    error: validation errors
    hlsl.hlsl:728:5: error: Access to out-of-bounds memory is disallowed.

    ShaderConductor 가 사용하는 DirectXShaderCompiler 최신 버전(1.7.2308)에서는 문제가 해결되는 것을 확인하여 해당 라이브러리를 별도로 빌드하여 dll 을 교체해줬습니다. 컴파일된 dxcompiler.dll 은 ShaderConductor/Lib/{Configurations} 각각 넣어줬습니다.
    DirectXShaderCompiler 를 컴파일 방법은 https://github.com/microsoft/DirectXShaderCompiler 의 ReadMe 의 Building Sources 에 잘 나옵니다. 명세된 프로그램들을 설치 한 후 설명대로 그대로 진행하면 됩니다.
    추가로 한글과 스샷으로 설치방법을 친절하게 설명해주는 블로그도 있습니다. 아래 링크 참고해주세요.
    [ Vulkan 연구 ] HLSL to SPIR-V : 2. DXC 빌드, [ Vulkan 연구 ] HLSL to SPIR-V : 3. SPIR-V CodeGen 빌드
     

    2. DirectXTex

    DDS, HDR, png, jpg 등등의 이미지 로드를 해주는 라이브러리입니다. MS 에서 제작한 것이라 DirectX12 에서만 사용가능한 줄 알았는데 Vulkan 에서도 사용할 수 있는 것이 확인하여 이미지 로드는 DirectXTex 라이브러리로 수행합니다. DirectXTex 는 Mipmap 을 생성해주는 기능도 포함하고 있어서 별도로 Mipmap 을 생성하는 렌더패스를 구현하지 않아도 되서 편리합니다. 아래 코드를 참고해주세요.

    DirectX::ScratchImage image;
    if (ExtName == ExtDDS) // DDS 이미지 로드
    {
        if (JFAIL(DirectX::LoadFromDDSFile(FilenameWChar.c_str(), DirectX::DDS_FLAGS_NONE, nullptr, image)))
            return std::weak_ptr<jImageData>();
    }
    else if (ExtName == ExtHDR) // HDR 이미지 로드
    {
        if (JFAIL(DirectX::LoadFromHDRFile(FilenameWChar.c_str(), nullptr, image)))
            return std::weak_ptr<jImageData>();
    }
    else // png, jpg 와 같은 일반 이미지 로드
    {
        DirectX::ScratchImage imageOrigin;
        if (JFAIL(DirectX::LoadFromWICFile(FilenameWChar.c_str(), DirectX::WIC_FLAGS_FORCE_RGB, nullptr, imageOrigin)))
            return std::weak_ptr<jImageData>();
    
        // 밉맵 생성도 가능함. png, jpg 의 경우 mipmap 이 포함되지 않기 때문에 이 과정을 추가해줌.
        int32 mipLevel = jTexture::GetMipLevels((int32)imageOrigin.GetMetadata().width, (int32)imageOrigin.GetMetadata().height);
        DirectX::GenerateMipMaps(*imageOrigin.GetImages(), DirectX::TEX_FILTER_BOX | DirectX::TEX_FILTER_SEPARATE_ALPHA, mipLevel, image);
    }
    
    // 이후 과정에서 DirectX::ScratchImage 를 사용하여 Vulkan, DX12 의 텍스쳐 생성 규약에 맞게 생성함.
    // 해당 부분은 아래 링크 파일을 참고해주세요.
    // https://github.com/scahp/jEngine/blob/f7982eed34ba4d7ce75d2a9520dfb304901cc675/jEngine/FileLoader/jImageFileLoader.cpp#L123
    ...

     

    3. xxHash, CityHash

    특정 구조체 내에 일부 멤버변수들에 대한 Hash 를 구해야하는 경우가 있을 수 있습니다. 이런 Hash 를 사용할 멤버 변수 마다 별도로 해시 함수를 호출해야 하기 때문에 함수 호출을 여러번해야 되고 이런 이유로 퍼포먼스가 떨어질 수 있습니다.

    struct INeedHash
    {
    	int32 A; // Hash
        int32 B; // Hash
        const char* C; // No Hash
    };
    
    // I want to generate Hash without member variable C.
    // So, I call XXH64 for each members. It would be bad performance.
    INeedHash Data;
    Hash = XXH64(Data.A, Hash);
    Hash = XXH64(Data.B, Hash);

     
    이 부분을 해결하기 위해서 아래와 같이 InstantStruct 를 사용합니다. 전달받은 변수들을 기반으로 Trivially copyable 형태의 struct 를 정의합니다. 그리고 전달 받은 멤버 변수의 값으로 초기화합니다. 이렇게 되면 1개의 struct 데이터 덩어리가 되며, 한번의 해시함수 호출로 모든 과정을 마칠 수 있습니다. 실제로 각각의 해시 함수를 호출 할 때 보다 성능이 더 좋았으며 CPU 병목지점이 Hash 함수 생성에서 다른 지점으로 옮겨갔습니다.
    InstantStruct 클래스의 전체 코드는 https://github.com/scahp/jEngine/blob/main/jEngine/Core/TInstantStruct.h 에 있습니다.

    // Easy hash generation from InstantStruct, it is slow calling 'Hash Generation Function' for each variables Indivisually.
    #define GETHASH_FROM_INSTANT_STRUCT(...) \
    [&]() -> uint64 { auto InstanceStruct = jInstantStruct(__VA_ARGS__); return XXH64(&InstanceStruct, sizeof(InstanceStruct), 0); }()
    
    // RenderTarget.h
    ...
    size_t GetHash() const
    {
      // Generate struct and set all member variables, then calling the XXH64 function once.
      return GETHASH_FROM_INSTANT_STRUCT(Type, Format, Width, Height, LayerCount, IsGenerateMipmap
        , SampleCount, RTClearValue.GetHash(), TextureCreateFlag, IsUseAsSubpassInput, IsMemoryless);         // without ResourceName
    }
    ...

     
     struct 로부터 Hash 를 얻어낼 때 주의할 점은 struct 에 컴파일러가 넣을 수 있는 패딩 값에 의해서 쓰레기 값이 struct 의 인스턴스에 들어갈 수 있다는 점입니다. 그래서 같은 데이터를 사용하더라도 Hash 결과가 달라질 수 있습니다. 이런 점을 피하기 위해서 패딩값이 적절히 잘 초기화 될 수 있도록 하거나 struct 를 #pragma pack(push, 1) ~ #pragma pack(pop) 으로 감싸줘서 패딩 값이 해시 생성에 포함되지 않도록 해줘야 합니다. 
     

    4. DirectX12 Agility SDK

    Windows10 에서 SM6.6 이 지원되지 않는 것을 발견했습니다. Windows10 의 마지막 업데이트에는 SM6.6 을 지원하는 버전이 포함되지 않기 때문이었습니다. (Windows11 은 SM6.6 이 지원되는 DirectX 라이브러리가 포함되어 있음)

     

    구글링 해본 결과 DirectX 라이브러리 업데이트 정책이 바뀌었는데, 예전에는 윈도우 업데이트를 통해서 DirectX 라이브러리를 업데이트 했지만 윈도우 업데이트 보다 상대적으로 자주 변경되는 DirectX 라이브러리를 관리하기 위해서 DirectX12 Agility SDK 를 사용하게 변경되었다고 합니다. 그래서 jEngine 이 필요한 버전의 lib 를 DirectX 12 Agility SDK 에서 받은 것으로 로드 할 수 있게 했습니다. 현재 613 버전이 적용되어 있습니다.

     

    DirectX 12 Agility SDK 를 사용방법은 Getting Started with the Agility SDK, 그리고 다운로드는 DirectX 12 Agility SDK Downloads 를 참고해주세요.
     

     

     

     

    'Graphics > Renderer Lab' 카테고리의 다른 글

    jEngine(Vulkan, DX12)  (0) 2023.11.11

    댓글

Designed by Tistory & scahp.