Condividi tramite


Panoramica dell'associazione di risorse

Le chiavi per comprendere l'associazione di risorse in DirectX 12 sono i concetti dei descrittori, delle tabelle di descrittori, degli heap di descrittori e delle firme di radice.

Risorse e pipeline grafica

Le risorse shader (ad esempio trame, tabelle costanti, immagini, buffer e così via) non sono associate direttamente alla pipeline dello shader; Viene invece fatto riferimento tramite un descrittore . Un descrittore è un piccolo oggetto che contiene informazioni su una risorsa.

I descrittori vengono raggruppati per formare tabelle descrittori . Ogni tabella del descrittore archivia informazioni su un intervallo di tipi di risorsa. Esistono molti tipi diversi di risorse. Le risorse più comuni sono:

  • Visualizzazioni buffer costanti (CBV)
  • Visualizzazioni di Accesso Non Ordinate (UAV)
  • Visualizzazioni delle risorse shader (SRVs)
  • Campionatori

I descrittori SRV, UAV e CBV possono essere combinati nella stessa tabella dei descrittori.

Le pipeline grafiche e di calcolo ottengono l'accesso alle risorse facendo riferimento a tabelle descrittori per indice.

Le tabelle dei descrittori vengono archiviate in un heap di descrittori . Gli heap di descrittori conterranno idealmente tutti i descrittori (nelle tabelle dei descrittori) per il rendering di uno o più fotogrammi. Tutte le risorse verranno archiviate in heap a livello utente.

Un altro concetto è quello di una firma radice . La firma radice è una convenzione di associazione, definita dall'applicazione, usata dagli shader per individuare le risorse a cui hanno bisogno. La firma radice può archiviare:

  • Indici per le tabelle descrittori in un heap descrittore, in cui il layout della tabella descrittore è stato definito in modo predefinito.
  • Costanti, in modo che le app possano associare costanti definite dall'utente (note come costanti radice ) direttamente agli shader senza dover passare attraverso descrittori e tabelle descrittori.
  • Un numero molto ridotto di descrittori direttamente nella firma radice, come una vista del buffer costante (CBV) che cambia per ogni disegno, salvando così l'applicazione dalla necessità di inserire tali descrittori in un heap di descrittori.

In altre parole, la firma radice fornisce ottimizzazioni delle prestazioni adatte per piccole quantità di dati che cambiano per disegno.

La progettazione direct3D 12 per l'associazione lo separa da altre attività, ad esempio la gestione della memoria, la gestione della durata degli oggetti, il rilevamento dello stato e la sincronizzazione della memoria (fare riferimento a differenze nel modello di associazione da Direct3D 11). L'associazione Direct3D 12 è progettata per essere a basso sovraccarico e ottimizzata per le chiamate API eseguite più di frequente. È anche scalabile attraverso hardware di fascia bassa e alta e scalabile da meno recente (la pipeline Direct3D 11 più lineare) agli approcci più recenti (più paralleli) alla programmazione del motore grafico.

Tipi di risorse e visualizzazioni

I tipi di risorsa sono uguali a Direct3D 11, ovvero:

  • Texture1D e Texture1DArray
  • Texture2D e Texture2DArray, Texture2DMS, Texture2DMSArray
  • Texture3D
  • Buffer (tipizzati, strutturati e grezzi)

Le visualizzazioni delle risorse sono simili ma leggermente diverse da Direct3D 11, sono state aggiunte visualizzazioni vertex e index buffer.

  • Visualizzazione Constant Buffer (CBV)
  • Visualizzazione di accesso non ordinato (UAV)
  • Visualizzazione risorse shader (SRV)
  • Campionatori
  • Visualizzazione destinazione di rendering (RTV)
  • Visualizzazione Stencil profondità (DSV)
  • Visualizzazione del buffer di indice (IBV)
  • Vista del Buffer di Vertici (VBV)
  • Visualizzazione dell'output del flusso (SOV)

Solo i primi quattro di queste viste sono effettivamente visibili agli shader, fare riferimento a Heap Descrittori Visibili agli Shader e Heap Descrittori Non Visibili agli Shader.

Flusso di controllo dell'associazione di risorse

Concentrandosi solo sulle firme radice, sui descrittori radice, sulle costanti radice, sulle tabelle descrittori e sugli heap del descrittore, il flusso della logica di rendering per un'app dovrebbe essere simile al seguente:

  • Creare uno o più oggetti firma radice, uno per ogni configurazione di associazione diversa necessaria per un'applicazione.
  • Creare shader e stato della pipeline con gli oggetti delle firme principali con cui verranno utilizzati.
  • Creare una o più heap dei descrittori che conterranno tutti i descrittori SRV, UAV e CBV per ogni frame di rendering.
  • Inizializzare gli heap del descrittore con i descrittori, se possibile, per i set di descrittori che verranno riutilizzati in più fotogrammi.
  • Per ogni frame di cui eseguire il rendering:
    • Per ogni elenco di comandi:
      • Impostare la firma radice corrente da usare (e modificare se necessario durante il rendering, operazione raramente necessaria).
      • Aggiornare le costanti della firma radice e/o i descrittori della firma radice per la nuova visualizzazione ( ad esempio le proiezioni world/view).
      • Per ogni elemento da disegnare:
        • Definire i nuovi descrittori necessari negli heap di descrittori per il rendering per oggetto. Per gli heap dei descrittori visibili allo shader, l'app deve assicurarsi di utilizzare uno spazio nell'heap dei descrittori che non sia già referenziato dal rendering che potrebbe essere in corso, ad esempio allocando lo spazio sequenzialmente tramite l'heap dei descrittori durante il rendering.
        • Aggiorna la firma di root con puntatori alle sezioni necessarie degli heap del descrittore. Ad esempio, una tabella descrittore potrebbe puntare ad alcuni descrittori statici (non modificabili) inizializzati in precedenza, mentre un'altra tabella descrittore potrebbe puntare ad alcuni descrittori dinamici configurati per il rendering corrente.
        • Aggiorna le costanti della firma di radice e/o i descrittori della firma di radice per il rendering di ciascun elemento.
        • Impostare lo stato della pipeline per l'elemento da disegnare (solo se è necessario un cambiamento), compatibile con la firma radice attualmente associata.
        • Disegnare
      • Ripeti (elemento successivo)
    • Ripeti (elenco dei comandi successivo)
    • Solo quando la GPU ha concluso l'utilizzo di tutta la memoria che non verrà più usata, questa può essere rilasciata. Non occorre eliminare i riferimenti ai descrittori se non viene inviato un rendering aggiuntivo che utilizza tali descrittori. Pertanto, il rendering successivo può puntare ad altre aree negli heap dei descrittori, oppure i descrittori obsoleti possono essere sovrascritti con descrittori validi per riutilizzare lo spazio degli heap dei descrittori.
  • Ripeti (frame successivo)

Si noti che altri tipi di descrittore, viste di destinazione di rendering (RTV), viste degli stencil di profondità (DSV), viste del buffer di indice (IBV), viste del buffer dei vertici (VBV) e viste di output del flusso (SOV) vengono gestite in modo diverso. Il driver gestisce il controllo delle versioni del set di descrittori associati per ogni disegno durante la registrazione dell'elenco di comandi (in modo analogo al modo in cui le associazioni di firma radice vengono associate dal driver/hardware). Questo comportamento è diverso dai contenuti degli heap dei descrittori visibili allo shader, per i quali l'applicazione deve allocare manualmente attraverso l'heap perché fa riferimento a descrittori diversi tra i disegni. Il controllo delle versioni del contenuto dell'heap visibile allo shader viene lasciato all'applicazione perché consente alle applicazioni di eseguire operazioni come il riutilizzo dei descrittori che non cambiano o usano set statici di grandi dimensioni di descrittori e usano l'indicizzazione shader (ad esempio per ID materiale) per selezionare i descrittori da usare dall'heap descrittore o usare combinazioni di tecniche per diversi set di descrittori. L'hardware non è equipaggiato per gestire questo tipo di flessibilità per gli altri tipi di descrittore (RTV, DSV, IBV, VBV, SOV).

Sottoripartizione

In Direct3D 12 l'app ha un controllo di basso livello sulla gestione della memoria. Nelle versioni precedenti di Direct3D, incluso Direct3D 11, ci sarebbe un'allocazione per ogni risorsa. In Direct3D 12 l'app può usare l'API per allocare un blocco di memoria di grandi dimensioni, maggiore di qualsiasi singolo oggetto. Al termine, l'app può creare descrittori per puntare a sezioni di quel blocco di memoria di grandi dimensioni. Questo processo di decidere cosa inserire dove (blocchi più piccoli all'interno del grande blocco) è noto come suballocazione. L'abilitazione dell'app a questo scopo può produrre miglioramenti nell'uso efficiente del calcolo e della memoria. Ad esempio, la ridenominazione delle risorse diventa obsoleta. Al posto di questo, le app possono usare i fences per determinare quando viene usata una specifica risorsa e quando non lo è, delimitando l'esecuzione delle liste di comandi laddove la lista di comandi richiede l'uso di quella particolare risorsa.

Liberare risorse

Prima che qualsiasi memoria associata alla pipeline possa essere liberata, la GPU deve aver completato il suo utilizzo.

L'attesa del rendering dei fotogrammi è probabilmente il modo più grossolano per essere certi che la GPU abbia terminato il suo compito. Con una granularità più fine, puoi usare nuovamente le barriere: quando un comando viene registrato in un elenco di comandi di cui si vuole tenere traccia del completamento, inserisci una barriera immediatamente dopo di esso. È quindi possibile eseguire varie operazioni di sincronizzazione con il recinto. Si invia un nuovo lavoro (elenchi di comandi) che attende fino a quando non viene superata una specificata barriera sulla GPU, che indica che tutto ciò che precede è stato completato, oppure si può richiedere che venga generato un evento CPU al passaggio della barriera (che l'applicazione può attendere con un thread dormiente). In Direct3D 11 questo è stato EnqueueSetEvent().