In che modo la CPU e la GPU interagiscono nella visualizzazione della grafica del computer?


58

Qui puoi vedere uno screenshot di un piccolo programma C ++ chiamato Triangle.exe con un triangolo rotante basato sull'API OpenGL.

inserisci qui la descrizione dell'immagine

Certamente un esempio molto semplice, ma penso che sia applicabile ad altre operazioni con schede grafiche.

Ero solo curioso e volevo conoscere l'intero processo facendo doppio clic su Triangle.exe in Windows XP fino a quando non vedo il triangolo che ruota sul monitor. Cosa succede, come interagiscono CPU (che gestisce per prima il .exe) e GPU (che alla fine emette il triangolo sullo schermo)?

Immagino che la visualizzazione di questo triangolo rotante sia principalmente il seguente hardware / software:

Hardware

  • HDD
  • Memoria di sistema (RAM)
  • processore
  • Memoria video
  • GPU
  • display LCD

Software

  • Sistema operativo
  • API DirectX / OpenGL
  • Driver Nvidia

Qualcuno può spiegare il processo, magari con una sorta di diagramma di flusso per l'illustrazione?

Non dovrebbe essere una spiegazione complessa che copre ogni singolo passaggio (suppongo che andrebbe oltre l'ambito), ma una spiegazione che può seguire un tecnico IT intermedio.

Sono abbastanza sicuro che molte persone che si definiscono professionisti IT non sono in grado di descrivere correttamente questo processo.


Il tuo dilemma sarebbe finito se potessi considerare la GPU un'estensione della CPU!
KawaiKx

Risposte:


55

Ho deciso di scrivere un po 'sull'aspetto della programmazione e su come i componenti si parlano. Forse farà luce su alcune aree.

La presentazione

Cosa serve per avere quella singola immagine, che hai pubblicato nella tua domanda, disegnata sullo schermo?

Esistono molti modi per disegnare un triangolo sullo schermo. Per semplicità, supponiamo che non siano stati utilizzati buffer di vertici. (Un buffer di vertici è un'area di memoria in cui vengono memorizzate le coordinate.) Supponiamo che il programma abbia semplicemente detto alla pipeline di elaborazione grafica di ogni singolo vertice (un vertice è solo una coordinata nello spazio) in una riga.

Ma , prima di poter disegnare qualsiasi cosa, dobbiamo prima eseguire delle impalcature. Vedremo perché dopo:

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();

// Drawing Using Triangles
glBegin(GL_TRIANGLES);

  // Red
  glColor3f(1.0f,0.0f,0.0f);
  // Top Of Triangle (Front)
  glVertex3f( 0.0f, 1.0f, 0.0f);

  // Green
  glColor3f(0.0f,1.0f,0.0f);
  // Left Of Triangle (Front)
  glVertex3f(-1.0f,-1.0f, 1.0f);

  // Blue
  glColor3f(0.0f,0.0f,1.0f);
  // Right Of Triangle (Front)
  glVertex3f( 1.0f,-1.0f, 1.0f);

// Done Drawing
glEnd();

Quindi cosa ha fatto?

Quando si scrive un programma che desidera utilizzare la scheda grafica, di solito si sceglie una sorta di interfaccia per il driver. Alcune interfacce ben note al driver sono:

  • OpenGL
  • Direct3D
  • CUDA

Per questo esempio rimarremo con OpenGL. Ora, la tua interfaccia con il driver è ciò che ti offre tutti gli strumenti necessari per far dialogare il tuo programma con la scheda grafica (o con il driver, che quindi comunica con la scheda).

Questa interfaccia è destinata a fornirti determinati strumenti . Questi strumenti prendono la forma di un'API che puoi chiamare dal tuo programma.

Quell'API è ciò che vediamo essere utilizzato nell'esempio sopra. Diamo un'occhiata più da vicino.

L'impalcatura

Prima di poter davvero eseguire qualsiasi disegno reale, dovrai eseguire una configurazione . Devi definire il tuo viewport (l'area che verrà effettivamente renderizzata), la tua prospettiva (la telecamera nel tuo mondo), quale anti-aliasing userai (per appianare il bordo del tuo triangolo) ...

Ma non vedremo nulla di tutto ciò. Daremo solo un'occhiata alle cose che dovrete fare in ogni fotogramma . Piace:

Svuotare lo schermo

La pipeline grafica non cancella lo schermo per ogni frame. Dovrai dirlo. Perché? Ecco perché:

inserisci qui la descrizione dell'immagine

Se non cancelli lo schermo, disegnerai semplicemente su di esso ogni fotogramma. Ecco perché chiamiamo glClearcon il GL_COLOR_BUFFER_BITset. L'altro bit ( GL_DEPTH_BUFFER_BIT) dice a OpenGL di cancellare il buffer di profondità . Questo buffer viene utilizzato per determinare quali pixel sono davanti (o dietro) altri pixel.

Trasformazione

inserisci qui la descrizione dell'immagine
Fonte immagine

La trasformazione è la parte in cui prendiamo tutte le coordinate di input (i vertici del nostro triangolo) e applichiamo la nostra matrice ModelView. Questa è la matrice che spiega come il nostro modello (i vertici) viene ruotato, ridimensionato e tradotto (spostato).

Successivamente, applichiamo la nostra matrice di proiezione. Questo sposta tutte le coordinate in modo che siano rivolte correttamente verso la nostra fotocamera.

Ora ci trasformiamo ancora una volta, con la nostra matrice Viewport. Facciamo questo per adattare il nostro modello alle dimensioni del nostro monitor. Ora abbiamo una serie di vertici che sono pronti per essere renderizzati!

Torneremo alla trasformazione un po 'più tardi.

Disegno

Per disegnare un triangolo, possiamo semplicemente dire a OpenGL di iniziare un nuovo elenco di triangoli chiamando glBegincon la GL_TRIANGLEScostante.
Ci sono anche altre forme che puoi disegnare. Come una striscia triangolare o una ventola triangolare . Queste sono principalmente ottimizzazioni, in quanto richiedono meno comunicazioni tra la CPU e la GPU per disegnare la stessa quantità di triangoli.

Successivamente, possiamo fornire un elenco di insiemi di 3 vertici che dovrebbero costituire ciascun triangolo. Ogni triangolo usa 3 coordinate (dato che siamo nello spazio 3D). Inoltre, fornisco anche un colore per ciascun vertice, chiamando glColor3f prima di chiamare glVertex3f.

La tonalità tra i 3 vertici (i 3 angoli del triangolo) viene calcolata automaticamente da OpenGL . Interpolerà il colore su tutta la faccia del poligono.

Interazione

Ora, quando fai clic sulla finestra. L'applicazione deve solo acquisire il messaggio della finestra che segnala il clic. Quindi è possibile eseguire qualsiasi azione nel programma desiderato.

Questo diventa molto più difficile quando vuoi iniziare a interagire con la tua scena 3D.

Devi prima sapere chiaramente a quale pixel l'utente ha fatto clic sulla finestra. Quindi, tenendo conto della tua prospettiva , puoi calcolare la direzione di un raggio, dal punto del mouse fai clic sulla tua scena. È quindi possibile calcolare se qualsiasi oggetto nella scena si interseca con quel raggio . Ora sai se l'utente ha fatto clic su un oggetto.

Quindi, come si fa a farlo ruotare?

Trasformazione

Sono a conoscenza di due tipi di trasformazioni che vengono generalmente applicate:

  • Trasformazione basata su matrice
  • Trasformazione basata sull'osso

La differenza è che le ossa influenzano i singoli vertici . Le matrici influenzano sempre tutti i vertici disegnati allo stesso modo. Diamo un'occhiata a un esempio.

Esempio

In precedenza, abbiamo caricato la nostra matrice di identità prima di disegnare il nostro triangolo. La matrice identità è quella che semplicemente non fornisce alcuna trasformazione . Quindi, qualunque cosa io disegni, è influenzata solo dalla mia prospettiva. Quindi, il triangolo non verrà ruotato affatto.

Se voglio ruotarlo ora, potrei fare io stesso la matematica (sulla CPU) e semplicemente chiamare glVertex3fcon altre coordinate (che sono ruotate). Oppure potrei lasciare che la GPU faccia tutto il lavoro, chiamando glRotatefprima di disegnare:

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               

amountè, ovviamente, solo un valore fisso. Se vuoi animare , dovrai tenerne traccia amounte aumentarlo ad ogni fotogramma.

Quindi, aspetta, cosa è successo a tutti i discorsi sulla matrice prima?

In questo semplice esempio, non dobbiamo preoccuparci delle matrici. Chiamiamo semplicemente glRotatefe si occupa di tutto ciò per noi.

glRotateproduce una rotazione di anglegradi attorno al vettore xyz. La matrice corrente (vedi glMatrixMode ) viene moltiplicata per una matrice di rotazione con il prodotto che sostituisce la matrice corrente, come se glMultMatrix fosse chiamato con la seguente matrice come argomento:

x 2 ⁡ 1 - c + cx ⁢ y ⁡ 1 - c - z ⁢ sx ⁢ z ⁡ 1 - c + y ⁢ s 0 y ⁢ x ⁡ 1 - c + z ⁢ sy 2 ⁡ 1 - c + cy ⁢ z ⁡ 1 - c - x ⁢ s 0 x ⁢ z ⁡ 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x ⁢ sz 2 ⁡ 1 - c + c 0 0 0 0 1

Bene, grazie per quello!

Conclusione

Ciò che diventa ovvio è che si parla molto con OpenGL. Ma non ci sta dicendo niente. Dov'è la comunicazione?

L'unica cosa che OpenGL ci sta dicendo in questo esempio è quando ha finito . Ogni operazione richiederà un certo periodo di tempo. Alcune operazioni richiedono incredibilmente tempo, altre sono incredibilmente veloci.

Inviare un vertice alla GPU sarà così veloce, non saprei nemmeno come esprimerlo. L'invio di migliaia di vertici dalla CPU alla GPU, ogni singolo frame, è molto probabilmente nessun problema.

La cancellazione dello schermo può richiedere un millisecondo o peggio (tieni presente che di solito hai solo 16 millisecondi di tempo per disegnare ogni fotogramma), a seconda della dimensione del tuo viewport. Per cancellarlo, OpenGL deve disegnare ogni singolo pixel con il colore che vuoi cancellare, che potrebbe essere milioni di pixel.

Oltre a ciò, possiamo praticamente chiedere a OpenGL solo le funzionalità del nostro adattatore grafico (risoluzione massima, anti-aliasing massimo, profondità di colore massima, ...).

Ma possiamo anche riempire una trama di pixel che hanno ciascuno un colore specifico. Ogni pixel contiene quindi un valore e la trama è un "file" gigante pieno di dati. Possiamo caricarlo nella scheda grafica (creando un buffer di trama), quindi caricare uno shader , dire a quello shader di utilizzare la nostra trama come input ed eseguire calcoli estremamente pesanti sul nostro "file".

Possiamo quindi "rendere" il risultato del nostro calcolo (sotto forma di nuovi colori) in una nuova trama.

È così che puoi far funzionare la GPU per te in altri modi. Suppongo che CUDA si esibisca in modo simile a quell'aspetto, ma non ho mai avuto l'opportunità di lavorarci.

Abbiamo toccato solo leggermente l'intero argomento. La programmazione grafica 3D è un inferno di una bestia.

inserisci qui la descrizione dell'immagine
Fonte immagine


38

È difficile capire esattamente cosa non capisci.

La GPU ha una serie di registri mappati dal BIOS. Ciò consente alla CPU di accedere alla memoria della GPU e indica alla GPU di eseguire le operazioni. La CPU inserisce i valori in quei registri per mappare parte della memoria della GPU in modo che la CPU possa accedervi. Quindi carica le istruzioni in quella memoria. Quindi scrive un valore in un registro che dice alla GPU di eseguire le istruzioni che la CPU ha caricato nella sua memoria.

Le informazioni sono costituite dal software che la GPU deve eseguire. Questo software è in bundle con il driver e quindi il driver gestisce la suddivisione della responsabilità tra CPU e GPU (eseguendo parti del suo codice su entrambi i dispositivi).

Il driver quindi gestisce una serie di "finestre" nella memoria GPU che la CPU può leggere e scrivere. In generale, il modello di accesso prevede che la CPU scriva istruzioni o informazioni nella memoria GPU mappata e quindi istruisca la GPU, attraverso un registro, a eseguire tali istruzioni o elaborare tali informazioni. Le informazioni includono la logica dello shader, le trame e così via.


1
Grazie per la tua spiegazione. Fondamentalmente quello che non ho capito è come il set di istruzioni della CPU comunica con il set di istruzioni della GPU, ma ovviamente è il driver che fa quella parte. Questo è ciò che intendevo con strati di astrazione.
JohnnyFromBF,

2
Non è previsto alcun set di istruzioni CPU. Il driver e il runtime compilano CUDA, OpenGL, Direct3D, ecc. In programmi / kernel GPU nativi, che vengono quindi caricati anche nella memoria del dispositivo. Il buffer dei comandi si riferisce quindi a quelli come qualsiasi altra risorsa.
Axel Gneiting,

2
Non sono sicuro a quali programmi ti riferisci (che girano sulla GPU e sono inclusi con i driver). La gpu è in gran parte hardware a funzione fissa e gli unici programmi che eseguirà sono shader, forniti dall'applicazione, non dal driver. Il driver compila solo questi programmi, quindi li carica nella memoria della GPU.
Ben Richards,

1
@ sidran32: Ad esempio, nell'architettura Kepler di nVidia, kernel, stream ed eventi sono creati da software che gira sulla GPU, non (di solito) la CPU. Il software lato GPU gestisce anche RDMA. Tutto quel software viene caricato nella memoria GPU dal driver e viene eseguito come "mini-OS" sulla GPU che gestisce il lato GPU della coppia cooperante CPU / GPU.
David Schwartz,

@DavidSchwartz Ho dimenticato le attività di calcolo della GPU. Tuttavia, si comportano comunque in modo simile agli shader, nell'implementazione, comunque. Non lo definirei un "mini-OS", tuttavia, poiché non ha le stesse funzionalità tipicamente associate ai sistemi operativi. È ancora un software molto specializzato, poiché la GPU non è progettata come una CPU (per una buona ragione).
Ben Richards,

13

Ero solo curioso e volevo conoscere l'intero processo facendo doppio clic su Triangle.exe in Windows XP fino a quando non vedo il triangolo che ruota sul monitor. Cosa succede, come interagiscono CPU (che gestisce per prima il .exe) e GPU (che alla fine emette il triangolo sullo schermo)?

Supponiamo che tu sappia effettivamente come un eseguibile viene eseguito su un sistema operativo e come tale eseguibile viene inviato dalla tua GPU al monitor, ma non sai cosa sta succedendo nel mezzo. Quindi, diamo un'occhiata da un aspetto hardware ed estendiamo ulteriormente la risposta dell'aspetto del programmatore ...

Qual è l'interfaccia tra CPU e GPU?

Utilizzando un driver , la CPU può comunicare attraverso la scheda madre come PCI con la scheda grafica e inviare comandi ad essa per eseguire alcune istruzioni GPU, accedere / aggiornare la memoria GPU , caricare un codice da eseguire sulla GPU e altro ...

Ma non puoi davvero parlare direttamente all'hardware o al driver dal codice; quindi, ciò dovrà avvenire tramite API come OpenGL, Direct3D, CUDA, HLSL, Cg. Mentre il primo esegue le istruzioni GPU e / o aggiorna la memoria GPU, il secondo eseguirà effettivamente il codice sulla GPU in quanto sono linguaggi di fisica / shader.

Perché eseguire il codice sulla GPU e non sulla CPU?

Mentre la CPU è brava a far funzionare i nostri programmi giornalieri di workstation e server, non si è pensato molto a tutta quella grafica brillante che si vede nei giochi di questi giorni. Ai tempi c'erano renderizzatori software che facevano il trucco da alcune cose 2D e 3D, ma erano molto limitanti. Quindi, qui è dove la GPU è entrata in gioco.

La GPU è ottimizzata per uno dei calcoli più importanti in grafica, Matrix Manipulation . Mentre la CPU deve calcolare ciascuna moltiplicazione in una manipolazione di matrice una per una (in seguito, cose come 3DNow! E SSE recuperate), la GPU può fare tutte quelle moltiplicazioni contemporaneamente! Parallelismo.

Ma i calcoli paralleli non sono l'unico motivo, un altro motivo è che la GPU è molto più vicina alla memoria video che lo rende molto più veloce rispetto al dover fare round trip attraverso la CPU, ecc ...

In che modo queste istruzioni GPU / memoria / codice mostrano la grafica?

C'è un pezzo mancante per far funzionare tutto questo, abbiamo bisogno di qualcosa che possiamo scrivere su cui possiamo quindi leggere e inviare allo schermo. Possiamo farlo creando un framebuffer . Qualunque operazione tu faccia, alla fine aggiornerai i pixel nel framebuffer; che oltre alla posizione contengono anche informazioni su colore e profondità.

Facciamo un esempio in cui volevi disegnare uno sprite di sangue (un'immagine) da qualche parte; in primo luogo, la struttura ad albero stessa viene caricata nella memoria GPU, il che rende facile ridisegnarla a piacimento. Quindi, per disegnare effettivamente lo sprite da qualche parte, possiamo tradurre lo sprite usando i vertici (mettendolo nella giusta posizione), rasterizzandolo (trasformandolo da un oggetto 3D in pixel) e aggiornando il framebuffer. Per avere un'idea migliore, ecco un diagramma di flusso della pipeline OpenGL da Wikipedia:

Questo è l'essenza principale dell'intera idea grafica, più ricerche sono i compiti per il lettore.


7

Per semplificare le cose, possiamo descriverlo in questo modo. Alcuni indirizzi di memoria sono riservati (dal BIOS e / o dal sistema operativo) non per la RAM ma per la scheda video. Tutti i dati scritti con quei valori (puntatori) vanno sulla carta. Quindi, in teoria, qualsiasi programma può scrivere direttamente sul videocard solo conoscendo l'intervallo di indirizzi e questo è esattamente come è stato fatto ai vecchi tempi. In pratica con i moderni sistemi operativi, questo è gestito dal driver video e / o dalla libreria grafica in alto (DirectX, OpenGL ecc.).


1
-1 chiede come una chiamata API DirectX dalla CPU può comunicare con la GPU e la tua risposta è "è gestita dal driver e / o DirectX" ? Questo non spiega anche come eseguire il codice personalizzato (ala CUDA).
BlueRaja - Danny Pflughoeft,

3
per favore impara a leggere. Ho detto scrivendo a specifici indirizzi di memoria riservati alla GPU anziché alla RAM. E questo spiega come è possibile eseguire tutto. Un intervallo di memoria è registrato per una scheda. Tutto ciò che scrivi in ​​quell'intervallo va alla GPU che esegue l'elaborazione dei vertici, CUDA, qualunque cosa.
AZ.

5

Le GPU sono generalmente guidate da buffer DMA. In altre parole, il driver compila i comandi che riceve dal programma dello spazio utente in un flusso di istruzioni (cambia stato, disegna in questo modo, cambia contesti, ecc.), Che vengono quindi copiati nella memoria del dispositivo. Indica quindi alla GPU di eseguire questo buffer di comando tramite un registro PCI o metodi simili.

Quindi ad ogni richiamo di richiamo ecc. Ciò che accade è che il driver dello spazio utente compilerà il comando, che quindi chiama il driver dello spazio del kernel tramite un interrupt e che infine invia il buffer dei comandi alla memoria del dispositivo e indica alla GPU di avviare il rendering.

Su console puoi anche divertirti a fare tutto da solo, specialmente su PS3.


0

Penso che la CPU invii i dati video alla GPU attraverso il bus e poi la GPU li visualizzi. GPU così più veloce in grado di gestire più dati dalla CPU. In questo modo parte dell'elaborazione di cpuoffload su GPU. Pertanto si ottiene una maggiore velocità nei giochi.

È un po 'come la RAM in cui la CPU memorizza le cose in modo che possa essere caricata ed elaborata rapidamente. Entrambi rendono i giochi più veloci.

Oppure la scheda audio o la scheda di rete funzionano con lo stesso principio, ovvero ottenere dati e scaricare parte del lavoro della CPU.


Questo duplica un'altra risposta e non aggiunge nuovi contenuti. Per favore, non pubblicare una risposta a meno che tu non abbia effettivamente qualcosa di nuovo da contribuire.
DavidPostill

0

Penso che op non sia sicuro di cosa esattamente la cpu stia dicendo alla scheda grafica e perché i comandi relativi alla grafica (come i comandi opengl o direct3d) non vengano inviati direttamente alla GPU.

La CPU dice semplicemente alla GPU cosa rendere. Tutte le istruzioni passano prima attraverso la CPU dove vengono installate / inizializzate affinché la GPU esegua effettivamente il rendering.

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.