diff --git a/include/application.hpp b/include/application.hpp index 7519640..19f2775 100644 --- a/include/application.hpp +++ b/include/application.hpp @@ -1,10 +1,12 @@ #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 @@ -148,14 +150,18 @@ private: // (owned resources) Mini::CommandPool cmdPool; - std::array copySemaphores; // copy current swap to frame + std::array cmdBufs1; + std::array cmdBufs2; + std::array copySemaphores1; // copy current swap to frame + std::array copySemaphores2; // (for present) std::array acquireSemaphores; // acquire new swapchain image std::array renderSemaphores; // fg is done std::array presentSemaphores; // copy is done, ready to present - Mini::Image frame_0, frame_1; + Mini::Image frame_0, frame_1, out_img; 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/mini/commandbuffer.hpp b/include/mini/commandbuffer.hpp index 0607a8d..6b0e4f2 100644 --- a/include/mini/commandbuffer.hpp +++ b/include/mini/commandbuffer.hpp @@ -71,8 +71,8 @@ namespace Mini { /// @throws LSFG::vulkan_error if submission fails. /// void submit(VkQueue queue, - const std::vector& waitSemaphores = {}, - const std::vector& signalSemaphores = {}); + const std::vector& waitSemaphores = {}, + const std::vector& signalSemaphores = {}); /// Get the state of the command buffer. [[nodiscard]] CommandBufferState getState() const { return *this->state; } diff --git a/lsfg-vk-gen/include/context.hpp b/lsfg-vk-gen/include/context.hpp index 15768f3..f056d46 100644 --- a/lsfg-vk-gen/include/context.hpp +++ b/lsfg-vk-gen/include/context.hpp @@ -1,9 +1,11 @@ #ifndef CONTEXT_HPP #define CONTEXT_HPP +#include "core/commandbuffer.hpp" #include "core/commandpool.hpp" #include "core/descriptorpool.hpp" #include "core/image.hpp" +#include "core/semaphore.hpp" #include "shaderchains/alpha.hpp" #include "shaderchains/beta.hpp" #include "shaderchains/delta.hpp" @@ -30,10 +32,11 @@ namespace LSFG { /// @param height Height of the input images. /// @param in0 File descriptor for the first input image. /// @param in1 File descriptor for the second input image. + /// @param out File descriptor for the output image. /// /// @throws LSFG::vulkan_error if the generator fails to initialize. /// - Context(const Core::Device& device, uint32_t width, uint32_t height, int in0, int in1); + Context(const Core::Device& device, uint32_t width, uint32_t height, int in0, int in1, int out); /// /// Schedule the next generation. @@ -57,6 +60,9 @@ namespace LSFG { Core::CommandPool cmdPool; Core::Image inImg_0, inImg_1; // inImg_0 is next (inImg_1 prev) when fc % 2 == 0 + std::array inSemaphores; + std::array outSemaphores; + std::array cmdBuffers; uint64_t fc{0}; Shaderchains::Downsample downsampleChain; diff --git a/lsfg-vk-gen/include/shaderchains/merge.hpp b/lsfg-vk-gen/include/shaderchains/merge.hpp index 3af00cf..1205c2b 100644 --- a/lsfg-vk-gen/include/shaderchains/merge.hpp +++ b/lsfg-vk-gen/include/shaderchains/merge.hpp @@ -34,6 +34,7 @@ namespace LSFG::Shaderchains { /// @param inImg3 The first related input texture /// @param inImg4 The second related input texture /// @param inImg5 The third related input texture + /// @param outFd File descriptor for the output image. /// /// @throws LSFG::vulkan_error if resource creation fails. /// @@ -42,7 +43,8 @@ namespace LSFG::Shaderchains { Core::Image inImg2, Core::Image inImg3, Core::Image inImg4, - Core::Image inImg5); + Core::Image inImg5, + int outFd); /// /// Dispatch the shaderchain. diff --git a/lsfg-vk-gen/public/lsfg.hpp b/lsfg-vk-gen/public/lsfg.hpp index d9a25bf..55fa9a2 100644 --- a/lsfg-vk-gen/public/lsfg.hpp +++ b/lsfg-vk-gen/public/lsfg.hpp @@ -21,11 +21,12 @@ namespace LSFG { /// @param height Height of the input images. /// @param in0 File descriptor for the first input image. /// @param in1 File descriptor for the second input image. + /// @param out File descriptor for the output image. /// @return A unique identifier for the created context. /// /// @throws LSFG::vulkan_error if the context cannot be created. /// - int32_t createContext(uint32_t width, uint32_t height, int in0, int in1); + int32_t createContext(uint32_t width, uint32_t height, int in0, int in1, int out); /// /// Present a context. diff --git a/lsfg-vk-gen/src/context.cpp b/lsfg-vk-gen/src/context.cpp index fcfbef4..c8546e7 100644 --- a/lsfg-vk-gen/src/context.cpp +++ b/lsfg-vk-gen/src/context.cpp @@ -8,7 +8,7 @@ using namespace LSFG; -Context::Context(const Core::Device& device, uint32_t width, uint32_t height, int in0, int in1) { +Context::Context(const Core::Device& device, uint32_t width, uint32_t height, int in0, int in1, int out) { // import images this->inImg_0 = Core::Image(device, { width, height }, VK_FORMAT_R8G8B8A8_UNORM, @@ -90,15 +90,19 @@ Context::Context(const Core::Device& device, uint32_t width, uint32_t height, in this->inImg_0, this->zetaChains.at(2).getOutImage(), this->epsilonChains.at(2).getOutImage(), - this->deltaChains.at(2).getOutImage() + this->deltaChains.at(2).getOutImage(), + out ); } void Context::present(const Core::Device& device, int inSem, int outSem) { - const Core::Semaphore inSemaphore(device, inSem); - const Core::Semaphore outSemaphore(device, outSem); + auto& inSemaphore = this->inSemaphores.at(this->fc % 8); + inSemaphore = Core::Semaphore(device, inSem); + auto& outSemaphore = this->outSemaphores.at(this->fc % 8); + outSemaphore = Core::Semaphore(device, outSem); - Core::CommandBuffer cmdBuffer(device, this->cmdPool); + auto& cmdBuffer = this->cmdBuffers.at(this->fc % 8); + cmdBuffer = Core::CommandBuffer(device, this->cmdPool); cmdBuffer.begin(); this->downsampleChain.Dispatch(cmdBuffer, fc); diff --git a/lsfg-vk-gen/src/lsfg.cpp b/lsfg-vk-gen/src/lsfg.cpp index cf70a7e..7b0750e 100644 --- a/lsfg-vk-gen/src/lsfg.cpp +++ b/lsfg-vk-gen/src/lsfg.cpp @@ -28,12 +28,12 @@ void LSFG::initialize() { std::srand(static_cast(std::time(nullptr))); } -int32_t LSFG::createContext(uint32_t width, uint32_t height, int in0, int in1) { +int32_t LSFG::createContext(uint32_t width, uint32_t height, int in0, int in1, int out) { if (!instance.has_value() || !device.has_value()) throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "LSFG not initialized"); auto id = std::rand(); - contexts.emplace(id, Context(*device, width, height, in0, in1)); + contexts.emplace(id, Context(*device, width, height, in0, in1, out)); return id; } diff --git a/lsfg-vk-gen/src/shaderchains/magic.cpp b/lsfg-vk-gen/src/shaderchains/magic.cpp index 450bc91..dca66b5 100644 --- a/lsfg-vk-gen/src/shaderchains/magic.cpp +++ b/lsfg-vk-gen/src/shaderchains/magic.cpp @@ -103,6 +103,6 @@ void Magic::Dispatch(const Core::CommandBuffer& buf, uint64_t fc) { .build(); this->pipeline.bind(buf); - this->descriptorSets.at(fc).bind(buf, this->pipeline); + this->descriptorSets.at(fc % 3).bind(buf, this->pipeline); buf.dispatch(threadsX, threadsY, 1); } diff --git a/lsfg-vk-gen/src/shaderchains/merge.cpp b/lsfg-vk-gen/src/shaderchains/merge.cpp index 43bd957..36ff3c3 100644 --- a/lsfg-vk-gen/src/shaderchains/merge.cpp +++ b/lsfg-vk-gen/src/shaderchains/merge.cpp @@ -8,7 +8,8 @@ Merge::Merge(const Core::Device& device, const Core::DescriptorPool& pool, Core::Image inImg2, Core::Image inImg3, Core::Image inImg4, - Core::Image inImg5) + Core::Image inImg5, + int outFd) : inImg1(std::move(inImg1)), inImg2(std::move(inImg2)), inImg3(std::move(inImg3)), @@ -30,7 +31,8 @@ Merge::Merge(const Core::Device& device, const Core::DescriptorPool& pool, extent, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - VK_IMAGE_ASPECT_COLOR_BIT); + VK_IMAGE_ASPECT_COLOR_BIT, + outFd); for (size_t fc = 0; fc < 2; fc++) { this->descriptorSets.at(fc).update(device) diff --git a/src/application.cpp b/src/application.cpp index 9e63c42..c9623b2 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,8 +1,11 @@ #include "application.hpp" +#include "mini/commandbuffer.hpp" #include "mini/commandpool.hpp" #include "mini/image.hpp" #include "mini/semaphore.hpp" +#include +#include #include #include #include @@ -44,7 +47,15 @@ SwapchainContext::SwapchainContext(const Application& app, VkSwapchainKHR swapch VK_IMAGE_ASPECT_COLOR_BIT, &frame1fd ); - auto id = LSFG::createContext(extent.width, extent.height, frame0fd, frame1fd); + int outfd{}; + this->out_img = Mini::Image( + app.getDevice(), app.getPhysicalDevice(), + extent, VK_FORMAT_R8G8B8A8_UNORM, + VK_IMAGE_USAGE_TRANSFER_DST_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, &outfd + ); + + auto id = LSFG::createContext(extent.width, extent.height, frame0fd, frame1fd, outfd); this->lsfgId = std::shared_ptr( new int32_t(id), [](const int32_t* id) { @@ -64,39 +75,222 @@ void Application::presentSwapchain(VkSwapchainKHR handle, VkQueue queue, void SwapchainContext::present(const Application& app, VkQueue queue, const std::vector& semaphores, uint32_t idx) { + + // present deferred frame if any + 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{}; - int presentSem{}; - Mini::Semaphore& copySemaphore = this->copySemaphores.at(this->frameIdx % 8); - copySemaphore = Mini::Semaphore(app.getDevice(), ©Sem); + 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()); Mini::Semaphore& acquireSemaphore = this->acquireSemaphores.at(this->frameIdx % 8); acquireSemaphore = Mini::Semaphore(app.getDevice()); + int renderSem{}; Mini::Semaphore& renderSemaphore = this->renderSemaphores.at(this->frameIdx % 8); - renderSemaphore = Mini::Semaphore(app.getDevice()); + renderSemaphore = Mini::Semaphore(app.getDevice(), &renderSem); Mini::Semaphore& presentSemaphore = this->presentSemaphores.at(this->frameIdx % 8); - presentSemaphore = Mini::Semaphore(app.getDevice(), &presentSem); + presentSemaphore = 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(); + cmdBuf1.submit(app.getGraphicsQueue(), + semaphores, { copySemaphore1.handle(), copySemaphore2.handle() }); // render the intermediary frame - // FIXME: i forgot the flippin output - // FIXME: semaphores are being destroyed in the context - LSFG::presentContext(*this->lsfgId, copySem, presentSem); + try { + LSFG::presentContext(*this->lsfgId, copySem, renderSem); + } catch(std::exception& e) { + std::cerr << "LSFG error: " << e.what() << '\n'; + } - // ... + // acquire the next swapchain image + 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& cmdBuf2 = this->cmdBufs2.at((this->frameIdx + 1) % 8); + cmdBuf2 = Mini::CommandBuffer(app.getDevice(), this->cmdPool); + cmdBuf2.begin(); + auto& srcImage2 = this->out_img; + 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(); + cmdBuf2.submit(app.getGraphicsQueue(), + { acquireSemaphore.handle(), renderSemaphore.handle() }, + { presentSemaphore.handle() }); + + // present the swapchain image + VkSemaphore presentSemaphoreHandle = presentSemaphore.handle(); const VkPresentInfoKHR presentInfo = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, - .waitSemaphoreCount = static_cast(semaphores.size()), - .pWaitSemaphores = semaphores.data(), + .waitSemaphoreCount = 1, + .pWaitSemaphores = &presentSemaphoreHandle, .swapchainCount = 1, .pSwapchains = &this->swapchain, - .pImageIndices = &idx + .pImageIndices = &newIdx, }; - auto res = vkQueuePresentKHR(queue, &presentInfo); - if (res != VK_SUCCESS) // FIXME: somehow return VK_SUBOPTIMAL_KHR + 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++; diff --git a/src/mini/commandbuffer.cpp b/src/mini/commandbuffer.cpp index 9cbc472..2014ca5 100644 --- a/src/mini/commandbuffer.cpp +++ b/src/mini/commandbuffer.cpp @@ -53,32 +53,23 @@ void CommandBuffer::end() { } void CommandBuffer::submit(VkQueue queue, - const std::vector& waitSemaphores, - const std::vector& signalSemaphores) { + const std::vector& waitSemaphores, + const std::vector& signalSemaphores) { if (*this->state != CommandBufferState::Full) throw std::logic_error("Command buffer is not in Full state"); const std::vector waitStages(waitSemaphores.size(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT); - std::vector waitSemaphoresHandles; - waitSemaphoresHandles.reserve(waitSemaphores.size()); - for (const auto& semaphore : waitSemaphores) - waitSemaphoresHandles.push_back(semaphore.handle()); - std::vector signalSemaphoresHandles; - signalSemaphoresHandles.reserve(signalSemaphores.size()); - for (const auto& semaphore : signalSemaphores) - signalSemaphoresHandles.push_back(semaphore.handle()); - const VkSubmitInfo submitInfo{ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, .waitSemaphoreCount = static_cast(waitSemaphores.size()), - .pWaitSemaphores = waitSemaphoresHandles.data(), + .pWaitSemaphores = waitSemaphores.data(), .pWaitDstStageMask = waitStages.data(), .commandBufferCount = 1, .pCommandBuffers = &(*this->commandBuffer), .signalSemaphoreCount = static_cast(signalSemaphores.size()), - .pSignalSemaphores = signalSemaphoresHandles.data() + .pSignalSemaphores = signalSemaphores.data() }; auto res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); if (res != VK_SUCCESS)