【问题标题】:Vulkan proper frame synchronizationVulkan 正确的帧同步
【发布时间】:2021-10-09 21:00:06
【问题描述】:

我是 Vulkan API 中的 trying to synchronize frames,但我遇到了一些奇怪的问题。我实现了这样的同步:

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 上移动窗口非常滞后,有时会冻结整个操作系统一秒钟,但这不是最大的问题。关闭窗口调用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 上正确同步帧?我究竟做错了什么?我错过了什么?

【问题讨论】:

    标签: c++ linux gpu vulkan


    【解决方案1】:

    您只有一组信号量。这意味着对这些信号量的访问可能会不同步。

    让我们看看没有干扰的代码:

    AcquireNextImageIndex( PresentationCompleteSemaphore, frameIndex );
    InFlightFences[frameIndex].WaitAndReset();
    QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[frameIndex] );
    Present( RenderCompleteSemaphore, frameIndex );
    

    现在,我们怎么知道我们可以在 Acquire 上重用 PresentationCompleteSemaphoreSubmit 等待\取消通知它,并且必须完成。我们可以从栅栏推断出这一点,但栅栏等待发生在Acquire 之后。所以信号量可能仍在使用中,而Acquire 试图重用它。这是一个可能的程序流程:

    AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 0;
    QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[0] );
    
    // hazard; QSubmit still might be waiting on PresentationCompleteSemaphore
    AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 1;
    

    我们怎么知道我们可以重复使用RenderCompleteSemaphoreQSubmit 只能在 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] );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-07
      • 2019-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-25
      • 1970-01-01
      相关资源
      最近更新 更多