Vulkan

Vulkan je nové grafické API (1.0 v roce 2016), jehož abstrakce nad grafickými kartami v porovnání například s OpenGL mnohem lépe odpovídá fyzické architektuře současných grafických karet
Díky tomu máme:
- potenciálně větší výkon
- méně nečekaných chování ovladače grafické karty

Hlavní výhody v porovnání s ostatními API

- Narozdíl od Direct3D a Metal je plně multiplatformní a umožňuje simultání vývoj na Windows, Linux i Android
- Jednotné API pro mobilní i stolní zařízení, narozdíl od OpenGL / OpenGL ES
- Lepší škálování na vícejádrových procesorech, Direct3D 11 a OpenGL 4 původně navržené pro single-core CPUs, dodané rozšíření pro podporu multi-core většinou neškáluje tak dobře jako Vulkan
- OpenGL využívá GLSL na psaní shaderů, přičemž k jejich kompilaci dochází až za běhu aplikace, tedy OpenGL ovladač musí implementovat také kompilátor, naproti tomu Vulkan přijímá shadery v již předkompilovaném binárním formátu zvaném SPIR-V (Standard Portable Intermediate Representation)
- Ray Tracing skrze rozšíření VK_KHR_ray_tracing

OpenGL Vulkan
Jeden globální stav Objektově založený bez globálního stavu
Stav je navázaný na jediný kontext Veškeré stavové změny se zapisují do command bufferu
Sekvenční provádění operací na jednom vlákně Možnost vícevláknového programování s více command buffery
Správa synchronizace a paměti GPU je standardně skrytá Explicitní kontrola nad memory managementem a synchronizací
Rozsáhlá kontrola chyb Za běhu žádná kontrola chyb, možnost validační vrstvy

Základní struktura kódu

- funkce, výčty a struktury se nachází v hlavičce vulkan.h, která je součástí Vulkan SDK
- funkce začínají na lower case vk prefix
- výčty a struktury mají Vk prefix, samotné výčtové typy potom VK_ prefix
- parametry se do funkcí předávají v podobě struktur

Pattern tvorby nového objektu
VkXXXCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_XXX_CREATE_INFO;
createInfo.pNext = nullptr;
createInfo.foo = ...;
createInfo.bar = ...;

VkXXX object;
if (vkCreateXXX(&createInfo, nullptr, &object) != VK_SUCCESS) {
    std::cerr << "Failed to create object" << std::endl;
    return false;
}

První trojúhelník

1. Instance and physical device selection
VkInstance
- popis naší aplikace
- výběr API rozšíření, která budeme používat
VkPhysicalDevice
- reprezentuje jedno fyzické GPU v počítači
- můžeme se dotazovat na parametry, jako například velikost VRAM, či zda se jedná o dedikovanou grafickou kartu
2. Logical device and queue families
VkDevice
- logická reprezentace GPU, může obsahovat více fyzických GPU najednou
- výběr VkPhysicalDeviceFeatures, které budeme využívat
- výběr queue family, kterou budeme chtít používat
- různé queue families podporují různé operace (graphics, compute, memory transfer, ...)
- různá GPU disponují různými queue families
3. Window surface and swap chain
VkSurfaceKHR
- cross-platform abstrakce nad okenním systémem daného OS
- při tvorbě instance předání odkazu na handle nativního okna
- v případě Windows se jedná o HWND
- řeší za nás knihovna GLFW, která podle OS vytvoří nativní okno a vrátí nám zpátky handle
VkSwapchainKHR
- kolekce render targetů
- hlavním smyslem je zajistit, aby se na obrazovce zobrazoval jiný obraz než ten, do kterého momentálně kreslíme
- počet render targetů závisý na zvoleném present módu, typicky double buffering (vsync) nebo triple buffering
4. Image views and framebuffers
- obrázek předaný od swap chain je potřeba zabalit do VkImageView a VkFramebuffer
5. Render passes
6. Graphics pipeline
VkPipeline
- popisuje konfigurovatelný stav grafické karty, jako je viewport size, operace nad depth bufferem a programovatelný stav pomocí VkShaderModule
- VkShaderModule objekty jsou vytvořeny z shader bytekódu
- které render targety budou během renderování využity určíme referencí na konkrétní render pass
7. Command pools and command buffers
- mnoho z operací, které Vulkan provádí, jako jsou například drawing commands, musí být prvně odeslány do fronty
- před odesláním je ale zapotřebí nahrát je do struktury VkCommandBuffer
- tyto se alokují z VkCommandPool, který je přidružený ke konkrétní queue family
- pro vykreslení trojúhelníku zapíšeme do command bufferu následující:
  1. Begin the render pass
  2. Bind the graphics pipeline
  3. Draw 3 vertices
  4. End the render pass
8. Main loop
- prvně získáme obrázek ze swap chainu voláním vkAcquireNextImageKHR
- vybereme command buffer odpovídající indexu obrázku ze swap chainu a vykonáme voláním vkQueueSubmit
- nakonec obrázek vrátíme do swap chainu za účelem zobrazení na obrazovce voláním vkQueuePresentKHR
TODO - synchronization

- Zdroje -
https://vulkan-tutorial.com
https://en.wikipedia.org/wiki/Vulkan_(API)
https://liamhinzman.com/blog/vulkan-fundamentals.html
https://github.com/SaschaWillems/Vulkan