CPU - Flusso dati memoria GPU [chiuso]


16

Sono un programmatore di grafica per principianti e mi sono chiesto di recente: in che modo i dati del modello (mesh e materiali) fluiscono dall'applicazione (memoria della CPU) alla scheda grafica (memoria della GPU?)? Supponi di avere un modello statico (ad esempio un edificio) che carico e configuro una volta e che non modifico per tutta la durata dell'app.

  • I suoi dati vengono inviati alla memoria GPU solo una volta e rimangono lì per sempre?
  • Quando il modello viene effettivamente reso ogni frame i processori GPU devono recuperare i suoi dati ogni volta dalla memoria GPU? Quello che voglio dire è - se avessi 2 modelli renderizzati più volte ciascuno - importerebbe se prima rendessi il primo più volte e poi il secondo più volte o se rendessi il primo solo una volta, il secondo solo una volta e continuato a interfogliarlo in quel modo? Potrei chiamare questa domanda "flusso interno di dati GPU" in questo senso.
  • Ovviamente le schede grafiche hanno una RAM limitata - quando non può contenere tutti i dati del modello necessari per il rendering di 1 frame, suppongo che continui a prenderne (parte) dalla RAM della CPU ogni frame, è corretto?

So che ci sono molti libri e cose su questo su Internet, ma forse hai alcune linee guida generali veloci su come gestire questo flusso di dati (quando inviare cosa e quanto, quando e come render)?

Modifica: ho dimenticato di fare una distinzione: c'è l' invio dei dati alla GPU e c'è l' impostazione / associazione dei buffer come correnti . Quest'ultimo causa qualche flusso di dati?

Edit2: Dopo aver letto il post di Raxvan, vorrei distinguere alcune azioni:

  • creazione di buffer con inizializzazione (come ha detto, posso archiviare i dati in CPU RAM o GPU)
  • aggiornamento dei dati del buffer (che credo sia semplice quando i dati sono conservati nella RAM della CPU e richiede il recupero dalla GPU alla RAM della CPU (e poi indietro) quando sono conservati nella RAM della GPU)
  • associando il buffer come attivo (è solo un modo per dire all'API che voglio che questo buffer sia renderizzato nella prossima chiamata di disegno e non fa nulla da solo ?)
  • API draw call (qui mi piacerebbe sentire da te cosa succede realmente lì)

Non sono un esperto in alcun modo, ma se stai usando OpenGL moderno (cioè non immediato) con VAO e VBO, i dati vengono inviati alla GPU e archiviati in VRAM ogni volta che usi uno della famiglia di comandi glBuffer. Quindi, ogni volta che lo disegni, i vertici rilevanti vengono prelevati da VRAM e resi. Se si tratta di un modello che si sposta, si tende a memorizzarlo staticamente e utilizzare le matrici per spostarsi dallo spazio modello allo spazio mondo / videocamera. Per quanto riguarda l'ultimo punto, non ho idea di cosa succede se si esaurisce la RAM. La mia ipotesi è che se si esaurisce la VRAM, i dati non vengono inviati, probabilmente con un codice di errore.
Polar

@Polar - non esattamente. GL in realtà non specifica in quale memoria è archiviato un oggetto buffer ed è persino libero di spostarlo in fase di esecuzione in base al modello di utilizzo. GL4.4 si rivolge in qualche modo a questo, ma nota che alla fine il meglio che può fornire è "una di quelle cose da suggerimento sciocco"; vedi opengl.org/registry/specs/ARB/buffer_storage.txt e in particolare i numeri 2 e 9.
Maximus Minimus,

1
@JimmyShelter Ah, grazie - sarebbe bello se avessimo meno di "quelle sciocchezze di suggerimenti" e una specifica più concreta.
Polar,

@Polar - la cosa fastidiosa è che ARB_buffer_storage avrebbe potuto evitare di includere un altro suggerimento, ma i designer hanno perso l'occasione. Vabbè, forse 4.5 finalmente riusciranno.
Maximus Minimus,

2
Si prega di non modificare le domande per "rispondere" alle risposte. Pubblica invece una nuova domanda.

Risposte:


12

I suoi dati vengono inviati alla memoria GPU solo una volta e rimangono lì per sempre?

Di solito sì, ma il driver è libero di fare ciò che è "ottimale", i dati potrebbero essere archiviati su VRAM o RAM o potrebbero essere semplicemente memorizzati nella cache qui è un atricle che spiega cosa succede effettivamente con il flusso VBO .

Ad esempio, se è stato contrassegnato come buffer openGL dinamico (ad es. VBO), è più probabile che sia archiviato nella RAM. La GPU usa l'accesso diretto alla memoria (DMA) per accedere direttamente al ram senza l'intervento della CPU, questo è controllato dal controller DMA nella scheda grafica e nel driver grafico ed è eseguito in modalità kernel.

Quando il modello viene effettivamente reso ogni frame i processori GPU devono recuperare i suoi dati ogni volta dalla memoria GPU, anche se un modello esegue il rendering di più tempi sequenziali?

Proprio come le CPU, le GPU sono autorizzate a riordinare le istruzioni GPU e le operazioni di accesso alla memoria (leggi: esecuzione fuori ordine ), quindi molto probabilmente la GPU gestirà lo scenario menzionato accedendo alla memoria che si trova nella sua cache (di solito si accede di recente ), ma a volte non può farlo.

Ovviamente le schede grafiche hanno una RAM limitata - quando non può contenere tutti i dati del modello necessari per il rendering di 1 frame, suppongo che continui a prenderne (parte) dalla RAM della CPU ogni frame, è corretto?

Non vuoi che questo accada. Tuttavia, indipendentemente dal fatto che ciò accada, la GPU inizierà a spostare la memoria tra RAM e VRAM (il processore dei comandi sulla GPU è responsabile per questo), il che renderà il rendering molto più lento, il che causerà lo stallo della GPU, perché dovrà attendere i dati da copiare da / a V / RAM.

C'è l'invio dei dati alla GPU e l'impostazione / associazione dei buffer come correnti. Quest'ultimo causa qualche flusso di dati?

Le GPU contengono un buffer dei comandi e tutti i comandi API vengono inviati a questo buffer, si noti che ciò può avvenire contemporaneamente ai dati copiati nella GPU. Il buffer dell'anello di comando è una coda di comunicazione tra CPU e GPU , qualsiasi comando che deve essere eseguito deve essere inviato alla coda in modo che possa essere eseguito dalla GPU. Proprio come qualsiasi operazione di associazione di nuovi buffer deve essere inviata alla GPU in modo che possa accedere a una posizione di memoria.

Questo è uno dei motivi per cui glBegin / glEnd è stato deprecato, l'invio di nuovi comandi richiede la sincronizzazione della coda (usando recinzioni / barriere di memoria).

inserisci qui la descrizione dell'immagine

Per quanto riguarda gli altri punti:

Creazione buffer con inizializzazione

È possibile allocare un buffer senza inizializzazione e conservarlo per un uso successivo. Oppure puoi allocare un buffer e copiare i dati contemporaneamente (parlando del livello API).

aggiornamento dei dati del buffer

È possibile utilizzare glMapBuffer per aggiornare la memoria sul lato GPU. se la memoria verrà copiata da / su RAM non è in realtà poart dello standard e varierà notevolmente a seconda del fornitore, del tipo di GPU e del driver.

API draw call (qui mi piacerebbe sentire da te cosa succede realmente lì).

Il mio secondo punto nella domanda principale riguarda questo.

associando il buffer come attivo (è solo un modo per dire all'API che voglio che questo buffer sia> renderizzato nella prossima chiamata di disegno e non fa nulla da solo?)

Pensa di associare come usare il thispuntatore in qualsiasi linguaggio orientato agli oggetti, anche se non esattamente lo stesso, tutte le chiamate API conseguenti saranno relative a quel buffer di bind.


3

In generale il confine e il coinvolgimento della cpu contro la gpu sono specifici sulla piattaforma, ma la maggior parte segue questo modello: cpu ha qualche ram, anche gpu e puoi spostare la memoria (in alcuni casi la ram è condivisa ma per il per semplicità, atteniamoci a separare gli arieti).

primo punto : i dati che si inizializzano è possibile scegliere di mantenerli nella RAM della CPU o nella RAM della GPU, e sono vantaggi per entrambi. Quando si esegue il rendering di qualcosa, la GPU deve eseguire il lavoro pesante, quindi è ovvio che i dati già presenti nel mem GPU forniranno prestazioni migliori. per la CPU deve prima inviare i dati alla GPU (che può scegliere di conservarli per un po ') e quindi eseguire il rendering.

secondo punto : ci sono molti trucchi nel rendering, ma il modo principale di fare è con i poligoni. Su un frame gpu eseguirà il rendering degli oggetti fatti di poligoni uno per uno e dopo aver terminato che la GPU invierà l'immagine al display. Non esiste un concetto come gli oggetti, ci sono solo poligoni e il modo in cui li metti insieme creerà un'immagine. il compito della GPU è proiettare quei poligoni da 3d a 2d e applicare l'effetto (se lo si desidera). I poligoni vanno solo in modo diretto CPU-> GPU-> SCREEN o GPU-> SCREEN direttamente (se i poligoni sono già nel ram GPU)

terzo punto : quando si esegue il rendering di animazioni, ad esempio, è meglio mantenere i dati vicini alla CPU perché lì esegue il lavoro pesante, non sarebbe ottimale mantenere i dati in GPU, spostarli nella CPU e tornare indietro di ogni frame. Esistono molti altri esempi come questo, ma in generale tutti i dati rimarranno vicini a chiunque stia eseguendo i calcoli. Di solito si desidera spostare il maggior numero possibile di dati nella RAM GPU per ottenere prestazioni.

L'effettivo invio dei dati alla gpu viene effettuato dall'API utilizzata (directx / opengl o altro) e il concetto di associazione e cose come questa sono solo astrazioni, in modo che l'API comprenda ciò che si desidera fare.

Modifica per la modifica:

  • buffer creation with initialisation: è come la differenza tra int a = new int[10]e a[0] = 0,a[1] = 1.... etc quando crei un buffer fai spazio per i dati e quando inizi i dati inserisci le cose che vuoi.

  • buffer data updatese è sul ram della CPU, allora vertex * verticespuoi giocarci, se non è lì, dovresti spostarlo dalla GPU vertex * vertices = map(buffer_id);(la mappa è una funzione mitologica che dovrebbe spostare i dati dalla GPU al ram della CPU, ha anche il suo opposto buffer_id = create_buffer(vertices);

  • binding the buffer as activeè solo un concetto che chiamano bindingrendering è un processo complesso ed è come chiamare una funzione con 10000 parametri. Binding è solo un termine che hanno usato per dire quale buffer va dove. Non c'è vera magia dietro questo termine, non converte, non sposta o rialloca i buffer, ma dice semplicemente al guidatore che nella prossima chiamata di disegno usa questo buffer.

  • API draw callDopo tutti i tamponi di rilegatura e impostazione, questo è il luogo in cui la gomma incontra la strada. La chiamata di disegno prenderà tutti i dati (o gli ID che indicano i dati) che hai specificato, li ha inviati alla GPU (se necessario) e dice alla GPU di iniziare a sgranocchiare i numeri. Questo non è del tutto vero su tutte le piattaforme, ci sono molte differenze, ma per semplificare la cosa il sorteggio dirà alla GPU di ... disegnare.


2

La risposta più corretta è, dipende da come la programmate, ma questa è una buona cosa di cui preoccuparsi. Mentre le GPU sono diventate incredibilmente veloci, la larghezza di banda da e verso la GPU RAM non lo è e sarà il tuo collo di bottiglia più frustrante.

I suoi dati vengono inviati alla memoria GPU solo una volta e rimangono lì per sempre?

Speriamo di si. Per la velocità di rendering, vuoi che il maggior numero possibile di dati si trovi sulla GPU, invece di inviarlo nuovamente ad ogni frame. I VBO servono a questo preciso scopo. Ci sono VBO sia statici che dinamici, il primo è il migliore per i modelli statici e il secondo è il migliore per i modelli i cui vertici cambieranno ogni frame (diciamo, un sistema di particelle). Anche quando si tratta di VBO dinamici, tuttavia, non si desidera inviare nuovamente tutti i vertici ad ogni frame; solo quelli che stanno cambiando.

Nel caso del tuo edificio, i dati del vertice rimarranno semplicemente lì, e l'unica cosa che cambia sono le tue matrici (modello / mondo, proiezione e vista).

Nel caso di un sistema di particelle, ho creato un VBO dinamico abbastanza grande da contenere il numero massimo di particelle che esisterà per quel sistema. Ogni fotogramma a cui invio i dati per le particelle emesse da quel fotogramma, insieme a un paio di uniformi, e tutto qui. Quando disegno, posso specificare un punto iniziale e finale in quel VBO, quindi non devo cancellare i dati delle particelle. Posso solo dire di non disegnare quelli.

Quando il modello viene effettivamente reso ogni frame i processori GPU devono recuperare i suoi dati ogni volta dalla memoria GPU? Quello che voglio dire è - se avessi 2 modelli renderizzati più volte ciascuno - importerebbe se prima rendessi il primo più volte e poi il secondo più volte o se rendessi il primo solo una volta, il secondo solo una volta e continuato a interfogliarlo in quel modo?

L'atto di inviare più chiamate di pesca invece di una sola è un limite molto più grande. Controlla il rendering istanziato; potrebbe aiutarti molto e rendere inutile la risposta a questa domanda. Ho avuto alcuni problemi con i driver che non ho ancora risolto, ma se riesci a farlo funzionare, allora il problema è stato risolto.

Ovviamente le schede grafiche hanno una RAM limitata - quando non può contenere tutti i dati del modello necessari per il rendering di 1 frame, suppongo che continui a prenderne (parte) dalla RAM della CPU ogni frame, è corretto?

Non vuoi esaurire la RAM della GPU. Se lo fai, allora cambia le cose in modo da non farlo. Nello scenario molto ipotetico che finisci, probabilmente si bloccherà in qualche modo, ma non l'ho mai visto accadere, quindi onestamente non lo so.

Ho dimenticato di fare una distinzione: c'è l'invio dei dati alla GPU e c'è l'impostazione / associazione dei buffer come correnti. Quest'ultimo causa qualche flusso di dati?

Nessun flusso di dati significativo, no. Ci sono dei costi, ma questo è vero per ogni riga di codice che scrivi. Scopri quanto ti costa, di nuovo, a cosa serve la profilazione.

creazione di buffer con inizializzazione

La risposta di Raxvan suona bene, ma non è del tutto accurata. In OpenGL, la creazione del buffer non riserva spazio. Se vuoi riservare spazio senza passare alcun dato, puoi chiamare glBufferData e passare null. (Vedi la sezione note qui .)

aggiornamento dei dati del buffer

Immagino che intendi glBufferData, o altre funzioni del genere, giusto? Qui è dove si verifica il trasferimento reale dei dati. (A meno che non passi null, come ho appena detto nell'ultimo paragrafo.)

associando il buffer come attivo (è solo un modo per dire all'API che voglio che questo buffer sia renderizzato nella prossima chiamata di disegno e non fa nulla da solo?)

Sì, ma può fare un po 'di più. Ad esempio, se si associa un VAO (oggetto array di vertici), quindi si associa un VBO, quel VBO viene associato al VAO. Successivamente, se associ nuovamente quel VAO e chiami glDrawArrays, saprà quale VBO disegnare.

Nota che mentre molti tutorial ti faranno creare un VAO per ogni VBO, mi è stato detto che non è l'uso previsto. Presumibilmente dovresti creare un VAO e usarlo con ogni VBO che abbia gli stessi attributi. Non l'ho ancora provato, quindi non posso dire con certezza se è meglio o peggio.

Chiamata di disegno API

Quello che succede qui è piuttosto semplice (dal nostro punto di vista). Supponi di associare un VAO, quindi chiama glDrawArrays. Si specifica un punto di partenza e un conteggio, ed esegue lo shader di vertice per ogni vertice in quell'intervallo, che a sua volta passa i suoi output lungo la linea. L'intero processo è però un altro saggio a sé stante.


"quindi il problema è stato risolto" Sì, l'istanziamento sarebbe di grande aiuto, ma senza di essa dovrei comunque effettuare un richiamo per ogni oggetto. La stessa cosa in entrambi i casi. Quindi mi chiedo se l'ordine conta.
NPS,

@NPS - Importa alcuni . Se sono ordinati in modo da non dover continuare a cambiare i tuoi attacchi, sì, probabilmente sarà un importo minuscolo più veloce. Ma se devi fare di tutto per ordinarli, probabilmente sarà molto, molto più costoso. Ci sono troppe variabili che dipendono dalla tua implementazione per dire molto di più.
Icy Defiance
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.