Condividi tramite


Creazione di una firma radice

Le "root signature" sono una struttura di dati complessa contenente strutture annidate. Questi elementi possono essere definiti a livello di codice usando la definizione della struttura dei dati riportata di seguito, che include metodi per inizializzare i membri. In alternativa, possono essere creati in High Level Shading Language (HLSL), offrendo il vantaggio che il compilatore convaliderà in anticipo che il layout è compatibile con lo shader.

L'API per la creazione di una firma radice accetta una versione serializzata (indipendente, senza puntatori) della descrizione del layout descritta di seguito. Viene fornito un metodo per generare questa versione serializzata dalla struttura di dati C++, ma un altro modo per ottenere una definizione di firma radice serializzata consiste nel recuperarlo da uno shader compilato con una firma radice.

Per sfruttare i vantaggi delle ottimizzazioni dei driver per i descrittori e i dati della firma radice, fare riferimento a Firma Radice Versione 1.1

Tipi di associazione delle tabelle dei descrittori

L'enumerazione D3D12_DESCRIPTOR_RANGE_TYPE definisce i tipi di descrittori a cui è possibile fare riferimento come parte di una definizione di layout di tabella descrittore.

Si tratta di un intervallo in modo che, ad esempio, se una parte di una tabella descrittore ha 100 SRV, tale intervallo può essere dichiarato in una voce anziché 100. Pertanto, una definizione di tabella descrittore è una raccolta di intervalli.

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;

Intervallo descrittore

La struttura D3D12_DESCRIPTOR_RANGE definisce un intervallo di descrittori di un determinato tipo (ad esempio SRV) all'interno di una tabella del descrittore.

La macro D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND può in genere essere utilizzata per il parametro OffsetInDescriptorsFromTableStart di D3D12_DESCRIPTOR_RANGE. Ciò significa aggiungere l'intervallo descrittore definito dopo quello precedente nella tabella descrittore. Se l'applicazione vuole aliasre i descrittori o per qualche motivo vuole ignorare gli slot, può impostare OffsetInDescriptorsFromTableStart su qualsiasi offset desiderato. La definizione di intervalli sovrapposti di tipi diversi non è valida.

Il set di registri shader specificati dalla combinazione di RangeType, NumDescriptors, BaseShaderRegistere RegisterSpace non può entrare in conflitto o sovrapporsi a qualsiasi dichiarazione in una firma radice con D3D12_SHADER_VISIBILITY comuni (vedere la sezione relativa alla visibilità dello shader riportata di seguito).

Layout della tabella dei descrittori

La struttura D3D12_ROOT_DESCRIPTOR_TABLE dichiara il layout di una tabella descrittore come raccolta di intervalli di descrittori che iniziano in corrispondenza di un determinato offset di un heap del descrittore. I campionatori non sono consentiti nella stessa tabella descrittore di CBV/UAV/SRV.

Questo struct viene utilizzato quando il tipo di slot della firma radice è impostato su D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE.

Per impostare una tabella di descrizione grafica (CBV, SRV, UAV, Sampler), usare ID3D12GraphicsCommandList::SetGraphicsRootDescriptorTable.

Per impostare una tabella del descrittore di calcolo, usare ID3D12GraphicsCommandList::SetComputeRootDescriptorTable.

Costanti radice

La struttura D3D12_ROOT_CONSTANTS dichiara costanti inline nella firma principale che appare negli shader come un unico buffer costante.

Questa struttura viene utilizzata quando il tipo di slot della firma radice è impostato su D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS.

Descrittore radice

La struttura D3D12_ROOT_DESCRIPTOR dichiara i descrittori (che compaiono negli shader) inline nella firma principale.

Questo struct viene usato quando il tipo di slot della firma radice è impostato su D3D12_ROOT_PARAMETER_TYPE_CBV, D3D12_ROOT_PARAMETER_TYPE_SRV o D3D12_ROOT_PARAMETER_TYPE_UAV.

Visibilità shader

Il membro di D3D12_SHADER_VISIBILITY enumerazione impostata nel parametro di visibilità dello shader di D3D12_ROOT_PARAMETER determina quali shader vedono il contenuto di un determinato slot di firma radice. Il calcolo usa sempre _ALL (poiché è presente una sola fase attiva). La grafica può scegliere, ma se usa _ALL, tutte le fasi dello shader vedono qualsiasi elemento associato allo slot della firma radice.

Un uso della visibilità dello shader consiste nell'aiutare gli shader creati a prevedere associazioni diverse per ciascuna fase dello shader usando uno spazio dei nomi sovrapponibile. Ad esempio, un vertex shader può dichiarare:

Texture2D foo : register(t0);

e il pixel shader può dichiarare anche:

Texture2D bar : register(t0);

Se l'applicazione crea un'associazione di signature di root a t0 VISIBILITY_ALL, entrambi gli shader vedono la stessa texture. Se lo shader definisce effettivamente vuole che ogni shader visualizzi trame diverse, può definire 2 slot di firma radice con VISIBILITY_VERTEX e _PIXEL. Indipendentemente dalla visibilità su uno slot della firma radice, esso ha sempre lo stesso costo (costo determinato solo dal tipo di slot) rispetto a una dimensione massima fissa della firma radice.

Nell'hardware D3D11 di fascia bassa, SHADER_VISIBILITY viene preso in considerazione anche quando si convalidano le dimensioni delle tabelle del descrittore in un layout radice, poiché alcuni hardware D3D11 possono supportare solo una quantità massima di associazioni per fase. Queste restrizioni vengono imposte solo quando vengono eseguite su hardware di livello basso e non limitano affatto hardware più moderno.

Se una firma radice dispone di più tabelle descrittori definite che si sovrappongono tra loro nello spazio dei nomi (le associazioni di registro allo shader) e una di esse specifica _ALL per la visibilità, il layout non è valido (la creazione avrà esito negativo).

Definizione della firma radice

La struttura D3D12_ROOT_SIGNATURE_DESC può contenere tabelle dei descrittori e costanti inline, ogni tipo di slot è definito dalla struttura D3D12_ROOT_PARAMETER e l'enumerazione D3D12_ROOT_PARAMETER_TYPE.

Per avviare uno slot di firma radice, fare riferimento ai metodi SetComputeRoot** e SetGraphicsRoot** di ID3D12GraphicsCommandList.

I campionatori statici sono descritti nella root signature usando la struttura D3D12_STATIC_SAMPLER.

Un numero di flag limita l'accesso di determinati shader alla signature radice, vedere D3D12_ROOT_SIGNATURE_FLAGS.

Serializzazione/deserializzazione della struttura dei dati della firma radice

I metodi descritti in questa sezione vengono esportati da D3D12Core.dll e forniscono metodi per serializzare e deserializzare una struttura di dati della firma radice.

Il modulo serializzato è ciò che viene passato all'API durante la creazione di una firma radice. Se è stato creato uno shader con una firma radice (quando viene aggiunta tale funzionalità), lo shader compilato conterrà già una firma radice serializzata.

Se un'applicazione genera in modo procedurale una struttura di dati D3D12_ROOT_SIGNATURE_DESC, deve creare il modulo serializzato usando D3D12SerializeRootSignature. L'output di quello può essere passato in ID3D12Device::CreateRootSignature.

Se un'applicazione dispone già di una firma radice serializzata o ha uno shader compilato che contiene una firma radice e vuole individuare a livello di codice la definizione del layout (nota come "reflection"), D3D12CreateRootSignatureDeserializer può essere chiamato. Viene generata un'interfaccia ID3D12RootSignatureDeserializer che contiene un metodo per restituire la struttura dei dati D3D12_ROOT_SIGNATURE_DESC deserializzata. L'interfaccia gestisce la durata della struttura di dati deserializzata.

API di creazione della firma radice

L'API ID3D12Device::CreateRootSignature accetta una versione serializzata di una firma radice.

Firma radice negli oggetti stato della pipeline

I metodi per creare lo stato della pipeline (ID3D12Device::CreateGraphicsPipelineState e ID3D12Device::CreateComputePipelineState ) accettano un'interfaccia facoltativa ID3D12RootSignature come parametro di input (archiviato in una struttura D3D12_GRAPHICS_PIPELINE_STATE_DESC). Questa operazione eseguirà l'override di qualsiasi firma radice già presente negli shader.

Se una root signature viene passata in uno dei metodi di creazione dello stato della pipeline, questa root signature viene convalidata rispetto a tutti gli shader nel PSO per garantire la compatibilità e assegnata al driver da usare con tutti gli shader. Se uno degli shader contiene una firma radice diversa, viene sostituita dalla firma radice passata nell'API. Se non viene fornita una firma principale, tutti gli shader forniti devono avere una firma principale e queste devono corrispondere, poiché verrà fornita al driver. L'impostazione di un PSO su un elenco di comandi o un bundle non modifica la firma radice. Questa operazione viene eseguita dai metodi SetGraphicsRootSignature e SetComputeRootSignature. Dal momento in cui viene richiamato draw(graphics)/dispatch(compute), l'applicazione deve assicurarsi che il PSO corrente corrisponda alla signature di root corrente; in caso contrario, il comportamento non è definito.

Codice per la definizione di una firma radice versione 1.1

L'esempio seguente illustra come creare una firma radice con il formato seguente:

RootParameterIndex Contenuto Valori
[0] Costanti radice: { b2 } (1 CBV)
[1] Tabella descrittore: { t2-t7, u0-u3 } (6 SRV + 4 UAV)
[2] Radice CBV: { b0 } (1 CBV, dati statici)
[3] Tabella descrittore: { s0-s1 } (2 campionatori)
[4] Tabella del descrittore: { t8 - senza vincoli } (illimitato numero di SRV, descrittori volatili)
[5] Tabella descrittore: { (t0, spazio1) - illimitato } (numero illimitato di SRV, descrittori volatili)
[6] Tabella dei descrittori: { b1 } (1 CBV, dati statici)

 

Se la maggior parte delle parti della firma radice viene usata la maggior parte del tempo, può essere meglio che dover cambiare la firma radice troppo frequentemente. Le applicazioni devono ordinare le voci nella firma di radice, dal più frequentemente modificato al meno frequente. Quando un'app modifica le associazioni in qualsiasi parte della firma radice, il driver potrebbe dover creare una copia di alcuni o di tutto lo stato della firma radice, che può diventare un costo significativo quando viene moltiplicato per molteplici modifiche di stato.

Inoltre, la firma radice definirà un campionatore statico che esegue il filtro anisotropico delle trame al registro shader s3.

Dopo aver associato questa firma radice, le tabelle del descrittore, il CBV radice e le costanti possono essere assegnati allo spazio dei parametri [0..6]. Ad esempio, le tabelle del descrittore (intervalli in un heap descrittore) possono essere vincolate a ogni parametro radice [1] e [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));

Il codice seguente illustra come usare la firma radice precedente in un elenco di comandi grafici.

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

firme radice

Specificare le signature di root in HLSL

Uso di una firma root