Erstellen einer Stammsignatur

Stammsignaturen sind eine komplexe Datenstruktur, die geschachtelte Strukturen enthält. Diese können programmgesteuert mithilfe der folgenden Datenstrukturdefinition definiert werden (einschließlich Methoden zur Initialisierung von Membern). Alternativ können sie in High Level Shading Language (HLSL) erstellt werden. Dies bietet den Vorteil, dass der Compiler frühzeitig überprüft, ob das Layout mit dem Shader kompatibel ist.

Die API zum Erstellen einer Stammsignatur übernimmt eine serialisierte (eigenständige, zeigerfreie) Version der unten beschriebenen Layoutbeschreibung. Es wird eine Methode zum Generieren dieser serialisierten Version aus der C++-Datenstruktur bereitgestellt, aber eine andere Möglichkeit zum Abrufen einer serialisierten Stammsignaturdefinition besteht darin, sie aus einem Shader abzurufen, der mit einer Stammsignatur kompiliert wurde.

Wenn Sie die Treiberoptimierungen für Stammsignaturdeskriptoren und -daten nutzen möchten, lesen Sie Die Stammsignaturversion 1.1.

Deskriptortabellenbindungstypen

Die Enumeration D3D12_DESCRIPTOR_RANGE_TYPE definiert die Deskriptorentypen, auf die als Teil einer Deskriptortabellenlayoutdefinition verwiesen werden kann.

Es handelt sich um einen Bereich, sodass beispielsweise, wenn ein Teil einer Deskriptortabelle über 100 SRVs verfügt, dieser Bereich in einem Eintrag anstelle von 100 deklariert werden kann. Eine Deskriptortabellendefinition ist also eine Auflistung von Bereichen.

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;

Deskriptorbereich

Die D3D12_DESCRIPTOR_RANGE-Struktur definiert einen Bereich von Deskriptoren eines bestimmten Typs (z. B. SRVs) innerhalb einer Deskriptortabelle.

Das D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND Makro kann in der Regel für den OffsetInDescriptorsFromTableStart Parameter von D3D12_DESCRIPTOR_RANGE verwendet werden. Dies bedeutet, dass der Deskriptorbereich angefügt wird, der nach dem vorherigen in der Deskriptortabelle definiert ist. Wenn die Anwendung Deskriptoren aliasen möchte oder aus irgendeinem Grund Slots überspringen möchte, kann sie den gewünschten Offset festlegen OffsetInDescriptorsFromTableStart . Das Definieren überlappender Bereiche verschiedener Typen ist ungültig.

Der Satz von Shaderregistern, die durch die Kombination von RangeType, NumDescriptors, BaseShaderRegisterund RegisterSpace angegeben werden, darf keine Deklarationen in einer Stammsignatur mit gemeinsamen D3D12_SHADER_VISIBILITY in Konflikt stehen oder überlappen (siehe Abschnitt Shadersichtbarkeit unten).

Deskriptortabellenlayout

Die D3D12_ROOT_DESCRIPTOR_TABLE-Struktur deklariert das Layout einer Deskriptortabelle als Auflistung von Deskriptorbereichen, die bei einem bestimmten Offset eines Deskriptorheaps beginnen. Sampler sind in derselben Deskriptortabelle wie CBV/UAV/SRVs nicht zulässig.

Diese Struktur wird verwendet, wenn der Stammsignaturslottyp auf D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLEfestgelegt ist.

Verwenden Sie ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable, um eine Deskriptortabelle für Grafiken (CBV, SRV, UAV, Sampler) festzulegen.

Um eine Computedeskriptortabelle festzulegen, verwenden Sie ID3D12GraphicsCommandList::SetComputeRootDescriptorTable.

Stammkonstanten

Die D3D12_ROOT_CONSTANTS-Struktur deklariert Konstanten inline in der Stammsignatur, die in Shadern als einen Konstantenpuffer angezeigt werden.

Diese Struktur wird verwendet, wenn der Stammsignaturslottyp auf D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTSfestgelegt ist.

Stammdeskriptor

Die D3D12_ROOT_DESCRIPTOR-Struktur deklariert Deskriptoren (die in Shadern angezeigt werden) inline in der Stammsignatur.

Diese Struktur wird verwendet, wenn der Stammsignaturslottyp auf D3D12_ROOT_PARAMETER_TYPE_CBVoder D3D12_ROOT_PARAMETER_TYPE_SRVD3D12_ROOT_PARAMETER_TYPE_UAVfestgelegt ist.

Shadersicht

Das Element von D3D12_SHADER_VISIBILITY Enumeration, die im Shadersichtparameter von D3D12_ROOT_PARAMETER festgelegt ist, bestimmt, welche Shader den Inhalt eines bestimmten Stammsignaturslots sehen. Compute verwendet immer _ALL (da es nur eine aktive Phase gibt). Grafiken können auswählen, aber wenn sie _ALL verwendet, sehen alle Shaderstufen, was am Stammsignaturslot gebunden ist.

Eine Verwendung der Shadersichtbarkeit besteht darin, Shader zu unterstützen, die mit einem überlappenden Namespace unterschiedliche Bindungen pro Shaderstufe erwarten. Ein Vertex-Shader kann beispielsweise Folgendes deklarieren:

Texture2D foo : register(t0);

und der Pixel-Shader kann auch Folgendes deklarieren:

Texture2D bar : register(t0);

Wenn die Anwendung eine Stammsignaturbindung an t0 VISIBILITY_ALL vornimmt, sehen beide Shader dieselbe Textur. Wenn der Shader definiert, dass jeder Shader verschiedene Texturen sieht, kann er 2 Stammsignaturslots mit VISIBILITY_VERTEX und _PIXEL definieren. Unabhängig davon, wie die Sichtbarkeit eines Stammsignaturslots ist, hat er immer die gleichen Kosten (kosten nur je nach SlotType) für eine feste maximale Stammsignaturgröße.

Bei Low-End-D3D11-Hardware wird SHADER_VISIBILITY auch berücksichtigt, wenn die Größen von Deskriptortabellen in einem Stammlayout überprüft werden, da einige D3D11-Hardware nur eine maximale Anzahl von Bindungen pro Stufe unterstützen kann. Diese Einschränkungen werden nur auferlegt, wenn sie auf Hardware mit niedriger Ebene ausgeführt werden, und schränken modernere Hardware überhaupt nicht ein.

Wenn für eine Stammsignatur mehrere Deskriptortabellen definiert sind, die sich im Namespace überlappen (die Registerbindungen an den Shader) und eine davon _ALL für die Sichtbarkeit angibt, ist das Layout ungültig (die Erstellung schlägt fehl).

Stammsignaturdefinition

Die D3D12_ROOT_SIGNATURE_DESC-Struktur kann Deskriptortabellen und Inlinekonstanten, jeden Slottyp enthalten, der durch die D3D12_ROOT_PARAMETER-Struktur und die Enumeration D3D12_ROOT_PARAMETER_TYPE definiert wird.

Informationen zum Initiieren eines Stammsignaturslots finden Sie in den Methoden SetComputeRoot*** und SetGraphicsRoot*** von ID3D12GraphicsCommandList.

Statische Sampler werden in der Stammsignatur mit der D3D12_STATIC_SAMPLER-Struktur beschrieben.

Eine Reihe von Flags schränkt den Zugriff bestimmter Shader auf die Stammsignatur ein, siehe D3D12_ROOT_SIGNATURE_FLAGS.

Stammsignaturdatenstrukturserialisierung/Deserialisierung

Die in diesem Abschnitt beschriebenen Methoden werden von D3D12Core.dll exportiert und bieten Methoden zum Serialisieren und Deserialisieren einer Stammsignaturdatenstruktur.

Das serialisierte Formular wird beim Erstellen einer Stammsignatur an die API übergeben. Wenn ein Shader mit einer Stammsignatur erstellt wurde (wenn diese Funktion hinzugefügt wird), enthält der kompilierte Shader bereits eine serialisierte Stammsignatur.

Wenn eine Anwendung prozedural eine D3D12_ROOT_SIGNATURE_DESC Datenstruktur generiert, muss sie das serialisierte Formular mithilfe von D3D12SerializeRootSignature erstellen. Die Ausgabe davon kann an ID3D12Device::CreateRootSignature übergeben werden.

Wenn eine Anwendung bereits über eine serialisierte Stammsignatur verfügt oder über einen kompilierten Shader verfügt, der eine Stammsignatur enthält und die Layoutdefinition programmgesteuert ermitteln möchte (als "Reflektion" bezeichnet), kann D3D12CreateRootSignatureDeserializer aufgerufen werden. Dadurch wird eine ID3D12RootSignatureDeserializer-Schnittstelle generiert, die eine Methode zum Zurückgeben der deserialisierten D3D12_ROOT_SIGNATURE_DESC Datenstruktur enthält. Die Schnittstelle besitzt die Lebensdauer der deserialisierten Datenstruktur.

API zum Erstellen von Stammsignaturen

Die ID3D12Device::CreateRootSignature-API übernimmt eine serialisierte Version einer Stammsignatur.

Stammsignatur in Pipelinestatusobjekten

Die Methoden zum Erstellen des Pipelinestatus (ID3D12Device::CreateGraphicsPipelineState und ID3D12Device::CreateComputePipelineState ) verwenden eine optionale ID3D12RootSignature-Schnittstelle als Eingabeparameter (gespeichert in einer D3D12_GRAPHICS_PIPELINE_STATE_DESC-Struktur ). Dadurch werden alle Stammsignaturen überschrieben, die bereits in den Shadern enthalten sind.

Wenn eine Stammsignatur an eine der Methoden zum Erstellen des Pipelinezustands übergeben wird, wird diese Stammsignatur mit allen Shadern im PSO auf Kompatibilität überprüft und dem Treiber zur Verwendung mit allen Shadern zugewiesen. Wenn einer der Shader eine andere Stammsignatur enthält, wird sie durch die Stammsignatur ersetzt, die an der API übergeben wird. Wenn eine Stammsignatur nicht übergeben wird, müssen alle übergebenen Shader über eine Stammsignatur verfügen, und sie müssen übereinstimmen . Dies wird dem Treiber zugewiesen. Das Festlegen eines PSO für eine Befehlsliste oder ein Bundle ändert die Stammsignatur nicht. Dies wird durch die Methoden SetGraphicsRootSignature und SetComputeRootSignature erreicht. Bis zum Aufruf von draw(graphics)/dispatch(compute) muss die Anwendung sicherstellen, dass der aktuelle PSO mit der aktuellen Stammsignatur übereinstimmt. andernfalls ist das Verhalten nicht definiert.

Code zum Definieren einer Stammsignatur der Version 1.1

Das folgende Beispiel zeigt, wie Sie eine Stammsignatur im folgenden Format erstellen:

RootParameterIndex Inhalte Werte
[0] Stammkonstanten: { b2 } (1 CBV)
[1] Deskriptortabelle: { t2-t7, u0-u3 } (6 SRVs + 4 UAVs)
[2] Stamm-CBV: { b0 } (1 CBV, statische Daten)
[3] Deskriptortabelle: { s0-s1 } (2 Sampler)
[4] Deskriptortabelle: { t8 - unbounded } (ungebundene Anzahl von SRVs, flüchtige Deskriptoren)
[5] Deskriptortabelle: { (t0, space1) - unbounded } (ungebundene Anzahl von SRVs, flüchtige Deskriptoren)
[6] Deskriptortabelle: { b1 } (1 CBV, statische Daten)

 

Wenn die meisten Teile der Stammsignatur die meiste Zeit verwendet werden, kann es besser sein, als die Stammsignatur zu häufig wechseln zu müssen. Anwendungen sollten Einträge in der Stammsignatur von der am häufigsten in die geringste Änderung sortieren. Wenn eine App die Bindungen in einen beliebigen Teil der Stammsignatur ändert, muss der Treiber möglicherweise eine Kopie eines teils oder des gesamten Stammsignaturzustands erstellen, was zu nichttriviellen Kosten werden kann, wenn er über viele Zustandsänderungen multipliziert wird.

Darüber hinaus definiert die Stammsignatur einen statischen Sampler, der anisotrope Texturfilterung beim Shaderregister s3 ausführt.

Nachdem diese Stammsignatur gebunden ist, können Deskriptortabellen, Stamm-CBV und Konstanten dem Parameterbereich [0..6] zugewiesen werden. z. B. Deskriptortabellen (Bereiche in einem Deskriptorheap) können an jeden der Stammparameter [1] und [3..6] gebunden werden.

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));

Der folgende Code veranschaulicht, wie die obige Stammsignatur in einer Grafikbefehlsliste verwendet werden kann.

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(...);
}

Stammsignaturen

Festlegen von Stammsignaturen in HLSL

Verwenden einer Stammsignatur