mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-04-05 01:58:06 +00:00
refactor(cleanup): implement basic/none frame pacing
This commit is contained in:
parent
7e936784ab
commit
e0fac3e5a9
15 changed files with 476 additions and 654 deletions
|
|
@ -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 <vulkan/vulkan_core.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
///
|
||||
/// 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<VkImage>& 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<VkSemaphore>& 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<VkImage> swapchainImages;
|
||||
VkExtent2D extent;
|
||||
|
||||
std::shared_ptr<int32_t> lsfgCtxId; // lsfg context id
|
||||
Mini::Image frame_0, frame_1; // frames shared with lsfg. write to frame_0 when fc % 2 == 0
|
||||
std::vector<Mini::Image> 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<Mini::Semaphore, 2> preCopySemaphores; // signal when preCopyBuf is done
|
||||
|
||||
std::vector<Mini::Semaphore> renderSemaphores; // signal when lsfg is done with frame n
|
||||
|
||||
std::vector<Mini::Semaphore> acquireSemaphores; // signal for swapchain image n
|
||||
|
||||
std::vector<Mini::CommandBuffer> postCopyBufs; // copy from out_n to swapchain image
|
||||
std::vector<Mini::Semaphore> postCopySemaphores; // signal when postCopyBuf is done
|
||||
std::vector<Mini::Semaphore> prevPostCopySemaphores; // signal for previous postCopyBuf
|
||||
}; // data for a single render pass
|
||||
std::array<RenderPassInfo, 8> passInfos; // allocate 8 because why not
|
||||
};
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
|
||||
namespace Hooks {
|
||||
|
||||
/// Vulkan device information structure.
|
||||
struct DeviceInfo {
|
||||
VkDevice device;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
std::pair<uint32_t, VkQueue> queue; // graphics family
|
||||
};
|
||||
|
||||
/// Map of hooked Vulkan functions.
|
||||
extern std::unordered_map<std::string, PFN_vkVoidFunction> hooks;
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
#include "descriptor_set.hpp"
|
||||
#include "image.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "timeline_semaphore.hpp"
|
||||
#include "vulkan.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
|
@ -34,6 +34,18 @@ namespace vk {
|
|||
const vk::Image& image,
|
||||
const std::optional<VkClearColorValue>& 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<vk::Barrier>& preBarriers,
|
||||
std::pair<VkImage, VkImage> images, VkExtent2D extent,
|
||||
const std::vector<vk::Barrier>& 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<vk::Barrier>& 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<VkSemaphore> waitSemaphores,
|
||||
VkSemaphore waitTimelineSemaphore, uint64_t waitValue,
|
||||
std::vector<VkSemaphore> signalSemaphores,
|
||||
VkSemaphore signalTimelineSemaphore, uint64_t signalValue) const;
|
||||
|
||||
/// submit the command buffer instantly
|
||||
/// @param vk the vulkan instance
|
||||
|
|
|
|||
|
|
@ -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<int> fd = std::nullopt);
|
||||
|
||||
/// get the underlying VkSemaphore handle
|
||||
/// @return the VkSemaphore handle
|
||||
[[nodiscard]] const auto& handle() const { return *this->semaphore; }
|
||||
private:
|
||||
ls::owned_ptr<VkSemaphore> semaphore;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <cstdint>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
|
@ -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<vk::Barrier>& preBarriers,
|
||||
std::pair<VkImage, VkImage> images, VkExtent2D extent,
|
||||
const std::vector<vk::Barrier>& 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<uint32_t>(preBarriers.size()), preBarriers.data()
|
||||
);
|
||||
|
||||
const VkImageBlit region{
|
||||
.srcSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.layerCount = 1
|
||||
},
|
||||
.srcOffsets = {
|
||||
{ 0, 0, 0 },
|
||||
{ static_cast<int32_t>(extent.width),
|
||||
static_cast<int32_t>(extent.height), 1 }
|
||||
},
|
||||
.dstSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.layerCount = 1
|
||||
},
|
||||
.dstOffsets = {
|
||||
{ 0, 0, 0 },
|
||||
{ static_cast<int32_t>(extent.width),
|
||||
static_cast<int32_t>(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<uint32_t>(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<VkSemaphore> waitSemaphores,
|
||||
VkSemaphore waitTimelineSemaphore, uint64_t waitValue,
|
||||
std::vector<VkSemaphore> 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<uint64_t> waitValues(waitSemaphores.size(), 0);
|
||||
waitValues.back() = waitValue;
|
||||
|
||||
if (signalTimelineSemaphore)
|
||||
signalSemaphores.push_back(signalTimelineSemaphore);
|
||||
|
||||
std::vector<uint64_t> 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<uint32_t>(waitValues.size()),
|
||||
.pWaitSemaphoreValues = waitValues.data(),
|
||||
.signalSemaphoreValueCount = static_cast<uint32_t>(signalValues.size()),
|
||||
.pSignalSemaphoreValues = signalValues.data()
|
||||
};
|
||||
const VkPipelineStageFlags stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
std::vector<VkPipelineStageFlags> 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<uint32_t>(waitSemaphores.size()),
|
||||
.pWaitSemaphores = waitSemaphores.data(),
|
||||
.pWaitDstStageMask = stages.data(),
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &*this->commandBuffer,
|
||||
.signalSemaphoreCount = 1,
|
||||
.pSignalSemaphores = &signalSemaphore.handle()
|
||||
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
|
||||
.pSignalSemaphores = signalSemaphores.data()
|
||||
};
|
||||
res = vk.df().QueueSubmit(vk.queue(), 1, &submitInfo, VK_NULL_HANDLE);
|
||||
if (res != VK_SUCCESS)
|
||||
|
|
|
|||
|
|
@ -320,6 +320,7 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi
|
|||
.BeginCommandBuffer = dpa<PFN_vkBeginCommandBuffer>(f, d, "vkBeginCommandBuffer"),
|
||||
.EndCommandBuffer = dpa<PFN_vkEndCommandBuffer>(f, d, "vkEndCommandBuffer"),
|
||||
.CmdPipelineBarrier = dpa<PFN_vkCmdPipelineBarrier>(f, d, "vkCmdPipelineBarrier"),
|
||||
.CmdBlitImage = dpa<PFN_vkCmdBlitImage>(f, d, "vkCmdBlitImage"),
|
||||
.CmdClearColorImage = dpa<PFN_vkCmdClearColorImage>(f, d, "vkCmdClearColorImage"),
|
||||
.CmdBindPipeline = dpa<PFN_vkCmdBindPipeline>(f, d, "vkCmdBindPipeline"),
|
||||
.CmdBindDescriptorSets = dpa<PFN_vkCmdBindDescriptorSets>(f, d, "vkCmdBindDescriptorSets"),
|
||||
|
|
@ -367,6 +368,10 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi
|
|||
dpa<PFN_vkCreateSwapchainKHR>(f, d, "vkCreateSwapchainKHR") : nullptr,
|
||||
.GetSwapchainImagesKHR = graphical ?
|
||||
dpa<PFN_vkGetSwapchainImagesKHR>(f, d, "vkGetSwapchainImagesKHR") : nullptr,
|
||||
.AcquireNextImageKHR = graphical ?
|
||||
dpa<PFN_vkAcquireNextImageKHR>(f, d, "vkAcquireNextImageKHR") : nullptr,
|
||||
.QueuePresentKHR = graphical ?
|
||||
dpa<PFN_vkQueuePresentKHR>(f, d, "vkQueuePresentKHR") : nullptr,
|
||||
.DestroySwapchainKHR = graphical ?
|
||||
dpa<PFN_vkDestroySwapchainKHR>(f, d, "vkDestroySwapchainKHR") : nullptr
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
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<int> sourceFds(2);
|
||||
std::vector<int> 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<ls::R<lsfgvk::Context>>(
|
||||
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<lsfgvk::Context>& 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<VkSemaphore>& 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<VkSwapchainPresentModeInfoEXT*>(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<VkPresentModeKHR*>(info->pPresentModes)[i] = // NOLINT
|
||||
VK_PRESENT_MODE_FIFO_KHR;
|
||||
}
|
||||
|
||||
info = reinterpret_cast<VkSwapchainPresentModeInfoEXT*>(const_cast<void*>(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<VkSemaphore> 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<VkSemaphore> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
|
@ -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<VkSemaphore>& semaphores);
|
||||
private:
|
||||
std::vector<vk::Image> sourceImages;
|
||||
std::vector<vk::Image> destinationImages;
|
||||
ls::lazy<vk::TimelineSemaphore> syncSemaphore;
|
||||
|
||||
std::optional<vk::CommandBuffer> renderCommandBuffer;
|
||||
ls::lazy<vk::TimelineSemaphore> renderSemaphore;
|
||||
struct RenderPass {
|
||||
vk::CommandBuffer commandBuffer;
|
||||
vk::Semaphore acquireSemaphore;
|
||||
std::pair<vk::Semaphore, vk::Semaphore> postCopySemaphore;
|
||||
};
|
||||
std::vector<RenderPass> passes;
|
||||
|
||||
ls::R<lsfgvk::Instance> instance;
|
||||
ls::owned_ptr<ls::R<lsfgvk::Context>> ctx;
|
||||
size_t idx{1};
|
||||
size_t fidx{0}; // real frame index
|
||||
|
||||
GameConf profile;
|
||||
SwapchainInfo info;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
|
|
@ -30,6 +32,7 @@ namespace {
|
|||
vk::VulkanInstanceFuncs funcs;
|
||||
|
||||
std::unordered_map<VkDevice, vk::Vulkan> devices;
|
||||
std::unordered_map<VkSwapchainKHR, ls::R<vk::Vulkan>> 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<vk::Vulkan>(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<VkSemaphore> 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<void*>(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
|
||||
},
|
||||
|
|
|
|||
203
src/context.cpp
203
src/context.cpp
|
|
@ -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 <vulkan/vulkan_core.h>
|
||||
#include <lsfg_3_1.hpp>
|
||||
#include <lsfg_3_1p.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
|
||||
VkExtent2D extent, const std::vector<VkImage>& 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<int, 2> 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<int> 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<int32_t>(
|
||||
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<VkSemaphore>& 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<VkSemaphore> 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<int> 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<VkSemaphore> 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<uint32_t>(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;
|
||||
}
|
||||
312
src/hooks.cpp
312
src/hooks.cpp
|
|
@ -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 <vulkan/vulkan_core.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<uint32_t>(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<VkDevice, DeviceInfo> 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<uint32_t>(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<VkSwapchainKHR, LsContext> swapchains;
|
||||
std::unordered_map<VkSwapchainKHR, VkDevice> 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<uint32_t>(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<VkImage> 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<const VkSwapchainPresentModeInfoEXT*>(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<VkPresentModeKHR*>(presentModeInfo->pPresentModes)[i] =
|
||||
conf->e_present;
|
||||
}
|
||||
presentModeInfo =
|
||||
reinterpret_cast<const VkSwapchainPresentModeInfoEXT*>(presentModeInfo->pNext);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// NOLINTEND | present the next frame
|
||||
VkResult res{}; // might return VK_SUBOPTIMAL_KHR
|
||||
try {
|
||||
// present the swapchain
|
||||
std::vector<VkSemaphore> 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<std::string, PFN_vkVoidFunction> Hooks::hooks = {
|
||||
// instance hooks
|
||||
{"vkCreateInstance", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateInstance)},
|
||||
|
||||
// device hooks
|
||||
{"vkCreateDevicePre", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateDevicePre)},
|
||||
{"vkCreateDevicePost", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateDevicePost)},
|
||||
{"vkDestroyDevice", reinterpret_cast<PFN_vkVoidFunction>(myvkDestroyDevice)},
|
||||
|
||||
// swapchain hooks
|
||||
{"vkCreateSwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(myvkCreateSwapchainKHR)},
|
||||
{"vkQueuePresentKHR", reinterpret_cast<PFN_vkVoidFunction>(myvkQueuePresentKHR)},
|
||||
{"vkDestroySwapchainKHR", reinterpret_cast<PFN_vkVoidFunction>(myvkDestroySwapchainKHR)}
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue