wip: cave planner squash-merged into wip/combined (L173, RED - not for main)
Five conflicts resolved (CURRENT-STATE, ResolvedLayoutInternal.h, StreamBenchmarkHeadless.cpp, StreamBenchmarkPipeline.h, WorldAssemblyPrototype.h) keeping liminal + cave planner; L156 band fit only; evaluator v239 with v239 baselines (18/18 Clang/GCC W1/W8/W12). Fast tier FAILS (FI upper arrival: 40 unsupported chamber samples, 10 chamber-floor shell violations); TN and VS composed sweeps 0/16 (passage, seam, shell gates). Living plane loads at PostConfigInit (UE smoke passes). Source/: VoxelForgeCoreAdapter.cpp; VoxelForge.uplugin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
No files matched your search
@@ -0,0 +1,33 @@
|
||||
# Design49 — flat landing pad at tunnel ends (owner idea, reviewed 2026-11-02)
|
||||
|
||||
Owner: "the end of the tunnel should be flat; flatten the arrival with a fade-out that blends to the surrounding
|
||||
density; a cheap stamp applied at arrival, no matter what's there."
|
||||
|
||||
A read-only design review (Claude Opus 5.5, medium effort) of `wip/l134-cave-planner` recommends a **clamp, not a
|
||||
lerp**. Each tunnel end gets a level disc at the tunnel's end floor P. Out to a blend radius, the native ground is
|
||||
squeezed between two gentle cones (12°) starting at P: a lower one that fills solid, an upper one that carves air.
|
||||
Inside the disc the floor is exactly flat; outside it, the slope is never steeper than max(cone, native), with no
|
||||
overhang or cliff. The full note is in the session transcript; key points:
|
||||
|
||||
- **Placement:** the last step of `ApplyResolvedStrateComposition`, after the passage composer, on both the native and
|
||||
non-native branches. The fill is masked by the tube-only distance (bore stays open). It replaces the landing-chamber
|
||||
box, shell and floor-shell special cases that caused L152–L156.
|
||||
- **Formula** (metres; ×cos α for 1-Lipschitz). d = max(0, r−R), t = tan α.
|
||||
- Fill: `Lo = P − t·d`, `F = max(z−Lo, (Lo−Ds)−z, r−(R+B))`.
|
||||
- Air: `Hi = P + t·d`, `A = min(z−Hi, (Hi+H)−z, (R+B)−r)`.
|
||||
- Result: `c = max(min(native, max(F, −tubeDist)), A)`.
|
||||
- Hard min/max inside R; smooth-min width ramps 0 → 0.5 m over the first metre outside it.
|
||||
- Why not lerp: native density saturates at about ±4.125 voxels (`DetailSupremum`), so a lerp makes cliffs and
|
||||
films.
|
||||
- **Parameters:** α = 12°, R = max(mouthRadius + 1 m, 3 m), B = min(8 m, |Δh_ring|/t + 1 m), H = 3 m, Ds = 1.25 m.
|
||||
- **Planner:**
|
||||
- P = h(end), with h′ = 0 over the last R.
|
||||
- Sample the edge ring; move P into the feasible band or take the next deterministic site.
|
||||
- Rewrite route heights inside R+B to the clamped ground.
|
||||
- Content-band check including P−tB−Ds and P+tB+H.
|
||||
- **Bounds:** the pad influence box goes into the proof bounds, the fallback overlap test, the **tile feature
|
||||
bounds** (critical: the composer early-returns on an empty route list) and the proof-miss sampler.
|
||||
- **Audits:** new `LandingPadAudit` (flatness ±2 cm, support, headroom, 36 radial blend transects ≤ 15° with one
|
||||
crossing) plus negative controls (pad off, 2% tilt, B halved, bore mask removed).
|
||||
- **Risks:** caves under pads (connectivity), low ceilings, shaft footprints, island edges and their air gap, liminal
|
||||
decks; off-route edges on non-heightfield layers are a metric only.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Linux134 — Tunnel Network cave planner
|
||||
|
||||
`TunnelNetwork` dispatches native `Core.Caves.PrimitiveField` v1. Seed 0 is strict-valid: 30 compound rooms, 72 room primitives, 31 edges, 3 branches, and a destination chamber. Its 27-point backbone spans 400.620 m horizontally with a 59.021 m floor change and an 8.381° maximum grade.
|
||||
|
||||
Design 4 gives backbone rooms and edges one continuous floor profile `h(s)`. Edge curves follow that profile; each room primitive is placed at its own profiled floor. Side branches keep independent flat floors. Overlap backing is capped at the lower floor. This removes the descending-joint capsule blockage. The incoming passage remains fully open: 21,425/21,425 samples.
|
||||
|
||||
The final-source fast tier passes in 370.8 s, including owner passage, shell, floor, ripple and negative-control audits. The default composed route audit passes: 0 blocked, 0 unsupported, 0 grade failures across 51,975 cave-route capsule samples; blocked-route controls detect their controls. The tile seam audit also passes (4 pairs, 4,900 shared samples, 0 mismatches).
|
||||
|
||||
Seed 2's cave route passes independently (0 blocked, 0 unsupported, 0 grade failures), but its full composed audit still fails at `passage/1-2/0`: 22 unsupported samples, first at `(15.153, 5.830, -754.680) m`. Seed 1's cave route has 24 blocked samples at the arrival mouth, beginning near `(8.380, -12.618, -864.420) m` on `passage/2-3/0`. Explicit seeds 0 and 3 produce no sweepable route candidates. The default seed is the only fully green composed audit among these runs.
|
||||
|
||||
Clang and GCC W1/W8/W12 match all 13 pipeline hash lines at the cave camera; each route audit passes. Seven legacy/planner cutaway pairs cover the arrival room, two backbone rooms, two tunnels, destination chamber, and departure room. All planner views render; several legacy cutaways show sparse geometry. Review them in `Saved/Linux/l134/captures/COMPARE.html`.
|
||||
|
||||
L80 timing used five interleaved samples per binary at W8/W12. Values below are the planner median change versus `assembler/world`; each cell is whole-view ready / LOD0–2 ready.
|
||||
|
||||
| Strate | W8 | W12 |
|
||||
|---|---:|---:|
|
||||
| 0 | -0.70% / +1.52% | +0.97% / +3.50% |
|
||||
| 1 | +3.81% / +8.38% | +2.41% / +6.19% |
|
||||
| 2 | +3.38% / +12.90% | +3.98% / +10.26% |
|
||||
| 3 | +7.97% / +13.54% | +1.93% / +7.67% |
|
||||
|
||||
The VFHost Linux Development build succeeds with `-NoUBA -MaxParallelActions=4`. Final Clang/GCC source stamp: `f3b69ea0ce186c855643702a88e0d0732c81c18ec93c17d1824c25210e6408a3`. Canonical owner hashes are pinned at v230.
|
||||
|
||||
Logs and timing data are under `Saved/Linux/l134`; the timing summary is `timing/v230-summary.json`, the final fast log is `verify-fast-final-source.log`, and the host build log is `ubt.log`.
|
||||
|
||||
The separate legacy `--audit-native-caves` signature check still fails its mouth offset (it expects 1.00 m; the route contract is 1.13 m) and ripple span (1.8878 m against its 1.25 m cap). Its center-air, floor-solid, and shell probes pass; the composed owner route audit above is green.
|
||||
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 219 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 187 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 297 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 196 KiB |
@@ -0,0 +1 @@
|
||||
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
|
||||
@@ -0,0 +1 @@
|
||||
{"schema_version":3,"world_identity":"owner-world/seed/0","owner_voxel_size_um":125000,"core_voxel_size_um":250000,"strate_layout_chunk_size_um":8000000,"strate_layout_chunk_width_subvoxels":8192,"strates":[{"stable_id":"owner-world/seed/0/strate/0/generator/SurfaceWorld","order":0,"strate_index":0,"generator_kind":"SurfaceWorld","recipe_id":"SurfaceWorld.Heightfield2D","recipe_version":2,"interior_bottom_um":-504000000,"interior_top_um":8000000,"voxel_size_um":250000,"lattice_origin_um":[0,0,0],"ceiling":{"kind":"ceiling","bottom_um":7500000,"top_um":8000000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid ceiling from strate BoundarySealThickness; breakable by default","density_side":"interior-facing lower surface","cap_path":"shared resolved-layout density band"},"content":{"kind":"content","bottom_um":-503500000,"top_um":7500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":65536,"behaviour":"resolved generator interior","density_side":"none","cap_path":"none"},"bedrock":{"kind":"bedrock","bottom_um":-504000000,"top_um":-503500000,"present":true,"diggable":true,"protected":false,"solid_density_q16":524288,"behaviour":"solid bedrock floor from strate BoundarySealThickness; breakable by default","density_side":"interior-facing upper surface","cap_path":"shared resolved-layout density band"},"transition_mode":"Gradient","transition_blend_chunks":1,"transition_width_um":8000000,"transition_warp_amplitude_um":0,"transition_interpenetration_um":2000000,"transition_honoured_in_core":true,"transition_falls_back_to_hard":false,"transition_dispatch_path":"shared composition moves both band faces together with the smooth declared-width transition field","bound_dispatch_path":"BuildSurfaceStratumPlan uses resolved layout chunk width from ResolvedLayoutSnapshotV3","query_dispatch_path":"NativeSurfaceDensityAt selects floor(CoreZ / resolved layout chunk width) from ResolvedLayoutSnapshotV3","manager_lookup":"UVoxelStrateManager::GetGeneratorTypeForChunk; transition is controlled by upper strate","manager_chunk_width_um":8000000,"dispatch_chunk_width_um":8000000,"anchors":[{"kind":"cap_profile","stable_id":"biome/4097","units":"1/256 Core voxel","values":[-18022,0,64000,1536,0]},{"kind":"route_point","stable_id":"route/0","units":"1/256 Core voxel","values":[8192,24576,-381223,1024]},{"kind":"route_point","stable_id":"route/1","units":"1/256 Core voxel","values":[8192,24320,-381254,1024]},{"kind":"route_point","stable_id":"route/2","units":"1/256 Core voxel","values":[8192,24064,-381284,1024]},{"kind":"route_point","stable_id":"route/3","units":"1/256 Core voxel","values":[8192,23808,-381302,1024]},{"kind":"route_point","stable_id":"route/4","units":"1/256 Core voxel","values":[8192,23552,-381304,1024]},{"kind":"route_point","stable_id":"route/5","units":"1/256 Core voxel","values":[8192,23296,-381291,1024]},{"kind":"route_point","stable_id":"route/6","units":"1/256 Core voxel","values":[8192,23040,-381270,1024]},{"kind":"route_point","stable_id":"route/7","units":"1/256 Core voxel","values":[8192,22784,-381242,1024]},{"kind":"route_point","stable_id":"route/8","units":"1/256 Core voxel","values":[8192,22528,-381218,1024]},{"kind":"route_point","stable_id":"route/9","units":"1/256 Core voxel","values":[8192,22272,-381192,1024]},{"kind":"route_point","stable_id":"route/10","units":"1/256 Core voxel","values":[8192,22016,-381161,1024]},{"kind":"route_point","stable_id":"route/11","units":"1/256 Core voxel","values":[8192,21760,-381118,1024]},{"kind":"route_point","stable_id":"route/12","units":"1/256 Core voxel","values":[8192,21504,-381122,1024]},{"kind":"route_point","stable_id":"route/13","units":"1/256 Core voxel","values":[8192,21248,-381155,1024]},{"kind":"route_point","stable_id":"route/14","units":"1/256 Core voxel","values":[8192,20992,-381183,1024]},{"kind":"route_point","stable_id":"route/15","units":"1/256 Core voxel","values":[8192,20736,-381207,1024]},{"kind":"route_point","stable_id":"route/16","units":"1/256 Core voxel","values":[8192,20480,-381219,1024]},{"kind":"route_point","stable_id":"route/17","units":"1/256 Core voxel","values":[8192,20224,-381220,1024]},{"kind":"route_point","stable_id":"route/18","units":"1/256 Core voxel","values":[8192,19968,-381202,1024]},{"kind":"route_point","stable_id":"route/19","units":"1/256 Core voxel","values":[8192,19712,-381167,1024]},{"kind":"route_point","stable_id":"route/20","units":"1/256 Core voxel","values":[8192,19456,-381130,1024]},{"kind":"route_point","stable_id":"route/21","units":"1/256 Core voxel","values":[8192,19200,-381101,1024]},{"kind":"route_point","stable_id":"route/22","units":"1/256 Core voxel","values":[8192,18944,-381072,1024]},{"kind":"route_point","stable_id":"route/23","units":"1/256 Core voxel","values":[8192,18688,-381092,1024]},{"kind":"route_point","stable_id":"route/24","units":"1/256 Core voxel","values":[8192,18432,-381149,1024]},{"kind":"route_point","stable_id"Line truncated
|
||||
@@ -0,0 +1,32 @@
|
||||
# Linux135 — Tunnel Network planner
|
||||
|
||||
Source v236 (`8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5`) on `wip/l134-cave-planner`.
|
||||
|
||||
## Route audits
|
||||
|
||||
Seeds 0–15 pass the composed passage, seam, shell, naturalness, mouth-profile, ripple, and blocked-route controls (16/16). The negative control still detects all three blocked routes. No audit threshold changed.
|
||||
|
||||
The mouth joint now takes its floor/profile from the resolved mouth geometry and carries that shared profile into the planner's first and last edges. This fixes seed 1's blocked arrival and seed 2's unsupported arrival. For seeds 0 and 3, the first 648 regular route candidates exceeded the grade bound; the bounded deterministic fallback finds a route. “No sweepable route candidates” was a planner search miss, not an empty cave.
|
||||
|
||||
## Cost
|
||||
|
||||
L80 medians are near-ready / whole-view percent deltas. The serialized 144-run matrix started at load 0.81 / 98.4% CPU idle. Each cell gives `assembler/world` first, then exact L134 (`a3c8e36`).
|
||||
|
||||
| Layer | W8 | W12 |
|
||||
|---|---:|---:|
|
||||
| 0 | +3.88 / +0.27%; +1.79 / -0.21% | +8.22 / +4.09%; +3.89 / +2.49% |
|
||||
| 1 | +8.82 / +4.96%; +4.14 / +1.71% | +11.83 / +5.35%; +4.10 / +2.09% |
|
||||
| 2 | +13.33 / +3.77%; +2.34 / -0.09% | +12.56 / +5.20%; +1.40 / +0.61% |
|
||||
| 3 | +13.65 / +3.99%; -0.25 / +0.44% | +12.71 / +5.14%; +2.97 / +2.12% |
|
||||
|
||||
The two speed changes were bounded cave AABBs with a 1 mm float guard, and normalized edge-BVH bounds with one normalization per sample. This improves on L134, but misses the `assembler/world` target: near-ready remains 3.88–13.65% slower. Layers 1–3 overlap the native cave volume and spend time on roughly 4.95–5.76 million near-LOD cave samples per view; layer 0 has none at LOD0–3 and is still up 3.88% at W8, showing some generic composer/classification cost as well.
|
||||
|
||||
Planning is about 3.75–4.19 ms per view (cavern planning 0.46–0.49 ms). At LOD0–3, room/edge candidates average 0.39/0.23 per composer tile on layer 1, 0.47/0.27 on layer 2, and 0.06/0.04 on layer 3. Tile proofs skip work at LOD0–3; measured cave views have no tile skips at LOD4–8. The reported 180–320 ns density-call estimate includes all shape fields, not cave-only time. Profile: `Saved/Linux/l135/profile-summary-speed2.json`.
|
||||
|
||||
## Gates and pictures
|
||||
|
||||
Fast tier passes in 301.7 s. Clang and GCC each match 13 hash lines at W1/W8/W12. Route seams and the negative controls pass; canonical baselines are v236. The post-timing VFHost Linux Development build succeeds: 27/27 actions, 77.70 s, using `-NoUBA -MaxParallelActions=4`.
|
||||
|
||||
The seven-view gallery has four planner views at the planned floor plus 1.6 m and three legacy views at legacy coordinates. Open `captures/COMPARE.html`; camera coordinates and renderer settings are in `captures/capture-index.json`.
|
||||
|
||||
Sweep: `Saved/Linux/l135/optimized-seed-sweep.json`. Timing: `Saved/Linux/l135/timing/summary.json`. Determinism: `Saved/Linux/l135/determinism-summary.json`. Host build log: `Saved/Linux/l135/ubt-l135-console.log`.
|
||||
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture eye-height planner interiors and legacy interiors at legacy coordinates."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Standalone/Research/WorldAssemblerM3/Linux135/captures"
|
||||
SAVED = ROOT / "Saved/Linux/l135"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
LAYOUT = SAVED / "capture-default-layout.json"
|
||||
HEADLESS = SAVED / "bin/VoxelForgeCoreHeadless-clang-l135"
|
||||
RENDERER = SAVED / "bin/VoxelForgeOffscreenCapture-clang-l135"
|
||||
STAMP = "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5"
|
||||
EYE_METRES = 1.6
|
||||
|
||||
|
||||
def run(command: list[str], log_path: Path) -> None:
|
||||
wrapped = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", *command]
|
||||
with log_path.open("w", encoding="utf-8") as log:
|
||||
result = subprocess.run(wrapped, cwd=ROOT, stdout=log,
|
||||
stderr=subprocess.STDOUT, timeout=1740,
|
||||
check=False)
|
||||
if result.returncode:
|
||||
raise SystemExit(f"command failed ({result.returncode}): {log_path}")
|
||||
|
||||
|
||||
def xyz(value: list[int] | list[float]) -> tuple[float, float, float]:
|
||||
return tuple(component / 1_000_000.0 for component in value) # type: ignore[return-value]
|
||||
|
||||
|
||||
def yaw_to(dx: float, dy: float) -> float:
|
||||
# The renderer's yaw zero points along +Y (Forward = sin(yaw), cos(yaw), 0).
|
||||
return math.degrees(math.atan2(dx, dy))
|
||||
|
||||
|
||||
def camera_arg(camera: tuple[float, float, float], yaw: float) -> str:
|
||||
return ",".join(f"{part:.9f}" for part in (*camera, yaw, 0.0))
|
||||
|
||||
|
||||
def make_view(name: str, recipe: str, camera: tuple[float, float, float],
|
||||
yaw: float) -> dict[str, object]:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
mesh = SAVED / f"capture-{name}.vfmesh"
|
||||
image = OUT / f"{name}.png"
|
||||
mesh_log = SAVED / f"capture-{name}-mesh.log"
|
||||
render_log = SAVED / f"capture-{name}-render.log"
|
||||
command = [str(HEADLESS), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=8",
|
||||
"--clip-radius=1", "--max-clip-level=0", "--max-level=0",
|
||||
f"--camera={camera_arg(camera, yaw)}", f"--mesh-export={mesh}",
|
||||
"--capture-skip-walkability", "--force-generate",
|
||||
"--render-distance-chunks=0"]
|
||||
# The checked-in fixture is already seed 0. Keeping its Unreal-resolved owner
|
||||
# plan avoids rebuilding the broad SurfaceWorld spawn search for each camera.
|
||||
if recipe == "legacy-tunnel-network":
|
||||
command.append("--legacy-tunnel-network")
|
||||
else:
|
||||
command.append("--recipe=surface-world")
|
||||
run(command, mesh_log)
|
||||
render_command = [str(RENDERER), str(mesh), str(image), "--size=1600x900",
|
||||
"--shade=readable", "--height-tint=on", "--headlamp=off"]
|
||||
run(render_command, render_log)
|
||||
mesh_output = mesh_log.read_text(encoding="utf-8", errors="replace")
|
||||
render_output = render_log.read_text(encoding="utf-8", errors="replace")
|
||||
if STAMP not in mesh_output or STAMP not in render_output:
|
||||
raise SystemExit(f"source stamp mismatch for {name}")
|
||||
if "status=PASS" not in render_output:
|
||||
raise SystemExit(f"render failed for {name}")
|
||||
return {"name": name, "recipe": recipe, "camera_m": list(camera),
|
||||
"yaw_deg": yaw,
|
||||
"camera_height_basis": "floor + player eye offset" if recipe == "surface-world"
|
||||
else ("legacy room floor + player eye offset" if name.endswith("room")
|
||||
else "near the legacy tunnel centerline"),
|
||||
"eye_offset_m": EYE_METRES
|
||||
if recipe == "surface-world" or name.endswith("room") else None,
|
||||
"mesh": str(mesh.relative_to(ROOT)), "image": str(image.relative_to(ROOT)),
|
||||
"mesh_log": str(mesh_log.relative_to(ROOT)),
|
||||
"render_log": str(render_log.relative_to(ROOT))}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
|
||||
rooms = {room["stable_id"]: room for room in layout["cavern_rooms"]}
|
||||
edges = {edge["stable_id"]: edge for edge in layout["cavern_edges"]}
|
||||
views: list[dict[str, object]] = []
|
||||
|
||||
arrival = rooms["tunnel-network/strate-2/room-0/primitive-0"]
|
||||
room1 = rooms["tunnel-network/strate-2/room-1/primitive-0"]
|
||||
arrival_center = xyz(arrival["center_um"])
|
||||
arrival_floor = arrival["floor_um"] / 1_000_000.0
|
||||
views.append(make_view("planner-arrival-room", "surface-world",
|
||||
(arrival_center[0], arrival_center[1], arrival_floor + EYE_METRES),
|
||||
yaw_to(room1["center_um"][0] - arrival["center_um"][0],
|
||||
room1["center_um"][1] - arrival["center_um"][1])))
|
||||
|
||||
tunnel = edges["tunnel-network/strate-2/route-edge-1"]["polyline_um"]
|
||||
tunnel_start, tunnel_end = xyz(tunnel[0]), xyz(tunnel[-1])
|
||||
tunnel_floor = tunnel[1][2] / 1_000_000.0
|
||||
views.append(make_view("planner-tunnel", "surface-world",
|
||||
((tunnel_start[0] + tunnel_end[0]) * 0.5,
|
||||
(tunnel_start[1] + tunnel_end[1]) * 0.5,
|
||||
tunnel_floor + EYE_METRES),
|
||||
yaw_to(tunnel_end[0] - tunnel_start[0], tunnel_end[1] - tunnel_start[1])))
|
||||
|
||||
compound = rooms["tunnel-network/strate-2/room-19/primitive-0"]
|
||||
compound_center = xyz(compound["center_um"])
|
||||
compound_next = rooms["tunnel-network/strate-2/room-20/primitive-0"]
|
||||
views.append(make_view("planner-compound-room", "surface-world",
|
||||
(compound_center[0], compound_center[1],
|
||||
compound["floor_um"] / 1_000_000.0 + EYE_METRES),
|
||||
yaw_to(compound_next["center_um"][0] - compound["center_um"][0],
|
||||
compound_next["center_um"][1] - compound["center_um"][1])))
|
||||
|
||||
destination = rooms["tunnel-network/strate-2/room-33/primitive-0"]
|
||||
destination_center = xyz(destination["center_um"])
|
||||
upstream = rooms["tunnel-network/strate-2/room-32/primitive-0"]
|
||||
views.append(make_view("planner-destination-chamber", "surface-world",
|
||||
(destination_center[0], destination_center[1],
|
||||
destination["floor_um"] / 1_000_000.0 + EYE_METRES),
|
||||
yaw_to(upstream["center_um"][0] - destination["center_um"][0],
|
||||
upstream["center_um"][1] - destination["center_um"][1])))
|
||||
|
||||
# The compiled legacy network has a deterministic origin chamber at (0, 0).
|
||||
# Its room floor is hash-rolled from the owner fixture; these are legacy world
|
||||
# coordinates, independent of the native planner's passage route.
|
||||
legacy_origin_floor = -819.9169921875
|
||||
legacy_origin_eye = legacy_origin_floor + EYE_METRES
|
||||
legacy_neighbor = (39.327854, 5.115777, -797.724731)
|
||||
views.append(make_view("legacy-origin-room", "legacy-tunnel-network",
|
||||
(0.0, 0.0, legacy_origin_eye), yaw_to(legacy_neighbor[0], legacy_neighbor[1])))
|
||||
|
||||
# Hash-selected (cell 1,0) is the origin graph's nearest connected room. Its
|
||||
# cut-plane floor estimate is -801.0 m before the authored local relief.
|
||||
legacy_neighbor_floor = -800.9986572265625
|
||||
legacy_neighbor_eye = legacy_neighbor_floor + EYE_METRES
|
||||
views.append(make_view("legacy-connected-room", "legacy-tunnel-network",
|
||||
(legacy_neighbor[0], legacy_neighbor[1], legacy_neighbor_eye),
|
||||
yaw_to(-legacy_neighbor[0], -legacy_neighbor[1])))
|
||||
|
||||
legacy_tunnel_mid = (legacy_neighbor[0] * 0.5, legacy_neighbor[1] * 0.5,
|
||||
(-812.0 + legacy_neighbor[2]) * 0.5)
|
||||
views.append(make_view("legacy-origin-tunnel", "legacy-tunnel-network",
|
||||
legacy_tunnel_mid, yaw_to(legacy_neighbor[0], legacy_neighbor[1])))
|
||||
|
||||
manifest = {"source_stamp": STAMP, "seed": 0,
|
||||
"renderer": "readable, height tint on, headlamp off; 1600x900",
|
||||
"views": views}
|
||||
(OUT / "capture-index.json").write_text(
|
||||
json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
cards = "\n".join(
|
||||
f'<figure><a href="{Path(str(view["image"])).name}">'
|
||||
f'<img src="{Path(str(view["image"])).name}" alt="{view["name"]}"></a>'
|
||||
f'<figcaption>{view["name"]}</figcaption></figure>' for view in views)
|
||||
(OUT / "COMPARE.html").write_text(
|
||||
"<!doctype html><meta charset=\"utf-8\"><title>L135 interior views</title>\n"
|
||||
"<style>body{font:16px sans-serif;background:#202329;color:#eee;margin:24px}"
|
||||
"h1{font-size:22px}section{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:18px}"
|
||||
"figure{margin:0}img{width:100%;height:auto;display:block}figcaption{padding:6px 0}</style>\n"
|
||||
"<h1>Planner and legacy interiors, seed 0</h1><section>\n"
|
||||
+ cards + "\n</section>\n", encoding="utf-8")
|
||||
print(f"[L135CaptureSummary] views={len(views)} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check Clang/GCC cave-path pipeline hashes at 1, 8, and 12 workers."""
|
||||
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/l135"
|
||||
DATA = ROOT / "Standalone/data/regression/owner_world_2strate_l61.json"
|
||||
BINARIES = {
|
||||
"clang": OUT / "bin/VoxelForgeCoreHeadless-clang-l135",
|
||||
"gcc": OUT / "bin/VoxelForgeCoreHeadless-gcc-l135",
|
||||
}
|
||||
CAMERA = "-4.688214,38.662384,-505.781342,-28.446,0"
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing L135 determinism binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
baseline: tuple[str, ...] | None = None
|
||||
stamp: str | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
label = f"determinism-{compiler}-w{workers}"
|
||||
log = OUT / f"{label}.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=0",
|
||||
"--max-level=0", f"--camera={CAMERA}", "--stream-hash-tiles",
|
||||
"--force-generate", "--recipe=surface-world", "--render-distance-chunks=0",
|
||||
"--landmark-chamber-demo", "--ore-veins-demo"]
|
||||
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")
|
||||
found_stamp = STAMP_RE.search(content)
|
||||
hashes = tuple(HASH_RE.findall(content))
|
||||
passed = result.returncode == 0 and found_stamp is not None and len(hashes) >= 4
|
||||
if found_stamp is not None:
|
||||
if stamp is None:
|
||||
stamp = found_stamp.group(1)
|
||||
passed &= stamp == found_stamp.group(1)
|
||||
if baseline is None and passed:
|
||||
baseline = hashes
|
||||
elif baseline is not None:
|
||||
passed &= hashes == baseline
|
||||
row = {"compiler": compiler, "workers": workers,
|
||||
"exit": result.returncode, "stamp": found_stamp.group(1)
|
||||
if found_stamp else None,
|
||||
"hash_lines": len(hashes), "hashes_match_first": hashes == baseline,
|
||||
"passed": bool(passed), "elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L135DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
if not passed:
|
||||
(OUT / "determinism-summary.json").write_text(json.dumps(
|
||||
{"status": "FAIL", "runs": rows}, indent=2) + "\n")
|
||||
raise SystemExit(f"determinism mismatch; see {log}")
|
||||
summary = {"status": "PASS", "stamp": stamp, "runs": rows}
|
||||
(OUT / "determinism-summary.json").write_text(
|
||||
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L135DeterminismSummary] runs={len(rows)} stamp={stamp} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Collect final-stamp cave, candidate, proof, and startup counters by stratum."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l135"
|
||||
BIN = OUT / "bin/VoxelForgeCoreHeadless-clang-l135"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5"
|
||||
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),
|
||||
}
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
PLAN_RE = re.compile(r"^\[WorldAssemblerPlanTiming\] total_ms=([0-9.]+) status=([^ ]+)$", re.M)
|
||||
CAVE_RE = re.compile(r"^\[CavernPlannerV1\].* plan_ms=([0-9.]+) status=([^ ]+)$", re.M)
|
||||
TILE_RE = re.compile(r"^\[WorldAssemblerTilePipeline\] (.*)$", re.M)
|
||||
CLASS_RE = re.compile(r"^\[WorldAssemblerLODClassification\] (.*)$", re.M)
|
||||
COMPOSER_RE = re.compile(r"^\[PassageComposerTile\] (.*)$", re.M)
|
||||
FIELDS_RE = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
|
||||
|
||||
def fields(line: str) -> dict[str, str]:
|
||||
return dict(FIELDS_RE.findall(line))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L135 profile binary or owner fixture")
|
||||
rows = []
|
||||
for strate, xyz in CAMERAS.items():
|
||||
camera = ",".join(f"{value:.12f}" for value in xyz) + ",0,0"
|
||||
log = OUT / f"profile-final-speed2-s{strate}.log"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), 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",
|
||||
"--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")
|
||||
found_stamp = STAMP_RE.search(content)
|
||||
if result.returncode or found_stamp is None or found_stamp.group(1) != STAMP:
|
||||
raise SystemExit(f"final-stamp profile failed: {log}, exit={result.returncode}")
|
||||
plan, cave = PLAN_RE.search(content), CAVE_RE.search(content)
|
||||
tile = TILE_RE.search(content)
|
||||
if plan is None or cave is None or tile is None:
|
||||
raise SystemExit(f"profile counters missing: {log}")
|
||||
aggregate = defaultdict(lambda: defaultdict(int))
|
||||
for match in CLASS_RE.finditer(content):
|
||||
v = fields(match.group(1)); lod = int(v["lod"])
|
||||
for key in ("tiles", "tile_skips", "density_point_calls", "field_eval_sample_points",
|
||||
"proof_air_cells", "proof_solid_cells"):
|
||||
aggregate[lod][key] = int(v.get(key, "0"))
|
||||
aggregate[lod]["field_eval_ms_est"] = float(v.get("field_eval_ms_est", "0"))
|
||||
for match in COMPOSER_RE.finditer(content):
|
||||
v = fields(match.group(1)); lod = int(v["key"].split("lod", 1)[1].rstrip(")"))
|
||||
aggregate[lod]["composer_tiles"] += 1
|
||||
for key in ("samples", "native_cave_samples", "room_candidates", "edge_candidates",
|
||||
"features", "route_candidates", "passage_iterations"):
|
||||
aggregate[lod][key] += int(v.get(key, "0"))
|
||||
aggregate[lod]["composer_worker_ms"] += float(v.get("worker_ms", "0"))
|
||||
for lod, values in aggregate.items():
|
||||
calls = values.get("density_point_calls", 0)
|
||||
values["field_eval_ns_per_density_call_est"] = round(
|
||||
values.get("field_eval_ms_est", 0.0) * 1.0e6 / calls, 1) if calls else None
|
||||
composer_tiles = values.get("composer_tiles", 0)
|
||||
values["room_candidates_per_composer_tile"] = round(
|
||||
values.get("room_candidates", 0) / composer_tiles, 3) if composer_tiles else None
|
||||
values["edge_candidates_per_composer_tile"] = round(
|
||||
values.get("edge_candidates", 0) / composer_tiles, 3) if composer_tiles else None
|
||||
tile_fields = fields(tile.group(1))
|
||||
tile_summary = {}
|
||||
for key, value in tile_fields.items():
|
||||
try:
|
||||
tile_summary[key] = int(value)
|
||||
except ValueError:
|
||||
try:
|
||||
tile_summary[key] = float(value)
|
||||
except ValueError:
|
||||
continue
|
||||
row = {"strate": strate, "source_stamp": found_stamp.group(1),
|
||||
"world_assembler_plan_ms": float(plan.group(1)),
|
||||
"cavern_planner_ms": float(cave.group(1)),
|
||||
"tile_pipeline": tile_summary,
|
||||
"lod": {str(k): dict(v) for k, v in sorted(aggregate.items())},
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L135ProfileRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
summary = {"status": "PASS", "source_stamp": STAMP, "strata": rows,
|
||||
"field_eval_ns_per_density_call_note":
|
||||
"Estimate includes all shape-field density calls at this view, not native-cave-exclusive time.",
|
||||
"tile_skip_note":
|
||||
"tile_pipeline.whole_tile_skips and lod.tile_skips are aggregate classifiers; they are not cave-only attribution."}
|
||||
(OUT / "profile-summary-speed2.json").write_text(
|
||||
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print("[L135ProfileSummary] strata=4 status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the L135 composed cave-passage and tile-seam audits for fixed seeds."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l135"
|
||||
BIN = OUT / "bin/VoxelForgeCoreHeadless-clang-l135"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
REQUIRED = {
|
||||
"passage": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"route_negative_control": re.compile(
|
||||
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
seeds = [int(value) for value in sys.argv[1:]] or list(range(16))
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L135 headless binary or owner fixture")
|
||||
results = []
|
||||
for seed in seeds:
|
||||
log = OUT / f"optimized-seed{seed}.log"
|
||||
layout = OUT / f"optimized-seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=0",
|
||||
"--force-generate", f"--seed={seed}", "--recipe=surface-world",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
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")
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
blocked_controls = re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)
|
||||
checks["three_blocked_controls"] = len(blocked_controls) == 3
|
||||
passed = result.returncode == 0 and all(checks.values())
|
||||
row = {"seed": seed, "exit": result.returncode, "passed": passed,
|
||||
"checks": checks, "blocked_controls": len(blocked_controls),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L135SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
if not passed:
|
||||
raise SystemExit(f"seed {seed} failed a composed audit; see {log}")
|
||||
summary = {"seeds": seeds, "passed": len(results), "failed": 0,
|
||||
"results": results}
|
||||
(OUT / "optimized-seed-sweep.json").write_text(
|
||||
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L135SeedSweepSummary] passed={len(results)}/{len(seeds)} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Five interleaved L80 timings against assembler/world and the L134 revision."""
|
||||
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/l135/timing"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
|
||||
"a3c8e36-v230": ROOT / "Saved/Linux/l134/bin/VoxelForgeCoreHeadless-l134-clang",
|
||||
"l135-v236": ROOT / "Saved/Linux/l135/bin/VoxelForgeCoreHeadless-clang-l135",
|
||||
}
|
||||
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)
|
||||
PLAN_RE = re.compile(r"^\[WorldAssemblerPlanTiming\] total_ms=([0-9.]+) status=([^ ]+)$", re.M)
|
||||
CAVE_RE = re.compile(r"^\[CavernPlannerV1\].* plan_ms=([0-9.]+) status=([^ ]+)$", 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}-l135"
|
||||
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)))
|
||||
plan, cave = PLAN_RE.search(content), CAVE_RE.search(content)
|
||||
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")),
|
||||
"plan_ms": float(plan.group(1)) if plan else None,
|
||||
"cave_plan_ms": float(cave.group(1)) if cave else None,
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
print("[L135TimingRun] " + 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]
|
||||
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 L135 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"[L135TimingGate] 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", encoding="utf-8")
|
||||
|
||||
measured = [row for row in rows if row["phase"] == "measured"]
|
||||
summary = []
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
item = {"strate": strate, "workers": workers,
|
||||
"replicates_per_version": 5}
|
||||
medians = {}
|
||||
for version in BINARIES:
|
||||
group = [row for row in measured if row["strate"] == strate
|
||||
and row["workers"] == workers and row["version"] == version]
|
||||
medians[version] = {}
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
values = [row[key] for row in group]
|
||||
medians[version][key] = statistics.median(values)
|
||||
item[f"{version}_{key}_values"] = values
|
||||
item[f"{version}_{key}_median"] = medians[version][key]
|
||||
for baseline in ("assembler-world", "a3c8e36-v230"):
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
item[f"vs_{baseline}_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["l135-v236"][key]
|
||||
/ medians[baseline][key] - 1.0), 2)
|
||||
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())
|
||||
@@ -0,0 +1,11 @@
|
||||
<!doctype html><meta charset="utf-8"><title>L135 interior views</title>
|
||||
<style>body{font:16px sans-serif;background:#202329;color:#eee;margin:24px}h1{font-size:22px}section{display:grid;grid-template-columns:repeat(auto-fit,minmax(420px,1fr));gap:18px}figure{margin:0}img{width:100%;height:auto;display:block}figcaption{padding:6px 0}</style>
|
||||
<h1>Planner and legacy interiors, seed 0</h1><section>
|
||||
<figure><a href="planner-arrival-room.png"><img src="planner-arrival-room.png" alt="planner-arrival-room"></a><figcaption>planner-arrival-room</figcaption></figure>
|
||||
<figure><a href="planner-tunnel.png"><img src="planner-tunnel.png" alt="planner-tunnel"></a><figcaption>planner-tunnel</figcaption></figure>
|
||||
<figure><a href="planner-compound-room.png"><img src="planner-compound-room.png" alt="planner-compound-room"></a><figcaption>planner-compound-room</figcaption></figure>
|
||||
<figure><a href="planner-destination-chamber.png"><img src="planner-destination-chamber.png" alt="planner-destination-chamber"></a><figcaption>planner-destination-chamber</figcaption></figure>
|
||||
<figure><a href="legacy-origin-room.png"><img src="legacy-origin-room.png" alt="legacy-origin-room"></a><figcaption>legacy-origin-room</figcaption></figure>
|
||||
<figure><a href="legacy-connected-room.png"><img src="legacy-connected-room.png" alt="legacy-connected-room"></a><figcaption>legacy-connected-room</figcaption></figure>
|
||||
<figure><a href="legacy-origin-tunnel.png"><img src="legacy-origin-tunnel.png" alt="legacy-origin-tunnel"></a><figcaption>legacy-origin-tunnel</figcaption></figure>
|
||||
</section>
|
||||
@@ -0,0 +1,119 @@
|
||||
{
|
||||
"source_stamp": "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5",
|
||||
"seed": 0,
|
||||
"renderer": "readable, height tint on, headlamp off; 1600x900",
|
||||
"views": [
|
||||
{
|
||||
"name": "planner-arrival-room",
|
||||
"recipe": "surface-world",
|
||||
"camera_m": [
|
||||
35.593114,
|
||||
-21.55801,
|
||||
-755.864011
|
||||
],
|
||||
"yaw_deg": 114.47628370565772,
|
||||
"camera_height_basis": "floor + player eye offset",
|
||||
"eye_offset_m": 1.6,
|
||||
"mesh": "Saved/Linux/l135/capture-planner-arrival-room.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-arrival-room.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-planner-arrival-room-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-planner-arrival-room-render.log"
|
||||
},
|
||||
{
|
||||
"name": "planner-tunnel",
|
||||
"recipe": "surface-world",
|
||||
"camera_m": [
|
||||
42.874177,
|
||||
-24.8725425,
|
||||
-757.112565
|
||||
],
|
||||
"yaw_deg": 114.47628370565774,
|
||||
"camera_height_basis": "floor + player eye offset",
|
||||
"eye_offset_m": 1.6,
|
||||
"mesh": "Saved/Linux/l135/capture-planner-tunnel.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-tunnel.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-planner-tunnel-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-planner-tunnel-render.log"
|
||||
},
|
||||
{
|
||||
"name": "planner-compound-room",
|
||||
"recipe": "surface-world",
|
||||
"camera_m": [
|
||||
-39.519627,
|
||||
-52.685596,
|
||||
-785.85419
|
||||
],
|
||||
"yaw_deg": -74.782578137959,
|
||||
"camera_height_basis": "floor + player eye offset",
|
||||
"eye_offset_m": 1.6,
|
||||
"mesh": "Saved/Linux/l135/capture-planner-compound-room.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-compound-room.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-planner-compound-room-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-planner-compound-room-render.log"
|
||||
},
|
||||
{
|
||||
"name": "planner-destination-chamber",
|
||||
"recipe": "surface-world",
|
||||
"camera_m": [
|
||||
-28.634259,
|
||||
-4.85816,
|
||||
-810.5142579999999
|
||||
],
|
||||
"yaw_deg": -89.33032962272898,
|
||||
"camera_height_basis": "floor + player eye offset",
|
||||
"eye_offset_m": 1.6,
|
||||
"mesh": "Saved/Linux/l135/capture-planner-destination-chamber.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/planner-destination-chamber.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-planner-destination-chamber-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-planner-destination-chamber-render.log"
|
||||
},
|
||||
{
|
||||
"name": "legacy-origin-room",
|
||||
"recipe": "legacy-tunnel-network",
|
||||
"camera_m": [
|
||||
0.0,
|
||||
0.0,
|
||||
-818.3169921875
|
||||
],
|
||||
"yaw_deg": 82.58856646117444,
|
||||
"camera_height_basis": "legacy room floor + player eye offset",
|
||||
"eye_offset_m": 1.6,
|
||||
"mesh": "Saved/Linux/l135/capture-legacy-origin-room.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/legacy-origin-room.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-legacy-origin-room-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-legacy-origin-room-render.log"
|
||||
},
|
||||
{
|
||||
"name": "legacy-connected-room",
|
||||
"recipe": "legacy-tunnel-network",
|
||||
"camera_m": [
|
||||
39.327854,
|
||||
5.115777,
|
||||
-799.3986572265625
|
||||
],
|
||||
"yaw_deg": -97.41143353882556,
|
||||
"camera_height_basis": "legacy room floor + player eye offset",
|
||||
"eye_offset_m": 1.6,
|
||||
"mesh": "Saved/Linux/l135/capture-legacy-connected-room.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/legacy-connected-room.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-legacy-connected-room-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-legacy-connected-room-render.log"
|
||||
},
|
||||
{
|
||||
"name": "legacy-origin-tunnel",
|
||||
"recipe": "legacy-tunnel-network",
|
||||
"camera_m": [
|
||||
19.663927,
|
||||
2.5578885,
|
||||
-804.8623655
|
||||
],
|
||||
"yaw_deg": 82.58856646117444,
|
||||
"camera_height_basis": "near the legacy tunnel centerline",
|
||||
"eye_offset_m": null,
|
||||
"mesh": "Saved/Linux/l135/capture-legacy-origin-tunnel.vfmesh",
|
||||
"image": "Standalone/Research/WorldAssemblerM3/Linux135/captures/legacy-origin-tunnel.png",
|
||||
"mesh_log": "Saved/Linux/l135/capture-legacy-origin-tunnel-mesh.log",
|
||||
"render_log": "Saved/Linux/l135/capture-legacy-origin-tunnel-render.log"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 115 KiB |
|
After Width: | Height: | Size: 135 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 142 KiB |
@@ -0,0 +1,33 @@
|
||||
# Linux136 — cave planner speed
|
||||
|
||||
Source stamp `ebe6b32a7cd96c5aa69c6dc64ae6b9c6ff4bc18bc484efc5148611c48723c28f` (v239; v236 is commit `2d89023`).
|
||||
|
||||
## Findings
|
||||
|
||||
The tile query now keeps sorted passage IDs with route candidates, so the composer skips unrelated routes. A route-free tile returns before per-sample coordinate and scale math. Native cave evaluation also returns the exact host-rock value when its conservative tile feature list is empty. These paths preserve v236 output; no version bump was needed.
|
||||
|
||||
The LOD0–2 census shows the shared composer does substantial work outside Tunnel Network. Surface World has zero native cave samples at LOD0–3, yet near-ready passage visits are 19.61M on both `assembler/world` and v236. The route-ID path reduces that count to 3.20M, but cumulative near-ready composer worker time stays around 3.5 s (3.42 s baseline, 3.51 s v236, 3.52 s v239). Whole-view classification worker time is 584 ms / 641 ms / 623 ms. Planning is about 4 ms. This rules out cave evaluation and planning as the Surface World regression, but the census does not isolate mouth-profile checks from the other shared composer/classifier work.
|
||||
|
||||
Floating Islands and Vertical Shafts have unrelated-route churn: v236 adds 7.79M and 8.43M passage visits over baseline. v239 brings those totals to 7.89M and 2.54M, and reduces composer worker time by about 9% and 8% versus v236. Tunnel Network falls from 34.63M visits in v236 to 7.47M; its composer worker time returns to baseline. The views still process about 4.7–5.4M native cave samples. The attempted LOD4–8 cave tile proofs produced no skips: every cave-view tile at those LODs remained unclassified.
|
||||
|
||||
## L80 timing
|
||||
|
||||
Five interleaved measured runs per version and cell, one warmup per version. The pre-run gate was load 0.74 and 99.3% CPU idle. Cells show near-ready and whole-view median deltas versus `assembler/world`, in W8/W12 order; the last column shows near-ready deltas versus v236.
|
||||
|
||||
| Stratum | Near-ready Δ | Whole-view Δ | Near-ready Δ vs v236 |
|
||||
|---|---:|---:|---:|
|
||||
| Surface World (0) | +4.29% / +5.78% | +2.45% / −0.17% | +0.52% / −1.99% |
|
||||
| Floating Islands (1) | +8.88% / +5.48% | +1.31% / −0.48% | −2.40% / −4.29% |
|
||||
| Tunnel Network (2) | +8.23% / +8.26% | +0.35% / +0.88% | −3.51% / −3.97% |
|
||||
| Vertical Shafts (3) | +13.29% / +6.36% | +1.26% / −1.97% | −4.22% / −3.81% |
|
||||
|
||||
v239 improves on v236 in seven of eight near-ready cells, but misses the requested ≤+3% target in all eight cells versus `assembler/world`. Whole-view deltas stay within +2.45%/−1.97%.
|
||||
|
||||
## Gates
|
||||
|
||||
- Fast tier: PASS, 289.9 s.
|
||||
- Composed passage, seam, shell, mouth, ripple, naturalness, and blocked-route audits: 16/16 seeds PASS.
|
||||
- Clang and GCC at W1/W8/W12: all six runs match 13 hash lines.
|
||||
- Linux VFHostEditor Development: PASS, 5/5 actions in 41.33 s with `-NoUBA -MaxParallelActions=4`; no `prlimit`.
|
||||
|
||||
Timing, census, seed, determinism, and build logs are under `Saved/Linux/l136/`. The completed timing matrix is `Saved/Linux/l136/timing/summary.json`; the interrupted partial matrix is excluded. No commit or push was made.
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare assembler/world and v236 composer/classifier phase counters."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l136/census"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
|
||||
"v236": ROOT / "Saved/Linux/l135/bin/VoxelForgeCoreHeadless-clang-l135",
|
||||
"v239": ROOT / "Saved/Linux/l136/bin/VoxelForgeCoreHeadless-l136-clang",
|
||||
}
|
||||
STAMPS = {
|
||||
"assembler-world": "8b3e7bfb4cde2b408ab940925eab973d8375d6b719774d6f72f64ee72b526c35",
|
||||
"v236": "8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5",
|
||||
"v239": "ebe6b32a7cd96c5aa69c6dc64ae6b9c6ff4bc18bc484efc5148611c48723c28f",
|
||||
}
|
||||
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),
|
||||
}
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
|
||||
|
||||
def field(line: str) -> dict[str, str]:
|
||||
return dict(FIELDS.findall(line))
|
||||
|
||||
|
||||
def run(strate: int, version: str) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
log = OUT / f"{version}-s{strate}.log"
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[version]), 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",
|
||||
"--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")
|
||||
stamp_match = re.search(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", content, re.M)
|
||||
if result.returncode or stamp_match is None or stamp_match.group(1) != STAMPS[version]:
|
||||
raise SystemExit(f"census failed/stamp mismatch: {log}, exit={result.returncode}, stamp={stamp_match}")
|
||||
lod: dict[int, dict] = defaultdict(lambda: defaultdict(float))
|
||||
for line in content.splitlines():
|
||||
if line.startswith("[WorldAssemblerLODClassification] "):
|
||||
values = field(line)
|
||||
target = lod[int(values["lod"])]
|
||||
for name in ("tiles", "all_air", "all_solid", "mixed", "unclassified",
|
||||
"tile_skips", "density_point_calls", "field_eval_sample_points",
|
||||
"proof_air_cells", "proof_solid_cells"):
|
||||
target[name] = int(values.get(name, "0"))
|
||||
target["field_eval_ms_est"] = float(values.get("field_eval_ms_est", "0"))
|
||||
elif line.startswith("[PassageComposerTile] "):
|
||||
values = field(line)
|
||||
level = int(re.search(r"lod(\d+)\)", values["key"]).group(1))
|
||||
target = lod[level]
|
||||
target["composer_tiles"] += 1
|
||||
for name in ("samples", "field_calls", "native_cave_samples", "native_field_calls",
|
||||
"room_candidates", "edge_candidates", "features", "route_candidates",
|
||||
"passage_iterations", "carve_segment_visits"):
|
||||
target[name] += int(values.get(name, "0"))
|
||||
target["composer_worker_ms"] += float(values.get("worker_ms", "0"))
|
||||
plan = re.search(r"^\[WorldAssemblerPlanTiming\] total_ms=([0-9.]+)", content, re.M)
|
||||
cave = re.search(r"^\[CavernPlannerV1\].* plan_ms=([0-9.]+)", content, re.M)
|
||||
pipeline = re.search(r"^\[WorldAssemblerTilePipeline\] (.*)$", content, re.M)
|
||||
pipeline_fields = field(pipeline.group(1)) if pipeline else {}
|
||||
result_row = {
|
||||
"strate": strate, "version": version, "source_stamp": stamp_match.group(1),
|
||||
"plan_ms": float(plan.group(1)) if plan else None,
|
||||
"cave_plan_ms": float(cave.group(1)) if cave else None,
|
||||
"tile_pipeline": {key: (float(value) if "." in value else int(value))
|
||||
for key, value in pipeline_fields.items()
|
||||
if re.fullmatch(r"-?\d+(?:\.\d+)?", value)},
|
||||
"lod": {str(key): dict(values) for key, values in sorted(lod.items())},
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L136CensusRun] " + json.dumps(result_row, sort_keys=True), flush=True)
|
||||
return result_row
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing census fixture or binary")
|
||||
rows = []
|
||||
for strate in range(4):
|
||||
for version in BINARIES:
|
||||
rows.append(run(strate, version))
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
|
||||
summary = {"status": "PASS", "rows": rows,
|
||||
"notes": [
|
||||
"classifier milliseconds and composer worker milliseconds are cumulative counters, not exclusive wall time",
|
||||
"assembler/world predates native cave field counters; field sample fields are only comparable where emitted",
|
||||
]}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check exact-stamp Clang/GCC cave-path hashes at 1, 8, and 12 workers."""
|
||||
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/l136/determinism"
|
||||
DATA = ROOT / "Standalone/data/regression/owner_world_2strate_l61.json"
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Saved/Linux/l136/fast/bin/VoxelForgeCoreHeadless-fast-clang",
|
||||
"gcc": ROOT / "Saved/Linux/l136/fast/bin/VoxelForgeCoreHeadless-fast-gcc",
|
||||
}
|
||||
STAMP = "ebe6b32a7cd96c5aa69c6dc64ae6b9c6ff4bc18bc484efc5148611c48723c28f"
|
||||
CAMERA = "-4.688214,38.662384,-505.781342,-28.446,0"
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing L136 determinism binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
baseline: tuple[str, ...] | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
label = f"determinism-{compiler}-w{workers}"
|
||||
log = OUT / f"{label}.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=0",
|
||||
"--max-level=0", f"--camera={CAMERA}", "--stream-hash-tiles",
|
||||
"--force-generate", "--recipe=surface-world", "--render-distance-chunks=0",
|
||||
"--landmark-chamber-demo", "--ore-veins-demo"]
|
||||
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")
|
||||
found_stamp = STAMP_RE.search(content)
|
||||
hashes = tuple(HASH_RE.findall(content))
|
||||
passed = (result.returncode == 0 and found_stamp is not None
|
||||
and found_stamp.group(1) == STAMP and len(hashes) >= 4)
|
||||
if baseline is None and passed:
|
||||
baseline = hashes
|
||||
elif baseline is not None:
|
||||
passed &= hashes == baseline
|
||||
row = {"compiler": compiler, "workers": workers,
|
||||
"exit": result.returncode, "stamp": found_stamp.group(1)
|
||||
if found_stamp else None,
|
||||
"hash_lines": len(hashes), "hashes_match_first": hashes == baseline,
|
||||
"passed": bool(passed), "elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L136DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
if not passed:
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "FAIL", "stamp": STAMP, "runs": rows}, indent=2) + "\n")
|
||||
raise SystemExit(f"determinism mismatch; see {log}")
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "PASS", "stamp": STAMP, "runs": rows}, indent=2) + "\n")
|
||||
print(f"[L136DeterminismSummary] runs={len(rows)} stamp={STAMP} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the v239 composed cave-passage and tile-seam audits for fixed seeds."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l136/seeds"
|
||||
BIN = ROOT / "Saved/Linux/l136/bin/VoxelForgeCoreHeadless-l136-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "ebe6b32a7cd96c5aa69c6dc64ae6b9c6ff4bc18bc484efc5148611c48723c28f"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
REQUIRED = {
|
||||
"passage": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"route_negative_control": re.compile(
|
||||
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
seeds = [int(value) for value in sys.argv[1:]] or list(range(16))
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L136 headless binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
for seed in seeds:
|
||||
log = OUT / f"seed{seed}.log"
|
||||
layout = OUT / f"seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=0",
|
||||
"--force-generate", f"--seed={seed}", "--recipe=surface-world",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
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")
|
||||
found_stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = found_stamp is not None and found_stamp.group(1) == STAMP
|
||||
blocked_controls = re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)
|
||||
checks["three_blocked_controls"] = len(blocked_controls) == 3
|
||||
passed = result.returncode == 0 and all(checks.values())
|
||||
row = {"seed": seed, "exit": result.returncode, "passed": passed,
|
||||
"checks": checks, "blocked_controls": len(blocked_controls),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L136SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(results, indent=2) + "\n")
|
||||
if not passed:
|
||||
raise SystemExit(f"seed {seed} failed a composed audit; see {log}")
|
||||
summary = {"source_stamp": STAMP, "seeds": seeds, "passed": len(results),
|
||||
"failed": 0, "results": results}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
print(f"[L136SeedSweepSummary] passed={len(results)}/{len(seeds)} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Five interleaved L80 timings for assembler/world, v236, and v239."""
|
||||
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/l136/timing"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": (ROOT / "Saved/Linux/l130/bin/VoxelForgeCoreHeadless-l130-clang",
|
||||
"8b3e7bfb4cde2b408ab940925eab973d8375d6b719774d6f72f64ee72b526c35"),
|
||||
"v236": (ROOT / "Saved/Linux/l135/bin/VoxelForgeCoreHeadless-clang-l135",
|
||||
"8dc4294ce0250a591ee4515471c3c547e796ffa4346ab44f0ae1740bbc3797f5"),
|
||||
"v239": (ROOT / "Saved/Linux/l136/bin/VoxelForgeCoreHeadless-l136-clang",
|
||||
"ebe6b32a7cd96c5aa69c6dc64ae6b9c6ff4bc18bc484efc5148611c48723c28f"),
|
||||
}
|
||||
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)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
PLAN_RE = re.compile(r"^\[WorldAssemblerPlanTiming\] total_ms=([0-9.]+) status=([^ ]+)$", re.M)
|
||||
CAVE_RE = re.compile(r"^\[CavernPlannerV1\].* plan_ms=([0-9.]+) status=([^ ]+)$", re.M)
|
||||
|
||||
|
||||
def run(strate: int, workers: int, version: str, phase: str,
|
||||
replicate: int) -> dict:
|
||||
binary, expected_stamp = BINARIES[version]
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
|
||||
log = OUT / f"{label}.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}", "--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, stamp = (PIPELINE_RE.search(content), NEAR_RE.search(content),
|
||||
STAMP_RE.search(content))
|
||||
if (result.returncode or pipeline is None or near is None or stamp is None
|
||||
or stamp.group(1) != expected_stamp):
|
||||
raise SystemExit(f"L80 timing failed/stamp mismatch: {log}; exit={result.returncode}")
|
||||
fields = dict(FIELDS_RE.findall(pipeline.group(1)))
|
||||
plan, cave = PLAN_RE.search(content), CAVE_RE.search(content)
|
||||
row = {"strate": strate, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
|
||||
"lod02_ready_s": float(near.group(3)),
|
||||
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
|
||||
"plan_ms": float(plan.group(1)) if plan else None,
|
||||
"cave_plan_ms": float(cave.group(1)) if cave else None,
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
print("[L136TimingRun] " + 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]
|
||||
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 BINARIES.values()) or not DATA.is_file():
|
||||
raise SystemExit("missing L136 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"[L136TimingGate] 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:
|
||||
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):
|
||||
item = {"strate": strate, "workers": workers, "replicates_per_version": 5}
|
||||
medians = {}
|
||||
for version in BINARIES:
|
||||
group = [row for row in measured if row["strate"] == strate
|
||||
and row["workers"] == workers and row["version"] == version]
|
||||
medians[version] = {}
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
values = [row[key] for row in group]
|
||||
medians[version][key] = statistics.median(values)
|
||||
item[f"{version}_{key}_values"] = values
|
||||
item[f"{version}_{key}_median"] = medians[version][key]
|
||||
for baseline in ("assembler-world", "v236"):
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
item[f"v239_vs_{baseline}_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["v239"][key] / medians[baseline][key] - 1.0), 2)
|
||||
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())
|
||||
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Symbolize L137 L77 samples and compare inclusive worker functions."""
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l137/profile"
|
||||
BINARIES = {
|
||||
"assembler-world": ROOT / "Saved/Linux/l137/profile-bin/VoxelForgeCoreHeadless-l137-profile-base",
|
||||
"head": ROOT / "Saved/Linux/l137/profile-bin/VoxelForgeCoreHeadless-l137-profile-head",
|
||||
}
|
||||
SAMPLE_RE = re.compile(
|
||||
r"^\[L77Sample\] pc=(0x\w+) lod=(\d+) stage=(\S+) route=(\S+) "
|
||||
r"phase=(\S+) halo=(\d+) weight=(\d+)$", re.M)
|
||||
|
||||
|
||||
def samples(version: str) -> tuple[list[dict], set[str]]:
|
||||
rows, pcs = [], set()
|
||||
for replicate in (1, 2, 3):
|
||||
path = OUT / f"sw-{version}-r{replicate}.log"
|
||||
for pc, lod, stage, route, phase, halo, weight in SAMPLE_RE.findall(
|
||||
path.read_text(encoding="utf-8", errors="replace")):
|
||||
lod_value = int(lod)
|
||||
if not 0 <= lod_value <= 8:
|
||||
continue
|
||||
pcs.add(pc)
|
||||
rows.append({"version": version, "replicate": replicate,
|
||||
"pc": pc, "lod": lod_value, "stage": stage,
|
||||
"route": route, "phase": phase, "halo": int(halo),
|
||||
"weight": int(weight)})
|
||||
return rows, pcs
|
||||
|
||||
|
||||
def symbolize(version: str, pcs: set[str]) -> dict[str, str]:
|
||||
command = ["llvm-symbolizer-19", "--demangle", "--inlines", "--output-style=JSON",
|
||||
f"--obj={BINARIES[version]}"]
|
||||
result = subprocess.run(command, input="\n".join(sorted(pcs)) + "\n",
|
||||
capture_output=True, text=True, check=True)
|
||||
decoder = json.JSONDecoder()
|
||||
offset, output = 0, {}
|
||||
while offset < len(result.stdout):
|
||||
while offset < len(result.stdout) and result.stdout[offset].isspace():
|
||||
offset += 1
|
||||
if offset == len(result.stdout):
|
||||
break
|
||||
item, offset = decoder.raw_decode(result.stdout, offset)
|
||||
frames = item.get("Symbol", [])
|
||||
output[item["Address"]] = (
|
||||
frames[0].get("FunctionName", "?") if frames else "?")
|
||||
if output.keys() != pcs:
|
||||
raise RuntimeError(f"{version}: symbolizer resolved {len(output)}/{len(pcs)} PCs")
|
||||
return output
|
||||
|
||||
|
||||
def main() -> int:
|
||||
totals: dict[str, Counter] = {}
|
||||
lod_totals: dict[str, Counter] = {}
|
||||
detail_rows = []
|
||||
for version in BINARIES:
|
||||
rows, pcs = samples(version)
|
||||
symbols = symbolize(version, pcs)
|
||||
totals[version] = Counter()
|
||||
lod_totals[version] = Counter()
|
||||
for row in rows:
|
||||
function = symbols[row["pc"]]
|
||||
weight = row["weight"]
|
||||
totals[version][function] += weight
|
||||
lod_totals[version][row["lod"]] += weight
|
||||
detail_rows.append({**row, "function": function})
|
||||
total_by_version = {key: sum(counter.values()) for key, counter in totals.items()}
|
||||
functions = set(totals["assembler-world"]) | set(totals["head"])
|
||||
result = []
|
||||
for function in functions:
|
||||
base_ticks = totals["assembler-world"][function]
|
||||
head_ticks = totals["head"][function]
|
||||
base_pct = 100.0 * base_ticks / total_by_version["assembler-world"]
|
||||
head_pct = 100.0 * head_ticks / total_by_version["head"]
|
||||
result.append({
|
||||
"function": function,
|
||||
"assembler_world_ticks": base_ticks,
|
||||
"assembler_world_share_pct": round(base_pct, 3),
|
||||
"head_ticks": head_ticks,
|
||||
"head_share_pct": round(head_pct, 3),
|
||||
"share_delta_pp": round(head_pct - base_pct, 3),
|
||||
})
|
||||
result.sort(key=lambda row: abs(row["share_delta_pp"]), reverse=True)
|
||||
with (OUT / "function-diff.csv").open("w", encoding="utf-8", newline="") as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=list(result[0]))
|
||||
writer.writeheader()
|
||||
writer.writerows(result)
|
||||
with (OUT / "symbolized-samples.csv").open(
|
||||
"w", encoding="utf-8", newline="") as stream:
|
||||
writer = csv.DictWriter(stream, fieldnames=list(detail_rows[0]))
|
||||
writer.writeheader()
|
||||
writer.writerows(detail_rows)
|
||||
summary = {
|
||||
"scope": "worker samples LOD0-8, all LODs included; lod=255 main-thread samples excluded",
|
||||
"runs_per_version": 3,
|
||||
"weighted_ticks": total_by_version,
|
||||
"lod_weighted_ticks": {version: dict(sorted(values.items()))
|
||||
for version, values in lod_totals.items()},
|
||||
"function_diff": result,
|
||||
}
|
||||
(OUT / "function-diff.json").write_text(
|
||||
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print("[L137ProfileDiff] " + json.dumps({
|
||||
"scope": summary["scope"], "weighted_ticks": total_by_version,
|
||||
"lod_weighted_ticks": summary["lod_weighted_ticks"],
|
||||
"largest_share_deltas": result[:20]}, sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,26 @@
|
||||
# Linux137 report
|
||||
|
||||
## Measurement
|
||||
|
||||
Two `assembler/world` binaries had identical SHA-256. On Surface World, their L80 near-ready medians differed by +0.52% at W8 and −0.59% at W12. The no-op v228 control differed by −0.37% and −0.05%; its stream hashes matched v227. Use ±1% as the median noise floor. Individual paired controls varied by up to about 2.5% (3.6% for one no-op pair).
|
||||
|
||||
The three-run L77 profile sampled LOD 0–8 with no lost samples. Function share changes, `HEAD` versus `assembler/world`: `CachedColumn` +0.93 pp, shared column `TryRead` +0.58 pp, `prepareSweptTubeBase` +0.38 pp, and `sweptSample` +0.17 pp. `ApplyResolvedStratePassageCarves` fell by 0.97 pp and `FindBoundStratumForManagerZ` by 0.39 pp. The profile points more toward column-cache work and the sweep lookup than mouth-profile work; it does not isolate one cause for every world.
|
||||
|
||||
Changes retained: removed the unused `floorShellSignedDistance` sample field; removed per-lookup shared-pointer refcounting and batched diagnostic counters per query scope.
|
||||
|
||||
## L80 near-ready medians
|
||||
|
||||
Each delta is candidate versus `assembler/world`; the A/A median noise floor is ±1%.
|
||||
|
||||
| World | W8 delta | W12 delta |
|
||||
|---|---:|---:|
|
||||
| Surface World | +3.58% (±1%) | +2.90% (±1%) |
|
||||
| Floating Islands | +0.19% (±1%) | +2.20% (±1%) |
|
||||
| Tunnel Network | −3.60% (±1%) | +6.84% (±1%) |
|
||||
| Vertical Shafts | +4.31% (±1%) | +3.96% (±1%) |
|
||||
|
||||
The requested targets are not met except Floating Islands W8 and Tunnel Network W8. Whole-view medians were within −8.39% to +0.27% of baseline.
|
||||
|
||||
## Verification
|
||||
|
||||
Fast tier passed (273 checks); the composed sweep passed 16/16 seeds with seams and blocked controls; Clang/GCC W1/W8/W12 matched all 13 hashes. Linux UE host build passed with `-NoUBA -MaxParallelActions=4` and no `prlimit`. Output audits stayed green, so version and baselines were left unchanged. No commit, push, or stash.
|
||||
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
"""L80 Surface World A/A and no-op evaluator-version control timings."""
|
||||
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/l137/timing/aa"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"aa_a": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
|
||||
"aa_b": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-b",
|
||||
"v228": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-v228",
|
||||
}
|
||||
CAMERA = "8.000000000000,16.000000000000,-372.262695312500,0,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+) cold_ready_s=([0-9.]+)$",
|
||||
re.M)
|
||||
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def run(workers: int, version: str, phase: str, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
label = f"sw-w{workers}-{version}-{phase}{replicate}"
|
||||
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, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content)
|
||||
if result.returncode or pipeline is None or near is None or stamp is None:
|
||||
raise SystemExit(f"L80 run failed or trace missing: {log}; exit={result.returncode}")
|
||||
fields = dict(FIELDS.findall(pipeline.group(1)))
|
||||
row = {
|
||||
"strate": 0, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
|
||||
"lod02_ready_s": float(near.group(3)),
|
||||
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
|
||||
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L137AARun] " + 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]
|
||||
|
||||
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:
|
||||
missing = [str(path) for path in [DATA, *BINARIES.values()] if not path.is_file()]
|
||||
if missing:
|
||||
raise SystemExit("missing L137 A/A input: " + ", ".join(missing))
|
||||
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"[L137AAGate] 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 = []
|
||||
versions = tuple(BINARIES)
|
||||
for workers in (8, 12):
|
||||
for version in versions:
|
||||
run(workers, version, "warmup", 0)
|
||||
orders = (versions, ("v228", "aa_b", "aa_a"),
|
||||
("aa_b", "aa_a", "v228"), ("aa_a", "v228", "aa_b"),
|
||||
("v228", "aa_a", "aa_b"))
|
||||
for replicate, order in enumerate(orders, 1):
|
||||
for version in order:
|
||||
rows.append(run(workers, version, "measured", replicate))
|
||||
(OUT / "runs-partial.json").write_text(
|
||||
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
summary = []
|
||||
for workers in (8, 12):
|
||||
group = [row for row in rows if row["workers"] == workers]
|
||||
medians = {
|
||||
version: {
|
||||
key: statistics.median(row[key] for row in group
|
||||
if row["version"] == version)
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s")
|
||||
}
|
||||
for version in versions
|
||||
}
|
||||
item = {"strate": 0, "workers": workers, "replicates_per_version": 5,
|
||||
"medians": medians}
|
||||
for metric in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
for lhs, rhs in (("aa_b", "aa_a"), ("v228", "aa_a")):
|
||||
item[f"{lhs}_vs_{rhs}_{metric}_delta_pct"] = round(
|
||||
100.0 * (medians[lhs][metric] / medians[rhs][metric] - 1.0), 3)
|
||||
paired = []
|
||||
for replicate in range(1, 6):
|
||||
values = {row["version"]: row[metric] for row in group
|
||||
if row["phase"] == "measured" and row["replicate"] == replicate}
|
||||
paired.append({"replicate": replicate,
|
||||
"aa_b_vs_aa_a_pct": round(
|
||||
100.0 * (values["aa_b"] / values["aa_a"] - 1.0), 3),
|
||||
"v228_vs_aa_a_pct": round(
|
||||
100.0 * (values["v228"] / values["aa_a"] - 1.0), 3)})
|
||||
item[f"{metric}_paired_deltas_pct"] = paired
|
||||
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())
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare L137 Clang/GCC composed hashes at W1, W8, and W12."""
|
||||
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/l137/determinism"
|
||||
DATA = ROOT / "Standalone/data/regression/owner_world_2strate_l61.json"
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Saved/Linux/l137/fast/bin/VoxelForgeCoreHeadless-fast-clang",
|
||||
"gcc": ROOT / "Saved/Linux/l137/fast/bin/VoxelForgeCoreHeadless-fast-gcc",
|
||||
}
|
||||
STAMP = "dcf2ffa55fd6d62b3c7faf76229c3b99c635868dc1b694fc6e8f39e738775a4e"
|
||||
CAMERA = "-4.688214,38.662384,-505.781342,-28.446,0"
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing L137 determinism binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
baseline: tuple[str, ...] | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
label = f"determinism-{compiler}-w{workers}"
|
||||
log = OUT / f"{label}.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=0",
|
||||
"--max-level=0", f"--camera={CAMERA}", "--stream-hash-tiles",
|
||||
"--force-generate", "--recipe=surface-world", "--render-distance-chunks=0",
|
||||
"--landmark-chamber-demo", "--ore-veins-demo"]
|
||||
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")
|
||||
stamp = STAMP_RE.search(content)
|
||||
hashes = tuple(HASH_RE.findall(content))
|
||||
passed = (result.returncode == 0 and stamp is not None
|
||||
and stamp.group(1) == STAMP and len(hashes) >= 4)
|
||||
if baseline is None and passed:
|
||||
baseline = hashes
|
||||
elif baseline is not None:
|
||||
passed &= hashes == baseline
|
||||
row = {"compiler": compiler, "workers": workers, "exit": result.returncode,
|
||||
"stamp": stamp.group(1) if stamp else None, "hash_lines": len(hashes),
|
||||
"hashes_match_first": hashes == baseline, "passed": bool(passed),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L137DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
if not passed:
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "FAIL", "stamp": STAMP, "runs": rows}, indent=2) + "\n")
|
||||
raise SystemExit(f"determinism mismatch; see {log}")
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "PASS", "stamp": STAMP, "runs": rows}, indent=2) + "\n")
|
||||
print(f"[L137DeterminismSummary] runs={len(rows)} stamp={STAMP} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Five interleaved L80 timings for assembler/world and the L137 candidate."""
|
||||
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/l137/timing/final"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": (
|
||||
ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
|
||||
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
|
||||
"l137": (
|
||||
ROOT / "Saved/Linux/l137/fast/bin/VoxelForgeCoreHeadless-fast-clang",
|
||||
"dcf2ffa55fd6d62b3c7faf76229c3b99c635868dc1b694fc6e8f39e738775a4e"),
|
||||
}
|
||||
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.compile(r"^\[StreamPipeline\] (.*)$", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
NEAR = re.compile(
|
||||
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
|
||||
re.M)
|
||||
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
binary, expected_stamp = BINARIES[version]
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
|
||||
log = OUT / f"{label}.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}", "--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, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content)
|
||||
if (result.returncode or pipeline is None or near is None or stamp is None
|
||||
or stamp.group(1) != expected_stamp):
|
||||
raise SystemExit(f"L80 timing failed or stamp mismatch: {log}; exit={result.returncode}")
|
||||
fields = dict(FIELDS.findall(pipeline.group(1)))
|
||||
row = {
|
||||
"strate": strate, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
|
||||
"lod02_ready_s": float(near.group(3)),
|
||||
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
|
||||
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L137FinalTimingRun] " + 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]
|
||||
|
||||
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, *(entry[0] for entry in BINARIES.values())]):
|
||||
raise SystemExit("missing L137 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"[L137FinalTimingGate] 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:
|
||||
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", encoding="utf-8")
|
||||
|
||||
measured = [row for row in rows if row["phase"] == "measured"]
|
||||
summary = []
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
group = [row for row in measured
|
||||
if row["strate"] == strate and row["workers"] == workers]
|
||||
entry = {"strate": strate, "workers": workers, "replicates_per_version": 5}
|
||||
medians = {}
|
||||
for version in BINARIES:
|
||||
version_rows = [row for row in group if row["version"] == version]
|
||||
medians[version] = {
|
||||
key: statistics.median(row[key] for row in version_rows)
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s")}
|
||||
for key in medians[version]:
|
||||
entry[f"{version}_{key}_values"] = [row[key] for row in version_rows]
|
||||
entry[f"{version}_{key}_median"] = medians[version][key]
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
entry[f"l137_vs_assembler-world_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["l137"][key]
|
||||
/ medians["assembler-world"][key] - 1.0), 2)
|
||||
entry[f"{key}_paired_deltas_pct"] = [
|
||||
round(100.0 * (next(row[key] for row in group
|
||||
if row["version"] == "l137" and row["replicate"] == replicate)
|
||||
/ next(row[key] for row in group
|
||||
if row["version"] == "assembler-world"
|
||||
and row["replicate"] == replicate) - 1.0), 3)
|
||||
for replicate in range(1, 6)]
|
||||
summary.append(entry)
|
||||
(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())
|
||||
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""All-LOD inclusive L77 samples for assembler/world and L137 HEAD."""
|
||||
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/l137/profile"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": ROOT / "Saved/Linux/l137/profile-bin/VoxelForgeCoreHeadless-l137-profile-base",
|
||||
"head": ROOT / "Saved/Linux/l137/profile-bin/VoxelForgeCoreHeadless-l137-profile-head",
|
||||
}
|
||||
STAMPS = {
|
||||
"assembler-world": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
|
||||
"head": "dcf2ffa55fd6d62b3c7faf76229c3b99c635868dc1b694fc6e8f39e738775a4e",
|
||||
}
|
||||
CAMERA = "8.000000000000,16.000000000000,-372.262695312500,0,0"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
SAMPLER_RE = re.compile(r"^\[L77Sampler\] action=stop (.*)$", re.M)
|
||||
SAMPLE_RE = re.compile(
|
||||
r"^\[L77Sample\] pc=(0x\w+) lod=(\d+) stage=(\S+) route=(\S+) "
|
||||
r"phase=(\S+) halo=(\d+) weight=(\d+)$", re.M)
|
||||
|
||||
|
||||
def run(version: str, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
log = OUT / f"sw-{version}-r{replicate}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[version]), 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",
|
||||
"--linux77-profile",
|
||||
]
|
||||
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")
|
||||
stamp, sampler = STAMP_RE.search(content), SAMPLER_RE.search(content)
|
||||
samples = SAMPLE_RE.findall(content)
|
||||
if (result.returncode or stamp is None or sampler is None
|
||||
or stamp.group(1) != STAMPS[version] or "status=PASS" not in sampler.group(1)):
|
||||
raise SystemExit(f"L77 profile failed or stamp mismatch: {log}; exit={result.returncode}")
|
||||
sam = dict(re.findall(r"([a-zA-Z0-9_]+)=([^ ]+)", sampler.group(1)))
|
||||
lods = sorted({int(row[1]) for row in samples})
|
||||
row = {
|
||||
"world": "Surface World", "version": version, "replicate": replicate,
|
||||
"source_stamp": stamp.group(1), "workers": 8, "lods_sampled": lods,
|
||||
"stored_samples": len(samples), "expanded_ticks": int(sam["expanded_ticks"]),
|
||||
"lost": int(sam["lost"]), "timer_threads": int(sam["timer_threads"]),
|
||||
"profiled_thread_cpu_s": float(sam["profiled_thread_cpu_s"]),
|
||||
"handler_cpu_ms": float(sam["handler_cpu_ms"]),
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L137ProfileRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
return row
|
||||
|
||||
|
||||
def main() -> int:
|
||||
missing = [str(path) for path in [DATA, *BINARIES.values()] if not path.is_file()]
|
||||
if missing:
|
||||
raise SystemExit("missing L137 profile input: " + ", ".join(missing))
|
||||
rows = []
|
||||
for replicate, order in enumerate((
|
||||
("assembler-world", "head"), ("head", "assembler-world"),
|
||||
("assembler-world", "head")), 1):
|
||||
for version in order:
|
||||
rows.append(run(version, replicate))
|
||||
(OUT / "runs-partial.json").write_text(
|
||||
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
(OUT / "runs.json").write_text(json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the L137 composed passage and tile-seam audits for seeds 0 through 15."""
|
||||
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/l137/seeds"
|
||||
BIN = ROOT / "Saved/Linux/l137/fast/bin/VoxelForgeCoreHeadless-fast-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "dcf2ffa55fd6d62b3c7faf76229c3b99c635868dc1b694fc6e8f39e738775a4e"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
REQUIRED = {
|
||||
"passage": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"route_negative_control": re.compile(
|
||||
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L137 fast binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
for seed in range(16):
|
||||
log = OUT / f"seed{seed}.log"
|
||||
layout = OUT / f"seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=0",
|
||||
"--force-generate", f"--seed={seed}", "--recipe=surface-world",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
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")
|
||||
found_stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = found_stamp is not None and found_stamp.group(1) == STAMP
|
||||
blocked = re.findall(r"^\[ResolvedPassageBlockedControl\].*detected=true$",
|
||||
content, re.M)
|
||||
checks["three_blocked_controls"] = len(blocked) == 3
|
||||
passed = result.returncode == 0 and all(checks.values())
|
||||
row = {"seed": seed, "exit": result.returncode, "passed": passed,
|
||||
"checks": checks, "blocked_controls": len(blocked),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L137SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(results, indent=2) + "\n")
|
||||
if not passed:
|
||||
raise SystemExit(f"seed {seed} failed composed audits; see {log}")
|
||||
summary = {"source_stamp": STAMP, "seeds": list(range(16)),
|
||||
"passed": len(results), "failed": 0, "results": results}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
print("[L137SeedSweepSummary] passed=16/16 status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare rendered Surface World hashes for v227 and the no-op v228 bump."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l137/output-check"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"v227": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
|
||||
"v228": ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-v228",
|
||||
}
|
||||
CAMERA = "8.000000000000,16.000000000000,-372.262695312500,0,0"
|
||||
HASHES = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
|
||||
|
||||
def run(version: str) -> tuple[list[str], str]:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
log = OUT / f"surface-world-{version}.log"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BINARIES[version]), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=8",
|
||||
f"--camera={CAMERA}", "--recipe=surface-world", "--stream-hash",
|
||||
"--stream-scheduler-policy=3",
|
||||
]
|
||||
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")
|
||||
hashes = HASHES.findall(content)
|
||||
if result.returncode or len(hashes) < 4:
|
||||
raise SystemExit(f"hash run failed: {version}; exit={result.returncode}; log={log}")
|
||||
return hashes, str(log.relative_to(ROOT))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
rows = []
|
||||
for version in BINARIES:
|
||||
hashes, log = run(version)
|
||||
rows.append({"version": version, "hashes": hashes, "log": log})
|
||||
match = rows[0]["hashes"] == rows[1]["hashes"]
|
||||
summary = {"output_hashes_match": match, "rows": rows}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
print("[L137VersionOutput] " + json.dumps({
|
||||
"output_hashes_match": match,
|
||||
"hash_lines": len(rows[0]["hashes"]),
|
||||
"v227": rows[0]["hashes"], "v228": rows[1]["hashes"]}, sort_keys=True))
|
||||
return 0 if match else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Summarize per-tile cache counters from the five L137 paired timing runs."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import statistics
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
SOURCE = ROOT / "Saved/Linux/l137/timing/final-design1"
|
||||
OUT = ROOT / "Saved/Linux/l138/prechange/column-cache-counts.json"
|
||||
STAMPS = {
|
||||
"assembler-world": "4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621",
|
||||
"l137": "7913d019d968de2dbaea2282b90fa8ff41b53f996c022e9e6fed92712cb40ecf",
|
||||
}
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
METRICS = (
|
||||
"column_point_calls", "column_local_hits", "column_local_misses",
|
||||
"column_conflict_misses", "column_shared_hits", "column_formula_evaluations",
|
||||
)
|
||||
|
||||
|
||||
def read_run(strate: int, workers: int, version: str, replicate: int) -> dict:
|
||||
path = SOURCE / f"s{strate}-w{workers}-{version}-measured{replicate}.log"
|
||||
if not path.is_file():
|
||||
raise SystemExit(f"missing pre-change L137 run: {path}")
|
||||
text = path.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = re.search(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", text, re.M)
|
||||
if stamp is None or stamp.group(1) != STAMPS[version]:
|
||||
raise SystemExit(f"unexpected source stamp in {path}")
|
||||
rows = {}
|
||||
for line in text.splitlines():
|
||||
if not line.startswith("[WorldAssemblerLODCostDetail] "):
|
||||
continue
|
||||
fields = dict(FIELDS.findall(line))
|
||||
rows[int(fields["lod"])] = fields
|
||||
if set(rows) != set(range(9)):
|
||||
raise SystemExit(f"incomplete LOD counters in {path}: {sorted(rows)}")
|
||||
return {"path": str(path.relative_to(ROOT)), "rows": rows}
|
||||
|
||||
|
||||
def summarize(runs: list[dict], lods: range) -> dict:
|
||||
per_run = []
|
||||
for run in runs:
|
||||
selected = [run["rows"][lod] for lod in lods]
|
||||
tile_count = sum(int(row["tiles"]) for row in selected)
|
||||
per_run.append({
|
||||
"tiles": tile_count,
|
||||
**{metric: sum(int(row[metric]) for row in selected) / tile_count
|
||||
for metric in METRICS},
|
||||
})
|
||||
return {
|
||||
"tiles_per_run": statistics.mean(row["tiles"] for row in per_run),
|
||||
"mean_per_tile": {
|
||||
metric: statistics.mean(row[metric] for row in per_run)
|
||||
for metric in METRICS
|
||||
},
|
||||
"run_range_per_tile": {
|
||||
metric: [min(row[metric] for row in per_run),
|
||||
max(row[metric] for row in per_run)]
|
||||
for metric in METRICS
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
result = {
|
||||
"source": "five measured L137 L80 runs per version, worker count, and stratum",
|
||||
"pre_change_stamps": STAMPS,
|
||||
"cells": [],
|
||||
}
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
versions = {
|
||||
version: [read_run(strate, workers, version, replicate)
|
||||
for replicate in range(1, 6)]
|
||||
for version in STAMPS
|
||||
}
|
||||
item = {"strate": strate, "workers": workers}
|
||||
for window, lods in (("near_lod0_2", range(3)), ("whole_lod0_8", range(9))):
|
||||
item[window] = {
|
||||
version: summarize(runs, lods)
|
||||
for version, runs in versions.items()
|
||||
}
|
||||
item[window]["candidate_minus_baseline_per_tile"] = {
|
||||
metric: (item[window]["l137"]["mean_per_tile"][metric]
|
||||
- item[window]["assembler-world"]["mean_per_tile"][metric])
|
||||
for metric in METRICS
|
||||
}
|
||||
item["logs"] = {
|
||||
version: [run["path"] for run in runs]
|
||||
for version, runs in versions.items()
|
||||
}
|
||||
result["cells"].append(item)
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUT.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L138PrechangeCacheCounts] cells={len(result['cells'])} output={OUT.relative_to(ROOT)}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,49 @@
|
||||
# Linux138 — final cave planner speed pass
|
||||
|
||||
Source stamp: `9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f`.
|
||||
|
||||
## Result
|
||||
|
||||
The suspected column-cache regression is not present in the measured counters. Calls and formula evaluations per tile match `assembler/world`; the small miss deltas change sign between W8 and W12. The L77 `CachedColumn` and shared `TryRead` increases were normalized sample-share changes, not evidence of more cache calls.
|
||||
|
||||
I removed repeated route identity checks and added a fast path for prepared sweep samples that are stamped outside all passage influence. The path preserves the sample's flat distance in the union and skips wall/floor shell work whose bounds are positive. The speed bar is still missed on several near-ready cells, especially Vertical Shafts. No version bump or baseline update was needed: output audits remained green.
|
||||
|
||||
## Before-change cache census
|
||||
|
||||
Means per near-ready LOD0–2 tile, averaged across the five measured L137 L80 runs per worker count. Each cell is `assembler/world / L137 candidate`.
|
||||
|
||||
| Stratum / workers | Point calls | Local hits | Local misses | Shared hits | Formula evaluations |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| Surface World / W8 | 6529.395 / 6529.396 | 5712.251 / 5720.303 | 817.144 / 809.092 | 570.207 / 562.158 | 246.937 / 246.934 |
|
||||
| Surface World / W12 | 6529.395 / 6529.396 | 5642.386 / 5641.558 | 887.009 / 887.838 | 639.976 / 640.851 | 247.033 / 246.987 |
|
||||
|
||||
Floating Islands, Tunnel Network, and Vertical Shafts had zero generic heightfield column calls and cache hits/misses per near-ready tile in both binaries. Floating Islands' separate local/shared cache counters were also zero in its profiled LOD0–2 tiles. The shared `TryRead` path is reached on local misses: W8 had about 8 fewer misses per tile on the candidate, while W12 had 0.83 more. Cache code, key, and window are unchanged versus `assembler/world` in `WorldAssemblerInternal.h`, `SharedColumnCache.h`, and `FloatingIslandsInternal.h`.
|
||||
|
||||
L77 reports each function's share of sampled worker ticks. A higher share can result when other work takes a smaller share; it is not a cache-call counter. The before-change census is in `Saved/Linux/l138/prechange/column-cache-counts.json`; the separate Floating Islands counters are in `Saved/Linux/l138/prechange/island-cache/summary.json`.
|
||||
|
||||
## Change and timing
|
||||
|
||||
`ShapeWorldInternal.h` now reuses the already-computed Tunnel Network stratum predicate in the passage loop. For a swept sample with no exact contribution, it adds the same flat outside distance to the union, then skips relief, mouth, wall, and shell calculations. The influence bound is 1.053 m (0.75 m shell + 0.21 m relief + 0.083 m corner support + 0.01 m guard); the stamped sentinel is one subvoxel beyond it.
|
||||
|
||||
Final L80 used one warm-up and five interleaved runs per binary, at W8/W12 across all four strata. The quiet-host gate passed at load 1.34 and 98.9% CPU idle. Deltas are candidate medians versus `assembler/world`; the L137 A/A noise floor is ±1%.
|
||||
|
||||
| Stratum | Near-ready W8 / W12 | Whole-view W8 / W12 |
|
||||
|---|---:|---:|
|
||||
| Surface World | +0.34% / +1.72% | −0.48% / −0.93% |
|
||||
| Floating Islands | −4.34% / +2.87% | −3.11% / −1.50% |
|
||||
| Tunnel Network | −2.54% / +2.86% | −3.96% / −1.73% |
|
||||
| Vertical Shafts | +4.66% / +3.25% | −1.73% / −2.06% |
|
||||
|
||||
Only Surface World W8 is within ±1% near-ready. The per-LOD worker totals show LOD2 down 24–39% on Floating Islands, Tunnel Network, and Vertical Shafts, but Vertical Shafts LOD0/1 are up 5–22%; that remaining work keeps its near-ready medians outside the bar. Tunnel Network W8 is the closest of its two near-ready cells.
|
||||
|
||||
## Checks
|
||||
|
||||
- Fast tier: PASS, 273 checks, 286.0 s.
|
||||
- Composed sweep: PASS, 16/16 seeds, with passage, seam, shell, mouth-profile, naturalness, ripple, and blocked-route controls.
|
||||
- Clang/GCC W1/W8/W12: PASS, all 13 determinism hash lines match.
|
||||
- Linux `VFHostEditor` Development: PASS, 5 actions, 38.19 s with `-NoUBA -MaxParallelActions=4`; no `prlimit`.
|
||||
- The full verification tier and Windows UE build were not run. No Unreal `Source/` files changed.
|
||||
|
||||
The first fast-tier invocation used the verifier's default `l42` output directory and refused its stale binary. I reran it with `--out Saved/Linux/l138`; the final-source run passed.
|
||||
|
||||
Build, fast-tier, seed, determinism, cache-census, and timing logs are under `Saved/Linux/l138/`; the final timing matrix is `Saved/Linux/l138/timing/summary.json`. Changes are uncommitted.
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare L138 Clang/GCC composed hashes at W1, W8, and W12."""
|
||||
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/l138/determinism"
|
||||
DATA = ROOT / "Standalone/data/regression/owner_world_2strate_l61.json"
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang",
|
||||
"gcc": ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-gcc",
|
||||
}
|
||||
STAMP = "9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f"
|
||||
CAMERA = "-4.688214,38.662384,-505.781342,-28.446,0"
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing L138 determinism binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
reference: tuple[str, ...] | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
name = f"determinism-{compiler}-w{workers}"
|
||||
log = OUT / f"{name}.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=0",
|
||||
"--max-level=0", f"--camera={CAMERA}", "--stream-hash-tiles",
|
||||
"--force-generate", "--recipe=surface-world", "--render-distance-chunks=0",
|
||||
"--landmark-chamber-demo", "--ore-veins-demo"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
hashes = tuple(HASH_RE.findall(content))
|
||||
passed = (proc.returncode == 0 and stamp is not None
|
||||
and stamp.group(1) == STAMP and len(hashes) >= 4)
|
||||
if reference is None and passed:
|
||||
reference = hashes
|
||||
elif reference is not None:
|
||||
passed = passed and hashes == reference
|
||||
row = {"compiler": compiler, "workers": workers, "exit": proc.returncode,
|
||||
"stamp": stamp.group(1) if stamp else None, "hash_lines": len(hashes),
|
||||
"hashes_match_first": hashes == reference, "passed": bool(passed),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L138DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
if not passed:
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "FAIL", "stamp": STAMP, "runs": rows}, indent=2) + "\n")
|
||||
raise SystemExit(f"determinism mismatch; see {log}")
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "PASS", "stamp": STAMP, "runs": rows}, indent=2) + "\n")
|
||||
print(f"[L138DeterminismSummary] runs={len(rows)} stamp={STAMP} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run five interleaved L80 timings for assembler/world and the L138 candidate."""
|
||||
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/l138/timing"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": (
|
||||
ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
|
||||
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
|
||||
"l138": (
|
||||
ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang",
|
||||
"9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f"),
|
||||
}
|
||||
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.compile(r"^\[StreamPipeline\] (.*)$", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
NEAR = re.compile(
|
||||
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
|
||||
re.M)
|
||||
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
binary, expected_stamp = BINARIES[version]
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
|
||||
log = OUT / f"{label}.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}", "--profile-lod-work", "--stream-scheduler-policy=3",
|
||||
]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = 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, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content)
|
||||
if (proc.returncode or pipeline is None or near is None or stamp is None
|
||||
or stamp.group(1) != expected_stamp):
|
||||
raise SystemExit(f"L80 timing failed or stamp mismatch: {log}; exit={proc.returncode}")
|
||||
fields = dict(FIELDS.findall(pipeline.group(1)))
|
||||
row = {
|
||||
"strate": strate, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
|
||||
"lod02_ready_s": float(near.group(3)),
|
||||
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
|
||||
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L138FinalTimingRun] " + 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]
|
||||
|
||||
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, *(entry[0] for entry in BINARIES.values())]):
|
||||
raise SystemExit("missing L138 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"[L138FinalTimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
|
||||
if load_1m >= 1.5 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:
|
||||
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", encoding="utf-8")
|
||||
|
||||
measured = [row for row in rows if row["phase"] == "measured"]
|
||||
summary = []
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
group = [row for row in measured
|
||||
if row["strate"] == strate and row["workers"] == workers]
|
||||
entry = {"strate": strate, "workers": workers, "replicates_per_version": 5}
|
||||
medians = {
|
||||
version: {
|
||||
key: statistics.median(row[key] for row in group
|
||||
if row["version"] == version)
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s")
|
||||
}
|
||||
for version in BINARIES
|
||||
}
|
||||
entry["medians"] = medians
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
entry[f"l138_vs_assembler-world_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["l138"][key] / medians["assembler-world"][key] - 1.0), 2)
|
||||
entry[f"{key}_paired_deltas_pct"] = [
|
||||
round(100.0 * (
|
||||
next(row[key] for row in group
|
||||
if row["version"] == "l138" and row["replicate"] == replicate)
|
||||
/ next(row[key] for row in group
|
||||
if row["version"] == "assembler-world"
|
||||
and row["replicate"] == replicate) - 1.0), 3)
|
||||
for replicate in range(1, 6)
|
||||
]
|
||||
summary.append(entry)
|
||||
(OUT / "summary.json").write_text(json.dumps({
|
||||
"a_a_noise_floor_pct": 1.0,
|
||||
"timing_protocol": "one warmup per binary, five interleaved runs, W8/W12, all four strata",
|
||||
"runs": rows,
|
||||
"summary": summary,
|
||||
}, indent=2) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Count pre-change Floating Islands cache calls from L137 binaries."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l138/prechange/island-cache"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": (
|
||||
ROOT / "Saved/Linux/l137/profile-bin/VoxelForgeCoreHeadless-l137-profile-base",
|
||||
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
|
||||
"l137": (
|
||||
ROOT / "Saved/Linux/l137/profile-bin/VoxelForgeCoreHeadless-l137-profile-head",
|
||||
"dcf2ffa55fd6d62b3c7faf76229c3b99c635868dc1b694fc6e8f39e738775a4e"),
|
||||
}
|
||||
CAMERAS = {
|
||||
1: (25.8369140625, 3.19921875, -737.0),
|
||||
}
|
||||
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
TILE = re.compile(r"^\[CpuTileStages\] key=\([^)]*,lod(\d+)\).*", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
CACHE_FIELDS = (
|
||||
"fi_local_hit", "fi_local_miss", "fi_local_conflicts",
|
||||
"fi_shared_hit", "fi_shared_miss", "fi_shared_computes",
|
||||
"fi_shared_conflicts",
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
for workers in (8, 12):
|
||||
for version, (binary, expected_stamp) in BINARIES.items():
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[1]) + ",0,0"
|
||||
log = OUT / f"s1-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}", "--profile-lod-work",
|
||||
"--profile-passage-composer", "--stream-scheduler-policy=3",
|
||||
]
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
text = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP.search(text)
|
||||
if proc.returncode or not stamp or stamp.group(1) != expected_stamp:
|
||||
raise SystemExit(f"pre-change cache census failed: {log}")
|
||||
tiles = []
|
||||
for line in text.splitlines():
|
||||
match = TILE.match(line)
|
||||
if not match or int(match.group(1)) > 2:
|
||||
continue
|
||||
fields = dict(FIELDS.findall(line))
|
||||
tiles.append({key: int(fields.get(key, "0")) for key in CACHE_FIELDS})
|
||||
if not tiles:
|
||||
raise SystemExit(f"no LOD0-2 tile-cache counters in {log}")
|
||||
total = {key: sum(tile[key] for tile in tiles) for key in CACHE_FIELDS}
|
||||
row = {
|
||||
"strate": 1, "workers": workers, "version": version,
|
||||
"source_stamp": stamp.group(1), "tiles": len(tiles),
|
||||
"totals": total,
|
||||
"per_tile": {key: value / len(tiles) for key, value in total.items()},
|
||||
"logs": str(log.relative_to(ROOT)),
|
||||
}
|
||||
rows.append(row)
|
||||
print("[L138PrechangeIslandCache] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "summary.json").write_text(json.dumps(rows, indent=2) + "\n")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the 16-seed composed passage and seam audits on the L138 candidate."""
|
||||
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/l138/seeds"
|
||||
BIN = ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
REQUIRED = {
|
||||
"passage": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"route_negative_control": re.compile(
|
||||
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L138 Clang binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
for seed in range(16):
|
||||
log = OUT / f"seed{seed}.log"
|
||||
layout = OUT / f"seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=8", "--clip-radius=3", "--max-clip-level=8", "--max-level=0",
|
||||
"--force-generate", f"--seed={seed}", "--recipe=surface-world",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
|
||||
blocked = re.findall(r"^\[ResolvedPassageBlockedControl\].*detected=true$",
|
||||
content, re.M)
|
||||
checks["three_blocked_controls"] = len(blocked) == 3
|
||||
passed = proc.returncode == 0 and all(checks.values())
|
||||
row = {"seed": seed, "exit": proc.returncode, "passed": passed,
|
||||
"checks": checks, "blocked_controls": len(blocked),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L138SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(results, indent=2) + "\n")
|
||||
if not passed:
|
||||
raise SystemExit(f"seed {seed} failed composed audits; see {log}")
|
||||
summary = {"source_stamp": STAMP, "seeds": list(range(16)),
|
||||
"passed": len(results), "failed": 0, "results": results}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
print("[L138SeedSweepSummary] passed=16/16 status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,92 @@
|
||||
# Linux139 — Vertical Shafts on the Core planner
|
||||
|
||||
## Result
|
||||
|
||||
S3 now reports `Core.Caves.PrimitiveField` / `Caves-native`, using the shared Tunnel Network
|
||||
planner. The composed acceptance gate is not green: **15/16 seeds pass; seed 2 fails the passage
|
||||
shell and tile seam checks at the arrival from passage 2–3.** This worktree is not promoted as an
|
||||
accepted Vertical Shafts generator.
|
||||
|
||||
The basal primordial route law means arrival passage 2–3 to a walkable terminal landing above the
|
||||
bedrock seal. There is no departure mouth at the bottom of the owner world. The shared planner
|
||||
builds that walk-only spine, its access tunnels, and landings within the existing grade contract.
|
||||
The full-height open shafts and their ledges are an exploration tier: a drop or climb is not
|
||||
walk-only traversal, and climb or mine access stays in the reporting tiers. `Core.Lattice` was not
|
||||
used because L58's VerticalShafts lattice route looked like a corkscrew, not an open shaft.
|
||||
|
||||
## Plan and parameter mapping
|
||||
|
||||
`PlanResolvedCavernNetworkV1` is reused without a planner fork. `Core.Caves.PrimitiveField` is the
|
||||
shared evaluator, extended with `shapeKind=1`: the existing room/tunnel shapes do not represent a
|
||||
capped, full-height vertical bore, and the L58 lattice route was a corkscrew. Shaft records use
|
||||
that cylinder primitive with ledges, short access bores, and cross-shaft links. Generic cave
|
||||
branches are disabled in shaft mode.
|
||||
|
||||
| `FVerticalShaftParams` field(s) | Core setting |
|
||||
|---|---|
|
||||
| `ShaftSpacing` | Shared planner room spacing; owner voxels convert to integer micrometres. |
|
||||
| `ShaftDensity` | `roomDensityQ16`, controlling shaft placement; a nonzero setting guarantees the route's initial shaft. |
|
||||
| `ShaftMinRadius`, `ShaftMaxRadius` | Minimum and maximum shaft radii. |
|
||||
| `CrossConnectChance` | `tunnelDensityQ16` for links between shafts. |
|
||||
| `ConnectorRadius` | Equal minimum/maximum radius for access and connector bores. |
|
||||
| `LedgeSpacing`, `LedgeDepth` | Vertical shelf interval and intrusion; zero spacing disables shelves and zero depth makes them nonintrusive. |
|
||||
| `SurfaceRoughness` | Roughness amplitude; frequency derives as `max(0.5 m, 4 owner voxels)` because no frequency field exists. |
|
||||
| `BoundarySealThickness`, `BaseDensity` | Existing resolved top/basal boundary bands via `sealAndDensity`, outside planner settings. |
|
||||
|
||||
## Measurements
|
||||
|
||||
The final source stamp was `b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1`.
|
||||
The composed sweep covered seeds 0–15 with one run per seed (reported process time 320.485 s in
|
||||
total; individual runs 10.296–34.015 s). Seeds 0, 1, and 3–15 passed passages, route, seams, shell,
|
||||
and blocked-route controls. Seed 2's route grades/support, naturalness, mouth profile, ripple,
|
||||
dispatch, and negative controls passed; its composed passage, seam, and shell summaries failed.
|
||||
The shell audit reported eight `wall_missing` samples on `passage/2-3/0`, at route stations
|
||||
489.627–490.321 m. The final log records positive native density at those samples and negative
|
||||
wall-shell margins. See `Saved/Linux/l139/seeds/seed2-final.log` and
|
||||
`Saved/Linux/l139/seeds/summary.json`.
|
||||
|
||||
Three geometry variants were tried against this same shell gate: starting passage clearance at
|
||||
12 m, adding a guarded cubic lateral bow, and starting clearance at 8 m. The first and third still
|
||||
failed wall checks; the bow failed floor checks. Per the three-failure stop rule, these were
|
||||
discarded. The final binary retains the successful Q16 endpoint-normalization correction needed
|
||||
for seed 1 and no seed-2 geometry patch.
|
||||
|
||||
L80 ran after the seed sweep. The host gate was **load 1.31, CPU idle 99.4%**. It used one warm-up
|
||||
per build and stratum/worker cell, then five interleaved measured runs for each of three builds at
|
||||
W8 and W12 across all four strata: 120 measured runs total. Comparisons use `fa4417a`,
|
||||
`assembler/world`, and the L139 binary; the established L137 A/A noise floor is ±1% ([A/A report](../Linux137/REPORT.md)).
|
||||
Times are medians in seconds; deltas are L139 relative to each comparison build. Paired values in
|
||||
each cell are W8 / W12.
|
||||
|
||||
| Stratum | LOD0–2 L139 | Δ vs `fa4417a` | Δ vs `assembler/world` | Whole-view L139 | Δ vs `fa4417a` | Δ vs `assembler/world` |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| S0 Surface World | 0.838 / 0.886 | +1.71% / +3.97% | +2.92% / +5.06% | 1.862 / 1.804 | +2.42% / +1.63% | −0.11% / −0.33% |
|
||||
| S1 Floating Islands | 1.036 / 1.038 | +4.72% / +4.61% | +2.09% / +5.35% | 1.842 / 1.830 | +0.11% / +1.16% | −6.73% / −1.24% |
|
||||
| S2 Tunnel Network | 1.105 / 1.078 | +2.44% / +3.07% | +3.57% / +7.57% | 1.876 / 1.808 | −1.11% / +0.00% | −4.33% / −1.15% |
|
||||
| S3 Vertical Shafts | 0.899 / 0.884 | −3.81% / −0.94% | −0.95% / +2.00% | 1.706 / 1.657 | −6.57% / −2.53% | −6.21% / −4.77% |
|
||||
|
||||
Whole-view medians are numerically lower than `assembler/world` in all eight cells; the S0 W8 and
|
||||
W12 differences are within the ±1% noise floor. Seven of eight LOD0–2 cells are slower than
|
||||
`assembler/world`; the remaining cell is within the noise floor. This is characterization of an
|
||||
unaccepted build, not a promotion claim. Raw runs and medians are in
|
||||
`Saved/Linux/l139/timing/summary.json`.
|
||||
|
||||
## Checks and captures
|
||||
|
||||
- Fast tier: **PASS**, 284.0 s; the S3 dispatch line is `VerticalShafts` → `Core.Caves.PrimitiveField` v1 / `Caves-native` / `FCavesFieldAdapter`.
|
||||
- Clang and GCC determinism: **PASS**, W1/W8/W12 (six cases), 13 hash lines and resolved-layout identity matched. Both compilers built the final source stamp above.
|
||||
- Canonical v237 baselines added for Surface World and Floating Islands; both match their v236 values.
|
||||
- Five eye-height views were captured before and after: arrival mouth, shaft access landing, looking down, looking up from a ledge, and basal terminal landing. All five L139 views pass the existing quality threshold. Two L138 baseline views fail it: the old layer has no shaft at the selected site, so the down-shaft view is flat, and the upward view is low contrast. The threshold was not relaxed. Images and camera details are in `captures/`.
|
||||
- The outside views are `captures/after-whole-layer-density-cutaway.jpg` (coarse LOD density-mesh slice at y=−5 m) and `captures/after-whole-layer-side-elevation.svg` (resolved-plan projection that labels the walk route and all 53 shaft primitives). The cutaway passes the unchanged image-quality threshold; its plan companion makes the full shaft layout clearer.
|
||||
- Linux `VFHostEditor` Development build: **PASS**, 27 actions, 72.58 s, run after timing with `-NoUBA -MaxParallelActions=4` and no `prlimit`. It emitted three pre-existing deprecated RealtimeMesh API warnings from `VoxelWorld.cpp`; that file was not changed.
|
||||
- `git diff --check` and Python bytecode compilation of the L139 runners passed. Windows build and Windows determinism were not run.
|
||||
|
||||
Logs and source-stamped binaries are under `Saved/Linux/l139/`. The only changed Unreal `Source/`
|
||||
file is `Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp`.
|
||||
|
||||
## Remaining risk
|
||||
|
||||
The passage shell and seam break at the incoming mouth on seed 2 remain unresolved. The local
|
||||
clearance/heading premise failed three geometry attempts; a new route-to-mouth design premise is
|
||||
needed before another geometry change. Walk-only connectivity stays guaranteed by construction;
|
||||
vertical drops, climbs, and mine access remain reporting tiers.
|
||||
@@ -0,0 +1,278 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture comparable player-height views of the L138 and L139 owner shaft layer."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
LAYOUT = ROOT / "Saved/Linux/l139/seeds/seed0.json"
|
||||
OUT = ROOT / "Saved/Linux/l139/captures"
|
||||
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux139/captures"
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
VERSIONS = {
|
||||
"before-l138": {
|
||||
"headless": ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang",
|
||||
"renderer": ROOT / "Saved/Linux/l138/bin/VoxelForgeOffscreenCapture-l138-clang",
|
||||
"stamp": "9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f",
|
||||
},
|
||||
"after-l139": {
|
||||
"headless": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
|
||||
"renderer": ROOT / "Saved/Linux/l139/bin/VoxelForgeOffscreenCapture-l139-clang",
|
||||
"stamp": "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1",
|
||||
},
|
||||
}
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
|
||||
|
||||
|
||||
def fmt(values: tuple[float, ...]) -> str:
|
||||
return ",".join(f"{value:.6f}" for value in values)
|
||||
|
||||
|
||||
def angles(camera: tuple[float, float, float],
|
||||
target: tuple[float, float, float]) -> tuple[float, float]:
|
||||
dx, dy, dz = (target[i] - camera[i] for i in range(3))
|
||||
return math.degrees(math.atan2(dx, dy)), math.degrees(
|
||||
math.atan2(dz, math.hypot(dx, dy)))
|
||||
|
||||
|
||||
def stamped_run(name: str, command: list[str], log: Path, expected: str,
|
||||
env: dict[str, str]) -> str:
|
||||
binary = Path(command[0])
|
||||
probe = subprocess.run([*LIMIT, "timeout", "15s", str(binary), "--print-source-stamp"],
|
||||
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=20)
|
||||
match = STAMP_RE.search(probe.stdout or "")
|
||||
if probe.returncode != 0 or not match or match.group(1) != expected:
|
||||
raise RuntimeError(f"{name}: executable stamp mismatch for {binary}")
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
|
||||
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=1745, check=False)
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
emitted = STAMP_RE.search(output)
|
||||
if (result.returncode != 0 or not emitted or emitted.group(1) != expected):
|
||||
raise RuntimeError(f"{name}: process failed or source changed; see {log}")
|
||||
print(f"[L139CaptureRun] step={name} elapsed_s={time.monotonic()-started:.1f} "
|
||||
f"log={log.relative_to(ROOT)}", flush=True)
|
||||
return output
|
||||
|
||||
|
||||
def point_um(values: list[int]) -> tuple[float, float, float]:
|
||||
return tuple(value / 1_000_000.0 for value in values)
|
||||
|
||||
|
||||
def make_views(layout: dict) -> list[dict]:
|
||||
edges = {row["stable_id"]: row for row in layout["cavern_edges"]}
|
||||
rooms = {row["stable_id"]: row for row in layout["cavern_rooms"]}
|
||||
shaft = rooms["vertical-shafts/strate-3/shaft-0/primitive-0"]
|
||||
access = edges["vertical-shafts/strate-3/shaft-access-0"]["polyline_um"]
|
||||
arrival = edges["vertical-shafts/strate-3/route-edge-0"]["polyline_um"]
|
||||
basal = rooms["vertical-shafts/strate-3/basal-landing/primitive-0"]
|
||||
center = point_um(shaft["center_um"])
|
||||
half = point_um(shaft["half_extents_um"])
|
||||
shaft_top = center[2] + half[2]
|
||||
shaft_bottom = center[2] - half[2]
|
||||
route_a, route_b = point_um(arrival[0]), point_um(arrival[1])
|
||||
tx, ty = route_b[0] - route_a[0], route_b[1] - route_a[1]
|
||||
tlen = max(1.0e-9, math.hypot(tx, ty))
|
||||
tangent = (tx / tlen, ty / tlen)
|
||||
mouth_camera = (route_a[0] - tangent[0] * 2.0,
|
||||
route_a[1] - tangent[1] * 2.0, route_a[2] + 1.45)
|
||||
mouth_target = (route_a[0] + tangent[0] * 8.0,
|
||||
route_a[1] + tangent[1] * 8.0, route_a[2] + 1.45)
|
||||
access_a, access_b = point_um(access[0]), point_um(access[-1])
|
||||
access_camera = (access_a[0], access_a[1], access_a[2] + 1.45)
|
||||
access_target = (access_b[0], access_b[1], access_b[2] + 1.45)
|
||||
shaft_lip_camera = (access_b[0], access_b[1], access_b[2] + 1.45)
|
||||
basal_center = point_um(basal["center_um"])
|
||||
basal_camera = (basal_center[0], basal_center[1], basal["floor_um"] / 1.0e6 + 1.45)
|
||||
basal_target = (basal_center[0] + tangent[0] * 8.0,
|
||||
basal_center[1] + tangent[1] * 8.0, basal_camera[2])
|
||||
ledge_z = shaft_top - 12 * (shaft["ledge_spacing_um"] / 1.0e6)
|
||||
return [
|
||||
{"name": "arrival-mouth", "camera": mouth_camera, "target": mouth_target,
|
||||
"focus": (route_a[0], route_a[1], route_a[2] + 1.4, 12.0),
|
||||
"note": "player eye at the passage arrival, looking into the connected layer"},
|
||||
{"name": "landing-at-shaft-access", "camera": access_camera,
|
||||
"target": access_target,
|
||||
"focus": (center[0], center[1], access_a[2], 14.0),
|
||||
"note": "walkable route landing and short shaft access tunnel"},
|
||||
{"name": "looking-down-shaft", "camera": shaft_lip_camera,
|
||||
"target": (center[0], center[1], shaft_top - 30.0),
|
||||
"focus": (center[0], center[1], shaft_top - 25.0, 14.0),
|
||||
"fov": 35.0,
|
||||
"note": "player eye at the upper access landing, looking down the open shaft"},
|
||||
{"name": "looking-up-from-ledge", "camera": (center[0], center[1], ledge_z + 1.45),
|
||||
"target": (center[0], center[1], shaft_top - 2.0),
|
||||
"focus": (center[0], center[1], shaft_top - 22.0, 20.0),
|
||||
"note": "player eye at an internal ledge, looking up; reaching this ledge is a climb tier"},
|
||||
{"name": "basal-terminal-landing", "camera": basal_camera,
|
||||
"target": basal_target,
|
||||
"focus": (basal_center[0], basal_center[1], basal_center[2], 16.0),
|
||||
"note": "walkable terminal landing above the basal seal"},
|
||||
]
|
||||
|
||||
|
||||
def write_overview(layout: dict, destination: Path) -> None:
|
||||
shafts = [room for room in layout["cavern_rooms"] if room.get("shape_kind") == 1]
|
||||
route_edges = sorted((edge for edge in layout["cavern_edges"]
|
||||
if edge["stable_id"].startswith("vertical-shafts/strate-3/route-edge-")),
|
||||
key=lambda edge: int(edge["stable_id"].rsplit("-", 1)[1]))
|
||||
if not shafts or not route_edges:
|
||||
raise ValueError("overview needs the resolved shaft primitives and walk route")
|
||||
points = [point_um(row) for edge in route_edges for row in edge["polyline_um"]]
|
||||
shaft_x_bounds = [(point_um(room["center_um"])[0]
|
||||
-room["half_extents_um"][0]/1e6,
|
||||
point_um(room["center_um"])[0]+room["half_extents_um"][0]/1e6)
|
||||
for room in shafts]
|
||||
x_min = min(min(bounds[0] for bounds in shaft_x_bounds),
|
||||
min(row[0] for row in points))
|
||||
x_max = max(x + room["half_extents_um"][0] / 1e6
|
||||
for room in shafts for x in [point_um(room["center_um"])[0]])
|
||||
x_max = max(x_max, max(row[0] for row in points))
|
||||
z_min = min(point_um(room["center_um"])[2] - room["half_extents_um"][2] / 1e6
|
||||
for room in shafts)
|
||||
z_max = max(point_um(room["center_um"])[2] + room["half_extents_um"][2] / 1e6
|
||||
for room in shafts)
|
||||
z_min = min(z_min, min(row[2] for row in points))
|
||||
z_max = max(z_max, max(row[2] for row in points))
|
||||
width, height, margin = 1800.0, 760.0, 72.0
|
||||
sx = (width - 2 * margin) / max(1.0, x_max - x_min)
|
||||
sz = (height - 2 * margin) / max(1.0, z_max - z_min)
|
||||
scale = min(sx, sz)
|
||||
x_offset = margin + ((width - 2 * margin) - (x_max - x_min) * scale) * 0.5
|
||||
z_offset = margin + ((height - 2 * margin) - (z_max - z_min) * scale) * 0.5
|
||||
def xy(x: float, z: float) -> tuple[float, float]:
|
||||
return x_offset + (x - x_min) * scale, height - (z_offset + (z - z_min) * scale)
|
||||
route_xy = [xy(p[0], p[2]) for p in points]
|
||||
route = " ".join(("M" if i == 0 else "L") + f"{x:.2f},{y:.2f}"
|
||||
for i, (x, y) in enumerate(route_xy))
|
||||
cylinders = []
|
||||
for room in shafts:
|
||||
x, _y, z = point_um(room["center_um"])
|
||||
radius = room["half_extents_um"][0] / 1e6
|
||||
half_z = room["half_extents_um"][2] / 1e6
|
||||
left, top = xy(x - radius, z + half_z)
|
||||
right, bottom = xy(x + radius, z - half_z)
|
||||
cylinders.append(f'<rect x="{left:.2f}" y="{top:.2f}" width="{max(1.2,right-left):.2f}" '
|
||||
f'height="{bottom-top:.2f}" rx="{min(7.0,(right-left)/2):.2f}" '
|
||||
'fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/>')
|
||||
rx0, ry0 = xy(x_min, 0.0)
|
||||
rx1, ry1 = xy(x_max, 0.0)
|
||||
svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="760" viewBox="0 0 1800 760">
|
||||
<rect width="1800" height="760" fill="#07111a"/>
|
||||
<text x="72" y="42" fill="#edf6fb" font-family="sans-serif" font-size="24">Vertical Shafts — whole-layer side elevation</text>
|
||||
<text x="72" y="64" fill="#9bb5c5" font-family="sans-serif" font-size="13">Resolved plan • seed 0 • strate 3 • vertical axis and route shown in metres • Y projected out</text>
|
||||
<line x1="{rx0:.1f}" y1="{ry0:.1f}" x2="{rx1:.1f}" y2="{ry1:.1f}" stroke="#315060" stroke-dasharray="5 8"/>
|
||||
{''.join(cylinders)}
|
||||
<path d="{route}" fill="none" stroke="#f4bf68" stroke-width="3.0" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="82" y="716" fill="#f4bf68" font-family="sans-serif" font-size="15">walk-only graded spine and landings</text>
|
||||
<rect x="416" y="704" width="15" height="15" fill="#93dded" fill-opacity="0.65" stroke="#42b8d1"/>
|
||||
<text x="440" y="716" fill="#b8dce5" font-family="sans-serif" font-size="15">open vertical shaft primitives (53)</text>
|
||||
<text x="1718" y="716" fill="#9bb5c5" text-anchor="end" font-family="sans-serif" font-size="12">Plan projection, not a density mesh</text>
|
||||
</svg>'''
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
destination.write_text(svg, encoding="utf-8")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not DATA.is_file() or not LAYOUT.is_file():
|
||||
raise SystemExit("missing owner fixture or passing seed-0 resolved layout")
|
||||
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
|
||||
views = make_views(layout)
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
ARTIFACTS.mkdir(parents=True, exist_ok=True)
|
||||
env = os.environ.copy()
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
|
||||
quality = ROOT / "Standalone/Tools/verify/capture_quality.py"
|
||||
manifest = []
|
||||
quality_failures = []
|
||||
for version, config in VERSIONS.items():
|
||||
phase = "before" if version.startswith("before") else "after"
|
||||
for view in views:
|
||||
name = view["name"]
|
||||
camera, target = view["camera"], view["target"]
|
||||
yaw, pitch = angles(camera, target)
|
||||
mesh_dir = OUT / "meshes" / phase
|
||||
mesh_dir.mkdir(parents=True, exist_ok=True)
|
||||
mesh = mesh_dir / f"{name}.vfmesh"
|
||||
capture_layout = mesh_dir / f"{name}-layout.json"
|
||||
export_log = OUT / f"{phase}-{name}-export.log"
|
||||
export = [config["headless"], DATA, "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=12",
|
||||
"--clip-radius=6", "--max-clip-level=0", "--max-level=0",
|
||||
"--force-generate", "--seed=0", "--recipe=surface-world",
|
||||
"--render-distance-chunks=0", f"--camera={fmt((*camera,yaw,pitch))}",
|
||||
f"--resolved-layout-output={capture_layout}", f"--mesh-export={mesh}"]
|
||||
stamped_run(f"{phase}-{name}-export", list(map(str, export)), export_log,
|
||||
config["stamp"], env)
|
||||
if not mesh.is_file() or mesh.stat().st_size == 0:
|
||||
raise RuntimeError(f"{phase}-{name}: empty mesh export")
|
||||
png = OUT / f"{phase}-{name}.png"
|
||||
jpg = ARTIFACTS / f"{phase}-{name}.jpg"
|
||||
fov = float(view.get("fov", 78.0))
|
||||
render = [config["renderer"], mesh, png, "--size=1280x720",
|
||||
f"--fov-deg={fov:.1f}", f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
|
||||
f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable",
|
||||
"--headlamp=on", "--height-tint=on", "--height-center-m=-950",
|
||||
"--height-range-m=60", "--tile-edges=off",
|
||||
f"--focus-region={fmt(view['focus'])}"]
|
||||
stamped_run(f"{phase}-{name}-render", list(map(str, render)),
|
||||
OUT / f"{phase}-{name}-render.log", config["stamp"], env)
|
||||
jpeg = ["ffmpeg", "-nostdin", "-hide_banner", "-loglevel", "error", "-y",
|
||||
"-i", png, "-frames:v", "1", "-q:v", "4", jpg]
|
||||
subprocess.run([*LIMIT, "timeout", "120s", *map(str, jpeg)], cwd=ROOT,
|
||||
env=env, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT,
|
||||
timeout=125)
|
||||
check = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
|
||||
quality, jpg], cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=125)
|
||||
quality_ok = check.returncode == 0 and "status=PASS" in check.stdout
|
||||
quality_result = (check.stdout.strip().splitlines()[-1]
|
||||
if check.stdout.strip() else f"quality command exit={check.returncode}")
|
||||
if not quality_ok:
|
||||
quality_failures.append(f"{phase}-{name}")
|
||||
manifest.append({"phase": phase, "view": name, "file": str(jpg.relative_to(ROOT)),
|
||||
"bytes": jpg.stat().st_size, "camera_m": camera, "target_m": target,
|
||||
"yaw_deg": yaw, "pitch_deg": pitch, "fov_deg": fov,
|
||||
"note": view["note"],
|
||||
"quality_status": "PASS" if quality_ok else "FAIL",
|
||||
"quality": quality_result})
|
||||
print(f"[L139Capture] phase={phase} view={name} file={jpg.relative_to(ROOT)} "
|
||||
f"bytes={jpg.stat().st_size} quality={'PASS' if quality_ok else 'FAIL'}",
|
||||
flush=True)
|
||||
overview = ARTIFACTS / "after-whole-layer-side-elevation.svg"
|
||||
write_overview(layout, overview)
|
||||
manifest.append({"phase": "after", "view": "whole-layer-side-elevation",
|
||||
"file": str(overview.relative_to(ROOT)), "shafts": sum(
|
||||
room.get("shape_kind") == 1 for room in layout["cavern_rooms"]),
|
||||
"walk_route_edges": len([edge for edge in layout["cavern_edges"]
|
||||
if edge["stable_id"].startswith("vertical-shafts/strate-3/route-edge-")]),
|
||||
"note": "resolved-plan side projection; not a density-mesh capture"})
|
||||
(ARTIFACTS / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
status = "PASS" if not quality_failures else "FAIL"
|
||||
print(f"[L139CaptureSummary] spot_views={len(views)} comparisons={len(views)*2} "
|
||||
f"whole_layer_projection=1 quality_failures={len(quality_failures)} "
|
||||
f"status={status}", flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as error:
|
||||
print(f"[L139CaptureFailure] {error}", file=sys.stderr, flush=True)
|
||||
raise SystemExit(1)
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare composed owner hashes and resolved graph identity at W1/W8/W12."""
|
||||
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/l139/determinism"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
|
||||
"gcc": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-gcc",
|
||||
}
|
||||
CAMERA = "-20.062500,-5.068359,-900.353516,0,0"
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing L139 determinism binary or composed owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
reference_hashes: tuple[str, ...] | None = None
|
||||
reference_layout_identity: str | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
name = f"determinism-{compiler}-w{workers}"
|
||||
log, layout = OUT / f"{name}.log", OUT / f"{name}.json"
|
||||
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=0",
|
||||
f"--camera={CAMERA}", "--stream-hash", "--force-generate", "--seed=0",
|
||||
"--recipe=surface-world", "--render-distance-chunks=0",
|
||||
"--density-dispatch-report", f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
hashes = tuple(HASH_RE.findall(content))
|
||||
current_layout = json.loads(layout.read_text(encoding="utf-8"))
|
||||
stable_identity = current_layout.get("stable_identity")
|
||||
dispatch_ok = bool(re.search(
|
||||
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", content, re.M))
|
||||
passed = (proc.returncode == 0 and stamp is not None and stamp.group(1) == STAMP
|
||||
and len(hashes) >= 4 and dispatch_ok and bool(stable_identity))
|
||||
if reference_hashes is None and passed:
|
||||
reference_hashes = hashes
|
||||
reference_layout_identity = stable_identity
|
||||
else:
|
||||
passed = (passed and hashes == reference_hashes
|
||||
and stable_identity == reference_layout_identity)
|
||||
row = {"compiler": compiler, "workers": workers, "exit": proc.returncode,
|
||||
"stamp": stamp.group(1) if stamp else None, "hash_lines": len(hashes),
|
||||
"hashes_match_first": hashes == reference_hashes,
|
||||
"layout_identity": stable_identity,
|
||||
"layout_matches_first": stable_identity == reference_layout_identity,
|
||||
"shaft_dispatch": dispatch_ok, "passed": bool(passed),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L139DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
|
||||
if not passed:
|
||||
raise SystemExit(f"composed owner determinism mismatch; see {log}")
|
||||
(OUT / "summary.json").write_text(json.dumps({"status": "PASS", "stamp": STAMP,
|
||||
"layout_identity": reference_layout_identity, "runs": rows}, indent=2) + "\n")
|
||||
print(f"[L139DeterminismSummary] runs={len(rows)} source_stamp={STAMP} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run interleaved L80 timings for L139, fa4417a, and assembler/world."""
|
||||
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/l139/timing"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": (
|
||||
ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
|
||||
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
|
||||
"fa4417a": (
|
||||
ROOT / "Saved/Linux/l138/bin/VoxelForgeCoreHeadless-l138-clang",
|
||||
"9aade4e6cb33e86c36b89440644d9ef5d440288fb6ff0af03af128291c95f93f"),
|
||||
"l139": (
|
||||
ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
|
||||
"b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"),
|
||||
}
|
||||
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.compile(r"^\[StreamPipeline\] (.*)$", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
NEAR = re.compile(
|
||||
r"^\[WorldAssemblerNearLODReady\] lods=0-2 tiles=(\d+)/(\d+) cold_ready_s=([0-9.]+)$",
|
||||
re.M)
|
||||
STAMP = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
|
||||
|
||||
def run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
binary, expected_stamp = BINARIES[version]
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
|
||||
log = OUT / f"{label}.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}", "--profile-lod-work", "--stream-scheduler-policy=3",
|
||||
]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = 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, stamp = PIPELINE.search(content), NEAR.search(content), STAMP.search(content)
|
||||
if (proc.returncode or pipeline is None or near is None or stamp is None
|
||||
or stamp.group(1) != expected_stamp):
|
||||
raise SystemExit(f"L80 timing failed or stamp mismatch: {log}; exit={proc.returncode}")
|
||||
fields = dict(FIELDS.findall(pipeline.group(1)))
|
||||
row = {
|
||||
"strate": strate, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
|
||||
"lod02_ready_s": float(near.group(3)),
|
||||
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
|
||||
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
}
|
||||
print("[L139FinalTimingRun] " + 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]
|
||||
|
||||
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, *(entry[0] for entry in BINARIES.values())]):
|
||||
raise SystemExit("missing L139 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"[L139FinalTimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
|
||||
if load_1m >= 1.5 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:
|
||||
run(strate, workers, version, "warmup", 0)
|
||||
for replicate in range(1, 6):
|
||||
order = tuple(BINARIES)
|
||||
order = order[replicate % 3:] + order[:replicate % 3]
|
||||
if replicate % 2 == 0:
|
||||
order = tuple(reversed(order))
|
||||
for version in order:
|
||||
rows.append(run(strate, workers, version, "measured", replicate))
|
||||
(OUT / "runs-partial.json").write_text(
|
||||
json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
||||
|
||||
measured = [row for row in rows if row["phase"] == "measured"]
|
||||
summary = []
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
group = [row for row in measured
|
||||
if row["strate"] == strate and row["workers"] == workers]
|
||||
medians = {
|
||||
version: {
|
||||
key: statistics.median(row[key] for row in group
|
||||
if row["version"] == version)
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s")
|
||||
}
|
||||
for version in BINARIES
|
||||
}
|
||||
entry = {"strate": strate, "workers": workers,
|
||||
"replicates_per_version": 5, "medians": medians}
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
for baseline in ("fa4417a", "assembler-world"):
|
||||
entry[f"l139_vs_{baseline}_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["l139"][key] / medians[baseline][key] - 1.0), 2)
|
||||
entry[f"l139_vs_fa4417a_{key}_paired_deltas_pct"] = [
|
||||
round(100.0 * (
|
||||
next(row[key] for row in group
|
||||
if row["version"] == "l139" and row["replicate"] == replicate)
|
||||
/ next(row[key] for row in group
|
||||
if row["version"] == "fa4417a" and row["replicate"] == replicate)
|
||||
- 1.0), 3) for replicate in range(1, 6)]
|
||||
entry[f"l139_vs_assembler-world_{key}_paired_deltas_pct"] = [
|
||||
round(100.0 * (
|
||||
next(row[key] for row in group
|
||||
if row["version"] == "l139" and row["replicate"] == replicate)
|
||||
/ next(row[key] for row in group
|
||||
if row["version"] == "assembler-world"
|
||||
and row["replicate"] == replicate) - 1.0), 3)
|
||||
for replicate in range(1, 6)]
|
||||
summary.append(entry)
|
||||
(OUT / "summary.json").write_text(json.dumps({
|
||||
"a_a_noise_floor_pct": 1.0,
|
||||
"fa4417a_commit": "fa4417a",
|
||||
"timing_protocol": (
|
||||
"one warmup per binary, five interleaved runs, W8/W12, all four owner layers"),
|
||||
"runs": rows,
|
||||
"summary": summary,
|
||||
}, indent=2) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Audit the composed owner world, including the basal Vertical Shafts route."""
|
||||
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/l139/seeds"
|
||||
BIN = ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
REQUIRED = {
|
||||
"passages": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"route": re.compile(
|
||||
r"^\[ResolvedStrateRouteAudit\] strate=3 .*unsupported=0 grade_failures=0 .*status=PASS$",
|
||||
re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"shaft_dispatch": re.compile(
|
||||
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M),
|
||||
"shaft_blocked_route": re.compile(
|
||||
r"^\[VerticalShaftsBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L139 Clang binary or composed owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
partial = OUT / "runs-partial.json"
|
||||
if partial.is_file():
|
||||
for prior in json.loads(partial.read_text(encoding="utf-8")):
|
||||
seed = len(results)
|
||||
if prior.get("seed") != seed:
|
||||
break
|
||||
log = ROOT / prior.get("log", "")
|
||||
layout = ROOT / prior.get("layout", "")
|
||||
if not log.is_file() or not layout.is_file():
|
||||
break
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content))
|
||||
for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
|
||||
checks["blocked_passage_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
|
||||
checks["passage_count"] = len(re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*walkability=PASS$", content, re.M)) == 3
|
||||
passed = prior.get("exit") == 0 and all(checks.values())
|
||||
if checks != prior.get("checks") or passed != prior.get("passed"):
|
||||
break
|
||||
results.append(prior)
|
||||
print(f"[L139SeedSweepResume] reused={len(results)} source_stamp={STAMP}",
|
||||
flush=True)
|
||||
for seed in range(len(results), 16):
|
||||
log = OUT / f"seed{seed}.log"
|
||||
layout = OUT / f"seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}",
|
||||
"--recipe=surface-world", "--density-dispatch-report",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
|
||||
checks["blocked_passage_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
|
||||
checks["passage_count"] = len(re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*walkability=PASS$", content, re.M)) == 3
|
||||
passed = proc.returncode == 0 and all(checks.values())
|
||||
row = {"seed": seed, "exit": proc.returncode, "passed": passed,
|
||||
"checks": checks, "elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L139SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
partial.write_text(json.dumps(results, indent=2) + "\n")
|
||||
passed_count = sum(row["passed"] for row in results)
|
||||
failed_count = len(results) - passed_count
|
||||
summary = {"source_stamp": STAMP, "seeds": list(range(len(results))),
|
||||
"passed": passed_count, "failed": failed_count, "results": results}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
print(f"[L139SeedSweepSummary] passed={passed_count}/16 failed={failed_count} "
|
||||
f"status={'PASS' if len(results)==16 and failed_count==0 else 'FAIL'}")
|
||||
return 0 if len(results) == 16 and failed_count == 0 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render a coarse whole-strate cutaway from the L139 density mesh."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BIN = ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang"
|
||||
RENDERER = ROOT / "Saved/Linux/l139/bin/VoxelForgeOffscreenCapture-l139-clang"
|
||||
OUT = ROOT / "Saved/Linux/l139/captures"
|
||||
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux139/captures"
|
||||
STAMP = "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
SLICE_Y = -5.0
|
||||
CAMERA = (-450.0, SLICE_Y - 100.0, -950.0)
|
||||
YAW, PITCH, FOV = 0.0, 0.0, 70.0
|
||||
|
||||
|
||||
def stamped_run(label: str, command: list[str], log: Path, env: dict[str, str]) -> str:
|
||||
probe = subprocess.run([*LIMIT, "timeout", "15s", str(command[0]), "--print-source-stamp"],
|
||||
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=20)
|
||||
match = STAMP_RE.search(probe.stdout or "")
|
||||
if probe.returncode != 0 or not match or match.group(1) != STAMP:
|
||||
raise RuntimeError(f"{label}: source stamp mismatch")
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
|
||||
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=1745, check=False)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
emitted = STAMP_RE.search(content)
|
||||
if result.returncode != 0 or not emitted or emitted.group(1) != STAMP:
|
||||
raise RuntimeError(f"{label}: process failed or source changed; see {log}")
|
||||
print(f"[L139WholeLayerCapture] step={label} elapsed_s={time.monotonic()-started:.1f} "
|
||||
f"log={log.relative_to(ROOT)}", flush=True)
|
||||
return content
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not DATA.is_file() or not BIN.is_file() or not RENDERER.is_file():
|
||||
raise SystemExit("missing owner fixture or final L139 capture binaries")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
ARTIFACTS.mkdir(parents=True, exist_ok=True)
|
||||
env = os.environ.copy()
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
|
||||
stem = "whole-layer-density-cutaway"
|
||||
mesh = OUT / f"{stem}.vfmesh"
|
||||
layout = OUT / f"{stem}-layout.json"
|
||||
camera_text = ",".join(f"{value:.6f}" for value in (*CAMERA, YAW, PITCH))
|
||||
export = [BIN, DATA, "--world-assembler-prototype", "--stream-benchmark",
|
||||
"--stream-pipeline", "--workers=12", "--clip-radius=3", "--max-clip-level=8",
|
||||
"--max-level=8", "--force-generate", "--seed=0", "--recipe=surface-world",
|
||||
"--render-distance-chunks=0", "--capture-skip-walkability", "--density-dispatch-report",
|
||||
f"--camera={camera_text}", f"--resolved-layout-output={layout}", f"--mesh-export={mesh}"]
|
||||
content = stamped_run("export", export, OUT / f"{stem}-export.log", env)
|
||||
dispatch = re.search(
|
||||
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", content, re.M)
|
||||
if not dispatch or not mesh.is_file() or mesh.stat().st_size == 0:
|
||||
raise RuntimeError("coarse density export is empty or lacks native shaft dispatch")
|
||||
png = OUT / f"{stem}.png"
|
||||
jpg = ARTIFACTS / "after-whole-layer-density-cutaway.jpg"
|
||||
render = [RENDERER, mesh, png, "--size=2200x320", f"--fov-deg={FOV:.1f}",
|
||||
"--camera-m=" + ",".join(f"{value:.6f}" for value in CAMERA),
|
||||
f"--yaw-deg={YAW:.6f}", f"--pitch-deg={PITCH:.6f}",
|
||||
f"--clip-plane=0,-1,0,{SLICE_Y:.6f}", "--tile-skirts=off", "--shade=readable",
|
||||
"--headlamp=on", "--height-tint=off", "--height-center-m=-950",
|
||||
"--height-range-m=80", "--tile-edges=off"]
|
||||
stamped_run("render", render, OUT / f"{stem}-render.log", env)
|
||||
subprocess.run([*LIMIT, "timeout", "120s", "ffmpeg", "-nostdin", "-hide_banner",
|
||||
"-loglevel", "error", "-y", "-i", png, "-frames:v", "1", "-q:v", "3", jpg],
|
||||
cwd=ROOT, env=env, check=True, stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT, timeout=125)
|
||||
quality = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
|
||||
ROOT / "Standalone/Tools/verify/capture_quality.py", jpg], cwd=ROOT, env=env,
|
||||
text=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
check=False, timeout=125)
|
||||
quality_line = quality.stdout.strip().splitlines()[-1] if quality.stdout.strip() else ""
|
||||
ok = quality.returncode == 0 and "status=PASS" in quality_line
|
||||
manifest_path = ARTIFACTS / "manifest.json"
|
||||
manifest = json.loads(manifest_path.read_text(encoding="utf-8")) if manifest_path.is_file() else []
|
||||
manifest = [row for row in manifest if row.get("view") != "whole-layer-density-cutaway"]
|
||||
manifest.append({"phase": "after", "view": "whole-layer-density-cutaway",
|
||||
"file": str(jpg.relative_to(ROOT)), "bytes": jpg.stat().st_size,
|
||||
"camera_m": CAMERA, "target_m": (-450.0, SLICE_Y, -950.0),
|
||||
"yaw_deg": YAW, "pitch_deg": PITCH, "fov_deg": FOV,
|
||||
"clip_plane": [0.0, -1.0, 0.0, SLICE_Y],
|
||||
"note": "outside side cutaway through the coarse LOD density mesh across the whole Vertical Shafts strate at y=-5 m",
|
||||
"quality_status": "PASS" if ok else "FAIL", "quality": quality_line})
|
||||
manifest_path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L139WholeLayerCaptureSummary] file={jpg.relative_to(ROOT)} "
|
||||
f"quality={'PASS' if ok else 'FAIL'} {quality_line}", flush=True)
|
||||
return 0 if ok else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1800" height="760" viewBox="0 0 1800 760">
|
||||
<rect width="1800" height="760" fill="#07111a"/>
|
||||
<text x="72" y="42" fill="#edf6fb" font-family="sans-serif" font-size="24">Vertical Shafts — whole-layer side elevation</text>
|
||||
<text x="72" y="64" fill="#9bb5c5" font-family="sans-serif" font-size="13">Resolved plan • seed 0 • strate 3 • vertical axis and route shown in metres • Y projected out</text>
|
||||
<line x1="72.0" y1="-1440.6" x2="1728.0" y2="-1440.6" stroke="#315060" stroke-dasharray="5 8"/>
|
||||
<rect x="1707.84" y="284.18" width="4.65" height="191.65" rx="2.33" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1667.56" y="288.45" width="5.72" height="187.37" rx="2.86" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1245.04" y="344.01" width="6.24" height="131.81" rx="3.12" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1206.94" y="349.61" width="5.68" height="126.22" rx="2.84" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1188.40" y="352.41" width="4.37" height="123.41" rx="2.18" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1168.07" y="355.22" width="6.64" height="120.60" rx="3.32" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1130.81" y="360.85" width="4.40" height="114.97" rx="2.20" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1092.31" y="366.48" width="4.62" height="109.35" rx="2.31" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1072.90" y="369.29" width="5.04" height="106.54" rx="2.52" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1053.43" y="372.09" width="5.61" height="103.74" rx="2.80" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1034.94" y="374.88" width="4.20" height="100.94" rx="2.10" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1015.20" y="377.67" width="5.28" height="98.16" rx="2.64" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1648.28" y="290.59" width="5.89" height="185.23" rx="2.95" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="995.75" y="380.44" width="5.81" height="95.38" rx="2.90" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="976.77" y="383.20" width="5.37" height="92.62" rx="2.68" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="938.19" y="388.67" width="5.75" height="87.15" rx="2.88" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="918.75" y="391.38" width="6.25" height="84.44" rx="3.13" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="896.89" y="394.44" width="6.22" height="81.38" rx="3.11" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="880.62" y="396.74" width="5.74" height="79.09" rx="2.87" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="862.36" y="399.38" width="3.87" height="76.45" rx="1.94" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="842.60" y="401.99" width="5.01" height="73.83" rx="2.50" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="804.64" y="407.14" width="4.16" height="68.69" rx="2.08" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="785.14" y="409.66" width="4.77" height="66.16" rx="2.39" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1629.77" y="292.79" width="4.53" height="183.04" rx="2.26" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="765.24" y="412.16" width="6.18" height="63.67" rx="3.09" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="746.73" y="414.61" width="4.82" height="61.21" rx="2.41" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="727.40" y="417.04" width="5.08" height="58.79" rx="2.54" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="707.91" y="419.42" width="5.68" height="56.41" rx="2.84" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="669.16" y="424.06" width="6.41" height="51.77" rx="3.20" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="650.97" y="426.31" width="4.40" height="49.51" rx="2.20" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="593.65" y="432.79" width="3.89" height="43.04" rx="1.94" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="573.84" y="434.85" width="5.11" height="40.98" rx="2.56" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="478.04" y="444.31" width="4.78" height="31.52" rx="2.39" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="440.09" y="447.66" width="3.90" height="28.16" rx="1.95" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="1590.94" y="297.31" width="5.40" height="178.52" rx="2.70" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rect x="420.88" y="449.24" width="3.93" height="26.58" rx="1.97" fill="#93dded" fill-opacity="0.45" stroke="#42b8d1" stroke-width="1.2"/><rLine truncated
|
||||
<path d="M1728.00,290.01 L1716.48,291.19 L1716.48,291.19 L1710.17,291.85 L1710.17,291.85 L1689.61,294.04 L1689.61,294.04 L1670.42,296.13 L1670.42,296.13 L1651.23,298.27 L1651.23,298.27 L1632.03,300.46 L1632.03,300.46 L1612.84,302.70 L1612.84,302.70 L1593.65,304.98 L1593.65,304.98 L1574.45,307.31 L1574.45,307.31 L1555.26,309.68 L1555.26,309.68 L1536.07,312.09 L1536.07,312.09 L1516.87,314.53 L1516.87,314.53 L1497.68,317.01 L1497.68,317.01 L1478.49,319.53 L1478.49,319.53 L1459.29,322.07 L1459.29,322.07 L1440.10,324.65 L1440.10,324.65 L1420.91,327.26 L1420.91,327.26 L1401.71,329.89 L1401.71,329.89 L1382.52,332.55 L1382.52,332.55 L1363.33,335.23 L1363.33,335.23 L1344.13,337.93 L1344.13,337.93 L1324.94,340.65 L1324.94,340.65 L1305.74,343.39 L1305.74,343.39 L1286.55,346.14 L1286.55,346.14 L1267.36,348.91 L1267.36,348.91 L1248.16,351.69 L1248.16,351.69 L1228.97,354.48 L1228.97,354.48 L1209.78,357.28 L1209.78,357.28 L1190.58,360.09 L1190.58,360.09 L1171.39,362.90 L1171.39,362.90 L1152.20,365.71 L1152.20,365.71 L1133.00,368.53 L1133.00,368.53 L1113.81,371.34 L1113.81,371.34 L1094.62,374.16 L1094.62,374.16 L1075.42,376.97 L1075.42,376.97 L1056.23,379.77 L1056.23,379.77 L1037.04,382.56 L1037.04,382.56 L1017.84,385.35 L1017.84,385.35 L998.65,388.12 L998.65,388.12 L979.46,390.88 L979.46,390.88 L960.26,393.62 L960.26,393.62 L941.07,396.35 L941.07,396.35 L921.88,399.06 L921.88,399.06 L902.68,401.75 L902.68,401.75 L900.00,402.12 L900.00,402.12 L883.49,404.41 L883.49,404.41 L864.30,407.05 L864.30,407.05 L845.10,409.67 L845.10,409.67 L825.91,412.26 L825.91,412.26 L806.72,414.82 L806.72,414.82 L787.52,417.34 L787.52,417.34 L768.33,419.83 L768.33,419.83 L749.14,422.29 L749.14,422.29 L729.94,424.71 L729.94,424.71 L710.75,427.09 L710.75,427.09 L691.56,429.44 L691.56,429.44 L672.36,431.73 L672.36,431.73 L653.17,433.99 L653.17,433.99 L633.98,436.20 L633.98,436.20 L614.78,438.36 L614.78,438.36 L595.59,440.47 L595.59,440.47 L576.39,442.53 L576.39,442.53 L557.20,444.53 L557.20,444.53 L538.01,446.48 L538.01,446.48 L518.81,448.37 L518.81,448.37 L499.62,450.21 L499.62,450.21 L480.43,451.98 L480.43,451.98 L461.23,453.69 L461.23,453.69 L442.04,455.34 L442.04,455.34 L422.85,456.92 L422.85,456.92 L403.65,458.43 L403.65,458.43 L384.46,459.88 L384.46,459.88 L365.27,461.25 L365.27,461.25 L346.07,462.55 L346.07,462.55 L326.88,463.77 L326.88,463.77 L307.69,464.91 L307.69,464.91 L288.49,465.98 L288.49,465.98 L269.30,466.97 L269.30,466.97 L250.11,467.87 L250.11,467.87 L230.91,468.69 L230.91,468.69 L211.72,469.42 L211.72,469.42 L192.53,470.06 L192.53,470.06 L173.33,470.62 L173.33,470.62 L154.14,471.08 L154.14,471.08 L134.95,471.45 L134.95,471.45 L115.75,471.73 L115.75,471.73 L96.56,471.90 L96.56,471.90 L89.83,471.94 L89.83,471.94 L83.52,471.97 L83.52,471.97 L77.37,471.98 L77.37,471.98 L72.00,471.99" fill="none" stroke="#f4bf68" stroke-width="3.0" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<text x="82" y="716" fill="#f4bf68" font-family="sans-serif" font-size="15">walk-only graded spine and landings</text>
|
||||
<rect x="416" y="704" width="15" height="15" fill="#93dded" fill-opacity="0.65" stroke="#42b8d1"/>
|
||||
<text x="440" y="716" fill="#b8dce5" font-family="sans-serif" font-size="15">open vertical shaft primitives (53)</text>
|
||||
<text x="1718" y="716" fill="#9bb5c5" text-anchor="end" font-family="sans-serif" font-size="12">Plan projection, not a density mesh</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,258 @@
|
||||
[
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "arrival-mouth",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-arrival-mouth.jpg",
|
||||
"bytes": 21788,
|
||||
"camera_m": [
|
||||
-20.0625,
|
||||
-5.068359,
|
||||
-900.237499
|
||||
],
|
||||
"target_m": [
|
||||
-30.0625,
|
||||
-5.068359,
|
||||
-900.237499
|
||||
],
|
||||
"yaw_deg": -90.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye at the passage arrival, looking into the connected layer",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-arrival-mouth.jpg pixels=921600 mean_luma=110.102 stddev_luma=3.380 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "landing-at-shaft-access",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-landing-at-shaft-access.jpg",
|
||||
"bytes": 15114,
|
||||
"camera_m": [
|
||||
-31.354577,
|
||||
-5.068359,
|
||||
-901.1982009999999
|
||||
],
|
||||
"target_m": [
|
||||
-31.354577,
|
||||
-9.889916,
|
||||
-901.1982009999999
|
||||
],
|
||||
"yaw_deg": 180.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "walkable route landing and short shaft access tunnel",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-landing-at-shaft-access.jpg pixels=921600 mean_luma=110.563 stddev_luma=3.056 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "looking-down-shaft",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-down-shaft.jpg",
|
||||
"bytes": 11005,
|
||||
"camera_m": [
|
||||
-31.354577,
|
||||
-9.889916,
|
||||
-901.1982009999999
|
||||
],
|
||||
"target_m": [
|
||||
-31.354577,
|
||||
-10.852462,
|
||||
-928.6482010000001
|
||||
],
|
||||
"yaw_deg": 180.0,
|
||||
"pitch_deg": -87.99172181188044,
|
||||
"fov_deg": 35.0,
|
||||
"note": "player eye at the upper access landing, looking down the open shaft",
|
||||
"quality_status": "FAIL",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-down-shaft.jpg pixels=921600 mean_luma=112.000 stddev_luma=0.000 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "looking-up-from-ledge",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-up-from-ledge.jpg",
|
||||
"bytes": 15507,
|
||||
"camera_m": [
|
||||
-31.354577,
|
||||
-10.852462,
|
||||
-945.198201
|
||||
],
|
||||
"target_m": [
|
||||
-31.354577,
|
||||
-10.852462,
|
||||
-900.6482010000001
|
||||
],
|
||||
"yaw_deg": 0.0,
|
||||
"pitch_deg": 90.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye at an internal ledge, looking up; reaching this ledge is a climb tier",
|
||||
"quality_status": "FAIL",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-looking-up-from-ledge.jpg pixels=921600 mean_luma=111.693 stddev_luma=1.515 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=FAIL"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "basal-terminal-landing",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/before-basal-terminal-landing.jpg",
|
||||
"bytes": 13933,
|
||||
"camera_m": [
|
||||
-884.858259,
|
||||
-5.068359,
|
||||
-995.05
|
||||
],
|
||||
"target_m": [
|
||||
-892.858259,
|
||||
-5.068359,
|
||||
-995.05
|
||||
],
|
||||
"yaw_deg": -90.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "walkable terminal landing above the basal seal",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/before-basal-terminal-landing.jpg pixels=921600 mean_luma=108.996 stddev_luma=6.932 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "arrival-mouth",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-arrival-mouth.jpg",
|
||||
"bytes": 19044,
|
||||
"camera_m": [
|
||||
-20.0625,
|
||||
-5.068359,
|
||||
-900.237499
|
||||
],
|
||||
"target_m": [
|
||||
-30.0625,
|
||||
-5.068359,
|
||||
-900.237499
|
||||
],
|
||||
"yaw_deg": -90.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye at the passage arrival, looking into the connected layer",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-arrival-mouth.jpg pixels=921600 mean_luma=125.004 stddev_luma=16.734 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "landing-at-shaft-access",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-landing-at-shaft-access.jpg",
|
||||
"bytes": 15595,
|
||||
"camera_m": [
|
||||
-31.354577,
|
||||
-5.068359,
|
||||
-901.1982009999999
|
||||
],
|
||||
"target_m": [
|
||||
-31.354577,
|
||||
-9.889916,
|
||||
-901.1982009999999
|
||||
],
|
||||
"yaw_deg": 180.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "walkable route landing and short shaft access tunnel",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-landing-at-shaft-access.jpg pixels=921600 mean_luma=117.622 stddev_luma=14.149 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "looking-down-shaft",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-down-shaft.jpg",
|
||||
"bytes": 24396,
|
||||
"camera_m": [
|
||||
-31.354577,
|
||||
-9.889916,
|
||||
-901.1982009999999
|
||||
],
|
||||
"target_m": [
|
||||
-31.354577,
|
||||
-10.852462,
|
||||
-928.6482010000001
|
||||
],
|
||||
"yaw_deg": 180.0,
|
||||
"pitch_deg": -87.99172181188044,
|
||||
"fov_deg": 35.0,
|
||||
"note": "player eye at the upper access landing, looking down the open shaft",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-down-shaft.jpg pixels=921600 mean_luma=112.365 stddev_luma=3.113 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "looking-up-from-ledge",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-up-from-ledge.jpg",
|
||||
"bytes": 23172,
|
||||
"camera_m": [
|
||||
-31.354577,
|
||||
-10.852462,
|
||||
-945.198201
|
||||
],
|
||||
"target_m": [
|
||||
-31.354577,
|
||||
-10.852462,
|
||||
-900.6482010000001
|
||||
],
|
||||
"yaw_deg": 0.0,
|
||||
"pitch_deg": 90.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye at an internal ledge, looking up; reaching this ledge is a climb tier",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-looking-up-from-ledge.jpg pixels=921600 mean_luma=111.549 stddev_luma=2.538 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "basal-terminal-landing",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-basal-terminal-landing.jpg",
|
||||
"bytes": 12527,
|
||||
"camera_m": [
|
||||
-884.858259,
|
||||
-5.068359,
|
||||
-995.05
|
||||
],
|
||||
"target_m": [
|
||||
-892.858259,
|
||||
-5.068359,
|
||||
-995.05
|
||||
],
|
||||
"yaw_deg": -90.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "walkable terminal landing above the basal seal",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-basal-terminal-landing.jpg pixels=921600 mean_luma=125.238 stddev_luma=3.555 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "whole-layer-side-elevation",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-whole-layer-side-elevation.svg",
|
||||
"shafts": 53,
|
||||
"walk_route_edges": 91,
|
||||
"note": "resolved-plan side projection; not a density-mesh capture"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "whole-layer-density-cutaway",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux139/captures/after-whole-layer-density-cutaway.jpg",
|
||||
"bytes": 20280,
|
||||
"camera_m": [
|
||||
-450.0,
|
||||
-105.0,
|
||||
-950.0
|
||||
],
|
||||
"target_m": [
|
||||
-450.0,
|
||||
-5.0,
|
||||
-950.0
|
||||
],
|
||||
"yaw_deg": 0.0,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 70.0,
|
||||
"clip_plane": [
|
||||
0.0,
|
||||
-1.0,
|
||||
0.0,
|
||||
-5.0
|
||||
],
|
||||
"note": "outside side cutaway through the coarse LOD density mesh across the whole Vertical Shafts strate at the denser shaft row",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux139/captures/after-whole-layer-density-cutaway.jpg pixels=704000 mean_luma=113.286 stddev_luma=3.364 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,34 @@
|
||||
# L140 — Floating Islands planned around passages
|
||||
|
||||
**Not accepted: 0/16 composed seeds passed.** The new Core recipe reaches native S1 dispatch and meets its dedicated route, passage-clearance, and mouth-support checks on seeds 0 and 10. Seed 10 still has one blocked capsule in the composed owner route; both seeds fail the unchanged passage-ripple gate. Fourteen seeds cannot produce a corridor-aware plan.
|
||||
|
||||
## Change
|
||||
|
||||
Core evaluator version is 239 and Floating Islands geometry version is 10. The owner adapter resolves the fixed passage layout first, then plans nine native islands and a walk route around the 0–1 arrival and 1–2 departure tubes. The plan records route and island anchors in the resolved layout. The clearance check reserves the 1.053 m passage influence reach plus the 0.880 m capsule half-height and checks five support probes at each mouth. Shape composition preserves native landing support and passage shell distance for this recipe. Direct legacy recipe fixtures retain their earlier attach-before-layout order.
|
||||
|
||||
S1 binds to `FloatingIslands-native` in the explicit Core owner mode. `bUseVoxelForgeCore` remains opt-in; the fast-tier default owner pose still reports the legacy analytic S1 path.
|
||||
|
||||
## Composed sweep
|
||||
|
||||
The final-source Clang run covered seeds 0–15. Results and per-seed logs are in [`summary.json`](../../../../Saved/Linux/l140/seeds/summary.json) and `Saved/Linux/l140/seeds/` (source stamp `6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da`).
|
||||
|
||||
- **Seed 0:** the planner tried 11 detours; its 94.5 m route has 1.47 m conservative tube clearance. Native route audit: 128 points, 0 blocked capsule samples, 0 unsupported points, 0 grade failures. Passage check: 5,325 tube samples, 0 island conflicts, 676,275 route checks, 0 route conflicts, arrival and departure support 5/5, 2.824 m minimum excess clearance. The composed S1 route and seams pass. Ripple is 1/3: wall ripple is 6.85 cm on 0–1 and 7.10 cm on 1–2 against the unchanged 3.50 cm limit.
|
||||
- **Seed 10:** the native route and passage checks pass, with 5/5 mouth support and 0 isolated route conflicts. The composed S1 capsule audit finds one blocked sample at `(-6.246, -28.615, -737.982) m`, inside the resolved 0–1 passage; composed density is `-0.002062` while native island density is `+1.730469`. Ripple is again 1/3 (6.22 cm on 0–1 and 7.19 cm on 1–2). Seams and negative controls pass.
|
||||
- **Seeds 1, 2, 7, 8, 9, 12, 13, 15:** no candidate clears the tubes; best conservative clearances range from −5.994 m to −0.406 m. **Seeds 3, 5, 6, 14:** candidates clear the tubes but the supported island profile cannot reach both fixed mouth heights. **Seeds 4 and 11:** candidate islands escape the sealed vertical layout.
|
||||
|
||||
The dedicated gate is therefore 0/16. No geometry variant was attempted after these results. The repeated 0–1/1–2 ripple failure is also consistent with L67's closed authored-radius experiment: it measured 7.27 cm on 1–2 against the same 3.50 cm limit ([L67 report](../../Linux67/REPORT.md)).
|
||||
|
||||
## Other checks
|
||||
|
||||
- **Fast tier:** PASS, 287.8 s; negative controls and existing worker-hash checks passed. It continues to verify the default legacy S1 dispatch.
|
||||
- **Determinism:** PASS, six runs (Clang/GCC × W1/W8/W12). All produced the same 512-tile density, mesh, payload, topology, material, and exact-query hashes, with layout identity `resolved-layout-v3-fnv64:08da20d55ff6820f`. Native recipe hash: `0b6a23bc73a3b867`. See the [baseline](baselines/native-owner-fi-seed0.json) and [`summary.json`](../../../../Saved/Linux/l140/determinism/summary.json).
|
||||
- **Captures:** ten before/after JPEGs passed the existing image-quality gate. The [manifest](captures/manifest.json) records eye-height arrival, island edge, route between islands, departure, and wide silhouette views for both versions. See [before](captures/before-arrival-mouth.jpg), [before edge](captures/before-island-edge.jpg), [before route](captures/before-walk-between-islands.jpg), [before departure](captures/before-departure-mouth.jpg), [before wide](captures/before-wide-silhouette.jpg); [after](captures/after-arrival-mouth.jpg), [after edge](captures/after-island-edge.jpg), [after route](captures/after-walk-between-islands.jpg), [after departure](captures/after-departure-mouth.jpg), [after wide](captures/after-wide-silhouette.jpg).
|
||||
- **Linux VFHostEditor:** PASS in 73.58 s. Build log: [`ubt.log`](../../../../Saved/Linux/l140/ubt.log).
|
||||
- **L80 timing:** not completed. The machine gate passed (load 0.97, 2-second CPU idle 98.5%), but the candidate warm-up could not construct native S1 around the checked-in Unreal-exported passage layout: 0 detours, best clearance −3.509 m. No measured L80 pairs were recorded; the ±1% A/A floor remains the L137 reference. A separate 30-second setup probe with a regenerated seed-0 owner plan timed out during passage-route planning, so I did not run a mismatched or incomplete timing matrix.
|
||||
- `git diff --check`: PASS.
|
||||
|
||||
## Remaining issues
|
||||
|
||||
The fixed exported owner layout and many generated seeds leave no route-clear island arrangement. On seed 10, the native-only clearance check also misses a collision that the final composed owner route catches. Ripple remains above the existing limit on both upper passages. Native S1 is still opt-in and is not promoted by this round.
|
||||
|
||||
Changed Unreal source: [`VoxelForgeCoreAdapter.cpp`](../../../../Source/VoxelForge/Private/VoxelForgeCoreAdapter.cpp). Windows compilation remains for the owner.
|
||||
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture comparable player-height and wide views of owner S1 before and after L140."""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import argparse
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
LAYOUT = ROOT / "Saved/Linux/l140/seeds/seed0-composed-layout.json"
|
||||
OUT = ROOT / "Saved/Linux/l140/captures"
|
||||
ARTIFACTS = ROOT / "Standalone/Research/WorldAssemblerM3/Linux140/captures"
|
||||
LIMIT = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--"]
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
|
||||
VERSIONS = {
|
||||
"before-head": {
|
||||
"headless": ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
|
||||
"renderer": ROOT / "Saved/Linux/l139/bin/VoxelForgeOffscreenCapture-l139-clang",
|
||||
"stamp": "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1",
|
||||
"native": False,
|
||||
},
|
||||
"after-l140": {
|
||||
"headless": ROOT / "Saved/Linux/l140/bin/VoxelForgeCoreHeadless-l140-clang",
|
||||
"renderer": ROOT / "Saved/Linux/l140/bin/VoxelForgeOffscreenCapture-l140-clang",
|
||||
"stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"native": True,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def fmt(values: tuple[float, ...]) -> str:
|
||||
return ",".join(f"{value:.6f}" for value in values)
|
||||
|
||||
|
||||
def angles(camera: tuple[float, float, float], target: tuple[float, float, float]) -> tuple[float, float]:
|
||||
dx, dy, dz = (target[i] - camera[i] for i in range(3))
|
||||
return math.degrees(math.atan2(dx, dy)), math.degrees(math.atan2(dz, math.hypot(dx, dy)))
|
||||
|
||||
|
||||
def stamped_run(name: str, command: list[str], log: Path, expected: str,
|
||||
env: dict[str, str]) -> str:
|
||||
binary = Path(command[0])
|
||||
probe = subprocess.run([*LIMIT, "timeout", "15s", str(binary), "--print-source-stamp"],
|
||||
cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=20)
|
||||
match = STAMP_RE.search(probe.stdout or "")
|
||||
if probe.returncode != 0 or not match or match.group(1) != expected:
|
||||
raise RuntimeError(f"{name}: executable stamp mismatch for {binary}")
|
||||
log.parent.mkdir(parents=True, exist_ok=True)
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run([*LIMIT, "timeout", "1740s", *map(str, command)],
|
||||
cwd=ROOT, env=env, stdout=stream, stderr=subprocess.STDOUT,
|
||||
timeout=1745, check=False)
|
||||
output = log.read_text(encoding="utf-8", errors="replace")
|
||||
emitted = STAMP_RE.search(output)
|
||||
if result.returncode != 0 or not emitted or emitted.group(1) != expected:
|
||||
raise RuntimeError(f"{name}: process failed or source changed; see {log}")
|
||||
print(f"[L140CaptureRun] step={name} elapsed_s={time.monotonic()-started:.1f} "
|
||||
f"log={log.relative_to(ROOT)}", flush=True)
|
||||
return output
|
||||
|
||||
|
||||
def qpoint(values: list[int], scale_m: float) -> tuple[float, float, float]:
|
||||
return tuple(value * scale_m for value in values[:3])
|
||||
|
||||
|
||||
def make_views(layout: dict) -> list[dict[str, object]]:
|
||||
scale_m = layout["core_voxel_size_um"] / 1_000_000.0 / 256.0
|
||||
strate = next(row for row in layout["strates"] if row["strate_index"] == 1)
|
||||
anchors = strate["anchors"]
|
||||
route = [qpoint(row["values"], scale_m) for row in anchors if row["kind"] == "route_point"]
|
||||
islands = [row for row in anchors if row["kind"] == "island_anchor"]
|
||||
if len(route) < 8 or len(islands) < 5:
|
||||
raise ValueError("seed-0 resolved layout lacks a complete S1 route or island chain")
|
||||
|
||||
def tangent(index: int) -> tuple[float, float]:
|
||||
a, b = route[max(0, index - 1)], route[min(len(route) - 1, index + 1)]
|
||||
dx, dy = b[0] - a[0], b[1] - a[1]
|
||||
length = max(1e-9, math.hypot(dx, dy))
|
||||
return dx / length, dy / length
|
||||
|
||||
def eye(point: tuple[float, float, float]) -> tuple[float, float, float]:
|
||||
return point[0], point[1], point[2] + 1.45
|
||||
|
||||
i = len(route) // 3
|
||||
tx, ty = tangent(i)
|
||||
arrival_t = tangent(0)
|
||||
departure_t = tangent(len(route) - 1)
|
||||
arrival, departure = route[0], route[-1]
|
||||
arrival_camera = eye((arrival[0] - 2.5 * arrival_t[0], arrival[1] - 2.5 * arrival_t[1], arrival[2]))
|
||||
arrival_target = eye((arrival[0] + 8.0 * arrival_t[0], arrival[1] + 8.0 * arrival_t[1], arrival[2]))
|
||||
departure_camera = eye((departure[0] - 2.5 * departure_t[0], departure[1] - 2.5 * departure_t[1], departure[2]))
|
||||
departure_target = eye((departure[0] + 8.0 * departure_t[0], departure[1] + 8.0 * departure_t[1], departure[2]))
|
||||
edge_point = route[i]
|
||||
edge_camera = eye((edge_point[0] + 6.0 * ty, edge_point[1] - 6.0 * tx, edge_point[2]))
|
||||
edge_target = (edge_point[0] + 4.0 * tx, edge_point[1] + 4.0 * ty, edge_point[2] + 0.35)
|
||||
centers = [qpoint(row["values"], scale_m) for row in islands]
|
||||
middle_index = len(route) // 2
|
||||
midpoint = route[middle_index]
|
||||
mid_t = tangent(middle_index)
|
||||
walk_camera = eye((midpoint[0] - 2.0 * mid_t[0], midpoint[1] - 2.0 * mid_t[1], midpoint[2]))
|
||||
next_island_index = min(len(centers) - 1, 5)
|
||||
next_island_top = centers[next_island_index][2] + islands[next_island_index]["values"][4] * scale_m
|
||||
walk_target = (centers[next_island_index][0], centers[next_island_index][1], next_island_top + 0.5)
|
||||
walk_focus = tuple((centers[4][axis] + centers[5][axis]) * 0.5 for axis in range(3)) + (18.0,)
|
||||
cx = sum(p[0] for p in centers) / len(centers)
|
||||
cy = sum(p[1] for p in centers) / len(centers)
|
||||
cz = sum(p[2] + row["values"][4] * scale_m for p, row in zip(centers, islands)) / len(centers)
|
||||
wide_camera = (cx, cy + 23.0, cz + 10.0)
|
||||
wide_target = (cx, cy, cz)
|
||||
return [
|
||||
{"name": "arrival-mouth", "camera": arrival_camera, "target": arrival_target,
|
||||
"focus": (*arrival, 14.0), "note": "player eye at the fixed 0-1 arrival mouth"},
|
||||
{"name": "island-edge", "camera": edge_camera, "target": edge_target,
|
||||
"focus": (*edge_point, 18.0), "note": "player eye beside the route on an island edge"},
|
||||
{"name": "walk-between-islands", "camera": walk_camera, "target": walk_target,
|
||||
"focus": walk_focus, "note": "player eye looking from one route landing toward the next island"},
|
||||
{"name": "departure-mouth", "camera": departure_camera, "target": departure_target,
|
||||
"focus": (*departure, 14.0), "note": "player eye approaching the fixed 1-2 departure mouth"},
|
||||
{"name": "wide-silhouette", "camera": wide_camera, "target": wide_target,
|
||||
"focus": (cx, cy, cz, 48.0), "fov": 72.0,
|
||||
"note": "wide side view showing the island chain silhouette"},
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--views", nargs="*", default=None,
|
||||
help="optional view names to rerender in both phases")
|
||||
args = parser.parse_args()
|
||||
if not DATA.is_file() or not LAYOUT.is_file():
|
||||
raise SystemExit("missing owner fixture or final seed-0 resolved layout")
|
||||
layout = json.loads(LAYOUT.read_text(encoding="utf-8"))
|
||||
views = make_views(layout)
|
||||
if args.views:
|
||||
unknown = set(args.views) - {str(view["name"]) for view in views}
|
||||
if unknown:
|
||||
raise SystemExit(f"unknown capture views: {sorted(unknown)}")
|
||||
views = [view for view in views if view["name"] in set(args.views)]
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
ARTIFACTS.mkdir(parents=True, exist_ok=True)
|
||||
env = os.environ.copy()
|
||||
env["LP_NUM_THREADS"] = "12"
|
||||
env["XDG_CACHE_HOME"] = str(OUT / "xdg-cache")
|
||||
quality = ROOT / "Standalone/Tools/verify/capture_quality.py"
|
||||
manifest: list[dict[str, object]] = []
|
||||
old_manifest_path = ARTIFACTS / "manifest.json"
|
||||
existing = json.loads(old_manifest_path.read_text(encoding="utf-8")) \
|
||||
if old_manifest_path.is_file() else {"captures": []}
|
||||
rerendered = {str(view["name"]) for view in views}
|
||||
for row in existing.get("captures", []):
|
||||
if row.get("view") not in rerendered:
|
||||
manifest.append(row)
|
||||
for version, config in VERSIONS.items():
|
||||
phase = "before" if version.startswith("before") else "after"
|
||||
for view in views:
|
||||
name = str(view["name"])
|
||||
camera, target = view["camera"], view["target"]
|
||||
assert isinstance(camera, tuple) and isinstance(target, tuple)
|
||||
yaw, pitch = angles(camera, target)
|
||||
mesh_dir = OUT / "meshes" / phase
|
||||
mesh_dir.mkdir(parents=True, exist_ok=True)
|
||||
mesh = mesh_dir / f"{name}.vfmesh"
|
||||
capture_layout = mesh_dir / f"{name}-layout.json"
|
||||
flags = ["--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
"--workers=12", "--clip-radius=6", "--max-clip-level=0", "--max-level=0",
|
||||
"--force-generate", "--seed=0", "--recipe=surface-world",
|
||||
"--render-distance-chunks=0", f"--camera={fmt((*camera,yaw,pitch))}",
|
||||
f"--resolved-layout-output={capture_layout}", f"--mesh-export={mesh}"]
|
||||
if config["native"]:
|
||||
flags.extend(["--native-owner-fi", "--density-dispatch-report"])
|
||||
export = [config["headless"], DATA, *flags]
|
||||
stamped_run(f"{phase}-{name}-export", list(map(str, export)),
|
||||
OUT / f"{phase}-{name}-export.log", config["stamp"], env)
|
||||
if not mesh.is_file() or mesh.stat().st_size == 0:
|
||||
raise RuntimeError(f"{phase}-{name}: empty mesh export")
|
||||
png = OUT / f"{phase}-{name}.png"
|
||||
jpg = ARTIFACTS / f"{phase}-{name}.jpg"
|
||||
focus = view["focus"]
|
||||
assert isinstance(focus, tuple)
|
||||
fov = float(view.get("fov", 78.0))
|
||||
render = [config["renderer"], mesh, png, "--size=1280x720",
|
||||
f"--fov-deg={fov:.1f}", f"--camera-m={fmt(camera)}", f"--yaw-deg={yaw:.6f}",
|
||||
f"--pitch-deg={pitch:.6f}", "--tile-skirts=off", "--shade=readable",
|
||||
"--headlamp=on", "--height-tint=on", "--height-center-m=-740",
|
||||
"--height-range-m=70", "--tile-edges=off", f"--focus-region={fmt(focus)}"]
|
||||
stamped_run(f"{phase}-{name}-render", list(map(str, render)),
|
||||
OUT / f"{phase}-{name}-render.log", config["stamp"], env)
|
||||
subprocess.run([*LIMIT, "timeout", "120s", "ffmpeg", "-nostdin", "-hide_banner",
|
||||
"-loglevel", "error", "-y", "-i", png, "-frames:v", "1", "-q:v", "4", jpg],
|
||||
cwd=ROOT, env=env, check=True, stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.STDOUT, timeout=125)
|
||||
check = subprocess.run([*LIMIT, "timeout", "120s", sys.executable,
|
||||
quality, jpg], cwd=ROOT, env=env, text=True, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=125)
|
||||
result = check.stdout.strip().splitlines()[-1] if check.stdout.strip() else f"exit={check.returncode}"
|
||||
ok = check.returncode == 0 and "status=PASS" in check.stdout
|
||||
manifest.append({"phase": phase, "view": name, "file": str(jpg.relative_to(ROOT)),
|
||||
"bytes": jpg.stat().st_size, "camera_m": camera, "target_m": target,
|
||||
"yaw_deg": yaw, "pitch_deg": pitch, "fov_deg": fov,
|
||||
"note": view["note"], "quality_status": "PASS" if ok else "FAIL", "quality": result})
|
||||
print(f"[L140Capture] phase={phase} view={name} file={jpg.relative_to(ROOT)} "
|
||||
f"quality={'PASS' if ok else 'FAIL'} {result}", flush=True)
|
||||
manifest.sort(key=lambda row: (str(row["phase"]), str(row["view"])))
|
||||
summary = {"seed": 0, "source_stamp": VERSIONS["after-l140"]["stamp"],
|
||||
"before_stamp": VERSIONS["before-head"]["stamp"], "views_per_phase": 5,
|
||||
"all_quality_pass": all(row["quality_status"] == "PASS" for row in manifest),
|
||||
"captures": manifest}
|
||||
(ARTIFACTS / "manifest.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L140CaptureSummary] views_per_phase={len(views)} captures={len(manifest)} "
|
||||
f"all_quality_pass={summary['all_quality_pass']}", flush=True)
|
||||
return 0 if summary["all_quality_pass"] else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check native Floating Islands owner identity and hashes at W1/W8/W12."""
|
||||
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/l140/determinism"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da"
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Saved/Linux/l140/bin/VoxelForgeCoreHeadless-l140-clang",
|
||||
"gcc": ROOT / "Saved/Linux/l140/bin/VoxelForgeCoreHeadless-l140-gcc",
|
||||
}
|
||||
CAMERA = "25.836914,3.199219,-737.000000,0,0"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
NATIVE_FI = re.compile(
|
||||
r"^\[DensityDispatch\] strate=1 generator=FloatingIslands "
|
||||
r"recipe=FloatingIslands\.ClusterField version=10 "
|
||||
r"bound_path=FloatingIslands-native adapter=FFloatingIslandFieldAdapter$", re.M)
|
||||
FI_RECIPE_HASH = re.compile(r"^\[FloatingIslandWorldAssembly\].*recipe_hash=([0-9a-f]+) status=PASS$", re.M)
|
||||
FI_CLEARANCE = re.compile(r"^\[FloatingIslandPassageClearance\].*status=PASS$", re.M)
|
||||
FI_ROUTE_AUDIT = re.compile(r"^\[FloatingIslandRouteAudit\].*status=PASS$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not DATA.is_file() or any(not path.is_file() for path in BINARIES.values()):
|
||||
raise SystemExit("missing L140 Clang/GCC determinism binary or owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows: list[dict[str, object]] = []
|
||||
reference_hashes: list[str] | None = None
|
||||
reference_identity: str | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
name = f"native-owner-fi-{compiler}-w{workers}"
|
||||
log, layout = OUT / f"{name}.log", OUT / f"{name}.json"
|
||||
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=0",
|
||||
f"--camera={CAMERA}", "--stream-hash", "--force-generate", "--seed=0",
|
||||
"--recipe=surface-world", "--native-owner-fi", "--render-distance-chunks=0",
|
||||
"--density-dispatch-report", f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
hashes = HASH_RE.findall(content)
|
||||
current_layout = json.loads(layout.read_text(encoding="utf-8")) if layout.is_file() else {}
|
||||
identity = current_layout.get("stable_identity")
|
||||
native_ok = bool(NATIVE_FI.search(content))
|
||||
plan_ok = ("[FloatingIslandRouteAudit]" in content
|
||||
and "[FloatingIslandPassageClearance]" in content)
|
||||
recipe_hash = FI_RECIPE_HASH.search(content)
|
||||
clearance = FI_CLEARANCE.search(content)
|
||||
route_audit = FI_ROUTE_AUDIT.search(content)
|
||||
passed = (proc.returncode == 0 and stamp is not None and stamp.group(1) == STAMP
|
||||
and len(hashes) >= 4 and native_ok and plan_ok and recipe_hash is not None
|
||||
and clearance is not None and route_audit is not None and bool(identity))
|
||||
if reference_hashes is None and passed:
|
||||
reference_hashes, reference_identity = hashes, identity
|
||||
else:
|
||||
passed = passed and hashes == reference_hashes and identity == reference_identity
|
||||
row = {"compiler": compiler, "workers": workers, "exit": proc.returncode,
|
||||
"source_stamp": stamp.group(1) if stamp else None, "hash_lines": hashes,
|
||||
"layout_identity": identity, "native_fi_dispatch": native_ok,
|
||||
"native_plan_audits": plan_ok, "floating_island_recipe_hash":
|
||||
recipe_hash.group(1) if recipe_hash else None,
|
||||
"passage_clearance_line": clearance.group(0) if clearance else None,
|
||||
"route_audit_line": route_audit.group(0) if route_audit else None,
|
||||
"passed": bool(passed),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L140DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n")
|
||||
if not passed:
|
||||
(OUT / "summary.json").write_text(json.dumps({"status": "FAIL", "runs": rows}, indent=2) + "\n")
|
||||
raise SystemExit(f"native owner determinism mismatch; see {log}")
|
||||
summary = {"status": "PASS", "source_stamp": STAMP,
|
||||
"layout_identity": reference_identity, "runs": rows}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
(ROOT / "Standalone/Research/WorldAssemblerM3/Linux140/baselines").mkdir(parents=True, exist_ok=True)
|
||||
(ROOT / "Standalone/Research/WorldAssemblerM3/Linux140/baselines/native-owner-fi-seed0.json").write_text(
|
||||
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L140DeterminismSummary] runs={len(rows)} source_stamp={STAMP} status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the composed L140 owner gates for all requested seeds."""
|
||||
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/l140/seeds"
|
||||
BIN = ROOT / "Saved/Linux/l140/bin/VoxelForgeCoreHeadless-l140-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
CHECKS = {
|
||||
"native_dispatch": re.compile(
|
||||
r"^\[DensityDispatch\] strate=1 generator=FloatingIslands "
|
||||
r"recipe=FloatingIslands\.ClusterField version=10 "
|
||||
r"bound_path=FloatingIslands-native adapter=FFloatingIslandFieldAdapter$", re.M),
|
||||
"island_route": re.compile(r"^\[FloatingIslandRouteAudit\].*status=PASS$", re.M),
|
||||
"passage_clearance": re.compile(r"^\[FloatingIslandPassageClearance\].*status=PASS$", re.M),
|
||||
"strate1_route": re.compile(
|
||||
r"^\[ResolvedStrateRouteAudit\] strate=1 .*blocked=0 .*unsupported=0 "
|
||||
r"grade_failures=0 mouth_binding=PASS status=PASS$", re.M),
|
||||
"route_summary": re.compile(
|
||||
r"^\[ResolvedPassageAuditSummary\].*route_walkable=4 .*route_bindings=4 "
|
||||
r"route_binding_failures=0 .*blocked_controls=3/3", re.M),
|
||||
"passages": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def run(seed: int, label: str, seams_only: bool) -> tuple[int, str, float]:
|
||||
log = OUT / f"seed{seed}-{label}.log"
|
||||
layout = OUT / f"seed{seed}-{label}-layout.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}",
|
||||
"--recipe=surface-world", "--native-owner-fi", "--density-dispatch-report"]
|
||||
command.append("--audit-passage-tile-seams" if seams_only else "--audit-resolved-layout-passages")
|
||||
if not seams_only:
|
||||
command.append("--audit-passage-tile-seams")
|
||||
command.append(f"--resolved-layout-output={layout}")
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
return proc.returncode, str(log.relative_to(ROOT)), round(time.monotonic() - started, 3)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L140 Clang binary or composed owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
for seed in range(16):
|
||||
exit_code, log_name, elapsed = run(seed, "composed", False)
|
||||
content = (ROOT / log_name).read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in CHECKS.items()}
|
||||
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
|
||||
checks["blocked_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
|
||||
checks["negative_controls"] = (
|
||||
"[ResolvedLayoutValidatorControls] rejected=10/10 status=PASS" in content
|
||||
and len(re.findall(r"^\[PassageNaturalnessNegativeControl\].*detected=true$",
|
||||
content, re.M)) == 1
|
||||
and len(re.findall(r"^\[PassageMouthProfileNegativeControl\].*detected=true$",
|
||||
content, re.M)) == 1
|
||||
and len(re.findall(r"^\[PassageFloorStepNegativeControl\].*detected=true$",
|
||||
content, re.M)) == 3
|
||||
and "synthetic_spheres_negative_control=DETECTED" in content
|
||||
and "floor_beaded_negative_control=DETECTED" in content)
|
||||
seam_match = re.search(r"^\[PassageTileSeamAudit\].*status=(PASS|FAIL)$", content, re.M)
|
||||
seam_exit = exit_code
|
||||
seam_log = log_name
|
||||
seam_elapsed = 0.0
|
||||
if seam_match is None:
|
||||
seam_exit, seam_log, seam_elapsed = run(seed, "seams", True)
|
||||
seam_content = (ROOT / seam_log).read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(seam_content)
|
||||
checks["source_stamp"] &= stamp is not None and stamp.group(1) == STAMP
|
||||
seam_match = re.search(
|
||||
r"^\[PassageTileSeamAudit\].*status=(PASS|FAIL)$", seam_content, re.M)
|
||||
content += "\n" + seam_content
|
||||
checks["seams"] = seam_match is not None and seam_match.group(1) == "PASS"
|
||||
all_gates = all(checks.values()) and exit_code == 0 and seam_exit == 0
|
||||
row = {"seed": seed, "exit": exit_code, "seam_exit": seam_exit,
|
||||
"passed": all_gates, "checks": checks,
|
||||
"elapsed_s": elapsed + seam_elapsed, "log": log_name, "seam_log": seam_log}
|
||||
results.append(row)
|
||||
print("[L140SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(results, indent=2) + "\n")
|
||||
passed = sum(row["passed"] for row in results)
|
||||
summary = {"source_stamp": STAMP, "seeds": list(range(16)),
|
||||
"passed": passed, "failed": 16 - passed, "results": results}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
|
||||
print(f"[L140SeedSweepSummary] passed={passed}/16 failed={16-passed} status={'PASS' if passed==16 else 'FAIL'}",
|
||||
flush=True)
|
||||
return 0 if passed == 16 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""L80 timing: native L140 vs L139 HEAD and assembler/world, all owner strata."""
|
||||
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/l140/timing"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"assembler-world": (
|
||||
ROOT / "Saved/Linux/l137/bin/VoxelForgeCoreHeadless-l137-aa-a",
|
||||
"4499fb2a8a4ede7d6cbc68d1b794f104658cdaae93ed9cacbc91fe7ff3fc3621"),
|
||||
"head": (
|
||||
ROOT / "Saved/Linux/l139/bin/VoxelForgeCoreHeadless-l139-clang",
|
||||
"b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1"),
|
||||
"l140-native-fi": (
|
||||
ROOT / "Saved/Linux/l140/bin/VoxelForgeCoreHeadless-l140-clang",
|
||||
"6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da"),
|
||||
}
|
||||
CAMERAS = {
|
||||
# The camera override also selects the owner SurfaceWorld departure mouth.
|
||||
# Hold XY to the seed-0 native-FI valid mouth so every layer/version shares one layout.
|
||||
0: (25.8369140625, 3.19921875, -372.2626953125),
|
||||
1: (25.8369140625, 3.19921875, -737.0),
|
||||
2: (25.8369140625, 3.19921875, -753.3740234375),
|
||||
3: (25.8369140625, 3.19921875, -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)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]{64})$", re.M)
|
||||
L140_AA_FLOOR_PCT = 1.0 # L137's same-binary A/A median control.
|
||||
|
||||
|
||||
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]
|
||||
|
||||
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 run(strate: int, workers: int, version: str, phase: str, replicate: int) -> dict[str, object]:
|
||||
binary, expected_stamp = BINARIES[version]
|
||||
camera = ",".join(f"{value:.12f}" for value in CAMERAS[strate]) + ",0,0"
|
||||
label = f"s{strate}-w{workers}-{version}-{phase}{replicate}"
|
||||
log = OUT / f"{label}.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}", "--profile-lod-work", "--stream-scheduler-policy=3",
|
||||
"--recipe=surface-world"]
|
||||
if version == "l140-native-fi":
|
||||
command.append("--native-owner-fi")
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = 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, stamp = PIPELINE_RE.search(content), NEAR_RE.search(content), STAMP_RE.search(content)
|
||||
if (proc.returncode or pipeline is None or near is None or stamp is None
|
||||
or stamp.group(1) != expected_stamp):
|
||||
raise SystemExit(f"L80 run failed or stamp mismatch: {log}; exit={proc.returncode}")
|
||||
if version == "l140-native-fi" and not re.search(
|
||||
r"^\[FloatingIslandWorldAssembly\].*dispatch=FloatingIslands-native .*status=PASS$",
|
||||
content, re.M):
|
||||
raise SystemExit(f"candidate timing did not bind native FloatingIslands: {log}")
|
||||
fields = dict(FIELDS_RE.findall(pipeline.group(1)))
|
||||
row = {"strate": strate, "workers": workers, "version": version,
|
||||
"phase": phase, "replicate": replicate, "source_stamp": stamp.group(1),
|
||||
"lod02_ready_s": float(near.group(3)),
|
||||
"whole_view_ready_s": float(fields["whole_view_ready"].removesuffix("s")),
|
||||
"whole_tiles": int(fields["whole_tiles"].split("/")[0]),
|
||||
"wall_s": round(time.monotonic() - started, 3), "log": str(log.relative_to(ROOT))}
|
||||
print("[L140TimingRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
return row
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path, _ in BINARIES.values()) or not DATA.is_file():
|
||||
raise SystemExit("missing L80 timing binaries or owner fixture")
|
||||
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"[L140TimingGate] load_1m={load_1m:.2f} cpu_idle_2s={idle:.1f}%", flush=True)
|
||||
if load_1m >= 1.5 or idle < 97.0:
|
||||
raise SystemExit("timing refused: machine is above the L80 quiet-load gate")
|
||||
rows: list[dict[str, object]] = []
|
||||
for strate in range(4):
|
||||
for workers in (8, 12):
|
||||
for version in BINARIES:
|
||||
run(strate, workers, version, "warmup", 0)
|
||||
for replicate in range(1, 6):
|
||||
order = tuple(BINARIES)
|
||||
order = order[replicate % len(order):] + order[:replicate % len(order)]
|
||||
if replicate % 2 == 0:
|
||||
order = tuple(reversed(order))
|
||||
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):
|
||||
group = [row for row in measured if row["strate"] == strate and row["workers"] == workers]
|
||||
medians = {version: {key: statistics.median(
|
||||
row[key] for row in group if row["version"] == version)
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s")} for version in BINARIES}
|
||||
entry: dict[str, object] = {"strate": strate, "workers": workers,
|
||||
"replicates_per_version": 5, "medians": medians}
|
||||
for key in ("lod02_ready_s", "whole_view_ready_s"):
|
||||
for baseline in ("head", "assembler-world"):
|
||||
entry[f"l140_vs_{baseline}_{key}_delta_pct"] = round(
|
||||
100.0 * (medians["l140-native-fi"][key] / medians[baseline][key] - 1.0), 2)
|
||||
entry[f"l140_vs_{baseline}_{key}_paired_deltas_pct"] = [round(100.0 * (
|
||||
next(row[key] for row in group if row["version"] == "l140-native-fi"
|
||||
and row["replicate"] == replicate)
|
||||
/ next(row[key] for row in group if row["version"] == baseline
|
||||
and row["replicate"] == replicate) - 1.0), 3)
|
||||
for replicate in range(1, 6)]
|
||||
summary.append(entry)
|
||||
result = {"a_a_noise_floor_pct": L140_AA_FLOOR_PCT,
|
||||
"a_a_reference": "L137 same-binary median control; assembler/world source stamp",
|
||||
"timing_protocol": "quiet machine; one warm-up per binary per cell; five interleaved measured runs; W8/W12; all four owner layers",
|
||||
"machine": {"load_1m": load_1m, "cpu_idle_2s_pct": idle},
|
||||
"source_stamps": {name: stamp for name, (_path, stamp) in BINARIES.items()},
|
||||
"runs": rows, "summary": summary}
|
||||
(OUT / "summary.json").write_text(json.dumps(result, indent=2) + "\n")
|
||||
print(f"[L140TimingSummary] measured_runs={len(measured)} warmups=24 aa_floor_pct=1.0 status=PASS",
|
||||
flush=True)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"status": "PASS",
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"runs": [
|
||||
{
|
||||
"compiler": "clang",
|
||||
"workers": 1,
|
||||
"exit": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"hash_lines": [
|
||||
"[PipelineFullDetailHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineWholeViewHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineMaterialHash] tiles=512 material=0xc3cb5fcccf78f4ee",
|
||||
"[PipelineLODHash] lod=0 tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineExactQueryHash] probes=9 density=0x74eac0457ddc2026 unclamped=0x74eac0457ddc2026 detail=0xefcba20c0c70c415 collision=0xdd5b083a1fa82cc7 path=direct-Core-queries"
|
||||
],
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"native_fi_dispatch": true,
|
||||
"native_plan_audits": true,
|
||||
"passed": true,
|
||||
"elapsed_s": 101.92,
|
||||
"log": "Saved/Linux/l140/determinism/native-owner-fi-clang-w1.log",
|
||||
"layout": "Saved/Linux/l140/determinism/native-owner-fi-clang-w1.json",
|
||||
"floating_island_recipe_hash": "0b6a23bc73a3b867",
|
||||
"passage_clearance_line": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"route_audit_line": "[FloatingIslandRouteAudit] points=128 centers=911 capsule_samples=247792 blocked=0 support_samples=6961 unsupported=0 grade_failures=0 status=PASS"
|
||||
},
|
||||
{
|
||||
"compiler": "clang",
|
||||
"workers": 8,
|
||||
"exit": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"hash_lines": [
|
||||
"[PipelineFullDetailHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineWholeViewHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineMaterialHash] tiles=512 material=0xc3cb5fcccf78f4ee",
|
||||
"[PipelineLODHash] lod=0 tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineExactQueryHash] probes=9 density=0x74eac0457ddc2026 unclamped=0x74eac0457ddc2026 detail=0xefcba20c0c70c415 collision=0xdd5b083a1fa82cc7 path=direct-Core-queries"
|
||||
],
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"native_fi_dispatch": true,
|
||||
"native_plan_audits": true,
|
||||
"passed": true,
|
||||
"elapsed_s": 68.892,
|
||||
"log": "Saved/Linux/l140/determinism/native-owner-fi-clang-w8.log",
|
||||
"layout": "Saved/Linux/l140/determinism/native-owner-fi-clang-w8.json",
|
||||
"floating_island_recipe_hash": "0b6a23bc73a3b867",
|
||||
"passage_clearance_line": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"route_audit_line": "[FloatingIslandRouteAudit] points=128 centers=911 capsule_samples=247792 blocked=0 support_samples=6961 unsupported=0 grade_failures=0 status=PASS"
|
||||
},
|
||||
{
|
||||
"compiler": "clang",
|
||||
"workers": 12,
|
||||
"exit": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"hash_lines": [
|
||||
"[PipelineFullDetailHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineWholeViewHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineMaterialHash] tiles=512 material=0xc3cb5fcccf78f4ee",
|
||||
"[PipelineLODHash] lod=0 tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineExactQueryHash] probes=9 density=0x74eac0457ddc2026 unclamped=0x74eac0457ddc2026 detail=0xefcba20c0c70c415 collision=0xdd5b083a1fa82cc7 path=direct-Core-queries"
|
||||
],
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"native_fi_dispatch": true,
|
||||
"native_plan_audits": true,
|
||||
"passed": true,
|
||||
"elapsed_s": 68.96,
|
||||
"log": "Saved/Linux/l140/determinism/native-owner-fi-clang-w12.log",
|
||||
"layout": "Saved/Linux/l140/determinism/native-owner-fi-clang-w12.json",
|
||||
"floating_island_recipe_hash": "0b6a23bc73a3b867",
|
||||
"passage_clearance_line": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"route_audit_line": "[FloatingIslandRouteAudit] points=128 centers=911 capsule_samples=247792 blocked=0 support_samples=6961 unsupported=0 grade_failures=0 status=PASS"
|
||||
},
|
||||
{
|
||||
"compiler": "gcc",
|
||||
"workers": 1,
|
||||
"exit": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"hash_lines": [
|
||||
"[PipelineFullDetailHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineWholeViewHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineMaterialHash] tiles=512 material=0xc3cb5fcccf78f4ee",
|
||||
"[PipelineLODHash] lod=0 tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineExactQueryHash] probes=9 density=0x74eac0457ddc2026 unclamped=0x74eac0457ddc2026 detail=0xefcba20c0c70c415 collision=0xdd5b083a1fa82cc7 path=direct-Core-queries"
|
||||
],
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"native_fi_dispatch": true,
|
||||
"native_plan_audits": true,
|
||||
"passed": true,
|
||||
"elapsed_s": 100.676,
|
||||
"log": "Saved/Linux/l140/determinism/native-owner-fi-gcc-w1.log",
|
||||
"layout": "Saved/Linux/l140/determinism/native-owner-fi-gcc-w1.json",
|
||||
"floating_island_recipe_hash": "0b6a23bc73a3b867",
|
||||
"passage_clearance_line": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"route_audit_line": "[FloatingIslandRouteAudit] points=128 centers=911 capsule_samples=247792 blocked=0 support_samples=6961 unsupported=0 grade_failures=0 status=PASS"
|
||||
},
|
||||
{
|
||||
"compiler": "gcc",
|
||||
"workers": 8,
|
||||
"exit": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"hash_lines": [
|
||||
"[PipelineFullDetailHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineWholeViewHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineMaterialHash] tiles=512 material=0xc3cb5fcccf78f4ee",
|
||||
"[PipelineLODHash] lod=0 tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineExactQueryHash] probes=9 density=0x74eac0457ddc2026 unclamped=0x74eac0457ddc2026 detail=0xefcba20c0c70c415 collision=0xdd5b083a1fa82cc7 path=direct-Core-queries"
|
||||
],
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"native_fi_dispatch": true,
|
||||
"native_plan_audits": true,
|
||||
"passed": true,
|
||||
"elapsed_s": 70.398,
|
||||
"log": "Saved/Linux/l140/determinism/native-owner-fi-gcc-w8.log",
|
||||
"layout": "Saved/Linux/l140/determinism/native-owner-fi-gcc-w8.json",
|
||||
"floating_island_recipe_hash": "0b6a23bc73a3b867",
|
||||
"passage_clearance_line": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"route_audit_line": "[FloatingIslandRouteAudit] points=128 centers=911 capsule_samples=247792 blocked=0 support_samples=6961 unsupported=0 grade_failures=0 status=PASS"
|
||||
},
|
||||
{
|
||||
"compiler": "gcc",
|
||||
"workers": 12,
|
||||
"exit": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"hash_lines": [
|
||||
"[PipelineFullDetailHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineWholeViewHash] tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineMaterialHash] tiles=512 material=0xc3cb5fcccf78f4ee",
|
||||
"[PipelineLODHash] lod=0 tiles=512 density=0xe10ac12305f1dbc0 mesh=0x822e56e8f5bfa6f0 payload=0x0be1f08906997251 topology=0x650bbeb9e554d390",
|
||||
"[PipelineLODHash] lod=1 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=2 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=3 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=4 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=5 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=6 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=7 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineLODHash] lod=8 tiles=0 density=0xcbf29ce484222325 mesh=0xcbf29ce484222325 payload=0xcbf29ce484222325 topology=0xcbf29ce484222325",
|
||||
"[PipelineExactQueryHash] probes=9 density=0x74eac0457ddc2026 unclamped=0x74eac0457ddc2026 detail=0xefcba20c0c70c415 collision=0xdd5b083a1fa82cc7 path=direct-Core-queries"
|
||||
],
|
||||
"layout_identity": "resolved-layout-v3-fnv64:08da20d55ff6820f",
|
||||
"native_fi_dispatch": true,
|
||||
"native_plan_audits": true,
|
||||
"passed": true,
|
||||
"elapsed_s": 69.061,
|
||||
"log": "Saved/Linux/l140/determinism/native-owner-fi-gcc-w12.log",
|
||||
"layout": "Saved/Linux/l140/determinism/native-owner-fi-gcc-w12.json",
|
||||
"floating_island_recipe_hash": "0b6a23bc73a3b867",
|
||||
"passage_clearance_line": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"route_audit_line": "[FloatingIslandRouteAudit] points=128 centers=911 capsule_samples=247792 blocked=0 support_samples=6961 unsupported=0 grade_failures=0 status=PASS"
|
||||
}
|
||||
],
|
||||
"world_assembly_evaluator_version": 239,
|
||||
"floating_island_geometry_version": 10,
|
||||
"native_dispatch": "FloatingIslands-native",
|
||||
"route_and_clearance_baseline": {
|
||||
"route_audit": "[FloatingIslandRouteAudit] points=128 centers=... status=PASS",
|
||||
"passage_clearance": "[FloatingIslandPassageClearance] strate=1 tube_samples=5325 island_blocked=0 route_checks=676275 route_blocked=0 arrival_support=5/5 departure_support=5/5 clearance_excess_m=2.824 portal_exclusion_m=13.0 influence_reach_m=1.053 capsule_bound_m=0.880 status=PASS",
|
||||
"note": "The six identical hash/layout rows are the concrete baselines; route point counts differ by seed only in other sweep cases."
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"seed": 0,
|
||||
"source_stamp": "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da",
|
||||
"before_stamp": "b8603b6cb3975ca0f9669759e4e2464561c557fdb7aa6836acef123ecf0b99d1",
|
||||
"views_per_phase": 5,
|
||||
"all_quality_pass": true,
|
||||
"captures": [
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "arrival-mouth",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/after-arrival-mouth.jpg",
|
||||
"bytes": 20205,
|
||||
"camera_m": [
|
||||
23.77218612173332,
|
||||
4.6087926326387905,
|
||||
-735.55
|
||||
],
|
||||
"target_m": [
|
||||
32.44404347295337,
|
||||
-1.3114176744441277,
|
||||
-735.55
|
||||
],
|
||||
"yaw_deg": 124.32105280346487,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye at the fixed 0-1 arrival mouth",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/after-arrival-mouth.jpg pixels=921600 mean_luma=111.598 stddev_luma=5.081 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "departure-mouth",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/after-departure-mouth.jpg",
|
||||
"bytes": 23901,
|
||||
"camera_m": [
|
||||
21.556817528943192,
|
||||
-14.930993681478485,
|
||||
-730.751171875
|
||||
],
|
||||
"target_m": [
|
||||
11.51544953238179,
|
||||
-11.861640531768845,
|
||||
-730.751171875
|
||||
],
|
||||
"yaw_deg": -73.00313688448165,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye approaching the fixed 1-2 departure mouth",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/after-departure-mouth.jpg pixels=921600 mean_luma=112.186 stddev_luma=5.529 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "island-edge",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/after-island-edge.jpg",
|
||||
"bytes": 19984,
|
||||
"camera_m": [
|
||||
47.84242754056169,
|
||||
-19.331304817383565,
|
||||
-734.4728515625
|
||||
],
|
||||
"target_m": [
|
||||
54.57926831575571,
|
||||
-16.75934518129221,
|
||||
-735.5728515625
|
||||
],
|
||||
"yaw_deg": 69.10437717375885,
|
||||
"pitch_deg": -8.673184187101441,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye beside the route on an island edge",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/after-island-edge.jpg pixels=921600 mean_luma=114.169 stddev_luma=8.688 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "walk-between-islands",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/after-walk-between-islands.jpg",
|
||||
"bytes": 20752,
|
||||
"camera_m": [
|
||||
64.3971173756484,
|
||||
-22.884388870806152,
|
||||
-733.137890625
|
||||
],
|
||||
"target_m": [
|
||||
53.12109375,
|
||||
-24.2802734375,
|
||||
-734.537109375
|
||||
],
|
||||
"yaw_deg": -97.05687302971492,
|
||||
"pitch_deg": -7.020510748634912,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye looking from one route landing toward the next island",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/after-walk-between-islands.jpg pixels=921600 mean_luma=111.849 stddev_luma=7.186 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "after",
|
||||
"view": "wide-silhouette",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/after-wide-silhouette.jpg",
|
||||
"bytes": 22894,
|
||||
"camera_m": [
|
||||
41.099609375,
|
||||
8.620008680555555,
|
||||
-725.6368272569445
|
||||
],
|
||||
"target_m": [
|
||||
41.099609375,
|
||||
-14.379991319444445,
|
||||
-735.6368272569445
|
||||
],
|
||||
"yaw_deg": 180.0,
|
||||
"pitch_deg": -23.498565675952097,
|
||||
"fov_deg": 72.0,
|
||||
"note": "wide side view showing the island chain silhouette",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/after-wide-silhouette.jpg pixels=921600 mean_luma=116.109 stddev_luma=7.003 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "arrival-mouth",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/before-arrival-mouth.jpg",
|
||||
"bytes": 13808,
|
||||
"camera_m": [
|
||||
23.77218612173332,
|
||||
4.6087926326387905,
|
||||
-735.55
|
||||
],
|
||||
"target_m": [
|
||||
32.44404347295337,
|
||||
-1.3114176744441277,
|
||||
-735.55
|
||||
],
|
||||
"yaw_deg": 124.32105280346487,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye at the fixed 0-1 arrival mouth",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/before-arrival-mouth.jpg pixels=921600 mean_luma=132.078 stddev_luma=8.080 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "departure-mouth",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/before-departure-mouth.jpg",
|
||||
"bytes": 17309,
|
||||
"camera_m": [
|
||||
21.556817528943192,
|
||||
-14.930993681478485,
|
||||
-730.751171875
|
||||
],
|
||||
"target_m": [
|
||||
11.51544953238179,
|
||||
-11.861640531768845,
|
||||
-730.751171875
|
||||
],
|
||||
"yaw_deg": -73.00313688448165,
|
||||
"pitch_deg": 0.0,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye approaching the fixed 1-2 departure mouth",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/before-departure-mouth.jpg pixels=921600 mean_luma=118.383 stddev_luma=9.908 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "island-edge",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/before-island-edge.jpg",
|
||||
"bytes": 15208,
|
||||
"camera_m": [
|
||||
47.84242754056169,
|
||||
-19.331304817383565,
|
||||
-734.4728515625
|
||||
],
|
||||
"target_m": [
|
||||
54.57926831575571,
|
||||
-16.75934518129221,
|
||||
-735.5728515625
|
||||
],
|
||||
"yaw_deg": 69.10437717375885,
|
||||
"pitch_deg": -8.673184187101441,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye beside the route on an island edge",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/before-island-edge.jpg pixels=921600 mean_luma=109.945 stddev_luma=4.383 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "walk-between-islands",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/before-walk-between-islands.jpg",
|
||||
"bytes": 15126,
|
||||
"camera_m": [
|
||||
64.3971173756484,
|
||||
-22.884388870806152,
|
||||
-733.137890625
|
||||
],
|
||||
"target_m": [
|
||||
53.12109375,
|
||||
-24.2802734375,
|
||||
-734.537109375
|
||||
],
|
||||
"yaw_deg": -97.05687302971492,
|
||||
"pitch_deg": -7.020510748634912,
|
||||
"fov_deg": 78.0,
|
||||
"note": "player eye looking from one route landing toward the next island",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/before-walk-between-islands.jpg pixels=921600 mean_luma=110.305 stddev_luma=4.132 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
},
|
||||
{
|
||||
"phase": "before",
|
||||
"view": "wide-silhouette",
|
||||
"file": "Standalone/Research/WorldAssemblerM3/Linux140/captures/before-wide-silhouette.jpg",
|
||||
"bytes": 15268,
|
||||
"camera_m": [
|
||||
41.099609375,
|
||||
8.620008680555555,
|
||||
-725.6368272569445
|
||||
],
|
||||
"target_m": [
|
||||
41.099609375,
|
||||
-14.379991319444445,
|
||||
-735.6368272569445
|
||||
],
|
||||
"yaw_deg": 180.0,
|
||||
"pitch_deg": -23.498565675952097,
|
||||
"fov_deg": 72.0,
|
||||
"note": "wide side view showing the island chain silhouette",
|
||||
"quality_status": "PASS",
|
||||
"quality": "[CaptureQuality] image=/home/jahni/VoxelM/Plugins/VoxelForge/Standalone/Research/WorldAssemblerM3/Linux140/captures/before-wide-silhouette.jpg pixels=921600 mean_luma=114.186 stddev_luma=3.531 coverage_luma_gt12=1.00000 thresholds=(stddev>=2.0,coverage>=0.001) status=PASS"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
# Linux147 — passage audits on the Core cave planner
|
||||
|
||||
**Result:** The L145 ripple-distance audit and L146 passage-interior air gate pass across all 16 seeds in both requested matrices. The two matrices each measured 5,171,796 capsule samples inside passage interiors with **zero solid samples**. Their no-passage controls detected solid in 48/48 passage interiors, and their masked-gap ripple controls passed 48/48 times. No generation code changed.
|
||||
|
||||
The only non-green result is seed 2's known interface 2–3 shell/seam failure. Vertical Shafts remains 15/16, matching L139. The Tunnel Network comparison is 15/16 against L135's earlier 16/16; seed 2 is already known to fail after the L139 shaft change. It is not a failure introduced by either new audit gate.
|
||||
|
||||
## Audit changes
|
||||
|
||||
- `StreamBenchmarkPipeline.h` now requires zero solid capsule samples inside each passage interior. It runs the same interior classifier over the passage-stripped field and requires positive solid detections for every passage.
|
||||
- The ripple fitter keeps each valid wall sample's actual route distance instead of compressing distances across missing probes. Its synthetic 5 m masked-gap control measures a 0.0581 m sine ripple and rejects a smooth cubic at 0.0000 m.
|
||||
- No Core planner, evaluator, Unreal `Source/`, or generation code changed. `git diff --check` passed.
|
||||
|
||||
## Per-seed verdicts
|
||||
|
||||
Tunnel Network compares with the L135 16/16 composed sweep; Vertical Shafts compares with the L139 15/16 sweep. `P` means pass and `F` means fail.
|
||||
|
||||
| Seed | Tunnel Network baseline → L147 | Vertical Shafts baseline → L147 | Verdict change |
|
||||
|---:|:---:|:---:|---|
|
||||
| 0 | P → P | P → P | None |
|
||||
| 1 | P → P | P → P | None |
|
||||
| 2 | P → F | F → F | Tunnel comparison: L135 predates the L139 shaft change. Current seed 2 has the existing interface 2–3 shell/seam failure; the new interior and ripple gates pass. Vertical Shafts remains the same known failure. |
|
||||
| 3 | P → P | P → P | None |
|
||||
| 4 | P → P | P → P | None |
|
||||
| 5 | P → P | P → P | None |
|
||||
| 6 | P → P | P → P | None |
|
||||
| 7 | P → P | P → P | None |
|
||||
| 8 | P → P | P → P | None |
|
||||
| 9 | P → P | P → P | None |
|
||||
| 10 | P → P | P → P | None |
|
||||
| 11 | P → P | P → P | None |
|
||||
| 12 | P → P | P → P | None |
|
||||
| 13 | P → P | P → P | None |
|
||||
| 14 | P → P | P → P | None |
|
||||
| 15 | P → P | P → P | None |
|
||||
|
||||
There were no newly passing seeds and no verdict changes caused by the added gates. For seed 2, all three passages reported `interior_air=PASS`; its interface 2–3 shell had eight `wall_missing` samples at route stations 489.627–490.321 m. The samples were outside the passage interior. The first was at `(-21.678, -1.714, -888.614) m`, with composed and native density `+2.341949`, expected wall classification, and wall-shell margin `−0.297164 m`. The passage and seam summaries fail as they did in L139. No interior-solid root cause trace was needed because the measured interior-solid count is zero.
|
||||
|
||||
## Measurements and checks
|
||||
|
||||
- Clang 19 standalone build (`-O2`, `-ffp-contract=off`, four jobs) passed. L147 source stamp: `c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f`.
|
||||
- Tunnel Network: seeds 0–15, one composed run per seed, 430.715 s summed process time (13.542–51.457 s per seed); 15/16 passed. Logs and resolved layouts are under `Saved/Linux/l147/seeds/tunnel-network/`.
|
||||
- Vertical Shafts: seeds 0–15, one composed run per seed, 392.185 s summed process time (13.069–39.769 s per seed); 15/16 passed. Logs and resolved layouts are under `Saved/Linux/l147/seeds/vertical-shafts/`.
|
||||
- Across each matrix: 5,171,796 passage-interior capsule samples, 0 solid; 48/48 no-passage controls detected interior solid; 48/48 masked-gap ripple controls passed. Passage walkability, ripple, and all other existing gates passed for seeds other than the known seed-2 shell/seam failure.
|
||||
- Fast tier passed in 357.3 s: `Saved/Linux/l147/fast/fast-summary.json`. The first invocation stopped before running checks because the verifier's expected binary path was empty; it passed after using the completed L147 build there.
|
||||
- Owner identity comparison against the HEAD-stamped L140 binary passed: all pipeline hash lines, plan hash `538ee11456eb0dab`, resolved-layout identity `resolved-layout-v3-fnv64:d310515572977ebf`, and resolved-layout SHA-256 `50a0342902b6b897c5746fd86fac559244151894155cab6db0822229bfb877e4` matched. Details: `Saved/Linux/l147/identity/summary.json`.
|
||||
- `git diff --check` passed. No L80 timing, Unreal build, Windows build, or cross-compiler determinism run was requested or performed.
|
||||
|
||||
Runners: [`RunL147SeedSweep.py`](RunL147SeedSweep.py) and [`RunL147OwnerIdentity.py`](RunL147OwnerIdentity.py). No Unreal `Source/` files changed.
|
||||
|
||||
## Remaining risk
|
||||
|
||||
The seed-2 interface 2–3 shell/seam failure remains unresolved. It does not put solid samples inside the passage interior and is unchanged from the known L139 Vertical Shafts result. This round made no generation fix.
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare owner and resolved-planner hashes against the exact L147 parent build."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
OUT = ROOT / "Saved/Linux/l147/identity"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
HEAD_BIN = ROOT / "Saved/Linux/l140/bin/VoxelForgeCoreHeadless-l140-clang"
|
||||
L147_BIN = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l147-clang"
|
||||
HEAD_STAMP = "6205e6c180ef32c41792dc3e0d8bf443a96df41974f40334af4bf3a157ab88da"
|
||||
L147_STAMP = "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
PIPELINE_HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
|
||||
PLAN_RE = re.compile(r"^\[WorldAssemblerPlanSerialization\].*plan_hash=([0-9a-f]+).*$", re.M)
|
||||
|
||||
|
||||
def run(label: str, binary: Path, expected_stamp: str) -> dict[str, object]:
|
||||
if not binary.is_file():
|
||||
raise SystemExit(f"missing {label} binary: {binary}")
|
||||
log = OUT / f"{label}.log"
|
||||
layout = OUT / f"{label}-layout.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(binary), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=0", "--force-generate", "--seed=0",
|
||||
"--recipe=surface-world", "--stream-hash",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
pipeline_hashes = PIPELINE_HASH_RE.findall(content)
|
||||
plan = PLAN_RE.search(content)
|
||||
layout_bytes = layout.read_bytes() if layout.is_file() else b""
|
||||
result = {
|
||||
"label": label,
|
||||
"exit": proc.returncode,
|
||||
"stamp": stamp.group(1) if stamp else None,
|
||||
"stamp_matches_expected": stamp is not None and stamp.group(1) == expected_stamp,
|
||||
"pipeline_hashes": pipeline_hashes,
|
||||
"plan_hash": plan.group(1) if plan else None,
|
||||
"resolved_layout_sha256": hashlib.sha256(layout_bytes).hexdigest() if layout_bytes else None,
|
||||
"resolved_layout_identity": None,
|
||||
"log": str(log.relative_to(ROOT)),
|
||||
"layout": str(layout.relative_to(ROOT)),
|
||||
}
|
||||
if layout_bytes:
|
||||
result["resolved_layout_identity"] = json.loads(layout_bytes).get("stable_identity")
|
||||
return result
|
||||
|
||||
|
||||
def main() -> int:
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
head = run("head", HEAD_BIN, HEAD_STAMP)
|
||||
l147 = run("l147", L147_BIN, L147_STAMP)
|
||||
passed = (head["exit"] == 0 and l147["exit"] == 0
|
||||
and head["stamp_matches_expected"] and l147["stamp_matches_expected"]
|
||||
and bool(head["pipeline_hashes"])
|
||||
and head["pipeline_hashes"] == l147["pipeline_hashes"]
|
||||
and head["plan_hash"] is not None and head["plan_hash"] == l147["plan_hash"]
|
||||
and head["resolved_layout_sha256"] is not None
|
||||
and head["resolved_layout_sha256"] == l147["resolved_layout_sha256"]
|
||||
and head["resolved_layout_identity"] == l147["resolved_layout_identity"])
|
||||
summary = {"status": "PASS" if passed else "FAIL", "head": head, "l147": l147,
|
||||
"owner_pipeline_hashes_identical": head["pipeline_hashes"] == l147["pipeline_hashes"],
|
||||
"plan_hash_identical": head["plan_hash"] == l147["plan_hash"],
|
||||
"resolved_layout_identical":
|
||||
head["resolved_layout_sha256"] == l147["resolved_layout_sha256"]}
|
||||
path = OUT / "summary.json"
|
||||
path.write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L147OwnerIdentity] status={summary['status']} "
|
||||
f"owner_hashes={summary['owner_pipeline_hashes_identical']} "
|
||||
f"planner_hash={summary['plan_hash_identical']} "
|
||||
f"resolved_layout={summary['resolved_layout_identical']} "
|
||||
f"head_stamp={head['stamp']} l147_stamp={l147['stamp']} "
|
||||
f"summary={path.relative_to(ROOT)}")
|
||||
return 0 if passed else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the L147 composed passage audit for all 16 owner seeds."""
|
||||
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/l147/seeds"
|
||||
BIN = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l147-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "c6b8a100355e271ddc40eb5973569d2ff4e2ba8d9c628c72f8e0af2493f0609f"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
BASELINES = {
|
||||
"tunnel-network": ROOT / "Saved/Linux/l135/optimized-seed-sweep.json",
|
||||
"vertical-shafts": ROOT / "Saved/Linux/l139/seeds/summary.json",
|
||||
}
|
||||
DISPATCH = {
|
||||
"tunnel-network": re.compile(
|
||||
r"^\[DensityDispatch\] strate=2 generator=TunnelNetwork "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M),
|
||||
"vertical-shafts": re.compile(
|
||||
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M),
|
||||
}
|
||||
ROUTES = {
|
||||
"tunnel-network": re.compile(
|
||||
r"^\[ResolvedStrateRouteAudit\] strate=2 .*unsupported=0 "
|
||||
r"grade_failures=0 .*status=PASS$", re.M),
|
||||
"vertical-shafts": re.compile(
|
||||
r"^\[ResolvedStrateRouteAudit\] strate=3 .*unsupported=0 "
|
||||
r"grade_failures=0 .*status=PASS$", re.M),
|
||||
}
|
||||
CONTROLS = {
|
||||
"tunnel-network": re.compile(
|
||||
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
"vertical-shafts": re.compile(
|
||||
r"^\[VerticalShaftsBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
REQUIRED = {
|
||||
"passages": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"passage_interior": re.compile(
|
||||
r"^\[PassageInteriorCapsuleAuditSummary\].*status=PASS$", re.M),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--kind", choices=tuple(BASELINES), required=True)
|
||||
args = parser.parse_args()
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L147 Clang binary or composed owner fixture")
|
||||
baseline_path = BASELINES[args.kind]
|
||||
baseline = json.loads(baseline_path.read_text(encoding="utf-8"))
|
||||
baseline_results = {int(row["seed"]): row for row in baseline["results"]}
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
mode_out = OUT / args.kind
|
||||
mode_out.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
partial = mode_out / "runs-partial.json"
|
||||
for seed in range(16):
|
||||
log = mode_out / f"seed{seed}.log"
|
||||
layout = mode_out / f"seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}",
|
||||
"--recipe=surface-world", "--density-dispatch-report",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
|
||||
checks["dispatch"] = bool(DISPATCH[args.kind].search(content))
|
||||
checks["route"] = bool(ROUTES[args.kind].search(content))
|
||||
checks["blocked_route_control"] = bool(CONTROLS[args.kind].search(content))
|
||||
checks["three_passage_interiors"] = len(re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*interior_air=PASS", content, re.M)) == 3
|
||||
checks["three_interior_negative_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageInteriorNegativeControl\].*detected=true status=PASS$",
|
||||
content, re.M)) == 3
|
||||
checks["three_ripple_gap_controls"] = len(re.findall(
|
||||
r"^\[PassageRippleSamplingControl\].*status=PASS$", content, re.M)) == 3
|
||||
checks["three_blocked_passage_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
|
||||
checks["passage_count"] = len(re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*walkability=PASS$", content, re.M)) == 3
|
||||
checks["layout_output"] = layout.is_file()
|
||||
passed = proc.returncode == 0 and all(checks.values())
|
||||
prior = baseline_results.get(seed, {})
|
||||
prior_passed = bool(prior.get("passed", False))
|
||||
change = ("newly failing" if prior_passed and not passed else
|
||||
"newly passing" if not prior_passed and passed else
|
||||
"still passing" if prior_passed else "still failing")
|
||||
failed_checks = [name for name, ok in checks.items() if not ok]
|
||||
reason = (", ".join(failed_checks) if failed_checks else
|
||||
"all previous and new gates pass" if passed else
|
||||
f"process exit {proc.returncode}")
|
||||
row = {"kind": args.kind, "seed": seed, "prior_verdict": "PASS" if prior_passed else "FAIL",
|
||||
"verdict": "PASS" if passed else "FAIL", "change": change,
|
||||
"reason": reason, "exit": proc.returncode, "checks": checks,
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L147SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
partial.write_text(json.dumps(results, indent=2) + "\n", encoding="utf-8")
|
||||
passed_count = sum(row["verdict"] == "PASS" for row in results)
|
||||
summary = {"kind": args.kind, "source_stamp": STAMP,
|
||||
"baseline": str(baseline_path.relative_to(ROOT)), "seeds": list(range(16)),
|
||||
"passed": passed_count, "failed": 16 - passed_count, "results": results}
|
||||
summary_path = mode_out / "summary.json"
|
||||
summary_path.write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L147SeedSweepSummary] kind={args.kind} passed={passed_count}/16 "
|
||||
f"failed={16-passed_count} status={'PASS' if passed_count == 16 else 'FAIL'}",
|
||||
flush=True)
|
||||
return 0 if passed_count == 16 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,29 @@
|
||||
# Linux148 — passage exclusion envelope
|
||||
|
||||
**Result:** The shared plan-time bounds audit and its negative control work, and the L147 seed-2 carver is identified. The 8 m mouth joint is too small for the existing route candidates: both composed matrices pass only **7/16** seeds. Per the three-failure stop rule, this round stops without another planner adjustment and is not ready to promote.
|
||||
|
||||
## Root cause and rule
|
||||
|
||||
The L147 Vertical Shafts seed-2 wall sample at `(-21.678, -1.714, -888.614) m` is carved by `vertical-shafts/strate-3/route-edge-0`. At that point its native tube/floor SDF combines to about `-0.553 m`, predicting `+2.21` Core voxels of air; the observed native density is `+2.341949`. No other cave primitive has a negative native SDF there. The edge's start anchor is `passage/2-3/0/lower-mouth`, confirming this sample lies in its passage connection.
|
||||
|
||||
The shared gate samples the resolved passage spline, expands it by the 1.053 m shell/influence reach, and checks each layer primitive's conservative AABB against that envelope. It permits overlap only for a named passage mouth, within an 8 m sphere (the planner's 2 m overlap plus 6 m lead). Other overlap rejects the graph before it is written into the layout. A forced AABB at a passage centerline is rejected as the negative control.
|
||||
|
||||
The shared Core API is generic; this round wires it through `PlanResolvedCavernNetworkV1`, used by Tunnel Network and Vertical Shafts. Other layer planners must call it when they resolve their primitive bounds.
|
||||
|
||||
The mouth radius is the limiting assumption. Existing route-edge bounds in Tunnel Network seeds 0, 1, 4, and 7 meet passage `1-2` at 12.5–19.6 m from the mouth; seed 8 also has room bounds at 17.5–20.5 m. These are connected route candidates, but they fall outside the 8 m sphere. In seed 10, `vertical-shafts/strate-3/shaft-1/primitive-0` also overlaps passage `2-3`; it has no mouth association and remains a separate candidate to resolve.
|
||||
|
||||
## Measurements and checks
|
||||
|
||||
- Clang 19 and GCC builds passed with source stamp `593551a985ec0d1acc271260e4c8f15a5261c90edb60e33a56ed6df27e269ffb`.
|
||||
- Tunnel Network: **7/16**; full runs were seeds 2, 3, 5, 6, 9, 11, and 12. Vertical Shafts: **7/16**, the same seeds. The other nine seeds in each matrix were rejected by the plan-time exclusion audit. The run logs and per-seed layouts are under `Saved/Linux/l148/seeds/`.
|
||||
- Across the seven full runs in each matrix: 2,186,289 passage-interior capsule samples, zero solid; 53,262 shell wall samples, zero violations. Passage, ripple, and negative-control gates passed on those runs. Seed 2's passage `2-3` had zero wall samples outside the mouth joint, so its shell result has no wall coverage beyond that joint.
|
||||
- The passing layouts match their L147 stable identities. Seed 2 is `resolved-layout-v3-fnv64:64988eac83c0220c`.
|
||||
- Fast tier passed in 362.6 s: `Saved/Linux/l148/fast/fast-summary.json`.
|
||||
- Clang/GCC W1/W8/W12 passed on seed 2: 13 pipeline hash lines and the resolved-layout identity matched across all six runs. Summary: `Saved/Linux/l148/determinism/summary.json`.
|
||||
- `git diff --check` and Python syntax checks passed. No Unreal build was run.
|
||||
|
||||
The successful outputs remain evaluator v237 and layout-identical to L147. No baselines or captures were added. The nine rejected seeds produced no resolved layout, so there is no accepted before/after scene to capture. This is an incomplete, unpromoted planner change.
|
||||
|
||||
## Remaining risk
|
||||
|
||||
The audit catches conservative-bounds conflicts, but the current mouth sphere does not cover the existing connected-route bounds, and the optional shaft collision needs a deterministic candidate decision. The 16/16 requirement is unmet. No Unreal `Source/` files changed.
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare L148 composed owner hashes and layout identity at W1/W8/W12."""
|
||||
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/l148/determinism"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
BINARIES = {
|
||||
"clang": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l148-clang",
|
||||
"gcc": ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l148-gcc",
|
||||
}
|
||||
CAMERA = "-20.062500,-5.068359,-900.353516,0,0"
|
||||
HASH_RE = re.compile(
|
||||
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|MaterialHash|LODHash|ExactQueryHash)\].*$",
|
||||
re.M)
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
EXCLUSION_RE = re.compile(
|
||||
r"^\[CavernPassageExclusionAudit\] strate=[23] primitives=\d+ "
|
||||
r"outside_joint=0 mouth_joint=\d+ negative_controls=1/1 "
|
||||
r"first_primitive=none first_passage=none status=PASS$", re.M)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if any(not path.is_file() for path in (DATA, *BINARIES.values())):
|
||||
raise SystemExit("missing L148 Clang/GCC binary or composed owner fixture")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
rows = []
|
||||
reference_hashes: tuple[str, ...] | None = None
|
||||
reference_identity: str | None = None
|
||||
reference_stamp: str | None = None
|
||||
for compiler, binary in BINARIES.items():
|
||||
for workers in (1, 8, 12):
|
||||
name = f"determinism-{compiler}-w{workers}"
|
||||
log, layout = OUT / f"{name}.log", OUT / f"{name}.json"
|
||||
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=0",
|
||||
f"--camera={CAMERA}", "--stream-hash", "--force-generate", "--seed=2",
|
||||
"--recipe=surface-world", "--render-distance-chunks=0",
|
||||
"--density-dispatch-report", "--audit-resolved-layout-passages",
|
||||
"--audit-passage-tile-seams", f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp_match = STAMP_RE.search(content)
|
||||
stamp = stamp_match.group(1) if stamp_match else None
|
||||
hashes = tuple(HASH_RE.findall(content))
|
||||
identity = (json.loads(layout.read_text(encoding="utf-8")).get("stable_identity")
|
||||
if layout.is_file() else None)
|
||||
audits = EXCLUSION_RE.findall(content)
|
||||
shell_ok = bool(re.search(
|
||||
r"^\[PassageShellAuditSummary\].*status=PASS$", content, re.M))
|
||||
passed = (proc.returncode == 0 and stamp is not None and bool(hashes)
|
||||
and len(audits) == 2 and shell_ok and bool(identity))
|
||||
if reference_stamp is None and passed:
|
||||
reference_stamp, reference_hashes, reference_identity = stamp, hashes, identity
|
||||
else:
|
||||
passed = (passed and stamp == reference_stamp and hashes == reference_hashes
|
||||
and identity == reference_identity)
|
||||
row = {"compiler": compiler, "workers": workers, "exit": proc.returncode,
|
||||
"stamp": stamp, "hash_lines": len(hashes),
|
||||
"hashes_match_first": hashes == reference_hashes,
|
||||
"layout_identity": identity,
|
||||
"layout_matches_first": identity == reference_identity,
|
||||
"exclusion_audits": len(audits), "shell_gate": shell_ok,
|
||||
"passed": bool(passed),
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
rows.append(row)
|
||||
print("[L148DeterminismRun] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
(OUT / "runs-partial.json").write_text(json.dumps(rows, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
if not passed:
|
||||
(OUT / "summary.json").write_text(json.dumps(
|
||||
{"status": "FAIL", "runs": rows}, indent=2) + "\n", encoding="utf-8")
|
||||
raise SystemExit(f"composed owner determinism mismatch; see {log}")
|
||||
summary = {"status": "PASS", "stamp": reference_stamp,
|
||||
"layout_identity": reference_identity, "runs": rows}
|
||||
(OUT / "summary.json").write_text(json.dumps(summary, indent=2) + "\n",
|
||||
encoding="utf-8")
|
||||
print(f"[L148DeterminismSummary] runs={len(rows)} source_stamp={reference_stamp} "
|
||||
f"status=PASS")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the L148 composed passage gates for all 16 owner seeds."""
|
||||
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/l148/seeds"
|
||||
BIN = ROOT / "Standalone/bin/linux/VoxelForgeCoreHeadless-l148-clang"
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
STAMP = "593551a985ec0d1acc271260e4c8f15a5261c90edb60e33a56ed6df27e269ffb"
|
||||
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
||||
BASELINES = {
|
||||
"tunnel-network": ROOT / "Saved/Linux/l135/optimized-seed-sweep.json",
|
||||
"vertical-shafts": ROOT / "Saved/Linux/l139/seeds/summary.json",
|
||||
}
|
||||
DISPATCH = {
|
||||
"tunnel-network": re.compile(
|
||||
r"^\[DensityDispatch\] strate=2 generator=TunnelNetwork "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M),
|
||||
"vertical-shafts": re.compile(
|
||||
r"^\[DensityDispatch\] strate=3 generator=VerticalShafts "
|
||||
r"recipe=Core\.Caves\.PrimitiveField version=1 "
|
||||
r"bound_path=Caves-native adapter=FCavesFieldAdapter$", re.M),
|
||||
}
|
||||
ROUTES = {
|
||||
"tunnel-network": re.compile(
|
||||
r"^\[ResolvedStrateRouteAudit\] strate=2 .*unsupported=0 "
|
||||
r"grade_failures=0 .*status=PASS$", re.M),
|
||||
"vertical-shafts": re.compile(
|
||||
r"^\[ResolvedStrateRouteAudit\] strate=3 .*unsupported=0 "
|
||||
r"grade_failures=0 .*status=PASS$", re.M),
|
||||
}
|
||||
CONTROLS = {
|
||||
"tunnel-network": re.compile(
|
||||
r"^\[TunnelNetworkBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
"vertical-shafts": re.compile(
|
||||
r"^\[VerticalShaftsBlockedRouteControl\].*expected=FAIL status=PASS$", re.M),
|
||||
}
|
||||
REQUIRED = {
|
||||
"passages": re.compile(r"^\[ResolvedPassageAuditSummary\].*status=PASS$", re.M),
|
||||
"seams": re.compile(r"^\[PassageTileSeamAudit\].*status=PASS$", re.M),
|
||||
"shell": re.compile(r"^\[PassageShellAuditSummary\].*status=PASS$", re.M),
|
||||
"naturalness": re.compile(r"^\[PassageNaturalnessSummary\].*status=PASS$", re.M),
|
||||
"mouth_profile": re.compile(r"^\[PassageMouthProfileSummary\].*status=PASS$", re.M),
|
||||
"ripple": re.compile(r"^\[PassageRippleAuditSummary\].*status=PASS$", re.M),
|
||||
"passage_interior": re.compile(
|
||||
r"^\[PassageInteriorCapsuleAuditSummary\].*status=PASS$", re.M),
|
||||
}
|
||||
EXCLUSION = {
|
||||
strate: re.compile(
|
||||
rf"^\[CavernPassageExclusionAudit\] strate={strate} primitives=\d+ "
|
||||
r"outside_joint=0 mouth_joint=\d+ negative_controls=1/1 "
|
||||
r"first_primitive=none first_passage=none status=PASS$", re.M)
|
||||
for strate in (2, 3)
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--kind", choices=tuple(BASELINES), required=True)
|
||||
args = parser.parse_args()
|
||||
if not BIN.is_file() or not DATA.is_file():
|
||||
raise SystemExit("missing L148 Clang binary or composed owner fixture")
|
||||
baseline_path = BASELINES[args.kind]
|
||||
baseline = json.loads(baseline_path.read_text(encoding="utf-8"))
|
||||
baseline_results = {int(row["seed"]): row for row in baseline["results"]}
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
mode_out = OUT / args.kind
|
||||
mode_out.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
partial = mode_out / "runs-partial.json"
|
||||
for seed in range(16):
|
||||
log = mode_out / f"seed{seed}.log"
|
||||
layout = mode_out / f"seed{seed}.json"
|
||||
old_layout = ROOT / f"Saved/Linux/l147/seeds/{args.kind}/seed{seed}.json"
|
||||
command = ["taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(BIN), str(DATA), "--world-assembler-prototype",
|
||||
"--stream-benchmark", "--stream-pipeline", "--workers=8", "--clip-radius=3",
|
||||
"--max-clip-level=8", "--max-level=0", "--force-generate", f"--seed={seed}",
|
||||
"--recipe=surface-world", "--density-dispatch-report",
|
||||
"--audit-resolved-layout-passages", "--audit-passage-tile-seams",
|
||||
f"--resolved-layout-output={layout}"]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as output:
|
||||
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
stamp = STAMP_RE.search(content)
|
||||
checks = {name: bool(pattern.search(content)) for name, pattern in REQUIRED.items()}
|
||||
checks["source_stamp"] = stamp is not None and stamp.group(1) == STAMP
|
||||
checks["dispatch"] = bool(DISPATCH[args.kind].search(content))
|
||||
checks["route"] = bool(ROUTES[args.kind].search(content))
|
||||
checks["blocked_route_control"] = bool(CONTROLS[args.kind].search(content))
|
||||
checks["strate2_exclusion_audit"] = bool(EXCLUSION[2].search(content))
|
||||
checks["strate3_exclusion_audit"] = bool(EXCLUSION[3].search(content))
|
||||
checks["three_passage_interiors"] = len(re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*interior_air=PASS", content, re.M)) == 3
|
||||
checks["three_interior_negative_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageInteriorNegativeControl\].*detected=true status=PASS$",
|
||||
content, re.M)) == 3
|
||||
checks["three_ripple_gap_controls"] = len(re.findall(
|
||||
r"^\[PassageRippleSamplingControl\].*status=PASS$", content, re.M)) == 3
|
||||
checks["three_blocked_passage_controls"] = len(re.findall(
|
||||
r"^\[ResolvedPassageBlockedControl\].*detected=true$", content, re.M)) == 3
|
||||
checks["passage_count"] = len(re.findall(
|
||||
r"^\[ResolvedPassageAudit\].*walkability=PASS$", content, re.M)) == 3
|
||||
checks["layout_output"] = layout.is_file()
|
||||
old_identity = None
|
||||
new_identity = None
|
||||
if layout.is_file():
|
||||
new_identity = json.loads(layout.read_text(encoding="utf-8")).get("stable_identity")
|
||||
if old_layout.is_file():
|
||||
old_identity = json.loads(old_layout.read_text(encoding="utf-8")).get("stable_identity")
|
||||
checks["layout_identity_matches_l147"] = (
|
||||
old_identity is not None and new_identity is not None
|
||||
and old_identity == new_identity)
|
||||
passed = proc.returncode == 0 and all(checks.values())
|
||||
prior = baseline_results.get(seed, {})
|
||||
prior_passed = bool(prior.get("passed", False))
|
||||
change = ("newly failing" if prior_passed and not passed else
|
||||
"newly passing" if not prior_passed and passed else
|
||||
"still passing" if prior_passed else "still failing")
|
||||
failed_checks = [name for name, ok in checks.items() if not ok]
|
||||
reason = (", ".join(failed_checks) if failed_checks else
|
||||
"all gates pass and resolved identity matches L147" if passed else
|
||||
f"process exit {proc.returncode}")
|
||||
row = {"kind": args.kind, "seed": seed,
|
||||
"prior_verdict": "PASS" if prior_passed else "FAIL",
|
||||
"verdict": "PASS" if passed else "FAIL", "change": change,
|
||||
"reason": reason, "exit": proc.returncode, "checks": checks,
|
||||
"layout_identity": new_identity,
|
||||
"elapsed_s": round(time.monotonic() - started, 3),
|
||||
"log": str(log.relative_to(ROOT)), "layout": str(layout.relative_to(ROOT))}
|
||||
results.append(row)
|
||||
print("[L148SeedSweep] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
partial.write_text(json.dumps(results, indent=2) + "\n", encoding="utf-8")
|
||||
passed_count = sum(row["verdict"] == "PASS" for row in results)
|
||||
summary = {"kind": args.kind, "source_stamp": STAMP,
|
||||
"baseline": str(baseline_path.relative_to(ROOT)), "seeds": list(range(16)),
|
||||
"passed": passed_count, "failed": 16 - passed_count, "results": results}
|
||||
summary_path = mode_out / "summary.json"
|
||||
summary_path.write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"[L148SeedSweepSummary] kind={args.kind} passed={passed_count}/16 "
|
||||
f"failed={16-passed_count} status={'PASS' if passed_count == 16 else 'FAIL'}",
|
||||
flush=True)
|
||||
return 0 if passed_count == 16 else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||