From 81071969fc0ce555b335baab303d8e3023f9243f Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Sat, 31 Oct 2026 02:11:10 +0100 Subject: [PATCH] docs+cleanup: CURRENT-STATE.md and dead L86/L88 ablation switches removed (L131) One current-state page (shipping behaviour, references with sources, gaps, verification, closed lines), linked from CLAUDE.md. Removes the L86/L88 passage/boundary ablation switches and the unused legacy route-grade audit; inventory of every kept switch in the report. Output identical: 24/24 + 18/18 hashes, fast tier, walk/seam audits, UE host build; timing unchanged (-0.2%, CI spans 0). No Source/ changes. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1 --- ARCHITECTURE.md | 15 +-- CLAUDE.md | 1 + CODEMAP.md | 4 +- CURRENT-STATE.md | 57 +++++++++ Standalone/Core/src/ShapeWorldInternal.h | 68 ++--------- .../WorldAssemblerM3/Linux131/REPORT.md | 50 ++++++++ .../Linux131/RunL131CanonicalMatrix.py | 75 ++++++++++++ .../Linux131/RunL131HashIdentity.py | 84 +++++++++++++ .../Linux131/RunL131Timing.py | 115 ++++++++++++++++++ Standalone/Source/StreamBenchmarkPipeline.h | 8 +- Standalone/Source/WorldAssemblyPrototype.h | 74 ----------- 11 files changed, 404 insertions(+), 147 deletions(-) create mode 100644 CURRENT-STATE.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux131/RunL131CanonicalMatrix.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux131/RunL131HashIdentity.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 95223ef..a8762dc 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1522,11 +1522,12 @@ collision ring became walkable at 0.632 s. `voxel.StreamingCoarseFirst=1` with residency by about one third on the staged plan, but null-RHI residency does not prove rendered enclosure. Keep coarse-first opt-in until that view is checked in a rendered owner-plan run. -The separate `voxel.StreamingStartupApplyBoost` switch stays default-off. At `1`, it temporarily allows -12 visible mesh submissions / 14 ms while the initial desired set and walkable ring are incomplete, -with a 30 second ceiling; at `0`, the authored budget is unchanged. On the matched legacy-order test, -the boost changed full-set readiness from 17.412 s to 17.346 s and removed count-cap frames, but did -not improve end-to-end residency. Keep the normal 4 / 2 ms guard for play. +The separate `voxel.StreamingStartupApplyBoost` switch is default-on in the current code. At `1`, it +temporarily allows up to 24 visible mesh submissions / 14 ms while the initial desired set and +walkable ring are incomplete, with a 30 second ceiling; at `0`, the authored budget is unchanged. +The historical matched S6 run changed full-set readiness from 17.412 s to 17.346 s and removed +count-cap frames, but did not improve end-to-end residency. These current defaults are defined in +`Source/VoxelForge/Private/VoxelWorld.cpp`; the S6 measurement remains historical context. The S6 trace found the shared pool was not saturated: its 10-slot idle estimate was about 132 slot-seconds on legacy order and 73 on gated coarse-first, while game-thread tile applies totaled only @@ -1556,8 +1557,8 @@ by its RAII guard when the worker lambda exits. This still counts queued and run results do not consume an admission slot. `voxel.StreamingLegacyPendingResultCap=1` restores the old `PendingTiles` accounting; the test harness's `NearReserveMode=Legacy` also sets `voxel.StreamingDynamicNearReserve=0`, reproducing the S6 path. `StreamingDynamicNearReserve` now -defaults to `1`; `StreamingCoarseFirst` remains `0`, and the apply boost remains `0` in the matched -measurement. +defaults to `1`; `StreamingCoarseFirst` remains `0`. That historical S6 matched measurement disabled +the startup apply boost (`voxel.StreamingStartupApplyBoost=0`); the current default is on (§8.27). On the 2,578-key owner SurfaceWorld trace, the old-path control reached the full set in 17.527 s, walkable ring in 0.650 s, and full LOD0 in 2.281 s. Dynamic admission reached them in 7.780 s, diff --git a/CLAUDE.md b/CLAUDE.md index 18bc29c..966d77d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,7 @@ then wait for his results / pasted errors. Same for in-editor checks — ask for try to run anything. ## Read first (in order, load only what the task needs) +- **[CURRENT-STATE.md](CURRENT-STATE.md) — current shipping snapshot and verification entry point.** - **[CODEMAP.md](CODEMAP.md) — ALWAYS.** Navigation: what it is, data-flow (§2), symbol→`file:line` index (§3), "I want to change X → go here" (§5), conventions & gotchas (§6). Trust **symbol names over line numbers** (lines drift). diff --git a/CODEMAP.md b/CODEMAP.md index 56d375f..21f31f1 100644 --- a/CODEMAP.md +++ b/CODEMAP.md @@ -388,7 +388,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error** | `AdvanceHeadlessDigMotionTest` / `RecordHeadlessDigMotionFrame` | 3060 / 3217 | Explicit `-voxel.TestDigMotion=1` repro: optional near digs, then repeats underfoot digs and samples `Generator->GetDensityAt` immediately after each edit, on its first subsequent simulated frame, and on every following observation frame. `-voxel.TestDigMotionFieldOverride=1` is a measurement-only prototype that clears `CurrentFloor` and switches CMC to falling when the sampled post-edit density is air; it is inactive by default. The test counts false support, false air and downward velocity beyond one gravity step. `-voxel.TestDigMotionUnderfootDigCount` is clamped to 1–100; `-voxel.TestDigMotionUnderfootIntervalSeconds=0` performs per-frame rapid edits. The accompanying gap audit is scoped to the dig session. | | `MaybeFinishHeadlessStreamingTest` | — | The existing `voxel.TestExitSeconds` diagnostic emits `VoxelForgeStreamingExitDiag` at most every 30 elapsed seconds, including threshold and early-return test flags; no output when the timer is unconfigured. | | `UpdateVisualStalenessAudit` | 5034 | Test-only camera-frustum coverage audit: tracks up to 24 real pre-skirt isosurface points per visible component, then counts frames where no visible tile component covers a previously visible, still-desired point (excluding edited bounds). `voxel.TestVisualGapInjectEarlyHide=1` hides a valid component for three frames and must be caught. `voxel.TestGapAuditLegacyHandoff=1` restores old handoff culling for A/B baselines. | -| `ProcessPendingChunks` | 7435 | Drains `CriticalProcessQueue`, `EditedProcessQueue`, `NearProcessQueue`, then `ProcessQueue`; applies under the authored 4-mesh/2 ms budget during normal play and the bounded shared startup budget (12 meshes/14 ms) until the initial desired set and walkable ring are ready. `voxel.StreamingStartupApplyBoost=0` restores the authored budget. `startup_apply_config` traces the actor-read CVar and both budgets; `startup_apply_budget` records activation/end. Reads per-request cancellation before removing its map entry, traces stale/obsolete/aborted discards with epoch and ordinal, then applies via `ApplyTileResult`. Step 1 prequeue still releases here and emits `VFStep1UploadCorpusReleased`. | +| `ProcessPendingChunks` | 7435 | Drains `CriticalProcessQueue`, `EditedProcessQueue`, `NearProcessQueue`, then `ProcessQueue`; applies under the authored 4-mesh/2 ms budget during normal play and the default-on bounded startup budget (24 meshes/14 ms, at most 30 s) until the initial desired set and walkable ring are ready. `voxel.StreamingStartupApplyBoost=0` restores the authored budget. `startup_apply_config` traces the actor-read CVar and both budgets; `startup_apply_budget` records activation/end. Reads per-request cancellation before removing its map entry, traces stale/obsolete/aborted discards with epoch and ordinal, then applies via `ApplyTileResult`. Step 1 prequeue still releases here and emits `VFStep1UploadCorpusReleased`. | | `ApplyTileResult` | — | **Shared game-thread apply** for one `FChunkResult` (async drain + sync carve): discards stale generation epochs and keys no longer desired, marks loaded, ingests capture, EMPTY releases the tile's existing component (a re-gen can flip content→empty on band change — old geometry must not linger), else `ApplyMeshToTile`. Returns true iff a visible mesh uploaded (counts the budget). Doesn't touch `PendingTiles` (caller's). | | `FVoxelStackSampler` / `FScopedVoxelStackRegistration` (`VoxelStackSampler.h/.cpp`) | — | **Diagnostic raw stack sampler, off by default.** A sampler thread captures registered generation workers' stacks (`CaptureThreadStackBackTrace`) every N µs and tags each with the tile's LOD. It writes bounded raw PCs to CSV plus a symbolization-free summary under `Saved/`; symbol resolution is deferred until after process exit so the measured process never initializes/calls DbgHelp for this diagnostic. Lifecycle teardown is serialized and registration TLS is session-tagged. Registration is per tile TASK in `LoadTile` (and the export's tasks), never per sample; disabled costs one relaxed atomic load. Game `-voxel.SampleStacks=`, commandlet `-samplestacks[=us]`. | | Room-cache performance (2026-09-13): `VF_FindPlayerFitPointForRoomMemoized` / `GPlayerFitMemo` (`VoxelCaveMorphology.cpp`), `voxel.TileCacheWindow` + `VoxelGenLOD::TileOriginVoxels/TileCellsPerAxis` (`VoxelGenerator.h`), `FChunkSDFSpatialIndex` | — | **Player-fit memo:** exact 97-word key, process-wide, `voxel.PlayerFitMemo`. **Tile cache window:** the room cache covers the requesting tile's footprint plus a fixed collect margin. It is set via `TGuardValue` in `GenerateMesh`/classify, and is window-invariant by construction (the margin does not grow with the tile). **LOD-gated:** fused path at LOD ≥ `voxel.TileCacheWindowMinLOD` (3), op-stack at LOD ≥ `voxel.TileCacheWindowOpStackMinLOD` (4); lower LODs keep the 4-chunk worker window, because the tile window slowed LOD0 evaluation ~6%. Strates whose authored terrain ops exceed the proven 256-voxel extent (or are malformed/unloaded) fall back to the legacy window (`VF_ComputeTerrainOpEnvelope`). `voxel.SpatialIndex` is -1 auto (off at fused LOD0, on elsewhere), 0 or 1. **Spatial index:** ordered XY buckets over cache primitives, exact rejection. The `static_assert` in `VoxelStrateTypes.h` ties `VF_STRATE_PARAM_FIELDS` to `FStrateGenerationParams` (83 fields + 8 B padding). | @@ -396,7 +396,7 @@ casse cette arithmetique *structurellement* — `BeginPlay` loggue une **Error** | `RecordTileHash` / `WriteTileHashDump` | — | Headless `-voxel.TileHashDump=` parity instrument. Records the exact `FVoxelMeshData` used by the streaming result, including empty/classified tiles, then writes sorted absolute tile keys, LOD step/cells, content-band limits, settings digest context, and the shared `VoxelGeometryHash` at `EndPlay`. | | `SyncRemeshTile` | — | **INSTANT DIG**: level-0 same-frame re-mesh on the game thread. Uses `PreEditDensityCache` when the tile is resident in the bounded local cache, replays the current DiffLayer over that exact procedural lattice, then meshes/applies inline so a carve is visible THIS frame. A cache miss uses normal generation; it captures the unedited lattice if the tile needs meshing, while uniform-tile proofs remain enabled. | | `PreEditDensityCache` / `VoxelForgePreEditDensityCache::BuildEditedGrid` | — | Per-world cache of the pawn's current L0 tile: exact 35³ pre-edit samples, max **171,500 bytes**. It is pruned when the pawn crosses chunk boundaries and cleared on `RegenerateAllChunks`. `BuildEditedGrid` snapshots touched diff chunks once and reproduces `GetDensityAt`'s final `pre-edit - EvaluateMods` operation before the per-call shared-grid mesher path. | -| `UpdateChunksAroundPosition` / `GetGenerationAdmissionCount` | 10990 / 10971 | Shared Core/legacy scheduler. Builds the desired set and delta-culls leavers; admits the occupied/support tile plus collision-required LOD0 tiles in the pawn's 3×3 XY ring as `CollisionCritical`. S5 coarse-first admission stays opt-in (`voxel.StreamingCoarseFirst=1`). `voxel.StreamingDynamicNearReserve=1` is now the default: it keeps the authored near reserve while moving or until the stationary walkable ring is ready, then makes those slots available to coarse work. Admission counts submitted queued/running tasks, not completed results waiting in the MPSC result queues; `voxel.StreamingLegacyPendingResultCap=1` restores the old `PendingTiles`-based cap for the negative control. `PendingTiles` still lasts through game-thread apply for duplicate suppression and result bookkeeping. The clipmap stays non-overlapping and load-before-unload / `TransitionHold` reconciliation remains authoritative. Standalone has no mirror of this actor scheduler. §8.10 + §8.26–8.28. | +| `UpdateChunksAroundPosition` / `GetGenerationAdmissionCount` | 10990 / 10971 | Shared Core/legacy scheduler. Builds the desired set and delta-culls leavers; admits the occupied/support tile plus collision-required LOD0 tiles in the pawn's 3×3 XY ring as `CollisionCritical`. S5 coarse-first admission stays opt-in (`voxel.StreamingCoarseFirst=1`). `voxel.StreamingDynamicNearReserve=1` is the default: it keeps the authored near reserve while moving or until the stationary walkable ring is ready, then makes those slots available to coarse work. Admission counts submitted queued/running tasks, not completed results waiting in the MPSC result queues; `voxel.StreamingLegacyPendingResultCap=1` restores the old `PendingTiles`-based cap for the negative control. `voxel.StreamingFarApplySupplement=1` is the default L121 lane: after the visible-apply cap, at most one far result is admitted when under half the apply-time budget and startup boost is inactive. `PendingTiles` still lasts through game-thread apply for duplicate suppression and result bookkeeping. The clipmap stays non-overlapping and load-before-unload / `TransitionHold` reconciliation remains authoritative. Standalone has no mirror of this actor scheduler. §8.10 + §8.26–8.28. | | `AdvanceHeadlessCollisionGateStressTest` / `ObserveHeadlessCollisionGateStressTest` / `MaybeFinishHeadlessCollisionGateStressTest` / `MeasureHeadlessCollisionGateStressTerrain` | — | Explicit `-voxel.TestCollisionGateStress=1` harness only. The normal spawn-height cvar offsets from the measured density surface; `voxel.TestCollisionGateStressDeepDrop=1` carves a process-local box shaft from above the top to just above the floor tile, extending along the post-landing route so the pawn does not run into the shaft sidewall, temporarily raises only the transient test DiffLayer brush-radius allowance for that fixture, removes only the local pawn's redundant streaming anchor for this isolated repro, proves every sample in two complete outside-ring level-0 tiles is air before seeding their known-empty status, prewarms the known-empty in-ring column, and asserts that the support walk reaches the in-layout floor outside the aligned level-0 render ring before the pawn drops. The log reports whether the fixed gate retains that otherwise out-of-ring target. Other streaming anchors remain active. The fixture rejects a target or floor outside the layout, a non-air empty-tile proof, or a floor inside the aligned level-0 ring. The `GetDensityAt` column at capsule feet defines the local air/solid surface; the deep-drop column crossing is cached after its first exact scan. Each frame records actor position, velocity, direct foot tile and gate support readiness, gate state, signed feet clearance, and a ≤0.5-voxel sweep between positions. PASS requires no measured clearance below −5 cm; deep-drop gate-on also requires horizontal travel during a hold. Result logs include the terminal density column, first crossing below spawn height, hold count/time, and gated horizontal progress. | | `BuildDesiredTiles` | 6482 | Builds `DesiredSorted`+`DesiredStamped` (player clipmap shells), then adds streaming anchors and the pending player's collision-only support column before the leaver sweep. It orders from the true local pawn position and builds `CriticalDesiredTiles` for the unresolved support target, occupied/support and heading tiles. Also rebuilds `DesiredTransitionDescendants` (ancestor → desired descendants, including face/edge-touching replacements) for the reconcile pass. | | `CollisionGateDesiredTiles` / `RequestCollisionGateSupportTile` | 5062 | Retains an unresolved support target and up to three level-0 tiles below it across desired-set rebuilds. The additional keys are hidden collision-only tiles, queued ahead of ordinary streaming, and pruned once resolved or outside the active column. | diff --git a/CURRENT-STATE.md b/CURRENT-STATE.md new file mode 100644 index 0000000..a1344f3 --- /dev/null +++ b/CURRENT-STATE.md @@ -0,0 +1,57 @@ +# VoxelForge current state + +As of 2026-10-31. The L130 baseline is on `assembler/world` as `044cd6d`; this L131 work branch starts at `cbbfd46`. Current-state claims below point to their code or measured report. + +## What ships today + +- **Core:** evaluator version **227**. Unreal's `AVoxelWorld::bUseVoxelForgeCore` is opt-in and defaults to `false`; the legacy generator remains the default runtime path. [Core version](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h), [runtime switch](Source/VoxelForge/Public/VoxelWorld.h), [L130 report](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md). +- **Generator dispatch when Core is enabled:** `SurfaceWorld` uses the Core surface adapter; `FloatingIslands` uses its Core adapter. `Caves` uses the Core cave field only with `Core.Caves.PrimitiveField` v1. `Maze` and `VerticalShafts` use the Core lattice field only with `Core.Lattice.PrimitiveField` v1. Other generator/recipe combinations fall through to `FLegacyGeneratorFieldAdapter`. [Dispatch](Standalone/Core/src/ShapeWorldInternal.h), [recipe checks](Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h). +- **Passages:** continuous smooth swept tubes with a continuous floor profile. L125 stamps conservative route slabs and outside sentinels into the tile cache; exact route geometry is still evaluated. All 21,425 route samples passed the open-air walk check; the nine-station floor probe's maximum root offset was 7.884 mm. L130 sets the shared influence reach to **1.0533 m** (0.75 m shell + 0.21 m relief + 0.50 m smooth-max support / 6 + 0.01 m margin), converted once from metres to Core voxels for the proof, feature, fallback, and off-grid bounds. [L125](Standalone/Research/WorldAssemblerM3/Linux125/REPORT.md), [L130](Standalone/Research/WorldAssemblerM3/Linux130/REPORT.md). +- **Streaming:** arrival uses a bounded startup apply burst: `voxel.StreamingStartupApplyBoost=1` by default, up to 24 visible meshes or 14 ms per frame for at most 30 seconds, until the initial desired set and walkable ring are ready. `voxel.StreamingFarApplySupplement=1` is also default-on: after the normal visible apply cap, at most one far result applies when under half the apply-time budget; it is inactive during the startup burst. Dynamic near reserve defaults on; coarse-first remains off; the legacy pending-result cap defaults off. The far-apply lane is Unreal-only. [CVar source](Source/VoxelForge/Private/VoxelWorld.cpp), [L121 report](Standalone/Research/WorldAssemblerM3/Linux121/REPORT.md), [architecture](ARCHITECTURE.md). +- **Determinism:** proven on Linux with Clang and GCC at W1/W8/W12. L131's unchanged-output comparison covers owner4's SW/FI/TN/VS views in 24 compiler/worker cases; the canonical owner4/owner2 recipe matrix also passes. Windows determinism is unverified. [L131 checks](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md), [v227 canonical hashes](Standalone/Tools/verify/baselines/owner-hash/canonical/). + +## Current performance references + +Headless medians in seconds, five interleaved L80-style measurements per version in each cell. Near-ready means the complete LOD0–2 set; whole-view is the full requested view. Values below are the edited L131 binary; the paired pre-cleanup comparison is in the [L131 report](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md) and [timing summary](Saved/Linux/l131/timing/summary.json). + +| Stratum | Near-ready W8 / W12 | Whole-view W8 / W12 | +|---|---:|---:| +| S0 | 0.804 / 0.848 | 1.811 / 1.832 | +| S1 | 1.014 / 1.002 | 1.993 / 1.886 | +| S2 | 1.060 / 1.012 | 1.960 / 1.865 | +| S3 | 0.917 / 0.884 | 1.887 / 1.782 | + +Across 40 paired runs per metric, the mean relative change from pre-cleanup L130 was −0.22% near-ready (95% CI −1.02% to +0.57%) and −0.29% whole-view (−1.04% to +0.46%); neither indicates a measurable slowdown. The machine gate was load 0.81 and 99.2% idle. [L80 protocol](Standalone/Research/WorldAssemblerM3/Linux80/REPORT.md), [runner and raw runs](Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py). + +The latest recorded Linux Unreal start-to-LOD0–2 values are L127's one run per stratum: S0 2.456 s, S1 2.231 s, S2 2.514 s, S3 2.221 s. L130 did not rerun UE startup or movement. Five L127 runs at 30 m/s, 12 s moving plus 5 s dwell, had median moving near coverage **40.22%**, far holes **3.46%**, tick p99 **10.80 ms**, and worst tick **13.27 ms**. After stopping, near coverage reached 100% and far holes reached 0%. These are the latest UE references, not v227 UE measurements. [L127 report](Standalone/Research/WorldAssemblerM3/Linux127/REPORT.md), [L127 movement summary](Saved/Linux/l127/ue/movement-pairs-summary.json), [L127 start summaries](Saved/Linux/l127/ue/merge/start-summary.json). + +## Open gaps + +- Multiplayer world lifecycle is unfinished: seed/layout replication, authoritative modification RPCs, and serialized diff replay for late join are still open. See [ARCHITECTURE §9.5 and §9.7](ARCHITECTURE.md). +- Determinism has not been verified on Windows. Linux Clang/GCC agreement does not establish MSVC agreement. +- NullRHI measures residency and apply timing, not the first presented pixel or rendered enclosure. L127 also found four low-contrast views among 23 otherwise verified captures. [L121](Standalone/Research/WorldAssemblerM3/Linux121/REPORT.md), [L127](Standalone/Research/WorldAssemblerM3/Linux127/REPORT.md). +- UE startup/movement numbers above predate L130's v227 reach change; there is no L130/L131 UE movement comparison. + +## How to verify + +- Build standalone Clang or GCC with [`Standalone/BuildLinux.sh`](Standalone/BuildLinux.sh), for example `bash Standalone/BuildLinux.sh --compiler=clang++-19 --variant=l131-clang --fp-contract=off --jobs=4`. The script pins CPUs 0–11 and applies `prlimit --as=6442450944` itself. +- Fast tier: `python3 Standalone/Tools/verify/verify.py fast --out Saved/Linux/l131 --overall-timeout 1740`. It runs the walkability and current owner checks; per-run logs and the summary stay under `Saved/Linux/l131`. +- Canonical output baselines are the v227 files in `Standalone/Tools/verify/baselines/owner-hash/canonical/`. L131 also provides reproducible pre-cleanup identity matrices for owner4 tile hashes and owner4/owner2 canonical hashes under `Standalone/Research/WorldAssemblerM3/Linux131/`. +- Timing follows L80: quiet machine, no overlapping build, one warm-up per version, five interleaved runs per stratum and worker count, W8/W12. Each headless run uses `taskset -c 0-11`, `prlimit --as=6442450944`, and a 1,740 s timeout. See [`RunL131Timing.py`](Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py). +- Linux host build: `/home/jahni/UnrealEngine/Engine/Build/BatchFiles/Linux/Build.sh VFHostEditor Linux Development -Project=/home/jahni/VFHost/VFHost.uproject -NoUBA -MaxParallelActions=4 -Log=/home/jahni/VoxelM/Plugins/VoxelForge/Saved/Linux/l131/ubt.log`. Run Unreal without `prlimit`. + +## Closed experiments + +These rejected speed/streaming proposals stay closed unless new evidence changes their measured failure. Reports and surviving patches are linked in the [L131 inventory](Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md). + +| Line | Result | +|---|---| +| L116 tunnel chord bound and z cache | Pair count fell 20.8%, but stayed 1.96× L109 and readiness was 15–30% slower. Patch: [`chord-bound-zcolumn-not-merged.patch`](Standalone/Research/WorldAssemblerM3/Linux116/patches/chord-bound-zcolumn-not-merged.patch), commit `89504ec`; see [report](Standalone/Research/WorldAssemblerM3/Linux116/REPORT.md). | +| L118 pair pruning / deferred floor work | Pruned 17–18% of candidate pairs but remained 17.2–25.7% behind L109 near-ready. [Report](Standalone/Research/WorldAssemblerM3/Linux118/REPORT.md); patch: [`l118-designs.patch`](Standalone/Research/WorldAssemblerM3/Linux118/l118-designs.patch). | +| L126 floor-call filters | Cut floor calls 20–23% without reducing pair counts; all eight cells missed L109. Candidate source was restored and no standalone patch was kept; see [report](Standalone/Research/WorldAssemblerM3/Linux126/REPORT.md) and commit `01a5094`. | +| L128 half-open slab ownership | Ownership audit passed, but readiness was 14.8–28.0% behind L109 and moving coverage dropped. Report and patch `l128-slab-ownership.patch` are in archived commit `7022d02`. | +| L129 2 m physical reach | v225's metre/Core-voxel bounds mismatch made the correctly bounded candidate do more work; it was slower and failed UE movement gates. Report and patch `l129-reach-2m.patch` are in archived commit `0d1140c`. | +| L120 priority split / extra worker slots | Neither met coverage and tick gates; 10 slots reached a 16.67 ms worst tick against 15.43 ms baseline. [Report](Standalone/Research/WorldAssemblerM3/Linux120/REPORT.md); no production patch retained. | +| L122 admission relaxation | Early admission missed p99 or far-hole gates; no behavior retained. [Report](Standalone/Research/WorldAssemblerM3/Linux122/REPORT.md); patch: [`l122-trace.patch`](Standalone/Research/WorldAssemblerM3/Linux122/l122-trace.patch). | +| L123 lookahead / completion gate | Lookahead failed p99 and worsened near/far coverage; queue-only also failed p99. [Report](Standalone/Research/WorldAssemblerM3/Linux123/REPORT.md); patch: [`l123-queue-lookahead.patch`](Standalone/Research/WorldAssemblerM3/Linux123/l123-queue-lookahead.patch). | +| L124 early LOD0 pages | Optimistic gain stayed below 100 ms except negative cases; no publication/scheduling implementation was made. [Report](Standalone/Research/WorldAssemblerM3/Linux124/REPORT.md). | diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h index 0032e03..37bad31 100644 --- a/Standalone/Core/src/ShapeWorldInternal.h +++ b/Standalone/Core/src/ShapeWorldInternal.h @@ -33,48 +33,22 @@ #include #include -// L88-only composition split. These switches let the timing harness remove passage work and -// resolved boundary-band work independently. They are diagnostic only, never world settings. -static bool VF_IsL88DiagnosticSwitchEnabled(const char* Name) +static bool VF_IsDiagnosticEnvSwitchEnabled(const char* Name) { const char* Value=std::getenv(Name); return Value&&Value[0]=='1'&&Value[1]=='\0'; } -static bool VF_IsL88PassageCompositionDisabled() -{ - static const bool bDisabled=VF_IsL88DiagnosticSwitchEnabled( - "VOXELFORGE_L88_DISABLE_PASSAGES") - ||VF_IsL88DiagnosticSwitchEnabled("VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION"); - return bDisabled; -} - -static bool VF_IsL88BoundaryBandsDisabled() -{ - static const bool bDisabled=VF_IsL88DiagnosticSwitchEnabled( - "VOXELFORGE_L88_DISABLE_BOUNDARY_BANDS") - ||VF_IsL88DiagnosticSwitchEnabled("VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION"); - return bDisabled; -} - -static bool VF_IsL88AllowPassageAblationWalkabilityFailure() -{ - static const bool bEnabled=VF_IsL88DiagnosticSwitchEnabled( - "VOXELFORGE_L88_ALLOW_PASSAGE_ABLATION_WALKABILITY_FAILURE"); - return bEnabled; -} - - static bool VF_IsL98PassagePrepareTraceEnabled() { - static const bool bEnabled=VF_IsL88DiagnosticSwitchEnabled( + static const bool bEnabled=VF_IsDiagnosticEnvSwitchEnabled( "VOXELFORGE_L98_PASSAGE_PREP_TRACE"); return bEnabled; } static bool VF_IsL130PassageReachProofAuditEnabled() { - static const bool bEnabled=VF_IsL88DiagnosticSwitchEnabled( + static const bool bEnabled=VF_IsDiagnosticEnvSwitchEnabled( "VOXELFORGE_L130_PASSAGE_REACH_PROOF_AUDIT"); return bEnabled; } @@ -327,18 +301,14 @@ public: { mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange( minimumSubvoxels_[2],maximumSubvoxels_[2]); - const bool disablePassageComposition=VF_IsL88PassageCompositionDisabled(); - if(VF_IsL88BoundaryBandsDisabled())mayContainResolvedBoundaryBand_=false; - mayContainTransitionPassageFade_=!disablePassageComposition - &&shape.ResolvedTransitionPassageFadeIntersectsTileXY( - minimumSubvoxels_,maximumSubvoxels_); - if(!disablePassageComposition) - featureIndices_=shape.BuildPassageFeatureTileList( - minimumSubvoxels_,maximumSubvoxels_); + mayContainTransitionPassageFade_=shape.ResolvedTransitionPassageFadeIntersectsTileXY( + minimumSubvoxels_,maximumSubvoxels_); + featureIndices_=shape.BuildPassageFeatureTileList( + minimumSubvoxels_,maximumSubvoxels_); stampIndices_=shape.BuildStampTileList(minimumSubvoxels_,maximumSubvoxels_); cavernFeatureIndices_=shape.BuildCavernFeatureTileList( minimumSubvoxels_,maximumSubvoxels_); - passageRoutes_.assign(shape.ResolvedLayout&&!disablePassageComposition + passageRoutes_.assign(shape.ResolvedLayout ?shape.ResolvedLayout->passages.size():0,0); passageRouteSegments_.resize(passageRoutes_.size()); for(const std::uint32_t featureIndex:featureIndices_) @@ -1561,12 +1531,8 @@ public: // the generic stratum transition distance creates a discontinuity at a // mouth that crosses a wide transition band. Keep that opt-in field whole; // its host-rock value already supplies the ceiling and bedrock. - const bool disablePassageComposition=!nativeLattice - &&VF_IsL88PassageCompositionDisabled(); - const bool disableBoundaryBands=!nativeLattice - &&VF_IsL88BoundaryBandsDisabled(); float boundary=graphDensity; - if(!nativeLattice&&!disableBoundaryBands) + if(!nativeLattice) { VoxelForgeCore::Linux77Profiler::FScopedPhase l77BandsPhase( VoxelForgeCore::Linux77Profiler::EPhase::Bands); @@ -1595,7 +1561,6 @@ public: ?bound->Resolved->passageCapabilities.hasNativeCaveField :bound->Resolved->passageCapabilities.hasNativeLatticeField)) { - if(!disablePassageComposition) { VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase( VoxelForgeCore::Linux77Profiler::EPhase::Passage); @@ -1626,7 +1591,6 @@ public: ELegacyFieldTerm::ComposerPassage); final=ApplyResolvedCavernFloorCaps(stamped,X,Y,Z); } - if(!disablePassageComposition) { VoxelForgeCore::Linux77Profiler::FScopedPhase l77PassagePhase( VoxelForgeCore::Linux77Profiler::EPhase::Passage); @@ -2332,18 +2296,8 @@ public: const bool bPassageRoutesValid=!bValidatePassageRoutes||ValidatePassageRoutes(); if(!bPassageRoutesValid) { - if(VF_IsL88PassageCompositionDisabled() - &&!VF_IsL88BoundaryBandsDisabled() - &&VF_IsL88AllowPassageAblationWalkabilityFailure()) - { - std::printf("[L88PassageAblation] route walkability failed as expected; " - "continuing only to measure passage-off/bands-on readiness\n"); - } - else - { - Error="compiled shape world contains a solid passage centreline sample"; - return false; - } + Error="compiled shape world contains a solid passage centreline sample"; + return false; } Stats.PassageValidationSeconds=std::chrono::duration(Clock::now()-Substage).count(); Built=true; diff --git a/Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md new file mode 100644 index 0000000..be3673f --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux131/REPORT.md @@ -0,0 +1,50 @@ +# Linux131 — current state and dead experiment cleanup + +Branch `wip/l131-current-state`, based on `cbbfd46`; Core evaluator remains v227. The root [`CURRENT-STATE.md`](../../../../CURRENT-STATE.md) consolidates the shipping behavior, current references, verification commands, open gaps, and closed proposals. + +## Removed + +- Removed `VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION`, `VOXELFORGE_L88_DISABLE_PASSAGES`, `VOXELFORGE_L88_DISABLE_BOUNDARY_BANDS`, and `VOXELFORGE_L88_ALLOW_PASSAGE_ABLATION_WALKABILITY_FAILURE`. These only supported archived passage/boundary ablations in `Linux86/RunL86Ceiling.py`, `Linux87/RunL87Timeline.py`, and `Linux88/RunL88CeilingSplit.py`; no current `verify.py` gate or live comparison uses them. Passage feature selection, passage composition, and boundary bands now run on the normal path, and passage-route validation cannot be waived by the old ablation flag. +- Removed the `VOXELFORGE_AUDIT_LEGACY_ROUTE_GRADE` block from `Standalone/Source/WorldAssemblyPrototype.h`. The source contained its only reference; no test, current tool, or report invoked it. +- Kept route-candidate grading and all user-visible behavior. This cleanup adds no evaluator version or baseline change. + +## Switch and hook inventory + +### Environment switches in compiled Core/Source + +| Switch | Effect and consumer | Keep decision | +|---|---|---| +| `VOXELFORGE_L74_TIGHT_PROOF_CONTROL` | Proof negative control. `Linux74/FalsifierL74.py` and `Linux75/FalsifierL75.py`. | Keep for falsifier reproducibility; not a current fast-tier input. | +| `VOXELFORGE_L75_TN_BLOCK_REGRESSION` | Fixed TunnelNetwork regression path in Core and mesher. `RunTnBlockRegressionL75.py` and `FalsifierL75.py`. | Keep; the regression tools still consume it. | +| `VOXELFORGE_L98_PASSAGE_PREP_TRACE` | Emits preparation-stage timing/count data. `RunL98Hashes.py` and `RunL98ReuseCounts.py`. | Keep as a focused profiler for future preparation comparisons. | +| `VOXELFORGE_L98_PASSAGE_REUSE_PROBE` | Compares the prepared projection with direct scalar queries. `RunL98ReuseCounts.py`. | Keep for exactness probes. | +| `VOXELFORGE_L104_FAR_HIT_CACHE` | Selects indexed versus linear far-hit candidate lookup. `RunL104ReadinessPairs.py` and `RunL104ProbeStats.py`. | Keep for reproducible A/B comparisons; cache defaults on. | +| `VOXELFORGE_L104_FAR_HIT_STATS` | Emits far-hit candidate/query counters. `RunL104ProbeStats.py`. | Keep for the measurement tool. | +| `VOXELFORGE_L115_DENSITY_PROFILE` | Enables density and floor-query profiling. `RunL115DensityProbe.py`, `RunL119DensityProbe.py`, and `RunL125DensityProbe.py`. | Keep; these remain the focused floor-probe path. | +| `VOXELFORGE_L130_PASSAGE_REACH_PROOF_AUDIT` | Checks passage proof/feature influence bounds. L130's reach audit and report. | Keep for reach changes and audit reproduction. | + +Archived runners also set `VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS`, `VOXELFORGE_L86_PASSAGE_RESULT_CACHE`, `VOXELFORGE_L90_PASSAGE_BRICK_METRES`, `VOXELFORGE_L90_PASSAGE_CANDIDATE_CENSUS`, `VOXELFORGE_L90_PASSAGE_REACH_METRES`, `VOXELFORGE_L91_PASSAGE_SOURCE_FLOOR_VOXELS`, `VOXELFORGE_L93_PASSAGE_CARVE_REACH_METRES`, `VOXELFORGE_L98_PASSAGE_REUSE_COUNTS`, `VOXELFORGE_L114_DENSITY_PROFILE`, and `VOXELFORGE_L118_TAG_TIMING`. None has a compiled-source reader at `cbbfd46`; they are inert historical runner strings. + +### Other experiments, diagnostics, and legacy paths retained + +- **World-changing tunnel ablations:** all 14 `voxel.TunnelAblate*` controls remain (`CaveWarp`, `DetailOps`, `RoomSDF`, `TunnelSDF`, `TunnelCore`, `PassageCarving`, `PassageStructuralPosts`, `NativeFloor`, `Disturbances`, `OriginSpine`, `BoundarySeal`, `LandingPosts`, `XYEdgeSeal`, `PitChimneySDF`). The editor commandlet writes their resolved mask into export and result manifests; the archived `PERF-SAMPLED.md` and `WORK-NEXT.md` record their measurements. No current runner invokes an ablation. Keep for manifest attribution and historical reproduction; removing it needs a metadata migration. Source: [`VoxelDensityAblation.cpp`](../../../../Source/VoxelForge/Private/VoxelDensityAblation.cpp), [`VoxelForgeExploreCommandlet.cpp`](../../../../Source/VoxelForgeEditor/Private/VoxelForgeExploreCommandlet.cpp). +- **Research fields:** `voxel.ResearchRedefinedDensityField`, `voxel.ResearchRedefinedDensityDetailScale`, `voxel.ResearchParameterizedCave`, and `voxel.ResearchParameterizedCaveSet` are default-off and used by `Tools/VoxelForgeTest.ps1` steps 2d/2e/2f. The region-eval commandlet consumes `SetResearchBrickBinningEnabled`, `SetResearchBrickRoomReachScale`, and `SetResearchRegionCacheWindowEnabled`; keep these editor-only experiment paths. Source: [`VoxelForgeRegionEvalExperiment.cpp`](../../../../Source/VoxelForgeEditor/Private/VoxelForgeRegionEvalExperiment.cpp), [`VoxelForgeTest.ps1`](../../../../Tools/VoxelForgeTest.ps1). +- **Runtime profiling and trace:** `voxel.SampleStacks` still starts `FVoxelStackSampler` (the guarded `Tools/VoxelForgeTest.ps1 -SampleDensityStacks` path consumes it); `voxel.ProfileDensity`, `voxel.ProfileDensityFull`, `voxel.ProfileTileGeneration`, `voxel.ProfileCollisionCookStages`, and `voxel.PerfAttribution` remain opt-in. `voxel.StartupTraceFile`, `voxel.StartupTraceMaxTiles`, and `voxel.StartupTraceUntilEndPlay` feed the Unreal startup trace used by the arrival/movement runners. Keep. Build-time `VOXELFORGE_PROFILE_LEGACY_TERMS` (L76) and `VOXELFORGE_PROFILE_L77`/`--linux77-profile` (stack sampler and route census) remain in `Standalone/BuildLinux.sh` and Core. +- **Standalone profiles and timelines:** retained `--profile-column-overlap`, `--profile-lod-work`, `--profile-passage-composer`, `--instrument-tile-drain`, `--cold-worker-timeline`, `--linux77-profile`, `--tls-probe=N`, `--tls-probe-touch=...`, and `--diagnostic-slow-shape-compile-ms=N`. The viewer's `--profile-attribution`, `--profile-detail-band`, and `--profile-far-column-overlap` remain. The L77 profiler, tile drain instrumentation, timelines, and TLS probe are measurement hooks; archived reports and runners still provide reproducible consumers. +- **Standalone audits:** all source-advertised hooks remain: `--audit-ore-vein-yield`, `--audit-resolved-layout-boundaries`, `--audit-strate-transitions`, `--audit-resolved-layout-passages`, `--audit-heightfield-profile`, `--audit-motif-intervals`, `--audit-landmark-reachability`, `--audit-native-caves`, `--audit-native-lattice`, `--audit-surface-clearance`, `--audit-uniform-block-proofs`, `--audit-base-page-equivalence`, `--audit-route-parity`, `--audit-passage-tile-seams`, `--audit-passage-source-clamp`, `--audit-passage-carve-reach`, `--audit-tile-shape-readiness`, and the mesher's `--audit-raw-skirts`. `verify.py fast` consumes the current route, passage, block-proof, transition, native-field and negative-control subset; dedicated L37/L75/L125/L130 runners exercise the specialized audits. Keep the whole audit set. +- **Evaluator A/B controls:** `voxel.FastIsFinite`, `voxel.UseFusedEvaluator`, `voxel.WormBlockSkip`, `voxel.TileCacheWindow` / its two minimum-LOD controls, `voxel.SpatialIndex`, `voxel.ChunkRoutingCache`, `voxel.UseOperatorBlock`, `voxel.CoreColumnCacheDirectMapped`, `voxel.CoreMCGradientMemoization`, `voxel.FloorRound1PassageProjectionCache`, `voxel.PlayerFitMemo`, `voxel.UseBlockEarlyOut`, `voxel.OuterClassifierMode`, and `voxel.SealedSolidProof` remain. They select optimized or comparison paths; the density block equivalence test explicitly forces `UseFusedEvaluator=0`, while most others are production defaults or historical measurement controls. `voxel.WormsForceOff` is a world-changing development measurement override. Kept; no current L131 gate changes them. +- **Standalone A/B and geometry controls:** `--exact-midpoint-density-memo`, `--no-exact-midpoint-density-memo`, `--active-lane-batching`, `--proof-block-cells=N`, `--parallel-sign-edge-repair`, `--serial-sign-edge-repair`, and `--shape-helper-limit=8|12` remain. The L94 report documents serial edge repair; no current `verify.py` consumer was found for the other CLI A/B controls, so they remain pending a separately reviewed removal. `voxel.TilePostReachDebug`, `voxel.TilePostReachScale`, and `voxel.TileReachCostDiagnostics` remain for the tile-post-reach test; `voxel.TunnelMouthTrim` remains a development-only room-mouth geometry control. No L131 gate uses these. +- `FLegacyGeneratorFieldAdapter` remains the Core fallback for unsupported generator/recipe combinations, and Unreal's Core switch remains default-off. `voxel.StreamingLegacyPendingResultCap=1` remains the L120–L123 admission negative control; `voxel.StreamingCoarseFirst` remains opt-in pending a rendered owner-plan check. `bStandaloneResearchRoundTunnels` and `bStandaloneResearchEnhancedWalls` remain consumed by the standalone viewer/Core adapter. + +## Checks + +- Clang 19 and GCC standalone builds passed with the L131 source stamp `5119cee164406856cbca496d160c0e0a8124ae2529fa7c0ebd51634a7c65a6cb`. `Standalone/BuildLinux.sh` applied the required CPU and address-space limits. +- L80 timing: one warm-up per binary and five interleaved pairs in each of four strata × W8/W12 cells, comparing the pre-cleanup L130 binary with L131. Load was 0.81; CPU idle was 99.2%. Across 40 pairs per metric, mean relative change was −0.22% near-ready (95% CI −1.02% to +0.57%) and −0.29% whole-view (−1.04% to +0.46%); the intervals include zero. [Raw timing summary](../../../../Saved/Linux/l131/timing/summary.json). +- Exact hashes: **24/24** owner4 SW/FI/TN/VS × Clang/GCC × W1/W8/W12 tile and whole-view identities; **18/18** canonical cases across owner4 SurfaceWorld and owner2 SurfaceWorld/FloatingIslands. All match the pre-cleanup L130 binary and agree across compiler/worker combinations. For transparency, forcing the owner4 dataset through `--recipe=floating-islands` exited during setup with “could not find a finite SurfaceWorld strate”, before hashing; it is not a valid canonical case. [Tile matrix](../../../../Saved/Linux/l131/hash-matrix/summary.json), [canonical matrix](../../../../Saved/Linux/l131/canonical-matrix/summary.json). +- Walkability: **21,425/21,425** passage centerline samples open. Seam audit: **4 pairs / 4,900** shared samples, zero mismatches. [Audit log](../../../../Saved/Linux/l131/seam-audit.log). +- Fast tier: **PASS**, 259 gated checks plus 5 diagnostics, 80 runs, 371.0 s. The five diagnostic-only comparisons are historical baselines; all required checks pass. [Summary](../../../../Saved/Linux/l131/fast-summary.json). +- Linux `VFHostEditor`: **PASS**, 27/27 actions, 78.81 s with `-NoUBA -MaxParallelActions=4`, no `prlimit`. UBT's default log rotation first failed before compilation in the read-only user config directory; the successful rerun set `-Log=.../Saved/Linux/l131/ubt.log`. Three existing RealtimeMesh deprecation warnings remain. [Build log](../../../../Saved/Linux/l131/ue-build.log). +- Unreal `Source/` files changed: **none**. No Windows compile is requested by this diff; Windows determinism remains unverified. +- `git diff --check` passes. No commit, push, or stash was made. + +Builds, per-run logs, timing records, hash records, and audit output are under `Saved/Linux/l131/`. diff --git a/Standalone/Research/WorldAssemblerM3/Linux131/RunL131CanonicalMatrix.py b/Standalone/Research/WorldAssemblerM3/Linux131/RunL131CanonicalMatrix.py new file mode 100644 index 0000000..ab1ff6c --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux131/RunL131CanonicalMatrix.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Compare 18 canonical owner4/owner2 hashes with the pre-cleanup L130 binary.""" +from __future__ import annotations + +import json +import re +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l131/canonical-matrix" +DATASETS = { + "owner4": ROOT / "Standalone/data/owner_world.json", + "owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json", +} +RECIPES = {"owner4": ("surface-world",), "owner2": ("surface-world", "floating-islands")} +BINARIES = { + "base": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-{compiler}", + "l131": ROOT / "Saved/Linux/l131/bin/VoxelForgeCoreHeadless-l131-{compiler}", +} +HASH_RE = re.compile(r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M) + + +def run(dataset: str, recipe: str, compiler: str, workers: int, version: str) -> tuple[list[str], float]: + binary = Path(str(BINARIES[version]).format(compiler=compiler)) + label = f"{dataset}-{recipe}-{compiler}-w{workers}-{version}" + log = OUT / f"{label}.log" + command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1740s", str(binary), str(DATASETS[dataset]), + "--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline", + "--force-generate", f"--workers={workers}", "--clip-radius=3", + "--max-clip-level=8", "--max-level=8", f"--recipe={recipe}", "--stream-hash"] + started = time.monotonic() + with log.open("w", encoding="utf-8") as stream: + process = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, check=False, timeout=1740) + hashes = HASH_RE.findall(log.read_text(encoding="utf-8", errors="replace")) + if process.returncode or len(hashes) != 12: + raise RuntimeError(f"hash run failed: {log}; exit={process.returncode}; rows={len(hashes)}") + return hashes, time.monotonic() - started + + +def main() -> int: + OUT.mkdir(parents=True, exist_ok=True) + rows = [] + groups = {} + for dataset, recipes in RECIPES.items(): + for recipe in recipes: + signatures = [] + for workers in (1, 8, 12): + for compiler in ("clang", "gcc"): + before, before_s = run(dataset, recipe, compiler, workers, "base") + after, after_s = run(dataset, recipe, compiler, workers, "l131") + row = {"dataset": dataset, "recipe": recipe, "compiler": compiler, + "workers": workers, "hash_mismatches": sum(a != b for a, b in zip(before, after)), + "rows": len(after), "base_s": round(before_s, 3), "l131_s": round(after_s, 3), + "base_log": f"Saved/Linux/l131/canonical-matrix/{dataset}-{recipe}-{compiler}-w{workers}-base.log", + "l131_log": f"Saved/Linux/l131/canonical-matrix/{dataset}-{recipe}-{compiler}-w{workers}-l131.log"} + rows.append(row) + signatures.append((compiler, workers, after)) + print("[L131CanonicalHash] " + json.dumps(row, sort_keys=True), flush=True) + (OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n") + first = signatures[0][2] + stable = all(sig == first for _, _, sig in signatures) + groups[f"{dataset}/{recipe}"] = {"base_equals_l131": all(row["hash_mismatches"] == 0 for row in rows if row["dataset"] == dataset and row["recipe"] == recipe), + "all_clang_gcc_workers_agree": stable} + failures = [row for row in rows if row["hash_mismatches"]] + (OUT / "summary.json").write_text(json.dumps({"runs": rows, "groups": groups, + "passed": len(rows)-len(failures), "failed": len(failures)}, indent=2) + "\n") + return 1 if failures or not all(group["all_clang_gcc_workers_agree"] for group in groups.values()) else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux131/RunL131HashIdentity.py b/Standalone/Research/WorldAssemblerM3/Linux131/RunL131HashIdentity.py new file mode 100644 index 0000000..45e1c28 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux131/RunL131HashIdentity.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 +"""Compare 24 owner4 tile-hash cases with the pre-cleanup L130 binary.""" +from __future__ import annotations + +import json +import re +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l131/hash-matrix" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARIES = { + "base": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-{compiler}", + "l131": ROOT / "Saved/Linux/l131/bin/VoxelForgeCoreHeadless-l131-{compiler}", +} +CAMERAS = { + "sw": ((8.0, 16.0, -372.2626953125), 2850), + "fi": ((25.8369140625, 3.19921875, -737.0), 2249), + "tn": ((19.166015625, -14.2001953125, -753.3740234375), 2233), + "vs": ((-20.0625, -5.068359375, -900.353515625), 2329), +} +TILE_RE = re.compile( + r"^\[PipelineTileHash\] lod=(-?\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " + r"density=(0x[0-9a-f]+) mesh=(0x[0-9a-f]+) payload=(0x[0-9a-f]+) " + r"topology=(0x[0-9a-f]+)$", re.M) +WHOLE_RE = re.compile( + r"^\[PipelineWholeViewHash\] tiles=(\d+) density=(0x[0-9a-f]+) " + r"mesh=(0x[0-9a-f]+) payload=(0x[0-9a-f]+) topology=(0x[0-9a-f]+)$", re.M) + + +def capture(camera_name: str, compiler: str, workers: int, version: str) -> tuple[dict, tuple[str, ...], float]: + camera, expected_tiles = CAMERAS[camera_name] + camera_arg = ",".join(f"{value:.12f}" for value in camera) + ",0,0" + binary = Path(str(BINARIES[version]).format(compiler=compiler)) + log = OUT / f"owner4-{camera_name}-{compiler}-w{workers}-{version}.log" + command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", f"--workers={workers}", + "--clip-radius=3", "--max-clip-level=8", "--max-level=8", + f"--camera={camera_arg}", "--stream-hash-tiles"] + started = time.monotonic() + with log.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, check=False, timeout=1740) + elapsed = time.monotonic() - started + output = log.read_text(encoding="utf-8", errors="replace") + tiles = {(int(match.group(1)), int(match.group(2)), int(match.group(3)), int(match.group(4))): + match.group(5, 6, 7, 8) for match in TILE_RE.finditer(output)} + whole = WHOLE_RE.search(output) + if result.returncode or whole is None or len(tiles) != expected_tiles or len(tiles) != int(whole.group(1)): + raise RuntimeError(f"incomplete {version} {camera_name}/{compiler}/W{workers}: {log}") + return tiles, whole.groups(), elapsed + + +def main() -> int: + OUT.mkdir(parents=True, exist_ok=True) + rows = [] + for camera_name in CAMERAS: + for compiler in ("clang", "gcc"): + for workers in (1, 8, 12): + old_tiles, old_whole, old_s = capture(camera_name, compiler, workers, "base") + new_tiles, new_whole, new_s = capture(camera_name, compiler, workers, "l131") + keys = set(old_tiles) | set(new_tiles) + tile_mismatches = sum(old_tiles.get(key) != new_tiles.get(key) for key in keys) + row = {"owner": "owner4", "camera": camera_name, "compiler": compiler, + "workers": workers, "tiles": len(new_tiles), + "tile_mismatches": tile_mismatches, + "whole_view_match": old_whole == new_whole, + "base_s": round(old_s, 3), "l131_s": round(new_s, 3), + "base_log": f"Saved/Linux/l131/hash-matrix/owner4-{camera_name}-{compiler}-w{workers}-base.log", + "l131_log": f"Saved/Linux/l131/hash-matrix/owner4-{camera_name}-{compiler}-w{workers}-l131.log"} + rows.append(row) + print("[L131HashIdentity] " + json.dumps(row, sort_keys=True), flush=True) + (OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n") + failures = [row for row in rows if row["tile_mismatches"] or not row["whole_view_match"]] + (OUT / "summary.json").write_text(json.dumps({"runs": rows, "passed": len(rows)-len(failures), + "failed": len(failures)}, indent=2) + "\n") + return 1 if failures else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py b/Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py new file mode 100644 index 0000000..fc76e81 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux131/RunL131Timing.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Five interleaved L80 readiness measurements per stratum and worker count.""" +from __future__ import annotations + +import json +import os +import re +import statistics +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l131/timing" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARIES = { + "base": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang", + "l131": ROOT / "Saved/Linux/l131/bin/VoxelForgeCoreHeadless-l131-clang", +} +CAMERAS = { + 0: (8.0, 16.0, -372.2626953125), + 1: (25.8369140625, 3.19921875, -737.0), + 2: (19.166015625, -14.2001953125, -753.3740234375), + 3: (-20.0625, -5.068359375, -900.353515625), +} +PIPELINE_RE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M) +FIELDS_RE = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") +NEAR_RE = re.compile(r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$", re.M) + + +def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict: + camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0" + label = f"s{strate}-w{workers}-{version}-{phase}{replicate}-l131-v227" + log = OUT / f"{label}.log" + command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "timeout", "1740s", str(BINARIES[version]), str(DATA), + "--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline", + f"--workers={workers}", "--clip-radius=3", "--max-clip-level=8", + "--max-level=8", f"--camera={camera}", "--profile-lod-work", + "--stream-scheduler-policy=3"] + started = time.monotonic() + with log.open("w", encoding="utf-8") as output: + result = subprocess.run(command, cwd=ROOT, stdout=output, + stderr=subprocess.STDOUT, check=False, timeout=1740) + content = log.read_text(encoding="utf-8", errors="replace") + pipeline, near = PIPELINE_RE.search(content), NEAR_RE.search(content) + if result.returncode or pipeline is None or near is None: + raise RuntimeError(f"timing run failed: {log}; exit={result.returncode}") + fields = dict(FIELDS_RE.findall(pipeline.group(1))) + row = {"strate": strate, "workers": workers, "version": version, + "phase": phase, "replicate": replicate, + "lod02_ready_s": float(near.group(3)), + "whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")), + "wall_s": round(time.monotonic() - started, 3), + "log": str(log.relative_to(ROOT))} + print("[L131TimingRun] " + json.dumps(row, sort_keys=True), flush=True) + return row + + +def quiet_machine() -> tuple[float, float]: + load_1m = os.getloadavg()[0] + def totals() -> tuple[int, int]: + fields = Path("/proc/stat").read_text().splitlines()[0].split() + values = [int(value) for value in fields[1:]] + return sum(values), values[3] + (values[4] if len(values) > 4 else 0) + before = totals() + time.sleep(2.0) + after = totals() + idle = 100.0 * (after[1] - before[1]) / max(1, after[0] - before[0]) + return load_1m, idle + + +def main() -> int: + if any(not path.is_file() for path in (DATA, *BINARIES.values())): + raise SystemExit("missing timing input") + OUT.mkdir(parents=True, exist_ok=True) + load_1m, idle = quiet_machine() + (OUT / "machine-load-before.txt").write_text( + f"1-minute load: {load_1m:.2f}\n2-second CPU idle: {idle:.1f}%\n") + print(f"[L131TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True) + if load_1m >= 0.9 or idle < 97.0: + raise SystemExit("timing refused: machine is above the L80 quiet-load gate") + + rows = [] + for strate in range(4): + for workers in (8, 12): + for version in BINARIES: + rows.append(run(strate, workers, version, "warmup", 0)) + for replicate in range(1, 6): + order = tuple(BINARIES) if replicate % 2 else tuple(reversed(BINARIES)) + for version in order: + rows.append(run(strate, workers, version, "measured", replicate)) + (OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n") + + measured = [row for row in rows if row["phase"] == "measured"] + summary = [] + for strate in range(4): + for workers in (8, 12): + old = [row for row in measured if row["strate"] == strate and row["workers"] == workers and row["version"] == "base"] + new = [row for row in measured if row["strate"] == strate and row["workers"] == workers and row["version"] == "l131"] + item = {"strate": strate, "workers": workers, "replicates_per_version": 5} + for key in ("lod02_ready_s", "whole_view_ready_s"): + old_med, new_med = statistics.median(row[key] for row in old), statistics.median(row[key] for row in new) + item[f"base_{key}_median"] = old_med + item[f"l131_{key}_median"] = new_med + item[f"delta_pct_{key}"] = round(100.0 * (new_med / old_med - 1.0), 2) + item[f"base_{key}_values"] = [row[key] for row in old] + item[f"l131_{key}_values"] = [row[key] for row in new] + summary.append(item) + (OUT / "summary.json").write_text(json.dumps({"runs": rows, "summary": summary}, indent=2) + "\n") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h index 820aac2..06626c9 100644 --- a/Standalone/Source/StreamBenchmarkPipeline.h +++ b/Standalone/Source/StreamBenchmarkPipeline.h @@ -2566,15 +2566,9 @@ namespace StandaloneStreamBenchmark ShapeRequest.detail=DetailVariant;ShapeRequest.includeFarWorld=bIncludeFarWorld&&!bDeferFarWorld; ShapeRequest.roundTunnels=bRoundTunnels; ShapeRequest.enhancedTunnelWalls=bEnhancedTunnelWalls; - const char* L86DisablePassages=std::getenv( - "VOXELFORGE_L86_DISABLE_PASSAGE_COMPOSITION"); - const bool bL86DisablePassages=L86DisablePassages - &&L86DisablePassages[0]=='1'&&L86DisablePassages[1]=='\0'; - ShapeRequest.validatePassageRoutes=!bCaptureSkipWalkability&&!bL86DisablePassages; + ShapeRequest.validatePassageRoutes=!bCaptureSkipWalkability; if(bCaptureSkipWalkability) std::printf("[CaptureWalkabilityGate] validation=deferred geometry_input=unchanged mode=mesh-export\n"); - if(bL86DisablePassages) - std::printf("[L86PassageCeiling] validation=skipped geometry=passage_composition_disabled diagnostic_only=true\n"); FStandaloneStreamingGenerator Streamer(World,WorkerCount,bHashDensity,nullptr,MaxLevel, bSkipUvAndColors,bSkipMaterialGroups,bSkipUV,bSkipColors,&Shape,bProfileColumnOverlap, FarHorizonLOD8InteriorStepVoxels,bUseSharedColumnCache, diff --git a/Standalone/Source/WorldAssemblyPrototype.h b/Standalone/Source/WorldAssemblyPrototype.h index b9baa9d..dbe2a7c 100644 --- a/Standalone/Source/WorldAssemblyPrototype.h +++ b/Standalone/Source/WorldAssemblyPrototype.h @@ -153,80 +153,6 @@ namespace StandaloneWorldAssemblyPrototype candidateCenterSpacing=32; } const double coreVoxelMetres=static_cast(coreVoxelSizeMicrons)/1000000.0; - if(std::getenv("VOXELFORGE_AUDIT_LEGACY_ROUTE_GRADE")) - { - constexpr std::int32_t legacyCenterSpacing=64; - constexpr std::int32_t legacyWaypointSpacing=8; - std::array legacySegments{}; - std::uint64_t legacyGradeFailCandidates=0; - double legacyMinimumGround=std::numeric_limits::infinity(); - double legacyMaximumGround=-std::numeric_limits::infinity(); - for(std::int32_t centerY=-256;centerY<=256;centerY+=legacyCenterSpacing) - for(std::int32_t centerX=-256;centerX<=256;centerX+=legacyCenterSpacing) - for(const Direction& direction:directions) - { - bool candidateGradeFail=false; - std::int64_t previousGround=0,previousX=0,previousY=0; - for(std::int32_t index=0;index<=32;++index) - { - const std::int64_t along=(index-16)*legacyWaypointSpacing; - const std::int64_t x=centerX+std::llround( - static_cast(along)*direction.x/65536.0); - const std::int64_t y=centerY+std::llround( - static_cast(along)*direction.y/65536.0); - const auto column=finalField.point(WorldAssemblyFixed(static_cast(x)), - WorldAssemblyFixed(static_cast(y)),0); - const double groundMetres=static_cast(column.groundHeightSubvoxels) - *coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel; - legacyMinimumGround=std::min(legacyMinimumGround,groundMetres); - legacyMaximumGround=std::max(legacyMaximumGround,groundMetres); - if(index>0) - { - const std::int64_t horizontalQ=static_cast(std::llround( - std::hypot(static_cast(x-previousX), - static_cast(y-previousY))*WorldAssemblySubvoxelsPerVoxel)); - const std::int64_t riseQ=std::abs( - column.groundHeightSubvoxels-previousGround); - if(horizontalQ==0||riseQ*65536>horizontalQ*GradeQ16) - { - candidateGradeFail=true; - GradeSegmentAudit& segment=legacySegments[ - static_cast(index-1)]; - ++segment.failures; - const double grade=horizontalQ>0 - ?static_cast(riseQ)/static_cast(horizontalQ) - :std::numeric_limits::infinity(); - if(grade>segment.maximumGrade) - { - segment.maximumGrade=grade; - segment.fromGround=previousGround; - segment.toGround=column.groundHeightSubvoxels; - segment.fromX=previousX;segment.fromY=previousY; - segment.toX=x;segment.toY=y; - } - } - } - previousGround=column.groundHeightSubvoxels;previousX=x;previousY=y; - } - if(candidateGradeFail)++legacyGradeFailCandidates; - } - std::printf("[WorldAssemblerLegacyRouteGradeProfile] candidates=648 grade_fail_candidates=%llu ground_range_m=%.3f..%.3f grade_limit_deg=15.000 chord_m=%.3f waypoint_step_m=%.3f center_step_m=%.3f\n", - static_cast(legacyGradeFailCandidates), - legacyMinimumGround,legacyMaximumGround,256.0*coreVoxelMetres, - legacyWaypointSpacing*coreVoxelMetres,legacyCenterSpacing*coreVoxelMetres); - const double metresPerSubvoxel=coreVoxelMetres/WorldAssemblySubvoxelsPerVoxel; - for(std::size_t segmentIndex=0;segmentIndex%.3f xy_m=(%.3f,%.3f)->(%.3f,%.3f)\n", - segmentIndex,static_cast(segment.failures), - std::atan(segment.maximumGrade)*57.29577951308232, - segment.fromGround*metresPerSubvoxel,segment.toGround*metresPerSubvoxel, - segment.fromX*coreVoxelMetres,segment.fromY*coreVoxelMetres, - segment.toX*coreVoxelMetres,segment.toY*coreVoxelMetres); - } - } std::printf("[WorldAssemblerRouteCandidateEnvelope] max_chord_m=%.3f length_variants=%zu center_step_m=%.3f arrival_radius_m=%.3f departure_radius_m=%.3f\n", 2.0*candidateHalfLengths.front()*coreVoxelMetres,candidateHalfLengths.size(), candidateCenterSpacing*coreVoxelMetres,