diff --git a/docs/lectures/01.md b/docs/lectures/01.md index 57d4bcd40e945cfe142661e5edb5bfafdce460df..3344875e3466b1c87b4b8f7bf904c11b38f444ca 100644 --- a/docs/lectures/01.md +++ b/docs/lectures/01.md @@ -1,3 +1,7 @@ +!!! info "" + + A leckéhez tartozó teljes kód megtalálható a hozzá tartozó [github branch-en](https://git.sch.bme.hu/kszk/devteam/vulkan-workshop/-/tree/lecture_01-final). + # Inicializálás Mielőtt nekilátnál a kódolásnak, hozz létre egy kiinduló projektet. A példák kiinduló kódja megtalálható [ezen a linken](https://git.sch.bme.hu/kszk/devteam/vulkan-workshop/-/tree/starter). @@ -8,7 +12,7 @@ A példák Vulkan mellett [glm](https://github.com/g-truc/glm), [GLFW](https://w ## Általános -A workshop folyamán a [Vulkan hivatalos C++ binding-ja](https://github.com/KhronosGroup/Vulkan-Hpp)it fogjuk használni, és a kód részletek is C++20-ban lettek megírva. +A leckék során a [Vulkan hivatalos C++ binding-ja](https://github.com/KhronosGroup/Vulkan-Hpp)it fogjuk használni, és a kód részletek is C++20-ban lettek megírva. ## Instance @@ -18,7 +22,7 @@ Bármilyen Vulkan kód kiinduló eleme egy `vk::Instance` (C-ben `VkInstance`) o A C++ binding-ban [RAII támogatás](https://github.com/KhronosGroup/Vulkan-Hpp/blob/main/vk_raii_ProgrammingGuide.md) is adott. De ez tapasztalat alapján nekünk inkább csak a fordítást fogja lassítani, minthogy a kódolásban könnyedséget okozna. -Mivel ez egy Vulkan handle, ezért használat után nekünk kell "felszabadítani". Habár nem best practice, de mi minden ilyen handle helyett egy C++ `unique_ptr`-hez hasonló struktúrával fogjuk használni ezeket, amit a Vulkan binding biztosít. +Mivel ez egy általunk létrehozott erőforrás, ezért használat után is nekünk kell "felszabadítani". Habár nem feltétlen *best practice*, de mi minden ilyen *handle*-t egy C++ `unique_ptr`-hez hasonló struktúrába csomagolva fogunk használni, amit a Vulkan binding biztosít. ??? example "Hozzuk létre a `Renderer` osztályt!" @@ -41,7 +45,8 @@ Mivel ez egy Vulkan handle, ezért használat után nekünk kell "felszabadítan ```cpp title="Renderer.cpp" #include "Renderer.hpp" - [[nodiscard]] static auto create_instance() -> vk::UniqueInstance { + [[nodiscard]] + static auto create_instance() -> vk::UniqueInstance { return {}; } @@ -50,12 +55,14 @@ Mivel ez egy Vulkan handle, ezért használat után nekünk kell "felszabadítan Bizonyos játékmotorokhoz és appokhoz külön hardveres támogatás van, amit a Vulkan-nak ilyenkor lehet jelezni. Az applikációnk által használni tervezett legnagyobb API verzió *1.0* lesz. -Ezeket az értékeket gyűjti össze a `vk::Application` struct, amelyet mi default-inicializálunk. +Ezeket az értékeket gyűjti össze a `vk::ApplicationInfo`. ??? example "Hozzuk létre a `vk::ApplicationInfo`-t!" ```cpp - constexpr vk::ApplicationInfo application_info{}; + constexpr static vk::ApplicationInfo application_info{ + .apiVersion = VK_API_VERSION_1_0, + }; ``` !!! info inline end "Vulkan layers" @@ -67,7 +74,7 @@ Debug-oláshoz beállítünk [egy layer](https://vulkan-tutorial.com/Drawing_a_t ??? example "Ezt egy globális változóba szervezzük, mert még később is jól jöhet." ```cpp - const std::vector<std::string> g_layers{ + const std::vector<const char*> g_layers{ #ifdef ENGINE_VULKAN_DEBUG "VK_LAYER_KHRONOS_validation" #endif @@ -84,18 +91,21 @@ Ezeknél több mindent is beállíthatnánk még az Instance létrehozásához, ```cpp const std::vector<const char*> g_layers{ - #ifdef ENGINE_VULKAN_DEBUG + #ifdef RENDERER_VULKAN_DEBUG "VK_LAYER_KHRONOS_validation" #endif }; - [[nodiscard]] static auto create_instance() -> vk::UniqueInstance + [[nodiscard]] + auto create_instance() -> vk::UniqueInstance { - constexpr static vk::ApplicationInfo application_info{}; + constexpr static vk::ApplicationInfo application_info{ + .apiVersion = VK_API_VERSION_1_0, + }; const vk::InstanceCreateInfo create_info{ - .pApplicationInfo = &application_info, - .enabledLayerCount = static_cast<uint32_t>(g_layers.size()), + .pApplicationInfo = &application_info, + .enabledLayerCount = static_cast<uint32_t>(g_layers.size()), .ppEnabledLayerNames = g_layers.data(), }; @@ -105,7 +115,7 @@ Ezeknél több mindent is beállíthatnánk még az Instance létrehozásához, ## Physical Device -Minden a gépen jelen levő Vulkan-képes processzort egy `vk::PhysicalDevice`-al reprezentál könyvtár. Ezeket a `vk::Instance::enumeratePhysicalDevices` függvénnyel le is kérhetjük. +Minden a gépen jelen levő Vulkan-képes processzort egy `vk::PhysicalDevice`-al reprezentál a könyvtár. Ezeket a `vk::Instance::enumeratePhysicalDevices` függvénnyel le is kérhetjük. Érdemes egy diszkrét GPU-val dolgozni, ha az jelen van, rosszabb esetben integrálttal. Így válasszuk ki a számunkra legmegfelelőbb egységet. @@ -121,14 +131,13 @@ Minden a gépen jelen levő Vulkan-képes processzort egy `vk::PhysicalDevice`-a } auto ranked_devices_view{ - physical_devices - | std::views::transform([](vk::PhysicalDevice t_physical_device) { - switch (t_physical_device.getProperties().deviceType) { + physical_devices | std::views::transform([](vk::PhysicalDevice physical_device) { + switch (physical_device.getProperties().deviceType) { case vk::PhysicalDeviceType::eDiscreteGpu: - return std::make_pair(t_physical_device, 2); + return std::make_pair(physical_device, 2); case vk::PhysicalDeviceType::eIntegratedGpu: - return std::make_pair(t_physical_device, 1); - default: return std::make_pair(t_physical_device, 0); + return std::make_pair(physical_device, 1); + default: return std::make_pair(physical_device, 0); } }) }; @@ -152,22 +161,23 @@ Minden a gépen jelen levő Vulkan-képes processzort egy `vk::PhysicalDevice`-a Mielőtt elkezdünk a GPU-n dolgozni, azelőtt elengedhetetlen a használni tervezett extra funkciók megadása. Egyelőre ezzel mi még nem élünk. -A (Logical) Device szintaktikailag nagyon hasonlít az Instance-hez. Itt is lesz olyan, amit majd csak később állítunk be. A mostani alkalommal egyedül a `vk::Queue`-kra fogunk koncentrálunk. +A (Logical) Device szintaktikailag nagyon hasonlít az Instance-hez. Itt is lesz olyan, amit majd csak később állítunk be. A mostani alkalommal egyedül a `vk::Queue`-kra koncentrálunk. -A grafikus kártya egy hihetetlenül parallelizált eszköz. Ám ennek a kihasználásához adatot kell neki küldeni, és megmondani hogy mit csináljon. Ez a parancs feldolgozás *Queue*-kon keresztül történik, amelyek képesek párhuzamosan több "command" végrehajtására. +A grafikus kártya egy hihetetlenül parallelizált eszköz - ám ennek a kihasználásához adatot kell neki küldeni, és megmondani hogy mit csináljon. Ez a parancs feldolgozás *Queue*-kon keresztül történik, amelyek képesek párhuzamosan több "command" végrehajtására. -!!! note "" +!!! tip "" Habár egy `vk::Queue` egyszerre több `vk::CommandBuffer` végrehajtására is képes, `vk::CommandBuffer`-eket feldolgozásra küldeni egy `vk::Queue`-nak továbbra is csak egy szálon lehetséges. -Egy `vk::Queue` többféle feladat végrehajtására is képes - legyen az grafikai, általános, adat-átvitel, vagy valami más. A GPU tervezők számunkra a hasonló tulajdonságokkal rendelkező *Queue*-kat úgynevezett *Queue family*-kben teszik elérhetővé. +Egy `vk::Queue` többféle feladat végrehajtására is képes lehet - legyen az grafikai, általános feldolgozás, adat-átvitel, vagy valami más. A GPU tervezők számunkra a hasonló tulajdonságokkal rendelkező *Queue*-kat úgynevezett *Queue family*-kbe csoportosítják. -Válasszunk ki egy grafikai munkát támogató családot (ennek megkötése, hogy adat-átvitelt is támogasson), és abból is **egy** *Queue*-t. A mi céljainkhoz ez az egy elég lesz mindenre. +Válasszunk ki egy grafikai munkát támogató családot (ebből már következik, hogy adat-átvitelt is támogat), és abból is **egy** *Queue*-t. A mi céljainkhoz ez az egy elég lesz mindenre. !!! example "" ```cpp - [[nodiscard]] static auto find_graphics_queue_family( + [[nodiscard]] + static auto find_graphics_queue_family( vk::PhysicalDevice t_physical_device ) -> uint32_t { @@ -188,7 +198,8 @@ Adott minden, hogy a *Device*-t is létrehozzuk. !!! example "" ```cpp - [[nodiscard]] static auto create_device(const vk::PhysicalDevice t_physical_device) + [[nodiscard]] + static auto create_device(const vk::PhysicalDevice t_physical_device) -> vk::UniqueDevice { const vk::DeviceQueueCreateInfo queue_create_info{ @@ -208,125 +219,3 @@ Adott minden, hogy a *Device*-t is létrehozzuk. !!! tip "" A `Renderer`-hez adjuk hozzá az így létrejövő `vk::UniqueDevice`-t, és a kiválasztott `vk::Queue`-t is. (Utóbbi lentebb a végső kódban látható.) - -## Elkészült kód - -!!! example "" - - ```cpp title="Renderer.hpp" - #pragma once - - #include <vulkan/vulkan.hpp> - - class Renderer { - public: - Renderer(); - - private: - vk::UniqueInstance m_instance; - vk::PhysicalDevice m_physical_device; - uint32_t m_graphics_queue_family; - vk::UniqueDevice m_device; - vk::Queue m_graphics_queue; - }; - ``` - - ```cpp title="Renderer.cpp" - #include "Renderer.hpp" - - #include <algorithm> - #include <ranges> - #include <vector> - - const std::vector<const char*> g_layers{ - #ifdef ENGINE_VULKAN_DEBUG - "VK_LAYER_KHRONOS_validation" - #endif - }; - - [[nodiscard]] static auto create_instance() -> vk::UniqueInstance - { - constexpr static vk::ApplicationInfo application_info{}; - - const vk::InstanceCreateInfo create_info{ - .pApplicationInfo = &application_info, - .enabledLayerCount = static_cast<uint32_t>(g_layers.size()), - .ppEnabledLayerNames = g_layers.data(), - }; - - return vk::createInstanceUnique(create_info); - } - - [[nodiscard]] auto static choose_physical_device(const vk::Instance t_instance) - -> vk::PhysicalDevice - { - const auto physical_devices{ t_instance.enumeratePhysicalDevices() }; - - if (std::ranges::empty(physical_devices)) { - throw std::runtime_error{ "No Vulkan physical device is available." }; - } - - auto ranked_devices_view{ - physical_devices - | std::views::transform([](vk::PhysicalDevice t_physical_device) { - switch (t_physical_device.getProperties().deviceType) { - case vk::PhysicalDeviceType::eDiscreteGpu: - return std::make_pair(t_physical_device, 2); - case vk::PhysicalDeviceType::eIntegratedGpu: - return std::make_pair(t_physical_device, 1); - default: return std::make_pair(t_physical_device, 0); - } - }) - }; - std::vector ranked_devices( - ranked_devices_view.begin(), ranked_devices_view.end() - ); - - std::ranges::sort( - ranked_devices, - std::ranges::greater{}, - &std::pair<vk::PhysicalDevice, int>::second - ); - - return ranked_devices.front().first; - } - - [[nodiscard]] auto static find_graphics_queue_family( - const vk::PhysicalDevice t_physical_device - ) -> uint32_t - { - uint32_t index{}; - for (const auto& properties : t_physical_device.getQueueFamilyProperties()) - { - if (properties.queueFlags & vk::QueueFlagBits::eGraphics) { - return index; - } - index++; - } - throw std::runtime_error{ "Could not find graphics queue family" }; - } - - [[nodiscard]] auto static create_device(const vk::PhysicalDevice t_physical_device) - -> vk::UniqueDevice - { - const vk::DeviceQueueCreateInfo queue_create_info{ - .queueFamilyIndex = find_graphics_queue_family(t_physical_device), - .queueCount = 1, - }; - - vk::DeviceCreateInfo device_create_info{ - .queueCreateInfoCount = 1, - .pQueueCreateInfos = &queue_create_info, - }; - - return t_physical_device.createDeviceUnique(device_create_info); - } - - Renderer::Renderer() - : m_instance{ create_instance() }, - m_physical_device{ choose_physical_device(*m_instance) }, - m_graphics_queue_family{ find_graphics_queue_family(m_physical_device) }, - m_device{ create_device(m_physical_device) }, - m_graphics_queue{ m_device->getQueue(m_graphics_queue_family, 0) } - {} - ``` diff --git a/mkdocs.yml b/mkdocs.yml index 626d86334825902d9193f201a3be2fbfc4d4cc5f..026eecdd5bd64ef93afe132695234cde9b272338 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -47,4 +47,5 @@ markdown_extensions: - pymdownx.tabbed: alternate_style: true nav: + - index.md - 1. lecke: lectures/01.md