Commit Graph
1 Commits
Author SHA1 Message Date
HayatoGandClaude Opus 5.5 420ab44676 Frame recorder: TLG_FRAME_RECORD_TRIGGER records two frames command by command
For effects that flicker in a static scene (fog/volumetrics, sea foam, lights): once the
trigger file exists, the next two complete frames (flip to flip, on the GPU command thread)
are written to TLG_DUMP_DIR/frame_record_0.txt and _1.txt, then the file is removed.

One line per command in the order the GPU thread runs them, graphics and async compute
queues (q=gfx / q=c<vqid>): draws, indirect draws, dispatches, DMA, events, WaitRegMem,
WriteData, markers, flips. Draws and dispatches carry the pgm hash and permutation index of
each stage, a hash of the user data, every bound buffer (address, size, hash of up to 64 KiB),
images (address, format, size, mips), sampler hashes, vertex/index buffers, render targets and
depth, viewport, scissor and blend/depth/stencil registers, counts or dispatch sizes.
Indirect arguments are given as the guest wrote them (args.cpu) and as the GPU read them at
that draw (args).

Buffer contents: memory the GPU wrote (buffer cache GPU-modified) is copied from the GPU
buffer at the draw into a staging buffer (TLG_FRAME_RECORD_GPU_MB, default 256) and hashed
once the frames are done (g...); everything else is hashed from guest memory (c...) under
runtime_fault_recover, so a fault (unmapped or read-protected page) gives "fault" instead of
a readback or a crash on the GPU thread. The copies end the render pass; a draw that clears
its targets inside the pass in progress skips them (gpu=skipped_clear) so nothing is cleared
twice.

While armed or recording the draw pipeline is off (one thread, stream order; one warm-up
frame lets it run dry). Not triggered: one relaxed atomic load per PM4 packet and per
UseDrawPipe call, a getenv-cached check per flip.

scripts/frame_diff.py aligns the two lists by queue, kind and shaders and prints the commands
whose inputs differ, grouped by shaders, most suspicious first (commands in one frame only,
indirect args, counts, state, GPU-written buffers...). Changes shared by many commands
(camera constants, TAA ping-pong) are listed once as frame-global and weigh little.

Verified: Linux build (TLG_LTO=OFF) clean; frame_diff.py on synthetic records. Not run in
game yet (the user tests); the shader cache key is unchanged (no recompiler or pipeline
cache sources touched).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01JyWhQUvUQf2YSf8HJWnAtd
2026-10-13 23:20:27 -03:00