Pass di rendering Direct3D 12

La funzionalità di passaggio del rendering è nuova 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 tali chiamate 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 a/dalla memoria off-chip consentendo all'applicazione di identificare meglio i requisiti di ordinamento delle risorse e le dipendenze dei dati.

Un driver visualizzato scritto in modo esplicito per sfruttare la funzionalità di passaggio del rendering offre i risultati migliori. Tuttavia, il rendering passa LE API può essere eseguito anche nei driver preesistenti (anche se non necessariamente con miglioramenti delle prestazioni).

Questi sono gli scenari in cui il rendering passa è progettato per fornire valore.

Consentire all'applicazione di evitare carichi/archivi non necessari di risorse da/a memoria principale in un'architettura di rendering posticipato (TBDR) Tile-Based

Una delle proposte di valore dei passaggi di rendering è che fornisce una posizione centrale per indicare le dipendenze dei dati dell'applicazione per un set di operazioni di rendering. Queste dipendenze dati consentono al driver di visualizzazione di controllare 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.

Consenti all'architettura TBDR di risorse persistente in modo opportunistico nella cache sul chip tra i 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 in più elenchi di comandi.

Un modello di rendering comune consente all'applicazione di eseguire il rendering degli stessi oggetti di rendering in più elenchi di comandi in modo seriale, anche se i comandi di rendering vengono generati in parallelo. L'uso del rendering passa in questo scenario consente di combinare questi passaggi in modo tale (poiché l'applicazione sa che riprenderà il rendering nell'elenco di comandi immediato) che il driver di visualizzazione può evitare uno scaricamento della 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 archivi. Per sfruttare correttamente la funzionalità di passaggio del rendering, l'applicazione ha queste responsabilità.

  • Identificare correttamente le dipendenze dei dati o dell'ordinamento per le relative operazioni.
  • Ordinare gli invii in modo da ridurre al minimo gli scarichi (quindi ridurre al minimo l'uso dei flag di _PRESERVE ).
  • Usare correttamente le barriere delle risorse e tenere traccia dello stato delle risorse.
  • Evitare copie/cancellate non autorizzate. Per identificare questi elementi, è possibile usare gli avvisi di prestazioni automatizzati dallo strumento PIX in Windows.

Uso della funzionalità pass pass di rendering

Che cos'è un passaggio di rendering?

Un passaggio di rendering è definito da questi elementi.

  • Set di associazioni di output fisse per la durata del passaggio di rendering. Queste associazioni sono destinate a una o più visualizzazioni di destinazione di rendering (RTV) e/o a una visualizzazione stencil di profondità (DSV).
  • Elenco delle operazioni GPU destinate a tale set di associazioni di output.
  • Metadati che descrivono le dipendenze di carico/archivio per tutte le associazioni di output destinate al passaggio di rendering.

Dichiarare le associazioni di output

All'inizio di un passaggio di rendering, dichiarare associazioni alle destinazioni di rendering e/o al buffer di profondità/stencil. È facoltativo associare a target di rendering ed è facoltativo associare a un buffer di profondità/stencil. Tuttavia, è necessario eseguire il binding a almeno uno dei due e nell'esempio di codice riportato di seguito viene associato a entrambi.

Dichiarare queste associazioni 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).
}

Si imposta il primo campo della struttura D3D12_RENDER_PASS_RENDER_TARGET_DESC sull'handle descrittore della CPU corrispondente a una o più visualizzazioni di destinazione di rendering (RTV). Analogamente, D3D12_RENDER_PASS_DEPTH_STENCIL_DESC contiene l'handle descrittore DELLA CPU corrispondente a una visualizzazione stencil di profondità (DSV). Gli handle del descrittore della CPU sono gli stessi che altrimenti si passerebbe a ID3D12GraphicsCommandList::OMSetRenderTargets. E, come con OMSetRenderTargets, i descrittori della CPU vengono ignorati dai rispettivi heaps (descrittore CPU) al momento della chiamata a BeginRenderPass.

I dischi rtv e DSV non vengono ereditati nel passaggio di rendering. Invece, devono essere impostati. Né gli RTV e la DSV dichiarati in BeginRenderPass vengono propagati all'elenco dei comandi. Invece, si trovano 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 passaggio di rendering 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 di passaggi di rendering multi thread vengono illustrate nella sezione Eseguire il rendering dei flag pass Flag, di seguito.

Una scrittura eseguita dall'interno di un passaggio di rendering non è valida per la lettura da 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 la barriera da RENDER_TARGET a SHADER_RESOURCE nella destinazione di rendering attualmente associata. Per altre informazioni, vedere la sezione Rendering passa e 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 approfondito e il rendering della fusione di destinazione. Queste API sono quindi non consentite all'interno di un passaggio di rendering (il runtime di base rimuove l'elenco di comandi se uno di essi viene chiamato durante la registrazione).

Eseguire il rendering di passaggi e barriere delle risorse

Non è possibile leggere da, 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 nella destinazione di rendering attualmente associata e il livello di debug avrà un errore a tale effetto. Tuttavia, questa stessa barriera su una destinazione di rendering scritta all'esterno del passaggio di rendering corrente è conforme, perché le scritture verranno completate prima del passaggio di rendering corrente a partire. È possibile trarre vantaggio dalla conoscenza di determinate ottimizzazioni che un driver di visualizzazione può fare in questo senso. Dato un carico di lavoro conforme, un driver di visualizzazione potrebbe spostare eventuali barriere rilevate nel rendering passando all'inizio del passaggio di rendering. Lì, possono essere coalesci (e non interferire con eventuali operazioni 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.

Ecco un esempio di ottimizzazione del driver più completo, che presuppone che si disponga di un motore di rendering con una progettazione di associazione di risorse pre-Direct3D 12-style, 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 frame. Nel frame che segue, quando il motore passa all'associazione della risorsa come visualizzazione risorsa shader (SRV), troverà 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 al di fuori di questo passaggio di rendering corrente e di conseguenza (e di seguito è disponibile 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 di scrittura descritto in questa sezione e l'ultimo.

Si tratta di 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 in RTV/DSV attualmente associati.
  • D3D12_RESOURCE_STATE_DEPTH_WRITE a qualsiasi stato di lettura in RTV/DSV attualmente associati.
  • Qualsiasi barriera di aliasing.
  • Barriere di accesso non ordinate (UAV). 

Dichiarazione di accesso alle risorse

All'ora beginRenderPass , oltre a dichiarare tutte le risorse che fungono da RTV e/o DSV all'interno del passaggio, è necessario specificare anche le caratteristiche di accesso iniziale e finale. Come si può vedere nell'esempio di codice nella sezione Dichiarare le associazioni di output sopra, si esegue questa operazione con le strutture D3D12_RENDER_PASS_RENDER_TARGET_DESC e D3D12_RENDER_PASS_DEPTH_STENCIL_DESC .

Per altre informazioni, 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 .

Flag di passaggio di rendering

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 si emettono 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 il tiling, 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 flag di visualizzazione/accesso identici tra i passaggi e potrebbe non avere alcuna operazione di GPU in caso di intervento (ad esempio, disegna, invia, elimina, cancella, copie, update-tile-mappings, write-buffer-immediate, query, query risolte) tra il passaggio di rendering di sospensione e il passaggio di rendering di ripresa.

Il caso d'uso previsto è il rendering multithread, in cui si supponga che quattro elenchi di comandi (ognuno con i propri passaggi di rendering) possa essere destinato alle stesse destinazioni di rendering. Quando i passaggi di rendering vengono sospesi/ripresi tra elenchi di comandi, gli elenchi di comandi devono essere eseguiti nella stessa chiamata a ID3D12CommandQueue::ExecuteCommandLists.

Un passaggio di rendering può essere ripreso e 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à dei passaggi di 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 fornirà sempre un vantaggio. È possibile usare codice simile all'esempio di codice precedente per determinare se/quando vale la pena eseguire comandi quando viene eseguito il rendering e quando non è sicuramente un vantaggio , ovvero quando il runtime esegue solo il mapping all'area 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 .