Commit Graph

41 Commits

Author SHA1 Message Date
Mouhand Kaddo 82ac3bd93e fix(agc): skip read-only depth resummarize draws 2026-10-12 02:01:36 +04:00
Sanjaysrinivas 1fe527bbec fix(agc): route registered NGG workgroups
Defer registered NGG workgroup preparation until routing is known. Share the routing decision with static ABI resolution and draws. Gate the wave64 fixture on supported mesh subgroups; retain device-independent routing coverage.
2026-10-11 15:20:24 +02:00
Adrià 3c3992c5c2
Merge pull request #2985 from MrMha/fix/wait-memory-timeout-2832
fix(agc): keep polling unsatisfied memory waits after diagnostics
2026-10-11 10:50:34 +02:00
Adrià 2bb78d2c7a
Merge pull request #1825 from TheAggie/feat/agc-ngg-vertex-mesh-v2
feat(agc): run unregistered NGG vertex programs that need a workgroup on the mesh path
2026-10-11 09:35:19 +02:00
Adrià 19a3d0c8a9
Merge pull request #3213 from Raghav314/fix/libc-heap-without-gpu
fix(libc): register guest heap blocks without GPU access
2026-10-11 09:14:14 +02:00
Scott Schuster 0a1b31f5c4 test(agc): cover NGG vertex programs routed to the mesh path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:56:37 +02:00
MrMha 056534a969 test(agc): finish persistent label waits with real producers
Apply the three-case test correction offered by Scramblrrrr in 36b26122a5458028f76fe7c7710333fcfca80f6d on issue #2832. Preserve all current-main test additions outside those three functions. Credit the supplied implementation to its contributor.

Independent Windows Vulkan results and the timeout-return negative control are reported in issue #2832. Local validation: exact three-function source match, preservation of unrelated Submit.cpp content, conventions and whitespace. Native Windows/GPU execution was not performed here. Shutdown cancellation and persistent-wait policy remain unresolved.

AI-assisted: yes.

Co-authored-by: Scramblrrrr <70121301+Scramblrrrr@users.noreply.github.com>
2026-10-11 09:54:17 +04:00
raghav314 aa013f59ac fix(libc): register guest heap blocks without GPU access
AnyPS5's libc heap registered every block with GPU access, so every
address-based build imported or mirrored each malloc block the title had
made. The console's libc.prx maps its heap with protection 3 (CPU read
and write, no GPU bits), so the GPU never sees it there either.

Heap blocks are now registered without GPU access; reallocating keeps the
block's access. The AvPlayer fallback for a title that passes no texture
allocator stands in for that allocator, which hands out memory the GPU
reads, so it allocates through a new GuestHeapAlignForGpu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 07:02:30 +05:30
Scott Schuster 39fdc48b5f fix(agc): read unregistered graphics programs only while no shader is registered
An unregistered graphics program is read raw only while the shader registry
is empty; with shaders registered it throws, as before. A draw that uses a
raw program is noted, and RegisterShader throws once that has happened,
since a title that registers shaders after drawing unregistered programs is
not understood. The draw cache revalidates a raw program by reading it raw.

AI-assisted (Claude Code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:12:14 -05:00
Scott Schuster d1d1681bae fix(agc): drop a cached draw whose unregistered program was rewritten
The register draw key identifies an unregistered program only by its
address, and a key hit reuses the entry's decode without reading the
program again. A different program uploaded to the same address with
the same registers would run the old compiled shader. A hit now checks
the decode's unregistered programs against guest memory and drops the
entry when one changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:00:20 -05:00
Scott Schuster dddd9f29c2 test(agc): cover raw program snapshots and headerless vertex decode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:59:28 -05:00
Scott Schuster 767b8f83a2 feat(agc): accept graphics programs without an AGC shader header
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:59:28 -05:00
alfalfa-star 14c2eca3b0 perf(agc): sync only the source for DMA_DATA copies into host-only memory
A DMA_DATA copy larger than the store limit runs as a transfer between
host imports. When the destination is not registered guest memory (the
GPU can neither read nor write it, so no import exists), the transfer
was refused and the packet drained the device: every batch recorded so
far was submitted and waited for, then the CPU copied.

Such a copy only has to see the source's results. When CopyBuffer
refused for want of an import (path 2) and nothing can still store over
the destination (VulkanDevice::StoresPendingOver: a recorded write, a
label, a copied write-back, a storage image's pending write-back or a
unit shadow), the packet now flushes storage results pending over the
source and waits for the batches that write it (Recorder::SyncThrough),
then copies on the CPU without a drain. Anything else still drains as
before. The [sync] line counts these copies.

agc_driver_tests copies a buffer the GPU just wrote into host memory,
once directly and once behind a label into the destination, and checks
the bytes and that the copy did not drain.

PPSA14632 (LEGO Horizon Adventures) copies 256 KiB of GPU results into
host memory this way, about 50 times per 10 s: device drains drop from
about 50 to none per 10 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 16:07:13 +02:00
Adrià 56bfc4d1c9
Merge pull request #2034 from TheAggie/fix/shader-truncated-decode-out-of-range
fix(shader): report a truncated memory, image or export instruction as out of range
2026-10-10 08:08:33 +02:00
EdouardSence 52b77c2022 fix(agc): defer shaders that cannot be prepared at registration to their first use
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 14:26:30 +02:00
Scott Schuster 1e5b8d9e7c fix(shader): report a truncated memory, image or export instruction as out of range
ReadRawComputeShader decodes a growing prefix of guest memory and reads
more when the decoder throws std::out_of_range, which the vector, scalar
and instruction-length checks throw for an instruction cut off by the end
of the span. The memory (SMEM, DS, MUBUF, MTBUF, FLAT), image and export
decoders threw std::runtime_error instead, so a two-word instruction
across a window boundary failed the read: vkQuake terminated on "truncated
FLAT instruction". These now throw std::out_of_range too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:02:17 -05:00
Adrià Franch 0cbc9586bc feat(shader): carry the shader float mode into the recompile request
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-08 22:17:07 +02:00
boykopovar 2767da6a8a fix(tests): check Vulkan availability before shader registration 2026-10-08 19:09:13 +03:00
boykopovar 6741e90a1c fix(shader): restore prepared image compatibility and update regression fixtures 2026-10-08 18:32:12 +03:00
Dean b25c6cce04
Merge pull request #1362 from gjonn/fix/agc-int-sel-send-data
fix(libSceAgcDriver): do not raise an interrupt for RELEASE_MEM send-data-after-write-confirm
2026-10-08 22:36:24 +13:00
Adrià Franch ef451ab7ac test(agc): CLEAR_STATE resets SPI_PS_INPUT_ENA/ADDR to zero 2026-10-08 10:16:02 +02:00
Gjon Gojcevic 9e4e3a8976 fix(libSceAgcDriver): do not raise an interrupt for RELEASE_MEM send-data-after-write-confirm
INT_SEL 3 waits for write confirmation before the data write and sends no interrupt; only the other non-zero values raise an end-of-pipe event.
2026-10-07 20:18:20 -04:00
Adrià 2cc5735d30
Merge pull request #913 from adriadam10/feat/agc-submit-multi-acbs
feat(libSceAgcDriver): implement sceAgcDriverSubmitMultiAcbs
2026-10-07 22:29:47 +02:00
Adrià Franch 78193c10f8 feat(libSceAgcDriver): implement sceAgcDriverSubmitMultiAcbs
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-07 22:04:58 +02:00
Merijn a089fe16ba test(agc): cover the misaligned event and the delivered context id
sceAgcDriverGetEqContextId is now checked with a misaligned event like
sceAgcDriverGetEqEventType, and on the end-of-pipe events the submit
test delivers: a graphics interrupt reports context 0 and a compute
interrupt context 0x20, the queues that raised them. The TechnicalDebt
line says why ident is returned and that KytyPS5 returns the event's
data instead.

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:53:21 +02:00
Merijn 29a4d4b035 feat(agc): implement sceAgcDriverGetEqContextId
Return the id a graphics event was registered with in
sceAgcDriverAddEqEvent, which titles derive from their context
(PPSA12544: 0 or 1 for graphics, 0x20 + 8 * pipe + queue for compute) and
read back to dispatch the event. Other event filters, a null or
misaligned event and an id above 32 bits throw. The meaning is an
assumption and recorded in the technical debt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 21:53:21 +02:00
Celegans12 4e18def4c1 fix(agc): accept unaligned shader headers
Driver::RegisterShader required the Shader header at an 8-byte
boundary (alignof(Shader)) and read its fields through the pointer.
PPSA26344 uses its shader packs in place: its compute shader objects
point at headers 4 and 1 bytes past an 8-byte boundary, so the first
sceAgcCreateShader of such a header threw "null or misaligned address".

The header is now checked for null and readable memory at any
address, and its fixed fields and the registers the trace prints are
copied out with memcpy. The code address must still be 256-byte
aligned, and the header and size checks are unchanged. The console's
rule is unknown and is listed in TechnicalDebt.
2026-10-07 04:23:21 +02:00
Dean 451b72ecc6
Merge pull request #695 from tv7/feat/agc-submit-multi-dcbs
feat(libSceAgcDriver): implement sceAgcDriverSubmitMultiDcbs and sceAgcDriverAgrSubmitMultiDcbs
2026-10-06 22:26:54 +13:00
Nicolas Abosch e5191ebf13 fix(agc): reuse validated raw compute snapshots 2026-10-05 22:52:03 -03:00
Nicolas Abosch 41cd420cc6 feat(agc): support version initialization and raw compute programs 2026-10-05 22:47:38 -03:00
alfalfa-star 124be3e7e8 feat(agc-driver): execute boolean SET_PREDICATION
SET_PREDICATION threw, and packets with the predicate header bit were
rejected. The executor now keeps the queue's predication: operations 3
and 4 read a 64- or 32-bit value and bit 8 selects whether predicated
packets run when it is non-zero or zero; operation 0 clears it. Before
a predicated packet runs while predication is active, queued labels are
recorded and the value is read, as for COND_EXEC; the packet is skipped
when the predicate fails.

Indirect buffers are inlined when a submission is copied, so a
predicated INDIRECT_BUFFER call keeps its header in front of the
inlined commands and records their end like a COND_EXEC range; the
block runs or is skipped on one evaluation, as on the command
processor. Query predication, a predicated COND_EXEC, predicated chains
and flips inside predicated command buffers throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 21:02:47 +02:00
Lexroid 3e65449c40 feat(libSceAgcDriver): implement sceAgcDriverSubmitMultiDcbs and sceAgcDriverAgrSubmitMultiDcbs
Both submit each DCB of the address and size arrays to the graphics queue
in array order, as sceAgcDriverSubmitDcb does for one. A count of 0 is
accepted without reading the arrays; null arrays with a non-zero count and
arrays outside guest memory throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:07:20 +03:00
Dean Galvin 04f12028dc fix(agc): run a wait-free submission of another queue after queue 0's earlier submissions
PPSA21564 submits the queue 0 work of each intro video frame F first, then 1
to 5 ms later a 12 MiB DMA_DATA on queue 0x3a that refills the video texture
F sampled, for frame F + 2 (two textures, used in turn). Nothing orders them
but time: no label, wait or flip lies between the copy and the draw, which
is the first packet of the frame's first submission. A GPU that keeps up
starts each submission as it arrives, so the draw reads its texture right
after the frame is submitted, and the copy submitted after it lands later,
every frame.

Here the queue workers record independently, so the copy was recorded either
before or after the draw depending on how far queue 0's worker lagged: the
video showed the new frame or the one before, and every switch between the
two was a frame shown backwards.

A submission with no waits on another queue now starts only after queue 0
has run every submission it received earlier, as on a GPU that keeps up.
While queue 0 is blocked in a wait, the held submission is released when its
queue stores the awaited label (it or a later submission of that queue), or
when nothing else could store it: no other worker is running and no recorded
GPU work or completion writes it. A GPU that keeps up would be blocked at
that wait too and run the other queue's submission meanwhile, so the order
holds only while queue 0 still has work it would already have done, and no
wait can deadlock on it.
2026-10-04 01:46:55 +13:00
Dean Galvin a3ffb73309 perf(agc): deliver end-of-pipe interrupts after their work without draining
A RELEASE_MEM with an interrupt select drained the device: the queue
worker submitted everything recorded and waited for the GPU before it
stored the label and delivered the interrupt. PPSA21564 issues about 300
of them per frame, so the worker and the GPU took turns.

The label is now recorded like any other (on the GPU behind the work, or
as a completion action behind pending write-backs), and the delivery
becomes a completion action of the batch holding the work before the
packet (Recorder::AfterRecordedWork, after the batch's earlier
completions). It is counted with the completion labels, so idle workers
and pollers reap it, and it starts the label flush deadline. With draws
or dispatches recorded since the last submit the batch is submitted at
once. A batch holding only label stores stays open and goes out with the
next work, at the label flush deadline, at a wait, at a flip or at the
end of the submission, like any other pending label. A label the GPU
cannot store, or one that does not decode, still drains.

The interrupt so never arrives before the work ahead of it completed and
its label landed; it can only arrive later, by at most the flush
deadline plus the work recorded into the same batch.
2026-10-03 09:56:05 +13:00
Dean Galvin a36ef52b33 fix(agc): decide a 64-bit label wait by the dwords its mask compares
A WAIT_REG_MEM_64 whose mask leaves the high dword out compares the low
dword alone, but the wait paths looked for all eight bytes: the
recorder's label table and the stores committed since the submission
want an entry for each dword, while a producer's RELEASE_MEM or
WRITE_DATA stores 32 bits, so neither ever satisfied such a wait, and the
held-at-submission check refused it when an earlier packet stored the
unrelated high dword.

Pm4::WaitAwaitedBytes gives the bytes that decide the compare (4 for
WAIT_REG_MEM and for a WAIT_REG_MEM_64 whose high mask dword is zero,
else 8), and the wait paths use it. The timeout report now prints a
64-bit wait's reference and mask (it printed the reference's two halves
as "ref" and "mask").
2026-10-01 08:39:53 +13:00
Dean Galvin 7df4ce4007 fix(agc): satisfy label waits from stores made after their submission
A title may reclaim a label (reset it to 0) as soon as the submission
that released it is done, trusting every queue waiting on it to have
passed its WAIT_REG_MEM within microseconds, as the CP polls from the
moment it reaches the wait. A worker that reaches a later wait of the
same submission long after (it records and compiles in between) found
the reset label and timed out.

A memory wait now also passes when
- a label store the driver committed (recorded on the GPU, as a
  completion action, or on the CPU) after the wait's submission gave the
  awaited bytes a value the wait accepts (the last four stores per dword,
  with their eventSerial stamps). Such a store belongs to this use of the
  label or a later one, since the title reset it before submitting.
- the awaited bytes held such a value when the game submitted, and no
  earlier packet of the queue (in the submission, or one still queued or
  running) stores to them.

A store made before the submission never satisfies a wait.
2026-10-01 08:39:53 +13:00
Dean Galvin 33d058fd35 feat(agc): register driver eq events and deliver end-of-pipe interrupts
sceAgcDriverAddEqEvent and sceAgcDriverDeleteEqEvent were NotImplemented,
so a title that waits on its equeue for the GPU to reach the end of a
command buffer could never be woken.

AddEqEvent now registers a graphics-core event (filter -14, ident = the
given id, EV_CLEAR, data counting the interrupts since the last wait) on
the kernel equeue and remembers the registration; DeleteEqEvent removes
both. An invalid equeue or deleting an event that is not registered
throws.

The driver delivers the interrupt when it executes a RELEASE_MEM whose
INT_SEL is non-zero. Such a packet is not deferred as a GPU label: the
recorded work before it is drained, its memory write (if any) is done,
and then the event registered with the id of the issuing queue is
triggered (0 for the graphics ring, the ACB queue id for compute), which
matches how titles derive their event ids from the ring they submit on.
2026-09-29 13:15:25 +13:00
boykopovar 74613f7e0f feat(agc): extend graphics compiler contract and add mesh and tessellation paths 2026-09-15 21:19:06 +03:00
boykopovar 990ae6a0ff feat(agcDriver): add opcode 18 handling as clearing 2026-09-14 11:18:21 +03:00
boykopovar d31e35e1ab feat(agc): add flip presentation and suspend synchronization (wip) 2026-09-14 07:39:38 +03:00
boykopovar ed171b23da feat(agc): add driver submission queue and vulkan compute path 2026-09-14 06:37:19 +03:00