Copia e accesso ai dati delle risorse (Direct3D 10)

Non è più necessario pensare alle risorse come create nella memoria video o nella memoria di sistema. Oppure se il runtime deve gestire la memoria. Grazie all'architettura del nuovo WDDM (Windows Display Driver Model), le applicazioni creano ora risorse Direct3D 10 con flag di utilizzo diversi per indicare come l'applicazione intende usare i dati delle risorse. Il nuovo modello di driver virtualizza la memoria usata dalle risorse; diventa quindi responsabilità del gestore del sistema operativo/driver/memoria di inserire le risorse nell'area più efficiente della memoria possibile a causa dell'utilizzo previsto.

Il caso predefinito è che le risorse siano disponibili per la GPU. Naturalmente, avendo detto che, ci sono momenti in cui i dati delle risorse devono essere disponibili per la CPU. La copia dei dati delle risorse intorno a in modo che il processore appropriato possa accedervi senza influire sulle prestazioni richiede una conoscenza del funzionamento dei metodi API.

Copia dei dati delle risorse

Le risorse vengono create in memoria quando Direct3D esegue una chiamata Crea. Possono essere creati in memoria video, memoria di sistema o qualsiasi altro tipo di memoria. Poiché il modello di driver WDDM virtualizza questa memoria, le applicazioni non devono più tenere traccia del tipo di risorse di memoria create.

Idealmente, tutte le risorse si trovano in memoria video in modo che la GPU possa avere accesso immediato a loro. Tuttavia, a volte è necessario che la CPU legga i dati delle risorse o per la GPU per accedere ai dati delle risorse a cui è stata scritta la CPU. Direct3D 10 gestisce questi diversi scenari richiedendo all'applicazione di specificare un utilizzo e quindi offre diversi metodi per copiare i dati delle risorse quando necessario.

A seconda della modalità di creazione della risorsa, non è sempre possibile accedere direttamente ai dati sottostanti. Ciò può significare che i dati delle risorse devono essere copiati dalla risorsa di origine a un'altra risorsa accessibile dal processore appropriato. In termini di Direct3D 10, le risorse predefinite possono essere accessibili direttamente dalla GPU, le risorse dinamiche e di staging possono essere accessibili direttamente dalla CPU.

Dopo aver creato una risorsa , non è possibile modificarla. Copiare invece il contenuto di una risorsa in un'altra risorsa creata con un utilizzo diverso. Direct3D 10 offre questa funzionalità con tre metodi diversi. I primi due metodi( ID3D10Device::CopyResource e ID3D10Device::CopySubresourceRegion) sono progettati per copiare i dati delle risorse da una risorsa a un'altra. Il terzo metodo (ID3D10Device::UpdateSubresource) è progettato per copiare i dati dalla memoria a una risorsa.

Esistono due tipi principali di risorse: mappabili e non mappabili. Le risorse create con utilizzo di gestione dinamica o temporanea sono mappabili, mentre le risorse create con utilizzo predefinito o non modificabile non sono mappabili.

La copia dei dati tra le risorse non mappabili è molto veloce perché si tratta del caso più comune ed è stata ottimizzata per eseguire correttamente. Poiché queste risorse non sono direttamente accessibili dalla CPU, sono ottimizzate in modo che la GPU possa modificarle rapidamente.

La copia dei dati tra le risorse mappabili è più problematica perché le prestazioni dipendono dall'utilizzo con cui è stata creata la risorsa. Ad esempio, la GPU può leggere una risorsa dinamica abbastanza rapidamente, ma non può scriverle e la GPU non può leggere o scrivere direttamente nelle risorse di gestione temporanea.

Le applicazioni che desiderano copiare i dati da una risorsa con utilizzo predefinito a una risorsa con utilizzo di staging (per consentire alla CPU di leggere i dati, ad esempio il problema di readback della GPU) deve farlo con attenzione. Per altre informazioni su questo ultimo caso, vedere Accesso ai dati delle risorse .

Accesso ai dati delle risorse

L'accesso a una risorsa richiede il mapping della risorsa; il mapping significa essenzialmente che l'applicazione sta tentando di concedere alla CPU l'accesso alla memoria. Il processo di mapping di una risorsa in modo che la CPU possa accedere alla memoria sottostante può causare alcuni colli di bottiglia delle prestazioni e per questo motivo, è necessario prestare attenzione a come e quando eseguire questa attività.

Le prestazioni possono essere interrotte se l'applicazione tenta di eseguire il mapping di una risorsa al momento errato. Se l'applicazione tenta di accedere ai risultati di un'operazione prima del completamento dell'operazione, si verificherà uno stallo della pipeline.

L'esecuzione di un'operazione di mapping al momento errato potrebbe potenzialmente causare un grave calo delle prestazioni forzando la GPU e la CPU per sincronizzarsi tra loro. Questa sincronizzazione si verifica se l'applicazione vuole accedere a una risorsa prima che la GPU venga completata la copia in una risorsa che la CPU può eseguire il mapping.

La CPU può leggere solo dalle risorse create con il flag di D3D10_USAGE_STAGING. Poiché le risorse create con questo flag non possono essere impostate come output della pipeline, se la CPU vuole leggere i dati in una risorsa generata dalla GPU, i dati devono essere copiati in una risorsa creata con il flag di staging. L'applicazione può eseguire questa operazione usando i metodi ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion per copiare il contenuto di una risorsa in un altro. L'applicazione può quindi accedere a questa risorsa chiamando il metodo Map appropriato. Quando l'accesso alla risorsa non è più necessario, l'applicazione deve quindi chiamare il metodo Unmap corrispondente. Ad esempio, ID3D10Texture2D::Map e ID3D10Texture2D::Unmap. I diversi metodi Map restituiscono alcuni valori specifici a seconda dei flag di input. Per informazioni dettagliate, vedere La sezione Osservazioni mappa .

Nota

Quando l'applicazione chiama il metodo Map, riceve un puntatore ai dati della risorsa per accedere. Il runtime garantisce che il puntatore abbia un allineamento specifico, a seconda del livello di funzionalità. Per D3D_FEATURE_LEVEL_10_0 e superiore, il puntatore è allineato a 16 byte. Per D3D_FEATURE_LEVEL_10_0 inferiore, il puntatore è allineato a 4 byte. L'allineamento a 16 byte consente all'applicazione di eseguire operazioni ottimizzate per SSE sui dati in modo nativo, senza allineare o copiare.

 

Considerazioni sulle prestazioni

È consigliabile pensare a un PC come computer in esecuzione come architettura parallela con due tipi principali di processori: uno o più CPU e uno o più GPU. Come in qualsiasi architettura parallela, le prestazioni migliori vengono ottenute quando ogni processore è pianificato con attività sufficienti per impedire l'inattività e quando il lavoro di un processore non è in attesa sul lavoro di un altro.

Lo scenario peggiore per il parallelismo GPU/CPU è la necessità di forzare un processore ad attendere i risultati del lavoro svolto da un altro. Direct3D 10 tenta di rimuovere questo costo rendendo asincrono i metodi ID3D10Device::CopyResource e ID3D10Device::CopySubresourceRegion asincroni; la copia non è necessariamente eseguita dal momento in cui il metodo restituisce. Il vantaggio di questo è che l'applicazione non paga il costo delle prestazioni per copiare effettivamente i dati finché la CPU accede ai dati, ovvero quando viene chiamato Map. Se il metodo Map viene chiamato dopo la copia dei dati, non si verifica alcuna perdita di prestazioni. D'altra parte, se il metodo Map viene chiamato prima della copia dei dati, si verificherà uno stallo della pipeline.

Le chiamate asincrone in Direct3D 10 (che sono la maggior parte dei metodi e soprattutto le chiamate di rendering) vengono archiviate in quello che viene chiamato buffer dei comandi. Questo buffer è interno al driver grafico e viene usato per eseguire il batch di chiamate all'hardware sottostante in modo che il passaggio costoso dalla modalità utente alla modalità kernel in Microsoft Windows si verifichi più raramente possibile.

Il buffer dei comandi viene scaricato, causando così un commutatore in modalità utente/kernel, in una delle quattro situazioni, come indicato di seguito.

  1. Viene chiamato presente .
  2. ID3D10Device::Flush viene chiamato.
  3. Il buffer dei comandi è pieno; la dimensione è dinamica ed è controllata dal sistema operativo e dal driver grafico.
  4. La CPU richiede l'accesso ai risultati di un comando in attesa di esecuzione nel buffer dei comandi.

Delle quattro situazioni precedenti, il numero quattro è il più critico per le prestazioni. Se l'applicazione genera una chiamata ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion, questa chiamata viene accodata nel buffer dei comandi. Se l'applicazione tenta quindi di eseguire il mapping della risorsa di gestione temporanea che era la destinazione della chiamata di copia prima che il buffer di comando sia stato scaricato, si verificherà uno stallo della pipeline perché non solo la chiamata al metodo Copy deve essere eseguita, ma anche tutti gli altri comandi memorizzati nel buffer dei comandi devono essere eseguiti. Ciò causerà la sincronizzazione della GPU e della CPU perché la CPU sarà in attesa di accedere alla risorsa di gestione temporanea mentre la GPU svuota il buffer dei comandi e infine riempie la risorsa necessaria per la CPU. Al termine della copia, la CPU inizierà ad accedere alla risorsa di gestione temporanea, ma durante questo periodo la GPU sarà inattiva.

Questa operazione spesso in fase di esecuzione degraderà notevolmente le prestazioni. Per questo motivo, il mapping delle risorse create con l'utilizzo predefinito deve essere eseguito con attenzione. L'applicazione deve attendere abbastanza tempo per cui il buffer dei comandi deve essere svuotato e quindi tutti questi comandi terminano l'esecuzione prima di cercare di eseguire il mapping della risorsa di gestione temporanea corrispondente. Quanto tempo deve attendere l'applicazione? Almeno due fotogrammi perché questo consente di sfruttare al massimo il parallelismo tra le CPU e la GPU. Il modo in cui funziona la GPU è che, mentre l'applicazione elabora frame N inviando chiamate al buffer dei comandi, la GPU è occupato a eseguire le chiamate dal frame precedente, N-1.

Quindi, se un'applicazione vuole eseguire il mapping di una risorsa che ha origine nella memoria video e chiama ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion in frame N, questa chiamata inizierà effettivamente a essere eseguita al frame N+1, quando l'applicazione invia chiamate per il frame successivo. La copia deve essere completata al termine dell'elaborazione del frame N+2 dell'applicazione.

Frame Stato GPU/CPU
N
  • Problemi di CPU esegue il rendering delle chiamate per il frame corrente.
N+1
  • GPU che esegue chiamate inviate dalla CPU durante il frame N.
  • Problemi di CPU esegue il rendering delle chiamate per il frame corrente.
N+2
  • La GPU ha completato l'esecuzione di chiamate inviate dalla CPU durante il frame N. Risultati pronti.
  • GPU che esegue chiamate inviate dalla CPU durante il frame N+1.
  • Problemi di CPU esegue il rendering delle chiamate per il frame corrente.
N+3
  • La GPU ha completato l'esecuzione di chiamate inviate dalla CPU durante il frame N+1. Risultati pronti.
  • GPU che esegue chiamate inviate dalla CPU durante il frame N+2.
  • Problemi di CPU esegue il rendering delle chiamate per il frame corrente.
N+4 ...

 

Risorse (Direct3D 10)