#include #include #include #include #include "device_manager.hpp" #include "../structs/swap_chain_support_details.hpp" struct PhysicalDeviceInfo { vk::PhysicalDevice physical_device; vk::SampleCountFlagBits msaa_samples; f32 max_line_width; bool wide_line_support; }; struct LogicalDeviceInfo { vk::UniqueDevice device; vk::Queue graphics_queue; vk::Queue present_queue; }; fn DeviceManager::pickPhysicalDevice( const vk::Instance& instance, const vk::SurfaceKHR& surface, std::span requiredExtensions ) -> std::expected { // Get all available physical devices auto devices = instance.enumeratePhysicalDevices(); if (devices.empty()) return std::unexpected("Failed to find GPUs with Vulkan support!"); #ifndef NDEBUG fmt::println("Available devices:"); #endif // Find the first suitable device using ranges auto suitableDevice = std::ranges::find_if(devices, [&](const auto& device) { return isDeviceSuitable(device, surface, requiredExtensions); }); if (suitableDevice == devices.end()) return std::unexpected("Failed to find a suitable GPU!"); vk::PhysicalDeviceProperties deviceProperties = suitableDevice->getProperties(); #ifndef NDEBUG fmt::println("\t{}", deviceProperties.deviceName.data()); fmt::println("Maximum supported line width: {}", deviceProperties.limits.lineWidthRange[1]); fmt::println("Wide lines supported: {}", deviceProperties.limits.lineWidthRange[1] > 1.0F ? "yes" : "no"); #endif return PhysicalDeviceInfo { .physical_device = *suitableDevice, .msaa_samples = getMaxUsableSampleCount(*suitableDevice), .max_line_width = deviceProperties.limits.lineWidthRange[1], .wide_line_support = deviceProperties.limits.lineWidthRange[1] > 1.0F, }; } fn DeviceManager::createLogicalDevice( const vk::PhysicalDevice& physicalDevice, const vk::SurfaceKHR& surface, std::span requiredExtensions, bool enableValidationLayers, std::span validationLayers ) -> std::expected { try { QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface); std::set uniqueQueueFamilies = { indices.graphics_family.value(), indices.present_family.value() }; std::vector queueCreateInfos; f32 queuePriority = 1.0F; // Use ranges to transform unique queue families into create infos std::ranges::transform(uniqueQueueFamilies, std::back_inserter(queueCreateInfos), [&](u32 queueFamily) { return vk::DeviceQueueCreateInfo { .queueFamilyIndex = queueFamily, .queueCount = 1, .pQueuePriorities = &queuePriority, }; }); vk::PhysicalDeviceFeatures deviceFeatures { .fillModeNonSolid = vk::True, .wideLines = vk::True, .samplerAnisotropy = vk::True, }; vk::DeviceCreateInfo createInfo { .queueCreateInfoCount = static_cast(queueCreateInfos.size()), .pQueueCreateInfos = queueCreateInfos.data(), .enabledExtensionCount = static_cast(requiredExtensions.size()), .ppEnabledExtensionNames = requiredExtensions.data(), .pEnabledFeatures = &deviceFeatures, }; if (enableValidationLayers) { createInfo.enabledLayerCount = static_cast(validationLayers.size()); createInfo.ppEnabledLayerNames = validationLayers.data(); } auto device = physicalDevice.createDeviceUnique(createInfo); auto graphicsQueue = device->getQueue(indices.graphics_family.value(), 0); auto presentQueue = device->getQueue(indices.present_family.value(), 0); return LogicalDeviceInfo { .device = std::move(device), .graphics_queue = graphicsQueue, .present_queue = presentQueue, }; } catch (const vk::SystemError& e) { return std::unexpected(fmt::format("Failed to create logical device: {}", e.what())); } } fn DeviceManager::getMaxUsableSampleCount(const vk::PhysicalDevice& physicalDevice ) -> vk::SampleCountFlagBits { vk::PhysicalDeviceProperties physicalDeviceProperties = physicalDevice.getProperties(); vk::SampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts; if (counts & vk::SampleCountFlagBits::e64) return vk::SampleCountFlagBits::e64; if (counts & vk::SampleCountFlagBits::e32) return vk::SampleCountFlagBits::e32; if (counts & vk::SampleCountFlagBits::e16) return vk::SampleCountFlagBits::e16; if (counts & vk::SampleCountFlagBits::e8) return vk::SampleCountFlagBits::e8; if (counts & vk::SampleCountFlagBits::e4) return vk::SampleCountFlagBits::e4; if (counts & vk::SampleCountFlagBits::e2) return vk::SampleCountFlagBits::e2; return vk::SampleCountFlagBits::e1; } fn DeviceManager::findQueueFamilies(const vk::PhysicalDevice& device, const vk::SurfaceKHR& surface) -> QueueFamilyIndices { QueueFamilyIndices indices; // Get queue family properties std::vector queueFamilies = device.getQueueFamilyProperties(); // Find queue family with graphics support for (size_t i = 0; i < queueFamilies.size(); i++) { if (queueFamilies[i].queueFlags & vk::QueueFlagBits::eGraphics) indices.graphics_family = static_cast(i); // Check for presentation support if (device.getSurfaceSupportKHR(static_cast(i), surface)) indices.present_family = static_cast(i); if (indices.isComplete()) break; } return indices; } fn DeviceManager::isDeviceSuitable( const vk::PhysicalDevice& device, const vk::SurfaceKHR& surface, std::span requiredExtensions ) -> bool { // Check queue family support QueueFamilyIndices indices = findQueueFamilies(device, surface); // Check extension support bool extensionsSupported = checkDeviceExtensionSupport(device, requiredExtensions); // Check swap chain support bool swapChainAdequate = false; if (extensionsSupported) { SwapChainSupportDetails swapChainSupport; swapChainSupport.capabilities = device.getSurfaceCapabilitiesKHR(surface); swapChainSupport.formats = device.getSurfaceFormatsKHR(surface); swapChainSupport.present_modes = device.getSurfacePresentModesKHR(surface); swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.present_modes.empty(); } // Check device features vk::PhysicalDeviceFeatures supportedFeatures = device.getFeatures(); return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy; } fn DeviceManager::checkDeviceExtensionSupport( const vk::PhysicalDevice& device, std::span requiredExtensions ) -> bool { std::vector availableExtensions = device.enumerateDeviceExtensionProperties(); // Use ranges to check if all required extensions are available return std::ranges::all_of(requiredExtensions, [&](const char* required) { return std::ranges::any_of(availableExtensions, [&](const vk::ExtensionProperties& available) { return strcmp(required, available.extensionName) == 0; }); }); }