루트 서명은 중첩된 구조를 포함하는 복잡한 데이터 구조입니다. 아래 데이터 구조 정의(멤버를 초기화하는 데 도움이 되는 메서드 포함)를 사용하여 프로그래밍 방식으로 정의할 수 있습니다. 또는 HLSL(High Level Shading Language)으로 작성할 수 있습니다. 컴파일러가 레이아웃이 셰이더와 호환된다는 유효성을 조기에 확인할 수 있다는 장점이 있습니다.
루트 서명을 만들기 위한 API는 아래에 설명된 레이아웃 설명의 직렬화된(자체 포함, 포인터 없는) 버전을 사용합니다. C++ 데이터 구조에서 이 직렬화된 버전을 생성하는 메서드가 제공되지만 직렬화된 루트 서명 정의를 가져오는 또 다른 방법은 루트 서명으로 컴파일된 셰이더에서 검색하는 것입니다.
루트 서명 설명자 및 데이터에 대한 드라이버 최적화를 활용하려면 루트 서명 버전 1.1 참조하세요.
- 설명자 테이블 바인딩 형식
- 설명자 범위
- 디스크립터 테이블 레이아웃
- 루트 상수
- 루트 설명자
- 셰이더 가시성
- 루트 서명 정의
- 루트 서명 데이터 구조 직렬화/역직렬화
- 루트 서명 만들기 API
- 파이프라인 상태 개체에서의 루트 서명
- 버전 1.1 루트 서명 정의하기 위한 코드
- 관련 항목
설명자 테이블의 바인드 유형
열거형 D3D12_DESCRIPTOR_RANGE_TYPE는 설명자 테이블 레이아웃 정의의 일부로 참조할 수 있는 설명자 유형을 정의합니다.
예를 들어 설명자 테이블의 일부에 SRV가 100개 있는 경우 해당 범위를 100이 아닌 하나의 항목으로 선언할 수 있도록 하는 범위입니다. 따라서 설명자 테이블 정의는 범위의 컬렉션입니다.
typedef enum D3D12_DESCRIPTOR_RANGE_TYPE
{
D3D12_DESCRIPTOR_RANGE_TYPE_SRV,
D3D12_DESCRIPTOR_RANGE_TYPE_UAV,
D3D12_DESCRIPTOR_RANGE_TYPE_CBV,
D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER
} D3D12_DESCRIPTOR_RANGE_TYPE;
설명자 범위
D3D12_DESCRIPTOR_RANGE 구조체는 설명자 테이블 내에서 지정된 형식(예: SRV)의 설명자 범위를 정의합니다.
D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND
매크로는 일반적으로 D3D12_DESCRIPTOR_RANGEOffsetInDescriptorsFromTableStart
매개 변수에 사용할 수 있습니다. 즉, 설명자 테이블의 이전 설명자 범위 이후에 정의되는 설명자 범위를 추가합니다. 애플리케이션에서 설명자를 별칭으로 지정하거나 어떤 이유로 슬롯을 건너뛰려는 경우, OffsetInDescriptorsFromTableStart
을 원하는 오프셋으로 자유롭게 설정할 수 있습니다. 서로 다른 형식의 겹치는 범위를 정의하는 것은 유효하지 않습니다.
RangeType
, NumDescriptors
, BaseShaderRegister
및 RegisterSpace
로 지정한 셰이더 레지스터 집합은 공통 D3D12_SHADER_VISIBILITY 있는 루트 서명의 어떠한 선언에서도 상호 겹치거나 충돌할 수 없습니다 (아래 셰이더 표시 유형 섹션 참조).
설명자 테이블 레이아웃
D3D12_ROOT_DESCRIPTOR_TABLE 구조체는 설명자 테이블의 레이아웃을 설명자 힙의 지정된 오프셋에서 시작하는 설명자 범위의 컬렉션으로 선언합니다. 샘플러가 CBV/UAV/SRV와 동일한 설명자 테이블에서 허용되지 않습니다.
이 구조체는 루트 서명 슬롯 형식이 D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE
설정되면 사용됩니다.
그래픽(CBV, SRV, UAV, Sampler) 설명자 테이블을 설정하려면 ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable사용합니다.
컴퓨팅 설명자 테이블을 설정하려면 ID3D12GraphicsCommandList::SetComputeRootDescriptorTable사용합니다.
루트 상수
D3D12_ROOT_CONSTANTS 구조체는 셰이더에 하나의 상수 버퍼로 표시되는 루트 서명에서 상수 인라인을 선언합니다.
이 구조체는 루트 서명 슬롯 형식이 D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS
설정되면 사용됩니다.
루트 디스크립터
D3D12_ROOT_DESCRIPTOR 구조체는 루트 서명에서 설명자(셰이더에 표시됨)를 인라인으로 선언합니다.
이 구조체는 루트 서명 슬롯 형식이 D3D12_ROOT_PARAMETER_TYPE_CBV
, D3D12_ROOT_PARAMETER_TYPE_SRV
또는 D3D12_ROOT_PARAMETER_TYPE_UAV
때 사용됩니다.
셰이더 가시성
D3D12_ROOT_PARAMETER의 셰이더 가시성 매개 변수에 설정된 D3D12_SHADER_VISIBILITY 열거형의 멤버는 특정 루트 서명 슬롯의 내용을 볼 수 있는 셰이더를 결정합니다. 컴퓨팅은 항상 _ALL 사용합니다(활성 단계가 하나뿐이므로). 그래픽은 선택할 수 있지만 _ALL 사용하는 경우 모든 셰이더 단계에서 루트 서명 슬롯에 바인딩된 내용을 볼 수 있습니다.
셰이더 가시성의 한 가지 사용은 겹치는 네임스페이스를 사용하여 셰이더 단계당 다른 바인딩을 예상하여 작성되는 셰이더를 돕는 것입니다. 예를 들어 꼭짓점 셰이더는 다음을 선언할 수 있습니다.
Texture2D foo : register(t0);
픽셀 셰이더는 다음을 선언할 수도 있습니다.
Texture2D bar : register(t0);
애플리케이션이 t0 VISIBILITY_ALL 루트 서명 바인딩을 만드는 경우 두 셰이더 모두 동일한 질감을 볼 수 있습니다. 셰이더가 실제로 각 셰이더에 서로 다른 텍스처를 표시하도록 정의하는 경우 VISIBILITY_VERTEX 및 _PIXEL 사용하여 2개의 루트 서명 슬롯을 정의할 수 있습니다. 루트 서명 슬롯의 표시 유형에 관계없이 고정된 최대 루트 서명 크기 하나에 대해 항상 동일한 비용(SlotType이 무엇인지에 따라 비용만)을 가집니다.
저사양 D3D11 하드웨어에서는 일부 D3D11 하드웨어가 스테이지당 최대 바인딩 수만 지원할 수 있으므로, 루트 레이아웃에서 설명자 테이블의 크기를 확인할 때 SHADER_VISIBILITY도 고려됩니다. 이러한 제한은 하위 계층 하드웨어에서 실행되는 경우에만 적용되며 최신 하드웨어를 전혀 제한하지 않습니다.
루트 서명에 네임스페이스(셰이더에 대한 레지스터 바인딩)에서 서로 겹치는 여러 설명자 테이블이 정의되어 있고 그 중 하나가 표시 여부를 위해 _ALL 지정하는 경우 레이아웃이 유효하지 않습니다(생성 실패).
루트 서명 정의
D3D12_ROOT_SIGNATURE_DESC 구조체에는 설명자 테이블과 인라인 상수, D3D12_ROOT_PARAMETER 구조체로 정의된 각 슬롯 형식 및 열거형 D3D12_ROOT_PARAMETER_TYPE포함될 수 있습니다.
루트 서명 슬롯을 시작하려면 ID3D12GraphicsCommandListSetComputeRoot*** 및 SetGraphicsRoot*** 메서드를 참조하세요.
정적 샘플러가 D3D12_STATIC_SAMPLER 구조를 사용하여 루트 서명에 설명되어 있습니다.
여러 플래그는 특정 셰이더의 루트 서명에 대한 액세스를 제한합니다. D3D12_ROOT_SIGNATURE_FLAGS를 참조하십시오.
루트 서명 데이터 구조 직렬화/역직렬화
이 섹션에서 설명하는 메서드는 D3D12Core.dll 내보내며 루트 서명 데이터 구조를 직렬화하고 역직렬화하는 메서드를 제공합니다.
직렬화된 양식은 루트 서명을 만들 때 API에 전달됩니다. 셰이더가 루트 서명으로 작성된 경우(해당 기능이 추가될 때) 컴파일된 셰이더에는 이미 직렬화된 루트 서명이 포함됩니다.
애플리케이션에서 D3D12_ROOT_SIGNATURE_DESC 데이터 구조를 프로시저 방식으로 생성하는 경우 D3D12SerializeRootSignature사용하여 직렬화된 형식을 만들어야 합니다. 해당 출력은 ID3D12Device::CreateRootSignature에 전달될 수 있습니다.
애플리케이션에 직렬화된 루트 서명이 이미 있거나 루트 서명을 포함하고 프로그래밍 방식으로 레이아웃 정의("리플렉션"이라고 함)를 검색하려는 컴파일된 셰이더가 있는 경우 D3D12CreateRootSignatureDeserializer 호출할 수 있습니다. 이렇게 하면 역직렬화된 D3D12_ROOT_SIGNATURE_DESC 데이터 구조를 반환하는 메서드가 포함된 ID3D12RootSignatureDeserializer 인터페이스가 생성됩니다. 인터페이스는 역직렬화된 데이터 구조의 수명을 소유합니다.
루트 서명 만들기 API
ID3D12Device::CreateRootSignature API는 직렬화된 버전의 루트 서명을 사용합니다.
파이프라인 상태 개체의 루트 서명
파이프라인 상태(ID3D12Device::CreateGraphicsPipelineState 및 ID3D12Device::CreateComputePipelineState)를 만드는 메서드는 선택적 ID3D12RootSignature 인터페이스를 입력 매개 변수로 사용합니다(D3D12_GRAPHICS_PIPELINE_STATE_DESC 구조에 저장됨). 이 작업은 셰이더에 이미 있는 루트 서명을 재정의하게 됩니다.
루트 서명이 만들기 파이프라인 상태 메서드 중 하나에 전달되는 경우 이 루트 서명은 호환성을 위해 PSO의 모든 셰이더에 대해 유효성을 검사하고 드라이버에 모든 셰이더와 함께 사용할 수 있도록 지정됩니다. 셰이더에 다른 루트 서명이 있는 경우 API에 전달된 루트 서명으로 대체됩니다. 루트 서명이 전달되지 않으면 전달된 모든 셰이더에 루트 서명이 있어야 하며 일치해야 합니다. 이 서명은 드라이버에 제공됩니다. 명령 목록 또는 번들에 PSO를 설정해도 루트 서명은 변경되지 않습니다. 이는 SetGraphicsRootSignature메서드와 SetComputeRootSignature메서드에 의해 수행됩니다. 그리기(그래픽)/디스패치(컴퓨팅)가 호출될 때 애플리케이션은 현재 PSO가 현재 루트 서명과 일치하는지 확인해야 합니다. 그렇지 않으면 동작이 정의되지 않습니다.
버전 1.1 루트 서명을 정의하기 위한 코드
아래 예제에서는 다음 형식으로 루트 서명을 만드는 방법을 보여줍니다.
루트매개변수인덱스 | 목차 | 가치 |
---|---|---|
[0] | 루트 상수: { b2 } | (1 CBV) |
[1] | 설명자 테이블: { t2-t7, u0-u3 } | (SRV 6대 + UAV 4대) |
[2] | 루트 CBV: { b0 } | (1 CBV, 정적 데이터) |
[3] | 설명자 테이블: { s0-s1 } | (샘플러 2명) |
[4] | 설명자 테이블: { t8 - 바인딩되지 않음 } | (무제한 수의 SRV, 휘발성 설명자) |
[5] | 설명자 테이블: { (t0, space1) - 무한대 } | (무제한의 SRV 수, 휘발성 설명자) |
[6] | 설명자 테이블: { b1 } | (1 CBV, 정적 데이터) |
루트 서명의 대부분의 부분이 대부분의 시간을 사용하는 경우 루트 서명을 너무 자주 전환해야 하는 것보다 더 좋을 수 있습니다. 애플리케이션은 루트 서명의 항목을 가장 자주 변경에서 최소로 정렬해야 합니다. 앱이 바인딩을 루트 서명의 모든 부분으로 변경하는 경우 드라이버는 루트 서명 상태의 일부 또는 전부를 복사해야 할 수 있으며, 이는 여러 상태 변경 내용에 곱할 때 사소한 비용이 될 수 있습니다.
또한 루트 서명은 셰이더 레지스터 s3에서 이방성 텍스처 필터링을 수행하는 정적 샘플러를 정의합니다.
이 루트 서명이 바인딩되면 설명자 테이블, 루트 CBV 및 상수를 [0..6] 매개 변수 공간에 할당할 수 있습니다. 예를 들어 설명자 테이블(설명자 힙의 범위)은 각 루트 매개 변수 [1] 및 [3..6]에 바인딩할 수 있습니다.
CD3DX12_DESCRIPTOR_RANGE1 DescRange[6];
DescRange[0].Init(D3D12_DESCRIPTOR_RANGE_SRV,6,2); // t2-t7
DescRange[1].Init(D3D12_DESCRIPTOR_RANGE_UAV,4,0); // u0-u3
DescRange[2].Init(D3D12_DESCRIPTOR_RANGE_SAMPLER,2,0); // s0-s1
DescRange[3].Init(D3D12_DESCRIPTOR_RANGE_SRV,-1,8, 0,
D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE); // t8-unbounded
DescRange[4].Init(D3D12_DESCRIPTOR_RANGE_SRV,-1,0,1,
D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE);
// (t0,space1)-unbounded
DescRange[5].Init(D3D12_DESCRIPTOR_RANGE_CBV,1,1,
D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); // b1
CD3DX12_ROOT_PARAMETER1 RP[7];
RP[0].InitAsConstants(3,2); // 3 constants at b2
RP[1].InitAsDescriptorTable(2,&DescRange[0]); // 2 ranges t2-t7 and u0-u3
RP[2].InitAsConstantBufferView(0, 0,
D3D12_ROOT_DESCRIPTOR_FLAG_DATA_STATIC); // b0
RP[3].InitAsDescriptorTable(1,&DescRange[2]); // s0-s1
RP[4].InitAsDescriptorTable(1,&DescRange[3]); // t8-unbounded
RP[5].InitAsDescriptorTable(1,&DescRange[4]); // (t0,space1)-unbounded
RP[6].InitAsDescriptorTable(1,&DescRange[5]); // b1
CD3DX12_STATIC_SAMPLER StaticSamplers[1];
StaticSamplers[0].Init(3, D3D12_FILTER_ANISOTROPIC); // s3
CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC RootSig(7,RP,1,StaticSamplers);
ID3DBlob* pSerializedRootSig;
CheckHR(D3D12SerializeVersionedRootSignature(&RootSig,pSerializedRootSig));
ID3D12RootSignature* pRootSignature;
hr = CheckHR(pDevice->CreateRootSignature(
pSerializedRootSig->GetBufferPointer(),pSerializedRootSig->GetBufferSize(),
__uuidof(ID3D12RootSignature),
&pRootSignature));
다음 코드에서는 그래픽 명령 목록에서 위의 루트 서명을 사용하는 방법을 보여 줍니다.
InitializeMyDescriptorHeapContentsAheadOfTime(); // for simplicity of the
// example
CreatePipelineStatesAhreadOfTime(pRootSignature); // The root signature is passed into
// shader / pipeline state creation
...
ID3D12DescriptorHeap* pHeaps[2] = {pCommonHeap, pSamplerHeap};
pGraphicsCommandList->SetDescriptorHeaps(2,pHeaps);
pGraphicsCommandList->SetGraphicsRootSignature(pRootSignature);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
6,heapOffsetForMoreData,DescRange[5].NumDescriptors);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(5,heapOffsetForMisc,5000);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(4,heapOffsetForTerrain,20000);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
3,heapOffsetForSamplers,DescRange[2].NumDescriptors);
pGraphicsCommandList->SetComputeRootConstantBufferView(2,pDynamicCBHeap,&CBVDesc);
MY_PER_DRAW_STUFF stuff;
InitMyPerDrawStuff(&stuff);
pGraphicsCommandList->SetGraphicsRoot32BitConstants(
0,RTSlot[0].Constants.Num32BitValues,&stuff,0);
SetMyRTVAndOtherMiscBindings();
for(UINT i = 0; i < numObjects; i++)
{
pGraphicsCommandList->SetPipelineState(PSO[i]);
pGraphicsCommandList->SetGraphicsRootDescriptorTable(
1,heapOffsetForFooAndBar[i],DescRange[1].NumDescriptors);
pGraphicsCommandList->SetGraphicsRoot32BitConstant(0,i,drawIDOffset);
SetMyIndexBuffers(i);
pGraphicsCommandList->DrawIndexedInstanced(...);
}
관련 항목
-
루트 서명 사용하기