SceneRenderer 实例收集扁平化:batchMats 从 vector<vector> 改为单 vector 按 (first,count) 切片,优化大场景下批次实例存储与 cache 命中率
This commit is contained in:
@@ -103,10 +103,11 @@ namespace Engine {
|
|||||||
u32 entityBits = 0; // 实体位模式(HitProxy id pass 消费)
|
u32 entityBits = 0; // 实体位模式(HitProxy id pass 消费)
|
||||||
};
|
};
|
||||||
CullResult result;
|
CullResult result;
|
||||||
// 每 batch 自己的实例列表(按 mesh 分组)。GPU 端 batch 语义 =
|
// 扁平实例数组(所有 batch 的实例共用一份连续内存)。
|
||||||
// firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh
|
// InstanceBatch::first/count 在本数组里切片——大场景下避免
|
||||||
// 时实例分散各处),扁平化成连续数组推迟到 appendView
|
// vector<vector> 的小块分配(每个 batch 自己一个 vector,push_back
|
||||||
std::vector<std::vector<InstanceMatrices>> batchMats;
|
// 触发各自重分配,cache miss 严重)。每帧 clear() 复用容量
|
||||||
|
std::vector<InstanceMatrices> instances;
|
||||||
// 每 batch 实例距离累加(透明排序用:平均距离降序 = 远→近)
|
// 每 batch 实例距离累加(透明排序用:平均距离降序 = 远→近)
|
||||||
std::vector<f32> batchDistSum;
|
std::vector<f32> batchDistSum;
|
||||||
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
|
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
|
||||||
@@ -117,7 +118,7 @@ namespace Engine {
|
|||||||
{
|
{
|
||||||
result.batches.clear();
|
result.batches.clear();
|
||||||
result.instanceCount = 0;
|
result.instanceCount = 0;
|
||||||
batchMats.clear();
|
instances.clear(); // 容量保留,复用
|
||||||
batchDistSum.clear();
|
batchDistSum.clear();
|
||||||
for (auto& e : lookup) e.fill(0); // 容量保留,置零复用
|
for (auto& e : lookup) e.fill(0); // 容量保留,置零复用
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -613,16 +613,18 @@ namespace Engine
|
|||||||
if (batchIdx == 0) {
|
if (batchIdx == 0) {
|
||||||
batchIdx = static_cast<u32>(s.result.batches.size()) + 1;
|
batchIdx = static_cast<u32>(s.result.batches.size()) + 1;
|
||||||
lodSlots[lod] = batchIdx;
|
lodSlots[lod] = batchIdx;
|
||||||
s.result.batches.push_back({mesh.get(), 0, 0, lod});
|
// first = 当前 instances 数组大小(batch 还没实例)
|
||||||
s.batchMats.emplace_back();
|
// push_back 实例后区间自然连续
|
||||||
|
s.result.batches.push_back(
|
||||||
|
{mesh.get(), static_cast<u32>(s.instances.size()), 0, lod});
|
||||||
s.batchDistSum.push_back(0.0f);
|
s.batchDistSum.push_back(0.0f);
|
||||||
}
|
}
|
||||||
// 实例归入自己的 batch(mesh 分组),保证同 batch
|
// 实例归入自己的 batch(mesh 分组),保证同 batch
|
||||||
// 实例在扁平化后连续——firstInstance 语义的前提
|
// 实例在扁平数组里连续——firstInstance 语义的前提
|
||||||
const u32 bi = batchIdx - 1;
|
const u32 bi = batchIdx - 1;
|
||||||
s.result.batches[bi].count++;
|
s.result.batches[bi].count++;
|
||||||
s.batchMats[bi].push_back(
|
s.instances.push_back(
|
||||||
{t.World(), prevWorld, skinBase, entKey});
|
{t.World(), prevWorld, skinBase, entKey}); // 单 vector push
|
||||||
s.batchDistSum[bi] += dist; // 透明排序用
|
s.batchDistSum[bi] += dist; // 透明排序用
|
||||||
s.result.instanceCount++;
|
s.result.instanceCount++;
|
||||||
++totalInstances;
|
++totalInstances;
|
||||||
@@ -655,10 +657,15 @@ namespace Engine
|
|||||||
dst.batches.reserve(s.result.batches.size());
|
dst.batches.reserve(s.result.batches.size());
|
||||||
for (size_t i = 0; i < s.result.batches.size(); ++i) {
|
for (size_t i = 0; i < s.result.batches.size(); ++i) {
|
||||||
InstanceBatch b = s.result.batches[i];
|
InstanceBatch b = s.result.batches[i];
|
||||||
|
// ✅ 保存原 first(s.instances 数组里的下标),b.first 改成
|
||||||
|
// m_Frame 数组里的基址
|
||||||
|
const u32 srcFirst = b.first;
|
||||||
b.first = base; // 扁平化后的连续区间基址
|
b.first = base; // 扁平化后的连续区间基址
|
||||||
base += b.count;
|
base += b.count;
|
||||||
dst.batches.push_back(b);
|
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.instanceModels.push_back(m.cur);
|
||||||
m_Frame.prevInstanceModels.push_back(m.prev);
|
m_Frame.prevInstanceModels.push_back(m.prev);
|
||||||
// 与实例序平行对齐(shader 按 gl_InstanceIndex 取)
|
// 与实例序平行对齐(shader 按 gl_InstanceIndex 取)
|
||||||
@@ -669,6 +676,7 @@ namespace Engine
|
|||||||
}
|
}
|
||||||
dst.instanceCount = s.result.instanceCount;
|
dst.instanceCount = s.result.instanceCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
appendView(m_Frame.mainView, m_MainScratch);
|
appendView(m_Frame.mainView, m_MainScratch);
|
||||||
for (u32 c = 0; c < kMaxCascades; ++c) {
|
for (u32 c = 0; c < kMaxCascades; ++c) {
|
||||||
appendView(m_Frame.shadowCascades[c], m_CascScratch[c]);
|
appendView(m_Frame.shadowCascades[c], m_CascScratch[c]);
|
||||||
@@ -681,6 +689,9 @@ namespace Engine
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排
|
// ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排
|
||||||
|
// ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排。
|
||||||
|
// 扁平 instances 数组按 order 顺序构建新数组(每个 batch 的
|
||||||
|
// (first,count) 切片搬过去,新 first = 当前已搬数量)----
|
||||||
{
|
{
|
||||||
auto& tb = m_TranslucentScratch.result.batches;
|
auto& tb = m_TranslucentScratch.result.batches;
|
||||||
if (tb.size() > 1) {
|
if (tb.size() > 1) {
|
||||||
@@ -690,30 +701,31 @@ namespace Engine
|
|||||||
return m_TranslucentScratch.batchDistSum[a] / tb[a].count >
|
return m_TranslucentScratch.batchDistSum[a] / tb[a].count >
|
||||||
m_TranslucentScratch.batchDistSum[b] / tb[b].count;
|
m_TranslucentScratch.batchDistSum[b] / tb[b].count;
|
||||||
});
|
});
|
||||||
CullResult sorted;
|
// 临时构建(容量保留由 swap 进 m_TranslucentScratch 复用)
|
||||||
sorted.instanceCount = m_TranslucentScratch.result.instanceCount;
|
std::vector<InstanceBatch> sortedBatches;
|
||||||
std::vector<ViewScratch::InstanceMatrices> sortedMats;
|
std::vector<ViewScratch::InstanceMatrices> sortedInstances;
|
||||||
std::vector<f32> sortedDist;
|
std::vector<f32> sortedDist;
|
||||||
|
sortedBatches.reserve(tb.size());
|
||||||
|
sortedDist.reserve(tb.size());
|
||||||
|
// 同场景实例总数不变,capacity 可直接复用(reserve 不浪费)
|
||||||
|
sortedInstances.reserve(m_TranslucentScratch.instances.size());
|
||||||
|
|
||||||
for (const u32 i : order) {
|
for (const u32 i : order) {
|
||||||
sorted.batches.push_back(tb[i]);
|
// 新 batch first = 已搬过去的新 instances 数量
|
||||||
|
InstanceBatch b = tb[i];
|
||||||
|
b.first = static_cast<u32>(sortedInstances.size());
|
||||||
|
sortedBatches.push_back(b);
|
||||||
sortedDist.push_back(m_TranslucentScratch.batchDistSum[i]);
|
sortedDist.push_back(m_TranslucentScratch.batchDistSum[i]);
|
||||||
for (auto& m : m_TranslucentScratch.batchMats[i]) {
|
// 按 (原 first, count) 切片搬运原扁平数组
|
||||||
sortedMats.push_back(std::move(m));
|
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);
|
m_TranslucentScratch.batchDistSum = std::move(sortedDist);
|
||||||
// batchMats 按新分组的 count 顺序切分重装(保留容量)
|
m_TranslucentScratch.instances = std::move(sortedInstances);
|
||||||
m_TranslucentScratch.batchMats.assign(
|
// instanceCount 不变(重排不丢实例)
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appendView(m_Frame.translucentView, m_TranslucentScratch);
|
appendView(m_Frame.translucentView, m_TranslucentScratch);
|
||||||
|
|||||||
Reference in New Issue
Block a user