refactor hooks

This commit is contained in:
PancakeTAS 2025-07-03 08:01:36 +02:00
parent 990f45f06d
commit d5cfab75d7
No known key found for this signature in database
15 changed files with 568 additions and 746 deletions

View file

@ -44,6 +44,7 @@ target_compile_options(lsfg-vk PRIVATE
-Wno-switch-default # ignore missing default
-Wno-padded # ignore automatic padding
-Wno-exit-time-destructors # allow globals
-Wno-global-constructors # allow globals
# required for vulkan
-Wno-cast-function-type-strict
)

View file

@ -1,171 +0,0 @@
#ifndef APPLICATION_HPP
#define APPLICATION_HPP
#include "mini/commandbuffer.hpp"
#include "mini/commandpool.hpp"
#include "mini/image.hpp"
#include "mini/semaphore.hpp"
#include <array>
#include <optional>
#include <unordered_map>
#include <vector>
#include <vulkan/vulkan_core.h>
class SwapchainContext;
///
/// Main application class, wrapping around the Vulkan device.
///
class Application {
public:
///
/// Create the application.
///
/// @param device Vulkan device
/// @param physicalDevice Vulkan physical device
/// @param graphicsQueue Vulkan queue for graphics operations
/// @param graphicsQueueFamilyIndex The family index of the graphics queue
///
/// @throws LSFG::vulkan_error if any Vulkan call fails.
///
Application(VkDevice device, VkPhysicalDevice physicalDevice,
VkQueue graphicsQueue, uint32_t graphicsQueueFamilyIndex);
///
/// Add a swapchain to the application.
///
/// @param handle The Vulkan handle of the swapchain.
/// @param format The format of the swapchain.
/// @param extent The extent of the images.
/// @param images The swapchain images.
///
/// @throws LSFG::vulkan_error if any Vulkan call fails.
///
void addSwapchain(VkSwapchainKHR handle, VkFormat format, VkExtent2D extent,
const std::vector<VkImage>& images);
///
/// Present the next frame on a given swapchain.
///
/// @param handle The Vulkan handle of the swapchain to present on.
/// @param queue The Vulkan queue to present the frame on.
/// @param semaphores The semaphores to wait on before presenting.
/// @param idx The index of the swapchain image to present.
/// @param pNext Pointer to the next structure in a chain, if any.
///
/// @throws LSFG::vulkan_error if any Vulkan call fails.
///
void presentSwapchain(VkSwapchainKHR handle, VkQueue queue,
const std::vector<VkSemaphore>& semaphores, uint32_t idx, const void* pNext);
///
/// Remove a swapchain from the application.
///
/// @param handle The Vulkan handle of the swapchain state to remove.
/// @return true if the swapchain was removed, false if it was already retired.
///
bool removeSwapchain(VkSwapchainKHR handle);
/// Get the Vulkan device.
[[nodiscard]] VkDevice getDevice() const { return this->device; }
/// Get the Vulkan physical device.
[[nodiscard]] VkPhysicalDevice getPhysicalDevice() const { return this->physicalDevice; }
/// Get the Vulkan graphics queue.
[[nodiscard]] VkQueue getGraphicsQueue() const { return this->graphicsQueue; }
/// Get the graphics queue family index.
[[nodiscard]] uint32_t getGraphicsQueueFamilyIndex() const { return this->graphicsQueueFamilyIndex; }
// Non-copyable and non-movable
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
Application(Application&&) = delete;
Application& operator=(Application&&) = delete;
/// Destructor, cleans up resources.
~Application();
private:
// (non-owned resources)
VkDevice device;
VkPhysicalDevice physicalDevice;
VkQueue graphicsQueue;
uint32_t graphicsQueueFamilyIndex;
// (owned resources)
std::unordered_map<VkSwapchainKHR, SwapchainContext> swapchains;
};
///
/// An application's Vulkan swapchain and it's associated resources.
///
class SwapchainContext {
public:
///
/// Create the swapchain context.
///
/// @param app The application context to use.
/// @param swapchain The Vulkan swapchain handle.
/// @param format The format of the swapchain images.
/// @param extent The extent of the swapchain images.
/// @param images The swapchain images.
///
/// @throws LSFG::vulkan_error if any Vulkan call fails.
///
SwapchainContext(const Application& app, VkSwapchainKHR swapchain,
VkFormat format, VkExtent2D extent, const std::vector<VkImage>& images);
///
/// Present the next frame
///
/// @param app The application context to use
/// @param queue The Vulkan queue to present the frame on.
/// @param semaphores The semaphores to wait on before presenting.
/// @param idx The index of the swapchain image to present.
/// @param pNext Pointer to the next structure in a chain, if any.
///
/// @throws LSFG::vulkan_error if any Vulkan call fails.
///
void present(const Application& app, VkQueue queue,
const std::vector<VkSemaphore>& semaphores, uint32_t idx, const void* pNext);
/// Get the Vulkan swapchain handle.
[[nodiscard]] VkSwapchainKHR handle() const { return this->swapchain; }
/// Get the format of the swapchain images.
[[nodiscard]] VkFormat getFormat() const { return this->format; }
/// Get the extent of the swapchain images.
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
/// Get the swapchain images.
[[nodiscard]] const std::vector<VkImage>& getImages() const { return this->images; }
// Non-copyable, trivially moveable and destructible
SwapchainContext(const SwapchainContext&) = delete;
SwapchainContext& operator=(const SwapchainContext&) = delete;
SwapchainContext(SwapchainContext&&) = default;
SwapchainContext& operator=(SwapchainContext&&) = default;
~SwapchainContext() = default;
private:
// (non-owned resources)
VkSwapchainKHR swapchain;
VkFormat format;
VkExtent2D extent;
std::vector<VkImage> images;
// (owned resources)
Mini::CommandPool cmdPool;
std::array<Mini::CommandBuffer, 8> cmdBufs1;
std::array<std::vector<Mini::CommandBuffer>, 8> cmdBufs2;
std::array<Mini::Semaphore, 8> copySemaphores1; // copy current swap to frame
std::array<Mini::Semaphore, 8> copySemaphores2; // (for present)
std::array<std::vector<Mini::Semaphore>, 8> acquireSemaphores; // acquire new swapchain image
std::array<std::vector<Mini::Semaphore>, 8> renderSemaphores; // fg is done
std::array<std::vector<Mini::Semaphore>, 8> prevPresentSemaphores; // for inorder fg
std::array<std::vector<Mini::Semaphore>, 8> presentSemaphores; // copy is done, ready to present
Mini::Image frame_0, frame_1;
std::vector<Mini::Image> outImgs;
std::shared_ptr<int32_t> lsfgId;
uint64_t frameIdx{0};
std::optional<uint32_t> deferredIdx; // index of the frame to present next
};
#endif // APPLICATION_HPP

83
include/context.hpp Normal file
View file

@ -0,0 +1,83 @@
#ifndef CONTEXT_HPP
#define CONTEXT_HPP
#include "hooks.hpp"
#include "mini/commandbuffer.hpp"
#include "mini/commandpool.hpp"
#include "mini/image.hpp"
#include "mini/semaphore.hpp"
#include <array>
#include <cstdint>
#include <memory>
#include <vulkan/vulkan_core.h>
#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
};
#endif // CONTEXT_HPP

View file

@ -1,8 +1,20 @@
#ifndef HOOKS_HPP
#define HOOKS_HPP
#include <vulkan/vulkan_core.h>
#include <utility>
namespace Hooks {
/// Vulkan device information structure.
struct DeviceInfo {
VkDevice device;
VkPhysicalDevice physicalDevice;
std::pair<uint32_t, VkQueue> queue; // graphics family
uint64_t frameGen; // amount of frames to generate
};
///
/// Install overrides for hooked Vulkan functions.
///

View file

@ -36,8 +36,6 @@ namespace Mini {
[[nodiscard]] auto handle() const { return *this->image; }
/// Get the Vulkan device memory handle.
[[nodiscard]] auto getMemory() const { return *this->memory; }
/// Get the Vulkan image view handle.
[[nodiscard]] auto getView() const { return *this->view; }
/// Get the extent of the image.
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
/// Get the format of the image.
@ -45,11 +43,6 @@ namespace Mini {
/// Get the aspect flags of the image.
[[nodiscard]] VkImageAspectFlags getAspectFlags() const { return this->aspectFlags; }
/// Set the layout of the image.
void setLayout(VkImageLayout layout) { *this->layout = layout; }
/// Get the current layout of the image.
[[nodiscard]] VkImageLayout getLayout() const { return *this->layout; }
/// Trivially copyable, moveable and destructible
Image(const Image&) noexcept = default;
Image& operator=(const Image&) noexcept = default;
@ -59,9 +52,6 @@ namespace Mini {
private:
std::shared_ptr<VkImage> image;
std::shared_ptr<VkDeviceMemory> memory;
std::shared_ptr<VkImageView> view;
std::shared_ptr<VkImageLayout> layout;
VkExtent2D extent{};
VkFormat format{};

55
include/utils.hpp Normal file
View file

@ -0,0 +1,55 @@
#ifndef UTILS_HPP
#define UTILS_HPP
#include <vector>
#include <vulkan/vulkan_core.h>
#include <utility>
namespace Utils {
///
/// Find a queue in the physical device that supports the given queue flags.
///
/// @param device The Vulkan device to use for queue retrieval.
/// @param physicalDevice The physical device to search in.
/// @param desc The device creation info, used to determine enabled queue families.
/// @param flags The queue flags to search for (e.g., VK_QUEUE_GRAPHICS_BIT).
/// @return Pair of queue family index and queue handle.
///
/// @throws LSFG::vulkan_error if no suitable queue is found.
///
std::pair<uint32_t, VkQueue> findQueue(VkDevice device, VkPhysicalDevice physicalDevice,
VkDeviceCreateInfo* desc, VkQueueFlags flags);
///
/// Ensure a list of extensions is present in the given array.
///
/// @param extensions The array of extensions to check.
/// @param requiredExtensions The list of required extensions to ensure are present.
///
std::vector<const char*> addExtensions(const char* const* extensions, size_t count,
const std::vector<const char*>& requiredExtensions);
///
/// Copy an image from source to destination in a command buffer.
///
/// @param buf The command buffer to record the copy operation into.
/// @param src The source image to copy from.
/// @param dst The destination image to copy to.
/// @param width The width of the image to copy.
/// @param height The height of the image to copy.
/// @param pre The pipeline stage to wait on.
/// @param post The pipeline stage to provide after the copy.
/// @param makeSrcPresentable If true, the source image will be made presentable after the copy.
/// @param makeDstPresentable If true, the destination image will be made presentable after the copy.
///
void copyImage(VkCommandBuffer buf,
VkImage src, VkImage dst,
uint32_t width, uint32_t height,
VkPipelineStageFlags pre, VkPipelineStageFlags post,
bool makeSrcPresentable, bool makeDstPresentable);
}
#endif // UTILS_HPP

View file

@ -5,6 +5,7 @@
#include <cassert>
#include <format>
#include <optional>
#include <vulkan/vulkan_core.h>
using namespace LSFG;

View file

@ -1,346 +0,0 @@
#include "application.hpp"
#include "mini/commandbuffer.hpp"
#include "mini/commandpool.hpp"
#include "mini/image.hpp"
#include "mini/semaphore.hpp"
#include <lsfg.hpp>
#include <memory>
#include <vector>
#include <vulkan/vulkan_core.h>
const int FRAMEGEN_N = 2; // number of frames to generate
Application::Application(VkDevice device, VkPhysicalDevice physicalDevice,
VkQueue graphicsQueue, uint32_t graphicsQueueFamilyIndex)
: device(device), physicalDevice(physicalDevice),
graphicsQueue(graphicsQueue), graphicsQueueFamilyIndex(graphicsQueueFamilyIndex) {
LSFG::initialize();
}
void Application::addSwapchain(VkSwapchainKHR handle, VkFormat format, VkExtent2D extent,
const std::vector<VkImage>& images) {
auto it = this->swapchains.find(handle);
if (it != this->swapchains.end())
return; // already added
this->swapchains.emplace(handle, SwapchainContext(*this, handle, format, extent, images));
}
SwapchainContext::SwapchainContext(const Application& app, VkSwapchainKHR swapchain,
VkFormat format, VkExtent2D extent, const std::vector<VkImage>& images)
: swapchain(swapchain), format(format), extent(extent), images(images) {
this->cmdPool = Mini::CommandPool(app.getDevice(), app.getGraphicsQueueFamilyIndex());
int frame0fd{};
this->frame_0 = Mini::Image(
app.getDevice(), app.getPhysicalDevice(),
extent, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, &frame0fd
);
int frame1fd{};
this->frame_1 = Mini::Image(
app.getDevice(), app.getPhysicalDevice(),
extent, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, &frame1fd
);
std::vector<int> outFds(FRAMEGEN_N);
for (size_t i = 0; i < FRAMEGEN_N; ++i) {
this->outImgs.emplace_back(
app.getDevice(), app.getPhysicalDevice(),
extent, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT, &outFds.at(i)
);
}
for (size_t i = 0; i < 8; i++) {
this->cmdBufs2.at(i).resize(FRAMEGEN_N);
this->acquireSemaphores.at(i).resize(FRAMEGEN_N);
this->renderSemaphores.at(i).resize(FRAMEGEN_N);
this->prevPresentSemaphores.at(i).resize(FRAMEGEN_N);
this->presentSemaphores.at(i).resize(FRAMEGEN_N);
}
auto id = LSFG::createContext(extent.width, extent.height, frame0fd, frame1fd, outFds);
this->lsfgId = std::shared_ptr<int32_t>(
new int32_t(id),
[](const int32_t* id) {
LSFG::deleteContext(*id);
}
);
}
void Application::presentSwapchain(VkSwapchainKHR handle, VkQueue queue,
const std::vector<VkSemaphore>& semaphores, uint32_t idx, const void* pNext) {
auto it = this->swapchains.find(handle);
if (it == this->swapchains.end())
throw std::logic_error("Swapchain not found");
it->second.present(*this, queue, semaphores, idx, pNext);
}
void SwapchainContext::present(const Application& app, VkQueue queue,
const std::vector<VkSemaphore>& semaphores, uint32_t idx, const void* pNext) {
// present deferred frame if any
bool yes = this->deferredIdx.has_value();
if (this->deferredIdx.has_value()) {
VkSemaphore deferredSemaphore = this->copySemaphores2.at((this->frameIdx - 1) % 8).handle();
const uint32_t deferredIdx = this->deferredIdx.value();
const VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &deferredSemaphore,
.swapchainCount = 1,
.pSwapchains = &this->swapchain,
.pImageIndices = &deferredIdx,
};
auto res = vkQueuePresentKHR(queue, &presentInfo);
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
throw LSFG::vulkan_error(res, "Failed to present deferred swapchain image");
}
this->deferredIdx.emplace(idx);
// create semaphores
int copySem{};
Mini::Semaphore& copySemaphore1 = this->copySemaphores1.at(this->frameIdx % 8);
copySemaphore1 = Mini::Semaphore(app.getDevice(), &copySem);
Mini::Semaphore& copySemaphore2 = this->copySemaphores2.at(this->frameIdx % 8);
copySemaphore2 = Mini::Semaphore(app.getDevice());
// copy swapchain image to next frame image
auto& cmdBuf1 = this->cmdBufs1.at(this->frameIdx % 8);
cmdBuf1 = Mini::CommandBuffer(app.getDevice(), this->cmdPool);
cmdBuf1.begin();
auto& srcImage1 = this->images.at(idx);
auto& dstImage1 = (this->frameIdx % 2 == 0) ? this->frame_0 : this->frame_1;
const VkImageMemoryBarrier srcBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.image = srcImage1,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const VkImageMemoryBarrier dstBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.image = dstImage1.handle(),
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const std::vector<VkImageMemoryBarrier> barriers = { srcBarrier, dstBarrier };
vkCmdPipelineBarrier(cmdBuf1.handle(),
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr, 2, barriers.data());
const VkImageCopy imageCopy{
.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1
},
.dstSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1
},
.extent = {
.width = this->extent.width,
.height = this->extent.height,
.depth = 1
}
};
vkCmdCopyImage(cmdBuf1.handle(),
srcImage1, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dstImage1.handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &imageCopy);
const VkImageMemoryBarrier presentBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = dstImage1.handle(),
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
vkCmdPipelineBarrier(cmdBuf1.handle(),
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &presentBarrier);
cmdBuf1.end();
std::vector<VkSemaphore> semaphores2 = semaphores;
if (yes) {
VkSemaphore prevPresentSemaphore = this->prevPresentSemaphores.at((this->frameIdx - 1) % 8).at(FRAMEGEN_N - 1).handle();
semaphores2.emplace_back(prevPresentSemaphore);
}
cmdBuf1.submit(app.getGraphicsQueue(),
semaphores2, { copySemaphore1.handle(), copySemaphore2.handle() });
// render the intermediary frames
std::vector<int> renderSems(FRAMEGEN_N);
auto& renderSemaphores = this->renderSemaphores.at(this->frameIdx % 8);
for (size_t i = 0; i < FRAMEGEN_N; i++)
renderSemaphores.at(i) = Mini::Semaphore(app.getDevice(), &renderSems.at(i));
LSFG::presentContext(*this->lsfgId, copySem, renderSems);
auto& acquireSemaphores = this->acquireSemaphores.at(this->frameIdx % 8);
auto& prevPresentSemaphores = this->prevPresentSemaphores.at(this->frameIdx % 8);
auto& presentSemaphores = this->presentSemaphores.at(this->frameIdx % 8);
auto& cmdBufs2 = this->cmdBufs2.at(this->frameIdx % 8);
for (size_t i = 0; i < FRAMEGEN_N; i++) {
// acquire the next swapchain image
auto& acquireSemaphore = acquireSemaphores.at(i);
acquireSemaphore = Mini::Semaphore(app.getDevice());
uint32_t newIdx{};
auto res = vkAcquireNextImageKHR(app.getDevice(), this->swapchain, UINT64_MAX,
acquireSemaphore.handle(), VK_NULL_HANDLE, &newIdx);
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image");
// copy the output image to the swapchain image
auto& presentSemaphore = presentSemaphores.at(i);
presentSemaphore = Mini::Semaphore(app.getDevice());
auto& cmdBuf2 = cmdBufs2.at(i);
cmdBuf2 = Mini::CommandBuffer(app.getDevice(), this->cmdPool);
cmdBuf2.begin();
auto& srcImage2 = this->outImgs.at(i);
auto& dstImage2 = this->images.at(newIdx);
const VkImageMemoryBarrier srcBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.image = srcImage2.handle(),
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const VkImageMemoryBarrier dstBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.image = dstImage2,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const std::vector<VkImageMemoryBarrier> barriers2 = { srcBarrier2, dstBarrier2 };
vkCmdPipelineBarrier(cmdBuf2.handle(),
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr, 2, barriers2.data());
const VkImageCopy imageCopy2{
.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1
},
.dstSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1
},
.extent = {
.width = this->extent.width,
.height = this->extent.height,
.depth = 1
}
};
vkCmdCopyImage(cmdBuf2.handle(),
srcImage2.handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dstImage2, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &imageCopy2);
const VkImageMemoryBarrier presentBarrier2{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = dstImage2,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
vkCmdPipelineBarrier(cmdBuf2.handle(),
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &presentBarrier2);
cmdBuf2.end();
std::vector<VkSemaphore> signalSemaphores;
signalSemaphores.emplace_back(presentSemaphore.handle());
// signal next present semaphore
prevPresentSemaphores.at(i) = Mini::Semaphore(app.getDevice());
signalSemaphores.emplace_back(prevPresentSemaphores.at(i).handle());
auto& renderSemaphore = renderSemaphores.at(i);
cmdBuf2.submit(app.getGraphicsQueue(),
{ acquireSemaphore.handle(), renderSemaphore.handle() },
signalSemaphores);
// present the swapchain image
std::vector<VkSemaphore> waitSemaphores;
waitSemaphores.emplace_back(presentSemaphore.handle());
if (i != 0) // wait for previous present semaphore
waitSemaphores.emplace_back(prevPresentSemaphores.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 = &newIdx,
};
res = vkQueuePresentKHR(queue, &presentInfo);
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) // FIXME: somehow return VK_SUBOPTIMAL_KHR
throw LSFG::vulkan_error(res, "Failed to present swapchain");
}
this->frameIdx++;
}
bool Application::removeSwapchain(VkSwapchainKHR handle) {
auto it = this->swapchains.find(handle);
if (it == this->swapchains.end())
return false; // already retired... hopefully
this->swapchains.erase(it);
return true;
}
Application::~Application() {
this->swapchains.clear();
LSFG::finalize();
}

155
src/context.cpp Normal file
View file

@ -0,0 +1,155 @@
#include "context.hpp"
#include "utils.hpp"
#include <lsfg.hpp>
#include <vector>
#include <vulkan/vulkan_core.h>
LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
VkExtent2D extent, const std::vector<VkImage>& swapchainImages)
: swapchain(swapchain), swapchainImages(swapchainImages),
extent(extent) {
// initialize lsfg
int frame_0_fd{};
this->frame_0 = Mini::Image(
info.device, info.physicalDevice,
extent, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT,
&frame_0_fd);
int frame_1_fd{};
this->frame_1 = Mini::Image(
info.device, info.physicalDevice,
extent, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_TRANSFER_DST_BIT,
VK_IMAGE_ASPECT_COLOR_BIT,
&frame_1_fd);
std::vector<int> out_n_fds(info.frameGen);
for (size_t i = 0; i < info.frameGen; ++i)
this->out_n.emplace_back(
info.device, info.physicalDevice,
extent, VK_FORMAT_R8G8B8A8_UNORM,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_IMAGE_ASPECT_COLOR_BIT,
&out_n_fds.at(i));
this->lsfgCtxId = std::shared_ptr<int32_t>(
new int32_t(LSFG::createContext(extent.width, extent.height,
frame_0_fd, frame_1_fd, out_n_fds)),
[](const int32_t* id) {
LSFG::deleteContext(*id);
}
);
// 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(info.frameGen);
pass.acquireSemaphores.resize(info.frameGen);
pass.postCopyBufs.resize(info.frameGen);
pass.postCopySemaphores.resize(info.frameGen);
pass.prevPostCopySemaphores.resize(info.frameGen);
}
}
VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue,
const std::vector<VkSemaphore>& gameRenderSemaphores, uint32_t presentIdx) {
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();
pass.preCopyBuf.submit(info.queue.second,
gameRenderSemaphores,
{ pass.preCopySemaphores.at(0).handle() });
// 2. render intermediary frames
std::vector<int> renderSemaphoreFds(info.frameGen);
for (size_t i = 0; i < info.frameGen; ++i)
pass.renderSemaphores.at(i) = Mini::Semaphore(info.device, &renderSemaphoreFds.at(i));
LSFG::presentContext(*this->lsfgCtxId,
preCopySemaphoreFd,
renderSemaphoreFds);
for (size_t i = 0; i < info.frameGen; i++) {
// 3. acquire next swapchain image
pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device);
uint32_t imageIdx{};
auto res = vkAcquireNextImageKHR(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 = vkQueuePresentKHR(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(info.frameGen - 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 = vkQueuePresentKHR(queue, &presentInfo);
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR)
throw LSFG::vulkan_error(res, "Failed to present swapchain image");
this->frameIdx++;
return res;
}

View file

@ -1,39 +1,35 @@
#include "hooks.hpp"
#include "loader/dl.hpp"
#include "loader/vk.hpp"
#include "application.hpp"
#include "context.hpp"
#include "hooks.hpp"
#include "log.hpp"
#include "utils.hpp"
#include <iostream>
#include <lsfg.hpp>
#include <optional>
#include <vulkan/vulkan_core.h>
#include <string>
#include <unordered_map>
using namespace Hooks;
namespace {
bool initialized{false};
std::optional<Application> application;
// instance hooks
VkResult myvkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance) {
// add extensions
std::vector<const char*> extensions(pCreateInfo->enabledExtensionCount);
std::copy_n(pCreateInfo->ppEnabledExtensionNames, extensions.size(), extensions.data());
// create lsfg
LSFG::initialize();
const std::vector<const char*> requiredExtensions = {
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME
};
for (const auto& ext : requiredExtensions) {
auto it = std::ranges::find(extensions, ext);
if (it == extensions.end())
extensions.push_back(ext);
}
// add extensions
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());
@ -41,108 +37,87 @@ namespace {
return vkCreateInstance(&createInfo, pAllocator, pInstance);
}
void myvkDestroyInstance(
VkInstance instance,
const VkAllocationCallbacks* pAllocator) {
LSFG::finalize(); // destroy lsfg
vkDestroyInstance(instance, pAllocator);
}
// device hooks
std::unordered_map<VkDevice, DeviceInfo> devices;
VkResult myvkCreateDevice(
VkPhysicalDevice physicalDevice,
const VkDeviceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDevice* pDevice) {
// add extensions
std::vector<const char*> extensions(pCreateInfo->enabledExtensionCount);
std::copy_n(pCreateInfo->ppEnabledExtensionNames, extensions.size(), extensions.data());
const std::vector<const char*> requiredExtensions = {
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME
};
for (const auto& ext : requiredExtensions) {
auto it = std::ranges::find(extensions, ext);
if (it == extensions.end())
extensions.push_back(ext);
}
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 = vkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice);
// extract graphics and present queues
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos(pCreateInfo->queueCreateInfoCount);
std::copy_n(pCreateInfo->pQueueCreateInfos, queueCreateInfos.size(), queueCreateInfos.data());
uint32_t familyCount{};
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr);
std::vector<VkQueueFamilyProperties> families(familyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, families.data());
std::optional<uint32_t> graphicsFamilyIdx;
for (uint32_t i = 0; i < families.size(); ++i) {
auto it = std::ranges::find_if(queueCreateInfos,
[i](const VkDeviceQueueCreateInfo& info) {
return info.queueFamilyIndex == i;
}) ;
if (it == queueCreateInfos.end())
continue; // skip if this family is not used by the device
if (families.at(i).queueFlags & VK_QUEUE_GRAPHICS_BIT)
graphicsFamilyIdx.emplace(i);
}
if (!graphicsFamilyIdx.has_value()) {
Log::error("No suitable queue family found for graphics or present");
exit(EXIT_FAILURE);
}
VkQueue graphicsQueue{};
vkGetDeviceQueue(*pDevice, *graphicsFamilyIdx, 0, &graphicsQueue);
// create the main application
if (application.has_value()) {
Log::error("Application already initialized, are you trying to create a second device?");
exit(EXIT_FAILURE);
}
// store device info
try {
application.emplace(*pDevice, physicalDevice, graphicsQueue, *graphicsFamilyIdx);
Log::info("lsfg-vk(hooks): Application created successfully");
} catch (const LSFG::vulkan_error& e) {
Log::error("Encountered Vulkan error {:x} while creating application: {}",
static_cast<uint32_t>(e.error()), e.what());
exit(EXIT_FAILURE);
const char* frameGen = std::getenv("LSFG_MULTIPLIER");
if (!frameGen) frameGen = "1";
devices.emplace(*pDevice, DeviceInfo {
.device = *pDevice,
.physicalDevice = physicalDevice,
.queue = Utils::findQueue(*pDevice, physicalDevice, &createInfo,
VK_QUEUE_GRAPHICS_BIT),
.frameGen = std::stoul(frameGen)
});
} catch (const std::exception& e) {
Log::error("Encountered error while creating application: {}", e.what());
exit(EXIT_FAILURE);
Log::error("Failed to create device info: {}", e.what());
return VK_ERROR_INITIALIZATION_FAILED;
}
return res;
}
void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator) {
devices.erase(device); // erase device info
vkDestroyDevice(device, pAllocator);
}
// swapchain hooks
std::unordered_map<VkSwapchainKHR, LsContext> swapchains;
std::unordered_map<VkSwapchainKHR, VkDevice> swapchainToDeviceTable;
VkResult myvkCreateSwapchainKHR(
VkDevice device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSwapchainKHR* pSwapchain) {
VkSwapchainCreateInfoKHR createInfo = *pCreateInfo;
createInfo.minImageCount += 3;
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
auto res = vkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
auto& deviceInfo = devices.at(device);
// add the swapchain to the application
if (!application.has_value()) {
Log::error("Application not initialized, cannot create swapchain");
exit(EXIT_FAILURE);
// update swapchain create info
VkSwapchainCreateInfoKHR createInfo = *pCreateInfo;
createInfo.minImageCount += 1 + deviceInfo.frameGen; // 1 deferred + N framegen, FIXME: check hardware max
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; // allow copy from/to images
createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // force vsync
auto res = vkCreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
if (res != VK_SUCCESS) {
Log::error("Failed to create swapchain: {:x}", static_cast<uint32_t>(res));
return res;
}
try {
if (pCreateInfo->oldSwapchain) {
if (!application->removeSwapchain(pCreateInfo->oldSwapchain))
throw std::runtime_error("Failed to remove old swapchain");
Log::info("lsfg-vk(hooks): Swapchain retired successfully");
}
// get swapchain images
uint32_t imageCount{};
auto res = vkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
res = vkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
if (res != VK_SUCCESS || imageCount == 0)
throw LSFG::vulkan_error(res, "Failed to get swapchain images count");
@ -151,17 +126,20 @@ namespace {
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to get swapchain images");
application->addSwapchain(*pSwapchain,
pCreateInfo->imageFormat, pCreateInfo->imageExtent, swapchainImages);
Log::info("lsfg-vk(hooks): Swapchain created successfully with {} images",
swapchainImages.size());
// create swapchain context
swapchains.emplace(*pSwapchain, LsContext(
deviceInfo, *pSwapchain, pCreateInfo->imageExtent,
swapchainImages
));
swapchainToDeviceTable.emplace(*pSwapchain, device);
} catch (const LSFG::vulkan_error& e) {
Log::error("Encountered Vulkan error {:x} while creating swapchain: {}",
static_cast<uint32_t>(e.error()), e.what());
exit(EXIT_FAILURE);
return e.error();
} catch (const std::exception& e) {
Log::error("Encountered error while creating swapchain: {}", e.what());
exit(EXIT_FAILURE);
return VK_ERROR_INITIALIZATION_FAILED;
}
return res;
@ -170,67 +148,36 @@ namespace {
VkResult myvkQueuePresentKHR(
VkQueue queue,
const VkPresentInfoKHR* pPresentInfo) {
if (!application.has_value()) {
Log::error("Application not initialized, cannot present frame");
return VK_ERROR_INITIALIZATION_FAILED;
}
auto& deviceInfo = devices.at(swapchainToDeviceTable.at(*pPresentInfo->pSwapchains));
auto& swapchain = swapchains.at(*pPresentInfo->pSwapchains);
// present the next frame
try {
std::vector<VkSemaphore> waitSemaphores(pPresentInfo->waitSemaphoreCount);
std::copy_n(pPresentInfo->pWaitSemaphores, waitSemaphores.size(), waitSemaphores.data());
application->presentSwapchain(*pPresentInfo->pSwapchains,
queue, waitSemaphores, *pPresentInfo->pImageIndices, pPresentInfo->pNext);
Log::info("lsfg-vk(hooks): Frame presented successfully");
// present the next frame
return swapchain.present(deviceInfo, pPresentInfo->pNext,
queue, waitSemaphores, *pPresentInfo->pImageIndices);
} catch (const LSFG::vulkan_error& e) {
Log::error("Encountered Vulkan error {:x} while presenting: {}",
static_cast<uint32_t>(e.error()), e.what());
return e.error(); // do not exit
return e.error();
} catch (const std::exception& e) {
Log::error("Encountered error while creating presenting: {}", e.what());
exit(EXIT_FAILURE);
return VK_ERROR_INITIALIZATION_FAILED;
}
return VK_SUCCESS;
}
void myvkDestroySwapchainKHR(
VkDevice device,
VkSwapchainKHR swapchain,
const VkAllocationCallbacks* pAllocator) {
if (!application.has_value()) {
Log::error("Application not initialized, cannot destroy swapchain");
exit(EXIT_FAILURE);
}
// remove the swapchain from the application
try {
if (application->removeSwapchain(swapchain))
Log::info("lsfg-vk(hooks): Swapchain retired successfully");
} catch (const std::exception& e) {
Log::error("Encountered error while removing swapchain: {}", e.what());
exit(EXIT_FAILURE);
}
swapchains.erase(swapchain); // erase swapchain context
swapchainToDeviceTable.erase(swapchain);
vkDestroySwapchainKHR(device, swapchain, pAllocator);
}
void myvkDestroyDevice(
VkDevice device,
const VkAllocationCallbacks* pAllocator) {
// destroy the main application
if (application.has_value()) {
application.reset();
Log::info("lsfg-vk(hooks): Application destroyed successfully");
} else {
Log::warn("lsfg-vk(hooks): No application to destroy, continuing");
}
vkDestroyDevice(device, pAllocator);
}
bool initialized{false};
}
void Hooks::initialize() {
@ -239,49 +186,29 @@ void Hooks::initialize() {
return;
}
// register hooks to vulkan loader
Loader::VK::registerSymbol("vkCreateInstance",
reinterpret_cast<void*>(myvkCreateInstance));
Loader::VK::registerSymbol("vkCreateDevice",
reinterpret_cast<void*>(myvkCreateDevice));
Loader::VK::registerSymbol("vkDestroyDevice",
reinterpret_cast<void*>(myvkDestroyDevice));
Loader::VK::registerSymbol("vkCreateSwapchainKHR",
reinterpret_cast<void*>(myvkCreateSwapchainKHR));
Loader::VK::registerSymbol("vkDestroySwapchainKHR",
reinterpret_cast<void*>(myvkDestroySwapchainKHR));
Loader::VK::registerSymbol("vkQueuePresentKHR",
reinterpret_cast<void*>(myvkQueuePresentKHR));
// list of hooks to register
const std::vector<std::pair<std::string, void*>> hooks = {
{ "vkCreateInstance", reinterpret_cast<void*>(myvkCreateInstance) },
{ "vkDestroyInstance", reinterpret_cast<void*>(myvkDestroyInstance) },
{ "vkCreateDevice", reinterpret_cast<void*>(myvkCreateDevice) },
{ "vkDestroyDevice", reinterpret_cast<void*>(myvkDestroyDevice) },
{ "vkCreateSwapchainKHR", reinterpret_cast<void*>(myvkCreateSwapchainKHR) },
{ "vkQueuePresentKHR", reinterpret_cast<void*>(myvkQueuePresentKHR) },
{ "vkDestroySwapchainKHR", reinterpret_cast<void*>(myvkDestroySwapchainKHR) }
};
// register hooks to dynamic loader under libvulkan.so.1
Loader::DL::File vk1("libvulkan.so.1");
vk1.defineSymbol("vkCreateInstance",
reinterpret_cast<void*>(myvkCreateInstance));
vk1.defineSymbol("vkCreateDevice",
reinterpret_cast<void*>(myvkCreateDevice));
vk1.defineSymbol("vkDestroyDevice",
reinterpret_cast<void*>(myvkDestroyDevice));
vk1.defineSymbol("vkCreateSwapchainKHR",
reinterpret_cast<void*>(myvkCreateSwapchainKHR));
vk1.defineSymbol("vkDestroySwapchainKHR",
reinterpret_cast<void*>(myvkDestroySwapchainKHR));
vk1.defineSymbol("vkQueuePresentKHR",
reinterpret_cast<void*>(myvkQueuePresentKHR));
Loader::DL::registerFile(vk1);
// register hooks to Vulkan loader
for (const auto& hook : hooks)
Loader::VK::registerSymbol(hook.first, hook.second);
// register hooks to dynamic loader under libvulkan.so
Loader::DL::File vk2("libvulkan.so");
vk2.defineSymbol("vkCreateInstance",
reinterpret_cast<void*>(myvkCreateInstance));
vk2.defineSymbol("vkCreateDevice",
reinterpret_cast<void*>(myvkCreateDevice));
vk2.defineSymbol("vkDestroyDevice",
reinterpret_cast<void*>(myvkDestroyDevice));
vk2.defineSymbol("vkCreateSwapchainKHR",
reinterpret_cast<void*>(myvkCreateSwapchainKHR));
vk2.defineSymbol("vkDestroySwapchainKHR",
reinterpret_cast<void*>(myvkDestroySwapchainKHR));
vk2.defineSymbol("vkQueuePresentKHR",
reinterpret_cast<void*>(myvkQueuePresentKHR));
Loader::DL::registerFile(vk2);
// register hooks to dynamic loader under libvulkan.so.1 and libvulkan.so
for (const char* libName : {"libvulkan.so.1", "libvulkan.so"}) {
Loader::DL::File vkLib(libName);
for (const auto& hook : hooks)
vkLib.defineSymbol(hook.first, hook.second);
Loader::DL::registerFile(vkLib);
}
initialized = true;
Log::info("Vulkan hooks initialized successfully");
}

View file

@ -1,5 +1,6 @@
#include "mini/commandbuffer.hpp"
#include "lsfg.hpp"
#include <lsfg.hpp>
using namespace Mini;

View file

@ -1,5 +1,6 @@
#include "mini/commandpool.hpp"
#include "lsfg.hpp"
#include <lsfg.hpp>
using namespace Mini;

View file

@ -1,5 +1,6 @@
#include "mini/image.hpp"
#include "lsfg.hpp"
#include <lsfg.hpp>
#include <optional>
@ -81,30 +82,6 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
// create image view
const VkImageViewCreateInfo viewDesc{
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = imageHandle,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.components = {
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
.g = VK_COMPONENT_SWIZZLE_IDENTITY,
.b = VK_COMPONENT_SWIZZLE_IDENTITY,
.a = VK_COMPONENT_SWIZZLE_IDENTITY
},
.subresourceRange = {
.aspectMask = aspectFlags,
.levelCount = 1,
.layerCount = 1
}
};
VkImageView viewHandle{};
res = vkCreateImageView(device, &viewDesc, nullptr, &viewHandle);
if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create image view");
// obtain the sharing fd
auto vkGetMemoryFdKHR =
reinterpret_cast<PFN_vkGetMemoryFdKHR>(vkGetDeviceProcAddr(device, "vkGetMemoryFdKHR"));
@ -119,7 +96,6 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
throw LSFG::vulkan_error(res, "Failed to obtain sharing fd for Vulkan image");
// store objects in shared ptr
this->layout = std::make_shared<VkImageLayout>(VK_IMAGE_LAYOUT_UNDEFINED);
this->image = std::shared_ptr<VkImage>(
new VkImage(imageHandle),
[dev = device](VkImage* img) {
@ -132,10 +108,4 @@ Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
vkFreeMemory(dev, *mem, nullptr);
}
);
this->view = std::shared_ptr<VkImageView>(
new VkImageView(viewHandle),
[dev = device](VkImageView* imgView) {
vkDestroyImageView(dev, *imgView, nullptr);
}
);
}

View file

@ -1,5 +1,6 @@
#include "mini/semaphore.hpp"
#include "lsfg.hpp"
#include <lsfg.hpp>
using namespace Mini;

142
src/utils.cpp Normal file
View file

@ -0,0 +1,142 @@
#include "utils.hpp"
#include <lsfg.hpp>
#include <algorithm>
#include <optional>
using namespace Utils;
std::pair<uint32_t, VkQueue> Utils::findQueue(VkDevice device, VkPhysicalDevice physicalDevice,
VkDeviceCreateInfo* desc, VkQueueFlags flags) {
std::vector<VkDeviceQueueCreateInfo> enabledQueues(desc->queueCreateInfoCount);
std::copy_n(desc->pQueueCreateInfos, enabledQueues.size(), enabledQueues.data());
uint32_t familyCount{};
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr);
std::vector<VkQueueFamilyProperties> families(familyCount);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, families.data());
std::optional<uint32_t> idx;
for (const auto& queueInfo : enabledQueues) {
if ((queueInfo.queueFamilyIndex < families.size()) &&
(families[queueInfo.queueFamilyIndex].queueFlags & flags)) {
idx = queueInfo.queueFamilyIndex;
break;
}
}
if (!idx.has_value())
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No suitable queue found");
VkQueue queue{};
vkGetDeviceQueue(device, *idx, 0, &queue);
return { *idx, queue };
}
std::vector<const char*> Utils::addExtensions(const char* const* extensions, size_t count,
const std::vector<const char*>& requiredExtensions) {
std::vector<const char*> ext(count);
std::copy_n(extensions, count, ext.data());
for (const auto& e : requiredExtensions) {
auto it = std::ranges::find(ext, e);
if (it == ext.end())
ext.push_back(e);
}
return ext;
}
void Utils::copyImage(VkCommandBuffer buf,
VkImage src, VkImage dst,
uint32_t width, uint32_t height,
VkPipelineStageFlags pre, VkPipelineStageFlags post,
bool makeSrcPresentable, bool makeDstPresentable) {
const VkImageMemoryBarrier srcBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.image = src,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const VkImageMemoryBarrier dstBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.image = dst,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
const std::vector<VkImageMemoryBarrier> barriers = { srcBarrier, dstBarrier };
vkCmdPipelineBarrier(buf,
pre, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
0, nullptr, 0, nullptr,
static_cast<uint32_t>(barriers.size()), barriers.data());
const VkImageCopy imageCopy{
.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1
},
.dstSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.layerCount = 1
},
.extent = {
.width = width,
.height = height,
.depth = 1
}
};
vkCmdCopyImage(buf,
src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &imageCopy);
if (makeSrcPresentable) {
const VkImageMemoryBarrier presentBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = src,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
vkCmdPipelineBarrier(buf,
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
0, nullptr, 0, nullptr,
1, &presentBarrier);
}
if (makeDstPresentable) {
const VkImageMemoryBarrier presentBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.image = dst,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1
}
};
vkCmdPipelineBarrier(buf,
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
0, nullptr, 0, nullptr,
1, &presentBarrier);
}
}