Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
92 lines
16 KiB
Markdown
92 lines
16 KiB
Markdown
# VoxelForge R32 — audited architecture reframe
|
||
|
||
## Work estimate
|
||
|
||
The L60 matrix used 12 workers, clip radius 3 and LOD0–8. The owner export supplies `FullResClipLevels=2` and `CoarseTileCells=16`; `generateTile` uses 32 cells per axis below LOD2 and 16 thereafter. L60’s actual per-LOD tile counts are in the [matrix JSON](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix.json:84>) and its benchmark settings are in the [L60 report](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md:25>). The owner export itself says clip radius 6, so the benchmark’s explicit radius 3 override matters.
|
||
|
||
For `n` cells per axis, the dense baseline is `n³` candidate cells per tile. Marching Cubes needs `(n+1)³` corner values; this implementation adds a one-sample gradient halo on every side, for `(n+3)³` dense grid slots. That halo and the cell selection are visible in [`GenerateMesh`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp:428>) and the resolution choice in [`World::generateTile`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:2121>).
|
||
|
||
| LOD | Tiles: SW / profiles and motifs | Cells per tile | Candidate cells: SW / profiles and motifs | Dense grid slots with halo: SW / profiles and motifs |
|
||
|---:|---:|---:|---:|---:|
|
||
| 0 | 512 / 512 | 32,768 | 16,777,216 / 16,777,216 | 21,952,000 / 21,952,000 |
|
||
| 1 | 448 / 448 | 32,768 | 14,680,064 / 14,680,064 | 19,208,000 / 19,208,000 |
|
||
| 2 | 320 / 320 | 4,096 | 1,310,720 / 1,310,720 | 2,194,880 / 2,194,880 |
|
||
| 3 | 208 / 208 | 4,096 | 851,968 / 851,968 | 1,426,672 / 1,426,672 |
|
||
| 4 | 240 / 144 | 4,096 | 983,040 / 589,824 | 1,646,160 / 987,696 |
|
||
| 5 | 144 / 96 | 4,096 | 589,824 / 393,216 | 987,696 / 658,464 |
|
||
| 6 | 96 / 96 | 4,096 | 393,216 / 393,216 | 658,464 / 658,464 |
|
||
| 7 | 96 / 96 | 4,096 | 393,216 / 393,216 | 658,464 / 658,464 |
|
||
| 8 | 66 / 66 | 4,096 | 270,336 / 270,336 | 452,694 / 452,694 |
|
||
| **Total** | **2,130 / 1,986** | | **36,249,600 / 35,659,776** | **49,185,030 / 48,197,334** |
|
||
|
||
For example, SW LOD0 is `512 × 32³ = 16,777,216` candidate cells and `512 × 35³ = 21,952,000` halo slots. Profiles and motifs have the same counts through LOD3; their smaller total comes from fewer LOD4 and LOD5 tiles. Core MC corners alone are `(33³)` for full resolution and `(17³)` for coarse tiles: 40.248M SW points and 39.540M profile/motif points across tile grids.
|
||
|
||
These are **per-tile dense slots**, not unique world coordinates or guaranteed density-function calls. Neighboring grids can repeat coordinates, and whole-tile or block proofs can avoid work. LOD sample spacing in base 25 cm voxels is `1, 2, 8, 16, 32, 64, 128, 256, 512` for LOD0–8.
|
||
|
||
**L55 matches the dense arithmetic where it should.** Its SW LOD0 measured candidate/classified total is exactly 16,777,216. Its per-tile instrumentation reports 42,875 LOD0 sample slots; coarse tiles use 4,096 candidate cells and 6,859 slots. The L55 report also records FI at 14,483,456 candidate cells, equivalent to 442 full LOD0 grids, with 892,416 proof-skipped air cells. So 16.78M is a dense candidate ceiling, not the measured candidate count for every generator. L55’s static run had 2,370 tiles, while L60 has 2,130 or 1,986; its cumulative totals cannot be compared directly with the L60 table. The L55 report gives aggregate candidate counters for LOD0, not aggregate candidate totals for every LOD. See [L55 counters and worker totals](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux55/REPORT.md:13>).
|
||
|
||
## What one second demands
|
||
|
||
L60 whole-view medians were SW 1.669 s, profiles 1.303–1.317 s, caves 1.408 s, motifs 1.326 s, lattice 1.701 s and FI 4.926 s. The near ring was about 0.48–0.56 s for all except FI at 0.977 s ([L60 report](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux60/REPORT.md:27>)).
|
||
|
||
The following is an optimistic work budget: it takes L60 run 1’s summed worker-seconds and shape time, assumes perfect balance across all 12 workers, and holds shape time fixed. “Required cut” is the worker-time reduction needed to fit in one second under those assumptions. The actual cold times are higher than `shape + worker-sum/12`, so real cuts must be larger. Worker sums come from the [SW](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix-natural-sw-run1.log:81>), [FI](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix-floating-islands-run1.log:86>), [caves](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix-caves-run1.log:83>), [lattice](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix-lattice-run1.log:83>), [profile](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix-profile-periodicHills-run1.log:50>) and [motif](</home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l60/speed-matrix-motifs-run1.log:52>) logs.
|
||
|
||
| Family | L60 median | Run 1 worker-sum | Shape time | Worker-s budget for 1 s | Minimum worker-s cut |
|
||
|---|---:|---:|---:|---:|---:|
|
||
| SW | 1.669 s | 13.835 s | 0.412 s | 7.056 s | 6.779 s (49%) |
|
||
| Profiles | 1.303–1.317 s | 9.971–10.079 s | 0.412–0.423 s | 6.924–7.056 s | about 3.05 s (about 31%) |
|
||
| Caves | 1.408 s | 10.915 s | 0.402 s | 7.176 s | 3.739 s (34%) |
|
||
| Motifs | 1.326 s | 10.197 s | 0.412 s | 7.056 s | 3.141 s (31%) |
|
||
| Lattice | 1.701 s | 13.779 s | 0.461 s | 6.468 s | 7.311 s (53%) |
|
||
| FI | 4.926 s | 49.277 s | 0.450 s | 6.600 s | 42.677 s (87%) |
|
||
|
||
The dense counts do not make one second physically impossible: with about 0.4 s for shape setup, the remaining 0.6 s implies roughly 60M candidate cells/s and 82M halo slots/s across 12 workers. But that arithmetic omits field complexity, proof work, normals, mesh output, skirts and scheduling. L60’s measured worker sums show the actual required reductions above.
|
||
|
||
L55’s stage totals show why faster sampling alone is insufficient. SW spent 5.503 of 13.316 worker-s in grid work; FI spent 36.414 of 50.178 worker-s there. Even eliminating all measured grid work would leave, ideally, about 1.05 s for SW and 1.59 s for FI after the respective measured shape stages. The FI result needs cuts to other work too. These are optimistic calculations from the [L55 stage report](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux55/REPORT.md:30>).
|
||
|
||
## External benchmark audit
|
||
|
||
There is no public apples-to-apples per-core benchmark for SDF sampling plus CPU Marching Cubes among the named shipped games. The available numbers describe different workloads:
|
||
|
||
| Source | Published number or finding | What it measures |
|
||
|---|---|---|
|
||
| **Teardown** | No SDF/meshing throughput figure found. | The developer interview describes custom voxel volumes, CPU voxel collision and GPU rendering; it gives no per-core terrain meshing rate. [Game Developer interview](https://www.gamedeveloper.com/design/how-beautiful-voxels-laid-the-way-for-i-teardown-s-i-heist-y-framework) |
|
||
| **Dreams** | A pre-production PS4 prototype reports 10–100M voxel evaluations/s and over 99% culling. | This is not a per-CPU-core rate, and the source labels the prototype as not representative of the final game. Its MC prototype used compute shaders. [Alex Evans SIGGRAPH slides](https://advances.realtimerendering.com/s2015/AlexEvans_SIGGRAPH-2015-sml.pdf) |
|
||
| **Enshrouded** | No CPU voxel-meshing throughput figure found. | Keen identifies its proprietary Holistic Engine but publishes no relevant rate. [Official FAQ](https://enshrouded.com/en-US/faq) |
|
||
| **Astroneer** | No throughput figure found. | The developer discusses streaming and optimizing the Marching Cubes terrain-deformation algorithm, without a cell/s or per-core result. [Developer post](https://blog.astroneer.space/p/going-faster/) |
|
||
| **No Man’s Sky** | No throughput figure found. | The official update says dual Marching Cubes increased terrain-generation speed, without absolute timing or CPU-core data. [Worlds Part I](https://www.nomanssky.com/worlds-part-i-update/) |
|
||
| **Transvoxel** | 256 regular cases, 512 transition cases and 73 transition equivalence classes. | These are algorithm case counts, not throughput measurements. [Transvoxel paper](https://transvoxel.org/Transvoxel.pdf) |
|
||
|
||
A few sourced proxies help set scale, but none predicts VoxelForge’s rate:
|
||
|
||
- FastNoise2 reports 3D AVX2 rates of 494M value, 261M Perlin, 268M simplex and 52M cellular samples/s on a 4.9 GHz Intel 7820X using clang-cl. Its table does not state thread count, and measures noise alone—not this field graph plus meshing. [FastNoise2 benchmark](https://github.com/Auburn/FastNoise2)
|
||
- A single-thread CSG surface-sampling paper reports about 10–50M samples/s for simple CSG and 0.5–2M/s for complex CSG on an i9-9900K. It adaptively samples quadric surfaces; it does not densely evaluate an SDF lattice or mesh it. [Sampling from Quadric-Based CSG Surfaces](https://onlinelibrary.wiley.com/doi/10.1111/cgf.14380)
|
||
- A CPU block-voxel mesher author reports 0.48 ms to mesh a 32³ chunk. That uses block occupancy and face merging, rather than smooth SDF sampling and Marching Cubes. [Sector’s Edge mesher notes](https://vercidium.com/blog/further-voxel-world-optimisations/)
|
||
|
||
## Ranked architecture ideas mapped to VoxelForge
|
||
|
||
1. **Conservative surface support and active-cell selection — highest potential; XL.** `World::generateTile` calls [`ClassifyBoxDetailed`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:2344>) and the field bounds live in [`ShapeWorldInternal.h`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:5453>). Extend these proofs to identify a narrow, guaranteed surface band, then sample/mesh only cells whose bounds can cross zero. Current L55 proof skips were zero for SW LOD0 and 892,416 FI LOD0 air cells; those measured savings fall short of the 49% and 87% optimistic work cuts. **Expected gain is unmeasured.** A false sign proof creates holes; bounds must cover every field and edit. Preserve negative-is-solid semantics, exact boundary samples and the normal halo.
|
||
|
||
2. **Sparse brick/octree data flow — high potential, but coupled to the bounds above; XL.** [`GenerateMesh`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp:923>) currently allocates a dense grid and a dense `RequiredGridSamples` mask even when proof blocks skip work. Replace that mask and dense traversal with a compact list of active blocks/cells, while feeding the existing MC case logic. Sparse storage alone does not prove cells empty or reduce field calls; do not count its potential gain twice with surface support. Keep active-cell order canonical to protect cross-compiler hashes and seams.
|
||
|
||
3. **SIMD-first field evaluation plus an FI-specific cache pass — medium/high potential; L–XL.** [`DensityAtLatticeX8`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:3157>) handles native-only samples lane by lane; native island evaluation is also lane-based. FI’s [`IslandColumnCache`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/FloatingIslandsInternal.h:535>) has severe measured conflicts: 27.6M local and 3.7M shared conflicts, but only 151 ms measured lock wait, so the value of a cache redesign is not yet proven. Vectorize the expensive field operations and measure grid time by LOD; keep operation order and FMA disabled, and require exact scalar/batched agreement on MSVC too. SIMD applies mainly to field-grid time: L55’s measured grid stages alone are not enough to reach one second for FI.
|
||
|
||
4. **Parent-sample reuse and far-field precomputation — medium, uncertain cold-view gain; L–XL.** [`GenerateMesh`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp:902>) already reuses aligned samples when the grid step matches. Extend this only where parent/child grids or tile boundaries share exact world coordinates. Far tiles also run repair work, including [`AppendFarConnectorShells`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/FarShellsInternal.h:244>) and [`AppendFarStratumShells`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/FarShellsInternal.h:1051>). LOD rings have limited spatial overlap; caching helps repeated streaming in a session more clearly than a first cold view. Never drop caps/connectors without replacing their coverage guarantees. Cache keys and invalidation must include seed, layout and the affected edit diff.
|
||
|
||
5. **Shape setup reuse or optimization — measured ceiling about 0.4–0.46 s; M.** `World::compile` builds the shape, and the [`World` constructor](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/VoxelForgeCore.cpp:1093>) already creates per-world shared column caches. The logged shape stage is material to the simple generators. Eliminating it completely would still leave SW and lattice around 1.24–1.26 s and FI around 4.48 s by rough subtraction. A cache helps warm same-layout reuse; a cold miss still pays setup. Measure and reduce `World::compile` work before building another cache.
|
||
|
||
6. **Progressive refinement for VR and fast travel — useful for readiness, no cold-work reduction; S–M.** [`StreamingWorld` already ranks tiles by distance](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamingWorld.h:795>) and the viewer tracks neighborhood/full-detail readiness in [`StandaloneViewer.cpp`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StandaloneViewer.cpp:4773>). Prioritizing predicted travel direction can improve coverage during movement; it cannot reduce whole-view worker-seconds if all tiles are still generated. Keep old meshes until replacements are ready to avoid transition gaps. L60 moving-camera p95 was about 120 ms for most generators and 871 ms for FI, so this remains a separate streaming goal.
|
||
|
||
7. **Surface Nets or Dual Contouring — low priority for this target; XL.** This would replace the Marching Cubes extraction inside [`GenerateMesh`](</home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp:119>). It still needs field samples and has no measured VoxelForge gain. Expect changed topology, mesh hashes, normals and seam proofs; Dual Contouring also adds QEF arithmetic that must be made deterministic.
|
||
|
||
8. **Deterministic fixed-point GPU compute — low confidence; XL.** The mapped mesher is CPU code; there is no existing GPU path to tune. A deterministic port would need fixed-point field evaluation, classification, interpolation and mesh ordering on both CPU and GPU, with exact behavior for all layouts and edits. This could preserve peer regeneration only if the CPU reference and every GPU implementation produce identical results. No relevant VoxelForge benchmark supports the cost.
|
||
|
||
## Recommended path
|
||
|
||
1. Prototype conservative active-cell bounds with compact brick selection. First measure active cells and field samples by LOD for every generator, then test one surface family and FI against the existing exact outputs. The work budget requires at least a 49% cut for SW and 87% for FI, so stop early if the measured reduction is far below those floors.
|
||
2. Build a genuinely batched native field evaluator and run a scalar-versus-batched exact comparison, then measure grid-stage worker time by LOD. Include the FI cache conflict data in that experiment; do not assume its high conflict count translates into a large wall-time saving.
|
||
3. Profile FI’s non-grid work and far repair path separately, then target the dominant measured stage. The L55 arithmetic says that removing all grid work still leaves too much FI work for a one-second view; FI needs a dedicated second reduction beyond sampling.
|
||
|
||
**This path does not yet demonstrate one second.** It is a plausible research direction for profiles, motifs and caves, which need roughly 31–34% worker-time cuts under optimistic assumptions. SW and lattice need about 49–53%, and FI needs about 87% plus further gains to cover real scheduling overhead. I would treat one second for every cold view as a goal to prove with the same L60 matrix, not as the current forecast.
|
||
|
||
The main unknown is how many cells each generator can prove inactive with bounds that include edits and all cross-compiler edge cases. The public engine benchmarks do not answer that question, and the current VoxelForge measurements show that the existing proof path alone is insufficient. |