fix: GPUDriven batch 实例连续性——多实体共享 mesh 时实例数组错乱
实例数组按实体序收集([实体A的mesh][实体B的mesh]),而 GPU 端 batch 语义要求同一 mesh 的实例连续(firstInstance 基址 + gl_InstanceIndex 直查 SSBO)。多实体共享 Model 后 mesh i 的各实例 分散在数组各处,batch.first+i 落到别的 mesh 的变换上——第二颗 背包的几何全画到第一颗的相邻 mesh 位置(碎裂/缺失/近看缺块)。 修:剔除期实例先归入各自 batch(ViewScratch 持有 per-batch 实例 列表),拼接时按 batch 扁平化为连续区间,batch.first = 扁平化基址。 单实体路径行为不变(每 mesh 单实例天然连续),容量跨帧复用。
This commit is contained in:
@@ -68,16 +68,19 @@ namespace Engine {
|
||||
// ---- 剔除中间量(每帧 clear 复用,拼接进 m_Frame 后保留容量)----
|
||||
struct ViewScratch {
|
||||
CullResult result;
|
||||
std::vector<glm::mat4> worlds;
|
||||
std::vector<glm::mat4> prevWorlds;
|
||||
// 每 batch 自己的实例列表(按 mesh 分组)。GPU 端 batch 语义 =
|
||||
// firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh
|
||||
// 时实例分散各处),扁平化成连续数组推迟到 appendView
|
||||
std::vector<std::vector<glm::mat4>> batchWorlds;
|
||||
std::vector<std::vector<glm::mat4>> batchPrevWorlds;
|
||||
std::unordered_map<Mesh*, u32> lookup;
|
||||
|
||||
void Clear()
|
||||
{
|
||||
result.batches.clear();
|
||||
result.instanceCount = 0;
|
||||
worlds.clear();
|
||||
prevWorlds.clear();
|
||||
batchWorlds.clear();
|
||||
batchPrevWorlds.clear();
|
||||
lookup.clear();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -310,13 +310,17 @@ namespace Engine
|
||||
const auto [it, inserted] = s.lookup.emplace(
|
||||
mesh.get(), static_cast<u32>(s.result.batches.size()));
|
||||
if (inserted) {
|
||||
s.result.batches.push_back({mesh.get(),
|
||||
static_cast<u32>(s.worlds.size()), 0});
|
||||
s.result.batches.push_back({mesh.get(), 0, 0});
|
||||
s.batchWorlds.emplace_back();
|
||||
s.batchPrevWorlds.emplace_back();
|
||||
}
|
||||
s.result.batches[it->second].count++;
|
||||
// 实例归入自己的 batch(mesh 分组),保证同 batch
|
||||
// 实例在扁平化后连续——firstInstance 语义的前提
|
||||
const u32 batchIdx = it->second;
|
||||
s.result.batches[batchIdx].count++;
|
||||
s.batchWorlds[batchIdx].push_back(t.World());
|
||||
s.batchPrevWorlds[batchIdx].push_back(prevWorld);
|
||||
s.result.instanceCount++;
|
||||
s.worlds.push_back(t.World());
|
||||
s.prevWorlds.push_back(prevWorld);
|
||||
++totalInstances;
|
||||
};
|
||||
|
||||
@@ -327,21 +331,25 @@ namespace Engine
|
||||
}
|
||||
});
|
||||
|
||||
// ---- 4. 拼接:[主段|级联0|级联1|级联2],batch.first 加段基址 ----
|
||||
// ---- 4. 拼接:[主段|级联0|级联1|级联2],batch 内实例连续扁平化 ----
|
||||
auto appendView = [&](CullResult& dst, ViewScratch& s) {
|
||||
const u32 base = static_cast<u32>(m_Frame.instanceModels.size());
|
||||
u32 base = static_cast<u32>(m_Frame.instanceModels.size());
|
||||
dst.batches.clear();
|
||||
dst.batches.reserve(s.result.batches.size());
|
||||
for (auto& b : s.result.batches) {
|
||||
b.first += base;
|
||||
for (size_t i = 0; i < s.result.batches.size(); ++i) {
|
||||
InstanceBatch b = s.result.batches[i];
|
||||
b.first = base; // 扁平化后的连续区间基址
|
||||
base += b.count;
|
||||
dst.batches.push_back(b);
|
||||
m_Frame.instanceModels.insert(m_Frame.instanceModels.end(),
|
||||
s.batchWorlds[i].begin(),
|
||||
s.batchWorlds[i].end());
|
||||
m_Frame.prevInstanceModels.insert(
|
||||
m_Frame.prevInstanceModels.end(),
|
||||
s.batchPrevWorlds[i].begin(),
|
||||
s.batchPrevWorlds[i].end());
|
||||
}
|
||||
dst.instanceCount = s.result.instanceCount;
|
||||
m_Frame.instanceModels.insert(m_Frame.instanceModels.end(),
|
||||
s.worlds.begin(), s.worlds.end());
|
||||
m_Frame.prevInstanceModels.insert(m_Frame.prevInstanceModels.end(),
|
||||
s.prevWorlds.begin(),
|
||||
s.prevWorlds.end());
|
||||
};
|
||||
appendView(m_Frame.mainView, m_MainScratch);
|
||||
for (u32 c = 0; c < kMaxCascades; ++c) {
|
||||
|
||||
Reference in New Issue
Block a user