diff --git a/Engine/include/Engine/Renderer/Renderer.h b/Engine/include/Engine/Renderer/Renderer.h index 8f3f92f..35214da 100644 --- a/Engine/include/Engine/Renderer/Renderer.h +++ b/Engine/include/Engine/Renderer/Renderer.h @@ -158,6 +158,7 @@ namespace Engine { std::vector m_PassNames; // 执行序 pass 名 std::vector m_PassGpuMs; // 最近一帧各 pass 耗时 bool m_TimesPrimed[2] = {false, false}; // 每槽位首帧跳过读取 + f32 m_CollectMs = 0.0f; // 实例收集 CPU 耗时(遥测) public: f32 GetExposure() const { return m_Exposure; } diff --git a/Engine/src/Core/Application.cpp b/Engine/src/Core/Application.cpp index 1a152a9..e2eef75 100644 --- a/Engine/src/Core/Application.cpp +++ b/Engine/src/Core/Application.cpp @@ -77,6 +77,8 @@ namespace Engine { OnInit(); while (m_Running && !m_Window->ShouldClose()) { + const auto frameStart = std::chrono::steady_clock::now(); + m_Window->PollEvents(); auto now = std::chrono::steady_clock::now(); @@ -90,6 +92,8 @@ namespace Engine { // 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变 m_Scene->FlushDestroyQueue(); + const auto uiStart = std::chrono::steady_clock::now(); + // ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ---- if (m_ImGuiLayer) { m_ImGuiLayer->NewFrame(); @@ -100,11 +104,28 @@ namespace Engine { if (m_ImGuiLayer) { m_ImGuiLayer->BuildDrawData(); } + const auto uiEnd = std::chrono::steady_clock::now(); // ---- 主渲染(框架行为):RG 顺序 Forward → ImGui(loadOp=Load) → // Present,draw data 在 ImGui pass 里才真正提交 GPU ---- m_Renderer->Render(*m_Scene, *m_Assets); + // CPU 侧遥测(每 120 帧一行):定位帧耗时的 CPU 归属 + const auto frameEnd = std::chrono::steady_clock::now(); + static u32 cpuTelemetryCount = 0; + if (++cpuTelemetryCount % 120 == 1) { + const f32 uiMs = std::chrono::duration( + uiEnd - uiStart).count(); + const f32 renderMs = std::chrono::duration( + frameEnd - uiEnd).count(); + const f32 updateMs = std::chrono::duration( + uiStart - frameStart).count(); + const f32 totalMs = std::chrono::duration( + frameEnd - frameStart).count(); + ENGINE_INFO("CPU times: update={:.2f}ms ui={:.2f}ms render={:.2f}ms total={:.2f}ms", + updateMs, uiMs, renderMs, totalMs); + } + ++m_FrameCount; } diff --git a/Engine/src/Renderer/Renderer.cpp b/Engine/src/Renderer/Renderer.cpp index 548fb51..066210f 100644 --- a/Engine/src/Renderer/Renderer.cpp +++ b/Engine/src/Renderer/Renderer.cpp @@ -851,6 +851,7 @@ namespace Engine line += " " + m_PassNames[i] + "=" + std::to_string(m_PassGpuMs[i]).substr(0, 5) + "ms"; } + line += " collect=" + std::to_string(m_CollectMs).substr(0, 5) + "ms"; ENGINE_INFO("{}", line); } } @@ -939,6 +940,7 @@ namespace Engine // 两趟遍历:先收集唯一 mesh(保持首现序),再逐 batch 追加实例矩阵, // 保证每个 batch 的矩阵区间连续(draw 用 firstInstance 指向起点)。 // TODO: 实体量大时改哈希聚合 + 单趟填充 + const auto collectStart = std::chrono::steady_clock::now(); m_InstanceBatches.clear(); m_InstanceModels.clear(); bool instanceTruncated = false; @@ -990,6 +992,8 @@ namespace Engine m_ObjectBuffer->Update(frameSlot, m_InstanceModels.data(), m_InstanceModels.size() * sizeof(glm::mat4)); } + m_CollectMs = std::chrono::duration( + std::chrono::steady_clock::now() - collectStart).count(); // 2. acquire swapchain image u32 imageIndex = 0;