Vulkan 适当的帧同步
Vulkan proper frame synchronization
我在 Vulkan trying to synchronize frames API,但我遇到了一些奇怪的问题。我这样实现同步:
void RenderSystem::OnUpdate(const float deltaTime)
{
uint32_t frameIndex{};
auto result = SwapChain->AcquireNextImageIndex(PresentationCompleteSemaphore.get(),
nullptr,
&frameIndex);
InFlightFences[frameIndex]->Wait();
InFlightFences[frameIndex]->Reset();
if (result == VK_ERROR_OUT_OF_DATE_KHR)
{
Recreate();
return;
}
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
{
throw std::runtime_error("Error when acquiring next image...");
}
UpdateModelMatrix(deltaTime, frameIndex); // TODO: Remove this! For testing purposes only
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
GraphicsMainQueue.Submit({ TriangleCommandBuffers[frameIndex].get() },
{ PresentationCompleteSemaphore.get() },
{ RenderCompleteSemaphore.get() },
InFlightFences[frameIndex].get(),
waitStages);
result = PresentationQueue.Present({ RenderCompleteSemaphore.get() },
{ SwapChain.get() },
&frameIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || MainWindow->HasBeenResized())
Recreate();
else if (result != VK_SUCCESS)
throw std::runtime_error("Failed to present result!");
}
它对 Windows 10 非常有效。不幸的是,在 Linux Mint 上,它在某些情况下不起作用。首先,在 Linux 上移动 window 非常缓慢,有时会冻结整个 OS 一秒钟,但这不是最大的问题。关闭 window 调用 vkDeviceWaitIdle
并且...它会冻结应用程序。它永远不会开始响应,因为它将永远等待设备。验证层没有报告我的代码有任何问题。
我通过在我的函数底部移动栅栏同步部分解决了这个问题,但在我看来,这是一个次优的解决方案,因为我等待帧完成渲染,而不是准备下一帧。
// ...
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || MainWindow->HasBeenResized())
Recreate();
else if (result != VK_SUCCESS)
throw std::runtime_error("Failed to present result!");
InFlightFences[frameIndex]->Wait();
InFlightFences[frameIndex]->Reset();
}
如何正确同步 Windows 和 Linux 上的帧?我究竟做错了什么?我错过了什么?
您只有一组信号量。这意味着对这些信号量的访问可能不同步。
让我们看看没有干扰项的代码:
AcquireNextImageIndex( PresentationCompleteSemaphore, frameIndex );
InFlightFences[frameIndex].WaitAndReset();
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[frameIndex] );
Present( RenderCompleteSemaphore, frameIndex );
现在,我们怎么知道可以在 Acquire
上重用 PresentationCompleteSemaphore
? Submit
waits\unsignals吧,一定要写完。我们可以从栅栏中推断出这一点,但是栅栏等待发生在 Acquire
之后。因此,当 Acquire
尝试重用它时,信号量可能仍在使用中。这是一个可能的程序流程:
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 0;
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[0] );
// hazard; QSubmit still might be waiting on PresentationCompleteSemaphore
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 1;
我们怎么知道我们可以重用 RenderCompleteSemaphore
? QSubmit
只有在 Present
已经用完后才能使用它。目前唯一合理的推断方法是当 Acquire 返回相同的交换链图像时。这是一个可能的程序流程:
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 0;
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[0] );
Present( RenderCompleteSemaphore, 0 );
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 1;
// hazard; RenderCompleteSemaphore might still be waited on by Present
// which presented image 0, but we acquired image 1, so it might be async
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[1] );
我在 Vulkan trying to synchronize frames API,但我遇到了一些奇怪的问题。我这样实现同步:
void RenderSystem::OnUpdate(const float deltaTime)
{
uint32_t frameIndex{};
auto result = SwapChain->AcquireNextImageIndex(PresentationCompleteSemaphore.get(),
nullptr,
&frameIndex);
InFlightFences[frameIndex]->Wait();
InFlightFences[frameIndex]->Reset();
if (result == VK_ERROR_OUT_OF_DATE_KHR)
{
Recreate();
return;
}
else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR)
{
throw std::runtime_error("Error when acquiring next image...");
}
UpdateModelMatrix(deltaTime, frameIndex); // TODO: Remove this! For testing purposes only
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
GraphicsMainQueue.Submit({ TriangleCommandBuffers[frameIndex].get() },
{ PresentationCompleteSemaphore.get() },
{ RenderCompleteSemaphore.get() },
InFlightFences[frameIndex].get(),
waitStages);
result = PresentationQueue.Present({ RenderCompleteSemaphore.get() },
{ SwapChain.get() },
&frameIndex);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || MainWindow->HasBeenResized())
Recreate();
else if (result != VK_SUCCESS)
throw std::runtime_error("Failed to present result!");
}
它对 Windows 10 非常有效。不幸的是,在 Linux Mint 上,它在某些情况下不起作用。首先,在 Linux 上移动 window 非常缓慢,有时会冻结整个 OS 一秒钟,但这不是最大的问题。关闭 window 调用 vkDeviceWaitIdle
并且...它会冻结应用程序。它永远不会开始响应,因为它将永远等待设备。验证层没有报告我的代码有任何问题。
我通过在我的函数底部移动栅栏同步部分解决了这个问题,但在我看来,这是一个次优的解决方案,因为我等待帧完成渲染,而不是准备下一帧。
// ...
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || MainWindow->HasBeenResized())
Recreate();
else if (result != VK_SUCCESS)
throw std::runtime_error("Failed to present result!");
InFlightFences[frameIndex]->Wait();
InFlightFences[frameIndex]->Reset();
}
如何正确同步 Windows 和 Linux 上的帧?我究竟做错了什么?我错过了什么?
您只有一组信号量。这意味着对这些信号量的访问可能不同步。
让我们看看没有干扰项的代码:
AcquireNextImageIndex( PresentationCompleteSemaphore, frameIndex );
InFlightFences[frameIndex].WaitAndReset();
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[frameIndex] );
Present( RenderCompleteSemaphore, frameIndex );
现在,我们怎么知道可以在 Acquire
上重用 PresentationCompleteSemaphore
? Submit
waits\unsignals吧,一定要写完。我们可以从栅栏中推断出这一点,但是栅栏等待发生在 Acquire
之后。因此,当 Acquire
尝试重用它时,信号量可能仍在使用中。这是一个可能的程序流程:
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 0;
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[0] );
// hazard; QSubmit still might be waiting on PresentationCompleteSemaphore
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 1;
我们怎么知道我们可以重用 RenderCompleteSemaphore
? QSubmit
只有在 Present
已经用完后才能使用它。目前唯一合理的推断方法是当 Acquire 返回相同的交换链图像时。这是一个可能的程序流程:
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 0;
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[0] );
Present( RenderCompleteSemaphore, 0 );
AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 1;
// hazard; RenderCompleteSemaphore might still be waited on by Present
// which presented image 0, but we acquired image 1, so it might be async
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[1] );