From a7fc65008f7a856a5a73ffe326195a97f1297f66 Mon Sep 17 00:00:00 2001 From: pupbrained Date: Sat, 28 Sep 2024 21:55:26 -0400 Subject: [PATCH] swap chains --- flake.nix | 2 +- src/main.cpp | 207 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 184 insertions(+), 25 deletions(-) diff --git a/flake.nix b/flake.nix index e6e9d1f..29aa810 100644 --- a/flake.nix +++ b/flake.nix @@ -28,7 +28,7 @@ }; mkPkg = name: - pkgs.pkgsStatic.${name}.overrideAttrs { + pkgs.${name}.overrideAttrs { inherit (sources.${name}) pname version src; }; diff --git a/src/main.cpp b/src/main.cpp index 3887491..740977b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,20 +1,25 @@ #define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include +#include #include #include +#include #include +#include #include #include #include #include +#include #include "util/magic_enum.hpp" #include "util/types.h" -const uint32_t WIDTH = 800; -const uint32_t HEIGHT = 600; +const u32 WIDTH = 800; +const u32 HEIGHT = 600; +const std::array deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; const std::array validationLayers = { "VK_LAYER_KHRONOS_validation" }; #ifdef NDEBUG @@ -53,12 +58,18 @@ fn DestroyDebugUtilsMessengerEXT( } struct QueueFamilyIndices { - std::optional graphics_family; - std::optional present_family; + std::optional graphics_family; + std::optional present_family; fn isComplete() -> bool { return graphics_family.has_value() && present_family.has_value(); } }; +struct SwapChainSupportDetails { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector present_modes; +}; + class Application { public: fn run() -> void { @@ -81,13 +92,20 @@ class Application { VkQueue mGraphicsQueue; VkQueue mPresentQueue; + VkSwapchainKHR mSwapChain; + std::vector mSwapChainImages; + VkFormat mSwapChainImageFormat; + VkExtent2D mSwapChainExtent; + fn initWindow() -> void { - glfwInit(); + if (glfwInit() == GLFW_FALSE) + throw std::runtime_error("Failed to initialize GLFW!"); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); + if (mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); mWindow == nullptr) + throw std::runtime_error("Failed to create GLFW window!"); } fn initVulkan() -> void { @@ -96,6 +114,7 @@ class Application { createSurface(); pickPhysicalDevice(); createLogicalDevice(); + createSwapChain(); } fn mainLoop() -> void { @@ -103,6 +122,7 @@ class Application { } fn cleanup() -> void { + vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); vkDestroyDevice(mDevice, nullptr); if (enableValidationLayers) @@ -136,7 +156,7 @@ class Application { VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo {}; if (enableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); populateDebugMessengerCreateInfo(debugCreateInfo); @@ -192,7 +212,7 @@ class Application { } fn pickPhysicalDevice() -> void { - uint32_t deviceCount = 0; + u32 deviceCount = 0; vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); if (deviceCount == 0) @@ -215,11 +235,10 @@ class Application { QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); std::vector queueCreateInfos; - std::set uniqueQueueFamilies = { indices.graphics_family.value(), - indices.present_family.value() }; + std::set uniqueQueueFamilies = { indices.graphics_family.value(), indices.present_family.value() }; - float queuePriority = 1.0F; - for (uint32_t queueFamily : uniqueQueueFamilies) { + f32 queuePriority = 1.0F; + for (u32 queueFamily : uniqueQueueFamilies) { VkDeviceQueueCreateInfo queueCreateInfo {}; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueCreateInfo.queueFamilyIndex = queueFamily; @@ -233,15 +252,16 @@ class Application { VkDeviceCreateInfo createInfo {}; createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); + createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); createInfo.pQueueCreateInfos = queueCreateInfos.data(); createInfo.pEnabledFeatures = &deviceFeatures; - createInfo.enabledExtensionCount = 0; + createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); + createInfo.ppEnabledExtensionNames = deviceExtensions.data(); if (enableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); + createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } else { createInfo.enabledLayerCount = 0; @@ -255,16 +275,157 @@ class Application { vkGetDeviceQueue(mDevice, indices.present_family.value(), 0, &mPresentQueue); } + fn createSwapChain() -> void { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(mPhysicalDevice); + + VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats); + VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.present_modes); + VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities); + + u32 imageCount = swapChainSupport.capabilities.minImageCount + 1; + if (swapChainSupport.capabilities.maxImageCount > 0 && + imageCount > swapChainSupport.capabilities.maxImageCount) + imageCount = swapChainSupport.capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR createInfo {}; + createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + createInfo.surface = mSurface; + createInfo.minImageCount = imageCount; + createInfo.imageFormat = surfaceFormat.format; + createInfo.imageColorSpace = surfaceFormat.colorSpace; + createInfo.imageExtent = extent; + createInfo.imageArrayLayers = 1; + createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); + std::array queueFamilyIndices = { indices.graphics_family.value(), + indices.present_family.value() }; + + if (indices.graphics_family != indices.present_family) { + createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + createInfo.queueFamilyIndexCount = 2; + createInfo.pQueueFamilyIndices = queueFamilyIndices.data(); + } else { + createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; // Optional + createInfo.pQueueFamilyIndices = nullptr; // Optional + } + + createInfo.preTransform = swapChainSupport.capabilities.currentTransform; + createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + createInfo.presentMode = presentMode; + createInfo.clipped = VK_TRUE; + createInfo.oldSwapchain = VK_NULL_HANDLE; + + if (vkCreateSwapchainKHR(mDevice, &createInfo, nullptr, &mSwapChain) != VK_SUCCESS) + throw std::runtime_error("Failed to create swap chain!"); + + vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, nullptr); + mSwapChainImages.resize(imageCount); + vkGetSwapchainImagesKHR(mDevice, mSwapChain, &imageCount, mSwapChainImages.data()); + + mSwapChainImageFormat = surfaceFormat.format; + mSwapChainExtent = extent; + } + + static fn chooseSwapSurfaceFormat(const std::vector& availableFormats + ) -> VkSurfaceFormatKHR { + for (const auto& availableFormat : availableFormats) { + if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && + availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) + return availableFormat; + } + + return availableFormats[0]; + } + + static fn chooseSwapPresentMode(const std::vector& availablePresentModes + ) -> VkPresentModeKHR { + for (const auto& availablePresentMode : availablePresentModes) + if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) + return availablePresentMode; + + return VK_PRESENT_MODE_FIFO_KHR; + } + + fn chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) -> VkExtent2D { + if (capabilities.currentExtent.width != std::numeric_limits::max()) { + return capabilities.currentExtent; + } + + int width = 0, height = 0; + glfwGetFramebufferSize(mWindow, &width, &height); + + VkExtent2D actualExtent = { static_cast(width), static_cast(height) }; + + actualExtent.width = + std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actualExtent.height = + std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actualExtent; + } + + fn querySwapChainSupport(VkPhysicalDevice device) -> SwapChainSupportDetails { + SwapChainSupportDetails details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, mSurface, &details.capabilities); + + u32 formatCount = 0; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, nullptr); + + if (formatCount != 0) { + details.formats.resize(formatCount); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, mSurface, &formatCount, details.formats.data()); + } + + u32 presentModeCount = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, mSurface, &presentModeCount, nullptr); + + if (presentModeCount != 0) { + details.present_modes.resize(presentModeCount); + vkGetPhysicalDeviceSurfacePresentModesKHR( + device, mSurface, &presentModeCount, details.present_modes.data() + ); + } + + return details; + } + fn isDeviceSuitable(VkPhysicalDevice device) -> bool { QueueFamilyIndices indices = findQueueFamilies(device); - return indices.isComplete(); + const bool extensionsSupported = checkDeviceExtensionSupport(device); + + bool swapChainAdequate = false; + + if (extensionsSupported) { + SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device); + swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.present_modes.empty(); + } + + return indices.isComplete() && extensionsSupported && swapChainAdequate; + } + + static fn checkDeviceExtensionSupport(VkPhysicalDevice device) -> bool { + u32 extensionCount = 0; + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); + + std::vector availableExtensions(extensionCount); + vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data()); + + std::set requiredExtensions(deviceExtensions.begin(), deviceExtensions.end()); + + for (const VkExtensionProperties& extension : availableExtensions) + requiredExtensions.erase(static_cast(extension.extensionName)); + + return requiredExtensions.empty(); } fn findQueueFamilies(VkPhysicalDevice device) -> QueueFamilyIndices { QueueFamilyIndices indices; - uint32_t queueFamilyCount = 0; + u32 queueFamilyCount = 0; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); std::vector queueFamilies(queueFamilyCount); @@ -292,7 +453,7 @@ class Application { } static fn getRequiredExtensions() -> std::vector { - uint32_t glfwExtensionCount = 0; + u32 glfwExtensionCount = 0; const char** glfwExtensions = nullptr; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); @@ -309,7 +470,7 @@ class Application { } static fn checkValidationLayerSupport() -> bool { - uint32_t layerCount = 0; + u32 layerCount = 0; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); std::vector availableLayers(layerCount); @@ -319,9 +480,7 @@ class Application { bool layerFound = false; for (const auto& layerProperties : availableLayers) { - if (strcmp( - static_cast(layerName), static_cast(layerProperties.layerName) - ) == 0) { + if (strcmp(layerName, static_cast(layerProperties.layerName)) == 0) { layerFound = true; break; } @@ -340,13 +499,13 @@ class Application { const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* /*pUserData*/ ) -> VkBool32 { - std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl; + fmt::println("Validation Layer: {}", pCallbackData->pMessage); return VK_FALSE; } }; -auto main() -> int { +fn main() -> i32 { Application app; try {