From 50c3895c620e271fb35d252e8a68f28160085101 Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Sat, 25 Apr 2026 23:32:59 +0200 Subject: [PATCH] feat(bindless): Implement updated backend in lsfg-vk-layer --- lsfg-vk-backend/src/utility/vkhelper.cpp | 4 + lsfg-vk-layer/.clang-tidy | 1 - lsfg-vk-layer/src/entrypoint.cpp | 112 +++++++-------- lsfg-vk-layer/src/instance.cpp | 27 ++-- lsfg-vk-layer/src/instance.hpp | 86 ++++++++---- lsfg-vk-layer/src/swapchain.cpp | 167 ++++++++++------------- lsfg-vk-layer/src/swapchain.hpp | 71 ++++++---- 7 files changed, 241 insertions(+), 227 deletions(-) diff --git a/lsfg-vk-backend/src/utility/vkhelper.cpp b/lsfg-vk-backend/src/utility/vkhelper.cpp index 44e63a0..0eecdce 100644 --- a/lsfg-vk-backend/src/utility/vkhelper.cpp +++ b/lsfg-vk-backend/src/utility/vkhelper.cpp @@ -67,6 +67,10 @@ vk::PhysicalDevice vkhelper::findPhysicalDevice( auto& props{info.properties}; + // Check first if id is not given + if (id.empty()) + return physdev; + // Compare device name props.deviceName.back() = '\0'; // Ensure null-termination if (id == std::string(props.deviceName)) diff --git a/lsfg-vk-layer/.clang-tidy b/lsfg-vk-layer/.clang-tidy index b2348d2..a3c1728 100644 --- a/lsfg-vk-layer/.clang-tidy +++ b/lsfg-vk-layer/.clang-tidy @@ -29,4 +29,3 @@ Checks: # Vulkan layers often require C-style memory access - -cppcoreguidelines-pro-bounds-pointer-arithmetic - -cppcoreguidelines-pro-type-union-access -- -clang-diagnostic-unsafe-buffer-usage diff --git a/lsfg-vk-layer/src/entrypoint.cpp b/lsfg-vk-layer/src/entrypoint.cpp index 51e08ff..d04dd8b 100644 --- a/lsfg-vk-layer/src/entrypoint.cpp +++ b/lsfg-vk-layer/src/entrypoint.cpp @@ -22,17 +22,17 @@ using namespace lsfgvk::layer; namespace { - // global layer info initialized at layer negotiation + /// Global layer info initialized at layer negotiation struct LayerInfo { - std::unordered_map map; //!< function pointer override map + std::unordered_map map; //!< Function pointer override map PFN_vkGetInstanceProcAddr GetInstanceProcAddr; Root root; }* layer_info; // NOLINT (global variable) - // instance-wide info initialized at instance creation(s) + /// Instance-wide info initialized at instance creation(s) struct InstanceInfo { - std::vector handles; // there may be several instances + std::vector handles; // There may be several instances vk::VulkanInstanceFuncs funcs; std::unordered_map devices; @@ -40,44 +40,44 @@ namespace { std::unordered_map swapchainInfos; }* instance_info; // NOLINT (global variable) - // create instance + /// Create instance VkResult myvkCreateInstance( const VkInstanceCreateInfo* info, const VkAllocationCallbacks* alloc, VkInstance* instance) { - // apply layer chaining + // Apply layer chaining auto* layerInfo = reinterpret_cast(const_cast(info->pNext)); while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || layerInfo->function != VK_LAYER_LINK_INFO)) { layerInfo = reinterpret_cast(const_cast(layerInfo->pNext)); } if (!layerInfo) { - std::cerr << "lsfg-vk: no layer info found in pNext chain, " + std::cerr << "lsfg-vk: No layer info found in pNext chain, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } auto* linkInfo = layerInfo->u.pLayerInfo; if (!linkInfo) { - std::cerr << "lsfg-vk: link info is null, " + std::cerr << "lsfg-vk: Link info is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } layer_info->GetInstanceProcAddr = linkInfo->pfnNextGetInstanceProcAddr; if (!layer_info->GetInstanceProcAddr) { - std::cerr << "lsfg-vk: next layer's vkGetInstanceProcAddr is null, " + std::cerr << "lsfg-vk: Next layer's vkGetInstanceProcAddr is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } - layerInfo->u.pLayerInfo = linkInfo->pNext; // advance for next layer + layerInfo->u.pLayerInfo = linkInfo->pNext; // Advance for next layer - // create instance + // Create instance auto* vkCreateInstance = reinterpret_cast( layer_info->GetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance")); if (!vkCreateInstance) { - std::cerr << "lsfg-vk: failed to get next layer's vkCreateInstance, " + std::cerr << "lsfg-vk: Failed to get next layer's vkCreateInstance, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } @@ -103,64 +103,64 @@ namespace { return VK_SUCCESS; } 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. " + std::cerr << "lsfg-vk: Required Vulkan instance extensions are not present. " "Your GPU driver is not supported.\n"; return e.error(); } } - // create device + /// Create device VkResult myvkCreateDevice( VkPhysicalDevice physdev, const VkDeviceCreateInfo* info, const VkAllocationCallbacks* alloc, VkDevice* device) { - // apply layer chaining + // Apply layer chaining auto* layerInfo = reinterpret_cast(const_cast(info->pNext)); while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerInfo->function != VK_LAYER_LINK_INFO)) { layerInfo = reinterpret_cast(const_cast(layerInfo->pNext)); } if (!layerInfo) { - std::cerr << "lsfg-vk: no layer info found in pNext chain, " + std::cerr << "lsfg-vk: No layer info found in pNext chain, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } auto* linkInfo = layerInfo->u.pLayerInfo; if (!linkInfo) { - std::cerr << "lsfg-vk: link info is null, " + std::cerr << "lsfg-vk: Link info is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } instance_info->funcs.GetDeviceProcAddr = linkInfo->pfnNextGetDeviceProcAddr; if (!linkInfo->pfnNextGetDeviceProcAddr) { - std::cerr << "lsfg-vk: next layer's vkGetDeviceProcAddr is null, " + std::cerr << "lsfg-vk: Next layer's vkGetDeviceProcAddr is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } - layerInfo->u.pLayerInfo = linkInfo->pNext; // advance for next layer + layerInfo->u.pLayerInfo = linkInfo->pNext; // Advance for next layer - // fetch device loader functions + // Fetch device loader functions layerInfo = reinterpret_cast(const_cast(info->pNext)); while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerInfo->function != VK_LOADER_DATA_CALLBACK)) { layerInfo = reinterpret_cast(const_cast(layerInfo->pNext)); } if (!layerInfo) { - std::cerr << "lsfg-vk: no layer loader data found in pNext chain.\n"; + std::cerr << "lsfg-vk: No layer loader data found in pNext chain.\n"; return VK_ERROR_INITIALIZATION_FAILED; } auto* setLoaderData = layerInfo->u.pfnSetDeviceLoaderData; if (!setLoaderData) { - std::cerr << "lsfg-vk: instance loader data function is null.\n"; + std::cerr << "lsfg-vk: Instance loader data function is null.\n"; return VK_ERROR_INITIALIZATION_FAILED; } - // create device + // Create device try { VkDeviceCreateInfo newInfo = *info; layer_info->root.modifyDeviceCreateInfo(newInfo, @@ -172,12 +172,12 @@ namespace { ); } 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. " + std::cerr << "lsfg-vk: Required Vulkan device extensions are not present. " "Your GPU driver is not supported.\n"; return e.error(); } - // create layer instance + // Create layer instance try { instance_info->devices.emplace( *device, @@ -189,25 +189,25 @@ namespace { ) ); } catch (const std::exception& e) { - std::cerr << "lsfg-vk: something went wrong during lsfg-vk initialization:\n"; + std::cerr << "lsfg-vk: Something went wrong during lsfg-vk initialization:\n"; std::cerr << "- " << e.what() << '\n'; } return VK_SUCCESS; } - // destroy device + /// Destroy device void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* alloc) { - // destroy layer instance + // Destroy layer instance auto it = instance_info->devices.find(device); if (it != instance_info->devices.end()) instance_info->devices.erase(it); - // destroy device + // Destroy device auto vkDestroyDevice = reinterpret_cast( instance_info->funcs.GetDeviceProcAddr(device, "vkDestroyDevice")); if (!vkDestroyDevice) { - std::cerr << "lsfg-vk: failed to get next layer's vkDestroyDevice, " + std::cerr << "lsfg-vk: Failed to get next layer's vkDestroyDevice, " "the previous layer does not follow spec\n"; return; } @@ -215,24 +215,24 @@ namespace { vkDestroyDevice(device, alloc); } - // destroy instance + /// Destroy instance void myvkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* alloc) { - // remove instance handle + // Remove instance handle auto it = std::ranges::find(instance_info->handles, instance); if (it != instance_info->handles.end()) instance_info->handles.erase(it); - // destroy instance info if no handles remain + // Destroy instance info if no handles remain if (instance_info->handles.empty()) { delete instance_info; // NOLINT (memory management) instance_info = nullptr; } - // destroy instance + // Destroy instance auto vkDestroyInstance = reinterpret_cast( layer_info->GetInstanceProcAddr(instance, "vkDestroyInstance")); if (!vkDestroyInstance) { - std::cerr << "lsfg-vk: failed to get next layer's vkDestroyInstance, " + std::cerr << "lsfg-vk: Failed to get next layer's vkDestroyInstance, " "the previous layer does not follow spec\n"; return; } @@ -240,7 +240,7 @@ namespace { vkDestroyInstance(instance, alloc); } - // get optional function pointer override + /// Get optional function pointer override PFN_vkVoidFunction getProcAddr(const std::string& name) { auto it = layer_info->map.find(name); if (it != layer_info->map.end()) @@ -248,7 +248,7 @@ namespace { return nullptr; } - // get instance-level function pointers + /// Get instance-level function pointers PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* name) { if (!name) return nullptr; @@ -259,7 +259,7 @@ namespace { return layer_info->GetInstanceProcAddr(instance, name); } - // get device-level function pointers + /// Get device-level function pointers PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* name) { if (!name) return nullptr; @@ -282,7 +282,7 @@ namespace { return VK_ERROR_INITIALIZATION_FAILED; try { - // retire old swapchain + // Retire old swapchain if (info->oldSwapchain) { const auto& info_mapping = instance_info->swapchainInfos.find(info->oldSwapchain); if (info_mapping != instance_info->swapchainInfos.end()) @@ -295,9 +295,9 @@ namespace { layer_info->root.removeSwapchainContext(info->oldSwapchain); } - layer_info->root.update(); // ensure config is up to date + layer_info->root.update(); // Ensure config is up to date - // create swapchain + // Create swapchain VkSwapchainCreateInfoKHR newInfo = *info; layer_info->root.modifySwapchainCreateInfo(it->second, newInfo, [=, newInfo = &newInfo]() { @@ -308,7 +308,7 @@ namespace { } ); - // get all swapchain images + // Get all swapchain images uint32_t imageCount{}; auto res = it->second.df().GetSwapchainImagesKHR(device, *swapchain, &imageCount, VK_NULL_HANDLE); @@ -329,7 +329,7 @@ namespace { .presentMode = newInfo.presentMode }).first->second; - // create lsfg-vk swapchain + // Create lsfg-vk swapchain layer_info->root.createSwapchainContext(it->second, *swapchain, info); instance_info->swapchains.emplace(*swapchain, @@ -337,11 +337,11 @@ namespace { return res; } catch (const ls::vulkan_error& e) { - std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain creation:\n"; + 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 << "lsfg-vk: Something went wrong during lsfg-vk swapchain creation:\n"; std::cerr << "- " << e.what() << '\n'; return VK_ERROR_INITIALIZATION_FAILED; } @@ -352,12 +352,12 @@ namespace { #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" VkResult result = VK_SUCCESS; - // ensure layer config is up to date + // Ensure layer config is up to date bool reload{}; try { reload = layer_info->root.update(); } catch (const std::exception&) { - reload = false; // ignore parse errors + reload = false; // Ignore parse errors } if (reload) { @@ -376,7 +376,7 @@ namespace { } } - // present each swapchain + // Present each swapchain for (size_t i = 0; i < info->swapchainCount; i++) { const auto& swapchain = info->pSwapchains[i]; @@ -402,7 +402,7 @@ namespace { if (e.error() != VK_ERROR_OUT_OF_DATE_KHR) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain presentation:\n"; std::cerr << "- " << e.what() << '\n'; - } // silently swallow out-of-date errors + } // Silently swallow out-of-date errors result = e.error(); } catch (const std::exception& e) { @@ -437,7 +437,7 @@ namespace { layer_info->root.removeSwapchainContext(swapchain); - // destroy swapchain + // Destroy swapchain it->second.df().DestroySwapchainKHR(device, swapchain, alloc); } } @@ -445,13 +445,13 @@ namespace { /// Vulkan layer entrypoint __attribute__((visibility("default"))) VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVersionStruct) { - // ensure loader compatibility + // Ensure loader compatibility if (!pVersionStruct || pVersionStruct->sType != LAYER_NEGOTIATE_INTERFACE_STRUCT || pVersionStruct->loaderLayerInterfaceVersion < 2) return VK_ERROR_INITIALIZATION_FAILED; - // if the layer has already been initialized, skip + // If the layer has already been initialized, skip if (layer_info) { pVersionStruct->loaderLayerInterfaceVersion = 2; pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr; @@ -460,7 +460,7 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers return VK_SUCCESS; } - // load the layer configuration + // Load the layer configuration try { layer_info = new LayerInfo { // NOLINT (memory management) .map = { @@ -477,20 +477,20 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers .root = Root() }; - if (!layer_info->root.active()) { // skip inactive + if (!layer_info->root.active()) { // Skip inactive delete layer_info; // NOLINT (memory management) layer_info = nullptr; return VK_ERROR_INITIALIZATION_FAILED; } } catch (const std::exception& e) { - std::cerr << "lsfg-vk: something went wrong during lsfg-vk layer initialization:\n"; + std::cerr << "lsfg-vk: Something went wrong during lsfg-vk layer initialization:\n"; std::cerr << "- " << e.what() << '\n'; return VK_ERROR_INITIALIZATION_FAILED; } - // emplace function pointers/version + // Emplace function pointers/version pVersionStruct->loaderLayerInterfaceVersion = 2; pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr; pVersionStruct->pfnGetDeviceProcAddr = myvkGetDeviceProcAddr; diff --git a/lsfg-vk-layer/src/instance.cpp b/lsfg-vk-layer/src/instance.cpp index 00da4d1..53989e4 100644 --- a/lsfg-vk-layer/src/instance.cpp +++ b/lsfg-vk-layer/src/instance.cpp @@ -2,10 +2,10 @@ #include "instance.hpp" #include "lsfg-vk-common/helpers/paths.hpp" -#include "swapchain.hpp" #include "lsfg-vk-common/configuration/detection.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" +#include "swapchain.hpp" #include #include @@ -25,7 +25,7 @@ using namespace lsfgvk; using namespace lsfgvk::layer; namespace { - /// helper function to add required extensions + /// 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); @@ -45,14 +45,14 @@ namespace { } Root::Root() { - // find active profile + // Find active profile const auto& profile = findProfile(this->config.get(), ls::identify()); if (!profile.has_value()) return; this->active_profile = profile->second; - std::cerr << "lsfg-vk: using profile with name '" << this->active_profile->name << "' "; + std::cerr << "lsfg-vk: Using profile with name '" << this->active_profile->name << "' "; switch (profile->first) { case ls::IdentType::OVERRIDE: std::cerr << "(identified via override)\n"; @@ -167,10 +167,10 @@ void Root::modifySwapchainCreateInfo(const vk::Vulkan& vk, VkSwapchainCreateInfo void Root::createSwapchainContext(const vk::Vulkan& vk, VkSwapchainKHR swapchain, const SwapchainInfo& info) { if (!this->active_profile.has_value()) - throw ls::error("attempted to create swapchain context while layer is inactive"); + throw ls::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 + if (!this->backend.has_value()) { // Emplace backend late, due to loader bug const auto& global = this->config.get().global(); setenv("DISABLE_LSFGVK", "1", 1); @@ -183,23 +183,12 @@ void Root::createSwapchainContext(const vk::Vulkan& vk, dll = ls::findShaderDll(); 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); - }, + profile.gpu.value_or(""), dll, global.allow_fp16 ); } catch (const std::exception& e) { unsetenv("DISABLE_LSFGVK"); - throw ls::error("failed to create backend instance", e); + throw ls::error("Failed to create backend instance", e); } unsetenv("DISABLE_LSFGVK"); diff --git a/lsfg-vk-layer/src/instance.hpp b/lsfg-vk-layer/src/instance.hpp index 35083b6..57d41de 100644 --- a/lsfg-vk-layer/src/instance.hpp +++ b/lsfg-vk-layer/src/instance.hpp @@ -2,13 +2,14 @@ #pragma once -#include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk/lsfgvk.hpp" #include "lsfg-vk-common/configuration/config.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include "swapchain.hpp" +#include #include #include @@ -16,49 +17,75 @@ namespace lsfgvk::layer { - /// root context of the lsfg-vk layer + /// + /// Root context of the lsfg-vk layer + /// class Root { public: - /// create the lsfg-vk root context + /// + /// Create the lsfg-vk root context + /// /// @throws ls::error on failure + /// Root(); - /// check if the layer is active - /// @return true if active + /// + /// Check if the layer is active + /// + /// @return true If active + /// [[nodiscard]] bool active() const { return this->active_profile.has_value(); } - /// ensure the layer is up-to-date - /// @return true if the configuration was updated + /// + /// Ensure the layer is up-to-date + /// + /// @return true If the configuration was updated + /// bool update(); - /// modify instance create info - /// @param createInfo original create info - /// @param finish function to call after modification + /// + /// 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 + /// + /// 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 + /// + /// 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 + + /// + /// Create swapchain context + /// + /// @param vk Vulkan instance + /// @param swapchain Swapchain handle + /// @param info Swapchain info /// @throws ls::error on failure + /// void createSwapchainContext(const vk::Vulkan& vk, VkSwapchainKHR swapchain, const SwapchainInfo& info); - /// get swapchain context - /// @param swapchain swapchain handle - /// @return swapchain context + /// + /// Get swapchain context + /// + /// @param swapchain Swapchain handle + /// @return swapchain Context /// @throws ls::error if not found + /// [[nodiscard]] Swapchain& getSwapchainContext(VkSwapchainKHR swapchain) { const auto& it = this->swapchains.find(swapchain); if (it == this->swapchains.end()) @@ -66,14 +93,17 @@ namespace lsfgvk::layer { return it->second; } - /// remove swapchain context - /// @param swapchain swapchain handle + /// + /// Remove swapchain context + /// + /// @param swapchain Swapchain handle + /// void removeSwapchainContext(VkSwapchainKHR swapchain); private: ls::WatchedConfig config; std::optional active_profile; - ls::lazy backend; + ls::lazy backend; std::unordered_map swapchains; }; diff --git a/lsfg-vk-layer/src/swapchain.cpp b/lsfg-vk-layer/src/swapchain.cpp index 85033ae..8f0599e 100644 --- a/lsfg-vk-layer/src/swapchain.cpp +++ b/lsfg-vk-layer/src/swapchain.cpp @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ #include "swapchain.hpp" -#include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk/lsfgvk.hpp" #include "lsfg-vk-common/configuration/config.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" @@ -10,11 +10,10 @@ #include "lsfg-vk-common/vulkan/semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" -#include #include #include #include -#include +#include #include #include #include @@ -25,6 +24,7 @@ using namespace lsfgvk; using namespace lsfgvk::layer; namespace { + /// Barrier helper VkImageMemoryBarrier barrierHelper(VkImage handle, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, @@ -66,66 +66,45 @@ void layer::context_ModifySwapchainCreateInfo(const ls::GameConf& profile, uint3 } } -Swapchain::Swapchain(const vk::Vulkan& vk, backend::Instance& backend, +Swapchain::Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend, ls::GameConf profile, SwapchainInfo info) : instance(backend), profile(std::move(profile)), info(std::move(info)) { const VkExtent2D extent = this->info.extent; - const bool hdr = this->info.format > 57; - - std::vector sourceFds(2); - std::vector destinationFds(this->profile.multiplier - 1); - - this->sourceImages.reserve(sourceFds.size()); - for (int& fd : sourceFds) - this->sourceImages.emplace_back(vk, - extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - std::nullopt, &fd); - - this->destinationImages.reserve(destinationFds.size()); - for (int& fd : destinationFds) - this->destinationImages.emplace_back(vk, - extent, hdr ? VK_FORMAT_R16G16B16A16_SFLOAT : VK_FORMAT_R8G8B8A8_UNORM, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - std::nullopt, &fd); - - int syncFd{}; - this->syncSemaphore.emplace(vk, 0, std::nullopt, &syncFd); try { - this->ctx = ls::owned_ptr>( - new ls::R(backend.openContext( - { sourceFds.at(0), sourceFds.at(1) }, destinationFds, syncFd, - extent.width, extent.height, - hdr, 1.0F / this->profile.flow_scale, this->profile.performance_mode - )), - [backend = &backend](ls::R& ctx) { - backend->closeContext(ctx); - } + this->ctx = std::make_unique( + backend, + extent.width, extent.height, + this->profile.flow_scale, + this->profile.performance_mode ); - - backend::makeLeaking(); // don't worry about it :3 + this->total = static_cast(this->profile.multiplier) - 1; } catch (const std::exception& e) { - throw ls::error("failed to create swapchain context", e); + throw ls::error("Failed to create swapchain context", e); } + const auto exportedFds = this->ctx->exportFds(); + this->sourceImage.emplace(vk, + extent, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + exportedFds.sourceFd, std::nullopt, 2); + this->destinationImage.emplace(vk, + extent, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + exportedFds.destinationFd, std::nullopt, 2); // FIXME: Should be 1 + this->syncSemaphore.emplace(vk, 0, exportedFds.syncFd); + this->renderCommandBuffer.emplace(vk); this->renderFence.emplace(vk); - for (size_t i = 0; i < this->destinationImages.size(); i++) { + this->finalSemaphore.emplace(vk); + for (size_t i = 0; i < this->total; i++) { this->passes.emplace_back(RenderPass { .commandBuffer = vk::CommandBuffer(vk), - .acquireSemaphore = vk::Semaphore(vk) + .acquireSemaphore = vk::Semaphore(vk), + .copySemaphore = vk::Semaphore(vk) }); } - - const size_t frames = std::max(this->info.images.size(), this->destinationImages.size() + 2); - for (size_t i = 0; i < frames; i++) { - this->postCopySemaphores.emplace_back( - vk::Semaphore(vk), - vk::Semaphore(vk) - ); - } } VkResult Swapchain::present(const vk::Vulkan& vk, @@ -133,19 +112,18 @@ VkResult Swapchain::present(const vk::Vulkan& vk, void* next_chain, uint32_t imageIdx, const std::vector& semaphores) { const auto& swapchainImage = this->info.images.at(imageIdx); - const auto& sourceImage = this->sourceImages.at(this->fidx % 2); + const auto sourceImageIdx{static_cast(this->iteration) % 2}; - // schedule frame generation + // Schedule frame generation try { - this->instance.get().scheduleFrames(this->ctx.get()); + this->ctx->dispatch(this->total); } catch (const std::exception& e) { - throw ls::error("failed to schedule frames", e); + throw ls::error("Failed to schedule frames", e); } - // update present mode when not using pacing + // Update present mode when not using pacing if (this->profile.pacing == ls::Pacing::None) { #pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunknown-warning-option" #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" auto* info = reinterpret_cast(next_chain); while (info) { @@ -160,12 +138,12 @@ VkResult Swapchain::present(const vk::Vulkan& vk, #pragma clang diagnostic pop } - // wait for completion of previous frame - if (this->fidx && !this->renderFence->wait(vk, 150ULL * 1000 * 1000)) + // Wait for completion of previous frame + if (this->iteration && !this->renderFence->wait(vk, 150ULL * 1000 * 1000)) throw ls::vulkan_error(VK_TIMEOUT, "vkWaitForFences() failed"); this->renderFence->reset(vk); - // copy swapchain image into backend source image + // Copy swapchain image into backend source image const auto& cmdbuf = *this->renderCommandBuffer; cmdbuf.begin(vk); @@ -177,15 +155,15 @@ VkResult Swapchain::present(const vk::Vulkan& vk, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ), - barrierHelper(sourceImage.handle(), + barrierHelper(this->sourceImage->handle(), VK_ACCESS_NONE, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ), }, - { swapchainImage, sourceImage.handle() }, - sourceImage.getExtent(), + { swapchainImage, this->sourceImage->handle() }, + this->sourceImage->getExtent(), { barrierHelper(swapchainImage, VK_ACCESS_TRANSFER_READ_BIT, @@ -193,39 +171,40 @@ VkResult Swapchain::present(const vk::Vulkan& vk, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ), - } + }, + 0, sourceImageIdx ); cmdbuf.end(vk); + cmdbuf.submit(vk, semaphores, VK_NULL_HANDLE, 0, - {}, this->syncSemaphore->handle(), this->idx++ + {}, this->syncSemaphore->handle(), this->syncValue ); - for (size_t i = 0; i < this->destinationImages.size(); i++) { - auto& pcs = this->postCopySemaphores.at(this->idx % this->postCopySemaphores.size()); - auto& destinationImage = this->destinationImages.at(i); - auto& pass = this->passes.at(i); + for (size_t i = 0; i < this->passes.size(); i++) { + auto& pass{this->passes.at(i)}; + const bool last{i == (this->passes.size() - 1)}; - // acquire swapchain image - uint32_t aqImageIdx{}; + // Acquire swapchain image + uint32_t swapchainImageIdx{}; auto res = vk.df().AcquireNextImageKHR(vk.dev(), swapchain, UINT64_MAX, pass.acquireSemaphore.handle(), VK_NULL_HANDLE, - &aqImageIdx + &swapchainImageIdx ); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw ls::vulkan_error(res, "vkAcquireNextImageKHR() failed"); - const auto& aquiredSwapchainImage = this->info.images.at(aqImageIdx); + const auto& aquiredSwapchainImage = this->info.images.at(swapchainImageIdx); - // copy backend destination image into swapchain image + // Copy backend destination image into swapchain image auto& cmdbuf = pass.commandBuffer; cmdbuf.begin(vk); cmdbuf.blitImage(vk, { - barrierHelper(destinationImage.handle(), + barrierHelper(this->destinationImage->handle(), VK_ACCESS_NONE, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, @@ -238,8 +217,8 @@ VkResult Swapchain::present(const vk::Vulkan& vk, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ), }, - { destinationImage.handle(), aquiredSwapchainImage }, - destinationImage.getExtent(), + { this->destinationImage->handle(), aquiredSwapchainImage }, + this->destinationImage->getExtent(), { barrierHelper(aquiredSwapchainImage, VK_ACCESS_TRANSFER_WRITE_BIT, @@ -250,48 +229,43 @@ VkResult Swapchain::present(const vk::Vulkan& vk, } ); - std::vector waitSemaphores{ pass.acquireSemaphore.handle() }; - if (i) { // non-first pass - const auto& prevPCS = this->postCopySemaphores.at((this->idx - 1) % this->postCopySemaphores.size()); - waitSemaphores.push_back(prevPCS.second.handle()); - } - - const std::vector signalSemaphores{ - pcs.first.handle(), - pcs.second.handle() - }; - cmdbuf.end(vk); + + std::vector signalSemaphores{ pass.copySemaphore.handle() }; + if (last) + signalSemaphores.push_back(this->finalSemaphore->handle()); + + this->syncValue++; + cmdbuf.submit(vk, - waitSemaphores, this->syncSemaphore->handle(), this->idx, - signalSemaphores, VK_NULL_HANDLE, 0, - i == this->destinationImages.size() - 1 ? this->renderFence->handle() : VK_NULL_HANDLE + { pass.acquireSemaphore.handle() }, this->syncSemaphore->handle(), this->syncValue, + signalSemaphores, last ? nullptr : this->syncSemaphore->handle(), this->syncValue + 1, + last ? this->renderFence->handle() : VK_NULL_HANDLE ); - // present swapchain image + this->syncValue++; + + // Present swapchain image const VkPresentInfoKHR presentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = i ? nullptr : next_chain, .waitSemaphoreCount = 1, - .pWaitSemaphores = &pcs.first.handle(), + .pWaitSemaphores = &pass.copySemaphore.handle(), .swapchainCount = 1, .pSwapchains = &swapchain, - .pImageIndices = &aqImageIdx, + .pImageIndices = &swapchainImageIdx, }; res = vk.df().QueuePresentKHR(queue, &presentInfo); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw ls::vulkan_error(res, "vkQueuePresentKHR() failed"); - - this->idx++; } - // present original swapchain image - auto& lastPCS = this->postCopySemaphores.at((this->idx - 1) % this->postCopySemaphores.size()); + // Present original swapchain image const VkPresentInfoKHR presentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, - .pWaitSemaphores = &lastPCS.second.handle(), + .pWaitSemaphores = &this->finalSemaphore->handle(), .swapchainCount = 1, .pSwapchains = &swapchain, .pImageIndices = &imageIdx, @@ -300,6 +274,7 @@ VkResult Swapchain::present(const vk::Vulkan& vk, if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw ls::vulkan_error(res, "vkQueuePresentKHR() failed"); - this->fidx++; + this->iteration++; + return res; } diff --git a/lsfg-vk-layer/src/swapchain.hpp b/lsfg-vk-layer/src/swapchain.hpp index 3c711a0..4893a0a 100644 --- a/lsfg-vk-layer/src/swapchain.hpp +++ b/lsfg-vk-layer/src/swapchain.hpp @@ -2,7 +2,7 @@ #pragma once -#include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk/lsfgvk.hpp" #include "lsfg-vk-common/configuration/config.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/command_buffer.hpp" @@ -12,15 +12,18 @@ #include "lsfg-vk-common/vulkan/timeline_semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" +#include #include -#include +#include #include #include namespace lsfgvk::layer { - /// swapchain info struct + /// + /// Swapchain info struct + /// struct SwapchainInfo { std::vector images; VkFormat format; @@ -29,53 +32,67 @@ namespace lsfgvk::layer { 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 + /// + /// 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 ls::GameConf& profile, uint32_t maxImages, VkSwapchainCreateInfoKHR& createInfo); - /// swapchain context for a layer instance + /// + /// Swapchain context for a layer instance + /// class Swapchain { public: - /// create a new swapchain context - /// @param vk vulkan instance + /// + /// Create a new swapchain context + /// + /// @param vk Vulkan instance /// @param backend lsfg-vk backend instance - /// @param profile active game profile - /// @param info swapchain info - Swapchain(const vk::Vulkan& vk, backend::Instance& backend, + /// @param profile Active game profile + /// @param info Swapchain info + /// + Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend, ls::GameConf profile, SwapchainInfo info); - /// present a frame - /// @param vk vulkan instance - /// @param queue presentation queue - /// @param next_chain next chain pointer for the present info (WARN: shared!) - /// @param imageIdx swapchain image index to present to - /// @param semaphores semaphores to wait on before presenting - /// @throws ls::vulkan_error on vulkan errors + /// + /// Present a frame + /// + /// @param vk Vulkan instance + /// @param queue Presentation queue + /// @param next_chain next chain pointer for the present info (WARNING: shared!) + /// @param imageIdx Swapchain image index to present to + /// @param semaphores Semaphores to wait on before presenting + /// @throws ls::vulkan_error on vulkan error + /// VkResult present(const vk::Vulkan& vk, VkQueue queue, VkSwapchainKHR swapchain, void* next_chain, uint32_t imageIdx, const std::vector& semaphores); private: - std::vector sourceImages; - std::vector destinationImages; + ls::lazy sourceImage; + ls::lazy destinationImage; ls::lazy syncSemaphore; ls::lazy renderCommandBuffer; ls::lazy renderFence; + ls::lazy finalSemaphore; struct RenderPass { vk::CommandBuffer commandBuffer; vk::Semaphore acquireSemaphore; + vk::Semaphore copySemaphore; }; std::vector passes; - std::vector> postCopySemaphores; - ls::R instance; - ls::owned_ptr> ctx; - size_t idx{1}; - size_t fidx{0}; // real frame index + ls::R instance; + std::unique_ptr ctx; + uint32_t total{}; + + size_t iteration{0}; + size_t syncValue{1}; ls::GameConf profile; SwapchainInfo info;