diff --git a/src/main.cpp b/src/main.cpp index e08dde1..af8eef4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,10 @@ #include "fmt/base.h" #include "util/types.h" +namespace vk { + using DebugUtilsMessengerUnique = vk::UniqueHandle; +} + constexpr int WIDTH = 800; constexpr int HEIGHT = 600; @@ -36,8 +40,16 @@ class VulkanApp { vk::UniqueInstance mInstance; - vk::UniqueHandle mDebugMessenger; - vk::DispatchLoaderDynamic mLoader; + vk::DebugUtilsMessengerUnique mDebugMessenger; + vk::DispatchLoaderDynamic mLoader; + + vk::PhysicalDevice mPhysicalDevice; + + struct QueueFamilyIndices { + std::optional graphics_family; + + fn isComplete() -> bool { return graphics_family.has_value(); } + }; void initWindow() { glfwInit(); @@ -51,6 +63,55 @@ class VulkanApp { void initVulkan() { createInstance(); setupDebugMessenger(); + pickPhysicalDevice(); + } + + void mainLoop() { + while (!glfwWindowShouldClose(mWindow)) { glfwPollEvents(); } + } + + void cleanup() { + glfwDestroyWindow(mWindow); + glfwTerminate(); + } + + fn createInstance() -> void { + if (enableValidationLayers && !checkValidationLayerSupport()) + throw std::runtime_error("validation layers requested, but not available!"); + + vk::ApplicationInfo appInfo { .pApplicationName = "Hello Triangle", + .applicationVersion = 1, + .pEngineName = "No Engine", + .engineVersion = 1, + .apiVersion = VK_API_VERSION_1_0 }; + + // Retrieve extensions using custom function + std::vector extensions = getRequiredExtensions(); + + // Enable the portability extension and set flags + extensions.push_back("VK_KHR_portability_enumeration"); + + vk::InstanceCreateInfo createInfo { + .flags = vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR, + .pApplicationInfo = &appInfo, + .enabledLayerCount = enableValidationLayers ? static_cast(validationLayers.size()) : 0, + .ppEnabledLayerNames = enableValidationLayers ? validationLayers.data() : nullptr, + .enabledExtensionCount = static_cast(extensions.size()), + .ppEnabledExtensionNames = extensions.data() + }; + +#ifndef NDEBUG + fmt::println("Available extensions:"); + + for (const char* extension : extensions) fmt::println("\t{}", extension); +#endif + + try { + 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())); + } } void setupDebugMessenger() { @@ -70,53 +131,70 @@ class VulkanApp { mDebugMessenger = mInstance->createDebugUtilsMessengerEXTUnique(messengerCreateInfo, nullptr, mLoader); } - void mainLoop() { - while (!glfwWindowShouldClose(mWindow)) { glfwPollEvents(); } - } + fn pickPhysicalDevice() -> void { + std::vector devices = mInstance->enumeratePhysicalDevices(); - void cleanup() { - // NOTE: instance destruction is handled by UniqueInstance + if (devices.empty()) + throw std::runtime_error("Failed to find GPUs with Vulkan support!"); - glfwDestroyWindow(mWindow); +#ifndef NDEBUG + fmt::println("Available devices:"); +#endif - glfwTerminate(); - } + for (const auto& device : devices) { +#ifndef NDEBUG + vk::PhysicalDeviceProperties properties = device.getProperties(); + fmt::println("Device: {}", properties.deviceName.data()); +#endif - fn createInstance() -> void { - if (enableValidationLayers && !checkValidationLayerSupport()) - throw std::runtime_error("validation layers requested, but not available!"); - - vk::ApplicationInfo appInfo { .pApplicationName = "Hello Triangle", - .applicationVersion = 1, - .pEngineName = "No Engine", - .engineVersion = 1, - .apiVersion = VK_API_VERSION_1_0 }; - - // Retrieve extensions using custom function - std::vector extensions = getRequiredExtensions(); - - vk::InstanceCreateInfo createInfo { - .pApplicationInfo = &appInfo, - .enabledLayerCount = enableValidationLayers ? static_cast(validationLayers.size()) : 0, - .ppEnabledLayerNames = enableValidationLayers ? validationLayers.data() : nullptr - }; - - // Enable the portability extension and set flags - extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - createInfo.setEnabledExtensionCount(static_cast(extensions.size())) - .setPpEnabledExtensionNames(extensions.data()) - .setFlags(vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR); - - fmt::println("Available extensions:"); - - for (const char* extension : extensions) fmt::println("\t{}", extension); - - try { - 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())); + if (isDeviceSuitable(device)) { + mPhysicalDevice = device; + break; + } } + + if (!mPhysicalDevice) + throw std::runtime_error("Failed to find a suitable GPU!"); + } + + static fn isDeviceSuitable(vk::PhysicalDevice device) -> bool { + QueueFamilyIndices indices = findQueueFamilies(device); + + return indices.isComplete(); + } + + static fn findQueueFamilies(vk::PhysicalDevice device) -> QueueFamilyIndices { + QueueFamilyIndices indices; + + std::vector queueFamilies = device.getQueueFamilyProperties(); + + for (u32 i = 0; i < queueFamilies.size(); i++) { + if (queueFamilies[i].queueFlags & vk::QueueFlagBits::eGraphics) + indices.graphics_family = i; + + if (indices.isComplete()) + break; + } + + return indices; + } + + static fn getRequiredExtensions() -> std::vector { + u32 glfwExtensionCount = 0; + const char** glfwExtensions = nullptr; + glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); + + std::vector extensions; + + if (glfwExtensions) { + std::span extSpan(glfwExtensions, glfwExtensionCount); + extensions.assign(extSpan.begin(), extSpan.end()); + } + + if (enableValidationLayers) + extensions.push_back("VK_EXT_debug_utils"); + + return extensions; } static fn checkValidationLayerSupport() -> bool { @@ -138,24 +216,6 @@ class VulkanApp { return true; } - static fn getRequiredExtensions() -> std::vector { - u32 glfwExtensionCount = 0; - const char** glfwExtensions = nullptr; - glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); - - std::vector extensions; - - if (glfwExtensions) { - std::span extSpan(glfwExtensions, glfwExtensionCount); - extensions.assign(extSpan.begin(), extSpan.end()); - } - - if (enableValidationLayers) - extensions.push_back("VK_EXT_debug_utils"); - - return extensions; - } - static VKAPI_ATTR fn VKAPI_CALL debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT /*messageSeverity*/, VkDebugUtilsMessageTypeFlagsEXT /*messageType*/,