Passaggi di rendering di Direct3D 12
La funzionalità di rendering passa è una novità per Windows 10, versione 1809 (10.0; Build 17763) e introduce il concetto di passaggio di rendering direct3D 12. Un passaggio di rendering è costituito da un subset dei comandi registrati in un elenco di comandi.
Per dichiarare dove inizia e termina ogni passaggio di rendering, annidare i comandi appartenenti a che passano le chiamate interne a ID3D12GraphicsCommandList4::BeginRenderPass e EndRenderPass. Di conseguenza, qualsiasi elenco di comandi contiene zero, uno o più passaggi di rendering.
Scenari
I passaggi di rendering possono migliorare le prestazioni del renderer se si basa su Tile-Based rendering posticipato (TBDR), tra le altre tecniche. In particolare, la tecnica consente al renderer di migliorare l'efficienza della GPU riducendo il traffico di memoria da e verso la memoria off-chip, consentendo all'applicazione di identificare meglio i requisiti di ordinamento del rendering delle risorse e le dipendenze dei dati.
Un driver di visualizzazione scritto espressamente per sfruttare la funzionalità di passaggio di rendering offre i risultati migliori. Ma il rendering passa API può essere eseguito anche su driver preesistenti (anche se non necessariamente con miglioramenti delle prestazioni).
Questi sono gli scenari in cui i passaggi di rendering sono progettati per fornire valore.
Consentire all'applicazione di evitare carichi/archivi non necessari di risorse da/verso la memoria principale in un'architettura di rendering posticipato (TBDR) Tile-Based
Una delle proposte di valore dei passaggi di rendering consiste nel fatto che fornisce una posizione centrale per indicare le dipendenze dei dati dell'applicazione per un set di operazioni di rendering. Queste dipendenze di dati consentono al driver di visualizzazione di esaminare questi dati in fase di associazione/barriera e di emettere istruzioni che riducono al minimo i carichi/archivi delle risorse da/verso la memoria principale.
Consentire all'architettura TBDR di eseguire risorse opportunistiche persistenti nella cache on-chip nei passaggi di rendering (anche in elenchi di comandi separati)
Nota
In particolare, questo scenario è limitato ai casi in cui si sta scrivendo nella stessa destinazione di rendering tra più elenchi di comandi.
Un modello di rendering comune è che l'applicazione esegua il rendering nelle stesse destinazioni di rendering in più elenchi di comandi in modo seriale, anche se i comandi di rendering vengono generati in parallelo. L'uso dei passaggi di rendering in questo scenario consente di combinare questi passaggi in modo tale (poiché l'applicazione sa che riprenderà il rendering nell'elenco di comandi con esito positivo immediato) che il driver di visualizzazione può evitare uno scaricamento nella memoria principale nei limiti dell'elenco di comandi.
Responsabilità dell'applicazione
Anche con la funzionalità di rendering passa, né il runtime Direct3D 12 né il driver di visualizzazione prendono la responsabilità di dedurre opportunità di riordinare/evitare carichi e negozi. Per sfruttare correttamente la funzionalità di rendering, l'applicazione ha queste responsabilità.
- Identificare correttamente le dipendenze di dati/ordinamento per le relative operazioni.
- Ordinare gli invii in modo da ridurre al minimo gli scaricamenti (quindi, ridurre al minimo l'uso di flag _PRESERVE).
- Usare correttamente le barriere delle risorse e tenere traccia dello stato delle risorse.
- Evitare copie/cancellate non richiesto. Per identificare questi elementi, è possibile usare gli avvisi di prestazioni automatizzati dello strumento PIX in Windows.
Uso della funzionalità di passaggio di rendering
Che cos'è un di rendering di?
Un passaggio di rendering viene definito da questi elementi.
- Set di associazioni di output fisse per la durata del passaggio di rendering. Queste associazioni si trovano in una o più viste di destinazione di rendering (RTV) e/o in una visualizzazione stencil di profondità (DSV).
- Elenco di operazioni GPU destinate a tale set di associazioni di output.
- Metadati che descrivono le dipendenze di caricamento/archiviazione per tutte le associazioni di output destinate al passaggio di rendering.
Dichiarare le associazioni di output
All'inizio di un passaggio di rendering si dichiarano associazioni alle destinazioni di rendering e/o al buffer depth/stencil. È facoltativo eseguire il binding alle destinazioni di rendering ed è facoltativo eseguire il binding a un buffer depth/stencil. Tuttavia, è necessario eseguire il binding ad almeno uno dei due e nell'esempio di codice riportato di seguito viene associato a entrambi.
Queste associazioni vengono dichiarate in una chiamata a ID3D12GraphicsCommandList4::BeginRenderPass.
void render_passes(::ID3D12GraphicsCommandList4 * pIGCL4,
D3D12_CPU_DESCRIPTOR_HANDLE const& rtvCPUDescriptorHandle,
D3D12_CPU_DESCRIPTOR_HANDLE const& dsvCPUDescriptorHandle)
{
const float clearColor4[]{ 0.f, 0.f, 0.f, 0.f };
CD3DX12_CLEAR_VALUE clearValue{ DXGI_FORMAT_R32G32B32_FLOAT, clearColor4 };
D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessClear{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR, { clearValue } };
D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessPreserve{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE, {} };
D3D12_RENDER_PASS_RENDER_TARGET_DESC renderPassRenderTargetDesc{ rtvCPUDescriptorHandle, renderPassBeginningAccessClear, renderPassEndingAccessPreserve };
D3D12_RENDER_PASS_BEGINNING_ACCESS renderPassBeginningAccessNoAccess{ D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, {} };
D3D12_RENDER_PASS_ENDING_ACCESS renderPassEndingAccessNoAccess{ D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS, {} };
D3D12_RENDER_PASS_DEPTH_STENCIL_DESC renderPassDepthStencilDesc{ dsvCPUDescriptorHandle, renderPassBeginningAccessNoAccess, renderPassBeginningAccessNoAccess, renderPassEndingAccessNoAccess, renderPassEndingAccessNoAccess };
pIGCL4->BeginRenderPass(1, &renderPassRenderTargetDesc, &renderPassDepthStencilDesc, D3D12_RENDER_PASS_FLAG_NONE);
// Record command list.
pIGCL4->EndRenderPass();
// Begin/End further render passes and then execute the command list(s).
}
Impostare il primo campo della struttura D3D12_RENDER_PASS_RENDER_TARGET_DESC sull'handle del descrittore della CPU corrispondente a una o più viste di destinazione di rendering (RTV). Analogamente, D3D12_RENDER_PASS_DEPTH_STENCIL_DESC contiene l'handle del descrittore della CPU corrispondente a una visualizzazione depth stencil (DSV). Questi handle del descrittore della CPU sono gli stessi che altrimenti si passano a ID3D12GraphicsCommandList::OMSetRenderTargets. Analogamente a OMSetRenderTargets, i descrittori della CPU vengono ritagliati dai rispettivi heap (descrittore CPU) al momento della chiamata a BeginRenderPass.
Gli rtv e le DSV non vengono ereditati nel passaggio di rendering. Piuttosto, devono essere impostati. Né gli rtv e le DSV dichiarati in BeginRenderPass propagati all'elenco dei comandi. Si trovano invece in uno stato non definito dopo il passaggio di rendering.
Eseguire il rendering di passaggi e carichi di lavoro
Non è possibile annidare i passaggi di rendering e non è possibile avere un pass di rendering straddle più di un elenco di comandi (devono iniziare e terminare durante la registrazione in un singolo elenco di comandi). Le ottimizzazioni progettate per abilitare una generazione efficiente multithread dei passaggi di rendering vengono illustrate nella sezione flag di rendering, di seguito.
Una scrittura eseguita dall'interno di un passaggio di rendering non è valida da cui leggere fino a quando non viene eseguito un passaggio di rendering successivo. Ciò impedisce alcuni tipi di barriere all'interno del passaggio di rendering, ad esempio barriera da RENDER_TARGET a SHADER_RESOURCE nella destinazione di rendering attualmente associata. Per altre info, vedi la sezione Il rendering supera e le barriere delle risorse, di seguito.
L'unica eccezione al vincolo di lettura di scrittura appena menzionata implica le letture implicite che si verificano come parte del test di profondità e della fusione della destinazione di rendering. Pertanto, queste API non sono consentite all'interno di un passaggio di rendering (il runtime principale rimuove l'elenco di comandi se ne viene chiamato uno durante la registrazione).
- ID3D12GraphicsCommandList1::AtomicCopyBufferUINT
- ID3D12GraphicsCommandList1::AtomicCopyBufferUINT64
- ID3D12GraphicsCommandList4::BeginRenderPass
- ID3D12GraphicsCommandList::ClearDepthStencilView
- ID3D12GraphicsCommandList::ClearRenderTargetView
- ID3D12GraphicsCommandList::ClearState
- ID3D12GraphicsCommandList::ClearUnorderedAccessViewFloat
- ID3D12GraphicsCommandList::ClearUnorderedAccessViewUint
- ID3D12GraphicsCommandList::CopyBufferRegion
- ID3D12GraphicsCommandList::CopyResource
- ID3D12GraphicsCommandList::CopyTextureRegion
- ID3D12GraphicsCommandList::CopyTiles
- ID3D12GraphicsCommandList::D iscardResource
- ID3D12GraphicsCommandList::D ispatch
- ID3D12GraphicsCommandList::OMSetRenderTargets
- ID3D12GraphicsCommandList::ResolveQueryData
- ID3D12GraphicsCommandList::ResolveSubresource
- ID3D12GraphicsCommandList1::ResolveSubresourceRegion
- ID3D12GraphicsCommandList3::SetProtectedResourceSession
Eseguire il rendering di pass e barriere alle risorse
Non è possibile leggere o utilizzare una scrittura che si è verificata all'interno dello stesso passaggio di rendering. Alcune barriere non sono conformi a questo vincolo, ad esempio da D3D12_RESOURCE_STATE_RENDER_TARGET a *_SHADER_RESOURCE sulla destinazione di rendering attualmente associata (e il livello di debug genererà un errore a tale effetto). Tuttavia, la stessa barriera su una destinazione di rendering scritta all'esterno di il passaggio di rendering corrente è conforme, perché le scritture verranno completate prima dell'avvio del passaggio di rendering corrente. È possibile trarre vantaggio dalla conoscenza di alcune ottimizzazioni che un driver di visualizzazione può apportare in questo senso. Dato un carico di lavoro conforme, un driver di visualizzazione potrebbe spostare eventuali barriere rilevate nel passaggio di rendering all'inizio del passaggio di rendering. Lì, possono essere coalesci (e non interferire con alcuna operazione di tiling/binning). Si tratta di un'ottimizzazione valida a condizione che tutte le scritture siano state completate prima dell'avvio del passaggio di rendering corrente.
Di seguito è riportato un esempio di ottimizzazione del driver più completo, che presuppone che si disponga di un motore di rendering con una progettazione di binding di risorse di tipo pre-Direct3D 12, eseguendo barriere su richiesta in base alla modalità di associazione delle risorse. Quando si scrive in una visualizzazione di accesso non ordinata (UAV) verso la fine di un frame (da utilizzare nel frame seguente), il motore potrebbe lasciare la risorsa nello stato D3D12_RESOURCE_STATE_UNORDERED_ACCESS alla conclusione del fotogramma. Nel frame che segue, quando il motore esegue l'associazione della risorsa come visualizzazione risorse shader (SRV), rileva che la risorsa non è nello stato corretto e genererà una barriera da D3D12_RESOURCE_STATE_UNORDERED_ACCESS a D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE. Se tale barriera si verifica all'interno del passaggio di rendering, il driver di visualizzazione è giustificato presupponendo che tutte le scritture siano già state eseguite all'esterno di questo passaggio di rendering corrente e di conseguenza (e di seguito è in cui viene eseguita l'ottimizzazione) il driver di visualizzazione potrebbe spostare la barriera fino all'inizio del passaggio di rendering. Anche in questo caso, questa operazione è valida, purché il codice sia conforme al vincolo di lettura/scrittura descritto in questa sezione e l'ultimo.
Questi sono esempi di barriere conformi.
- D3D12_RESOURCE_STATE_UNORDERED_ACCESS a D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT.
- D3D12_RESOURCE_STATE_COPY_DEST a *_SHADER_RESOURCE.
E questi sono esempi di barriere non conformi.
- D3D12_RESOURCE_STATE_RENDER_TARGET a qualsiasi stato di lettura su VV/DSV attualmente associati.
- D3D12_RESOURCE_STATE_DEPTH_WRITE a qualsiasi stato di lettura su VV/DSV attualmente associati.
- Qualsiasi barriera di aliasing.
- Barriere UAV (Unrdered Access View).
Dichiarazione di accesso alle risorse
In BeginRenderPass ora, oltre a dichiarare tutte le risorse che fungono da RTV e/o DSV all'interno di tale passaggio, è necessario specificare anche le caratteristiche iniziale e finale di accesso. Come si può vedere nell'esempio di codice nella sezione Dichiarare le associazioni di output precedente, eseguire questa operazione con le strutture D3D12_RENDER_PASS_RENDER_TARGET_DESC e D3D12_RENDER_PASS_DEPTH_STENCIL_DESC.
Per altri dettagli, vedere le strutture D3D12_RENDER_PASS_BEGINNING_ACCESS e D3D12_RENDER_PASS_ENDING_ACCESS e le enumerazioni D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE e D3D12_RENDER_PASS_ENDING_ACCESS_TYPE.
Eseguire il rendering dei flag di passaggio
L'ultimo parametro passato a BeginRenderPass è un flag di passaggio di rendering (valore dell'enumerazione D3D12_RENDER_PASS_FLAGS).
enum D3D12_RENDER_PASS_FLAGS
{
D3D12_RENDER_PASS_FLAG_NONE = 0,
D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES = 0x1,
D3D12_RENDER_PASS_FLAG_SUSPENDING_PASS = 0x2,
D3D12_RENDER_PASS_FLAG_RESUMING_PASS = 0x4
};
Scritture UAV all'interno di un passaggio di rendering
Le scritture UAV (Unrdered Access View) sono consentite all'interno di un passaggio di rendering, ma è necessario indicare in modo specifico che verranno eseguite scritture UAV all'interno del passaggio di rendering specificando D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES, in modo che il driver di visualizzazione possa rifiutare esplicitamente l'associazione, se necessario.
Gli accessi UAV devono seguire il vincolo di lettura di scrittura descritto in precedenza (le scritture in un passaggio di rendering non sono valide per la lettura fino a quando non viene eseguito un passaggio di rendering successivo). Le barriere UAV non sono consentite all'interno di un passaggio di rendering.
Le associazioni UAV (tramite tabelle radice o descrittori radice) vengono ereditate nei passaggi di rendering e vengono propagate al di fuori dei passaggi di rendering.
Sospensione dei passaggi e ripresa dei passaggi
È possibile indicare un intero passaggio di rendering come passaggio di sospensione e/o un passaggio di ripresa. Una coppia di sospensione seguita da una ripresa deve avere visualizzazioni/flag di accesso identici tra i passaggi e potrebbe non avere operazioni GPU intermedie (ad esempio, disegni, dispatch, eliminazioni, cancella, copie, mapping update-tile-mapping, write-buffer-immediate, query, query, query risolte) tra il passaggio di rendering sospeso e il passaggio di rendering ripreso.
Il caso d'uso previsto è il rendering multithread, dove ad esempio quattro elenchi di comandi (ognuno con i propri passaggi di rendering) può avere come destinazione le stesse destinazioni di rendering. Quando i passaggi di rendering vengono sospesi o ripresi tra elenchi di comandi, gli elenchi di comandi devono essere eseguiti nella stessa chiamata a ID3D12CommandQueue::ExecuteCommandLists.
Un passaggio di rendering può essere sia ripreso che sospeso. Nell'esempio multithread appena specificato, gli elenchi di comandi 2 e 3 riprenderebbero rispettivamente da 1 e 2. E allo stesso tempo 2 e 3 sarebbero sospesi rispettivamente a 3 e 4.
Query per il supporto delle funzionalità di passaggio del rendering
È possibile chiamare ID3D12Device::CheckFeatureSupport per eseguire una query sulla misura in cui un driver di dispositivo e/o l'hardware supporta in modo efficiente il rendering.
D3D12_RENDER_PASS_TIER get_render_passes_tier(::ID3D12Device * pIDevice)
{
D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureSupport{};
winrt::check_hresult(
pIDevice->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &featureSupport, sizeof(featureSupport))
);
return featureSupport.RenderPassesTier;
}
...
D3D12_RENDER_PASS_TIER renderPassesTier{ get_render_passes_tier(pIDevice) };
A causa della logica di mapping del runtime, il rendering passa sempre la funzione . Tuttavia, a seconda del supporto delle funzionalità, non sempre fornirà un vantaggio. È possibile usare codice simile all'esempio di codice precedente per determinare se/quando vale la pena eseguire comandi durante il rendering e quando non è sicuramente un vantaggio (ovvero quando il runtime esegue solo il mapping alla superficie API esistente). L'esecuzione di questo controllo è particolarmente importante se si usa D3D11On12).
Per una descrizione dei tre livelli di supporto, vedere l'enumerazione D3D12_RENDER_PASS_TIER.