From 9cb5e53b17b966d5eb03e0bd23c68aac6d6802a7 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Tue, 27 Oct 2026 22:58:34 +0100 Subject: [PATCH] passages: exact density-aware outer bound skips passage work beyond its reach (L81, v214) The carve is max(source, -roughSDF/Q), so its reach grows with -source; the outer test uses -d*Q + 0.24 m (wall relief 0.21 + margin) beyond the route radius, disabled in mouth-support and column-overlap tiles. Skips 16% of passage iterations on the highest-activity tiles and all of them on ordinary tiles. 84 candidate/full pairs identical in every hash; 0 changed samples. Evaluator 214 for cache invalidation only. Cold LOD0-2 (L80 protocol): SW 1.30->1.25, FI 3.59->2.93, TN 3.18->3.51 (noise?), VS 1.33->1.22 s. Fast tier PASS. Note: the export's legacy 26-point root route owner_world.passages[0] fails a full-view pre-audit (65 solid samples, unchanged since L70); the canonical 211-point route used by the plan passes. Stale data to clean up. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1 --- .../include/VoxelForgeCore/VoxelForgeCore.h | 5 +- Standalone/Core/src/ShapeWorldInternal.h | 75 ++++++++ .../WorldAssemblerM3/Linux81/REPORT.md | 103 ++++++++++ .../WorldAssemblerM3/Linux81/RunL81Census.py | 83 ++++++++ .../Linux81/RunL81OwnerPassageAudits.py | 63 ++++++ .../Linux81/RunL81PassageSweep.py | 181 ++++++++++++++++++ .../Linux81/RunL81Readiness.py | 102 ++++++++++ Standalone/Source/StreamingWorld.h | 5 +- 8 files changed, 615 insertions(+), 2 deletions(-) create mode 100644 Standalone/Research/WorldAssemblerM3/Linux81/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux81/RunL81Census.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux81/RunL81OwnerPassageAudits.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux81/RunL81PassageSweep.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux81/RunL81Readiness.py diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h index 81c1a4e..158823f 100644 --- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h @@ -140,6 +140,9 @@ namespace VoxelForgeCore std::uint64_t passageTileMouthSegmentCandidates = 0; std::uint64_t passageComposerQueries = 0; std::uint64_t passageIterations = 0; + std::uint64_t passageTileBoundChecks = 0; + std::uint64_t passageTileBoundRejects = 0; + std::uint64_t passageTileBoundSkippedIterations = 0; std::uint64_t passageUpperMouthFilterChecks = 0; std::uint64_t passageColumnCandidates = 0; std::uint64_t passageColumnDistanceCalls = 0; @@ -456,7 +459,7 @@ namespace VoxelForgeCore // Version 210 keeps bulk native-cave sampling on the scalar field path at strata edges. // Version 211 makes coordinate biasing in the shared cell hash unsigned, removing signed-overflow UB. // Version 212 balances generated entry routes against existing walk lanes and preserves their shared treads. - inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 213; + inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 214; enum class FieldChannel : std::uint32_t { diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h index 903d23b..2fcb0d8 100644 --- a/Standalone/Core/src/ShapeWorldInternal.h +++ b/Standalone/Core/src/ShapeWorldInternal.h @@ -1045,6 +1045,81 @@ public: &&!passageTileQueries->hasColumnZOverlap() &&!passageTileQueries->hasAnyMouthSupport() &&!passageTileQueries->mayContainUpperMouthSupport())return density; + + // On solid samples, the SDF carve can still move the scalar density far outside the + // physical tunnel. The per-passage test below already accounts for this tail using the + // incoming density plus the bounded relief allowance. Apply that same proof before + // entering the passage loop for points outside the scalar influence reach. Columns and + // both mouth-support paths keep their own tile envelopes and therefore disable this + // cut. Optional extra padding is only for research sweeps; zero is the exact bound. + // Invalid values fail open. + static const double PassageBoundPaddingVoxels=[] + { + constexpr double DefaultPaddingVoxels=0.0; + const char* Value=std::getenv( + "VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS"); + if(!Value||!*Value)return DefaultPaddingVoxels; + char* End=nullptr; + const double Parsed=std::strtod(Value,&End); + if(End==Value||*End!='\0'||!std::isfinite(Parsed)||Parsed<0.0) + return std::numeric_limits::infinity(); + return Parsed; + }(); + if(passageTileScopeMatches&&density<0.0f + &&std::isfinite(PassageBoundPaddingVoxels) + &&!passageTileQueries->hasColumnZOverlap() + &&!passageTileQueries->hasAnyMouthSupport() + &&!passageTileQueries->mayContainUpperMouthSupport()) + { + if(passageProfile)++passageProfile->passageTileBoundChecks; + bool bAnyPassageWithinBound=false; + const double ExtraPadding=PassageBoundPaddingVoxels*Q; + for(std::size_t PassageIndex=0; + PassageIndexpassages.size();++PassageIndex) + { + const auto& Passage=ResolvedLayout->passages[PassageIndex]; + if(Passage.controlPoints.size()<2)continue; + const auto& Constants=PassageComposerConstants[PassageIndex]; + if(qz>Constants.firstFloorQ+2.0*Passage.mouthRadiusSubvoxels + ||qzValue;}); + const auto CarveLow=std::lower_bound(Passage.controlPoints.begin(), + Passage.controlPoints.begin()+LowerMouthIndex+1, + qz-Constants.carveSearchMargin, + [&](const auto& Point,double Value){return FloorProfile(Point)>Value;}); + const std::ptrdiff_t CarveBegin=std::max(0, + (CarveHigh-Passage.controlPoints.begin())-1); + const std::ptrdiff_t CarveEnd=std::min(LowerMouthIndex-1, + (CarveLow-Passage.controlPoints.begin())+1); + const double SignedDistanceLimit=-static_cast(density)*Q + +0.24*qPerMetre+ExtraPadding; + if(PassageMayAffectDensity(PassageIndex,CarveBegin,CarveEnd, + LowerMouthIndex,Constants.mouthSearchBegin,Constants.mouthSearchEnd, + Constants.lastIndex,qx,qy,qz,SignedDistanceLimit)) + { + bAnyPassageWithinBound=true; + break; + } + } + if(!bAnyPassageWithinBound) + { + if(passageProfile) + { + ++passageProfile->passageTileBoundRejects; + passageProfile->passageTileBoundSkippedIterations + +=ResolvedLayout->passages.size(); + } + return density; + } + } bool queryNearUpperMouthSupport=false; const auto checkUpperMouth=[&](const auto& passage) { diff --git a/Standalone/Research/WorldAssemblerM3/Linux81/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux81/REPORT.md new file mode 100644 index 0000000..eaa64c3 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux81/REPORT.md @@ -0,0 +1,103 @@ +# Linux81 — passage reach bound + +## Reach derived from the code + +The owner core voxel is 0.25 m. The passage carve is `max(source, -roughSignedDistance / Q)`. For a negative source value `d`, it can change the scalar as far as `-d` voxels outside the route radius. The outer test therefore uses `-d * Q + 0.24 m`; the extra sweep padding is added beyond that. The 0.24 m allowance covers the ±0.21 m wall relief plus 0.03 m margin; the mouth floor relief is bounded by ±0.20 m. + +| Term | Reach and gate in the implementation | +|---|---| +| Route carve and wall relief | Route radius plus the density-dependent `-d` tail and 0.24 m allowance. A fixed geometric pad cannot bound arbitrary negative source values. | +| Smooth joins | Catmull–Rom replaces the chord only on eligible route segments no longer than 8 m; it is a local centerline change, not a long-distance fade. Mouth-support SDF blending has a separate 0.45 m width. | +| Ordinary support | Route tread half-width is at most 1.25 m; the support field extends at most 1 m below its tread and 0.35 m above. Short-span decks are a separate wider term: at most 3.75 m half-width and 5 m thickness, ending 4.87 m below the bore bottom because the deck floor is raised 0.13 m. Crossing-column Z ranges are separately bounded. | +| Mouth shaping and fades | Upper-mouth shaping uses the first three route segments, within 2 m in plan, 8 m along the route, and 4 m of the floor. Carve fade is 8 m along the route; floor relief fades over 11 m. The lower-mouth support blend is gated by a 20 m distance with a 12 m transition. | +| Mouth-route support | The query is limited to 13 m from the upper mouth. Its route cross-section reaches at most 1.01 m from the centerline (0.56 m half-width plus 0.45 m blend); the tile filter uses 15 m XY and 8 m vertical bounds. The outer cut is disabled in mouth-support and column-overlap tiles. | +| Strata/cave smooth minimum | The downstream 12-voxel blend acts on the composed cave field. It does not create a passage difference where the passage field is unchanged. | +| Transition passage fade | Separate seam path: mouth radius + 1 m protected margin + 2 m fade, evaluated by its own XY tile test. | + +The old L80 geometric pad 0 missed the negative-density tail: its worst S0 sample had source density −18.1745 voxels, so the scalar reach was about 4.54 m + 0.24 m beyond the route radius (about 19.1 core voxels). It produced 19,604 changed samples across S0–S3 (19,434 / 63 / 86 / 21), no sign flips, and 6.64 cm maximum displacement. The large density deltas came from the carve’s `max(source, -SDF)` tail, not a far-field fade. + +For L81, **0 extra voxels** is the exact tested point: the density-aware predicate retains the `-d` tail, while the 0.24 m relief margin is already in the bound. No single fixed geometric pad under 8 voxels can cover that tail for all source values. + +## Padding sweep + +Three interleaved runs per padding and stratum compared against the full path (`inf`). Work counts cover 17 L80 high-activity tiles (51 tile/run pairs) and 16 ordinary tiles (48 pairs). Paired time is candidate minus full worker time; negative is faster. + +| Extra pad (voxels) | High skipped / total passage iterations | High share | Ordinary skipped / total | Ordinary share | Median paired tile Δms (high / ordinary) | +|---:|---:|---:|---:|---:|---:| +| 0 | 994,563 / 6,101,982 | 16.30% | 5,421,969 / 5,421,969 | 100.00% | −10.637 / +0.392 | +| 1 | 955,026 / 6,101,982 | 15.65% | 5,421,969 / 5,421,969 | 100.00% | −9.080 / +1.136 | +| 2 | 893,484 / 6,101,982 | 14.64% | 5,421,969 / 5,421,969 | 100.00% | −5.193 / −0.439 | +| 4 | 791,568 / 6,101,982 | 12.97% | 5,421,969 / 5,421,969 | 100.00% | −3.730 / +1.080 | +| 8 | 622,278 / 6,101,982 | 10.20% | 5,420,835 / 5,421,969 | 99.98% | −4.813 / +1.092 | +| 16 | 409,590 / 6,101,982 | 6.71% | 5,382,720 / 5,421,969 | 99.28% | −7.350 / +1.046 | +| 48 | 29,889 / 6,101,982 | 0.49% | 4,070,151 / 5,421,969 | 75.07% | +3.728 / +3.882 | + +All 84 candidate/full pairs matched in density, mesh, payload, topology, material, whole-view, full-detail, and per-LOD hashes. The compared set has 0 changed density samples, 0 sign flips, and 0 maximum/mean surface displacement at every padding. The clean run contains 960 LOD0–1 tiles per stratum per run; verification is recorded in `Saved/Linux/l81/sweep/hash-verification.json`. + +## Decision and readiness + +Ship the density-aware bound at 0 extra voxels. It skips real loop iterations, gives the best work saving in this sweep, and is hash-identical in the measured tile set. Captures and cross-compiler peer runs were not needed under the exact-bound branch of the owner rule; no captures were taken. + +Cold readiness used three interleaved measured full/exact pairs per stratum. Values are medians in seconds. + +| Stratum | LOD0–2 full → exact | Whole view full → exact | +|---|---:|---:| +| SurfaceWorld | 1.304 → 1.246 | 2.738 → 2.749 | +| FloatingIslands | 3.592 → 2.926 | 4.982 → 4.368 | +| TunnelNetwork | 3.177 → 3.505 | 4.612 → 5.022 | +| VerticalShafts | 1.326 → 1.222 | 2.405 → 2.315 | + +All 24 measured runs completed every readiness tile: LOD0–2 counts were 1,408 / 1,216 / 1,344 / 1,344 and whole-view counts were 2,850 / 2,249 / 2,233 / 2,329 in stratum order. + +Readiness is mixed by stratum; the tile work counters and hashes, rather than a broad timing claim, support the decision. + +The L77 LOD0–2 passage-share baseline was 65.9% FI and 65.4% TN. In the matched L81 profiler runs, full → exact means were 64.34% → 64.56% FI and 64.41% → 65.35% TN (two runs each). These small sampled-share changes are within the census variation. + +## Version, hashes, and failed runs + +- Evaluator version: 214. Source stamp: `8edc0292f2df6d279aa7e1d5c9c8c420ed36a9a138e17eed5310463e33e6146d`. Resolved layout identity: `resolved-layout-v3-fnv64:0589300eb07dfe06`. +- Exact outputs were checked over all four strata and the full 960-tile LOD0–1 target set. Captures: none, because the compared outputs are identical. +- The first sweep set mixed source stamps `6db4108c`, `f33a01da`, and `8edc0292`; it is excluded and preserved under `Saved/Linux/l81/sweep-mixed-stale/`. The final 96-run sweep is uniform at `8edc0292`. +- The first sweep parser attempt rejected `key=` as numeric; the parser was corrected before the clean sweep. No final sweep or readiness/census run timed out. +- The optional surface-world passage audit reported 65 solid centerline samples on passage 0, segment 15. The same failure and first sample (`−172, −138, −1794`) occurred with the L81 bound disabled (`inf`), so it is not caused by this bound; it remains a baseline audit failure. +- The floating-islands audit invocation stopped during setup because the selected recipe had no finite SurfaceWorld stratum; it produced no passage-audit result. + +## Follow-up: passage 0 audit and fast tier + +The surface-world audit used `Standalone/data/owner_world.json`, not a recipe fixture. It imported manifest +`bcc0d0d33ea39435` and resolved layout `resolved-layout-v3-fnv64:0589300eb07dfe06` with four strata. The +`--recipe=surface-world` flag selects the generator request. The imported manifest has one SurfaceWorld plan +and no native FloatingIslands plan, while the serialized owner layout still contains all four strata and +the SW→FI interface. + +There are two passage representations in this input. The full-view run (`--max-level=-1`, force-generated) +fails before the resolved-layout audit on the 26-point root `owner_world.passages[0]` route: 65/5,310 +centerline samples are solid, first at `(-172,-138,-1794)`. The canonical serialized SW→FI route has 211 +control points; its resolved-layout audit passes with 4,151 center samples, zero blocked capsule samples, +and all three interfaces passing. So the full-view failure is real for the legacy owner route data, while +the canonical route used by the L70 plan-parity audit is walkable. + +| Checkpoint | Canonical resolved route | Full-view root route | +|---|---|---| +| L70 `bd68aee`; verifier-only `018c0e1`; L71 `971206c` (same standalone runtime source) | PASS | 65 blockers | +| L72 `d970d75` (exact source build) | PASS | 65 blockers | +| L73–L76 `bd1b2a8`, `c67b200`, `9d5455e`, `90378a5` | PASS each | 65 blockers each | +| L77 `5e091d2` | Profiling-only change; no saved standalone binary | No field-evaluation change | +| L78–L79 `ec20994`, `2c8a26c` | PASS each | 65 blockers each | +| HEAD L80 `b06397d` (exact source build) | PASS | 65 blockers | +| L81 candidate | PASS | 65 blockers | + +The 65-sample result therefore predates L81 and is present in the exact L70 source build; no later commit in +this range introduced it. The L70 report's 3/3 result is for the canonical resolved route, which is a +different check from the full-view pre-audit against the root route data. L71 changes tracing scripts but not +the standalone runtime; `2dfe599` and `68cba5a` are read-only research commits. The floating-islands companion +invocation still stops during setup because that selected recipe has no finite SurfaceWorld stratum; it gives +no passage result. No source fix was made. + +`verify.py fast` passed on the L81 candidate in 321.2 s: 80 steps, zero unexpected failures. The four-strate +owner passage stage passed the 3/3 arrival/departure chain and W1/W8 per-stratum hash checks. Full logs and +summary are under `Saved/Linux/l81/verify-fast/`; the summary is `fast-summary.json`. + +Evaluator version 214 was an intentional cache invalidation. The evaluator version is serialized into and +hashed with the Core recipe; advancing it prevents results cached under the previous passage evaluator from +being reused. The measured density and tile hashes remain identical. diff --git a/Standalone/Research/WorldAssemblerM3/Linux81/RunL81Census.py b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81Census.py new file mode 100644 index 0000000..010fcef --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81Census.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +"""Repeat the L77 phase census on FI/TN with the L81 exact cut enabled.""" +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/l81/census" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l81/profile-bin/VoxelForgeCoreHeadless-l81-profile-clang" +CAMERAS = { + "FI": (25.8369140625, 3.19921875, -737.0), + "TN": (19.166015625, -14.2001953125, -753.3740234375), +} +SAMPLE = re.compile(r"^\[L77Sample\] .*?lod=(\d+) .*?phase=(\S+) .*?weight=(\d+)$", re.M) +SAMPLER = re.compile(r"^\[L77Sampler\] action=stop .*?samples=(\d+).*?status=PASS$", re.M) + + +def run(world: str, rep: int, mode: str, padding: str) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + log = OUT / f"l77-phase-{world.lower()}-{mode}-r{rep}.log" + camera = ",".join(f"{value:.12f}" for value in CAMERAS[world]) + ",0,0" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "env", f"VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS={padding}", "timeout", "1740s", + str(BINARY), str(DATA), "--world-assembler-prototype", "--stream-benchmark", + "--stream-pipeline", "--workers=8", "--clip-radius=3", "--max-clip-level=8", + "--max-level=8", f"--camera={camera}", "--profile-lod-work", "--linux77-profile", + ] + start = time.monotonic() + try: + with log.open("w", encoding="utf-8") as stream: + proc = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, check=False, timeout=1740) + except subprocess.TimeoutExpired as exc: + raise RuntimeError(f"census run exceeded 1,740 seconds: {log}") from exc + content = log.read_text(encoding="utf-8", errors="replace") + matches = [(int(lod), phase, int(weight)) for lod, phase, weight in SAMPLE.findall(content)] + sampler = SAMPLER.search(content) + if proc.returncode or not matches or not sampler: + raise RuntimeError(f"census failed exit={proc.returncode}: {log}") + near = [row for row in matches if row[0] <= 2] + passage = sum(weight for _, phase, weight in near if phase == "passages") + total = sum(weight for _, _, weight in near) + row = { + "world": world, "mode": mode, "padding_voxels": padding, + "replicate": rep, "exit_code": proc.returncode, + "wall_s": round(time.monotonic() - start, 3), + "sampler_samples": int(sampler.group(1)), "near_lod_samples": len(near), + "near_lod_weighted_ticks": total, "near_lod_passage_weighted_ticks": passage, + "near_lod_passage_share_pct": 100.0 * passage / total if total else 0.0, + "log": str(log.relative_to(ROOT)), + } + print("[L81CensusRun] " + json.dumps(row, sort_keys=True), flush=True) + return row + + +def main() -> int: + if not BINARY.is_file() or not DATA.is_file(): + raise SystemExit(f"missing input: {BINARY} or {DATA}") + rows = [run(world, rep, mode, padding) + for mode, padding in (("full", "inf"), ("exact", "0")) + for rep in (1, 2) for world in ("FI", "TN")] + by_world = {} + for world in CAMERAS: + by_world[world] = {} + for mode in ("full", "exact"): + values = [row["near_lod_passage_share_pct"] for row in rows + if row["world"] == world and row["mode"] == mode] + by_world[world][mode] = { + "runs_pct": values, "mean_pct": sum(values) / len(values)} + (OUT / "l77-phase-summary.json").write_text( + json.dumps({"runs": rows, "near_lod_passage_share": by_world}, indent=2) + "\n", + encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux81/RunL81OwnerPassageAudits.py b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81OwnerPassageAudits.py new file mode 100644 index 0000000..c76c16e --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81OwnerPassageAudits.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +"""Run the resolved owner passage audit for both owner recipes.""" +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/l81/audits" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l81/bin/VoxelForgeCoreHeadless-l81-clang" +RECIPE = re.compile(r"^\[WorldAssemblerRecipeAudit\] requested=([^ ]+) actual=([^ ]+).*status=PASS$", re.M) +PASSAGE = re.compile(r"^\[ResolvedPassageAudit\].*walkability=PASS$", re.M) +MOUTH = re.compile(r"^\[PassageMouthProfileAudit\].*status=PASS$", re.M) +SUMMARY = re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M) + + +def run(recipe: str) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + log = OUT / f"owner-{recipe}.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "env", "VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS=0", "timeout", "1740s", + str(BINARY), str(DATA), "--workers=12", "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--force-generate", "--clip-radius=3", + "--max-clip-level=8", "--max-level=-1", f"--recipe={recipe}", + "--audit-resolved-layout-passages", + ] + start = time.monotonic() + try: + with log.open("w", encoding="utf-8") as stream: + proc = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, check=False, timeout=1740) + except subprocess.TimeoutExpired as exc: + raise RuntimeError(f"passage audit exceeded 1,740 seconds: {log}") from exc + content = log.read_text(encoding="utf-8", errors="replace") + passages = len(PASSAGE.findall(content)) + mouths = len(MOUTH.findall(content)) + passed = (proc.returncode == 0 and RECIPE.findall(content) == [(recipe, recipe)] + and passages >= 1 and mouths == passages and SUMMARY.search(content) is not None) + row = {"recipe": recipe, "exit_code": proc.returncode, "passages": passages, + "mouth_profiles": mouths, "status": "PASS" if passed else "FAIL", + "wall_s": round(time.monotonic() - start, 3), "log": str(log.relative_to(ROOT))} + print("[L81PassageAuditRun] " + json.dumps(row, sort_keys=True), flush=True) + if not passed: + raise RuntimeError(f"owner passage audit failed: {log}") + return row + + +def main() -> int: + if not BINARY.is_file() or not DATA.is_file(): + raise SystemExit(f"missing input: {BINARY} or {DATA}") + rows = [run(recipe) for recipe in ("surface-world", "floating-islands")] + (OUT / "summary.json").write_text(json.dumps(rows, indent=2) + "\n", + encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux81/RunL81PassageSweep.py b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81PassageSweep.py new file mode 100644 index 0000000..7b212cf --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81PassageSweep.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +"""Interleaved exact passage-bound padding sweep on the L80 tile census.""" +from __future__ import annotations + +import json +import math +import re +import statistics +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l81/sweep" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l81/bin/VoxelForgeCoreHeadless-l81-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), +} +PADS = ("0", "1", "2", "4", "8", "16", "48", "inf") +REPS = 3 +TILE = re.compile(r"^\[PassageComposerTile\] (.*)$", re.M) +FIELD = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") +KEY = re.compile(r"key=\((-?\d+),(-?\d+),(-?\d+),lod(\d+)\)") +HASH = re.compile(r"^\[PipelineTileHash\] lod=(\d+) x=(-?\d+) y=(-?\d+) z=(-?\d+) " + r"density=(\S+) mesh=(\S+) payload=(\S+) topology=(\S+)$", re.M) + + +def parse(log: Path, strate: int, pad: str, rep: int) -> dict: + content = log.read_text(encoding="utf-8", errors="replace") + tiles = [] + for match in TILE.finditer(content): + values = dict(FIELD.findall(match.group(1))) + key = KEY.search(match.group(1)) + if not key: + continue + row = {name: int(value) for name, value in values.items() + if name not in ("key", "worker_ms")} + row["worker_ms"] = float(values["worker_ms"]) + row["key"] = tuple(int(key.group(i)) for i in range(1, 5)) + tiles.append(row) + hashes = {} + for m in HASH.finditer(content): + key = (int(m.group(2)), int(m.group(3)), int(m.group(4)), int(m.group(1))) + hashes[key] = m.group(5, 6, 7, 8) + if not tiles or not hashes: + raise RuntimeError(f"missing passage rows or tile hashes in {log}") + return {"strate": strate, "pad": pad, "replicate": rep, "log": str(log.relative_to(ROOT)), + "tiles": tiles, "hashes": hashes} + + +def run(strate: int, pad: str, rep: int) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + log = OUT / f"s{strate}-p{pad}-r{rep}.log" + camera = ",".join(f"{v:.12f}" for v in CAMERAS[strate]) + ",0,0" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "env", f"VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS={pad}", "timeout", "1740s", + str(BINARY), str(DATA), "--world-assembler-prototype", "--stream-benchmark", + "--stream-pipeline", "--workers=8", "--clip-radius=3", "--max-clip-level=8", + "--max-level=1", f"--camera={camera}", "--profile-lod-work", + "--profile-passage-composer", "--stream-hash-tiles", + ] + start = time.monotonic() + try: + with log.open("w", encoding="utf-8") as stream: + result = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, check=False, timeout=1740) + except subprocess.TimeoutExpired as exc: + raise RuntimeError(f"run exceeded 1,740 seconds: {log}") from exc + if result.returncode: + raise RuntimeError(f"run failed exit={result.returncode}: {log}") + parsed = parse(log, strate, pad, rep) + parsed["wall_s"] = round(time.monotonic() - start, 3) + parsed["exit_code"] = result.returncode + print("[L81PassageSweepRun] " + json.dumps( + {k: v for k, v in parsed.items() if k not in ("tiles", "hashes")}, sort_keys=True), + flush=True) + return parsed + + +def main() -> int: + if not BINARY.is_file() or not DATA.is_file(): + raise SystemExit(f"missing input: {BINARY} or {DATA}") + old = json.loads((ROOT / "Saved/Linux/l80/passage-census/candidate-pad4.json") + .read_text(encoding="utf-8")) + high = {int(run["strate"]): {tuple(key) for key in run["keys"]} for run in old} + results = [] + ordinary: dict[int, set[tuple[int, int, int, int]]] = {} + for strate in range(4): + for rep in range(1, REPS + 1): + offset = (strate + rep - 1) % len(PADS) + order = PADS[offset:] + PADS[:offset] + for pad in order: + run_data = run(strate, pad, rep) + if strate not in ordinary: + candidates = [row for row in run_data["tiles"] + if row["key"] not in high[strate] + and row.get("features", 0) == 0 + and row.get("tile_bound_checks", 0) > 0] + candidates.sort(key=lambda row: ( + row.get("tile_bound_rejects", 0), row.get("worker_ms", 0)), + reverse=True) + ordinary[strate] = {row["key"] for row in candidates[:4]} + results.append(run_data) + + indexed = {(r["strate"], r["pad"], r["replicate"]): r for r in results} + pad_rows = [] + failures = [] + for strate in range(4): + high_keys = high[strate] + ordinary_keys = ordinary[strate] + for pad in PADS[:-1]: + pair_deltas = {"high": [], "ordinary": []} + sums = {name: {key: 0 for key in ("tile_bound_checks", "tile_bound_rejects", + "tile_bound_skipped_iterations", "passage_iterations", "samples")} + for name in pair_deltas} + all_hashes_equal = True + for rep in range(1, REPS + 1): + candidate = indexed[(strate, pad, rep)] + baseline = indexed[(strate, "inf", rep)] + base_tiles = {row["key"]: row for row in baseline["tiles"]} + for row in candidate["tiles"]: + key = row["key"] + if key in high_keys: + group = "high" + elif key in ordinary_keys: + group = "ordinary" + else: + continue + base = base_tiles[key] + pair_deltas[group].append(row["worker_ms"] - base["worker_ms"]) + for metric in sums[group]: + sums[group][metric] += row.get(metric, 0) + if candidate["hashes"].get(key) != baseline["hashes"].get(key): + all_hashes_equal = False + failures.append({"strate": strate, "pad": pad, "replicate": rep, + "key": key, "candidate_hash": candidate["hashes"].get(key), + "baseline_hash": baseline["hashes"].get(key)}) + entry = {"strate": strate, "pad": pad, "replicates": REPS, + "high_keys": len(high_keys), "ordinary_keys": len(ordinary_keys), + "hashes_equal_to_full": all_hashes_equal} + for group in ("high", "ordinary"): + n = len(pair_deltas[group]) + count_denominator = sums[group]["tile_bound_skipped_iterations"] \ + + sums[group]["passage_iterations"] + entry[group] = { + **sums[group], + "skipped_iteration_fraction": ( + sums[group]["tile_bound_skipped_iterations"] / count_denominator + if count_denominator else 0.0), + "paired_tile_ms_delta_median": statistics.median(pair_deltas[group]) + if n else 0.0, + "paired_tile_ms_delta_mean": statistics.mean(pair_deltas[group]) + if n else 0.0, + } + pad_rows.append(entry) + + payload = { + "pads_voxels_extra": list(PADS[:-1]), "baseline": "infinite extra pad (fail-open full passage path)", + "high_tile_keys_from_l80": {str(k): [list(v) for v in sorted(keys)] + for k, keys in high.items()}, + "ordinary_tile_keys": {str(k): [list(v) for v in sorted(keys)] + for k, keys in ordinary.items()}, + "sweep_rows": pad_rows, + "hash_mismatches": failures, + "runs": [{k: v for k, v in r.items() if k not in ("tiles", "hashes")} + for r in results], + } + (OUT / "summary.json").write_text(json.dumps(payload, indent=2) + "\n", + encoding="utf-8") + print(f"[L81PassageSweepSummary] runs={len(results)} hash_mismatches={len(failures)} " + f"summary={OUT / 'summary.json'}", flush=True) + return 1 if failures else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux81/RunL81Readiness.py b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81Readiness.py new file mode 100644 index 0000000..15d55b6 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux81/RunL81Readiness.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""L80-protocol cold readiness comparison for the exact passage cut.""" +from __future__ import annotations + +import json +import re +import statistics +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l81/readiness" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l81/bin/VoxelForgeCoreHeadless-l81-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), +} +MODES = (("full", "inf"), ("exact", "0")) +PIPELINE = re.compile(r"^\[StreamPipeline\] (.*)$", re.M) +FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") +NEAR = re.compile(r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) " + r"cold_ready_s=([0-9.]+)$", re.M) + + +def run(strate: int, mode: str, padding: str, phase: str, rep: int) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + label = f"s{strate}-{mode}-{phase}{rep}" + log = OUT / f"{label}.log" + camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", + "env", f"VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS={padding}", "timeout", "1740s", + str(BINARY), str(DATA), "--world-assembler-prototype", "--stream-benchmark", + "--stream-pipeline", "--workers=8", "--clip-radius=3", "--max-clip-level=8", + "--max-level=8", f"--camera={camera}", "--profile-lod-work", + "--stream-scheduler-policy=3", + ] + start = time.monotonic() + try: + with log.open("w", encoding="utf-8") as stream: + proc = subprocess.run(command, cwd=ROOT, stdout=stream, + stderr=subprocess.STDOUT, check=False, timeout=1740) + except subprocess.TimeoutExpired as exc: + raise RuntimeError(f"readiness run exceeded 1,740 seconds: {log}") from exc + content = log.read_text(encoding="utf-8", errors="replace") + pipeline_match = PIPELINE.search(content) + near_match = NEAR.search(content) + if proc.returncode or not pipeline_match or not near_match: + raise RuntimeError(f"readiness run failed exit={proc.returncode}: {log}") + pipeline = dict(FIELDS.findall(pipeline_match.group(1))) + row = { + "strate": strate, "mode": mode, "padding_voxels": padding, + "phase": phase, "replicate": rep, "exit_code": proc.returncode, + "wall_s": round(time.monotonic() - start, 3), + "lod02_ready_s": float(near_match.group(3)), + "whole_view_ready_s": float(pipeline["whole_view_ready"].removesuffix("s")), + "whole_tiles": int(pipeline["whole_tiles"].split("/")[0]), + "log": str(log.relative_to(ROOT)), + } + print("[L81ReadinessRun] " + json.dumps(row, sort_keys=True), flush=True) + return row + + +def main() -> int: + if not BINARY.is_file() or not DATA.is_file(): + raise SystemExit(f"missing input: {BINARY} or {DATA}") + rows = [] + for strate in range(4): + for mode, padding in MODES: + run(strate, mode, padding, "warmup", 0) + orders = (("full", "exact"), ("exact", "full"), ("full", "exact")) + for strate in range(4): + for rep, order in enumerate(orders, 1): + for mode in order: + padding = dict(MODES)[mode] + rows.append(run(strate, mode, padding, "measured", rep)) + (OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n", + encoding="utf-8") + summary = [] + for strate in range(4): + group = [row for row in rows if row["strate"] == strate] + item = {"strate": strate, "measured_pairs": 3} + for mode, _ in MODES: + subset = [row for row in group if row["mode"] == mode] + item[f"{mode}_lod02_s"] = [row["lod02_ready_s"] for row in subset] + item[f"{mode}_lod02_median_s"] = statistics.median( + row["lod02_ready_s"] for row in subset) + item[f"{mode}_whole_s"] = [row["whole_view_ready_s"] for row in subset] + item[f"{mode}_whole_median_s"] = statistics.median( + row["whole_view_ready_s"] for row in subset) + summary.append(item) + (OUT / "summary.json").write_text(json.dumps({"runs": rows, "summary": summary}, + indent=2) + "\n", encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Source/StreamingWorld.h b/Standalone/Source/StreamingWorld.h index 5e25e51..a12e368 100644 --- a/Standalone/Source/StreamingWorld.h +++ b/Standalone/Source/StreamingWorld.h @@ -1306,7 +1306,7 @@ private: static_cast(D.islandSharedCacheConflictReplacements), static_cast(D.islandSharedCacheLockWaitNanoseconds)/1000.0); if(D.passageComposerProfiled) - std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu native_field_calls=%llu features=%llu route_candidates=%llu mouth_candidates=%llu queries=%llu passage_iterations=%llu upper_mouth_checks=%llu column_candidates=%llu column_distance_calls=%llu negative_bounds=%llu negative_rejects=%llu positive_bounds=%llu positive_rejects=%llu carve_segment_visits=%llu smooth_carve_joins=%llu support_segment_visits=%llu smooth_support_joins=%llu support_capsule_checks=%llu later_bore_checks=%llu other_capsule_checks=%llu mouth_support_calls=%llu mouth_route_segment_checks=%llu relief_field_calls=%llu smoothstep_evaluations=%llu\n", + std::printf("[PassageComposerTile] key=(%d,%d,%d,lod%d) worker_ms=%.3f samples=%llu native_field_calls=%llu features=%llu route_candidates=%llu mouth_candidates=%llu queries=%llu passage_iterations=%llu tile_bound_checks=%llu tile_bound_rejects=%llu tile_bound_skipped_iterations=%llu upper_mouth_checks=%llu column_candidates=%llu column_distance_calls=%llu negative_bounds=%llu negative_rejects=%llu positive_bounds=%llu positive_rejects=%llu carve_segment_visits=%llu smooth_carve_joins=%llu support_segment_visits=%llu smooth_support_joins=%llu support_capsule_checks=%llu later_bore_checks=%llu other_capsule_checks=%llu mouth_support_calls=%llu mouth_route_segment_checks=%llu relief_field_calls=%llu smoothstep_evaluations=%llu\n", Event.Key.Coord.X,Event.Key.Coord.Y,Event.Key.Coord.Z,Event.Key.Level, Event.WorkerMilliseconds,static_cast(Event.DensitySamples), static_cast(D.fieldCalls), @@ -1315,6 +1315,9 @@ private: static_cast(D.passageTileMouthSegmentCandidates), static_cast(D.passageComposerQueries), static_cast(D.passageIterations), + static_cast(D.passageTileBoundChecks), + static_cast(D.passageTileBoundRejects), + static_cast(D.passageTileBoundSkippedIterations), static_cast(D.passageUpperMouthFilterChecks), static_cast(D.passageColumnCandidates), static_cast(D.passageColumnDistanceCalls),