A per-game relinker-patches.toml (or --patches) replaces verified bytes in
the input executable and bundled modules before conversion. Entries are
checked against the expected bytes and against every in-place writer,
the ELF header range and relocation targets. An <output-stem>.patches.toml
manifest records the applied entries, and --verify-patches checks an input
or a converted output. With no patch file the output is unchanged.
Rosetta sets RDTSCP's ECX to its constant TSC_AUX, 0xAAAAAAAB, which is
not a processor index. --to-rosetta already makes RDPID return processor
0 for the same reason, so code reading the processor through RDTSCP saw
a different value from code reading it through RDPID.
--to-rosetta now moves RDTSCP into an optional stub that runs the
instruction unchanged and then loads 0 into ECX with a MOV, which
leaves the counter in EDX:EAX and the flags as they were. A site with no
movable instruction after it for the jump is kept, as for RDPID. The
Intel target and a plain relink are unchanged.
APS5_CRASH_STACK_BYTES=<n> prints n bytes of stack words from rsp after the
stack return address candidates, then eight words at every distinct stack word
that points at readable, non-executable memory outside the thread's stack.
Off when the variable is unset, empty or 0.
USAGE.md describes the refused posted unlocks and their log line, and
says that the icon queries and a damaged or unwritable unlock or
statistics file stop the game with an error. The debt entries of
libSceNpTrophy2 and libSceNpUniversalDataSystem no longer say that any
posted id is stored. Both USAGE.md and the libSceNpTrophy2 entry say
that without trophy data the platinum is not unlocked when the other
trophies are.
PPSA03490 (Demon Slayer: Kimetsu no Yaiba - The Hinokami Chronicles) has no playgo-* files in sce_sys. Its startup code asks scePlayGoGetLocus about chunk IDs 0, 1, 2, ... and stops when it gets SCE_PLAYGO_ERROR_BAD_CHUNK_ID, appending each ID to an array. With APS5_PLAYGO_UNLISTED_CHUNKS=installed no ID ever fails, the array keeps growing and startup aborts with 'Ran out of memory allocating 5565054976 bytes with alignment 65536'. With =absent the walk ends and the title gets past its logos to the menu and intro.
AI-assisted: yes. Written with AI assistance and reviewed by me.
A sparse import binds whole 64 KiB blocks, so a range whose memfd offset
is not a multiple of 64 KiB fell back to one UDMABUF_CREATE and, above
udmabuf's size limit, to copies. The sparse buffer now starts at the
block below the range: HostImport::lead holds that distance, the
import's device address points at the range itself, and every offset
into the import's buffer goes through HostImportOffset.
APS5_PAD_INPUT_FILE commands can set bit 0x200000 or 0x400000 to put a
finger on the left or right half of the touchpad, as the TouchLeft and
TouchRight keys do, so scripted runs can press touchpad combinations.
tools/pad_input.py queues them as touch-left and touch-right.
AI-assisted: yes
StorageTextureBudget gave the storage image cache a quarter of the VK_EXT_memory_budget
budget of the largest device-local heap (capped at TextureCacheBudget, never below 512 MiB)
whatever else the heap held, while SampledTextureBudget takes what that budget leaves after
the heap's other use. On a 10 GB card the storage cache kept 2024-2170 MiB into a level load
whose depth and render target allocations then failed with VK_ERROR_OUT_OF_DEVICE_MEMORY
with the heap at its reported budget where a run logged it.
The storage image budget is now what the reported budget leaves after the heap's use other
than the cached storage images, the unused part of the sampled textures' 2 GiB floor and an
eighth of the budget; the caps at a quarter of the budget and at TextureCacheBudget and the
512 MiB floor stay. The sampled budget still reserves TextureCacheBudget for the storage
cache, at least the storage cap, and the storage budget reserves the sampled textures alive,
so neither counts bytes the other may take; while the sampled textures alive stay within
their floor the sampled budget falls first, then the storage budget, and above it both fall
together. The budget line also prints when the storage image budget moves by more than a
tenth, with its previous value, and StorageTextureCacheBudget is exported for the execution
test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Unreal titles end in their own libc fatal path with the UTF-16
LowLevelFatalError message in a register, and the crash report shows
only the first 64 bytes of it as hex words, so the message is lost
(#4238: Demon Slayer PPSA03490, Stray PPSA02100).
For every general register that points at readable memory starting
with at least four printable ASCII characters, as UTF-16LE or as single
bytes, the report now prints the text up to a NUL, the first other
character, 512 characters or the end of readable memory (each page is
checked with VirtualQuery before it is read).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A graphics pipeline the store builds or a compute pipeline a dispatch builds is
appended to pipelines.journal next to the shader cache entries, as the
materialized result of each stage (the specialized SPIR-V, bindings and push
constants) plus the recipe the pipeline was built from: the stages'
identities, vertex input layout, descriptor set layout keys and the state
fields the Pipeline constructor reads. When a VulkanDevice is created with a
window (a headless device waits for its first draw or dispatch, since the
window's device usually replaces it first) the journal is loaded and its
pipelines are built on a pool of threads while the title loads, so the first
draw or dispatch of a configuration seen on an earlier run finds its pipeline
resident instead of compiling it on the queue worker.
The pipeline stores key on RecompileResult::Identity, a hash of the module
words, specialization constants, subgroup width and bindings that is the same
on every run (SharedSpirv::ContentHash, computed once per shared module),
rather than on the process-lifetime variant ids; a generated stage is keyed
like any other. CachedPipeline builds outside the store's mutex and the
library parts and link are built outside the library store's mutex, so replay
threads and queue workers build in parallel; a concurrent build of the same
key keeps the first inserted. A replayed pipeline enters the store at its
least recently used end and a full store takes none, and its optimized relink
waits for the first draw that binds it, so the title's own pipelines come
first in both. The compute pipeline creation moves into
VulkanDevice::createComputePipeline, shared by dispatches and the replay. The
store key also covers the color component mapping, the depth target's sample
count and a depth bias clamp, which the constructor reads.
The file names the device it was written for (the properties the driver's
pipeline cache file is named by) and starts over for another; a torn tail is
cut at a record boundary; two runs sharing the directory take turns on a lock
file and keep each other's records; a read failure never starts the file
over. A device cancels its replay before it is replaced or destroyed, and a
replaced device destroys its pipelines then rather than at the driver's stop.
APS5_NO_PIPELINE_JOURNAL=1 disables it; APS5_PIPELINE_JOURNAL_THREADS,
APS5_PIPELINE_JOURNAL_ENTRIES (the first recorded), APS5_PIPELINE_STORE_ENTRIES
and APS5_PIPELINE_JOURNAL_MAX_MIB bound it; APS5_PROFILE_DRAW prints a
[pipeline-journal] summary. APS5_VERIFY_SET_LAYOUTS=1 now also checks a
dispatch's set layout against the one described from its shader. The lavapipe
test suite, which shares one cache directory, runs with the journal off except
for the journal's own tests.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6hw3kRAuQdfcU9YCZgaoK
A GPU hang or a crash in a title leaves no record of what the queues
were last given: APS5_TRACE_GPU prints every submission (too much to
leave on), and the WAIT_REG_MEM timeout message shows only the packet
before the wait on the waiting queue.
Each queue now keeps a ring of its last 16 submissions
(APS5_PM4_RING=<n>, 0 to 4096, 0 disables): the TraceMs time in tenths
of a millisecond, serial, size, guest address and a copy of the first
512 dwords after indirect buffers are inlined, recorded under the
driver lock next to the serial, REWIND tails included. The ring of a
queue and the failure report registration are set up at the top of
Submit, before any output is reserved, so recording under the lock
does not allocate. Disabled, Submit only tests a static flag; enabled,
it costs one bounded copy per submission.
DumpSubmissionRings decodes the kept dwords with Pm4::PacketWords and
the new Pm4::KnownName, which returns the decoder's names as views of
static strings (empty for an unknown opcode, printed as UNKNOWN_0x%02x);
Pm4::Name now builds its std::string from it. Every line is formatted
with integer and string conversions only into a 512-byte stack buffer
and written with one WriteFile / write(2) on the stderr handle, so the
dump does not allocate from the C runtime heap, lines from other
threads do not interleave and the CRT stream lock is not taken (the
rule of #3871). The header gives the number of queues and the ring
size, and each queue line the submissions recorded and kept.
libc gets a small registry of failure reports
(LibcRegisterFailureReport_nid_no_patch, Unregister, Run); Run runs
the registered reports the first time it is called and returns at once
after that. The Windows crash report runs it after the thread dump of
an unhandled exception, after SIGABRT (which also covers the
APS5_HANG_DUMP_SECS timed dump, since it aborts) and after an invalid
CRT parameter, so an abort after a reported exception does not print
the rings twice. The driver registers its dump at the first submission
and unregisters at static destruction; the dump there waits a bounded
time for each ring lock. What the crash-path dump cannot do (a ring
held by the crashed thread, rings corrupted by the crash, Linux) is
recorded in technical debt.
APS5_STALL_WATCHDOG_SECS=<n> (0 to 86400, 0 disables) makes a waiter
that has not progressed for n seconds dump the rings once per wait: a
WAIT_REG_MEM (progress as for APS5_GPU_WAIT_TIMEOUT_MS: another packet
runs or completes), a REWIND waiting for the title, and a compute
submission held behind queue 0. The last one prints under the driver
lock, so submissions and the other queues wait for it (USAGE.md says
so). Both variables are read at the top of Submit, so an invalid value
of either throws at the first submission and never later inside a GPU
worker. The order wait behind queue 0 releases its order hold on scope
exit, so a throwing dump cannot leave it counted.
No packet semantics change; packet sizes and names are those of the
existing decoder (PM4 type-3 header layout as in Mesa
src/amd/common/sid.h). The queue bound of Submit and of the rings is
one constant, SubmissionQueueEnd.
Tests: agc_driver_pm4_tests gets ten modes, each its own ctest:
agc_driver_pm4_ring (APS5_PM4_RING=4, six submissions on queue 0 and
two on 0x20: the dump holds #3..#6 and #1..#2 in order, nothing
older), agc_driver_pm4_ring_empty (one line), _ring_disabled (one
line), _ring_invalid and agc_driver_pm4_stall_watchdog_invalid
(APS5_STALL_WATCHDOG_SECS=1x: the submission throws and is not
executed), _ring_report (LibcRunFailureReports_nid_no_patch dumps the
rings), _ring_report_heap (Windows: with ucrtbase.dll's HeapAlloc and
HeapReAlloc imports counting calls from the reporting thread, the
failure report of a submission holding INDEX_BUFFER_SIZE names it and
allocates nothing, while Pm4::Name of the same header is seen
allocating), and with APS5_STALL_WATCHDOG_SECS=1 a WAIT_REG_MEM
(agc_driver_pm4_stall_watchdog), a REWIND (_stall_watchdog_rewind) and
a compute submission held behind a queue 0 rendering wait
(_stall_watchdog_order), each held for about 2.5 s, dump once and
complete when released. The PM4 catalog test checks KnownName against
every opcode and custom name. windows_backtrace_failure_report checks
that abort() runs the registered reports once and not an unregistered
one; windows_backtrace_failure_report_once that abort() does not run
them again after an earlier run. With the dump decoding through
Pm4::Name, agc_driver_pm4_ring_report_heap fails with "the failure
report allocated from the C runtime heap"; with Run resetting its
flag, windows_backtrace_failure_report_once prints the report twice.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Shader cache section says only that subdirectories of other versions are removed after 14 days. housekeeping() in ShaderDiskCache.cpp removes nothing from the current version, and no size or entry limit applies there.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
main's StorageTextureBudget calls largestDeviceLocalHeap, the helper this branch made public as LargestDeviceLocalHeap for the overlay's VRAM line. The merge renames that call, so the merged tree builds.