Datamaskinens sentrale prosesseringsenhet (CPU) og grafikkbehandlingsenhet (GPU) samhandler hvert øyeblikk du bruker datamaskinen for å gi deg et skarpt og responsivt visuelt grensesnitt. Les videre for å bedre forstå hvordan de jobber sammen.
Foto av sskennel .
Dagens spørsmål og svar-økt kommer til oss med tillatelse fra SuperUser - en underavdeling av Stack Exchange, en grupperingsgruppering av spørsmål og svar-nettsteder.
Spørsmålet
SuperUser-leser Sathya stilte spørsmålet:
Her kan du se et skjermbilde av et lite C ++ - program kalt Triangle.exe med en roterende trekant basert på OpenGL API.
Riktignok et veldig grunnleggende eksempel, men jeg tror det gjelder andre grafikkortoperasjoner.
Jeg var bare nysgjerrig og ønsket å vite hele prosessen fra å dobbeltklikke på Triangle.exe under Windows XP til jeg kan se trekanten rotere på skjermen. Hva skjer, hvordan samhandler CPU (som først håndterer .exe) og GPU (som til slutt gir ut trekanten på skjermen)?
Jeg antar at involvert i å vise denne roterende trekanten er blant annet følgende maskinvare / programvare:
Maskinvare
- HDD
- Systemminne (RAM)
- prosessor
- Videominne
- GPU
- LCD-skjerm
Programvare
- Operativsystem
- DirectX / OpenGL API
- Nvidia Driver
Kan noen forklare prosessen, kanskje med et slags flytskjema for illustrasjon?
Det bør ikke være en kompleks forklaring som dekker hvert eneste trinn (antar at det vil gå utenfor omfanget), men en forklaring en mellomliggende IT-fyr kan følge.
Jeg er ganske sikker på at mange mennesker som til og med vil kalle seg IT-fagfolk ikke kunne beskrive denne prosessen riktig.
Svaret
Selv om flere medlemmer av samfunnet svarte på spørsmålet, gikk Oliver Salzburg den ekstra milen og svarte på det ikke bare med et detaljert svar, men utmerket tilhørende grafikk.
Bilde av JasonC, tilgjengelig som bakgrunn her .
Han skriver:
Jeg bestemte meg for å skrive litt om programmeringsaspektet og hvordan komponenter snakker med hverandre. Kanskje det vil kaste lys over visse områder.
Presentasjonen
Hva skal til for å til og med ha det eneste bildet, som du la ut i spørsmålet ditt, tegnet på skjermen?
Det er mange måter å tegne en trekant på skjermen. La oss anta at det ikke ble brukt noen toppunktbuffere. (EN toppunktbuffer er et område med minne hvor du lagrer koordinater.) La oss anta at programmet bare fortalte grafikkbehandlingsrørledningen om hvert eneste toppunkt (et toppunkt er bare en koordinat i rommet) på rad.
Men , før vi kan tegne noe, må vi først kjøre stillas. Vi får se Hvorfor seinere:
// Fjern skjermen og dybdebufferen glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Tilbakestill gjeldende modellvisningsmatrise glMatrixMode (GL_MODELVIEW); glLoadIdentity (); // Tegning ved hjelp av trekanter glBegin (GL_TRIANGLES); // Rød glColor3f (1.0f, 0.0f, 0.0f); // Toppen av trekanten (foran) glVertex3f (0.0f, 1.0f, 0.0f); // Grønn glColor3f (0.0f, 1.0f, 0.0f); // Left Of Triangle (foran) glVertex3f (-1.0f, -1.0f, 1.0f); // Blå glColor3f (0.0f, 0.0f, 1.0f); // Right Of Triangle (foran) glVertex3f (1.0f, -1.0f, 1.0f); // Ferdig tegning glEnd ();
Så hva gjorde det?
Når du skriver et program som vil bruke grafikkortet, velger du vanligvis et slags grensesnitt til driveren. Noen kjente grensesnitt til sjåføren er:
- OpenGL
- Direct3D
- MIRAKLER
For dette eksemplet holder vi oss til OpenGL. Nå, din grensesnitt til sjåføren er det som gir deg alle verktøyene du trenger for å lage programmet ditt snakke til grafikkortet (eller driveren, som deretter samtaler til kortet).
Dette grensesnittet vil garantert gi deg visse verktøy . Disse verktøyene har form av en BRANN som du kan ringe fra programmet ditt.
Det API er det vi ser blir brukt i eksemplet ovenfor. La oss se nærmere på det.
Stillaset
Før du virkelig kan tegne, må du utføre en oppsett . Du må definere visningsområdet ditt (området som faktisk skal gjengis), ditt perspektiv ( kamera inn i din verden), hvilken anti-aliasing du vil bruke (for å glatte ut kantene på trekanten din) ...
Men vi vil ikke se på noe av det. Vi tar bare en titt på tingene du må gjøre hver ramme . Som:
Tømme skjermen
Grafikkrørledningen vil ikke rydde skjermen for deg hver ramme. Du må fortelle det. Hvorfor? Dette er grunnen til at:
Hvis du ikke tømmer skjermen, vil du bare
draw over
det hver ramme. Derfor ringer vi
glClear
med
GL_COLOR_BUFFER_BIT
sett. Den andre biten (
GL_DEPTH_BUFFER_BIT
) forteller OpenGL om å fjerne
dybde
buffer. Denne bufferen brukes til å bestemme hvilke piksler som er foran (eller bak) andre piksler.
Transformasjon
Transformasjon er den delen der vi tar alle inngangskoordinatene (toppunktene i trekanten vår) og bruker ModelView-matrisen. Dette er matrisen som forklarer hvordan vår modell (toppunktene) roteres, skaleres og oversettes (flyttes).
Deretter bruker vi vår projeksjonsmatrise. Dette flytter alle koordinatene slik at de vender riktig mot kameraet vårt.
Nå transformerer vi enda en gang, med Viewport-matrisen. Vi gjør dette for å skalere modell til størrelsen på skjermen vår. Nå har vi et sett med hjørner som er klare til å bli gjengitt!
Vi kommer tilbake til transformasjon litt senere.
Tegning
For å tegne en trekant kan vi ganske enkelt be OpenGL om å starte en ny
liste over trekanter
ved å ringe
glBegynn
med
GL_TRIANGLES
konstant.
Det er også andre former du kan tegne. Som en
trekantstripe
eller a
trekantvifte
. Dette er først og fremst optimaliseringer, da de krever mindre kommunikasjon mellom CPU og GPU for å tegne like mange trekanter.
Etter det kan vi gi en liste over sett med 3 hjørner som skal utgjøre hver trekant. Hver trekant bruker tre koordinater (som vi er i 3D-rom). I tillegg gir jeg også en
farge
for hvert toppunkt, ved å ringe
glColor3f
før
ringer
glVertex3f
.
Skyggen mellom de 3 toppunktene (de tre hjørnene i trekanten) beregnes av OpenGL automatisk . Det vil interpolere fargen over hele polygonets ansikt.
Interaksjon
Nå, når du klikker på vinduet. Søknaden trenger bare å fange vindusmelding som signaliserer klikket. Deretter kan du kjøre hvilken som helst handling i programmet du ønsker.
Dette får en mye vanskeligere når du vil begynne å samhandle med 3D-scenen din.
Du må først vite tydelig hvilken piksel brukeren klikket på vinduet. Så tar du din perspektiv i betraktning, kan du beregne retningen på en stråle, fra det punktet med museklikk inn i scenen din. Du kan deretter beregne om noe objekt i scenen din krysser med den strålen . Nå vet du om brukeren klikket på et objekt.
Så hvordan får du det til å rotere?
Transformasjon
Jeg er klar over to typer transformasjoner som vanligvis brukes:
- Matrisebasert transformasjon
- Beinbasert transformasjon
Forskjellen er at bein påvirke singel hjørner . Matriser påvirker alltid alle tegnede hjørner på samme måte. La oss se på et eksempel.
Eksempel
Tidligere lastet vi inn identitetsmatrise før du tegner trekanten vår. Identitetsmatrisen er en som ganske enkelt gir ingen transformasjon i det hele tatt. Så uansett hva jeg tegner, påvirkes bare av perspektivet mitt. Så, trekanten vil ikke roteres i det hele tatt.
Hvis jeg vil rotere det nå, kan jeg enten gjøre matte selv (på CPU) og bare ringe
glVertex3f
med
annen
koordinater (som roteres). Eller jeg kunne la GPUen gjøre alt arbeidet ved å ringe
glRotatef
før tegning:
// Rotere trekanten på Y-aksen glRotatef (mengde, 0,0f, 1,0f, 0,0f);
beløp
er selvfølgelig bare en fast verdi. Hvis du vil
animert
, må du holde rede på
beløp
og øke den hver ramme.
Så vent, hva skjedde med all matrisesnakk tidligere?
I dette enkle eksemplet trenger vi ikke å bry oss om matriser. Vi ringer rett og slett
glRotatef
og det tar seg av alt dette for oss.
glRotereproduserer en rotasjon avvinkelgrader rundt vektoren x y z. Gjeldende matrise (se glMatrixMode ) multipliseres med en rotasjonsmatrise med produktet som erstatter den nåværende matrisen glMultMatrix ble kalt med følgende matrise som argument: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
Vel, takk for det!
Konklusjon
Det som blir åpenbart er at det er mye snakk til OpenGL. Men det er ikke fortellende oss hva som helst. Hvor er kommunikasjonen?
Det eneste OpenGL forteller oss i dette eksemplet er når det er gjort . Hver operasjon vil ta en viss tid. Noen operasjoner tar utrolig lang tid, andre er utrolig raske.
Sende toppunkt til GPU vil være så raskt at jeg ikke engang vet hvordan jeg skal uttrykke det. Å sende tusenvis av hjørner fra CPU til GPU, hver eneste ramme, er sannsynligvis ikke noe problem i det hele tatt.
Tømme skjermen kan ta et millisekund eller verre (husk at du vanligvis bare har omtrent 16 millisekunder tid til å tegne hver ramme), avhengig av hvor stort visningsområdet ditt er. For å fjerne det må OpenGL tegne hver eneste piksel i fargen du vil fjerne til, det kan være millioner av piksler.
Annet enn det, kan vi stort sett bare spørre OpenGL om funksjonene til grafikkortet vårt (maks oppløsning, maksimal utjevning, maks fargedybde, ...).
Men vi kan også fylle en tekstur med piksler som hver har en bestemt farge. Hver piksel har altså en verdi, og teksturen er en gigantisk "fil" fylt med data. Vi kan laste det inn i grafikkortet (ved å lage en teksturbuffer), og deretter legge inn a skyggelegging , fortell den skyggeleggeren å bruke teksturen vår som input og kjøre noen ekstremt tunge beregninger på "filen".
Vi kan deretter "gjengi" resultatet av beregningen vår (i form av nye farger) til en ny tekstur.
Slik kan du få GPU til å fungere for deg på andre måter. Jeg antar at CUDA presterer likt det aspektet, men jeg hadde aldri muligheten til å jobbe med det.
Vi berørte egentlig bare hele motivet. 3D-grafikkprogrammering er et helvetes dyr.
Har du noe å legge til forklaringen? Hør av i kommentarene. Vil du lese flere svar fra andre teknologikyndige Stack Exchange-brukere? Sjekk ut hele diskusjonstråden her .