From f52e8fa6898823692d93df34fd38dbc2243a0c5f Mon Sep 17 00:00:00 2001 From: n0F4x <ggabor2002@gmail.com> Date: Thu, 20 Jun 2024 15:39:54 +0200 Subject: [PATCH] Create swapchain --- docs/lectures/02/index.md | 265 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) diff --git a/docs/lectures/02/index.md b/docs/lectures/02/index.md index c257749..37a1566 100644 --- a/docs/lectures/02/index.md +++ b/docs/lectures/02/index.md @@ -148,3 +148,268 @@ Ilyenkor *clamp*-elni kell az általunk használni kívánt értéket a felszín A `surface_capabilities`-t majd az ablakfelszín segítségével fogjuk megkapni, a `framebuffer_size`-t pedig maga az ablak adja. ### Surface format + +A következő lépés, hogy megadjuk a képek pixelformátumát. +Ez lehetne például a jól ismert RGBA (red, green, blue, alpha), ahol minden *color channel* 8 bit-et kap. +Azonban a legtöbb képernyő a BGRA-t támogatja jól. + +A másik tulajdonság a színcsatornákban lévő értékek eloszlása/linearitása. +Mivel a szemünk nem lineáris intenzitással érzékeli az adott értékekhez tartozó fényt, ezért érdemes egy nem lineáris *color space* formátumot választani. +*(lásd. [gamma correction](https://en.wikipedia.org/wiki/Gamma_correction))* + +??? example "Példa kód" + + ```cpp title="Swapchain.cpp" + [[nodiscard]] + static auto choose_swapchain_surface_format( + const vk::SurfaceKHR surface, + const vk::PhysicalDevice physical_device + ) -> vk::SurfaceFormatKHR + { + const auto available_surface_formats{ physical_device.getSurfaceFormatsKHR(surface) }; + + for (const auto& surface_format : available_surface_formats) { + if (surface_format.format == vk::Format::eB8G8R8A8Srgb + && surface_format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) + { + return surface_format; + } + } + + for (const auto& surface_format : available_surface_formats) { + if (surface_format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { + return surface_format; + } + } + + return available_surface_formats.front(); + } + ``` + +### Képek száma + +Ajánlott a minimum támogatott értéktől legalább eggyel nagyobb számú képet használni a swapchain-hez. +Illetve arra is figyelnünk kell, hogy ne lépjük át a maximumot. +(Ha a maximum-hoz definiált érték 0, akkor nincs maximum.) + +??? example "Kódrészlet" + + ```cpp title="Swapchain.cpp" + [[nodiscard]] + static auto get_min_image_count(const vk::SurfaceCapabilitiesKHR& surface_capabilities + ) noexcept -> uint32_t + { + uint32_t image_count = surface_capabilities.minImageCount + 1; + if (surface_capabilities.maxImageCount > 0 + && image_count > surface_capabilities.maxImageCount) + { + image_count = surface_capabilities.maxImageCount; + } + + return image_count; + } + ``` + +### Presentation mode + +Még fontos kiemelni az [előadásdiákban](./Megjelenítés.pptx) is említett megjelenítési módokat (`Mailbox`, `Fifo`, ...). +Ahol nem annyira fontos az energiafogyasztás, ott erősen ajánlott a *Mailbox*. + +??? example "Hogyan ellenőrizzük, hogy tudunk-e *Mailbox*-ot használni?" + + ```cpp + [[nodiscard]] + static auto choose_swapchain_present_mode( + const vk::SurfaceKHR surface, + const vk::PhysicalDevice physical_device + ) -> vk::PresentModeKHR + { + const auto present_modes{ physical_device.getSurfacePresentModesKHR(surface) }; + return std::ranges::find(present_modes, vk::PresentModeKHR::eMailbox) + != std::cend(present_modes) + ? vk::PresentModeKHR::eMailbox + : vk::PresentModeKHR::eFifo; + } + ``` + +A jelenlegi megoldásból kivettem ezt az opciót az egyszerűség kedvéért. +*Fifo*-t fogunk használni, mert az se rossz, és alapértelmezetten mindenhol támogatott. + +### Létrehozás + +A választott képformátumot és méretet tároljuk el, mert még később szükségünk lesz rá. + +```cpp title="Swapchain.hpp" +class Swapchain { +public: + // ... + [[nodiscard]] + auto get() const noexcept -> vk::SwapchainKHR; + + [[nodiscard]] + auto extent() const noexcept -> vk::Extent2D; + [[nodiscard]] + auto format() const noexcept -> vk::Format; + +private: + vk::Extent2D m_extent; + vk::Format m_format; + vk::UniqueSwapchainKHR m_swapchain; + + explicit Swapchain( + const vk::Extent2D& extent, + vk::Format format, + vk::UniqueSwapchainKHR&& swapchain + ) noexcept; +}; +``` + +```cpp title="Swapchain.cpp" +auto Swapchain::get() const noexcept -> vk::SwapchainKHR +{ + return m_swapchain.get(); +} + +auto Swapchain::extent() const noexcept -> vk::Extent2D +{ + return m_extent; +} + +auto Swapchain::format() const noexcept -> vk::Format +{ + return m_format; +} + +Swapchain::Swapchain( + const vk::Extent2D& extent, + vk::Format format, + vk::UniqueSwapchainKHR&& swapchain +) noexcept + : m_extent{ extent }, + m_format{ format }, + m_swapchain{ std::move(swapchain) } +{} +``` + +Végül hozzuk létre a swapchain-t. + +```cpp title="Swapchain.hpp" +class Swapchain { +public: + // ... + [[nodiscard]] + static auto create( + vk::SurfaceKHR surface, + vk::PhysicalDevice physical_device, + uint32_t queue_family_index, + vk::Device device, + const vk::Extent2D& framebuffer_size + ) -> std::optional<Swapchain>; + // ... +``` + +0 szélességű/magasságú képet nem tudunk létrehozni, és nincs is értelme. +Ezért ebben az esetben visszaadunk egy `nullopt`-ot. +(Azaz az opcionális visszatérési értékben nem lesz semmi.) + +```cpp title="Swapchain.cpp" +auto Swapchain::create( + const vk::SurfaceKHR surface, + const vk::PhysicalDevice physical_device, + const uint32_t queue_family_index, + const vk::Device device, + const vk::Extent2D& framebuffer_size +) -> std::optional<Swapchain> +{ + const auto surface_capabilities{ physical_device.getSurfaceCapabilitiesKHR(surface) }; + + const auto extent = choose_extent(framebuffer_size, surface_capabilities); + if (extent.width == 0 || extent.height == 0) { + return std::nullopt; + } + + const auto surface_format{ choose_swapchain_surface_format(surface, physical_device) }; + + auto swapchain{ create_swapchain( + surface, + queue_family_index, + device, + surface_capabilities, + extent, + surface_format + ) }; + + return Swapchain{ extent, surface_format.format, std::move(swapchain) }; +} +``` + +```cpp title="Swapchain.cpp" +[[nodiscard]] +static auto create_swapchain( + const vk::SurfaceKHR surface, + const uint32_t queue_family_index, + const vk::Device device, + const vk::SurfaceCapabilitiesKHR& surface_capabilities, + const vk::Extent2D extent, + const vk::SurfaceFormatKHR surface_format +) -> vk::UniqueSwapchainKHR +{ + const std::array queue_family_indices{ queue_family_index }; + + const vk::SwapchainCreateInfoKHR create_info{ + .surface = surface, + .minImageCount = get_min_image_count(surface_capabilities), + .imageFormat = surface_format.format, + .imageColorSpace = surface_format.colorSpace, + .imageExtent = extent, + .imageArrayLayers = 1, + .imageUsage = vk::ImageUsageFlagBits::eColorAttachment, + .imageSharingMode = vk::SharingMode::eExclusive, + .queueFamilyIndexCount = static_cast<uint32_t>(queue_family_indices.size()), + .pQueueFamilyIndices = queue_family_indices.data(), + .preTransform = surface_capabilities.currentTransform, + .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque, + .presentMode = vk::PresentModeKHR::eFifo, + .clipped = vk::True + }; + + return device.createSwapchainKHRUnique(create_info); +} +``` + +??? note "Mi ez a sok paraméter amit megadtunk a swapchain-nek?" + + - `imageArrayLayers` - + Ez mindig egy, hacsak nem egy [sztereoszkopikus](https://en.wikipedia.org/wiki/Stereoscopy) alkalmazást fejlesztünk. + + - `imageUsage` - + Ezzel megadjuk, milyen módon fogjuk majd használni a képeket. + Mivel jelenleg csak rajzolni fogunk rájuk, ezért *color attachment*-ként fogjuk őket használni, de akár *transfer*-hez is használhatnánk például *post-processing*-hez. + + - `imageSharingMode` - + Több családból származó queue-k esetén beállíthatjuk, hogy egy kép melyik család tulajdona legyen. + Mi csak csak egy queue-t használunk, ezért exkluzívan ő uralja a képet. + Más esetben megadhatunk konkurrens tulajdon módot is. + + - `preTransform` + A képekhez hozzárendelhetünk valamilyen transformációt is. + Amennyiben ezzel nem szeretnénk élni, úgy a `surface_capabilities.currentTransform`-ot kell használni. + + - `compositeAlpha` + Ezzel megadhatjuk, hogy átfedésben lévő ablakok hogyan hassanak a képre (blending). + + - `clipped` + Amennyiben egy másik ablak miatt fedésbe kerülnek bizonyos pixelek, eldönthetjük, hogy megtartsuk-e őket későbbi műveletektre. + +```cpp title="Renderer.cpp" +Renderer::Renderer(const Window& window) + // ... + m_swapchain{ Swapchain::create( + m_surface.get(), + m_physical_device, + m_queue_family_index, + m_device.get(), + window.framebuffer_size() + ) } +{} +``` -- GitLab