diff --git a/src/graphics/presentation/window/swapchain.cpp b/src/graphics/presentation/window/swapchain.cpp index 99e8a289..07c74012 100644 --- a/src/graphics/presentation/window/swapchain.cpp +++ b/src/graphics/presentation/window/swapchain.cpp @@ -266,7 +266,7 @@ public: void Create(); void Recreate(bool surface_lost = false); [[nodiscard]] bool NeedsResize() const; - [[nodiscard]] Status AcquireNextImage(); + [[nodiscard]] Status AcquireNextImage(CommandScheduler& scheduler); [[nodiscard]] bool PrepareSystemOverlay(); void RecordPresentCommands(CommandBuffer& command, Presenter::Frame* source, const Presenter::Layer& overlay, bool draw_system_overlay); @@ -301,6 +301,7 @@ private: std::vector m_images; std::vector m_image_views; std::vector m_image_acquired; + std::vector m_image_acquired_ticks; std::vector m_render_complete; std::unique_ptr m_system_overlay; vk::DescriptorSetLayout m_overlay_descriptors = nullptr; @@ -501,6 +502,7 @@ void Swapchain::Create() { vk::SemaphoreCreateInfo semaphore_info {}; semaphore_info.sType = vk::StructureType::eSemaphoreCreateInfo; m_image_acquired.resize(m_images.size()); + m_image_acquired_ticks.assign(m_images.size(), 0); m_render_complete.resize(m_images.size()); for (size_t i = 0; i < m_images.size(); i++) { RequireVulkanSuccess( @@ -569,6 +571,7 @@ void Swapchain::Destroy() { m_images.clear(); m_image_views.clear(); m_image_acquired.clear(); + m_image_acquired_ticks.clear(); m_render_complete.clear(); } @@ -594,8 +597,11 @@ bool Swapchain::NeedsResize() const { m_window_extent.height != m_window.graphic_ctx.screen_height; } -Swapchain::Status Swapchain::AcquireNextImage() { +Swapchain::Status Swapchain::AcquireNextImage(CommandScheduler& scheduler) { EXIT_IF(m_handle == nullptr || m_frame_index >= m_image_acquired.size()); + // Repeated frames can wrap the semaphore ring without acquiring a new source frame. + // Wait for the submission that consumed this slot before signaling it again. + scheduler.Wait(m_image_acquired_ticks[m_frame_index]); m_image_index = static_cast(-1); const auto result = m_window.graphic_ctx.device.acquireNextImageKHR( m_handle, std::numeric_limits::max(), m_image_acquired[m_frame_index], nullptr, @@ -867,7 +873,9 @@ uint64_t Swapchain::Submit(CommandScheduler& scheduler) { SubmitInfo submit; submit.AddWait(m_image_acquired[m_frame_index], 1, vk::PipelineStageFlagBits::eTransfer); submit.AddSignal(m_render_complete[m_image_index]); - return scheduler.Submit(submit); + const auto tick = scheduler.Submit(submit); + m_image_acquired_ticks[m_frame_index] = tick; + return tick; } Swapchain::Status Swapchain::Present() { @@ -1023,7 +1031,7 @@ void Presenter::Impl::Present() { RecoverSwapchain(Swapchain::Status::Recreate); } for (uint32_t attempt = 0; attempt < 2; attempt++) { - auto status = swapchain.AcquireNextImage(); + auto status = swapchain.AcquireNextImage(present_scheduler); if (status != Swapchain::Status::Success) { RecoverSwapchain(status); continue;