Miután pár hete csak az image space occlusion cull-al foglalkozom elértem ahhoz a ponthoz, amikor már az optimalizációs lehetőségeket keresi az ember. Amikor a CPU oldali lehetőségek elfogynak, akkor jön a GPU oldal. Ezek közül a glReadPixels jár az élen tekintve, hogy kb. felezi az fps-t. Vegyünk sorra azt a néhány - mert sajnos nem sok van - extension-t amik ennek gyorsítására lettek kitalálva.
Pixel buffer object
Ez az OpenGL object az aszinkron adatmozgatást (PACK avagy letöltés és UNPACK azaz feltöltés) hivatott megoldani. Az aszinkronitás itt azt jelenti, hogy a glBindBuffer + glReadPixels utasításpár nem blokkolja az alkalmazást, hanem csak elindít egy párhuzamos adatátvitelt egy kliens oldali memóriaterületre. Ennek a címét majd a glMapBuffer utasítással tudjuk lekérdezni, amely - érthető okokból - már egy szinkronizációt is végrehajt. Amennyiben volt elég idő az átvitelre, akkor a szinkronizáció nem jár időveszteséggel ellenkező esetben semmivel nem lesz gyorsabb a mezei glReadPixels hívásnál.
Amennyiben sikerül biztosítanunk az elegendő időt rájövünk, hogy nem mindegy mit és hogyan olvasunk.
- A PBO-t GL_STREAM_READ-ként lefoglalnunk; a GL_STATIC_READ némileg lassabb.
- Ha színt akarunk olvasni, akkor csakis GL_BGRA formátumban és GL_UNSIGNED_BYTE típusként tegyük (azonban valószínű más típust is lehet használni még). Ilyenkor jelentős (2x) sebesség növekedést is tapasztalhatunk.
- Ha mélységet akarunk olvasni, akkor jönnek a meglepetések. Az általam használt nVidia kártyán ugyanis bármit használtam nem tapasztaltam gyorsulást. Tehát GL_DEPTH_COMPONENT mellé kipróbáltam GL_FLOAT-ot, GL_UNSIGNED_INT-et, GL_UNSIGNED_SHORT-ot (bár hardveresen gyorsított és 16 bites depth bufferrel rendelkező PFD nem volt) és a teszt kedvéért GL_UNSIGNED_BYTE-ot.
Így hát jöttek a további próbálkozások...
Packed depth - stencil
Ehhez az szükséges, hogy mélység buffer 24 bites legyen és mellette legyen 8 bites stencil buffer is. Ilyenkor nem GL_DEPTH_COMPONENT-et olvasunk, hanem GL_DEPTH_STENCIL-t GL_UNSIGNED_INT24_8 típusként. Sajnos ez sem segített a helyzeten.
NV copy depth to color
Úgy tűnik hát, hogy a GPU-k nem igazán szeretik a mélység buffer olvasását ellenben a szín bufferét igen. Erre valószínűleg nVidiánál is rájöttek (vagy eleve tudták...) így hát készítettek hozzá egy extension-t.
Megjegyzés: náluk nem ismeretlen ez a megoldás. A Tegra 1 és 2 platform is rossz felbontású mélység bufferrel bír (takarási problémák mindenfelé). Erre kitaláltak egy extension-t, ami "nem-lineáris kódolású"-vá alakítja a mélység buffert és így eltűnnek a problémák.
Az extension nevéből kitalálható a működési elv. Egy glCopyPixels hívással átmásolható a mélység buffer a szín bufferbe (GL_DEPTH_STENCIL_TO_RGBA_NV vagy GL_DEPTH_STENCIL_TO_BGRA_NV). De ez is ugyanolyan feltételek mellett használható, mint a packed depth-stencil (ami szintén az ő nevükhöz fűződik...) valamint a szín buffernek 32 bitesnek kell lennie.
Összegezve
Ha mélység buffert szeretnék olvasni nVidia kártyán akkor az alábbiakat érdemes megtenni.
- glCopyPixels(..., GL_DEPTH_STENCIL_TO_BGRA_NV);
- glBindBuffer (GL_PIXEL_PACK_BUFFER,...);
- glReadPixels (..., GL_BGRA, GL_UNSIGNED_BYTE,..);
- glMapBuffer (...)
- A lekért adatok elérhetők 24 biten elkódolt 0...1 tartományba eső float-ként.
Gutta cavat lapidem, non vi, sed saepe cadendo...