An address-based build maps every readable registered range: through a
host import when one can be made, else through a heap mirror, a
host-visible copy of the range. A range mirrored while the import budget
was full kept its mirror after a later pass imported it, until the guest
freed the range. The pass then read the range through the import, and the
mirror only held host-visible memory and a second copy of the bytes.
The pass now releases the heap mirror of every range it serves through
an import. Recorded work that bound the mirror keeps it until it
completes. If the import goes away while the range stays registered, the
next pass makes a new mirror from the guest bytes. The [buffers] image
mirrors profile line counts the released mirrors.
A build served by the cached address space walked every region of the
space for its mirrors, sorted the heap mirrors, compared each heap
mirror's blocks with the write tracker under its own lock and swept the
whole mirror registry. In an offline replay with 37,000 heap mirrors
that took about 10 ms per build, every build.
The space now keeps its writable mirrors, its sorted heap mirrors and
their adjacent runs from when it is published. Each run is still
flushed and collected; a run with no block stamped since the generation
its last complete check collected skips the per-mirror compare, and that
generation is stored only once the build's blocks were copied. The
registry sweep is skipped while every live mirror's range is held by the
cached address space's lease and no mirror was added, replaced or
dropped since.
LoadLibraryEx with an unresolved import shows the loader's "Entry Point
Not Found" message box unless SEM_FAILCRITICALERRORS is set, so
patched_libraries waited on the dialog until ctest's timeout instead of
printing the failing libraries. Set it at start, as GuestHeapRounding does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The POSIX form of sceKernelMlock: 0 on success, -1 with errno (EINVAL,
ENOMEM, EPERM, EAGAIN) where the sce form returns the kernel error, as
munmap and mprotect do beside it.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Functions referenced only through a REX.W RIP-relative LEA (constructors,
callbacks, thread entries called indirectly) were not reached by code
analysis, so their guest TLS accesses stayed unpatched on Windows.
Add LEA targets inside executable code as roots, both in the instruction
walk and when following the control-flow graph of unreached code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
pr_overlap.py took every added line with APS5_VABI before a name and a
parenthesis as an export, so a declaration such as
extern "C" int* APS5_VABI __error_nid_postfix(); made each pull request
that adds it "implement the same function" as all the others. A line
that ends in a semicolon is a declaration and is skipped; every
definition's first line in the tree ends in a brace, a parenthesis or
a comma.
Registers pr_overlap_exports.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Releasing direct memory left the virtual ranges that mapped it registered,
so sceKernelVirtualQuery reported them as flexible memory at offset 0 and
titles walking a range with FIND_NEXT released physical page 0.
Collect every view of the released pages before freeing them and unmap
them through UnmapRegistered, as shadPS4's MemoryManager::Free does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
nid_patcher turns the _nid_postfix names of a library into NIDs for ELF
and PE. A library built on macOS is a Mach-O dylib, whose exports are in
a dyld export trie and whose imports are in the chained fixups, so it
gets its own patcher.
- The export trie is read, every exported C name (without Mach-O's
leading underscore) is resolved to its NID, and the trie is rebuilt the
way ld64 lays it out: the offsets are uleb128, so the layout is repeated
until it is stable. A trie that no longer fits is appended to
__LINKEDIT, which has to end the file, and a signed image is refused.
- Weak definitions that are not marked _nid_postfix or _nid_no_patch keep
their names: dyld coalesces those across images by name.
- In the chained imports, names bound to a .prx dylib that are guest
facing (a _nid_postfix name or an sce prefix) are patched in place; a
name that would grow, or that shares storage with another import, is an
error. Images that use bind opcodes instead of chained fixups are
refused.
- ExportExclusions reads the exports of a reference Mach-O, so the
exclusion list works for libraries built on macOS as well.
The GUI missing-symbol and missing-module fixtures were started with
ShellExecuteExW. A process the shell starts gets the default error mode,
so the status the entry stub raises reaches Windows Error Reporting as an
APPCRASH. Collecting each report took about 2 s, sometimes over 10 s,
and under a parallel ctest run it could pass the 30 s wait, failing
windows_import_modules with "ShellExecuteExW child did not exit" while
WerFault still held the fixture's files.
The fixtures now start through CreateProcessW with STARTF_USESTDHANDLES
and no standard handles, so the stub's diagnostic still has nowhere to
write, and with the test's error mode, which keeps the report away.
The offscreen SDL driver cannot create a Vulkan window on Windows since
the VideoOut window uses SDL_WINDOW_VULKAN, so the tests that open a port
now exit 77 (skipped) like the other display tests, unless
ANYPS5_REQUIRE_DISPLAY is set. The tests also write app0/sce_sys/param.json,
which AppMetadata now requires.
presentLoop called AgcDriverShutdown outside its try block, so a stored
worker failure rethrown there terminated the process. Store it in
flipQueue->failure so VideoOutDriver::Shutdown rethrows it after the join.
Register the lifetime, decode and controls modes of video_out_flip_tests
in ctest.
MinGW windres passes -I paths to cc1 with backslash-escaped spaces and
parentheses, so compiling 3rdparty/freetype/src/base/ftver.rc fails when
the checkout or build path contains them. FreeType is linked as a static
library and the resource object is never pulled into an output, so it is
removed from the target's sources on Windows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The built Windows libraries import libgcc_s_seh-1.dll, libstdc++-6.dll and
libwinpthread-1.dll, and the entry stub loads them without searching PATH.
The release archives ship the DLLs in libs/, but the runtime layout only
told self-builders to copy the .prx files, which ends in error 126.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The rect-list control and evaluation shaders put their fault buffer one
past the highest vertex or fragment binding. Bindings are grouped per
stage (RuntimeAbi::BindingNumber), so that number could fall inside
another stage's group and collide with one of its bindings, and the
draw failed with a duplicate binding. The fault buffer now uses the
TessellationControl FaultBuffer binding.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at its first rect-list draw with "duplicate shader binding".
v_not_b32_sdwa takes the same dst_sel and dst_unused operands as the
other 32-bit VOP1 SDWA forms (RDNA2 ISA 6.3.1 "SDWA"; LLVM
VOP1Instructions.td gives V_NOT_B32 a VOP1_SDWA encoding with dst_sel
and dst_unused). The rule now allows byte and word destinations with
every unused mode, which the existing SDWA writeback implements; the
execution test checks WORD_0 with UNUSED_PRESERVE.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at a v_not_b32_sdwa with dst_sel:WORD_0 dst_unused:UNUSED_PRESERVE.
GFX10 image format 2 (8_SNORM, Mesa gfx10_format_table) resolves to
VK_FORMAT_R8_SNORM and is marked sampleable in the recompiler's format
table.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at a compute shader sampling an 8_SNORM image.
Debug markers are NOP payloads the command processor skips, so a pop
with no marker pushed (a title that pops a marker it pushed in an
earlier command buffer) has no effect on the GPU; it no longer throws.
Uncharted: Legacy of Thieves Collection (PPSA05684) pops one marker more than it pushes and stopped with "marker stack underflow".
A viewport whose X or Y scale is zero maps every vertex onto one line,
so triangle, strip, fan and rect-list draws through it cover no pixel.
Such draws, which titles use to skip work without rebuilding a command
buffer, get a valid 1x1 Vulkan viewport and an empty scissor instead of
throwing. Points, lines and other negative or non-finite scales still
throw.
Uncharted: Legacy of Thieves Collection (PPSA05684) stopped at such a draw with "unsupported viewport transform: scale=(0, -0, 1)".
libSceVdecsw is the software H.264 decoder interface PPSA05684 uses for
its movies. Decoder creation, compute queues, memory queries, decode
input and output, AVC picture info, reset and finalize are implemented
with the layouts of shadPS4's vdecsw.h, checked against the title's
movie decoder, and decode through libSceVideodec2. The H.264 stream the
Videodec2 test uses moves to a shared fixture, and a guest test decodes
it through Vdecsw.
Uncharted: Legacy of Thieves Collection (PPSA05684) plays its movies through libSceVdecsw and stopped at sceVdecswQueryComputeMemoryInfo.
The header-block PT_LOAD has the highest address but was the first
loadable entry in the table. Linux before 5.18 sizes the ET_DYN mapping
from the first and last PT_LOAD entries in table order, so the output
failed to exec with SIGSEGV. Write PT_PHDR and PT_INTERP first, sort the
kept PT_LOAD entries by address, and place the extra and header blocks
after them.
libstdc++ already defines NOMINMAX on MinGW, so the unguarded define
warned on every Windows build.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The geometry pass adds the mesh capability and extension to the device
target but kept its SPIR-V version. On a device without mesh shaders
that is 1.3, and the mesh module was rejected as exceeding the target.
VulkanDevice targets 1.4 whenever mesh shading is enabled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The FMASK identity read selected a uvec4 with a scalar bool condition,
which needs SPIR-V 1.4. Devices without mesh shaders target 1.3, so
every image test that reaches the path failed validation with
ANYPS5_ENABLE_SPIRV_TOOLS (#2138), as in #574.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Linux job reports "100% tests passed out of 371" with 20 agc_driver execution tests skipped
(llvmpipe reports subgroup size 8, so the tests that need a wider wave exit 77), and the Windows
job reports "100% tests passed, 0 tests failed out of 368" with 143 skipped - the runner has no
Vulkan driver and ANYPS5_REQUIRE_VULKAN is not set there, so 143 of the 147 GPU execution tests
silently do not run. ctest counts a skip as a pass, so neither job notices when the set grows.
Add tools/check_skips.py. It reads the file ctest writes with --output-junit and fails when a test
skips for a reason that is not expected:
- linux: a skip is expected only when the test's own output reports that the device's subgroups
are too narrow for it, so adding a wave32 test does not need the rule edited;
- windows: any agc_driver test, since the runner has no driver for them, while a skip of anything
else fails.
Both jobs now pass --output-junit and run the check after ctest. The JUnit file needs no
redirection, so the console output stays visible and no pipe can hide a failing ctest.
Verified here with the CI command and the CI device (lavapipe, subgroup 8): the real run reports
20 skipped, all expected; a 21st wave32 test skipping for the same reason passes; a skip whose
reason is something else exits 1; a file with 143 agc_driver skips passes with --job windows and
one non-agc skip in it exits 1.
On a host without IPv6, socket(AF_INET6) fails with EAFNOSUPPORT, so
guest_sce_net aborted at its first IPv6 socket and never ran the TCP,
UDP, epoll, sendmsg/recvmsg, resolver and NetCtl checks after it.
When sceNetSocket reports guest EAFNOSUPPORT (47) for the IPv6 socket,
skip those socket checks, run the rest of the test, and exit with 77,
which ctest reports as skipped. Hosts with IPv6 run every check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
llvmpipe builds the high dword of the shader clock with a left shift by
32 (emit_clock in Mesa's lp_bld_nir_soa.c), so it is always 0 and
s_memtime and s_memrealtime wrap every 2^32 ns. A pass of
agc_driver_shader_clock whose two reads straddle a wrap failed with
"memtime: lane N went back from 4294921902 to 3823"; dispatching right
before the boundary fails every lane of that pass.
On llvmpipe compare the low dwords modulo 2^32, which stays correct if
Mesa fixes the shift. Other devices keep the 64-bit comparison.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
When a queue worker failed, it published the failure and woke the idle
waiters before failing the flips of the submission it was executing.
A thread returning from AgcDriverWaitIdle with that failure could still
see one of those flips neither completed nor failed, which made
agc_driver_flip fail with "failed flip was completed successfully" in
2 of 240 runs under four-way parallel load on lavapipe.
Fail the executing submission's flips first, as ReportFailure already
does for the pending submissions it drops before it notifies.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA