diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fb353c..1b21351 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 ) diff --git a/include/application.hpp b/include/application.hpp deleted file mode 100644 index c011ab7..0000000 --- a/include/application.hpp +++ /dev/null @@ -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 -#include -#include -#include - -#include - -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& 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& 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 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& 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& 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& 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 images; - - // (owned resources) - Mini::CommandPool cmdPool; - std::array cmdBufs1; - std::array, 8> cmdBufs2; - std::array copySemaphores1; // copy current swap to frame - std::array copySemaphores2; // (for present) - std::array, 8> acquireSemaphores; // acquire new swapchain image - std::array, 8> renderSemaphores; // fg is done - std::array, 8> prevPresentSemaphores; // for inorder fg - std::array, 8> presentSemaphores; // copy is done, ready to present - - Mini::Image frame_0, frame_1; - std::vector outImgs; - std::shared_ptr lsfgId; - uint64_t frameIdx{0}; - std::optional deferredIdx; // index of the frame to present next -}; - -#endif // APPLICATION_HPP diff --git a/include/context.hpp b/include/context.hpp new file mode 100644 index 0000000..fbd2481 --- /dev/null +++ b/include/context.hpp @@ -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 +#include +#include +#include + +#include + +/// +/// This class is the frame generation context. There should be one instance per swapchain. +/// +class LsContext { +public: + /// + /// Create the swapchain context. + /// + /// @param info The device information to use. + /// @param swapchain The Vulkan swapchain to use. + /// @param extent The extent of the swapchain images. + /// @param swapchainImages The swapchain images to use. + /// + /// @throws LSFG::vulkan_error if any Vulkan call fails. + /// + LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, + VkExtent2D extent, const std::vector& swapchainImages); + + /// + /// Custom present logic. + /// + /// @param info The device information to use. + /// @param pNext Unknown pointer set in the present info structure. + /// @param queue The Vulkan queue to present the frame on. + /// @param gameRenderSemaphores The semaphores to wait on before presenting. + /// @param presentIdx The index of the swapchain image to present. + /// @return The result of the Vulkan present operation, which can be VK_SUCCESS or VK_SUBOPTIMAL_KHR. + /// + /// @throws LSFG::vulkan_error if any Vulkan call fails. + /// + VkResult present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue, + const std::vector& gameRenderSemaphores, uint32_t presentIdx); + + // Non-copyable, trivially moveable and destructible + LsContext(const LsContext&) = delete; + LsContext& operator=(const LsContext&) = delete; + LsContext(LsContext&&) = default; + LsContext& operator=(LsContext&&) = default; + ~LsContext() = default; +private: + VkSwapchainKHR swapchain; + std::vector swapchainImages; + VkExtent2D extent; + + std::shared_ptr lsfgCtxId; // lsfg context id + Mini::Image frame_0, frame_1; // frames shared with lsfg. write to frame_0 when fc % 2 == 0 + std::vector out_n; // output images shared with lsfg, indexed by framegen id + + Mini::CommandPool cmdPool; + uint64_t frameIdx{0}; + + struct RenderPassInfo { + Mini::CommandBuffer preCopyBuf; // copy from swapchain image to frame_0/frame_1 + std::array preCopySemaphores; // signal when preCopyBuf is done + + std::vector renderSemaphores; // signal when lsfg is done with frame n + + std::vector acquireSemaphores; // signal for swapchain image n + + std::vector postCopyBufs; // copy from out_n to swapchain image + std::vector postCopySemaphores; // signal when postCopyBuf is done + std::vector prevPostCopySemaphores; // signal for previous postCopyBuf + }; // data for a single render pass + std::array passInfos; // allocate 8 because why not +}; + +#endif // CONTEXT_HPP diff --git a/include/hooks.hpp b/include/hooks.hpp index f4ba294..0758e21 100644 --- a/include/hooks.hpp +++ b/include/hooks.hpp @@ -1,8 +1,20 @@ #ifndef HOOKS_HPP #define HOOKS_HPP +#include + +#include + namespace Hooks { + /// Vulkan device information structure. + struct DeviceInfo { + VkDevice device; + VkPhysicalDevice physicalDevice; + std::pair queue; // graphics family + uint64_t frameGen; // amount of frames to generate + }; + /// /// Install overrides for hooked Vulkan functions. /// diff --git a/include/mini/image.hpp b/include/mini/image.hpp index ed4d182..a1de0cf 100644 --- a/include/mini/image.hpp +++ b/include/mini/image.hpp @@ -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 image; std::shared_ptr memory; - std::shared_ptr view; - - std::shared_ptr layout; VkExtent2D extent{}; VkFormat format{}; diff --git a/include/utils.hpp b/include/utils.hpp new file mode 100644 index 0000000..136d50f --- /dev/null +++ b/include/utils.hpp @@ -0,0 +1,55 @@ +#ifndef UTILS_HPP +#define UTILS_HPP + +#include +#include + +#include + +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 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 addExtensions(const char* const* extensions, size_t count, + const std::vector& 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 diff --git a/lsfg-vk-gen/src/context.cpp b/lsfg-vk-gen/src/context.cpp index fcf6340..6e26566 100644 --- a/lsfg-vk-gen/src/context.cpp +++ b/lsfg-vk-gen/src/context.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace LSFG; diff --git a/src/application.cpp b/src/application.cpp deleted file mode 100644 index 684fc52..0000000 --- a/src/application.cpp +++ /dev/null @@ -1,346 +0,0 @@ -#include "application.hpp" -#include "mini/commandbuffer.hpp" -#include "mini/commandpool.hpp" -#include "mini/image.hpp" -#include "mini/semaphore.hpp" - -#include -#include -#include -#include - -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& 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& 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 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( - new int32_t(id), - [](const int32_t* id) { - LSFG::deleteContext(*id); - } - ); -} - -void Application::presentSwapchain(VkSwapchainKHR handle, VkQueue queue, - const std::vector& 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& 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 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 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 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 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 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 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(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(); -} diff --git a/src/context.cpp b/src/context.cpp new file mode 100644 index 0000000..e5c0412 --- /dev/null +++ b/src/context.cpp @@ -0,0 +1,155 @@ +#include "context.hpp" +#include "utils.hpp" + +#include + +#include +#include + +LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, + VkExtent2D extent, const std::vector& 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 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( + 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& 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 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 waitSemaphores{ pass.postCopySemaphores.at(i).handle() }; + if (i != 0) waitSemaphores.emplace_back(pass.prevPostCopySemaphores.at(i - 1).handle()); + + const VkPresentInfoKHR presentInfo{ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = i == 0 ? pNext : nullptr, // only set on first present + .waitSemaphoreCount = static_cast(waitSemaphores.size()), + .pWaitSemaphores = waitSemaphores.data(), + .swapchainCount = 1, + .pSwapchains = &this->swapchain, + .pImageIndices = &imageIdx, + }; + res = 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; +} diff --git a/src/hooks.cpp b/src/hooks.cpp index bd01b5d..3154b2f 100644 --- a/src/hooks.cpp +++ b/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 #include -#include -#include +#include +#include using namespace Hooks; namespace { - bool initialized{false}; - std::optional application; + + // instance hooks VkResult myvkCreateInstance( const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance) { - // add extensions - std::vector extensions(pCreateInfo->enabledExtensionCount); - std::copy_n(pCreateInfo->ppEnabledExtensionNames, extensions.size(), extensions.data()); + // create lsfg + LSFG::initialize(); - const std::vector 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(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 devices; + VkResult myvkCreateDevice( VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice) { // add extensions - std::vector extensions(pCreateInfo->enabledExtensionCount); - std::copy_n(pCreateInfo->ppEnabledExtensionNames, extensions.size(), extensions.data()); - - const std::vector 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(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); auto res = vkCreateDevice(physicalDevice, &createInfo, pAllocator, pDevice); - // extract graphics and present queues - std::vector queueCreateInfos(pCreateInfo->queueCreateInfoCount); - std::copy_n(pCreateInfo->pQueueCreateInfos, queueCreateInfos.size(), queueCreateInfos.data()); - - uint32_t familyCount{}; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr); - std::vector families(familyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, families.data()); - - std::optional 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(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 swapchains; + std::unordered_map 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(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(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 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(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(myvkCreateInstance)); - Loader::VK::registerSymbol("vkCreateDevice", - reinterpret_cast(myvkCreateDevice)); - Loader::VK::registerSymbol("vkDestroyDevice", - reinterpret_cast(myvkDestroyDevice)); - Loader::VK::registerSymbol("vkCreateSwapchainKHR", - reinterpret_cast(myvkCreateSwapchainKHR)); - Loader::VK::registerSymbol("vkDestroySwapchainKHR", - reinterpret_cast(myvkDestroySwapchainKHR)); - Loader::VK::registerSymbol("vkQueuePresentKHR", - reinterpret_cast(myvkQueuePresentKHR)); + // list of hooks to register + const std::vector> hooks = { + { "vkCreateInstance", reinterpret_cast(myvkCreateInstance) }, + { "vkDestroyInstance", reinterpret_cast(myvkDestroyInstance) }, + { "vkCreateDevice", reinterpret_cast(myvkCreateDevice) }, + { "vkDestroyDevice", reinterpret_cast(myvkDestroyDevice) }, + { "vkCreateSwapchainKHR", reinterpret_cast(myvkCreateSwapchainKHR) }, + { "vkQueuePresentKHR", reinterpret_cast(myvkQueuePresentKHR) }, + { "vkDestroySwapchainKHR", reinterpret_cast(myvkDestroySwapchainKHR) } + }; - // register hooks to dynamic loader under libvulkan.so.1 - Loader::DL::File vk1("libvulkan.so.1"); - vk1.defineSymbol("vkCreateInstance", - reinterpret_cast(myvkCreateInstance)); - vk1.defineSymbol("vkCreateDevice", - reinterpret_cast(myvkCreateDevice)); - vk1.defineSymbol("vkDestroyDevice", - reinterpret_cast(myvkDestroyDevice)); - vk1.defineSymbol("vkCreateSwapchainKHR", - reinterpret_cast(myvkCreateSwapchainKHR)); - vk1.defineSymbol("vkDestroySwapchainKHR", - reinterpret_cast(myvkDestroySwapchainKHR)); - vk1.defineSymbol("vkQueuePresentKHR", - reinterpret_cast(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(myvkCreateInstance)); - vk2.defineSymbol("vkCreateDevice", - reinterpret_cast(myvkCreateDevice)); - vk2.defineSymbol("vkDestroyDevice", - reinterpret_cast(myvkDestroyDevice)); - vk2.defineSymbol("vkCreateSwapchainKHR", - reinterpret_cast(myvkCreateSwapchainKHR)); - vk2.defineSymbol("vkDestroySwapchainKHR", - reinterpret_cast(myvkDestroySwapchainKHR)); - vk2.defineSymbol("vkQueuePresentKHR", - reinterpret_cast(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"); } diff --git a/src/mini/commandbuffer.cpp b/src/mini/commandbuffer.cpp index 2014ca5..8a8ac32 100644 --- a/src/mini/commandbuffer.cpp +++ b/src/mini/commandbuffer.cpp @@ -1,5 +1,6 @@ #include "mini/commandbuffer.hpp" -#include "lsfg.hpp" + +#include using namespace Mini; diff --git a/src/mini/commandpool.cpp b/src/mini/commandpool.cpp index d0a6b8e..3407410 100644 --- a/src/mini/commandpool.cpp +++ b/src/mini/commandpool.cpp @@ -1,5 +1,6 @@ #include "mini/commandpool.hpp" -#include "lsfg.hpp" + +#include using namespace Mini; diff --git a/src/mini/image.cpp b/src/mini/image.cpp index 2ec53a7..4fc8dcc 100644 --- a/src/mini/image.cpp +++ b/src/mini/image.cpp @@ -1,5 +1,6 @@ #include "mini/image.hpp" -#include "lsfg.hpp" + +#include #include @@ -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(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(VK_IMAGE_LAYOUT_UNDEFINED); this->image = std::shared_ptr( 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( - new VkImageView(viewHandle), - [dev = device](VkImageView* imgView) { - vkDestroyImageView(dev, *imgView, nullptr); - } - ); } diff --git a/src/mini/semaphore.cpp b/src/mini/semaphore.cpp index 6d803bc..f4b2225 100644 --- a/src/mini/semaphore.cpp +++ b/src/mini/semaphore.cpp @@ -1,5 +1,6 @@ #include "mini/semaphore.hpp" -#include "lsfg.hpp" + +#include using namespace Mini; diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..0bb0a0a --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,142 @@ +#include "utils.hpp" + +#include + +#include +#include + +using namespace Utils; + +std::pair Utils::findQueue(VkDevice device, VkPhysicalDevice physicalDevice, + VkDeviceCreateInfo* desc, VkQueueFlags flags) { + std::vector enabledQueues(desc->queueCreateInfoCount); + std::copy_n(desc->pQueueCreateInfos, enabledQueues.size(), enabledQueues.data()); + + uint32_t familyCount{}; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr); + std::vector families(familyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, families.data()); + + std::optional 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 Utils::addExtensions(const char* const* extensions, size_t count, + const std::vector& requiredExtensions) { + std::vector 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 barriers = { srcBarrier, dstBarrier }; + vkCmdPipelineBarrier(buf, + pre, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, nullptr, 0, nullptr, + static_cast(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); + } + +}