Hur interagerar CPU och GPU med att ge data grafik?
Datorns processor för central bearbetningsenhet (CPU) och grafikbehandlingsenhet (GPU) växlar varje ögonblick du använder datorn för att ge dig ett skarpt och lyhörd visuellt gränssnitt. Läs vidare för att bättre förstå hur de arbetar tillsammans.
Foto av sskennel.
Dagens Question & Answer-session kommer till oss med tillstånd av SuperUser-en indelning av Stack Exchange, en community-drive-gruppering av Q & A-webbplatser.
Frågan
SuperUser-läsaren Sathya ställde frågan:
Här kan du se en skärmdump av ett litet C ++-program som heter Triangle.exe med en roterande triangel baserat på OpenGL API.
Visserligen ett mycket grundläggande exempel men jag tycker att det är tillämpligt på andra grafikkortsoperationer.
Jag var bara nyfiken och ville veta hela processen från att dubbelklicka på Triangle.exe under Windows XP tills jag kan se triangeln rotera på bildskärmen. Vad händer, hur fungerar CPU (som hanterar .exe) och GPU (som slutligen matar ut triangeln på skärmen) samverkar?
Jag antar att det är främst följande hårdvara / mjukvara bland andra som är involverade i att visa denna roterande triangel:
Hårdvara
- HDD
- Systemminne (RAM)
- CPU
- Videominne
- GPU
- LCD skärm
programvara
- Operativ system
- DirectX / OpenGL API
- Nvidia Driver
Kan någon förklara processen, kanske med någon form av flödesschema för illustration?
Det borde inte vara en komplicerad förklaring som täcker varje steg (gissning som skulle gå utöver omfattningen), men en förklaring som en mellanliggande IT-kille kan följa.
Jag är ganska säker på att många människor som till och med skulle kalla sig IT-personal kunde inte beskriva processen korrekt.
Svaret
Även om flera medlemmar i samhället svarade på frågan, gick Oliver Salzburg den extra milen och besvarade den inte bara med ett detaljerat svar utan utmärkt medföljande grafik.
Bild av JasonC, tillgänglig som bakgrundsbild här.
Han skriver:
Jag bestämde mig för att skriva lite om programmeringsaspekten och hur komponenter pratar med varandra. Kanske kommer det att kasta lite ljus på vissa områden.
Presentationen
Vad tar det för att ens ha den enda bilden, som du skrev i din fråga, ritad på skärmen?
Det finns många sätt att rita en triangel på skärmen. För enkelhet, låt oss anta att inga vertex buffertar användes. (EN vertexbuffertär ett minnesområde där du lagrar koordinater.) Låt oss anta att programmet bara berättade grafikbehandlingsledningen om varje enskilt vertex (ett vertex är bara en koordinat i rymden) i rad.
Men, innan vi kan rita något måste vi först springa några ställningar. Vi får se Varför senare:
// Rensa skärmen och djupbufferten glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Återställ Nuvarande Modelview Matrix glMatrixMode (GL_MODELVIEW); glLoadIdentity (); // Ritning med trianglar glBegin (GL_TRIANGLES); // Red glColor3f (1,0f, 0,0f, 0,0f); // Top of Triangle (Front) glVertex3f (0,0f, 1,0f, 0,0f); // Grön glColor3f (0,0f, 1,0f, 0,0f); // Vänster av Triangle (Front) glVertex3f (-1.0f, -1.0f, 1.0f); // Blå glColor3f (0,0f, 0,0f, 1,0f); // Triangeln (Front) glVertex3f (1.0f, -1.0f, 1.0f); // Klar Ritning glEnd ();
Så vad gjorde det?
När du skriver ett program som vill använda grafikkortet, brukar du välja något slags gränssnitt till föraren. Några kända gränssnitt till föraren är:
- OpenGL
- Direct3D
- CUDA
För det här exemplet kommer vi att hålla fast vid OpenGL. Nu, din gränssnitt till föraren är det som ger dig alla verktyg du behöver för att göra ditt program prata till grafikkortet (eller föraren, vilken då samtal till kortet).
Detta gränssnitt är skyldigt att ge dig visshet verktyg. Dessa verktyg har formen av ett API som du kan ringa från ditt program.
Det API är vad vi ser att användas i exemplet ovan. Låt oss ta en närmare titt.
Ställningen
Innan du verkligen kan göra en faktisk ritning måste du utföra en inrätta. Du måste definiera ditt visningsport (det område som faktiskt kommer att göras), ditt perspektiv (the kamera in i din värld), vilken anti-aliasing du ska använda (för att släta ut den kantade av din triangel) ...
Men vi kommer inte titta på något av det. Vi tar bara en titt på de saker du måste göra varje ram. Tycka om:
Rensa skärmen
Grafikkortet kommer inte att rensa skärmen för dig varje ram. Du måste berätta det. Varför? Det här är varför:
Om du inte rensar skärmen kommer du helt enkelt rita över det varje ram. Det är därför vi ringer glClear
medGL_COLOR_BUFFER_BIT
uppsättning. Den andra biten (GL_DEPTH_BUFFER_BIT
) berättar för OpenGL att rensa djupbuffert. Denna buffert används för att bestämma vilka pixlar som finns framför (eller bakom) andra pixlar.
Omvandling
Bildkälla
Transformation är den del där vi tar alla inmatningskoordinater (våra trianglar) och tillämpar vår ModelView-matris. Detta är matrisen som förklarar hur vårt modell (spetsarna) roteras, skalas och översätts (flyttas).
Därefter applicerar vi vår Projektionsmatris. Detta flyttar alla koordinater så att de möter vår kamera korrekt.
Nu omvandlas vi en gång till, med vår Viewport-matris. Vi gör detta för att skala våra modell till storleken på vår bildskärm. Nu har vi en uppsättning kryssningar som är redo att bli gjorda!
Vi kommer tillbaka till omvandlingen lite senare.
Ritning
För att rita en triangel kan vi helt enkelt berätta för OpenGL att starta en ny lista över trianglar genom att ringa glBegin
med GL_TRIANGLES
konstant.
Det finns också andra former du kan rita. Gilla en triangelremsa eller en triangelfläkt. Dessa är främst optimeringar, eftersom de kräver mindre kommunikation mellan CPU och GPU för att dra samma mängd trianglar.
Därefter kan vi tillhandahålla en lista med uppsättningar av 3 vertikaler som ska utgöra varje triangel. Varje triangel använder 3 koordinater (som vi är i 3D-rymden). Dessutom ger jag också en Färg för varje toppunkt, genom att ringaglColor3f
innan kallelse glVertex3f
.
Skärmen mellan de 3 vertikalerna (trianglens 3 hörn) beräknas av OpenGLautomatiskt. Det kommer att interpolera färgen över hela ansiktet av polygonen.
Interaktion
Nu när du klickar på fönstret. Applikationen måste bara fånga fönstermeddelandet som signalerar klicket. Då kan du köra någon åtgärd i ditt program du vill ha.
Detta får en massa svårare när du vill börja interagera med din 3D-scen.
Du måste först klart veta vilken pixel användaren klickade på fönstret. Sedan tar du din perspektivmed hänsyn till, kan du beräkna riktningen för en stråle, från musklickets punkt till din scen. Du kan sedan beräkna om något objekt i din scen skär med den strålen. Nu vet du om användaren klickade på ett objekt.
Så, hur gör du det att rotera?
Omvandling
Jag är medveten om två typer av omvandlingar som vanligtvis används:
- Matrisbaserad transformation
- Benbaserad transformation
Skillnaden är det ben påverkar singel vertex. Matriser påverkar alltid alla ritade hörn på samma sätt. Låt oss titta på ett exempel.
Exempel
Tidigare laddade vi våra identitetsmatris innan vi ritar vår triangel. Identitetsmatrisen är en som helt enkelt tillhandahåller ingen transformation alls. Så, allt jag ritar, påverkas bara av mitt perspektiv. Så kommer triangeln inte att roteras alls.
Om jag vill rotera det nu, kunde jag antingen göra matte själv (på CPU) och ringa bara glVertex3f
medandra koordinater (som roteras). Eller jag kunde låta GPU göra allt arbete, genom att ringa glRotatef
innan teckning:
// Rotera triangeln på y-axeln glRotatef (mängd, 0,0f, 1,0f, 0,0f);
belopp
är givetvis bara ett fast värde. Om du vill animera, du måste hålla reda på belopp
och öka det varje ram.
Så vänta vad som hände med all matrisprat tidigare?
I det här enkla exemplet behöver vi inte bry sig om matriser. Vi ringer bara glRotatef
och det tar hand om allt det för oss.
glRotate
producerar en rotation avvinkel
grader runt vektorn x y z. Den aktuella matrisen (seeglMatrixMode) multipliceras med en rotationsmatris med produkten som ersätter den aktuella matrisen, som ifglMultMatrix kallades med följande matris som dess argument:x 2 1 - c + cx 1 - c - zxxzz 1 - c + y s 0 y x 1 - c + z 2 sy 1 - c + cyz 1 - c + x sz 2 1 - c + c 0 0 0 0 1 1 - c - x s 0 x zz 1 - c - yzzz 1 - c + xzz 2
Tja, tack för det!
Slutsats
Det som blir uppenbart är att det finns mycket prat till OpenGL. Men det säger inte oss något. Var är kommunikationen?
Det enda som OpenGL berättar för oss i det här exemplet är när det är klart. Varje åtgärd kommer att ta en viss tid. En del operation tar oerhört lång tid, andra är otroligt snabba.
Sänder en toppunkt till GPU kommer att vara så fort, jag skulle inte ens veta hur man uttrycker det. Att skicka tusentals kryssningar från CPU till GPU, varje enskild ram, är troligtvis ingen fråga alls.
Rensa skärmen kan ta en millisekund eller värre (tänk på att du oftast bara har cirka 16 millisekunder tid för att rita varje ram), beroende på hur stor din visning är. För att rensa det måste OpenGL rita varje pixel i den färg du vill rensa till, det kan vara miljoner pixlar.
Annat än det kan vi bara fråga OpenGL om funktionerna i vår grafikkort (maxupplösning, max anti-aliasing, max färgdjup, ...).
Men vi kan också fylla en textur med pixlar som alla har en viss färg. Varje pixel har sålunda ett värde och texturen är en jätte "fil" fylld med data. Vi kan ladda in det på grafikkortet (genom att skapa en texturbuffert), ladda sedan en skuggare, berätta att skuggningen använder texturen som en ingång och kör några extremt tunga beräkningar på vår "fil".
Vi kan sedan "göra" resultatet av vår beräkning (i form av nya färger) till en ny struktur.
Så kan du göra GPU-arbetet för dig på andra sätt. Jag antar att CUDA fungerar som den aspekten, men jag har aldrig haft möjlighet att arbeta med det.
Vi rörde oss bara lite om hela ämnet. 3D grafikprogrammering är ett helvete av ett djur.
Bildkälla
Har du något att lägga till förklaringen? Ljud av i kommentarerna. Vill du läsa mer svar från andra tech-savvy Stack Exchange-användare? Kolla in hela diskussionsgängan här.