@@ -617,15 +617,15 @@ VkResult SwapChainVkImpl::AcquireNextImage(DeviceContextVkImpl* pDeviceCtxVk)
617617 m_FrameCompleteFence->Wait (m_FrameIndex - m_SwapChainDesc.BufferCount );
618618 }
619619
620- VkSemaphore ImageAcquiredSemaphore = m_ImageAcquiredSemaphores[m_SemaphoreIndex]-> Get () ;
620+ RefCntAutoPtr<ManagedSemaphore>& ImageAcquiredSemaphore = m_ImageAcquiredSemaphores[m_SemaphoreIndex];
621621
622- VkResult res = vkAcquireNextImageKHR (LogicalDevice.GetVkDevice (), m_VkSwapChain, UINT64_MAX, ImageAcquiredSemaphore, VK_NULL_HANDLE, &m_BackBufferIndex);
622+ VkResult res = vkAcquireNextImageKHR (LogicalDevice.GetVkDevice (), m_VkSwapChain, UINT64_MAX, ImageAcquiredSemaphore-> Get () , VK_NULL_HANDLE, &m_BackBufferIndex);
623623 if (res == VK_SUCCESS)
624624 {
625625 // Next command in the device context must wait for the next image to be acquired.
626- // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore (6.4.2) .
626+ // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore.
627627 // Swapchain image may be used as render target or as destination for copy command.
628- pDeviceCtxVk->AddWaitSemaphore (m_ImageAcquiredSemaphores[m_SemaphoreIndex] , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
628+ pDeviceCtxVk->AddWaitSemaphore (ImageAcquiredSemaphore , VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
629629 if (!m_SwapChainImagesInitialized[m_BackBufferIndex])
630630 {
631631 // Vulkan validation layers do not like uninitialized memory.
@@ -661,13 +661,17 @@ void SwapChainVkImpl::Present(Uint32 SyncInterval)
661661 ITexture* pBackBuffer = GetCurrentBackBufferRTV ()->GetTexture ();
662662 pImmediateCtxVk->UnbindTextureFromFramebuffer (ClassPtrCast<TextureVkImpl>(pBackBuffer), false );
663663
664+ // To properly handle the case where vkAcquireNextImageKHR returns the same index twice in a row, use
665+ // a separate semaphore per swapchain image and index these semaphores using the index of the acquired image
666+ // https://github.com/DiligentGraphics/DiligentCore/issues/682
667+ RefCntAutoPtr<ManagedSemaphore>& DrawCompleteSemaphore = m_DrawCompleteSemaphores[m_BackBufferIndex];
664668 if (!m_IsMinimized)
665669 {
666670 // TransitionImageLayout() never triggers flush
667671 pImmediateCtxVk->TransitionImageLayout (pBackBuffer, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
668672 // The context can be empty if no render commands were issued by the app
669673 // VERIFY(pImmediateCtxVk->GetNumCommandsInCtx() != 0, "The context must not be flushed");
670- pImmediateCtxVk->AddSignalSemaphore (m_DrawCompleteSemaphores[m_SemaphoreIndex] );
674+ pImmediateCtxVk->AddSignalSemaphore (DrawCompleteSemaphore );
671675 pImmediateCtxVk->EnqueueSignal (m_FrameCompleteFence, m_FrameIndex++);
672676 }
673677
@@ -680,8 +684,8 @@ void SwapChainVkImpl::Present(Uint32 SyncInterval)
680684 PresentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
681685 PresentInfo.pNext = nullptr ;
682686 PresentInfo.waitSemaphoreCount = 1 ;
683- // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore (6.4.2)
684- VkSemaphore WaitSemaphore[] = {m_DrawCompleteSemaphores[m_SemaphoreIndex] ->Get ()};
687+ // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore
688+ VkSemaphore WaitSemaphore[] = {DrawCompleteSemaphore ->Get ()};
685689 PresentInfo.pWaitSemaphores = WaitSemaphore;
686690 PresentInfo.swapchainCount = 1 ;
687691 PresentInfo.pSwapchains = &m_VkSwapChain;
0 commit comments