From e0fac3e5a9596a0ad313210a3b6cbfa324766bcb Mon Sep 17 00:00:00 2001 From: PancakeTAS Date: Fri, 19 Dec 2025 18:03:03 +0100 Subject: [PATCH] refactor(cleanup): implement basic/none frame pacing --- include/context.hpp | 80 ----- include/hooks.hpp | 22 -- lsfg-vk-backend/src/lsfgvk.cpp | 8 +- .../lsfg-vk-common/vulkan/command_buffer.hpp | 28 +- .../lsfg-vk-common/vulkan/semaphore.hpp | 4 + .../include/lsfg-vk-common/vulkan/vulkan.hpp | 3 + lsfg-vk-common/src/vulkan/command_buffer.cpp | 91 ++++- lsfg-vk-common/src/vulkan/vulkan.cpp | 5 + lsfg-vk-debug/src/debug.cpp | 6 +- lsfg-vk-layer/src/configuration/detection.cpp | 2 +- lsfg-vk-layer/src/context/swapchain.cpp | 259 ++++++++++++++- lsfg-vk-layer/src/context/swapchain.hpp | 43 ++- lsfg-vk-layer/src/entrypoint.cpp | 64 +++- src/context.cpp | 203 ------------ src/hooks.cpp | 312 ------------------ 15 files changed, 476 insertions(+), 654 deletions(-) delete mode 100644 include/context.hpp delete mode 100644 include/hooks.hpp delete mode 100644 src/context.cpp delete mode 100644 src/hooks.cpp diff --git a/include/context.hpp b/include/context.hpp deleted file mode 100644 index 3438e48..0000000 --- a/include/context.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "hooks.hpp" -#include "mini/commandbuffer.hpp" -#include "mini/commandpool.hpp" -#include "mini/image.hpp" -#include "mini/semaphore.hpp" - -#include - -#include -#include -#include -#include - -/// -/// This class is the frame generation context. There should be one instance per swapchain. -/// -class LsContext { -public: - /// - /// Create the swapchain context. - /// - /// @param info The device information to use. - /// @param swapchain The Vulkan swapchain to use. - /// @param extent The extent of the swapchain images. - /// @param swapchainImages The swapchain images to use. - /// - /// @throws LSFG::vulkan_error if any Vulkan call fails. - /// - LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, - VkExtent2D extent, const std::vector& swapchainImages); - - /// - /// Custom present logic. - /// - /// @param info The device information to use. - /// @param pNext Unknown pointer set in the present info structure. - /// @param queue The Vulkan queue to present the frame on. - /// @param gameRenderSemaphores The semaphores to wait on before presenting. - /// @param presentIdx The index of the swapchain image to present. - /// @return The result of the Vulkan present operation, which can be VK_SUCCESS or VK_SUBOPTIMAL_KHR. - /// - /// @throws LSFG::vulkan_error if any Vulkan call fails. - /// - VkResult present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue, - const std::vector& gameRenderSemaphores, uint32_t presentIdx); - - // Non-copyable, trivially moveable and destructible - LsContext(const LsContext&) = delete; - LsContext& operator=(const LsContext&) = delete; - LsContext(LsContext&&) = default; - LsContext& operator=(LsContext&&) = default; - ~LsContext() = default; -private: - VkSwapchainKHR swapchain; - std::vector swapchainImages; - VkExtent2D extent; - - std::shared_ptr lsfgCtxId; // lsfg context id - Mini::Image frame_0, frame_1; // frames shared with lsfg. write to frame_0 when fc % 2 == 0 - std::vector out_n; // output images shared with lsfg, indexed by framegen id - - Mini::CommandPool cmdPool; - uint64_t frameIdx{0}; - - struct RenderPassInfo { - Mini::CommandBuffer preCopyBuf; // copy from swapchain image to frame_0/frame_1 - std::array preCopySemaphores; // signal when preCopyBuf is done - - std::vector renderSemaphores; // signal when lsfg is done with frame n - - std::vector acquireSemaphores; // signal for swapchain image n - - std::vector postCopyBufs; // copy from out_n to swapchain image - std::vector postCopySemaphores; // signal when postCopyBuf is done - std::vector prevPostCopySemaphores; // signal for previous postCopyBuf - }; // data for a single render pass - std::array passInfos; // allocate 8 because why not -}; diff --git a/include/hooks.hpp b/include/hooks.hpp deleted file mode 100644 index e38212c..0000000 --- a/include/hooks.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -namespace Hooks { - - /// Vulkan device information structure. - struct DeviceInfo { - VkDevice device; - VkPhysicalDevice physicalDevice; - std::pair queue; // graphics family - }; - - /// Map of hooked Vulkan functions. - extern std::unordered_map hooks; - -} diff --git a/lsfg-vk-backend/src/lsfgvk.cpp b/lsfg-vk-backend/src/lsfgvk.cpp index 1f3f821..db8bfb8 100644 --- a/lsfg-vk-backend/src/lsfgvk.cpp +++ b/lsfg-vk-backend/src/lsfgvk.cpp @@ -528,8 +528,8 @@ void Context::scheduleFrames() { this->beta1.render(ctx.vk, cmdbuf); cmdbuf.submit(this->ctx.vk, - this->syncSemaphore, this->idx, - this->prepassSemaphore, this->idx + {}, this->syncSemaphore.handle(), this->idx, + {}, this->prepassSemaphore.handle(), this->idx ); this->idx++; @@ -551,8 +551,8 @@ void Context::scheduleFrames() { pass.generate->render(ctx.vk, cmdbuf, this->fidx); cmdbuf.submit(this->ctx.vk, - this->prepassSemaphore, this->idx - 1, - this->syncSemaphore, this->idx + i + {}, this->prepassSemaphore.handle(), this->idx - 1, + {}, this->syncSemaphore.handle(), this->idx + i ); } diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp index 01426a3..3b6211b 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/command_buffer.hpp @@ -5,11 +5,11 @@ #include "descriptor_set.hpp" #include "image.hpp" #include "shader.hpp" -#include "timeline_semaphore.hpp" #include "vulkan.hpp" #include #include +#include #include #include @@ -34,6 +34,18 @@ namespace vk { const vk::Image& image, const std::optional& clearColor = std::nullopt) const; + /// blit an image + /// @param vk the vulkan instance + /// @param preBarriers image memory barriers to apply before blit + /// @param images source and destination images + /// @param extent the extent of the blit + /// @param postBarriers image memory barriers to apply after blit + /// throws ls::vulkan_error on failure + void blitImage(const vk::Vulkan& vk, + const std::vector& preBarriers, + std::pair images, VkExtent2D extent, + const std::vector& postBarriers) const; + /// dispatch a compute shader /// @param vk the vulkan instance /// @param shader the compute shader @@ -44,7 +56,7 @@ namespace vk { /// @param z dispatch size in Z void dispatch(const vk::Vulkan& vk, const vk::Shader& shader, const vk::DescriptorSet& set, const std::vector& barriers, - uint32_t x, uint32_t y, uint32_t z) const; + uint32_t x, uint32_t y, uint32_t z) const; /// copy buffer to image /// @param vk the vulkan instance @@ -55,14 +67,18 @@ namespace vk { /// submit the command buffer /// @param vk the vulkan instance - /// @param waitSemaphore the semaphore to wait on + /// @param waitSemaphores the semaphores to wait on + /// @param waitTimelineSemaphore the timeline semaphore to wait on /// @param waitValue the value to wait for - /// @param signalSemaphore the semaphore to signal + /// @param signalSemaphores the semaphores to signal + /// @param signalTimelineSemaphore the timeline semaphore to signal /// @param signalValue the value to signal /// @throws ls::vulkan_error on failure void submit(const vk::Vulkan& vk, - const vk::TimelineSemaphore& waitSemaphore, uint64_t waitValue, - const vk::TimelineSemaphore& signalSemaphore, uint64_t signalValue) const; + std::vector waitSemaphores, + VkSemaphore waitTimelineSemaphore, uint64_t waitValue, + std::vector signalSemaphores, + VkSemaphore signalTimelineSemaphore, uint64_t signalValue) const; /// submit the command buffer instantly /// @param vk the vulkan instance diff --git a/lsfg-vk-common/include/lsfg-vk-common/vulkan/semaphore.hpp b/lsfg-vk-common/include/lsfg-vk-common/vulkan/semaphore.hpp index 780db97..99b67ca 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/semaphore.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/semaphore.hpp @@ -16,6 +16,10 @@ namespace vk { /// @param fd optional file descriptor to import the semaphore from /// @throws ls::vulkan_error on failure Semaphore(const vk::Vulkan& vk, std::optional fd = std::nullopt); + + /// get the underlying VkSemaphore handle + /// @return the VkSemaphore handle + [[nodiscard]] const auto& handle() const { return *this->semaphore; } private: ls::owned_ptr semaphore; }; 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 23e8302..04b79b1 100644 --- a/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp +++ b/lsfg-vk-common/include/lsfg-vk-common/vulkan/vulkan.hpp @@ -66,6 +66,7 @@ namespace vk { PFN_vkBeginCommandBuffer BeginCommandBuffer; PFN_vkEndCommandBuffer EndCommandBuffer; PFN_vkCmdPipelineBarrier CmdPipelineBarrier; + PFN_vkCmdBlitImage CmdBlitImage; PFN_vkCmdClearColorImage CmdClearColorImage; PFN_vkCmdBindPipeline CmdBindPipeline; PFN_vkCmdBindDescriptorSets CmdBindDescriptorSets; @@ -106,6 +107,8 @@ namespace vk { PFN_vkGetSemaphoreFdKHR GetSemaphoreFdKHR; PFN_vkCreateSwapchainKHR CreateSwapchainKHR; PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR AcquireNextImageKHR; + PFN_vkQueuePresentKHR QueuePresentKHR; PFN_vkDestroySwapchainKHR DestroySwapchainKHR; }; diff --git a/lsfg-vk-common/src/vulkan/command_buffer.cpp b/lsfg-vk-common/src/vulkan/command_buffer.cpp index a88ba30..79d1598 100644 --- a/lsfg-vk-common/src/vulkan/command_buffer.cpp +++ b/lsfg-vk-common/src/vulkan/command_buffer.cpp @@ -6,11 +6,11 @@ #include "lsfg-vk-common/vulkan/fence.hpp" #include "lsfg-vk-common/vulkan/image.hpp" #include "lsfg-vk-common/vulkan/shader.hpp" -#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include #include +#include #include #include @@ -122,6 +122,54 @@ void CommandBuffer::dispatch(const vk::Vulkan& vk, vk.df().CmdDispatch(*this->commandBuffer, x, y, z); } +void CommandBuffer::blitImage(const vk::Vulkan& vk, + const std::vector& preBarriers, + std::pair images, VkExtent2D extent, + const std::vector& postBarriers) const { + vk.df().CmdPipelineBarrier(*this->commandBuffer, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, + 0, + 0, nullptr, + 0, nullptr, + static_cast(preBarriers.size()), preBarriers.data() + ); + + const VkImageBlit region{ + .srcSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1 + }, + .srcOffsets = { + { 0, 0, 0 }, + { static_cast(extent.width), + static_cast(extent.height), 1 } + }, + .dstSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .layerCount = 1 + }, + .dstOffsets = { + { 0, 0, 0 }, + { static_cast(extent.width), + static_cast(extent.height), 1 } + } + }; + vk.df().CmdBlitImage(*this->commandBuffer, + images.first, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + images.second, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, ®ion, + VK_FILTER_NEAREST + ); + + vk.df().CmdPipelineBarrier(*this->commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + 0, + 0, nullptr, + 0, nullptr, + static_cast(postBarriers.size()), postBarriers.data() + ); +} + void CommandBuffer::copyBufferToImage(const vk::Vulkan& vk, const vk::Buffer& buffer, const vk::Image& image) const { const VkImageMemoryBarrier barrier{ @@ -163,33 +211,48 @@ void CommandBuffer::copyBufferToImage(const vk::Vulkan& vk, ); } - void CommandBuffer::submit(const vk::Vulkan& vk, - const vk::TimelineSemaphore& waitSemaphore, uint64_t waitValue, - const vk::TimelineSemaphore& signalSemaphore, uint64_t signalValue) const { + std::vector waitSemaphores, + VkSemaphore waitTimelineSemaphore, uint64_t waitValue, + std::vector signalSemaphores, + VkSemaphore signalTimelineSemaphore, uint64_t signalValue) const { auto res = vk.df().EndCommandBuffer(*this->commandBuffer); if (res != VK_SUCCESS) throw ls::vulkan_error(res, "vkEndCommandBuffer() failed"); + // create arrays of semaphores and values + if (waitTimelineSemaphore) + waitSemaphores.push_back(waitTimelineSemaphore); + std::vector waitValues(waitSemaphores.size(), 0); + waitValues.back() = waitValue; + + if (signalTimelineSemaphore) + signalSemaphores.push_back(signalTimelineSemaphore); + + std::vector signalValues(signalSemaphores.size(), 0); + signalValues.back() = signalValue; + + // create submit info const VkTimelineSemaphoreSubmitInfo timelineInfo{ .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, - .waitSemaphoreValueCount = 1, - .pWaitSemaphoreValues = &waitValue, - .signalSemaphoreValueCount = 1, - .pSignalSemaphoreValues = &signalValue + .waitSemaphoreValueCount = static_cast(waitValues.size()), + .pWaitSemaphoreValues = waitValues.data(), + .signalSemaphoreValueCount = static_cast(signalValues.size()), + .pSignalSemaphoreValues = signalValues.data() }; - const VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + std::vector stages(waitSemaphores.size(), + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); const VkSubmitInfo submitInfo{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .pNext = &timelineInfo, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &waitSemaphore.handle(), - .pWaitDstStageMask = &stage, + .waitSemaphoreCount = static_cast(waitSemaphores.size()), + .pWaitSemaphores = waitSemaphores.data(), + .pWaitDstStageMask = stages.data(), .commandBufferCount = 1, .pCommandBuffers = &*this->commandBuffer, - .signalSemaphoreCount = 1, - .pSignalSemaphores = &signalSemaphore.handle() + .signalSemaphoreCount = static_cast(signalSemaphores.size()), + .pSignalSemaphores = signalSemaphores.data() }; res = vk.df().QueueSubmit(vk.queue(), 1, &submitInfo, VK_NULL_HANDLE); if (res != VK_SUCCESS) diff --git a/lsfg-vk-common/src/vulkan/vulkan.cpp b/lsfg-vk-common/src/vulkan/vulkan.cpp index 7ad6816..cac5ff7 100644 --- a/lsfg-vk-common/src/vulkan/vulkan.cpp +++ b/lsfg-vk-common/src/vulkan/vulkan.cpp @@ -320,6 +320,7 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi .BeginCommandBuffer = dpa(f, d, "vkBeginCommandBuffer"), .EndCommandBuffer = dpa(f, d, "vkEndCommandBuffer"), .CmdPipelineBarrier = dpa(f, d, "vkCmdPipelineBarrier"), + .CmdBlitImage = dpa(f, d, "vkCmdBlitImage"), .CmdClearColorImage = dpa(f, d, "vkCmdClearColorImage"), .CmdBindPipeline = dpa(f, d, "vkCmdBindPipeline"), .CmdBindDescriptorSets = dpa(f, d, "vkCmdBindDescriptorSets"), @@ -367,6 +368,10 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi dpa(f, d, "vkCreateSwapchainKHR") : nullptr, .GetSwapchainImagesKHR = graphical ? dpa(f, d, "vkGetSwapchainImagesKHR") : nullptr, + .AcquireNextImageKHR = graphical ? + dpa(f, d, "vkAcquireNextImageKHR") : nullptr, + .QueuePresentKHR = graphical ? + dpa(f, d, "vkQueuePresentKHR") : nullptr, .DestroySwapchainKHR = graphical ? dpa(f, d, "vkDestroySwapchainKHR") : nullptr }; diff --git a/lsfg-vk-debug/src/debug.cpp b/lsfg-vk-debug/src/debug.cpp index 8f4b1aa..dcbdd36 100644 --- a/lsfg-vk-debug/src/debug.cpp +++ b/lsfg-vk-debug/src/debug.cpp @@ -63,11 +63,7 @@ namespace { cmdbuf.copyBufferToImage(vk, stagingbuf, image); const vk::TimelineSemaphore sema{vk, 0}; - cmdbuf.submit(vk, sema, 1, sema, 2); - - sema.signal(vk, 1); - if (!sema.wait(vk, 2)) - throw std::runtime_error("image upload failed"); + cmdbuf.submit(vk); } } diff --git a/lsfg-vk-layer/src/configuration/detection.cpp b/lsfg-vk-layer/src/configuration/detection.cpp index e4144d5..beaccfd 100644 --- a/lsfg-vk-layer/src/configuration/detection.cpp +++ b/lsfg-vk-layer/src/configuration/detection.cpp @@ -52,7 +52,7 @@ Identification layer::identify() { if (!line.ends_with(".exe")) continue; - size_t pos = line.find_first_of('/'); + size_t pos = line.find_last_of('/'); if (pos == std::string::npos) { pos = line.find_last_of(' '); if (pos == std::string::npos) diff --git a/lsfg-vk-layer/src/context/swapchain.cpp b/lsfg-vk-layer/src/context/swapchain.cpp index f2e6062..2f3f71a 100644 --- a/lsfg-vk-layer/src/context/swapchain.cpp +++ b/lsfg-vk-layer/src/context/swapchain.cpp @@ -1,16 +1,51 @@ #include "swapchain.hpp" #include "../configuration/config.hpp" #include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk-common/helpers/errors.hpp" +#include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/command_buffer.hpp" +#include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" +#include #include +#include +#include +#include +#include -#include #include using namespace lsfgvk; using namespace lsfgvk::layer; +namespace { + VkImageMemoryBarrier barrierHelper(VkImage handle, + VkAccessFlags srcAccessMask, + VkAccessFlags dstAccessMask, + VkImageLayout oldLayout, + VkImageLayout newLayout) { + return VkImageMemoryBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = srcAccessMask, + .dstAccessMask = dstAccessMask, + .oldLayout = oldLayout, + .newLayout = newLayout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = handle, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1 + } + }; + } +} + void layer::context_ModifySwapchainCreateInfo(const GameConf& profile, uint32_t maxImages, VkSwapchainCreateInfoKHR& createInfo) { createInfo.imageUsage |= @@ -28,6 +63,224 @@ void layer::context_ModifySwapchainCreateInfo(const GameConf& profile, uint32_t } Swapchain::Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend, - const GameConf& profile, const SwapchainInfo& info) { - std::cerr << "lsfg-vk: swapchain created :3\n"; + 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); + + this->ctx = ls::owned_ptr>( + new ls::R(backend.openContext( + { sourceFds.at(0), sourceFds.at(1) }, destinationFds, syncFd, + extent.width, extent.height, + hdr, this->profile.flow_scale, this->profile.performance_mode + )), + [backend = &backend](ls::R& ctx) { + backend->closeContext(ctx); + } + ); + + this->renderCommandBuffer.emplace(vk); + this->renderSemaphore.emplace(vk, 0); + for (size_t i = 0; i < this->destinationImages.size(); i++) { + this->passes.emplace_back(RenderPass { + .commandBuffer = vk::CommandBuffer(vk), + .acquireSemaphore = vk::Semaphore(vk), + .postCopySemaphore = { + vk::Semaphore(vk), + vk::Semaphore(vk) + } + }); + } +} + +VkResult Swapchain::present(const vk::Vulkan& vk, + VkQueue queue, VkSwapchainKHR swapchain, + 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); + + // schedule frame generation + this->instance.get().scheduleFrames(this->ctx.get()); + + // update present mode when not using pacing + if (this->profile.pacing == Pacing::None) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + auto* info = reinterpret_cast(next_chain); + while (info) { + if (info->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT) { + for (size_t i = 0; i < info->swapchainCount; i++) + const_cast(info->pPresentModes)[i] = // NOLINT + VK_PRESENT_MODE_FIFO_KHR; + } + + info = reinterpret_cast(const_cast(info->pNext)); + } +#pragma clang diagnostic pop + } + + // wait for completion of previous frame + auto no_timeout = this->renderSemaphore->wait(vk, this->idx - 1, 150UL * 1000 * 1000); // 150ms + if (!no_timeout) throw ls::vulkan_error(VK_TIMEOUT, "vkWaitSemaphores() failed"); + + // copy swapchain image into backend source image + auto& cmdbuf = this->renderCommandBuffer.emplace(vk); + + cmdbuf.blitImage(vk, + { + barrierHelper(swapchainImage, + VK_ACCESS_NONE, + VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + ), + barrierHelper(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(), + { + barrierHelper(swapchainImage, + VK_ACCESS_TRANSFER_READ_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + ), + } + ); + + cmdbuf.submit(vk, + semaphores, nullptr, 0, + {}, this->syncSemaphore->handle(), this->idx++ + ); + + for (size_t i = 0; i < this->destinationImages.size(); i++) { + auto& destinationImage = this->destinationImages.at(i); + auto& pass = this->passes.at(i); + pass = RenderPass { + .commandBuffer = vk::CommandBuffer(vk), + .acquireSemaphore = vk::Semaphore(vk), + .postCopySemaphore = { + vk::Semaphore(vk), + vk::Semaphore(vk) + } + }; + + // acquire swapchain image + uint32_t aqImageIdx{}; + auto res = vk.df().AcquireNextImageKHR(vk.dev(), swapchain, + UINT64_MAX, pass.acquireSemaphore.handle(), + VK_NULL_HANDLE, + &aqImageIdx + ); + if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) + throw ls::vulkan_error(res, "vkAcquireNextImageKHR() failed"); + + const auto& aquiredSwapchainImage = this->info.images.at(aqImageIdx); + + // copy backend destination image into swapchain image + auto& cmdbuf = pass.commandBuffer; + + cmdbuf.blitImage(vk, + { + barrierHelper(destinationImage.handle(), + VK_ACCESS_NONE, + VK_ACCESS_TRANSFER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL + ), + barrierHelper(aquiredSwapchainImage, + VK_ACCESS_NONE, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL + ), + }, + { destinationImage.handle(), aquiredSwapchainImage }, + destinationImage.getExtent(), + { + barrierHelper(aquiredSwapchainImage, + VK_ACCESS_TRANSFER_WRITE_BIT, + VK_ACCESS_MEMORY_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + ), + } + ); + + std::vector waitSemaphores{ pass.acquireSemaphore.handle() }; + if (i) { // non-first pass + const auto& prevPass = this->passes.at(i - 1); + waitSemaphores.push_back(prevPass.postCopySemaphore.second.handle()); + } + + const std::vector signalSemaphores{ + pass.postCopySemaphore.first.handle(), + pass.postCopySemaphore.second.handle() + }; + + cmdbuf.submit(vk, + waitSemaphores, this->syncSemaphore->handle(), this->idx, + signalSemaphores, this->renderSemaphore->handle(), this->idx + ); + + // present swapchain image + const VkPresentInfoKHR presentInfo{ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = i ? nullptr : next_chain, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &pass.postCopySemaphore.first.handle(), + .swapchainCount = 1, + .pSwapchains = &swapchain, + .pImageIndices = &aqImageIdx, + }; + 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 + const auto& lastPass = this->passes.at(this->destinationImages.size() - 1); + const VkPresentInfoKHR presentInfo{ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &lastPass.postCopySemaphore.second.handle(), + .swapchainCount = 1, + .pSwapchains = &swapchain, + .pImageIndices = &imageIdx, + }; + auto res = vk.df().QueuePresentKHR(queue, &presentInfo); + if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) + throw ls::vulkan_error(res, "vkQueuePresentKHR() failed"); + + return res; } diff --git a/lsfg-vk-layer/src/context/swapchain.hpp b/lsfg-vk-layer/src/context/swapchain.hpp index 1360a69..e9eaf51 100644 --- a/lsfg-vk-layer/src/context/swapchain.hpp +++ b/lsfg-vk-layer/src/context/swapchain.hpp @@ -2,8 +2,15 @@ #include "../configuration/config.hpp" #include "lsfg-vk-backend/lsfgvk.hpp" +#include "lsfg-vk-common/helpers/pointers.hpp" +#include "lsfg-vk-common/vulkan/command_buffer.hpp" +#include "lsfg-vk-common/vulkan/image.hpp" +#include "lsfg-vk-common/vulkan/semaphore.hpp" +#include "lsfg-vk-common/vulkan/timeline_semaphore.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" +#include +#include #include #include @@ -31,12 +38,44 @@ namespace lsfgvk::layer { public: /// create a new swapchain context /// @param vk vulkan instance - /// @param profile active game profile /// @param backend lsfg-vk backend instance + /// @param profile active game profile /// @param info swapchain info Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend, - const GameConf& profile, const SwapchainInfo& info); + 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 + 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 syncSemaphore; + + std::optional renderCommandBuffer; + ls::lazy renderSemaphore; + struct RenderPass { + vk::CommandBuffer commandBuffer; + vk::Semaphore acquireSemaphore; + std::pair postCopySemaphore; + }; + std::vector passes; + + ls::R instance; + ls::owned_ptr> ctx; + size_t idx{1}; + size_t fidx{0}; // real frame index + + GameConf profile; + SwapchainInfo info; }; } diff --git a/lsfg-vk-layer/src/entrypoint.cpp b/lsfg-vk-layer/src/entrypoint.cpp index babb0ad..84888da 100644 --- a/lsfg-vk-layer/src/entrypoint.cpp +++ b/lsfg-vk-layer/src/entrypoint.cpp @@ -1,7 +1,9 @@ #include "context/instance.hpp" #include "lsfg-vk-common/helpers/errors.hpp" +#include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" +#include #include #include #include @@ -30,6 +32,7 @@ namespace { vk::VulkanInstanceFuncs funcs; std::unordered_map devices; + std::unordered_map> swapchains; }* instance_info; // create instance @@ -276,8 +279,13 @@ namespace { try { // retire old swapchain - if (info->oldSwapchain) + if (info->oldSwapchain) { + const auto& mapping = instance_info->swapchains.find(info->oldSwapchain); + if (mapping != instance_info->swapchains.end()) + instance_info->swapchains.erase(mapping); + layer_info->root.removeSwapchainContext(info->oldSwapchain); + } layer_info->root.update(); // ensure config is up to date @@ -314,6 +322,9 @@ namespace { .presentMode = newInfo.presentMode }); + instance_info->swapchains.emplace(*swapchain, + ls::R(it->second)); + return res; } catch (const ls::vulkan_error& e) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain creation:\n"; @@ -326,6 +337,51 @@ namespace { } } + VkResult myvkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* info) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" + VkResult result = VK_SUCCESS; + + // present each swapchain + for (size_t i = 0; i < info->swapchainCount; i++) { + const auto& swapchain = info->pSwapchains[i]; // NOLINT (array index) + + const auto& it = instance_info->swapchains.find(swapchain); + if (it == instance_info->swapchains.end()) + return VK_ERROR_INITIALIZATION_FAILED; + + try { + std::vector waitSemaphores; + waitSemaphores.reserve(info->waitSemaphoreCount); + + for (size_t j = 0; j < info->waitSemaphoreCount; j++) + waitSemaphores.push_back(info->pWaitSemaphores[j]); // NOLINT (array index) + + auto& context = layer_info->root.getSwapchainContext(swapchain); + result = context.present(it->second, + queue, swapchain, + const_cast(info->pNext), + info->pImageIndices[i], // NOLINT (array index) + { waitSemaphores.begin(), waitSemaphores.end() } + ); + } catch (const ls::vulkan_error& e) { + std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain presentation:\n"; + std::cerr << "- " << e.what() << '\n'; + result = e.error(); + } catch (const std::exception& e) { + std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain presentation:\n"; + std::cerr << "- " << e.what() << '\n'; + result = VK_ERROR_UNKNOWN; + } + + if (result != VK_SUCCESS && info->pResults) + info->pResults[i] = result; // NOLINT (array index) + } + + return result; +#pragma clang diagnostic pop + } + void myvkDestroySwapchainKHR( VkDevice device, VkSwapchainKHR swapchain, @@ -334,7 +390,10 @@ namespace { if (it == instance_info->devices.end()) return; - // destroy lsfg-vk swapchain + const auto& mapping = instance_info->swapchains.find(swapchain); + if (mapping != instance_info->swapchains.end()) + instance_info->swapchains.erase(mapping); + layer_info->root.removeSwapchainContext(swapchain); // destroy swapchain @@ -361,6 +420,7 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers { "vkDestroyDevice", VKPTR(myvkDestroyDevice) }, { "vkDestroyInstance", VKPTR(myvkDestroyInstance) }, { "vkCreateSwapchainKHR", VKPTR(myvkCreateSwapchainKHR) }, + { "vkQueuePresentKHR", VKPTR(myvkQueuePresentKHR) }, { "vkDestroySwapchainKHR", VKPTR(myvkDestroySwapchainKHR) } #undef VKPTR }, diff --git a/src/context.cpp b/src/context.cpp deleted file mode 100644 index 8b14152..0000000 --- a/src/context.cpp +++ /dev/null @@ -1,203 +0,0 @@ -#include "context.hpp" -#include "config/config.hpp" -#include "common/exception.hpp" -#include "extract/extract.hpp" -#include "utils/utils.hpp" -#include "hooks.hpp" -#include "layer.hpp" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, - VkExtent2D extent, const std::vector& swapchainImages) - : swapchain(swapchain), swapchainImages(swapchainImages), - extent(extent) { - if (!Config::currentConf.has_value()) - throw std::runtime_error("No configuration set"); - auto& globalConf = Config::globalConf; - auto& conf = *Config::currentConf; - - // we could take the format from the swapchain, - // but honestly this is safer. - const VkFormat format = conf.hdr - ? VK_FORMAT_R8G8B8A8_UNORM - : VK_FORMAT_R16G16B16A16_SFLOAT; - - // prepare textures for lsfg - std::array fds{}; - this->frame_0 = Mini::Image(info.device, info.physicalDevice, - extent, format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, - &fds.at(0)); - this->frame_1 = Mini::Image(info.device, info.physicalDevice, - extent, format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, - &fds.at(1)); - - std::vector outFds(conf.multiplier - 1); - for (size_t i = 0; i < (conf.multiplier - 1); ++i) - this->out_n.emplace_back(info.device, info.physicalDevice, - extent, format, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_ASPECT_COLOR_BIT, - &outFds.at(i)); - - // initialize lsfg - auto* lsfgInitialize = LSFG_3_1::initialize; - auto* lsfgCreateContext = LSFG_3_1::createContext; - auto* lsfgDeleteContext = LSFG_3_1::deleteContext; - if (conf.performance) { - lsfgInitialize = LSFG_3_1P::initialize; - lsfgCreateContext = LSFG_3_1P::createContext; - lsfgDeleteContext = LSFG_3_1P::deleteContext; - } - - setenv("DISABLE_LSFG", "1", 1); // NOLINT - - lsfgInitialize( - Utils::getDeviceUUID(info.physicalDevice), - conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1, - globalConf.no_fp16, - Extract::getShader - ); - - this->lsfgCtxId = std::shared_ptr( - new int32_t(lsfgCreateContext(fds.at(0), fds.at(1), outFds, extent, format)), - [lsfgDeleteContext = lsfgDeleteContext](const int32_t* id) { - lsfgDeleteContext(*id); - } - ); - - unsetenv("DISABLE_LSFG"); // NOLINT - - // prepare render passes - this->cmdPool = Mini::CommandPool(info.device, info.queue.first); - for (size_t i = 0; i < 8; i++) { - auto& pass = this->passInfos.at(i); - pass.renderSemaphores.resize(conf.multiplier - 1); - pass.acquireSemaphores.resize(conf.multiplier - 1); - pass.postCopyBufs.resize(conf.multiplier - 1); - pass.postCopySemaphores.resize(conf.multiplier - 1); - pass.prevPostCopySemaphores.resize(conf.multiplier - 1); - } -} - -VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue, - const std::vector& gameRenderSemaphores, uint32_t presentIdx) { - if (!Config::currentConf.has_value()) - throw std::runtime_error("No configuration set"); - auto& conf = *Config::currentConf; - - auto& pass = this->passInfos.at(this->frameIdx % 8); - - // 1. copy swapchain image to frame_0/frame_1 - int preCopySemaphoreFd{}; - pass.preCopySemaphores.at(0) = Mini::Semaphore(info.device, &preCopySemaphoreFd); - pass.preCopySemaphores.at(1) = Mini::Semaphore(info.device); - pass.preCopyBuf = Mini::CommandBuffer(info.device, this->cmdPool); - pass.preCopyBuf.begin(); - - Utils::copyImage(pass.preCopyBuf.handle(), - this->swapchainImages.at(presentIdx), - this->frameIdx % 2 == 0 ? this->frame_0.handle() : this->frame_1.handle(), - this->extent.width, this->extent.height, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - true, false); - - pass.preCopyBuf.end(); - - std::vector gameRenderSemaphores2 = gameRenderSemaphores; - if (this->frameIdx > 0) - gameRenderSemaphores2.emplace_back(this->passInfos.at((this->frameIdx - 1) % 8) - .preCopySemaphores.at(1).handle()); - pass.preCopyBuf.submit(info.queue.second, - gameRenderSemaphores2, - { pass.preCopySemaphores.at(0).handle(), - pass.preCopySemaphores.at(1).handle() }); - - // 2. render intermediary frames - std::vector renderSemaphoreFds(conf.multiplier - 1); - for (size_t i = 0; i < (conf.multiplier - 1); ++i) - pass.renderSemaphores.at(i) = Mini::Semaphore(info.device, &renderSemaphoreFds.at(i)); - - if (conf.performance) - LSFG_3_1P::presentContext(*this->lsfgCtxId, - preCopySemaphoreFd, - renderSemaphoreFds); - else - LSFG_3_1::presentContext(*this->lsfgCtxId, - preCopySemaphoreFd, - renderSemaphoreFds); - - for (size_t i = 0; i < (conf.multiplier - 1); i++) { - // 3. acquire next swapchain image - pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device); - uint32_t imageIdx{}; - auto res = Layer::ovkAcquireNextImageKHR(info.device, this->swapchain, UINT64_MAX, - pass.acquireSemaphores.at(i).handle(), VK_NULL_HANDLE, &imageIdx); - if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) - throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image"); - - // 4. copy output image to swapchain image - pass.postCopySemaphores.at(i) = Mini::Semaphore(info.device); - pass.prevPostCopySemaphores.at(i) = Mini::Semaphore(info.device); - pass.postCopyBufs.at(i) = Mini::CommandBuffer(info.device, this->cmdPool); - pass.postCopyBufs.at(i).begin(); - - Utils::copyImage(pass.postCopyBufs.at(i).handle(), - this->out_n.at(i).handle(), - this->swapchainImages.at(imageIdx), - this->extent.width, this->extent.height, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, - false, true); - - pass.postCopyBufs.at(i).end(); - pass.postCopyBufs.at(i).submit(info.queue.second, - { pass.acquireSemaphores.at(i).handle(), - pass.renderSemaphores.at(i).handle() }, - { pass.postCopySemaphores.at(i).handle(), - pass.prevPostCopySemaphores.at(i).handle() }); - - // 5. present swapchain image - std::vector waitSemaphores{ pass.postCopySemaphores.at(i).handle() }; - if (i != 0) waitSemaphores.emplace_back(pass.prevPostCopySemaphores.at(i - 1).handle()); - - const VkPresentInfoKHR presentInfo{ - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .pNext = i == 0 ? pNext : nullptr, // only set on first present - .waitSemaphoreCount = static_cast(waitSemaphores.size()), - .pWaitSemaphores = waitSemaphores.data(), - .swapchainCount = 1, - .pSwapchains = &this->swapchain, - .pImageIndices = &imageIdx, - }; - res = Layer::ovkQueuePresentKHR(queue, &presentInfo); - if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) - throw LSFG::vulkan_error(res, "Failed to present swapchain image"); - } - - // 6. present actual next frame - VkSemaphore lastPrevPostCopySemaphore = - pass.prevPostCopySemaphores.at(conf.multiplier - 1 - 1).handle(); - const VkPresentInfoKHR presentInfo{ - .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = 1, - .pWaitSemaphores = &lastPrevPostCopySemaphore, - .swapchainCount = 1, - .pSwapchains = &this->swapchain, - .pImageIndices = &presentIdx, - }; - auto res = Layer::ovkQueuePresentKHR(queue, &presentInfo); - if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) - throw LSFG::vulkan_error(res, "Failed to present swapchain image"); - - this->frameIdx++; - return res; -} diff --git a/src/hooks.cpp b/src/hooks.cpp deleted file mode 100644 index c338027..0000000 --- a/src/hooks.cpp +++ /dev/null @@ -1,312 +0,0 @@ -#include "hooks.hpp" -#include "common/exception.hpp" -#include "config/config.hpp" -#include "utils/utils.hpp" -#include "context.hpp" -#include "layer.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace Hooks; - -namespace { - - /// - /// Add extensions to the instance create info. - /// - VkResult myvkCreateInstance( - const VkInstanceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkInstance* pInstance) { - auto extensions = Utils::addExtensions( - pCreateInfo->ppEnabledExtensionNames, - pCreateInfo->enabledExtensionCount, - { - "VK_KHR_get_physical_device_properties2", - "VK_KHR_external_memory_capabilities", - "VK_KHR_external_semaphore_capabilities" - } - ); - VkInstanceCreateInfo createInfo = *pCreateInfo; - createInfo.enabledExtensionCount = static_cast(extensions.size()); - createInfo.ppEnabledExtensionNames = extensions.data(); - auto res = Layer::ovkCreateInstance(&createInfo, pAllocator, pInstance); - if (res == VK_ERROR_EXTENSION_NOT_PRESENT) - throw std::runtime_error( - "Required Vulkan instance extensions are not present." - "Your GPU driver is not supported."); - return res; - } - - /// Map of devices to related information. - std::unordered_map deviceToInfo; - - /// - /// Add extensions to the device create info. - /// (function pointers are not initialized yet) - /// - VkResult myvkCreateDevicePre( - VkPhysicalDevice physicalDevice, - const VkDeviceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDevice* pDevice) { - // add extensions - auto extensions = Utils::addExtensions( - pCreateInfo->ppEnabledExtensionNames, - pCreateInfo->enabledExtensionCount, - { - "VK_KHR_external_memory", - "VK_KHR_external_memory_fd", - "VK_KHR_external_semaphore", - "VK_KHR_external_semaphore_fd" - } - ); - VkDeviceCreateInfo createInfo = *pCreateInfo; - createInfo.enabledExtensionCount = static_cast(extensions.size()); - createInfo.ppEnabledExtensionNames = extensions.data(); - auto res = Layer::ovkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice); - if (res == VK_ERROR_EXTENSION_NOT_PRESENT) - throw std::runtime_error( - "Required Vulkan device extensions are not present." - "Your GPU driver is not supported."); - return res; - } - - /// - /// Add related device information after the device is created. - /// - VkResult myvkCreateDevicePost( - VkPhysicalDevice physicalDevice, - VkDeviceCreateInfo* pCreateInfo, - const VkAllocationCallbacks*, - VkDevice* pDevice) { - deviceToInfo.emplace(*pDevice, DeviceInfo { - .device = *pDevice, - .physicalDevice = physicalDevice, - .queue = Utils::findQueue(*pDevice, physicalDevice, pCreateInfo, VK_QUEUE_GRAPHICS_BIT) - }); - return VK_SUCCESS; - } - - /// Erase the device information when the device is destroyed. - void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) noexcept { - deviceToInfo.erase(device); - Layer::ovkDestroyDevice(device, pAllocator); - } - - std::unordered_map swapchains; - std::unordered_map swapchainToDeviceTable; - - /// - /// Adjust swapchain creation parameters and create a swapchain context. - /// - VkResult myvkCreateSwapchainKHR( - VkDevice device, - const VkSwapchainCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSwapchainKHR* pSwapchain) noexcept { - // retire potential old swapchain - if (pCreateInfo->oldSwapchain) { - swapchains.erase(pCreateInfo->oldSwapchain); - swapchainToDeviceTable.erase(pCreateInfo->oldSwapchain); - } - - // ensure configuration is up to date - Config::checkStatus(); - - // return early if disabled - if (!Config::currentConf.has_value() || Config::currentConf->multiplier <= 1) - return Layer::ovkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); - auto& conf = Config::currentConf; - - - // find device - auto it = deviceToInfo.find(device); - if (it == deviceToInfo.end()) { - Utils::logLimitN("swapMap", 5, "Device not found in map"); - return Layer::ovkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); - } - Utils::resetLimitN("swapMap"); - auto& deviceInfo = it->second; - - // increase amount of images in swapchain - VkSwapchainCreateInfoKHR createInfo = *pCreateInfo; - const auto maxImages = Utils::getMaxImageCount( - deviceInfo.physicalDevice, pCreateInfo->surface); - createInfo.minImageCount = createInfo.minImageCount + 1 - + static_cast(deviceInfo.queue.first); - if (createInfo.minImageCount > maxImages) { - createInfo.minImageCount = maxImages; - Utils::logLimitN("swapCount", 10, - "Requested image count (" + - std::to_string(pCreateInfo->minImageCount) + ") " - "exceeds maximum allowed (" + - std::to_string(maxImages) + "). " - "Continuing with maximum allowed image count. " - "This might lead to performance degradation."); - } else { - Utils::resetLimitN("swapCount"); - } - - // allow copy operations on swapchain images - createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - - // enforce present mode - createInfo.presentMode = conf->e_present; - - // create swapchain - auto res = Layer::ovkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain); - if (res != VK_SUCCESS) - return res; // can't be caused by lsfg-vk (yet) - - try { - // get all swapchain images - uint32_t imageCount{}; - res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr); - if (res != VK_SUCCESS || imageCount == 0) - throw LSFG::vulkan_error(res, "Failed to get swapchain image count"); - - std::vector swapchainImages(imageCount); - res = Layer::ovkGetSwapchainImagesKHR(device, *pSwapchain, - &imageCount, swapchainImages.data()); - if (res != VK_SUCCESS) - throw LSFG::vulkan_error(res, "Failed to get swapchain images"); - - // create swapchain context - swapchainToDeviceTable.emplace(*pSwapchain, device); - swapchains.emplace(*pSwapchain, LsContext( - deviceInfo, *pSwapchain, pCreateInfo->imageExtent, - swapchainImages - )); - - std::cerr << "lsfg-vk: Swapchain context " << - (createInfo.oldSwapchain ? "recreated" : "created") - << " (using " << imageCount << " images).\n"; - - Utils::resetLimitN("swapCtxCreate"); - } catch (const std::exception& e) { - Utils::logLimitN("swapCtxCreate", 5, - "An error occurred while creating the swapchain wrapper:\n" - "- " + std::string(e.what())); - return VK_SUCCESS; // swapchain is still valid - } - return VK_SUCCESS; - } - - /// - /// Update presentation parameters and present the next frame(s). - /// - VkResult myvkQueuePresentKHR( - VkQueue queue, - const VkPresentInfoKHR* pPresentInfo) noexcept { - // ensure configuration is up to date - if (!Config::checkStatus()) - return VK_ERROR_OUT_OF_DATE_KHR; - - // return early if disabled - if (!Config::currentConf.has_value() || Config::currentConf->multiplier <= 1) - return Layer::ovkQueuePresentKHR(queue, pPresentInfo); - auto& conf = Config::currentConf; - - - // find swapchain device - auto it = swapchainToDeviceTable.find(*pPresentInfo->pSwapchains); - if (it == swapchainToDeviceTable.end()) { - Utils::logLimitN("swapMap", 5, - "Swapchain not found in map"); - return Layer::ovkQueuePresentKHR(queue, pPresentInfo); - } - Utils::resetLimitN("swapMap"); - - // find device info - auto it2 = deviceToInfo.find(it->second); - if (it2 == deviceToInfo.end()) { - Utils::logLimitN("deviceMap", 5, - "Device not found in map"); - return Layer::ovkQueuePresentKHR(queue, pPresentInfo); - } - Utils::resetLimitN("deviceMap"); - auto& deviceInfo = it2->second; - - // find swapchain context - auto it3 = swapchains.find(*pPresentInfo->pSwapchains); - if (it3 == swapchains.end()) { - Utils::logLimitN("swapCtxMap", 5, - "Swapchain context not found in map"); - return Layer::ovkQueuePresentKHR(queue, pPresentInfo); - } - Utils::resetLimitN("swapCtxMap"); - auto& swapchain = it3->second; - - // enforce present mode | NOLINTBEGIN - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" - const VkSwapchainPresentModeInfoEXT* presentModeInfo = - reinterpret_cast(pPresentInfo->pNext); - while (presentModeInfo) { - if (presentModeInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_PRESENT_MODE_INFO_EXT) { - for (size_t i = 0; i < presentModeInfo->swapchainCount; i++) - const_cast(presentModeInfo->pPresentModes)[i] = - conf->e_present; - } - presentModeInfo = - reinterpret_cast(presentModeInfo->pNext); - } - #pragma clang diagnostic pop - - // NOLINTEND | present the next frame - VkResult res{}; // might return VK_SUBOPTIMAL_KHR - try { - // present the swapchain - std::vector semaphores(pPresentInfo->waitSemaphoreCount); - std::copy_n(pPresentInfo->pWaitSemaphores, semaphores.size(), semaphores.data()); - - res = swapchain.present(deviceInfo, pPresentInfo->pNext, - queue, semaphores, *pPresentInfo->pImageIndices); - - Utils::resetLimitN("swapPresent"); - } catch (const std::exception& e) { - Utils::logLimitN("swapPresent", 5, - "An error occurred while presenting the swapchain:\n" - "- " + std::string(e.what())); - return VK_ERROR_INITIALIZATION_FAILED; - } - return res; - } - - /// Erase the swapchain context and mapping when the swapchain is destroyed. - void myvkDestroySwapchainKHR( - VkDevice device, - VkSwapchainKHR swapchain, - const VkAllocationCallbacks* pAllocator) noexcept { - swapchains.erase(swapchain); - swapchainToDeviceTable.erase(swapchain); - Layer::ovkDestroySwapchainKHR(device, swapchain, pAllocator); - } -} - -std::unordered_map Hooks::hooks = { - // instance hooks - {"vkCreateInstance", reinterpret_cast(myvkCreateInstance)}, - - // device hooks - {"vkCreateDevicePre", reinterpret_cast(myvkCreateDevicePre)}, - {"vkCreateDevicePost", reinterpret_cast(myvkCreateDevicePost)}, - {"vkDestroyDevice", reinterpret_cast(myvkDestroyDevice)}, - - // swapchain hooks - {"vkCreateSwapchainKHR", reinterpret_cast(myvkCreateSwapchainKHR)}, - {"vkQueuePresentKHR", reinterpret_cast(myvkQueuePresentKHR)}, - {"vkDestroySwapchainKHR", reinterpret_cast(myvkDestroySwapchainKHR)} -};