From e038cb39fad83e6cce78b8836ca0d0b4bb909654 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Sun, 4 Oct 2026 21:26:48 +0800 Subject: [PATCH] =?UTF-8?q?SceneRenderer=20=E5=AE=9E=E4=BE=8B=E6=94=B6?= =?UTF-8?q?=E9=9B=86=E6=89=81=E5=B9=B3=E5=8C=96=EF=BC=9AbatchMats=20?= =?UTF-8?q?=E4=BB=8E=20vector=20=E6=94=B9=E4=B8=BA=E5=8D=95=20vect?= =?UTF-8?q?or=20=E6=8C=89=20(first,count)=20=E5=88=87=E7=89=87=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=A7=E5=9C=BA=E6=99=AF=E4=B8=8B=E6=89=B9?= =?UTF-8?q?=E6=AC=A1=E5=AE=9E=E4=BE=8B=E5=AD=98=E5=82=A8=E4=B8=8E=20cache?= =?UTF-8?q?=20=E5=91=BD=E4=B8=AD=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../include/Engine/Renderer/SceneRenderer.h | 11 ++-- Engine/src/Renderer/SceneRenderer.cpp | 62 +++++++++++-------- 2 files changed, 43 insertions(+), 30 deletions(-) diff --git a/Engine/include/Engine/Renderer/SceneRenderer.h b/Engine/include/Engine/Renderer/SceneRenderer.h index e0fbcca..e1c23fc 100644 --- a/Engine/include/Engine/Renderer/SceneRenderer.h +++ b/Engine/include/Engine/Renderer/SceneRenderer.h @@ -103,10 +103,11 @@ namespace Engine { u32 entityBits = 0; // 实体位模式(HitProxy id pass 消费) }; CullResult result; - // 每 batch 自己的实例列表(按 mesh 分组)。GPU 端 batch 语义 = - // firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh - // 时实例分散各处),扁平化成连续数组推迟到 appendView - std::vector> batchMats; + // 扁平实例数组(所有 batch 的实例共用一份连续内存)。 + // InstanceBatch::first/count 在本数组里切片——大场景下避免 + // vector 的小块分配(每个 batch 自己一个 vector,push_back + // 触发各自重分配,cache miss 严重)。每帧 clear() 复用容量 + std::vector instances; // 每 batch 实例距离累加(透明排序用:平均距离降序 = 远→近) std::vector batchDistSum; // batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的 @@ -117,7 +118,7 @@ namespace Engine { { result.batches.clear(); result.instanceCount = 0; - batchMats.clear(); + instances.clear(); // 容量保留,复用 batchDistSum.clear(); for (auto& e : lookup) e.fill(0); // 容量保留,置零复用 } diff --git a/Engine/src/Renderer/SceneRenderer.cpp b/Engine/src/Renderer/SceneRenderer.cpp index ce44079..36c496d 100644 --- a/Engine/src/Renderer/SceneRenderer.cpp +++ b/Engine/src/Renderer/SceneRenderer.cpp @@ -594,7 +594,7 @@ namespace Engine } auto tryAppend = [&](ViewScratch& s, const Frustum& frustum, - u32 lod) { + u32 lod) { if (!frustum.IntersectsSphere(worldCenter, worldRadius)) { return; } @@ -613,16 +613,18 @@ namespace Engine if (batchIdx == 0) { batchIdx = static_cast(s.result.batches.size()) + 1; lodSlots[lod] = batchIdx; - s.result.batches.push_back({mesh.get(), 0, 0, lod}); - s.batchMats.emplace_back(); + // first = 当前 instances 数组大小(batch 还没实例) + // push_back 实例后区间自然连续 + s.result.batches.push_back( + {mesh.get(), static_cast(s.instances.size()), 0, lod}); s.batchDistSum.push_back(0.0f); } // 实例归入自己的 batch(mesh 分组),保证同 batch - // 实例在扁平化后连续——firstInstance 语义的前提 + // 实例在扁平数组里连续——firstInstance 语义的前提 const u32 bi = batchIdx - 1; s.result.batches[bi].count++; - s.batchMats[bi].push_back( - {t.World(), prevWorld, skinBase, entKey}); + s.instances.push_back( + {t.World(), prevWorld, skinBase, entKey}); // 单 vector push s.batchDistSum[bi] += dist; // 透明排序用 s.result.instanceCount++; ++totalInstances; @@ -655,10 +657,15 @@ namespace Engine dst.batches.reserve(s.result.batches.size()); for (size_t i = 0; i < s.result.batches.size(); ++i) { InstanceBatch b = s.result.batches[i]; + // ✅ 保存原 first(s.instances 数组里的下标),b.first 改成 + // m_Frame 数组里的基址 + const u32 srcFirst = b.first; b.first = base; // 扁平化后的连续区间基址 base += b.count; dst.batches.push_back(b); - for (const auto& m : s.batchMats[i]) { + // 按 batch 的 (srcFirst, count) 切片扁平数组 + for (u32 j = 0; j < b.count; ++j) { + const auto& m = s.instances[srcFirst + j]; m_Frame.instanceModels.push_back(m.cur); m_Frame.prevInstanceModels.push_back(m.prev); // 与实例序平行对齐(shader 按 gl_InstanceIndex 取) @@ -669,6 +676,7 @@ namespace Engine } dst.instanceCount = s.result.instanceCount; }; + appendView(m_Frame.mainView, m_MainScratch); for (u32 c = 0; c < kMaxCascades; ++c) { appendView(m_Frame.shadowCascades[c], m_CascScratch[c]); @@ -681,6 +689,9 @@ namespace Engine // } // ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排 + // ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排。 + // 扁平 instances 数组按 order 顺序构建新数组(每个 batch 的 + // (first,count) 切片搬过去,新 first = 当前已搬数量)---- { auto& tb = m_TranslucentScratch.result.batches; if (tb.size() > 1) { @@ -690,30 +701,31 @@ namespace Engine return m_TranslucentScratch.batchDistSum[a] / tb[a].count > m_TranslucentScratch.batchDistSum[b] / tb[b].count; }); - CullResult sorted; - sorted.instanceCount = m_TranslucentScratch.result.instanceCount; - std::vector sortedMats; + // 临时构建(容量保留由 swap 进 m_TranslucentScratch 复用) + std::vector sortedBatches; + std::vector sortedInstances; std::vector sortedDist; + sortedBatches.reserve(tb.size()); + sortedDist.reserve(tb.size()); + // 同场景实例总数不变,capacity 可直接复用(reserve 不浪费) + sortedInstances.reserve(m_TranslucentScratch.instances.size()); + for (const u32 i : order) { - sorted.batches.push_back(tb[i]); + // 新 batch first = 已搬过去的新 instances 数量 + InstanceBatch b = tb[i]; + b.first = static_cast(sortedInstances.size()); + sortedBatches.push_back(b); sortedDist.push_back(m_TranslucentScratch.batchDistSum[i]); - for (auto& m : m_TranslucentScratch.batchMats[i]) { - sortedMats.push_back(std::move(m)); + // 按 (原 first, count) 切片搬运原扁平数组 + for (u32 j = 0; j < tb[i].count; ++j) { + sortedInstances.push_back(std::move( + m_TranslucentScratch.instances[tb[i].first + j])); } } - m_TranslucentScratch.result = std::move(sorted); + m_TranslucentScratch.result.batches = std::move(sortedBatches); m_TranslucentScratch.batchDistSum = std::move(sortedDist); - // batchMats 按新分组的 count 顺序切分重装(保留容量) - m_TranslucentScratch.batchMats.assign( - m_TranslucentScratch.result.batches.size(), {}); - u32 offset = 0; - for (size_t i = 0; i < m_TranslucentScratch.result.batches.size(); ++i) { - const u32 n = m_TranslucentScratch.result.batches[i].count; - m_TranslucentScratch.batchMats[i].assign( - std::make_move_iterator(sortedMats.begin() + offset), - std::make_move_iterator(sortedMats.begin() + offset + n)); - offset += n; - } + m_TranslucentScratch.instances = std::move(sortedInstances); + // instanceCount 不变(重排不丢实例) } } appendView(m_Frame.translucentView, m_TranslucentScratch);