Conversione da Direct3D 11 a Direct3D 12

Questa sezione fornisce alcune indicazioni sulla conversione da un motore grafico Direct3D 11 personalizzato a Direct3D 12.

Creazione del dispositivo

Sia Direct3D 11 che Direct3D 12 condividono un modello di creazione di dispositivi simile. I driver Direct3D 12 esistenti sono tutti D3D_FEATURE_LEVEL_11_0 o superiore, quindi è possibile ignorare i livelli di funzionalità precedenti e le limitazioni associate.

Tenere presente anche che con Direct3D 12 è consigliabile enumerare in modo esplicito le informazioni sul dispositivo usando le interfacce DXGI. In Direct3D 11 puoi riconcatenare il dispositivo DXGI dal dispositivo Direct3D e questo non è supportato per Direct3D 12.

La creazione di un dispositivo software WARP in Direct3D 12 viene eseguita fornendo un adattatore esplicito ottenuto da IDXGIFactory4::EnumWarpAdapter. Il dispositivo WARP per Direct3D 12 è disponibile solo nei sistemi con la funzionalità facoltativa Strumenti grafici abilitata.

Nota

Non esiste alcun equivalente a D3D11CreateDeviceAndSwapChain. Anche con Direct3D 11, sconsigliamo l'uso di questa funzione perché spesso è meglio creare il dispositivo e la swapchain in passaggi distinti.

Risorse di cui è stato eseguito il commit

Gli oggetti creati con le interfacce seguenti in Direct3D 11, traducono in ciò che vengono chiamati "risorse di cui è stato eseguito il commit" in Direct3D 12. Una risorsa di cui è stato eseguito il commit è una risorsa con spazio indirizzi virtuale e pagine fisiche associate. Si tratta di un concetto del modello di memoria di Microsoft Windows Device Driver 2 (WDD2), su cui si basa Direct3D 12.

Risorse direct3D 11:

In Direct3D 12 sono tutti rappresentati da ID3D12Resource e ID3D12Device::CreateCommittedResource.

Risorse riservate

Le risorse riservate sono risorse in cui è stato allocato solo lo spazio degli indirizzi virtuali, la memoria fisica non viene allocata fino a quando non viene eseguita una chiamata a ID3D12Device::CreateHeap. Si tratta essenzialmente dello stesso concetto delle risorse affiancate in Direct3D 11.

I flag (D3D11_RESOURCE_MISC_FLAG) usati in Direct3D 11 per configurare le risorse affiancate, quindi eseguirne il mapping alla memoria fisica.

  • D3D11_RESOURCE_MISC_TILED
  • D3D11_RESOURCE_MISC_TILE_POOL

Caricamento dei dati

In Direct3D 11 c'è l'aspetto di una singola sequenza temporale (chiamate che seguono una sequenza, ad esempio i dati inizializzati con D3D11_SUBRESOURCE_DATA, viene eseguita una chiamata a ID3D11DeviceContext::UpdateSubresource e quindi una chiamata a ID3D11DeviceContext::Map). Il numero di copie create dei dati non è ovvio per uno sviluppatore Direct3D 11.

In Direct3D 12 sono disponibili due sequenze temporali, la sequenza temporale della GPU (configurata dalle chiamate a CopyTextureRegion e CopyBufferRegion dalla memoria mappabile) e la sequenza temporale della CPU (determinata dalle chiamate a Map). Le funzioni helper vengono fornite (nel file d3dx12.h) denominate Updatesubresources che usano una sequenza temporale condivisa. Esistono diverse varianti di questa funzione helper, una che usa ID3D12Device::GetCopyableFootprints, un'altra che usa un meccanismo di allocazione heap e un'altra che usa un meccanismo di allocazione dello stack. Queste funzioni helper copiano le risorse sia nella GPU che nella CPU tramite un'area di gestione temporanea intermedia della memoria.

In genere la GPU e la CPU dispongono di una propria copia di una risorsa associata alla propria sequenza temporale. L'approccio della sequenza temporale condivisa mantiene in modo analogo due copie.

Shader e oggetti shader

In Direct3D 11 c'è molta creazione di oggetti shader e state e l'impostazione dello stato di tali oggetti, usando i metodi di creazione ID3D11Device e i metodi set ID3D11DeviceContext. In genere viene effettuato un numero elevato di chiamate a questi metodi, che vengono quindi combinati in fase di disegno dal driver per impostare lo stato della pipeline corretto.

In Direct3D 12 questa impostazione dello stato della pipeline è stata combinata in un singolo oggetto (CreateComputePipelineState per un motore di calcolo e CreateGraphicsPipelineState per un motore di grafica), che viene quindi collegato a un elenco di comandi prima della chiamata di disegno con una chiamata a SetPipelineState.

Queste chiamate sostituiscono tutte le singole chiamate per impostare shader, layout di input, stato blend, stato rasterizzatore, stato dello stencil di profondità e così via, in Direct3D 11

  • Metodi del dispositivo 11: CreateInputLayout, CreateXShader, CreateDepthStencilStatee CreateRasterizerState.
  • Metodi di Contesto di dispositivo 11: IASetInputLayout, xxSetShader, OMSetBlendState, OMSetDepthStencilStatee RSSetState.

Anche se Direct3D 12 può supportare BLOB shader compilati meno recenti, gli shader devono essere compilati usando il modello shader 5.1 con le API FXC/D3DCompile o usando il modello di shader 6 usando il compilatore DXIL DXC. È consigliabile convalidare il supporto del modello shader 6 con CheckFeatureSupport e D3D12_FEATURE_SHADER_MODEL.

Invio del lavoro alla GPU

In Direct3D 11 c'è poco controllo sul modo in cui viene effettivamente inviato il lavoro, è in gran parte gestito dal driver, anche se alcuni controlli sono abilitati tramite le chiamate ID3D11DeviceContext::Flush e IDXGISwapChain1::P resent1.

In Direct3D 12 l'invio di lavoro è molto esplicito e controllato dall'app. Il costrutto principale per l'invio del lavoro è l'ID3D12GraphicsCommandList, che viene usato per registrare tutti i comandi delle app (ed è molto simile al concetto di contesto posticipato ID3D11). L'archivio di backup per un elenco di comandi viene fornito da ID3D12CommandAllocator, che consente all'app di gestire l'utilizzo della memoria dell'elenco dei comandi esponendo effettivamente la memoria che il driver Direct3D 12 userà per archiviare l'elenco dei comandi.

Infine, ID3D12CommandQueue è una coda first-in first-out che archivia l'ordine corretto degli elenchi di comandi per l'invio alla GPU. Solo quando un elenco di comandi ha completato l'esecuzione nella GPU, l'elenco di comandi successivo dalla coda verrà inviato dal driver.

In Direct3D 11 non esiste alcun concetto esplicito di una coda di comandi. Nella configurazione comune per Direct3D 12, l'elenco dei comandi attualmente aperto D3D12_COMMAND_LIST_TYPE_DIRECT per il frame corrente può essere considerato analogo al contesto immediato direct3D 11. In questo modo sono disponibili molte delle stesse funzioni.

D3D11DeviceContext ID3D12GraphicsCommand List
ClearDepthStencilView ClearDepthStencilView
ClearRenderTargetView ClearRenderTargetView
ClearUnorderedAccess* ClearUnorderedAccess*
Draw, DrawInstanced DrawInstanced
DrawIndexed, DrawIndexedInstanced DrawIndexedInstanced
Invio Invio
IASetInputLayout, xxSetShader e così via. SetPipelineState
OMSetBlendState OMSetBlendFactor
OMSetDepthStencilState OMSetStencilRef
OMSetRenderTargets OMSetRenderTargets
RSSetViewports RSSetViewports
RSSetScissorRects RSSetScissorRects
IASetPrimitiveTopology IASetPrimitiveTopology
IASetVertexBuffers IASetVertexBuffers
IASetIndexBuffer IASetIndexBuffer
ResolveSubresource ResolveSubresource
CopySubresourceRegion CopyBufferRegion
UpdateSubresource CopyTextureRegion
CopyResource CopyResource

Nota

Un elenco di comandi creato con D3D12_COMMAND_LIST_TYPE_BUNDLE è simliar in un contesto posticipato. Direct3D 12 supporta anche la possibilità di accedere ad alcune funzionalità di un contesto immediato contemporaneamente al rendering tramite D3D12_COMMAND_LIST_TYPE_COPY e D3D12_COMMAND_LIST_TYPE_COMPUTE tipi di elenco di comandi.

Sincronizzazione CPU/GPU

Nella sincronizzazione CPU/GPU Direct3D 11 era in gran parte automatica e non era necessario che l'app mantenga lo stato della memoria fisica.

In Direct3D 12 l'app deve gestire in modo esplicito le due sequenze temporali (CPU e GPU). Ciò richiede che le informazioni siano mantenute, dall'app, sulle risorse richieste dalla GPU e per quanto tempo. Ciò significa anche che l'app è responsabile di garantire che il contenuto delle risorse (risorse di cui è stato eseguito il commit, gli heap, gli allocatori di comando, ad esempio) non cambierà fino al termine dell'uso della GPU.

L'oggetto principale per la sincronizzazione delle sequenze temporali è l'oggetto ID3D12Fence . Il funzionamento delle recinzioni è abbastanza semplice, consentono alla GPU di segnalare quando è stata completata un'attività. La GPU e la CPU possono entrambi segnalare e possono attendere entrambi i recinti.

In genere, l'approccio consiste nel fatto che quando si invia un elenco di comandi per l'esecuzione, un segnale di isolamento viene trasmesso dalla GPU al completamento (al termine della lettura dei dati), consentendo alla CPU di riutilizzare o eliminare definitivamente le risorse.

In Direct3D 11 il flag ID3D11DeviceContext::Map D3D11_MAP_WRITE_DISCARD essenzialmente considerato ogni risorsa come una fornitura infinita di memoria in cui l'app potrebbe scrivere (un processo noto come "ridenominazione"). In Direct3D 12 il processo è esplicito: è necessario allocare memoria aggiuntiva e i recinti devono essere usati per sincronizzare le operazioni. I buffer circolari (costituiti da buffer di grandi dimensioni) potrebbero essere una buona tecnica per questo, vedere lo scenario del buffer circolare in Gestione risorse basata su recinto.

uso di un buffer circolare

Associazione di risorse

Le visualizzazioni in Direct3D 11 (visualizzazioni delle risorse shader, visualizzazioni di destinazione di rendering e così via), sono state ampiamente sostituite in Direct3D 12 con il concetto di descrittore. I metodi di creazione esistono ancora in Direct3D 12 (ad esempio CreateShaderResourceView e CreateRenderTargetView), chiamati dopo la creazione dell'heap del descrittore, per scrivere i dati nell'heap. L'associazione in Direct3D 12 è ora gestita dagli handle del descrittore descritti in una firma radice e inviati usando i metodi SetGraphicsRootDescriptorTable o SetComputeRootDescriptorTable.

I mapping dei dettagli delle firme radice tra il numero di slot della firma radice e le tabelle del descrittore, in cui la tabella descrittore può contenere riferimenti alle risorse disponibili per vertex shader, pixel shader e altri shader, ad esempio buffer costanti, viste delle risorse shader e sampler. Questa flessibilità disconnette lo spazio di registrazione HLSL dallo spazio di associazione API in Direct3D 12, a differenza di Direct3D 11 in cui è presente un mapping uno a uno tra questi.

Una delle implicazioni di questo sistema è che l'app è responsabile della ridenominazione delle tabelle dei descrittori, che consente agli sviluppatori di comprendere il costo delle prestazioni della modifica anche di un singolo descrittore per ogni chiamata di disegno.

Una nuova funzionalità di Direct3D 12 è che un'app può controllare quali descrittori sono condivisi tra le fasi dello shader. Nelle risorse Direct3D 11, ad esempio le UAV, vengono condivise tra tutte le fasi dello shader. Abilitando la disabilitazione dei descrittori per determinate fasi dello shader, i registri usati dai descrittori disabilitati sono disponibili per l'uso da parte dei descrittori abilitati per una determinata fase dello shader.

Nella tabella seguente viene illustrata una firma radice di esempio.

Slot del parametro radice Voce tabella descrittore
0 Intervallo descrittore vs b0-b13
1 Intervallo descrittore vs t0-t127
2 Intervallo descrittore vs s0-s16
3 Intervallo descrittore PS b0-b13
...
14 Intervallo descrittore DS s0-16
15 Intervallo descrittore condiviso u0-u63

 

Stato della risorsa

Nello stato della risorsa Direct3D 11 non viene gestito dall'app, ma dal driver.

In Direct3D 12 mantenere lo stato delle risorse diventa responsabilità dell'app, per abilitare il parallelismo completo nella registrazione degli elenchi di comandi: l'app deve gestire le sequenze temporali di registrazione per gli elenchi di comandi (che possono essere eseguite in parallelo) e le sequenze temporali di esecuzione che devono essere sequenziali.

Una transizione dello stato della risorsa viene gestita dal metodo ResourceBarrier. Principalmente l'app deve informare il driver quando l'utilizzo delle risorse cambia. Ad esempio, se una risorsa viene usata come destinazione di rendering e quindi deve essere usata come input per un vertex shader nella chiamata di disegno successiva, potrebbe essere necessario un breve stallo nell'operazione GPU per completare l'operazione di destinazione di rendering prima di gestire il vertex shader.

Questo sistema consente la sincronizzazione con granularità fine (blocchi GPU) della pipeline grafica, nonché scaricamenti della cache e possibilmente alcune modifiche al layout della memoria (ad esempio la visualizzazione di destinazione di rendering alla decompressione della visualizzazione degli stencil di profondità).

Questa operazione è nota come barriera di transizione. Esistono altri tipi di barriere, in Direct3D 11 l'ID3D11DeviceContext2 ::TiledResourceBarrier ha abilitato l'uso della stessa memoria fisica da parte di due diverse risorse affiancate. In Direct3D 12 questo viene definito "barriera di aliasing". Le barriere di aliasing possono essere usate sia per le risorse affiancate che per le risorse posizionate in Direct3D 12. Inoltre, c'è la barriera UAV. In Direct3D 11 tutte le operazioni di invio e disegno UAV devono essere serializzate, anche se queste operazioni possono essere pipeline o lavorare in parallelo. Per Direct3D 12 questa restrizione viene rimossa dall'aggiunta di una barriera UAV. Una barriera UAV garantisce che le operazioni UAV siano sequenziali, quindi se una seconda operazione richiede che il primo venga completato, il secondo verrà costretto ad attendere con l'aggiunta della barriera. L'operazione predefinita per gli UAV è semplicemente che le operazioni procederanno il più rapidamente possibile.

Chiaramente ci sono miglioramenti delle prestazioni se un carico di lavoro può essere parallelizzato.

Portachiavi

La catena di scambio DXGI è la base per le catene di scambio in Direct3D 11 e 12. Esistono alcune differenze minime, in Direct3D 11 i tre tipi di catena di scambio sono SEQUENTIAL, DISCARD e FLIP_SEQUENTIAL. Per Direct3D 12 sono disponibili solo due tipi: FLIP_SEQUENTIAL e FLIP_DISCARD. Come indicato in precedenza, è necessario creare in modo esplicito la porta di scambio tramite IDXGIFactory4 o versione successiva e usare la stessa interfaccia per qualsiasi enumerazione dell'adapter.

In Direct3D 11 è presente una rotazione backbuffer automatica: è necessaria una sola visualizzazione di destinazione di rendering per il buffer nascosto 0. In Direct3D 12 la rotazione del buffer è esplicita, deve essere presente una visualizzazione di destinazione di rendering per ogni buffer nascosto. Usare il metodo IDXGISwapChain3::GetCurrentBackBufferIndex per selezionarne uno in cui eseguire il rendering. Anche in questo caso, questa flessibilità aggiuntiva consente una maggiore parallelizzazione.

Nota

Anche se esistono diversi modi per configurare l'applicazione, in genere le applicazioni hanno un ID3D12CommandAllocator per ogni buffer della catena di scambio. In questo modo l'applicazione può continuare a creare un set di comandi per il fotogramma successivo mentre la GPU esegue il rendering del precedente.

Rendering di funzioni fisse

In Direct3D 11 esistono alcuni metodi che semplificavano varie operazioni di livello superiore, ad esempio GenerateMips (creazione di catene mip complete) e DrawAuto (usando l'output del flusso come input shader senza ulteriore input dall'app). Questi metodi non sono disponibili in Direct3D 12, l'app deve gestire queste operazioni creando shader per eseguirle.

Quote e fine

La tabella seguente illustra una serie di funzionalità simili tra Direct3D 11 e 12, ma non identiche.

Direct3D 11 Direct3D 12
ID3D11Query ID3D12QueryHeap consente di raggruppare le query, riducendo i costi.
ID3D11Predicate Il predicamento è ora abilitato con dati in un buffer completamente trasparente. L'oggetto Direct3D 11 ID3D11Predicate viene sostituito da ID3D12Resource::Map, che deve seguire una chiamata a ResolveQueryData e un'operazione di sincronizzazione GPU usando un recinto per attendere che i dati siano pronti. Fare riferimento a Predicazione.
Contatore nascosto UAV/SO L'app è responsabile dell'allocazione e della gestione dei contatori SO/UAV. Fare riferimento a Contatori di output di flusso e contatori UAV.
MinLOD dinamico della risorsa (livello minimo di dettaglio) È stato spostato nel descrittore SRV statico MinLOD.
Draw*Indirect/DispatchIndirect I metodi indiretti di disegno vengono tutti uniti nel metodo ExecuteIndirect.
I formati DepthStencil sono interleaved I formati DepthStencil sono planari. Ad esempio, un formato di 24 bit di profondità, 8 bit di stencil verrebbero archiviati nel formato 24/8/24/8... ecc in Direct3D 11, ma come 24/24/24... seguito da 8/8/8... in Direct3D 12. Si noti che ogni piano è una propria sottorisorsa in D3D12 (fare riferimento a Sottorisorse).
ResizeTilePool Le risorse riservate possono essere mappate a più heap. Quando un pool di riquadri sarebbe stato ampliato in D3D11, è possibile allocare un heap aggiuntivo in D3D12.

 

Esercitazioni video sull'apprendimento avanzato DirectX: Guida alla conversione da DirectX 11 a DirectX 12

Informazioni su Direct3D 12

Uso di Direct3D 11, Direct3D 10 e Direct2D