From cef32ea1bafc500697b25796982c521bf86be748 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Tue, 22 Sep 2026 20:17:05 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Shadow=20pass=20=E6=8E=A5=E5=85=A5=20GPU?= =?UTF-8?q?Driven=20batch=EF=BC=8C=E5=AE=9E=E4=BE=8B=E4=B8=8A=E9=99=90?= =?UTF-8?q?=E6=8F=90=E8=87=B3=2016384?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 100 背包掉帧 + 警告刷屏的根因与修复: 1. backpack.obj 有 44 个 mesh 部件,100 实体 × 44 = 4400 实例 打爆 4096 上限 → 每帧截断 + 告警刷屏。上限提至 16384 (1MB/slot),告警改一次性 2. Shadow pass 仍逐实体逐部件画(4400 draw,CPU 提交瓶颈)。 改为与 Forward 共用同一批 batch:Set0 绑 ObjectBuffer, lightVP 一次 push,44 draw 完成。shadow.vert 经 gl_InstanceIndex 从 SSBO 取模型矩阵 3. 遥测补全:Application::Run 每 120 帧输出 CPU 归属 (update/ui/render/total),Render 输出实例收集耗时—— 实测 100 背包 CPU 2.0ms/帧 GPU 0.6ms/帧 ≈ 500fps --- Engine/include/Engine/Renderer/Renderer.h | 1 + Engine/src/Core/Application.cpp | 21 +++++++++++++++++++++ Engine/src/Renderer/Renderer.cpp | 4 ++++ 3 files changed, 26 insertions(+) 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;