From 7e936784abef19c9342358e836796b4080e00df4 Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Wed, 17 Dec 2025 12:49:30 +0100 Subject: [PATCH] refactor(cleanup): initial swapchain logic & temporary backend creation --- .../lsfg-vk-common/helpers/pointers.hpp | 13 ++ .../include/lsfg-vk-common/vulkan/vulkan.hpp | 24 ++- lsfg-vk-common/src/vulkan/vulkan.cpp | 42 ++-- lsfg-vk-layer/CMakeLists.txt | 1 + lsfg-vk-layer/src/context/instance.cpp | 176 ++++++++++++---- lsfg-vk-layer/src/context/instance.hpp | 60 +++++- lsfg-vk-layer/src/context/swapchain.cpp | 33 +++ lsfg-vk-layer/src/context/swapchain.hpp | 42 ++++ lsfg-vk-layer/src/entrypoint.cpp | 190 ++++++++++++------ 9 files changed, 457 insertions(+), 124 deletions(-) create mode 100644 lsfg-vk-layer/src/context/swapchain.cpp create mode 100644 lsfg-vk-layer/src/context/swapchain.hpp diff --git a/lsfg-vk-common/include/lsfg-vk-common/helpers/pointers.hpp b/lsfg-vk-common/include/lsfg-vk-common/helpers/pointers.hpp index 9c4d373..d62a456 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/helpers/pointers.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/helpers/pointers.hpp @@ -29,6 +29,10 @@ namespace ls { return *this->opt; } + /// check if value is present + /// @return true if value is present + [[nodiscard]] bool has_value() const { return this->opt.has_value(); } + /// get reference to value /// @return reference to value /// @throws std::logic_error if no value present @@ -46,6 +50,15 @@ namespace ls { throw std::logic_error("lazy: no value present"); return &(*this->opt); } + + /// get a mutable reference to value + /// @return mutable reference to value + /// @throws std::logic_error if no value present + T& mut() { + if (!this->opt.has_value()) + throw std::logic_error("lazy: no value present"); + return *this->opt; + } private: std::optional opt{}; }; diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp index 9863c37..23e8302 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp @@ -25,12 +25,18 @@ namespace vk { PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties; PFN_vkCreateDevice CreateDevice; PFN_vkGetDeviceProcAddr GetDeviceProcAddr; + + // extension functions + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR; }; /// initialize vulkan instance function pointers /// @param instance vulkan instance handle /// @param mpa function to get instance proc addresses - VulkanInstanceFuncs initVulkanInstanceFuncs(VkInstance instance, PFN_vkGetInstanceProcAddr mpa); + /// @param graphical whether the device is graphical (rather than compute) + /// @return initialized function pointers + VulkanInstanceFuncs initVulkanInstanceFuncs(VkInstance instance, PFN_vkGetInstanceProcAddr mpa, + bool graphical); using PhysicalDeviceSelector = const std::function< VkPhysicalDevice( @@ -98,12 +104,18 @@ namespace vk { PFN_vkGetMemoryFdKHR GetMemoryFdKHR; PFN_vkImportSemaphoreFdKHR ImportSemaphoreFdKHR; PFN_vkGetSemaphoreFdKHR GetSemaphoreFdKHR; + PFN_vkCreateSwapchainKHR CreateSwapchainKHR; + PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; + PFN_vkDestroySwapchainKHR DestroySwapchainKHR; }; /// initialize vulkan device function pointers /// @param fi instance function pointers /// @param device logical device handle - VulkanDeviceFuncs initVulkanDeviceFuncs(const VulkanInstanceFuncs& fi, VkDevice device); + /// @param graphical whether the device is graphical (rather than compute) + /// @return initialized function pointers + VulkanDeviceFuncs initVulkanDeviceFuncs(const VulkanInstanceFuncs& fi, VkDevice device, + bool graphical); /// vulkan version wrapper class version { @@ -167,6 +179,9 @@ namespace vk { /// get the vulkan device /// @return the device handle [[nodiscard]] const auto& dev() const { return this->device.get(); } + /// get the physical device + /// @return the physical device handle + [[nodiscard]] VkPhysicalDevice physdev() const { return this->phys_dev; } /// get the command pool /// @return the command pool handle [[nodiscard]] const auto& cmdpool() const { return this->cmdPool.get(); } @@ -181,6 +196,9 @@ namespace vk { /// @return true if fp16 is supported [[nodiscard]] bool supportsFP16() const { return this->fp16; } + /// get instance-level function pointers + /// @return the instance function pointers + [[nodiscard]] const auto& fi() const { return this->instance_funcs; } /// get device-level function pointers /// @return the device function pointers [[nodiscard]] const auto& df() const { return this->device_funcs; } @@ -191,7 +209,7 @@ namespace vk { ls::owned_ptr instance; VulkanInstanceFuncs instance_funcs; - VkPhysicalDevice physdev; + VkPhysicalDevice phys_dev; uint32_t queueFamilyIdx; bool fp16; diff --git a/lsfg-vk-common/src/vulkan/vulkan.cpp b/lsfg-vk-common/src/vulkan/vulkan.cpp index c0980af..7ad6816 100644 --- a/lsfg-vk-common/src/vulkan/vulkan.cpp +++ b/lsfg-vk-common/src/vulkan/vulkan.cpp @@ -173,7 +173,8 @@ namespace { const std::vector requestedExtensions{ VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, - VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME // TODO: possibly attempt to get rid of + VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, // TODO: possibly attempt to get rid of + VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME }; const VkDeviceCreateInfo deviceInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, @@ -268,7 +269,8 @@ namespace { } /// initialize vulkan instance function pointers -VulkanInstanceFuncs vk::initVulkanInstanceFuncs(VkInstance i, PFN_vkGetInstanceProcAddr mpa) { +VulkanInstanceFuncs vk::initVulkanInstanceFuncs(VkInstance i, PFN_vkGetInstanceProcAddr mpa, + bool graphical) { return { .DestroyInstance = ipa(mpa, i, "vkDestroyInstance"), .EnumeratePhysicalDevices = ipa(mpa, i, @@ -280,17 +282,22 @@ VulkanInstanceFuncs vk::initVulkanInstanceFuncs(VkInstance i, PFN_vkGetInstanceP .GetPhysicalDeviceQueueFamilyProperties = ipa(mpa, i, "vkGetPhysicalDeviceQueueFamilyProperties"), - .GetPhysicalDeviceFeatures2 = ipa(mpa, i, - "vkGetPhysicalDeviceFeatures2"), + .GetPhysicalDeviceFeatures2 = graphical ? + nullptr : ipa(mpa, i, "vkGetPhysicalDeviceFeatures2"), .GetPhysicalDeviceMemoryProperties = ipa(mpa, i, "vkGetPhysicalDeviceMemoryProperties"), .CreateDevice = ipa(mpa, i, "vkCreateDevice"), .GetDeviceProcAddr = ipa(mpa, i, "vkGetDeviceProcAddr"), + + .GetPhysicalDeviceSurfaceCapabilitiesKHR = graphical ? + ipa(mpa, i, + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR") : nullptr }; } /// initialize vulkan device function pointers -VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevice d) { +VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevice d, + bool graphical) { return { .GetDeviceQueue = dpa(f, d, "vkGetDeviceQueue"), .DeviceWaitIdle = dpa(f, d, "vkDeviceWaitIdle"), @@ -355,6 +362,13 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi .GetMemoryFdKHR = dpa(f, d, "vkGetMemoryFdKHR"), .ImportSemaphoreFdKHR = dpa(f, d, "vkImportSemaphoreFdKHR"), .GetSemaphoreFdKHR = dpa(f, d, "vkGetSemaphoreFdKHR"), + + .CreateSwapchainKHR = graphical ? + dpa(f, d, "vkCreateSwapchainKHR") : nullptr, + .GetSwapchainImagesKHR = graphical ? + dpa(f, d, "vkGetSwapchainImagesKHR") : nullptr, + .DestroySwapchainKHR = graphical ? + dpa(f, d, "vkDestroySwapchainKHR") : nullptr }; } @@ -367,23 +381,23 @@ Vulkan::Vulkan(const std::string& appName, version appVersion, appName, appVersion, engineName, engineVersion )), - instance_funcs(initVulkanInstanceFuncs(*this->instance, get_mpa())), - physdev(findPhysicalDevice(this->instance_funcs, + instance_funcs(initVulkanInstanceFuncs(*this->instance, get_mpa(), false)), + phys_dev(findPhysicalDevice(this->instance_funcs, *this->instance, selectPhysicalDevice )), - queueFamilyIdx(findQFI(this->instance_funcs, this->physdev, + queueFamilyIdx(findQFI(this->instance_funcs, this->phys_dev, isGraphical ? VK_QUEUE_GRAPHICS_BIT : VK_QUEUE_COMPUTE_BIT)), - fp16(checkFP16(this->instance_funcs, this->physdev)), + fp16(checkFP16(this->instance_funcs, this->phys_dev)), device(createLogicalDevice(this->instance_funcs, - this->physdev, + this->phys_dev, this->queueFamilyIdx, this->fp16 )), setLoaderData(setLoaderData), device_funcs(initVulkanDeviceFuncs( this->instance_funcs, - *this->device + *this->device, false )), computeQueue(getQueue(this->device_funcs, *this->device, this->setLoaderData, @@ -405,8 +419,8 @@ Vulkan::Vulkan(VkInstance instance, VkDevice device, std::optional setLoaderData) : instance(new VkInstance(instance)), instance_funcs(instanceFuncs), - physdev(physdev), - queueFamilyIdx(findQFI(this->instance_funcs, this->physdev, + phys_dev(physdev), + queueFamilyIdx(findQFI(this->instance_funcs, this->phys_dev, isGraphical ? VK_QUEUE_GRAPHICS_BIT : VK_QUEUE_COMPUTE_BIT)), fp16(false), device(new VkDevice(device)), @@ -431,7 +445,7 @@ std::optional Vulkan::findMemoryTypeIndex( VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VkPhysicalDeviceMemoryProperties props; - this->instance_funcs.GetPhysicalDeviceMemoryProperties(this->physdev, &props); + this->instance_funcs.GetPhysicalDeviceMemoryProperties(this->phys_dev, &props); std::array memTypes = std::to_array(props.memoryTypes); for (uint32_t i = 0; i < props.memoryTypeCount; ++i) diff --git a/lsfg-vk-layer/CMakeLists.txt b/lsfg-vk-layer/CMakeLists.txt index 9c1ac7a..630216b 100644 --- a/lsfg-vk-layer/CMakeLists.txt +++ b/lsfg-vk-layer/CMakeLists.txt @@ -2,6 +2,7 @@ set(LAYER_SOURCES "src/configuration/config.cpp" "src/configuration/detection.cpp" "src/context/instance.cpp" + "src/context/swapchain.cpp" "src/entrypoint.cpp") add_library(lsfg-vk-layer SHARED ${LAYER_SOURCES}) diff --git a/lsfg-vk-layer/src/context/instance.cpp b/lsfg-vk-layer/src/context/instance.cpp index 91bf59f..cdafd5c 100644 --- a/lsfg-vk-layer/src/context/instance.cpp +++ b/lsfg-vk-layer/src/context/instance.cpp @@ -1,20 +1,45 @@ #include "instance.hpp" #include "../configuration/config.hpp" #include "../configuration/detection.hpp" +#include "swapchain.hpp" #include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk-common/helpers/errors.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" +#include +#include #include #include +#include #include #include #include #include #include +#include + using namespace lsfgvk; using namespace lsfgvk::layer; namespace { + /// helper function to add required extensions + std::vector add_extensions(const char* const* existingExtensions, size_t count, + const std::vector& requiredExtensions) { + std::vector extensions(count); + std::copy_n(existingExtensions, count, extensions.data()); + + for (const auto& requiredExtension : requiredExtensions) { + auto it = std::ranges::find_if(extensions, + [requiredExtension](const char* extension) { + return std::string(extension) == std::string(requiredExtension); + }); + if (it == extensions.end()) + extensions.push_back(requiredExtension); + } + + return extensions; + } // find the shader dll std::filesystem::path findShaderDll() { const std::vector FRAGMENTS{{ @@ -82,47 +107,130 @@ Root::Root() { std::cerr << "(identified via process name)\n"; break; } - - // create backend - /*const auto& global = this->config.getGlobalConf(); - this->backend.emplace( - [gpu = profile->second.gpu]( - const std::string& deviceName, - std::pair ids, - const std::optional& pci - ) { - if (!gpu) - return true; - - return (deviceName == *gpu) - || (ids.first + ":" + ids.second == *gpu) - || (pci && *pci == *gpu); - }, - global.dll.value_or(findShaderDll()), - global.allow_fp16 - );*/ } -std::vector Root::instanceExtensions() const { +void Root::update() { + if (!this->config.isUpToDate()) + this->config.reload(); +} + +void Root::modifyInstanceCreateInfo(VkInstanceCreateInfo& createInfo, + const std::function& finish) const { if (!this->active_profile.has_value()) - return {}; + return; - return { - "VK_KHR_get_physical_device_properties2", - "VK_KHR_external_memory_capabilities", - "VK_KHR_external_semaphore_capabilities" - }; + auto extensions = add_extensions( + createInfo.ppEnabledExtensionNames, + createInfo.enabledExtensionCount, + { + "VK_KHR_get_physical_device_properties2", + "VK_KHR_external_memory_capabilities", + "VK_KHR_external_semaphore_capabilities" + } + ); + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + finish(); } -std::vector Root::deviceExtensions() const { +void Root::modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo, + const std::function& finish) const { if (!this->active_profile.has_value()) - return {}; + return; - return { - "VK_KHR_external_memory", - "VK_KHR_external_memory_fd", - "VK_KHR_external_semaphore", - "VK_KHR_external_semaphore_fd", - "VK_KHR_timeline_semaphore" + auto extensions = add_extensions( + createInfo.ppEnabledExtensionNames, + createInfo.enabledExtensionCount, + { + "VK_KHR_external_memory", + "VK_KHR_external_memory_fd", + "VK_KHR_external_semaphore", + "VK_KHR_external_semaphore_fd", + "VK_KHR_timeline_semaphore" + } + ); + createInfo.enabledExtensionCount = static_cast(extensions.size()); + createInfo.ppEnabledExtensionNames = extensions.data(); + + bool isFeatureEnabled = false; + auto* featureInfo = reinterpret_cast(const_cast(createInfo.pNext)); + while (featureInfo) { + if (featureInfo->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES) { + auto* features = reinterpret_cast(featureInfo); + features->timelineSemaphore = VK_TRUE; + isFeatureEnabled = true; + } else if (featureInfo->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES) { + auto* features = reinterpret_cast(featureInfo); + features->timelineSemaphore = VK_TRUE; + isFeatureEnabled = true; + } + + featureInfo = const_cast(featureInfo->pNext); + } + + VkPhysicalDeviceTimelineSemaphoreFeatures timelineFeatures{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + .pNext = const_cast(createInfo.pNext), + .timelineSemaphore = VK_TRUE }; + if (!isFeatureEnabled) + createInfo.pNext = &timelineFeatures; + + finish(); +} + +void Root::modifySwapchainCreateInfo(const vk::Vulkan& vk, VkSwapchainCreateInfoKHR& createInfo, + const std::function& finish) const { + if (!this->active_profile.has_value()) + return; + + VkSurfaceCapabilitiesKHR caps{}; + auto res = vk.fi().GetPhysicalDeviceSurfaceCapabilitiesKHR( + vk.physdev(), createInfo.surface, &caps); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR() failed"); + + context_ModifySwapchainCreateInfo(*this->active_profile, caps.maxImageCount, createInfo); + + finish(); +} + +void Root::createSwapchainContext(const vk::Vulkan& vk, + VkSwapchainKHR swapchain, const SwapchainInfo& info) { + if (!this->active_profile.has_value()) + throw lsfgvk::error("attempted to create swapchain context while layer is inactive"); + const auto& profile = *this->active_profile; + + if (!this->backend.has_value()) { // emplace backend late, due to loader bug + const auto& global = this->config.getGlobalConf(); + + setenv("DISABLE_LSFGVK", "1", 1); // NOLINT (c++-include) + + this->backend.emplace( + [gpu = profile.gpu]( + const std::string& deviceName, + std::pair ids, + const std::optional& pci + ) { + if (!gpu) + return true; + + return (deviceName == *gpu) + || (ids.first + ":" + ids.second == *gpu) + || (pci && *pci == *gpu); + }, + global.dll.value_or(findShaderDll()), + global.allow_fp16 + ); + + unsetenv("DISABLE_LSFGVK"); // NOLINT (c++-include) + } + + this->swapchains.emplace(swapchain, + Swapchain(vk, this->backend.mut(), profile, info)); +} + +void Root::removeSwapchainContext(VkSwapchainKHR swapchain) { + this->swapchains.erase(swapchain); } diff --git a/lsfg-vk-layer/src/context/instance.hpp b/lsfg-vk-layer/src/context/instance.hpp index 2a75f09..805fc28 100644 --- a/lsfg-vk-layer/src/context/instance.hpp +++ b/lsfg-vk-layer/src/context/instance.hpp @@ -3,9 +3,13 @@ #include "../configuration/config.hpp" #include "lsfg-vk-backend/lsfgvk.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" +#include "swapchain.hpp" #include -#include +#include + +#include namespace lsfgvk::layer { @@ -20,17 +24,59 @@ namespace lsfgvk::layer { /// @return true if active [[nodiscard]] bool active() const { return this->active_profile.has_value(); } - /// required instance extensions - /// @return list of extension names - [[nodiscard]] std::vector instanceExtensions() const; - /// required device extensions - /// @return list of extension names - [[nodiscard]] std::vector deviceExtensions() const; + /// ensure the layer is up-to-date + void update(); + + // /// required instance extensions + // /// @return list of extension names + // [[nodiscard]] std::vector instanceExtensions() const; + // /// required device extensions + // /// @return list of extension names + // [[nodiscard]] std::vector deviceExtensions() const; + + /// modify instance create info + /// @param createInfo original create info + /// @param finish function to call after modification + void modifyInstanceCreateInfo(VkInstanceCreateInfo& createInfo, + const std::function& finish) const; + /// modify device create info + /// @param createInfo original create info + /// @param finish function to call after modification + void modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo, + const std::function& finish) const; + + /// modify swapchain create info + /// @param vk vulkan instance + /// @param createInfo original create info + /// @param finish function to call after modification + void modifySwapchainCreateInfo(const vk::Vulkan& vk, VkSwapchainCreateInfoKHR& createInfo, + const std::function& finish) const; + /// create swapchain context + /// @param vk vulkan instance + /// @param swapchain swapchain handle + /// @param info swapchain info + void createSwapchainContext(const vk::Vulkan& vk, VkSwapchainKHR swapchain, + const SwapchainInfo& info); + /// get swapchain context + /// @param swapchain swapchain handle + /// @return swapchain context + /// @throws lsfgvk::error if not found + [[nodiscard]] Swapchain& getSwapchainContext(VkSwapchainKHR swapchain) { + const auto& it = this->swapchains.find(swapchain); + if (it == this->swapchains.end()) + throw lsfgvk::error("swapchain context not found"); + + return it->second; + } + /// remove swapchain context + /// @param swapchain swapchain handle + void removeSwapchainContext(VkSwapchainKHR swapchain); private: Configuration config; std::optional active_profile; ls::lazy backend; + std::unordered_map swapchains; }; } diff --git a/lsfg-vk-layer/src/context/swapchain.cpp b/lsfg-vk-layer/src/context/swapchain.cpp new file mode 100644 index 0000000..f2e6062 --- /dev/null +++ b/lsfg-vk-layer/src/context/swapchain.cpp @@ -0,0 +1,33 @@ +#include "swapchain.hpp" +#include "../configuration/config.hpp" +#include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" + +#include + +#include +#include + +using namespace lsfgvk; +using namespace lsfgvk::layer; + +void layer::context_ModifySwapchainCreateInfo(const GameConf& profile, uint32_t maxImages, + VkSwapchainCreateInfoKHR& createInfo) { + createInfo.imageUsage |= + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + + switch (profile.pacing) { + case lsfgvk::layer::Pacing::None: + createInfo.minImageCount += profile.multiplier; + if (maxImages && createInfo.minImageCount > maxImages) + createInfo.minImageCount = maxImages; + + createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; + break; + } +} + +Swapchain::Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend, + const GameConf& profile, const SwapchainInfo& info) { + std::cerr << "lsfg-vk: swapchain created :3\n"; +} diff --git a/lsfg-vk-layer/src/context/swapchain.hpp b/lsfg-vk-layer/src/context/swapchain.hpp new file mode 100644 index 0000000..1360a69 --- /dev/null +++ b/lsfg-vk-layer/src/context/swapchain.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "../configuration/config.hpp" +#include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk-common/vulkan/vulkan.hpp" + +#include + +#include + +namespace lsfgvk::layer { + + /// swapchain info struct + struct SwapchainInfo { + std::vector images; + VkFormat format; + VkColorSpaceKHR colorSpace; + VkExtent2D extent; + VkPresentModeKHR presentMode; + }; + + /// modify the swapchain create info based on the profile pre-swapchain creation + /// @param profile active game profile + /// @param maxImages maximum number of images supported by the surface + /// @param createInfo swapchain create info to modify + void context_ModifySwapchainCreateInfo(const GameConf& profile, uint32_t maxImages, + VkSwapchainCreateInfoKHR& createInfo); + + /// swapchain context for a layer instance + class Swapchain { + public: + /// create a new swapchain context + /// @param vk vulkan instance + /// @param profile active game profile + /// @param backend lsfg-vk backend instance + /// @param info swapchain info + Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend, + const GameConf& profile, const SwapchainInfo& info); + private: + }; + +} diff --git a/lsfg-vk-layer/src/entrypoint.cpp b/lsfg-vk-layer/src/entrypoint.cpp index 26d18ed..babb0ad 100644 --- a/lsfg-vk-layer/src/entrypoint.cpp +++ b/lsfg-vk-layer/src/entrypoint.cpp @@ -1,13 +1,13 @@ #include "context/instance.hpp" +#include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" -#include -#include #include #include #include #include #include +#include #include #include @@ -15,26 +15,6 @@ using namespace lsfgvk; -namespace { - /// helper function to add required extensions - std::vector add_extensions(const char* const* existingExtensions, size_t count, - const std::vector& requiredExtensions) { - std::vector extensions(count); - std::copy_n(existingExtensions, count, extensions.data()); - - for (const auto& requiredExtension : requiredExtensions) { - auto it = std::ranges::find_if(extensions, - [requiredExtension](const char* extension) { - return std::string(extension) == std::string(requiredExtension); - }); - if (it == extensions.end()) - extensions.push_back(requiredExtension); - } - - return extensions; - } -} - namespace { // global layer info initialized at layer negotiation struct LayerInfo { @@ -94,26 +74,25 @@ namespace { return VK_ERROR_INITIALIZATION_FAILED; } - auto extensions = add_extensions( - info->ppEnabledExtensionNames, - info->enabledExtensionCount, - layer_info->root.instanceExtensions()); - - VkInstanceCreateInfo newInfo = *info; - newInfo.enabledExtensionCount = static_cast(extensions.size()); - newInfo.ppEnabledExtensionNames = extensions.data(); - - auto res = vkCreateInstance(&newInfo, alloc, instance); - if (res == VK_ERROR_EXTENSION_NOT_PRESENT) - std::cerr << "lsfg-vk: required Vulkan instance extensions are not present. " - "Your GPU driver is not supported.\n"; - - if (res != VK_SUCCESS) - return res; + try { + VkInstanceCreateInfo newInfo = *info; + layer_info->root.modifyInstanceCreateInfo(newInfo, + [=, newInfo = &newInfo]() { + auto res = vkCreateInstance(newInfo, alloc, instance); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkCreateInstance() failed"); + } + ); + } catch (const ls::vulkan_error& e) { + if (e.error() == VK_ERROR_EXTENSION_NOT_PRESENT) + std::cerr << "lsfg-vk: required Vulkan instance extensions are not present. " + "Your GPU driver is not supported.\n"; + return e.error(); + } instance_info = new InstanceInfo{ .handle = *instance, - .funcs = vk::initVulkanInstanceFuncs(*instance, layer_info->GetInstanceProcAddr), + .funcs = vk::initVulkanInstanceFuncs(*instance, layer_info->GetInstanceProcAddr, true), .devices = {} }; return VK_SUCCESS; @@ -171,22 +150,21 @@ namespace { } // create device - auto extensions = add_extensions( - info->ppEnabledExtensionNames, - info->enabledExtensionCount, - layer_info->root.deviceExtensions()); - - VkDeviceCreateInfo newInfo = *info; - newInfo.enabledExtensionCount = static_cast(extensions.size()); - newInfo.ppEnabledExtensionNames = extensions.data(); - - auto res = instance_info->funcs.CreateDevice(physdev, &newInfo, alloc, device); - if (res == VK_ERROR_EXTENSION_NOT_PRESENT) - std::cerr << "lsfg-vk: required Vulkan device extensions are not present. " - "Your GPU driver is not supported.\n"; - - if (res != VK_SUCCESS) - return res; + try { + VkDeviceCreateInfo newInfo = *info; + layer_info->root.modifyDeviceCreateInfo(newInfo, + [=, newInfo = &newInfo]() { + auto res = instance_info->funcs.CreateDevice(physdev, newInfo, alloc, device); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkCreateDevice() failed"); + } + ); + } catch (const ls::vulkan_error& e) { + if (e.error() == VK_ERROR_EXTENSION_NOT_PRESENT) + std::cerr << "lsfg-vk: required Vulkan device extensions are not present. " + "Your GPU driver is not supported.\n"; + return e.error(); + } // create layer instance try { @@ -194,7 +172,8 @@ namespace { *device, vk::Vulkan( instance_info->handle, *device, physdev, - instance_info->funcs, vk::initVulkanDeviceFuncs(instance_info->funcs, *device), + instance_info->funcs, vk::initVulkanDeviceFuncs(instance_info->funcs, *device, + true), true, setLoaderData ) ); @@ -260,30 +239,107 @@ namespace { } // get instance-level function pointers - PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* pName) { - if (!pName) return nullptr; + PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* name) { + if (!name) return nullptr; - if (std::string(pName) == "vkCreateInstance") // pre-instance function + if (std::string(name) == "vkCreateInstance") // pre-instance function return reinterpret_cast(myvkCreateInstance); - auto func = getProcAddr(pName); + auto func = getProcAddr(name); if (func) return func; if (!layer_info->GetInstanceProcAddr) return nullptr; - return layer_info->GetInstanceProcAddr(instance, pName); + return layer_info->GetInstanceProcAddr(instance, name); } // get device-level function pointers - PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* pName) { - if (!pName) return nullptr; + PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* name) { + if (!name) return nullptr; - auto func = getProcAddr(pName); + auto func = getProcAddr(name); if (func) return func; if (!instance_info->funcs.GetDeviceProcAddr) return nullptr; - return instance_info->funcs.GetDeviceProcAddr(device, pName); + return instance_info->funcs.GetDeviceProcAddr(device, name); + } +} + +namespace { + VkResult myvkCreateSwapchainKHR( + VkDevice device, + const VkSwapchainCreateInfoKHR* info, + const VkAllocationCallbacks* alloc, + VkSwapchainKHR* swapchain) { + const auto& it = instance_info->devices.find(device); + if (it == instance_info->devices.end()) + return VK_ERROR_INITIALIZATION_FAILED; + + try { + // retire old swapchain + if (info->oldSwapchain) + layer_info->root.removeSwapchainContext(info->oldSwapchain); + + layer_info->root.update(); // ensure config is up to date + + // create swapchain + VkSwapchainCreateInfoKHR newInfo = *info; + layer_info->root.modifySwapchainCreateInfo(it->second, newInfo, + [=, newInfo = &newInfo]() { + auto res = it->second.df().CreateSwapchainKHR( + device, newInfo, alloc, swapchain); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkCreateSwapchainKHR() failed"); + } + ); + + // get all swapchain images + uint32_t imageCount{}; + auto res = it->second.df().GetSwapchainImagesKHR(device, *swapchain, + &imageCount, nullptr); + if (res != VK_SUCCESS || imageCount == 0) + throw ls::vulkan_error(res, "vkGetSwapchainImagesKHR() failed"); + + std::vector swapchainImages(imageCount); + res = it->second.df().GetSwapchainImagesKHR(device, *swapchain, + &imageCount, swapchainImages.data()); + if (res != VK_SUCCESS) + throw ls::vulkan_error(res, "vkGetSwapchainImagesKHR() failed"); + + // create lsfg-vk swapchain + layer_info->root.createSwapchainContext(it->second, *swapchain, { + .images = std::move(swapchainImages), + .format = newInfo.imageFormat, + .colorSpace = newInfo.imageColorSpace, + .extent = newInfo.imageExtent, + .presentMode = newInfo.presentMode + }); + + return res; + } catch (const ls::vulkan_error& e) { + std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain creation:\n"; + std::cerr << "- " << e.what() << '\n'; + return e.error(); + } catch (const std::exception& e) { + std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain creation:\n"; + std::cerr << "- " << e.what() << '\n'; + return VK_ERROR_INITIALIZATION_FAILED; + } } + void myvkDestroySwapchainKHR( + VkDevice device, + VkSwapchainKHR swapchain, + const VkAllocationCallbacks* alloc) { + const auto& it = instance_info->devices.find(device); + if (it == instance_info->devices.end()) + return; + + // destroy lsfg-vk swapchain + layer_info->root.removeSwapchainContext(swapchain); + + // destroy swapchain + it->second.df().DestroySwapchainKHR(device, swapchain, alloc); + } } /// Vulkan layer entrypoint @@ -303,7 +359,9 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers { "vkCreateInstance", VKPTR(myvkCreateInstance) }, { "vkCreateDevice", VKPTR(myvkCreateDevice) }, { "vkDestroyDevice", VKPTR(myvkDestroyDevice) }, - { "vkDestroyInstance", VKPTR(myvkDestroyInstance) } + { "vkDestroyInstance", VKPTR(myvkDestroyInstance) }, + { "vkCreateSwapchainKHR", VKPTR(myvkCreateSwapchainKHR) }, + { "vkDestroySwapchainKHR", VKPTR(myvkDestroySwapchainKHR) } #undef VKPTR }, .root = layer::Root()