Implement scePthreadGetschedparam and scePthreadSetschedparam on top of
the thread priority, POSIX utimes and fsync on top of sceKernelUtimes
and sceKernelFsync, and sceUserServiceGetAccessibilityZoomFollowFocus
like the other accessibility queries. Add the remaining imports of
PPSA12544 as unimplemented exports and record unknown NIDs and
signatures in the technical debt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scePsmlMfsrGetContextBufferRequirement1100, scePsmlMfsrCreateContext1100
and scePsmlMfsrGetDispatchMfsrPacket1100 were not exported, so a title
importing them failed to load.
In Sony's libScePsml all three return 0x8A810001 (not initialized)
without writing their outputs until scePsmlMfsrInit has succeeded.
AnyPS5's scePsmlMfsrInit throws, so that is the only state a title can
reach, and the three calls now return that error.
The existing stubs for scePsmlMfsrInit, scePsmlMfsrReleaseContext and
scePsmlMfsrGetDispatchMfsrPacketSizeInDwords get their real names and
still throw. libScePsml_debug gets a CMakeLists.txt for the new
guest_psml test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A title's own libc.prx implements __cxa_thread_atexit and the
destructor pass of exit() through _sceLibcInternalThreadAtexit and
_sceLibcInternalThreadDtors. Its __cxa_finalize calls
_sceLibcInternalForceTlsDestructor.
Destructors are kept per thread and run newest first, including those
registered while they run, as FreeBSD's __cxa_thread_call_dtors does.
A null destructor or one outside every loaded image throws instead of
being skipped. The first registration installs the pass as the
libkernel thread exit hook, so it runs on every guest thread before
the thread can be joined. _sceLibcInternalForceTlsDestructor throws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Titles that list libSceUsbd.prx as a needed module stop in the
startup stub with "Failed to load module: libs\libSceUsbd.prx"
(GetLastError 126), even when they import no function from it.
Add libSceUsbd with an Export.cpp that contains only APS5_DUMMY_FUN,
like libkernel_sys, libkernel_web, libSceAgcVsh, libSceAjm and
libSceNgs2. The module now loads, exports no sceUsbd function and
guesses no signature, so a title that does import one still stops
with "FAIL: unresolved ELF import" for that NID.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IntrusivePtr<T> is three pointers: object, deleter, LibContext. The default
constructors zero them, the copy constructors copy them and take a reference
on the object, operator-> and operator* return the object, and the destructors
drop a reference and hand the object to the deleter when it was the last one.
The last-reference release of a pointer without a deleter needs the object's
destructor and the LibContext memory pool and still throws.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The constructor was a silent stub that left the guest object
uninitialised and c_str threw. The constructor now writes the 0x20-byte
object, c_str returns the buffer or an empty string, and the destructor
throws for a non-null buffer, whose release needs the LibContext
allocator.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3D textures in SW_4KB_S or SW_64KB_S whose smaller levels reach the mip
tail were refused ("3D texture mip tails are not implemented"). The
tail now follows addrlib's GFX10 thick layout
(Gfx10Lib::ComputeSurfaceInfoMacroTiled):
- a level enters the tail once its size, rounded up, fits the thick
tail extent (the block with its height halved for 4 KiB blocks and
its width halved for 64 KiB blocks) and at most GetMaxNumMipsInTail
levels remain (5 for 4 KiB, 10 for 64 KiB);
- each slab starts with one tail block, then the other levels from the
smallest to level 0;
- tail level i sits in slot maxMipsInTail - 1 - i, at the element offset
addrlib derives from that slot's byte offset and the 256-byte thick
micro block, at every depth slice.
The tests check element addresses of five volumes (4 KiB and 64 KiB
blocks, 1 to 16 bytes per element, odd sizes, a chain that starts in
the tail) against Mesa addrlib's Addr2ComputeSurfaceAddrFromCoord for
a Navi10 configuration.
PPSA21564 clears each occlusion query slot with a 256-byte WRITE_DATA
before its begin dump, about 8,400 times per frame in one level. The
clear is larger than a deferred label, so it is recorded at once, and
Recorder::NoteLabel entered all 64 of its dwords in the pending-label
table: 64 map inserts, 64 batch dword entries and 64 group entries, then
64 lookups in CloseLabelGroup and 64 erases when the batch is reaped. A
replay of that pattern (21 clears per batch, 20 batches in flight) costs
1.6 us for the inserts, 0.7 us for the close and 1.3 us for the erases
per clear, about 31 ms of the graphics worker per frame there. The new
path costs 0.07 us per clear in the same replay.
The table serves WAIT_REG_MEM values and CPU pollers, which read 4 or 8
bytes. A store larger than 64 bytes (the bound the driver's own label
history already uses) now enters one range instead of its dwords:
- table entries inside the range are removed, as the store replaced
them before: a lookup there finds no value and falls back to the
pending-write path, where it used to find the store's value;
- the range leaves with its batch, like the dword entries;
- PendingLabelIn sees the range, and the write-evidence check, which
syncs a capture read over any pending label dword, now also syncs
over a pending range (Recorder::WideLabelIn).
The store is still a pending write of the open batch (the flush hook
syncs CPU readers), still starts the label flush deadline and is
recorded exactly as before. Stores of 64 bytes or less are unchanged.
Test: agc_driver_recorder_tests largeLabelTests (a 256-byte label enters
no dwords and removes the older entry under it but not one beside it,
its range, pending write and deadline, a later 4-byte label over it,
a 64-byte label entered in full, everything gone after the batch).
Every occlusion counter dump the recorder handles on the GPU ends the
batch's occlusion query, copies the pending results, folds them with a
compute dispatch, then resets and begins a new query. Each of those
commands was resolved through vkGetDeviceProcAddr, a loader name lookup,
on every call: seven lookups per dump on the graphics worker, and a title
dumps the counter twice per occlusion query. The mesh argument and timing
paths resolved their bind, push, dispatch and reset per call too.
They join the entry points the recorder already resolves in its
constructor: vkCmdBeginQuery, vkCmdEndQuery, vkCmdResetQueryPool,
vkCmdCopyQueryPoolResults, vkCmdBindPipeline, vkCmdPushConstants and
vkCmdDispatch. The same functions are called with the same arguments;
APS5_NO_PROC_TABLE=1 still resolves every call.
Report the process time at which sceAudioOutOutput or sceAudioOutOutputs
accepted the port's last block. A port that never output reports 0, and
waiting with a null pointer does not move the time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
clock_gettime and clock_getres served three FreeBSD clock ids with the wrong
clock or not at all:
- CLOCK_VIRTUAL (1) was the monotonic clock on Windows and threw on Linux; it
is the user CPU time of the process.
- CLOCK_PROF (2) threw on both hosts; it is the user plus system CPU time of
the process.
- CLOCK_SECOND (13) returned the coarse realtime clock with nanoseconds and a
sub-second resolution; it is the current second with tv_nsec 0 and a
resolution of one second.
Behaviour follows FreeBSD 11 clock_gettime(2) and kern_clock_gettime /
kern_clock_getres in sys/kern/kern_time.c.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The DCB variant threw "not implemented" while the ACB one wrote a
5-dword packet. Both now go through Agc::Command::WritePrimeUtcl2 and
the size query returns 20 bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Format through the shared wide formatter, truncate to the buffer and
return the full length, as C11 K.3.9.1.3 specifies. A null buffer or
format, a zero or oversized n, %n and a null %s argument are
runtime-constraint violations that return a negative value.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A GPU-direct storage write-back retiles the image's elements into an
uninitialised device scratch buffer and then copies whole ranges of it
into the import or the import's unit shadow. The retile writes only the
bytes elements occupy, so every other byte of the copied ranges was
stored from uninitialised scratch memory: the padding of the tile blocks
a surface covers only partly (its last block column and row), mip tail
blocks, the bytes between mips and the pitch padding of linear surfaces.
Another surface over the same memory, or the CPU, then read garbage
there instead of what the memory held. Both the block-window path and
the whole-layer path did this.
Before the retile, the bytes of the copied ranges that no element holds
are now copied into the scratch from where they are current (a fresh unit
shadow, else the import), so the write-back stores them unchanged. Whole
tile blocks every byte of which holds an element are left out
(CoveredMipBytes), so only partly covered blocks pay for the extra copy.
Tests: CoveredMipBytes against the XOR address equations of the four
64 KiB XOR modes at every element size; a device test that stores
cleared images over patterned memory (tiled with and without mip chains,
off a 64 KiB boundary, 4 KiB standard, linear; plain and watched memory)
and checks every byte that is not a texel kept its pattern.
A title that ships sce_module/libc.prx makes addHost drop the host
libc.prx from the executable's dependencies. Every host prx needs it, but
their DT_RUNPATH is empty, so loading fails with "libc.prx: cannot open
shared object file".
Mirror the Windows rule from WindowsImportBuilder: when libSceLibcInternal.prx
is a host dependency and no host libc.prx remains, append libc.prx as the
last host dependency. The title's own imports still bind to the guest
modules first.
Refs #476