Files
VoxelForge/Standalone/Research/WorldAssemblerM3/Research11/REPORT.md
T
Fr0zkaandClaude Opus 5.5 036bd530fc fix(core): worker-count-dependent tiles near passage support columns (L44); random-camera determinism sweep
L27 added passage support columns, but World::isTileShapeFree only bounded the route and bridge, so tiles
touched by a column could be approved for early native-only generation; depending on scheduling they used
that incomplete field or the compiled one (six LOD0 tiles differed between W1 and W12 at an SW camera near
the arrival mouth). The shape-free proof now includes a conservative envelope for every possible column.
The compiled world is unchanged (the old W1 variant), so no version bump. verify tiers gain a seeded
(SplitMix64 0x4c3434) random-camera W1/W8/W12 sweep across both recipes and several clip settings; it fails
before the fix and passes after. fast/medium/full PASS; cost within noise. Research R11: suspect review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
2026-10-23 23:00:21 +02:00

27 lines
6.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
**Suspects — I couldn’t append to `Saved/Linux/claude/shared-notes.md` because the sandbox is read-only:** L4’s per-worker heightfield cache; L7’s shared column cache; L28’s passage feature lists and their interaction with tile-scoped state.
| Rank / commit | File and function | Mechanism and fit | Cheapest confirmation |
|---|---|---|---|
| 1 — L4 `7770074` | [WorldAssemblerInternal.h:CachedColumn](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/WorldAssemblerInternal.h:1378); [ThreadLocalStorage.h:FLazy](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/include/VoxelForgeCore/ThreadLocalStorage.h) | LOD0 uses a per-thread direct-mapped heightfield cache. Worker count partitions tile samples across different cache histories. Its key checks recipe, plan and XY, but not the active feature-query scope used by `EvaluateColumn`; a context omission or incomplete candidate list could make a cached value depend on which worker filled it. **Best fit for stable worker-count partitioning.** Cache collisions alone should only evict entries, because hits check the full key. | Temporarily bypass the TLS heightfield cache and compare W1/W12. No standalone switch disables this cache. |
| 2 — L7 `4b1108b` | [SharedColumnCache.h:getOrCompute](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/include/VoxelForgeCore/SharedColumnCache.h:96); [WorldAssemblerInternal.h:CachedColumn](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/WorldAssemblerInternal.h:1604) | The shared cache publishes the first computed value for a recipe/plan/XY key; worker scheduling determines which query fills it first. The same tile-scoped feature-query context is absent from that key. The lock and atomic version checks look designed to prevent torn fills, so this fits only if the factory can vary by context. | Add `--column-cache=thread-local` to both runs. This disables L7, but **leaves the L4 TLS cache active**. |
| 3 — L28 `f23ab7b` | [ShapeWorldInternal.h:FScopedPassageTileQueries](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Core/src/ShapeWorldInternal.h:156); `BuildPassageFeatureTileList` | Per-tile passage candidates come from BVHs and are exposed through a thread-local scope. This is the strongest spatial fit for a camera near a passage; a missed candidate could omit a carve or mouth feature. The lists are built from immutable shape data and look deterministic per tile, so they need an interaction with scoped state or caching to explain worker dependence by themselves. | Force the passage query to fail open and use the full feature list, then compare W1/W12. There is no CLI flag that disables this culling. |
| L11 `b1cfaf5`, L13 `976d012` | [StreamBenchmarkPipeline.h:ShapeFreeDesired](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/StreamBenchmarkPipeline.h:525); `VoxelForgeCore.cpp:generateTile` | Shape-free tiles can use the native-only route while shape compilation runs. Worker scheduling could change which tiles take that route. Fit is lower here: the target is near a passage, and `isTileShapeFree` is intended to exclude passage-overlapping tiles. | `--audit-tile-shape-readiness` compares compiled and native-only output for eligible near-ring tiles. `--diagnostic-slow-shape-compile-ms=250` is a timing sensitivity check. |
| L9 `7650abb`, L12 `32b8670`, L13 `976d012` | `ShapeWorldInternal.h` proof classification; `VoxelForgeCore.cpp:compileTileRegion` | L9’s added proof optimization targets LOD1. The command is LOD0 only; its general LOD0 proof path is tile-local and does not itself explain worker order. L12 pages are built for region/audit routes; this static command has no region crossing. L13 fixed a page/base field-history issue, but that route should also be absent. | `--audit-uniform-block-proofs` audits the Step=2 path, so it does not cover this LOD0 run. Page audits are not a useful first check here. |
| S14 `ec2afe9` | [VoxelMarchingCubesMesher.cpp:GradientMemo](/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Source/VoxelForge/Private/VoxelMarchingCubesMesher.cpp:1487) | The memo is thread-local, but its validity is reset per tile and its indices refer to that tile’s density grid. It should not carry values between tiles or workers. | Unreal has `voxel.CoreMCGradientMemoization=0`; the standalone CLI has no equivalent. Low fit. |
| L18–L19, L21–L37; L40–L42 | `ResolvedLayoutInternal.h`, `ShapeWorldInternal.h`, field-binding code | Strata ceilings, resolved layout, transition bands, passage geometry, and L40 field bindings are resolved or compiled state, not worker-local state. L40 is also ruled out as the sole cause because L37 reproduces. No strong worker-order mechanism found. | No direct off switch. |
| Cross-cutting | `HashTileRows`; unordered containers; floating-point work | Tile rows are sorted before hashing. The unordered containers I found are used for lookup or membership, not output order; no density reduction whose order changes with worker count stood out. | No obvious flag; low fit. |
### Top three
1. **L4 per-worker cache:** This is the clearest partitioning mechanism, and the command’s LOD0 path selects direct mapping. The specific risk is that `EvaluateColumn` consults a tile-scoped feature list, while the cached value is keyed only by recipe, plan and XY. That list is meant to include every feature relevant at the point, so this is a hypothesis to verify, not evidence of a known bad hit. Bypass the TLS heightfield cache to test it.
2. **L7 shared cache:** The shared cache also omits the tile query scope from its key, and first-fill order varies with worker scheduling. Its synchronization appears to protect cache integrity; the concern is whether two valid query contexts can produce different values for the same key. Run W1 and W12 with `--column-cache=thread-local`. If the mismatch disappears, investigate the shared fill path; if it remains, L4 is still active.
3. **L28 passage lists:** This best matches the location, but the BVH/list path appears deterministic per tile. Force full passage-feature evaluation as a diagnostic. If that removes the worker-count difference, check the bounds and scope feeding the list.
### Flags to try
- **First:** `--column-cache=thread-local` on both worker counts. It disables the shared cache only.
- **Readiness check:** `--audit-tile-shape-readiness`; optionally add `--diagnostic-slow-shape-compile-ms=250` to vary overlap with shape compilation.
- **Not an immediate LOD0 check:** `--audit-uniform-block-proofs` covers the Step=2 proof path, not the reported LOD0 path.
- I found no standalone CLI flag to disable the L4 TLS cache or passage BVH culling.