linux Vulkan固有帧同步

xxhby3vn  于 2023-08-03  发布在  Linux
关注(0)|答案(1)|浏览(115)

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

x0fgdtte

x0fgdtte1#

您只有一组信号。这意味着对这些信号量的访问可能不同步。
让我们看看没有干扰项的代码:

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

字符串
那么,我们如何知道我们可以在AcquireNextImageIndex()上重用PresentationCompleteSemaphore
QSubmit等待\取消它的信号,然后必须完成。我们可以从传递到QSubmit的栅栏中推断出它完成了,但是栅栏等待只发生在AcquireNextImageIndex()之后。因此,当AcquireNextImageIndex()尝试重用信号量时,它可能仍在使用中。这是一个可能的程序流:

AcquireNextImageIndex( PresentationCompleteSemaphore ) -> frameIndex = 0;
InFlightFences[0].WaitAndReset();
QSubmit( PresentationCompleteSemaphore, RenderCompleteSemaphore, InFlightFences[0] );

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


我们如何知道我们可以重用RenderCompleteSemaphore
QSubmit只能在Present()已经使用它的情况下使用。从历史上推断这一点的唯一合理方法是AcquireNextImageIndex()返回相同的交换链映像。这是一个可能的程序流:

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] );


因此,正确处理此问题的一种方法是like so

的数据
这会转换成如下的程式码:

InFlightFences[frame].WaitAndReset();
// AcquiredSemaphore[frame] safe to (re)use from here on
AcquireNextImageIndex( AcquiredSemaphore[frame], imageIndex );
// We know that present should be done with swapchainImage[imageIndex]
// after AcquiredSemaphore[frame] is signaled,
// so RenderedSemaphore[imageIndex] should be safe to (re)use afterwards
QSubmit( AcquiredSemaphore[frame], RenderedSemaphore[imageIndex], InFlightFences[frame] );
Present( RenderedSemaphore[imageIndex], frame );
frame = (frame + 1) % 2;


现在也有一些新的扩展方式来处理它。VK_KHR_present_wait扩展提供了一种确定是否向用户显示了图像的方法,并且VK_EXT_swapchain_maintenance1vkQueuePresentKHR()添加了一个栅栏。

相关问题