Commit Graph
14017 Commits
Author SHA1 Message Date
Ondra97 bf861be946 test(agc): wait for pending result on the tested surface 2026-10-16 10:50:01 +02:00
Yunus Shaikh a48aa76934 perf(agc): step a remembered evicting refusal down without a new scan
An evicting request whose block is refused but leaves room for its own
range steps down to a smaller block, and only the last block it tried
was remembered. Once an address-based build had stepped down inside a
block, every later evicting request for the rest of that block scanned
every import at the block's level before it reached the memory below.

A refusal is now remembered before the request steps down, with the
room it left. A remembered refusal answers with the same decision the
scan would make: while the memory is current only a lower budget can
change that room, so the request steps down to the same smaller block
when its range fits in the room, and is refused otherwise.
2026-10-16 08:34:53 +00:00
Romans Pokrovskis b15060bd9d test(agc): wait for the second-store image itself to be pending 2026-10-16 11:31:37 +03:00
Yunus Shaikh cceba477f6 perf(agc): remember an evicting join's budget refusal
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.
2026-10-16 08:18:34 +00:00
Yunus Shaikh cc42ab0198 perf(agc): answer any range of a refused block from its evicting refusal
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.
2026-10-16 08:17:09 +00:00
Ondra97 915e9f2d5d test(libkernel): avoid spinning in rwlock retirement regression 2026-10-16 10:09:13 +02:00
Yunus Shaikh a3fefab585 Merge main into perf/agc-remember-evicting-refusals 2026-10-16 00:56:15 -07:00
Mehran 92e5b971c6 perf(shader): keep memoized results in a set-associative table 2026-10-16 10:55:47 +03:00
Yunus Shaikh 9a48a9605c fix(libkernel): map a protected main stack up to its top on Linux
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.
2026-10-16 07:28:23 +00:00
Yunus Shaikh 8d2053a21e fix(libkernel): keep a host thread's stack range once queried on Linux
glibc reports the main thread's stack as reaching down to the end of
the mapping below it. The pages a protect maps under the main stack
become that mapping, so after the first protect sceKernelIsStack
reported a stack starting above them, and a second protect of the same
pages, such as restoring them to read/write, failed as outside any
stack. Each thread now queries its host stack once, which also stops
every protect on the main thread from reading /proc/self/maps.
2026-10-16 07:27:37 +00:00
Yunus Shaikh d5c12169fc fix(libkernel): map a host stack's unmapped pages in runs on Linux
MapUnmappedPages probed every 4 KiB page of the protected range with
msync before mapping it. Under an unlimited stack limit the main
thread's reported stack reaches down to the heap, tens of TiB, so a
guest protect over the whole stack ran for hours. Mapped and unmapped
runs are now found by halving the remaining range, and the unmapped
ones are mapped without reserving swap, as the stack itself grows.
2026-10-16 07:24:41 +00:00
Yunus Shaikh 219da69c32 perf(agc): keep an evicting refusal remembered while the budget shrinks
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.
2026-10-16 07:18:33 +00:00
Tripchipp bd9d0c3d7d fix(tools): keep debt entries without a core link in the index 2026-10-16 09:09:21 +02:00
Yunus Shaikh a7928027fc fix(agc): keep a host heap report with no room from turning off refusal memory
After a vkAllocateMemory failure the default import budget follows the
host heap's VK_EXT_memory_budget report. A report that leaves imports
no room made that budget 0, and the refusal memory reads a budget of 0
as none: an address-based build or an import when a range is mapped then
skipped its remembered refusals and ran the whole import path again for
every refused range, each time. The report's other usage grows with the
copies and mirrors that stand in for refused imports, so the budget can
reach 0 by itself once refusals pile up.

The budget the report sets is now at least 1 byte. Nothing more is
imported than at 0, and refusals are remembered again.
2026-10-16 07:05:19 +00:00
Johan David c8f7fc06e0 fix(agc): compare the depth write base only when the depth plane is written
decodeDepth required DB_Z_WRITE_BASE to equal DB_Z_READ_BASE whenever
the depth plane was not read-only, also for draws that never write it.
PPSA31327 01.015.000 stops there on a stencil-only draw:
DB_DEPTH_CONTROL 0x00000701 (Z_ENABLE and Z_WRITE_ENABLE clear),
DB_RENDER_CONTROL 0, DB_DEPTH_VIEW 0, Z read base 0xed11340000 and
Z write base 0, with equal stencil read and write bases.

The bases are now compared only when the depth plane can be written: a
depth clear, a draw with both Z_ENABLE and Z_WRITE_ENABLE (the condition
that sets depthWrite), or an expand pass with DEPTH_COMPRESS_DISABLE or
STENCIL_COMPRESS_DISABLE in DB_RENDER_CONTROL, which still throws. The
surface keeps the read base. The stencil check is unchanged.

DepthStencilTests accepts a stencil-only draw, Z_WRITE_ENABLE without
Z_ENABLE and a depth test without writes at another write base, and
still rejects a written plane, a depth clear and the compress-disable
bits there.
2026-10-16 02:03:06 -05:00
simonbronkandClaude Opus 5.5 2c1b825ae5 test(agc): expect the color sample-count refusal for a one-sample target under a 2x scan without MSAA_ENABLE
#5882 lets a multisampled scan without MSAA_ENABLE through the earlier
check, so the draw is now refused where its one-sample color target meets
the rasterizer's sample count.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 09:00:29 +02:00
fogydes 5fc766b161 feat(libSceAgcDriver): implement driver flip packet size accessor and export interface 2026-10-16 12:29:19 +05:30
simonbronkandClaude Opus 5.5 b766ce4fb5 feat(agc): keep sample, sample-index and per-sample shading on the sample-zero path
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]>
2026-10-16 08:58:33 +02:00
simonbronkandClaude Opus 5.5 9bf98992ff feat(agc): rasterize a multisampled draw into one-sample targets at sample 0's locations
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]>
2026-10-16 08:58:33 +02:00
simonbronk 13b6650cf9 fix(relinker): accept omitted empty array sizes in strict filtering 2026-10-16 08:58:06 +02:00
simonbronk 10321363fc merge: integrate prerequisite PR #6545 with current main 2026-10-16 08:57:13 +02:00
Ondra97 fcf65455f5 fix(savedata): prevent duplicate mounts of one backing directory 2026-10-16 08:53:05 +02:00
simonbronk 46cafca20f fix(relinker): reject Unicode output aliases on Windows 2026-10-16 08:50:15 +02:00
Karim Ibrahim ceec3da0a0 fix(libc): share the constraint handler across libc source files
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.
2026-10-16 02:46:25 -04:00
Yunus Shaikh 4ee79e522e perf(agc): remember an evicting import request's budget refusal
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.
2026-10-16 06:45:27 +00:00
Yunus Shaikh 815b8add6f test(agc): check that a settle read ends the driver's marking of an import
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.
2026-10-15 23:43:07 -07:00
Yunus Shaikh 3d2d8e5305 test(agc): check that a sample mask the order moves throws under early tests with memory writes
The sample order checks validate the 2x checkerboard with a uniform
mask of sample 0, early Z, a depth test and a pixel program that writes
memory, on a device that reorders the checkerboard. The order moves the
mask into the pixel shader, after the early tests, so the draw throws.
The same draw validates when the pixel program writes no memory, and on
a device with a 2x2 grid, where the mask stays static.
2026-10-16 06:23:50 +00:00
Yunus Shaikh ee406d6179 docs(agc): give the 3070's per-count sample location grids and say why a moved sample mask throws
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.
2026-10-16 06:23:50 +00:00
Yunus Shaikh 7476390517 perf(agc): free an import retired during its settle read after the read
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.
2026-10-15 23:20:30 -07:00
Yunus Shaikh c3f947ef47 docs(agc): say the 3070 draws 2x patterns on its grid and name the depth transitions and the sample mask throw
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.
2026-10-16 06:16:00 +00:00
Yunus Shaikh 212c35f08a fix(agc): name the depth target's sample locations after a pass a draw recipe opens
A pass that draws multisampled depth at custom sample locations ends
with a transition to TRANSFER_SRC_OPTIMAL and back that names them, so
the depth is not read back at other locations. The pass's end runs the
work of the draw that opened it, and Draw sets the locations for it, but
DrawWithRecipe built its RecordedDraw without them. Draw recipes are
refused for multisampled color and for reordering pipelines, not for
depth-only multisampled draws, so a recipe hit that opened a pass, on
the native grid or with a uniform custom pattern, left its depth without
the transition however many draws followed it.

Draw and DrawWithRecipe now take the locations from passDepthLocations,
on the recipe's pipeline for a replay. The recipe keeps its pipeline,
whose key holds the custom locations, and Draw's caller passes the same
depth target, so the helper sees what Draw saw for the recorded draw.

The sample order tests' --recipe-depth-locations records a depth-only
2x draw with a uniform custom pattern and a recipe, reads every sample,
clears the depth, replays the draw through DrawWithRecipe as the first
draw of a new pass and reads again. It requires the replay to leave the
recorded depth bit for bit, and skips without custom sample locations at
2 samples, or where even the recorded draw reads back away from the
guest's locations.
2026-10-16 06:16:00 +00:00
Ondra97 f2b4a71aed fix(libkernel): finish rwlock notifications before releasing state 2026-10-16 07:52:22 +02:00
Karim Ibrahim edf4efe360 feat(libc): implement qsort_s
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.
2026-10-16 01:31:40 -04:00
Karim Ibrahim 18ecee64d2 fix(relinker): leave weak imports of bundled modules unbound on Windows
Domain::GuestImport had no weak flag, so WindowsPePatcher handed every
import of a bundled module to the entry stub as a strong one, and a
weak import that no library exports stopped the title with
"unresolved ELF import". The executable's own weak imports already
bind to null there. libOSMesa.so.8 of the Quake homebrew has ten weak
imports that nothing provides, such as ZSTD_trace_compress_begin and
the SDK's __dlopen.

The guest relocation writer now records the ELF weak binding, and the
stub binds such an import to null when no library exports it.
2026-10-16 01:17:32 -04:00
xXPBSXxandClaude Sonnet 5.5 2aeb06f369 test(agc): cover the color to depth format mapping of depth compare copies
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 07:10:31 +02:00
bappisinsane f4c4498325 test(tools): cover prepare_release_notes truncation and file output 2026-10-16 11:05:24 +06:00
xXPBSXxandClaude Sonnet 5.5 018edc8cbd test(libSceSystemService): cover status, safe area, notice screen and event defaults
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 07:04:18 +02:00
xXPBSXxandClaude Sonnet 5.5 b5d0d7bfbb test(libkernel): cover getgid and issetugid
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 07:02:18 +02:00
xXPBSXxandClaude Sonnet 5.5 edb74cb58b test(agc): cover the DWORD rounding of guest buffer view ranges
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 07:01:12 +02:00
xXPBSXxandClaude Sonnet 5.5 72471f5c68 test(libSceNetCtl): cover GetStateV6, GetResult and GetNatInfo in guest_net_ctl
sceNetCtlGetStateV6, sceNetCtlGetResult and sceNetCtlGetNatInfo are
exported and called by titles but no test calls them. The checks pin what
the code does today: a null output is refused with the invalid-address
error, GetStateV6 reports disconnected or IP obtained, GetResult writes
0, and GetNatInfo reports not connected.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 06:57:25 +02:00
TamaleroandClaude Opus 5.5 ae5046eeb2 feat(libkernel): add sceCoredumpConfigDumpMode as a throwing stub
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]>
2026-10-15 21:57:24 -07:00
xXPBSXxandClaude Sonnet 5.5 f2b580bc32 test(libSceRtc): cover microseconds, minutes, hours and weeks tick addition
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 06:57:12 +02:00
al3w0f205 5353773f1e test(libSceCommonDialog): add guest common dialog test coverage 2026-10-15 23:53:53 -05:00
David 0e6ad36fe4 docs(debt): say how mktime converts a time before 1900 on macOS 2026-10-16 08:45:29 +04:00
David 20404ab3d2 fix(libc): convert mktime before 1900 a 400-year cycle later on macOS
macOS's mktime returns -1 for every year before 1900, so a guest mktime of
such a time failed there although localtime converts it. Windows already
moves a time outside its CRT's range a whole number of 400-year Gregorian
cycles, where weekdays and leap years repeat, converts it there and moves
the result back. macOS now does the same below 1 January 1900; each host
keeps its own range, and Windows's is unchanged.
2026-10-16 08:45:29 +04:00
David 80a9ef9e3b docs(debt): say where NetCtl finds the default route and why GetIfStat throws on macOS 2026-10-16 08:42:35 +04:00
David 6d4cde8e40 test(libSceNetCtl): check which macOS routing messages give a default route
A routing table built by hand with an unscoped default route, a host
route, a host route to 0.0.0.0, a rejecting default route, an
interface-scoped default route, one that is down, one on an interface
that does not exist and a truncated last message must give the unscoped
route first and the scoped one after it, and must count every match
while writing only as many as fit.
2026-10-16 08:42:35 +04:00
David 8af08029b8 fix(libSceNetCtl): read the default route from the routing table on macOS
sceNetCtlGetInfo found the host's default route in /proc/net/route, which
macOS does not have, so every IPv4 query threw "the host routing table is
unavailable" there. On macOS it now reads the IPv4 routes that have a
gateway with sysctl(NET_RT_FLAGS, RTF_GATEWAY) and keeps those to
0.0.0.0 that are up, neither rejecting nor host routes, on an interface
that still exists. An interface-scoped default route ranks after the
unscoped one, the route macOS sends traffic by. The parsing is exported
as NetCtlDefaultRoutes_nid_no_patch so it can be tested on a routing
table built by hand.
2026-10-16 08:42:35 +04:00
SandstromPLandClaude Opus 5.5 051dac94ee fix(relinker): find the PLT before the code in PS4 modules
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]>
2026-10-16 09:43:51 +05:30
SandstromPLandClaude Opus 5.5 ab9d21afcf fix(relinker): run a guest DT_INIT of 0 as the module initializer
The guest reader stored DT_INIT as a plain address, and every user of
it read 0 as "no initializer". The libc.prx and libSceFios2.prx that
Until Dawn (CUSA02636) bundles have DT_INIT present with value 0, the
start of their text segment. So libc.prx, which also has a preinit
array, failed with "Guest PREINIT_ARRAY has no module initializer",
and libSceFios2.prx's initializer never ran.

GuestImage now records whether the tag is present (HasInit). The
reader, the Linux, macOS and Windows writers and the code analysis use
it instead of Init != 0, so an initializer at 0 is mapped, started and
analysed like any other.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 09:42:46 +05:30