如何测量Vulkan管道的执行时间

2024-04-19

Summary

我希望能够测量 GPU 上运行整个图形管道所花费的时间(以毫秒为单位)。目标:能够在优化代码之前/之后保存基准(下一步将是 mipmap 纹理)以查看改进。这在 OpenGL 中非常简单,但我是 Vulkan 新手,需要一些帮助。

我浏览了相关的现有答案(here https://stackoverflow.com/questions/49178289/measuring-the-time-of-the-fragment-shader-with-queries and here https://stackoverflow.com/questions/63121342/how-to-use-vulkan-timestamp-queries?rq=1),但它们实际上并没有多大帮助。而且我在任何地方都找不到代码示例,所以我敢在这里提问。

通过文档页面,我发现了一些我认为应该使用的函数,因此我设置了如下所示的功能:

1:创建查询池

void CreateQueryPool()
{
    VkQueryPoolCreateInfo createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
    createInfo.pNext = nullptr; // Optional
    createInfo.flags = 0; // Reserved for future use, must be 0!

    createInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
    createInfo.queryCount = mCommandBuffers.size() * 2; // REVIEW

    VkResult result = vkCreateQueryPool(mDevice, &createInfo, nullptr, &mTimeQueryPool);
    if (result != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create time query pool!");
    }
}

我的想法是queryCount = mCommandBuffers.size() * 2在渲染之前和之后有空间用于单独的查询时间戳,但我不知道这个假设是否正确。

2:记录命令缓冲区

// recording command buffer i:
vkCmdWriteTimestamp(mCommandBuffers[i], VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mTimeQueryPool, i);
// render pass ...
vkCmdWriteTimestamp(mCommandBuffers[i], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mTimeQueryPool, i);

vkCmdCopyQueryPoolResults(/* many parameters here */);

我正在寻找一些澄清:

  • 写入同一个查询索引会产生什么后果?我是否需要两个单独的查询池 - 一个用于渲染前,一个用于渲染后?
  • 我应该如何处理同步?我假设每个命令缓冲区都有一个单独的查询。
  • 对于包含查询结果的目标缓冲区,是否足以存储具有“主机可见位”的地方,或者我是否需要暂存内存以用于“仅设备可见”?我对这个也有点迷失。

我无法找到任何关于如何测量渲染时间的在线示例,但我只是假设这是一项常见的任务,肯定在某个地方一定有一个示例。


所以,感谢@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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何测量Vulkan管道的执行时间 的相关文章

  • 我在Excel中有3个时间段 - 我需要知道最长连续时间段的持续时间

    请帮忙 理想情况下 我真的很想仅使用公式来解决这个问题 而不是 VBA 或任何我认为 花哨 的东西 我所工作的项目为持续参与提供奖金 我们有三个 有时更多 参与时间段 这些时间段可能会重叠和 或可能有没有参与的空间 神奇的数字是 84 天的
  • tkinter 和 time.sleep

    我试图在等待 5 秒后删除文本框中的文本 但程序不会运行 并且会休眠其他所有内容 还有一种方法可以让我的文本框休眠 这样我就可以在文本冻结时运行其他代码 from time import time sleep from Tkinter im
  • 获取远程服务器的准确时间

    在 C 中 如何查询远程服务器的当前时间 类似的功能 net time servername 但返回包含秒的日期戳 Thanks 您可以使用网络远程TOD http msdn microsoft com en us library aa37
  • 减去 r 中的时间

    我有一个包含两个时间变量的数据集 EndVisitTime and BoxTime 我每天都会制作数据集 因此这些观察结果都是在一天内进行的 Date lt 2014 8 12 EndVisitTime lt c 00 00 32 00 0
  • 比较列表推导式和显式循环(3 个数组生成器比 1 个 for 循环快)

    我做了作业 无意中发现算法的速度出现了奇怪的不一致 这是相同函数的代码的 2 个版本 但有 1 个区别 在第一个版本中 我使用 3 次数组生成器来过滤某些数组 在第二个版本中 我使用 1 个 for 循环和 3 个 if 语句来执行相同的过
  • Vulkan:上传 3 通道图像到设备

    假设主机端有一个3通道图像 float或uint8 需要传输到设备图像 vkCmdCopyBufferToImage用于它 对于设备图像的格式 我看到两个选项 使用 R32G32B32A32 SFLOAT R8G8B8A8 SNORM 并将
  • 使用 print in 循环会减慢循环速度

    Using print in a loop slows down the loop Printing something I tried with Hello 100 times take 2 sec without it it takes
  • 如何在C中将UTC时间转换为本地时间?

    这是一个简单的问题 但解决方案似乎远非简单 我想知道如何从 UTC 转换为本地时间 我正在寻找一种标准的 C 解决方案 并且或多或少保证可以在任何位置的任何计算机上工作 我已仔细阅读以下链接 但在那里找不到解决方案 在C中将包含本地时间的字
  • 更精确的 distance_of_time_in_words

    distance of time in words很棒 但有时不够精细 我需要一个能够以文字报告准确时间距离的函数 例如 上午 7 50 到上午 10 10 的距离应该是 2 小时 20 分钟 而不是 大约 2 小时 或其他什么distan
  • extern auto 变量没有初始值设定项

    我需要在我的 C 程序中使用全局时间戳 std chrono high resolution clock now 我在头文件Header h中声明了它 include
  • srand 在 C++ 中太慢,它返回相同的数字

    我只是想拥有简单的 RndInt limit 函数 该函数将返回以限制作为限制的随机数 cout lt lt Enter higher limit of random range cin gt gt limit while limit 0
  • Golang - 如何在特定时间执行函数

    我需要在一天中的特定时间运行一个函数 例如 0010 0610 1210 1810 我目前的方法使用自动收报机for range time Tick 21600 time Second 我以这些时间间隔之一 例如 1210 手动启动该程序
  • 比较ios中的两个时间值? [复制]

    这个问题在这里已经有答案了 在我的应用程序中 我想检查当前时间是在变量中保存的时间之前还是之后 就像我的时间1一样time1 08 15 12 我的时间2是time2 18 12 8 所以我想比较 time1 和 time2 目前这些变量是
  • C++:如何通过时间和本地时间获取实际时间?

    我正在寻找一种在 C 中以 HH MM SS 方式节省时间的方法 我在这里看到它们有很多解决方案 经过一番研究后我选择了time and localtime 然而 似乎localtime函数有点棘手 因为它says http rabbit
  • 在无头模式下独立运行 Unity,同时捕获屏幕截图

    我需要创建一个在无头模式下运行的统一项目 使用 batchmode 命令 但它必须捕获屏幕截图 例如每一秒并将它们写到一个文件中 我知道在无头模式下 您需要强制调用 Camera Render 才能渲染任何内容 在捕获第一个屏幕截图后 时间
  • java:如何仅选择jtable中的一个单元格而不是整行

    在 jTable 中 我希望当用户单击单元格时 这句话会打印在屏幕上 I am cell in row X and column Y 其中 x 和 Y 是单击单元格的行和列 但我得到的是 例如 当我单击第 1 行和第 4 列中的单元格时 我
  • 如何通过使用内置的 Date 类来节省时间?

    这个问题的目的是使用内置的 Date 类收集日期 时间计算的解决方案 而不是编写冗长的复杂函数 我会自己写一些答案 如果有人想出一些非常聪明的东西 我会接受答案 但这主要是作为解决方案的集合 因为我经常看到处理日期的代码过于复杂 请记住这是
  • 仅以 int 形式显示和保存小时数

    如何仅显示小时并使用 int 变量 我的意思是打印时间 例如 20 30 44 PM 我只想存储小时 即 int 变量中的 20 小时 怎么做 有谁知道的话可以告诉我密码吗 谢谢 尝试使用日历get http docs oracle com
  • HTML5 Canvas 避免任何子像素渲染

    As seen here https stackoverflow com questions 7017998 html 5 canvas avoid fill behaviour on overlap我在画布中的亚像素精度方面遇到了一些问题
  • 从纪元到相对日期的秒数

    我正在处理自纪元以来的日期 并且已经得到了 例如 date 6928727 56235 我想将其转换为另一种相对格式 以便我能够将其转换为与纪元相关的格式 使用 time gmtime date 它返回 year 1970 mon 3 da

随机推荐