前言

筆者也是初入圖形學坑的菜鳥,並不是甚麼資深程序員。如果有錯誤之處,請指出,筆者會立刻進行修改。

接觸DirectX 12時,有很多覺得困惑的地方,其中便是Descriptor(View)RootSignature的關係。筆者相信也有很多初學者有相同的疑問。所以借此篇博文,儘可能地解釋清楚兩者之間的關係。

Descriptor Heap & Descriptor & View

在說明RootSinature前,首先要理清Heap和Descriptor和View之間的關係。

Descriptor與View

在dx12中descriptor與View是等價的(請參閱這裏)。descriptor有

  • DSV (Depth Stencil View)
  • RTV (Render Target View)
  • SRV (Shader Resource View)
  • CBV (Constant Buffer View)
  • UAV (Unordered Access View)
  • Sampler

descriptor實際上是對buffer的描述,存儲了resource的地址以及大小。以CBV為例

1
2
3
4
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
cbvDesc.BufferLocation = cBuffer->GetGPUVirtualAddress ();
cbvDesc.SizeInBytes = objCBByteSize;
device->CreateConstantBufferView (&cbvDesc, cbvHeap->GetCPUDescriptorHandleForHeapStart ());

保存了constant_buffer的內存地址以及其大小。

Descriptor與Resource的關係

Descriptor Heap

Descriptor Heap是descriptor的堆。存放大量相同類型的descriptor。依然以CBV為例

1
2
3
4
5
6
D3D12_DESCRIPTOR_HEAP_DESC cbvHeapDesc;
cbvHeapDesc.NumDescriptors = 1;
cbvHeapDesc.NodeMask = 0;
cbvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvHeapDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
m_Device->CreateDescriptorHeap (&cbvHeapDesc, IID_PPV_ARGS (&m_CBVHeap));

這個Heap中保存一個Descriptor,其類型為CBV_SRV_UAV

Descriptor Heap

Why descriptor?

正如在Descriptor與View小節中提到,descriptor是對buffer的一種描述。buffer就是一堆數據,而這些數據可以有不同的用途,由descriptor解釋。例如一buffer作為渲染目標時,被descriptor解釋為RTV。得到的貼圖buffer可以再被解釋為SRV,作為Shader的資源使用。

Descriptor與RootSignature

Shader是一個大函數

為了能簡單地理解RootSiangature,我們將Shader看作是一個大函數,而RootSiganture則是說明了Shader函數的參數格式。

參數的類型有三種

  • Descriptor Table : Descriptor數組
  • Root Descriptor : 單一Descriptor
  • Root Constant : 常數

即,程序員可以為Shader函數準備: descriptor的數組、單一的descriptor、常數項的參數。以Descriptor Table為例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
D3D12_DESCRIPTOR_RANGE descTblRange = {};
descTblRange.NumDescriptors = 1;
descTblRange.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV;
descTblRange.BaseShaderRegister = 0;
descTblRange.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;

D3D12_ROOT_PARAMETER rootparam = {};
rootparam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
rootparam.DescriptorTable.pDescriptorRanges = &descTblRange;
rootparam.DescriptorTable.NumDescriptorRanges = 1;
rootparam.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;

D3D12_ROOT_SIGNATURE_DESC rootSigDesc = {};
rootSigDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
rootSigDesc.pParameters = &rootparam;
rootSigDesc.NumParameters = 1;

// build rootsignature...

// draw...

ID3D12DescriptorHeap* descriptorHeaps[] = {cbvHeap.Get ()};
cmdList->SetGraphicsRootSignature (rootSignature.Get ());
cmdList->SetDescriptorHeaps (_countof (descriptorHeaps), descriptorHeaps);
cmdList->SetGraphicsRootDescriptorTable (0, cbvHeap->GetGPUDescriptorHandleForHeapStart ());

// draw...

設置了一個descriptor數組作為參數,這個數組中存在一個CBV。在渲染時,利用命令將要使用的DescriptorHeap以及Heap的起始地址設置好便可。

總結

descriptor就是view,用於描述buffer的,給出了buffer的作用。而descriptor heap是保存descriptor的堆。rootsignature說明shader函數的參數格式。MSDN提供的圖片能更好地說明它們之間的關係

MSDN

MSDN

References