레스터라이제이션 파이프라인은 GPU를 사용해서 만들어진 파이프라인중 제일 오래된 파이프라인입니다. 원하는 메쉬를 가장 빠르고 값싸게 만들려면 해당 파이프라인이 제일 제격입니다. 나중에 시간이 지나고 GPU의 성능이 월등히 좋아진다면 레스터라이제이션이 제일먼저 폐지 될수도 있습니다. 메쉬 쉐이더, 레이트레이싱등이 대체 될 수도 있죠.

Descriptor Heaps

image.png

image.png

디스크립터 힙은 그래픽 오브젝트에 할당된 오브젝트를 설명을 저장해둔 공간입니다.

(Descriptor Heap은 Descriptor들이 저장된 GPU힙 메모리를 의미한다.)

"셰이더(Shader)가 사용하는 GPU 리소스들(텍스처, 버퍼 등)의 **설명서(Descriptor)**를

GPU가 읽을 수 있도록 메모리에 연속적으로 저장한 공간"이라고 생각하면 됩니다.

🔧 예전(D3D11)과의 차이점

Direct3D 11 Direct3D 12
뷰(View)를 자동으로 관리 뷰를 직접 생성하고 힙에 저장해야 함
시스템이 리소스 바인딩 처리 개발자가 명시적으로 디스크립터 바인딩
ID3D12DescriptorHeap* renderTargetViewHeap;

D3D12_DESCRIPTOR_HEAP_DESC rtvHeapDesc = {};
rtvHeapDesc.NumDescriptors = backbufferCount;
rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
rtvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
ThrowIfFailed(device->CreateDescriptorHeap(
    &rtvHeapDesc, IID_PPV_ARGS(&renderTargetViewHeap)));

Root Signature

image.png

image.png

루트 시그니처는 리소스 유형을 정의합니다. 예를들어 상수버퍼, 구조화버퍼, 샘플러, 텍스처 등등을 나타냅니다.

셰이더(Shader)가 사용할 수 있는 리소스들의 레이아웃(구조와 종류)을 GPU에게 알려주는 계약서 같은 것

“셰이더가 어떤 자원(CBV, SRV, UAV, 샘플러 등)을 쓸 건지, 어디에 배치돼 있을지를 정의”하는 구조

<aside> 💡

RTV(Render Target View)는 루트 시그니처로 정의하지 않는다.


🔍 왜 그런가?

루트 시그니처는 GPU의 셰이더 프로그램이 접근할 리소스(SRV, UAV, CBV, Sampler 등) 를 정의하는 "셰이더 바인딩 구조"야.

👉 반면에 RTV는 셰이더에서 직접 접근하지 않아.

// 👋 Declare Handles
ID3D12RootSignature* rootSignature;

// 🔎 Determine if we can get Root Signature Version 1.1:
D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {};
featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1;

if (FAILED(device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE,
                                       &featureData, sizeof(featureData))))
{
    featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0;
}

// 📂 Individual GPU Resources
D3D12_DESCRIPTOR_RANGE1 ranges[1];
ranges[0].BaseShaderRegister = 0;
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
ranges[0].NumDescriptors = 1;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = 0;
ranges[0].Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE;

//🗄️ Groups of GPU Resources
D3D12_ROOT_PARAMETER1 rootParameters[1];
rootParameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootParameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;

rootParameters[0].DescriptorTable.NumDescriptorRanges = 1;
rootParameters[0].DescriptorTable.pDescriptorRanges = ranges;

// 🏢  Overall Layout
D3D12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc;
rootSignatureDesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
rootSignatureDesc.Desc_1_1.Flags =
    D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
rootSignatureDesc.Desc_1_1.NumParameters = 1;
rootSignatureDesc.Desc_1_1.pParameters = rootParameters;
rootSignatureDesc.Desc_1_1.NumStaticSamplers = 0;
rootSignatureDesc.Desc_1_1.pStaticSamplers = nullptr;

ID3DBlob* signature;
ID3DBlob* error;
try
{
    // 🌱 Create the root signature
    ThrowIfFailed(D3D12SerializeVersionedRootSignature(&rootSignatureDesc,
                                                       &signature, &error));
    ThrowIfFailed(device->CreateRootSignature(0, signature->GetBufferPointer(),
                                              signature->GetBufferSize(),
                                              IID_PPV_ARGS(&rootSignature)));
    rootSignature->SetName(L"Hello Triangle Root Signature");
}
catch (std::exception e)
{
    const char* errStr = (const char*)error->GetBufferPointer();
    std::cout << errStr;
    error->Release();
    error = nullptr;
}

if (signature)
{
    signature->Release();
    signature = nullptr;
}