diff --git a/src/main.cpp b/src/main.cpp index a808475..684ce2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,80 +1,30 @@ -#define VULKAN_HPP_NO_CONSTRUCTORS -#include - #define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include -#include #include -#include -#include #include -#include -#include -#include -#include #include -#include "util/magic_enum.hpp" +#define VULKAN_HPP_NO_CONSTRUCTORS +#include + +#include "fmt/base.h" #include "util/types.h" -const u32 WIDTH = 800; -const u32 HEIGHT = 600; +constexpr int WIDTH = 800; +constexpr int HEIGHT = 600; -const std::array deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; -const std::array validationLayers = { "VK_LAYER_KHRONOS_validation" }; +constexpr std::array validationLayers = { "VK_LAYER_KHRONOS_validation" }; #ifdef NDEBUG -const bool enableValidationLayers = false; +constexpr bool enableValidationLayers = false; #else -const bool enableValidationLayers = true; +constexpr bool enableValidationLayers = true; #endif -fn CreateDebugUtilsMessengerEXT( - VkInstance instance, - const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugUtilsMessengerEXT* pDebugMessenger -) -> VkResult { - auto func = std::bit_cast( - vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT") - ); - - if (func != nullptr) - return func(instance, pCreateInfo, pAllocator, pDebugMessenger); - - return VK_ERROR_EXTENSION_NOT_PRESENT; -} - -fn DestroyDebugUtilsMessengerEXT( - VkInstance instance, - VkDebugUtilsMessengerEXT debugMessenger, - const VkAllocationCallbacks* pAllocator -) -> void { - auto func = std::bit_cast( - vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT") - ); - - if (func != nullptr) - func(instance, debugMessenger, pAllocator); -} - -struct QueueFamilyIndices { - 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 { +class HelloTriangleApplication { public: - fn run() -> void { + void run() { initWindow(); initVulkan(); mainLoop(); @@ -84,58 +34,52 @@ class Application { private: GLFWwindow* mWindow; - VkInstance mInstance; - VkDebugUtilsMessengerEXT mDebugMessenger; - VkSurfaceKHR mSurface; + vk::UniqueInstance mInstance; - VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE; - VkDevice mDevice; + vk::UniqueHandle mDebugMessenger; + vk::DispatchLoaderDynamic mLoader; - VkQueue mGraphicsQueue; - VkQueue mPresentQueue; - - VkSwapchainKHR mSwapChain; - std::vector mSwapChainImages; - VkFormat mSwapChainImageFormat; - VkExtent2D mSwapChainExtent; - std::vector mSwapChainImageViews; - - fn initWindow() -> void { - if (glfwInit() == GLFW_FALSE) - throw std::runtime_error("Failed to initialize GLFW!"); + void initWindow() { + glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); - if (mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); mWindow == nullptr) - throw std::runtime_error("Failed to create GLFW window!"); + mWindow = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr); } - fn initVulkan() -> void { + void initVulkan() { createInstance(); setupDebugMessenger(); - createSurface(); - pickPhysicalDevice(); - createLogicalDevice(); - createSwapChain(); - createImageViews(); } - fn mainLoop() -> void { + void setupDebugMessenger() { + if (!enableValidationLayers) + return; + + auto messengerCreateInfo = vk::DebugUtilsMessengerCreateInfoEXT() + .setMessageSeverity( + vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | + vk::DebugUtilsMessageSeverityFlagBitsEXT::eError + ) + .setMessageType( + vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | + vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation + ) + .setPfnUserCallback(debugCallback) + .setPUserData(nullptr); // Optional + + mDebugMessenger = mInstance->createDebugUtilsMessengerEXTUnique(messengerCreateInfo, nullptr, mLoader); + } + + void mainLoop() { while (!glfwWindowShouldClose(mWindow)) { glfwPollEvents(); } } - fn cleanup() -> void { - for (VkImageView_T* imageView : mSwapChainImageViews) { vkDestroyImageView(mDevice, imageView, nullptr); } - - vkDestroySwapchainKHR(mDevice, mSwapChain, nullptr); - vkDestroyDevice(mDevice, nullptr); - - if (enableValidationLayers) - DestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, nullptr); - - vkDestroySurfaceKHR(mInstance, mSurface, nullptr); - vkDestroyInstance(mInstance, nullptr); + void cleanup() { + // NOTE: instance destruction is handled by UniqueInstance glfwDestroyWindow(mWindow); @@ -150,7 +94,7 @@ class Application { .applicationVersion = 1, .pEngineName = "No Engine", .engineVersion = 1, - .apiVersion = VK_API_VERSION_1_1 }; + .apiVersion = VK_API_VERSION_1_0 }; vk::InstanceCreateInfo createInfo { .pApplicationInfo = &appInfo }; @@ -162,7 +106,6 @@ class Application { createInfo.setEnabledLayerCount(static_cast(validationLayers.size())) .setPpEnabledLayerNames(validationLayers.data()); - populateDebugMessengerCreateInfo(debugCreateInfo); createInfo.setPNext(&debugCreateInfo); } else { createInfo.setEnabledLayerCount(0).setPNext(nullptr); @@ -174,312 +117,35 @@ class Application { .setPpEnabledExtensionNames(extensions.data()) .setFlags(vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR); - for (const char* extension : extensions) std::cout << extension << std::endl; + fmt::println("Available extensions:"); + + for (const char* extension : extensions) fmt::println("\t{}", extension); try { - mInstance = vk::createInstance(createInfo); + mInstance = vk::createInstanceUnique(createInfo); + mLoader = vk::DispatchLoaderDynamic(mInstance.get(), vkGetInstanceProcAddr); } catch (const vk::SystemError& err) { throw std::runtime_error("Failed to create instance: " + std::string(err.what())); } } - static fn populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) -> void { - createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - createInfo.pfnUserCallback = debugCallback; - } + static fn checkValidationLayerSupport() -> bool { + std::vector availableLayers = vk::enumerateInstanceLayerProperties(); - fn setupDebugMessenger() -> void { - if (!enableValidationLayers) - return; + for (const char* layerName : validationLayers) { + bool layerFound = false; - VkDebugUtilsMessengerCreateInfoEXT createInfo; - populateDebugMessengerCreateInfo(createInfo); + for (const auto& layerProperties : availableLayers) + if (strcmp(layerName, layerProperties.layerName) == 0) { + layerFound = true; + break; + } - if (CreateDebugUtilsMessengerEXT(mInstance, &createInfo, nullptr, &mDebugMessenger) != VK_SUCCESS) - throw std::runtime_error("failed to set up debug messenger!"); - } - - fn createSurface() -> void { - if (VkResult result = glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface); - result != VK_SUCCESS) - throw std::runtime_error( - "Failed to create window surface! Error: " + string(magic_enum::enum_name(result)) - ); - } - - fn pickPhysicalDevice() -> void { - u32 deviceCount = 0; - vkEnumeratePhysicalDevices(mInstance, &deviceCount, nullptr); - - if (deviceCount == 0) - throw std::runtime_error("failed to find GPUs with Vulkan support!"); - - std::vector devices(deviceCount); - vkEnumeratePhysicalDevices(mInstance, &deviceCount, devices.data()); - - for (const auto& device : devices) - if (isDeviceSuitable(device)) { - mPhysicalDevice = device; - break; - } - - if (mPhysicalDevice == VK_NULL_HANDLE) - throw std::runtime_error("failed to find a suitable GPU!"); - } - - fn createLogicalDevice() -> void { - QueueFamilyIndices indices = findQueueFamilies(mPhysicalDevice); - - std::vector queueCreateInfos; - std::set uniqueQueueFamilies = { indices.graphics_family.value(), indices.present_family.value() }; - - f32 queuePriority = 1.0F; - for (u32 queueFamily : uniqueQueueFamilies) { - VkDeviceQueueCreateInfo queueCreateInfo {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = queueFamily; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &queuePriority; - queueCreateInfos.push_back(queueCreateInfo); + if (!layerFound) + return false; } - VkPhysicalDeviceFeatures deviceFeatures {}; - - VkDeviceCreateInfo createInfo {}; - createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - - createInfo.queueCreateInfoCount = static_cast(queueCreateInfos.size()); - createInfo.pQueueCreateInfos = queueCreateInfos.data(); - - createInfo.pEnabledFeatures = &deviceFeatures; - - createInfo.enabledExtensionCount = static_cast(deviceExtensions.size()); - createInfo.ppEnabledExtensionNames = deviceExtensions.data(); - - if (enableValidationLayers) { - createInfo.enabledLayerCount = static_cast(validationLayers.size()); - createInfo.ppEnabledLayerNames = validationLayers.data(); - } else { - createInfo.enabledLayerCount = 0; - } - - if (vkCreateDevice(mPhysicalDevice, &createInfo, nullptr, &mDevice) != VK_SUCCESS) { - throw std::runtime_error("failed to create logical device!"); - } - - vkGetDeviceQueue(mDevice, indices.graphics_family.value(), 0, &mGraphicsQueue); - 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; - } - - fn createImageViews() -> void { - mSwapChainImageViews.resize(mSwapChainImages.size()); - - for (usize i = 0; i < mSwapChainImages.size(); i++) { - VkImageViewCreateInfo createInfo {}; - createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - createInfo.image = mSwapChainImages[i]; - - createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - createInfo.format = mSwapChainImageFormat; - - createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; - createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; - - createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - createInfo.subresourceRange.baseMipLevel = 0; - createInfo.subresourceRange.levelCount = 1; - createInfo.subresourceRange.baseArrayLayer = 0; - createInfo.subresourceRange.layerCount = 1; - - if (vkCreateImageView(mDevice, &createInfo, nullptr, &mSwapChainImageViews[i]) != VK_SUCCESS) - throw std::runtime_error("Failed to create image views!"); - } - } - - 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); - - 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; - - u32 queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); - - u32 idx = 0; - - for (const auto& queueFamily : queueFamilies) { - if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) - indices.graphics_family = idx; - - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, idx, mSurface, &presentSupport); - - if (presentSupport) - indices.present_family = idx; - - if (indices.isComplete()) - break; - - idx++; - } - - return indices; + return true; } static fn getRequiredExtensions() -> std::vector { @@ -499,44 +165,20 @@ class Application { return extensions; } - static fn checkValidationLayerSupport() -> bool { - u32 layerCount = 0; - vkEnumerateInstanceLayerProperties(&layerCount, nullptr); - - std::vector availableLayers(layerCount); - vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()); - - for (const char* layerName : validationLayers) { - bool layerFound = false; - - for (const auto& layerProperties : availableLayers) { - if (strcmp(layerName, static_cast(layerProperties.layerName)) == 0) { - layerFound = true; - break; - } - } - - if (!layerFound) - return false; - } - - return true; - } - static VKAPI_ATTR fn VKAPI_CALL debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT /*messageSeverity*/, VkDebugUtilsMessageTypeFlagsEXT /*messageType*/, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* /*pUserData*/ ) -> VkBool32 { - fmt::println("Validation Layer: {}", pCallbackData->pMessage); + fmt::println("Validation layer: {}", pCallbackData->pMessage); return VK_FALSE; } }; fn main() -> i32 { - Application app; + HelloTriangleApplication app; try { app.run();