streaming: one supplemental far apply per frame when the budget is half unused (L121)
After the normal visible-apply count is reached and <50% of the 2 ms budget is spent, apply one horizon result (off during startup boost; voxel.StreamingFarApplySupplement=0 disables). 30 m/s UE, 5 pairs: far holes 4.38% -> 2.70%, far finish-to-apply p50 1.105 -> 0.114 s, worst tick 14.74 ms (<15.43), near coverage +0.3 pt, arrival whole-view -313 ms. Geometry unchanged (24/24 hashes), fast tier green. Adds trace-only RMC proxy publication timing. 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
161565936c
commit
5c519f5206
8 files changed
+1115
-5
No files matched your search
@@ -0,0 +1,285 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Five interleaved L121 apply-supplement pairs at 30 m/s."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import statistics
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[4]
|
||||
HOST = Path("/home/jahni/VFHost")
|
||||
EDITOR = Path("/home/jahni/UnrealEngine/Engine/Binaries/Linux/UnrealEditor-Cmd")
|
||||
DATA = ROOT / "Standalone/data/owner_world.json"
|
||||
HEADLESS = ROOT / "Saved/Linux/l108/bin/VoxelForgeCoreHeadless-l108-after-clang"
|
||||
OUT = ROOT / "Saved/Linux/l121/movement/far-apply-supplement"
|
||||
HOST_SAVED = HOST / "Saved/Linux/l121"
|
||||
HOST_OUT = HOST / "Plugins/VoxelForge/Saved/Linux/l121/far-apply-supplement"
|
||||
ACTIVE_DESIGN = "far_apply_supplement"
|
||||
SPAWN_M = (19.166015625, -14.2001953125, -753.3740234375)
|
||||
MOVING_SAMPLE = re.compile(r"^\[L79MovingSample\] (.*)$", re.M)
|
||||
MOVING_SUMMARY = re.compile(r"^\[L79MovingSummary\] (.*)$", re.M)
|
||||
FIELDS = re.compile(r"([a-zA-Z0-9_]+)=([^ ]+)")
|
||||
|
||||
|
||||
def p99(values: list[float]) -> float | None:
|
||||
if not values:
|
||||
return None
|
||||
values.sort()
|
||||
return values[max(0, math.ceil(0.99 * len(values)) - 1)]
|
||||
|
||||
|
||||
def parse_events(path: Path) -> list[dict]:
|
||||
events = []
|
||||
if not path.is_file():
|
||||
return events
|
||||
for line in path.read_text(encoding="utf-8", errors="replace").splitlines():
|
||||
if not line.startswith("{"):
|
||||
continue
|
||||
try:
|
||||
events.append(json.loads(line))
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
return events
|
||||
|
||||
|
||||
def summarize(rows: list[dict], phase: str) -> dict:
|
||||
moving = [row for row in rows if row["phase"] == "move"]
|
||||
dwell = [row for row in rows if row["phase"] == "dwell"]
|
||||
def moving_mean(name: str) -> float | None:
|
||||
values = [row[name] for row in moving if name in row]
|
||||
return statistics.mean(values) if values else None
|
||||
complete = next((row for row in dwell
|
||||
if row["near_ready_pct"] >= 99.999
|
||||
and row["far_hole_pct"] <= 0.001), None)
|
||||
end = dwell[-1] if dwell else {}
|
||||
return {
|
||||
"moving_samples": len(moving),
|
||||
"dwell_samples": len(dwell),
|
||||
"near_ready_moving_mean_pct": moving_mean("near_ready_pct"),
|
||||
"lod0_ready_moving_mean_pct": moving_mean("lod0_ready_pct"),
|
||||
"lod1_ready_moving_mean_pct": moving_mean("lod1_ready_pct"),
|
||||
"lod2_ready_moving_mean_pct": moving_mean("lod2_ready_pct"),
|
||||
"far_holes_moving_mean_pct": moving_mean("far_hole_pct"),
|
||||
"near_ready_dwell_end_pct": end.get("near_ready_pct"),
|
||||
"far_holes_dwell_end_pct": end.get("far_hole_pct"),
|
||||
"dwell_complete_after_stop_s": complete.get("since_stop_s") if complete else None,
|
||||
"phase": phase,
|
||||
}
|
||||
|
||||
|
||||
def run_headless(condition: str, repetition: int) -> dict:
|
||||
worker_count = 8
|
||||
folder = OUT / condition / "headless"
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
log = folder / f"fast-r{repetition}.log"
|
||||
camera = ",".join(f"{value:.12f}" for value in SPAWN_M) + ",0,0"
|
||||
command = [
|
||||
"taskset", "-c", "0-11", "prlimit", "--as=6442450944", "--",
|
||||
"timeout", "1740s", str(HEADLESS), str(DATA),
|
||||
"--world-assembler-prototype", "--stream-benchmark", "--stream-pipeline",
|
||||
f"--workers={worker_count}", "--clip-radius=3", "--max-clip-level=8",
|
||||
"--max-level=8", f"--camera={camera}", "--profile-lod-work",
|
||||
"--stream-scheduler-policy=3", "--moving-camera-speed-mps=30.0",
|
||||
"--moving-camera-duration-s=12", "--moving-camera-dwell-s=5",
|
||||
]
|
||||
started = time.monotonic()
|
||||
with log.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(command, cwd=ROOT, stdout=stream,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
content = log.read_text(encoding="utf-8", errors="replace")
|
||||
rows = []
|
||||
for match in MOVING_SAMPLE.finditer(content):
|
||||
row = dict(FIELDS.findall(match.group(1)))
|
||||
for name in ("t_s", "near_ready_pct", "walkable_ring_ready_pct",
|
||||
"far_hole_pct", "queue_update_ms"):
|
||||
if name in row:
|
||||
row[name] = float(row[name])
|
||||
row["phase"] = row.get("phase", "move")
|
||||
row["since_stop_s"] = max(0.0, row.get("t_s", 0.0) - 12.0)
|
||||
rows.append(row)
|
||||
engine_summary = {}
|
||||
if match := MOVING_SUMMARY.search(content):
|
||||
engine_summary = dict(FIELDS.findall(match.group(1)))
|
||||
if result.returncode or not rows or int(engine_summary.get("generation_failures", "-1")) != 0:
|
||||
raise RuntimeError(f"headless run failed: {log}; exit={result.returncode}")
|
||||
moving_updates = [row["queue_update_ms"] for row in rows
|
||||
if row["phase"] == "move" and "queue_update_ms" in row]
|
||||
return {
|
||||
"condition": condition, "platform": "headless", "speed_mps": 30.0,
|
||||
"repetition": repetition, "worker_count": worker_count,
|
||||
"exit_code": result.returncode,
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"update_work_p99_ms": p99(moving_updates),
|
||||
"update_work_max_ms": max(moving_updates, default=None),
|
||||
"log": str(log.relative_to(ROOT)), **summarize(rows, "headless"),
|
||||
}
|
||||
|
||||
|
||||
def run_unreal(condition: str, repetition: int) -> dict:
|
||||
core_limit = 12
|
||||
worker_cap = 1
|
||||
far_apply_supplement = 0 if condition == "baseline" else 1
|
||||
folder = OUT / condition / "unreal"
|
||||
host_folder = HOST_OUT / condition / "output"
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
trace = folder / f"fast-r{repetition}.trace.jsonl"
|
||||
abslog = folder / f"fast-r{repetition}.log"
|
||||
stdout = folder / f"fast-r{repetition}.stdout.log"
|
||||
export = host_folder / f"fast-r{repetition}"
|
||||
userdir = HOST_SAVED / "user" / condition / f"fast-r{repetition}"
|
||||
zendata = HOST_SAVED / "zen" / condition / f"fast-r{repetition}"
|
||||
ddc = HOST_SAVED / "DDC"
|
||||
for path in (export, userdir, zendata, ddc):
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
spawn_cm = ",".join(f"{value * 100.0:.3f}" for value in SPAWN_M)
|
||||
command = [
|
||||
str(EDITOR), str(HOST / "VFHost.uproject"), "-nullrhi", "-unattended",
|
||||
"-nop4", "-nosplash", "-NoSound", f"-corelimit={core_limit}",
|
||||
"-NoZenAutoLaunch", "-notraceserver", "-nocrashreports",
|
||||
"-DisablePlugins=AndroidFileServer", "-ddc=NoZenLocalFallback",
|
||||
f"-userdir={userdir}", f"-ZenDataPath={zendata}",
|
||||
f"-LocalDataCachePath={ddc}", "-run=VoxelForgeExplore", "-seed=0",
|
||||
"-archetype=TunnelNetwork", "-slot=0", "-modes=export", "-gameconfig=1",
|
||||
"-coreworldtest=1", "-ownerfourstrateplan=1", "-corevoxelsizecm=25",
|
||||
"-coreislandtest=0", "-corefallbacktest=0", "-coreofftest=0",
|
||||
"-coreplantest=0", "-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings",
|
||||
"-corehybridmesher=0", f"-out={export}",
|
||||
"-voxel.StreamingCoarseFirst=1", "-voxel.StreamingDynamicNearReserve=1",
|
||||
"-voxel.StreamingLegacyPendingResultCap=0", "-voxel.CoreRegionConcurrentAdmission=1",
|
||||
"-voxel.StreamingResultBacklogCap=256", "-voxel.StreamingReadyQueueDepth=8",
|
||||
f"-voxel.StreamingBackgroundWorkerCap={worker_cap}",
|
||||
"-voxel.CoreColumnCacheDirectMapped=0",
|
||||
f"-voxel.StreamingFarApplySupplement={far_apply_supplement}",
|
||||
"-voxel.CoreMCGradientMemoization=1", "-voxel.StreamingCollisionLookahead=1",
|
||||
"-voxel.TestMove=1", "-voxel.TestMoveSpeedCmPerSecond=3000.0",
|
||||
"-voxel.TestMoveStartSeconds=6.0", "-voxel.TestMoveDurationSeconds=12.0",
|
||||
"-voxel.TestExitSeconds=23.0", "-voxel.TestMoveDirection=1,0,0",
|
||||
"-voxel.StreamingSchedulerPolicy=3", "-voxel.TestExitOnSteady=0",
|
||||
"-coreworldstreamtrace=1", f"-voxel.StartupTraceSpawnCm={spawn_cm}",
|
||||
"-voxel.StartupTraceUntilEndPlay=1", "-voxel.StartupTraceThroughCrossing=1",
|
||||
"-voxel.StartupTraceMaxTiles=16384", f"-voxel.StartupTraceFile={trace}",
|
||||
f"-abslog={abslog}",
|
||||
]
|
||||
wrapped = ["taskset", "-c", "0-11", "timeout", "1740s", *command]
|
||||
started = time.monotonic()
|
||||
with stdout.open("w", encoding="utf-8") as stream:
|
||||
result = subprocess.run(wrapped, cwd=ROOT, stdout=stream,
|
||||
stderr=subprocess.STDOUT, check=False, timeout=1740)
|
||||
events = parse_events(trace)
|
||||
samples = [event for event in events if event.get("type") == "streaming_sample"]
|
||||
moving_samples = [event for event in samples
|
||||
if float(event.get("speed_cm_s", 0.0)) >= 50.0]
|
||||
move_start = min((float(event.get("t_s", 0.0)) for event in moving_samples),
|
||||
default=float("inf"))
|
||||
stop_event = next((event for event in events
|
||||
if event.get("type") == "streaming_test_move_stopped"), None)
|
||||
stop = float(stop_event.get("t_s", 0.0)) if stop_event else float("inf")
|
||||
rows = []
|
||||
for event in samples:
|
||||
desired = event.get("desired_by_lod", [0] * 9)
|
||||
resident = event.get("resident_by_lod", [0] * 9)
|
||||
covered = event.get("covered_by_lod", resident)
|
||||
near_desired = sum(desired[:3])
|
||||
near_resident = sum(resident[:3])
|
||||
far_desired = sum(desired[3:])
|
||||
far_covered = sum(covered[3:])
|
||||
t_s = float(event.get("t_s", 0.0))
|
||||
speed = float(event.get("speed_cm_s", 0.0))
|
||||
phase = "move" if speed >= 50.0 else ("dwell" if t_s >= stop else "arrival")
|
||||
rows.append({
|
||||
"phase": phase, "t_s": t_s,
|
||||
"since_stop_s": max(0.0, t_s - stop) if phase == "dwell" else None,
|
||||
"near_ready_pct": 100.0 * near_resident / max(1, near_desired),
|
||||
"lod0_ready_pct": 100.0 * resident[0] / max(1, desired[0]),
|
||||
"lod1_ready_pct": 100.0 * resident[1] / max(1, desired[1]),
|
||||
"lod2_ready_pct": 100.0 * resident[2] / max(1, desired[2]),
|
||||
"far_hole_pct": 100.0 * (far_desired - far_covered) / max(1, far_desired),
|
||||
})
|
||||
dwell_rows = [row for row in rows if row["phase"] == "dwell"]
|
||||
complete = next((row for row in dwell_rows
|
||||
if row["near_ready_pct"] >= 99.999
|
||||
and row["far_hole_pct"] <= 0.001), None)
|
||||
moving_work = [event for event in events
|
||||
if event.get("type") == "game_thread_work_frame"
|
||||
and move_start <= float(event.get("t_s", 0.0)) < stop]
|
||||
ticks = [float(event.get("tick_ms", 0.0)) for event in moving_work]
|
||||
updates = [float(event.get("update_chunks_ms", 0.0)) for event in moving_work]
|
||||
backend = next((event for event in events
|
||||
if event.get("type") == "worker_backend_config"), {})
|
||||
worker_cap_readback = next((event for event in events
|
||||
if event.get("type") == "streaming_worker_cap_readback"), {})
|
||||
if result.returncode or len(moving_samples) < 40 or len(dwell_rows) < 16:
|
||||
raise RuntimeError(f"Unreal run failed/incomplete: {trace}; exit={result.returncode}; "
|
||||
f"move={len(moving_samples)} dwell={len(dwell_rows)}")
|
||||
expected_far_supplement = 0 if condition == "baseline" else 1
|
||||
expected_near_priority = "BackgroundLow"
|
||||
if (backend.get("near_priority") != expected_near_priority
|
||||
or backend.get("horizon_priority") != "BackgroundLow"
|
||||
or int(worker_cap_readback.get("streaming_background_worker_cap", -1)) != worker_cap
|
||||
or int(backend.get("streaming_far_apply_supplement", -1)) != expected_far_supplement
|
||||
or int(backend.get("effective_tile_slots", -1)) != (8 if worker_cap else 10)):
|
||||
raise RuntimeError(f"priority CVar readback mismatch in {trace}: {backend}")
|
||||
return {
|
||||
"condition": condition, "platform": "unreal", "speed_mps": 30.0,
|
||||
"repetition": repetition, "core_limit": core_limit,
|
||||
"effective_tile_slots": backend.get("effective_tile_slots"),
|
||||
"streaming_background_worker_cap": worker_cap_readback.get(
|
||||
"streaming_background_worker_cap"),
|
||||
"taskgraph_background_threads": backend.get("taskgraph_background_threads"),
|
||||
"exit_code": result.returncode,
|
||||
"wall_s": round(time.monotonic() - started, 3),
|
||||
"game_thread_tick_p99_ms": p99(ticks),
|
||||
"game_thread_tick_max_ms": max(ticks, default=None),
|
||||
"stream_update_p99_ms": p99(updates),
|
||||
"stream_update_max_ms": max(updates, default=None),
|
||||
"dwell_complete_after_stop_s": complete.get("since_stop_s") if complete else None,
|
||||
"moving_samples": len(moving_samples), "dwell_samples": len(dwell_rows),
|
||||
"trace": str(trace.relative_to(ROOT)),
|
||||
"log": str(abslog.relative_to(ROOT)),
|
||||
**summarize(rows, "unreal"),
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
global ACTIVE_DESIGN, OUT, HOST_SAVED, HOST_OUT
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--platform", choices=("all", "headless", "unreal"), default="all")
|
||||
parser.add_argument("--design", choices=("far_apply_supplement",),
|
||||
default="far_apply_supplement")
|
||||
args = parser.parse_args()
|
||||
ACTIVE_DESIGN = args.design
|
||||
design_path = "far-apply-supplement"
|
||||
OUT = ROOT / "Saved/Linux/l121/movement" / design_path
|
||||
HOST_SAVED = HOST / "Saved/Linux/l121" / design_path
|
||||
HOST_OUT = HOST / "Plugins/VoxelForge/Saved/Linux/l121" / design_path
|
||||
if not DATA.is_file() or not HEADLESS.is_file() or not EDITOR.is_file():
|
||||
raise SystemExit("missing owner data, L108 headless binary, or Unreal editor")
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
results = []
|
||||
partial = OUT / "runs-partial.json"
|
||||
for repetition in range(1, 6):
|
||||
candidate = "far_supplement"
|
||||
conditions = ("baseline", candidate) if repetition % 2 else (candidate, "baseline")
|
||||
platforms = (("headless", "unreal") if repetition % 2 else ("unreal", "headless")) \
|
||||
if args.platform == "all" else (args.platform,)
|
||||
for condition in conditions:
|
||||
for platform in platforms:
|
||||
row = (run_headless(condition, repetition) if platform == "headless"
|
||||
else run_unreal(condition, repetition))
|
||||
results.append(row)
|
||||
partial.write_text(json.dumps(results, indent=2) + "\n", encoding="utf-8")
|
||||
print("[L121Movement] " + json.dumps(row, sort_keys=True), flush=True)
|
||||
design_description = "one supplemental far result after the normal four applies, with the 2 ms time budget"
|
||||
(OUT / "summary.json").write_text(
|
||||
json.dumps({"design": design_description,
|
||||
"runs": results}, indent=2) + "\n", encoding="utf-8")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user