Home Page Twitter Facebook Feed RSS
NotJustCode
Apri

Root Parameter 2725 Visite) DirectX 12

Andiamo ora ad introdurre l'argomento che per certi versi é la vera novità delle Direct3D12.

Fino alla versione 11 di Direct3D eravamo abituati a caricare ogni risorsa in un apposito slot in modo che Direct3D potesse usarla per il draw. Ad ogni oggetto spostavamo texture, constant buffer e ripetevamo il draw. Ogni spostamento era una chiamata tra Cpu e Gpu, tempo che viene sottratto al rendering.

Inoltre, nel caso si usino tante risorse per ogni rendering, occorre effettuare moltissime chiamate. Pensiamo al fatto che molti effetti richiedono anche 10/20 oggetti tra texture, constant buffer e simili.

Nascono così le root table. Queste sono un modo per organizzare le risorse in modo che vengano passate in poche chiamate raggruppandole per tipologia ed utilizzo.

Uno shader ha come input il vertice ed una serie di variabili:

ConstantBuffer: contenenti dati generici

Texture: contenenti le immagini da applicare al modello

Sampler: le proprietà con cui leggere le texture

Ognuna è scritta nel codice HLSL indicando, tra le varie cose, il registro. Ad esempio

cbuffer myBuffer: register(b0)

{

            float4x4 transform;

};

Il constant buffer denominato myBuffer é inserito nello slot b0 e contiene un oggetto Float4x4 (matrice).

Il successivo utilizzerà lo slot b1,b2 e così via.

Le texture useranno gli slot t0, t1 etc., i sampler quelli s0, s1 etc.

In Direct3D12 possiamo scegliere se gestire ognuna in modo separato o se raggrupparle; ognuno di questi gruppi è chiamato RootParameter

Un RootParameter può contenere uno o più parametri dello stesso tipo, anche non consecutivi. Ogni volta che creiamo una risorsa questa viene associata ad un Heap che conterrà una serie di indirizzi occupati ognuno da una risorsa.

Il gioco che bisogna fare è questo:

Se il nostro shader ha 3 constant buffer e nel nostro Heap abbiamo salvato 3 Resource consecutive possiamo creare 3 parametri da 1 buffer o un parametro da 3.

Nel primo caso dovremo impostare per ogni parametro la sua posizione nell'Heap, nel secondo invece passeremo solo la posizione del primo, automaticamente Direct3D andrà a leggere i successivi.

Cominciamo da un esempio semplice: uno shader con 1 constant buffer

 

RootParameter[] parameters = new RootParameter[]

{

new RootParameter(ShaderVisibility.Vertex, new DescriptorRange() {RangeType=DescriptorRangeType.ConstantBufferView,DescriptorCount=1,BaseShaderRegister=0 }),

};

Molto semplice, un parametro visibile solo dal vertex shader contenente un solo ConstantBuffer nel registro 0

 

Ora un caso più complesso: 3 constant buffer da gestire con 2 parametri, uno per il registro 0 ed uno per i registri 1 e 2

RootParameter[] parameters = new RootParameter[]

{

new RootParameter(ShaderVisibility.All, new DescriptorRange() {RangeType=DescriptorRangeType.ConstantBufferView,DescriptorCount=1,BaseShaderRegister=0 }),

new RootParameter(ShaderVisibility.All, new DescriptorRange() {RangeType=DescriptorRangeType.ConstantBufferView,DescriptorCount=2,BaseShaderRegister=1 }),

};

 

Infine un esempio che contiene anche 2 texture e 1 sampler

RootParameter[] parameters = new RootParameter[]

{

new RootParameter(ShaderVisibility.All, new DescriptorRange() {RangeType=DescriptorRangeType.ConstantBufferView,DescriptorCount=1,BaseShaderRegister=0 }),

new RootParameter(ShaderVisibility.All, new DescriptorRange() {RangeType=DescriptorRangeType.ConstantBufferView,DescriptorCount=2,BaseShaderRegister=1 }),

new RootParameter(ShaderVisibility.Pixel,new DescriptorRange() {RangeType=DescriptorRangeType.ShaderResourceView,DescriptorCount=2,BaseShaderRegister=0 }),

new RootParameter(ShaderVisibility.Pixel,new DescriptorRange() {RangeType=DescriptorRangeType.Sampler,DescriptorCount=1,BaseShaderRegister=0 }),

};

In questa immagine una rappresentazione visita di come le risorse si posizionano negli heap e quindi nei parametri

Usate questi parametri per creare la rootSignature

RootSignatureDescription rootSignatureDesc = new RootSignatureDescription(RootSignatureFlags.AllowInputAssemblerInputLayout, parameters);

rootSignature = device.CreateRootSignature(rootSignatureDesc.Serialize());

Attenzione, se la root segnature non corrisponderà allo shader genererà un errore.

Infine per associare una risorsa alla command list

CommandList.SetGraphicsRootDescriptorTable(N, HeapPosition);

Dove N è l'indice del parametro e HeapPosition la posizione del buffer nell'heap.

Prima del draw dovrete essere sicuri di aver inserito gli heap in posizione

CommandList.SetDescriptorHeaps(3, new DescriptorHeap[] { constantBufferViewHeap, textureViewHeap, samplerViewHeap });