7.6 루트 서명 추가 설명
1. 루트 매개변수
루트 서명이 루트 매개변수의 배열로 정의된 점을 기억할 것이다.
지금까지는 서술자 테이블에 해당하는 루트 매개변수 하나로만 이루어졌다. 그러나 꼭 서술자 테이블이어야 하는 것은 아니다.
루트 매개변수는 세 종류이다.
- 서술자 테이블: 힙 안에 있는 일련의 서술자들의 구간을 지정한다.
- 루트 서술자: 묶을 자원을 직접 식별하는 하나의 서술자로 힙에 둘 필요가 없다. 상수 버퍼에 대한 CBV나 기타 자원에 대한 SRV/UAV만 묶을 수 있다. 특히, 텍스처에 대한 SRV는 루트 서술자로 묶을 수 없다.
- 루트 상수: 직접 묶을 32비트 상수 값들의 목록이다.
성능상 이유로 매개변수의 비용은 다음과 같다.
- 서술자 테이블: DWORD 하나
- 루트 서술자: DWORD 두 개
- 루트 상수: 32비트 상수당 DWORD 하나
2. 서술자 테이블
https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_root_descriptor_table
D3D12_ROOT_DESCRIPTOR_TABLE (d3d12.h) - Win32 apps
Describes the root signature 1.0 layout of a descriptor table as a collection of descriptor ranges that are all relative to a single base descriptor handle.
docs.microsoft.com
3. 루트 서술자
루트 상수를 정의하려면 D3D12_ROOT_PARAMETER의 Descriptor멤버를 채워 루트 상수에 대해 좀 더 구체적인 정보를 제공해야 한다.
https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_root_constants
D3D12_ROOT_CONSTANTS (d3d12.h) - Win32 apps
Describes constants inline in the root signature that appear in shaders as one constant buffer.
docs.microsoft.com
4. 루트 상수
루트 상수를 정의하려면 D3D12_ROOT_PARAMETER의 Constants멤버를 채워 루트 상수에 대해 좀 더 구체적인 정보를 제공해야 한다.
https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_root_constants
D3D12_ROOT_CONSTANTS (d3d12.h) - Win32 apps
Describes constants inline in the root signature that appear in shaders as one constant buffer.
docs.microsoft.com
셰이더의 관점에서 루트 상수는 그냥 상수 버퍼의 한 자료와 다를 바 없다. 루트 서술자처럼, 서술자 힙이 필요하지 않다.
5. 좀 더 복잡한 루트 서명 예제
다음과 같은 셰이더가 있다고 하자.
Texture2D gDiffuseMap : register(t0);
cbuffer cbPerObject : register(b0)
{
float4x4 gWorld;
float4x4 gTexTransform;
};
cbuffer cbPass : register(b1)
{
float4x4 gView;
float4x4 gInvView;
float4x4 gProj;
float4x4 gInvProj;
float4x4 gViewProj;
float4x4 gInvViewProj;
float3 gEyePosW;
float cbPerObjectPad1;
float2 gRenderTargetSize;
float2 gInvRenderTargetSize;
float gNearZ;
float gFarZ;
float gTotalTime;
float gDeltaTime;
float4 gAmbientLight;
Light gLights[MaxLights];
};
cbuffer cbMaterial : register(b2)
{
float4 gDiffuseAlbedo;
float3 gFresnelR0;
float gRoughness;
float4x4 gMatTransform;
};
이 셰이더를 위한 루트 서명은 다음과 같을 것이다.
CD3DX12_DESCRIPTOR_RANGE texTable;
texTable.Init(
D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
1, // 서술자 개수
0); // 레지스터(t0)
CD3DX12_ROOT_PARAMETER slotRootParam[4];
slotRootParam[0].InitAsDescriptorTable(1, &texTable, D3D12_SHADER_VISIBILITY_PIXEL);
slotRootParam[1].InitAsConstantBufferView(0);
slotRootParam[2].InitAsConstantBufferView(1);
slotRootParam[3].InitAsConstantBufferView(2);
CD3DX12_ROOT_SIGNATURE_DESC rootSignature(4, slotRootParam, 0, nullptr);
6. 루트 인수의 버전 적용
응용 프로그램이 루트 매개변수에 실제로 전달하는 값을 루트 인수(root argument)라고 한다. 다음은 그리기 호출에서 루트 인수들을 변경하는 예시이다.
for(size_t i = 0; i < mRitem.size(); ++i)
{
...
...
// 현 프레임 자원과 렌더 항목을 위한 CBV 오프셋
int cbvOffset = mCurrFrameResourceIndex*(int)mRitem.size();
cbvOffset += ri.Cbindex;
CbvHandle.Offset(cbvOffset, mCbvSrvDecriptorSize);
// 이 그리기 호출에서 사용할 서술자 지정
cmdList->SetGraphicsRootDescriptorTable(0, cbvTable);
cmdList->DrawIndexedInstanced(
ri->IndexCount,
1,
ri->StartIndexLocation,
ri->BaseVertexLocation,
0);
}
각 명령은 해당 그리기 호출 시점에서 설정되어 있던 루트 인수들로 실행된다. 이는 하드웨어가 각 그리기 호출 시점에서의 루트 인수들의 상태('스냅숏')를 자동으로 저장하기 때문이다. 다른 말로, 그리기 호출마다 루트 인수들의 고유한 버전이 만들어 진다.