所以,感谢@karlschultz,我设法让一些东西发挥作用。因此,为了防止其他人寻找相同的答案,我决定在这里发布我的发现。对于那里的 Vulkan 专家:如果我犯了明显的错误,请告诉我,我会在这里纠正它们!
查询池创建
我填写一个VkQueryPoolCreateInfo
结构如我的问题中所述,并让它queryCount
字段等于命令缓冲区数量的两倍,用于存储之前查询的空间and渲染后。
这里重要的是在使用查询之前重置查询池中的所有条目,and写入查询后重置查询。这需要进行一些更改:
1)询问图形队列是否支持时间戳
当选择图形队列系列时,结构体VkQueueFamilyProperties
有一个字段timestampValidBits
必须大于0,否则队列族不能用于时间戳查询!
2)确定时间戳周期
物理设备包含一个特殊值,该值指示时间戳查询加 1 所需的纳秒数。这对于将查询结果解释为例如纳秒或毫秒。该值是一个float
,并且可以通过调用来检索vkGetPhysicalDeviceProperties
并看着田野VkPhysicalDeviceProperties.limits.timestampPeriod
.
3)请求查询重置支持
在逻辑设备创建期间,必须填写一个结构并将其添加到pNext
链以启用主机查询重置功能:
VkDeviceCreateInfo createInfo{};
VkPhysicalDeviceHostQueryResetFeatures resetFeatures;
resetFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES;
resetFeatures.pNext = nullptr;
resetFeatures.hostQueryReset = VK_TRUE;
createInfo.pNext = &resetFeatures;
4) 记录命令缓冲区
时间戳查询应该在渲染过程的范围之外,如下所示。由于管道阶段(潜在的)时间重叠,无法测量单个着色器(例如片段着色器)的运行时间,只能测量整个管道或渲染通道范围之外的任何内容。
vkCmdWriteTimestamp(mCommandBuffers[i], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mTimeQueryPool, i * 2);
vkCmdBeginRenderPass(/* ... */);
// render here...
vkCmdEndRenderPass(mCommandBuffers[i]);
vkCmdWriteTimestamp(mCommandBuffers[i], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mTimeQueryPool, i * 2 + 1);
5) 检索查询结果
为此我们有两种方法:vkCmdCopyQueryPoolResults
and vkGetQueryPoolResults
。我选择使用后者,因为它极大地简化了设置并且不需要与 GPU 缓冲区同步。
鉴于我有一个交换链索引(在我的场景中,命令缓冲区索引也是如此!),我有这样的设置:
void FetchRenderTimeResults(uint32_t swapchainIndex)
{
uint64_t buffer[2];
VkResult result = vkGetQueryPoolResults(mDevice, mTimeQueryPool, swapchainIndex * 2, 2, sizeof(uint64_t) * 2, buffer, sizeof(uint64_t),
VK_QUERY_RESULT_64_BIT);
if (result == VK_NOT_READY)
{
return;
}
else if (result == VK_SUCCESS)
{
mTimeQueryResults[swapchainIndex] = buffer[1] - buffer[0];
}
else
{
throw std::runtime_error("Failed to receive query results!");
}
// Queries must be reset after each individual use.
vkResetQueryPool(mDevice, mTimeQueryPool, swapchainIndex * 2, 2);
}
变量mTimeQueryResults
指的是std::vector<uint64_t>
其中包含每个交换链的结果。我使用它通过使用步骤 2) 中确定的时间戳周期来计算每秒的平均渲染时间。
并且一定不要忘记通过调用来清理查询池vkDestroyQueryPool
.
省略了很多细节,对于像我这样的 Vulkan 菜鸟来说,这个设置很可怕,花了几天时间才弄清楚。希望这能减轻其他人的头痛。
更多信息在文档 https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#queries-timestamps.