feat: RenderGraph GPU timestamp profiling

- RG::SetQueryPool 可选注入;Execute 期每 pass 前后各写一枚
  writeTimestamp2(BOTTOM_OF_PIPE),索引按帧槽位分区 + 执行序排布
- 每帧录制开头 reset 本槽位查询区间(上一轮结果已在 fence 等待后读取)
- Renderer 在 WaitFence 后读 64bit 结果换算毫秒(timestampPeriod 缓存),
  经 GetPassNames/GetPassGpuMs 透出给 ImGui 面板显示
- 每槽位首帧跳过读取(查询尚未 primed,避免 VUID-09401)
This commit is contained in:
2026-09-22 19:25:42 +08:00
parent 908e688178
commit 41e4cc85d5
5 changed files with 120 additions and 0 deletions
+39
View File
@@ -607,14 +607,46 @@ namespace Engine::RG {
return entry.physical[frameSlot]->view;
}
void RenderGraph::SetQueryPool(vk::QueryPool pool, u32 queriesPerFrame)
{
m_QueryPool = pool;
m_QueriesPerFrame = queriesPerFrame;
}
std::vector<std::string> RenderGraph::GetPassNames() const
{
std::vector<std::string> names;
names.reserve(m_ExecutionOrder.size());
for (const u32 passIdx : m_ExecutionOrder) {
names.push_back(m_Passes[passIdx].name);
}
return names;
}
// ---------- 执行 ----------
void RenderGraph::Execute(const FrameContext& frame)
{
// GPU timestamp:可选。池由 Renderer 创建注入;每帧开头重置本槽位
// 区间(上一轮结果已在 fence 等待后被读取),每个 pass 前后各一枚
// 时间戳(BOTTOM_OF_PIPE),索引按执行序排布
u32 tsPos = 0;
if (m_QueryPool) {
frame.cmd.resetQueryPool(m_QueryPool,
frame.frameSlot * m_QueriesPerFrame,
m_QueriesPerFrame);
}
for (const u32 passIdx : m_ExecutionOrder) {
auto& pass = m_Passes[passIdx];
if (pass.culled) continue;
if (m_QueryPool) {
frame.cmd.writeTimestamp2(
vk::PipelineStageFlagBits2::eBottomOfPipe,
m_QueryPool, frame.frameSlot * m_QueriesPerFrame + tsPos * 2);
}
// barrier:按槽位状态挑选变体,批量提交
m_ScratchBarriers.clear();
for (const auto& bp : pass.barriers) {
@@ -652,6 +684,13 @@ namespace Engine::RG {
ctx.graph = this;
pass.exec(ctx);
}
if (m_QueryPool) {
frame.cmd.writeTimestamp2(
vk::PipelineStageFlagBits2::eBottomOfPipe,
m_QueryPool, frame.frameSlot * m_QueriesPerFrame + tsPos * 2 + 1);
}
++tsPos;
}
}