Context
The Bistro FBX import produces a 292 MB .zemesh file (2.8M triangles, no optimization). This causes:
- Cold-start hot-reload crash: 292 MB exceeds the 64 MB deserializer scratch
- Slow GPU rendering: vertices are in import order, not GPU-cache order
- Excessive VRAM usage
Proposed solution
Integrate meshoptimizer (https://github.com/zeux/meshoptimizer) — MIT, single header+source, used by Bevy, O3DE, Godot.
Run a 3-pass optimization pipeline in both FbxImporter and GltfImporter after building the flat vertex/index arrays, before SerializeMeshAssetFile:
Pass 1 — Vertex cache optimization
meshopt_optimizeVertexCache(indices.data(), indices.data(), index_count, vertex_count);
Reorders triangles for GPU post-transform vertex cache. Reduces vertex shader invocations.
Pass 2 — Overdraw reduction
meshopt_optimizeOverdraw(indices.data(), indices.data(), index_count,
vertex_positions, vertex_count, vertex_stride, 1.05f);
Clusters triangles by depth to reduce pixel shader overdraw.
Pass 3 — Vertex fetch optimization
meshopt_optimizeVertexFetch(vertices.data(), indices.data(), index_count,
vertices.data(), vertex_count, vertex_stride);
Reorders vertices to match the optimized index order — reduces cache misses on vertex fetch.
Expected impact
| Scene |
Before |
After (estimated) |
| Bistro exterior (.zemesh) |
292 MB |
~35–50 MB |
| Render frame time |
Baseline |
~15–30% improvement |
| GPU vertex cache hit rate |
~40–60% |
~85–95% |
Integration
- Add via
FetchContent_Declare in dependencies.cmake (single source file pattern like ufbx)
- Call after vertex dedup in
FbxImporter::ImportFile and GltfImporter::ImportFile
- Gate behind
ImportOptions.OptimizeMesh = true (default on) in the UI
Prerequisite
Fixes the cold-start crash from large .zemesh files (#668 follow-on).
Context
The Bistro FBX import produces a 292 MB .zemesh file (2.8M triangles, no optimization). This causes:
Proposed solution
Integrate meshoptimizer (https://github.com/zeux/meshoptimizer) — MIT, single header+source, used by Bevy, O3DE, Godot.
Run a 3-pass optimization pipeline in both
FbxImporterandGltfImporterafter building the flat vertex/index arrays, beforeSerializeMeshAssetFile:Pass 1 — Vertex cache optimization
meshopt_optimizeVertexCache(indices.data(), indices.data(), index_count, vertex_count);Reorders triangles for GPU post-transform vertex cache. Reduces vertex shader invocations.
Pass 2 — Overdraw reduction
meshopt_optimizeOverdraw(indices.data(), indices.data(), index_count, vertex_positions, vertex_count, vertex_stride, 1.05f);Clusters triangles by depth to reduce pixel shader overdraw.
Pass 3 — Vertex fetch optimization
meshopt_optimizeVertexFetch(vertices.data(), indices.data(), index_count, vertices.data(), vertex_count, vertex_stride);Reorders vertices to match the optimized index order — reduces cache misses on vertex fetch.
Expected impact
Integration
FetchContent_Declareindependencies.cmake(single source file pattern like ufbx)FbxImporter::ImportFileandGltfImporter::ImportFileImportOptions.OptimizeMesh = true(default on) in the UIPrerequisite
Fixes the cold-start crash from large .zemesh files (#668 follow-on).