如何使用 Vulkan 时间戳查询?

How to use Vulkan Timestamp Queries?

这是我尝试测量 GPU 工作负载的简化伪代码:

for(N) vkCmdDrawIndexed();

vkCmdWriteTimestamp(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
vkCmdWriteTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);

submit();
vkDeviceWaitIdle();
vkGetQueryPoolResults();

注意事项:

我创建了一个大数据集,视觉上渲染一帧大约需要 2 秒(~2000 毫秒)。但是计算出的时间只有 2(两个)不同的值 - 0.001024ms 或 0.002048ms,因此逐帧输出可能如下所示:

0.001024ms
0.001024ms
0.002048ms
0.001024ms
0.002048ms
0.002048ms
...

不知道你怎么样,但我发现这些值 非常 可疑。我对此没有答案。也许在那个时候,最后一个绘制命令到达命令处理器所有的工作都已经完成了,但是为什么是 1024 和 2048??

我尝试修改代码,将第一个时间戳移到上面,即:

vkCmdWriteTimestamp(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);

for(N) vkCmdDrawIndexed();
    
vkCmdWriteTimestamp(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);

现在,当预处理器命中时间戳命令时,它会立即写入查询值,因为没有先前的工作,也没有什么可等待的(记住空闲设备)。这次我有另一个更接近真值的值:

20.9336ms
20.9736ms
21.036ms
21.0196ms
20.9572ms
21.3586ms
...

哪个更好但仍然远远超出预期~2000ms。

这是怎么回事,当我设置时间戳时设备内部发生了什么,如何获得正确的值?

虽然 Vulkan 中的命令 可以 乱序执行(在某些限制范围内),但您不应该广泛地 期望 命令被执行出问题了。对于计时器查询尤其如此,如果它们被乱序执行,就其含义而言将是 不可靠

鉴于此,您的代码是说,“做一堆工作。然后查询管道开始准备执行新命令所需的时间,然后查询管道结束所需的时间要到达的管道。”好吧,一旦 大部分 工作完成,管道的开始可能只准备好执行新命令。

基本上,您认为发生的事情是这样的:

top        work work work work work work | timer
stage1                                    work work work work work work 
stage2                                        work work work work work work 
bottom                                            work work work work work work | timer

但是 没有什么 需要 GPU 以这种方式执行。几乎可以肯定实际发生的是:

time->
top        work work work work work work | timer
stage1         work work work work work work 
stage2             work work work work work work 
bottom                 work work work work work work | timer

所以你的两个计时器只完成了实际工作的一小部分。

想要的是这个:

top        timer | work work work work work work
stage1                 work work work work work work 
stage2                     work work work work work work 
bottom                         work work work work work work | timer

查询整组工作从开始到结束的时间。

所以将第一个查询放在您要测量其时间的工作之前。