Commit Graph
14017 Commits
Author SHA1 Message Date
Yunus Shaikh e87f3ae920 test(shader): store, update atomically and read packed through a T# whose record the CPU evaluates
verifySelectedRecordDirectOperations plans image_store, image_atomic_add,
image_load_pck and image_load_by2 through a heap T# keyed by an SRT value,
which must be bound directly, and through a T# keyed per lane, which must
stay a table with its unsupported mark. A T# that is both sampled and
stored is bound directly for both, and one that is only sampled still
selects its record.

RunSelectedStoreTests stores through a record keyed by an SRT value on the
GPU and reads the texels back. Before the fix both failed: the T# was
planned through an image table, and the store faulted as an unsupported
operation.
2026-10-15 08:50:15 +00:00
Yunus Shaikh fe56f60561 fix(shader): bind a T# whose record the CPU evaluates directly when a table cannot express its operation
An image table cannot store, update atomically or read PCK and BY2/BY4
through its records (#757): those operations fault and read zeros. #6243
turns a heap T# whose words and record offset the SRT walker evaluates
into a selected one-record table, which kept that limitation although
such a T# is one known descriptor, as #5188's direct images were.

PlanTableColumns now binds such a T# directly, for every operation of the
shader on it, when any of them is a store, an atomic, a packed or a
BY2/BY4 access. A T# keyed per lane, or by a value the walker cannot
evaluate, keeps its table and its unsupported-operation fault. The disk
format moves to 37 so that plans cached by #6243's builds are not reused.
2026-10-15 08:50:15 +00:00
Yunus Shaikh 70c88b3e0f docs: say that the overlay's DRIVER line can exceed FRAME with several workers 2026-10-15 01:48:16 -07:00
Ondra97 7cdb99763d fix(font): serialize shared FreeType face lifetime 2026-10-15 10:46:49 +02:00
Dean 721db55dcd Merge pull request #6066 from meh7an/perf/agc-register-read-log-when-open
perf(agc): skip the register read log while no log is open
2026-10-15 21:45:35 +13:00
Dean 07f0cfc448 Merge pull request #6037 from meh7an/perf/agc-present-integer-resident-images
perf(agc): present 8-bit integer resident images without a readback
2026-10-15 21:45:07 +13:00
Dean db4e4b071f Merge pull request #5893 from ThatsLeo/perf/agc-unified-memory-budgets
perf(agc): keep a smaller staging pool on system-memory heaps
2026-10-15 21:44:40 +13:00
Dean 83fe6348ea Merge pull request #5910 from milescoviello/perf/registration-drop-decoded
perf(agc): free a registered shader's decoded program once it is planned
2026-10-15 21:44:11 +13:00
Adrià 2baae7aa30 Merge pull request #6168 from parl0v/fix/detiler-pool-recycle
fix(agc): recycle detiler descriptor pools once their batches complete
2026-10-15 10:43:52 +02:00
Adrià c0be1f69bc Merge pull request #6267 from Ondra97/codex/fix-draw-trace-windows-link
fix(build): link draw trace test to Windows unwind runtime
2026-10-15 10:33:39 +02:00
David Fontanet BujaldónandClaude Opus 5.5 02b376a1d4 feat(tools): flag Unknown exports whose NID is already a symbol in the tree
An APS5_EXPORT whose name says the NID is unknown can carry the NID of a
symbol nid_patcher already produces from core/libs/prx: a _nid_postfix
helper, an APS5_VABI export or an assembly function. The NID is then not
unknown, and the stub can take the place of that symbol. #5608 exported
YNw9Xk5iNWo from libSceVideoOut as a throwing stub; it is the NID of
MouseIsOpen_nid_postfix, which libSceVideoOut imports from libSceMouse,
so presentation failed with "YNw9Xk5iNWo not implemented" (#5738, #6245).

The unknown-nid rule reports an added Unknown export with the NID of a
symbol in core/libs/prx, and an added symbol with the NID of an Unknown
export. Named aliases, such as the libc and libSceAmpr exports that share
NIDs on purpose, are not checked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:29:03 +02:00
Ondra97 87ee02882c fix(build): link draw trace test to Windows unwind runtime 2026-10-15 10:28:21 +02:00
EdouardSenceandClaude Opus 5.5 7ca97ea137 test(agc): lift the host import budget in the sparse dma-buf import test
The test imports ranges just above udmabuf's size limit and keeps the first
one live. With size_limit_mb=8192 each range is 8257 MiB, so on a host with
32 GB of RAM the default budget (half the physical memory) refuses the second
import and HostImportFor returns nullptr instead of reaching the sparse path:
"a failure at UDMABUF_CREATE after the sparse path was chosen did not throw".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:15:22 +02:00
Adrià FranchandClaude 15734fb3fa fix(agc): keep detiler descriptor pools unrecycled without a recorder
Co-Authored-By: Claude <[email protected]>
2026-10-15 10:14:29 +02:00
Yunus Shaikh 17ccfc497c perf(shader): walk a changed table column from the words its compare read
A kept column the runtime cannot vouch for is read and compared with its kept words. When they
differ, the fresh walk read the same runs again, so a capture that follows every change to a table
read each word twice: 1,048,576 words per capture for a 65,536-record column, 12% slower than
before the reuse with the write watch and 16% slower outside it.

The compare now leaves its words with the snapshotter, and the fresh walk takes them instead of
reading when its plan has the same runs: words the capture has already read read the same again.
Such a capture reads each word once, as before the reuse, and the 65,536-record column costs 7%
(12% outside the write watch) more than before the reuse; the rest is keeping the column. With 16
distinct T#s it costs 5 to 8% less. agc_shader_table_snapshot_reuse expects a changed table to be
read exactly once per capture.
2026-10-15 08:10:51 +00:00
Yunus Shaikh 0431419d16 fix(agc): store pending GPU results before vouching for an unwritten table column
Every read and store through GuestMemory calls the flush hook first. Recorder's FlushForAccess waits
for recorded GPU work over the range, records a queued label that writes it, publishes the unit
shadows over it and stores the storage image results kept on the GPU for it. A storage image result
is stamped only when it is stored (its write-back's MarkWritten), so ShaderMemory's stamp and check,
which never read the column, could vouch for words a read would have replaced: the column skipped
its read and the snapshot kept the T#s from before the result.

stampWrites and unchangedSince now call GuestMemory::FlushGpuWrites over the range after the pending
write query and before the tracker, as a read does, and as the dispatch cache stores pending images
before it validates an entry. The stamp then covers what the flush stored, so the capture after it
skips the read again.

agc_shader_table_snapshot_reuse installs a flush hook that stores a new T# into the table on its first
call over it. Without the flush in unchangedSince the reused snapshot keeps the old T#; without the
one in stampWrites the next capture compares the table instead of skipping its read.
2026-10-15 08:10:51 +00:00
Yunus Shaikh 5686705ddc test(agc): check the image table snapshot reuse against every write path
agc_shader_table_snapshot_reuse (agc_shader_memory_tests --table-snapshot-reuse) captures a
4,096-record image table in write-watched guest arena memory and checks each capture's snapshot and
captured words against the table:
- an unchanged table is reused without reading a word, and its snapshot equals the first one;
- a CPU store, a driver store (GuestMemory::Write, as a CP write or a DMA copy or fill makes it), a
  GPU result stored by the driver (StoreOwnBytes) and a pending GPU write with a known value make it
  read again, and the new T# is in the snapshot;
- a MarkWritten stamp (a GPU write into an import), a pending write the capture syncs on and a guest
  mapping change make it compared again;
- after the reuses, the map selects each key's record;
- a table outside the write watch is compared and reused, and a store into it is seen;
- the memo keeps at most 16 columns.
Without write watching, the captures of an unchanged table expect a compared reuse instead.
2026-10-15 08:10:51 +00:00
Yunus Shaikh b02c442bbb perf(shader): reuse an image table column's snapshot while its memory is unwritten
Every capture of a shader with an image table read every record of each column and looked each
record up among the snapshot's distinct descriptors. A 32,768-record column of distinct T#s cost
6.1 ms per capture here (2.3 ms with 16 distinct T#s), and Resident Evil Village's title captures
such columns on nearly every draw.

TableSnapshotter now keeps the last 16 column snapshots per thread (at most 4 Mi words in all,
least recently used first out), keyed by heap base and size, record shape and key count: the runs
it read, their words, each record's state and code, and the distinct T#s. A narrowed, selected or
pointer-indirect column, or one with an unmapped record, is not kept. A capture of a kept column
takes the codes and the distinct T#s from it with no per-record work. When they are the snapshot's
first words they are taken as they are, and the words index is built only if a later column needs
it, so the result is the snapshot a fresh walk would produce, codes and words order included.

The runtime gets three optional callbacks: stampWrites gives a stamp for a range before any of it is
read, unchangedSince tells whether nothing wrote the range since, and supplyWords hands words that
equal guest memory to the capture as read, so they stay in the captured regions the draw cache
compares. ShaderMemory implements them with the write tracker. The stamp is CollectWrites's
generation, refused when this ShaderMemory already fetched a page of the range or recorded GPU work
writes it. A range is unchanged while no block of it is stamped after the stamp (collected CPU
stores, the driver's Write, WriteChanged and StoreOwnBytes, MarkWritten for GPU writes into
imports), no recorded GPU work writes it and the guest allocation generation is the stamp's. When
every run is vouched for, the column is not read at all; otherwise its runs are read and the
snapshot is reused only when they are equal word for word.

The 32,768-record column now costs 0.48 ms per capture with no word read when unwritten, and about
0.9 ms outside the write watch. APS5_NO_TABLE_SNAPSHOT_REUSE=1 turns the reuse off. The [image-table]
profile line counts the reuses, and ResourceMaterializer::TableReuse returns them with the words the
snapshotter read.
2026-10-15 08:10:46 +00:00
Yunus Shaikh a0dc9d374f test(agc): check the stamps of a store into partly watched memory
A driver store and an own store across a watched block and an unwatched
one must stamp the watched block and no other. On Linux, a store across
the halves of a split block must stamp it, and a store into its unwatched
half alone must not.
2026-10-15 08:10:46 +00:00
Yunus Shaikh c886c28d17 fix(agc): stamp the watched blocks of a partly watched store
MarkWritten and StoreOwnBytes return 0 for a range that is not wholly
watched, and they stamped none of its blocks. The watched blocks such a
store wrote kept their old stamps, so UnchangedSince and StoredOver over
them still read as unchanged after a GPU write that crossed into an
unwatched import. A vouched shader table column or a cached range then
kept bytes that had been replaced.

Both now stamp each block whose part of the store starts or ends in
watched memory, as the wholly watched path stamps every block. They still
return 0 and note the store as unstamped. This costs one or two coverage
lookups per block, and only on this path.
2026-10-15 08:10:46 +00:00
LeandroLP23andClaude Opus 5.5 79d72b6836 fix(shader): kill demoted quad-mask pixels when the pixel program returns
A pixel that PA_SC_AA_MASK leaves uncovered is demoted to a helper
invocation so the covered pixels keep their derivatives. When the guest
exports under an EXEC mask that clears that lane, the colour output is
never written. On the Radeon 860M (Windows driver 32.0.13058.8001) such
a demoted invocation still writes undefined colour to the target, so
agc_driver_quad_pixel_mask fails at its first implicit-LOD sample under a
partial mask (2). Killing the uncovered invocations at the return, after every
derivative is taken, discards them on every driver.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:09:52 +02:00
Adrià db81992c55 Merge pull request #5238 from MarcZX100/fix/trace-draw-program-addresses-5166
feat(agc): trace shader addresses for each draw
2026-10-15 10:06:31 +02:00
Adrià FranchandClaude 290be56265 test(agc): skip the dma-buf import probe when /dev/udmabuf can't be opened
Co-Authored-By: Claude <[email protected]>
2026-10-15 10:05:09 +02:00
Yossi Yadgar 39575d66e9 docs(debt): note which register-defaults table and type layout libSceAgc returns 2026-10-15 11:04:54 +03:00
Yossi Yadgar 52e288ef43 test(libSceAgc): find the MRT0 colour-target defaults the way a title does
Reads the root at its byte offsets, walks the type table as key and
bank-index pairs and checks that the MRT0 key leads to the sixteen
colour-target registers: block 78 of the table sceAgcGetRegisterDefaults
returns, block 73 of version 10, and a valid block in every version from
4. Fails on the compact entries and on the version 0 legacy table.
2026-10-15 11:04:32 +03:00
Yunus Shaikh ace66bd616 test(agc): name the overlay statistics test's tolerance check within
MinGW's minwindef.h defines near and far as empty macros, so a helper
named near stops compiling in any test that comes to include windows.h.
The project keeps those names out of its code.
2026-10-15 08:03:49 +00:00
Yossi Yadgar dce1573ea0 fix(libSceAgc): return the newest public register defaults from sceAgcGetRegisterDefaults
The legacy call takes no version and answered with the version 0 table.
On a console it returns the running system software's table: 128 keyed
entries over 79 context blocks on 10.x and 137 over 84 on 12.02 and 13.x,
as ps5-vulkan measured and relies on. Version 0 holds 150 entries and its
MRT0 block starts at 0x318 followed by 0x319, so PPSA99011 refused it
even with the types fixed. Return the version 13 table, which is the
137-entry one.
2026-10-15 11:03:41 +03:00
Yossi Yadgar f7750c7769 fix(libSceAgc): hand out register-default types as key and bank-index pairs
The defaults root exposed the compact 12-byte type entries, whose second
word also packs each block's register count and stride above bit 10.
Titles read the type table as 8-byte {key, index << 2 | bank} pairs, so
PS5 FSR4 Showcase (PPSA99011) found the MRT0 key at index 529482 and
refused to build its compose pass. Build the pairs once per table and
check that every index lies inside its bank.
2026-10-15 11:03:12 +03:00
Adrià FranchandClaude 23fccd7632 fix(agc): settle only imports of 1 MiB or more
Co-Authored-By: Claude <[email protected]>
2026-10-15 09:58:21 +02:00
Yunus Shaikh 6d17a1dea7 test(agc): store through a written V# merged with a read-only V# past an import
A read-only V# across two imports into unregistered memory, a read-only and
a written V# in that memory: UploadPrepare merges the three into one copy
that begins inside the first import. The written V# maps its own bytes in
that copy, counts as a copied write, and WriteBack lands its store on its
bytes alone.
2026-10-15 07:48:13 +00:00
Yunus Shaikh f3300a074d test(agc): prove the merged read-only V# test keeps the address space, and cross access
The test of two read-only V#s merged over three imports now asserts that
its second build hits the cached address space without a dissolve and that
the third leaves it only for the import change. One crossed piece is
writable, so a join of the crossed run cannot erase the overlap the test is
about.
2026-10-15 07:48:13 +00:00
Adrià FranchandClaude 5b91ba2bb5 fix(agc): do not begin the reused import-probe command buffer as one-time submit
commands[0] is submitted twice (writtenAfterSecondSubmit); the flag made the validation layer report a second submit of a one-time buffer.

Co-Authored-By: Claude <[email protected]>
2026-10-15 09:46:53 +02:00
Josip Parlov 295b1c55f4 fix(agc): drop the import-watch environment switches
The second queue that settles imports is always created on Windows, and a device without one keeps
the comparisons. The other-family settle path is tested through the recorder test device built
with its settle queue on another family (agc_import_watch_other_family) instead of
APS5_SETTLE_OTHER_FAMILY.
2026-10-15 09:46:48 +02:00
Josip Parlov 42178b494d test(agc): a store from another thread within an import window
The settle read keeps a private import's window open for one round trip on the settle queue, so a
store from another thread can land inside it. CheckImportWindowOtherThread: a store of new bytes from
another thread within the window counts as written, and a same-value one keeps its possible CPU
touch. The debt entry notes the longer window.
2026-10-15 09:46:48 +02:00
Josip Parlov ef5da022af fix(agc): settle watched imports on any copy queue and probe the settle itself
Review on #6060: the Windows probe now settles a 4,096-page import with the same 4-byte read
production uses and requires no marks after it. A failed settle destroys the import instead of
leaking it. The settle's destination, command buffer and fence are created once per device. Where
the graphics family has a single queue (AMD on Windows), the settle queue comes from another family
that copies, through a buffer aliasing the import's memory. The recorder tests' device gets the
settle queue too, and agc_driver_layered_volume_submit_other_family runs that path
(APS5_SETTLE_OTHER_FAMILY=1).
2026-10-15 09:46:48 +02:00
Adrià FranchandClaude 1ff6ca7a67 docs(architecture): describe settled watched imports of private memory on Windows
Co-Authored-By: Claude <[email protected]>
2026-10-15 09:46:48 +02:00
Josip Parlov 9ba58c2a60 perf(agc): keep Windows host imports of write-watched private memory watched
On Windows a host import without a section alias left guest write tracking, and its changes were
found by comparison. Since resident direct memory became private MEM_WRITE_WATCH memory, its
imports take that path, and every storage-texture refresh over it compares its whole snapshot.

ProbeImportWriteProtection gets a Windows body on MEM_WRITE_WATCH scratch memory, and
decideImportWatch keeps direct imports of private read-write memory watched when GetWriteWatch
stays honest after an import's first GPU use. The driver marks every imported page written until
that use completes, so each such import is settled inside ImportWatched's window by a 4-byte read on
a second queue of the graphics family (the imports lock comes after GpuMutex, so the main queue
cannot be used there). APS5_NO_WATCHED_PRIVATE_IMPORTS=1 keeps the comparisons.
2026-10-15 09:46:48 +02:00
Yunus Shaikh 253fd9512e test(agc): resolve BDA stores under a read-only V# across imports to the writable import
Two writable 64 KiB pieces and a read-only one, one import window each,
with a read-only V# across the boundary: through the kept address space, a
hit on it and a dissolve after UploadPrepare, the BDA table has no
overlapping ranges, stores up to the boundary resolve to the writable
import and reads past it find the read-only one.
2026-10-15 07:45:49 +00:00
Yunus Shaikh 982518169a fix(agc): keep a build's BDA table disjoint where its read-only copy overlaps the address space
A read-only V# that crosses imports of the cached address space is bound
through a copy of its bytes, and that copy can begin inside an import. The
BDA table listed the copy and the space's ranges by begin, and a shader
takes the last range beginning at or below an address, so the bytes from
the copy's begin to the end of that import resolved to the copy. The copy
is read-only, so a BDA store or atomic into a writable import faulted with
Permission, the GPU side of the rule owner() no longer applies on the CPU.

The table now gives every byte one range: the space's ranges, or after a
dissolve the direct and mirrored regions, keep the bytes they hold, and a
build's copies keep only the bytes nothing live holds.
2026-10-15 07:45:49 +00:00
QRMOE 7e34059a43 test(libc): match getcwd allocation size in application heap check 2026-10-15 09:42:24 +02:00
Claude 411ce81a6e test(agc): cover ordered-append modifiers on indirect dispatches
DISPATCH_INDIRECT accepts ORDERED_APPEND_ENBL like DISPATCH_DIRECT and
still refuses ORDERED_APPEND_MODE.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_019hfwvntiwabeqdZ6avJssk
2026-10-15 07:37:40 +00:00
Claude fcc426b6a2 fix(shader): add the ordered count of a first active lane in the upper wave64 half
The ordered count's amount was shuffled from the first half's value of
the first active lane's host lane, so a wave64 program whose active lanes
were all in lanes 32 to 63 added the inactive lane's value from the lower
half. Read the amount through the context shuffle, which selects the half
the first active guest lane lives in, as v_readfirstlane_b32 does.

The GDS test now masks the ordered count to the upper part of each wave,
including a wave64 whose first active lane is lane 40, prefills the
result register to check that inactive lanes stay untouched, and keeps
the M0 cases of the counter addressing.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_019hfwvntiwabeqdZ6avJssk
2026-10-15 07:37:40 +00:00
Parth Bhanti 09b1ae6f6c fix(libkernel_sys): return 0 for AT_SYMLINK_NOFOLLOW in fchmodat 2026-10-15 00:35:44 -07:00
screammandClaude Fable 5.1 e3ce9bac07 fix(agc): hold a guest allocation lease while a queue stores idle results
Once every queue has been idle for 50 ms, the queue 0 worker stores
pending storage image results on its own (#3777, #4410). That store runs
at no ordering point of the guest and took no lease of the guest
allocations, so a guest unmap could run alongside it. The compare of
memory outside the write watch (StorageTexture::compareUntracked) then
read memory the unmap had released: the driver forgets its cached page
states only when the unmap's registry mutation ends, after the memory
is gone, so the range still read as mapped.

The idle store now holds a lease (GuestAllocationsAcquire) under the GPU
mutex, the order the other lease holders in the driver use. An unmap
waits in GuestAllocationsRequireUnpinned until the store is done, and a
store that starts during an unmap waits for the mutation to end, finds
the range no longer registered and drops its results.

ProsperoEden (PPSA99008, homebrew catalog, run without app0/eboot.bin)
closes its launcher, its GL driver unmaps its direct memory at
0x200000000+0x14000000 while both 1920x1080 color buffers there still
have results pending, and the queue worker faulted in compareUntracked
under FlushAllPending("idle"):

  FATAL: unhandled exception 0xc0000005 on thread 69080 'queue worker 0x0'
    read of 0x0000000200000000

On main d92a8dad3 that happens in 3 of 3 cold runs. With this change 6
of 6 runs exit with code 0 and log "storage image 0x200000000+0x10e0000:
memory no longer registered; 1 pending ranges dropped" for both images.

New test agc_driver_idle_store_over_unmap unmaps an untracked arena
surface with a pending result and releases the GPU mutex while the unmap
is in progress. Without the lease the queue worker faults at the same
compare (3 of 3); with it the result stays pending until the unmap ends
and is then dropped.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-15 09:01:16 +02:00
Donson 6a880abf28 fix(shader): cap SPIR-V scalar replacement 2026-10-15 14:58:41 +08:00
ArjenS-LA 96f506f870 fix(relinker): preserve weak imports in guest module registries 2026-10-14 23:39:04 -07:00
ArjenS-LA f9f6872b8d fix(tools): audit ordinary homebrew import names 2026-10-14 23:38:52 -07:00
fogydes e088fd8741 fix(libScePlayGo): let users answer unlisted chunks 2026-10-15 11:48:51 +05:30
alfalfa-starandClaude Opus 5.5 17ca453199 perf(agc): draw an indirect rect list from its records on the GPU
ClassifyIndirectDraw sent every indirect rect-list draw to the CPU path
(non-vertex path), which reads the records on the CPU. When the GPU writes
those records earlier in the frame, the read waits for every batch up to
the producer: in Kena (PPSA01802) each such DRAW_INDIRECT waited about
15 ms, about once per frame.

A rect list on the vertex path is drawn by its generated tessellation
stages from three-vertex patches, with no draw-dependent constants, so the
records can be consumed on the GPU like any vertex-path draw. The
classifier now keeps a rect list without mesh or tessellation stages on
the GPU path, and Graphics::Draw accepts it. APS5_NO_RECT_INDIRECT=1
restores the CPU path.

agc_driver_rect_list_indirect submits a direct rect-list draw and the
same rectangle from a DRAW_INDIRECT record and requires the same bytes,
and a record without vertices to draw nothing; it passes on either path.
agc_driver_draw_offset_indices requires an auto indirect rect-list record
whose packet names no offset registers to be classified to the GPU.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 08:17:50 +02:00
mrcrapto 72d6c9e16b fix(agc): discard recorder batches after submission failure 2026-10-15 06:06:00 +00:00