Panoramica dell'associazione di risorse

Le chiavi per comprendere l'associazione di risorse in DirectX 12 sono i concetti dei descrittori, delle tabelle descrittori, degli heap descrittori e delle firme 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 del descrittore. 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)
  • Viste di accesso non ordinate (UAV)
  • Visualizzazioni risorse shader (SRV)
  • Campionatori

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

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

Le tabelle del descrittore vengono archiviate in un heap del descrittore. Gli heap descrittori conterranno idealmente tutti i descrittori (nelle tabelle descrittore) per il rendering di uno o più fotogrammi. Tutte le risorse verranno archiviate in heap in modalità 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 devono accedere. 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 all'interno della firma radice, ad esempio una visualizzazione buffer costante (CBV) che cambia per disegno, salvando così l'applicazione dalla necessità di inserire tali descrittori in un heap descrittore.

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

La progettazione di 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 (vedere Differenze nel modello di associazione da Direct3D 11). L'associazione Direct3D 12 è progettata per essere a basso sovraccarico e ottimizzata per le chiamate API effettuate più di frequente. È anche scalabile tra hardware di fascia bassa e di fascia 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 viste

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

  • Texture1D e Texture1DArray
  • Texture2D e Texture2DArray, Texture2DMS, Texture2DMSArray
  • Texture3D
  • Buffer (tipizzato, strutturato e non elaborato)

Le visualizzazioni delle risorse sono simili ma leggermente diverse da Direct3D 11, sono state aggiunte viste del vertex e del buffer di indice.

  • Constant Buffer View (CBV)
  • Visualizzazione di accesso non ordinato (UAV)
  • Visualizzazione risorse shader (SRV)
  • Campionatori
  • Visualizzazione destinazione di rendering (RTV)
  • Visualizzazione Depth Stencil (DSV)
  • Visualizzazione buffer di indice (IBV)
  • Visualizzazione buffer vertex (VBV)
  • Visualizzazione output di flusso (SOV)

Solo i primi quattro di queste visualizzazioni sono effettivamente visibili agli shader, fare riferimento agli heap descrittori visibili dello shader e agli heap visibili non shader.

Flusso di controllo dell'associazione di risorse

Concentrandosi solo sulle firme radice, sui descrittori radice, sulle costanti radice, sulle tabelle del descrittore 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 della firma radice con cui verranno usati.
  • Creare un descrittore (o, se necessario, altro) che conterrà tutti i descrittori SRV, UAV e CBV per ogni fotogramma di rendering.
  • Inizializzare gli heap del descrittore con descrittori laddove possibile per set di descrittori che verranno riutilizzati in più fotogrammi.
  • Per il rendering di ogni frame:
    • Per ogni elenco di comandi:
      • Impostare la firma radice corrente da usare e modificare se necessario durante il rendering, che raramente è necessaria.
      • Aggiornare le costanti della firma radice e/o i descrittori di firma radice per la nuova visualizzazione ,ad esempio le proiezioni di mondo/visualizzazione.
      • Per ogni elemento da disegnare:
        • Definire eventuali nuovi descrittori negli heap del descrittore in base alle esigenze per il rendering per oggetto. Per gli heap del descrittore visibile agli shader, l'app deve assicurarsi di usare lo spazio dell'heap descrittore che non è già stato fatto riferimento tramite rendering che potrebbe essere in anteprima, ad esempio allocando in modo lineare lo spazio tramite l'heap del descrittore durante il rendering.
        • Aggiornare la firma radice con puntatori alle aree richieste 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.
        • Aggiornare le costanti della firma radice e/o i descrittori di firma radice per il rendering per elemento.
        • Impostare lo stato della pipeline per l'elemento da disegnare (solo se necessario, se necessario), compatibile con la firma radice attualmente associata.
        • Disegna
      • Ripeti (elemento successivo)
    • Ripeti (elenco di comandi successivo)
    • Rigorosamente quando la GPU è terminata con qualsiasi memoria che non verrà più usata, può essere rilasciata. Non è necessario eliminare i riferimenti dei descrittori se non viene inviato un rendering aggiuntivo che usa tali descrittori. Pertanto, il rendering successivo può puntare ad altre aree negli heap del descrittore o i descrittori non aggiornati possono essere sovrascritti con descrittori validi per riutilizzare lo spazio dell'heap del descrittore.
  • 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 (analogamente al modo in cui le associazioni di firma radice vengono con controllo delle versioni dal driver/hardware). Questo comportamento è diverso dal contenuto degli heap del descrittore visibile allo shader, per il quale l'applicazione deve allocare manualmente tramite l'heap perché fa riferimento a descrittori diversi tra le estrazioni. 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 dello 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 è dotato di questa flessibilità per gli altri tipi di descrittore (RTV, DSV, IBV, VBV, SOV).

Sottorilevazione

In Direct3D 12 l'app ha un controllo di basso livello sulla gestione della memoria. Nelle versioni precedenti di Direct3D, incluso Direct3D 11, esisterebbe un'allocazione per risorsa. In Direct3D 12, l'app può usare l'API per allocare un blocco di memoria di grandi dimensioni, maggiore di qualsiasi singolo oggetto necessario. Al termine, l'app può creare descrittori per puntare a sezioni di quel blocco di memoria di grandi dimensioni. Questo processo di decisione su cosa inserire (blocchi più piccoli all'interno del blocco di grandi dimensioni) è noto come suballocation. Consentendo all'app di eseguire questa operazione, è possibile ottenere miglioramenti nell'uso efficiente del calcolo e della memoria. Ad esempio, viene eseguito il rendering obsoleto della ridenominazione delle risorse. Al posto di questo, le app possono usare i limiti per determinare quando viene usata una determinata risorsa e quando non è fencing nelle esecuzioni dell'elenco di comandi in cui l'elenco di comandi richiede l'uso di quella particolare risorsa.

Liberare risorse

Prima che sia possibile liberare qualsiasi memoria associata alla pipeline, è necessario che la GPU venga terminata.

L'attesa del rendering dei fotogrammi è probabilmente il modo più grossolano per essere certi che la GPU sia terminata. Con una granularità più fine, è possibile usare di nuovo le recinzioni, quando un comando viene registrato in un elenco di comandi che si vuole tenere traccia del completamento, inserire un recinto 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 passato un limite specificato sulla GPU, che indica che tutto prima del completamento, oppure è possibile richiedere che venga generato un evento della CPU quando il limite è passato (che l'app può essere in attesa con un thread di sospensione). In Direct3D 11 questo era EnqueueSetEvent().