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
146 lines
6.7 KiB
Python
146 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Compare owner canonical hashes across Clang/GCC and W1/W8/W12."""
|
|
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/l173/canonical-hashes"
|
|
DATASETS = {
|
|
"owner4": ROOT / "Standalone/data/owner_world.json",
|
|
"owner2": ROOT / "Standalone/data/regression/owner_world_2strate_l61.json",
|
|
}
|
|
RECIPES = {"owner4": ("surface-world",),
|
|
"owner2": ("surface-world", "floating-islands")}
|
|
BINARIES = {
|
|
"clang": ROOT / "Saved/Linux/l173/fast/bin/VoxelForgeCoreHeadless-fast-clang",
|
|
"gcc": ROOT / "Saved/Linux/l173/bin/VoxelForgeCoreHeadless-l173-gcc",
|
|
}
|
|
BASELINES = {
|
|
("owner2", "surface-world"): (
|
|
"owner-hash-surface-world-style1-v227.baseline",
|
|
"owner-hash-surface-world-style1-v237.baseline",
|
|
"owner-hash-surface-world-style1-v239.baseline"),
|
|
("owner2", "floating-islands"): (
|
|
"owner-hash-floating-islands-style2-v227.baseline",
|
|
"owner-hash-floating-islands-style2-v237.baseline",
|
|
"owner-hash-floating-islands-style2-v239.baseline"),
|
|
("owner4", "surface-world"): (
|
|
"owner-hash-owner4-surface-world-style1-v227.baseline",
|
|
"owner-hash-owner4-surface-world-style1-v239.baseline"),
|
|
}
|
|
BASELINE_ROOT = ROOT / "Standalone/Tools/verify/baselines/owner-hash/canonical"
|
|
HASH_RE = re.compile(
|
|
r"^\[Pipeline(?:FullDetailHash|WholeViewHash|LODHash|ExactQueryHash)\].*$", re.M)
|
|
STAMP_RE = re.compile(r"^\[VoxelForgeSourceStamp\] sha256=([0-9a-f]+)$", re.M)
|
|
|
|
|
|
def main() -> int:
|
|
OUT.mkdir(parents=True, exist_ok=True)
|
|
stamp = subprocess.check_output(
|
|
[sys.executable, str(ROOT / "Standalone/Tools/verify/source_stamp.py"),
|
|
"stamp", "--root", str(ROOT)], text=True).strip()
|
|
baseline_hashes = {}
|
|
for key, filenames in BASELINES.items():
|
|
baseline_hashes[key] = {}
|
|
for filename in filenames:
|
|
path = BASELINE_ROOT / filename
|
|
if path.is_file():
|
|
baseline_hashes[key][filename] = HASH_RE.findall(
|
|
path.read_text(encoding="utf-8", errors="replace"))
|
|
|
|
rows = []
|
|
groups = {}
|
|
partial = OUT / "runs-partial.json"
|
|
for dataset, recipes in RECIPES.items():
|
|
for recipe in recipes:
|
|
signatures = []
|
|
for workers in (1, 8, 12):
|
|
for compiler in ("clang", "gcc"):
|
|
binary = BINARIES[compiler]
|
|
label = f"{dataset}-{recipe}-{compiler}-w{workers}"
|
|
log = OUT / f"{label}.log"
|
|
layout = OUT / f"{label}.layout.json"
|
|
command = [
|
|
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
|
"timeout", "1740s", str(binary), str(DATASETS[dataset]),
|
|
"--world-assembler-prototype", "--stream-benchmark",
|
|
"--stream-pipeline", "--force-generate", f"--workers={workers}",
|
|
"--clip-radius=3", "--max-clip-level=8", "--max-level=8",
|
|
f"--recipe={recipe}", "--stream-hash",
|
|
f"--resolved-layout-output={layout}",
|
|
]
|
|
# Rebuild owner4's Core plan from the same exported seed. Its checked-in
|
|
# Unreal snapshot currently fails canonical import after the planner merge.
|
|
if dataset == "owner4":
|
|
command.append("--seed=0")
|
|
started = time.monotonic()
|
|
code: int | str
|
|
try:
|
|
with log.open("w", encoding="utf-8") as output:
|
|
proc = subprocess.run(command, cwd=ROOT, stdout=output,
|
|
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
|
code = proc.returncode
|
|
except subprocess.TimeoutExpired:
|
|
code = "OUTER_TIMEOUT"
|
|
output = log.read_text(encoding="utf-8", errors="replace") \
|
|
if log.is_file() else ""
|
|
hashes = HASH_RE.findall(output)
|
|
stamp_match = STAMP_RE.search(output)
|
|
run_ok = (code == 0 and len(hashes) == 12 and stamp_match is not None
|
|
and stamp_match.group(1) == stamp)
|
|
row = {
|
|
"dataset": dataset, "recipe": recipe, "compiler": compiler,
|
|
"workers": workers, "seed_override": 0 if dataset == "owner4" else None,
|
|
"exit_code": code, "run_passed": run_ok,
|
|
"hashes": hashes,
|
|
"source_stamp": stamp_match.group(1) if stamp_match else None,
|
|
"elapsed_s": round(time.monotonic() - started, 3),
|
|
"log": str(log.relative_to(ROOT)),
|
|
}
|
|
rows.append(row)
|
|
signatures.append(row)
|
|
print("[L173CanonicalHashRun] " + json.dumps(row, sort_keys=True),
|
|
flush=True)
|
|
partial.write_text(json.dumps(rows, indent=2) + "\n", encoding="utf-8")
|
|
|
|
expected = baseline_hashes[(dataset, recipe)]
|
|
first = signatures[0]["hashes"] if signatures else []
|
|
deterministic = (len(signatures) == 6
|
|
and all(row["run_passed"] and row["hashes"] == first
|
|
for row in signatures))
|
|
baseline_matches = {
|
|
name: bool(first) and first == values
|
|
for name, values in expected.items()
|
|
}
|
|
groups[f"{dataset}/{recipe}"] = {
|
|
"all_compilers_workers_match": deterministic,
|
|
"hash_rows": len(first),
|
|
"baseline_matches": baseline_matches,
|
|
"source_stamp": stamp,
|
|
}
|
|
print("[L173CanonicalHashGroup] " + json.dumps(
|
|
{"group": f"{dataset}/{recipe}", **groups[f"{dataset}/{recipe}"]},
|
|
sort_keys=True), flush=True)
|
|
|
|
passed = (len(rows) == 18
|
|
and all(row["run_passed"] for row in rows)
|
|
and all(group["all_compilers_workers_match"] for group in groups.values()))
|
|
summary = {"status": "PASS" if passed else "FAIL", "source_stamp": stamp,
|
|
"runs": rows, "groups": groups}
|
|
(OUT / "summary.json").write_text(
|
|
json.dumps(summary, indent=2) + "\n", encoding="utf-8")
|
|
print(f"[L173CanonicalHashSummary] status={summary['status']} runs={len(rows)} "
|
|
f"groups={len(groups)}", flush=True)
|
|
return 0 if passed else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|