Předchozí článek byl věnován trochu obecnějšímu náhledu na nové grafické API Vulkan. V následujícím článku se již podíváme Vulkanu pod kapotu a porovnáme programátorské přístupy Vulkanu a OpenGL.
Vulkan je méně abstraktní a více low-level. Umožní tedy koncové aplikaci lépe optimalizovat výkon ale na oplátku si vyžádá od vývojáře mnohem více obslužného kódu a také mnohem větší zodpovědnost. Obsluha Vulkanu je oproti OpenGL opravdu výrazně složitější a grafičtí programátoři-začátečníci by stále měli začínat spíše s OpenGL (OpenGL se do důchodu zdaleka nechystá).
Vulkan je ANSI-C API, nepoužívá tedy objektové přístupy a stejně jako OpenGL je založen na free-entry-point funkcích (pokud chcete používání Vukanu zásadně zlidštit, doporučuji použít c++ wrapper Vulkan-HPP). OpenGL bylo založené na globálním stavovém automatu který se držel aktuálního OpenGL kontextu. Veškerá nastavení (př. GlPointSize() ) tak programátor vrhal globálními funkcemi po aktuálním kontextu a ten si vše pamatoval dokud nebylo nastavení přepsáno novým, nebo kontext zrušen. Vulkan nemá kontext ani stavový automat. Stav a data vázaná k aplikaci drží handler pojmenovaný VkInstance. Spousta dat je také předávána mezi funkcemi pomocí množství emunerations – na rozdíl od OpenGL je tedy Vulkan strong-typed. Vulkan také není vázán paradigmatem že se „bude kreslit něco na obrazovku“. Aplikace může vyžadovat například offscreen-rendering, ale také nemusí vyžadovat rendering vůbec. Vulkan lze totiž využít i pro ryze výpočetní operace – tedy stejně jako bychom běžně využili OpenCL.
Pro splnění svým ambiciozních cílů Vulkan přidává spoustu nových funkcí a mechanismů, které OpenGL neobsahoval nebo je před uživatelem skrýval pod vrstvou abstraktnosti. Vzhůru tedy podívat se na zoubek všem těm Command bufferům, pipelines, rendering passes, Swapchains,…
Command Buffers
Mechanismus Command bufferů je pravděpodobně jedna z nejpřínosnějších novinek Vulkanu. Jedním ze základních nedostatků OpenGL byla nemožnost využít více jader CPU pro volání draw-calls. I na dnešních 4 a více jádrových CPU tedy OpenGL kreslilo pouze jedním vláknem. Ne, že by OpenGL nemělo command buffer, ale byl ukrytý ve struktuře ovladače a měl jen implicitní mechanismy pro synchronizaci (pomineme-li glFinish() a glFlush()). Šlo o immediate mode API. Pokud tedy aplikace zavolala draw-call (např glDrawArrays(GL_TRIANGLES, 0, 3);), operace byla okamžitě zařazena do fronty na straně ovladače, ale na straně aplikace nebylo možné zjistit kdy/jestli již byla zpracována. Nebylo také možné optimalizovat volání tak, aby ovladač neodesílal do GPU zbytečně nekompletní frontu operací. Zahájení/ukončení přenosu vždy znamená určitý overhead a tak je výhodné provést přenos co nejvíce draw-calls najednou.
Command Buffers mechanismus představuje klíč k vícevláknové podpoře API Vulkan. Uživatelská aplikace může vytvořit libovolné množství Command bufferů a následně každý plnit draw-calls či jinými operacemi z různých vláken. Následně lze odeslat veškeré nastřádané operace naráz (lze např. omezit submity do GPU na jediný pro každý snímek), popř. jak je to výhodné pro aktuální grafickou/výpočetní úlohu.
Opět – Vulkan není jen o kreslení. Command Buffer může obsahovat příkazy pro:
- Přenos dat (Transfer)
- Vykreslování (Graphics)
- Výpočetní operace (Compute)
- Synchronizaci
Existují dva druhy Command bufferů – Primární a Sekundární. Primární je obvykle pouze jeden. Sekundární Buffer existuje obvykle jeden pro každé vlákno. Při finalizaci práce s Buffery je obsah sekundárních Command bufferů sloučen do primárního Bufferu a je proveden submit do GPU. Každý Command buffer je alokován že svého Command poolu, objektu který spravuje potřebnou paměť a řídí základní synchronizaci.
Velmi užitečnou vlastností Command bufferů je jejich znovupoužitelnost. Pokud je renderován snímek který se příliš neliší od snímku předchozího (v prováděných operacích, ne pixel od pixelu) lze Command buffery opětovně využít a ušetřit tak zásadní množství CPU času.
Render pass
Jde o jakýsi návod jak renderovat. Zahrnuje základní nastavení pro vykreslení scény či správu vstupních parametry pro shadery. Můžu také zahrnovat více subpassů pro renderování různých výstupních bufferů (Depth-buffer, Gbuffer, atd…). Představuje zastřešující prvek pro kreslení čehokoliv v rámci Vulkanu. Všechny vykreslovací příkazy tak obvykle vyžadují být “uvnitř” rendering passu. V při základním renderování tak v Command bufferu nalezneme vkCmdBeginRenderPass → Bind Pipeline → Bind Descriptors → Draw → vkCmdEndRenderPass.
Swapchain
V OpenGL, na konci renderovacího procesu scéna obvykle končí ve výstupním bufferu (v případě double bufferingu v back bufferu). Vulkan ovšem žádný podobný defaultní výstupní buffer nemá. Série výstupních bufferů se zde nazývá Swapchain a koncová aplikace si ji musí vytvořit sama. Jelikož práce s výstupními buffery vyžaduje platformě závislé postupy je navíc Swapchain dostupný pouze jako rozšíření a jeho podporu si musí koncová aplikace explicitně ověřit.
Závěr
Vulkanu patří nepochybně budoucnost ale jednoduchost nasazení OpenGL mu dává stále právo na život v mnoha (zejména neherních) aplikacích. Zejména od verze OpenGL 4.0. lze navíc mechanismy renderování přiblížit těm typickým pro Vulkan. Implementace Vulkanu je zatím v mnoha ohledech na začátku a zejména grafické karty nVidia mohou vykazovat dokonce propad výkonu oproti OpenGL. Lepší HW/SW přizpůsobení moderním low-level API (v případě nVidia zejména vylepšení Asynchronous compute) lze ovšem nanejvýš očekávat. Pak se Vulkan může stát synonymem pro intrakci s GPU na několik dalších desetiletí.
Napsat komentář