kevent threw for any descriptor that is not a guest socket. The read end
of a pipe now gets the descriptor knote, following FreeBSD 11's
filt_piperead: data is the number of buffered bytes and EV_EOF is set
once the write end is closed. GuestSockets::Ready falls back to the new
PipeReadState for a descriptor that is not a socket, and WaitAny waits on
the pipe with ppoll on Linux or in 1 ms WSAPoll slices on Windows, only
when the queue holds a pipe. On Windows the equeue waker now starts
Winsock itself, since a queue can wait on a pipe before any socket exists.
The host heap's VK_EXT_memory_budget report lowered a set
APS5_HOST_IMPORT_MIB of 6400 MiB to 4480 MiB on Resident Evil Village
(PPSA01556), where 6400 MiB runs without a vkAllocateMemory failure, and
85,047 imports were refused. A set APS5_HOST_IMPORT_MIB is now never
lowered by the report, and the default is held to the report only once
an import's vkAllocateMemory has failed. The failure ceiling is
unchanged. The first time the report would lower the budget, a [gpu]
host import budget line prints the heap's reported budget and usage and
the live imports, and the refusal line names where its budget came from.
A heap mirror is refreshed from the write watch. acquireMirror checked
the watch only when it made a mirror and reused an existing one as it
was. Once part of the range left the watch, the next address-based build
threw "a heap mirror's range is no longer write-watched" from the
refresh, and a build served from the address-space cache threw the same
from its heap runs. A range leaves the watch for good when a host import
that does not keep the watch covers it: on Windows a direct import of
private memory that is not settled (below the 1 MiB settle floor, or any
when imports cannot be settled), elsewhere any import while imports are
compared.
A heap mirror now records the unwatch serial at which its range was last
seen fully watched. Its reuse, and a cache hit over it, compare that
serial with GuestMemory::UnwatchSerial and look at the watch again only
when a range has left it since. A mirror whose range is no longer
watched is not reused, the cache is not taken (before its imports are
stamped), and the pass plans the range again: an import if the budget
allows, else a copy. A refused heap mirror also drops the registry's
stale one, whose buffer would stay allocated otherwise.
sceSysmoduleIsLoaded, sceSysmoduleLoadModule and sceSysmoduleUnloadModule
return success for an id missing from the module table, because titles that
load or unload such an id aborted otherwise (b4db9fdd6, a2520aa07). Nothing
tested or recorded it, and two commented-out throws were left in the code.
Remove the commented-out throws, list the three functions under Silent stubs
in the library's technical debt file, and test that unknown ids succeed
without changing state while the internal variants still throw.
Written with AI assistance (Claude).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Since ab3e92605 the entry stub gives each missing standard handle
anyps5.log beside the executable, and the crash report writes to the
standard error handle, so it reaches the log too. The entry now records
only what is still lost: the stub's errors about the executable path or
about waiting for the previous LoadExec process, written before the log
is opened, and output to the missing handles when the log cannot be
created.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_0154rPujpieMaCnZ6WMobg7M
A geometry program with VGT_GS_OUT_PRIM_TYPE 1 was refused as an
unsupported output assembly. Each primitive export of an NGG line-strip
program is one line whose indices are packed like a triangle's, so the
mesh module now declares OutputLinesEXT with PrimitiveLineIndicesEXT for
it. A subgroup gets VGT_GS_MAX_VERT_OUT - 1 lines per input primitive and
at least GE_MAX_OUTPUT_PER_SUBGROUP line slots; fast launch keeps using
PRIM_AMP_FACTOR. The output size is part of the cache, draw, validation
and pipeline keys, and the request serialization version becomes 31.
Triangle-strip geometry is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The logger thread, the bounded queue that blocks its caller, the LogSink
hook, APS5_LOG_SYNC and APS5_LOG_STATS, where the queue is flushed and
what stays outside it: direct fprintf(stderr) and printf calls are written
at once, so they can appear before older queued lines.
APS5_LAZY_SHADER_PREPARE=1 keeps registration validation and decoding but skips queueing the preparation. Draws and dispatches prepare a deferred snapshot on first use and cache it; explicit ABI resolution still prepares its stages. Titles that register thousands of shaders at boot no longer hold every prepared shader in memory before the first frame.
Cover lazy dispatch, metadata and failure cases in both asynchronous and synchronous modes.
AnyPS5 exports some libkernel functions, such as __error, from
libc.prx, which libkernel.prx links. On Linux they resolve through that
dependency. On Windows, the import stubs search only the listed
modules, and libc.prx was listed only when libSceLibcInternal.prx was
needed. A PS4 homebrew that needs libkernel.prx but no C library (the
OpenOrbis graphics sample) failed with "unresolved ELF import
9BcDykPmo1I". libc.prx is now also listed when libkernel.prx is needed.
PS5 executables need libSceLibcInternal.prx and convert unchanged.
Village's 2x checkerboard puts its two samples at x = +4/16 and -4/16
and swaps their indices on odd rows. On a device without a 2x2 sample
location grid every pixel takes pixel (0,0)'s locations, so the odd
rows hold the console's sample 1 in sample 0 and the reverse: a pass
that reads the samples by index, as checkerboard reconstruction does,
exchanges the two half-pixels of every odd row, and SampleID, sample
masks and exported coverage name the wrong samples there.
Such a pattern holds one set of positions in every pixel, so drawing
with pixel (0,0)'s locations is exact up to the order of each pixel's
samples. The draw now keeps the guest's order: the pixel shader reads
SampleID, applies the sample mask and exports coverage through each
pixel's order, and every render pass drawing with the stand-in puts the
samples of its multisampled attachments in the stand-in's order before
it and back in the guest's after it. Color targets do that through a
storage view of their texels as unsigned integers, depth through a copy
and a per-sample pass writing FragDepth, and stencil through
VK_EXT_shader_stencil_export or one pass per bit.
It needs shaderStorageImageMultisample with a storage view of the color
format, sampleRateShading, and a depth format FragDepth writes back
exactly (16-bit or 32-bit float); otherwise, and for patterns whose
pixels hold different positions, the stand-in alone is used, and the
log line says why. Devices with the grid are unchanged.
Recompile requests carry the order as serialization version 32 (#6470
takes 31), and the shader cache format becomes 40 (open pull requests
take 37 and 39).
sceImeDialogInit wrote "Slayer" into an empty text buffer and the
dialog finished on the second status poll, so a title never got text
from the user: ProsperoRadio's search typed "Slayer" by itself and
found no station.
The dialog now runs until the user answers on the host keyboard, the
way #4756 made sceImeOpen's panel-less IME take it. While no IME panel
is open, libSceIme hands the game window's text and editing keys to
a sink that the dialog registers. Typing edits the text up to
maxTextLength without splitting a surrogate pair, Enter writes it to
the title's buffer and finishes with end status 0, and Escape finishes
with 1. Abort still finishes with 2. A second Init reports busy, and a
null parameter or buffer or a maximum length outside 1 to 2048 is
refused, as in shadPS4.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
A resource-cache entry keeps every sampled texture of its descriptor sets alive. The sampled texture cache passes over textures something else holds and, past its ceiling, drops them anyway; textures it makes without caching are never in it. Entries holding either kept them alive with no budget counting them, bounded only by the 1,024-entry and 65,536-element counts, until device memory ran out.
The texture cache now marks such textures released. ResourceCache::Insert refuses descriptor sets holding one and hands the entries that hold one back to the caller, like RemoveImage does for evicted storage images.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Imports were held to a configured budget only. With APS5_HOST_IMPORT_MIB
at 6864 on a 15.4 GB PC, Village's imports reached the NVIDIA driver's
pinning ceiling of about 6.56 GiB, and from there every import was tried
and failed at vkAllocateMemory, 992 times.
Where the device enables VK_EXT_memory_budget, the budget is now also at
most the host heap's reported budget less its other usage and an eighth,
in 64 MiB steps, read at most once a second. A vkAllocateMemory failure
within an eighth of the budget, or the third in a row, lowers the budget
to the live imports, so later imports are refused or evict instead of
failing; an isolated failure further below only marks its range failed,
as before. A remembered refusal stays remembered while the budget does
not grow.
FC 27 stops on Windows with Failed to load module / error 126 because this PRX is absent. Export Initialize and Terminate as no-ops and keep RequestDevices throwing until the buffer layout is known.
Co-authored-by: Cursor <[email protected]>
sceAudioOut2ContextGetQueueLevel left out the grains that
sceAudioOut2ContextAdvance completed and no push had consumed yet. While
pushes filled the 40 ms cushion the level stayed at 0, so a title that
renders as many grains as the level leaves room for and pushes them on
another thread rendered a full queue again after each push. PPSA23226's
Wwise sink does that and stopped once more than 16 completed grains
awaited a push.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An address-based build asks for every registered GPU range whenever it
rebuilds its address space, and at a full budget it was refused each
one again: a title held at its default budget counted 304,394 refusals
over about 12,000 ranges at its title screen.
A request that may not evict (the build's walk, an import when a range
is mapped) now remembers a refused run with the live bytes and the
budget at the refusal. Until live imports drop below those bytes, the
budget changes or one of the run's ranges changes, a request for that
range is answered from memory, still counted as refused and also as
remembered, and a remembered block is skipped, so a block stepped down
for the budget keeps its import across registry changes. A request
that may evict always tries again, and a new import forgets the
refusals of the runs it covers. The shutdown report and the budget
line print how many refusals were remembered.