Constant Buffer 2500 Visite) DirectX 12
Ci sono 3 tipologie di oggetti da passare agli shader: texture, Sampler e constant buffer. Questi vengono utilizzati per contenere le tipologie più disparate di informazioni, come matrici di trasformazione, colori, vettori, insomma, tutto ciò che servirà per elaborare i nostri modelli.
Innanzitutto creiamo un Heap per conservare i nostri buffer
DescriptorHeapDescription cbvHeapDesc = new DescriptorHeapDescription()
{
DescriptorCount = 1,
Flags = DescriptorHeapFlags.ShaderVisible,
Type = DescriptorHeapType.ConstantBufferViewShaderResourceViewUnorderedAccessView
};
Heap constantBufferViewHeap = device.CreateDescriptorHeap(cbvHeapDesc);
Questo Heap può gestire sia constant buffer che texture.
Iniziamo quindi a definire cosa vogliamo passare al nostro shader tramite una struct c#
struct ConstantBuffer
{
public Vector4 offset;
};
Questa dovrà essere presente anche nello shader, magari con nomi diversi ma con le stesse tipologie di dati disposti nello stesso ordine.
Resource constantBuffer = device.CreateCommittedResource(new HeapProperties(HeapType.Upload), HeapFlags.None, ResourceDescription.Buffer(1024 * 64), ResourceStates.GenericRead);
In questo esempio ho creato un constant buffer capiente 64kb, il massimo disponibile. Non è necessaria questa dimensione in quanto basta che la dimensione sia sufficiente a contenere i dati che ci servono, purché la dimensione sia multipla di 256 byte.
Carichiamo quindi nell'heap la sua vista
ConstantBufferViewDescription cbvDesc = new ConstantBufferViewDescription()
{
BufferLocation = constantBuffer.GPUVirtualAddress,
SizeInBytes = (Utilities.SizeOf<ConstantBuffer>() + 255) & ~255
};
device.CreateConstantBufferView(cbvDesc, constantBufferViewHeap.CPUDescriptorHandleForHeapStart);
Come vedete ho definito una ConstantBufferViewDescription e passata nella prima posizione del mio Heap. Ricordatevi che se caricate più viste nello stesso Heap dovrete incrementare la posizione di partenza. Notate la proprietà SizeInBytes che va espressa in multipli di 256. Questo potrebbe far sprecare memoria ma è necessario a DirectX per ottimizzare la lettura dei constant buffer.
Potete anche creare un constant buffer che contiene più strutture dati e giocare con la posizione di memoria GPUVirtualAddress.
Se la prima struttura occupa 256 byte sommate 256 per creare una vista alla seconda struttura.
Passate quindi nella lista il vostro constant buffer tramite la rootparameter
commandList.SetGraphicsRootDescriptorTable(0, constantBufferViewHeap.GPUDescriptorHandleForHeapStart);
In questo caso la pipeline dovrà avere il costant buffer in prima posizione.
Ricordatevi che la posizione nell'heap va incrementata se utilizzate risorse salvate in posizioni successive.
Utilizzate sempre device.GetDescriptorHandleIncrementSize per sapere quanto occupa ogni tipo di risorsa nell'heap.
Ora lo shader ricevera i dati contenuti nel buffer. Utilizzate il metodo map per passare i dati
IntPtr constantBufferPointer = constantBuffer.Map(0);
Utilities.Write(constantBufferPointer, ref constantBufferData);
Il motivo per cui non vedete il metodo unmap é perché possiamo lasciare aperti i buffer e scriverci quando vogliamo. Basta quindi usare il map all'inizio e poi scriverci solamente
Vi rimando al demo HelloConstantBuffer presente nel mio repository.