Una delle cose fondamentali utilizzando DirectX è il poter accedere alle risorse contenuti nei buffer. Ad esempio può essere necessario poter leggere i vertici contenuti nelle mesh e magari modificarli.
Direct3D10 ha modificato in maniera sostanziale la gestione della memoria creando delle regole che possono inizialmente creare dei problemi ma che alla fine diventano un ottimo sistema di gestione. Ogni risorsa infatti è creata in modo da poter essere letta o scritta solamente dalla GPU o dalla CPU. Questo ad esempio può significare che una certa risorsa può essere creata per poter essere letta dalla GPU (esempio una texture quando viene mandata a video) e scritta dalla CPU (per poter essere modificata), oppure il contrario, oppure essere gestita completamente solamente dalla GPU o solamente dalla CPU.
In Direct3D10 infatti esistono 4 tipi di usage, ossia le caratteristiche di utilizzo:
- D3D10_USAGE_DEFAULT : può essere letta e scritta solo dalla GPU (esempio di lettura è il vertex buffer, mentre quello in scrittura è un render target, ossia una texture su cui si fa il rendering)
- D3D10_USAGE_IMMUTABLE : può essere utilizzata solamente in lettura dalla GPU
- D3D10_USAGE_DYNAMIC : può essere letta dalla GPU e scritta dalla CPU (ad esempio per creare un vertex buffer che deve cambiare continuamente)
- D3D10_USAGE_STAGING : può essere letta e scritta solo dalla CPU
Non tutte le risorse possono essere create in tutte le modalità. Ad esempio in Staging non possiamo creare una texture. Questo porterebbe a pensare che solo alcune tipologie di risorse possano essere lette. Inoltre come si vede solo la modalità Staging ha funzionalità di lettura da CPU e quindi è l'unica che può essere letta e modificata da codice, ma allo stesso tempo non si può utilizzare per il rendering.
In DirectX10 si utilizzano quindi le funzioni di Copy che permettono di copiare una risorsa tra buffer. Potete quindi copiare risorse da una risorsa di tipo Default, Immutable e Dynamic in una di tipo Staging, modificarla e ricopiarla sul buffer di origine.
L'unica risorsa su cui non si può andare a scrivere è solo quella definita come Immutable. Per creare una risorsa di staging si utilizza la classica forma di creazione di un buffer con BindFlags impostato a zero, usage a Staging ed CPUAccessFlags in lettura e scrittura.
D3D10_BUFFER_DESC bd;
bd.ByteWidth = stride * vertexCount;
bd.MiscFlags = 0;
bd.BindFlags = 0;
bd.Usage = D3D10_USAGE_STAGING;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE;
InitData.pSysMem = new byte[length];
ID3D10Buffer* buffer;
HRESULT hr=device->CreateBuffer( &bd, &InitData, &buffer );
Notate che la pSysMem è stata impostata con un array vuoto. Per poter utilizzare la funzionalità di copia dovete creare una risorsa di dimensione uguale a quella di origine
A questo punto potete utilizzare le funzionalità di copia
device->CopyResource(buffer,vertexBuffer);
Con questa istruzione ho copiato un vertexBuffer su uno staging.
Ora dovrete andare a leggere le risorse. Per farlo si utilizza l'istruzione Map (simile alla lock di DirectX9)
VERTEX* vertices;
buffer->Map(D3D10_MAP_READ_WRITE ,0,(void**)&vertices);
//QUI MODIFICHERETE I DATI
buffer->Unmap();
L'istruzione map restituisce un puntatore di tipo void** (puntatore ad array di puntatori). Passandogli il puntatore ad una struttura che rappresenti il contenuto della risorsa (nel mio caso una struttura chiamata Vertex contenente posizione, normale e coordinata texture del vertice) questo sarà puntato verso la risorsa in memoria. Potete modificare come volete il contenuto dell'array, i risultati automaticamente si riporteranno sul buffer. Attenzione però, non potete cambiare il puntatore all'array, sia chiaro.
Quando avete finito dovrete utilizzare l'istruzione UnMap per liberare le risorse. In questo modo l'array vertices non punterà più alle risorse del buffer.
A questo punto potete nuovamente copiare il buffer modificato sul buffer originario ed utilizzarlo per il rendering.
Vi lascio con un pò di consigli
- Nel caso di risorse dynamic, non effettuate il CopyResource su quest'ultimo. Le risorse dynamic possono essere scritte direttamente usando Map ed Unmap. Potete quindi bloccare la risorsa in Staging per leggere i dati ma usare il Map del buffer dinamico come destinazione. Questo farà risparmiare un bel pò di lavoro alla macchina
- Il processo di copia e soprattutto di Map ed Unmap sono eseguite in CPU, quindi non sono veloci quanto le altre operazione DirectX
- Per conoscere le caratteristiche dei buffer usate l'istruzione GetDesc
- Potete prendere i buffer della mesh usando le istruzioni GetDeviceVertexBuffer e GetDeviceIndexBuffer
- Usate risorse Dynamic per buffer con frequenti scritture, Default per le risorse da mandare a video alla massima velocità possibile. Utilizzate Immutable solo per motivi di sicurezza (nel caso non vogliate modificare il contenuto da altre parti del codice)
- La copia tra risorse può avvenire solo tra buffer di pari dimensioni e formati (nel caso di texture)
Non rilascerò un esempio vista la semplicità dell'operazione
I commenti sono disabilitati.