A join's budget refusal was never remembered, so a buffer bound across
ranges that the budget could not take tried the join, with a scan of
every import, at each upload's pre-pass. A join now remembers its final
refusal like any other evicting request, under the same conditions. The
memory keeps whether it was a join and answers only the same kind of
request, since a join skips the held-import check and never steps down.
An evicting refusal was remembered for the range holding the requested
address and dropped by a request for another range of the same run, so
requests landing in different ranges of one refused block, as uploads
and accesses across a block of adjacent ranges do, scanned the imports
every time. The memory now keeps the room the refusal left and answers
any range of the run that is larger than it: such a range cannot step
down to a block of its own either. A smaller range still recomputes,
and a run that cannot step down at all is answered for every range.
A protect below the main thread's stack mapping mapped only the
protected pages. Linux keeps a growing stack one guard gap (1 MiB by
default) above a readable mapping, so after a read-only protect, or a
protect later restored to read/write, at the bottom of the reported
stack the stack could no longer grow into the megabyte above it and a
deep enough call faulted where the console has stack pages. The pages
between the protect and the top of the stack are now mapped as well,
so the stack never has to grow past them.
The remembered refusal of a request that may evict expired on any budget
change. A smaller budget can only keep the request refused, and #6479
moves the budget in 64 MiB steps once a second, so the memory now ends
only when the budget grows, as #6479's stillRefused does.
A multisampled rasterizer drawing into one-sample targets keeps
PERSP_SAMPLE, LINEAR_SAMPLE and ANCILLARY inputs, and with PS_ITER_SAMPLE
also centroid inputs, since only sample 0's coverage reaches the targets.
Coverage inputs, and the sample index with POS_X_FLOAT or POS_Y_FLOAT,
still throw. The input rule for the non-iterated case follows
fdfawef324's proposal on #5907 (PPSA07411).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A draw whose rasterizer has more samples than its color or depth targets
threw "a color target's sample count differs from the rasterizer's" or
"the depth target's sample count differs from the rasterizer's". When
every target has one sample, MSAA_ENABLE is set, and there is no
alpha-to-coverage, no PS_ITER_SAMPLE and no sample-dependent pixel input,
only sample 0's coverage reaches the targets, so draw it with one sample
at sample 0's location in each pixel of the grid. One-sample depth images
are created sample-location compatible when the device allows it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SonyCrt.hpp kept the constraint handler slot in an unnamed namespace,
so every source file that included it had its own slot. A handler
installed with set_constraint_handler_s in WideConversion.cpp was not
the one qsort_s in FileAndHeap.cpp called, and its violations aborted.
The slot and its mutex now have one instance per module, and the
handler type uses the guest calling convention, as the handlers a title
installs and the default handler it gets back are guest functions.
At a full host import budget with nothing evictable, a request that may
evict tried again on every use, and each try walked every live import to
add up what could be evicted. In a title's first scene at its 6400 MiB
budget that was 37,627 refusals per present, each one a walk over about
4,400 imports, while the address-based builds kept every import held.
Such a request now remembers its final refusal, the one with no smaller
block left to try. Later requests for the same run, from the same range,
are answered from that memory, still counted as refused and remembered,
until an import is made or retired, the budget or the import window
changes, the run's ranges change, or an import could have become
evictable. An import becomes evictable only once the use count passes
its last use by the 256-use protection, and a held one also needs a
higher build floor, so the walk records the first such use among the
imports it could not evict, held ones apart: the memory survives a new
build while no held import can age out yet. Requests that may not evict
keep their behaviour.
A Windows import under 1 MiB returns to the write watch once its settle
read has completed, though main-queue work submitted before the read may
have used the import first, and an earlier import of the same pages may
not have run its first use yet. On the NVIDIA driver (617.42) neither
marks the pages once the read has completed, so the rewatch needs no wait
for the main queue. The docs said the marking lasts until the import's
first GPU use completes; they now say that a completed GPU use ends it,
the settle read included.
agc_import_settle_first_use submits 4096 copies of a 64 KiB import on
the main queue before its settle read is queued. The import must return
to the write watch while the copies still run, and the driver must mark
none of its pages afterwards.
agc_import_settle_earlier_import records 4096 copies of a 64 KiB window
import, replaces it with the import of its whole 128 KiB range, and
submits the copies only once the range is back in the write watch. The
driver must mark none of the range's pages while they run. A driver that
keeps marking fails either test.
The device setup logs each sample count's largest grid. On the RTX 3070
Laptop GPU (617.42) it is 2x4 at 2 samples, 2x2 at 4 and 1x2 at 8,
while maxSampleLocationGridSize is 1x1 for every count; the AMD Radeon
iGPU reports 2x2 both ways. So the 3070's 2x patterns take the native
grid, and the stand-in plus the exact path serve its 8-sample patterns
and devices without a grid.
The throw for a uniform partial sample mask that the order moves stays.
The order is fixed at decode, before compilation shows whether the pixel
program writes memory, and falling back would lose exactness on draws
the exact path gets right. The entry now says when the draw throws, what
#6451's stand-in did with it, and what a fix would take.
Retiring a Windows import whose settle read was still running waited for
the read's batch under the imports mutex, so lookups on every thread
waited with it. The retired import is now kept with that batch and freed
once the batch has completed. Only a guest unmap of its memory still
waits for the batch, in the pin waiter, which waits for the main queue
there anyway.
The [buffers] profile line counts these retirements and the unmaps that
waited, apart from the imports of 1 MiB or more settled inside their
window, which the old wait counter mixed with the retirement waits.
agc_import_settle_retire replaces a 64 KiB window import with the import
of its whole range, 64 times, right after its settle read was submitted.
No replacement may wait, at least one must find the read running, and
the last import must return to the write watch.
The debt entry said the RTX 3070 Laptop GPU reports a 1x1 grid for
every count. That is VkPhysicalDeviceSampleLocationsPropertiesEXT; the
device setup reads each count's VkMultisamplePropertiesEXT, which gives
an even grid at 2 samples, so the 3070 draws 2x patterns on its grid.
The entry also gains the transition that ends a pass drawing depth at
custom sample locations, and the throw for a draw with early depth or
stencil tests whose pixel program writes memory when the order moves a
partial sample mask into the pixel shader.
The Quake homebrew's libOSMesa.so.8 imports qsort_s, the only strong
import left unresolved once its symbol names resolve through NIDs.
qsort_s sorts as qsort does, with the context passed as the
comparator's third argument, and returns EINVAL after calling the
constraint handler for the runtime constraint violations of FreeBSD
13's qsort_s. The sort helpers take any comparator so both functions
share the FreeBSD 11 algorithm.
PPSA16833 imports it from libkernel, and its executable does not load
while no library exports the NID. The signature is unknown, so it throws
if called, like sceCoredumpWriteUserString.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FindSceCodeEnd looked for the PLT only after the last function of the
unwind table and failed with "PLT not found after the last unwind
function" otherwise. The libSceFios2.prx and libc.prx that Until Dawn
(CUSA02636) bundles have their PLT at 0xc8, at the start of the text
segment, before the code. In both, the last unwind function is
followed by the module's DT_FINI routine, which has no unwind entry,
and then by read-only data.
When no PLT follows the last unwind function, the search now also looks
before it. A PLT found there must not overlap an unwind function, and
the code extent then ends at the last unwind function, so what follows
it stays out of the scans and the code analysis, including code
without an unwind entry such as the DT_FINI routine of both modules
(recorded in the debt file). A missing PLT now fails with "PLT not
found before or after the unwind functions".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ShaderRegistry.cpp prepares the registered shader types 0, 1, 2, 4, 6 and 7, skips 5, and logs and leaves every other type unprepared at registration; Hello Neighbor 2, Ad Infinitum and Ghostrunner register type 8.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A heap mirror whose range had left the write watch was dropped and its
range planned again. When the host import budget refused the range, that
plan was a per-build copy, and past APS5_ADDRESS_COPY_MAX_MIB (64 MiB) the
copy stopped the process ("an address-based build copies ... past ..."),
where refreshing the mirror in full would have carried on.
Plan such a range again only when the import path takes it now. A cached
address space whose heap or sparse mirror has left the watch is rebuilt
only if importing the mirror's range succeeds, and the rebuild then finds
that import; otherwise the space is kept and the mirror refreshed in full,
every block counted as written. A build that plans its ranges tries the
import first, so acquireMirror keeps the existing mirror of a range that
reaches it, and a sparse mirror whose pieces are unwatched is refreshed in
full instead of planned again.
SetAddressCopyLimit sets the copy cap in-process, as SetHostImportBudget
sets the budget. The heap and sparse unwatch tests set it below the heap
the old plan would copy: the refused heap's mirror is kept and refreshed in
full (its unwatched block and a watched block the CPU wrote both reach it),
and once the budget holds the heap the next build imports it and releases
the mirror.
Trails in the Sky 2nd Chapter creates a custom rack with module id 0x20, which Custom.cpp rejects; the module and its structure are unknown (#4246).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Broken Sword - Shadow of the Templars: Reforged stops at AJM decoding codec 23, which is not one of the codec ids known in Ajm.cpp (MP3 0, ATRAC9 1, M4AAC 2, Opus 24).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The fchdir entry still said FreeBSD's search-permission check was not
modelled, although it has run on Linux since 0e4d94ce3 and now runs on
every host but Windows.
ARCHITECTURE.md and the debt entry describe how imports under 1 MiB are
settled without waiting and return to the write watch, which the debt
entry listed as not implemented.
A sparse heap mirror, made for a heap with uncommitted pages, is
refreshed from the write watch through its committed pieces. The check
that a reused heap mirror's range is still watched covered the heap
mirrors acquireMirror reuses and the cached space's heap runs, not
sparse ones: once a committed piece left the watch, refreshSparseMirror
threw "a sparse heap mirror's range is no longer write-watched" from
acquireMirror's reuse and from a cache hit's sparse refresh.
refreshSparseMirror now returns false when a piece is no longer
watched. Both callers already take false as not reusable: acquireMirror
makes the mirror again, finds the range unwatched, drops the stale
mirror and leaves the range to a copy, and a cache hit gives up the
cached space.
agc_sparse_heap_mirror_unwatch mirrors a 1 MiB reservation with 256 KiB
committed, takes one committed block off the write watch and stores new
bytes there from the CPU; the next build must not reuse the mirror and
must serve the new bytes. agc_sparse_heap_mirror_unwatch_no_space_cache
runs the same without the address-space cache. Without the fix both
throw. They are registered on Linux; the Windows path uses the guest
arena as the existing sparse heap test does and has not run on Windows.
A sleep of 1 ms or less returns once the AGC driver sees a GPU batch
complete (a thread waits on the recorder's timeline semaphore), runs a
batch's completion actions, or VideoOut completes a flip. A thread
returns early at most 8 times in a run of back-to-back sleeps; the run
restarts when the thread submits AGC work or did not sleep for the
requested time. APS5_NO_SLEEP_WAKE=1 keeps every sleep in full.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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.