共用方式為


建立根簽章

根簽章是包含巢狀結構的複雜數據結構。 您可以使用下列數據結構定義,以程式設計方式定義這些定義(其中包括可協助初始化成員的方法)。 或者,它們可以以高階底紋語言 (HLSL) 撰寫– 讓編譯程式能儘早驗證配置與著色器相容的優點。

用來建立根簽章的 API 會使用下列版面配置描述的經過串行化的(自我封閉、無指標)版本。 方法可用來從C++數據結構產生這個串行化版本,但另一個取得串行化根簽章定義的方法是從已使用根簽章編譯的著色器中擷取它。

如果您希望善用驅動程式對根簽章描述子和數據的最佳化,請參閱 根簽章版本 1.1

描述項數據表系結類型

列舉 D3D12_DESCRIPTOR_RANGE_TYPE 會定義描述元的類型,這些描述項可做為描述項數據表配置定義的一部分參考。

這是一個範圍,例如,如果描述項數據表的一部分有100個SRV,該範圍可以在一個專案中宣告,而不是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 結構用於定義描述元表中某個指定型別(例如 SRVs)的描述元範圍。

D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND 巨集通常可用於 D3D12_DESCRIPTOR_RANGEOffsetInDescriptorsFromTableStart 參數。 這表示將所定義的描述元範圍附加在描述符表中前一個範圍之後。 如果應用程式想要設定描述符別名,或基於某些原因想要略過插槽,它可以將 OffsetInDescriptorsFromTableStart 設定為所需的任何位移。 定義不同類型的重疊範圍無效。

RangeTypeNumDescriptorsBaseShaderRegisterRegisterSpace 組合所指定的著色器寄存器集合,不可在根簽名中與任何具有共同 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_CBVD3D12_ROOT_PARAMETER_TYPE_SRVD3D12_ROOT_PARAMETER_TYPE_UAV時,會使用此結構。

著色器可見性

D3D12_ROOT_PARAMETER 著色器可見度參數中的 D3D12_SHADER_VISIBILITY 列舉成員會決定哪些著色器能夠看到指定根簽章插槽的內容。 計算一律使用_ALL(因為只有一個活躍階段)。 繪圖可以自行選擇,但如果使用_ALL,所有著色器階段都會看到系結在根簽名位置的所有內容。

著色器可見性的其中一個用法是協助管理預期使用重疊的命名空間的著色器,這些著色器在不同的著色器階段會有不同的綁定。 例如,頂點著色器可能會宣告:

Texture2D foo : register(t0);

像素著色器也可以宣告:

Texture2D bar : register(t0);

如果應用程式將根簽章系結至 t0 VISIBILITY_ALL,則兩個著色器都會看到相同的紋理。 如果著色器確實希望每個著色器都能看到不同的紋理,可以定義 2 個根簽署插槽,分別使用 VISIBILITY_VERTEX 和 _PIXEL。 無論根簽章位置上的可見度為何,它一律會有相同的成本(僅視 SlotType 是什麼成本而定),以達到一個固定的根簽章大小上限。

在低端 D3D11 硬體上,SHADER_VISIBILITY 也會在驗證根佈局中描述符表的大小時使用,因為某些 D3D11 硬體只能支援每個階段的系結數量上限。 只有在低層硬體上執行時,才會施加這些限制,而且完全不會限制更多新式硬體。

如果根簽章已定義多個描述元數據表,這些數據表在命名空間中彼此重疊(著色器的緩存器系結),且其中任何一個都指定_ALL可見性,則配置無效(建立將會失敗)。

根簽章定義

D3D12_ROOT_SIGNATURE_DESC 結構可以包含描述符表和內嵌常數,每個槽位類型由 D3D12_ROOT_PARAMETER 結構和枚舉 D3D12_ROOT_PARAMETER_TYPE定義。

若要啟用根簽章位置,請參閱 ID3D12GraphicsCommandList中的 SetComputeRoot***SetGraphicsRoot*** 方法。

靜態取樣器會使用 D3D12_STATIC_SAMPLER 結構在根簽章中描述。

許多旗標會限制特定著色器存取根簽章,請參閱 D3D12_ROOT_SIGNATURE_FLAGS

根簽章數據結構串行化/還原串行化

本節所述的方法由 D3D12Core.dll 匯出,並提供序列化和反序列化根簽章資料結構的方法。

串行化表單是在建立根簽章時傳遞至 API 的內容。 如果著色器包含根簽名(當功能已被新增時),那麼編譯的著色器會已經包含串行化的根簽名。

如果應用程式在程式上產生 D3D12_ROOT_SIGNATURE_DESC 數據結構,則必須使用 D3D12SerializeRootSignature來建立串行化表單。 的輸出可以傳遞至 ID3D12Device::CreateRootSignature

如果應用程式已經有序列化的根簽章,或是具有包含根簽章的已編譯著色器,並且希望程序化地探索其版面配置定義(稱為「反射」),可以呼叫 D3D12CreateRootSignatureDeserializer。 這會產生 ID3D12RootSignatureDeserializer 介面,其中包含傳回解序列化後的 D3D12_ROOT_SIGNATURE_DESC 資料結構的方法。 介面掌控反序列化資料結構的生命周期。

根簽章建立 API

ID3D12Device::CreateRootSignature API 會採用已串行化版本的根簽章。

管線狀態物件中的根簽章

建立管線狀態的方法(ID3D12Device::CreateGraphicsPipelineStateID3D12Device::CreateComputePipelineState)採用選擇性的 ID3D12RootSignature 介面作為輸入參數(儲存在 D3D12_GRAPHICS_PIPELINE_STATE_DESC 結構中)。 這會覆寫已在著色器中存在的任何根簽名。

如果根簽章傳遞至其中一個建立管線狀態方法,此根簽章會針對 PSO 中的所有著色器進行驗證,以取得相容性,並提供給驅動程式以搭配所有著色器使用。 如果任何著色器具有不同的根簽名,則會被API中傳入的根簽名取代。 如果未傳入根簽章,傳入的所有著色器都必須有根簽章,而且必須相符 - 這會提供給驅動程式。 在命令清單或套件組合上設定 PSO 並不會變更根簽章。 透過方法 SetGraphicsRootSignatureSetComputeRootSignature完成。 在叫用 draw(graphics)/dispatch(compute) 時,應用程式必須確保目前的 PSO 符合目前的根簽章:否則,行為是未定義的。

定義 1.1 版根簽章的程式代碼

下列範例示範如何使用下列格式建立根簽章:

RootParameterIndex 內容 價值觀
[0] 根常數: { b2 } (1 CBV)
[1] 描述項數據表: { t2-t7, u0-u3 } (6 SRV + 4 個UAV)
[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(...);
}

根簽章

在 HLSL 中指定根簽章

使用根簽章