Copia e accesso ai dati delle risorse (Direct3D 10)
Non è più necessario considerare le risorse 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 ora creano risorse Direct3D 10 con diversi flag di utilizzo 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 sistema operativo, del driver o del gestore della memoria di posizionare le risorse nell'area di memoria più efficiente possibile in base all'utilizzo previsto.
Il caso predefinito è che le risorse siano disponibili per la GPU. Naturalmente, detto questo, ci sono momenti in cui i dati delle risorse devono essere disponibili per la CPU. La copia dei dati delle risorse in modo che il processore appropriato possa accedervi senza influire sulle prestazioni richiede una certa conoscenza del funzionamento dei metodi API.
Copia dei dati delle risorse
Le risorse vengono create in memoria quando Direct3D esegue una chiamata Create. 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 in cui vengono create.
Idealmente, tutte le risorse si trovano nella memoria video in modo che la GPU possa avere accesso immediato a tali risorse. Tuttavia, a volte è necessario che la CPU legga i dati della risorsa o che la GPU acceda ai dati della risorsa a cui la CPU ha scritto. 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 della risorsa 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, il suo utilizzo non può essere modificato. 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: mappabile e non mappabile. Le risorse create con utilizzi dinamici o di staging sono mappabili, mentre le risorse create con utilizzi predefiniti o non modificabili non sono mappabili.
La copia di dati tra risorse non mappabili è molto veloce perché questo è il caso più comune ed è stato ottimizzato per ottenere prestazioni buone. 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 staging.
Le applicazioni che desiderano copiare 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 GPU) devono farlo con attenzione. Per altri dettagli su questo ultimo caso, vedere Accesso ai dati delle risorse.
Accesso ai dati delle risorse
Per accedere a una risorsa è necessario eseguire 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 interrompersi se l'applicazione tenta di eseguire il mapping di una risorsa in un momento errato. Se l'applicazione tenta di accedere ai risultati di un'operazione prima del completamento dell'operazione, si verificherà un blocco della pipeline.
L'esecuzione di un'operazione di mapping in un momento errato potrebbe causare un grave calo delle prestazioni forzando la GPU e la CPU a eseguire la sincronizzazione tra loro. Questa sincronizzazione si verificherà se l'applicazione vuole accedere a una risorsa prima che la GPU abbia finito di copiare in una risorsa che la CPU può mappare.
La CPU può leggere solo dalle risorse create con il flag 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'altra. L'applicazione può quindi ottenere l'accesso 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 i dettagli, vedere la sezione Osservazioni mappa .
Nota
Quando l'applicazione chiama il metodo Map, riceve un puntatore ai dati delle risorse a cui accedere. Il runtime garantisce che il puntatore abbia un allineamento specifico, a seconda del livello di funzionalità . Per D3D_FEATURE_LEVEL_10_0 e versioni successive, il puntatore è allineato a 16 byte. Per un valore inferiore a D3D_FEATURE_LEVEL_10_0, il puntatore è allineato a 4 byte. L'allineamento a 16 byte consente all'applicazione di eseguire operazioni SSEottimizzate per i dati in modo nativo, senza riallineamento o copia.
Considerazioni sulle prestazioni
È consigliabile considerare un PC come un 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 si ottengono quando ogni processore è pianificato con attività sufficienti per impedire che funzioni inattive e quando il lavoro di un processore non è in attesa del 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; la copia non è necessariamente eseguita dal momento in cui il metodo restituisce. Il vantaggio di questo è che l'applicazione non paga il costo in termini di prestazioni della copia effettiva dei dati fino a quando la CPU non accede ai dati, ossia quando si chiama Map. Se il metodo Map viene chiamato dopo che i dati sono stati effettivamente copiati, non si verifica alcuna perdita di prestazioni. D'altra parte, se il metodo Map viene chiamato prima che i dati siano stati copiati, si verificherà un blocco della pipeline.
Le chiamate asincrone in Direct3D 10 (che sono la maggior parte dei metodi e soprattutto le chiamate di rendering) vengono archiviate in ciò che viene chiamato buffer dei comandi. Questo buffer è interno al driver grafico e viene usato per eseguire in batch chiamate all'hardware sottostante in modo che il passaggio costoso dalla modalità utente alla modalità kernel in Microsoft Windows si verifichi il più raramente possibile.
Il buffer dei comandi viene scaricato, causando così un passaggio dalla modalità utente a quella kernel, in una delle quattro situazioni seguenti.
- il presente viene chiamato.
- viene chiamato ID3D10Device::Flush.
- Il buffer dei comandi è pieno; la sua dimensione è dinamica ed è controllata dal sistema operativo e dal driver grafico.
- La CPU richiede l'accesso ai risultati di un comando in attesa dell'esecuzione nel buffer dei comandi.
Tra le quattro situazioni precedenti, il numero quattro è il più critico per le prestazioni. Se l'applicazione effettua una chiamata ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion, questa viene accodata nel buffer dei comandi. Se l'applicazione tenta quindi di eseguire il mapping della risorsa di staging che era la destinazione della chiamata di copia prima dello scaricamento del buffer dei comandi, si verificherà un blocco 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. In questo modo la GPU e la CPU verranno sincronizzate perché la CPU sarà in attesa di accedere alla risorsa di staging 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 staging, ma durante questo periodo la GPU sarà inattiva.
Eseguire frequentemente questa operazione in fase di esecuzione comporta un grave peggioramento delle 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 svuotare il buffer dei comandi e quindi completare l'esecuzione di tutti questi comandi prima di cercare di eseguire il mapping della risorsa di staging corrispondente. Quanto tempo deve attendere l'applicazione? Almeno due fotogrammi perché ciò consentirà il parallelismo tra le CPU e la GPU da sfruttare al massimo. Il funzionamento della GPU è che mentre l'applicazione sta elaborando frame N inviando chiamate al buffer dei comandi, la GPU sta eseguendo le chiamate dal frame precedente, N-1.
Pertanto, se un'applicazione vuole eseguire il mapping di una risorsa che ha origine nella memoria video e chiama ID3D10Device::CopyResource o ID3D10Device::CopySubresourceRegion al frame N, questa chiamata inizierà effettivamente a essere eseguita al frame N+1, quando l'applicazione invia chiamate per il fotogramma successivo. La copia deve essere completata al termine dell'elaborazione del frame N+2 dell'applicazione.
Cornice | Stato GPU/CPU |
---|---|
N |
|
N+1 |
|
N+2 |
|
N+3 |
|
N+4 | ... |
Argomenti correlati