mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-10-30 07:01:10 +00:00
refactor hooks
This commit is contained in:
parent
990f45f06d
commit
d5cfab75d7
15 changed files with 568 additions and 746 deletions
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
83
include/context.hpp
Normal 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
|
||||
|
|
@ -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.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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
55
include/utils.hpp
Normal 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
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <cassert>
|
||||
#include <format>
|
||||
#include <optional>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace LSFG;
|
||||
|
||||
|
|
|
|||
|
|
@ -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(), ©Sem);
|
||||
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
155
src/context.cpp
Normal 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;
|
||||
}
|
||||
295
src/hooks.cpp
295
src/hooks.cpp
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "mini/commandbuffer.hpp"
|
||||
#include "lsfg.hpp"
|
||||
|
||||
#include <lsfg.hpp>
|
||||
|
||||
using namespace Mini;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "mini/commandpool.hpp"
|
||||
#include "lsfg.hpp"
|
||||
|
||||
#include <lsfg.hpp>
|
||||
|
||||
using namespace Mini;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "mini/semaphore.hpp"
|
||||
#include "lsfg.hpp"
|
||||
|
||||
#include <lsfg.hpp>
|
||||
|
||||
using namespace Mini;
|
||||
|
||||
|
|
|
|||
142
src/utils.cpp
Normal file
142
src/utils.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue