feat: LOD 网格分级——meshopt 解析期简化 + 按距离/级联选级

vendored meshoptimizer 1.2(submodule 环境故障改直接 vendor)。Mesh
多 LOD:各级索引连续拼进同一 index buffer(共享顶点,LodRange 区间
选择),解析期 meshopt_simplify 生成链(LOD0=原始,每级目标减半,
相对误差 1e-2,小网格复用上级);都在 worker 线程,加载零额外卡顿。

SceneRenderer 按 (mesh, LOD) 聚合 batch(lookup key = 指针<<8|lod):
主视图按屏占比近似 radius/dist 选级(阈值 10/20 倍半径);阴影级联
按级联序降级(越远越简)。Draw 按 batch.lod 选索引区间。

A/B 对比(100 背包 10×10,RTX 5060 Laptop,vsync off):
- 纯背包:LOD 开 93-95 fps / 关 90-107 fps —— 无显著差异
- 加 SunTemple:LOD 开 52-56 / 关 51-55 —— 无显著差异
结论:当前场景瓶颈不在顶点数(5060 顶点吞吐富余),LOD 基础设施
保留(几何复杂资产/低端 GPU/更大场景生效),帧率问题转向 Tracy
GPU 时间线定位真实瓶颈。

坑:LodRange 记录"LOD 不足复用最后一级"时 first 不得继续累加
(buffer 无重复数据,累加必越界——VUID-08798 抓的)。
This commit is contained in:
2026-09-24 20:53:19 +08:00
parent 34b593f734
commit 5bdfb53566
125 changed files with 67608 additions and 29 deletions
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/python3
import re
import sys
# regenerate with wasmpack.py generate
table = [32, 0, 65, 2, 1, 106, 34, 33, 3, 128, 11, 4, 13, 64, 6, 253, 10, 7, 15, 116, 127, 5, 8, 12, 40, 16, 19, 54, 20, 9, 27, 255, 113, 17, 42, 67, 24, 23, 146, 148, 18, 14, 22, 45, 70, 69, 56, 114, 101, 21, 25, 63, 75, 136, 108, 28, 118, 29, 73, 115]
palette = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:;";
def encode(buffer):
result = ''
for ch in buffer.read():
if ch in table:
index = table.index(ch)
result += palette[index]
else:
result += palette[60 + ch // 64]
result += palette[ch % 64]
return result
def stats(buffer):
hist = [0] * 256
for ch in buffer.read():
hist[ch] += 1
result = [i for i in range(256)]
result.sort(key=lambda i: hist[i], reverse=True)
return result
def patch(target, stem, code):
with open(target, 'r') as f: content = f.read()
pattern = r'(["\']).*\1(;\s*//\s*embed! ' + stem + r')'
result = re.sub(pattern, r'\1' + re.escape(code) + r'\1\2', content)
with open(target, 'w') as f: f.write(result)
if len(sys.argv) >= 2 and sys.argv[1] == 'generate':
print(stats(sys.stdin.buffer)[:60])
elif len(sys.argv) >= 2 and sys.argv[1] == 'patch':
code = encode(sys.stdin.buffer)
patch(sys.argv[2], sys.argv[3], code)
else:
print(encode(sys.stdin.buffer))