Core.Caves.PrimitiveField v1, opt-in per strate (legacy TunnelNetwork assets stay legacy): solid host rock and one smooth union of two elliptical spine tubes and an ellipsoid room, each intersected with its rising floor by cubic smooth-max (W 2.4 m, measured fillet 0.70 m, no trailing floor cap). Bound in the L40 field table and L42 capabilities, BVH feature candidates, isTileShapeFree aware, exact interval boxes (0 misses). Capsule flood fill arrival -> departure passes (headroom >= 0.3 m, grade <= 15 deg); the generalized whole-surface audit passes with its controls failing. Cold whole view 0.727 s, near ring 0.19 s. Graph ordering/serialization changes are scoped to native caves (the flagged L47 demo is unchanged); verify tiers write captures under --out only. fast/medium/full PASS, Clang and GCC. Research R23 (deepened): per-tile LOD0 kernel speed plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
17 KiB
VoxelForge R23 — Making Each Tile Cheaper
LOD0 stage model
World::generateTile activates the shared column caches, binds the shape world, runs tile classification, calls GenerateMesh, and copies the mesher timings into diagnostics. The mesher builds a 32³-cell LOD0 tile with a 35³ padded density grid, then classifies cells, computes gradients, interpolates edges, streams triangles, emits skirts, and returns arrays for assembly. See World::generateTile and UVoxelMarchingCubesMesher::GenerateMesh.
L52 used 512 LOD0 tiles. These are summed worker times, not wall times. Stage shares below use each recipe’s LOD0 worker sum as the denominator. Gradient, interpolation, and dedup/stream timings are sampled and extrapolated; their subtotals do not add exactly to mc_exclusive. mc_other is a residual, not an allocation timer.
| LOD0 stage | SW worker ms (% of 2,031.707 ms) | FI worker ms (% of 28,633.183 ms) |
|---|---|---|
| MC exclusive, excluding skirts | 1,284.636 (63.23%) | 27,019.018 (94.36%) |
| Density grid | 608.641 (29.96%) | 26,703.171 (93.26%) |
| Cell classification | 131.076 (6.45%) | 104.673 (0.37%) |
| Gradient normals | 49.508 (2.44%) | 24.732 (0.09%) |
| Edge interpolation, including midpoint re-queries | 370.383 (18.23%) | 30.921 (0.11%) |
| Vertex dedup and triangle streaming | 95.139 (4.68%) | 66.466 (0.23%) |
MC residual (other) |
47.487 (2.34%) | 106.111 (0.37%) |
| Skirts | 650.112 (32.00%) | 1,094.614 (3.82%) |
| Mesh assembly | 10.458 (0.52%) | 13.247 (0.05%) |
The direct profile rows and counts are in the SW LOD0 log and FI LOD0 log.
| LOD0 count | SW | FI |
|---|---|---|
| Density-grid samples | 21,952,000 (42,875/tile; full 35³ grid) | 17,883,062 (34,928/tile; 81.46% of full grid) |
| Candidate cell slots before proof skips | Up to 16,777,216 | Up to 16,777,216 |
| Classified and surface cells | Not logged | Not logged |
| Gradient queries / fallback density probes | Not logged | Not logged |
| Midpoint density re-queries | Not logged | Not logged |
| Generated skirt triangles | 179,600 | 198,464 |
density_samples is the mesher’s grid-fill count. It does not count gradient fallbacks, midpoint re-queries, or skirt probes; shared-density-grid copies also do not increment it. CellClassificationCalls and SurfaceCellCalls exist locally but are not emitted. Gradient fallback calls and memo hits, and midpoint calls, already exist in FVoxelMarchingCubesStageTimings and Core diagnostics; the L52 LOD logger omits them. Add per-LOD output for those counters, proof-skipped cells, active cells, and unique midpoint edges. The skirt timer and triangle count are logged, but skirt edge scans and density probes are not.
The generic heightfield column counters are zero at LOD0 for both recipes. That does not mean FI’s island column cache is idle: IslandDensity uses a separate four-way TLS cache and a four-way shared cache, activated by World::generateTile. The shared cache takes a per-set spin lock on misses. Add local hits/misses, shared hits/misses/computes, conflict replacements, and lock-wait time at IslandColumnCache and TSharedColumnValueCache.
The LOD0 grid costs about 0.0277 worker-seconds per million samples for SW and 1.493 worker-seconds per million for FI, at the current sample mix. Before-to-final FI profiles show samples falling from 18.951M to 17.883M and grid time from 28.335s to 26.703s. This was a package change, not an isolated proof A/B. The current proof audit checked 27,727 blocks and 36.9M samples with zero violations across Clang and GCC; it does not report LOD0 counts by skip reason. See the L52 report.
Tile classification is a separate all-LOD aggregate: L52 reports 0.622 ms for SW and 22.465 ms for FI. FI’s all-LOD classifier counted 14,119 air, 18,857 mixed, and 240 unknown boxes, with 216 whole-tile skips. These are not LOD0 cell counts, and classification_worker_ms can overlap mesher proof work. Correction to R22: the FI figure is 22.465 milliseconds, not 22.5 worker-seconds.
Techniques and measured opportunity
There is no isolated L52 A/B result for the new techniques below. I give the measured worker-time budget or a counter-based saving formula rather than inventing a point forecast.
-
Proof-guided sparse sampling — highest FI opportunity. The existing 8³ block proofs and required-sample mask are in GenerateMesh, sample-mask setup, and grid fill. Measure smaller proof blocks and active sample runs; the current proof mask disables the X8 batch path. At the current sample mix, each additional million samples avoided corresponds to about 1.493 FI or 0.0277 SW worker-seconds, before proof overhead. The earlier FI package delta corresponds to 1.632 worker-seconds less grid time, but cannot be attributed to this change alone. Keep exact samples for retained cells and the one-sample gradient halo; mixed or unknown blocks must fall back. The NVIDIA sparse-MC design gives the Lipschitz pruning bound; do not assume
L=1for composed or roughened fields unless that bound is proven for the recipe. -
SIMD density evaluation — worth measuring for FI. The batch route is in GenerateMesh and DensityAtLatticeX8; FI’s expensive island evaluation is in IslandDensity. The current FI X8 API still evaluates native-island lanes serially, and proof-masked fills disable X8 batching. Both the i9-12900H and Ryzen 7 5800X3D support AVX2, which is eight float32 lanes; a 16-lane batch is two AVX2 vectors, not a wider native instruction. The Intel specs list AVX2 on a 6P+8E/20-thread part; AMD lists AVX2/FMA3 on the 5800X3D (Intel, AMD). The grid-stage ceilings are 0.609 SW and 26.703 FI worker-seconds; each measured 10% grid-stage cut would save 0.061 and 2.670 worker-seconds, respectively. That is a sensitivity, not a forecast. FastNoise2 reports AVX2 3D value noise at 494.49M samples/s versus 64.13M for FastNoise Lite, and Perlin at 261.10M versus 47.93M on an i7-7820X; those are noise-kernel numbers, not this composite SDF (FastNoise2). Both CPUs have AVX2 width 8; benchmarking each is necessary.
-
Cache midpoint density queries by edge — exact SW candidate. The midpoint re-query is inside edge interpolation. Cache only the density result for repeated identical midpoint inputs; keep the existing orientation-specific interpolation arithmetic.
EdgeMidpointDensityCallsexists but is not logged. The entire interpolation stage is a hard ceiling of 0.370 worker-seconds SW and 0.031 FI, or 30.9 and 2.6 ms of ideal W12 drain. The actual query saving is unknown until attempts, unique edges, hits, field-evaluation time, and total interpolation time are reported. -
MC kernel layout / Flying Edges. The cell loop uses AVX2 to classify eight X cells at once and only processes surface cells; the interpolation and
GetOrCreateVertexpath is at GenerateMesh and vertex dedup. Row-major X-fast storage and TLS scratch reuse already exist. The Newman et al. SIMD MC paper reports roughly 2× over compiler-optimized C and 4× over unoptimized C on older x86. The Flying Edges paper reports 6.76s versus 2.39s for Synchronized Templates at 36 threads on a 2048³ plasma volume. Those are not this field or a direct forecast. The current non-grid MC budget is 0.676 SW and 0.316 FI worker-seconds, at most 56 and 26 ms ideal W12 drain. Flying Edges can avoid repeated edge work and point merging, but changes output ordering and likely needs a recipe version bump. -
Grid gradients versus analytic gradients. The current GradAt path already computes central differences from cached density-grid samples and memoizes them. The Round 18 report measured fallback probes falling from 11,474 to 3,018 per sampled p95 and normal cost from 65.10 to 24.16 ns/sample. The remaining L52 gradient-stage ceiling is only 49.5 ms SW and 24.7 ms FI worker-time. Analytic gradients change normals. For comparison, GPU Gems describes central differences as six volume samples; ExaBrick reports 2.70s central-difference versus 130ms analytic rendering, but that workload pays additional ray traversals, unlike our already-filled grid (GPU Gems, ExaBrick paper).
-
Skirt-face culling — strong SW candidate, versioned output. Skirts take 0.650 SW and 1.095 FI worker-seconds at LOD0. The viewer already computes face visibility at ComputeVisibleSkirtFaceMask and filters triangles at draw time; mesher extrusion is at EmitSkirts. A 125-tile mixed-LOD capture found 87.4% of SW and 87.0% of FI skirt triangles hidden. Applied linearly to all LOD skirt timers, that gives a modeled 1.55/2.32 worker-seconds gross, or 0.129/0.194s ideal W12 drain. Triangle share is not a time share; edge scanning remains. Pass the neighbor mask into meshing, include it in mesh-cache identity, and rebuild when neighbor coverage changes. The captured percentages are from SW and FI.
-
Transvoxel, Surface Nets, dual contouring. These are not first-round LOD0 speed wins. Transvoxel handles 2:1 LOD boundaries, using nine high-resolution samples and 512 cases grouped into 73 classes; it could replace some seam skirts but adds transition-cell work (Transvoxel). On an RTX 5090 sphere benchmark, uniform 512³ MC took 5.4 ms versus 6.9 ms for Surface Nets and 7.0 ms for one dual-contouring variant; sparse results were 1.9, 2.2, and 2.3 ms, with 300k active cells versus 134M dense cells (isoext benchmark). These GPU results are not a CPU prediction. All three alternatives change topology or seam geometry. Dreams’ SIGGRAPH slides describe GPU MC with compaction, while Astroneer identifies MC as a core algorithm to optimize; neither supplies a comparable CPU speedup (Dreams, Astroneer).
Per-tile scratch is already substantially reused: density grid, required-sample mask, gradient memo, vertex maps, and triangle arrays are thread-local and retain capacity. A per-tile SurfaceVertexDensitySamples vector still allocates, but there is no allocator-only timer. Add scratch growth/allocation counts and bytes; do not treat mc_other as allocator cost.
Exactness and determinism
| Technique | Output class | Clang/GCC risk with FP contraction off |
|---|---|---|
| Proven block/sample omission with original samples, traversal, and gradient halo | Bit-identical | Low if bounds are sound; audit every recipe, including new caves. |
| SIMD with the same per-lane operation order | Bit-identical possible | Avoid FMA, reassociation, approximate reciprocal/sqrt, and different transcendental implementations. Existing X8 results and L52 Clang/GCC hashes pass; GCC’s default contraction previously broke SW W1 LOD2. |
| Reuse cached midpoint-density result for identical inputs | Bit-identical | Key exact input bits; preserve existing interpolation arithmetic. |
| Remove or replace midpoint re-query; analytic gradients | Slight change; version bump | Peer-identical remains required; normals or vertex positions can change. |
| Scratch/cache/layout reuse | Bit-identical | Reset state fully; retain output order and arithmetic. |
| Hidden skirt-face generation skip | Visible output can match for the same neighbor set; mesh payload changes, so bump version/cache identity | Neighbor masks and invalidation must be deterministic. |
| Flying Edges, Surface Nets, dual contouring, Transvoxel | Geometry/order changes; version bump | Use stable traversal and tables, then require Clang/GCC peer hashes. |
Current builds use -fno-fast-math and FP contraction off; explicit FMA intrinsics can still fuse. Keep the L52 Clang/GCC W1/W8/W12 matrix as the determinism gate.
Sequenced plan and projections
Use the unprofiled paired W12 medians from R22 as the whole-view baseline: SW 1.718s; FI 5.614s. Convert worker-time deltas to wall-time only as an ideal W12 estimate; retain paired unprofiled whole-view measurements as the score.
-
Round 1 — instrument and establish baseline. Emit per-LOD candidate/classified/surface/proof-skipped cells; sample omissions by reason; X8 calls and active lanes;
GradAtcalls, memo hits/misses and fallback probes; midpoint attempts/unique edges/hits and query time; skirt face candidates/probes/times; FI local/shared cache stats and lock waits; scratch allocations/capacity growth. Keep profile timers from overlapping or label them as such. Gate: counters reconcile with existing sample and skirt totals, output hashes match, and unprofiled W12 stays at 1.718/5.614s. -
Round 2 — isolate kernel changes. For FI, A/B proof block sizes and active-lane batching, measuring samples removed and net
mc_grid_ms; for SW, A/B the exact midpoint-density memo, measuring query hits and interpolation time. Gate: zero proof violations, Clang/GCC W1/W8/W12 hashes match, and whole-view medians improve without profile-only gains. A 10% FI grid-stage reduction is a useful pass threshold: 2.670 worker-seconds, or 0.223s ideal W12. Without that result, credit no FI saving. Credit SW only from its measured A/B delta; its full interpolation-stage ceiling is 31ms ideal W12. -
Round 3 — cull hidden skirt faces before extrusion. Pass the viewer’s neighbor mask into the mesher and cache key. Gate: fixed-view captures match visible output, neighbor/LOD transitions have no gaps, mask changes invalidate the mesh, and whole-view W12 medians improve. The capture-based scenario is 0.129s SW and 0.194s FI; treat it as optimistic until measured.
| Stage | Conditional whole-view projection |
|---|---|
| Round 1, counters only | SW 1.718s; FI 5.614s |
| Round 2, FI grid gate passes; no SW delta yet measured | SW 1.718s; FI about 5.39s; if FI gate fails, 5.61s |
| Round 3, skirt scenario also passes | SW about 1.59s; FI about 5.20s if Round 2 passed, or 5.42s if it did not |
| Any gate fails | Retain the last measured baseline for that recipe |
These projections are scenarios, not forecasts. Even removing all LOD0 worker work would save at most about 0.17s SW or 2.39s FI on an ideal 12-worker drain; LOD0-only changes cannot take SW from 1.718s to 1s.