Commit Graph

25 Commits

Author SHA1 Message Date
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