fix: Shadow pass 接入 GPUDriven batch,实例上限提至 16384
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
This commit is contained in:
@@ -158,6 +158,7 @@ namespace Engine {
|
|||||||
std::vector<std::string> m_PassNames; // 执行序 pass 名
|
std::vector<std::string> m_PassNames; // 执行序 pass 名
|
||||||
std::vector<f32> m_PassGpuMs; // 最近一帧各 pass 耗时
|
std::vector<f32> m_PassGpuMs; // 最近一帧各 pass 耗时
|
||||||
bool m_TimesPrimed[2] = {false, false}; // 每槽位首帧跳过读取
|
bool m_TimesPrimed[2] = {false, false}; // 每槽位首帧跳过读取
|
||||||
|
f32 m_CollectMs = 0.0f; // 实例收集 CPU 耗时(遥测)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
f32 GetExposure() const { return m_Exposure; }
|
f32 GetExposure() const { return m_Exposure; }
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ namespace Engine {
|
|||||||
OnInit();
|
OnInit();
|
||||||
|
|
||||||
while (m_Running && !m_Window->ShouldClose()) {
|
while (m_Running && !m_Window->ShouldClose()) {
|
||||||
|
const auto frameStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
m_Window->PollEvents();
|
m_Window->PollEvents();
|
||||||
|
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
@@ -90,6 +92,8 @@ namespace Engine {
|
|||||||
// 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变
|
// 延迟销毁在渲染前统一 flush,保证渲染遍历时池结构不变
|
||||||
m_Scene->FlushDestroyQueue();
|
m_Scene->FlushDestroyQueue();
|
||||||
|
|
||||||
|
const auto uiStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
// ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ----
|
// ---- UI 构建段:OnRenderOverlay 只"声明"控件,不直接碰 GPU ----
|
||||||
if (m_ImGuiLayer) {
|
if (m_ImGuiLayer) {
|
||||||
m_ImGuiLayer->NewFrame();
|
m_ImGuiLayer->NewFrame();
|
||||||
@@ -100,11 +104,28 @@ namespace Engine {
|
|||||||
if (m_ImGuiLayer) {
|
if (m_ImGuiLayer) {
|
||||||
m_ImGuiLayer->BuildDrawData();
|
m_ImGuiLayer->BuildDrawData();
|
||||||
}
|
}
|
||||||
|
const auto uiEnd = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
// ---- 主渲染(框架行为):RG 顺序 Forward → ImGui(loadOp=Load) →
|
// ---- 主渲染(框架行为):RG 顺序 Forward → ImGui(loadOp=Load) →
|
||||||
// Present,draw data 在 ImGui pass 里才真正提交 GPU ----
|
// Present,draw data 在 ImGui pass 里才真正提交 GPU ----
|
||||||
m_Renderer->Render(*m_Scene, *m_Assets);
|
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<f32, std::milli>(
|
||||||
|
uiEnd - uiStart).count();
|
||||||
|
const f32 renderMs = std::chrono::duration<f32, std::milli>(
|
||||||
|
frameEnd - uiEnd).count();
|
||||||
|
const f32 updateMs = std::chrono::duration<f32, std::milli>(
|
||||||
|
uiStart - frameStart).count();
|
||||||
|
const f32 totalMs = std::chrono::duration<f32, std::milli>(
|
||||||
|
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;
|
++m_FrameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -851,6 +851,7 @@ namespace Engine
|
|||||||
line += " " + m_PassNames[i] + "=" +
|
line += " " + m_PassNames[i] + "=" +
|
||||||
std::to_string(m_PassGpuMs[i]).substr(0, 5) + "ms";
|
std::to_string(m_PassGpuMs[i]).substr(0, 5) + "ms";
|
||||||
}
|
}
|
||||||
|
line += " collect=" + std::to_string(m_CollectMs).substr(0, 5) + "ms";
|
||||||
ENGINE_INFO("{}", line);
|
ENGINE_INFO("{}", line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -939,6 +940,7 @@ namespace Engine
|
|||||||
// 两趟遍历:先收集唯一 mesh(保持首现序),再逐 batch 追加实例矩阵,
|
// 两趟遍历:先收集唯一 mesh(保持首现序),再逐 batch 追加实例矩阵,
|
||||||
// 保证每个 batch 的矩阵区间连续(draw 用 firstInstance 指向起点)。
|
// 保证每个 batch 的矩阵区间连续(draw 用 firstInstance 指向起点)。
|
||||||
// TODO: 实体量大时改哈希聚合 + 单趟填充
|
// TODO: 实体量大时改哈希聚合 + 单趟填充
|
||||||
|
const auto collectStart = std::chrono::steady_clock::now();
|
||||||
m_InstanceBatches.clear();
|
m_InstanceBatches.clear();
|
||||||
m_InstanceModels.clear();
|
m_InstanceModels.clear();
|
||||||
bool instanceTruncated = false;
|
bool instanceTruncated = false;
|
||||||
@@ -990,6 +992,8 @@ namespace Engine
|
|||||||
m_ObjectBuffer->Update(frameSlot, m_InstanceModels.data(),
|
m_ObjectBuffer->Update(frameSlot, m_InstanceModels.data(),
|
||||||
m_InstanceModels.size() * sizeof(glm::mat4));
|
m_InstanceModels.size() * sizeof(glm::mat4));
|
||||||
}
|
}
|
||||||
|
m_CollectMs = std::chrono::duration<f32, std::milli>(
|
||||||
|
std::chrono::steady_clock::now() - collectStart).count();
|
||||||
|
|
||||||
// 2. acquire swapchain image
|
// 2. acquire swapchain image
|
||||||
u32 imageIndex = 0;
|
u32 imageIndex = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user