From 8805291d3e5007a100d9838f7cb00e4e07d5ad76 Mon Sep 17 00:00:00 2001 From: Fr0zka Date: Wed, 28 Oct 2026 05:50:11 +0100 Subject: [PATCH] diag: passage source-floor clamp rejected (L91); field has steep non-distance tails Clamping the passage-composition source density at -c (c=2..6 voxels, diagnostic, default off) changes the surface far beyond the bar: c=6 still 65 sign flips, up to 165 cm displacement, 120 deg normal change (downstream composition consumes deep values). Source gradient at crossings is distance-like at the median but has huge tails (FI p90 |grad| 512, max 894 vox/vox), so the density-dependent passage reach is inflated by non-distance values. No world change. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1 --- .../include/VoxelForgeCore/VoxelForgeCore.h | 6 + Standalone/Core/src/ShapeWorldInternal.h | 90 +++- Standalone/Core/src/VoxelForgeCore.cpp | 5 +- .../WorldAssemblerM3/Linux91/REPORT.md | 57 +++ .../Linux91/RunL91CandidateCensus.py | 182 ++++++++ .../Linux91/RunL91ClampEffects.py | 80 ++++ .../Linux91/RunL91SurfaceCaptures.py | 91 ++++ Standalone/Source/StreamBenchmarkHeadless.cpp | 6 +- Standalone/Source/StreamBenchmarkPipeline.h | 431 +++++++++++++++++- 9 files changed, 937 insertions(+), 11 deletions(-) create mode 100644 Standalone/Research/WorldAssemblerM3/Linux91/REPORT.md create mode 100644 Standalone/Research/WorldAssemblerM3/Linux91/RunL91CandidateCensus.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux91/RunL91ClampEffects.py create mode 100644 Standalone/Research/WorldAssemblerM3/Linux91/RunL91SurfaceCaptures.py diff --git a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h index 9f629df..ff1cab3 100644 --- a/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h +++ b/Standalone/Core/include/VoxelForgeCore/VoxelForgeCore.h @@ -1809,6 +1809,10 @@ namespace VoxelForgeCore bool profileMesherStages = false; // Research-only: count passage-composer work per generated tile. bool profilePassageComposer = false; + // Research-only field comparison. A nonnegative value clamps the source density + // entering passage composition to at least -N voxel units for this tile. + // A negative value leaves the process-level diagnostic override in control. + double diagnosticPassageSourceFloorVoxels = -1.0; // Research-only: collect tile-drain kernel counters and floating-island cache metrics. // Inactive on ordinary generation so the hot path only pays the option check. bool instrumentTileDrain = false; @@ -1907,6 +1911,8 @@ namespace VoxelForgeCore std::string densityDispatchPathAt(Vec3d voxelPosition) const; // Analytic source distance without the occupancy-preserving near-zero clamp or packed proxy. float unclampedDensityAt(Vec3d voxelPosition) const; + // Research-only source field entering passage composition, with passage carving omitted. + float passageSourceDensityAt(Vec3d voxelPosition) const; DetailFieldSample detailSampleAt(Vec3d voxelPosition) const; float densityAtLattice(Vec3i voxelPosition) const; bool isSolidAt(Vec3d voxelPosition) const; diff --git a/Standalone/Core/src/ShapeWorldInternal.h b/Standalone/Core/src/ShapeWorldInternal.h index 7531550..6e2ea54 100644 --- a/Standalone/Core/src/ShapeWorldInternal.h +++ b/Standalone/Core/src/ShapeWorldInternal.h @@ -70,6 +70,20 @@ static bool VF_IsL90PassageCandidateCensusEnabled() return bEnabled; } +static double VF_L91PassageSourceFloorVoxels() +{ + static const double Value=[] + { + const char* Raw=std::getenv("VOXELFORGE_L91_PASSAGE_SOURCE_FLOOR_VOXELS"); + if(!Raw||!*Raw)return 0.0; + char* End=nullptr; + const double Parsed=std::strtod(Raw,&End); + return End!=Raw&&*End=='\0'&&std::isfinite(Parsed)&&Parsed>0.0 + ?Parsed:0.0; + }(); + return Value; +} + // Standalone-only world compiler. It compiles the runtime room/tunnel/passages into // axis-line intervals, composes rock/cave signs into packed Z-column words, then displaces // only the conservative surface band. The copied Unreal generator remains the A/B reference. @@ -226,12 +240,18 @@ public: FScopedPassageTileQueries(const FStandaloneShapeWorld& shape, const std::array& minimumSubvoxels, const std::array& maximumSubvoxels, - VoxelForgeCore::TileGenerationStats* profileStats=nullptr,int32 profileLod=0) + VoxelForgeCore::TileGenerationStats* profileStats=nullptr,int32 profileLod=0, + double passageSourceFloorVoxels=-1.0) :shape_(&shape),minimumSubvoxels_(minimumSubvoxels), maximumSubvoxels_(maximumSubvoxels),previous_(ActivePassageTileQueries), profileStats_(profileStats),profileLod_(profileLod), - candidateProfiled_(profileStats!=nullptr&&shape.L90PassageCandidateProgramEnabled) + candidateProfiled_(profileStats!=nullptr&&shape.L90PassageCandidateProgramEnabled), + passageSourceFloorVoxels_(std::isfinite(passageSourceFloorVoxels) + &&passageSourceFloorVoxels>=0.0?passageSourceFloorVoxels + :VF_L91PassageSourceFloorVoxels()) { + if(passageSourceFloorVoxels_>0.0) + clampCounters_=std::make_shared(); mayContainResolvedBoundaryBand_=shape.ResolvedBoundaryBandsIntersectSubvoxelRange( minimumSubvoxels_[2],maximumSubvoxels_[2]); const bool disablePassageComposition=VF_IsL88PassageCompositionDisabled(); @@ -338,11 +358,18 @@ public: mayContainResolvedBoundaryBand_(source.mayContainResolvedBoundaryBand_), mayContainTransitionPassageFade_(source.mayContainTransitionPassageFade_), previous_(ActivePassageTileQueries),profileLod_(source.profileLod_), - candidateProfiled_(source.candidateProfiled_) + candidateProfiled_(source.candidateProfiled_), + passageSourceFloorVoxels_(source.passageSourceFloorVoxels_), + clampCounters_(source.clampCounters_),ownsClampCounters_(false) { ActivePassageTileQueries=this; } ~FScopedPassageTileQueries() { ActivePassageTileQueries=previous_; + if(ownsClampCounters_&&clampCounters_&&passageSourceFloorVoxels_>0.0) + std::printf("[L91PassageSourceClampTile] lod=%d floor_vox=%.3f source_clamped=%llu source_delta_sum=%.9g deepest_source_vox=%.9g\n", + profileLod_,passageSourceFloorVoxels_, + static_cast(clampCounters_->samples.load()), + clampCounters_->deltaSum.load(),clampCounters_->deepestSource.load()); if(!candidateProfiled_||l90Queries_==0)return; std::array,4> HistText{}; constexpr std::array Names={"carve","tread","clearance","mouth_column"}; @@ -451,6 +478,20 @@ public: VoxelForgeCore::TileGenerationStats* profileStats() const noexcept {return profileStats_;} bool candidateCensusProfiled() const noexcept{return candidateProfiled_;} + double passageSourceFloorVoxels() const noexcept{return passageSourceFloorVoxels_;} + void recordPassageSourceClamp(float sourceDensity) const noexcept + { + if(passageSourceFloorVoxels_<=0.0)return; + const double delta=-passageSourceFloorVoxels_-static_cast(sourceDensity); + if(!(delta>0.0)||!std::isfinite(delta))return; + if(!clampCounters_)clampCounters_=std::make_shared(); + clampCounters_->samples.fetch_add(1,std::memory_order_relaxed); + clampCounters_->deltaSum.fetch_add(delta,std::memory_order_relaxed); + double prior=clampCounters_->deepestSource.load(std::memory_order_relaxed); + while(static_cast(sourceDensity)deepestSource.compare_exchange_weak(prior,sourceDensity, + std::memory_order_relaxed,std::memory_order_relaxed)){} + } void recordL90CandidateHit(const std::array& counts,bool haveBrick) const { if(!candidateProfiled_)return; @@ -491,6 +532,12 @@ public: {return cavernFeatureIndices_;} private: + struct FPassageSourceClampCounters + { + std::atomic samples{0}; + std::atomic deltaSum{0.0}; + std::atomic deepestSource{0.0}; + }; const FStandaloneShapeWorld* shape_; std::array minimumSubvoxels_,maximumSubvoxels_; std::vector featureIndices_; @@ -508,6 +555,9 @@ public: VoxelForgeCore::TileGenerationStats* profileStats_=nullptr; int32 profileLod_=0; bool candidateProfiled_=false; + double passageSourceFloorVoxels_=0.0; + mutable std::shared_ptr clampCounters_; + bool ownsClampCounters_=true; mutable std::uint64_t l90Queries_=0,l90NoBrickQueries_=0; mutable std::uint64_t l90FallbackIterations_=0,l90FallbackNanoseconds_=0, l90QueryNanoseconds_=0,l90FallbackCarveVisits_=0,l90FallbackTreadVisits_=0, @@ -1269,6 +1319,16 @@ public: ~FL90CandidateBrickScope() {FStandaloneShapeWorld::ActivePassageCandidateBrick=previous;} } l90CandidateBrickScope(l90CandidateBrick); + const double passageSourceFloorVoxels=passageTileScopeMatches + ?passageTileQueries->passageSourceFloorVoxels() + :VF_L91PassageSourceFloorVoxels(); + if(passageSourceFloorVoxels>0.0 + &&static_cast(density)<-passageSourceFloorVoxels) + { + if(passageTileScopeMatches) + passageTileQueries->recordPassageSourceClamp(density); + density=static_cast(-passageSourceFloorVoxels); + } if(passageTileScopeMatches&&density>=0.0f &&!passageTileQueries->hasRouteFeatureOverlap() &&!passageTileQueries->hasColumnZOverlap() @@ -4002,6 +4062,9 @@ public: return AnalyticDensityAt(X,Y,Z); } + float PassageSourceDensityAt(float X,float Y,float Z) const + {return NativePassageDensityAt(X,Y,Z);} + std::vector OreVeinTileCandidates( const std::array& minimum, const std::array& maximum) const @@ -5900,12 +5963,24 @@ private: L90PassageCandidateBricks.clear(); L90PassageCandidateProgramValid=false; L90PassageCandidateBuildFailures=0; - L90PassageCandidateReachMetres=10.0; + const auto ReadPositiveEnv=[](const char* Name,double Default) + { + const char* Raw=std::getenv(Name); + if(!Raw||!*Raw)return Default; + char* End=nullptr; + const double Parsed=std::strtod(Raw,&End); + return End!=Raw&&*End=='\0'&&std::isfinite(Parsed)&&Parsed>0.0 + ?Parsed:Default; + }; + L90PassageCandidateReachMetres=ReadPositiveEnv( + "VOXELFORGE_L90_PASSAGE_REACH_METRES",10.0); + const double brickMetres=ReadPositiveEnv( + "VOXELFORGE_L90_PASSAGE_BRICK_METRES",4.0); constexpr double Q=static_cast(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel); const double qPerMetre=Q*1000000.0/std::max(1, ResolvedLayout->coreVoxelSizeMicrometres); const double voxelsPerMetre=qPerMetre/Q; - L90PassageCandidateBrickEdgeSubvoxels=4.0*qPerMetre; + L90PassageCandidateBrickEdgeSubvoxels=brickMetres*qPerMetre; const double reachSubvoxels=L90PassageCandidateReachMetres*qPerMetre; const double cubicPadding=1.0; std::array emittedReferences{}; @@ -6241,8 +6316,9 @@ private: L90PassageCandidateProgramValid=L90PassageCandidateBuildFailures==0; const double buildMilliseconds=std::chrono::duration( FClock::now()-buildStart).count(); - std::printf("[L90PassageCandidateBuild] brick_m=4 reach_m=%.1f bricks=%zu emitted=(%llu,%llu,%llu,%llu) unique=(%llu,%llu,%llu,%llu) bytes_estimate=%zu build_ms=%.3f failures=%llu valid=%d\n", - L90PassageCandidateReachMetres,L90PassageCandidateBricks.size(), + std::printf("[L90PassageCandidateBuild] brick_m=%.3f reach_m=%.3f source_floor_vox=%.3f bricks=%zu emitted=(%llu,%llu,%llu,%llu) unique=(%llu,%llu,%llu,%llu) bytes_estimate=%zu build_ms=%.3f failures=%llu valid=%d\n", + brickMetres,L90PassageCandidateReachMetres,VF_L91PassageSourceFloorVoxels(), + L90PassageCandidateBricks.size(), static_cast(emittedReferences[0]), static_cast(emittedReferences[1]), static_cast(emittedReferences[2]), diff --git a/Standalone/Core/src/VoxelForgeCore.cpp b/Standalone/Core/src/VoxelForgeCore.cpp index 4dbe513..a8ac0d1 100644 --- a/Standalone/Core/src/VoxelForgeCore.cpp +++ b/Standalone/Core/src/VoxelForgeCore.cpp @@ -1909,6 +1909,8 @@ namespace VoxelForgeCore :"uncompiled"; } float World::unclampedDensityAt(Vec3d position) const { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DensityAtUnclamped(static_cast(position.x),static_cast(position.y),static_cast(position.z)):0.0f; } + float World::passageSourceDensityAt(Vec3d position) const + { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->PassageSourceDensityAt(static_cast(position.x),static_cast(position.y),static_cast(position.z)):0.0f; } DetailFieldSample World::detailSampleAt(Vec3d position) const { return impl_&&impl_->shapeReady.load(std::memory_order_acquire)&&impl_->shape?impl_->shape->DetailSampleAt(static_cast(position.x),static_cast(position.y),static_cast(position.z)):DetailFieldSample{}; } float World::densityAtLattice(Vec3i position) const @@ -2794,7 +2796,8 @@ namespace VoxelForgeCore // queryMinimum/queryMaximum ci-dessus couvrent exactement ce domaine inclusif. const FStandaloneShapeWorld::FScopedPassageTileQueries passageFeatureScope( tileShape,queryMinimum,queryMaximum, - options.profilePassageComposer?&out.diagnostics:nullptr,key.lod); + options.profilePassageComposer?&out.diagnostics:nullptr,key.lod, + options.diagnosticPassageSourceFloorVoxels); if(options.profilePassageComposer) { out.diagnostics.passageComposerProfiled=true; diff --git a/Standalone/Research/WorldAssemblerM3/Linux91/REPORT.md b/Standalone/Research/WorldAssemblerM3/Linux91/REPORT.md new file mode 100644 index 0000000..0e92498 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux91/REPORT.md @@ -0,0 +1,57 @@ +# Linux 91 — passage source floor + +## Acceptance bar declared before measurement + +For every owner stratum and LOD0–2: zero density sign flips, maximum edge-interpolated surface displacement ≤1 cm, maximum paired mesh-vertex normal-angle change ≤2°, and all passage audits passing. Capture and inspect moved surfaces. Measure source-field gradients at surface crossings. Apply any candidate floor only to the source passed into passage composition, preserving other field features. + +## Result + +**No tested floor is acceptable; stop before the census and prototype.** The comparison clamped only the passage-composition source. Each floor changed samples and meshes, so this is not a no-op. Each stratum covered its full desired near set at LOD0–2: S0 1,408 tiles (512/448/448); FI 1,216 (512/448/256); TN 1,344 (512/448/384); S3 1,344 (512/448/384). There were 174,736,448 total density samples per floor across the four strata. + +The 0.25 m core voxel makes the proposed reach `c·Q + 0.24 m`: c=2/3/4/6 gives 0.74/0.99/1.24/1.74 m. No smallest acceptable c exists. + +| c (vox) | Stratum | Changed samples | Sign flips | Max / mean displacement (cm) | Max normal change (°) | Changed mesh tiles | +|---:|:---|---:|---:|---:|---:|---:| +| 2 | S0 | 22.17M | 212 | 89.72 / 1.62 | 36.65 | 237 | +| 2 | FI | 25.76M | 124 | 91.67 / 1.06 | 84.80 | 155 | +| 2 | TN | 16.46M | 124 | 95.01 / 1.54 | 79.33 | 164 | +| 2 | S3 | 4.03M | 0 | 185.92 / 9.47 | 116.43 | 198 | +| 3 | S0 | 21.98M | 121 | 71.62 / 0.81 | 34.46 | 210 | +| 3 | FI | 25.66M | 54 | 72.08 / 0.47 | 80.24 | 147 | +| 3 | TN | 16.36M | 54 | 76.92 / 0.67 | 80.24 | 151 | +| 3 | S3 | 4.01M | 0 | 180.36 / 6.60 | 118.75 | 189 | +| 4 | S0 | 21.79M | 91 | 58.19 / 0.38 | 35.29 | 141 | +| 4 | FI | 25.55M | 6 | 59.35 / 0.16 | 82.15 | 107 | +| 4 | TN | 16.27M | 6 | 62.94 / 0.24 | 82.15 | 134 | +| 4 | S3 | 3.99M | 0 | 175.11 / 4.60 | 120.47 | 189 | +| 6 | S0 | 21.40M | 65 | 38.24 / 0.08 | 35.24 | 121 | +| 6 | FI | 1.67M | 0 | 43.78 / 0.04 | 81.05 | 101 | +| 6 | TN | 2.52M | 0 | 43.78 / 0.05 | 81.05 | 128 | +| 6 | S3 | 2.23M | 0 | 165.43 / 1.88 | 120.10 | 180 | + +The route-near moved-crossing classifier tagged wall/floor/mouth changes at every floor. Totals across strata (wall, floor, mouth, other) were: c2 (4,806, 2,527, 7,587, 96,946); c3 (3,547, 2,046, 7,343, 81,736); c4 (2,215, 1,371, 7,252, 70,981); c6 (665, 272, 2,461, 33,139). These region labels use nearest-route-chord heuristics. Normal maxima compare index-paired vertices; normal changes in tiles with incompatible vertex/index pairing were unmeasured (183/151/141/112 tiles for c2/c3/c4/c6). + +The source gradient is near distance-like at the median, but has steep outliers. Central differences use a 1-voxel step at source `|d|≤2` crossings outside the approximate passage region: + +| Stratum | Samples | p50 / p90 / max `|∇d|` (vox/vox) | +|:---|---:|---:| +| S0 | 7,137 | 1.205 / 1.815 / 4.741 | +| FI | 1,929 | 1.001 / 511.674 / 893.726 | +| TN | 2,142 | 0.993 / 1.047 / 893.726 | +| S3 | 3,079 | 0.932 / 1.036 / 444.254 | + +Thus the distance-field hypothesis holds around the median, not across the measured tails. Even c6 still has 65 sign flips overall, up to 165.43 cm displacement, and up to 120.47° paired normal change. S3 has zero flips at every floor but substantial crossing motion, showing that sign preservation alone does not bound the surface change. + +Matched baseline/c6 captures were generated for all four strata at the audit cameras: [S0](../../../../Saved/Linux/l91/captures/s0-c0.png), [FI](../../../../Saved/Linux/l91/captures/fi-c0.png), [TN](../../../../Saved/Linux/l91/captures/tn-c0.png), [S3](../../../../Saved/Linux/l91/captures/s3-c0.png), with corresponding `*-c6.png` files beside them. The views are qualitative and do not resolve the measured local shifts. + +## Census, prototype, readiness + +The L90 census was not rerun at 2 m or 4 m bricks because no acceptable c was found. The compiled evaluator prototype on `passage/1-2/0` and W8/W12 LOD0–2 timing were not run. Passage walkability audits were not run after the effect gate failed. No production clamp was adopted; its diagnostic floor defaults off and is confined to passage composition. Therefore no version bump, hash repin, or fast-tier/same-world adoption run was made. Same-world peers and cross-compiler equality remain unmeasured for the rejected candidate. Existing production hashes/default behavior were not intentionally changed. + +## Runs and evidence + +- Clang 19 standalone build passed; source stamp: `93fac418c7497299b43bd183625ba2117ec8096b2daeb6ea2f74f0171b348597`. +- All four clamp audits covered the expected complete near tile sets, with zero generation failures and invalid comparisons. Their exit code 14 marks the threshold failure, not a crash. +- All 16 baseline/c6 export and capture steps passed. No process exceeded 30 minutes. All headless runs used `taskset -c 0-11` and `prlimit --as=6442450944`. +- The first standalone build launch failed before compiling because its log directory did not exist; after creating it, the Clang build passed. No Unreal build was run because the clamp failed the effect gate before production integration. +- Detailed counts/logs: `Saved/Linux/l91/clamp-effects/`; captures/logs: `Saved/Linux/l91/captures/`; census runner remains unexecuted at `RunL91CandidateCensus.py`. diff --git a/Standalone/Research/WorldAssemblerM3/Linux91/RunL91CandidateCensus.py b/Standalone/Research/WorldAssemblerM3/Linux91/RunL91CandidateCensus.py new file mode 100644 index 0000000..3cf2185 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux91/RunL91CandidateCensus.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +"""Rerun the L90 candidate census at a measured source floor and two brick sizes.""" +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l91/candidate-census" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l91/bin/VoxelForgeCoreHeadless-l91-clang19" +STRATA = { + "S0": {"camera": (8.0, 16.0, -372.2626953125), "near": 1408}, + "FI": {"camera": (25.8369140625, 3.19921875, -737.0), "near": 1216}, + "TN": {"camera": (19.166015625, -14.2001953125, -753.3740234375), "near": 1344}, + "S3": {"camera": (-20.0625, -5.068359375, -900.353515625), "near": 1344}, +} +FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") +BUILD = re.compile(r"^\[L90PassageCandidateBuild\] (.*)$", re.M) +CENSUS = re.compile(r"^\[L90PassageCandidateTile\] (.*)$", re.M) +NEAR = re.compile(r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$", re.M) +STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=(\S+)$", re.M) +HISTS = ("carve_hist", "tread_hist", "clearance_hist", "mouth_column_hist") +SEMANTICS = tuple(name.removesuffix("_hist") for name in HISTS) + + +def parse_fields(line: str) -> dict[str, str]: + return dict(FIELDS.findall(line)) + + +def histogram(value: str) -> list[int]: + items = [int(item) for item in value.split(",")] + if len(items) != 17: + raise ValueError(f"expected 17 histogram bins, got {len(items)}") + return items + + +def percentile(values: list[int], p: float, active: bool = False) -> int: + start = 1 if active else 0 + population = sum(values[start:]) + target = max(1, int(p * population + 0.999999999)) + seen = 0 + for size in range(start, len(values)): + seen += values[size] + if seen >= target: + return size + return 16 + + +def run(stratum: str, floor: float, brick: float) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + spec = STRATA[stratum] + reach = floor * 0.25 + 0.24 + camera = ",".join(f"{value:.12f}" for value in spec["camera"]) + ",0,0" + log = OUT / f"{stratum.lower()}-c{floor:g}-b{brick:g}m-w8.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", "env", + "VOXELFORGE_L90_PASSAGE_CANDIDATE_CENSUS=1", + "VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS=0", + f"VOXELFORGE_L91_PASSAGE_SOURCE_FLOOR_VOXELS={floor:g}", + f"VOXELFORGE_L90_PASSAGE_REACH_METRES={reach:.6f}", + f"VOXELFORGE_L90_PASSAGE_BRICK_METRES={brick:g}", + "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", "--profile-passage-composer", + ] + started = 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=1750) + except subprocess.TimeoutExpired: + result = None + content = log.read_text(encoding="utf-8", errors="replace") + builds = [parse_fields(match.group(1)) for match in BUILD.finditer(content)] + near = NEAR.search(content) + rows = [] + for match in CENSUS.finditer(content): + fields = parse_fields(match.group(1)) + if int(fields["lod"]) > 2: + continue + rows.append(fields) + histograms = {name.removesuffix("_hist"): [0] * 17 for name in HISTS} + fallback_ns = query_ns = 0 + for row in rows: + fallback_ns += int(row["fallback_ns"]) + query_ns += int(row["query_ns"]) + for name in HISTS: + values = histogram(row[name]) + key = name.removesuffix("_hist") + histograms[key] = [a + b for a, b in zip(histograms[key], values)] + metrics = {} + for name, values in histograms.items(): + metrics[name] = { + "queries": sum(values), + "active_p90": percentile(values, 0.90, active=True), + "active_p95": percentile(values, 0.95, active=True), + "all_p90": percentile(values, 0.90), + "histogram_0_to_15_plus": values, + } + row = { + "stratum": stratum, "floor_vox": floor, "reach_m": round(reach, 6), + "brick_m": brick, "workers": 8, + "exit_code": result.returncode if result is not None else "timeout", + "wall_s": round(time.monotonic() - started, 3), + "near_tiles": spec["near"], + "near_complete": bool(near and int(near.group(1)) == spec["near"] + and int(near.group(2)) == spec["near"]), + "lod02_ready_s": float(near.group(3)) if near else None, + "census_rows": len(rows), "census_queries": sum(v["queries"] for v in metrics.values()) // 4, + "fallback_ns": fallback_ns, "query_ns": query_ns, + "fallback_share": fallback_ns / query_ns if query_ns else None, + "histograms": metrics, "build": builds[-1] if builds else None, + "source_stamp": STAMP.search(content).group(1) if STAMP.search(content) else None, + "log": str(log.relative_to(ROOT)), + } + print("[L91CandidateCensusRun] " + json.dumps(row, sort_keys=True), flush=True) + return row + + +def aggregate(rows: list[dict]) -> dict: + output = {} + for brick in sorted({row["brick_m"] for row in rows}): + selected = [row for row in rows if row["brick_m"] == brick] + combined = {name: [0] * 17 for name in SEMANTICS} + fallback_ns = query_ns = 0 + for row in selected: + fallback_ns += row["fallback_ns"] + query_ns += row["query_ns"] + for name in SEMANTICS: + values = row["histograms"][name]["histogram_0_to_15_plus"] + combined[name] = [a + b for a, b in zip(combined[name], values)] + distributions = {name: { + "queries": sum(values), + "all_p90": percentile(values, 0.90), + "active_p90": percentile(values, 0.90, active=True), + "active_p95": percentile(values, 0.95, active=True), + "histogram_0_to_15_plus": values, + } for name, values in combined.items()} + weighted_fallback = fallback_ns / query_ns if query_ns else 1.0 + stratum_fallback = {row["stratum"]: row["fallback_share"] for row in selected} + p90_ok = distributions["carve"]["active_p90"] <= 6 \ + and distributions["mouth_column"]["active_p90"] <= 6 + fallback_ok = weighted_fallback < 0.10 and all( + value is not None and value < 0.10 for value in stratum_fallback.values()) + output[str(brick)] = { + "strata": [row["stratum"] for row in selected], + "weighted_fallback_share": weighted_fallback, + "stratum_fallback_share": stratum_fallback, + "weighted_semantics": distributions, + "p90_pass": p90_ok, "fallback_pass": fallback_ok, + "census_pass": p90_ok and fallback_ok and len(selected) == 4 + and all(row["near_complete"] and row["exit_code"] == 0 for row in selected), + } + return output + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--floor", type=float, required=True) + parser.add_argument("--bricks", type=float, nargs="+", default=[2.0, 4.0]) + parser.add_argument("--strata", nargs="+", choices=tuple(STRATA), default=tuple(STRATA)) + args = parser.parse_args() + if not BINARY.is_file() or not DATA.is_file(): + raise SystemExit(f"missing input: {BINARY} or {DATA}") + rows = [run(stratum, args.floor, brick) + for brick in args.bricks for stratum in args.strata] + payload = {"floor_vox": args.floor, "reach_m": args.floor * 0.25 + 0.24, + "runs": rows, "aggregates_by_brick_m": aggregate(rows)} + (OUT / "summary.json").write_text(json.dumps(payload, indent=2) + "\n", + encoding="utf-8") + return 0 if all(value["census_pass"] + for value in payload["aggregates_by_brick_m"].values()) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux91/RunL91ClampEffects.py b/Standalone/Research/WorldAssemblerM3/Linux91/RunL91ClampEffects.py new file mode 100644 index 0000000..52818f3 --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux91/RunL91ClampEffects.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 +"""Measure source-floor effects across one owner stratum's complete near view.""" +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/l91/clamp-effects" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l91/bin/VoxelForgeCoreHeadless-l91-clang19" +STRATA = { + "S0": {"camera": (8.0, 16.0, -372.2626953125), "near": 1408}, + "FI": {"camera": (25.8369140625, 3.19921875, -737.0), "near": 1216}, + "TN": {"camera": (19.166015625, -14.2001953125, -753.3740234375), "near": 1344}, + "S3": {"camera": (-20.0625, -5.068359375, -900.353515625), "near": 1344}, +} +STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=(\S+)$", re.M) +COVERAGE = re.compile(r"^\[L91PassageClampCoverage\] (.*)$", re.M) +SUMMARY = re.compile(r"^\[L91PassageClampSummary\] (.*)$", re.M) +GRADIENT = re.compile(r"^\[L91PassageSourceGradient\] (.*)$", re.M) +AUDIT = re.compile(r"^\[L91PassageClampAudit\] (.*)$", re.M) +FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)") + + +def run(stratum: str) -> dict: + OUT.mkdir(parents=True, exist_ok=True) + spec = STRATA[stratum] + camera = ",".join(f"{value:.12f}" for value in spec["camera"]) + ",0,0" + log = OUT / f"{stratum.lower()}-effects.log" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", "env", + "VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS=0", "timeout", "1740s", + str(BINARY), str(DATA), "--world-assembler-prototype", "--stream-benchmark", + "--stream-pipeline", "--workers=8", "--clip-radius=3", "--max-clip-level=8", + "--max-level=2", f"--camera={camera}", "--audit-passage-source-clamp", + ] + 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=1750) + content = log.read_text(encoding="utf-8", errors="replace") + coverage = COVERAGE.search(content) + summaries = [dict(FIELDS.findall(match.group(1))) for match in SUMMARY.finditer(content)] + gradient = GRADIENT.search(content) + audit = AUDIT.search(content) + row = { + "stratum": stratum, + "exit_code": result.returncode, + "wall_s": round(time.monotonic() - started, 3), + "expected_near_tiles": spec["near"], + "coverage": dict(FIELDS.findall(coverage.group(1))) if coverage else None, + "floors": summaries, + "source_gradient": dict(FIELDS.findall(gradient.group(1))) if gradient else None, + "audit": dict(FIELDS.findall(audit.group(1))) if audit else None, + "source_stamp": STAMP.search(content).group(1) if STAMP.search(content) else None, + "log": str(log.relative_to(ROOT)), + } + (OUT / f"{stratum.lower()}-effects.json").write_text( + json.dumps(row, indent=2) + "\n", encoding="utf-8") + print("[L91ClampEffectsRun] " + json.dumps(row, sort_keys=True), flush=True) + return row + + +def main() -> int: + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("--stratum", choices=tuple(STRATA), required=True) + args = parser.parse_args() + if not BINARY.is_file() or not DATA.is_file(): + raise SystemExit(f"missing input: {BINARY} or {DATA}") + run(args.stratum) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Research/WorldAssemblerM3/Linux91/RunL91SurfaceCaptures.py b/Standalone/Research/WorldAssemblerM3/Linux91/RunL91SurfaceCaptures.py new file mode 100644 index 0000000..000b03f --- /dev/null +++ b/Standalone/Research/WorldAssemblerM3/Linux91/RunL91SurfaceCaptures.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +"""Capture matched baseline and c=6 near-LOD views for each owner stratum.""" +from __future__ import annotations + +import json +import subprocess +import time +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[4] +OUT = ROOT / "Saved/Linux/l91/captures" +DATA = ROOT / "Standalone/data/owner_world.json" +BINARY = ROOT / "Saved/Linux/l91/bin/VoxelForgeCoreHeadless-l91-clang19" +CAPTURE = ROOT / "Saved/Linux/l91/bin/VoxelForgeOffscreenCapture-l91-clang19" +STRATA = { + "S0": (8.0, 16.0, -372.2626953125), + "FI": (25.8369140625, 3.19921875, -737.0), + "TN": (19.166015625, -14.2001953125, -753.3740234375), + "S3": (-20.0625, -5.068359375, -900.353515625), +} + + +def export(stratum: str, floor: float) -> dict: + name = f"{stratum.lower()}-c{floor:g}" + scene = OUT / f"{name}.vfmesh" + log = OUT / f"{name}-export.log" + camera = ",".join(f"{value:.12f}" for value in STRATA[stratum]) + ",0,0" + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", "env", + "VOXELFORGE_L81_PASSAGE_BOUND_PADDING_VOXELS=0", + f"VOXELFORGE_L91_PASSAGE_SOURCE_FLOOR_VOXELS={floor:g}", + "timeout", "1740s", str(BINARY), str(DATA), "--world-assembler-prototype", + "--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3", + "--max-clip-level=8", "--max-level=2", f"--camera={camera}", + "--capture-skip-walkability", "--mesh-coverage", f"--mesh-export={scene}", + ] + 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=1750) + return {"name": name, "stratum": stratum, "floor_vox": floor, + "exit_code": result.returncode, "wall_s": round(time.monotonic() - started, 3), + "scene": str(scene.relative_to(ROOT)), "log": str(log.relative_to(ROOT)), + "scene_bytes": scene.stat().st_size if scene.exists() else 0} + + +def render(stratum: str, floor: float) -> dict: + name = f"{stratum.lower()}-c{floor:g}" + scene = OUT / f"{name}.vfmesh" + image = OUT / f"{name}.png" + log = OUT / f"{name}-capture.log" + x, y, z = STRATA[stratum] + command = [ + "taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--", "timeout", + "1740s", str(CAPTURE), str(scene), str(image), "--size=1600x900", + "--fov-deg=90", f"--camera-m={x:.12g},{y:.12g},{z:.12g}", + "--yaw-deg=0", "--pitch-deg=0", "--shade=readable", "--height-tint=on", + ] + 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=1750) + return {"name": name, "stratum": stratum, "floor_vox": floor, + "exit_code": result.returncode, "wall_s": round(time.monotonic() - started, 3), + "image": str(image.relative_to(ROOT)), "log": str(log.relative_to(ROOT)), + "image_bytes": image.stat().st_size if image.exists() else 0} + + +def main() -> int: + OUT.mkdir(parents=True, exist_ok=True) + rows = [] + for stratum in STRATA: + for floor in (0.0, 6.0): + row = export(stratum, floor) + rows.append(row) + if row["exit_code"] != 0 or row["scene_bytes"] == 0: + break + row = render(stratum, floor) + rows.append(row) + if row["exit_code"] != 0 or row["image_bytes"] == 0: + break + if rows[-1]["exit_code"] != 0: + break + (OUT / "summary.json").write_text(json.dumps(rows, indent=2) + "\n", + encoding="utf-8") + print("[L91CaptureSummary] " + json.dumps(rows, sort_keys=True), flush=True) + return 0 if len(rows) == 16 and all(row["exit_code"] == 0 for row in rows) else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/Standalone/Source/StreamBenchmarkHeadless.cpp b/Standalone/Source/StreamBenchmarkHeadless.cpp index 16f8342..f46ff00 100644 --- a/Standalone/Source/StreamBenchmarkHeadless.cpp +++ b/Standalone/Source/StreamBenchmarkHeadless.cpp @@ -59,6 +59,7 @@ namespace int ShapeHelperLimit=8; bool AuditBasePageEquivalence=false; bool AuditRouteParity=false; + bool AuditPassageSourceClamp=false; bool RegionCrossing=false; bool AuditTileShapeReadiness=false; int DiagnosticSlowShapeCompileMs=0; @@ -1460,6 +1461,7 @@ namespace " --shape-helper-limit=8|12 (default 8; shape build A/B)\n" " --audit-base-page-equivalence (hash one tile through both shape routes)\n" " --audit-route-parity (compare page, native-only, exact-query and mesh routes)\n" + " --audit-passage-source-clamp (compare passage source floors 2,3,4,6 over all near tiles)\n" " --audit-tile-shape-readiness (run a deliberately mis-marked tile against the native-only guard)\n" " --diagnostic-slow-shape-compile-ms=N (diagnostic overlap control; default 0)\n" " --render-distance-chunks=N (0 disables the extra far ring)\n" @@ -1536,6 +1538,7 @@ namespace } else if(arg=="--audit-base-page-equivalence")options.AuditBasePageEquivalence=true; else if(arg=="--audit-route-parity")options.AuditRouteParity=true; + else if(arg=="--audit-passage-source-clamp")options.AuditPassageSourceClamp=true; else if(arg=="--region-crossing")options.RegionCrossing=true; else if(arg=="--audit-tile-shape-readiness")options.AuditTileShapeReadiness=true; else if(arg=="--auto-surface-camera")options.AutoSurfaceCamera=true; @@ -2276,5 +2279,6 @@ int main(int argc,char** argv) options.StreamHashAllowUniformSkips,options.Linux77Profile, options.StreamingSchedulerPolicy,options.MovingCameraTrace, options.MovingCameraSpeedMetersPerSecond,options.MovingCameraDurationSeconds, - options.MovingCameraDwellSeconds,options.ColdWorkerTimeline); + options.MovingCameraDwellSeconds,options.ColdWorkerTimeline, + options.AuditPassageSourceClamp); } diff --git a/Standalone/Source/StreamBenchmarkPipeline.h b/Standalone/Source/StreamBenchmarkPipeline.h index 65aa228..074170b 100644 --- a/Standalone/Source/StreamBenchmarkPipeline.h +++ b/Standalone/Source/StreamBenchmarkPipeline.h @@ -132,6 +132,426 @@ namespace StandaloneStreamBenchmark return Result; } + struct FPassageClampComparison + { + std::uint64_t samples=0,changedSamples=0,signFlips=0; + std::uint64_t crossingEdgeMismatches=0,sharedCrossingEdges=0,movedCrossingEdges=0; + std::uint64_t normalVertices=0,normalPairingFailures=0,changedNormalVertices=0; + double maxAbsDensityDelta=0.0,maxSurfaceDisplacementCm=0.0; + double sumSurfaceDisplacementCm=0.0,maxNormalAngleDegrees=0.0; + double sumNormalAngleDegrees=0.0; + std::array movedEdgeRegions{},normalVertexRegions{}; + bool meshChanged=false,valid=false; + }; + + struct FPassageRegion + { + int category=3; // 0 wall, 1 floor, 2 mouth, 3 outside the passage envelope + double signedWallDistanceVoxels=std::numeric_limits::infinity(); + }; + + inline FPassageRegion ClassifyPassageRegion( + const VoxelForgeCore::ResolvedLayoutSnapshotV3* Layout, + const std::array& PositionVoxels) + { + FPassageRegion best; + if(!Layout)return best; + constexpr double Q=static_cast(VoxelForgeCore::WorldAssemblySubvoxelsPerVoxel); + double bestAbsoluteWallDistance=std::numeric_limits::infinity(); + for(const auto& passage:Layout->passages) + { + if(passage.controlPoints.size()<2)continue; + for(std::size_t index=0;index+1 delta{},fromA{}; + double lengthSquared=0.0,dot=0.0; + for(std::size_t axis=0;axis<3;++axis) + { + delta[axis]=(static_cast(b.positionSubvoxels[axis]) + -a.positionSubvoxels[axis])/Q; + fromA[axis]=PositionVoxels[axis] + -static_cast(a.positionSubvoxels[axis])/Q; + lengthSquared+=delta[axis]*delta[axis]; + dot+=fromA[axis]*delta[axis]; + } + const double t=lengthSquared>0.0 + ?std::clamp(dot/lengthSquared,0.0,1.0):0.0; + double distanceSquared=0.0; + for(std::size_t axis=0;axis<3;++axis) + { + const double difference=fromA[axis]-t*delta[axis]; + distanceSquared+=difference*difference; + } + const double radius=(static_cast(a.radiusSubvoxels)+t*( + static_cast(b.radiusSubvoxels)-a.radiusSubvoxels))/Q; + const double signedWallDistance=std::sqrt(distanceSquared)-radius; + if(std::abs(signedWallDistance)>=bestAbsoluteWallDistance)continue; + bestAbsoluteWallDistance=std::abs(signedWallDistance); + best.signedWallDistanceVoxels=signedWallDistance; + const bool endpointSegment=index<8 + ||index+9>=passage.controlPoints.size(); + if(endpointSegment) + best.category=2; + else + { + const double vertical=fromA[2]-t*delta[2]; + best.category=radius>0.0&&vertical<-0.55*radius?1:0; + } + } + } + if(best.signedWallDistanceVoxels>48.0)best.category=3; + return best; + } + + inline FPassageClampComparison ComparePassageClampMeshes( + const VoxelForgeCore::TileMesh& Base,const VoxelForgeCore::TileMesh& Candidate, + const VoxelForgeCore::ResolvedLayoutSnapshotV3* Layout, + std::uint32_t CoreVoxelSizeMicrons) + { + FPassageClampComparison result; + if(Base.debugDensitySamples.empty() + ||Base.debugDensitySamples.size()!=Candidate.debugDensitySamples.size())return result; + const double root=std::cbrt(static_cast(Base.debugDensitySamples.size())); + const std::int32_t dim=static_cast(std::llround(root)); + if(dim<4||static_cast(dim)*dim*dim!=Base.debugDensitySamples.size())return result; + const std::int32_t gridDim=dim-3; + const std::int32_t extent=VoxelForgeCore::CoreTileVoxelCount<(CoreVoxelSizeMicrons)/10000.0; + const std::array origin={Base.key.coordinate.x*extent, + Base.key.coordinate.y*extent,Base.key.coordinate.z*extent}; + const auto index=[dim](std::int32_t x,std::int32_t y,std::int32_t z) + {return (static_cast(z)*dim+y)*dim+x;}; + const auto solid=[](float density){return density<0.0f;}; + for(std::int32_t z=0;z(a)-b)); + ++result.samples; + } + const auto inspectEdge=[&](std::int32_t x,std::int32_t y,std::int32_t z, + std::int32_t nx,std::int32_t ny,std::int32_t nz) + { + const float a0=Base.debugDensitySamples[index(x,y,z)]; + const float a1=Base.debugDensitySamples[index(nx,ny,nz)]; + const float b0=Candidate.debugDensitySamples[index(x,y,z)]; + const float b1=Candidate.debugDensitySamples[index(nx,ny,nz)]; + const bool crossBase=solid(a0)!=solid(a1); + const bool crossCandidate=solid(b0)!=solid(b1); + if(crossBase!=crossCandidate)++result.crossingEdgeMismatches; + if(!crossBase||!crossCandidate)return; + const double ta=static_cast(a0)/(static_cast(a0)-a1); + const double tb=static_cast(b0)/(static_cast(b0)-b1); + const double displacementCm=std::abs(ta-tb)*step*voxelSizeCm; + result.maxSurfaceDisplacementCm=std::max(result.maxSurfaceDisplacementCm, + displacementCm); + result.sumSurfaceDisplacementCm+=displacementCm; + ++result.sharedCrossingEdges; + if(displacementCm<=1.0e-9)return; + ++result.movedCrossingEdges; + std::array location={origin[0]+(x-1)*step, + origin[1]+(y-1)*step,origin[2]+(z-1)*step}; + location[0]+=(nx-x)*step*(ta+tb)*0.5; + location[1]+=(ny-y)*step*(ta+tb)*0.5; + location[2]+=(nz-z)*step*(ta+tb)*0.5; + const int region=ClassifyPassageRegion(Layout,location).category; + ++result.movedEdgeRegions[static_cast(region)]; + }; + for(std::int32_t z=0;z0.0&&lengthB>0.0))continue; + const double cosine=std::clamp((a.x*b.x+a.y*b.y+a.z*b.z) + /(lengthA*lengthB),-1.0,1.0); + const double angle=std::acos(cosine)*RadiansToDegrees; + result.maxNormalAngleDegrees=std::max(result.maxNormalAngleDegrees,angle); + result.sumNormalAngleDegrees+=angle; + ++result.normalVertices; + if(angle>1.0e-6) + { + ++result.changedNormalVertices; + const double geometryVoxelSizeCm=voxelSizeCm; + const auto& position=Candidate.vertices[vertex].position; + const std::array location={position.x/geometryVoxelSizeCm, + position.y/geometryVoxelSizeCm,position.z/geometryVoxelSizeCm}; + const int region=ClassifyPassageRegion(Layout,location).category; + ++result.normalVertexRegions[static_cast(region)]; + } + } + } + result.meshChanged=Base.meshHash!=Candidate.meshHash + ||Base.vertices.size()!=Candidate.vertices.size() + ||Base.indices!=Candidate.indices; + result.valid=true; + return result; + } + + struct FPassageSourceGradientStats + { + std::uint64_t candidateCrossings=0,sourceNearZeroSamples=0; + std::vector gradientMagnitudes; + }; + + inline void GatherPassageSourceGradients(const VoxelForgeCore::World& World, + const VoxelForgeCore::TileMesh& Mesh, + const VoxelForgeCore::ResolvedLayoutSnapshotV3* Layout, + FPassageSourceGradientStats& Stats) + { + if(Mesh.debugDensitySamples.empty())return; + const double root=std::cbrt(static_cast(Mesh.debugDensitySamples.size())); + const std::int32_t dim=static_cast(std::llround(root)); + if(dim<4||static_cast(dim)*dim*dim!=Mesh.debugDensitySamples.size())return; + const std::int32_t gridDim=dim-3; + const std::int32_t extent=VoxelForgeCore::CoreTileVoxelCount< origin={Mesh.key.coordinate.x*extent, + Mesh.key.coordinate.y*extent,Mesh.key.coordinate.z*extent}; + const auto index=[dim](std::int32_t x,std::int32_t y,std::int32_t z) + {return (static_cast(z)*dim+y)*dim+x;}; + std::uint64_t eligibleCrossings=0; + constexpr std::uint64_t SampleStride=32; + constexpr double H=1.0; + const auto inspect=[&](std::int32_t x,std::int32_t y,std::int32_t z, + std::int32_t nx,std::int32_t ny,std::int32_t nz) + { + const float d0=Mesh.debugDensitySamples[index(x,y,z)]; + const float d1=Mesh.debugDensitySamples[index(nx,ny,nz)]; + if((d0<0.0f)==(d1<0.0f))return; + const double t=static_cast(d0)/(static_cast(d0)-d1); + std::array location={origin[0]+(x-1)*step, + origin[1]+(y-1)*step,origin[2]+(z-1)*step}; + location[0]+=(nx-x)*step*t; + location[1]+=(ny-y)*step*t; + location[2]+=(nz-z)*step*t; + if(ClassifyPassageRegion(Layout,location).category!=3)return; + ++Stats.candidateCrossings; + if((eligibleCrossings++%SampleStride)!=0)return; + const VoxelForgeCore::Vec3d point{location[0],location[1],location[2]}; + const double center=World.passageSourceDensityAt(point); + if(!std::isfinite(center)||std::abs(center)>2.0)return; + ++Stats.sourceNearZeroSamples; + std::array gradient{}; + for(std::size_t axis=0;axis<3;++axis) + { + auto before=point,after=point; + if(axis==0){before.x-=H;after.x+=H;} + else if(axis==1){before.y-=H;after.y+=H;} + else {before.z-=H;after.z+=H;} + gradient[axis]=(World.passageSourceDensityAt(after) + -World.passageSourceDensityAt(before))/(2.0*H); + } + const double magnitude=std::sqrt(gradient[0]*gradient[0] + +gradient[1]*gradient[1]+gradient[2]*gradient[2]); + if(std::isfinite(magnitude))Stats.gradientMagnitudes.push_back(magnitude); + }; + for(std::int32_t z=0;z Floors={2.0,3.0,4.0,6.0}; + std::vector keys; + std::array expectedByLod{}; + for(const FVoxelTileKey& tile:Desired.Tiles) + { + if(tile.Level<0||tile.Level>2)continue; + keys.push_back({{tile.Coord.X,tile.Coord.Y,tile.Coord.Z},tile.Level}); + ++expectedByLod[static_cast(tile.Level)]; + } + if(keys.empty()) + { + std::printf("[L91PassageClampAudit] status=FAIL reason=no_lod0_2_tiles\n"); + return 14; + } + std::printf("[L91PassageClampCoverage] tiles=%zu lod0=%llu lod1=%llu lod2=%llu mode=full_desired_near_set\n", + keys.size(),static_cast(expectedByLod[0]), + static_cast(expectedByLod[1]), + static_cast(expectedByLod[2])); + VoxelForgeCore::TileGenerationOptions baseOptions; + baseOptions.hashDensity=true; + baseOptions.captureDensitySamples=true; + baseOptions.forceFullDensityCapture=true; + baseOptions.skipUvAndColors=true; + baseOptions.skipMaterialGroups=true; + VoxelForgeCore::TileGenerationOptions candidateOptions=baseOptions; + candidateOptions.profilePassageComposer=true; + std::array tileCount{},sampleCount{},changedSamples{},signFlips{}, + crossingMismatches{},sharedCrossings{},movedCrossings{},changedTiles{}, + normalVertices{},normalPairingFailures{},changedNormalVertices{}; + std::array movedRegions[4]{},normalRegions[4]{}; + std::array maxDisplacementCm{},sumDisplacementCm{},maxNormalAngle{}; + std::array sumNormalAngle{}; + FPassageSourceGradientStats gradientStats; + std::uint64_t generationFailures=0,invalidComparisons=0; + for(const VoxelForgeCore::TileKey& key:keys) + { + std::string error; + const auto base=Shape.generateTile(key,baseOptions,error); + if(!base) + { + ++generationFailures; + std::printf("[L91PassageClampTileFailure] mode=base key=(%d,%d,%d,lod%d) error=%s\n", + key.coordinate.x,key.coordinate.y,key.coordinate.z,key.lod,error.c_str()); + continue; + } + GatherPassageSourceGradients(Shape,*base,Layout,gradientStats); + for(std::size_t floorIndex=0;floorIndex(comparison.samples), + static_cast(comparison.changedSamples), + static_cast(comparison.signFlips), + static_cast(comparison.movedCrossingEdges), + comparison.maxSurfaceDisplacementCm, + static_cast(comparison.normalVertices), + comparison.maxNormalAngleDegrees,comparison.meshChanged?1:0); + } + } + bool acceptedAtAllFloors=generationFailures==0&&invalidComparisons==0 + &&keys.size()>0; + for(std::size_t floorIndex=0;floorIndex(tileCount[floorIndex]), + keys.size(),static_cast(sampleCount[floorIndex]), + static_cast(changedSamples[floorIndex]), + static_cast(signFlips[floorIndex]), + static_cast(crossingMismatches[floorIndex]), + static_cast(sharedCrossings[floorIndex]), + static_cast(movedCrossings[floorIndex]), + maxDisplacementCm[floorIndex],meanDisplacement, + static_cast(changedTiles[floorIndex]), + static_cast(normalVertices[floorIndex]), + static_cast(changedNormalVertices[floorIndex]), + maxNormalAngle[floorIndex],meanNormal, + static_cast(normalPairingFailures[floorIndex]), + static_cast(movedRegions[floorIndex][0]), + static_cast(movedRegions[floorIndex][1]), + static_cast(movedRegions[floorIndex][2]), + static_cast(movedRegions[floorIndex][3]), + static_cast(normalRegions[floorIndex][0]), + static_cast(normalRegions[floorIndex][1]), + static_cast(normalRegions[floorIndex][2]), + static_cast(normalRegions[floorIndex][3]), + accepted?"PASS":"FAIL"); + } + std::sort(gradientStats.gradientMagnitudes.begin(), + gradientStats.gradientMagnitudes.end()); + const auto percentile=[](const std::vector& values,double p) + { + if(values.empty())return 0.0; + const std::size_t index=std::min(values.size()-1, + static_cast(std::ceil(p*values.size())-1)); + return values[index]; + }; + const double gradientMean=gradientStats.gradientMagnitudes.empty()?0.0: + std::accumulate(gradientStats.gradientMagnitudes.begin(), + gradientStats.gradientMagnitudes.end(),0.0) + /gradientStats.gradientMagnitudes.size(); + std::printf("[L91PassageSourceGradient] surface_source_crossings=%llu sampled_near_zero=%llu gradient_samples=%zu step_vox=1 source_abs_limit_vox=2 p50=%.6g p90=%.6g max=%.6g mean=%.6g status=%s\n", + static_cast(gradientStats.candidateCrossings), + static_cast(gradientStats.sourceNearZeroSamples), + gradientStats.gradientMagnitudes.size(),percentile(gradientStats.gradientMagnitudes,0.5), + percentile(gradientStats.gradientMagnitudes,0.9), + gradientStats.gradientMagnitudes.empty()?0.0:gradientStats.gradientMagnitudes.back(), + gradientMean,gradientStats.gradientMagnitudes.size()>=64?"MEASURED":"LOW_SAMPLE"); + std::printf("[L91PassageClampAudit] tiles=%zu generation_failures=%llu invalid_comparisons=%llu status=%s\n", + keys.size(),static_cast(generationFailures), + static_cast(invalidComparisons), + acceptedAtAllFloors?"PASS":"FAIL"); + return acceptedAtAllFloors?0:14; + } + struct FLandmarkReachabilityResult { std::uint64_t pathSamples=0,capsuleSamples=0,blockedCapsuleSamples=0; @@ -1885,7 +2305,7 @@ namespace StandaloneStreamBenchmark bool bLinux77Profile=false,int32 SchedulerPolicy=-1, bool bMovingCameraTrace=false,double MovingCameraSpeedMetersPerSecond=0.0, double MovingCameraDurationSeconds=12.0,double MovingCameraDwellSeconds=5.0, - bool bColdWorkerTimeline=false) + bool bColdWorkerTimeline=false,bool bAuditPassageSourceClamp=false) { using Clock=std::chrono::steady_clock; const std::clock_t ColdCpuOrigin=std::clock(); @@ -2054,7 +2474,8 @@ namespace StandaloneStreamBenchmark const bool bFarWorldAudit=bAuditNativeCaves||bAuditNativeLattice||bAuditMotifIntervals ||bAuditOreVeinYield||bAuditLandmarkReachability||bAuditStrateTransitions ||bAuditResolvedLayoutPassages||bAuditResolvedLayoutBoundaries - ||bAuditTileShapeReadiness||bAuditBasePageEquivalence||bAuditRouteParity; + ||bAuditTileShapeReadiness||bAuditBasePageEquivalence||bAuditRouteParity + ||bAuditPassageSourceClamp; const bool bDeferFarWorld=bIncludeFarWorld&&!bMovingCameraTrace&&!bFarWorldAudit; VoxelForgeCore::ShapeBuildRequest ShapeRequest; ShapeRequest.minVoxel={FineMin.X,FineMin.Y,FineMin.Z}; @@ -2134,6 +2555,12 @@ namespace StandaloneStreamBenchmark Streamer.Stop(); std::fprintf(stderr,"[StreamPipeline] full shape build failed: %s\n",BuildError.c_str());return 2; } + if(bAuditPassageSourceClamp) + { + Streamer.Stop(); + return AuditPassageSourceClamp(Shape,Desired,ResolvedLayout, + World.CoreVoxelSizeMicrons); + } if(bAuditNativeCaves) { Streamer.Stop();