Condividi tramite


Creazione di una firma radice

Le firme radice sono una struttura di dati complessa contenente strutture nidificate. 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 al compilatore il vantaggio che il compilatore convalida in anticipo che il layout è compatibile con lo shader.

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

Se si desidera sfruttare le ottimizzazioni del driver per i descrittori e i dati delle firme radice, fare riferimento alla versione 1.1 della firma radice

Tipi di associazione tabella descrittore

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

Si tratta di un intervallo in modo che, ad esempio, se parte di una tabella descrittore ha 100 SRV, tale intervallo può essere dichiarato in una voce anziché 100. Quindi 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 descrittore.

La D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND macro può in genere essere usata per il OffsetInDescriptorsFromTableStart parametro 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, , NumDescriptorsBaseShaderRegistere RegisterSpace non può conflitto o sovrapporsi a qualsiasi dichiarazione in una firma radice con D3D12_SHADER_VISIBILITY comuni (fare riferimento alla sezione visibilità shader riportata di seguito).

Layout tabella descrittore

La struttura D3D12_ROOT_DESCRIPTOR_TABLE dichiara il layout di una tabella descrittore come raccolta di intervalli descrittori che iniziano a un determinato offset di un heap descrittore. Gli esempi non sono consentiti nella stessa tabella descrittore di CBV/UAV/SRV.

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

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

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

Costanti radice

La struttura D3D12_ROOT_CONSTANTS dichiara costanti inline nella firma radice che vengono visualizzate in shader come un buffer costante.

Questo struct viene usato 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 (visualizzati negli shader) inline nella firma radice.

Questo struct viene usato quando il tipo di slot della firma radice è impostato su D3D12_ROOT_PARAMETER_TYPE_CBVo D3D12_ROOT_PARAMETER_TYPE_UAVD3D12_ROOT_PARAMETER_TYPE_SRV .

Visibilità shader

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

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

Texture2D foo : register(t0);

e il pixel shader può anche dichiarare:

Texture2D bar : register(t0);

Se l'applicazione effettua un'associazione di firme radice a t0 VISIBILITY_ALL, entrambi gli shader vedono la stessa trama. Se lo shader definisce effettivamente ogni shader vuole che ogni shader visualizzi trame diverse, può definire 2 slot di firma radice con VISIBILITY_VERTEX e _PIXEL. Indipendentemente dal fatto che la visibilità si trova su uno slot di firma radice, ha sempre lo stesso costo (costo solo a seconda di quello che slotType è) verso una dimensione massima di firma radice fissa.

Nell'hardware D3D11 a basso termine, SHADER_VISIBILITY viene anche preso in considerazione quando si convalidano le dimensioni delle tabelle descrittori 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 a basso livello e non limitano più hardware moderno a tutti.

Se una firma radice ha più tabelle descrittori definite che si sovrappongono tra loro nello spazio dei nomi (le associazioni di registro al 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 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.

Gli esempi statici sono descritti nella firma radice usando la struttura D3D12_STATIC_SAMPLER .

Un numero di flag limita l'accesso di determinati shader alla firma radice, fare riferimento a 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 la serializzazione e la deserializzazione di una struttura di dati della firma radice.

Il modulo serializzato è ciò che viene passato all'API durante la creazione di una firma radice. Se un shader è stato creato con una firma radice in esso (quando tale funzionalità viene aggiunta), 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 che può essere passato in ID3D12Device::CreateRootSignature.

Se un'applicazione ha già una firma radice serializzata o ha uno shader compilato contenente una firma radice e desidera individuare a livello di codice la definizione di layout (nota come "reflection"), D3D12CreateRootSignatureDeserializer può essere chiamata. In questo modo viene generata un'interfaccia ID3D12RootSignatureDeserializer , che contiene un metodo per restituire la struttura di dati deserializzata D3D12_ROOT_SIGNATURE_DESC . L'interfaccia possiede la durata della struttura dei dati deserializzati.

API di creazione di firme 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 ID3D12RootSignature facoltativa come parametro di input (archiviato in una struttura D3D12_GRAPHICS_PIPELINE_STATE_DESC). Questa operazione esegue l'override di qualsiasi firma radice già presente negli shader.

Se una firma radice viene passata in uno dei metodi di stato della pipeline di creazione, questa firma radice viene convalidata in tutti gli shader del PSO per la compatibilità e assegnata al driver da usare con tutti gli shader. Se uno degli shader ha una firma radice diversa in esso, viene sostituito dalla firma radice passata all'API. Se una firma radice non viene passata, tutti gli shader passati devono avere una firma radice e devono corrispondere: questo verrà assegnato al driver. L'impostazione di un psO in 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 l'oggetto PSO corrente corrisponda alla firma radice corrente; in caso contrario, il comportamento non è definito.

Codice per la definizione di una firma radice versione 1.1

Nell'esempio seguente viene illustrato 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] CBV radice: { b0 } (1 CBV, dati statici)
[3] Tabella descrittore: { s0-s1 } (2 Esempi)
[4] Tabella descrittore: { t8 - unbounded } (unbounded # di SRV, descrittori volatili)
[5] Tabella descrittore: { (t0, space1) - unbounded } (unbounded # di SRV, descrittori volatili)
[6] Tabella descrittore: { b1 } (1 CBV, dati statici)

 

Se la maggior parte delle parti della firma radice viene usata la maggior parte del tempo, può essere migliore di dover cambiare troppo spesso la firma radice. Le applicazioni devono ordinare le voci nella firma radice dalla modifica più frequente al minimo. Quando un'app modifica le associazioni a qualsiasi parte della firma radice, il driver potrebbe dover eseguire una copia di uno o di tutti gli stati della firma radice, che può diventare un costo nontriviale quando viene moltiplicato in molte modifiche dello stato.

Inoltre, la firma radice definirà un sampler statico che esegue il filtro delle trame anisotropiche nel registro shader s3.

Dopo aver associato questa firma radice, è possibile assegnare tabelle descrittori, cbV radice e costanti allo spazio dei parametri [0..6]. Ad esempio, le tabelle descrittori (intervalli in un heap descrittore) possono essere associate 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 può essere usata 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

Specifica delle firme radice in HLSL

Uso di una firma radice