diff --git a/aster/CMakeLists.txt b/aster/CMakeLists.txt index 10b4f66..2ba28a1 100644 --- a/aster/CMakeLists.txt +++ b/aster/CMakeLists.txt @@ -18,7 +18,8 @@ set(HEADER_FILES context.h window.h physical_device.h - device.h) + device.h + swapchain.h) set(SOURCE_FILES logger.cpp @@ -26,7 +27,8 @@ set(SOURCE_FILES context.cpp window.cpp physical_device.cpp - device.cpp) + device.cpp + swapchain.cpp) add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES}) set_property(TARGET aster_core PROPERTY CXX_STANDARD 20) diff --git a/aster/swapchain.cpp b/aster/swapchain.cpp new file mode 100644 index 0000000..225ab56 --- /dev/null +++ b/aster/swapchain.cpp @@ -0,0 +1,155 @@ +/// ============================================= +// Aster: swapchain.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================== + +#include "swapchain.h" + +#include "device.h" +#include "physical_device.h" +#include "window.h" + +vk::SurfaceCapabilitiesKHR +getSurfaceCapabilities(vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface) +{ + vk::SurfaceCapabilitiesKHR surfaceCapabilities; + + vk::Result result = physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities); + ERROR_IF(failed(result), "Fetching surface capabilities failed. Cause: {}", result) + THEN_ABORT(result); + + return surfaceCapabilities; +} + +eastl::vector +getSurfaceFormats(vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface) +{ + u32 count = 0; + vk::Result result = physicalDevice.getSurfaceFormatsKHR(surface, &count, nullptr); + ERROR_IF(failed(result), "Fetching surface formats failed. Cause: {}", result) + THEN_ABORT(result); + + eastl::vector surfaceFormats(count); + result = physicalDevice.getSurfaceFormatsKHR(surface, &count, surfaceFormats.data()); + ERROR_IF(failed(result), "Fetching surface formats failed. Cause: {}", result) + THEN_ABORT(result); + + return surfaceFormats; +} + +eastl::vector +getSurfacePresentModes(vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface) +{ + u32 count = 0; + vk::Result result = physicalDevice.getSurfacePresentModesKHR(surface, &count, nullptr); + ERROR_IF(failed(result), "Fetching surface present modes failed. Cause: {}", result) + THEN_ABORT(result); + + eastl::vector surfacePresentModes(count); + result = physicalDevice.getSurfacePresentModesKHR(surface, &count, surfacePresentModes.data()); + ERROR_IF(failed(result), "Fetching surface present modes failed. Cause: {}", result) + THEN_ABORT(result); + + return surfacePresentModes; +} + +Swapchain::Swapchain(const Window *window, const Device *device, NameString &&name) + : m_Device(device) + , m_Name(std::move(name)) +{ + this->Create(window); +} + +Swapchain::~Swapchain() +{ + if (m_Swapchain) + { + m_Device->m_Device.destroy(m_Swapchain, nullptr); + m_Swapchain = nullptr; + DEBUG("Swapchain '{}' destroyed.", m_Name); + } +} + +void +Swapchain::Create(const Window *window) +{ + auto surfaceCapabilities = getSurfaceCapabilities(m_Device->m_PhysicalDevice, window->m_Surface); + auto surfaceFormats = getSurfaceFormats(m_Device->m_PhysicalDevice, window->m_Surface); + auto presentModes = getSurfacePresentModes(m_Device->m_PhysicalDevice, window->m_Surface); + + vk::Format swapchainFormat = vk::Format::eUndefined; + vk::ColorSpaceKHR swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear; + for (auto [format, colorSpace] : surfaceFormats) + { + if (format == vk::Format::eB8G8R8A8Srgb && colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) + { + swapchainFormat = format; + swapchainColorSpace = colorSpace; + } + } + if (swapchainFormat == vk::Format::eUndefined) + { + WARN("Preferred Swapchain format not found. Falling back."); + auto surfaceFormat = surfaceFormats.front(); + swapchainFormat = surfaceFormat.format; + swapchainColorSpace = surfaceFormat.colorSpace; + } + + vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo; + for (auto presentMode : presentModes) + { + if (presentMode == vk::PresentModeKHR::eMailbox) + { + swapchainPresentMode = presentMode; + break; + } + } + + vk::Extent2D swapchainExtent; + if (surfaceCapabilities.currentExtent.width != MaxValue) + { + swapchainExtent = surfaceCapabilities.currentExtent; + } + else + { + vk::Extent2D extent = window->GetSize(); + vk::Extent2D minExtent = surfaceCapabilities.minImageExtent; + vk::Extent2D maxExtent = surfaceCapabilities.maxImageExtent; + + swapchainExtent.width = glm::clamp(extent.width, minExtent.width, maxExtent.width); + swapchainExtent.height = glm::clamp(extent.height, minExtent.height, maxExtent.height); + } + + u32 swapchainImageCount = 3; + if (surfaceCapabilities.maxImageCount > 0) + { + swapchainImageCount = + glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount); + } + + // TODO: Note that different queues might need the images to be shared. + + const vk::SwapchainCreateInfoKHR swapchainCreateInfo = { + .surface = window->m_Surface, + .minImageCount = swapchainImageCount, + .imageFormat = swapchainFormat, + .imageColorSpace = swapchainColorSpace, + .imageExtent = swapchainExtent, + .imageArrayLayers = 1, + .imageUsage = vk::ImageUsageFlagBits::eColorAttachment, + .imageSharingMode = vk::SharingMode::eExclusive, + .preTransform = surfaceCapabilities.currentTransform, + .compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque, + .presentMode = swapchainPresentMode, + .clipped = true, + .oldSwapchain = m_Swapchain, + }; + + vk::SwapchainKHR swapchain; + vk::Result result = m_Device->m_Device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain); + ERROR_IF(failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result) + THEN_ABORT(result) + ELSE_DEBUG("Created Swapchain '{}'", m_Name); + + m_Swapchain = swapchain; +} \ No newline at end of file diff --git a/aster/swapchain.h b/aster/swapchain.h new file mode 100644 index 0000000..faf9c20 --- /dev/null +++ b/aster/swapchain.h @@ -0,0 +1,23 @@ +/// ============================================= +// Aster: swapchain.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================== + +#pragma once + +#include "global.h" + +struct PhysicalDevice; +struct Window; +struct Device; + +struct Swapchain final +{ + const Device *m_Device; + vk::SwapchainKHR m_Swapchain; + NameString m_Name; + + Swapchain(const Window *window, const Device *device, NameString &&name); + ~Swapchain(); + void Create(const Window *window); +}; diff --git a/aster/window.cpp b/aster/window.cpp index 3da5f1f..da41cc3 100644 --- a/aster/window.cpp +++ b/aster/window.cpp @@ -4,6 +4,7 @@ // ============================================= #include "window.h" + #include "context.h" #include "glfw_context.h" #include "logger.h" @@ -20,6 +21,15 @@ Window::SetWindowSize(const u32 width, const u32 height) const noexcept glfwSetWindowSize(m_Window, cast(width), cast(height)); } +vk::Extent2D +Window::GetSize() const +{ + int width; + int height; + glfwGetWindowSize(m_Window, &width, &height); + return {cast(width), cast(height)}; +} + Window::Window(cstr title, Context *context, vk::Extent2D extent, b8 isFullScreen) { m_Context = context; diff --git a/aster/window.h b/aster/window.h index 36a0594..34843ab 100644 --- a/aster/window.h +++ b/aster/window.h @@ -29,6 +29,7 @@ struct Window final void SetWindowSize(const vk::Extent2D &extent) const noexcept; void SetWindowSize(u32 width, u32 height) const noexcept; + [[nodiscard]] vk::Extent2D GetSize() const; // Ctor/Dtor Window(cstr title, Context *context, vk::Extent2D extent, b8 isFullScreen = false); diff --git a/triangle/aster.cpp b/triangle/aster.cpp index 10273b0..4513902 100644 --- a/triangle/aster.cpp +++ b/triangle/aster.cpp @@ -1,10 +1,12 @@ #include "aster/constants.h" +#include "aster/context.h" #include "aster/device.h" #include "aster/glfw_context.h" #include "aster/physical_device.h" #include "aster/window.h" #include "aster/global.h" +#include "aster/swapchain.h" constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent | @@ -62,7 +64,7 @@ findAppropriateQueueAllocation(const PhysicalDevice *physicalDevice) int main(int, char **) { - MIN_LOG_LEVEL(Logger::LogType::eInfo); + MIN_LOG_LEVEL(Logger::LogType::eDebug); GlfwContext glfwContext = {}; Context context = {"Aster", VERSION}; @@ -78,6 +80,8 @@ main(int, char **) Device device = {&context, &deviceToUse, &features, {queueAllocation}, "Primary Device"}; + Swapchain swapchain = {&window, &device, "Primary Chain"}; + while (window.Poll()) { }