The conflict in core/libs/prx/libSceAgcDriver/CMakeLists.txt from #12862's split is resolved with tools/agc_test_split.py resolve, which moves this PR's test registrations into tests/cmake/.
A record-count user word was made relocatable after only the push,
specialization and captured-word checks. When the same word also builds
a second descriptor, the relocated stage patched the first descriptor's
count and kept the second one's earlier count. A count word is now
relocatable only when it appears in exactly one binding word, like the
base word.
agc_driver_draw_relocate_user_values gains the tagging case with two
read-only bindings sharing a count, and a draw that builds two descriptors
from one count SGPR, grows that count after the relocation is learned and
checks the second descriptor's newly covered dword; a control with a count
SGPR per descriptor still relocates and draws the grown range.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Beyond keeping both sides:
- CMakeLists.txt:3149-3153: agc_driver_native_depth_buffer_clear joins
the branch's set_tests_properties lists for the native depth tests
(and the WIN32 list) instead of main's own line that re-lists clears
and clear_refusals; the properties are the same.
- NativeMultisampleDepth.cpp:1153-1170: main's two
completionCandidate && fault == ClearFault::None blocks sit around
ArmHtileDepthClear inside the branch's ClearSource::HtileDispatch path,
where the branch moved that code; the fill paths make no HTILE clear
candidate.
- NativeMultisampleDepth.cpp:2556: main's --buffer-clear call passes
ClearSource::HtileDispatch, the parameter the branch added to
CheckDepthTarget.
Record an HTILE clear's mask/value with its pending note and recorder ID.
When that write covers the HTILE and determines its ZMask/SMem bits,
expand the metadata on the GPU without a readback. Retire mask proofs
with their recorder or depth device, preserving other owners and the
bounded cache's conservative overlap invalidation.
Recognize the structured-buffer variant and carry its completion range
through preparation, upload and binding. Admit only that range while
its native owner has a pending clear, retaining color guards and
excluding these resources from dispatch and recipe caches.
Cover store-back, PM4 buffer clears, guard refusals, recorder recreation,
live owner isolation and scoped cleanup with GPU byte/wait assertions.
AI-assisted: yes
Queue 0's idle store wrote every pending storage image back to guest memory
in one pass once the queues had been idle for 50 ms. In a title's movies
that is about 15 upscaled 3840x2176 targets (~0.5 GB) per pass, and the
title's next flip waited ~0.6 s behind it, about every 3 s.
The idle store now writes back 4 MiB at a time, in the block units the
images already track, and stops as soon as queue 0 has queued work or
another queue is running; the rest stays pending for the next idle period.
The quiet period is unchanged.
A draw found through a relocation alias (#4943) may only move user words
that the stage's SRT walk used as a base and never as a value. Two kinds of
user words that move between otherwise equal draws fell outside that:
- A buffer V# held directly in user SGPRs. Its words are values, so a move
was refused. When the V# base appears only in one read-only GuestBuffers
element of the compiled stage (the next two words equal, not in push data
other than its own forwarded slot, not in specialization constants or
captured words, and no captured run inside its range), its base and
record words are relocatable: they leave the alias key, and the relocated
stage patches that element's descriptor.
- The s_swappc target of a GCN vertex program (its fetch subroutine). It is
linked into the code, so a move was refused. Such a word is relocatable
when it feeds no binding, push word or captured word, and its alias key
hashes the callee's code (UserDataCalleeLength words at the address),
so a copy of the same subroutine elsewhere relocates and changed code
does not.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Staging a swept write-only element (#7618) costs a copy-in and a
copy-back per use. In PPSA03912's town that saves GPU time, but in its
main menu the 202 MiB element is faster in place (73 vs 91 ms of GPU
time per frame), so the menu lost about a fifth of its frames.
Decide per element by measurement. The busiest element is probed in
its current mode, the other mode and its current mode again; the other
mode is kept when its frames are faster by more than twice the
standard error. A repeated answer doubles the wait for the next probe
from 20 s up to 160 s, and a jump of the frame GPU time by more than a
quarter (a scene change) probes again at once. The frame GPU time comes
from the whole-batch timestamps, which the first probe turns on.
Resource builds made under the other decision are not reused.
APS5_NO_ADAPTIVE_STAGING=1 stages every eligible element, as before.
The policy is in AdaptiveStaging.hpp, fed by the caller with frame
counts and time; agc_adaptive_staging covers keeping the faster mode,
frame noise, the back-off cap, a scene change and the share threshold.
AI-assisted. Tested by me on my own machine.
ReplayComputePipeline never registered its build in computePipelinesBuilding, so a dispatch of the same pipeline during the journal replay compiled it again. Register the build the way computePipeline() does, so the dispatch waits for it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A queue's passes on label dwords and the takes its own stores make of them move to LabelTakes. A wait only considers stores recorded after its submission entered the driver, so a pass or take at a serial no newer than that entry can no longer hide a store: each queue's passes are kept in serial order and dropped when its worker starts a submission received after them (for a rewind tail, the oldest submission of the queue still pending bounds it).
noteLabelIssue reads a per-(queue, dword) count of live passes without the lock and takes labelStoresMutex only when a pass may exist. Passes and takes of a queue, and its stores that take them, all run on that queue's worker.
The host's pad code now reaches the display only through the poll, so
the observe entry point has no caller. The shared-display tests use the
poll and also pin that, with ANYPS5_PAD_STATUS unset, the provider is
not read until a request, is read at once with ANYPS5_PAD_STATUS=1, and
that an invalid value stops the first poll with an error naming the
variable.
PadStatusDisplay::Poll asks the provider for a snapshot only when the
panel can be shown: in the events mode always, otherwise only while a
request keeps it visible. With ANYPS5_PAD_STATUS unset or 0 and no
request the provider is not called, so the host builds no snapshot and
reads no controller state for it. An unchanged snapshot is not observed
again, and a display that stopped being polled observes the next
snapshot afresh when a request makes it wanted again. The entry point
for the host's pad code is AgcDriverPadStatusPoll_nid_postfix.
The host's pad code describes a controller with PadSlotStatus,
PadStatusSnapshot and PadStatusProvider, and test executables that
include its headers do not have the Vulkan headers on their include
path. The types and the player slot count move to PadStatus.hpp, which
needs only the standard library; PadStatusOverlay.hpp includes it.
The presentation records the panel after the overlay plane and the
system keyboard and before the performance overlay, in its own overlay
instance, and chains the image layouts through it like the keyboard's.
The system keyboard has priority: while it is shown the panel is not
drawn. With the panel hidden, which is the case unless ANYPS5_PAD_STATUS=1
or a request shows it, every layout and branch of the presentation is
the one it was.
The panel reuses the performance overlay's layout and font: a 32-column
text-only OverlayContent. It sits in the corner mirrored horizontally
from ANYPS5_OVERLAY_POS, so it never covers the performance overlay
when both are shown. PadStatusDrawn() gives the system keyboard
priority: the panel is not drawn while the keyboard is. PadStatusOverlayLines()
returns the lines to draw now, or nothing while the panel is hidden, and
the observe and show entry points are the ones the host's pad code
will call.
One line per slot that has ever had a controller: "P1 DUALSENSE WIRED",
"P2 XBOX BATTERY MEDIUM" or "P3 DISCONNECTED". The overlay font draws
digits, upper-case letters and a few signs, so a controller name from
the host is reduced to those glyphs and cut to 12 columns with a final
dot; every line fits the 32-column panel. A battery the host cannot
report reads "BATTERY UNKNOWN". With no controller seen the display
lists "NO CONTROLLER".
A controller status panel for the overlay needs to stay on screen for a
few seconds and then disappear. PadStatusDisplay keeps that state with
an injected clock: ANYPS5_PAD_STATUS=1 shows it for 4 s when a slot
connects, disconnects, changes controller or enters a lower battery
class, a new event restarts the 4 s, and Request() shows it on demand
whatever the mode. With ANYPS5_PAD_STATUS unset or 0 only Request()
shows it. A value other than 0 or 1 is refused with an error naming the
variable.
The pad status types (PadSlotStatus, PadStatusSnapshot and the
PadStatusProvider seam) carry no SDL types so tests can inject them.
Nothing reads the display yet.
Main's 102b7a5cf0 brings #12566's HostOnlySampleRanges, added in MultisampleTarget.cpp where this branch adds MultisampleColorTargetOver; the merge keeps both.
The collapsed-stacks file is written to a .tmp next to it and then moved
over the final name. That move still used MoveFileExA with the UTF-8
path, so under a non-ASCII directory the .tmp was never published.
Both paths now go through std::filesystem::path and MoveFileExW, with
the same MOVEFILE_REPLACE_EXISTING.
The worker sampler test now also reads the published .collapsed.txt,
checks its "# samples=N" header with N > 0, and checks that the .tmp is
gone.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
APS5_VIDEOOUT_TELEMETRY_DIR, APS5_SAMPLE_THREADS, APS5_SAMPLE_WORKER and
APS5_SAMPLE_COMMITTER were read with getenv and opened with the narrow
fopen/ofstream, so on Windows they worked only for characters in the
ANSI code page, and the tests, which pass UTF-8 path.string() values,
failed whenever %TEMP% was not ASCII.
They are now read with _wgetenv and carried as UTF-8, as the other
diagnostic directories (ANYPS5_SCREENSHOTS, APS5_DUMP_SUBMITS,
APS5_PAD_INPUT_FILE) already are, and the files are opened through
std::filesystem::path. The worker sampler test sets its variables with
_wputenv_s and reads the results through std::filesystem::path.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
noteLabelIssue runs for every executed store packet. RELEASE_MEM and WRITE_DATA, the common label writes, now go through DecodeLabelWrite, which DecodeStores already used for them and which allocates nothing, and stores the label history doesn't keep are dropped before labelStoresMutex is taken.
libSceAgcDriver.prx failed to load in the patched library check with an undefined symbol for HostCpuModel, because the host report called libkernel's plain C++ functions, which the patched libkernel does not export to it. The report now calls the extern C helpers and splits the joined feature list back into names.