sceKernelGetSystemExVersion, sysKernelGetLowerLimitSysexVersion,
sysKernelGetLowerLimitUpdVersion and sceKernelGetTraceMemoryStats were
NotImplemented stubs in libkernel_sys. libkernel, libkernel_sys and
libkernel_web of firmware 13.42 answer them without asking the kernel:
empty strings, a zero version and zero statistics, with the null checks
and return codes the firmware uses, including the plain 22 of
sceKernelGetSystemExVersion.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD resolves the descriptor with getvnode in sys_fchflags
(vfs_syscalls.c:2403), which fails with EINVAL for a descriptor that has no
vnode. fchflags threw for a kqueue descriptor instead.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
AppMetadata throws length_error for a title or title ID that does not fit its
buffer, which the title ID lookup did not catch, so a homebrew title with a long
ID made sceConvertKeycodeGetImeKeyboardType throw instead of using the PS5
fallback. Catch it with the other metadata failures and cover oversized,
malformed and missing metadata at the export. sceUserServiceGetKeyboardType also
gets a test that a malformed ANYPS5_LANGUAGE is rejected with the output left
untouched, and the debt entries say so.
The recompiler marks every guest f32 add, subtract and multiply
NoContraction, because the guest rounds each one separately.
SPIRV-Cross turns each of them into a call to a helper that Metal
compiles without optimization, which dominates fragment time on
MoltenVK, and its multiply helper, fma(a, b, +0.0), turns a -0.0
product into +0.0.
On MoltenVK they are now GLSL Fma whose second step is exact, which
gives the separately rounded result: a * b is fma(a, b, -0.0), a + b
is fma(1.0, a, b) and a - b is fma(-1.0, b, a). The sin, cos and log2
series and the offset image coordinates and gather texel scale use the
same forms. The choice is a SpirvTarget flag set for
VK_DRIVER_ID_MOLTENVK, part of the shader cache key and of the request
serialization (version 44); other drivers are unchanged.
agc_driver_f32_separate_rounding checks a multiply feeding an add or a
subtract on rows where fusing them changes the result. It,
agc_driver_f32_arithmetic_nan and agc_driver_f32_denormal_flush also
run the fused forms on devices that do not use them, so lavapipe checks
them against the same rows.
Co-authored-by: Ycaro Gabriel <[email protected]>
On Windows a thread_local destructor in a module runs after emulated TLS freed
the object (prx_thread_local_destructors). Keep the per-thread map in
HostThreadLocal like the other per-thread objects in libSceAgcDriver.
A linear color target got a resident image only when its memory already
had a host import (#6892). Every other draw into it was synchronous:
copied in, drawn, waited for on the CPU and copied back. #6892 only
looked the import up because a first version that made it broke
agc_driver_pixel_ancillary_word and agc_driver_pixel_fixed_point_position:
the import over a short-lived block was compared, and the next block
allocated at the same address stayed outside the write watch. A page
committed again inside an excluded block is watched again since #6759.
On a lookup miss the draw now makes the import itself, with the new
ColorTarget requester, when its registered ranges are writable, the
whole import run is write-watched and the import keeps it watched: a
separate alias of shared memory, a direct import of private memory that
settles on Windows (1 MiB or more, when the import probe keeps the
watch and a settle queue exists), or an import the probe keeps watched
on other systems, where a dma-buf import must stay watched too if the
range has shared backing. Private pages that the write watch armed
read-only count as writable when their guest protection is, because the
import opens them (a resident direct-memory run the CPU has not stored
to since its last collect). The gate's scan stops at the first page
that decides a refusal, and a run of private memory whose direct import
would be compared is refused after one page query. A refusal is
remembered for its run until the registry, the committed pages
(Windows) or the write watch change, or a shared alias is mapped over
the run, so a target that stays synchronous costs a lookup per draw.
importRunAt checks this after its remembered failures and refusals and
before it retires or evicts anything, through the helpers its own
decision now uses: importWatching (settled, watched host-pointer and
dma-buf imports) and importPages (the Windows page scan). A target whose
import would be compared stays synchronous, because a compared import
makes every refresh of the resident image compare the whole target. A
target over an import that already exists is resident as before, with
ExistingHostImportFor as the fast path, and APS5_NO_RESIDENT_LINEAR=1
still keeps every linear target synchronous.
This is the case draft #1191 (MichonGoddijn231849) was to bring back
on top of #6892 once partial-block write-watch coverage existed.
agc_driver_linear_target_import_creation covers a target in freshly
registered, write-watched memory (the draw is recorded, the target is
imported and stays watched), a block freed and allocated again at the
same address (it is watched, and the next draw starts from the CPU's
bytes), a resident direct-memory run armed read-only by the write watch
(the draw is recorded and the target imported; Windows), the gate over
armed and read-only private memory (a compared import of an armed run is
refused without opening it, memory committed read-only behind a writable
registry range is refused without a count, an armed run is imported
where imports stay watched; Windows), a 64 KiB block whose import would
be compared (no import, a synchronous draw, the block stays watched), a
1 MiB block the guest registered read-only (no import, a synchronous
draw), a 1 MiB run with one 64 KiB block outside the write watch
(refused, the target stays watched), and the same run once that block is
committed again (the refused run is imported; Windows).
On other systems the test forces ImportWatch::Watch, so the watched
cases also run where imports are compared (lavapipe in CI). The import
requester tests count the new requester and check that it makes no
compared import, no import of a range the guest cannot write and no
import of memory outside the write watch.
Co-Authored-By: Claude <[email protected]>
agc_driver_linear_target_draw_bindings required the second copying draw
to stay in the open batch. That holds only while the copied target's
memory is write-watched. The draw's own store of the drawn linear targets
stores the copied target too, and when its memory is not watched (a host
import that is compared, as on llvmpipe), marking the target drawn again
captures its guest bytes, which syncs through the open batch: one more
submission, after the draw is recorded.
The test now runs its scenario twice: once as the device sets it up, and
once with the block taken out of the write watch after the first draws.
A watched target must not submit; an unwatched one must submit exactly
once. The descriptor-set and output checks run in both.
Co-Authored-By: Claude <[email protected]>
Only a lean recorded draw stored the drawn linear targets whose memory
it touches before it. A draw on the other path (synchronous because a
color target is not resident, a stencil-replay build checks faults or
APS5_SYNC_COMPLETION_DRAWS=1 is set, or recorded with
APS5_DRAW_TRANSITIONS=1) found such a target still only listed. Its
resource build stores the pending images over the memory it binds, but
not over the leased heaps an address-based build maps, so a read
through a pointer got the bytes from before the draw that rendered the
target.
The same store now runs on that path before the draw's command batch is
made. A synchronous draw's batch submits the recorded work first, so
the store lands before it in queue order.
AddressPasses --synchronous-target-read renders a linear target, then,
in the same submission, reads it through the address table in a draw
into an unregistered target, which is not resident, so that draw is
synchronous. It must read the drawn color, as it does on main. On a
device where the linear target is not resident the rendering draw is
synchronous too, and the case says it is skipped. The new
agc_driver_address_passes_draw_transitions entry runs the --targets
cases with APS5_DRAW_TRANSITIONS=1, so a recorded draw takes the other
path; its linear case reads the bytes from before the rendering draw
without this store.
Co-Authored-By: Claude <[email protected]>
A draw into a resident linear target only lists it, and an
address-based build stores no pending image over the leased heaps it
maps, so before such a dispatch the target reaches memory only through
the store recordDispatch makes of the drawn targets the dispatch
touches.
LinearTargetDispatchAccess draws into a resident linear target under a
scissor, then, with no completion point in between, runs an
address-based dispatch that loads a drawn texel through a pointer and
stores it into another registered block, which must hold the drawn
value. Without that store the dispatch reads the bytes from before the
draw; with main's store after each draw it reads the drawn value too.
Co-Authored-By: Claude <[email protected]>
The store of the drawn linear targets before a recorded draw ran after
PrepareDrawBindings, which keeps the draw's snapshot descriptor sets in
the batch open at that moment only. When the store synced that batch
(an earlier address-based draw in it reads the target's memory) or
submitted it, the draw was bound in the next batch with nothing keeping
its sets. They were recycled once the draw was recorded, and the next
draw with the same set layout rewrote them before the first draw ran,
which then read that draw's buffers.
The bindings are now prepared after the store, in the batch the draw is
recorded in, and their snapshots skip memory the store writes.
LinearTargetDrawBindings renders a linear target with an address-based
draw, then runs two draws that each copy a snapshotted buffer into a
copied output. The first one stores the target, which syncs the batch.
Both outputs, both targets and the descriptor sets the two copying
draws allocate are checked.
Co-Authored-By: Claude <[email protected]>
A drawn target kept the bounding box of its draws' scissors, so a store
at a completion point also wrote the pixels between two disjoint
scissors, over CPU stores made there after the draws.
- The target keeps the clipped scissor rectangles. Two merge only when
their union is a rectangle: one inside the other, or the same columns
overlapping or touching in rows, or the same rows overlapping or
touching in columns.
- The store still copies the rectangles' bounding box out of the image,
but writes to guest memory, for each row, only the union of the
column runs of the rectangles that cover that row.
- At most 16 rectangles wait for a completion point: the draw that
brings a 17th stores the target right after it, with all of them.
- A target the rectangle store does not cover (attachment proxy, several
mips or layers, 3D or DCC surface, results of a dispatch, fill or
copy) is stored after each draw again, instead of whole at the next
completion point.
- LinearTargetRectangleBound draws and waits once before its draws, so
the target's upload runs before the CPU stores instead of in the same
batch, adds CPU stores between the two scissors, and adds a case with
17 one-row scissors and a CPU store between two of them.
Co-Authored-By: Claude <[email protected]>
- MarkDrawn registers the scissor while any layer is pending instead of
while the image is dirty. A flush on another thread can take the image
out of the pending set between MarkSliceDirty and the registration;
layerPending changes only under the GPU lock, so the flusher, which
reads the drawn area after taking that lock, stores the widened area.
- AccessKeptByCpu no longer skips or evicts a drawn target. Its store
writes CPU-stamped blocks inside the drawn rectangle, so the hook's
CPU-kept rule would drop the draw's results.
- A drawn target that is stored whole keeps main's rule that CPU-written
64 KiB blocks stay the CPU's.
- A linear target that is no longer cached is stored after its draw, as
before: the pending registry holds raw pointers.
- LinearTargetCompletionPoints skips without host import, where the
target is never resident. LinearTargetCpuWrites adds a driver read of
the target between the CPU stores and the store.
Co-Authored-By: Claude <[email protected]>
- A flush from a thread without the GPU lock takes the lock before it takes a drawn target out of the pending set, so a flip, label or interrupt on the queue worker cannot run between the two and get ahead of the target's store.
- A label written on the GPU stores the drawn targets first and submits that store with the label, so the label's end-of-pipe interrupt is not held back to a later submission.
- The pending-writer check skips the target's own earlier stores only when the flush stores that target alone.
Co-Authored-By: Claude <[email protected]>
A type 0 signal read the semaphore, added one and wrote it back, so an
increment made by a CPU thread or another queue in between was lost.
Signals now store or add with atomic operations and a wait takes one
with a compare-exchange. The new test races two queues and a CPU thread
incrementing the same word.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With the store deferred to completion points, a draw or dispatch that
reads a drawn linear target's guest memory through an address, or
writes it through a buffer, saw the bytes from before the draw that
rendered it, and the later rectangle store would land over its writes.
agc_driver_address_passes caught the read: a draw reading through the
address table the linear target the previous draw rendered read the
bytes before it, where the per-draw store had stored them first.
Before a recorded draw and a dispatch, the drawn targets whose memory
the work reads in place or writes are stored, and all of them for
address-based work, whose memory is unknown.
Co-Authored-By: Claude <[email protected]>
A recorded draw into a resident linear color target stored the whole
image to guest memory right after the draw. Every draw copied the full
surface, waited for the previous draw's store in the open batch (the
pending-writer check of FlushPending), and the recorded copy landed over
CPU stores made after Draw returned (#8152).
A draw now records its scissor, clipped to the image, as a union
rectangle in the target and lists the target as drawn. The listed
targets are stored before each point where the guest can first observe
the draws' completion: a flip (inside its locked block, before the
flip's submit, so the flip's end-of-pipe interrupt and the presenter
follow the store), a drain (which covers APS5_SYNC_FLIP=1),
VulkanDevice::WaitIdle, a label the GPU writes for the graphics queue
(WriteLabelOnGpu), and an interrupt-only RELEASE_MEM in
preparePacketMemory and in commitLabel, which submit the store's batch
before the interrupt can be delivered. The idle store, the access hooks
and the eviction store are unchanged and also store by rectangle.
A store of a drawn target copies the rectangle out of the image and
writes it row by row at the guest pitch into the host import, stamped
with one MarkWritten per run of 64 KiB blocks. A block that only other
driver stores wrote since the target's generation keeps the old rule.
In a block the CPU wrote, or that changed outside the write watch, only
the rectangle's rows are stored, so CPU stores outside the rectangle
stay, and the target re-uploads at its next use. A target with an
attachment proxy, several mips or layers, a 3D or DCC surface, or
results of a dispatch, fill or copy is stored whole at the same points.
A store no longer waits for the same target's previous store: the
pending-writer check skips the write notes of the target's own stores
when it has a host import.
APS5_PROFILE_DRAW adds the drawn targets' stores by reason and the
pending drawn targets (count, MiB, oldest) to the [storage] line.
The tests cover a target that stays pending across draws and labels
until WaitIdle without a submission, #8152's CPU stores after Draw
(ported from #8246, seeded and unseeded, inside and outside the write
watch), CPU stores outside the drawn rectangle and in the gap before a
target that starts past a 64 KiB boundary, and a flip, a drain, a label
and an interrupt-only RELEASE_MEM through AgcDriver::Submit with the
queue held by a WAIT_REG_MEM, inside and outside the write watch.
Co-Authored-By: Claude <[email protected]>
MEM_SEMAPHORE (0x39) threw "cooperative command-queue waits are not
implemented". A signal now writes 1 (signal type 1) or adds 1 (signal
type 0) to the 64-bit semaphore; a wait polls until it is non-zero and
takes one. Mailbox waits throw.
Co-Authored-By: Claude Opus 5.5 <[email protected]>