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
133 lines
4.1 KiB
Python
Executable File
133 lines
4.1 KiB
Python
Executable File
#!/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)
|