diff --git a/engine/src/renderer/render_context.cpp b/engine/src/renderer/render_context.cpp index 67a293e..73d7b51 100644 --- a/engine/src/renderer/render_context.cpp +++ b/engine/src/renderer/render_context.cpp @@ -86,22 +86,28 @@ void RenderContext::endFrame() { FrameData& frame = this->frames[this->currentFrameIndex]; frame.commandBuffer.end(); - vk::Semaphore waitSemaphore = frame.imageAvailable.get(); vk::Semaphore signalSemaphore = this->renderFinishedSemaphores.at(this->currentImageIndex).get(); - vk::PipelineStageFlags waitStage = SWAPCHAIN_ACQUIRE_STAGE; - vk::CommandBuffer commandBuffer = frame.commandBuffer; - vk::SubmitInfo submitInfo{}; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &waitSemaphore; - submitInfo.pWaitDstStageMask = &waitStage; - submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &commandBuffer; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &signalSemaphore; + vk::SemaphoreSubmitInfo waitInfo{}; + waitInfo.semaphore = frame.imageAvailable.get(); + waitInfo.stageMask = SWAPCHAIN_ACQUIRE_STAGE; - this->device.getGraphicsQueue().submit(submitInfo, frame.inFlight.get()); + // Present has no pipeline stage to wait on, so signal once everything in + // the submission is done. + vk::SemaphoreSubmitInfo signalInfo{}; + signalInfo.semaphore = signalSemaphore; + signalInfo.stageMask = vk::PipelineStageFlagBits2::eAllCommands; + + vk::CommandBufferSubmitInfo commandBufferInfo{}; + commandBufferInfo.commandBuffer = frame.commandBuffer; + + vk::SubmitInfo2 submitInfo{}; + submitInfo.setWaitSemaphoreInfos(waitInfo); + submitInfo.setCommandBufferInfos(commandBufferInfo); + submitInfo.setSignalSemaphoreInfos(signalInfo); + + this->device.getGraphicsQueue().submit2(submitInfo, frame.inFlight.get()); vk::Result result = this->swapchain->present( this->device.getPresentQueue(), this->currentImageIndex, signalSemaphore); diff --git a/engine/src/renderer/render_graph.cpp b/engine/src/renderer/render_graph.cpp index 880dd5b..af5b978 100644 --- a/engine/src/renderer/render_graph.cpp +++ b/engine/src/renderer/render_graph.cpp @@ -55,8 +55,6 @@ void RenderGraph::transitionTo(ImageRef ref, const AccessState& next, pending.barriers.push_back(makeImageBarrier( imported.image->get(), imported.image->getSubresourceRange(), imported.state, next)); - pending.srcStages |= imported.state.stage; - pending.dstStages |= next.stage; imported.state = next; } else { // Same-layout read after read. Nothing to record, but a later write has @@ -68,12 +66,7 @@ void RenderGraph::transitionTo(ImageRef ref, const AccessState& next, void RenderGraph::flush(vk::CommandBuffer commandBuffer, PendingBarriers& pending) { - if (pending.barriers.empty()) { - return; - } - - commandBuffer.pipelineBarrier(pending.srcStages, pending.dstStages, {}, - nullptr, nullptr, pending.barriers); + recordImageBarriers(commandBuffer, pending.barriers); pending = PendingBarriers{}; } diff --git a/engine/src/renderer/render_graph.hpp b/engine/src/renderer/render_graph.hpp index ff2cd32..be2e5ef 100644 --- a/engine/src/renderer/render_graph.hpp +++ b/engine/src/renderer/render_graph.hpp @@ -104,9 +104,7 @@ class RenderGraph { }; struct PendingBarriers { - std::vector barriers; - vk::PipelineStageFlags srcStages; - vk::PipelineStageFlags dstStages; + std::vector barriers; }; void transitionTo(ImageRef ref, const AccessState& next, diff --git a/engine/src/vulkan/access.cpp b/engine/src/vulkan/access.cpp index 640e369..027d9dc 100644 --- a/engine/src/vulkan/access.cpp +++ b/engine/src/vulkan/access.cpp @@ -14,10 +14,11 @@ constexpr std::array ALL_ACCESSES = { Access::Present, }; -constexpr vk::AccessFlags WRITE_ACCESSES = - vk::AccessFlagBits::eColorAttachmentWrite | - vk::AccessFlagBits::eDepthStencilAttachmentWrite | - vk::AccessFlagBits::eShaderWrite | vk::AccessFlagBits::eTransferWrite; +constexpr vk::AccessFlags2 WRITE_ACCESSES = + vk::AccessFlagBits2::eColorAttachmentWrite | + vk::AccessFlagBits2::eDepthStencilAttachmentWrite | + vk::AccessFlagBits2::eShaderStorageWrite | + vk::AccessFlagBits2::eTransferWrite; } // namespace @@ -25,34 +26,37 @@ AccessState getAccessState(Access access) { switch (access) { case Access::ColorWrite: return {vk::ImageLayout::eColorAttachmentOptimal, - vk::PipelineStageFlagBits::eColorAttachmentOutput, - vk::AccessFlagBits::eColorAttachmentRead | - vk::AccessFlagBits::eColorAttachmentWrite}; + vk::PipelineStageFlagBits2::eColorAttachmentOutput, + vk::AccessFlagBits2::eColorAttachmentRead | + vk::AccessFlagBits2::eColorAttachmentWrite}; case Access::DepthWrite: return {vk::ImageLayout::eDepthStencilAttachmentOptimal, - vk::PipelineStageFlagBits::eEarlyFragmentTests | - vk::PipelineStageFlagBits::eLateFragmentTests, - vk::AccessFlagBits::eDepthStencilAttachmentRead | - vk::AccessFlagBits::eDepthStencilAttachmentWrite}; + vk::PipelineStageFlagBits2::eEarlyFragmentTests | + vk::PipelineStageFlagBits2::eLateFragmentTests, + vk::AccessFlagBits2::eDepthStencilAttachmentRead | + vk::AccessFlagBits2::eDepthStencilAttachmentWrite}; case Access::SampledRead: return {vk::ImageLayout::eShaderReadOnlyOptimal, - vk::PipelineStageFlagBits::eFragmentShader, - vk::AccessFlagBits::eShaderRead}; + vk::PipelineStageFlagBits2::eFragmentShader, + vk::AccessFlagBits2::eShaderSampledRead}; case Access::StorageReadWrite: - return { - vk::ImageLayout::eGeneral, vk::PipelineStageFlagBits::eComputeShader, - vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite}; + return {vk::ImageLayout::eGeneral, + vk::PipelineStageFlagBits2::eComputeShader, + vk::AccessFlagBits2::eShaderStorageRead | + vk::AccessFlagBits2::eShaderStorageWrite}; case Access::TransferSrc: return {vk::ImageLayout::eTransferSrcOptimal, - vk::PipelineStageFlagBits::eTransfer, - vk::AccessFlagBits::eTransferRead}; + vk::PipelineStageFlagBits2::eTransfer, + vk::AccessFlagBits2::eTransferRead}; case Access::TransferDst: return {vk::ImageLayout::eTransferDstOptimal, - vk::PipelineStageFlagBits::eTransfer, - vk::AccessFlagBits::eTransferWrite}; + vk::PipelineStageFlagBits2::eTransfer, + vk::AccessFlagBits2::eTransferWrite}; case Access::Present: + // Presentation isn't a pipeline stage; the present waits on a semaphore + // instead, so the barrier into this state has nothing to block. return {vk::ImageLayout::ePresentSrcKHR, - vk::PipelineStageFlagBits::eBottomOfPipe, vk::AccessFlags{}}; + vk::PipelineStageFlagBits2::eNone, vk::AccessFlags2{}}; } log::fatal("Unknown Access value."); @@ -83,22 +87,37 @@ bool needsBarrier(const AccessState& from, const AccessState& to) { return from.layout != to.layout || writesImage(from) || writesImage(to); } -vk::ImageMemoryBarrier makeImageBarrier(vk::Image image, - const vk::ImageSubresourceRange& range, - const AccessState& from, - const AccessState& to) { - vk::ImageMemoryBarrier barrier{}; +vk::ImageMemoryBarrier2 makeImageBarrier(vk::Image image, + const vk::ImageSubresourceRange& range, + const AccessState& from, + const AccessState& to) { + vk::ImageMemoryBarrier2 barrier{}; + barrier.srcStageMask = from.stage; + barrier.dstStageMask = to.stage; + // Only writes need to be made available; reads just need the execution + // dependency the stage masks already give. + barrier.srcAccessMask = from.access & WRITE_ACCESSES; + barrier.dstAccessMask = to.access; barrier.oldLayout = from.layout; barrier.newLayout = to.layout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.image = image; barrier.subresourceRange = range; - // Only writes need to be made available; reads just need the execution - // dependency the stage masks already give. - barrier.srcAccessMask = from.access & WRITE_ACCESSES; - barrier.dstAccessMask = to.access; return barrier; } +void recordImageBarriers( + vk::CommandBuffer commandBuffer, + vk::ArrayProxy barriers) { + if (barriers.empty()) { + return; + } + + vk::DependencyInfo dependency{}; + dependency.imageMemoryBarrierCount = barriers.size(); + dependency.pImageMemoryBarriers = barriers.data(); + commandBuffer.pipelineBarrier2(dependency); +} + } // namespace sophia diff --git a/engine/src/vulkan/access.hpp b/engine/src/vulkan/access.hpp index 67c35f4..3f78e67 100644 --- a/engine/src/vulkan/access.hpp +++ b/engine/src/vulkan/access.hpp @@ -25,8 +25,8 @@ enum class Access { */ struct AccessState { vk::ImageLayout layout = vk::ImageLayout::eUndefined; - vk::PipelineStageFlags stage = vk::PipelineStageFlagBits::eTopOfPipe; - vk::AccessFlags access; + vk::PipelineStageFlags2 stage = vk::PipelineStageFlagBits2::eNone; + vk::AccessFlags2 access; }; /** @@ -35,8 +35,8 @@ struct AccessState { * just-acquired state has to use this as its source stage, otherwise the * layout transition isn't ordered after the acquire. */ -inline constexpr vk::PipelineStageFlags SWAPCHAIN_ACQUIRE_STAGE = - vk::PipelineStageFlagBits::eColorAttachmentOutput; +inline constexpr vk::PipelineStageFlags2 SWAPCHAIN_ACQUIRE_STAGE = + vk::PipelineStageFlagBits2::eColorAttachmentOutput; AccessState getAccessState(Access access); @@ -55,9 +55,17 @@ bool writesImage(const AccessState& state); */ bool needsBarrier(const AccessState& from, const AccessState& to); -vk::ImageMemoryBarrier makeImageBarrier(vk::Image image, - const vk::ImageSubresourceRange& range, - const AccessState& from, - const AccessState& to); +/** + * A barrier carrying its own stage masks, for recording with + * pipelineBarrier2. Barriers flushed together don't share stages. + */ +vk::ImageMemoryBarrier2 makeImageBarrier(vk::Image image, + const vk::ImageSubresourceRange& range, + const AccessState& from, + const AccessState& to); + +void recordImageBarriers( + vk::CommandBuffer commandBuffer, + vk::ArrayProxy barriers); } // namespace sophia diff --git a/engine/src/vulkan/image.cpp b/engine/src/vulkan/image.cpp index 8d8b93c..d1cd9a5 100644 --- a/engine/src/vulkan/image.cpp +++ b/engine/src/vulkan/image.cpp @@ -56,10 +56,9 @@ void Image::transitionLayout(vk::CommandBuffer commandBuffer, AccessState to = getStateForLayout(newLayout); if (needsBarrier(from, to)) { - vk::ImageMemoryBarrier barrier = + vk::ImageMemoryBarrier2 barrier = makeImageBarrier(this->get(), this->getSubresourceRange(), from, to); - commandBuffer.pipelineBarrier(from.stage, to.stage, {}, nullptr, nullptr, - barrier); + recordImageBarriers(commandBuffer, barrier); } this->layout = newLayout;