Előadást letölteni
Az előadás letöltése folymat van. Kérjük, várjon
1
ELTE IK 2015-2016 tavaszi félév Valasek Gábor
Haladó(bb) OpenGL ELTE IK tavaszi félév Valasek Gábor
2
Grafikus API-k általában
3
Grafikus API-k általában
Ajánlott irodalom: Fabian Giesen cikksorozata: trip-through-the-graphics-pipeline-2011-index/ OpenGL Insights:
4
Grafikus API-k az OS-ben
Program Grafikus API User-mode driver Ütemező(k) Kernel-mode driver GPU busz
5
Program A saját alkalmazásunk
A grafikus hardverhez valamilyen API-n keresztül férünk hozzá De ami azt illeti, minden máshoz is: ez ugyanis user mode-ban fut
6
Grafikus API Az összes erőforráskezelő és egyéb, grafikus hardvert érintő utasítás ezen keresztül intézhető Emellett számon tartja, hogy mi az aktuális állapota a program grafikus context-(jei)nek, ellenőrzi a paraméterek validitását, hibakeresést stb. végez, esetleg kötegeli az elvégzendő munkákat DirectX-nél shader validálás és linkelés is itt történik (OpenGL-nél driver-ben)
7
Kernel és user mód Kernel mode:
Az itt végrehajtott kódnak korlátozásoktól mentes, teljes hozzáférése van a hardverhez A fizikai memória bármely részét tudja címezni Az itteni crash-ektől száll el az egész rendszer User mode: Az így futó kód nem tud közvetlenül hozzáférni hardverhez és a memóriához, ehhez a rendszer és egyéb API-kat kell használnia
8
Kernel és user mód Hardveresen is támogatott, pl. x86 architektúrákon négy szintű hozzáférés van
9
Kernel és user mód Context: a program jelenlegi állapota, beleértve az aktuális pillanatban a regiszterek értékeit is Context switching: amikor a programunk unrunnable-lé válik (mert például éppen a HDD-re várakozik), akkor egy másikat futtat tovább a rendszer Ekkor a két program context-jét egyszerűen kicseréli a CPU (ld. GPU architektúrás óra) Ha más módúak is, még a context switch mellett Ringet is kell váltania a programnak
10
User mód Program user módban futtatásakor
A Windows létrehoz egy process-t a programnak Minden user mode-ú programnak van egy saját virtuális memóriája, amit valahogy az OS lemap-pel a fizikai memóriára (vagy akár disk- re – lásd lapozófájlok Win-ben) A process hozzáférése a virtuális memóriához korlátozott – illetve mivel tényleg virtuális, ezért nem tudnak hozzáférni a másikéhoz sem – hogy ne akadjon össze az OS-sel és a többi process-szel OS API-n keresztül tudnak csak egymással kommunikálni a process-szek
11
Kernel mód Program kernel módban futtatásakor
Minden kernel módú kód ugyanazt a virtuális címteret látja Ennek megfelelően nem csak a többi driver-be tud belenyúlni, hanem az OS-be is → és jön a... (*)
12
Kernel és user mód
13
Kernel és user mód További részletek az MS honlapján
us/library/windows/hardware/ff554836(v=vs.85).as px
14
User-mode driver (UMD)
Egy alacsonyabb szintű API-t valósít meg (DDI), de azért még hasonló ahhoz (DX, OGL stb.), amit a programból hívunk Bizonyos dolgokat egyértelműbben fejez ki (pl. memóriafoglalásokat) Fizikailag pl. a nvd3dum.dll vagy atiumd*.dll fájlokban lakik A hívó process címterében van, amikor használjuk
15
User-mode driver (UMD)
A shaderek fordítása is ezen a szinten történik DirectX-nél ezek nem csak szintaktikailag helyesek már bemenetként is, hanem már magas szintű optimalizáláson is átestek Erre jönnek az alacsony (hw-tól is és meghívó programtól is függő) optimalizálások az UMD- ben DX-nél HLSL → IR → D3D bytekód út Egyes API állapot is végül lehet shaderkóddal lesz implementálva (pl. textúrahatárok)
16
User-mode driver (UMD)
A legtöbb létrehozás/fordítás deferred végrehajtású: csak akkor történik meg, amikor először lesz tényleg szükség a dologra A memóriakezelést a Kernel szintű résztől kapott nagyobb memóriadarabon belül csinálja: azaz a KMD-től kap egy fizikai területet, amin belül foglal (de az UMD címteréből a GPU fizikai címterébe leképezés már Kernel) Rendszer és videomemória közötti adatátviteleket is ütemezi
17
User-mode driver (UMD)
A legfontosabb feladata pedig a command buffer-ek feltöltése Ezeket fizikailag a Kernel mód allokálja és adja át az UMD szintnek A command bufferbe kerül minden utasítás (állapotváltoztatások, rajzolások), méghozzá az adott hardver számára értelmezhető formában
18
User-mode driver (UMD)
A kompatibilitáshoz szükséges dolgokat is itt oldják meg Általában a régi fixed function pipeline kódokat is itt fordítják át a hardvernek megfelelően (nyilván beépített shaderekre)
19
User-mode driver (UMD)
Fontos: a GPU-t egyszerre több program is akarja használni (legalábbis desktop-on!) Így lényegében minden ilyen programnak van egy UMD-je, ahonnan az utasítások az OS GPU ütemezőjén keresztül mennek fel a grafikus kártyára Ez végzi a feladatok párhuzamos végrehajtását időosztásos módon Figyeljetek: ha másik program dolgait kell kirajzolni, akkor (GPU) context switch jön!
20
User-mode driver (UMD)
Fontos, hogy a jelenlegi driverimplementációkat a piaci igények igazítják Rengeteg kézzel beállított, alkalmazásspecifikus dolog van A fenti API/UMD tagolódás a DirectX-re jellemzőbb, sajnos OpenGL-ben a kettő összefolyik
21
Kernel-mode driver (KMD)
A tényleges hardveres dolgokat ez végzi (fizikai memóriára mappelés, inicializálás, megjelenítési módok lekérdezése és beállítása, hardveres egér stb.) Mivel Kernel szintű, csak egy van belőle Ha ez elcrash-sel, akkor BSOD is lehet belőle (de azért nem mindig)
22
Kernel-mode driver (KMD)
Itt történik a command buffer-ek tényleges kezelése UMD parancs végrehajtása: Az UMD összeállítja a command buffer-jét és elküldi az ütemezőnek Az ütemező amikor a GPU hozzáférési sorban a process kerül sorra, átadja a command buffer-jét a KMD-nek A KMD a megkapott command buffert a GPU fő command buffer-jébe helyezi el (amiből ma már több is van)
23
Kernel-mode driver (KMD)
Az utolsó lépésben történhet RAM → GPU RAM átadás is, ha nem tudja a GPU command processor címezni a rendszermemóriát A parancspuffert két pointerrel kezeli (amiket regiszterekben tárol a GPU): Read pointer: a fő command buffer helye a memóriában Write pointer: a KMD meddig írt bele a command buffer-be
24
Memória
25
Memória
26
Memória
27
Memória Újabb architektúrákon a CPU-RAM sávszélesség maximuma (throughput) 25 Gbps A késleltetés (tehát ha nem a cache-ből, hanem a memóriából kell olvasni) olyan 140 órajel (összehasonlításképp: L1, L2, L3-ból rendre 4, 11, 39 órajel Nehalem-en) Ezzel szemben egy mai közepes (GeForce 950) GPU-n 105, felső-középkategóriában (GeForce 980) 224, míg egy Titan X-nél 331 Gbps De a GPU RAM késleltetése órajel!
28
Memória A DRAM-ban a chip-pek egy 2D tömbként vannak elrendezve
Minden egyes memóriacím tehát egy (sor, oszlop) pár DRAM írás/olvasás ettől függetlenül az adott sorban lévő összes elemet érinti a kapcsolások miatt Így akkor érhetjük el a maximális throughput-ot, ha így (=soronként) olvasunk és írunk
29
Busz A CPU és a GPU (illetve a RAM és a GPU RAM) közötti összeköttetés a PCIe interfészen keresztül történik Figyeljünk arra, hogy itt is adott a sávszélesség Általában akkor válik fájdalmassá, ha streamelnünk kell adatokat a RAM-ból a GPU-ra
30
Memória - adatátvitel Jelenleg kétféle memória van, amikor „GPU memóriáról” beszélünk: GPU RAM Rendszermemóriába map-pelt GPU memória Ez architektúrától függően különbözőképpen kezelhető Egy jó MMU (memory management unit) is dönthet, ami elintézi, hogy a gyakori adatok 1-be, a kevésbé gyakoriak 2-ben legyenek tárolva...
31
GDDR GDDR GDDR GDDR A GPU (FU-któl írás/ olvasás, fetch stb.) Controller ... Controller cím adat adat vezérlés GPU mem hub MMU Lokális? PCIe interfész ... Forrás:
32
Memória adatátvitel Kétféle GPU van a piacon:
Integrált: ekkor fizikailag egy lapkán van a CPU és a GPU, így nagyobb az adatátvitel ← de általában kisebb a grafikus teljesítmény Diszkrét: külön slot-ban van a GPU, ekkor DMA controllerrel kell átvinni a memóriát a PCIe sínen
33
Fő parancsfeldolgozó Az ide vezető memóriautak mind nagy sávszélességű és nagy késleltetési utak Maga a fő parancsfeldolgozó egy (nagyon hosszú) ring bufferből szedi a KMD-ből érkező parancsokat Az aktuális parancsot a Command Processor (CP) dolgozza fel, ami utána azt a szál vagy stream ütemezőnek adja tovább Egy utasítás általában egy memóriadarabra is mutat
34
Forrás: http://developer.amd.com/gpu_assets/R5xx_Acceleration_v1.2.pdf
35
Parancsok feldolgozása
A fő command buffer-re úgy is lehet gondolni, mint két szál (a CPU-n futó grafikus program és a GPU) közötti kommunikációs csatornára Egyúttal ez egy osztott erőforrás, ennek megfelelően szinkronizálni is kell: ezt a fence- eken keresztül teszik Ezeket a GPU beolvassa és megfelelő regiszterek módosításával tudatja a CPU-val, hogy az adott feltétel, amit a fence igényelt, teljesült
36
Fő parancsfeldolgozó Mivel nagyon sokszor van igény arra, hogy a GPU olvassa a rendszermemóriát, ezért modern GPU-kon van DMA erre a célra (Fermi-n például kettő is) Illetve egy MMU, ami lényegében virtualizálja a GPU-n futó programok számára a memóriát (l. korábban) Honnan tudja a driver, hogy mehet az utasítás? Például DX alatt flush, present, lock!
37
Fő parancsfeldolgozó A bejövő utasítások lehetnek tényleges parancsok, állapotváltások, adatok A fő parancsfeldolgozó tárolja a GPU aktuális állapotát Lényegében egy állapotgépnek tekinthető, ami a belső állapotát a végrehajtott utasításoknak megfelelően módosítja, a CPU program leképezett regisztereit frissíti és megszakításokat küld a CPU-nak
38
Fő parancsfeldolgozó A végrehajtandó parancsok között lehetnek 2D és 3D parancsok (sőt, akár text mode-beliek is), ezek általában dedikált egységekhez mennek Arra is volt példa, hogy a 2D-s és 3D-s parancsokat külön feldolgozóegység hajtotta végre
39
Állapotváltások Az állapotváltozást hozó utasítások a GPU-n problémásak, mivel egyszerre nagyon sok szál is érintett lehet egyetlen memóriadarab módosításában A GPU-n pedig egyszerre nagyon sok szál fut A különböző típusú állapotoknál más-más megoldásokhoz nyúlnak a vendorok
40
Állapotváltások Ennek a megoldása nem triviális:
Kikényszeríthetjük, hogy az állapotváltás előtt minden, az érintett memóriát referáló utasítás végetérjen Az állapotot az utasítások részévé is tehetjük, azaz egy kvázi plusz paraméterként továbbadhatjuk a parancsok mellett (ha eléggé kicsi az állapottér) Használhatunk double buffering-et is az állapotokra (márhogy ezt a double bufferinget, l. majd a valósidejű tervmintás órát) Stb.
41
Szinkronizáció Kétféle megközelítést használ a GPU arra, hogy értesítse a CPU-t bizonyos feltételek teljesüléséről: Megszakításokat küld (a ritkán történő, de magas prioritású eseményekről, például a vertical retrace-ről) ← interrupt alapú A saját regisztereiben eltárolja az események paramétereit, amiket a CPU utána bármikor lekérdezhet ← polling alapú
42
Szinkronizáció Maguk a feltételek a fence-ek lennének
A GPU-n belüli várakozásokat „feltétel teljesüléséig várakozz” típusú utasításokkal valósítják meg – vagy bármi más barrier-rel
43
OpenGL
44
Utasítások kiadása Az OpenGL-es utasításokból egy vagy több tényleges command queue-ba kerülő utasítás születik Ha egy utasítás felfért a GPU-n lévő fő parancssorra, akkor 'issued' állapotú lesz Ha nem fért fel, akkor 'unissued' Ha fence utasítást hajt végre a GPU, akkor jelez, hogy ez a fence sikeresen teljesítve Arra figyeljünk, hogy a driver tipikusan néhány frame-mel előrébb jár (általában 3)
45
Szinkronizáció OpenGL-ben
Explicit szinkronizáció: glFinish(): blokkolja a hívó szálat amíg az összes, glFinish()-ig kiadott OpenGL utasítás végrehajtása véget nem ér glFlush(): blokkolja a hívó szálat, amíg az összes, glFlush()-ig kiadott rajzolási utasítás rá nem kerül a GPU fő paranccsorára Szinkronizáció objektumokon keresztül
46
Szinkronizáció OpenGL-ben: fence
GLsync fence = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); ... // utasítások, amiket be kell várni a folytatás előtt GLenum result = glClientWaitSync( fence, GL_SYNC_FLUSH_COMMANDS_BIT, GLuint64( )); // ellenőrizzük, hogy a fence teljesült-e vagy más van: if (result == GL_CONDITION_SATISFIED ) ...
47
Szinkronizáció OpenGL-ben: fence
GLsync fence = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); ... GLenum result = glClientWaitSync( fence, GL_SYNC_FLUSH_COMMANDS_BIT, GLuint64( )); if (result == GL_CONDITION_SATISFIED ) ... Egyelőre (OpenGL 4.5 – azaz örökre is) ez a két paraméter kötelezően ez kell legyen.
48
Szinkronizáció OpenGL-ben: fence
GLsync fence = glFenceSync( GL_SYNC_GPU_COMMANDS_COMPLETE, 0 ); ... GLenum result = glClientWaitSync( fence, GL_SYNC_FLUSH_COMMANDS_BIT, GLuint64( )); if (result == GL_CONDITION_SATISFIED ) ... Ez viszont lehet 0 is, de akkor adjunk ki egy glFlush()-t a parancs előtt, különben előfordulhat, hogy új rajzolási parancs híján örökre várakozunk. (U.i. a GPU nem jelzi, hogy kiürült a command queue-ja, még ha van egy rakás kliensoldali unissued parancs is!) A glFlush()-t ha nem akarjuk kézzel kiadni, akkor használjuk ezt a paramétert itt!
49
Szinkronizáció OpenGL-ben: fence
GLsync glFenceSync(GLenum condition, GLbitfield flags) Létrehoz egy új szinkronizációs objektumot és berakja az OpenGL parancssorba (driver oldaliba) Amikor ezt feldolgozza a GPU, akkor minden ezen várakozót továbbenged A függvény visszatérési érték egy nemnulla azonosító lesz
50
Szinkronizáció OpenGL-ben: fence
GLsync glFenceSync(GLenum condition, GLbitfield flags) Csak GL_SYNC_GPU_COMMANDS_COMPLETE lehet
51
Szinkronizáció OpenGL-ben: fence
GLsync glFenceSync(GLenum condition, GLbitfield flags) Mivel egyetlen ilyen flag-et sem specifikáltak végül az OpenGL-ben, ezért csak nullát tudunk ide írni
52
Szinkronizáció OpenGL-ben: fence
void glDeleteSync(GLsync sync) Egyszerűen törli a paraméterben kapott szinkronizációs objektumot, ha épp nincs használatban Különben ad neki egy „törlendő” flag-et és amint elfüstöl (=GPU feldolgozza és mindenki, aki rajta blokkolt kilép a várakozásból) törli Ha nullát adunk meg neki, aki szép csendben nem csinál semmit
53
Szinkronizáció OpenGL-ben: fence
enum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) Blokkolja a szálat amíg a GPU fő parancssora fel nem dolgozza a sync fence-et, vagy hiba nem történik
54
Szinkronizáció OpenGL-ben: fence
enum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) A visszatérési értékből tudjuk mi is volt végül: GL_ALREADY_SIGNALED: már kiadta, nem kell várni GL_TIMEOUT_EXPIRED: lejárt a max várakozás GL_CONDITION_SATISFIED: várakozás közben, de még timeout előtt feldolgozta a GPU a fence-t GL_WAIT_FAILED: valami más baj van (glGetError-ból kiderül, hogy mi)
55
Szinkronizáció OpenGL-ben: fence
enum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) A fence, aminek a feldolgozására várunk
56
Szinkronizáció OpenGL-ben: fence
enum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) Ez ennek az utasításnak a flush-olását irányítja (és nyilván ezen keresztül a megelőzőkét is, akik még nem kerültek fel a sorba) Vagy semmi (ekkor figyeljünk arra, hogy kézzel kell egy glFlush()) vagy pedig GL_SYNC_FLUSH_COMMANDS_BIT
57
Szinkronizáció OpenGL-ben: fence
enum glClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) Legfeljebb mennyi ideig várakozzon a kliens a fence teljesülésére, nanosecundum-ban Ha nullát írunk, akkor nem várunk egyáltalán (a visszatérési értékből viszont tudjuk, hogy teljesült- e vagy sem a fence)
58
Szinkronizáció OpenGL-ben: fence
Ezen kívül úgy általában le tudjuk kérdezni egy sync objektum attribútumait a glGetSync segítségével
59
Szinkronizáció OpenGL-ben
Egyes utasítások implicit glFinish()-t vagy glFlush()-t provokálnak ki Például framebuffer váltásnál ha még van rajzoló utasítás az előző FBO-ra, akkor jöhet szinkronizáció FBO olvasása a CPU-ról szintén szinkronizálást provokál, ahogy általában a GPU-s pufferek olvasási műveletei is
60
Buffer Object Legáltalánosabban, OpenGL-ben egy buffer egy memóriadarabot kezelő objektum Akkor kap szemantikát, ha egy „buffer target”-re bind-oljuk (pl. így dől el, hogy ő vertexeket tároló puffer lesz) A GPU-n történő allokációt ÉS a rendszermemóriából történő feltöltést a glBufferData utasítás végzi Fontos viszont: a glBufferData MINDIG allokál Tehát ha csak frissíteni akarunk, akkor más kell
61
Pufferek feltöltése: glBufferData
Hasonló a memcpy-hez (sőt: néha az!), ha a glBufferData data pointere nem null, akkor: A driver átmásolja az adatokat data-ról egy GPU DMA által látható memóriaterületre A driver elküldi az adatátviteli parancsot és visszatér a glBufferData hívásból Ezzel aszinkron, a GPU MMU vagy rendszer RAM-ban hagyja a memóriát egy pin-locked lapon, vagy pedig átviszi a GPU-ra – usage hint-ektől és implementációtól függően
62
Pufferek feltöltése: glMapBuffer
A glMapBuffer és glUnmapBuffer utasítások már közvetlen a GPU DMA által elérhető címet adják át az alkalmazásnak Ezzel megúszhatjuk a memcpy-t rendszermemórián belül, de... ...a driver csak azután tud visszatérni, hogy megvan a memóriacím, amihez lehet meg kell várnia egy GPU → RAM másolást! Megfelelő extension-ökkel az is elérhető, hogy a map-pelt memória 64 bájtos rácsra illeszkedjen
63
glBufferData A usage hint-ek figyelembevétele teljesen GPU és driver függő (az OpenGL Insights-ból, Intel Core i5 760-nal és GeForce GTX 470-nel): Eljárás Usage hint Hová került Adatátviteli sebesség glBufferData/ glBufferSubData GL_STATIC_DRAW device 3.79 GB/s glMapBuffer/ glUnmapBuffer GL_STREAM_DRAW pinned system rendszermemóriából használta 5.73 GB/s
64
Pufferek közti adatmozgatás
A következőket glCopyBufferSubData és glTexImage2D függvényekkel csinálták, GL_RGBA8 formátummal (ez utóbbi elég rossz ötlet is lehet, vendortól függően):
65
glBufferSubData void glBufferSubData( GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data) Frissíti az aktuálisan target-re bindolt puffert a data által mutatott rendszermemóriabeli adatokkal offset (byte) buffer object size (byte)
66
Buffer Object Az előbbi hasznos, ha valamiért már eleve rendelkezésünkre áll a rendszermemóriában az, amit fel szeretnénk tölteni Például ha minden GPU-n lévő erőforrásnak van egy másolata a RAM-ban Viszont van, hogy nem tudunk mindent redundánsan eltárolni De lehetőség van arra, hogy a buffer object tartalmát bemásoljuk a RAM-ba és utána a módosított értékeket visszatöltsük
67
glMapBuffer void * glMapBuffer( GLenum target, GLenum access)
Az aktív target-re bind-olt buffer object teljes tartalmát bemásolja a kliens (=program CPU-n) memóriaterébe (=RAM) A visszatérési érték a visszamásolt tartalom kezdőpontja (hiba esetén NULL) Az access lehet GL_READ_ONLY, GL_WRITE_ONLY, GL_READ_WRITE
68
glUnmapBuffer GLboolean glUnmapBuffer(GLenum target)
Visszatölti a target kliens memóriaterében lévő (akár módosított) leképezését a host memóriaterébe (GPU) Ha hiba történt, false-t ad vissza Eddig a pontig garantált csak, hogy a glMapBuffer által visszaadott címen a lekérdezett puffer adatai találhatóak
69
Map/UnMap glBindBuffer(GL_ARRAY_BUFFER, buf);
void* ptr = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY); memcpy(ptr, data, sizeof(data)); glUnmapBuffer(GL_ARRAY_BUFFER);
70
Map/UnMap Természetesen ennek is vannak különböző verziói:
glMapBufferRange / glFlushMappedBufferRange glMapNamedBufferRange / glFlushMappedNamedBufferRange
71
glMapBufferRange A glMapBufferRange és glFlushMappedRange extra access paraméterei: GL_MAP_READ_BIT, GL_MAP_WRITE_BIT: a visszadott pointer írásra/olvasásra használható csak. Ha ez nem teljesül, nincs hibaüzenet, de a viselkedés definiálatlan. GL_MAP_PERSISTENT_BIT: a kliens tartósan a visszaadott címen akarja tudni a puffert. Eközben lehet rajzolási utasításokat is kiadni. GL_MAP_COHERENT_BIT: perzisztens mapping legyen koherens, vagyis ha írunk a pufferbe (akár CPU-ról, akár GPU-ról), arról a másik is értesül
72
glMapBufferRange A glMapBufferRange és glFlushMappedRange extra access paraméterei: GL_MAP_INVALIDATE_BUFFER_BIT/ GL_MAP_INVALIDATE_RANGE_BIT: a puffer egészének/részének előző tartalmát dobja el a driver. Read-del ne használjuk! GL_MAP_FLUSH_EXPLICIT_BIT: ha módosítjuk a range-en belül a memóriát, akkor egy glFlush lesz GL_MAP_UNSYNCHRONIZED_BIT: bízza ránk az OpenGL, hogy mikor rakjuk vissza a GPU-ra az adatokat
73
Pufferek másolása source offset (byte) size (byte)
source buffer object glCopyBufferSubData destination offset (byte) dest. buffer object size (byte)
74
glCopyBufferSubData void glCopyBufferSubData( GLenum readtarget, GLenum writetarget, GLintptr readoffset, GLintptr writeoffset, GLsizeiptr size) A read és write target-ek különbözőek kell, hogy legyenek (GL_ARRAY_BUFFER stb.) A lehetőségek közül kettőt kiemelnék: GL_COPY_READ_BUFFER GL_COPY_WRITE_BUFFER
75
glCopyBufferSubData GLfloat vertexData[] = { ... };
glBindBuffer( GL_COPY_READ_BUFFER, vbo1); glBindBuffer( GL_COPY_WRITE_BUFFER, vbo2); glCopyBufferSubData( GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
76
glCopyBufferSubData GLfloat vertexData[] = { ... };
glBindBuffer( GL_ARRAY_BUFFER, vbo1); glBindBuffer( GL_COPY_WRITE_BUFFER, vbo2); glCopyBufferSubData( GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, sizeof(vertexData));
77
glCopyBufferSubData GLfloat vertexData[] = { ... };
glBindBuffer( GL_COPY_WRITE_BUFFER, vbo1); glBindBuffer( GL_ARRAY_BUFFER, vbo2); glCopyBufferSubData( GL_COPY_WRITE_BUFFER, GL_ARRAY_BUFFER, 0, 0, sizeof(vertexData));
78
Pufferek kezelése Ezzel még csak elkezdődnek a dolgok
Néhány érdekes részlet: hts/OpenGLInsights- AsynchronousBufferTransfers.pdf Egyébként maga a könyv még mindig rengeteg hasznos információt tartalmaz: Cozzi/dp/ Az egyik szerző blogja pedig még naprakészebb infók tárháza:
79
Streaming Ezt csináljuk, amikor nagyon sűrűn töltünk fel adatok a GPU-ra Persze lehet glSubBufferData-val Három népszerű, okosabb módszer: Több puffer round-robinolva Puffer újraspecifikációja (orphaning) Kézi szinkronizáció glMapBufferRange segítségével
80
Streaming: 0. verzió Simán csak töltsük fel újra a puffer megfelelő darabját Ez bizony implicit szinkronizációt hoz Kiindulási kód: chmark.zip
81
Streaming: round robin
Ne egyetlen puffert foglaljunk az adatunknak, hanem többet (n-et) A programban az n-ediket frissítjük, amíg az (n-1)-edikből rajzolunk ki → így nem kell szinkronizálni Ezt akár többszálú programban is megcsinálhatjuk – a főszálból rajzolunk, míg a dolgozó szálban újrageneráljuk a puffer tartalmát (amit akár mondjuk diskről is szedhetünk be közben)
82
Streaming: orphaning Ugyanaz, mint a round robin, csak itt az egész a driver-en belül történik: glBufferData data pointere legyen null és a mérete illetve usage hint-je maradjon meg Ezután glBufferData-val töltsük fel ismét! GlBindBuffer( GL_ARRAY_BUFFER, my_buffer_object); GlBufferData( GL_ARRAY_BUFFER, data_size, nullptr, GL_STREAM_DRAW); GlBufferData( GL_ARRAY_BUFFER, data_size, data_ptr, GL_STREAM_DRAW);
83
Streaming: orphaning Alternatívaként megoldható glMapBufferRange segítségével is: glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); void* data = glMapBufferRange(GL_ARRAY_BUFFER, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT ); glUnmapBuffer(GL_ARRAY_BUFFER);
84
Streaming: manuális sync
Itt lényegében glMapBufferRange-et hívunk GL_MAP_UNSYNCHRONIZED_BIT paraméterrel Itt is több puffert használunk Egy fence segítségével meggyőződünk arról, hogy a most írásra került puffert már senki sem használja ...de általában a driver maximum 3 frame-mel jár előrébb, így hit alapon használhatunk egy 3 elemből álló chain-t is...
85
Streaming: manuális sync
Algoritmus: Update()-ben: glClientWaitSync-eljünk a pufferunkon Miután visszatér belőle a rendszer, glDeleteSync Adatstream glMapBufferRange a következő paraméterekkel: GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT Végül glUnmapBuffer Render() végén: m_fences[m_current] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
86
Shaderek linkelése Linkelés előtt a következő beállításokat kell megtennünk: Vertex shader bemeneti változóinak attribútum location-jeit meghatározni (vagy kliens oldalon glBindAttrib* fv-vel, vagy shaderben layout location-nel) Fragment shader kimeneti location-jeit meghatározni (vagy kliens oldalon glFragData* fv-vel, vagy shaderben layout location-nel) A transform feedback kimenetet beállítani (ha van) A multiprogram shaderekben a kódkiválasztást megtenni
87
Image Load Store OpenGL 4.2 óta core-ban is elérhető lehetőség, hogy textúrákat olvassunk és írjunk Mivel írás is van, ezért szükségünk van arra, hogy az írás/olvasás szinkronizálási problémákat kezelni tudjuk – erre vannak a memory qualifier-ek (és barrier-ekkel) Maga az imageXd a következő típusú tárolókból olvashat:
88
Image Type Corresponding Texture Type gimage1D GL_TEXTURE_1D single layer from GL_TEXTURE_1D_ARRAY gimage2D GL_TEXTURE_2D single layer from: GL_TEXTURE_2D_ARRAY GL_TEXTURE_CUBE_MAP GL_TEXTURE_CUBE_MAP_ARRAY GL_TEXTURE_3D gimage3D gimageCube gimage2DRect GL_TEXTURE_RECTANGLE gimage1DArray gimage2DArray gimageCubeArray GL_TEXTURE_CUBE_MAP_ARRAY (requires GL 4.0 or ARB_texture_cube_map_array) gimageBuffer GL_TEXTURE_BUFFER gimage2DMS GL_TEXTURE_2D_MULTISAMPLE GL_TEXTURE_2D_MULTISAMPLE_ARRAY gimage2DMSArray
89
Image Load Store A shaderben „kép” méretét az ivec imageSize(gimage image) paranccsal kérhetjük le Olvasni egész koordinátákról tudunk: gvec4 imageLoad( gimage image, IMAGE_COORD) Írni pedig a következőképpen: void imageStore(gimage image, IMAGE_COORD, gvec4 data)
90
Image Load Store Atomi műveletek segítségével végezhetünk műveleteket is Például imageAtomicExchange, imageAtomicCompSwap, imageAtomicAdd, imageAtomicAnd, imageAtomicOr, imageAtomicXor, imageAtomicMin, imageAtomicMax
91
Image Load Store Kliens oldalról az Image Object a textúrához hasonlóan kezelendő Minden shader stage-hez tehát van valahány Image Unit, amikhez konkrét Image-eket lehet rendelni A glBindImageTexture segítségével A shaderekben található image* uniformokat pedig valahányas indexű Image Unit-hoz bindoljuk
92
Shader Storage Buffer Object
Inkoherens író/olvasó utasításokkal elérhető tároló (mint az ILS) A megengedett méretnek legalább 16MB-osnak kell lennie minden OGL implementációban GLSL-ben buffer típusnévvel jelölendőek
93
Direct State Access Program bind-olása nélkül is írhatunk a uniformá változókba: glUseProgram(prog1); glGetUniformLocation("foo", &loc1); glUniform3f(loc1, 1, 2, 3); glUseProgram(prog2); glGetUniformLocation("bar", &loc2); glUniform3f(loc2, 3, 2, 1); <=> glProgramUniform3f(prog1, loc1, 1, 2, 3); glProgramUniform3f(prog2, loc2, 3, 2, 1);
94
Direct State Access OpenGL Object Type Context Prefix DSA Prefix
Texture Object "Tex" "Texture" Framebuffer Object "Framebuffer" "NamedFramebuffer" Buffer Object "Buffer" "NamedBuffer" Transform Feedback Object N/A1 "TransformFeedback" Vertex Array Object "VertexAttrib" "VertexArray" Sampler Object N/A2 "Sampler" Query Object "Query"
95
Shader Storage Buffer Object
Érdemes figyelni arra, hogy core-ban SSBO OpenGL 4.3, DSA 4.5 (!) feature Ennek megfelelően mielőtt használnád, győződj meg arról, hogy a megrendelő is hasznát tudja látni, különben extension-höz kell nyúlni Az meg vendor-specifikus, szóval tesztelj (és ne feledd: Intel (72.8%) >> NVIDIA (15.7%) > AMD (11.5%), globális piaci részesedésben) Persze specifikusabban más számok is kijöhetnek:
96
Interfész blokkok GLSL input, output, uniform vagy tároló puffer változók csoportja storage_qualifier block_name { <define members here> } instance_name;
97
Interfész blokkok A storage qualifier lehet in out uniform
buffer (OGL 4.3-tól fölfelé) A block_name-et használja a linker, hogy megfeleltesse egymásnak a különböző shaderek input és output interfészét, illetve a több helyen előforduló uniform változókat
98
Interfész blokkok Egy interface block-nak van egy block indexe: a szám, ami az adott intefész blokkot azonosítja GLuint glGetUniformBlockIndex( GLuint program, const char *name) Ha az interfész blokk puffer tárolású (uniform vagy buffer storage qualifier-es), akkor a block index-hez egy bindolási pontot kell megeadni void glUniformBlockBinding( GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding)
99
Interfész blokkok A bindolási pontra pedig rárakhatunk egy puffert
void glBindBufferBase(GLenum target, GLuint index, GLuint buffer) A binding pontok maximális számát a következőképpen lehet lekérdezni: glGetIntegerv( GL_MAX_UNIFORM_BUFFER_BINDINGS,&va lue) A Buffer Object-et GL_UNIFORM_BUFFER target-re kell bind-olni létrehozáskor
100
Interfész blokkok
101
Uniform block Több shader változó használhatja a program szempontjából ugyanazon uniform változókat Ezeket mégis külön-külön kellene feltöltenünk stb., mert ugyanannak a uniform változónak más lesz a címe a különböző shader programokban A uniform block segítségével ezen segíthetünk (csak használjunk shared layout-ot)
102
Uniform block – a shaderben
uniform BlobSettings { vec4 InnerColor; vec4 OuterColor; float RadiusInner; float RadiusOuter; };
103
Uniform block A uniform változók adatait tartalmazó puffer objektum a uniform buffer object A változókra hivatkozásnál elég az adattag nevét írni, nem kell prefixelni az UBO nevével (tehát pl. elég az InnerColor, nem kell BlobSettings.InnerColor)
104
Uniform block GLuint blockIndex = glGetUniformBlockIndex( programHandle, "BlobSettings"); GLint blockSize; glGetActiveUniformBlockiv( programHandle, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); GLubyte * blockBuffer= (GLubyte *)malloc(blockSize);
105
Uniform block const GLchar *names[] = { "InnerColor", "OuterColor",
"RadiusInner", "RadiusOuter" }; GLuint indices[4]; glGetUniformIndices( programHandle, 4, names, indices); GLint offset[4]; glGetActiveUniformsiv( programHandle, 4, indices, GL_UNIFORM_OFFSET, offset);
106
Uniform block GLfloat outerColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
GLfloat innerColor[] = {1.0f, 1.0f, 0.75f, 1.0f}; GLfloat innerRadius = 0.25f, outerRadius = 0.45f; memcpy(blockBuffer + offset[0], innerColor, 4 * sizeof(GLfloat)); memcpy(blockBuffer + offset[1], outerColor, 4 * sizeof(GLfloat)); memcpy(blockBuffer + offset[2], &innerRadius, sizeof(GLfloat)); memcpy(blockBuffer + offset[3], &outerRadius, sizeof(GLfloat));
107
Uniform block GLuint uboHandle; glGenBuffers( 1, &uboHandle );
glBindBuffer( GL_UNIFORM_BUFFER, uboHandle ); glBufferData( GL_UNIFORM_BUFFER, blockSize, blockBuffer, GL_DYNAMIC_DRAW ); glBindBufferBase( GL_UNIFORM_BUFFER, blockIndex, uboHandle );
108
Layout qualifiers Befolyásolja, hogy egy adott változó tárolása miképp történik és egyéb dolgokat Általánosan így néz ki: layout(qualifier1, qualifier2 = value, ...) variable_definition Az értékek (value) egész típusúak kell legyenek (vagy 4.4-től felfelé fordításidejű egész konstans kifejezés)
109
Interface layout A vertex shader input interfészének és a VAO attribútumindexeknek az összerendelését a layout paranccsal is meg lehet tenni: Ekkor layout ( location = 0 ) vec4 pos; és glBindAttribLocation(prg,”pos”,0); ugyanazt csinálja (megfelelő előzmények mellett) ...csak előbb meg kell tanulni location-öket számolni :)
110
Location A memória absztrakt reprezentációja
Rendkívül fontos, mert ez az alapja annak, hogy mik feleltethetőek meg egymásnak és mik nem! (Ugyanannyi location-t kell foglalniuk) Rule of thumb-ként: egy vec4 = 1 location De sajnos a location fajtájától függően ez változhat Továbbá hogy egy változó beleszámítson a location-ök számolásába aktívnak kell lennie (a shader eredményéhez hozzá kell járulnia)
111
Location Háromféle location van:
Attribútum location: array pufferek és vertex shader input interface-ek közötti megfeleltetésre (vagyis a „VAO indexek”) Varying location: a különböző programozható fázisok közötti kommunikációra használt in és out változók location-jei Fragment output location: a fragment shader kimenetei változóinak a megfeleltetése a glDrawBuffers táblával
112
Location (OGL Insights)
113
Location A GL_MAX_VERTEX_ATTRIBS határozza meg az egy vertexhez tartozó location-ök maximális számát (legalább 16) Ha egy attribútum egy location (vagyis <= vec4), akkor ennyi attribútuma lehet egy vertexnek (nem pedig annyi, ahányszor vec4-nyi a foglalás!!!) A fragment shader kimeneteinek max száma a GL_MAX_COLOR_ATTACHMENTS segítségével kérhető le (legalább 8)
114
Location
115
Location A vertex shader input interface VAO-val egyeztetése (interface match) location alapú (=vec4 alapú), ezért alapesetben nagyon megengedő Ha egy attribútum vec3-ként van VBO-ban, de a shader vec4-ként definiálja, akkor automatikusan castolódik Méghozzá a vec4(0,0,0,1) alapértéknek megfelelően → ingyen DKR→homogén konverzió! De a double...
116
Location
117
Location A különböző shader fázisok közötti interface match location-ökön keresztül is elérhető Ekkor ez (.vert) layout(location = 0) out vec4 color; és ez (.frag) layout(location = 0) in vec4 diffuseAlbedo; egyezést fog adni
118
Layout qualifiers Vigyázzunk, hogy jól adjuk meg a location-öket!
Mik lesznek itt a location-ök? struct MyData { vec3 field1; float field2[3]; }; layout (location = 0) out vec3 arr[3]; layout (location = ?) out MyData outDat; layout (location = ?) out mat4 trf;
119
Layout qualifiers Vigyázzunk, hogy jól adjuk meg a location-öket!
Mik lesznek itt a location-ök? struct MyData { vec3 field1; float field2[3]; }; layout (location = 0) out vec3 arr[3]; layout (location = 3) out MyData outDat; layout (location = 7) out mat4 trf;
120
Layout qualifiers A fentieket felül lehet írni (nyilván nem úgy, hogy rátolod egy korábbi adattag location-jére): layout(location = 0) out Block { vec2 first; dvec4 second[3]; vec4 third; layout(location = 10) vec2 fourth; dvec4 fifth; layout(location = 8) dvec3 sixth; vec3 seventh; };
121
Layout qualifiers Uniform, shader storage object-ek és minden átlátszatlan típus (pl. sampler2D) binding indexét is meghatározhatjuk: layout(binding = 3) uniform sampler2D mainTexture; layout(binding = 1, std140) uniform MainBlock { vec3 data; };
122
Layout qualifiers Sőt, a uniform-ok location-jeit is:
layout(location = 2) uniform mat4 modelToWorldMatrix; Ezután a glGetUniformLocation( prog, "modelToWorldMatrix"); biztosan 2-t ad vissza
123
Layout qualifiers Még azt is meg kell valahogyan határozni, hogy a memóriában hogyan vannak egymás után az adatok Lehet packed, shared, std140 és std430 Az std-k előnye, hogy nem kell egyesével lekérdezni az adattag offset-eket
124
Beépített változók Vertex shader - input
in int gl_VertexID; in int gl_InstanceID;
125
Beépített változók Vertex shader - output
out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };
126
Beépített változók Tessellation control shader - input
in int gl_PatchVerticesIn; in int gl_PrimitiveID; in int gl_InvocationID;
127
Beépített változók Tessellation control shader - output
patch out float gl_TessLevelOuter[4]; patch out float gl_TessLevelInner[2]; out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_out[];
128
Beépített változók Tessellation eval shader - input
in vec3 gl_TessCoord; in int gl_PatchVerticesIn; in int gl_PrimitiveID; patch in float gl_TessLevelOuter[4]; patch in float gl_TessLevelInner[2]; in gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_in[gl_MaxPatchVertices];
129
Beépített változók Tessellation eval shader - output
out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };
130
Beépített változók Geometry shader - input
in int gl_PrimitiveIDIn; in int gl_InvocationID; in gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; } gl_in[];
131
Beépített változók Geometry shader - output
out int gl_PrimitiveID; out int gl_Layer; out int gl_ViewportIndex; out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };
132
Beépített változók Fragment shader - input
in vec4 gl_FragCoord; in bool gl_FrontFacing; in vec2 gl_PointCoord; in int gl_SampleID; in vec2 gl_SamplePosition; in int gl_SampleMaskIn[]; in float gl_ClipDistance[]; in int gl_PrimitiveID; in int gl_Layer; in int gl_ViewportIndex;
133
Beépített változók Fragment shader - uniform
struct gl_DepthRangeParameters { float near; float far; float diff; }; uniform gl_DepthRangeParameters gl_DepthRange;
134
Beépített változók Fragment shader - output
out float gl_FragDepth; out int gl_SampleMask[];
135
Fragment shader Tanultuk, hogy amennyiben a fragment shader nem változtatja a fragment mélységét, akkor early z culling-ot tud használni a GPU Viszont van lehetőségünk, hogy vállalást tegyünk, milyen módon változtatja a mélységértéket a fragment shader layout(depth_<condition>) out float gl_FragDepth <condition> \in {any, greater, less, unchanged }
136
Fragment shader A gl_FrontFacing hasznos tud lenni, ha a hátrafelé néző lapokat is ki akarjuk rajzolni, de megkülönböztetve az előrefelé nézőktől Csak kapcsoljuk ki a backface culling-ot, hiszen ha az aktív, akkor a vágásnál (geometry shader után) eldobja a rendszer a hátrafelé néző primitíveket
137
Szubrutinok Egyeseket nagyon csábítja a lehetőség, hogy über-shadereket csináljanak Amik hatalmas elágazásokkal kezdődnek, valamilyen uniform változó értékétől függően (pl. most hányadfokú polinom gyökeit keresem, ennek függvényében vagy megoldóképletet, vagy gyökkeresést használok) A szubrutinokkal ezek lesznek kiválthatóak – kicsit hasonlóak lesznek, mint a függvénypointerek C-ben
138
Szubrutinok Először definiáljuk a függvény szignatúráját
subroutine vec4 colorRedBlue (); Aztán csinálunk variációkat a témára subroutine (colorRedBlue ) vec4 redColor() { return vec4(1, 0, 0, 1); } És subroutine (colorRedBlue ) vec4 blueColor() { return vec4(0, 1, 0, 1); }
139
Szubrutinok Felveszünk egy új uniform változót
subroutine uniform colorRedBlue myRedBlueSelection; Aztán használjuk void main() { color = myRedBlueSelection(); gl_Position = pvm * position ; }
140
Szubrutinok GLuint routineC1 =
glGetSubroutineIndex(p, GL_VERTEX_SHADER, "redColor"); GLuint routineC2 = glGetSubroutineIndex(p, GL_VERTEX_SHADER, "blueColor"); GLuint v1 = GlGetSubroutineUniformLocation( program_ID, GL_VERTEX_SHADER, "myRedBlueSelection");
141
Szubrutinok Ezután már csak a glUseProgram után és a kirajzolás előtt be kell állítani, hogy melyik megvalósításra mutasson a subroutine változó Ehhez a glUniformSubroutinesuiv hívást használjuk A megfelelő nevű szubrutin indexét glGetSubroutineIndex(prg, <shader>, „nev”) adja vissza
142
int maxSub,maxSubU,activeS,countActiveSU;
char name[256]; int len, numCompS; glGetIntegerv(GL_MAX_SUBROUTINES, &maxSub); glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxSubU); glGetProgramStageiv(p, GL_VERTEX_SHADER, GL_ACTIVE_SUBROUTINE_UNIFORMS, &countActiveSU);
143
for (int i = 0; i < countActiveSU; ++i) {
glGetActiveSubroutineUniformName(p, GL_VERTEX_SHADER, i, 256, &len, name); glGetActiveSubroutineUniformiv(p, GL_VERTEX_SHADER, i, GL_NUM_COMPATIBLE_SUBROUTINES, &numCompS); int *s = new int[numCompS]; glGetActiveSubroutineUniformiv(p, GL_VERTEX_SHADER, i, GL_COMPATIBLE_SUBROUTINES, s); for (int j=0; j < numCompS; ++j) { glGetActiveSubroutineName(p, GL_VERTEX_SHADER, s[j], , &len, name); } delete s;
144
Shader binárisok Linkelés előtt megmondhatjuk, hogy a program bináris tárgykódja lekérhető legyen glProgramParameteri( programHandle, GL_PROGRAM_BINARY_RETRIEVABLE_HINT, GL_TRUE); Majd inkelés után, elmentéshez lekérjük a hosszát: glGetProgramiv(programHandle, GL_PROGRAM_BINARY_LENGTH, &bufSize);
145
Shader binárisok A bináris kódot és formátumát lekérjük
GLenum binaryFormat; glGetProgramBinary(programHandle, bufSize, NULL, &binaryFormat, &buffer[0]); Egy programnak pedig így tudjuk odaadni std::vector buf(bufSize); glProgramBinary(prg2Handle, binaryFormat, &buf[0], bufSize);
146
Shader binárisok A bináris csak a linkelés pillanatában aktuális állapotát őrzi meg a programnak Tehát például a uniform változók az alapértelmezett kezdőértékeikkel lesznek kiírva Viszont nem platformfüggetlen a bináris kód Nem is eszközfüggetlen Sőt, ugyanazon a gép, ugyanazon eszközén sem biztos, hogy nem kell újrafordítani egy driverfrissítés után...
147
Shader binárisok
148
Shader binárisok Program binary formats are not intended to be transmitted. It is not reasonable to expect different hardware vendors to accept the same binary formats. It is not reasonable to expect different hardware from the same vendor to accept the same binary formats. Indeed, you cannot expect the cached version to work even on the same machine. Driver updates between when the data was cached and when it is reloaded can change the acceptable binary formats. Therefore, glProgramBinary can fail frequently. If you use this functionality, you must have a fallback for creating your shaders if the binary is rejected.
149
Transform feedback
150
OpenGL több context-tel
Lehetőség van arra, hogy több OpenGL context- et is létrehozzunk Sőt, ezek bizonyos erőforrásait meg is oszthatjuk egymással Arra figyeljünk, hogy a konkrét ablakozórendszertől függő „tegyük aktívvá X kontextet” eljárást hívjuk meg mindig, amikor a saját contextünket használnánk
151
OpenGL több context-tel
Szálak esetén: Minden szálnak egyetlen aktív OpenGL context-je lehet egy adott pillanatban Viszont különböző időpontokban különböző context-ek is lehetnek aktívak a szálon belül Mivel a fő parancsfeldolgozónál úgyis sorba kell állni, ezért nem lesz sebességgyorsulás, hogy ha több szálon rajzolunk De ha egy szálon rajzolunk, a többin pedig adatot feltöltünk már rögtön más a helyzet!
152
OpenGL több context-tel - megosztás
Az adatjellegű erőforrások megoszthatóak: VBO-k Index bufferek Shader programok (de vigyázz, magaddal viszed a uniform-ok bind értékeit) Textúrák PBO-k samplerek
153
OpenGL több context-tel - megosztás
Állapot jellegű (vagy inkább tároló, összefoglaló) erőforrások nem oszthatóak meg: VAO FBO
154
OpenGL több context-tel - megosztás
A megosztás előtt össze kell kapcsolni a két context-et Például Windows-on: glrc1=wglCreateContext(dc); glrc2=wglCreateContext(dc); wglShareLists(glrc1, glrc2); Ezzel összekapcsoljuk az erőforrásaik névtereit
155
OpenGL több context-tel - megosztás
A közös névtér azt jelenti, hogy az erőforrások számlálói közös indexre kerülnek Azaz ha létrehozunk egy VBO-t glrc1-ben és 1-es az azonosítója, akkor ha glrc2-ben is létrehozunk egy VBO-t, akkor az ő azonosítója 2-es lesz (m_VBOid == 2)
156
Framebuffer Object
157
FBO A context-hez tartozik egy default framebuffer
Ez az operációs rendszer adja át És az OS is kezeli (a felbontását, formátumát stb.) A default framebuffer (a fentiek miatt) GL-ből nem módosítható, nem törölhető Viszont lehetőség van az aktuális rajzolási parancsokat egy saját FBO-ba irányítani
158
FBO Logikailag a hátterében pixelek kétdimenziós tömbje áll
A pixelekhez különböző adatok tartozhatnak, amik úgynevezett logikai pufferekben vannak: Szín(ek) – tehát ez akár több puffer is lehet Stencil Mélységi A default FBO-ban 4 puffer van: front left, front right illetve back left, back right
159
FBO pufferek A különböző logikai pufferekben tehát minden pixelhez tartoznak adatok Ezek az adatok valahány bitből állnak Ha úgy gondoljuk azt, hogy ezeket az attribútumokat egymásra pakoljuk a pixeleken, akkor a megfelelő attribútumok megfelelő bitjei úgynevezett bitplane-eket alkotnak [0,0]:b1 [1,0]:b1 [2,0]:b1 [0,0]:b0 [1,0]:b0 [2,0]:b0 pixel [0,0]:g1 [1,0]:g1 [2,0]:g1 bitplane [0,0]:g0 [1,0]:g0 [2,0]:g0 [0,0]:r1 [1,0]:r1 [2,0]:r1 [0,0]:r0 [1,0]:r0 [2,0]:r0
160
FBO A színkomponensek különbözőképp lehetnek tárolva (normalizált fix-pontos vagy lebegőpontos stb.) De minden komponenst (R, G, B, A) ugyanúgy kell eltárolni
Hasonló előadás
© 2024 SlidePlayer.hu Inc.
All rights reserved.