core: cave planner holds for 16 seeds; mouth joints planned from the resolved mouth (L135, v236)
Mouth floor/profile carried into the first/last edges (fixes seeds 1 and 2); fixed a Q16/Q32 scale bug in the mouth clearance scorer; shortest grade-safe handles; bounded cave AABBs and hoisted edge-BVH normalisation. Seeds 0-15 pass all composed audits and negative controls; fast tier, Clang/GCC W1/W8/W12 green. Near-ready still +3.9..+13.7% vs assembler/world (Surface World +3.9..+8.2% with no caves). Not for merge. 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,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 |