Informazioni su rendering, batch, scheda grafica, prestazioni ecc. + XNA?


12

So che il titolo è un po 'vago ma è difficile descrivere ciò che sto davvero cercando, ma qui va.

Quando si tratta di rendering della CPU, le prestazioni sono per lo più facili da stimare e semplici, ma quando si tratta di GPU a causa della mia mancanza di informazioni tecniche di base, sono all'oscuro. Sto usando XNA, quindi sarebbe bello se la teoria potesse essere correlata a questo.

Quindi quello che voglio veramente sapere è, cosa succede quando e dove (CPU / GPU) quando si eseguono azioni specifiche di disegno? Che cos'è un batch? Che influenza hanno gli effetti, le proiezioni ecc.? I dati persistono sulla scheda grafica o vengono trasferiti su ogni passaggio? Quando si parla di larghezza di banda, si parla di una larghezza di banda interna della scheda grafica o della pipeline dalla CPU alla GPU?
Nota: in realtà non sto cercando informazioni su come avviene il processo di disegno, sono affari della GPU, sono interessato a tutte le spese generali che lo precedono.

Mi piacerebbe capire cosa succede quando eseguo l'azione X, per adattare le mie architetture e pratiche a questo.

Tutti gli articoli (possibilmente con esempi di codice), informazioni, collegamenti, tutorial che forniscono maggiori informazioni su come scrivere giochi migliori sono molto apprezzati. Grazie :)


2
Sebbene fosse originariamente XNA, ho aggiunto il tag DirectX, in quanto questa è la tecnologia di base: potrebbe aiutarti a ottenere risposte migliori. Dai un'occhiata anche a questa risposta che potrebbe darti un buon punto di partenza.
Andrew Russell,

@AndrewRussell Grazie mille :). In realtà ho già letto vari articoli sull'argomento incluso quello. Ma non ha coperto tutto ciò che vorrei sapere.
Aidiakapi,

Risposte:


20

Mi piace pensare alle prestazioni in termini di " limiti ". È un modo pratico per concettualizzare un sistema abbastanza complicato e interconnesso. Quando hai un problema di prestazioni, fai la domanda: "Quali limiti sto colpendo?" (Oppure: "Sono associato CPU / GPU?")

Puoi suddividerlo in più livelli. Al livello più alto hai la CPU e la GPU. Potrebbe essere associato alla CPU (GPU inattiva in attesa di CPU) o associato alla GPU (CPU in attesa su GPU). Ecco un buon post sul blog sull'argomento.

Puoi scomporlo ulteriormente. Sul lato CPU , potresti utilizzare tutti i tuoi cicli sui dati già presenti nella cache della CPU. Oppure potresti avere una memoria limitata , lasciando la CPU inattiva in attesa che i dati arrivino dalla memoria principale ( quindi ottimizza il layout dei dati ). Potresti analizzarlo ulteriormente.

(Mentre sto facendo una vasta panoramica delle prestazioni relative a XNA, farò notare che un'allocazione di un tipo di riferimento ( classnon struct), sebbene normalmente economica, potrebbe innescare il garbage collector, che brucerà molti cicli, specialmente su Xbox 360 . Vedi qui per i dettagli).

Per quanto riguarda la GPU , inizierò indicandoti questo eccellente post sul blog che ha molti dettagli. Se desideri un livello di dettaglio folle sulla pipeline, leggi questa serie di post sul blog . ( Eccone uno più semplice ).

Per dirla semplicemente, alcuni dei più grandi sono: " limite di riempimento " (quanti pixel puoi scrivere sul backbuffer - spesso quanto sovraccarico puoi avere), " limite di shader " (quanto complicati possono essere i tuoi shader e quanti dati è possibile inviare), " limite texture-fetch / texture-larghezza di banda " (quanti dati texture è possibile accedere).

E, ora, arriviamo a quello grande - che è quello che stai davvero chiedendo - dove la CPU e la GPU devono interagire (tramite le varie API e driver). Liberamente c'è il " limite batch " e la " larghezza di banda ". (Nota che la prima parte della serie che ho menzionato in precedenza approfondisce i dettagli.)

Ma, fondamentalmente, un batch ( come già sapete ) accade ogni volta che chiamate una delle GraphicsDevice.Draw*funzioni (o parte di XNA, come SpriteBatch, fa questo per voi). Come avrete sicuramente letto, ne ottenete alcune migliaia * di questi per frame. Questo è un limite della CPU, quindi compete con l'altro utilizzo della CPU. Fondamentalmente è il driver a impacchettare tutto ciò che gli hai detto di disegnare e inviarlo alla GPU.

E poi c'è la larghezza di banda della GPU. Ecco quanti dati grezzi puoi trasferire lì. Ciò include tutte le informazioni sullo stato che vanno con i batch - tutto dall'impostazione dello stato di rendering e delle costanti / parametri dello shader (che include cose come matrici world / view / project), ai vertici quando si usano le DrawUser*funzioni. Esso comprende anche eventuali chiamate verso SetDatae GetDatain trame, tamponi, ecc vertex

A questo punto dovrei dire che tutto ciò su cui puoi fare affidamento SetData(trame, buffer dei vertici e degli indici, ecc.), Così come Effects - rimane nella memoria della GPU. Non viene costantemente inviato nuovamente alla GPU. Un comando draw che fa riferimento a quei dati viene semplicemente inviato con un puntatore a tali dati.

(Inoltre: puoi solo inviare comandi di disegno dal thread principale, ma puoi farlo SetDatasu qualsiasi thread.)

XNA complica le cose un po 'con i suoi rendere le classi di stato ( BlendState, DepthStencilState, ecc). Questi dati di stato viene inviato per chiamata draw (in ogni lotto). Non sono sicuro al 100%, ma ho l'impressione che sia inviato pigramente (invia solo lo stato che cambia). In entrambi i casi, i cambiamenti di stato sono economici fino al punto di libero, rispetto al costo di un batch.

Infine, l'ultima cosa da menzionare è la pipeline GPU interna . Non vuoi forzarlo a scaricare scrivendo sui dati che deve ancora leggere o leggendo i dati che deve ancora scrivere. Un flush della pipeline significa che attende che le operazioni finiscano, in modo che tutto sia in uno stato coerente quando si accede ai dati.

I due casi GetDataparticolari a RenderTarget2Dcui prestare attenzione sono: fare appello a qualcosa di dinamico, in particolare su un oggetto su cui la GPU potrebbe scrivere. Questo è estremamente negativo per le prestazioni - non farlo.

L'altro caso si rivolge SetDataai buffer vertice / indice. Se devi farlo spesso, usa un DynamicVertexBuffer(anche DynamicIndexBuffer). Questi consentono alla GPU di sapere che cambieranno spesso e di fare un po 'di buffering interno per evitare lo svuotamento della pipeline.

(Si noti inoltre che i buffer dinamici sono più veloci dei DrawUser*metodi, ma devono essere pre-allocati alla dimensione massima richiesta.)

... E questo è praticamente tutto ciò che so sulle prestazioni di XNA :)


Grazie mille! Questo è esattamente quello che stavo cercando e sperando :).
Aidiakapi,

1
Alcune centinaia di batch per frame sembrano eccessivamente pessimisti. La regola empirica che ho sempre sentito è da 2K a 3K batch per frame. Alcuni giochi sono noti per ottenere fino a 10 KB su PC, ma penso che ciò richieda grande cura per raggiungere.
Nathan Reed,

Giusto. La cifra "poche centinaia" deriva dalla carta "batch batch batch", che elenca "25k batch / s al 100% di una CPU da 1 GHz". Ma quel documento ha ormai un decennio e da allora i driver e le CPU sono notevolmente migliorati. Aggiornerò questo (e i miei altri) per leggere "alcune migliaia".
Andrew Russell,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.