experiment: LivingPlane repeatable + standalone UE runtime module (L164)
Readback barrier fix (GL_TEXTURE_UPDATE_BARRIER_BIT before glGetTexImage); check_repeatability.py compares full-field checksums across query/capture settings - Intel GPU PASS (3b9d45905c7aafff), llvmpipe PASS, mismatch control fails. New runtime module VoxelForgeLivingPlane (separate from generation): RDG global shader (Shaders/LivingPlane.usf), ping-pong RGBA32F render targets, actor with WPO plane and object inputs, no CPU readback. Compiles in Linux UE 5.7. Windows build: Source/VoxelForgeLivingPlane/*, Shaders/LivingPlane.usf, VoxelForge.uplugin. Setup: UE-SETUP.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
e1d87eed26
commit
3b71d35eba
13 files changed
+868
-8
No files matched your search
+132
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare final field checksums across different query and capture settings."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import pathlib
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
PLUGIN_ROOT = pathlib.Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def read_checksum(path: pathlib.Path) -> str:
|
||||
try:
|
||||
with path.open(encoding="utf-8") as source:
|
||||
result = json.load(source)
|
||||
except (OSError, json.JSONDecodeError) as error:
|
||||
raise RuntimeError(f"could not read {path}: {error}") from error
|
||||
checksum = result.get("field_checksum_fnv1a64")
|
||||
if not isinstance(checksum, str) or len(checksum) != 16:
|
||||
raise RuntimeError(f"{path} has no 16-digit field checksum")
|
||||
return checksum
|
||||
|
||||
|
||||
def compare(left: pathlib.Path, right: pathlib.Path) -> bool:
|
||||
left_checksum = read_checksum(left)
|
||||
right_checksum = read_checksum(right)
|
||||
if left_checksum != right_checksum:
|
||||
print(
|
||||
"FAIL field checksum mismatch: "
|
||||
f"{left}={left_checksum}, {right}={right_checksum}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False
|
||||
print(f"PASS field checksum matches: {left_checksum}")
|
||||
return True
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--binary",
|
||||
type=pathlib.Path,
|
||||
default=PLUGIN_ROOT / "Saved/Linux/l164/living-plane",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--out",
|
||||
type=pathlib.Path,
|
||||
default=PLUGIN_ROOT / "Saved/Linux/l164/repeatability",
|
||||
)
|
||||
parser.add_argument("--device", choices=("auto", "cpu", "gpu"), default="cpu")
|
||||
parser.add_argument("--size", type=int, default=512)
|
||||
parser.add_argument("--steps", type=int, default=1800)
|
||||
parser.add_argument(
|
||||
"--compare-only",
|
||||
nargs=2,
|
||||
type=pathlib.Path,
|
||||
metavar=("LEFT_JSON", "RIGHT_JSON"),
|
||||
help="compare existing timing.json files; used to exercise the failure path",
|
||||
)
|
||||
options = parser.parse_args()
|
||||
|
||||
if options.compare_only:
|
||||
return 0 if compare(*options.compare_only) else 1
|
||||
|
||||
if not options.binary.is_file():
|
||||
parser.error(f"binary not found: {options.binary}; run Experiments/LivingPlane/build.sh")
|
||||
|
||||
options.out.mkdir(parents=True, exist_ok=True)
|
||||
off_dir = options.out / "queries-off_capture-off"
|
||||
on_dir = options.out / "queries-on_capture-every-900"
|
||||
for directory in (off_dir, on_dir):
|
||||
if directory.exists():
|
||||
shutil.rmtree(directory)
|
||||
|
||||
common = [
|
||||
f"--device={options.device}",
|
||||
"--scene=alive",
|
||||
f"--size={options.size}",
|
||||
f"--steps={options.steps}",
|
||||
]
|
||||
runs = (
|
||||
("--capture-every=0", "--queries=off", off_dir),
|
||||
("--capture-every=900", "--queries=on", on_dir),
|
||||
)
|
||||
for capture, queries, output in runs:
|
||||
command = [
|
||||
"taskset",
|
||||
"-c",
|
||||
"0-11",
|
||||
"prlimit",
|
||||
"--as=6442450944",
|
||||
"--",
|
||||
"timeout",
|
||||
"1740s",
|
||||
str(options.binary.resolve()),
|
||||
*common,
|
||||
capture,
|
||||
queries,
|
||||
f"--out={output}",
|
||||
]
|
||||
print("RUN " + " ".join(command), flush=True)
|
||||
log_path = options.out / f"{output.name}.log"
|
||||
with log_path.open("w", encoding="utf-8") as log_file:
|
||||
try:
|
||||
subprocess.run(
|
||||
command,
|
||||
cwd=PLUGIN_ROOT,
|
||||
check=True,
|
||||
stdout=log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
print(log_path.read_text(encoding="utf-8"), file=sys.stderr)
|
||||
raise
|
||||
print(log_path.read_text(encoding="utf-8").rstrip())
|
||||
|
||||
left = off_dir / "timing.json"
|
||||
right = on_dir / "timing.json"
|
||||
return 0 if compare(left, right) else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except (OSError, RuntimeError, subprocess.CalledProcessError) as error:
|
||||
print(f"FAIL repeatability check: {error}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
Reference in new issue
Block a user