Az előadás letöltése folymat van. Kérjük, várjon

Az előadás letöltése folymat van. Kérjük, várjon

ELTE IK tavaszi félév Valasek Gábor

Hasonló előadás


Az előadások a következő témára: "ELTE IK tavaszi félév Valasek Gábor"— Előadás másolata:

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, akkor 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
void glWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) A GL szerver (=GPU) blokkol addig, amíg az első argumentumban adott sync objektum signaled állapotba nem kerül Jelenleg a flags csak 0 nulla Jelenleg a timeout csak GL_TIMEOUT_IGNORED lehet

59 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

60 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

61 Adatátadás CPU és GPU között

62 DMA A DMA (direct memory access) vezérlő segítségével mozgat adatokat a rendszermemória és GPU memória között a rendszer Az adatátadás aszinkron (a CPU szemszögéből) és nem kell a CPU-nak felügyelnie De GPU oldalról ez szekvencializálva is megjelenthet (pl. még Fermi sem tudott egyszerre adatot átvinni és renderelni, de a Quadro-k egy ideje már képesek rá)

63 Pinned memory Hívják még pin-locked-nak vagy page-locked-nak
A rendszermemória olyan darabja, amit a GPU DMA tud címezni Emellett nem engedi a lapozót, hogy disk-re helyezze át

64 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

65 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

66 Pufferek feltöltése: glMapBuffer
A glMapBuffer és glUnmapBuffer utasítások már közvetlen a GPU DMA által elérhető pin-lockolt rendszer RAM címet adják át az alkalmazásnak (vagy nem – implementáció függő) 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

67 Kitérő: alignment Egy leírás arról, hogy miért fontos az, hogy garantáltan valamilyen rácsra illeszkedik-e a visszaadott pointer vagy sem: dalign/

68 Kitérő: alignment Memória a programozó szerint
Memória a processzor szerint

69 Kitérő: alignment 0-s és 1-es bájt olvasása, bájt
granularitású CPU-val 0-s és 1-es bájt olvasása, 2 bájt granularitású CPU-val

70 Kitérő: alignment

71 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

72 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):

73 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)

74 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

75 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

76 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

77 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);

78 Map/UnMap Természetesen ennek is vannak különböző verziói:
glMapBufferRange / glFlushMappedBufferRange glMapNamedBufferRange / glFlushMappedNamedBufferRange

79 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

80 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

81 Pufferek másolása source offset (byte) size (byte)
source buffer object glCopyBufferSubData destination offset (byte) dest. buffer object size (byte)

82 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

83 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));

84 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));

85 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));

86 Implicit szinkronizáció veszély
A GPU tipikusan 2-3 frame-mel előrébb jár, mint az alkalmazás Viszont ez azt is jelenti, hogy az aktuális frame- ben glBufferSubData-val vagy glMapBuffer[Range] segítségével módosítunk olyan puffert, amiből még épp kirajzolna a GPU

87 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:

88 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

89 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

90 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)

91 Streaming: orphaning Ugyanaz, mint a round robin, csak itt az egész a driver-en belül történik: glBufferData data pointere legyen nullptr é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);

92 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);

93 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...

94 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);

95 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:

96 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​

97 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)

98 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

99 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

100 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

101 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);

102 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"

103 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:

104 Shaderek

105 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

106 Interfész blokkok GLSL input, output, uniform vagy tároló puffer változók csoportja storage_qualifier block_name { <define members here> } instance_name;

107 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

108 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)

109 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

110 Interfész blokkok

111 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)

112 Uniform block – a shaderben
uniform BlobSettings { vec4 InnerColor; vec4 OuterColor; float RadiusInner; float RadiusOuter; };

113 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)

114 Uniform block GLuint blockIndex = glGetUniformBlockIndex( programHandle, "BlobSettings"); GLint blockSize; glGetActiveUniformBlockiv( programHandle, blockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize); GLubyte * blockBuffer= (GLubyte *)malloc(blockSize);

115 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);

116 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));

117 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 );

118 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)

119 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 :)

120 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)

121 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

122 Location (OGL Insights)

123 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)

124 Location

125 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...

126 Location

127 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

128 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;

129 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;

130 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; };

131 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; };

132 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

133 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

134 Beépített változók Vertex shader - input
in int gl_VertexID; in int gl_InstanceID;

135 Beépített változók Vertex shader - output
out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };

136 Beépített változók Tessellation control shader - input
in int gl_PatchVerticesIn; in int gl_PrimitiveID; in int gl_InvocationID;

137 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[];

138 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];

139 Beépített változók Tessellation eval shader - output
out gl_PerVertex { vec4 gl_Position; float gl_PointSize; float gl_ClipDistance[]; };

140 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[];

141 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[]; };

142 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;

143 Beépített változók Fragment shader - uniform
struct gl_DepthRangeParameters { float near; float far; float diff; }; uniform gl_DepthRangeParameters gl_DepthRange;

144 Beépített változók Fragment shader - output
out float gl_FragDepth; out int gl_SampleMask[];

145 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 }

146 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

147 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

148 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); }

149 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 ; }

150 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");

151 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

152 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);

153 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;

154 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);

155 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);

156 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...

157 Shader binárisok

158 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.

159 Transform feedback

160 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

161 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!

162 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

163 OpenGL több context-tel - megosztás
Állapot jellegű (vagy inkább tároló, összefoglaló) erőforrások nem oszthatóak meg: VAO FBO

164 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

165 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)

166 Framebuffer Object

167 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

168 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

169 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

170 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

171 Default és saját FBO A default FBO-t az OS adja
Neki van a színpufferekből potenciálisan 4 változata ( {front|back}{left|right} ) - de implementációfüggő, hogy valójában mennyit ad (van-e right, van-e back) Az alkalmazásban tetszőleges számú saját FBO-t csinálhatunk, amik viszont néhány dologban mások: Pixel ownership teszten minden pixele átmegy Lehet különböző méretűek az attachment-be kerülő képek, de ilyenkor a legnagyobb közös részen túli darabok definiálatlanok

172 FBO létrehozása Először a glGenFrameBuffers segítségével le kell generálni a szükséges számú azonosítót Ezután a glBindFrameBuffer segítségével bind- olunk, ahol 2(+1) target lehetséges: GL_DRAW_BUFFER: a rajzolási parancsok ide írnak GL_READ_BUFFER: az olvasási parancsok innen olvasnak – megegyezhet az előzővel GL_FRAMEBUFFER: mindkettő (de másutt nem feltétlen!)

173 FBO specifikálása Az FBO különböző logikai puffereket kezel:
Színpufferek (GL_COLOR_ATTACHMENT0- n) Mélységi puffer (GL_DEPTH_ATTACHMENT) Stencil puffer (GL_STENCIL_ATTACHMENT) Ezekre a szerepekre textúrák és úgynevezett renderbuffer-ek is hozzárendelhetőek

174 FBO attachment-ek Több FBO-nak is lehet ugyanaz a fizikai erőforrás (textúra vagy renderbuffer) egy attachment-je Az attachment általában egy 2D-s tömb Azaz mip-map-elt textúra esetén az egyik szint Cube map-nek az egyes lapjai külön-külön attachment-ek kellenek, hogy legyenek (mip- szintenként) Viszont lehet 3D-s dolgot is (cube map, texture array stb.), de ekkor a különböző elemek más- más layer lesznek

175 Renderbuffer Egyszerű adattárolásért felelős objektum
Egyetlen képet tartalmaz egy belsőleg használható formátumon Szokásos módon kezelendő: glGenRenderbuffers glBindRenderBuffers Adatfeltöltés glRenderbufferStorage, glNamedRenderbufferStorage stb. segítségével glDeleteRenderBuffers

176 Renderbufer attach void glFramebufferRenderbuffer( enum target, enum attachment, enum renderbuffertarget, uint renderbuffer ) A target-en aktív FBO-nak az attachment csatolási pontjára kapcsolja a renderbuffer-t A renderbuffertarget csak és kizárólag GL_RENDERBUFFER lehet


Letölteni ppt "ELTE IK tavaszi félév Valasek Gábor"

Hasonló előadás


Google Hirdetések