如何测量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管道的执行时间 的相关文章

  • Android 中 localTime 和 localDate 的替代类有哪些? [复制]

    这个问题在这里已经有答案了 我想使用从 android API 获得的长值 该值将日期返回为长值 表示为自纪元以来的毫秒数 我需要使用像 isBefore plusDays isAfter 这样的方法 Cursor managedCurso
  • 在 Vulkan 中,图形队列系列与当前队列系列分离是否有益?

    据我所知 队列系列可能支持呈现到屏幕但不支持图形 假设我有一个同时支持图形和呈现的队列系列 以及另一个仅支持呈现的队列系列 我应该为两个进程使用第一个队列系列 还是应该将第一个队列系列委托给图形 将后者委托给呈现 或者这两种方法之间没有明显
  • 如何在 C++ 中使用 LoadLibrary(..) 调用 kernel32.dll 函数 GetTickCount()

    我正在寻找一个在 Windows 机器上获取以毫秒为单位的时间的函数 本质上 我想调用这个 WinAPI 函数 GetTickCount 但我陷入了 使 用 LoadLibrary n 调用 GetTickCount 函数 部分 我搜索了每
  • CLOCKS_PER_SEC 与 std::clock() 的结果不匹配

    我正在使用以下短程序来测试std clock include
  • 使用 JavaScript 解析时间

    这可能很简单 但我有点困惑如何做到这一点 我如何使用 JavaScript 仅解析以下内容中的时间ISO 8601 http en wikipedia org wiki ISO 8601日期字符串 2009 12 06T17 10 00 换
  • 除了在断点处停止之外,如何测量一大块代码的时间?

    我正在 Windows 上开发 C 游戏 模拟 图形应用程序 编辑开始 如果重要的话 我正在使用 Visual Studio 2013 编辑完 Setup 我正在使用 QueryPerformanceCounter 测量从一帧到下一帧的时间
  • 为什么 setInterval 会无限循环

    我正在尝试设置一个计时器 以便它显示用户的秒数和分钟剩余时间 并且我正在使用setInterval获取秒数 如果有 60 秒 则会从用户处减少 1 分钟 问题是我变得无限for每次我尝试这样做时都会循环 就像是 var userObj na
  • 在C中将毫秒转换为秒

    简单的C问题 如何正确 简洁地将毫秒转换为秒 有两个限制 我在这个微小的 C 编译器子集中没有浮点支持 我需要将秒四舍五入到最接近的秒 1 499ms 向下舍入 500 999ms 向上舍入 不需要关心负值 int mseconds 160
  • Internet Explorer 不渲染从 JQuery ajax 帖子返回的 html

    我有一个带有输入框的页面 其 onkeyup 根据输入的内容 搜索字段 触发 JQuery ajax 帖子 ajax 调用回发的 html 应该填充页面上的另一个 div 这是 jquery ajax 帖子 var o me results
  • Java 8:如何创建毫秒、微秒或纳秒的 DateTimeFormatter?

    我需要创建格式化程序来解析具有可选的毫秒 微米或纳秒分数的时间戳 例如 对于我的需求 我看到以下机会 DateTimeFormatter formatter new DateTimeFormatterBuilder append DateT
  • Time.valueOf 方法返回错误值

    我使用 Time valueOf 方法将字符串 09 00 00 转换为 Time 对象 如下所示 Time valueOf LocalTime parse 09 00 00 当我调用 getTime 来显示我得到的值时 28800000
  • 如何在 Go 中获取给定月份的第一个星期一?

    我正在尝试获取给定月份的第一个星期一 我能想到的最好方法是循环前 7 天 然后返回 Weekday Monday 有一个更好的方法吗 通过查看时间的 Weekday 您可以计算出第一个星期一 package main import fmt
  • Python获取正确时区的当前时间[重复]

    这个问题在这里已经有答案了 现在我用 import datetime print datetime datetime now strftime X 将当前时间显示为字符串 问题是 我的计算机正在运行Europe Berlin时区 这里不考虑
  • 为什么我的 AlarmManager 会立即触发?

    我正在尝试构建一个警报应用程序 我之前让闹钟工作过 我可以设置不同的时间 闹钟就会适当地响起 然后我将 ChangeAlarmActivity 的布局更改为 TableLayout 现在它不起作用 我没有碰代码 以下是我设置闹钟的方法 In
  • 多线程——更快的方法?

    我有一堂有吸气剂的课程getInt 和一个二传手setInt 在某个领域 比如说领域 Integer Int 一个类的一个对象 比如说SomeClass The setInt 这里是同步的 getInt isn t 我正在更新的值Int来自
  • 在 R 中将时间从数字转换为时间格式

    我从 xls 文件中读取数据 显然 时间格式不正确 如下 举例 0 3840277777777778 0 3847222222222222 0 3854166666666667 确实 他们应该是 09 12 09 13 09 13 我不知道
  • C 代码获取相对于 UTC 的本地时间偏移(以分钟为单位)?

    我没有找到一种简单的方法来获取本地时间和 UTC 时间之间的时间偏移 以分钟为单位 起初我打算使用tzset 但它不提供夏令时 根据手册页 如果夏令时有效 它只是一个不为零的整数 虽然通常是一个小时 但在某些国家 地区可能是半小时 我宁愿避
  • 在 PowerShell 中显示当前时间和时区

    我正在尝试使用时区显示系统上的当地时间 如何在任何系统上以最简单的方式显示这种格式的时间 时间 美国东部时间上午 8 00 34 我目前正在使用以下脚本 localtz System TimeZoneInfo Local Select Ob
  • 是否可以获取当前的 html5 视频时间帧(毫秒)?

    我正在尝试构建一个实时视频字幕编辑器 并要求 JS DOM 返回当前视频时间帧 以毫秒为单位 根据 DOM video currentTime 仅返回以秒为单位的值 有没有办法以毫秒为单位获取值 ontimeupdateevent 给出当前
  • Jquery 两个字段的时间差(以小时为单位)

    我的表单中有两个字段 用户可以在其中选择输入时间 start time end time 我想在更改这些字段时重新计算另一个字段的值 我想做的是获取两次之间的小时数 例如 如果我的开始时间为 5 30 结束时间为 7 50 我想将结果 2

随机推荐