The recorder took its ranges once, at the snapshot, so a title that
committed memory during the captured frames replayed submissions from
memory the capture never had. Each memory delta now adds the allocations
the registry gained since the last one as new ranges with their nonzero
64 KiB blocks, and the header counts them as late_ranges.
A submission ended a captured frame when the driver's flip count changed
while it was being submitted, and the count was global, so a submission
that overlapped another thread's flipping one ended the frame as well.
The count is now kept per thread.
agc_driver_frame_replay copies its second frame from a block committed
during the capture, and agc_driver_frame_capture_eop checks that a flip
submitted on another thread leaves this thread's count unchanged; both
fail without the fix.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Order memory deltas and video output registration before the next
recorded call, and serialize the deadlock breaker with delta application
- Capture the blocks holding shader code with each shader delta
- Reserve explicit and automatic EOP flip tokens under one lock
- Record video output unregistration so replay can close and reopen a handle
- Clear already mapped ranges before restoring the snapshot so elided zero
blocks replay as zero
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An EOP flip reservation also ended a frame, and so did the submission
whose EOP release carries the reserved token, so a title flipping with
EOP releases counted each displayed frame twice: a capture of N frames
stopped after about N/2 and could start between the reservation and
the submission that flips. The reservation is now only recorded; a
frame ends at a CPU flip or at a submission that flips.
agc_driver_frame_capture_eop reserves and submits six EOP flips with
APS5_FRAME_CAPTURE_FRAMES=3 and checks that the capture starts after
the first flipping submission and holds three frames of one reservation
and one submission each.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
APS5_FRAME_CAPTURE=<dir> makes the AGC driver record a few frames of a
title: the shader registrations and ABI resolutions, video output and
EOP flip reservations it made since it started, a snapshot of every
readable guest allocation (the title image included) taken at a frame
boundary once the driver is idle, and for the next frames each
submission, flip and suspend point with the thread that made it, its
serial progress and the guest pages the write tracker saw the CPU write
before it. At the end it stores the presented display buffers as
reference images. APS5_FRAME_CAPTURE_AT_FLIP, _TRIGGER and _FRAMES pick
the frames.
agc_frame_replay maps the captured memory at its addresses and replays
the calls of each captured thread on a thread of its own, in captured
order, through the current build of the driver with no title running.
It paces GPU work to the in-flight depth the title had, releases GPU
waits that the title's CPU threads released without calling the driver
(AgcDriverAwaitedWaits), and reports frame times, released waits and
how closely the replayed display buffers match the references.
Tests: agc_frame_capture (file format) and agc_driver_frame_replay
(two captured frames of CPU writes and DMA copies replayed and
compared). Docs: BUILD.md, ENVIRONMENT.md and the libSceAgcDriver debt
file.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The note told Windows users to set TEMP and TMP to a directory the scanner
excludes, but since 39dd854c8 CTest sets TMPDIR, TEMP and TMP of the
relinker tests to core/relinker/test-temp in the build directory, so those
variables no longer move the executables. Name that directory instead and
add windows_bss and windows_fixed_base, which fail the same way when the
scanner blocks the executables they run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add libogg 1.3.6 and libvorbis 1.3.7 as submodules, built from source as
hidden static libraries, and implement the 31 libvorbisfile exports on top
of them. Decoder state lives in a host table keyed by the guest
OggVorbis_File, guest callbacks are trampolined, and vorbis_info is
returned in its LP64 layout.
libusb's CMake generates version_describe.h by running gen-describe.ps1
with -ExecutionPolicy Bypass. An execution policy set by Group Policy
takes precedence over that switch, so with Restricted or AllSigned the
Windows build stops at version_describe.h. Describe the failure and a
.cmd stand-in, passed as LIBUSB_POWERSHELL_EXECUTABLE, that runs
gen-describe.sh with Git for Windows' bash as the PowerShell script does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The start-up f32 denormal flush probe already stays out of the pipeline
counters and the pipeline journal, but APS5_DUMP_COMPUTE_PIPELINES still
captured its pipelines. On a device that runs the probe, every capture
directory gained twelve driver-internal records that count toward the
128-record limit, and agc_compute_pipeline_capture_run found 14 captures
for a fixture with two programs.
Queue 0's draw committer records the draws the packet worker hands
over. When the worker's APS5_SAMPLE_WORKER stacks wait in
DrawPipeline::Enqueue, the committer is the thread that bounds the
frame, and only APS5_SAMPLE_THREADS sees it: 40 addresses, 12 frames
deep, without whole stacks.
APS5_SAMPLE_COMMITTER=<file> attaches the worker's sampler to the
committer when its thread starts and writes the same two files. Both
use one start function, and StopWorkerSampler stops both.
Review of the benchmark report:
- The file is written to `<name>.tmp` and moved over the previous one, so a
crash or kill during a rewrite keeps the last complete report.
- A dedicated writer keeps only the newest pending snapshot, so two reports
can no longer be joined into one file when the writer falls behind.
- The report is formatted on the writer thread. The presenter hands over the
kept intervals, which are shared and never copied, so the interval that
closes no longer pays for formatting every earlier one.
- Hosts that are not x86 or report no CPU brand string leave the host fields
out instead of failing every summary line.
- A `contentVersion` that is not a string, or does not fit, is left empty
instead of failing the title's metadata.
- Tests cover the 240-interval rotation and `intervals_dropped`, the
newest-only writer, the pending write on stop and the atomic replace.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The summary line names the CPU, its thread count, physical memory and,
with param.json, the title ID, name and content version.
APS5_TRACE_BENCH=1 with APS5_TRACE_SUMMARY_EVERY rewrites
aps5-bench-<commit>-<pid>.json.gz at each interval with the time between
every two presents, so reports can be recomputed and checked from raw
frame times. param.json's contentVersion is read when present.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Shader registration and the shader ABI resolves run on the title's own
threads, which never bound a trace buffer, so their Shader.Register and
Shader.Resolve* scopes were missing from every trace and summary. Those
entry points now bind the calling thread as `title` with the 32,768-event
buffer the shader preparation threads use.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`Path.write_text(newline=...)` and `zip(strict=True)` were added in
Python 3.10. macOS ships 3.9.6 as /usr/bin/python3, the interpreter CMake
finds when no newer one is installed, so `prepare_release_notes` raised
TypeError when the release workflow wrote its notes and the
`pr_overlap_tools`, `release_package` and `linux_fixed_base` tests failed
on a stock macOS host. Writing the encoded text keeps the newline
translation off on every host, and the dynamic tag counts are compared
before the pairs are walked.
Git for Windows 2.56 (runner image 20261002) moved from mingw64 to ucrt64,
so the fixed SSL_CERT_FILE path no longer exists and the WinLibs CMake
fails the FFmpeg download with status 60 on every Windows build. Take the
bundle from whichever of ucrt64 and mingw64 exists.
Co-Authored-By: Claude <[email protected]>
The trace summary line keeps `trace_dropped` as before: true when any
thread lost a slice, any thread's totals table was full, or the gpu
track was full. When it is true, the line now also carries
`trace_dropped_threads`, one entry per affected thread name, so the reader
can tell which thread lost data and how much. Threads that share a
name, such as the shader prepare threads, share one entry: losses are
summed, the earliest fill is kept and totals_full is true if any is.
Each entry has `events_full_after_s`, the seconds from the first
present of the window to the start of the first lost slice. It is
absent for `gpu` and in every summary line (windowed frames/seconds
and APS5_TRACE_SUMMARY_EVERY) whenever the first lost slice starts
outside the line's span of presents, including a slice whose scope
began before the window opened or one lost after the last present.
`events_lost` is the number of slices not recorded and is absent when
0; in interval lines it counts the buffer's losses since it started.
`totals_full` is true when the thread's totals table overflowed, which
makes the summary numbers incomplete; a thread with only `events_lost`
has complete totals.
The Chrome trace gets a `trace buffer full` instant event on a thread
at the point where its buffer filled.
BUILD.md documents the new fields and the event.
`trace_dropped` is unchanged, and when nothing drops the default output
is byte-identical to before.
Refs #5786
Depends on #5808