为什么即使在三重缓冲时,我的 GPU 时间线中也会出现时间气泡?

Why are there time bubbles in my GPU timeline even when triple buffering?

我无法理解为什么在使用 PIX 时序捕获检查我的应用程序时我的 GPU 时间轴上有时间气泡。这是我正在谈论的其中一个时间泡泡的图片,以橙色突出显示:

时间线和我想象的完全不一样。由于我是三重缓冲,我希望 GPU 能够持续工作,帧之间没有任何时间间隔,因为 CPU 很容易在 GPU 处理完命令之前将命令提供给 GPU。相反,CPU 似乎并不领先 3 帧。似乎 CPU 在开始处理新帧之前一直在等待 GPU 完成。所以这让我想知道我的三重缓冲代码是否可能被破坏了?这是我移动到下一帧的代码:

void gpu_interface::next_frame()
{
    UINT64 current_frame_fence_value = get_frame_resource()->fence_value;
    UINT64 next_frame_fence_value = current_frame_fence_value + 1;

    check_hr(swapchain->Present(0, 0));
    check_hr(graphics_cmd_queue->Signal(fence.Get(), current_frame_fence_value));

    {
        // CPU and GPU frame-to-frame event.
        PIXEndEvent(graphics_cmd_queue.Get());
        PIXBeginEvent(graphics_cmd_queue.Get(), 0, "fence value: %d", next_frame_fence_value);
    }

    // Check if the next frame is ready to be rendered.
    // The GPU must have reached at least up to the fence value of the frame we're about to render.
    if (fence->GetCompletedValue() < current_frame_fence_value)
    {
        PIXBeginEvent(0, "CPU Waiting for GPU to reach fence value: %d", current_frame_fence_value);
        // Wait for the next frame resource to be ready
        fence->SetEventOnCompletion(current_frame_fence_value, fence_event);
        WaitForSingleObject(fence_event, INFINITE);
        PIXEndEvent();
    }
    // Next frame is ready to be rendered

    // Update the frame_index. GetCurrentBackBufferIndex() gets incremented after swapchain->Present() calls.
    frame_index = swapchain->GetCurrentBackBufferIndex();
    frames[frame_index].fence_value = next_frame_fence_value;
}

这是整个时序捕获:https://1drv.ms/u/s!AiGFMy6hVmtNgaky52n7QDrQ6o7V1A?e=MFc4xW

编辑:固定答案

void gpu_interface::next_frame()
{
    check_hr(swapchain->Present(0, 0));

    UINT64 current_frame_fence_value = get_frame_resource()->fence_value;
    UINT64 next_frame_fence_value = current_frame_fence_value + 1;

    check_hr(graphics_cmd_queue->Signal(fence.Get(), current_frame_fence_value));

    //// Update the frame_index. GetCurrentBackBufferIndex() gets incremented after swapchain->Present() calls.
    frame_index = swapchain->GetCurrentBackBufferIndex();

    // The GPU must have reached at least up to the fence value of the frame we're about to render.
    size_t minimum_fence = get_frame_resource()->fence_value;
    size_t completed = fence->GetCompletedValue();
    if (completed < minimum_fence)
    {
        PIXBeginEvent(0, "CPU Waiting for GPU to reach fence value: %d", minimum_fence);
        // Wait for the next frame resource to be ready
        fence->SetEventOnCompletion(minimum_fence, fence_event);
        WaitForSingleObject(fence_event, INFINITE);
        PIXEndEvent();
    }

    frames[frame_index].fence_value = next_frame_fence_value;

    {
        // CPU and GPU frame-to-frame event.
        PIXEndEvent(graphics_cmd_queue.Get());
        PIXBeginEvent(graphics_cmd_queue.Get(), 0, "fence value: %d", next_frame_fence_value);
    }
}

定时抓取正确代码:https://1drv.ms/u/s!AiGFMy6hVmtNgakzGizTiA_s-FwPqA?e=qIHHTw

您用 current_frame_fence_value 向队列发出信号,并在检查后立即

if (fence->GetCompletedValue() < current_frame_fence_value)

如果围栏完成了那个值。您需要检查下一帧的栅栏值以查看是否可以继续,即更新 frame_indexfence_values[frame_index]。它会是这样的:

void gpu_interface::next_frame()
{
    check_hr(swapchain->Present(0, 0));

    UINT64 current_frame_fence_value = get_frame_resource()->fence_value;

    check_hr(graphics_cmd_queue->Signal(fence.Get(), current_frame_fence_value));

    UINT64 next_frame_fence_value = current_frame_fence_value + 1;

    frame_index = swapchain->GetCurrentBackBufferIndex();

    // The GPU must have reached at least up to the fence value of the frame we're about to render.
    //current_frame_fence_value is not the fence value of the frame you are about the render, it is fence_values[frame_index]
    //note that frame_index is updated before this call
    if (fence->GetCompletedValue() < fence_values[frame_index])
    {
        // Wait for the next frame resource to be ready
        fence->SetEventOnCompletion(fence_values[frame_index], fence_event);
        WaitForSingleObject(fence_event, INFINITE);
    }

    frames[frame_index].fence_value = next_frame_fence_value;
}

尝试记下前几帧的围栏值,看看效果如何。