Files
builds/.agents/handoffs/forge-windows-parallel-replay.md
T

4.7 KiB
Raw Blame History

Forge: Windows A/B of parallel draw-list replay + recording (engine a967d12b)

Why: Windows sits at 45–50 fps with the RTX 5080 idle. The render thread is the limiter (10–13 ms wall, end_frame replay + submit 3.8–4.8 ms). The NVIDIA/WDDM driver path is paid serially on that thread for every vkCmd*. Engine 06ecc5df can cut large draw lists into chunks and record them into secondary command buffers on worker threads (Vulkan only, default off). Design + Mac results: engine .agents/wiki/parallel-draw-replay.md.

Since then the engine also does parallel recording (Forward+ splits a pass's render list over worker threads, each recording its own slice of the draw list; every driver; default off) — gpucull record on|off|status. It attacks the other half of the render thread (Godot's per-draw bookkeeping before the driver). Same page, § Parallel recording.

Nothing is published or pinned for this yet. This is a test build on Forge only.

1. Build + oracle (Vulkan, Khronos layer)

  1. git checkout a967d12b in the engine (replay + recording), build the Windows editor with the windows-x64 manifest flags. GodotSharp regen is not needed for the test: the game reaches the new API through Call.
  2. With the Vulkan SDK's validation layer available (--gpu-validation turns it on): bash tests/gpu_oracle/parallel_draw_replay/run.sh <tinqs.windows.editor.x86_64.mono.exe> vulkan Expect ORACLE: 10 passed, 0 failed, exit 0, zero VUID / validation lines in the output. Paste the output back.

2. In-game A/B (box otherwise idle, one engine, nothing else running)

The game pins 4.7.2@e64890fd3; skip the pin for this test build:

TINQS_ENGINE_SKIP_CHECK=1 GAME_ARGS=--gpu-profile GAME_RES=1600x900 bash tools/game.sh run
READY_TIMEOUT=400 bash tools/game.sh ready
curl -s -X POST 127.0.0.1:4328/command -d '{"command":"host_game"}'   # if it stops at the menu
# wait until in the world with fps settled (~50 s+), then:
curl -s -X POST 127.0.0.1:4328/command -d '{"command":"gpucull","value":"replay status"}'   # supported True
LOG="$APPDATA/Tinqs/app_userdata/Ariki/logs/godot.log"
OUT=.game-cli/replay-windows-$(date +%Y%m%d-%H%M)
ARIKI_API=http://127.0.0.1:4328 python tools/render_thread_profile.py --log "$LOG" \
  --poses tools/render_thread_poses.json --out "$OUT" --blocks 8 \
  --states "both off=gpucull:replay off;gpucull:record off" "replay=gpucull:replay on;gpucull:record off" \
           "record=gpucull:replay off;gpucull:record on" "both on=gpucull:replay on;gpucull:record on" \
  --restore "gpucull:replay off;gpucull:record off"

Also take three curl -s 127.0.0.1:4328/mainthread reads with replay off and with replay on (from e3582841a). They show which thread paces the frame.

Also: curl -s -X POST 127.0.0.1:4328/command -d '{"command":"occlusion","value":"off"}' vs on at the spawn pose (5 /perf reads each) — the 1.2–1.3 ms "Update Occlusion Buffer" label decides whether CPU occlusion culling earns its cost now that vegetation is GPU-culled.

Also, after the run above (it can be its own run if the first one has already started), GPU depth-pyramid occlusion for the gpu-culled vegetation (rendering/occlusion_culling/gpu_hzb, default off). It costs the render thread nothing. On the Mac it cut kept plants by 16 % and GPU time by ~2 ms; it has never been measured on Windows:

OUT=.game-cli/hzb-windows-$(date +%Y%m%d-%H%M)
ARIKI_API=http://127.0.0.1:4328 python tools/render_thread_profile.py --log "$LOG" \
  --poses tools/render_thread_poses.json --out "$OUT" --blocks 8 \
  --states "hzb off=gpucull:hzb off" "hzb on=gpucull:hzb on" \
  --restore "gpucull:hzb off"

Eye check with hzb on: turn the camera fast past a hill and a house. Plants must not pop in a frame late or vanish while visible.

Report per pose and state: frame ms, _draw wall, end_frame, render-CPU labels, fps p25, /perf gpucullCounters.parallelReplay (lists / chunks), pixel diff vs the A/A floor, and the full results.json.

3. Soak + eye check

gpucull replay on, walk 10 minutes (shore, inland, village, a tree felling), with --gpu-validation on a second run: no device lost, no PARALLEL-REPLAY-BUG, no validation errors. Eye check where tree/rock shadows fall on open sand. A stepped shadow edge was seen in one Mac capture and is probably a static-tile boundary; compare replay off vs on at the same spot.

Decision rule

Default flips on (rendering/rendering_device/parallel_draw_replay/enabled=true in the game's project.godot, then publish + pin) only if: oracle clean, soak clean, and frame time / end_frame drop at all 3 poses. gpu_hzb flips on (game project.godot) if frame time and render-GPU ms do not rise at any pose and the eye check is clean.