Skip to content
Snippets Groups Projects
Commit d992d968 authored by n0F4x's avatar n0F4x
Browse files

Proofread lecture 1

parent 41992f52
No related branches found
No related tags found
No related merge requests found
!!! 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,14 +91,17 @@ 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,
......@@ -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) }
{}
```
......@@ -47,4 +47,5 @@ markdown_extensions:
- pymdownx.tabbed:
alternate_style: true
nav:
- index.md
- 1. lecke: lectures/01.md
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment