Compare commits

..

2455 Commits

Author SHA1 Message Date
Dean f40b6930ff
Merge pull request #4914 from MarcZX100/codex/carry-link-follow-symlink-4026
fix(libkernel_sys): follow symbolic links in link
2026-10-14 17:05:05 +13:00
Dean 00f4b3351b
Merge pull request #3972 from semirkabir/feat/videoout-output-status-vrr
feat(videoout): implement sceVideoOutGetOutputStatusWithVrr
2026-10-14 16:44:42 +13:00
Dean a31043146e
Merge pull request #5367 from MarcZX100/marc/sysctl-leaf-enotdir-5356
fix(libkernel): return ENOTDIR for trailing sysctl leaf components
2026-10-14 16:41:21 +13:00
Dean a1ca1e7bad
Merge pull request #5137 from MarcZX100/codex/request-v21-compat
fix(shader): accept both version 21 request layouts
2026-10-14 16:41:11 +13:00
Dean 21def5c6f8
Merge pull request #5597 from JJenkx/fix/thread-stack-retirement-order
fix(libkernel): retire stack metadata before releasing mappings
2026-10-14 16:41:01 +13:00
ThatsLeo 9828710a38 feat(videoout): implement sceVideoOutGetOutputStatusWithVrr
Writes the 80-byte status described in #2746: the sceVideoOutGetOutputStatus
fields followed by zeros, with the documented codes for a null status and an
unknown handle.

(cherry picked from commit e893efad2d0a7b90243d07bf6669cc9b5f540432)
2026-10-14 16:37:40 +13:00
Dean a14ae8e232
Merge pull request #5186 from p3n1cillin/claude/carry-fioclex
fix(libkernel): handle FIOCLEX and FIONCLEX in ioctl
2026-10-14 16:31:08 +13:00
Dean a5f493f90d
Merge pull request #5152 from dev-mca/test/convert-keycode
test(libSceConvertKeycode): cover sceConvertKeycodeGetImeKeyboardType
2026-10-14 16:06:42 +13:00
Dean f5cda839ec
Merge pull request #4717 from Ondra97/codex/fix-relro-unused-filter
fix(relinker): map standalone RELRO in unused import filtering
2026-10-14 16:05:01 +13:00
Dean e1544df5f2
Merge pull request #4772 from TideOPL/perf/spirv-specialization
perf(shader): index specialization tables by SPIR-V id
2026-10-14 16:04:50 +13:00
Dean f265cd5593
Merge pull request #5234 from AnthonyClemens/fix/agc-integer-tessellation-partitioning
fix(agc): accept integer tessellation partitioning
2026-10-14 15:55:50 +13:00
Dean 970316ae04
Merge pull request #4704 from AlxcBTW/feat/input-mouse-look
feat(input): turn mouse speed into a smooth right-stick tilt
2026-10-14 15:55:08 +13:00
Dean 486420f91a
Merge pull request #4950 from MarcZX100/codex/pr-overlap-compatible-merge-base
fix(tools): use the shared merge base for overlap checks
2026-10-14 15:54:50 +13:00
Dean 60ad9ad47c
Merge pull request #4951 from Ondra97/codex/fix-shared-library-import-audit
fix(tools): audit stubs from shared library sources
2026-10-14 15:54:35 +13:00
Dean fcb30fd977
Merge pull request #5219 from pushkarreddyy/feat/agc-dcb-event-write-address
feat(libSceAgc): ignore address argument on non-sample-dump events in sceAgcDcbEventWrite
2026-10-14 15:54:22 +13:00
Dean a8cb091eba
Merge pull request #4955 from Ondra97/codex/fix-high-refresh-skip-policy
fix(tools): accept skips on displays without 120 Hz
2026-10-14 15:54:20 +13:00
Dean 39bf2b9e9d
Merge pull request #4986 from Ondra97/codex/fix-conventions-raw-literals
fix(conventions): ignore C++ raw literal contents
2026-10-14 15:54:04 +13:00
Dean 0fb2f6c89a
Merge pull request #4988 from babyhuey/feat/scenet-so-error
fix(libSceNet): report SO_ERROR from sceNetGetsockopt
2026-10-14 15:53:49 +13:00
Dean 3e4fe4ce31
Merge pull request #5596 from tibzejoker/port/pr-637-main
fix(libc): track writes to singly mapped direct memory without faults on Windows
2026-10-14 15:53:44 +13:00
Dean 0a56aaaf47
Merge pull request #5284 from MarcZX100/codex/nested-exit-5277
fix(libc): finish exit handlers after nested exit
2026-10-14 15:50:18 +13:00
Dean d1b3aa1372
Merge pull request #5195 from SandstromPL/fix/relinker-sce-code-analysis-extent
fix(relinker): keep code analysis inside the code extent of PS4 executables
2026-10-14 15:49:02 +13:00
Dean a7836b1495
Merge pull request #5094 from bubblegg/fix/savedata-save-size
fix(savedata): report a save's size as the mount that created it gave
2026-10-14 15:47:32 +13:00
Dean aa1927872f
Merge pull request #5197 from AlexOfTheForest/test/relinker-cli-module-and-target-options
test(cli): cover module-dir, sce-module-path and target option errors
2026-10-14 15:46:50 +13:00
Dean 094a34c1ab
Merge pull request #5158 from AaravMehta-07/codex/systemservice-contained-exec
fix(libSceSystemService): contain converted executable paths
2026-10-14 15:45:40 +13:00
Dean 94f96c8e2c
Merge pull request #5255 from Nesyth/fix/libkernel-apr-read-past-eof-v2
fix(libkernel): zero-fill APR reads that reach the end of the file
2026-10-14 15:44:14 +13:00
Dean a21d73733c
Merge pull request #5133 from SandstromPL/fix/savedata-search-other-title
fix(savedata): return no saves for a search with another title's ID
2026-10-14 15:41:58 +13:00
Dean a1e1b4d161
Merge pull request #5262 from NaitikJoshiPro/pr/relinker-executable-tls
feat(relinker): executable TLS relocations (TPOFF64, DTPOFF64, DTPMOD64)
2026-10-14 15:41:31 +13:00
Dean 0defec4386
Merge pull request #5199 from Zenodeon/conversion-diagnostics
fix(relinker): identify failed inputs and invalid dynamic values
2026-10-14 15:39:35 +13:00
Dean c693bd7566
Merge pull request #5190 from NumberGuan/codex/check-lsda-landing-addresses
fix(relinker): reject overflowing LSDA landing pad addresses
2026-10-14 15:39:00 +13:00
Dean 75ceccd5db
Merge pull request #4615 from MiniRML/fix/nid-elf-table-layout
fix(nid): validate ELF table layouts before patching
2026-10-14 15:38:11 +13:00
Dean 622fb542d2
Merge pull request #4764 from nikowolf123/fix/conventions-test-fixtures
fix(conventions): skip library test fixtures in the export checks
2026-10-14 15:37:56 +13:00
Dean 9b6555bd47
Merge pull request #4796 from xXPBSXx/feat/socket-options-sol-socket
fix(libkernel): report SO_ERROR and the other SOL_SOCKET options like FreeBSD
2026-10-14 15:37:41 +13:00
Dean cdea950b7e
Merge pull request #4825 from StefanBuc/prepare/elf-import-audit-20261012
fix(tools): validate empty ELF dynamic symbol table bounds
2026-10-14 15:37:26 +13:00
Dean 1e277eef6e
Merge pull request #4930 from MarcZX100/fix/videoout-frame-dump-linear
fix(videoout): dump linear display buffers
2026-10-14 15:37:10 +13:00
Dean 91e5f60880
Merge pull request #4822 from MarcZX100/fix/fopen-native-open-errors-4814
fix(libc): return native fopen errors
2026-10-14 15:36:11 +13:00
Dean 6d032559c3
Merge pull request #4789 from PrintOrb/fix/audioout-shared-sdl-init
fix(audioout): serialize SDL audio initialization for Dark Forces
2026-10-14 15:35:56 +13:00
Dean 2ba3147ac8
Merge pull request #4844 from johnmaia/debug/skip-shader
feat(agc): skip the draws and dispatches of one shader with APS5_SKIP_SHADER
2026-10-14 15:32:04 +13:00
Dean 803cbf2f81
Merge pull request #4715 from Lorkydey/fix/agc-inactive-vertex-point-size
fix(agc): accept inactive vertex point size on non-point draws
2026-10-14 15:31:48 +13:00
Dean c5f839f651
Merge pull request #4702 from tv7/feat/gs-fast-launch
feat(agc): decode GS_FAST_LAUNCH geometry with one point per subgroup
2026-10-14 15:31:33 +13:00
Dean 0df6e295c5
Merge pull request #290 from MichonGoddijn231849/split/host-import-windows
feat(agc): host import windows, least-recent eviction under the budget
2026-10-14 15:27:19 +13:00
Dean 71c71f50b7
Merge pull request #5029 from fdfawef324/perf/agc-partially-resident-snapshot-hits
perf(agc): trust write stamps for snapshots of partially committed textures
2026-10-14 15:21:38 +13:00
pushkarreddyy fbf619ef0d
feat(libSceAgc): ignore address argument on non-sample-dump events in sceAgcDcbEventWrite 2026-10-14 07:37:02 +05:30
Dean 016db923af
Merge pull request #5184 from p3n1cillin/claude/sysctl-vm-overcommit
feat(libkernel): report vm.overcommit through sysctlbyname
2026-10-14 14:52:25 +13:00
AlxcBTW c29e06c693 fix(agc): a joined host import may replace imports the build in flight already holds
A join runs before any region confirms an import and a retired import lives
through the retire-hold, so refusing it for held overlaps kept the joined run
copied for the rest of the build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:51:48 +13:00
Michon c871b5893d test(agc): host import windows, whole-range builds and budget refusals
agc_driver_graphics (BdaResources.cpp), each failing without its change:
- a view in the last window of a range whose first window was imported
  binds through an import of the whole range;
- an address-based build over the budget leaves the range's window in
  place (3 builds, same import);
- two uses across neighbouring cell edges share one import over 10
  rounds;
- a range mapped at the budget is refused at map time and retires
  nothing.
agc_driver_host_import_windows: windows follow their range, and
ClearHostImports drops them.
2026-10-14 14:51:48 +13:00
Michon e9249a0a47 feat(agc): host import windows, least-recent eviction under the budget
A registered range larger than 64 MiB is imported one 64 MiB window at a
time for buffer and fill accesses instead of whole, and the live imports
stay under a budget: APS5_HOST_IMPORT_MIB, else the larger of 6 GiB and
half the physical memory, at most the largest host heap less 2 GiB
(imports and type-3 mirrors share it; #167's rule). An import past the budget
retires the least recently used imports not used in the last 256
lookups.

- An access across window edges imports the cells it touches, grown to
  the union of the range's imports it overlaps, so uses across
  neighbouring edges share one import instead of replacing each other.
- An address-based build (AcquireRegistered) imports the range whole; a
  window over its first byte does not serve it.
- Every refusal (alignment, a failed run, a held import, the budget with
  what may be evicted) is decided before anything is retired.
- An import made when a range is mapped never evicts: the map observer
  holds no GPU lock.

The window size and the budget have setters for the tests.
2026-10-14 14:51:48 +13:00
Dean a98d42d08c
Merge pull request #4799 from ArjenS-LA/fix/trace-summary-selection
fix(tools): match explicit trace summaries to their capture
2026-10-14 14:51:27 +13:00
Dean 52d134c028
Merge pull request #4965 from pushkarreddyy/feat/agc-cmask-skip-higher-mip
feat(libSceAgcDriver): skip CMASK decode on single-sample views with viewMip > 0
2026-10-14 14:46:19 +13:00
Dean eacadec269
Merge pull request #4861 from Tamalero/fix/agc-metadata-pass-pass-through-depth
fix(agc): accept a CB metadata pass whose depth test passes every fragment without writing
2026-10-14 14:46:14 +13:00
Dean 959d39fb2b
Merge pull request #4801 from ArjenS-LA/fix/guest-posix-runpath
fix(relinker): validate Linux guest runpaths with target syntax
2026-10-14 14:45:57 +13:00
Dean 4586fdbc3b
Merge pull request #4469 from arcwiser/feat/libc-signal-list
feat(libSceLibcInternal): export sys_siglist and sys_nsig
2026-10-14 14:45:41 +13:00
p3nicillin dad29cac43 feat(libkernel): report vm.overcommit through sysctlbyname
The mimalloc in PS5MC (PPSA99420) reads vm.overcommit at start-up and
stopped on the unsupported name. It reports FreeBSD's default, 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:44:48 +13:00
Dean dff25dd604
Merge pull request #5098 from p3n1cillin/claude/sysctl-hw-realmem
feat(libkernel): report hw.realmem through sysctl
2026-10-14 14:44:32 +13:00
Dean 5a81f6085b
Merge pull request #4765 from nikowolf123/fix/conventions-template-subsections
fix(conventions): keep #### subsections inside the pull request template sections
2026-10-14 14:41:00 +13:00
Dean 1633843382
Merge pull request #4998 from ideabsassistenza/fix/debt-resolve-leading-entries
fix(debt): preserve leading entries during conflict resolution
2026-10-14 14:39:06 +13:00
Dean e5a272d7a6
Merge pull request #5359 from thuanquang/fix/output-path-directory
fix(relinker): reject a directory as the executable output path
2026-10-14 14:36:09 +13:00
Dean 3881dac903
Merge pull request #5148 from AaravMehta-07/codex/reject-relocatable-input
fix(relinker): reject relocatable ELF conversion inputs
2026-10-14 14:35:53 +13:00
Dean 54c2e14dc0
Merge pull request #5026 from AaravMehta-07/codex/reject-invalid-load-segments
fix(relinker): reject load file sizes beyond memory size
2026-10-14 14:35:37 +13:00
Dean e41cb23415
Merge pull request #5301 from wachi35676/fix/relinker-resource-path-spaces
fix(relinker): build the manifest resource in paths with spaces
2026-10-14 14:35:21 +13:00
Dean 52686e7959
Merge pull request #5300 from David-cmrg/fix/tests-ampr-cpp-error-pie
fix(tests): use position-independent unwind data in the C++ error fixture
2026-10-14 14:35:05 +13:00
Dean 4ddce7993d
Merge pull request #4845 from EagleBearerYS/test/guest-udp-readv-compare
test(libkernel): compare the unterminated readv buffer with memcmp
2026-10-14 14:34:41 +13:00
Dean 3e485ed53c
Merge pull request #4996 from MarcZX100/codex/mimg-sampler-vcc-4991
fix(shader): accept MIMG sampler tuples ending in VCC
2026-10-14 14:34:27 +13:00
Dean b9e0e4ed30
Merge pull request #5149 from xXPBSXx/fix/agc-texture-snorm-sint-2101010
fix(agc): sample 10_10_10_2 SNorm and SInt textures
2026-10-14 14:34:11 +13:00
Dean 97fce094c2
Merge pull request #5101 from Zheorgyan/fix/macos-import-lost-gpu-writes
fix(agc): make host imports over writable pages on macOS
2026-10-14 14:33:55 +13:00
Dean a87019d4ce
Merge pull request #4862 from Tamalero/fix/agc-blend-bypass-leaves-blending-off
fix(agc): leave blending off for a color target with CB_COLOR_INFO.BLEND_BYPASS
2026-10-14 14:33:39 +13:00
Dean 11b24ee816
Merge pull request #4863 from babyhuey/feat/libscevideoout-busy-open
fix(libSceVideoOut): return RESOURCE_BUSY when a bus is opened twice
2026-10-14 14:33:15 +13:00
Dean 8a437941bd
Merge pull request #4876 from EagleBearerYS/fix/savedata-failed-mount-keeps-result
fix(savedata): leave the mount result untouched when sceSaveDataMount3 fails
2026-10-14 14:32:59 +13:00
Dean 095a653c85
Merge pull request #5322 from lubaxe/fix/npauth-first-poll-running
fix(libSceNpAuth): report an async request as running on its first poll
2026-10-14 14:32:44 +13:00
Dean c5038c719f
Merge pull request #5170 from 0xRickx/fix/libscekeyboard-idle-count
fix(libSceKeyboard): report one key slot when no key is held
2026-10-14 14:32:30 +13:00
Dean 34355eeeac
Merge pull request #4942 from QRMOE/fix-libc-llroundl-upper-bound
fix(libc): accept the full llroundl integer range
2026-10-14 14:29:14 +13:00
Dean dd7174423a
Merge pull request #5009 from p3n1cillin/claude/libc-tzname
feat(libc): export tzname
2026-10-14 14:27:27 +13:00
Dean 0af3f150fa
Merge pull request #5153 from Kikoarl/feat/ces-sjis-cp932-profile-init
feat(ces): implement sceCesUcsProfileInitSJis1997Cp932
2026-10-14 14:27:11 +13:00
Dean 1535e2c688
Merge pull request #5414 from mariusyo07/feat/libc-mspace-aligned-alloc
feat(libc): implement sceLibcMspaceAlignedAlloc
2026-10-14 14:26:47 +13:00
Dean 1ebf8410a9
Merge pull request #5351 from MarcZX100/marc/fix-pathname-sysctl-eperm-5340
fix(libkernel): return EPERM before validating pathname sysctl MIB
2026-10-14 14:26:31 +13:00
Dean 2c444bb9fb
Merge pull request #5241 from MarcZX100/fix/sigwait-error-return-5224
fix(libkernel): return error numbers from sigwait
2026-10-14 14:26:15 +13:00
Dean 488ccece53
Merge pull request #4980 from MarcZX100/codex/poll-precise-wait-1471
fix(libkernel): use precise waits for empty poll sets
2026-10-14 14:26:00 +13:00
Dean 2e7ab67e8d
Merge pull request #4917 from MarcZX100/codex/carry-directory-read-4912
fix(libkernel): fail reads on Windows directory descriptors
2026-10-14 14:25:43 +13:00
Dean 7937c97237
Merge pull request #4923 from MarcZX100/codex/carry-ftruncate-not-file-4921
fix(libkernel): reject ftruncate on non-file Windows handles
2026-10-14 14:25:27 +13:00
Dean 8a51486bd2
Merge pull request #5393 from yixx16/fix/libkernel-vnode-windows-delete
fix(libkernel): keep the Windows vnode watch when the watched file is deleted
2026-10-14 14:25:11 +13:00
Dean d1c4618729
Merge pull request #4864 from milescoviello/carry/readv-pipe-windows-4858
fix(libkernel): fill every buffer of readv from a pipe on Windows
2026-10-14 14:24:56 +13:00
Dean c101c91bbb
Merge pull request #5272 from milescoviello/carry/siglongjmp-stack-windows-5270
fix(libkernel): switch the host stack bounds in siglongjmp on Windows
2026-10-14 14:24:40 +13:00
Dean 979a4b9bb7
Merge pull request #5464 from milescoviello/fix/kernel-stat-null-buffer
fix(libkernel): fail sceKernelStat with EFAULT for a null buffer
2026-10-14 14:24:24 +13:00
Dean c6926c9c00
Merge pull request #4958 from milescoviello/carry/flock-advisory-windows-4957
fix(libc): make flock advisory on Windows so other descriptors can read and write
2026-10-14 14:24:08 +13:00
p3nicillin fe54938490 feat(libkernel): report hw.realmem through sysctl
The dolphin_libretro core of RetroArch (PPSA99169) reads {CTL_HW,
HW_REALMEM} as the machine's memory size and stopped on the unsupported
MIB. It reports the same value as hw.physmem.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:23:55 +13:00
Dean fc1e388ed2
Merge pull request #5185 from p3n1cillin/claude/sysconf-clk-tck
feat(libkernel): report _SC_CLK_TCK through sysconf
2026-10-14 14:22:06 +13:00
arcwiser 8e0d6324af docs: group signal list debt with signal handling 2026-10-14 14:16:05 +13:00
screamm 9849a1db64 feat(libSceLibcInternal): export sys_siglist and sys_nsig
PS5CEMU-HAR (PPSA99360, ps5-payload-dev build, relinks with
--trap-sdk-syscall-stubs) imports the data objects sys_siglist
(yCdGspbNHZ8) and sys_nsig (ZD+Dp+-LsGg) from libSceLibcInternal.prx;
the import audit lists both as absent (registry entries 364 and 378,
after pthread_setaffinity_np at 276). No run has reached them yet; the
predicted stop line is

  FAIL: unresolved ELF import ZD+Dp+-LsGg (sys_nsig) from libSceLibcInternal.prx

or yCdGspbNHZ8 (sys_siglist), whichever the loader resolves first.

The table and count are FreeBSD 11.4 lib/libc/gen/siglist.c (8c639945):
32 descriptions, signal 0 to SIGUSR2 (NSIG 32), and sys_nsig is their
number, 32. ps5-payload-dev declares both as objects exported by
libSceLibcInternal and its strsignal reads them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 14:15:55 +13:00
pushkarreddyy acbc573212 feat(libSceAgcDriver): skip CMASK decode on single-sample views with viewMip > 0 2026-10-14 14:15:25 +13:00
Cesar Rodriguez 8c554cc42a fix(agc): accept a CB metadata pass whose depth test passes every fragment without writing
DecodeColorMetadataPass refuses an eliminate-fast-clear or DCC-decompress
pass when DB_DEPTH_CONTROL has any of its low four bits set and depth or
stencil planes are bound. Marvel's Guardians of the Galaxy (PPSA01748)
issues one with the Z test enabled, ZFUNC ALWAYS and no Z write
(DB_DEPTH_CONTROL 0x72) while a Z32 and an S8 target are bound. That test
passes every fragment and leaves depth unchanged, so the pass does no
depth work. Draws already treat this state as a pass-through
(depthPassThrough); the metadata pass now uses the same rule. Depth
bounds, depth writes, stencil tests and DB_RENDER_CONTROL clears or copies
are still refused, and the error names the four registers involved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 14:15:04 +13:00
ArjenS-LA 394c5a231e fix(relinker): validate Linux guest runpaths with target syntax 2026-10-14 14:14:38 +13:00
Dean ee9037d851
Merge pull request #4694 from EagleBearerYS/fix/agc-draw-snapshot-cap-at-build
perf(agc): read the draw snapshot cap when resources are built
2026-10-14 14:14:16 +13:00
Dean cd90ca50be
Merge pull request #4712 from Ondra97/codex/fix-debt-section-moves
fix(tools): preserve technical debt section moves
2026-10-14 14:13:58 +13:00
Dean b16a1a72f3
Merge pull request #4776 from babyhuey/feat/libkernel-sysctl-tsc-freq
feat(libkernel): report machdep.tsc_freq from sysctlbyname
2026-10-14 14:09:31 +13:00
Dean 19785b50e9
Merge pull request #4425 from Lorkydey/fix/import-audit-atomic-json
fix(import-audit): preserve JSON reports when writing fails
2026-10-14 14:06:16 +13:00
Dean d7bb2fee3d
Merge pull request #5580 from oneandonlydean/fix/videoout-dinput-1042
fix(videoout): disable SDL DirectInput joystick enumeration on Windows
2026-10-14 14:02:29 +13:00
Dean 604ce71864
Merge pull request #4947 from Kikoarl/perf/agc-draw-recipe-depth-expansion
perf(agc): expand draw recipes to support depth targets
2026-10-14 14:00:34 +13:00
Dean 1f1115dc90
Merge pull request #4795 from MarcZX100/codex/pipeline-cache-snapshot-lock-4781
fix(agc): serialize pipeline cache snapshots
2026-10-14 14:00:21 +13:00
Dean 0dea07ab27
Merge pull request #4517 from AlxcBTW/perf/host-import-join-exact
perf(agc-driver): join the imports of adjacent ranges of any size that a buffer is bound exactly over
2026-10-14 13:53:00 +13:00
thibaut b7d7e71bf6 docs(libc): move the Windows direct memory debt entry to libc.md
The entry links WindowsMappings.hpp, so the debt-file convention puts it in
docs/dev/debt/libc.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-14 02:39:10 +02:00
thibaut f32a9407e4 test(libc): require resident direct memory in the resident run tests
CheckBreakingAResidentRunKeepsConcurrentAccesses and
CheckRemappedDirectMemoryIsChargedOnce passed on main, where a singly
mapped direct memory page is a shared view and no resident run exists.
Both now require their first mapping to be private committed memory, so
on main they fail there instead of passing without exercising a run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-14 02:39:10 +02:00
thibaut 71da166a18 fix(libc): keep resident mappings to direct memory backings on Windows
Map made every singly mapped section page private resident memory. A JIT
or shm_open section is file backed and committed: its bytes live in the
file, and its alias descriptors map duplicated handles, which the physical
page slots do not match, so a resident copy diverged from the file, and
committing its view on a demote failed with access denied
(guest_jit_shared_memory). Only direct memory backings, mapped through the
new GuestArenaMapDirect, now become resident; other sections keep main's
shared views. Shared pages are committed only while their section page is
reserved, as on main.

The socket host write tests map their direct memory a second time, so the
write-protected shared path they check is still the one they exercise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-14 02:39:10 +02:00
DotDebian d9852df2a2 fix(libc): track writes to singly mapped direct memory without faults on Windows
A direct memory page mapped at one guest address now lives in private
MEM_WRITE_WATCH memory at that address, and its writes are collected with
GetWriteWatch like flexible memory: no fault is raised. A page mapped at a
second address moves its whole resident run into the section, behind a
read-only fence while it is copied, and every address becomes a view of it,
tracked by write protection. Unmapped resident pages that are not blank are
kept in a private reservation until they are mapped again, not in the
section.

Ported onto main's 4 KiB subpage tracking of shared views: resident runs
keep a per-page collected bit and enter the fresh ranges, so Fresh and
Armed collections select them like shared views; a demoted run carries its
collected bits into the views and reports every page once. MapAlias throws
through refuse() when a resident run extends outside the aliased range, and
the caller keeps main's fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-14 02:39:10 +02:00
MarcZX100 4307e3cb54 fix(agc): serialize pipeline cache snapshots 2026-10-14 13:34:43 +13:00
AlxcBTW d275a58814 fix(agc-driver): copy a region an exact join's import covers only in part
After an exact two-piece join, a bind across the join's end got the
joined import from importAllocation and the view failed as "rounded up to
its view range exceeds its GPU owner". UploadFinish now asks importJoined
for a region past the import's end and drops an import that still does
not cover the region, so the region is copied, as on main. The join test
binds across the end of an exact join after a third piece is registered.

Co-authored-by: TideOPL <57710757+TideOPL@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:34:31 +13:00
AlxcBTW ca95f74097 perf(agc-driver): join the imports of adjacent ranges of any size that a buffer is bound exactly over
A bound buffer is joined across adjacent registered ranges only when every
one of them is larger than the 8 MiB window. PPSA25344's loading binds 16 MiB
and 4 MiB buffers whose backing was registered in two to six adjacent
pieces with the same access, some of them smaller than the window, so each
use of such a buffer was copied on the CPU: up to 348 GB in the first 300 s
of loading.

A bound buffer that starts at the first crossed range's start and ends at the
last one's end now joins those ranges whatever their size. A heap grown in
pieces matches that only for a binding over exactly its current pieces, so
growth beside a joined import does not re-import it. joinedCovers, used when a
joined import retires, accepts any adjacent run of ranges.

importJoinTests binds exactly over two 64 KiB pieces of a partly filled
window and requires one import of both; a binding across them that starts
inside the first piece stays apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:34:31 +13:00
Dean ce5c839533
Merge pull request #4883 from nikowolf123/perf/shader-cache-resource-plan
perf(recompiler): keep the front-end resource plan in the shader disk cache
2026-10-14 13:33:01 +13:00
Dean 29d5f067b9
Merge pull request #5003 from tibzejoker/feat/depth-storage-array-views
feat(agc): storage image access to depth planes through 2D array views
2026-10-14 13:32:41 +13:00
Dean af991ac404
Merge pull request #4747 from TideOPL/perf/bc-block-copy
perf(agc): copy a pending block-compressed surface into its sampled texture on the GPU
2026-10-14 13:32:04 +13:00
Dean cb320fd884
Merge pull request #4603 from AlxcBTW/perf/agc-memory-value-fill
perf(agc): take the memory-value dword fill kernel as a buffer fill
2026-10-14 13:31:43 +13:00
Dean 520ee1a2e8
Merge pull request #4490 from karanvora2599/perf/shader-minmax-floor-denormal-free
perf(shader): treat f32 min, max, med3, floor and ceil results as denormal-free
2026-10-14 13:31:23 +13:00
Dean afc8541d18
Merge pull request #4487 from Pipodi/feat/agc-forced-srgb-views
feat(agc): sample non-sRGB textures through FORCE_DEGAMMA samplers as sRGB views
2026-10-14 13:31:02 +13:00
Dean 7cc6cd8633
Merge pull request #4465 from mrzacarias/fix/agc-buffer-pattern-fill
fix(agc): handle the fill kernel that reads its pattern from a buffer
2026-10-14 13:30:42 +13:00
JJenkx d1527840ea fix(libkernel): retire stack metadata before releasing mappings 2026-10-13 20:30:24 -04:00
fdfawef324 8d8fe39eed fix(agc): flush pending stores before a partial snapshot hit
A partially resident snapshot cannot be reused from committed write stamps while a recorder batch still owns an overlapping completion store. Make that case compare through the guest-memory flush hook before deciding whether the snapshot is unchanged.

Cover copied completion stores in both open and submitted batches, then verify that unchanged snapshots are reused and an unrelated pending write does not synchronize. The focused regression fails on the published PR head and passes with the guard. The existing fully committed snapshot path is unchanged.

Co-Authored-By: Codex <noreply@openai.com>
2026-10-13 20:25:46 -04:00
fdfawef324 955b56a2b5 perf(agc): trust write stamps for snapshots of partially committed textures
A sampled texture over a partially resident surface (a texture array whose
heap the guest commits on demand) never got a write generation: CollectWrites
fails on a range with reserved pages, so every cache hit compared the whole
committed part with its snapshot. CollectCommittedWrites collects the
committed runs one by one and returns the runs with the generation; the cache
entry records them, and a hit skips the compare only when the committed runs
are the same and UnchangedCommittedSince holds over them. A commit, decommit,
view mapping or unmapping inside the surface changes the runs or stamps the
new pages, so it is compared as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:25:46 -04:00
Dean Galvin 56d1eda2f0 docs(videoout): record disabled DirectInput controller backend
Co-authored-by: Tenith01 <102300949+Tenith01@users.noreply.github.com>
2026-10-14 13:14:15 +13:00
Tenith01 e94f12bd17 fix(videoout): disable DirectInput joystick enumeration on Windows 2026-10-14 13:14:15 +13:00
Dean feddf7f4c6
Merge pull request #4849 from MarcZX100/codex/fix-address-cache-shutdown-4846
fix(agc): release cached address space on device teardown
2026-10-14 13:13:08 +13:00
Dean 014f3623a7
Merge pull request #4508 from jradelmo/feat/flexible-memory-budget
fix(kernel): honor the declared flexible memory budget
2026-10-14 13:13:03 +13:00
Kikoarl 3ebad9045e perf(agc): expand draw recipes to support depth targets 2026-10-14 02:00:40 +02:00
David-cmrg 75c25e16f0 fix(tests): use position-independent unwind data in the C++ error fixture 2026-10-14 01:49:52 +02:00
Dean d1670009fb
Merge pull request #5429 from Dripl1x/perf/windows-mutex-wake
perf(libkernel): wake guest mutex waiters on release on Windows
2026-10-14 12:31:24 +13:00
Evgeniy Kazarin 99eaf73102 perf(libkernel): wake guest mutex waiters on release on Windows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 02:18:33 +03:00
Adelmo Junior 12851ef037 docs: document flexible memory budget behavior 2026-10-13 20:13:14 -03:00
Adelmo Junior 72211b70fe fix(kernel): honor the declared flexible memory budget
Read the optional aligned budget from param.json and apply it consistently to configured, available and mapped flexible memory. Add parser and allocation regression coverage.
2026-10-13 20:13:14 -03:00
Dean 7a0c838ca4
Merge pull request #4875 from nikowolf123/perf/libc-write-watch-capacity
perf(libc): size each write-watch query to the region it covers
2026-10-14 12:12:10 +13:00
AlxcBTW 6ffc99bedb perf(agc): take the memory-value dword fill kernel as a buffer fill
The 9-word kernel #3816 matches stores one dword per thread, loaded with
s_buffer_load_dword through the descriptor in user[4..7]. Run as a
compute dispatch, its stores are pending writes of unknown value, so a
later read of DCC keys in that range waits for the GPU.

fillBuffer now takes it as a uniform buffer fill when all of these hold:
- the output descriptor's format is 32_UINT, 32_SINT or 32_FLOAT (20-22),
  so buffer_store_format_x stores the loaded dword unchanged;
- the value descriptor covers the loaded dword: num_records times the
  stride (num_records alone when the stride is 0) is at least 4 bytes,
  the range the recompiler gives a scalar buffer load;
- exactly 8 user SGPRs and group id X enabled, so the group id is s8;
- a direct, non-partial dispatch, 64x1x1 threads, one group in Y and Z,
  and a 16-byte aligned range of whole 16-byte units.

The value is read when the fill is recorded, after the labels queued
before it, as the pattern-fill kernel's source is. When the value is one
byte repeated and that byte is a DCC key code, the fill goes into the GPU
key-store memo whatever ClassifyFill reports: the memo answers a key
read only while this fill is still the newest pending write over the
keys, and a fill over a color target's keys is not a storage image's
keys fill.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:44:41 +02:00
David 773e7f218c docs(debt): describe the byte the Windows flock locks 2026-10-13 22:29:33 +00:00
David b962be8536 test(libc): read and write through another descriptor while a flock is held 2026-10-13 22:29:20 +00:00
David 32fdaa65ae fix(libc): make flock advisory on Windows so other descriptors can read and write 2026-10-13 22:29:20 +00:00
Dean 30f961266a
Merge pull request #4973 from ducloc080202/fix/cq3-positioned-io
fix(libkernel): serialize positioned file transfers on Windows
2026-10-14 11:28:14 +13:00
Dean 83924acb18
Merge pull request #4467 from arcwiser/feat/libc-swscanf
feat(libc): implement swscanf
2026-10-14 11:27:57 +13:00
Dean Galvin 296ff64ae9 fix(libkernel): share positioned transfer locks on Windows only
The per-descriptor lock exists only in the Windows transfer path, so ShareDescriptor calls SharePositionedTransfer under _WIN32 like GuestFileLockShare. Linux failed to link libkernel with an undefined reference to it.
2026-10-14 11:18:55 +13:00
ducloc080202 0b46b3080a test(libkernel): cover concurrent pread against seek and read 2026-10-14 11:18:07 +13:00
ducloc080202 3cab3c05ec fix(libkernel): use per-descriptor locks for positioned transfers and seeks 2026-10-14 11:18:07 +13:00
Anshika Gupta 3915f6dc09 fix(libkernel): serialize positioned file transfers on Windows 2026-10-14 11:18:01 +13:00
screamm 8aa0d42824 feat(libc): implement swscanf
swscanf (HNnWdT43ues) runs the swscanf_s scanner without the buffer
size arguments after %s, %c and %[, as FreeBSD's swscanf.c passes its
arguments to vswscanf. Both entry points share one engine with a flag
for the sizes; swscanf_s behaves as before.

Title: PS5CEMU-HAR PPSA99360 imports swscanf from libSceLibcInternal.prx
(import audit with --trap-sdk-syscall-stubs, registry index 292, right
after pthread_setaffinity_np at 276). Predicted stop line once
pthread_setaffinity_np resolves, without this change:
FAIL: unresolved ELF import HNnWdT43ues (swscanf) from libSceLibcInternal.prx

NID from tools/nid_names.py, confirmed in shadPS4 aerolib.inl:51950
(e0a466753a1a). Prototype in ps5-payload-dev include/freebsd/wchar.h:133,
export in sce_stubs/libSceLibcInternal.c:8582 (f5d40f02770e).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 11:17:04 +13:00
Dean bf8dc610e6
Merge pull request #5425 from SandstromPL/feat/ces-module-library
feat(libSceCesCs): build libSceCesCs-module from the libSceCesCs source
2026-10-14 11:14:19 +13:00
Dean 703aa8f63d
Merge pull request #5411 from ps54all/feat/libscenet-resolver-ntoa6
feat(libSceNet): implement sceNetResolverStartNtoa6
2026-10-14 11:12:27 +13:00
Dean bfc10637d4
Merge pull request #5216 from oneandonlydean/up4/draw-key-second-sight
perf(agc): cache a draw key the second time it is seen
2026-10-14 11:06:03 +13:00
Dean 65beff9b19
Merge pull request #5478 from oneandonlydean/up4/sdwa-ubyte-1014
fix(recompiler): accept SDWA source selects on v_cvt_f32_ubyte1, ubyte2 and ubyte3
2026-10-14 11:05:47 +13:00
Dean c7601b7971
Merge pull request #4164 from hamb3r/feat/libc-unity-il2cpp-runtime
feat(libc): add missing Unity IL2CPP runtime imports and Dinkumware helpers
2026-10-14 10:45:32 +13:00
thibaut 696d1660c1 feat(agc): storage image access to depth planes through 2D array views
A 2D array storage view of a depth plane from slice 0 threw. It now gets
a storage image over all its slices: each slice with a depth surface at
the plane's address plus a whole depth slice per layer is copied from
that surface before every use and back after the work; the other slices
hold guest memory like any storage image. Cubes, stencil arrays, later
base slices and layer pitches other than the depth slice size still
throw.

GTA V (PPSA04264) stops at compute shader 0x242d4a200 on a 5-slice R32
Z_64KB_X array over its shadow depth surfaces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 23:38:51 +02:00
Dean 665ad7da74
Merge pull request #4374 from MarcZX100/codex/carry-getpriority-3632
feat(libkernel): implement getpriority for the guest process
2026-10-14 10:36:42 +13:00
Hussein Kadhim 091438f99b fix(libkernel): fail sysctl with ENOTDIR when a leaf MIB has a trailing component
FreeBSD sysctl_find_oid returns ENOTDIR when the MIB continues below a leaf object. The supported hw.ncpu, hw.model, hw.physmem and kern.arandom MIBs threw an unsupported MIB error instead.

(cherry picked from commit 6968aea8af578c4d56dc614cbe449274bb6e76e7)
2026-10-13 21:33:25 +00:00
David 1ba0a81820 feat(libkernel): implement getpriority for the guest process
PS5MC (PPSA99420, Kodi 22) imports getpriority (miQ1wVwSexE) from
libkernel.prx, one reference, next to chflags in its import order.
Predicted stop once the imports ahead of it are covered (not a run):

  FAIL: unresolved ELF import miQ1wVwSexE from libkernel.prx

The NID is computed with tools/nid_names.py and matches aerolib.

Follows FreeBSD 11.4 sys/kern/kern_resource.c sys_getpriority
(8c639945a44588f6cfdddfb057bb5aad88735da0) for the only guest process,
taken to run at nice 0 and to lead its own process group (as kill and
setsid already assume): PRIO_PROCESS and PRIO_PGRP with 0 or its pid,
and PRIO_USER with 0 or its uid 0, return 0; other ids give ESRCH and
an unknown which gives EINVAL. getpriority is a bare system call in
FreeBSD's libc (lib/libc/sys/Symbol.map), so errno is left unchanged
on success.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 63262245d274342b693fda146ae9feca3752b6e3)
2026-10-14 10:23:41 +13:00
Dean 8d98e8851f
Merge pull request #4474 from JJenkx/fix/multisample-target-retirement
fix(agc): retire fully unmapped multisample targets
2026-10-14 10:23:12 +13:00
Dean cf5f5b53b5
Merge pull request #4379 from MarcZX100/codex/carry-chflags-3632
feat(libkernel): carry chflags implementation
2026-10-14 10:22:02 +13:00
Dean d638e02860
Merge pull request #3991 from Sanjaysrinivas/perf/agc-lazy-storage-snapshot
perf(agc): allocate storage CPU snapshots on demand
2026-10-14 10:21:23 +13:00
Dean 7d757bbf0a
Merge pull request #4383 from pushkarreddyy/feat/libkernel-sigpending-sigsuspend
feat(libkernel): implement sigpending
2026-10-14 10:21:01 +13:00
Dean ea2b2d364f
Merge pull request #4514 from kallmek3nny/feat/libkernel-pthread-setaffinity
feat(libkernel): implement pthread_setaffinity_np
2026-10-14 10:20:33 +13:00
Dean Galvin 7b2104ee0d fix(recompiler): accept SDWA source selects on v_cvt_f32_ubyte1, ubyte2 and ubyte3
v_cvt_f32_ubyte0 accepts every SDWA source selector, but the other three byte
conversions had no VOP1 SDWA rule, so any SDWA source select on them was refused
("VOP1 SDWA source selector 4 of opcode 0x12 at pc 0xfcc is not supported").
The selected and optionally sign-extended source goes through the same operand
path as for ubyte0 before byte N is extracted: SDWA supplies source 0 (RDNA 2 ISA
guide 6.8 and 13.3.7) and the opcode reads S0.u[8N+7:8N] (12.8).

agc_driver_sdwa_cvt_f32_ubyte: WORD_0 and WORD_1 selects on ubyte1, and
sign-extended WORD_1 and BYTE_3 selects on ubyte2 and ubyte3.
2026-10-14 09:36:10 +13:00
MarcZX100 b6a10e91aa fix(libkernel): remove duplicate chflags error constant 2026-10-13 20:28:23 +00:00
Dean 0aa4cb1c2f
Merge pull request #3798 from YossiYad/carry/posixforwebkit-getservbyname
feat(libScePosixForWebKit): implement getservbyname
2026-10-14 09:24:05 +13:00
milescoviello eb78c6a45f fix(libkernel): fail sceKernelStat with EFAULT for a null buffer
Crimson Desert (PPSA25286) calls sceKernelStat with a null buffer at
start-up, and main throws "sceKernelStat: sb is null", which ends the
title in its first second.

FreeBSD 11.4 looks the path up first and only then copies the result
out, so a null buffer gets EFAULT for a path that exists and the lookup
error for one that does not (sys/kern/vfs_syscalls.c, sys_stat):

	error = kern_statat(td, 0, AT_FDCWD, uap->path, UIO_USERSPACE,
	    &sb, NULL);
	if (error == 0)
		error = copyout(&sb, uap->ub, sizeof (sb));

sceKernelStat now does the same: the lookup and its errors are as
before, and a null buffer returns SCE_KERNEL_ERROR_EFAULT where the
status would be copied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:20:07 +00:00
Dean a0f5916a29
Merge pull request #4417 from Jnbla6/fix/ngs2-rack-4001
feat(libSceNgs2.native): implement custom sampler rack id 0x4001 (#4334)
2026-10-14 08:55:20 +13:00
MarcZX100 852c808373 test(libSceNgs2): fix custom filter regression expectations 2026-10-14 08:47:31 +13:00
Zheorgyan 3960519b1e fix(agc): make host imports over writable pages on macOS
Co-authored-by: Marcio Rocha Zacarias <6743187+mrzacarias@users.noreply.github.com>
2026-10-13 21:43:21 +02:00
Dean Galvin 22f6b1735c docs: keep the technical debt index and end the NGS2 entry with a newline
The rebase onto main deleted docs/dev/TechnicalDebt.md, which is now the
index that points into docs/dev/debt/. Restore it from main and add the
missing final newline to docs/dev/debt/libSceNgs2.native.md.
2026-10-14 08:39:38 +13:00
jnbla6 40ebfae70b fix: throw on unsupported filter types and update debt docs 2026-10-14 08:39:32 +13:00
Jnbla6 ef52b16920 feat(libSceNgs2.native): add custom voice filter support and related tests for 0x13 issue case 2026-10-14 08:39:32 +13:00
Jnbla6 855823e85e feat(libSceNgs2.native): implement custom sampler rack id 0x4001 (#4334) 2026-10-14 08:39:32 +13:00
lorkydey d45fee9b69 fix(import-audit): preserve JSON reports when writing fails 2026-10-13 21:20:45 +02:00
mrfelek 48b69fad4d feat(libc): implement sceLibcMspaceAlignedAlloc 2026-10-13 21:20:32 +02:00
SandstromPL 299a736b54 feat(libSceCesCs): build libSceCesCs-module from the libSceCesCs source
Titles that link the character conversion library under its module name
need libSceCesCs-module.prx, which nothing built: Void Terrarium
(PPSA03061, #5055) and Call of Duty: Black Ops (PPSA34501, #5389) were
run with a copy of libSceCesCs.prx under that name. Both libraries are
now built from the same source by add_sce_ces_library, as libScePsml and
libSceFont-module are, and guest_ces_module runs the CesCs test against
the module-named library.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:44:56 +05:30
ps54all df32a44f79 feat(libSceNet): implement sceNetResolverStartNtoa6
Resolve a host name to its first IPv6 address through the host resolver
(getaddrinfo with AF_INET6) and write the 16-byte address. The lookup
shares the IPv4 path's resolver bookkeeping (abort preservation, error
state); a name without an IPv6 address fails with RESOLVER_ENODNS like a
failed lookup. Nonzero flags throw.

Metal Gear Solid 4 Master Collection (PPSA18194) imports it and failed to
load without it (unresolved zl35YNs9jnI from libSceNet.prx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:13:34 +02:00
MarcZX100 2ad825f659 fix(shader): require the version 22 F16 request flag 2026-10-13 19:09:49 +00:00
MarcZX100 0e087e974e test(shader): cover version 21 F16 request layout 2026-10-13 19:09:49 +00:00
MarcZX100 dffdb5002e fix(shader): read both version 21 request layouts 2026-10-13 19:09:49 +00:00
Stefan Bucataru 89a8ec58b1 fix(tools): validate empty ELF dynamic symbol table bounds 2026-10-13 21:07:36 +02:00
Dean dacd78525e
Merge pull request #4949 from fdfawef324/fix/agc-unwritten-sample-locations
fix(agc): start the sample location registers at their clear-state value
2026-10-14 08:03:16 +13:00
MarcZX100 dfc9223248 docs(libkernel): retain chflags debt after rebase 2026-10-13 18:54:49 +00:00
MarcZX100 8b5cfb9850 test(libkernel): declare chflags guest entry point 2026-10-13 18:54:07 +00:00
David 4dd97f7dbe feat(libkernel): implement chflags through the guest path resolver
PS5MC (PPSA99420, Kodi 22) imports chflags (ixrw0h2tWuI) from
libkernel.prx, one reference. Predicted stop once the imports ahead of
it (pthread_getthreadid_np, sendfile, wcstok, pause, setrlimit and the
termios calls) are covered (not a run):

  FAIL: unresolved ELF import ixrw0h2tWuI from libkernel.prx

The NID is computed with tools/nid_names.py and matches aerolib.

Follows FreeBSD 11.4 (8c639945a44588f6cfdddfb057bb5aad88735da0)
sys/kern/vfs_syscalls.c kern_chflagsat and setfflags, and
sys/ufs/ufs/ufs_vnops.c ufs_setattr, for a root caller: the lookup
through the guest resolver gives EFAULT, ENOENT and ENOTDIR (also for a
file reached through a trailing slash or used as a directory), a bit
outside the 17 UFS flags of sys/sys/stat.h (VNOVAL included) gives
EOPNOTSUPP, and SF_SNAPSHOT gives EPERM, since the snapshot flag cannot
be toggled. No host keeps BSD file flags and stat reports st_flags 0,
so flags 0 succeed without touching the file and any other flag throws
with the flags named. UF_READONLY and the other Windows-attribute flags
are not mapped to FILE_ATTRIBUTE_READONLY and its siblings: UFS stores
them without enforcing them, and on Windows the read-only attribute is
already the owner write bit of chmod.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 16e5b7ccd04e6467a5a282b6cfc9271304f42244)
2026-10-13 18:54:05 +00:00
Jesus 50b06110bb fix(libkernel): keep the Windows vnode watch when the watched file is deleted
Windows 10 1903 deletes a file with open handles by moving it into
$Extend\$Deleted until the last handle closes, so the next snapshot of a
watched file that a title (or the test) deleted saw a new path, tried to
watch that directory's changes, got ERROR_ACCESS_DENIED and threw from
the watch thread, which aborts the process; the path change was also
reported as NOTE_RENAME next to NOTE_DELETE. A snapshot with a pending
delete or no links now reports NOTE_DELETE alone and drops the parent
directory watch instead of replacing it, as the file has no directory
to be renamed or removed from any more.
2026-10-13 13:41:05 -05:00
fdfawef324 15955c089e fix(agc): start the sample location registers at their clear-state value
A draw with PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES > 0 whose context never
wrote PA_SC_AA_SAMPLE_LOCS_PIXEL_* (context 0x2fe-0x30d) was rejected
with "missing register in context bank at DWORD 0x2fe", because the
registers were absent from the initial context (PPSA07411 stops on it
after two frames). The MSAA_ENABLE single-sample path read them the
same way.

The sixteen registers now start at 0, both initially and after
CLEAR_STATE. That is the value in libSceAgc's own register defaults
(sceAgcGetRegisterDefaults2, every public table that holds context
registers, the tables the initial context already follows for
PA_SU_VTX_CNTL and PA_SC_CONSERVATIVE_RASTERIZATION_CNTL), in amdgpu's
GFX10 clear-state table (clearstate_gfx10.h, gfx10_SECT_CONTEXT_def_8,
loaded by gfx_v10_0_cp_gfx_start before CLEAR_STATE) and the GC 10.3
register reset value (gc_10_3_0_default.h). RADV, radeonsi and PAL
always write the locations themselves and do not settle the unwritten
value.

0 puts every sample at the pixel center. That is not the standard
pattern for any sample count, so such a draw decodes as custom
locations at the center, as a single-sample scan into a multisampled
target already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:06:05 -04:00
thuanquang 286cf28ec7 fix(relinker): reject a directory as the executable output path
An existing directory, or a path ending in a separator, was only
rejected when the executable was written, after the converted modules
had already been written to app0/ beside or inside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 00:49:48 +07:00
Dean 18c438b109
Merge pull request #4491 from xXPBSXx/fix/playgo-bound-no-chunk-table
fix(libScePlayGo): answer chunk ids from the package layout
2026-10-14 06:42:39 +13:00
Hussein Kadhim 2096410b29 fix(libkernel): report EPERM before EINVAL when writing kern.proc.pathname
FreeBSD sysctl_root rejects a write to a read-only node with EPERM before the node handler checks the MIB length, so a write with a short or long kern.proc.pathname MIB fails with EPERM. sysctlbyname for kern.proc.pathname now goes through sysctl with its 3-component MIB so it follows the same order.
2026-10-13 17:39:56 +00:00
Niko 5d3ea5f511 test(recompiler): set layerExported in the disk cache entry round trip
#5097 added ShaderInfo::layerExported to the entry encoder, and this
series moves that line into encodeShaderInfo, which plan entries use
too. The entry round trip left the flag false, so an encoder and a
decoder that both skipped it still passed. Set it like the other
ShaderInfo flags: with layerExported dropped from both, the round trip
now fails with "entry round trip: shader info differs".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 19:30:21 +02:00
boykopovar 8cf6a0bc3a
Merge pull request #5328 from boykopovar/chore/add-first-demo-collage
chore(readme): add games demo collage to readme
2026-10-13 20:18:47 +03:00
boykopovar bdded28693 chore(readme): add games demo collage to readme 2026-10-13 20:16:47 +03:00
Sasha Papo cb8b0caf7d fix(libSceNpAuth): report an async request as running on its first poll
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:07:19 -04:00
Dean 61b8742b6b
Merge pull request #4515 from AlxcBTW/perf/host-import-join
perf(agc-driver): join the imports of adjacent large ranges when a buffer is bound across them
2026-10-14 05:55:27 +13:00
Dean 9baec8793f
Merge pull request #3870 from yash27git/clean/Direct-Memory-Exception
fix(libkernel): harden DirectMemory validation and tests
2026-10-14 05:55:11 +13:00
Dean 40047f46bd
Merge pull request #4377 from MarcZX100/codex/carry-pthread-atfork-3628
feat(libkernel): implement pthread_atfork registration
2026-10-14 05:54:54 +13:00
Muhammad Wasif Ali Wasif 04421e7d4d fix(relinker): build the manifest resource in paths with spaces
windres passes include directories to the preprocessor unquoted, so
relinker.rc failed to compile when the checkout path contains a space.
The resource is now an object library without the relinker's include
directories.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:43:59 +05:00
screamm 74c3fb6576 feat(libkernel): implement pthread_setaffinity_np
PS5CEMU-HAR (PPSA99360, ps5-payload-dev build, relinks with
--trap-sdk-syscall-stubs) imports pthread_setaffinity_np (5KWrg7-ZqvE)
from libkernel.prx. babyhuey's run with #3787 merged onto main e7c70a2d
(PR #3787, 2026-10-12T10:54:44Z) stops at it:

  Windows: FAIL: unresolved ELF import 5KWrg7-ZqvE (pthread_setaffinity_np) from libkernel.prx
  Linux: undefined symbol: 5KWrg7-ZqvE (pthread_setaffinity_np)

It follows FreeBSD 11.4 libthr thr_affinity.c (8c639945) over
kern_cpuset_setaffinity: another thread is looked up first (EINVAL for
null, ESRCH when unknown or finished, as _thr_find_thread without dead
threads), a size other than the 32-byte cpuset_t returns ERANGE, the
mask is clipped to sceKernelGetAvailableCpumask and an empty result
returns EDEADLK, as cpuset_setaffinity does. The affinity is stored on
the guest thread through scePthreadSetaffinity, for the caller and for
any live guest thread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:39:07 +00:00
Niko 664ad77037 test(recompiler): pin the field counts of the structures the plan codec writes
The plan and ShaderInfo encoders write these structures field by field,
and the sizeof asserts are their only reminder to update the encoders. A
field that fits in existing padding keeps the size: main's layerExported
landed in ShaderInfo with sizeof still 232, and the three bools #5089
adds to DescriptorSource::IndirectImage keep it at 28. A warm cache
would then hand back entries without the new field and nothing would
fail.

Count the fields of every aggregate the plan codec reaches (IrResourcePlan,
GuestRegister, MemoryInfo, DescriptorSource and IndirectImage,
ResourceBlock, SrtRead, UniformFill, UniformFillPlan, and ShaderInfo with
its resource and stage I/O records) by brace-initializing each with more
and more convertible-to-anything values until the initializer no longer
compiles, and assert the counts. Unlike the size asserts this does not
depend on the ABI, so it is checked on every platform. IrValue is a
class with private state and keeps only its size assert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:32:19 +02:00
Niko ce8944f853 test(recompiler): plan an invariant descriptor phi in the disk cache test
The round trip requires some fixture to plan a phi, and only one did:
the loop counter of "bindless sample in a loop latch", which reaches the
plan through the latch condition. The counter is not invariant, so the
CPU walk can never evaluate that condition. A plan that keeps only the
conditions the walk can evaluate, as #5181 aims to, would hold no phi
and fail the check with "no fixture plans a phi".

"descriptor invariant phi" loads the image descriptor from the same SRT
offset on both sides of a branch on an SRT dword. Its eight dwords are
phis of equivalent reads: the tracker keeps them, since it splits only
descriptor phis that are not invariant, and the walk evaluates them
through the invariant read. The plan stores them as descriptor dwords,
whichever conditions it keeps, so the phi encoding stays covered. The
fixture goes after the other compute fixtures, so they keep their seeds
and their damaged-entry checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 70663fec42 test(recompiler): check that APS5_NO_PERF_DISK_PLANS=1 is cache-neutral
APS5_NO_PERF_DISK_PLANS=1 is listed as a neutral switch, so it stays out
of the variant key, and it exists to compare warm starts with and
without stored plans. Nothing tested either property: the cross-process
test ran with the switch unset only.

A fourth child process now sets the switch against the cache the first
process filled. It must run the front end for all 14 fixtures, neither
load nor store a plan, still hit all 14 stored variants, and produce the
same SPIR-V, bindings, push constants, snapshots, read traces and plans
as the first process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko a69f5e0371 test(recompiler): compare stored resource plans field by field
The plan tests compared a decoded plan with the original only through
the codec itself (re-encoding must give the same bytes) and through
materialization. Neither catches a field that EncodePlan and DecodePlan
both leave out: the bytes still agree, and the walk does not read every
field. Leaving userDataBase, the plan's sRGB formats, the value ids or
several MemoryInfo fields (secondaryOffset, dataDwords, dataFormat,
planningOnly, idxen) out of both sides passed every test. The hand-built
plan also never materializes (its uniform values cannot be evaluated),
so its 1000 runtimes all failed with the same error, and no plan held an
immediate wider than 32 bits.

samePlan compares every field of two plans without the codec: the value
graph by storage index (opcode, type, id, flags, register, immediate,
arguments, phi blocks), the block ids, the roots, and the plain data
through the structures' own equality. The round trip now requires it
for the 14 fixtures and the hand-built plan, which sets every MemoryInfo
field, all indirect table fields, a second buffer, the whole uniform
fill and a 64-bit immediate to values other than their defaults. The
second process also builds each fixture's plan with its own front end
and requires it to equal the plan it used, so a stored plan must match
what the front end builds in another process, not only its own
re-encoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko ed3f2776ed fix(recompiler): reject a stored plan whose image table names a missing source
DecodePlan checked every descriptor source index a plan holds except the
two inside an indirect image table: the heap source and the material
source. A damaged entry could therefore decode with a table that points
past the plan's descriptor sources, and only the materializer's own
bounds checks stood between it and an out-of-range read. The front end
interns both sources before the image source that names them, so a plan
it builds always passes the new check.

agc_shader_disk_cache now requires a plan whose table heap or material
source lies past the sources not to decode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 1ed6c21067 perf(recompiler): load the resource plan from the disk cache before the front end
getSource built ResourceProgram (PrepareResourceProgram and ExtractPlan)
for the first request of every shader a process saw, before
findOrCompileVariant looked in the disk cache. With a warm cache every
launch still decoded, built the CFG, structurized, translated and ran
SSA, the fold passes, SrtWalker and ResourceTracker for every shader;
only SPIR-V emission was skipped.

getSource now builds the plan key and tries LoadPlan first. On a hit it
sets the plan and leaves the program null: findOrCompileVariant already
rebuilds the program when no stored variant fits the layout. The loaded
plan takes the request's code address as its shader hash, as a plan
built in this process would. On a miss it builds the plan as before and
stores it. A plan the front end refuses is not stored. The disk is not
used when the cache is off, under the debug probe, with APS5_DUMP_IR
(which prints the front-end passes), or with APS5_NO_PERF_DISK_PLANS=1,
a cache-neutral switch for comparisons.

FrontEndBuilds() counts PrepareResourceProgram calls. The new
cross-process test prepares, captures and materializes 14 shader
fixtures in one process and repeats it in a second process sharing the
cache directory: the second process must run the front end 0 times, hit
14 plans and 14 variants, and produce byte-identical SPIR-V, bindings,
push constants, snapshots, read traces and plans. A damaged plan entry
is rejected and rebuilt alone with the same output and stored again; a
changed code word and another switch value miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 0c144502e5 feat(recompiler): store front-end resource plans in the shader disk cache
The disk cache keeps compiled variants (SPIR-V, shader info, binding
layout) but not the IrResourcePlan the front end extracts for the
resource walk, so a process can only get a plan by running the whole
front end. Since #471 the plan owns its values (ownPlanValues clones the
graph behind every root into valueStorage and blockStorage), so it can
be written out and rebuilt.

- EncodePlan writes the value graph in storage order, each value as
  LEB128 integers: opcode, type, id, flags, register, operand count,
  the immediate if it has one, each argument as the zigzag distance to
  the value's own index (0 for null) and, for phis, the index of each
  predecessor block. The roots (descriptor source dwords, SRT reads,
  control-flow conditions, uniform-fill values) are value indices; the
  plain data (memory info, indirect image tables, control-flow edges,
  materialization sources, guarded, clean and pure flat slots, the
  flags, the ShaderInfo through the variant's encoder) follows. A plan
  whose roots or arguments point outside its storage is not encoded.
  Control-flow conditions can pull most of a program into the plan:
  a synthetic 2,038-dword compute shader with a branch every 20 dwords
  has 42,775 plan values, which take 518 KB this way and 2.5 MB with
  fixed-width fields; its compiled variant entry is 935 KB.
- DecodePlan rebuilds the graph the way ownPlanValues does and rejects
  an unknown opcode, an overlong integer, an index past the values or
  blocks, a descriptor source, image, buffer or sampler past the
  sources, a branch without two successors, an edge past the blocks,
  and a uniform fill of more than four words.
- BuildPlanKey is the in-memory source key (RecompileCacheKey::Build and
  the host subgroup size), every code word and the switch key, after a
  plan entry tag. It leaves out the binding layout, so all layouts of a
  shader share one plan.
- EncodePlanEntry/DecodePlanEntry use the variant's header and hashes;
  LoadPlan and StorePlan use the same directory and background writer
  and count plan hits, misses, writes and load failures on their own.
- Size asserts cover the plan and the structures it is written from.
- FormatVersion 30.

agc_shader_disk_cache now round-trips the plans of 14 in-tree shader
fixtures (SRT reads, guarded and chained pointers, control-flow
conditions, an indirect image table, a loop phi, a fragment shader) and
a hand-built plan with a uniform fill. For each it materializes the
original and the decoded plan over 1000 random user-data and memory
runtimes and requires the same snapshot, poison, null-root count and
read trace, or the same error. Truncated, bit-flipped and
other-format entries are rejected, malformed plans do not decode, and
the key follows the code, the target and the host subgroup size but not
the layout, addresses or user data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Hussein Kadhim 37ea3b651a fix(libc): finish the exit handlers when one of them calls exit
A guest handler that called exit threw out of the libc exit path because the exiting thread waited on itself. Like FreeBSD, the nested call now runs the handlers that are still registered and ends the process with the status it passed.
2026-10-13 16:28:00 +00:00
Niko 5c802bbf10 refactor(recompiler): split the ShaderInfo encoder and the entry envelope
encodeInfo and decodeInfo wrote the ShaderInfo fields inline between the
compiled-shader header and the binding layout, and EncodeEntry and
DecodeEntry built and checked the file header (magic, format, source
version, key and payload hashes) inline. Move both into helpers,
encodeShaderInfo/decodeShaderInfo and wrapEntry/unwrapEntry, so that a
second kind of entry can reuse them.

The bytes of a compiled variant entry do not change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:23:35 +02:00
Hussein Kadhim ac82f3c6a9 fix(libkernel): switch the host stack bounds in siglongjmp on Windows
longjmp moves the Windows stack bounds onto the stack of the target jump buffer so that exceptions and stack probes keep working after a jump between guest stacks. siglongjmp restored the same registers without that step, so a jump from a context stack back to the thread stack left the thread bounds describing the stack it had left.
2026-10-13 16:09:27 +00:00
hamb3r 8652dbe35b feat(libc): add missing Unity IL2CPP runtime imports and Dinkumware helpers
Add missing libc.prx symbols required by Unity IL2CPP runtime:
- Implement _Dtest double classification in Math.cpp
- Implement _Inf double constant in Math.cpp
- Implement sceLibcBacktraceGetBufferSize and sceLibcBacktraceSelf in Backtrace.cpp
- Implement _Cnd_signal in Threads.cpp
- Implement _ZNSt8ios_base7_AddstdEPS_ in RuntimeSupport.cpp
- Implement __pointer_type_info and __function_type_info vtables in TypeInfo.cpp
- Remove duplicate stubs from libSceAmpr/Export.cpp
- Add unit tests in GuestMath.cpp and GuestLibcThreads.cpp
- Document unverified behaviors in docs/dev/debt/libc.md
2026-10-13 19:01:30 +03:00
Adrià 0214069e33
Merge pull request #5263 from adriadam10/ci/mesa-cache-per-cpu
ci(build): key the Mesa shader cache by the runner's CPU flags
2026-10-13 17:58:02 +02:00
Adrià Franch 32accfa938 ci(build): key the Mesa shader cache by the runner's CPU flags
The cache from #5107 was keyed by Mesa version only, so a runner restored
llvmpipe code compiled on another runner's CPU and about 85 agc tests died
with SIGILL (Illegal instruction) on main.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 17:57:49 +02:00
NaitikJoshiPro 6f856a7cda feat(relinker): executable TLS relocations (TPOFF64, DTPOFF64, DTPMOD64)
A PS5 eboot can keep initial-exec TLS as R_X86_64_TPOFF64 GOT relocations
(SuperTuxKart, STKT00001, has 13, all without a symbol), which the
relocation allowlist rejected. Relocations against the executable's own
TLS block (no symbol, or a defined STT_TLS symbol folded into the addend)
now go to the Linux ELF as symbolless dynamic relocations that the
dynamic loader resolves against where it placed the block; one against an
import stays named and is typed STT_TLS. The Windows and macOS images
resolve TPOFF64 and DTPOFF64 statically against the block
AlignUp(p_memsz, p_align) below the thread pointer, the offset
WindowsTlsBuilder and MacOsImage use, and reject DTPMOD64 and imported
TLS. TPOFF32 stays unsupported.

New test executable_tls_relocations covers the Linux, Windows and macOS
outputs and the rejected cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:24:37 +05:30
Adrià 9d0a41d5a1
Merge pull request #4611 from EagleBearerYS/fix/libscenet-epoll-out
fix(libSceNet): sceNetEpollWait takes 2 as the OUT event
2026-10-13 17:51:30 +02:00
Adrià 34cee1ef15
Merge pull request #4608 from MiniRML/fix/relinker-icon-png-chunks
fix(relinker): validate icon PNG critical chunks and order
2026-10-13 17:50:39 +02:00
Adrià 3e8d0793b7
Merge pull request #4588 from Nixitov/feat/libkernel-host-hardware
feat(libkernel): report the host SoC and CPU temperature
2026-10-13 17:49:54 +02:00
MarcZX100 85c090d49d test(libkernel): verify sigwait preserves errno 2026-10-13 15:49:45 +00:00
Hussein Kadhim 13b4de565b fix(libkernel): return the error number from sigwait
FreeBSD's sigwait hands the error number back as its return value and leaves errno alone; the guest export returned -1 and set errno.

(cherry picked from commit b627f6c9443dd94a6392959f33e52d837ee4cae4)
2026-10-13 15:49:28 +00:00
Adrià 27477e5f47
Merge pull request #4583 from MiniRML/fix/relinker-section-name-address
fix(relinker): zero the unmapped section-name table address
2026-10-13 17:49:15 +02:00
Adrià 8b20708599
Merge pull request #4579 from MiniRML/fix/release-configured-windows-runtime
fix(release): find the configured Windows runtime
2026-10-13 17:48:48 +02:00
Adrià 9dd3c2d1a6
Merge pull request #4547 from codingreapermaps/feat/relinker-trace-imports
feat(relinker): add --trace-imports for Windows executables
2026-10-13 17:48:21 +02:00
Adrià 67e86d93d5
Merge pull request #4250 from shahulmubeen-a/perf/shader-byte-buffer-stores
perf(shader): store 8- and 16-bit buffer components as bytes
2026-10-13 17:47:54 +02:00
Adrià c7beafd21a
Merge pull request #5256 from adriadam10/fix/libc-acosf-nan-bits
fix(libc): choose the acosf NaN bits instead of the host libm
2026-10-13 17:46:36 +02:00
Adrià Franch 0265f33636 fix(libc): choose the acosf NaN bits instead of the host libm
FreeBSD msun e_acosf.c returns (x-x)/(x-x) for |x| > 1 and NaN inputs: the
x86 default NaN 0xffc00000, or the quieted input NaN. std::acos left it to the
host libm, and guest_math failed on the runner's glibc 2.39.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 17:46:18 +02:00
Adrià 091c136ba6
Merge pull request #5223 from Nixitov/feat/libc-float-math
feat(libc): implement the missing float rounding and classification exports
2026-10-13 17:43:06 +02:00
Adrià 6679168360
Merge pull request #5210 from ErenYeager1836/carry/libkernel-coredump-user-file
feat(libkernel): answer the coredump user-file attach calls
2026-10-13 17:42:50 +02:00
Adrià 5030fbb5cb
Merge pull request #5201 from ErenYeager1836/carry/libkernel_sys-chroot
feat(libkernel_sys): implement chroot with FreeBSD lookup semantics
2026-10-13 17:42:22 +02:00
Niko 0f7cd5b7ac fix(conventions): skip library test fixtures in the export checks
The duplicate-export and silent-stub rules treated every APS5_VABI
definition under core/libs/prx/ as a library export, including mocks
and callbacks in a library's tests/ directory. Those files are built
only into separate test executables, never into the library, so they
are not exports. PR #3533 failed CI because a sceKernelGetProcessTime
mock in libSceAudioOut/tests was reported as a duplicate of libkernel's
export. An sce* fixture with a stub-shaped body was also reported as a
silent stub, and a real export that shares a name with a fixture in
another library's tests/ was reported as a duplicate.

Skip files under a tests directory on both sides of the check, with the
same test code() already uses for title-specific and progress.py uses
for its function counts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:39:20 +02:00
Adrià fdefd76609
Merge pull request #4705 from karanvora2599/perf/shader-unary-input-flush-once
perf(shader): drop the input flush that rcp, rsq, sqrt, exp and log results never use
2026-10-13 17:38:07 +02:00
Adrià e6fae88a42
Merge pull request #4684 from XairRrR/fix/agc-acquire-without-waiting
fix(agc): drop a presentation instead of waiting for a swapchain image
2026-10-13 17:36:29 +02:00
Adrià 068ab2aba6
Merge pull request #4638 from MarcZX100/codex/carry-win-getdents-throw-3327
fix(libkernel): throw for oversized directory entries
2026-10-13 17:32:31 +02:00
Adrià 6629c09392
Merge pull request #4619 from MiniRML/fix/nid-version-requirement-chains
fix(nid): bound version requirement chains to their section
2026-10-13 17:31:25 +02:00
Adrià 3f2782cc7c
Merge pull request #4618 from Borooooo/fix/relinker-long-paths
fix(relinker): accept paths longer than 260 characters on Windows
2026-10-13 17:31:05 +02:00
Adrià 765661581b
Merge pull request #4613 from EagleBearerYS/fix/libscenet-copied-address-length
fix(libSceNet): report copied socket address lengths
2026-10-13 17:30:34 +02:00
Adrià c35bc4b92c
Merge pull request #4612 from EagleBearerYS/fix/libscenet-epoll-ident
fix(libSceNet): report the registered socket as the epoll event ident
2026-10-13 17:30:05 +02:00
Adrià 2f6ae9fc88
Merge pull request #4610 from EagleBearerYS/fix/libscenet-sce-socket-timeouts
fix(libSceNet): take 0x1105 and 0x1106 as the send and receive timeouts
2026-10-13 17:28:11 +02:00
Adrià 842766d39c
Merge pull request #4606 from Nixitov/feat/import-audit-stop
feat(tools): report each converted title's current stop in import_audit
2026-10-13 17:26:59 +02:00
Adrià 9c4686321b
Merge pull request #4604 from MiniRML/fix/nid-gnu-hash-geometry
fix(nid): validate GNU hash geometry before rewriting
2026-10-13 17:26:25 +02:00
Adrià 284cb63dc9
Merge pull request #4601 from MiniRML/fix/nid-pe-export-references
fix(nid): resolve PE reference exports through their ordinals
2026-10-13 17:26:06 +02:00
Adrià 39890031e3
Merge pull request #4591 from MiniRML/fix/oracle-input-rows
fix(oracle): validate input words before tool lookup
2026-10-13 17:25:30 +02:00
Adrià a56c905df6
Merge pull request #4589 from MiniRML/fix/tools-cut-helper-accounting
fix(tools): exclude cut helpers from NID accounting
2026-10-13 17:24:55 +02:00
Adrià 9c05bf25f5
Merge pull request #4585 from jagrat04/feat/libkernel-cpuset-affinity-errors-pr
feat(libkernel): return ESRCH and EINVAL for unsupported cpuset affinity targets
2026-10-13 17:23:01 +02:00
Adrià 5a76fd716f
Merge pull request #4575 from MiniRML/fix/progress-raw-string-literals
fix(progress): ignore C++ raw-string contents
2026-10-13 17:19:23 +02:00
Adrià 382c451acf
Merge pull request #4564 from MiniRML/fix/nid-windows-unicode-paths
fix(nid): preserve Unicode paths on Windows
2026-10-13 17:19:00 +02:00
Adrià c422b57fb3
Merge pull request #4672 from milescoviello/carry/lseek-pipe-windows-4670
fix(libkernel): fail lseek on a pipe with ESPIPE on Windows
2026-10-13 17:13:35 +02:00
Adrià 7e4edf509a
Merge pull request #4616 from EagleBearerYS/fix/libc-strtod-quiet-nan-windows
fix(libc): return the default quiet NaN from the strtod family
2026-10-13 17:11:55 +02:00
billybobpicklio d7818c9e55 fix(agc): accept integer tessellation partitioning
A draw with VGT_TF_PARAM.PARTITIONING 0 (PART_INTEGER) was refused by the
state decoder and its hull and domain programs by the recompiler. It is now
drawn with Vulkan's equal spacing, the mode radeonsi programs as
PART_INTEGER, with tessellation factors bounded at 64 instead of
fractional-odd's 63. Power-of-two and fractional-even partitioning still
throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:11:46 -04:00
Adrià cba01c87d1
Merge pull request #4614 from EagleBearerYS/fix/libscenet-getsockname-unbound-windows
fix(libSceNet): name an unbound socket with the zero address on Windows
2026-10-13 17:11:20 +02:00
Nixitov 00f0589101 feat(libc): implement the float rounding and classification exports
PPSA99611 imports ceilf, floorf, truncf, isnan and isinf from libc.prx and stops at start-up on main. Implement them next to the existing double forms: the rounding results delegate to the standard library like the other float exports, and isnan/isinf reuse the __isnan/__isinf helpers isnanf already uses.
2026-10-13 17:09:47 +02:00
Adrià 242bae7445
Merge pull request #4597 from MiniRML/fix/release-destination-preflight
fix(release): preflight asset destination types
2026-10-13 17:07:51 +02:00
Adrià 9510e71d6a
Merge pull request #5221 from adriadam10/ci/dispatch-without-linux
ci(build): let a dispatched build skip the Linux job
2026-10-13 17:05:09 +02:00
Adrià eb5bee4bfe
Merge pull request #4557 from saxopa/fix/relinker-linux-program-header-slots
fix(relinker): move the Linux program header table when it has no free slot
2026-10-13 17:04:41 +02:00
Adrià 8bf10e51f4
Merge pull request #4554 from queeeef/fix/agc-sampled-dcc-key-writers
fix(agc): wait for recorded writes over a sampled texture's DCC keys before reading them
2026-10-13 17:04:12 +02:00
Adrià Franch 71f98dd746 ci(build): let a dispatched build skip the Linux job
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 17:03:50 +02:00
Adrià d67a9e5a45
Merge pull request #4553 from Nixitov/feat/fchmodat
feat(libkernel_sys): implement fchmodat
2026-10-13 17:03:44 +02:00
Adrià 1a217cd4a3
Merge pull request #5204 from ErenYeager1836/carry/appcontent-addcont-enqueue-download
feat(libSceAppContent): implement sceAppContentAddcontEnqueueDownload
2026-10-13 17:02:04 +02:00
Adrià a74f2efe84
Merge pull request #5097 from TideOPL/fix/ancillary-layer-export
fix(shader): read the ancillary render target index from Layer when the draw exports it
2026-10-13 17:01:11 +02:00
Adrià Franch 1a2a4265c1 docs(debt): record the GS_FAST_LAUNCH decode assumptions 2026-10-13 17:00:51 +02:00
Lexroid 7370aaf6d0 feat(agc): decode GS_FAST_LAUNCH geometry with one point per subgroup
VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH = 1 (mesh-style NGG, as RADV programs
it on gfx10.3) was refused. With a point list and GE_CNTL groups of one
point, it launches one subgroup per input point and the first thread
reads that point's index in the ES vertex id VGPR, which the mesh
prologue already provides. The subgroup's primitive limit comes from
GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR, and the GS and ES VGPR counts are
not required to be full.

Fast launch 2, other group sizes or inputs, and indexed fast-launch
draws still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:00:45 +02:00
adriadam10 bc568a1f6f fix(input): MouseModeEnabled reads the keyboard/mouse state
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 17:00:28 +02:00
AlxcBTW 38bc0fd348 feat(input): turn mouse speed into a smooth right-stick tilt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:00:20 +02:00
Adrià 3b94f923a5
Merge pull request #4633 from QRMOE/fix-libc-wcsxfrm-truncation
fix(libc): terminate truncated wcsxfrm results
2026-10-13 17:00:13 +02:00
Adrià fffe7351ad
Merge pull request #4625 from QRMOE/fix-libc-strtok-exhausted-state
fix(libc): clear exhausted strtok continuation state
2026-10-13 16:59:43 +02:00
Adrià 72a8e67122
Merge pull request #4621 from MiniRML/fix/nid-elf-shared-string-roles
fix(nid): preserve each ELF string consumer independently
2026-10-13 16:59:13 +02:00
codingreapermaps 430e5c833e feat(relinker): add --trace-imports for Windows executables
With --trace-imports the entry stub resolves TraceImport_nid_no_patch
in libc.prx and passes each function import through it before writing
the slot: every PLT slot, and a GOT slot only when the original symbol
is a function, so data imports such as __stdoutp keep their address.
Without the option the output is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:55:31 +02:00
Dean Galvin 0adbdcf45c perf(agc): cache a draw key the second time it is seen
A draw whose key has no entry captured its stages, built a variant per
stage (a copy of every captured region) and inserted a draw entry. A key
that never comes back paid for the variants and the entry for nothing,
and its entry pushed a live one out of the cache. Draws whose user data
changes every frame are such keys.

The driver now remembers the keys it has seen once, in a table of
four-way buckets holding four times the cache's size in keys. A key gets
its entry at its second sight; a key that already has an entry still
takes new variants at once. A full bucket replaces its oldest key, so
keys that share a bucket do not push each other out until more than four
of them come between two sights of one.

Test: agc_driver_draw_cache_reuse draws three times. The first two draws
find no entry, one entry is inserted, and the third draw hits it. A
sighting table of one bucket keeps two keys drawn in turn and replaces
its oldest key when full.
2026-10-14 03:53:28 +13:00
Adrià 60fb618708
Merge pull request #4551 from eljackjoestar-cmd/codex/preserve-color-slice-xor
fix(agc): preserve array slice XOR in color target views
2026-10-13 16:52:35 +02:00
Adrià c496fd530f
Merge pull request #4546 from codingreapermaps/feat/libc-call-trace-runtime
feat(libc): record traced import calls on Windows
2026-10-13 16:51:55 +02:00
Nixitov a3a3f3a9ce feat(tools): report each converted title's current stop in import_audit
The stop is the first unresolved import in the registry order, the line the loader fails on. Print it per registry and add it to the --json result.
2026-10-13 16:50:51 +02:00
Adrià b8683834f3
Merge pull request #4596 from MiniRML/fix/oracle-target-override
fix(oracle): honor the current target override
2026-10-13 16:48:06 +02:00
Adrià f2c314e902
Merge pull request #4577 from Moaziz667/fix/prime-remaining-host-mutexes
fix(agc,shader): prime the remaining host shared mutexes
2026-10-13 16:45:17 +02:00
Rick 8ea8ea386a fix(libSceKeyboard): report one key slot when no key is held
A keyboard record on the console never counts fewer than one key: with
nothing held, and with no keyboard connected, the count is 1 and that
slot holds code 0. SharpProspero's keyboard reader, PS5_Quake3e's notes
on records read from a console and the PS5 input research notes all say
so. AnyPS5 reported a count of 0. The count is now at least 1; the key
slots themselves are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 16:44:17 +02:00
Adrià 2f5c3a4bf5
Merge pull request #4559 from MarcZX100/codex/import-audit-sectionless-modules
fix(tools): audit sectionless guest modules
2026-10-13 16:43:53 +02:00
Adrià b4d76ba609
Merge pull request #4556 from Amoenus/codex/fix-inactive-covered-centroid
fix(agc): admit inactive covered-centroid mode
2026-10-13 16:43:05 +02:00
screamm 204355cc64
feat(libkernel): answer the coredump user-file attach calls
sceCoredumpAttachUserFile (5nc2gdLNsok) and
sceCoredumpAttachMemoryRegionAsUserFile (MEJ7tc7ThwM) get the
signatures of Orbital's db_types.json (cc408367) and SharpProspero's
KernelSystem.cs (5dfdb4b0): (uint32_t userValue, const char* path) and
(uint32_t userValue, const void* mem, size_t size, const char* name).

The handler is never invoked here, so both return
SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER (0x81180003, shadPS4
coredump_error.h e0a46675) and attach nothing; a null path or name and
a null, empty or wrapping region throw because the order of the
console's checks is unknown.

Carried from @screamm with author attribution preserved.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 20:11:39 +05:30
Adrià ffa2a6ecd3
Merge pull request #4550 from MiniRML/fix/nid-buffer-validation
fix(nid): validate buffer ranges and string terminators
2026-10-13 16:41:04 +02:00
SandstromPL 78f620cf9e fix(relinker): keep code analysis inside the code extent of PS4 executables
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:10:51 +05:30
Adrià 69b4ca5d9b
Merge pull request #4215 from Mouad-Scriptz-0/fix/agc-flip-eop-event
fix(libSceAgcDriver): raise the graphics end-of-pipe event from flip packets
2026-10-13 16:39:04 +02:00
MiniRML bfffd3359c fix(relinker): validate icon PNG critical chunks and order 2026-10-13 16:38:28 +02:00
Nesyth 417ea99df5 fix(libkernel): zero-fill APR reads that reach the end of the file 2026-10-13 16:38:17 +02:00
Zenodeon b6c224af45 fix(relinker): keep diagnostics compatible with pending refactors 2026-10-13 18:37:50 +04:00
Zenodeon 36c11dc926 fix(relinker): identify failed inputs and invalid dynamic values 2026-10-13 18:37:50 +04:00
JJenkx f7fd7569fa fix(agc): retire fully unmapped multisample targets 2026-10-13 10:37:07 -04:00
Shahul Mubeen A 066a8fbe30 perf(shader): store 8- and 16-bit buffer components as bytes in compute
A formatted store of an 8- or 16-bit component into a bound buffer
patched the dword around it with a compare-exchange loop (writeBuffer
in SpirvRuntimeBuffer.cpp). Ghost of Yotei's light-culling compute
pass (program 0x800038be00, 240x135 groups per frame) stores millions
of such components per dispatch and took 426 ms of GPU time per
dispatch on an RTX 4070 Ti SUPER.

Compute stores now go through a byte view of the bound buffers
(buffers_u8, an aliased declaration of the buffers binding, defined
on the first narrow store): one OpStore per byte under
StorageBuffer8BitAccess, which the shader device profile already
requires. Without the capability, and in every other stage, the
compare-exchange path remains: the draw-time validator accepts only
u32 and u64 aliased views. Loads, dword stores and stores through
GPU-selected descriptors (BDA) are unchanged.

Ghost of Yotei (PPSA26344 02.002.000), Windows 11, RTX 4070 Ti SUPER,
logo phase: that dispatch 426 -> 10.7 ms of GPU time, 1.9 -> 11 fps.

agc_formatted_buffer_byte_stores compiles a formatted 8_8_8_8 store
as compute with and without StorageBuffer8BitAccess and as a vertex
shader with it, and checks the byte view, the byte stores, the
compare-exchange loops and the fallbacks. agc_driver_formatted_store_unaligned
executes 8- and 16-bit formatted stores at misaligned offsets,
including stores that cross a dword, and checks every byte of the
output including the untouched neighbours.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 16:36:56 +02:00
Adrià e0d388fa28
Merge pull request #4555 from Xabilimon1/fix/macos-build-libkernel-getrlimit
fix(libkernel): build the fcntl status flags and the getrlimit test on macOS
2026-10-13 16:36:38 +02:00
Adrià Franch c6b1f84a7e docs(debt): record sceAppContentAddcontEnqueueDownload behaviour as unverified 2026-10-13 16:33:51 +02:00
David fdfe2204aa feat(libSceAppContent): implement sceAppContentAddcontEnqueueDownload
The signature (uint32_t service label, const NpUnifiedEntitlementLabel*)
follows SharpProspero Interop/AppContent/AppContent.cs (5dfdb4b0). dlc_emu
dlc_content.cpp (fcf69b16) answers NOT_FOUND for a label that is not a
known add-on and PARAMETER for a null label. No add-on content is ever
installed here, as sceAppContentAddcontMount and sceAppContentGetAddcontInfo
already model, so every non-null label answers NOT_FOUND and nothing is
queued. dlc_emu's nonzero-padding check is left out, as sibling functions
do not check it and no console source confirms it.

Carried from @screamm with author attribution preserved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:33:45 +02:00
Adrià b478fbbb1c
Merge pull request #5191 from p3n1cillin/claude/carry-chroot
feat(libkernel_sys): add chroot that keeps the guest root
2026-10-13 16:33:00 +02:00
Adrià 9dac817b19
Merge pull request #4548 from coltken95/fix/libc-printf-nan
fix(libc): print NaN without a sign in the guest printf formatter
2026-10-13 16:31:34 +02:00
Adrià c02df171c8
Merge pull request #4545 from thuanquang/test/tools-python39-hard-links
test(tools): create hard links with os.link for Python 3.9
2026-10-13 16:30:44 +02:00
Adrià 2672a73178
Merge pull request #4501 from devavijitsingh/fix/diagnose-self-module-skip
fix(relinker): warn when SELF modules are skipped
2026-10-13 16:30:12 +02:00
Nixitov 0f73f5f057 feat(libkernel): report the host SoC and CPU temperature
PPSA99058 and PPSA99420 import sceKernelGetCpuTemperature and sceKernelGetSocSensorTemperature and stop at start-up on main. Read the host CPU/package, board and NVMe sensors (hwmon/thermal on Linux, the ACPI thermal zones and the storage temperature property on Windows) and report an unsupported sensor with EINVAL.
2026-10-13 16:30:07 +02:00
Adrià 80e70b4292
Merge pull request #4408 from santhoshkumar-dev/codex/agc-render-target-vertex-aliasing
fix(agc): exclude skipped vertex records from target alias checks
2026-10-13 16:29:52 +02:00
Adrià b0d41be1ff
Merge pull request #4283 from MarcZX100/codex/carry-guest-thread-stacks-3283
fix(libkernel): place Linux guest thread stacks inside application area
2026-10-13 16:28:18 +02:00
MiniRML a8a78ded96 fix(progress): load the tokenizer beside the source file 2026-10-13 16:27:51 +02:00
MiniRML f37eecfdac fix(progress): ignore C++ raw-string contents 2026-10-13 16:27:51 +02:00
Adrià ef42c5f2af
Merge pull request #5183 from p3n1cillin/claude/carry-clock-settime
feat(libkernel): add clock_settime with FreeBSD argument checks
2026-10-13 16:27:05 +02:00
eljackjoestar-cmd 13818efc3b fix(agc): preserve array slice XOR in color target views 2026-10-13 16:25:05 +02:00
screamm f8a269e80a
feat(libkernel_sys): let chroot keep the guest root
Porpoise (PPSA99764) stops on its weak chroot import (#3632, #4497).
Run 5 on main c2565f014, and again on a copy of that runtime:

  FAIL: unresolved ELF import AKknh8Cx-V8 from libkernel_sys.prx

Its only call is in put_root_back (src/porpoise_jailbreak.cpp,
elripalda/Porpoise-Dolphin-Emulator-for-PS5 2ed1ab5a06995aa1): after a
jailbreak daemon has made the console's root its root and /app0 is
gone, it calls fchdir on the descriptor of "/" it opened at start, then
chroot("."), and checks the result. So it only ever puts back the root
it started in, and it does not use paths under any other root.

On top of the lookup errors of the previous commit, chroot now follows
FreeBSD 11.4 sys_chroot and pwd_chroot (8c639945, vfs_syscalls.c and
kern_descrip.c) for that case: a directory that is the guest root
("/", "/..", "." after fchdir to "/") succeeds without a change, since
fd_rdir would be set to the vnode it already is. The guest is taken
never to have changed its root, so with chroot_allow_open_directories 1
an open directory does not give EPERM. ENAMETOOLONG is added as in
pathconf, and a ".." through a file reports ENOTDIR instead of
escaping as a filesystem_error. Any other directory still throws: the
resolver's root and mount aliases are fixed, and no title is known that
needs a real new root.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 19:54:29 +05:30
screamm 7e5bec09f8
feat(libkernel_sys): add chroot with FreeBSD lookup errors
Porpoise (PPSA99764) imports chroot (AKknh8Cx-V8) from
libkernel_sys.prx, not libkernel.prx, one reference. Predicted stop
once pathconf, the pthread_attr calls and ftrylockfile ahead of it are
covered (not a run):

  FAIL: unresolved ELF import AKknh8Cx-V8 from libkernel_sys.prx

The NID is computed with tools/nid_names.py and matches aerolib.

Follows FreeBSD 11.4 sys/kern/vfs_syscalls.c sys_chroot and change_dir
(8c639945a44588f6cfdddfb057bb5aad88735da0) for a root caller up to the
lookup: EFAULT for a null path, ENOENT for an empty or missing one,
ENOTDIR for a file or a path through a file. A directory that is found
throws with the path named: the resolver's root is fixed and the mount
aliases (/app0, /savedata0) are host paths that do not move with it,
so it cannot be re-based, and a real chroot is not modelled. Nothing
shows that Porpoise calls it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 19:54:29 +05:30
TideOPL de5279bc0c fix(shader): serialize the pixel layer export flag from request version 23
main allocates request version 22 to the tessellation patches per group, so a version 22 request has no layer export byte.
2026-10-13 15:23:21 +01:00
Adrià 146f171549
Merge pull request #4271 from eljackjoestar-cmd/codex/reclaim-source-ir
perf(shader): reclaim unreachable cached source IR values
2026-10-13 16:22:26 +02:00
saxopa b3fa9d8318 fix(relinker): move the Linux program header table when it has no free slot
Linux output reused the input program header table in place, so it could
hold no more entries than the input declared. A title whose kept segments
and the five synthetic headers did not fit lost PT_GNU_EH_FRAME with a
warning, or failed with "Not enough program header slots". When more
headers are needed, the table is now written at the end of the extra block,
e_phoff points there and PT_PHDR maps it through the extra block PT_LOAD;
the header block then maps only the ELF header. Bytes after the input table
are left as they were.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:20:00 +02:00
Daan Dehing a910dee09f fix(agc): wait for recorded writes over a sampled texture's DCC keys before reading them
textureClearKeys reads the keys through a raw pointer, past the flush hook, so writes the
recorder holds for the key range are not in the bytes yet. A title that clears the keys of a
target with a kernel and samples it in the same batch had its keys read from the memory's
previous contents: in Palworld (PPSA23126) these were the float data of a reused heap, so the
keys read as mixed and the texels were read as stored.

When the keys read do not decode (not uncompressed, not a clear the format can fill),
WaitForKeyWriters answers from the pending fills and stores it knows, or waits for the batch
that writes the keys, and the keys are read again. Keys that decode are used as before.

RunSampledKeysBehindKernel (agc_driver_dcc_keys_first_write): key memory holding float bytes,
then the 0000 clear kernel recorded without a wait; the sampled texture's keys read Clear0000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:18:50 +02:00
Adrià 759c66564f
Merge pull request #4428 from hypermoist/perf/address-space-import-runs
perf(agc): keep adjacent ranges one import serves as one region of the cached address space
2026-10-13 16:18:14 +02:00
Alexej Szpevák fc8165e8b9
test(cli): cover module-dir, sce-module-path and target option errors
Add parser tests for --module-dir, --sce-module-path, --macos and
--to-rosetta, including their repeat and conflict errors, which had no
coverage.
2026-10-13 16:17:23 +02:00
TideOPL 7015c0443e fix(shader): read the ancillary render target index from Layer when the draw exports it 2026-10-13 15:15:55 +01:00
Adrià 5d7f1ffc1c
Merge pull request #4217 from Mouad-Scriptz-0/feat/videoout-mouse-capture
feat(libSceVideoOut): capture the host cursor for guest mice and scale their deltas
2026-10-13 16:15:45 +02:00
Adrià ac6d1b2013
Merge pull request #4163 from Celegans12/feat/agc-tessellation-patch-inputs
feat(agc): read hull patch constants in tessellation domain programs
2026-10-13 16:15:23 +02:00
Adrià Franch 603d94a53f test(libkernel_sys): reuse longName in the fchmodat check 2026-10-13 16:14:56 +02:00
Nixitov e3c1d01815 feat(libkernel_sys): implement fchmodat
2 Ship 2 Harkinian (PPSA99621) imports fchmodat through std::filesystem::permissions and stops at start-up on main. Resolve AT_FDCWD and absolute paths as usual and join other relative paths to the directory descriptor like openat, accept 0 and AT_SYMLINK_NOFOLLOW, and mask the mode to 07777.
2026-10-13 16:14:53 +02:00
codingreapermaps 3953a8d49e feat(libc): record traced import calls on Windows
TraceImport_nid_no_patch wraps a function of a library named in
APS5_TRACE_CALLS in a thunk that records the import, the thread and
the caller in a ring and counts the call, then jumps to the function.
One target keeps one thunk, so function pointers compare equal. The
crash and abort reports print the last 64 calls and the 32 most called
imports. Nothing calls TraceImport_nid_no_patch yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:13:12 +02:00
Adrià 8c772ad342
Merge pull request #4265 from fisherjoey/fix/fiber-host-stack
fix(libSceFiber): run fibers on host stacks with room for host calls
2026-10-13 16:12:14 +02:00
Adrià ab1755c258
Merge pull request #4459 from LeandroLP23/docs/contributing-gpus-tested
docs(contributing): ask GPU changes to name the GPUs their tests ran on
2026-10-13 16:11:54 +02:00
screamm 41b0ca597b feat(libkernel_sys): let chroot keep the guest root
Porpoise (PPSA99764) stops on its weak chroot import (#3632, #4497).
Run 5 on main c2565f014, and again on a copy of that runtime:

  FAIL: unresolved ELF import AKknh8Cx-V8 from libkernel_sys.prx

Its only call is in put_root_back (src/porpoise_jailbreak.cpp,
elripalda/Porpoise-Dolphin-Emulator-for-PS5 2ed1ab5a06995aa1): after a
jailbreak daemon has made the console's root its root and /app0 is
gone, it calls fchdir on the descriptor of "/" it opened at start, then
chroot("."), and checks the result. So it only ever puts back the root
it started in, and it does not use paths under any other root.

On top of the lookup errors of the previous commit, chroot now follows
FreeBSD 11.4 sys_chroot and pwd_chroot (8c639945, vfs_syscalls.c and
kern_descrip.c) for that case: a directory that is the guest root
("/", "/..", "." after fchdir to "/") succeeds without a change, since
fd_rdir would be set to the vnode it already is. The guest is taken
never to have changed its root, so with chroot_allow_open_directories 1
an open directory does not give EPERM. ENAMETOOLONG is added as in
pathconf, and a ".." through a file reports ENOTDIR instead of
escaping as a filesystem_error. Any other directory still throws: the
resolver's root and mount aliases are fixed, and no title is known that
needs a real new root.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:11:08 +01:00
screamm dc0d316fda feat(libkernel_sys): add chroot with FreeBSD lookup errors
Porpoise (PPSA99764) imports chroot (AKknh8Cx-V8) from
libkernel_sys.prx, not libkernel.prx, one reference. Predicted stop
once pathconf, the pthread_attr calls and ftrylockfile ahead of it are
covered (not a run):

  FAIL: unresolved ELF import AKknh8Cx-V8 from libkernel_sys.prx

The NID is computed with tools/nid_names.py and matches aerolib.

Follows FreeBSD 11.4 sys/kern/vfs_syscalls.c sys_chroot and change_dir
(8c639945a44588f6cfdddfb057bb5aad88735da0) for a root caller up to the
lookup: EFAULT for a null path, ENOENT for an empty or missing one,
ENOTDIR for a file or a path through a file. A directory that is found
throws with the path named: the resolver's root is fixed and the mount
aliases (/app0, /savedata0) are host paths that do not move with it,
so it cannot be re-based, and a real chroot is not modelled. Nothing
shows that Porpoise calls it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:11:08 +01:00
Yunus Shaikh 6a46563a93 refactor(savedata): clear the mount result once the mount point is made
#5173 changes the create condition right after the last error check, and #3950 removes the comment after the create block, so clearing the result in either place made the pull requests conflict. The result is still cleared only after every error return and before anything is written to it.
2026-10-13 07:10:41 -07:00
Adrià 91ed4ce625
Merge pull request #5174 from AkulTyagi11/feat/libc-implement-powidf2
feat(libc): implement __powidf2
2026-10-13 16:09:23 +02:00
Mouad-Scriptz-0 94087d08b0 feat(libSceVideoOut): capture the host cursor for guest mice and scale their deltas
The console's mouse reports relative motion, so the host cursor can never line
up with the cursor a title draws. While a title has a mouse open and the
window is focused the host cursor is hidden and captured, released on focus
loss or while the pad's mouse-to-stick toggle is on. Motion deltas are scaled
from the presented image's size in window units to the title's display
resolution with sub-pixel carry, so the title's cursor travels as far as the
host pointer would, on scaled desktops and letterboxed windows alike.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 14:05:52 +00:00
Tomás Ortellado f0da9edbfe fix(relinker): reject overflowing LSDA landing pad addresses 2026-10-13 11:05:22 -03:00
Adrià d0711248b1
Merge pull request #4279 from karanvora2599/fix/shader-coherent-gds
fix(shader): declare the GDS buffer coherent
2026-10-13 16:04:55 +02:00
Adrià Franch 745857448e test(libkernel): check the host-only stack guard on Linux only
Windows guest threads keep their normal stacks, so there is no host-only guard page below the guest stack to query.
2026-10-13 16:03:39 +02:00
MarcZX100 9b6e7f66c4 fix(libkernel): protect host-only guest stack pages 2026-10-13 16:03:36 +02:00
Cesar Rodriguez 7637cac13e fix(libkernel): place guest thread stacks inside the application area
On Linux, glibc maps the stack of every guest thread near the top of the
host address space (around 0x7fff00000000). On the console, application
stacks lie inside the application area below 0xFC00000000, and guest code
relies on that: ChakraCore's garbage collector indexes a 4 MiB table with
address >> 18, so it only covers addresses below 1 TiB and reports
Js::OutOfMemoryException for a stack above it.

Map each guest thread's stack with DoMapAnon, which allocates from the
guest arena, behind a PROT_NONE guard page, and pass it to pthread_attr_setstack.
The stack is freed after a join; the stack of a detached thread is freed once its
host thread is gone from /proc/self/task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:03:36 +02:00
Adrià e0263e2b87
Merge pull request #5065 from MarcZX100/codex/cxa-thread-atexit
fix(libc): export __cxa_thread_atexit
2026-10-13 16:03:19 +02:00
milescoviello 0d30059bf5 fix(libkernel): handle FIOCLEX and FIONCLEX in ioctl
ioctl() on a file, directory, pipe or character device threw "request
0x20006601 ... is not implemented" for FIOCLEX, and the same for
FIONCLEX, although the close-on-exec flag they change is already kept
for these descriptors and reported by fcntl(F_GETFD).

FreeBSD 11.4 handles both requests in kern_ioctl() for every descriptor,
before the request reaches the file's own ioctl, by changing the flag
F_GETFD reads. sys/kern/sys_generic.c, kern_ioctl():

	case FIONCLEX:
		fdp->fd_ofiles[fd].fde_flags &= ~UF_EXCLOSE;
		goto out;
	case FIOCLEX:
		fdp->fd_ofiles[fd].fde_flags |= UF_EXCLOSE;
		goto out;

sys/kern/kern_descrip.c, kern_fcntl():

	case F_GETFD:
	...
			td->td_retval[0] =
			    (fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0;

Both requests now set or clear that flag and return 0. libuv before 1.44
sets close-on-exec this way on FreeBSD (uv__cloexec_ioctl).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:59:42 +01:00
nabilfp ae9978a98e feat(libkernel): add clock_settime with FreeBSD argument checks
PS5MC (PPSA99420, Kodi 22) imports clock_settime (d7nUj1LOdDU) from
libkernel.prx, one reference, after sigwaitinfo in its import order.
Predicted stop once the imports ahead of it are covered (not a run):

  FAIL: unresolved ELF import d7nUj1LOdDU from libkernel.prx

The NID is computed with tools/nid_names.py and matches aerolib.

Follows FreeBSD 11.4 sys/kern/kern_time.c sys_clock_settime and
kern_clock_settime (8c639945a44588f6cfdddfb057bb5aad88735da0) for a
root caller: EFAULT for a null time (copyin), EINVAL for any clock but
CLOCK_REALTIME and for a negative tv_sec or a tv_nsec outside 0 to
999999999. A valid CLOCK_REALTIME request throws with the requested
time named, since the host clock must not be set and no guest clock
offset is kept.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Co-authored-by: David <david@example.com>
2026-10-13 14:57:46 +01:00
p3nicillin 72a685b906 feat(libkernel): report _SC_CLK_TCK through sysconf
The Python in PS5MC (PPSA99420) reads sysconf(_SC_CLK_TCK) while
importing its posix module and failed with "cannot read
ticks_per_second". It returns FreeBSD's CLK_TCK, 128.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:53:24 +01:00
Mouad-Scriptz-0 326e0bc29f fix(agc): raise the graphics end-of-pipe event when a flip completes
A flip packet now delivers the queue's end-of-pipe event once the work
recorded before it completes, as a RELEASE_MEM with INT_SEL does. Minecraft
(PPSA17221) registers the graphics event, never requests an interrupt in its
releases, and waits on the event after every flip while holding its render
lock, so it presented one frame and then waited forever.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:49:45 +02:00
Adrià Franch 7a8e1b2559 docs(debt): restore UTF-8 characters corrupted in shader.md
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:48:15 +02:00
eljackjoestar-cmd 57b2dc8584 perf(shader): reclaim unreachable cached source IR values 2026-10-13 15:48:04 +02:00
Adrià e152ba8f69
Merge pull request #4209 from karanvora2599/fix/shader-cube-gradients
fix(shader): keep cube image gradients out of the face coordinate bias
2026-10-13 15:45:43 +02:00
Adrià 6cd052848a
Merge pull request #4210 from karanvora2599/fix/agc-depth-before-shader
fix(agc): test depth before pixel shaders that ask for it
2026-10-13 15:44:24 +02:00
Karan Vora 0998eb4f23 fix(shader): declare the GDS buffer coherent
GDS is one memory for the whole GPU on AMD hardware: every work-group's
ds_read and ds_write with gds see the others' writes, with no cache in
between. The recompiler keeps it in a storage buffer that was not decorated
Coherent, so on a GPU whose L1 caches storage buffer loads, NVIDIA's from
Turing on among them, a plain GDS read could return a value cached before
another work-group's GDS write. Atomics were not affected. The gds buffer is
now declared Coherent.

verifyCoherentGds in agc_shader_memory compiles a ds_read_b32 and a
ds_write_b32 on GDS and checks that the gds buffer is Coherent.
2026-10-13 15:43:31 +02:00
Adrià 2cf23fcc56
Merge pull request #4114 from Celegans12/fix/agc-tessellation-num-patches
fix(agc): bound tessellation patch records by the draw's patch count
2026-10-13 15:40:48 +02:00
Adrià 6c0f83c7cf
Merge pull request #4211 from karanvora2599/perf/shader-bindless-table-once
perf(shader): search a bindless image table once per access
2026-10-13 15:39:50 +02:00
Adrià Franch 36eb627051 fix(libSceFiber): throw when a fiber's host stack cannot be set up
No fallback to the title's context: a failed mmap or mprotect throws,
as on Windows.
2026-10-13 15:37:30 +02:00
fdfawef324 81364388a0 fix(libSceFiber): run fibers on host stacks on Windows
#4265 gives each fiber an mmap'd host stack on Linux and leaves Windows
as it was: there a fiber still runs host calls inside the context the
title passed to sceFiberInitialize. Demon's Souls packs its 16 KiB job
fiber contexts back to back. A job that calls
sceAgcCreateInterpolantMapping reaches shader preparation on the fiber
(Driver::ResolveShaderAbi, PrepareRegistered, the RDNA decoder,
BuildShaderStageInputInfo), goes more than 16 KiB deep and overwrites
the frames at the top of the context below it. On the loading screen
after the opening cinematic the fiber that owns that context then
failed the title's stack check, or returned to 0x7fffffff.

On Windows each fiber now gets a host stack from VirtualAlloc, the
context size plus 1 MiB, committed like the 1 MiB host margin of guest
threads, with a PAGE_NOACCESS page at the bottom. While the fiber runs,
the TIB stack base and limit and the deallocation stack describe that
host stack, as Windows does for its own fibers, so stack probes and
GetCurrentThreadStackLimits see the stack the fiber runs on. A host
stack that cannot be allocated, protected or released throws instead
of falling back to the context.

agc_driver_gpu_map_on_fiber: GetCurrentThreadStackLimits on a fiber now
returns its host stack, which has room for the import, so on Windows a
GPU mapping made on a fiber imports on the fiber's stack instead of a
host thread. The fiber's context is still not written.

Test: on Windows, fiber checks that the TIB stack bounds inside a fiber
cover its host stack, at least the context plus 256 KiB. With #4265
alone it fails at "the thread's stack bounds are the fiber's host
stack", and without that check at "a host call from a 16 KiB fiber
writes nothing below its context"; with this change fiber and
agc_driver_gpu_map_on_fiber pass (Windows Server 2025, RTX 3060,
WinLibs GCC 15.2.0, Release).

Demon's Souls (PPSA01342) on that host: at the loading screen the two
16 KiB job fibers above the overwritten one now use 17,592 and 16,936
bytes of their host stacks, the title's stack check no longer fails
there, and the load goes on until a separate Vulkan allocation failure
(vkAllocateMemory of a 4 MiB host-visible buffer, out of device memory).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:37:23 +02:00
Joey Fisher 794714983c fix(libSceFiber): run fibers on host stacks with room for host calls
Host code that a guest fiber calls runs on the fiber's stack. Demon's
Souls (PPSA01341) gives its job fibers 16 KiB contexts packed next to each
other, and a fiber that reaches shader preparation through
Driver::ResolveGraphicsStagesAbi goes about 21 KiB deep when SPIRV-Tools
validates the module. That overwrites the saved frame of the fiber below,
and the next switch to that fiber pops garbage into rbp, rbx, r12-r15 and
rip (a jump to 0x1a in the observed crash).

Fibers now run on an mmap'd host stack of the context size plus 1 MiB,
with a guard page at the bottom, the same margin guest threads get since
#2647. The guest context is no longer written. Context size checks fill
and scan the top context-size bytes of the host stack, so
size_context_margin keeps its meaning. If the mapping fails the fiber
runs in its context as before. Windows is unchanged: it pins and
publishes the guest context as the stack, and that path is not covered
here.

The fiber test now runs a 256 KiB host call from a 16 KiB fiber placed at
the top of a guard-filled area and checks nothing below the context was
written. It fails on main.
2026-10-13 15:37:23 +02:00
Adrià 58f7242ea2
Merge pull request #3934 from MarcZX100/codex/carry-setlocale-category
fix(libc): reject invalid setlocale categories
2026-10-13 15:35:56 +02:00
Adrià 3a60a0d054
Merge pull request #4011 from karanvora2599/perf/shader-f16-compare-any-nan
perf(shader): skip the f16 NaN rebuild for operands that are only compared
2026-10-13 15:34:02 +02:00
Adrià Franch 1ab578a3f7 test(libc): allow the __cxa_thread_atexit export to call the MinGW registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:33:28 +02:00
Adrià Franch 9c383e3295 fix(libc): ignore the guest DSO of thread atexit on Windows in the shared bridge
MinGW's __cxa_thread_atexit asserts dso is null or its own __dso_handle, so every guest export that forwards a guest module pointer needs the null rule, not only LibcInternalExtCxaThreadAtexit. The test now passes a non-host DSO on Windows too.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:33:22 +02:00
MarcZX100 90c64af925 fix(libc): export __cxa_thread_atexit 2026-10-13 15:33:22 +02:00
Adrià 0c05d3cfc7
Merge pull request #4105 from MarcZX100/codex/carry-execv-execve-3558
feat(libkernel): add execve as a throwing export
2026-10-13 15:31:29 +02:00
Akul Tyagi b4e2ee0a13 feat(libc): implement __powidf2
The H+8UBOwfScI import was a throwing stub (libcCyberUnknown08). It is
the NID of "__powidf2", the double-precision integer-power helper that
complements __powisf2.
2026-10-13 19:00:24 +05:30
Adrià 5b08344585
Merge pull request #3907 from GalaxyGamingFR/fix/progress-read-encoding
fix(progress): read cmake and shader tables as utf-8
2026-10-13 15:27:02 +02:00
Adrià Franch 3973e5ad77 docs(technical-debt): record the hardware measurement of cube image gradients
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:26:05 +02:00
Karan Vora 73eaa1f18b fix(shader): keep cube image gradients out of the face coordinate bias
image_sample_d and image_sample_cd on a cube pass their gradients through the
same conversion as the face coordinates, which subtracts 1.0 to move AMD's
[1, 2] face range to [0, 1]. A gradient is a difference of coordinates, so the
bias turned a one-texel gradient into one of about -1 and the sample read the
smallest mip. Gradients now skip the bias.

agc_driver_image_sample_cube_derivatives samples every face of a 16x16 cube at
five levels through image_sample_d and checks the level and face it read.

TechnicalDebt records that the gradient handling follows Mesa and was not
measured on hardware.
2026-10-13 15:26:04 +02:00
Adrià e22a77eba2
Merge pull request #3901 from GalaxyGamingFR/fix/conventions-agent-file-case
fix(conventions): match agent instruction files case-insensitively
2026-10-13 15:26:01 +02:00
Adrià 3faa9cb400
Merge pull request #5103 from ErenYeager1836/carry/audioout-enable-chat
feat(libSceAudioOut): implement sceAudioOut2EnableChat
2026-10-13 15:22:57 +02:00
Adrià a252fcd75f
Merge pull request #4012 from queeeef/perf/agc-zero-dword-fill
perf(agc): handle the zero dword fill kernel as a buffer fill
2026-10-13 15:21:00 +02:00
Adrià Franch b81ababfc6 docs(technical-debt): record DEPTH_BEFORE_SHADER in debt/libSceAgcDriver.md 2026-10-13 15:19:51 +02:00
Karan Vora 320bd37c92 fix(agc): test depth before pixel shaders that ask for it
DB_SHADER_CONTROL.DEPTH_BEFORE_SHADER (bit 12) is what a pixel shader compiled
for early depth/stencil sets: the depth and stencil tests and writes happen
before the shader runs, even when it kills pixels or writes memory. The bit
was accepted but never read, so such shaders got EarlyFragmentTests only when
Z_ORDER was EARLY_Z_THEN_LATE_Z and they had no kill or exports. Their killed
pixels then left depth unwritten, and occluded pixels still wrote memory.
EARLY_Z_THEN_RE_Z (Z_ORDER 3) was not treated as early either.

DecodePixelStageInfo now sets earlyZ for DEPTH_BEFORE_SHADER, and for Z_ORDER
1 or 3 under the existing kill and export conditions, including the stencil
reference export. The backend no longer repeats those conditions; it keeps
only the alpha-to-coverage one, so a shader with alpha-to-coverage still runs
its tests late. earlyZ keeps its place in the request, so the serialization
version does not change.

earlyFragmentTestsTests in agc_driver_graphics checks which DB_SHADER_CONTROL
values declare EarlyFragmentTests, and that alpha-to-coverage keeps Z_ORDER 1
and DEPTH_BEFORE_SHADER late, directly and after a serialization round trip.
stencilRefExportTests checks that DEPTH_BEFORE_SHADER with a stencil reference
export still decodes as early.

TechnicalDebt records that the bit follows Mesa and that its combination with
a Z export, a mask export, a stencil export or alpha-to-coverage was not
measured.
2026-10-13 15:19:33 +02:00
Adrià Franch cdf4b174f4 fix(libkernel): keep the execv export and its debt line
The rename to execve dropped execv, which PPSA97358 imports.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:19:19 +02:00
MarcZX100 77016caa24 fix(libkernel): limit execve to unsupported behavior 2026-10-13 15:19:19 +02:00
David 4f1ae1cc3a feat(libkernel): implement execv and execve as throwing exports
XPSemu PPSA97358 imports both from libkernel.prx; with the imports ahead
of them covered, its predicted next stops (relink registry order, not an
observed run) are:

  FAIL: unresolved ELF import KaK52lYTBIs from libkernel.prx  (execv, 7 call sites)
  FAIL: unresolved ELF import kdguLiAheLI from libkernel.prx  (execve, 4 call sites)

Today the title stops earlier at the observed
  FAIL: unresolved ELF import aRo9AhFUXcM from libkernel.prx  (sigsetjmp, merged in #3486)

As for execvp (#2827), the process image cannot be replaced here, so both
throw with the execvp message pattern once FreeBSD 11's execve would start
loading the image (freebsd-src 8c639945, sys/kern/kern_exec.c
exec_copyin_args, do_execve, exec_check_permissions; lib/libc/gen/exec.c
execv passes environ). Before that they fail as FreeBSD does: EFAULT for a
null argument vector or path, ENOENT and ENOTDIR from the lookup as stat
reports it, EACCES for a path that is not a regular file. Paths of
PATH_MAX bytes or more and strings over ARG_MAX throw instead of guessing
the console's limits. The execv calls in XPSemu's sources run in a fork
child (net/tap.c), and its fork fails with ENOSYS (ZiZc3/XPSemu 8eec07b4,
ps5/compat/libc_missing.c, ps5/build-title.sh --defsym).

NIDs computed with tools/nid_names.py and confirmed in aerolib.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 57f0fa37132bc6c78bc02282044a00e2db859484)
2026-10-13 15:19:19 +02:00
xXPBSXx 79e162110a fix(libkernel): report SO_ERROR and the other SOL_SOCKET options like FreeBSD
getsockopt and setsockopt on a libkernel socket knew four SOL_SOCKET options
(SO_REUSEADDR, SO_BROADCAST, SO_SNDBUF, SO_RCVBUF) and failed every other
one with ENOPROTOOPT. The Mound's EOS SDK reads SO_ERROR on two of its
sockets after a connect, which therefore always failed.

The SOL_SOCKET options now follow FreeBSD 11.4 sosetopt and sogetopt:
- SO_ERROR returns the pending error of the socket as a FreeBSD errno and
  reports it once (Winsock does not clear it on read, so the library does).
- SO_TYPE, SO_ACCEPTCONN, SO_KEEPALIVE, SO_DONTROUTE, SO_OOBINLINE and
  SO_LINGER are read from and set on the host socket; the boolean options
  read back as their own bit, as FreeBSD returns so_options & option.
- SO_DEBUG, SO_USELOOPBACK, SO_REUSEPORT, SO_TIMESTAMP and SO_NOSIGPIPE stay
  ENOPROTOOPT until a title needs one.
- A value is read from its first int and fails with EINVAL when shorter, a
  getsockopt buffer is filled up to its length (sooptcopyin, sooptcopyout),
  SO_SNDBUF and SO_RCVBUF below 1 fail with EINVAL, and SO_LINGER outside
  0..65535 seconds fails with EDOM.

A connect on a non-blocking socket now fails with EINPROGRESS on Windows too
(it was EWOULDBLOCK, which a title waiting for the connection does not
expect).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 15:18:29 +02:00
Adrià 6ac63bf0cb
Merge pull request #5104 from Feuris/feat/libc-wcscat
feat(libc): implement wcscat
2026-10-13 15:18:07 +02:00
Adrià 5aaa394cff
Merge pull request #5080 from ErenYeager1836/carry/libSceAgc-amm-semaphore
feat(libSceAgc): implement sceAgcSetAmmSemaphoreMemory and sceAgcGetSemaphoreLabel
2026-10-13 15:17:12 +02:00
Adrià 1e7f4967ed
Merge pull request #3743 from YossiYad/carry/ampr-pad-launch
feat(libSceAmpr): implement the std::_Pad thread launch helpers
2026-10-13 15:15:37 +02:00
Adrià 21bdab007a
Merge pull request #1312 from TibixDev/pr/incremental-draw-fingerprints
perf(agc): maintain draw fingerprints on register writes
2026-10-13 15:15:17 +02:00
Adrià Franch 1a0de9e91f fix(libc): drop unused clocale include
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:13:01 +02:00
MarcZX100 2ec20dbd37 fix(libc): validate guest setlocale categories 2026-10-13 15:12:57 +02:00
MarcZX100 5eb99f8b65 fix(libc): reject invalid setlocale categories
(cherry picked from commit 099fa80b53eba577d29a0bfff672dc4f9c160ec1)
2026-10-13 15:12:56 +02:00
Karan Vora 352bf304fe perf(shader): skip the f16 NaN rebuild for operands that are only compared
With float16DenormPreserve, EmitF16BitsToF32 rebuilds the bits of a NaN
half after UnpackHalf2x16, because NVIDIA returns the default NaN and
Intel quiets it. When every use of the converted operand is an f32
comparison or a NaN test, directly or through abs and neg, only whether
it is NaN matters, so the conversion returns UnpackHalf2x16's result as
is. v_cmp_*_f16 goes from 11.5 to 4 SASS on an RTX 4060.

agc_driver_f16_to_f32_unpack adds v_cmp_u_f16 and v_cmp_lt_f16 over all
65,536 halves with each decode the device supports. Its SPIR-V check
requires the v_cvt_f32_f16 conversion to rebuild the NaN bits and the
compared conversions not to.
2026-10-13 15:11:54 +02:00
Aarav 2f453805aa fix(libSceSystemService): contain converted executable paths 2026-10-13 18:40:19 +05:30
Adrià c98fda869a
Merge pull request #4015 from sherySJ/agent/libc-time-eoverflow
fix(libc): keep the caller errno when localtime cannot represent a time
2026-10-13 15:10:07 +02:00
Adrià 4008e68e39
Merge pull request #3919 from GalaxyGamingFR/fix/stubgen-stdout-newlines
fix(stubgen): write LF to stdout on Windows
2026-10-13 15:09:35 +02:00
Adrià d5835a613b
Merge pull request #5102 from Dripl1x/codex/generic-error-category
feat(libc): implement generic error category messages
2026-10-13 15:08:29 +02:00
Adrià Franch 76075edd38 test(libSceAudioOut): define TestEnableChat; drop stale EnableChat unknown-signature line
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 15:06:32 +02:00
screamm 50cd318bb8 feat(libSceAudioOut): implement sceAudioOut2EnableChat
Palworld (PPSA23126, #2648) stops at start-up on the export, imported by
its bundled libdiscord_partner_sdk.prx:

    FAIL: unresolved ELF import oiOi7vgPfAY from libSceAudioOut.prx

PPSA28416 imports it from its bundled party-ps5-shipping.prx as well. The
call itself has not been observed yet.

SharpProspero (5dfdb4b0, Interop/Audio/AudioOut2.cs:460-464) declares
`int sceAudioOut2EnableChat()`, "Allows chat audio alongside the
application's own", and the ps5-payload-dev SDK stub (f5d40f02,
sce_stubs/libSceAudioOut.c:62) exports it as well. There is no context or
handle to validate. The host mixes no chat audio and AudioOut2 has no chat
port, so the call records that chat audio is allowed (traced once with
APS5_TRACE_AUDIOOUT2) and returns 0. Whether the console fails it without
a chat session or when repeated is recorded in technical debt, and the
PPSA28416 "signature unknown" entry is dropped.

NID oiOi7vgPfAY matches tools/nid_names.py and the shadPS4 aerolib list.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:06:27 +02:00
Adrià Franch 6ba5d68e07 docs(debt): move the AMM semaphore line away from the end of Functional 2026-10-13 15:01:48 +02:00
David 352d04e8dc feat(libSceAgc): implement sceAgcSetAmmSemaphoreMemory and sceAgcGetSemaphoreLabel
sceAgcSetAmmSemaphoreMemory(memory, sizeInBytes) registers the AMM semaphore memory once and zeroes the first 8 bytes of each 32-byte label; sceAgcGetSemaphoreLabel(index, label) writes memory + 32 * index for an index inside the registered memory.

Unblocks New Joe & Mac Caveman Ninja (PPSA02801) which imports both symbols. Matches OpenAGC and sharpemu agreements on signatures, 16 KiB alignment, 32-byte stride, and zeroed label counters.
2026-10-13 15:01:43 +02:00
Kikoarl e5f0938409 feat(ces): implement sceCesUcsProfileInitSJis1997Cp932 2026-10-13 15:00:31 +02:00
Yossi Yadgar dd1deb6965 fix(ampr): raise std::_Pad failures as the guest std::system_error
A failing _Pad primitive threw a host std::runtime_error, which no guest
catch can see. threadcall.cpp calls the checked wrappers of xthread
(_Mtx_lockX, _Cnd_waitX, _Thrd_startX), which pass a non-success code to
_Throw_C_error: _Thrd_nomem and _Thrd_timedout become
resource_unavailable_try_again, _Thrd_busy device_or_resource_busy and
_Thrd_error invalid_argument, raised through _Throw_Cpp_error, which main
now implements. A failing thread start is _Thrd_startX's
resource_unavailable_try_again.

_Release now wakes the launching thread with _Cnd_signal, as
threadcall.cpp does; libc has it since the pad was written.

guest_ampr_threads checks, in a forked child whose guest terminate
handler reports the thrown object, that _Release on the thread still
holding the pad's mutex throws std::system_error with EBUSY. It fails
with the previous host exception and with busy mapped to
invalid_argument.
2026-10-13 15:59:28 +03:00
melihcan 0afa191660 test(libSceConvertKeycode): cover sceConvertKeycodeGetImeKeyboardType
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:59:08 +02:00
xXPBSXx c6a4223420 fix(agc): sample 10_10_10_2 SNorm and SInt textures
Guest texture formats 51 (SNorm) and 55 (SInt) of the 10-bit packed group were missing from the texture lookup and the descriptor format table, so a descriptor using them threw 'unsupported guest texture format' while the UNorm (50) and UInt (54) siblings worked. Map them to A2B10G10R10_SNORM_PACK32 / A2B10G10R10_SINT_PACK32, classify them for sampling (float / signed integer), and list SINT among the integer formats.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 14:56:03 +02:00
Aarav 6f0f7c7461 fix(relinker): reject relocatable ELF conversion inputs 2026-10-13 18:23:31 +05:30
Yossi Yadgar 8e3b2a06f2 fix(ampr): exclude the private pad entry from the export inventory 2026-10-13 15:51:00 +03:00
Yossi Yadgar 49d8cf3e61 test(ampr): exercise simultaneous pad launches and destroyed storage 2026-10-13 15:51:00 +03:00
David d991cdccf9 feat(libSceAmpr): implement the std::_Pad thread launch helpers
Implement _ZNSt4_PadC2Ev, _ZNSt4_PadD2Ev, _ZNSt4_Pad7_LaunchEPP7pthread
and _ZNSt4_Pad8_ReleaseEv, which Deathloop (PPSA01670) and Minecraft
Legends (PPSA05510) import from libSceAmpr for the std::thread launch path.

The algorithm and object layout follow Dinkumware V6.00 thr/xthread and
thr/threadcall.cpp of Visual C++ 2013 (ganboing/crt.vc12 f1d47126): a
vtable pointer whose first slot is the pure virtual _Go(), then the
condition and mutex handles and bool _Started (0x8, 0x10, 0x18), with the
console's pointer-to-slot handles of libc Threads.cpp (#3079). The pad
uses libc's _Cnd_*/_Mtx_* exports, starts the guest thread with
pthread_create and an entry that calls _Go() and returns its result as
the exit code, as _Call_func does. The unverified layout, thread
attributes and error reporting are recorded in TechnicalDebt.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:51:00 +03:00
Adrià Franch a37ee5ff2c fix(libc): keep the caller errno when the Windows tm_year range overflows
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 14:50:07 +02:00
sherySJ b1cf01464a fix(libc): keep the caller errno when localtime cannot represent a time 2026-10-13 14:50:03 +02:00
Adrià Franch dd49e1df93 build(agc): put libc's directory on PATH for the register key test on Windows
The sibling AGC driver tests get this through the agcTest loop; without it the test exits 0xc0000135 (DLL not found) on Windows.
2026-10-13 14:49:31 +02:00
Adrià Franch 89414d3039 build(agc): link register key test with libc and libkernel like its sibling tests
The Windows build failed to start it (0xc0000135, DLL not found).
2026-10-13 14:49:24 +02:00
TibixDev 5942b8c7f1 perf(graphics): track draw register fingerprints incrementally
Track mutations against the current draw dependency mask and preserve device and shader registry identity in the draw key. Validate mutations, projected masks, PM4 packets and prepared graphics against full recomputation.

Written with AI assistance.
2026-10-13 14:49:24 +02:00
GalaxyGamingFR 1c32fd93c8 fix(progress): read cmake and shader tables as utf-8
scan_library and collect_shaders read CMakeLists.txt, the ISA table and the opcode header with the locale codec. On Windows that is cp1252, so a UTF-8 file containing U+2010 or U+2192 raised UnicodeDecodeError. These reads now pass encoding="utf-8" and errors="ignore", and a subprocess test with an ASCII default codec covers it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-13 14:48:50 +02:00
GalaxyGamingFR b12d6f5394 fix(conventions): match agent instruction files case-insensitively
The repository check compared the file name to a lower-case set, so a
file added as CLAUDE.md, Agents.md or GEMINI.md passed the check while
claude.md was rejected. Compare the lower-cased name so every spelling
of an agent instruction file is reported.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-13 14:48:43 +02:00
screamm 8b9529b99a fix(libScePosixForWebKit): keep the getservbyname result trivially destructible
The per-thread servent storage held std::string and std::vector
members, so getservbyname_nid_postfix registered a thread_local
destructor through __cxa_thread_atexit. On Windows that destructor runs
after emulated TLS freed the object, and prx_thread_local_destructors
(#4029) rejects libScePosixForWebKit.prx for it.

The result now lives in a thread_local struct of fixed-size char arrays
and an alias pointer array, sized for the longest name (submission),
alias (www-http), protocol (sctp) and alias count in the embedded table;
static_asserts check the sizes against the table and that the struct is
trivially destructible. The guest servent layout and the per-thread
result FreeBSD's getservent.c keeps in its NSS TLS state are unchanged.
Nothing is allocated, so the ENOMEM path is gone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:43:05 +03:00
screamm 15bbc19501 feat(libScePosixForWebKit): implement getservbyname
Implement getservbyname (H1bArJPagV0) in libScePosixForWebKit. Cloud
Play (PPSA99600) imports it from this library only (registry index 408,
call site 0x1042124); once fnmatch (#3545) is in, its predicted load
stop is:

    FAIL: unresolved ELF import H1bArJPagV0 from libScePosixForWebKit.prx

The ps5-payload-dev SDK stubs (f5d40f02, sce_stubs/) declare
getservbyname in libScePosixForWebKit.c and not in libkernel.c or
libSceLibcInternal.c. The call site is the service-name branch of a
getaddrinfo inside the title: a non-numeric service is looked up with
protocol "tcp", "udp" or "sctp" and the port is read big-endian at
offset 16 of the result, which fixes the servent layout used here
(name, aliases, 32-bit port, protocol at 0, 8, 16, 24).

The guest has no /etc/services and the console's file is unknown, so the
function searches 37 lines of FreeBSD 11.4 etc/services (8c639945):
ftp-data, ftp, ssh, telnet, smtp, domain, tftp, http, pop3, ntp, imap,
https, rtsp, submission, imaps and pop3s with their aliases and
protocols, in file order. None of their names or aliases appears on an
earlier line of the full file, so these lookups answer as the full file
would. The search follows files_servent in lib/libc/net/getservent.c
(8c639945): exact, case-sensitive match on the name or an alias, a null
protocol takes the first match, the port in network byte order, the
result in per-thread storage like getserv's NSS TLS state. Any other
name returns null without touching errno, as for a name the file lacks.
The repository's getaddrinfo still resolves service names through the
host, so there was no table to share; this is recorded in
TechnicalDebt.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:43:05 +03:00
Avijit Singh 48ea54ed3e fix(relinker): include output stream header
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-13 18:05:37 +05:30
Avijit Singh 1796482721 docs(relinker): clarify skipped SELF modules
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-13 18:04:49 +05:30
Avijit Singh f23c5c2fd2 test(relinker): cover SELF skip warnings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-13 18:04:49 +05:30
Avijit Singh 82f24e44a2 fix(relinker): warn on skipped SELF modules
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-13 18:04:49 +05:30
Romans Pokrovskis 44c4b3f8bf fix(agc): admit inactive covered-centroid mode
Admit PA_SC_AA_CONFIG bit29 only without active perspective or linear centroid inputs, or with a skipped pixel program. Active/unspecified centroid inputs and unrelated AA fields remain refused. Source: pinned PS5_Mesa 0b2d6d1a GFX10.3 schema/RADV producer; existing ENA/ADDR intersection. Console covered-centroid interpolation remains unmeasured.

Full build/all 124 runtime libraries, conventions and registration pass. Final Windows suite: 965 pass, zero fail, seven skips of 972. Isolated state test passes 20 repeats and fails 5/5 with main State.cpp; four guard mutants fail 5/5 each. Original two title register captures reproduce the main refusal and pass this check with candidate, five times each. NVIDIA relevant groups pass; AMD draw-cache failure also occurs on unchanged main and is reported in issue4435.

AI-assisted: Codex.
2026-10-13 15:34:41 +03:00
boykopovar 0f2683cb7d
Merge pull request #5135 from boykopovar/feat/web-relinker-ui
feat(ci): add relinker wasm build ci
2026-10-13 15:31:28 +03:00
Adrià c827e11872
Merge pull request #4086 from hugodearaujo/fix/setschedparam-console
fix(libkernel): check and keep the scheduling policy of a thread
2026-10-13 14:29:15 +02:00
boykopovar 693578f116 ci(pages): rename deployment workflow file 2026-10-13 15:27:02 +03:00
Adrià afbc22fb1a
Merge pull request #4140 from veosett/fix/relinker-windows-fixed-base
feat(relinker): support fixed Windows guest image bases
2026-10-13 14:26:54 +02:00
Adrià 94d9957725
Merge pull request #4078 from MarcZX100/codex/carry-ssl-connections-3766
fix(libSceSsl): add missing verification callback export
2026-10-13 14:26:09 +02:00
boykopovar dece64c6e8 ci(pages): build and deploy wasm converter 2026-10-13 15:26:04 +03:00
Adrià a164b1d711
Merge pull request #1359 from TibixDev/pr/copied-write-ordering
fix(agc): order direct reads after copied writes
2026-10-13 14:25:22 +02:00
Aarav bc014a887c fix(relinker): reject load file sizes beyond memory size 2026-10-13 17:55:11 +05:30
SandstromPL d34b78890d fix(savedata): return no saves for a search with another title's ID
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:53:29 +05:30
Adrià 398ccaaf96
Merge pull request #5108 from adriadam10/ci/agc-graphics-tests-link-driver
build(agc): link the graphics and BDA device tests against libSceAgcDriver
2026-10-13 14:19:16 +02:00
Adrià bc0375c72a
Merge pull request #5130 from adriadam10/ci/windows-keep-f16-unpack
ci(windows): build the f16 unpack execution test, it runs without a device
2026-10-13 14:18:44 +02:00
Adrià a60f8d940b
Merge pull request #5048 from MarcZX100/codex/libc-openlog-closelog
fix(libc): implement openlog and closelog state
2026-10-13 14:18:31 +02:00
Adrià Franch 5181b30d79 ci(windows): build the f16 unpack execution test, it runs without a device
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 14:18:28 +02:00
boykopovar 8485298e46
Merge pull request #5114 from boykopovar/feat/web-relinker-ui
feat(pages): add web relinker ui
2026-10-13 15:16:10 +03:00
Adrià a2a3a3a94d
Merge pull request #4062 from danzaio/fix/windows-log-without-standard-handles
fix(relinker): log Windows output when standard handles are missing
2026-10-13 14:11:42 +02:00
Adrià 96f3e9fe5d
Merge pull request #4041 from ee6aba54/fix/agc-cmask-unknown-codes
fix(agc): keep the texels of CMASK tiles with codes other than 0 and 15
2026-10-13 14:11:16 +02:00
boykopovar 860cecf11e docs(readme): link online converter and simplify progress history 2026-10-13 15:03:17 +03:00
Adrià Franch 14f2092c9e build(agc): link the graphics and BDA device tests against libSceAgcDriver
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:53:51 +02:00
boykopovar dae5690e59 fix(web): reject windows folder output before writing files 2026-10-13 14:53:19 +03:00
Adrià 14036cb0e8
Merge pull request #5107 from adriadam10/ci/linux-mesa-shader-cache
ci(linux): keep lavapipe's shader cache between builds
2026-10-13 13:51:53 +02:00
Adrià Franch aef5972ff5 ci(linux): keep lavapipe's shader cache between builds
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:51:10 +02:00
Celegans12 247162dc4f feat(agc): read hull patch constants in tessellation domain programs
TessellationLowering refused every domain-program read of the off-chip
ring outside its patch's control points, so domain programs that read
the hull's patch constants, like the quad terrain domain in Ghost of
Yotei at 0x8000 - 208 * (rel + 1), aborted.

Such reads now lower to a new PatchInput kind and become Patch inputs.
Hull outputs and domain inputs place a constant by its off-chip address
modulo the record stride, in a record-sized array at location
ceil(hsStride / 16); each program derives both on its own, so the two
interfaces match although they compile separately. Domain reads at an
address that does not move with the relative patch, across more than
one record or inside the control-point region still throw, as does a
domain program that reads patch constants but no control point. Hull
patch-constant writes now take the same bound: above the control points
of all NUM_PATCHES patches, not just one patch's.
2026-10-13 13:50:53 +02:00
Celegans12 25a83722c7 fix(agc): bound tessellation patch records by the draw's patch count
TessellationLowering proved ring and LDS addresses affine over a
relative patch index of up to 256. A hull that lays its patch-constant
records out downwards, at 0x8000 - 208 * (rel + 1) like the quad
particle hull in Ghost of Yotei, then reached a negative address for
the last slot and aborted as "not affine". The relative patch index of
hull and domain programs stays below VGT_LS_HS_CONFIG.NUM_PATCHES
(bits 7:0, patches per threadgroup), which is 63 for that draw.

DecodeShaderStages now carries NUM_PATCHES as patchesPerGroup in the
tessellation configuration (draw keys, hull and domain cache keys,
recompile request version 22) and refuses zero, and the lowering bounds
the relative patch index by it. Configurations without it keep 256.
2026-10-13 13:50:27 +02:00
Adrià ff8fe4dbc1
Merge pull request #3988 from karanvora2599/perf/shader-f16-unpack-half
perf(shader): convert f16 operands with UnpackHalf2x16 where f16 denormals are kept
2026-10-13 13:48:59 +02:00
Adrià c63dd4442c
Merge pull request #5106 from adriadam10/ci/agc-mesh-tests-link-driver
build(agc): link agc_driver_mesh_tests against libSceAgcDriver
2026-10-13 13:47:54 +02:00
Adrià Franch f20a32bec3 build(agc): link agc_driver_mesh_tests against libSceAgcDriver
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:46:45 +02:00
Dean ce8f7eeb3f
Merge pull request #4344 from Roojool/contrib/round2-c19
fix(tools): use configured Ninja for test registration audit
2026-10-14 00:46:35 +13:00
Adrià 84e2a8c0b3
Merge pull request #5105 from adriadam10/ci/windows-device-test-stubs
ci(windows): build only the execution tests that run without a Vulkan device
2026-10-13 13:45:23 +02:00
AlxcBTW 230b5c2683 perf(agc-driver): join the imports of adjacent large ranges when a buffer is bound across them
8 MiB window, so a range larger than the window is always imported alone. A
buffer bound across two such ranges then has no import and is copied whole on
every use: PPSA25344 binds a 368 MiB buffer across a 320.75 MiB and a 47.3 MiB
range and copies it on almost every frame.

When UploadFinish meets such a region, it imports the run of the ranges the
region crosses, provided every one of them is larger than the window, and
retires their own imports. It does this before any region of the build
confirms an import, so the retire moves the epoch ahead of every pointer the
build would use. A range that large is registered whole, not grown in 64 KiB
pieces like a heap, so the joined import is not re-imported as a heap grows; it
stands in refreshImports while exactly its ranges stand. A later join that
reaches a joined import takes it in, so two joins that share a range never
retire each other in turn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:45:21 +02:00
Adrià Franch 1137c57572 ci(windows): build only the execution tests that run without a Vulkan device
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:44:08 +02:00
Adrià 99e4dfefe7
Merge pull request #5100 from adriadam10/ci/dispatch-gate-matrices
ci(build): run relinker and context matrices on dispatch only when touched
2026-10-13 13:42:58 +02:00
Niko 44767a7b97 perf(libc): size each write-watch query to the region it covers
WindowsMappings::Collect handed GetWriteWatch the whole remaining output
capacity for every private region it walked. The call's cost follows that
capacity, not the size of the range: on Windows 11 (26200) a query of one
16 KiB region took 0.35 us with room for 4 entries and 6.5 us with room
for the 65536 the write tracker passes. Flexible memory is committed in
16 KiB allocations, one region each, so a resetting walk over 33 MiB of it
took 14.7 ms, 5.8 times a memcmp of the same bytes.

Each query now gets room for the pages its region spans, which is the most
it can report, so every walk returns the same pages; the 33 MiB walk takes
1.23 ms. guest_memory checks that a walk over eight 16 KiB allocations
reports every stored page once, in order, with room for 32, 5 and 1 pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:40:33 +02:00
Adrià 2f969f147b
Merge pull request #3973 from MarcZX100/codex/carry-rudp-end-terminate
fix(libSceRudp): correct End and Terminate functions
2026-10-13 13:40:04 +02:00
Adrià e57a793958
Merge pull request #3911 from GalaxyGamingFR/fix/pr-overlap-own-branch
fix(tools): drop the branch's own pull request in pr_overlap --branch
2026-10-13 13:39:14 +02:00
Adrià Franch 3c273e7081 ci(build): run relinker and context matrices on dispatch only when touched
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:39:04 +02:00
Dean 9fa50827dd
Merge pull request #4532 from DavidSamir/fix/libc-rename-replace-open-destination-windows
fix(libc): replace an open or read-only destination in rename on Windows
2026-10-14 00:38:14 +13:00
Evgeniy Kazarin e3cef332e6 feat(libc): implement generic error category messages 2026-10-13 14:38:04 +03:00
Feuris cd0e9dccd7 feat(libc): implement wcscat 2026-10-13 08:38:02 -03:00
Dean 18414447b9
Merge pull request #4525 from Lorkydey/fix/poll-priority-windows
fix(libkernel): support POLLPRI on Windows sockets
2026-10-14 00:37:59 +13:00
boykopovar 36ea209e72 fix(wasm): enable windows diagnostics and disable gui mode 2026-10-13 14:37:50 +03:00
Dean e09ee165d5
Merge pull request #3651 from MarcZX100/codex/libc-posix-spawn-3624
feat(libSceLibcInternal): implement posix_spawn actions
2026-10-14 00:37:44 +13:00
Dean bc1ded3a18
Merge pull request #4386 from MarcZX100/codex/carry-mlockall-getdtablesize-3628
feat(libkernel): add mlockall export
2026-10-14 00:37:29 +13:00
XairRrR 95f34a0504 fix(agc): report GPU time per presented or dropped frame
The [present] line divided the GPU busy and idle time of a window by the
presentations that got a swapchain image. That time covers the GPU work
of every frame, so with frames dropped for want of an image it read
several times too high (278.9 ms instead of about 52 ms on a hidden
window, reported on #4684). Those two figures are now divided by the
presented and dropped frames; the per-present blit figures are unchanged.
2026-10-13 13:34:19 +02:00
bubblegg 4b7a357bd2 fix(savedata): report a save's size as the mount that created it gave
Every native save reported SAVE_DATA_BLOCKS_MAX blocks, all of them free,
in the sceSaveDataDirNameSearch infos and in sceSaveDataGetMountInfo,
whatever the title created it with and whatever its files held. A save's
size is now the blocks of the sceSaveDataMount3 call that created it,
kept beside the save; a save without a kept size takes that of its next
mount that names one, and otherwise keeps SAVE_DATA_BLOCKS_MAX. Free
blocks are what the save's files leave of it, each file rounded up to
32 KiB blocks, and the blocks and free-blocks sort keys use both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:32:43 +00:00
boykopovar 33432725d6 fix(web): limit executable detection to app root 2026-10-13 14:30:57 +03:00
XairRrR 37d9ef133f fix(agc): drop a presentation instead of waiting for a swapchain image
The presenter acquired the next swapchain image with a 5 s timeout
before completing the guest's flip. With FIFO on Wayland, NVIDIA's
driver hands out about one image per second while the window is hidden
(another workspace or occluded): vkAcquireNextImageKHR blocked for
~1 s per flip, and the title ran at exactly 1 frame per second. Stack
snapshots of Uncharted: Legacy of Thieves Collection (PPSA05684) showed
the presenter in drmSyncobjTimelineWait under AcquireImage in every
snapshot taken during such a stall, and in none outside them.

The acquire no longer waits: VK_NOT_READY or VK_TIMEOUT drops that
presentation, as an out-of-date swapchain already does, and the flip
completes. APS5_PROFILE_DRAW's [present] line counts the drops.

Menu with the window moved to an empty workspace for 50 s: 7.5 fps and
about 65 drops per 10 s while hidden (1 fps before), no drops while
visible.
2026-10-13 13:30:47 +02:00
Adrià e99a1b0aa9
Merge pull request #3903 from GalaxyGamingFR/fix/conventions-body-bom
fix(conventions): read the PR body file with utf-8-sig
2026-10-13 13:30:34 +02:00
Adrià f56d638a5a
Merge pull request #3688 from CouchRiv/fix-libc-acosf-errno
fix(libc): keep the acosf result and leave errno unchanged
2026-10-13 13:29:58 +02:00
p3nicillin aebc3f1480 feat(libc): export tzname
ES-DE in RetroArch (PPSA99169) imports tzname from libSceLibcInternal and
fails to load without it; ICU's uprv_tzname reads it after localtime. It
points at the host's zone names and tzset refreshes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:28:33 +01:00
boykopovar a227719658 fix(web): detect guest modules by elf signature 2026-10-13 14:27:07 +03:00
boykopovar 1d99664fb9 feat(web): support folder input 2026-10-13 14:24:33 +03:00
boykopovar 5e762427b4 style(web): simplify converter heading and notes 2026-10-13 14:18:40 +03:00
Dean 5d50886ee4
Merge pull request #2076 from jdmagic/feat/agc-stencil-plane-storage
feat(libSceAgcDriver): take a one-layer 2D array view of a depth surface plane as the plane
2026-10-14 00:11:01 +13:00
Dean 3ee394ea9a
Merge pull request #4543 from coltken95/fix/libc-time
fix(libc): format strftime and asctime as FreeBSD 11 does
2026-10-14 00:10:43 +13:00
Dean 6f7cda184b
Merge pull request #4315 from JAB-dev/feat/agc-dcc-packed-float-clear
feat(agc): encode packed R11G11B10 float DCC fast clears
2026-10-14 00:10:16 +13:00
Dean 50578eb936
Merge pull request #4216 from Mouad-Scriptz-0/perf/guest-memory-registry-lookups
perf(libkernel): find guest allocations and direct mappings by address
2026-10-14 00:09:54 +13:00
Dean 0745fdd72f
Merge pull request #4872 from David-cmrg/fix/systemservice-loadexec-exit-order
fix(libSceSystemService): run guest exit handlers first on LoadExec exit
2026-10-14 00:09:38 +13:00
boykopovar 9144f7ac2c feat(web): add browser converter interface 2026-10-13 14:05:33 +03:00
boykopovar 57801875db refactor(web): load zip.js from pinned cdn version 2026-10-13 14:02:46 +03:00
Adrià c8eb1f52d3
Merge pull request #3898 from GalaxyGamingFR/fix/progress-wrapper-definitions
fix(progress): recognise non-static and noexcept stub helpers
2026-10-13 13:01:36 +02:00
lorkydey d4b59ce9fa fix(agc): accept inactive vertex point size on non-point draws 2026-10-13 12:56:44 +02:00
Kovalev Vladislav 319fa5779a feat(relinker): support fixed Windows guest image bases 2026-10-13 12:55:54 +02:00
Adrià b1328fc0f9
Merge pull request #4100 from nyavana/docs/relinker-help-refresh
docs(relinker): correct help and exit-code usage
2026-10-13 12:54:27 +02:00
hugodearaujo 5c6a9d22c4 fix(libkernel): check and keep the scheduling policy of a thread
On a PS5 (firmware 13.60) pthread_setschedparam and
scePthreadSetschedparam accept only SCHED_FIFO and SCHED_RR with a
priority of 256 to 767 or 896 to 959, return EINVAL otherwise, and
keep the policy for pthread_getschedparam. A created thread takes the
policy of explicit scheduling attributes, or its creator's when it
inherits. Here any policy and priority were accepted and SCHED_FIFO
was always reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:53:19 +02:00
Adrià Franch e34281b558 docs(debt): keep the other SSL stubs in the PPSA07411 entry
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 12:50:47 +02:00
MarcZX100 5d00bd7bbf feat(libSceSsl): add missing verification callback export 2026-10-13 12:50:43 +02:00
Yaash Singhh 4f87ac0b1c fix(libkernel): harden DirectMemory pool bounds and rollback 2026-10-13 10:49:27 +00:00
boykopovar 78df1e3244 feat(web): stream zip conversion and release library packaging 2026-10-13 13:48:23 +03:00
Dean 075169f7b3
Merge pull request #4404 from eljackjoestar-cmd/codex/demanded-shader-preparation
fix(agc): prioritize demanded shader preparation
2026-10-13 23:42:40 +13:00
Dean 768cd93977
Merge pull request #4082 from Celegans12/feat/agc-quad-tessellation
feat(agc): implement quad tessellation with counter-clockwise output and level clamps
2026-10-13 23:42:25 +13:00
Dean 1d7b075610
Merge pull request #4496 from Kikoarl/perf/agc-gds-dma-and-completion-label-sync
perf(agc): disambiguate completion label overlaps in CopyBuffer
2026-10-13 23:42:11 +13:00
boykopovar 34766c8236 feat(relinker): expose wasm conversion bindings 2026-10-13 13:38:47 +03:00
Adrià Franch 3949fb9d44 test(agc): skip copied-direct ordering without Vulkan and find libc on Windows
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 12:34:08 +02:00
TibixDev 29b227bf35 fix(agc): complete copied writes before direct consumers
Wait for overlapping CPU writebacks and completion labels before importing buffers or accepting cached image resources. Cover texture data and compression metadata, retain asynchronous disjoint and GPU-only dependencies, and avoid recursive completion waits.

Reproduced fresh-buffer and cached-image failures with CPU Vulkan before fixing them. Regression covers fresh/reused bindings, dispatch/draw copied writers, texel/metadata updates, labels and no-wait cases; passes CPU Vulkan and RX 6600 synchronization validation. Graphics and guest-memory suites pass. The Skyrim loading GPU fault remains unattributed.

AI-assisted implementation and independent code review.
2026-10-13 12:34:03 +02:00
Dean 53aea1ff34
Merge pull request #5024 from oneandonlydean/up4/storage-store-keeps-untracked-writes
fix(agc): keep CPU writes when a pending storage result is stored over untracked memory
2026-10-13 23:33:08 +13:00
Dean 387b8a0faa
Merge pull request #3763 from eljackjoestar-cmd/codex/dynamic-rendering-layers
fix(agc): render all attachment layers with dynamic rendering
2026-10-13 23:32:53 +13:00
Adrià Franch 99ebc650c3 fix(agc): zero dword fill only for 32-bit output formats
The kernel stores through the V# FORMAT; with an 8- or 16-bit format it writes 1 or 2 bytes per element, which a zero fill of the whole range would also clear between. Same check as MatchDwordPatternFill (format 20-22), with test rows.
2026-10-13 12:29:00 +02:00
jagrat Singh 9efabff1cb feat(libkernel): return ESRCH and EINVAL for unsupported cpuset affinity targets
Allow calling threads to query and set affinity using their own thread ID in addition to -1. Return ESRCH for non-matching thread or process IDs and EINVAL for invalid level/which arguments instead of throwing NotImplemented, matching FreeBSD cpuset semantics.
2026-10-13 15:58:11 +05:30
Daan Dehing 501453573c perf(agc): handle the zero dword fill kernel as a buffer fill
A wave64 compute kernel that stores dword 0 at index group * 64 + thread through a 4-byte
stride buffer is run as a buffer fill. Palworld (PPSA23126) clears its DCC keys with it every
frame; as a shader dispatch its writes were unknown to the DCC key proofs, so every refresh of
those targets waited for the GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:27:46 +02:00
Adrià c7632f036f
Merge pull request #5037 from Feuris/feat/libc-cnd-timedwait
feat(libc): implement _Cnd_timedwait
2026-10-13 12:26:09 +02:00
Adrià a1c2f9fa2c
Merge pull request #3902 from MarcZX100/codex/carry-amm-pool-references-3321
fix(ampr): preserve pooled pages across AMM remaps
2026-10-13 12:25:36 +02:00
Adrià ae244d527a
Merge pull request #5051 from ducloc080202/feat/system-service-challenge-activity
feat(libSceSystemService): no-op sceSystemServiceOpenChallengeActivity
2026-10-13 12:23:15 +02:00
boykopovar 3b5c35af9e
Merge pull request #5044 from boykopovar/refactor/relinker-core-di-iprogress
refactor(relinker): relinker core di
2026-10-13 13:23:12 +03:00
Dean Galvin cc3b47dda0 fix(libSceAgcDriver): take a one-layer 2D array plane view as a narrow exception to the non-2D refusal
Main refuses every storage view of a depth or stencil plane that is not
2D. A 2D array view is now taken as the plane only at layer 0 with one
layer; a view with more layers or another base layer still throws.

A one-layer 2D array view replaces a 2D view of the plane like any other
view, so a store back through the replaced 2D view throws. The depth
storage dispatch test runs one-layer views of both planes and checks
that the depth and stencil tests see what they stored, and that a
two-layer view still throws.
2026-10-13 23:17:51 +13:00
jdmagic e4a7cbfc83 feat(libSceAgcDriver): take a one-layer 2D array view of a depth surface plane as the plane
A storage view of a depth or stencil plane was taken for the depth surface
only as a 2D image. A 2D array view of layer 0 with one layer and the
plane's extent and format is the same plane, and now gets the plane's
storage image too; a view with more layers still takes the ordinary path.
The plane's storage image is recreated when the view's dimension changes.

PPSA01289 (Sackboy: A Big Adventure) binds the stencil plane of its
2880x1620 D32S8 surface as a one-layer R8_UINT 2D array from a compute
shader during presentation, and read guest memory instead of the stencil.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:15:15 +13:00
Karan Vora e4f212b2e2 perf(shader): convert f16 operands with UnpackHalf2x16 where f16 denormals are kept
Since #1463, every f16 operand read decodes sign, exponent and mantissa
with integer operations, 8 to 12 instructions more per read than
UnpackHalf2x16 on NVIDIA. The decode works around an AMD Windows driver
that folds the mask of a byte-selected half into UnpackHalf2x16.

A new target flag, float16DenormPreserve, is set when the device reports
shaderDenormPreserveFloat16 and denormBehaviorIndependence ALL, the same
rule as float64DenormPreserve. With it, EmitF16BitsToF32 uses
UnpackHalf2x16 and declares DenormPreserve for 16-bit floats, under
which SPV_KHR_float_controls keeps unpacked subnormal halves. NVIDIA
returns the default NaN for every NaN half, so the NaN bits are rebuilt
from the half: its sign at bit 31, its payload at bits 22:13. The
DenormPreserve 16 mode is declared once, shared with the native f32 to
f16 conversion. Without the flag (lavapipe; RADV reports 32_BIT_ONLY)
the integer decode stays. Halves of fields narrower than 16 bits, such
as SDWA byte selects, keep the integer decode on every device.

The flag is serialized (request version 17) and part of the cache key.

New test agc_driver_f16_to_f32_unpack converts all 65,536 halves, with
nonzero high bits, through v_cvt_f32_f16, and byte-selected halves
through v_add_f16 SDWA, with each decode the device supports, and
compares the bits with an exact decode. agc_driver_f16_to_f32_unpack_spirv
checks which decode the SPIR-V uses with and without the flag.
2026-10-13 12:15:00 +02:00
JAB-dev a96f8bd640 feat(agc): encode packed R11G11B10 float DCC fast clears
A fast clear of a packed float color target (format 36) was refused as unencodable. Give the format
a DCC layout (11, 11 and 10 bit float channels) and its one value, so 0000, 0001, 1110 and 1111
clear codes fill the texels and a texture keeps those keys.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 23:14:10 +13:00
Claude d0df62bd1f
fix(libc): format strftime and asctime as FreeBSD 11 does
strftime handed most conversions to the host's strftime, which differs
from FreeBSD 11's for E and O modifiers on conversions glibc does not
pair with them (%Ea was printed literally), padding flags on %+ and %v,
years outside 1000 to 9999 (%Y printed 899 and -1 where FreeBSD prints
0899 and -001), and out-of-range fields (%I of hour 100, %u of weekday
8). On Windows the padding flags printed nothing. strftime now follows
FreeBSD 11 strftime.c with the C locale on every host, and asctime
follows FreeBSD 11 asctime.c, padding years to four digits and setting
longer years apart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-13 10:12:15 +00:00
Mouad-Scriptz-0 df3d027ebe perf(libkernel): find guest allocations and direct mappings by address
sceKernelVirtualQuery, sceKernelMunmap, sceKernelReleaseDirectMemory and the
reservation paths walked a copy of the whole allocation registry or of every
direct mapping under one lock on each call. The registry now answers lookups
from its ordered map, applies range replacements as patches instead of
rebuilding the map, and direct mappings carry an index by physical offset so a
release finds only the mappings it covers. Minecraft (PPSA17221) holds about
7,000 direct mappings and spent its world generation in these walks.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 23:11:32 +13:00
Adrià Franch ff6c1ea5f3 fix(stubgen): reconfigure stdout only when it supports it
Callers that replace sys.stdout (test_stubgen redirect_stdout) have no reconfigure().

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 12:07:13 +02:00
GalaxyGamingFR f6d5404f45 fix(stubgen): write LF to stdout on Windows
When --out was not given, stubgen wrote the generated code and debt lines to
sys.stdout, which translates LF to CRLF on Windows, so redirecting stdout
produced CRLF files. stdout is now reconfigured to utf-8 with LF newlines,
matching the --out path. A subprocess test checks the bytes on stdout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-13 12:06:59 +02:00
David-cmrg 9f9c351f54 fix(libSceSystemService): run guest exit handlers first on LoadExec exit 2026-10-13 12:03:05 +02:00
boykopovar 04899cb0f7 fix(relinker): align console progress columns 2026-10-13 13:02:03 +03:00
GalaxyGamingFR 0817454371 fix(tools): drop the branch's own pull request in pr_overlap --branch
The --branch mode removed the branch's own open pull request only when its
head equalled the local branch exactly. Checking commits before pushing
left the branch ahead of its pull request, so the tool reported the user's
own pull request as overlapping. The filter is now a function that also
drops any pull request whose head is an ancestor of the branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-13 12:00:41 +02:00
boykopovar a534bd67ca fix(relinker): report intermediate output image progress 2026-10-13 12:59:23 +03:00
boykopovar d35b1afa51 fix(relinker): use consistent two-space nesting in runtime layout 2026-10-13 12:59:22 +03:00
boykopovar c9a94dd021 fix(relinker): remove layout padding and blank lines after headings 2026-10-13 12:59:04 +03:00
boykopovar 754b82163b fix(relinker): remove indentation from conversion summary 2026-10-13 12:58:48 +03:00
boykopovar 868d7a138d fix(relinker): separate console sections without spacing every message 2026-10-13 12:58:35 +03:00
boykopovar 6f2e4b6902 test(relinker): update runtime layout expectations for tree output 2026-10-13 12:58:35 +03:00
boykopovar 56c2a53188 fix(relinker): redraw console progress and format runtime layout as a tree 2026-10-13 12:58:34 +03:00
boykopovar e1ef33047c fix(relinker): validate scan ranges before narrowing memory offsets 2026-10-13 12:57:55 +03:00
boykopovar 3e658db4b6 refactor(relinker): remove the obsolete file-based icon builder 2026-10-13 12:57:55 +03:00
boykopovar 6224febfb0 feat(relinker): report instruction conversion progress for executables and modules 2026-10-13 12:57:54 +03:00
boykopovar b42aa8c540 feat(relinker): expose in-memory conversion through a portable core library 2026-10-13 12:57:53 +03:00
boykopovar 5e5eba8922 fix(relinker): reject layouts that would discard exception metadata 2026-10-13 12:57:06 +03:00
boykopovar 37e25a2a78 refactor(relinker): separate native module discovery from prx conversion 2026-10-13 12:57:05 +03:00
boykopovar 02cd2e57e2 refactor(relinker): move windows icon and diagnostic io to native adapters 2026-10-13 12:56:42 +03:00
MarcZX100 81f727c051 test(libc): match syslog PID formatting 2026-10-13 09:56:31 +00:00
MarcZX100 7313cf5589 docs(libc): record syslog tag fallback gap 2026-10-13 09:56:31 +00:00
MarcZX100 653c5449f9 fix(libc): honor openlog identity and facility 2026-10-13 09:56:31 +00:00
boykopovar cd0433fd82 feat(relinker): report syscall scanning and import analysis progress 2026-10-13 12:56:16 +03:00
boykopovar 04ca1721e5 feat(relinker): add progress interface and console reporter 2026-10-13 12:55:34 +03:00
Adrià 77b9f7270c
Merge pull request #3579 from Celegans12/fix/relinker-present-optional-provider-errors
fix(relinker): preserve load failures of present optional providers
2026-10-13 11:53:01 +02:00
Alex 988d304c5c fix(libc): keep the acosf result and leave errno unchanged
The previous commit replaced the out-of-range result with the default
quiet NaN 0x7fc00000, but main and FreeBSD e_acosf.c return 0xffc00000.

acosf now calls the host and restores errno, so only errno differs from
main. The guest test pins the result bits for 2.0f and -2.0f and keeps
the in-range case acosf(0.5f).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 11:49:14 +02:00
Alex 64e244eb70 fix(libc): match FreeBSD acosf errno
acosf returned 0xffc00000 (errno 33) for 2.0 (0x40000000), where FreeBSD
lib/msun/src/math.h returns 0xffc00000 (errno 0).

It now returns the FreeBSD result for that case. A guest test covers it
and fails on main.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-13 11:49:14 +02:00
Adrià 8bcd97cd5b
Merge pull request #1418 from gjonn/feat/agc-unnormalized-implicit-lod
feat(agc): sample the base level through unnormalized samplers
2026-10-13 11:49:07 +02:00
GalaxyGamingFR d15cc41299 fix(conventions): read the PR body file with utf-8-sig
A pull request body saved with a UTF-8 byte order mark starts with
U+FEFF before "### What", so the first heading is not recognised and the
checker reports the What section as missing. Decode the body file with
utf-8-sig so the mark is dropped and the sections are found.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-13 11:48:36 +02:00
GalaxyGamingFR 02dad4429f fix(progress): recognise non-static and noexcept stub helpers
The stub helper pattern required a leading static and a plain signature,
so exports calling helpers in anonymous namespaces, or helpers with
noexcept, a trailing return type or a pointer next to the name, counted
as done. The pattern now anchors at line start, makes static optional,
skips APS5_VABI exports and control keywords, and accepts those suffixes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-13 11:42:32 +02:00
ducloc080202 4aff906ca6 feat(libSceSystemService): no-op sceSystemServiceOpenChallengeActivity 2026-10-13 16:41:40 +07:00
Dean bb9a4d8a24
Merge pull request #5045 from oneandonlydean/up4/fix-clip-cull-draw-skip
test(agc): skip the clip and cull distance draw without a Vulkan device
2026-10-13 22:38:00 +13:00
Dean 2890f95494
Merge pull request #5025 from 0xRickx/fix/libkernel-sys-rename-import
fix(libkernel_sys): reach rename through a _nid_no_patch export
2026-10-13 22:37:36 +13:00
Adrià 08f672cc51
Merge pull request #5035 from ducloc080202/feat/system-service-controller-settings
feat(libSceSystemService): no-op sceSystemServiceShowControllerSettings
2026-10-13 11:35:54 +02:00
Feuris 572d330a13 feat(libc): implement _Cnd_timedwait 2026-10-13 06:34:01 -03:00
Dean Galvin 4a4bea9d1b test(agc): skip the clip and cull distance draw without a Vulkan device 2026-10-13 22:26:59 +13:00
xXPBSXx 51c7206fd0 fix(libScePlayGo): answer chunk ids from the package layout
A package with no playgo-* file in sce_sys has no PlayGo data, so only chunk 0
is valid and any other id returns SCE_PLAYGO_ERROR_BAD_CHUNK_ID. A package with
playgo-scenario.json but no chunk table (PPSA01748) keeps accepting every id.
A chunk table (playgo-chunk.dat or playgo-chunkdefs.xml) still decides when it
exists. A sce_sys that holds other playgo-* files without a chunk table or a
scenario file, or a playgo-chunk.dat that is not a plgx table with a nonzero
count, throws naming the file, since no such dump is known.

Without this a title that walks the ids until the first error (Sifu PPSA03001)
never gets the error.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 11:26:43 +02:00
Adrià 8299b2f7eb
Merge pull request #5036 from 0xRickx/feat/libc-atanhf
feat(libc): implement atanhf
2026-10-13 11:22:59 +02:00
Rujul Talekar 290675799e test(tools): drop the Ninja-on-PATH requirement from the registration suite
Remove test_built_uses_configured_ninja_outside_path per review: it copied
whatever ninja was on PATH and asserted one existed, so the suite failed on
exactly the setup this PR fixes. The rewritten tests already verify the
configured CMAKE_MAKE_PROGRAM is the program that runs; the suite now passes
with and without Ninja on PATH on Windows and Linux.
2026-10-13 14:51:04 +05:30
Ondra97 bde680c855 fix(relinker): map standalone RELRO in unused import filtering 2026-10-13 11:19:36 +02:00
screamm 5db1d8d5e9 fix(libkernel): accept POLLPRI on guest sockets under WSAPoll
Three catalog titles stop on Windows when they poll a guest socket for
POLLPRI|POLLOUT to see whether a non-blocking connect has completed:

- PPSA97510 PSXS5 (socket 0x10000002, thread gamedb, 13 s):
  what():  poll: POLLPRI is not supported by WSAPoll
- PPSA98510 SwanStation (sockets 0x10000003/0x10000002, threads gamedb
  and update): what():  poll: POLLPRI is not supported by WSAPoll
- PPSA99003 ProsperoTV ({0x10000001, POLLPRI|POLLOUT} at 5.3 s):
  what():  poll: POLLPRI is not supported by WSAPoll

FreeBSD's sopoll_generic (sys/kern/uipc_socket.c, freebsd-src 8c639945)
reports POLLPRI and POLLRDBAND alike, only while out-of-band data is
pending (so_oobmark or SBS_RCVATMARK). WSAPoll rejects POLLPRI in events
with WSAEINVAL but reports pending urgent data as POLLRDBAND, so on
Windows the guest POLLPRI is now asked of WSAPoll as POLLRDBAND and
POLLIN as POLLRDNORM (Winsock's POLLIN includes POLLRDBAND, which would
report urgent data as readable). Measured on this host: WSAPoll reports
POLLRDBAND after a peer's send(MSG_OOB), waits for it, and clears it once
the byte is read with MSG_OOB. Linux keeps polling POLLPRI on the host.

select needs no change: libkernel's select passes exceptfds as Urgent to
the libSceNet poller, which already asks WSAPoll for POLLRDBAND, and it
does not take libkernel guest sockets on either host.

The guest send and recv refuse MSG_OOB, so urgent data only comes from a
peer outside the guest's sockets; guest_poll now sends it from a host
socket and checks that a connected pair and a non-blocking connect
report POLLOUT and not POLLPRI. TechnicalDebt records where Winsock's
answer differs from FreeBSD's once the stream passes the mark.

Depends on #3649 (poll on file descriptors), whose two commits this
branch carries, since the titles reach this stop only with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 11:15:48 +02:00
Sanjaysrinivas 0612c181ff fix(agc): avoid storage snapshot overlap
Create uploads before publishing snapshot validity. Keep allocation
edits separate from the per-unit validity changes in #5024.
2026-10-13 11:11:58 +02:00
eljackjoestar-cmd dedb869fa2 test(agc): shut down the layered submission driver before exit 2026-10-13 13:07:18 +04:00
ducloc080202 46c7766f4f feat(libSceSystemService): no-op sceSystemServiceShowControllerSettings 2026-10-13 16:05:00 +07:00
eljackjoestar-cmd 2ae227ef85 fix(agc): integrate merged position exports and request version 21 2026-10-13 13:03:12 +04:00
eljackjoestar-cmd 78ad5d9dfb fix(agc): validate dynamic layer limits and host subgroup size 2026-10-13 13:01:17 +04:00
Dean 2a2a3dc650
Merge pull request #4226 from MarcZX100/codex/carry-wcsftime-3625
feat(libc): implement wcsftime
2026-10-13 22:01:10 +13:00
Dean 90285be0af
Merge pull request #4367 from MarcZX100/codex/carry-windows-alias-4260
fix(libc): alias non-contiguous shared guest views on Windows
2026-10-13 22:00:55 +13:00
eljackjoestar-cmd 13c4a2a6c6 fix(agc): preserve main interfaces in the layered rendering rebase 2026-10-13 13:00:51 +04:00
Dean 48c48ffd9e
Merge pull request #4390 from ErenYeager1836/fix/pngdec-validate-size-and-max-width
fix(libScePngDec): validate this_size on create
2026-10-13 22:00:41 +13:00
eljackjoestar-cmd a103e48c8b test(agc): use the current fragment push slot in layered readback 2026-10-13 13:00:28 +04:00
eljackjoestar-cmd 93a090ed8d test(agc): retain array-view checks after merge 2026-10-13 13:00:28 +04:00
eljackjoestar-cmd 60a1649e76 fix(agc): render all attachment layers with dynamic rendering 2026-10-13 13:00:28 +04:00
alfalfa-star 0d61dad293 docs(agc): record the DCC key extent assumed for 3D color targets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:28 +04:00
alfalfa-star 425a4daf16 fix(agc): report layer exports as ignored only when a draw does not route them
The warning fired for every draw with PA_CL_VS_OUT_CNTL layer bits,
including layered draws that now route the render target index. It is
checked after the color targets are decoded and leaves out the routed
bits and the misc vector enable. The comment that said layer exports
are never routed is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:28 +04:00
Dean 501f4f5aa7
Merge pull request #4835 from MarcZX100/fix/agc-init-version-error-4808
fix(agc): return error for unsupported init versions
2026-10-13 22:00:27 +13:00
Dean 02d4839b60
Merge pull request #4584 from xXPBSXx/fix/agc-line-strip-primitive
fix(agc): decode primitive type 3 as a line strip
2026-10-13 22:00:05 +13:00
alfalfa-star dd70286889 test(agc): draw a layered volume through the driver
agc_driver_layered_volume_submit submits a DCB whose context registers
alone describe a 3D render target (CB_COLOR_ATTRIB3 resource type 3D,
CB_COLOR_VIEW slices 1-3) and set PA_CL_VS_OUT_CNTL USE_VTX_RENDER_
TARGET_INDX, with both shaders registered before the draw. It checks
that each band lands in its own slice. Removing the layer export from
the vertex stage info in Driver::draw, or the bit-18 read in
DecodeState, fails it. Two more runs draw the volume under DCC keys:
uncompressed keys stay uncompressed, and 0000 fast-clear keys leave
every texel the draw does not write at the clear color and end
uncompressed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:04 +04:00
alfalfa-star 97d192eeae test(agc): pass the GPU flag to Mutation::Add in the layered volume test
Main added the GPU-visible argument to GuestAllocations::Mutation::Add.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:04 +04:00
alfalfa-star 1055617751 feat(agc): render layered draws into 3D color targets
A color view of several depth slices of a 3D target (CB_COLOR_VIEW
SLICE_START < SLICE_MAX) was rejected as "color views of several array
slices are unsupported". Titles render such volumes with one draw: an
instanced draw whose vertex stage picks each primitive's slice through
the misc position export (PA_CL_VS_OUT_CNTL USE_VTX_RENDER_TARGET_INDX
with VS_OUT_MISC_VEC_ENA).

Depends on #423, which carries PA_CL_VS_OUT_CNTL to the recompiler.

- DecodeColorBuffer takes the slice range of a 3D target
  (ColorTarget::layers, clamped to the surface's last slice), allows DCC
  on 3D targets, and sizes a tiled 3D target by its whole surface
  (SurfaceForTarget moves to State.cpp for that). DecodeState sets
  State::renderLayers and, for a layered draw, USE_VTX_RENDER_TARGET_INDX
  (State::layerExports); color targets of different slice counts and
  layered draws with a depth target throw.
- The driver adds State::layerExports to the PA_CL_VS_OUT_CNTL bits #423
  passes (it already passes VS_OUT_MISC_VEC_ENA), so the render target
  index reaches gl_Layer only for layered draws and stays ignored
  elsewhere as before.
- A vertex stage writing gl_Layer declares ShaderViewportIndexLayerEXT
  and SPV_EXT_shader_viewport_index_layer when the device offers them
  (the device enables VK_EXT_shader_viewport_index_layer when present),
  instead of SPIR-V 1.5 ShaderLayer, which a Vulkan 1.1 device cannot
  take. Graphics validation accepts the capability, the extension and a
  vertex Layer output when it is enabled, and a per-primitive Layer
  output of a mesh stage.
- The resident target's attachment view is a 2D array view of the
  slices, and the framebuffer has that many layers (Framebuffer and
  AcquireFramebuffer take the layer count).

The execution test agc_driver_layered_volume_draw draws three bands,
one per layer, into slices 1..3 of a 4-slice 64 KiB-tiled volume and
reads every slice back: slice 0 stays untouched and slice 1 + L holds
only band L. It also recompiles a mesh program with the layer export and
validates it against the layered state. The mesh case does not draw:
lavapipe's LLVM backend aborts compiling that mesh pipeline ("Cannot
emit physreg copy instruction"), which a test cannot catch. On RADV the
same mesh draw renders the bands into the right slices.

PPSA14632 (LEGO Horizon Adventures) renders its 64x64x64 RGBA16F
translucency lighting volumes (two DCC targets, 64 instances) this way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:04 +04:00
Sanjaysrinivas 3f314030b4 fix(agc): use storage factory in snapshot test 2026-10-13 10:59:20 +02:00
Sanjaysrinivas a296b9cacf fix(agc): register snapshot fixture GPU memory 2026-10-13 10:59:20 +02:00
Sanjaysrinivas 65eb466ddd fix(agc): avoid snapshot upload PR conflict 2026-10-13 10:59:20 +02:00
Sanjaysrinivas e2ee9430bf perf(agc): allocate CPU snapshots on demand 2026-10-13 10:59:20 +02:00
Celegans12 23342340cc feat(agc): implement quad tessellation with counter-clockwise output and level clamps
DecodeShaderStages accepted only clockwise triangle patches and no HOS
level clamp narrower than [1, 63], so terrain drawn as TRI_CCW quad
patches with VGT_HOS_MAX_TESS_LEVEL 9 aborted. Quad domains and TRI_CCW
now decode and emit Quads and VertexOrderCcw; isolines, other
partitionings and point or line output still throw.

VGT_HOS_MIN/MAX_TESS_LEVEL are read per draw and pushed in the last 8
bytes of the pre-rasterization push constant slot, where the hull reads
them to clamp its factors; they stay out of every key and the recompile
request, so one hull serves every level. Zero, negative and NaN outer
factors still cull; clamps outside 0 <= min <= 63, max >= 1,
min <= max throw. Quad hulls write four outer and two inner factors at
a 24-byte stride, the layout Mesa's TF ring store uses.
2026-10-13 10:56:16 +02:00
Rick 9ceb6d144d fix(libkernel_sys): reach rename through a _nid_no_patch export
renameat (2a8e7452) calls rename_nid_postfix when both paths are
AT_FDCWD-relative or absolute. Rename.cpp is built into both libc and
libkernel, and on Windows libkernel_sys bound that import to libkernel,
which comes first on its link line. The NID patcher then rewrote the
import to rename's NID, which only libc exports, as libkernel keeps its
copy under the plain name. So libkernel_sys.prx fails to load with error
127 on Windows, and with it every title that imports it. Linux resolves
the symbol globally and was not affected.

rename's body is now RenameGuestPath_nid_no_patch, which both libraries
export under that name, and renameat calls it, the way libkernel_sys
already reaches libkernel's other helpers. rename_nid_postfix forwards
to it, so its behaviour is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 10:48:14 +02:00
Rick 3fe04d4b72 feat(libc): implement atanhf
Dusklight (PPSA99305) imports atanhf (cPGyc5FGjy0) from
libSceLibcInternal.prx; the libc.prx it bundles only has a zero-return
stub for it.

The implementation follows the atanhf of the libc.prx bundled with
PPSA28319 (0x438a0), read from its disassembly: FreeBSD msun's
e_atanhf.c calling msun's s_log1pf.c, both as of 59bc2f09 (stable/9,
the revision the sinhf port took expm1f and expf from). The log1pf
paths for the positive arguments atanhf passes are inlined, as the
exported log1pf is still the host's. For |x| > 1, the infinities and
NaN, Sony's error helper raises FE_INVALID and sets errno to EDOM
before returning (x - x) / (x - x); for |x| = 1 it sets errno to
ERANGE before returning x / 0. Other arguments leave errno alone.

Running that code from the title's libc.prx on a Windows host gives
the same bits, errno and floating-point exceptions as the built
libc.prx for all 2^32 arguments, and msun's own e_atanhf.c and
s_log1pf.c built with the host compiler give the same bits. With the
host log1pf instead of the msun one, 4.7% of the arguments in [0, 1)
differ. guest_math pins bits for every branch reachable from atanhf,
both signs, and the errno and exception behaviour.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 10:48:06 +02:00
Dean 4bb2c7f394
Merge pull request #423 from MichonGoddijn231849/feat/clip-cull-distances
feat(agc): vertex clip and cull distances
2026-10-13 21:47:43 +13:00
Adrià b87708353c
Merge pull request #5017 from adriadam10/feat/proprietary-voice-chat-helper
feat(libSceProprietaryVoiceChatHelperPrx): implement the voice chat helper lifecycle
2026-10-13 10:47:32 +02:00
Dean Galvin e5449936f4 fix(agc): keep CPU writes when a pending storage result is stored over untracked memory
On memory outside the write watch a write-back compares guest memory with
the bytes saved at the upload and leaves the 64 KiB blocks that differ to
the CPU. A store of some of an image's units marked the whole saved copy
invalid, and a fill turned into an image clear or an image copy never
saved the bytes under it, so the next store had nothing to compare and
wrote its layers whole over what the CPU had written since.

The saved copy is now valid per tracked unit. A store, a drop or a stale
eviction invalidates only its own units, a CPU-path store makes the units
it stored valid again, and the write-back compares each run of selected
units that still has saved bytes. FillClear and CopyFrom save the bytes
of the units they fill when the surface is not watched, as an upload does.

Results over a range that left the watch while they were pending still
have no saved bytes; storing them now throws instead of overwriting the
CPU's writes (technical debt).
2026-10-13 21:46:12 +13:00
Dean d379bfd014
Merge pull request #5022 from oneandonlydean/up4/bda-read-selection-nvidia
fix(shader): load a BDA span in place from its own function
2026-10-13 21:44:21 +13:00
Dean Galvin 8c04efcfbc fix(shader): load a BDA span in place from its own function
read_bda_span chooses between the span's in-place dword loads and its
per-dword byte fallback, which calls read_bda_dword_bytes. Each shape of
that choice so far hangs a group of NVIDIA drivers: a dispatch never
finishes and the device is lost.

As the two arms of one selection, with the loads emitted in the arm,
it hung drivers 591.86 and 590.48.01 when lanes took both arms (#3291).
As two selections, the loads under the span's condition and the fallback
under its negation (#4759), it hangs 595.97, 596.36, 610.62, 610.74 and
610.57.04 (#4975).

The loads now live in load_bda_span, a DontInline function that calls
nothing, and read_bda_span is one selection again: one arm calls
load_bda_span, the other is the fallback as before. Probe, loads,
lookups, faults and stops are unchanged for every lane.

On an RTX 4090 (Linux, driver 610.57.04) the two-selection shape hangs
agc_driver_scalar_wide_loads and agc_driver_flat_apertures_0..13 in a
build with SPIRV-Tools, and agc_driver_bda_span_reads and
agc_driver_pixel_interlock in a build without. All pass with this
change in both builds. Drivers 591.86, 590.48.01, 595.97, 596.36,
610.62 and 610.74 are not tested; a shape with the loads in their own
DontInline function was reported passing on 591.86 in #3291.

agc_shader_memory now requires that a span read function holds no load
and calls its load function once, and that the load function holds the
four loads and no call.

Refs #4975
2026-10-13 21:40:18 +13:00
Michon a72dcb93aa test(agc): clip and cull distances drawn through the device and the driver
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:37:49 +13:00
Michon 04138f50ad feat(agc): vertex clip and cull distances
PA_CL_VS_OUT_CNTL's clip and cull enables and its misc and distance vector bits reach the recompiler's vertex info (request version 20), the draw key, the vertex-info comparison and the recompile cache key. Registration and draw-time preparation set them the same way the draw does; a program without pos1..pos3 exports gets none, so its artifact does not depend on the register. Guest clip distances share main's gl_ClipDistance array with the all-zero position distance; cull distances need shaderCullDistance. Up to eight clip and cull distances are accepted.

Co-Authored-By: john.maia <hi@johnmaia.dev>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:37:49 +13:00
Matheus Lima da Silva e933285d82 feat(libSceProprietaryVoiceChatHelperPrx): implement the voice chat helper lifecycle
Initialize and Terminate track whether the library is initialized, and
SetVoiceChatState returns success without recording anything: the host has
no system voice chat for a title's own voice chat to announce itself to,
and the state argument is unknown.

GetVoiceChatUsageState keeps throwing. With the state argument unreadable
there is no recorded usage state, and whether the console returns the state
or writes it through an output pointer, and how it encodes it, is unknown.

Misuse throws rather than guessing an error code, as the library's error
codes are unknown.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 10:34:39 +02:00
Adrià cac4e9688f
Merge pull request #4960 from fdfawef324/feat/libSceDbgThreadSanitizer
feat(libSceDbgThreadSanitizer): declare the TSan annotation calls and report the sanitizer disabled
2026-10-13 10:34:04 +02:00
Adrià 20890629f2
Merge pull request #4893 from Lorkydey/feat/audioout2-user-supported-attributes
feat(audioout2): report supported user attributes
2026-10-13 10:31:46 +02:00
Adrià cee54cdade
Merge pull request #4918 from Feuris/feat/libc-mtx-init-with-name
feat(libc): implement _Mtx_init_with_name
2026-10-13 10:30:43 +02:00
Santhosh Kumar 44623222c9 fix(agc): exclude skipped vertex records from target alias checks 2026-10-13 14:00:11 +05:30
Adrià 5f426f9478
Merge pull request #4902 from nicolasfalesy/feat/videoout-delete-vrr-status-event
feat(libSceVideoOut): implement sceVideoOutDeleteVrrActiveStatusEvent
2026-10-13 10:29:25 +02:00
Claude 13444c82e9
fix(libc): print NaN without a sign in the guest printf formatter
The guest formatter handed NaN to the host's snprintf, which prints
-nan for a negative NaN and +nan or " nan" with the + and space flags.
FreeBSD 11's vfprintf clears the sign of every NaN and prints nan (NAN
for the upper-case conversions), keeping the width and the - flag and
ignoring 0. The formatter now does the same for a, e, f and g and
their long double forms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-13 08:26:41 +00:00
Dean ad653c9bc3
Merge pull request #5015 from oneandonlydean/up4/fix-fmin-zero-sign
fix(libSceLibcInternal): return the negative zero from fminf for zeros of different sign
2026-10-13 21:25:42 +13:00
Dean 637ade873e
Merge pull request #5014 from oneandonlydean/up4/fix-windows-patched-libraries
fix(libc): hand out the unwind cursor table through a _nid_no_patch export
2026-10-13 21:25:34 +13:00
lorkydey 87ee82410b feat(audioout2): report supported user attributes 2026-10-13 10:24:04 +02:00
Adrià e96bfffa59
Merge pull request #4877 from jagrat04/feat/libkernel_sys-renameat
feat(libkernel_sys): implement renameat
2026-10-13 10:23:01 +02:00
jagrat Singh 2a8e745267 feat(libkernel_sys): implement renameat 2026-10-13 10:19:35 +02:00
Adrià 4899658e87
Merge pull request #4882 from Lorkydey/feat/libkernel-coredump-stop-info-cpu
feat(libkernel): answer CPU stop-info queries outside the coredump handler
2026-10-13 10:19:19 +02:00
Adrià db3d379c87
Merge pull request #4870 from jagrat04/feat/libkernel_sys-unlinkat
feat(libkernel_sys): implement unlinkat
2026-10-13 10:16:19 +02:00
Adrià 534451426a
Merge pull request #4860 from jagrat04/feat/libSceNpEntitlementAccess-pft-flag
feat(libSceNpEntitlementAccess): implement sceNpEntitlementAccessGetPftFlag
2026-10-13 10:13:42 +02:00
Dean Galvin cd9573b41c fix(libSceLibcInternal): return the negative zero from fminf for zeros of different sign 2026-10-13 21:12:48 +13:00
jagrat Singh 55ddffe269 feat(libkernel_sys): implement unlinkat 2026-10-13 10:11:24 +02:00
Adrià bd826d52f8
Merge pull request #4816 from winstrex/feat/agc-get-gs-prim-payload
feat(libSceAgc): implement sceAgcGetGsPrimPayload
2026-10-13 10:11:08 +02:00
Dean Galvin 3a9cc63683 fix(libc): hand out the unwind cursor table through a _nid_no_patch export
_Unwind_Context took its cursor table from LibcUnwind::CursorSlots, a C++
data symbol of libc. On Windows LibcUnwind::Frame holds an _Unwind_Context,
so libSceAgcDriver, which builds frames for its worker sampler, imported
_ZN10LibcUnwind11CursorSlotsE from libc.prx. The NID patcher renames that
export in the patched libc and leaves the import alone, so the patched
libSceAgcDriver, libSceAgc and libSceVideoOut failed to load with error 127
and patched_libraries failed. The table is now file-local and the context
reads it through UnwindCursorSlots_nid_no_patch, which keeps its name on
both sides.
2026-10-13 21:09:26 +13:00
Adrià Franch dd5853d8bc docs(debt): cite the source of sceNpEntitlementAccessGetPftFlag 2026-10-13 10:08:30 +02:00
jagrat Singh 7900b59d52 feat(libSceNpEntitlementAccess): implement sceNpEntitlementAccessGetPftFlag
Reports a full game, not a Play First Trial: 0 is written to the uint32_t output and 0 returned; a null pointer returns 0x817D0002.

Signature, value, one-word write and error match sceNpEntitlementAccessGetGameTrialsFlag and sharpemu src/SharpEmu.Libs/Np/NpEntitlementAccessExports.cs:98-116. 0x817D0002 is the parameter error of KytyPS5, prosper and sceNpEntitlementAccessGetEntitlementKey. The library has no initialization state, so none is checked. Update technical debt documentation and add unit test in guest_offline_imports.
2026-10-13 10:08:24 +02:00
Adrià 6e83cad175
Merge pull request #5010 from adriadam10/chore/remove-reactnative-vsh
chore(libs): remove libReactNative.Modules.Vsh and its WebAppLauncher
2026-10-13 10:05:48 +02:00
Adrià Franch ed79ae1a2f chore(libs): remove libReactNative.Modules.Vsh and its WebAppLauncher
They are modules of the console's own shell (VSH), not libraries a title
links. No title in the open PRs, discussions or code references them, and
every export is a stub that throws. Their Share*, ErrorDialog* and
SystemServiceParamGet* exports duplicate libSceShare, libSceErrorDialog
and libSceSystemService (#228, #241).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 10:04:28 +02:00
Adrià c080fc2d5b
Merge pull request #4786 from p3n1cillin/claude/load-exec-restart
feat: convert a title's other executables and start them with sceSystemServiceLoadExec
2026-10-13 10:02:50 +02:00
pushkarreddyy 837c862e22 feat(libkernel): implement sigpending 2026-10-13 13:30:35 +05:30
Adrià 4834930ea6
Merge pull request #4784 from freshstart2066-create/feat/libc-floor
feat(libc): implement floor
2026-10-13 09:59:40 +02:00
MarcZX100 7bb59f73b4 docs(libSceLibcInternal): record spawn import stops 2026-10-13 07:56:19 +00:00
David 2fe382c658 feat(libSceLibcInternal): implement the posix_spawn attribute and file action calls
XPSemu (PPSA97358) imports eleven posix_spawn calls from
libSceLibcInternal.prx, one reference each, and no call site is known.
Predicted stop (import order, not measured), behind isnanf (idx 296):
`FAIL: unresolved ELF import 44hlATrd47U from libSceLibcInternal.prx`
(posix_spawnattr_init, idx 347), followed by p--TkNVsXjA, LG6O0oW9bQU,
Sj7y+JO5PcM, Ud8CbISKRGM, Hq9-2AMG+ks, IUfBO5UIZNc, Heh4KJwyoX8 (348-355),
UV4m0bznVtU, Ani6e+T-y6Q and 3Nr9caNHhyg (379-381).

The nine object calls follow FreeBSD release/11.4.0 (8c639945a445)
lib/libc/gen/posix_spawn.c: posix_spawnattr_init allocates a zeroed
attribute object (ENOMEM on failure), _destroy frees it, _setflags and
_setsigdefault store the value; posix_spawn_file_actions_init allocates an
empty list (-1 on failure, as in 11.4), _destroy frees it with its
entries, and _addopen (path copied), _adddup2 and _addclose append in
order after returning EBADF for a negative descriptor. Null or
uninitialized objects, which FreeBSD dereferences, throw.

posix_spawn and posix_spawnp throw "process creation not implemented",
as execvp does: FreeBSD validates no argument before rfork/vfork, and no
child process can be created here. TechnicalDebt records them.
guest_posix_spawn covers the attribute round trips, the recorded action
sequence, the EBADF cases and the throws. NIDs computed with
tools/nid_names.py and present in aerolib.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 07:56:19 +00:00
Kikoarl 396b5189a9 perf(agc): disambiguate completion label overlaps in CopyBuffer 2026-10-13 09:56:09 +02:00
Adrià 42e5edadbe
Merge pull request #4708 from Aathish-D/feat/savedata-get-convert-progress
feat(libSceSaveData): implement sceSaveDataGetConvertProgress
2026-10-13 09:56:01 +02:00
Adrià 0587a755d6
Merge pull request #4775 from p3n1cillin/claude/pad-angular-velocity-deadband
feat(libScePad): apply the angular velocity deadband
2026-10-13 09:55:21 +02:00
Adrià a527286241
Merge pull request #4743 from jagrat04/feat/libkernel_sys-mkdirat
feat(libkernel_sys): implement mkdirat
2026-10-13 09:55:12 +02:00
eljackjoestar-cmd 68c2b0f746 fix(agc): prioritize demanded shader preparation 2026-10-13 11:54:29 +04:00
p3nicillin 99efb32c40 feat: convert a title's other executables and start them with sceSystemServiceLoadExec
The relinker's repeatable --executable converts another executable of the
title beside the output, sharing its libs, app0 and guest modules.
sceSystemServiceLoadExec maps /app0 paths to those executables, passes the
argument list on the new command line and finds the main executable through
APS5_EBOOT. RetroArch (PPSA99169) starts its frontend picker this way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:51:50 +01:00
David a7ea2bdcaa feat(libkernel): add mlockall
mlockall (x7g7Ebeo8-U, aerolib) follows FreeBSD 11.4's sys_mlockall
(sys/vm/vm_mmap.c:1067-1131, freebsd-src
8c639945a44588f6cfdddfb057bb5aad88735da0) for the flag check: 0 or
bits outside MCL_CURRENT (1) and MCL_FUTURE (2) fail with EINVAL. The
title process is taken to pass priv_check(PRIV_VM_MLOCK), as mlock
already does.

Valid flags throw. MCL_FUTURE would need every later guest and host
mapping to be locked; nothing in the mapping paths does that, and
passing it to the host would make later host allocations fail above
the host RLIMIT_MEMLOCK. MCL_CURRENT would have to lock the whole
process, which on AnyPS5 also holds host stacks, heaps, modules and
driver mappings (sceKernelVirtualQuery reports them as guest memory),
with ENOMEM above a guest RLIMIT_MEMLOCK that is not known (getrlimit
throws for it). mlock pins host pages (VirtualLock, mlock) range by
range and keeps no record, so there is no state to extend. Recorded
in TechnicalDebt.

XPSemu (PPSA97358) calls it only with -overcommit mem-lock=on
(ZiZc3/XPSemu 8eec07b4, os-posix.c:330-355, reached from
system/vl.c:831-835):

    int flags = MCL_CURRENT | MCL_FUTURE;
    ...
    ret = mlockall(flags);
    if (ret < 0) {
        error_report("mlockall: %s", strerror(errno));
    }

and exits on failure, so that option ends in the throw; without it
the title is not affected. Predicted stop removed (registry index 201,
not run):
FAIL: unresolved ELF import x7g7Ebeo8-U from libkernel.prx

guest_mlock checks EINVAL for 0, 4, 5 and -1 and the throw for 1, 2
and 3.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 87f4aa331b02d098fe27516809495447ff6a9fa6)
2026-10-13 07:51:35 +00:00
Arekatla Nishanth Chowdary ebd71eb2e7 test(libSceRudp): follow the End/Terminate swap in the context query and pump tests
guest_rudp_context_queries released its context with sceRudpEnd(context)
and shut the library down with sceRudpTerminate(), and guest_rudp_pump
shut it down with sceRudpTerminate(), the order main had before the
swap. They now call sceRudpTerminate(context) and sceRudpEnd().
2026-10-13 07:51:20 +00:00
David 4029b5d2c9 fix(libSceRudp): swap sceRudpEnd and sceRudpTerminate
sceRudpEnd takes no argument and shuts the library down; sceRudpTerminate
takes a context id and releases that context. main had the two swapped,
so a title calling sceRudpEnd() passed a stray register as a context id
and the library stayed initialised.

Evidence:
- PS5 console measurement by hugodearaujo in #3073 (firmware 13.60):
  "After `sceRudpEnd()` all three return `0x80770001` again"
  (sceRudpGetMaxSegmentSize, sceRudpProcessEvents, sceRudpNetFlush).
- AlexAltea/orbital cc408367da6a6ebbf5ac1ac559fefeadc73b2831
  tools/ida/db_types.json:1971,1996: `int sceRudpEnd(void)`,
  `int sceRudpTerminate(int ctxId)`.
- RPCS3 26098fb42d090cd522b4afcf3b754dd987c82978
  rpcs3/Emu/Cell/Modules/cellRudp.cpp:108: `cellRudpEnd()` tears the
  library down.
- AndnixSH/Vita3K-PS-Vita-OS-Fork c86ac732bc811057e690bed280c5076bdf95385e
  vita3k/modules/SceRudp/SceLibRudp.cpp:1032,1614: `sceRudpEnd()` clears
  every context, `sceRudpTerminate(int ctx_id)` erases one.

The bodies are unchanged, only exchanged. guest_offline_imports now ends
the library with sceRudpEnd(), and guest_rudp_status checks that
sceRudpTerminate releases one context and sceRudpEnd all of them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 07:51:20 +00:00
MarcZX100 a4752266b8 docs(libc): record Windows directory fopen behavior 2026-10-13 07:51:09 +00:00
MarcZX100 eefe4db018 fix(libc): return native fopen errors 2026-10-13 07:51:09 +00:00
Blazing Cook acd8c8c527 test(ampr): retain both pooled-page ownership regressions
Restore the original alias-remap regression alongside the pool-exhaustion
regression so each ownership fix remains covered after combining the series.
2026-10-13 07:50:53 +00:00
MarcZX100 45a157d931 fix(ampr): reserve remap targets before releasing pool pages 2026-10-13 07:50:53 +00:00
Blazing Cook a88a4c8e15 fix(ampr): reserve targets before releasing mapped pool pages
(cherry picked from commit 3e9a37eaa34c4d543f775bece42de3eb7a4647ea)
2026-10-13 07:50:53 +00:00
Blazing Cook ce98f83033 fix(ampr): retain pooled pages while remapping aliases
(cherry picked from commit a2408b5244c663849ae4f7022e7aff01f2ba5abc)
2026-10-13 07:50:53 +00:00
Aathish b078f475ec feat(libSceSaveData): implement sceSaveDataGetConvertProgress 2026-10-13 09:48:44 +02:00
Adrià fc1f063f51
Merge pull request #4622 from jagrat04/feat/libkernel-lutimes
feat(libkernel): implement lutimes
2026-10-13 09:48:07 +02:00
Adrià a24da90293
Merge pull request #4990 from 0xRickx/fix/libc-dynamic-cast-guest-abi
fix(libc): call __dynamic_cast and __cxa_init_primary_exception with the guest ABI
2026-10-13 09:46:33 +02:00
Adrià 8da50fd052
Merge pull request #4592 from leorossi2005/feat/libkernel_libSceLibcInternal_missing_ppsa99902
feat(libSceLibcInternal): implement fminf required for PPSA99902 startup
2026-10-13 09:46:04 +02:00
Adrià a373241de4
Merge pull request #4582 from jagrat04/feat/libkernel-madvise-madv-protect-pr
feat(libkernel): accept MADV_PROTECT advisory hint in madvise
2026-10-13 09:44:18 +02:00
MarcZX100 3e238ff11d fix(shader): accept MIMG sampler tuples ending in VCC 2026-10-13 07:40:17 +00:00
MarcZX100 8d455b8018 docs(libkernel): qualify atfork title reachability 2026-10-13 07:39:26 +00:00
David e8fd542f03 feat(libkernel): implement pthread_atfork
pthread_atfork (U9t5kJAWPnA, aerolib) appends the prepare, parent and
child handlers to a process-wide list under a lock and returns 0, or
ENOMEM when the entry cannot be allocated, as FreeBSD 11.4's
_pthread_atfork does (lib/libthr/thread/thr_fork.c:78-100, freebsd-src
8c639945a44588f6cfdddfb057bb5aad88735da0). Null handlers are accepted;
FreeBSD's fork skips them. The handlers never run: fork
(libkernel_sys) throws and vfork is absent, so no child process is
created. The list is kept, in registration order, for a later fork
(PthreadAtforkHandlers_nid_no_patch); recorded in TechnicalDebt.

XPSemu (PPSA97358) calls it from a constructor, before main
(ZiZc3/XPSemu 8eec07b4, util/rcu.c:488-495):

    static void __attribute__((__constructor__)) rcu_init(void)
    {
        smp_mb_global_init();
    #ifdef CONFIG_POSIX
        pthread_atfork(rcu_init_lock, rcu_init_unlock, rcu_init_child);
    #endif
        rcu_init_complete();
    }

so a throwing export would stop the title before main. Predicted stop
removed (registry index 255, not run):
FAIL: unresolved ELF import U9t5kJAWPnA from libkernel.prx

guest_pthread_atfork checks registration order, null handlers,
concurrent registration from four threads and that no handler runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit f37bccf6efd76edcde5d582800c89b83fdd30472)
2026-10-13 07:38:13 +00:00
Rick 7154138369 test(libc): register guest_dynamic_cast beside guest_array_callbacks
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 09:36:52 +02:00
Adrià 6d7b873702
Merge pull request #4945 from nicolasfalesy/fix/libc-unwind-backtrace-cursor
fix(libc): let a title's own libunwind read _Unwind_Backtrace frames
2026-10-13 09:36:45 +02:00
ideabsassistenza 6fb0727c48 fix(debt): preserve leading entries during conflict resolution 2026-10-13 09:34:22 +02:00
Yunus Shaikh 60fb528572 refactor(libSceNet): place the SCE timeout constants and timeout_option apart from new neighbours
#4988 adds a constant after NET_SO_RCVTIMEO and #4989 adds a function before receive_error, so defining the SCE timeout constants and timeout_option there made the pull requests conflict.
2026-10-13 00:34:06 -07:00
David 78ffdf0521 feat(libc): implement wcsftime
PS5MC (PPSA99420) imports wcsftime (XbVXpf5WF28, aerolib) as the last
absent libSceLibcInternal import in its registry order (index 481), so
its predicted stop is:

    FAIL: unresolved ELF import XbVXpf5WF28 from libSceLibcInternal.prx

The stop is predicted from the import order, not observed in a run.

wcsftime follows FreeBSD 11.4 lib/libc/locale/wcsftime.c (8c639945):
the wide format is converted with wcsrtombs, strftime fills a buffer of
maxsize * MB_CUR_MAX bytes limited to maxsize, and mbsrtowcs converts
the result back. A format unit that the single-byte C locale of
lib/libc/locale/none.c cannot encode returns 0 with EILSEQ, a maxsize
whose product with MB_CUR_MAX overflows returns 0 with EINVAL, and an
empty or overflowing result or a null buffer returns 0 with errno
unchanged; the ERANGE or EINVAL set by the Windows CRT's strftime is
discarded. A null format or tm throws, as FreeBSD dereferences it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 07:33:58 +00:00
Adrià 9ba9ac1c8b
Merge pull request #4834 from GoodTimeclubs/feat/exports-ppsa01317
feat(exports): export the calls PPSA01317 imports
2026-10-13 09:33:36 +02:00
MarcZX100 c4a61eae75 docs(agc): document shared view alias coverage 2026-10-13 07:31:42 +00:00
Angel21PC 2d1cc82bce fix(libc): alias non-contiguous shared guest views on Windows
On Windows, a host import of shared guest memory goes through a
read-write alias from WindowsMappings::MapAlias. The alias was a single
MapViewOfFile3 of one section, so MapAlias threw when the views of the
range were not one contiguous run of that section. One
sceKernelMapDirectMemory over two adjacent direct memory allocations
gives such a range, as each allocation has its own section, and so do
pages remapped in place to another physical order.

Such ranges are now aliased run by run: a placeholder reservation is
split at each run of consecutive pages of one section, and each run
maps that section at its offset. UnmapAlias unmaps every run and forgets
the alias even when one unmap fails. A contiguous range keeps the single
view, and a range with a page that is not a shared view is still
refused.

guest_memory maps two adjacent direct memory allocations of two pages
and one page with one call, and two pages in reverse physical order.
It reads and writes every page through the alias. The first test also
checks that the alias has one view per run and that unmapping it frees
its pages. A range whose second page is flexible memory is still
refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit fc6868d8b6fedd0a43987909cadef618f16ada4b)
2026-10-13 07:31:17 +00:00
Adrià 30caf2b2a5
Merge pull request #4682 from bubblegg/feat/netctl-get-if-stat
feat(libSceNetCtl): add sceNetCtlGetIfStat
2026-10-13 09:26:47 +02:00
Adrià 6c87f76b57
Merge pull request #4753 from p3n1cillin/claude/audio-out-ex-monitor-info
feat(libSceAudioOut): list no audio descriptors in sceAudioOutExGetMonitorInfo
2026-10-13 09:26:42 +02:00
Ondra97 74e239408b fix(tools): reject mixed high refresh skip diagnostics 2026-10-13 09:25:12 +02:00
p3nicillin 3292cd39fb feat(libSceSystemService): relaunch the title for sceSystemServiceLoadExec of its own eboot.bin
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:25:03 +01:00
Ondra97 7ebad36341 fix(tools): accept skips on displays without 120 Hz 2026-10-13 09:19:28 +02:00
babyhuey 0daf1d5ba3 fix(libSceNet): report SO_ERROR from sceNetGetsockopt 2026-10-13 03:15:45 -04:00
Ondra97 6706e44d95 fix(conventions): ignore C++ raw literal contents 2026-10-13 09:15:31 +02:00
Doston Abdullayev 9cdcd58171 fix(libkernel): use precise waits for empty poll sets
Carry the poll-only portion of Doston Abdullayev’s public #1471 fix, preserving its original authorship. The select timeout change is already covered separately by #4754.
2026-10-13 07:11:08 +00:00
Rick f4be0362c8 fix(libc): call __dynamic_cast and __cxa_init_primary_exception with the guest ABI
Every other export of the C++ runtime is declared APS5_VABI, but these two
were not. On Windows a title calls them with the System V convention while
they read the Microsoft one, so __dynamic_cast took its fourth argument, the
src2dst hint, as the source pointer: an ordinary downcast (hint 0) returned
null, and the negative hints made it read through an invalid pointer.

guest_dynamic_cast calls both through APS5_VABI declarations, as a title
does, and checks a single-inheritance downcast, a failed cross cast, a cast
of a base object and the header __cxa_init_primary_exception fills in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 09:10:08 +02:00
Alex Collini a141b8eb67 fix(agc): drop the comment in SrgbTwinFormat
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:09:33 +02:00
TideOPL 007721d43e perf(shader): index specialization tables by SPIR-V id
SpecializeSpirv kept its per-id data (folded values, result types, scalar
types, removed ids, blocks, edges, copies, definitions and the live sets) in
std::map and std::set, and repeats fold, prune and copy propagation over the
whole module until nothing changes. Ids are dense below the header's bound,
so the tables become vectors indexed by id. Lookups of operands that are
literals rather than ids stay bounds-checked, iteration keeps ascending id
order, and an id at or beyond the bound now throws.
2026-10-13 07:59:08 +01:00
Alex Collini 12d07b8edd fix(agc): give R8 and R8G8 no sRGB twin where the device decodes them in the shader
SrgbTwinFormat mapped R8_UNORM and R8G8_UNORM to R8_SRGB and R8G8_SRGB
without asking the device. Where 8_SRGB or 8_8_SRGB is decoded in the
shader (context.srgbDecodeFormats: the device cannot sample the sRGB
format, or APS5_SRGB_SHADER_DECODE=1), the forced view would have been
created with a format the device cannot sample. Those two formats now
get no twin there, so a FORCE_DEGAMMA sampler on them keeps the refusal.
The image is no longer created MUTABLE_FORMAT either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 08:58:19 +02:00
Dean c2565f0149
Merge pull request #4391 from MarcZX100/codex/socket-descriptors-4366
fix(libkernel): give guest sockets ordinary descriptor numbers below FD_SETSIZE
2026-10-13 19:52:08 +13:00
Dean d71423a575
Merge pull request #4911 from oneandonlydean/up4/atomic-buffer-fault-only
perf(shader): record a specialized buffer atomic's fault without the address tables
2026-10-13 19:52:01 +13:00
fdfawef324 0f77f3e07f feat(libSceDbgThreadSanitizer): declare the TSan annotation calls and report the sanitizer disabled
Black Myth: Wukong (PPSA23226) exits at start-up with
STATUS_DLL_NOT_FOUND after "Failed to load module:
libs\libSceDbgThreadSanitizer.prx". The executable does not need the
library: its bundled sce_module/libSceJobManager_nosubmission.prx lists
it in DT_NEEDED and imports __tsan_acquire and __tsan_release from it,
plus sceKernelIsThreadSanitizerEnabled from libkernel. The relinker
converts every module in sce_module and loads it at start-up (only
--module-dir directories are deferred), and GuestModuleBuilder adds each
module's host dependencies to the executable's DT_NEEDED, which the
Windows start-up stub loads.

libSceDbgThreadSanitizer now exports __tsan_acquire and __tsan_release
with the void(void* addr) signatures of LLVM compiler-rt's
tsan_interface.h (llvm-project 87f0227cb601, LLVM 20.1.8). Both throw:
AnyPS5 runs no race detector, and the module calls them only after
sceKernelIsThreadSanitizerEnabled reports the sanitizer enabled.
libkernel's sceKernelIsThreadSanitizerEnabled returns 0, as
sceKernelIsAddressSanitizerEnabled does; its int(void) signature is
assumed and recorded in the libkernel debt file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:12:05 -04:00
hypermist 6bf9ab86ff perf(agc): keep adjacent ranges one import serves as one region of the cached address space
A V# spanning two registered ranges dissolved the shared space into the
build's own regions (about 10k regions copied, sorted and looked up per
build in Killing Floor 3), although the import covering the run binds it
in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
2026-10-13 19:03:20 +13:00
MarcZX100 c7aca86c61 fix(tools): use the shared merge base for overlap checks 2026-10-13 06:01:10 +00:00
Ondra97 66edc7463f fix(tools): audit stubs from shared library sources 2026-10-13 07:59:10 +02:00
Nicolas Falesy db3ec02c54 fix(libc): let a title's own libunwind read _Unwind_Backtrace frames
Homebrew built with the ps5-payload-dev SDK links LLVM libunwind
statically but imports _Unwind_Backtrace from libSceLibcInternal. Its
trace callback then reads each frame with its own _Unwind_GetIP, which
treats the context as a libunwind cursor and calls through the virtual
table in its first word. AnyPS5's context started with the saved RAX, so
the call jumped to a register value and the title crashed on its error
path (vkQuake, PPSA99010, without its game data). The context now starts
with a table in AbstractUnwindCursor's slot order that answers register
reads and writes, validFloatReg and isSignalFrame; other slots throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EvDbpwUPTa78oCAJqe7m9K
2026-10-13 01:55:14 -04:00
MarcZX100 2a91815210 fix(tools): parse paginated overlap API responses 2026-10-13 05:52:46 +00:00
Dean ea2bbefb2b
Merge pull request #4402 from jakezwang/fix/spirv-redundancy-memory
fix(shader): release bundled optimizer traversal maps
2026-10-13 18:49:45 +13:00
Dean ebf417a51c
Merge pull request #4340 from ArjenS-LA/fix/guest-gnu-hash
fix(relinker): convert guest modules with GNU hash tables
2026-10-13 18:49:38 +13:00
QRMOE 2c23ced70e
test(libc): cover llroundl boundaries in every rounding mode 2026-10-13 07:47:45 +02:00
QRMOE f745a00a5d
fix(libc): accept the full llroundl integer range 2026-10-13 07:46:49 +02:00
LeandroLP23 fa889c063b docs(contributing): ask GPU changes to name the GPUs their tests ran on
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:45:50 +02:00
MarcZX100 abc997b0da fix(agc): release cached address space on device teardown 2026-10-13 05:35:37 +00:00
Dean 88107b7de6
Merge pull request #4535 from moong15/codex/windows-deferred-write-watch
fix(agc): defer write tracking for fresh Windows shared imports
2026-10-13 18:33:39 +13:00
Dean Galvin dbe16fb28c perf(shader): record a specialized buffer atomic's fault without the address tables
A program with an atomic on a V# it specializes was compiled as
address-based (usesDma) so that the atomic's unaligned-address check could
call the fault function. The atomic itself goes through the element's
storage buffer and nothing in such a program reads the address tables. In
the driver an address-based build leases all registered guest memory and
binds every V# of the program in place inside a host import, so none of
its elements is staged in device memory, the atomic one included.

The atomic now asks for the fault buffer alone (usesFaultBuffer) and
records the fault through RecordBdaFaultWords, which has both forms. The
fault record, the stop of the invocation and the atomic are unchanged. The
64-bit fault address needs shaderInt64, as the scalar buffer load does. A
tessellation-control program with such an atomic is refused as before.

Test: agc_shader_memory compiles a buffer_atomic_inc on a specialized V#
and requires no address table binding, one fault buffer, one call of the
fault function, logical addressing and the atomic element flag; without
shaderInt64 the program is refused.
2026-10-13 18:29:59 +13:00
Dean 2cc97e1057
Merge pull request #4197 from MarcZX100/codex/audit-coredump-userdata
feat(libkernel): implement coredump user data calls
2026-10-13 18:24:28 +13:00
babyhuey d39cb2c62f feat(libkernel): report machdep.tsc_freq from sysctlbyname 2026-10-13 01:18:43 -04:00
umang b24e1cc46f perf(agc): return from fresh Windows collection when nothing is fresh
MarkWritten collects the fresh portions of the written blocks before
each GPU stamp. On Windows that walked every shared view of those
blocks twice under the mappings mutex even when all of them had been
collected before. Keep the never-collected portions as address ranges,
as the Linux tracker keeps _fresh, and return after one lookup when
none overlaps the request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:37:27 +05:30
umang b14d087cd4 fix(agc): defer write tracking for fresh Windows shared imports 2026-10-13 10:37:27 +05:30
MarcZX100 c1bcd0b697 fix(videoout): dump linear display buffers 2026-10-13 05:05:31 +00:00
Dean 7e181fcc38
Merge pull request #3874 from oneandonlydean/up4/import-window-compare
perf(agc): compare an import window's pages when the window closes
2026-10-13 17:55:13 +13:00
Dean 597f976872
Merge pull request #3947 from MarcZX100/codex/carry-getsockname-unbound
fix(libkernel): return the zero address for unbound getsockname on Windows
2026-10-13 17:54:53 +13:00
Dean 3cfb915cf0
Merge pull request #4187 from Scramblrrrr/codex/cpu-copy-profile-spans
fix(agc): profile completed CPU copies with their actual spans
2026-10-13 17:54:30 +13:00
Dean e268844924
Merge pull request #4700 from Nesyth/fix/agc-comp-to-single-dcc-reads
fix(agc): read comp-to-single DCC keys of sampled textures and storage images
2026-10-13 17:54:09 +13:00
Dean 52edeeda5d
Merge pull request #4698 from Nesyth/perf/libc-shared-lease-snapshot
perf(libc): share one snapshot of the registered guest ranges between leases until the registry changes
2026-10-13 17:53:47 +13:00
Dean fa39549b50
Merge pull request #4654 from GustavoGamarra95/fix/agc-store-pending-under-written-buffers
fix(agc): store pending storage results before a use writes the same memory as a buffer
2026-10-13 17:53:26 +13:00
Dean 7e2a2ae5f9
Merge pull request #4363 from johnmaia/fix/gpl-shader-rendering
fix(agc): build pipeline library shader parts for the draw's depth format
2026-10-13 17:53:05 +13:00
Dean f9821b76ed
Merge pull request #4097 from kr4y4/fix/shader-f16-ternary-output-modifier
fix(shader): keep the output modifier of the f16 min3, max3, med3 and div_fixup ops
2026-10-13 17:52:50 +13:00
Dean 547807422c
Merge pull request #3471 from Kikoarl/perf/agc-copied-writes-overlap
perf(agc): disambiguate direct GPU writes from copied write-backs in indirect draws
2026-10-13 17:52:29 +13:00
Dean fe8d3eba0e
Merge pull request #3633 from tv7/fix/color-view-array-slice-range
fix(agc): render a color view of several array slices into its first slice
2026-10-13 17:51:58 +13:00
Dean 46e7cd3e83
Merge pull request #4838 from milescoviello/carry/seek-pipe-stream-windows-4837
fix(libc): fail fseek and ftell on a pipe with ESPIPE on Windows
2026-10-13 17:51:37 +13:00
Dean 9b7c8ea965
Merge pull request #4811 from milescoviello/carry/localtime-range-windows-4807
fix(libc): convert local times before 1970 and after 3000 on Windows
2026-10-13 17:51:16 +13:00
Dean e15c316ead
Merge pull request #4733 from fdfawef324/fix/libc-collate-char-capacity
fix(libc): size collate<char>::do_transform strings as the console does
2026-10-13 17:50:55 +13:00
Dean 1268ba8cd4
Merge pull request #4727 from gjonn/feat/hull-dynamic-control-point-index
feat(shader): read hull inputs at a control-point index computed from data
2026-10-13 17:50:35 +13:00
Dean e64bf3c5d0
Merge pull request #4662 from fdfawef324/fix/agc-gpu-map-import-off-guest-stack
fix(agc): import GPU-mapped memory on a host stack
2026-10-13 17:50:12 +13:00
Dean a6ed539e70
Merge pull request #4659 from AaravMehta-07/codex/sce-pthread-sem-overflow
fix(libkernel): guard SCE pthread semaphore count overflow
2026-10-13 17:49:51 +13:00
Dean b7025517e1
Merge pull request #4656 from milescoviello/fix/rwlock-writer-preference
fix(libkernel): hold back new rwlock readers while a writer waits
2026-10-13 17:49:30 +13:00
Dean 4fe3123627
Merge pull request #4640 from MarcZX100/codex/win-host-standard-handles-4631
fix(libc): preserve host standard handles on Windows redirects
2026-10-13 17:49:02 +13:00
Dean 88f48fddef
Merge pull request #4688 from eliyarson/fix/kernel-stat-relinked-prx
fix(libkernel): resolve original .prx names to relinked .guest.prx in stat, open and reachability
2026-10-13 17:48:42 +13:00
Dean 73a5f3a41c
Merge pull request #4667 from babyhuey/feat/libkernel-sysctl-hw-model
feat(libkernel): report hw.model from sysctl
2026-10-13 17:48:21 +13:00
Dean 2ffbb75ae7
Merge pull request #4206 from jweidenh/codex/sparse-buffer-hole-initialization
fix(agc): clear interior holes in recycled sparse buffers
2026-10-13 17:48:00 +13:00
Dean 432675d919
Merge pull request #4252 from karanvora2599/perf/shader-wave32-exec-lane-bits
perf(shader): keep wave32 EXEC and VCC lane bits without a ballot round trip
2026-10-13 17:47:39 +13:00
Dean 02f5c0bcb0
Merge pull request #4195 from rugbedbugg/fix/shader-narrowing-fold
fix(shader): preserve integer narrowing in constant folding
2026-10-13 17:47:18 +13:00
Dean 887953200b
Merge pull request #4248 from hypermoist/perf/guest-buffer-region-index-sort
perf(agc): sort guest buffer regions through indices before merging them
2026-10-13 17:47:05 +13:00
Dean cc55e1410a
Merge pull request #4661 from fdfawef324/fix/libc-shared-write-pages
fix(libc): track writes to shared guest memory per 4 KiB page on Windows
2026-10-13 17:46:44 +13:00
Dean 61e4b5506c
Merge pull request #4017 from jweidenh/codex/host-import-submission-lifetime
fix(agc): defer host import retirement until the GPU lock is held
2026-10-13 17:46:23 +13:00
screamm c8379be922 feat(libkernel): implement the coredump user-data calls
sceCoredumpWriteUserData (Dbbkj6YHWdo) and sceCoredumpDebugTextOut
(dei8oUx6DbU) get the signatures of Orbital's db_types.json (cc408367)
and SharpProspero's KernelSystem.cs (5dfdb4b0): ssize_t (const void*,
size_t) and void (const char*, int). WriteUserData moves out of
Unimplemented.cpp.

User data belongs to the core dump handler: the Vita's
sceCoredumpWriteUserData returns 0x800A0003 ("No user-data capture is
active") outside it (vita-headers e66ebe90), and the RED engine's PS4
crash handler makes every coredump call from its handler (Vangaurd
4f86d2b0). The handler is never invoked here, so WriteUserData returns
SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER (0x81180003, shadPS4
coredump_error.h e0a46675) sign-extended and keeps nothing; a null,
empty or wrapping buffer throws because the order of the console's
checks is unknown. DebugTextOut prints its text to stderr with the
[coredump] prefix; a negative length or a null text with a nonzero
length throws.

Evidence: Broken Sword Reforged (PPSA21480) reaches the family through
its sibling, #3704 comment 6113687542: "`ERROR agc_graphics.cpp:266
Sony call failed with code 8a6c9018` (the resource registration error,
followed by the error dialog), then `sceCoredumpWriteUserString not
implemented`." WriteUserString has no declaration in any source and
keeps throwing; its TechnicalDebt entry records the call. 007 First
Light (PPSA11386) imports WriteUserData (#2523) without calling it
before its stop.

Test: guest_coredump_user_data.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 04:43:50 +00:00
David 097bc538ee fix(libkernel): fail ftruncate on a pipe, a directory and a device with EINVAL on Windows
(cherry picked from commit 971fb51ef3f09d6543ef20e05e4a44572d2aadbd)
2026-10-13 04:18:07 +00:00
Feuris ab4ccade2c feat(libc): implement _Mtx_init_with_name 2026-10-13 01:05:37 -03:00
Hussein Kadhim 0438621d74 fix(libkernel): getsockname on an unbound socket returns the zero address on Windows
FreeBSD's in_getsockaddr and in6_getsockaddr succeed for a socket that has no
local address and return the wildcard address with port 0. Winsock fails
getsockname with WSAEINVAL until the socket is bound, which the guest saw as
EINVAL. Return the zero address of the socket's family for that error.
2026-10-13 17:01:30 +13:00
Dean e51428bf23
Merge pull request #4759 from fdfawef324/fix/bda-span-read-split
fix(shader): split a BDA span read's in-place loads from its fallback
2026-10-13 17:00:47 +13:00
David 2f1eb100a0 fix(libkernel): fail reads on a directory descriptor with EISDIR on Windows
(cherry picked from commit d7ef271ef7eeb1d82a337a0b0a9767ce68721bd4)
2026-10-13 04:00:38 +00:00
Dean 226b40db0f
Merge pull request #2132 from Raghav314/fix/agc-msaa-fill-clear
fix(agc): clear a multisampled target on a uniform fill of its color memory
2026-10-13 17:00:26 +13:00
Dean 3fed8e9bf3
Merge pull request #2121 from Raghav314/fix/agc-cb-resolve-source-samples
fix(agc): take a CB resolve's sample count from its source target
2026-10-13 17:00:06 +13:00
Dean 3779597778
Merge pull request #4658 from milescoviello/fix/sockopt-option-lengths
fix(libkernel): accept socket option values longer than an int
2026-10-13 16:53:12 +13:00
Dean 675c52ca98
Merge pull request #4580 from coltken95/fix/shader-same-target-branch
fix(shader): never branch conditionally to one label twice
2026-10-13 16:52:48 +13:00
Dean a675333d5a
Merge pull request #4600 from coltken95/fix/shader-folded-continue
fix(shader): keep the back-edge of a loop whose continue block is dead
2026-10-13 16:52:24 +13:00
Dean 3a0642e131
Merge pull request #4019 from ahir061/fix/macos-shader-cache-directory
fix(shader): find the shader cache directory on macOS
2026-10-13 16:52:01 +13:00
Dean 0d54077b9a
Merge pull request #4500 from ee6aba54/feat/agc-depth-surface-storage
feat(libSceAgcDriver): storage image access to depth surfaces
2026-10-13 16:50:53 +13:00
milescoviello 7cba4ce60a fix(libkernel_sys): let link follow a symbolic link like FreeBSD
link threw "a symbolic link as the existing path is not supported" when
its first argument named a symbolic link, so a valid call aborted the
title. FreeBSD's link follows that link (sys/kern/vfs_syscalls.c,
sys_link: "kern_linkat(td, AT_FDCWD, AT_FDCWD, uap->path, uap->link,
UIO_USERSPACE, FOLLOW)") and hard-links the file it points to; a link to
a directory then fails with EPERM ("if (vp->v_type == VDIR) ... return
(EPERM);") and a dangling one with ENOENT from namei.

link now checks the followed status of the existing path and, when it
is a symbolic link, creates the hard link to the file it resolves to.
std::filesystem::create_hard_link would otherwise link the symbolic
link itself on Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a521101b77c879234308711c5ec8a9f9cf00816f)
2026-10-13 03:46:17 +00:00
Dean 6124bf063f
Merge pull request #4907 from oneandonlydean/up4/fix-msaa-guest-access-segv
fix(agc): submit tiled multisample test targets before destroying them
2026-10-13 16:40:11 +13:00
MarcZX100 e0ec0c8094 fix(libkernel): route equeue socket checks by membership 2026-10-13 03:28:27 +00:00
Nithishvelan M a479fcd737
fix(libScePngDec): validate this_size on create
- Return SCE_PNG_DEC_ERROR_INVALID_SIZE when PngDecCreateParam::this_size does not match sizeof(PngDecCreateParam), matching libSceJpegDec behavior.
- Add test coverage for invalid create parameter size.
2026-10-13 08:55:25 +05:30
xXPBSXx 67e7a3029c fix(agc): decode primitive type 3 as a line strip
DecodeState mapped points, lists, strips, fans, rect lists and patches
but threw "unsupported primitive type 3" for VGT_PRIMITIVE_TYPE 3, the
line strip (DI_PT_LINESTRIP in Mesa's r600d.h). Record of Lodoss War
(PPSA05347) stops on it after a few minutes.

Vulkan allows primitive restart for line strips without the list
restart extension, and the precheck, the pipeline and the provoking
vertex check already treat the strip class that way; only the decode
lacked the case. The point and line depth bias enable stays rejected on
line strips like on line lists, and a line strip gets the line primitive
class for a pixel shader that reads the ancillary word.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 05:23:21 +02:00
MarcZX100 eb2b360f0b fix(libkernel): route updated socket descriptors by table 2026-10-13 03:22:38 +00:00
MarcZX100 c33036eefe fix(libkernel): return bad descriptor from fstat 2026-10-13 03:22:38 +00:00
MarcZX100 488e18691a fix(libkernel): route socket options by table membership 2026-10-13 03:22:38 +00:00
screamm e99816e8f3 fix(libkernel): give guest sockets ordinary descriptor numbers below FD_SETSIZE
libkernel numbered guest sockets from GuestSockets::FirstDescriptor
(0x10000000), so a title that puts a socket in a 1024-bit fd_set writes
fd / 8 bytes past it. The Epic Online Services SDK does this on its
RtcNetworkThread and three titles stop there (#1794):

Palworld PPSA23126 (queeeef, main a11c8f7f7):
  FATAL: unhandled exception 0xc0000005 on thread 37164 'RtcNetworkThread'
    rip 0x00007ffcdb1ea2be eossdk-ps5-shipping.prx.guest.prx+0xdea2be
    rcx 0000000010000005 rsi 0000000000400000 r12 0000000010000005
The Mound PPSA26223 (xXPBSXx, main 0a8b1d32b):
  FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread',
  rip eossdk-ps5-shipping.prx.guest.prx+0xefc6f4, rcx = 0x10000005,
  rsi = 0x400000
Dying Light 2 PPSA02262 (tv7):
  FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread'
    rcx 0000000010000005 ... rsi 0000000000400000 ... r12 0000000010000005

On FreeBSD 11.4 (8c639945) FD_SETSIZE is 1024 (sys/sys/select.h:58-59)
and sockets take the lowest free entry of the one process descriptor
table that files use (kern_descrip.c:1643-1660 fdalloc,
uipc_syscalls.c kern_socket through falloc).

Every socket number is now a real host descriptor: a zero-access NUL
handle registered with _open_osfhandle on Windows (as directory
descriptors already are) and an F_DUPFD_CLOEXEC duplicate of the host
socket on Linux. The socket table maps that number to the socket, so
"is this a socket" is a table lookup instead of a range check at the 31
former FirstDescriptor comparisons. close releases both the socket entry
and the number, dup and dup2 duplicate the number through the host table
and keep the socket shared, dup2 of a file onto a socket number drops the
socket entry, and sceKernelClose now closes sockets too. sceNetSocket and
sceNetAccept take their ids from the same table instead of a counter from
32, so libkernel and libSceNet numbers never collide.

select polls guest sockets alongside libSceNet sockets, the random device,
stdio and (on Linux) host descriptors; poll throws only for an open
descriptor that is not a guest socket and reports POLLNVAL otherwise.
File calls that used to fail with EBADF on the socket range return
FreeBSD's answer for a socket: lseek ESPIPE (kern_lseek), ftruncate and
getdirentries EINVAL (invfo_truncate, getvnode), flock EOPNOTSUPP;
fstat on a socket throws. pread on a closed descriptor returns EBADF.

title failing on host descriptor numbers; these three titles are that
case, and this change keeps host numbering for files and only moves
sockets into it.

Tests: guest_socket_descriptors (new: socket below 1024, select on a
socket and a file together, close frees the number for a file, dup of a
socket, sceKernelClose), guest_select (socket and pipe together, Linux),
guest_poll, guest_dup, guest_socket_pair, guest_fstatfs,
guest_posix_filesystem and guest_sce_net updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 20378375b31eb5db922db6e55f45e44a4dae6445)
2026-10-13 03:22:36 +00:00
Dean Galvin b32ba9eeef fix(agc): submit tiled multisample test targets before destroying them
The tiled cases of agc_multisample_guest_access destroyed each target while its layout transition and clear were still recorded, so the recorder later submitted commands for destroyed images and lavapipe crashed executing them. Each case now submits and waits first, as the linear case does. The 2x case runs only when the device renders 2x color, like the 8x case.
2026-10-13 16:07:32 +13:00
Nicolas Falesy 6f284b8298 feat(libSceVideoOut): implement sceVideoOutDeleteVrrActiveStatusEvent
The NID -q-vbO859Tw, declared as sceVideoOutUnknown_q_vbO859Tw for
PPSA02530, hashes to sceVideoOutDeleteVrrActiveStatusEvent. It removes the
handle's VRR status registration from the queue like the other Delete*Event
calls, the counterpart of sceVideoOutAddVrrActiveStatusEvent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017WeVmjnnPPKdxVTpr2Vjtw
2026-10-12 23:04:58 -04:00
Gjon Gojcevic 63ed0ffeaf feat(agc): sample the base level through unnormalized samplers
The hardware ignores the LOD for unnormalized coordinates and samples the
base level. A sampler used only by explicit-LOD, implicit-LOD, bias and
gradient samples gets a pipeline specialization constant set from its
unnormalized bit when the binding plan is prepared. When the constant is
set, each of its samples is emitted as OpImageSampleExplicitLod with LOD 0,
which is what Vulkan requires of unnormalized samplers. Otherwise the
sample keeps its own form. Such shaders are no longer refused.
2026-10-12 22:58:16 -04:00
Dean Galvin e322c6e064 fix(agc): a pending CMASK clear takes precedence over a refused color fill
ApplyFastClear took the color fill before the CMASK clear, so a partial
fill of a multisampled target's color memory threw at the next use even
when a CMASK fast clear was pending over the whole target. The CB reads
a cleared tile's color from the clear registers whatever the memory
holds, so the CMASK clear is applied and the recorded fill dropped.
2026-10-13 15:48:03 +13:00
raghav314 d57f9cfb03 fix(agc): take a CB resolve's sample count from its source target
DecodeColorMetadataPass treated a CB_COLOR_CONTROL resolve as one only
when PA_SC_AA_CONFIG has a sample count, and checked the source against
it. A title that resolves with the rasterizer single-sample fell through
to the draw path and threw "only normal color rendering with copy ROP is
supported (... mode resolve)".

A color target's sample count is CB_COLOR_ATTRIB.NUM_SAMPLES, separate
from the rasterizer's; radeonsi itself writes PA_SC_AA_CONFIG 0 for a
multisampled framebuffer when rasterizer multisampling and line
smoothing are off. So a resolve is decoded when either the rasterizer or
CB0 is multisampled, and its sample count is CB0's. When the rasterizer
is multisampled, the source still has to match it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:46:45 +13:00
Yunus Shaikh d6698c9045 refactor(agc): declare maxDrawSnapshotBytes after guestMemory
#757 and #758 add a member after usesFaultBuffer, so declaring maxDrawSnapshotBytes there made the pull requests conflict.
2026-10-12 19:43:34 -07:00
milescoviello 0dda76b1f9 fix(libc): check for a pipe on every stream seek on Windows
The pipe check cached its first answer in the FileStream, so a stream
first used on a regular file stayed classified as a file after its
descriptor was replaced with a pipe (dup2 onto fileno(stream)), and a
later fseek reached the C runtime again and dropped buffered bytes.
fseeko and ftello now ask GetFileType on each call, as lseek on a pipe
does since #4670, and FileStream no longer keeps the state.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:38:02 +00:00
David 7aabb9005c fix(libc): fail fseek and ftell on a pipe with ESPIPE on Windows 2026-10-13 02:38:01 +00:00
Dean c13dc7e6b4
Merge pull request #4558 from MiniRML/fix/kernel-json-number-locale
fix(kernel): parse JSON numbers independently of host locale
2026-10-13 15:37:49 +13:00
Dean 417c7d02a7
Merge pull request #4586 from ee6aba54/fix/agc-lookahead-write-data
fix(agc): count every CP data write of a dispatch group as a write of the run
2026-10-13 15:37:38 +13:00
Dean 7cad853fc1
Merge pull request #4690 from David-cmrg/fix/libkernel-create-data-dir
fix(libkernel): create /data at start-up
2026-10-13 15:37:26 +13:00
Dean bef5f45bbc
Merge pull request #4729 from zaydzaari/research/rtc-minute-separator-1342
fix(rtc): reject date strings ending at the minutes
2026-10-13 15:37:15 +13:00
Dean b85934913f
Merge pull request #4738 from milescoviello/carry/fdopen-append-windows-4735
fix(libc): append through a descriptor opened by fdopen in an append mode on Windows
2026-10-13 15:37:03 +13:00
Dean b17fb4a4a7
Merge pull request #4792 from marshmll/fix/linux-host-stderr-redirect
fix(libc): keep host diagnostics on stderr when a guest redirects it on Linux
2026-10-13 15:36:51 +13:00
Dean 255a91abd6
Merge pull request #4809 from MarcZX100/codex/unaligned-shader-abi-4782
fix(agc): accept unaligned shader headers in ABI resolution
2026-10-13 15:36:40 +13:00
Dean 1419ea91fd
Merge pull request #3958 from MarcZX100/codex/carry-accepted-socket-mode
fix(libkernel): inherit listener mode on accepted sockets
2026-10-13 15:36:27 +13:00
Dean 834de1ab10
Merge pull request #4199 from sanxchep/fix/msaa-guest-guard
fix(agc): size multisample guest guards by fragment blocks
2026-10-13 15:36:14 +13:00
Dean 37a0a3608c
Merge pull request #4480 from milescoviello/carry/fcntl-record-locks-windows-4479
feat(libkernel): implement fcntl record locks on Windows
2026-10-13 15:36:03 +13:00
Dean 495bf91a04
Merge pull request #4880 from David-cmrg/fix/libc-mspace-realloc-usable
fix(libc): keep a moved mspace block's usable bytes in realloc
2026-10-13 15:15:29 +13:00
Dean Galvin f15671ecf3 fix(agc): copy the depth plane again after a storage write
A sampled copy of a depth plane is kept while the surface's write
serial is unchanged. Storing a written storage view back into the
depth image did not bump the serial, so a later draw or dispatch that
sampled the plane as an array or cube kept the values from before the
store. The store-back bumps it now.

The dispatch test samples the plane through a copy, stores to it in a
dispatch and samples it again.
2026-10-13 15:00:36 +13:00
Dean Galvin 738ae9df06 test(agc): cover depth storage access through dispatches
Draw depth and stencil, read and rewrite each plane in compute
dispatches, and check the stored values with depth-tested and
stencil-tested draws, a following dispatch and the flushed guest bytes.
The array view, multisampled and two-view refusals are checked through
dispatches as well.
2026-10-13 15:00:36 +13:00
Dean Galvin 889e5d436a fix(agc): refuse depth storage views the plane copy cannot serve
A 2D array or cube view with the plane's extent and format reached the
ordinary path and read guest bytes the depth was never written to; it
throws again, with the message main has for it. A multisampled surface
is refused before the plane copy, as sampling it already is. A written
view that another view of the same plane replaced had its results
dropped at store-back; that throws now.

Update the debt entry to what is supported and what still throws.
2026-10-13 15:00:36 +13:00
Simon 7d06be06f0 fix(libSceAgcDriver): refuse storage access to a widened D16 depth plane
A D16_UNORM_S8_UINT surface is stored as D24_UNORM_S8_UINT or
D32_SFLOAT_S8_UINT on devices without the 16-bit format. Copying its
depth aspect yields 4 bytes per texel, so the 16-bit storage image of
the depth plane would receive the wrong layout. Such an access now
throws, naming the surface and both formats; the stencil plane keeps
its 8-bit layout and stays supported.
2026-10-13 15:00:36 +13:00
Simon 27e1122774 feat(libSceAgcDriver): storage image access to depth surfaces
Compute shaders bind the depth and stencil planes of a depth surface as
storage images, to read the depth or to write it before later draws
test against it. The driver kept depth on the GPU and refused such
bindings.

A depth surface now owns one storage image per plane (R32_SFLOAT or
R16_UNORM for depth, R8_UINT for stencil). Binding copies the plane
into it through a staging buffer; a written element is copied back into
the depth image during write-back, so the next draw sees the shader's
depth.

A view whose extent differs from the surface's means the memory was
reused for another texture and takes the ordinary texture path; this
applies to sampled views of a depth surface too.

PPSA15304 copies depth and stencil through storage images every frame.
2026-10-13 15:00:36 +13:00
David 69b9d186db fix(libc): replace an open or read-only destination in rename on Windows 2026-10-13 05:58:01 +04:00
Dean 37dd483084
Merge pull request #4468 from GustavoGamarra95/perf/agc-capture-one-sync-per-run
perf(agc): wait once per unit run when a storage image captures its guest bytes
2026-10-13 14:54:24 +13:00
Dean 2a55cfe1e8
Merge pull request #2491 from oneandonlydean/up639/depth-plane-unchanged-copy-on-425
perf(agc): skip the depth plane copy when no slice changed
2026-10-13 14:49:05 +13:00
Dean 26f8047889
Merge pull request #2490 from oneandonlydean/up639/depth-plane-guest-layers-on-425
perf(agc): keep unchanged guest layers of a depth plane copy
2026-10-13 14:48:43 +13:00
Dean c27687313c
Merge pull request #4859 from EagleBearerYS/fix/libkernel-flexible-map-flag-8000
fix(libkernel): accept flag 0x8000 in sceKernelMapFlexibleMemory
2026-10-13 14:48:21 +13:00
Dean 048206e18c
Merge pull request #4410 from MarcZX100/codex/idle-results-debounce-4398
fix(agc-driver): delay storage writeback until queue activity settles
2026-10-13 14:48:00 +13:00
Dean c3625c4789
Merge pull request #4120 from tibzejoker/fix/vector-window-direct-ssa
fix(shader): keep direct VGPR accesses in SSA when the window has only relative reads
2026-10-13 14:47:39 +13:00
David 7126090654 fix(libc): convert local times before 1970 and after 3000 on Windows 2026-10-13 01:47:03 +00:00
David-cmrg 00c35c159c fix(libc): keep a moved mspace block's usable bytes in realloc 2026-10-13 03:38:29 +02:00
Dean Galvin 50e5f088c9 perf(agc): skip the depth plane copy when no slice changed
Sampling a depth array, cube or R16_UINT view of depth surfaces went
through DepthPlaneCopy::Refresh on every use. Each use copied every slice
a depth surface holds into the staging buffer and the whole staging buffer
into the copy image, with three ALL_COMMANDS barriers, although nothing
had written the slices since the previous use.

Each depth surface now carries a write serial, bumped when a draw binds it
as its depth target and when a pending fast clear is applied. The copy
keeps the image and serial of every slice it copied. A use copies only the
slices whose surface or serial changed and the guest layers that changed
(as before), and records nothing when none did. A slice whose surface is
the depth target of the latest depth draw is copied at every use until
another depth target is bound, since that draw may still be recorded after
the copy.

Test: agc_driver_depth_plane_guest_layers counts the recorded slice copies
and copy fills; a second use with nothing written records none (it records
one of each without this change), a draw to a slice's surface or a CPU write
to a guest layer copies only that layer.
2026-10-13 14:33:40 +13:00
raghav314 1baf077a7e fix(agc): clear a multisampled target on a uniform fill of its color memory
A multisampled target's samples live only in its host image, so a fill
of its color memory never reached them: a title that clears its MSAA
targets that way (with no CMASK) kept drawing over the previous frame.

A uniform fill over the whole color memory now clears the host image to
that value at the target's next use, the same way a CMASK fast clear
does, and a pending CMASK clear still takes precedence. The color memory
is sized with addrlib's block dimensions for the sample count
(ComputeThinBlockDimension): a 64 KiB block holds width x height x
samples elements, laid out width first for 1x and 4x. A fill over part
of it, over a target whose footprint is not computed, or with a value
that has no exact clear value in the format throws at the next use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:01:57 +05:30
Dean 0c3dc9ca2b
Merge pull request #4381 from Celegans12/fix/agc-aliased-buffer-views
fix(agc): accept aliased views of the guest buffers in graphics shaders
2026-10-13 14:31:53 +13:00
Dean 22c30bed2b
Merge pull request #4842 from LeandroLP23/fix/agc-ancillary-layer-unwritten
fix(shader): read the ancillary render target index as 0
2026-10-13 14:31:32 +13:00
lorkydey c18daed286 fix(libkernel): validate CPU stop-info buffers and cover handler state 2026-10-13 03:21:58 +02:00
poonasor 02ed289566 feat(libkernel): implement sceCoredumpGetStopInfoCpu
PPSA34063 (NHL 27) calls it with a 16-byte buffer outside a coredump
handler and expects 0x81180003 there. The registered handler is never
invoked, so every call is outside one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d04f5d600e52372b968ce0f06139f519fe068724)
2026-10-13 03:18:53 +02:00
Dean d1fd9372ba
Merge pull request #3602 from CouchRiv/fix-libc-powf-special-value
fix(libc): powf(1, y) returns 1 for any y
2026-10-13 14:15:52 +13:00
Dean 9ddb59fb87
Merge pull request #3635 from MarcZX100/codex/libc-readdir-r-3556
feat(libc): implement readdir_r
2026-10-13 14:15:32 +13:00
Dean Galvin d9c0407391 test(shader): make the read-only window tests pass in the full Linux build
With SPIRV-Tools enabled the optimizer replaces a window that is only
indexed by constants with scalars, so the sampler spill case found no
array. It now reads M0 from the thread group id, as the first case does.

The direct access test reads and writes lanes across a wave32, which a
device with 8-lane subgroups cannot hold; it skips there like the
v_readlane and v_writelane tests.
2026-10-13 14:09:05 +13:00
jagrat Singh 8d08caa529 feat(libkernel): implement lutimes 2026-10-13 06:29:45 +05:30
thibaut c87f3771d1 test(shader): let direct SSA mirrors of a read-only window pass the stage input check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
thibaut efa96f73bc test(shader): check direct and relative reads of a read-only vector window on the GPU
Write window registers directly (VALU, v_writelane_b32, buffer load,
v_movrels_b32 results), read them back directly and through
v_movrels_b32 at several M0 values, including after a direct rewrite,
and feed a v_readlane_b32 of the written lane into the output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
thibaut ebf939ff10 test(shader): cover a sampler spilled through a read-only vector window
A compute program reads v4 relatively with v_movrels_b32, spills sampler dword
0 into v4 with v_writelane_b32, restores it with v_readlane_b32 and samples
with it. On main the restore reads the window array and resource analysis
refuses GetSamplerResource with ReadLane(LoadVectorWindow(Immediate),
Immediate).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
thibaut 2ecbbe40a3 fix(shader): keep direct VGPR accesses in SSA when the window has only relative reads
A program that uses relative VGPR addressing placed every register from the
lowest relatively addressed one upward in the window array, so even direct
accesses became array loads and stores. A descriptor spilled with v_writelane
and restored with v_readlane then lost its definition, and resource analysis
refused GetSamplerResource with ReadLane(LoadVectorWindow(Immediate)) (GTA V,
PPSA04264, shader 0x25a889400).

When the program has no indexed writes (v_movreld, v_movrelsd, v_swaprel),
direct accesses stay SSA registers and each direct write is mirrored into the
window for the relative reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 13:59:35 +13:00
Dean 510a2188cf
Merge pull request #4031 from AlxcBTW/carry/pixel-ancillary-word-v3
feat(shader): give reads the masked-select pass keeps the whole pixel ancillary word
2026-10-13 13:59:10 +13:00
Dean 4430780bdd
Merge pull request #3644 from DariusHQ/fix/libkernel-poll-pipes
fix(libkernel): poll pipe descriptors
2026-10-13 13:52:54 +13:00
Sanxchep Sharma 8bcf1cc0df fix(agc): size multisample guest guards by fragment blocks 2026-10-13 02:52:51 +02:00
Dean 007759a6b6
Merge pull request #4524 from jagrat04/feat/libkernel-segment-info
feat(libkernel): implement sceKernelInternalMemoryGetModuleSegmentInfo
2026-10-13 13:52:34 +13:00
Yunus Shaikh 4ccf888291 fix(savedata): leave the mount result untouched when sceSaveDataMount3 fails
Animal Well (PPSA22520) checks for its save with a CREATE2 mount it never
unmounts, then saves with a second mount of the same directory. That
mount returns BUSY, and the title keeps using the first mount point for
sceSaveDataPrepare, its save file and sceSaveDataUmount2.
sceSaveDataMount3 cleared the result before its checks, so the save was
written to "/save" in the game folder instead of the save directory, and
the first mount was never released. The result is now written only when
the mount succeeds, as shadPS4 does.
2026-10-12 17:47:46 -07:00
jagrat Singh ec0d61e58e feat(libkernel_sys): implement mkdirat 2026-10-13 06:11:56 +05:30
Gustavo Ariel Gamarra Rojas e260f24939 test(agc): refuse committed reads of a multisampled target's memory
The multisampled target guest access test now also reads the target's
memory through ReadCommitted and ReadCommittedUnsynced and expects both
to throw.
2026-10-12 21:41:23 -03:00
Gustavo Ariel Gamarra Rojas fa28772174 fix(agc): run the read guard in a committed read without the flush hook
ReadCommittedUnsynced skipped the read guard ReadCommitted runs before its
flush hook, so a storage image capture over a multisampled target's
memory read the bytes instead of throwing. Both reads now run the guard,
ReadCommitted still before its flush hook, and share the copy.
2026-10-12 21:41:23 -03:00
Dean 758aca7373
Merge pull request #4116 from hawwwwwk/investigate/relinker-eip-relative
fix(relinker): resolve EIP-relative imports in strict filter
2026-10-13 13:41:08 +13:00
Dean Galvin 64ca6e3329 fix(libkernel): follow pollscan for a pipe whose other end is closed
FreeBSD's pollscan clears POLLOUT whenever POLLHUP is set, so a write end
whose read end is closed reports POLLHUP alone, with POLLIN and POLLRDNORM
when they were asked for; both host paths reported POLLOUT | POLLHUP.

POLLOUT on a read end now throws. FreeBSD opens both ends FREAD | FWRITE
and pipe_poll reports the read end writable, which a one-way host pipe
cannot back.

guest_poll drained 1 KiB from the full pipe before expecting POLLOUT, but
Linux reports it only once a whole 4 KiB page is free, so the test aborted
there. It now drains the pipe, which is writable on every host.
2026-10-13 13:37:28 +13:00
Dean 284a1c3342
Merge pull request #4509 from ErenYeager1836/feat/webbrowser-dialog-predetermined-content
fix(libSceWebBrowserDialog): permit termination from finished state in sceWebBrowserDialogTerminate
2026-10-13 13:34:24 +13:00
Dean 1b360faa1d
Merge pull request #4441 from superhe11/codex/fix-elf-header-validation
fix(relinker): validate the ELF object version and header size
2026-10-13 13:34:03 +13:00
Alex a3b8c054f3 fix(libc): powf(1, y) returns 1 for any y
powf(1.0, nan) returned 0x7fc00001; FreeBSD msun (lib/msun/src/e_powf.c)
returns 0x3f800000. That case now follows FreeBSD; other inputs are
unchanged.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-12 20:32:48 -04:00
David 665e6f1782 fix(libkernel): fill every buffer of readv from a pipe on Windows 2026-10-13 00:30:45 +00:00
TideOPL 126a1e4137 perf(agc): copy a pending block-compressed surface into its sampled texture on the GPU
A compute pass can write a block-compressed surface through an uncompressed
storage view whose element is one block (32_32_UINT for 8-byte blocks,
32_32_32_32_UINT for 16-byte ones). cachedTexture rejected such a storage
image for the BC descriptor, so every sample stored the whole surface to
guest memory, compared it with the snapshot and uploaded it again.

Texture::CanBlockCopyFrom accepts a storage image over the same memory,
tiling and swizzle whose element is the block and whose extent is the
surface's extent in blocks, with whole blocks on every copied level. The
sampled texture then owns a BC image that vkCmdCopyImage refreshes from the
storage image when its version moved, through the same refreshed-on-reuse
path as the depth copy.
2026-10-13 01:29:21 +01:00
babyhuey c15e9f2a37 fix(libSceVideoOut): return RESOURCE_BUSY when a bus is opened twice 2026-10-12 20:26:26 -04:00
LeandroLP23 3334e41e45 fix(shader): read the ancillary render target index as 0
No pre-rasterization stage writes Layer: vertex layer exports are
ignored, so the first layer is used and the render target index field
of the packed ancillary word is 0. The lowering read the Layer builtin
for it instead. AMD's Windows driver drops the color writes of a
fragment shader that consumes an unwritten Layer, which made
agc_driver_relative_ancillary fail on AMD hardware (#4800); lavapipe
returns 0 there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:23:56 +13:00
Cesar Rodriguez baaad78e81 fix(agc): leave blending off for a color target with CB_COLOR_INFO.BLEND_BYPASS
DecodeState threw "blend bypass conflicts with enabled blending" when
CB_BLENDn_CONTROL enabled blending for a target whose CB_COLORn_INFO has
BLEND_BYPASS (bit 16, Mesa src/amd/registers/gfx10.json) set. The bit makes
the color block skip the blender for that target: Mesa sets it "according
to docs" for UINT/SINT and 8_24/24_8/X24_8_32_FLOAT targets, which the
blender cannot process (src/amd/common/ac_descriptors.c, shared by radeonsi
and RADV). Marvel's Guardians of the Galaxy (PPSA01748) issues such draws
right after its loading screen. Blending is now enabled only when it is
requested and not bypassed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:22:38 -07:00
Dean Galvin c52321fab5 fix(shader): move the primitive class to request version 19 and compose the ancillary word with render target index 0
main reads target.vertexSubgroups at request version 18 since #4470, so
reading the pixel primitive class at 18 as well read a version 18
request written by main one byte short. The class is now written and
read at version 19. agc_shader_memory loads a version 18 request as
main writes it, and it fails with "truncated data" when the class is
read at 18.

The composed word no longer reads the Layer builtin for bits 16-28.
Draws render to a single layer and guest layer exports are ignored, so
no stage writes Layer, and AMD's Windows driver drops the writes of a
fragment shader that reads it unwritten (#4800). The field is the
constant 0 and pixel_ancillary checks that the composed program has no
Layer read. The debt entry names the bits that have no source and are
assumed zero (2-7, 12-15, 29-31), and says that a change which makes
draws export a layer must restore the read.
2026-10-13 13:18:51 +13:00
Simon 51c043ba51 fix(agc): keep the texels of CMASK tiles with codes other than 0 and 15
A fast-clear materialization threw when a CMASK nibble held a code other
than 0 (cleared) or 15 (expanded). PPSA18391 (engine: Clickteam Fusion) fast
clears a target whose CMASK it never initialized, so some nibbles hold
leftover values such as 1 and 10, and presentation failed on the first
frame.

Such a tile now keeps its texels like an expanded tile, on the CPU path
and in the compute pass. A cleared tile of a DCC target still throws.
The graphics and CMASK fast-clear tests now check that an unknown code
leaves the tile unchanged instead of expecting the throw.
2026-10-13 02:17:33 +02:00
Celegans12 d644c31855 fix(agc): accept aliased views of the guest buffers in graphics shaders
The SPIR-V emitter declares extra views of the guest buffers binding:
buffers_coherent for GLC or DLC accesses (#2369) and buffers_u64 for
64-bit atomics. Each is another StorageBuffer variable on the same set
and binding, decorated Aliased, which Vulkan allows.

ShaderValidation, which checks the stages of every draw, accepted one
StorageBuffer variable per binding with u32 elements only, so such a
graphics shader was refused with "duplicate SPIR-V resource binding".

A second variable is now accepted when the binding is the guest buffers
array and every variable on it is Aliased; an aliased view may hold u64
elements. Other duplicates are still refused. agc_driver_graphics
validates pixel shaders with a GLC store and a 64-bit atomic, and checks
that the same modules without Aliased are refused.
2026-10-13 02:15:38 +02:00
AlxcBTW d0aca69eff test(agc): a relative read of the whole ancillary word gets the composed word
The relative-ancillary test expected the whole-word read through
v_movrels to be refused, which was main's behaviour before the word was
composed. It now converts that read to float and exports it: the
composed word of a triangle draw is 2 (primitive class 2, sample 0,
layer 0), so the pixel is 255, with and without the implicit position
inputs in front of the ancillary VGPR. TechnicalDebt's relative
addressing entry no longer says that whole-word uses throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:06:27 +13:00
AlxcBTW 18d9874a06 feat(shader): give reads the masked-select pass keeps the whole pixel ancillary word
A packed ancillary word that still reaches an export, or any other read
the masked-select pass cannot drop, no longer stops the recompile. After
that pass every remaining use gets the composed word: the primitive class
at bits 0-1, SampleId at bits 8-11 and Layer at bits 16-28, with every
other bit zero. Those lanes do export the ancillary VGPR on the console,
so the word has to be right, not just present.

The primitive class (0 point, 1 line, 2 triangle, 3 rectangle) comes from
the draw. RasterPrimitiveOf maps rect lists to 3, line lists on the vertex
path to 1 and everything else the driver accepts to 2, because its
geometry and tessellation paths only output triangles. It reaches the
shader as pipeline specialization constant 8233, the way dual-source
blending does: a request field that is folded into the specialized
module and is part of the result memo, the draw key and the serialized
request (version 17).

Only shaders that read the class pay for it. ShaderInfo records the read
(disk cache format 28), and only those shaders put the class in their
module key, so a shader that reads just the sample or layer field keeps
one module for every primitive class. The draw sets the class only when
the pixel shader loads the ancillary VGPR, so other draws keep the draw
cache entries they have today.

The VRS rates (bits 2-5) and the eye id (bit 7) are 0 because the driver
supports neither VRS nor multiview. SampleId is 0 because the driver
rejects MSAA and per-sample shading, and Layer is 0 while it ignores
vertex layer exports. TechnicalDebt.md records the layout sources and
these assumptions.

Tests:
- pixel_ancillary builds an export to target 0 whose two channels select
  between a packed value and a phi carrying the word. It checks the word
  for each primitive class, sample and layer, checks that the three
  shapes the masked-select pass keeps now compile with the composed word,
  and that a program whose select the pass drops reads no class.
- agc_driver_pixel_ancillary_word exports the word from a draw and reads
  back 2, 3 and 1 for triangle, rectangle and line. A shader that reads
  only the sample field shares one module across the classes, and the
  whole-word shader gets one per class. It also checks RasterPrimitiveOf.
- agc_shader_memory covers the version 17 round trip, and
  agc_shader_disk_cache the new flag.

Both ancillary tests fail with the old refusal when the composition is
disabled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:06:21 +13:00
Dean Galvin af7160475b perf(agc): keep unchanged guest layers of a depth plane copy
Sampling a depth surface as a 2D array, a cube or its integer bits
copies its planes into a per-use image. Each use read every slice no
depth surface holds from guest memory and detiled it again, even when
nothing had written it since: in one level of a heavier title that was
about 165,000 layer reads, about 650 GiB, in a five-minute run.

A slice read from guest memory now stays in the copy's staging buffer
with the write generation it was read at. The next use reads and
detiles it again only when its address, size or tiling changed, the
write tracker reports a write since, or recorded GPU work writes the
range; a slice a depth surface held in between is read again. Without
write watching every use reads, as before.
2026-10-13 12:59:26 +13:00
Dean 1c5e62878a
Merge pull request #425 from MichonGoddijn231849/feat/depth-surface-reads
feat(agc): depth planes sampled as 2D arrays, cubes and integer views
2026-10-13 12:58:34 +13:00
Dean e55b881f0f
Merge pull request #4598 from Lorkydey/feat/ngs2-listener-transform
feat(libSceNgs2): support default listener calculation
2026-10-13 12:56:18 +13:00
Dean 056d17ecc5
Merge pull request #3816 from alfalfa-star/fix/agc-htile-memory-value-fill
fix(agc): take an HTILE fill from a value in memory as a depth fast clear
2026-10-13 12:46:38 +13:00
alfalfa-star fd98327ba7 fix(agc): take a memory value fill only when the hardware stores the raw value
The kernel reads its value with s_buffer_load_dword through the V# in
user[4..7] and stores it with buffer_store_format_x through the V# in
user[0..3]. When the value V# covers less than a dword (NUM_RECORDS
times its stride, or NUM_RECORDS bytes for stride 0) the load returns 0,
and a store format other than 32_UINT, 32_SINT or 32_FLOAT converts the
value, so in both cases the HTILE does not receive the dword read from
memory. The match now requires both. Kena's own dispatch (format
32_UINT, a value V# of one 16-byte record) still matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:40:31 +13:00
alfalfa-star d31b13f491 fix(agc): take an HTILE fill from a value in memory as a depth fast clear
PPSA01802 (Kena: Bridge of Spirits) clears its depth HTILE every frame
with its own compute kernel: a 9-word dword fill that stores, through
the stride-4 V# in user[0..3], a value loaded from the pointer in
user[4..5]. Neither the DMA_DATA hook nor the fill kernels the driver
recognizes saw it, so the depth kept the first frames' contents and the
base pass and sky failed their depth test almost everywhere.

The kernel is matched exactly when the dispatch is decoded, the value is
read from guest memory, and the range is passed to NoteDepthMetadataFill
like a fill, so the whole-HTILE and ZMask rules apply unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:40:24 +13:00
Dean e6ecece94e
Merge pull request #4267 from EagleBearerYS/fix/shader-color-compare-8bit-unorm-zero
fix(shader): return 0 for 8-bit UNORM color comparison
2026-10-13 12:38:49 +13:00
Yunus Shaikh 584c59e5ac test(libkernel): compare the unterminated readv buffer with memcmp 2026-10-12 16:37:56 -07:00
john.maia 0900586191 feat(agc): skip the draws and dispatches of one shader with APS5_SKIP_SHADER
APS5_SKIP_SHADER=<code address, hex> drops every draw that runs a program
at that address and every dispatch of a compute program at that address, as
if the packet had run. Skipping one pass at a time shows which pass first
writes a wrong value into an image: Epic Mickey's (PPSA15657) green sky
light was bisected this way to the copy into its sky cube (#4551).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 00:35:38 +01:00
Dean d38d98bc84
Merge pull request #4068 from Kikoarl/feat/sysmodule-acm-module-id
feat(libSceSysmodule): register libSceAcm (0x10B) and libScePsml (0x137) module IDs
2026-10-13 12:28:42 +13:00
Dean Galvin 90d899d982 fix(agc): first-slice view of depth plane arrays, refuse plane copies of another host format
A 2D-array T# over a depth surface bound to a shader image declared 2D had
no first-layer view, so the descriptor got a null view and the shader read 0.
The depth plane texture now creates the 2D view of its first slice when its
view is a 2D array, for the depth image itself and for the plane copy.

A 16-bit depth surface with stencil may be stored as D24_UNORM_S8_UINT or
D32_SFLOAT_S8_UINT. The plane copy sizes and copies slices by the guest
format, so several slices or an R16_UINT view of such a surface now throw.
2026-10-13 12:18:50 +13:00
MarcZX100 cbd615bb42 fix(agc): return error for unsupported init versions 2026-10-12 23:15:39 +00:00
GoodTimeclubs 8a7e1195d4 feat(exports): export the calls PPSA01317 imports
Gran Turismo 7 (PPSA01317) fails to load on unresolved imports and on
two needed libraries that AnyPS5 does not build (#4782).

- sceAppContentDownloadDataFormat empties /download0, as
  sceAppContentTemporaryDataFormat does for /temp0.
- sceNpAuthSetTimeout returns REQUEST_NOT_FOUND for an unknown request
  like the other request calls, and OK otherwise; no request reaches the
  network the timeouts would bound.
- sceNpWebApi2PushEventSetHandleTimeout throws for an unknown library
  context, like sceNpWebApi2GetMemoryPoolStats, and returns OK
  otherwise; push event handles are not tracked and nothing waits on
  one offline.
- sceSslDisableVerifyOption, sceSslGetAlpnSelected, sceSslSetAlpn,
  sceSslSetMinSslVersion, sceAudioOutDeviceIdOpen and the unnamed
  libScePsml NIDs xNZM9Y5lf-M and lcZD9tJraAw are declared without
  parameters and throw; their signatures are unknown.
- libSceUsbStorage and libSceUsbStorageDialog exist with no exports,
  like libSceXml: the title lists both as needed but imports nothing
  from them.

Each assumption is recorded in the library's technical debt file.

Co-authored-by: dawid-swiecicki <8942606+dawid-swiecicki@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 01:11:29 +02:00
Michon dc0c2cf8c6 test(agc): agc_driver_depth_plane_sampling pins the sampled plane values
image_load of row 0 through each view, against a live surface drawn at a
known depth and guest-memory slices filled with known values: a 3-slice
D32 2D array, a D32 cube (all six faces), a D16 plane as R16_UINT, and a
D16 depth-bits R16_UINT array (both slices' raw values). A plane array
from base slice 1 is still refused. Fails on main (the array is refused)
and without the depth-bits format gate (GTA V's refusal message).
2026-10-13 12:11:02 +13:00
Michon 086f7d5091 feat(agc): depth planes sampled as 2D arrays, cubes and integer views
A texture over a depth surface could only sample one 2D slice of the
plane's own format; 2D arrays, cubes and the R16_UINT view of a D16
plane were refused ("sampling the depth plane of depth surface ... is
not implemented"). A one-slice 2D array now views the depth image as an
array. Several slices, a cube, or an integer view of a D16 plane are
copied into a DepthPlaneCopy image at every use: each slice from the
depth surface bound at its address (base plus one 64 KiB-block depth
slice per layer), otherwise from guest memory, detiled. A depth-bits
R16_UINT view (a depth swizzle) keeps the depth copy, since the shader
converts the sampled depth to its bits itself (GTA V's compute
0x242d4a200 samples a D16 plane array that way, reported by
@tibzejoker). Such a texture is not kept in resource templates.

Storage-image access to depth surfaces is no longer part of this
change; #4500 implements it.

TechnicalDebt (Depth plane copies) and ARCHITECTURE describe the copy and
what still throws.
2026-10-13 12:11:02 +13:00
Nesyth c809601df8 test(agc): sample comp-to-single keys stored over a pending storage image 2026-10-13 01:04:31 +02:00
Dean 90c683946f
Merge pull request #4819 from oneandonlydean/up4/fix-kqueue-test-flake
fix(libkernel): pair the inotify halves of a rename in a watched directory
2026-10-13 12:03:30 +13:00
jagrat Singh ea03a22c7f feat(libkernel): implement sceKernelInternalMemoryGetModuleSegmentInfo 2026-10-13 04:29:25 +05:30
Dean Galvin 59fcdea9c4 fix(libkernel): pair the inotify halves of a rename in a watched directory
inotify queues IN_MOVED_FROM and IN_MOVED_TO of a rename one after the
other. The watcher thread could read the first before the second was
queued and report a rename inside the directory as a move to another
directory (NOTE_WRITE | NOTE_EXTEND instead of NOTE_WRITE), which made
guest_kqueue abort on loaded runners.

The watcher now reads the queue until it is empty and, when a move half
has no partner, calls getdents64 on the directory before reading again:
that takes the directory lock rename holds until both halves are queued.
2026-10-13 11:53:02 +13:00
jagrat Singh 59bd0c1782 feat(libkernel): accept MADV_PROTECT advisory hint in madvise 2026-10-13 04:21:59 +05:30
winstrex 44debb29b6 docs(debt): record the sceAgcGetGsPrimPayload search and null pointers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:46:20 -03:00
winstrex cb6866653c feat(libSceAgc): implement sceAgcGetGsPrimPayload
Report the 8-byte GS primitive payload when the shader's context registers
set SPI_SHADER_IDX_FORMAT.IDX0_EXPORT_FORMAT to SPI_SHADER_2COMP, and 0
otherwise. The search runs over every context register, so an earlier
SPI_SHADER_IDX_FORMAT entry with another format does not end it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:46:20 -03:00
Yunus Shaikh 56d35172f5 fix(libkernel): accept flag 0x8000 in sceKernelMapFlexibleMemory
The libc.prx heap that Animal Well (PPSA22520 01.000.011, SDK 9.04)
bundles maps each arena with sceKernelMapFlexibleMemory(&addr, len, 3,
0x8000). It threw "Unsupported memory mapping flags 0x8000" before the
first frame. The flag is ignored as sceKernelMapNamedFlexibleMemoryInternal
already does; other unknown flags still throw.
2026-10-12 15:38:48 -07:00
MarcZX100 1988607409 test(agc): cover unaligned shader ABI headers 2026-10-12 22:28:55 +00:00
dawid-swiecicki f838fba198 fix(agc): accept unaligned shader headers when resolving shader ABIs
Gran Turismo 7 (PPSA01317) passes shader headers at 4-byte aligned addresses to
sceAgcUnknownCreateInterpolantMapping. RegisterShader already reads them through a
copy (#902); ResolveShaderAbi, ResolveGraphicsStagesAbi, ResolveGraphicsAbi and
RegisteredHeader still required 8-byte alignment and threw "null or misaligned
address". Read the fixed fields through GuestShaderFields at those sites.
2026-10-12 22:24:52 +00:00
ArjenS-LA 819c475266 fix(tools): match explicit trace summaries to their capture 2026-10-12 15:18:16 -07:00
Dean 43e3a70f81
Merge pull request #4356 from MarcZX100/codex/serialize-load-sensitive-tests
test(cmake): serialize load-sensitive Windows tests
2026-10-13 11:17:40 +13:00
Dean acf1dd7900
Merge pull request #4510 from simonbronk/fix/relinker-weak-imports
fix(relinker): keep weak undefined imports weak
2026-10-13 11:17:25 +13:00
Dean 62b9e7043c
Merge pull request #4362 from krishalivaibhav/codex/recorded-clear-barrier
fix(agc): align recorded fast-clear access scopes
2026-10-13 11:17:10 +13:00
Dean df5108f90e
Merge pull request #4530 from babyhuey/feat/libkernel-internal-memory-available-size
feat(libkernel): export sceKernelInternalMemoryGetAvailableSize
2026-10-13 11:16:17 +13:00
Yunus Shaikh 92e0970d6c test(libSceNet): run CheckUnboundSocketName before CheckUnspecifiedIpv6
#4785 calls its netstat check right after CheckPoolStats(), so calling CheckUnboundSocketName() there made the two pull requests conflict.
2026-10-12 15:11:43 -07:00
Yunus Shaikh a7e320d1f7 test(libSceNet): define CheckEpollWritable after CheckMessages
#4785 adds its netstat checks before CheckPoolStats, so defining CheckEpollWritable there made the two pull requests conflict.
2026-10-12 15:09:09 -07:00
fdfawef324 1eb13fdf02 fix(libc): track writes to shared guest memory per 4 KiB page on Windows
Writes into shared (direct) guest memory on Windows are caught by write
protecting each 16 KiB view and taking the fault. The fault reopened
and invalidated the whole view, so a collect reported all four 4 KiB
pages of a view whenever any byte of it was written, including pages
nobody wrote. A GPU surface that ends or starts inside such a view
then had its edge pages taken for CPU writes whenever the title wrote
the memory next to it, and the storage write-back kept the guest's
stale bytes there instead of the GPU's results.

Each view now keeps a write generation, a seen generation and the
write protection per 4 KiB page. A fault invalidates and reopens only
its page, a host write only the pages it covers, and a collect reports
and re-arms only the pages it covers. VirtualProtect is called once
per contiguous run of pages whose protection changes, so arming or
pinning a view takes one call when those pages are contiguous and two
when they are not. A guest store that fills a whole view takes up to
four faults instead of one.

A collect does not settle a page that it cannot write protect because
a host write is open on one of the page's views; it reports the page
again until a collect can arm it. main settled such a page and relied
on the end of the host write invalidating the whole view, so a guest
store into it during the host write was reported only after the host
write ended. With only the written pages invalidated there, the store
would not be reported at all.

A collect that runs out of room partway through a view now settles the
pages it already reported there and nothing after them; before, it
settled none of them and reported them again at the next collect.

CheckSharedWritePages writes one byte into the second 4 KiB page of a
view and requires a collect of the view's last two pages to report
nothing; main reports both. It also checks a store across two pages
and a file read into one page. CheckSharedWritesDuringHostWrite stores
into a fault-opened page of a view after a collect while a host write
is open on another page of it, and requires every collect of the view
and of its alias to report that page until the host write has ended.
agc_shared_write_partial_collect resets the two interior pages of a
view after writes to all four and requires the outer two to stay
pending. The existing shared-memory checks in guest_memory,
agc_shared_write_partial_collect and guest_socket_host_write (the
recvfrom sender outputs) now expect the pages written instead of the
whole view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:03:53 -04:00
marshmll 438d410824 fix(libc): keep host diagnostics on stderr when a guest redirects it on Linux
On Linux a guest stderr redirect also moves the host's: guest
descriptors are host descriptors, and the guest stderr stream wraps the
host stderr FILE. After dup2(fd, 2), or freopen(path, mode, stderr) with
full buffering, the libc terminate report, the host fprintf(stderr)
diagnostics and libstdc++'s terminate message go into the title's pipe
or file, and abort() discards what is still buffered. RetroArch
(PPSA99169) reopens stderr onto app0/trace.txt with full buffering, so
its stop on main left no message anywhere.

Before the first guest redirect of descriptor 2 (libkernel dup2, libc
freopen of a stream on descriptor 2), keep a close-on-exec duplicate of
the host descriptor 2 and point the host stderr at an unbuffered stream
on it. The guest stream keeps its own FILE and follows the redirect.
The libc terminate report writes to the kept descriptor with write()
instead of the stderr stream, and libkernel's sceKernelDebugOutText uses
the same kept descriptor.
2026-10-12 19:00:08 -03:00
Adrià ee49621900
Merge pull request #4787 from adriadam10/ci/api-budget
ci: cache the API state of the hourly workflows
2026-10-12 23:54:10 +02:00
Adrià Franch 4d15207553 ci: cache the API state of the hourly workflows
pr-overlap and area-labels walked every open PR on each run, spending the
1000 requests/hour that GITHUB_TOKEN gets per repository and starving the
other workflows (HTTP 403 'rate limit exceeded for installation').

pr-overlap keeps a hash of each report in actions/cache and only reads the
comments of PRs whose report changed. area-labels keeps head SHA plus the
labeler.yml blob per PR and labels only the changed ones, at most 40 per
run. conflict-label gets a concurrency group so pushes in a row don't stack
full passes.
2026-10-12 23:49:09 +02:00
BIGFERRY999 f8e2c6497b feat(libc): implement floor 2026-10-12 22:43:46 +01:00
Dean 8a67eb2e68
Merge pull request #4456 from Mouhand-Kaddo/fix/agc-library-used-descriptor-sets
fix(agc): retain used descriptor sets in pipeline libraries
2026-10-13 10:41:32 +13:00
Simone ad0d478a81 Merge remote-tracking branch 'origin/main' into fix/relinker-weak-imports
# Conflicts:
#	core/relinker/relinker/src/output/SysVDynamicSectionBuilder.cpp
#	core/relinker/relinker/src/pipeline/RelinkerPipeline.cpp
2026-10-12 23:40:54 +02:00
p3nicillin 1d115b8962 feat(libScePad): apply the angular velocity deadband
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:29:57 +01:00
krishalivaibhav 0b54425e3f test(agc): remove non-discriminating recorded clear fixture 2026-10-13 02:57:39 +05:30
Leonardo 02d1dbb9f3
feat(libSceLibcInternal): implement fminf required for PPSA99902 startup 2026-10-12 23:27:13 +02:00
David c22654102b fix(libc): keep the host standard handles when a guest replaces descriptor 0, 1 or 2 on Windows 2026-10-12 21:23:00 +00:00
Adrià 584cc4c571
Merge pull request #4761 from p3n1cillin/claude/agc-driver-validation-debug
feat(libSceAgcDriver): answer validation and shader debugging queries as the retail library does
2026-10-12 23:22:01 +02:00
Adrià 55b2b332c3
Merge pull request #4766 from p3n1cillin/claude/libc-heap-report-for-game
feat(libc): report heap backtraces and heap errors requested by the guest heap
2026-10-12 23:21:56 +02:00
Adrià d7323df946
Merge pull request #4755 from ivica00/feat/libc-nearbyint
feat(libc): implement nearbyint
2026-10-12 23:21:19 +02:00
Adrià Franch 91bad2fce5 test(libSceAudioOut): drop sceAudioOutExGetMonitorInfo from the not-implemented test
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 23:19:02 +02:00
p3nicillin e284717ebe feat(libSceAudioOut): list no audio descriptors in sceAudioOutExGetMonitorInfo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:19:02 +02:00
babyhuey ff56dadc0a feat(libkernel): export sceKernelInternalMemoryGetAvailableSize 2026-10-12 17:18:53 -04:00
Adrià 559979dc67
Merge pull request #4750 from p3n1cillin/claude/session-signaling-connection-status
feat(libSceNpSessionSignaling): answer connection status queries offline
2026-10-12 23:18:08 +02:00
Adrià ceb6927888
Merge pull request #4671 from jagrat04/feat/libkernel-killpg
feat(libkernel): implement killpg
2026-10-12 23:17:33 +02:00
p3nicillin d3ef2b30b7 feat(libc): report heap backtraces and heap errors requested by the guest heap
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:15:55 +01:00
Adrià b5b33511f7
Merge pull request #4710 from babyhuey/feat/libkernel-scekernelmmap
feat(libkernel): export sceKernelMmap
2026-10-12 23:15:49 +02:00
Adrià 962aba18f5
Merge pull request #4578 from jagrat04/feat/libkernel-getsid
feat(libkernel): implement getsid for calling process
2026-10-12 23:14:58 +02:00
Niko 7f39ac991b fix(conventions): keep #### subsections inside the pull request template sections
SECTION matched every heading that starts with ###, so a #### line opened
a new section named "# ...". A Tested section written as #### Linux and
#### Windows subsections was reported as missing or empty, and an
unchecked item after a #### line in Checklist was not reported.

A level-4 heading belongs to the level-3 section above it (CommonMark
4.2), and silent_stub_debt() already keeps deeper headings inside their
section. SECTION now matches only headings with exactly three #.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:14:49 +02:00
ArjenS-LA d31e283088 fix(libkernel): throw for unsupported directory entry names 2026-10-12 21:14:01 +00:00
Adrià Franch 53fc059af7 docs(technical-debt): record sceNetCtlGetIfStat assumptions
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 23:13:54 +02:00
bubblegg a2e97d25a6 fix(libSceNetCtl): read 64-bit interface byte counters on Linux
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:13:54 +02:00
bubblegg ec07f5a5ec fix(libSceNetCtl): keep the Linux interface counters to Linux
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:13:54 +02:00
bubblegg 46338a981c feat(libSceNetCtl): add sceNetCtlGetIfStat
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:13:54 +02:00
Adrià e0fad55e56
Merge pull request #4651 from ErenYeager1836/feat/libkernel-event-error
feat(libkernel): implement sceKernelGetEventError
2026-10-12 23:13:34 +02:00
lorkydey dadc23a8c1 fix(libSceNgs2): reject unverified listener transforms 2026-10-12 23:12:40 +02:00
Adrià 4fc51fd991
Merge pull request #4590 from AlxcBTW/feat/perf-template
docs: add a performance report issue form and ask for summary lines
2026-10-12 23:11:53 +02:00
ivica00 5883c25552 feat(libc): implement nearbyint
The float helper is already on main. The double helper rounds with the current mode and does not raise the inexact flag.
2026-10-12 23:11:50 +02:00
Adrià Franch 935e7d6ba1 test(libkernel): drop guest_ioctl_getsid, it pinned the getsid throw 2026-10-12 23:11:21 +02:00
jagrat Singh 3ce13e2b7d feat(libkernel): implement getsid for calling process 2026-10-12 23:11:21 +02:00
jagrat Singh f08dc5e3c3 feat(libkernel): implement killpg 2026-10-12 23:11:01 +02:00
p3nicillin 8e5a1cae23 feat(libSceAgcDriver): answer the validation and shader debugging queries as the retail library does
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:10:58 +01:00
lorkydey 4168cf3bbd feat(libSceNgs2): calculate the listener coordinate transform 2026-10-12 23:10:42 +02:00
Adrià 7940cacd83
Merge pull request #4655 from milescoviello/feat/libc-ceil
feat(libc): implement ceil
2026-10-12 23:10:14 +02:00
Adrià 6d2f279863
Merge pull request #4607 from yunusshaikh7/feat/tracy-profiler
feat(agc): report performance timer scopes to Tracy with APS5_ENABLE_TRACY
2026-10-12 23:09:21 +02:00
Dean 16cdf635e2
Merge pull request #4746 from oneandonlydean/up4/fix-arena-hole-test-flake
test(agc): reserve the arena host hole the write-tracking checks query
2026-10-13 10:08:48 +13:00
Adrià 5efd09afc4
Merge pull request #4653 from Lorkydey/feat/libc-statvfs
feat(libc): query filesystem statistics by guest path
2026-10-12 23:08:17 +02:00
David a0a238c86a feat(libkernel): implement sceKernelGetEventError
Return the event's data when EV_ERROR is set in its flags and 0 otherwise, as KytyPS5 does and as FreeBSD 11 kevent stores the errno of a failed change. A null event throws like the other accessors.
2026-10-12 23:07:39 +02:00
Adrià 5f01c80821
Merge pull request #4595 from jagrat04/feat/libkernel-setregid
feat(libkernel): implement setregid
2026-10-12 23:06:27 +02:00
Adrià 2a9068b159
Merge pull request #4138 from VitzS7/fix/libkernel-signal-waits
feat(libkernel): wait for pending guest signals in the signal waits
2026-10-12 23:06:25 +02:00
p3nicillin b9e9187cfc docs(debt): cite ASTRO BOT's call to sceNpSessionSignalingGetConnectionStatus
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:05:40 +01:00
p3nicillin 3fa1fe34c9 feat(libSceNpSessionSignaling): answer connection status queries offline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:05:23 +01:00
Adrià d415ce7b59
Merge pull request #4220 from xXPBSXx/fix/savedata-dir-name-search2
feat(libSceSaveData): answer sceSaveDataDirNameSearch2
2026-10-12 23:04:55 +02:00
PrintOrb 5031432db0 test(audioout): detect overlapping backend initialization 2026-10-12 16:04:06 -05:00
Adrià 5ccb19b2a0
Merge pull request #4101 from xXPBSXx/fix/kernel-page-table-stats
fix(libkernel): answer sceKernelGetPageTableStats
2026-10-12 23:02:33 +02:00
Dean 6f438a1c2e
Merge pull request #4318 from Amoenus/codex/fix-color-target-d64-x
feat(agc): support 64 KiB display XOR color targets
2026-10-13 10:01:57 +13:00
Adrià dabaaa7def
Merge pull request #3611 from nicolasfalesy/feat/libkernel-pathconf
feat(libkernel_sys): implement pathconf
2026-10-12 23:01:45 +02:00
Dean 38772ed8b2
Merge pull request #4498 from milescoviello/fix/udp-unspec-disconnect
fix(libkernel): dissolve a UDP association on connect to AF_UNSPEC
2026-10-13 10:01:24 +13:00
Dean d3f275a0e6
Merge pull request #4429 from babyhuey/feat/libc-prosperoai-math
feat(libc): implement expm1f, erff, lgamma and llroundl
2026-10-13 10:01:12 +13:00
Dean 7ac3f460e5
Merge pull request #3952 from xXPBSXx/fix/recompiler-resource-control-flow
fix(recompiler): leave out descriptor sources behind a branch the capture decides
2026-10-13 10:00:59 +13:00
Dean 15f3aeaa93
Merge pull request #3883 from hansjm10/feat/agcdriver-get-hs-offchip-param
feat(libSceAgcDriver): export sceAgcDriverGetHsOffchipParam
2026-10-13 10:00:46 +13:00
Dean 22bb451c9f
Merge pull request #3799 from xXPBSXx/fix/export-ppsa02530-imports-v4
fix(exports): export the calls PPSA02530 imports
2026-10-13 10:00:34 +13:00
Adrià 28c9dae73d
Merge pull request #4569 from pipe1os/feat/libkernel-fdopendir
feat(libSceLibcInternal): implement fdopendir
2026-10-12 23:00:17 +02:00
Adrià 4998307175
Merge pull request #4218 from Mouad-Scriptz-0/feat/signin-dialog-npauth-signed-out
feat(libSceNpAuth): return the signed-out error from sceNpAuthGetAuthorizedAppCode
2026-10-12 23:00:04 +02:00
Dean 307c0a3afb
Merge pull request #3819 from xXPBSXx/fix/export-ppsa29866-imports-v4
fix(exports): export the calls PPSA29866 imports
2026-10-13 09:59:30 +13:00
xXPBSXx 989504ed84 feat(libSceSaveData): answer sceSaveDataDirNameSearch2
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 22:59:26 +02:00
Adrià 158cdcc9b7
Merge pull request #4549 from Nixitov/feat/libc-nearbyintl
feat(libc): implement nearbyintl
2026-10-12 22:58:29 +02:00
Adrià 14db08435f
Merge pull request #3747 from semirkabir/feat/ult-queue-optparam
feat(libSceUlt): accept option parameters in sceUltQueueCreate and sceUltQueueDataResourcePoolCreate
2026-10-12 22:58:01 +02:00
Adrià 96e25f1fc1
Merge pull request #4567 from jagrat04/feat/libkernel-get-libkernel-text-location
feat(libkernel): implement sceKernelGetLibkernelTextLocation
2026-10-12 22:57:32 +02:00
Adrià 2fabe44c48
Merge pull request #3980 from MarcZX100/codex/carry-libkernel-setrlimit
feat(libkernel): export setrlimit
2026-10-12 22:57:08 +02:00
Adrià 955f4fbffe
Merge pull request #4207 from xXPBSXx/fix/ampr-amm-usage-stats
feat(libSceAmpr): answer sceAmprAmmGetUsageStatsData
2026-10-12 22:57:04 +02:00
David 62f6764cc2 feat(libkernel_sys): implement pathconf
Porpoise 2.7 (PPSA99764) stops at load on main 0f863c10:

    FAIL: unresolved ELF import oc98+zOImus from libkernel_sys.prx

oc98+zOImus is pathconf (tools/nid_names.py, aerolib.csv at
zecoxao/sce_symbols 2883963a). It is exported from libkernel_sys next to
link and readlink.

The lookup follows FreeBSD 11's kern_pathconf and namei (release/11.0.0,
671fb7df): links are followed, a path of 1024 bytes or more or a
component over 255 fails with ENAMETOOLONG, a missing path with ENOENT.
The values are the constants of FreeBSD 11's UFS ufs_pathconf, which
vop_stdpathconf shares where both answer. Names whose UFS value depends
on the mount, the kernel options or the page size, or on which UFS and
vop_stdpathconf disagree, throw; other names fail with EINVAL. The
console's filesystem values are unverified and recorded in
TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 22:56:28 +02:00
PrintOrb c35572ec18 fix(audioout): serialize shared SDL audio initialization 2026-10-12 15:54:46 -05:00
xXPBSXx d283d484a2 fix(libkernel): answer sceKernelGetPageTableStats
PPSA30601 (Resonance) stops at the throwing stub. The four int* arguments follow
OpenOrbis' libkernel.h, the Orbital type database and SharpProspero; the values
follow KytyPS5 (2048 entries of 2 MiB per side) without its accounting of used
entries, listed in TechnicalDebt.md. A null pointer returns EFAULT.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 22:54:27 +02:00
Mouad-Scriptz-0 09c985464c feat(libSceNpAuth): return the signed-out error from sceNpAuthGetAuthorizedAppCode
There is no PSN sign-in, so the code getter answers SCE_NP_ERROR_SIGNED_OUT
instead of throwing; its parameters are unknown and not read. Minecraft
(PPSA17221) calls it after its sign-in dialog and treats the result as
offline play. The request, token and wait calls are left to #3604.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 22:54:23 +02:00
Nesyth 83959f3153 test(agc): sample comp-to-single keys from write-watched memory that no import unwatches 2026-10-12 22:53:22 +02:00
Adrià 529f7bfd4a
Merge pull request #3848 from semirkabir/feat/usbd-libusb
feat(libSceUsbd): pass USB devices and transfers through to the host with libusb
2026-10-12 22:51:41 +02:00
Adrià c06fcc4ff3
Merge pull request #3764 from YossiYad/carry/libkernel-kqueue
feat(libkernel): implement kqueue, kevent and file events
2026-10-12 22:50:34 +02:00
Adrià c8debc7788
Merge pull request #4748 from jagrat04/feat/libkernel_sys-readlinkat
feat(libkernel_sys): implement readlinkat
2026-10-12 22:49:44 +02:00
DariusHQ 7250306b77 fix(libkernel): poll pipe descriptors
ProsperoTV (PPSA99003) stops after its menu opens because poll threw for
the read end of a pipe it polls next to its sockets. poll now reports pipe
readiness as FreeBSD's pipe_poll does: readable when data is buffered,
POLLHUP once the other end is closed, writable when PIPE_BUF bytes are
free. On Windows the pipe is checked with NtQueryInformationFile while the
sockets are waited on in 1 ms slices; on Linux the host poll waits on both.
A closed descriptor below the socket range reports POLLNVAL.
2026-10-12 23:49:25 +03:00
Semir Kabir 0634b443ef feat(libSceUlt): accept option parameters in sceUltQueueCreate and sceUltQueueDataResourcePoolCreate 2026-10-12 22:48:53 +02:00
Adrià 999c07a633
Merge pull request #3890 from MarcZX100/codex/carry-ampr-offset-bounds-3321
fix(ampr): reject command buffer offsets beyond bound memory
2026-10-12 22:48:32 +02:00
Adrià 03895fc51a
Merge pull request #3679 from MarcZX100/codex/ampr-cpp-error-3346
feat(libSceAmpr): implement std::_Throw_Cpp_error
2026-10-12 22:48:13 +02:00
Adrià Franch f89c7fb883 docs(libkernel): move the setrlimit debt line next to getrlimit
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 22:47:59 +02:00
MarcZX100 c049a4972c fix(libkernel): declare getrlimit before setrlimit 2026-10-12 22:47:55 +02:00
MarcZX100 01d201c346 feat(libkernel): export setrlimit 2026-10-12 22:47:55 +02:00
VitzS7 01874b4b25 fix(libkernel): mark pending signals under the pending lock
Before: MarkPending set the bit and notified while the waits checked the set
under pending.lock, so a signal raised between the check and the wait was lost
and sigwait, sigwaitinfo, a null-timeout sigtimedwait and pause hung forever.
Now: the bit is set while holding that lock, so a wait either sees it or is
notified after it has blocked. GuestSigtimedwait raises with no delay 500 times
without the first sleep the other senders use; on the previous code that hung in
2 of 10 runs, and it passes on the new one.
2026-10-12 17:47:34 -03:00
VitzS7 28063f1951 feat(libkernel): wait for pending guest signals in the signal waits
Before: raise, kill and pthread_kill delivered a signal at once or failed
with EINVAL, nothing stayed pending, and pause, sigwait, sigwaitinfo and
sigtimedwait called NotImplemented_nid_no_patch.

Now: a process-wide pending set holds signals 1 to 128. A signal raised
while it is blocked stays pending and is delivered when _sigprocmask
unblocks it, taken by sigwait, sigwaitinfo or sigtimedwait, or delivered by
pause, which runs its handler on the calling thread and returns -1 with
EINTR. pthread_kill to another thread leaves the signal pending for
whichever thread waits for it and answers ESRCH for a handle that is not a
live guest thread. sigtimedwait follows FreeBSD's sigtimedwait(2): EAGAIN
on timeout, EINVAL for a tv_nsec outside 0 to 1000000000 while nothing is
pending, and an indefinite wait with a null timeout.
2026-10-12 17:47:33 -03:00
Mouhand Kaddo 4f5f6d671c test(agc): ignore null shader modules in graphics mock
Pipeline libraries take ownership of shader modules during prebuild. Treat null handles as a no-op so moved-from modules do not decrement the live-object count twice.
2026-10-13 00:43:16 +04:00
Adrià c952c2b0ed
Merge pull request #4709 from fdfawef324/feat/agc-workloads
feat(agc): return null from the workload builders for unregistered streams
2026-10-12 22:41:39 +02:00
jagrat Singh 20243b81e4 feat(libkernel_sys): implement readlinkat 2026-10-13 02:09:59 +05:30
Adrià 84659e16d0
Merge pull request #4632 from AlxcBTW/feat/trace-gpu-track
feat(agc): add the GPU, GPU mutex waits and shader preparation to the trace
2026-10-12 22:39:23 +02:00
Adrià fa0cc752a6
Merge pull request #3604 from itsbo5/feat/libSceNpAuth-complete
feat(libSceNpAuth): implement offline auth requests
2026-10-12 22:38:22 +02:00
Adrià 532e041911
Merge pull request #4742 from yunusshaikh7/fix/trace-output-exit
fix(agc): stop the trace writer when the driver stops
2026-10-12 22:38:15 +02:00
Mouhand-Kaddo 9aeeea848f fix(agc): retain used descriptor sets in pipeline libraries 2026-10-13 00:38:10 +04:00
fdfawef324 f1b2e605e7 fix(shader): split a BDA span read's in-place loads from its fallback
On an RTX 3060 (Windows, driver 591.86) a dispatch never finishes when
the lanes of read_bda_span split between its in-place dword loads and
its per-dword byte fallback, which calls read_bda_dword_bytes: Windows
resets the GPU and the device is lost. The two were the arms of one
selection in the DontInline function. They are now two selections, the
loads under the span's condition and the fallback under its negation,
joined after both. Loads, lookups, faults and stops are unchanged for
every lane; only the SPIR-V control-flow shape changes (SPIR-V
specification 2.11, structured control flow). Every stage except mesh
shaders is emitted as SPIR-V 1.3, and OpSelect chooses between
composites with a scalar condition only from 1.4 on, so the join stays
a selection.

The same hang and fix were reported in #3291 on an RTX 4060 Laptop GPU
(Windows, driver 591.86) and an RTX 5060 (Linux, driver 590.48.01). An
RTX 4070 Laptop GPU on driver 616.92 does not hang with either shape.
The driver-side cause is unknown, so the shape is recorded in
docs/dev/debt/shader.md.

On main the hang fails nine tests on the RTX 3060, in two ways.
agc_driver_{flat,global}_loads{,_x2_x3,_x4} and
agc_driver_flat_apertures_{2,5} abort after 64 to 71 s: the lost batch
keeps its lease, so teardown waits 60 s for the pinned guest allocation.
agc_driver_pixel_interlock fails after about 4 to 7 s with
vkDeviceWaitIdle returning VK_ERROR_DEVICE_LOST (-4) and never reaches
the pin wait. All nine pass with the change.

Fixes #3291

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:37:43 -04:00
Adrià Franch b7c67a3364 fix(libkernel): accept NOTE_EXTEND on a directory watch on Windows
FreeBSD's vfs_kqfilter checks neither the vnode type nor fflags, so the
registration is valid; it is only delivered for a move between directories,
which the Windows directory handle cannot tell from a removal plus an addition.
Also moves the TechnicalDebt entries to docs/dev/debt after main split the file.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 22:37:31 +02:00
Yossi Yadgar 28043ff928 test(libkernel): rename, unlink and rmdir entries of a watched directory 2026-10-12 22:37:08 +02:00
Yossi Yadgar 2aacb762dd test(libkernel): rename and delete a watched file through kqueue 2026-10-12 22:37:08 +02:00
David 2979da6808 feat(libkernel): implement file events
Cloudberry Kingdom (PPSA23827, #3177) registers its first kevent change
as a file watch: ident 3, EVFILT_VNODE, flags 0x25
(EV_ADD|EV_ENABLE|EV_CLEAR), fflags 0x37
(NOTE_DELETE|WRITE|EXTEND|LINK|RENAME). Palworld's Discord module
(PPSA23126, #2648) imports sceKernelAddFileEvent and
sceKernelDeleteFileEvent, which no library exported.

EVFILT_VNODE watches the host file or directory behind the descriptor
and turns host changes into FreeBSD 11 vnode hints, added whole when
they share a bit with the requested fflags as filt_vfsvnode does
(sys/kern/vfs_subr.c, release/11.0.1 34bd3475): a growing write is
NOTE_WRITE|NOTE_EXTEND, a shrinking truncate or an attribute change
NOTE_ATTRIB, a link count change NOTE_LINK or NOTE_DELETE, a pending
delete NOTE_DELETE, a new path NOTE_RENAME, and in a directory a new,
removed or renamed entry NOTE_WRITE (with NOTE_LINK for a subdirectory,
as vop_mkdir_post/vop_rmdir_post). Windows uses FindFirstChangeNotification
on the parent directory (and the watched directory) and compares handle
snapshots every 50 ms, since a write through an open handle is not
reported until it is flushed or closed (measured); Linux uses inotify.
Closing the descriptor, deleting the event or the queue stops the
watcher.

sceKernelAddFileEvent(eq, fd, watch, udata) and
sceKernelDeleteFileEvent(eq, fd) take the OpenOrbis 0a1aaf9d
(libkernel.h:35,109) and SharpProspero 5dfdb4b0 (KernelEqueue.cs:164,168)
signatures; NIDs cG3t15OK4Ow and UmXngHKB6is confirmed with
tools/nid_names.py. The watch bits are the fflags and the event is added
with EV_CLEAR, which is not confirmed on a title.

Throws: NOTE_REVOKE, NOTE_OPEN, NOTE_CLOSE, NOTE_CLOSE_WRITE, NOTE_READ
and unknown bits, NOTE_EXTEND on a directory on Windows (a move from
another directory cannot be told from a creation) and descriptors that
are not files, directories, pipes or sockets (pipes, sockets and kqueues
fail with EINVAL as in FreeBSD). Recorded in TechnicalDebt.

guest_kqueue adds a vnode watch that sees a write to a temporary file
with Cloudberry's flags, fflags filtering, a re-registration, the sce
pair on an equeue, EINVAL for sockets, pipes and the kqueue itself, the
close of a watched descriptor and a directory watch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 22:37:08 +02:00
David 9ef4aa2c40 feat(libkernel): implement kqueue and kevent over the event queue
Cloudberry Kingdom (PPSA23827, #3177) calls kqueue right after start-up
and stopped on the throwing declarations merged with #3337; Palworld's
Discord module (PPSA23126, #2648) imports kevent.

kqueue reserves a host null-device descriptor in the file descriptor
space and registers it as an event queue that reports each ready event
once per call and moves level-triggered ones behind the others, as
kqueue_scan does. close and sceKernelClose close the queue and drop the
descriptor's events from every queue, dup2 drops them for the target,
and dup/dup2 of a kqueue throw, so a reused descriptor number is never
read as a stale kqueue.

kevent follows FreeBSD 11 kern_event.c (release/11.0.1 34bd3475):
kqueue_kevent for the changelist (filter 0 skipped, EV_SYSFLAGS
cleared, EV_ERROR receipts with the errno in data, EV_RECEIPT, the
early return when nevents is exhausted), kqueue_register for EV_ADD,
EV_DELETE, EV_ENABLE, EBADF, ENOENT and EINVAL, kqueue_scan for the
timeout (EINVAL for a bad timespec, zero polls, above INT32_MAX waits
forever) and filt_timerattach, filt_usertouch for the timer and user
filters. Read and write events reuse the guest socket poll of
sceKernelAddReadEvent/WriteEvent.

Throws: other filters, EV_DISABLE, EV_DISPATCH, EV_FORCEONESHOT,
NOTE_LOWAT, EV_CLEAR on a socket, read/write events on non-socket
descriptors or another kqueue, changes to an existing timer (11.0 and
11.4 differ), a NOTE_NSECONDS fraction above one second and a negative
nevents. Recorded in TechnicalDebt.

Test guest_kqueue covers creation and close, timers, a user event
triggered from another thread, read and write events on a socket pair,
EV_ERROR receipts, the timeout and the error cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 22:36:59 +02:00
virp-mta 62d12756b7 feat(libSceNpAuth): complete offline request lifecycle following shadPS4 2026-10-12 22:34:51 +02:00
Dean 27873b0f74
Merge pull request #4407 from oneandonlydean/up4/fix-mspace-fragments
fix(libc): round mspace block sizes to the 32-byte alignment
2026-10-13 09:34:01 +13:00
Adrià 798855b567
Merge pull request #4666 from ErenYeager1836/feat/content-search-empty-library
feat(libSceContentSearch): implement the search calls over an empty library
2026-10-12 22:33:39 +02:00
Lexroid 257ff1e38f test(agc): a color view of several slices of a target with no array slices renders into its only slice
Marvel's Wolverine draws into a 256x256 mipmapped 2D target
(CB_COLOR_ATTRIB3 0x4dc6c000, no array slices) through CB_COLOR_VIEW
0x4046000: slices 0 to 35 at mip 1. The view's last slice is not bounded
by the surface on that path, so the draw renders into slice 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:32:49 +03:00
Lexroid 1d8749d00d fix(agc): render a color view of several array slices into its first slice
A CB_COLOR_VIEW whose SLICE_MAX is past SLICE_START was rejected. Without
a render target index export every primitive goes to SLICE_START, so the
target is that slice, as for a one-slice view. Layered draws into such a
view, and views of several 3D slices, still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:32:49 +03:00
Semir Kabir 42cf82953c test(libSceUsbd): require a non-null active config interface array 2026-10-12 22:32:42 +02:00
Semir Kabir 6cab297837 test(libSceUsbd): skip null interface altsettings in the interrupt endpoint scan 2026-10-12 22:32:42 +02:00
Semir Kabir faff4a6453 fix(libSceUsbd): cancel transfers under the state lock 2026-10-12 22:32:42 +02:00
Semir Kabir 98d594c9c1 feat(libSceUsbd): pass USB devices and transfers through to the host with libusb 2026-10-12 22:32:42 +02:00
Adrià 7cb200b82d
Merge pull request #4714 from p3n1cillin/claude/netctl-ap-dialog
feat(libSceNetCtlApDialog): run the offline access-point dialog lifecycle
2026-10-12 22:32:40 +02:00
Adrià 42496c5e57
Merge pull request #3865 from CnRJay/perf/agc-integer-dcc-clear
perf(agc): clear integer-format storage images with DCC code 0000 on the GPU
2026-10-12 22:28:17 +02:00
Yunus Shaikh 016b2dbe82 fix(agc): stop the trace writer when the driver stops
With APS5_TRACE_FRAMES or APS5_TRACE_SECONDS set, the trace output keeps
its writer in a static unique_ptr that is only destroyed at process
exit. On Windows its thread no longer runs by then, so the join in
~ProfileOutput never returns and the title does not exit once the trace
was written. Driver::stop now stops it next to the stderr writer, while
its thread still runs; the destructor at exit then has nothing to join.
2026-10-12 13:26:32 -07:00
Adrià 44ab0c39ee
Merge pull request #3886 from PrintOrb/fix/videoout-high-frame-rate
fix(videoout): support permitted high-frame-rate output for Quake II
2026-10-12 22:23:31 +02:00
fdfawef324 ef022d53b8 fix(agc): import GPU mappings on a host thread when the stack is short
The GPU-map observer imported a new mapping into Vulkan on the thread
that made the mapping, on whatever stack it ran. Guest fibers run on
contexts the title allocates in guest memory, with no guard page;
Demon's Souls packs its 16 KiB job fiber contexts back to back. A GPU
mapping made on a fiber used 10,616 bytes of the fiber's stack on
NVIDIA (Add, NoteGpuMapping, the registry walk in GuestAllocationsEnd,
the import, the fiber's entry and return), about 7.5 KiB of it for the
import, so a fiber that mapped GPU memory deep in a job could overrun
its context into the next one.

The observer now imports inline when the stack pointer lies inside the
thread's stack bounds with at least 256 KiB below it, and otherwise
starts a host thread for the import and joins it. On Windows the bounds
come from GetCurrentThreadStackLimits, which reads the TIB bounds that
libSceFiber sets to the fiber context while a fiber runs; on Linux from
pthread_getattr_np, whose bounds a fiber's stack lies outside of. A
mapping made while the thread holds the GPU mutex still imports inline:
a host thread could wait for that mutex forever. A host thread that
cannot be started is reported like any failed import, which is still
printed to stderr while the mapping carries on.

agc_driver_gpu_map_on_fiber maps GPU memory from a fiber with a 64 KiB
context and requires that the mapping use at most 8 KiB of it (10,616
bytes inline, 2,960 on a host thread) and start one host thread, then
maps the same memory from the main thread and requires that it start
none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:22:50 -04:00
Adrià Franch b3c8be5887 test(agc): run the integer storage clear check from ctest 2026-10-12 22:22:48 +02:00
Adrià Franch 09aa34d063 test(agc): pin the integer storage clear to DCC code 0000
StorageClearAvailable takes code 0000 on R8/R16/R32_UINT and refuses every
other code; drop the header comment that said integer formats were refused.
2026-10-12 22:22:48 +02:00
Chad 92b46a2b72 perf(agc): clear integer-format storage images with DCC code 0000 on the GPU
ClearColorFor refused every integer format, so a fast-cleared R32_UINT/R16_UINT/R8_UINT surface was uploaded through the CPU path: a guest read that waits for the GPU writers, then FillDccClear's zero texels. Code 0000 is zero bits in any format, so it maps to a zero integer clear. PPSA28416: ~1.4 s per 10 s of CPU uploads gone, 4.9 -> 5.3 fps.
2026-10-12 22:22:48 +02:00
Adrià 825620ae56
Merge pull request #3791 from YossiYad/carry/pragmata-small-exports
feat(exports): implement three small PRAGMATA exports
2026-10-12 22:22:19 +02:00
Adrià 621ce1da17
Merge pull request #3846 from tv7/fix/d16s8-depth-fallback
fix(agc): store D16 depth with stencil as D24 or D32 where the device lacks it
2026-10-12 22:20:05 +02:00
Adrià d5fcd087bd
Merge pull request #3821 from semirkabir/feat/coredump-attach-outside-handler
feat(libkernel): answer the coredump attach calls made outside the handler
2026-10-12 22:19:52 +02:00
Adrià 5382635cd0
Merge pull request #3800 from YossiYad/carry/libkernel-setitimer
feat(libkernel): add setitimer on the alarm timer
2026-10-12 22:19:39 +02:00
babyhuey c69eb7027d feat(libkernel): report hw.model from sysctl 2026-10-12 16:18:42 -04:00
Adrià 9ea71307a9
Merge pull request #3872 from LeandroLP23/fix/kernel-windows-directory-dup
fix(libkernel): share the directory state between duplicated descriptors on Windows
2026-10-12 22:18:02 +02:00
Adrià 16f993a534
Merge pull request #3787 from YossiYad/carry/libkernel-jit-shared-memory
feat(libkernel): implement the JIT shared memory calls
2026-10-12 22:17:55 +02:00
Adrià 9f8a1080aa
Merge pull request #3728 from MarcZX100/codex/net-resolver-abort-3153
feat(libSceNet): implement resolver abort and multiple records
2026-10-12 22:17:43 +02:00
AlxcBTW eb8a7255d2 feat(agc): add the GPU, GPU mutex waits and shader preparation to the trace
The trace gets a gpu track: one slice per recorder batch from its timestamp
pair, and with APS5_PROFILE_GPU=1 one per command class range under the
[gputime] class names. The GPU clock is mapped onto the CPU clock from
calibrated timestamps (VK_KHR_calibrated_timestamps or the EXT version,
enabled only when a trace switch is set) taken when the window opens and
when it closes. A traced thread's wait for the GPU mutex becomes a
GpuMutex.wait.<site> slice, and the shader preparation threads are traced
with smaller buffers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:13:44 +02:00
Adrià 0428328b79
Merge pull request #4581 from AlxcBTW/feat/trace-digest
feat(tools): digest a performance trace for an issue or a pull request
2026-10-12 22:13:37 +02:00
Adrià Franch 9387da4fa2 style(agc): drop the duplicated DepthSurface.hpp include in Pipeline.cpp 2026-10-12 22:13:27 +02:00
Lexroid 522c7424a8 fix(agc): check sample location depth support on the host depth format
The depth image is created with the host format, so the sample locations
check for the image and for the pipeline asks about that format too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:13:24 +02:00
Adrià Franch 2115dac9a9 docs(debt): record the D16 stencil depth host format fallback
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 22:13:24 +02:00
Lexroid 7d34a6aa8b fix(agc): store D16 depth with stencil as D24 or D32 where the device lacks it
A 16-bit depth target with stencil maps to VK_FORMAT_D16_UNORM_S8_UINT,
which NVIDIA devices do not support as an attachment, so creating it threw.
The surface, its views and the pipelines now use the first of D16_UNORM_S8_UINT,
D24_UNORM_S8_UINT and D32_SFLOAT_S8_UINT the device can attach and sample.
Guest-side choices (16-bit sampled views, bias units) still follow the guest
format. A constant depth bias is scaled by 256 for D24 storage; on D32 float
storage it throws, as its unit depends on the depth value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:13:24 +02:00
Adrià 198e022107
Merge pull request #3781 from YossiYad/carry/libkernel-termios-calls
feat(libkernel): implement the remaining termios calls
2026-10-12 22:12:13 +02:00
Adrià 52c9b65480
Merge pull request #3849 from LeandroLP23/docs/debt-windows-gui-console
docs(debt): update the console and random device entries to current main
2026-10-12 22:11:28 +02:00
David dbbd6d582c fix(libc): append through a descriptor opened by fdopen in an append mode on Windows 2026-10-12 20:09:54 +00:00
Yossi Yadgar 5e1ffc29ae fix(libkernel): report the setitimer interval from getitimer
getitimer on main predates setitimer and always reported a zero it_interval, so a periodic ITIMER_REAL armed by setitimer read back as one-shot.
2026-10-12 22:08:23 +02:00
screamm 5471844c5d feat(libkernel): add setitimer on the alarm timer
PS5MC (PPSA99420) imports setitimer (hPWDGx8ioXQ, aerolib) at registry
index 472, after tcsetpgrp and before getitimer. Predicted stop:
"FAIL: unresolved ELF import hPWDGx8ioXQ from libkernel.prx".

setitimer follows FreeBSD 11.4 kern_time.c (8c639945) sys_setitimer and
kern_setitimer: EINVAL for an unknown timer and for an itimerfix failure
or more than INT32_MAX / 2 seconds; a zero value clears the interval and
disarms; a null value reads the timer, EFAULT when the output is null too.
ITIMER_REAL shares the alarm state, now with an interval that re-arms it
as realitexpire does; alarm clears it, as FreeBSD's alarm.c does.

Guest SIGALRM handlers are not run, so an expiry with a handler, or with
SIGALRM blocked (signals are never left pending), throws naming the
interval; SIG_IGN drops it and the default disposition exits with 142,
as alarm already did. Intervals below 1 ms throw (itimerfix rounds them
to tick / 16 and the console's hz is unknown). ITIMER_VIRTUAL and
ITIMER_PROF throw when armed and read and disarm as zero.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 22:08:23 +02:00
Adrià 0142215687
Merge pull request #3720 from MarcZX100/codex/share-clip-extended-3333
feat(libSceShare): implement extended video clip capture
2026-10-12 22:07:24 +02:00
Adrià Franch 5b73ecb385 docs(technical-debt): cite shadPS4 for the coredump error value and move the line away from adjacent PRs
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 22:04:17 +02:00
dantdmdarealguy 90f00d4d24 feat(libkernel): answer the coredump attach calls made outside the handler
sceCoredumpAttachMemoryRegion and sceCoredumpAttachUserMemoryFile take
(uint32_t userValue, const void* mem, size_t size): the Orbital IDA type
database and the SharpProspero bindings agree on the first, the second
has only SharpProspero. Attaching belongs inside the core dump handler
the system runs after the process stops; a call from anywhere else gets
SCE_COREDUMP_ERROR_NOT_IN_COREDUMP_HANDLER. The handler is never invoked
here, so every call returns that error and nothing is recorded. Whether
the console checks the region before the calling context is unknown, so
a null, empty or wrapping region throws.

The error value 0x81180003 is inferred from fpPS4's 0x81180001 and
0x81180002 and the numbering of the Vita's SceCoredump error table;
TechnicalDebt records the sources and what remains unverified.

guest_coredump_attach: the error before, during and after a handler
registration, from another thread too, without running the handler or
touching the region; null, empty and wrapping regions throw naming the
function.
2026-10-12 22:04:17 +02:00
Adrià Franch 833cc357da docs(debt): remove the duplicated fused PPSA23566 line
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 22:04:02 +02:00
Adrià Franch 8f1077a29c docs(debt): drop sceNetResolverAbort from the unknown-signature line
The PR added a second PPSA23566 line fused with the PPSA28416 one instead of editing the original.
2026-10-12 22:03:56 +02:00
David 3d64981643 feat(libSceNet): implement sceNetResolverAbort and sceNetResolverStartNtoaMultipleRecords
UFC 6 (PPSA23566) imports sceNetResolverAbort (#992, #1502); Palworld
(PPSA23126) and LEGO Horizon Adventures (PPSA14632) import
sceNetResolverStartNtoaMultipleRecords.

Signatures and behaviour follow shadPS4 811bfcd7d17e
(src/core/libraries/net/net.cpp, net_resolver.cpp); the abort signature
also matches SharpProspero Interop/Net/Socket.cs.

sceNetResolverStartNtoaMultipleRecords shares the lookup of the Ex form
and, like it, throws for any flag. sceNetResolverAbort returns EBADF for
an unknown resolver and throws for flag bits other than NTOA (1) and
ATON (2) preservation. Host lookups cannot be interrupted, so an abort
during a running lookup makes it fail with EINTR when the host call
returns, without writing its output; with nothing running, the
preservation flags make the next lookup of that kind fail with EINTR.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit bcc8be8a58aa6a9e6b7c288d8a90af6ed63837aa)
2026-10-12 22:03:56 +02:00
Adrià 9742ec12c8
Merge pull request #3694 from MarcZX100/codex/libc-getopt-3562
feat(libSceLibcInternal): implement getopt and getopt_long
2026-10-12 22:03:44 +02:00
Adrià 70da6f41c3
Merge pull request #3718 from MarcZX100/codex/libsce-np-ea-access-3339
feat(libSceNpPartner001): implement EA access init and terminate
2026-10-12 22:03:30 +02:00
screamm 3b7ac1596d fix(libkernel): create the JIT shared memory backing in one call
Review of #3787 asked for no retries after an error in Jit.cpp.

Linux: memfd_create is called once with MFD_CLOEXEC. MFD_EXEC was
tried first and dropped on EINVAL; the flag only sets the file's execute
permission bits (Linux 6.3 and later), not whether the object maps
PROT_EXEC, which is decided by the mapping protection, as for the direct
memory memfd in DirectMemory.cpp. A one-shot choice from the headers
would fail on older kernels, so the flag is dropped; TechnicalDebt
records it.

Windows: the delete-on-close backing file is named from the process id,
a process-wide atomic counter and the performance counter, so CREATE_NEW
cannot meet an existing name, and any CreateFileW failure throws with
the Win32 error instead of looping up to 64 times on ERROR_FILE_EXISTS.
The descriptor stays a CRT descriptor on the file, so close, dup and
dup2 keep working as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:59:19 +02:00
Yossi Yadgar 7c473fa398 test(libkernel): keep JIT shared memory descriptors in step across dup2 2026-10-12 21:59:19 +02:00
screamm 7d3dd554da feat(libkernel): implement the JIT shared memory calls
The three JIT shared memory exports (avvJ3J0H0EY, MR221Mwo0Pc,
YKT49TOLQWs) are absent imports of three homebrew titles that cannot
finish loading without them. Predicted next stops (registry order):

- VLC PPSA85300 after getdtablesize (idx 354):
  "FAIL: unresolved ELF import YKT49TOLQWs from libkernel.prx"
  (Map idx 452, Create idx 462)
- XPSemu PPSA97358 after tcsendbreak (idx 214):
  "FAIL: unresolved ELF import avvJ3J0H0EY from libkernel.prx"
  (Create idx 398, Map idx 399)
- RetroArch PPSA99169:
  "FAIL: unresolved ELF import avvJ3J0H0EY from libkernel.prx"
  (Create 476, Alias 477, Map 478)

XPSemu and VLC (ps5/compat/mmap.c, __wrap_mmap) create an object for
every anonymous PROT_EXEC mapping (QEMU's TCG buffer) and map it with
the same protection, falling back to flexible memory on any non-zero
return. RetroArch's eboot runs a probe: Create("ps5-platform-probe",
0x10000, 7, &fd), Alias(fd, 3, &alias), Map(fd, 5, &address), then logs
"jit create=0x%08x alias=0x%08x map=0x%08x".

Signatures from Orbital cc408367 tools/ida/db_types.json (name, len,
maxProt, fdOut; fd, maxProt, fdOut; fd, prot, startOut), matching
ps4sdk 4df9d001 include/sce/kernel.h, ps5-jar-loader 8034177e
LibKernel.java and the three titles' call sites. SharpProspero 5dfdb4b0
declares the alias as returning the descriptor; the RetroArch call site
passes an out pointer.

The object is a host anonymous file (memfd on Linux, a delete-on-close
temporary file mapped as a PAGE_EXECUTE_READWRITE section on Windows),
the alias a duplicate descriptor, and each map maps the whole object
through the direct memory placement path, so a write through one view
is visible through the others. Bad lengths and protection bits are
EINVAL, protection past the descriptor's maximum is EACCES (FreeBSD 11
vm_map_protect), unknown descriptors are EBADF; close, dup and dup2
keep the descriptor table in step. Unverified details are recorded in
TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:59:19 +02:00
screamm 96988048ad feat(libSceSharePlay): implement sceSharePlayProhibitController
PRAGMATA (PPSA02530) imports sceSharePlayProhibitController (NID
FzQS6DREDfk), which no library exports on main, so the load fails on the
unresolved import. It is one of the "six declared-only imports" of
xXPBSXx's branch in #3043 (comment 6107100345): "These only stop the
load failure; as described above, the title calls some of them later and
needs their real behavior."

sharpemu src/SharpEmu.Libs/Share/SharePlayExports.cs:9-31
(a2983692930564815a77f81e74c151268f5b935f, used as a source of facts)
takes one value, accepts 0 and 1 and returns 0. Nothing here would read
the setting, as no Share Play session or visitor controller exists, so
it is not stored. sharpemu rejects other values with its synthetic
0x80020003, not a Share Play code, so they throw, as
sceSharePlayInitialize does for its unknown error. Like the other exports
of the library it does not check initialization. The NID hashes from the
name and is in aerolib. Unverified points are in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:58:43 +02:00
screamm 4ab6a047f6 feat(libSceRazorCpu): implement sceRazorCpuSync
PRAGMATA (PPSA02530) imports sceRazorCpuSync (NID Ax7NjOzctIM), which no
library exports on main, so the load fails on the unresolved import. The
title calls it after sceConvertKeycodeGetCharacter. From #3043 (comment
6106731532): "`sceRazorCpuSync`: answering 0 and doing nothing was
enough to carry on (the other `sceRazorCpu*` calls in `main` are no-ops
without a capture)."

SharpProspero src/SharpProspero/Interop/Debug/RazorCpu.cs:119-122
(5dfdb4b0b166e83dc9ae275001afb695f12c7174, GPL-3.0, used as a source of
facts) gives int sceRazorCpuSync(void), an unnamed frame-boundary event.
Without a capture there is no event to record, so it returns 0 and does
nothing, like the other sceRazorCpu markers here and the shadPS4 stub
(razor_cpu.cpp:143, 3a9f57265d28975658f1ca941daa5c879c7a74bd). The NID
hashes from the name and is in aerolib. Unverified points are in
TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:58:39 +02:00
screamm 5690b84a49 feat(libSceNpEntitlementAccess): implement sceNpEntitlementAccessGetGameTrialsFlag
PRAGMATA (PPSA02530) calls it after sceRazorCpuSync and stops on main
with "sceNpEntitlementAccessGetGameTrialsFlag not implemented". From
called once; I logged four register values but cannot tell which are
arguments. Answering 0 without writing anything carried on." Killing
Floor 3 (PPSA08427) and RE4 also import it.

The title is reported as a full game, not a game trial: 0 is written to
the uint32_t output and 0 returned; a null pointer returns 0x817D0002.
Signature, value, one-word write and error follow sharpemu
src/SharpEmu.Libs/Np/NpEntitlementAccessExports.cs:98-116 and its test
NpEntitlementAccessExportsTests.cs:16-36 (a2983692930564815a77f81e74c151268f5b935f),
used as a source of facts; 0x817D0002 is also the parameter error of
KytyPS5, prosper and sceNpEntitlementAccessGetEntitlementKey here. The
library has no initialization state, so none is checked. The NID
y7JD1gfwyfA hashes from the name and is in aerolib. Unverified points
are in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:58:36 +02:00
Yunus Shaikh 8371431ef1 build: add Tracy with the other third-party projects
The include sat next to add_subdirectory(core/libs), which other open pull requests rewrite. Tracy is now added after FFmpeg, still at the top-level scope, so the client is built with the same flags.
2026-10-12 12:55:40 -07:00
Yunus Shaikh 28ca53dc89 feat(agc): report performance timer scopes to Tracy with APS5_ENABLE_TRACY
APS5_ENABLE_TRACY=ON builds the Tracy 0.14.1 client from the new
3rdparty/tracy submodule as a shared library next to the patched
libraries. Every PerformanceTimer scope of libSceAgcDriver and
libSceVideoOut becomes a Tracy zone, each presented flip ends a frame,
and the queue workers and the presenter name their threads. The client
listens only on localhost and records only while a profiler is
connected; sampling, system tracing, call stacks, code transfer and its
crash handler are off. Without the option nothing of Tracy is compiled.

On MinGW the client is built without thread_local destructors and with
TRACY_DELAYED_INIT, because emulated TLS frees thread_local objects
before their destructors run; a thread's event producer then stays
alive until the process exits instead of being destroyed on freed
memory.
2026-10-12 12:55:40 -07:00
xXPBSXx badb94cc78 docs(debt): list the PPSA02530 imports in debt/libSceAcm.md
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:51:37 +02:00
xXPBSXx 5377f0fe04 fix(exports): leave sceRazorCpuSync and sceSharePlayProhibitController to #3791
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:51:37 +02:00
xXPBSXx 147c0db150 fix(exports): export the calls PPSA02530 imports
PPSA02530 (PRAGMATA) fails to load without these imports that no library exports. Their signatures are unknown, so they are declared without parameters and throw until they are implemented. Adds a TechnicalDebt entry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:51:36 +02:00
Adrià 3e0e01f35f
Merge pull request #4526 from AlxcBTW/feat/trace-capture
feat(agc): record a present window as a Chrome trace and a summary line
2026-10-12 21:49:54 +02:00
Yossi Yadgar 0c1fa50e8e test(libkernel): termios calls on a Unix socket and a directory descriptor 2026-10-12 21:48:20 +02:00
screamm 51b3e3ef38 feat(libkernel): implement the remaining termios calls
Adds tcsendbreak (vXE-dIUIZlE), tcdrain (brGGJWsZauw), tcflush
(FxHW5NuEXsY), tcflow (OG8xRaGIpqk), tcgetpgrp (5OEl1mRhUm0) and
tcsetpgrp (YMuesZTyTl4) next to tcgetattr and tcsetattr in
File/src/Terminal.cpp. The NIDs come from tools/nid_names.py and match
aerolib.csv (zecoxao/sce_symbols 2883963a).

They follow FreeBSD 11.4 lib/libc/gen/termios.c (8c639945), which
implements each one over a terminal ioctl: tcflush returns EINVAL for a
queue other than TCIFLUSH, TCOFLUSH or TCIOFLUSH and tcflow for an
action outside TCOOFF..TCION before the descriptor is used; every other
call fails with the error the merged tcgetattr gives (EBADF for a closed
descriptor, ENOTTY for a file or pipe, EADDRNOTAVAIL or EOPNOTSUPP for a
socket, per soo_ioctl and in_control/in6_control). tcsendbreak fails on
TIOCSBRK before its 0.4 s pause, and tcflow's TCIOFF/TCION fail in
tcgetattr. FreeBSD's pipe_ioctl answers TIOCGPGRP/TIOCSPGRP on a pipe or
FIFO with the SIGIO owner, which is not modelled, so tcgetpgrp and
tcsetpgrp throw on a host pipe above descriptor 2.

Titles (homebrew catalog #1578), predicted stops from registry order:
- PS5MC PPSA99420 imports tcsendbreak (442), tcdrain (443), tcflush
  (444), tcflow (445), tcgetpgrp (462) and tcsetpgrp (463); once pause
  and setrlimit are in, it stops at
  "FAIL: unresolved ELF import vXE-dIUIZlE from libkernel.prx".
- XPSemu PPSA97358 imports tcsendbreak (214), its first uncovered import
  after execv, sigwait, setrlimit, mlockall, setuid and getopt:
  "FAIL: unresolved ELF import vXE-dIUIZlE from libkernel.prx".

guest_terminal covers each call over stdin, stdout, stderr, a file, both
pipe ends and an AF_INET socket, the EINVAL cases (also on -1), and
EBADF for -1, a closed socket, a closed pipe and a closed file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:48:20 +02:00
Gjon Gojcevic 0b7de0e737 feat(shader): read hull inputs at a control-point index computed from data
A hull program may read its input control points from LDS at an index it
computes from data, such as an index loaded from memory or min(invocation, n).
The tessellation lowering refused such a read because its address is not
affine in the invocation and relative patch.

The address evaluator now carries a dynamic term for a hull LDS read: a value
that is not affine but does not depend on the relative patch index, with the
multiple it is scaled by. Terms of the same value add up ((t << 1) + t of
t = index << 3 is 24 * index); terms of different values keep the greatest
common divisor of their multiples. The read is lowered from the program's own
address arithmetic, and the relative index may reach it only with the stride
of a patch's inputs. A computed offset that is not a whole number of control
points is refused.

The SPIR-V emitter clamps a hull input control-point index to the patch's last
input, recorded as technical debt: on the console an index past the patch
reads LDS of a neighbouring patch.
2026-10-12 15:47:59 -04:00
Zayd Zaari 396412496f fix(rtc): reject date strings ending at the minutes
Require the measured separator after RFC date minutes when seconds are absent. Preserve the destination tick on the parse failure and retain the accepted trailing-space and seconds controls.

AI-assisted; based on guarded PS5 measurements with controlled input backing storage.
2026-10-12 19:44:03 +00:00
Adrià Franch 6116fc75e1 docs(agc): say the last present of a trace window has no folded sample
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 21:42:59 +02:00
AlxcBTW af52bc430f style(agc): restore the space before QueryBdaByteFeatures
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:42:59 +02:00
AlxcBTW 5c7da226df fix(agc): write the trace's gzip framing around a raw deflate stream
The zlib in the tree (libjpeg-turbo's) is built with NO_GZIP, so
deflateInit2 refuses the gzip wrapper (windowBits 31) and no trace was
written. The trace file now writes the gzip header, a raw deflate stream,
and the CRC-32 and size trailer itself. The writer's result line is flushed,
since stderr is buffered in a title process. The file writing moves to
TraceFile, and a test inflates a written file and checks its CRC-32 and
size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:42:59 +02:00
AlxcBTW 5668e0d0e7 feat(agc): record a present window as a Chrome trace and a summary line
APS5_TRACE_FRAMES=<start>:<count> (or APS5_TRACE_SECONDS=<start>:<length>)
records every PerformanceTimer scope and stage of the queue workers and the
presenter inside the window as complete events, named after their frame
timing keys, into a preallocated buffer per thread. Slices under 20 us are
counted per thread instead of written; a full buffer stops that thread and
sets trace_dropped. When the window closes, a ProfileOutput writer thread
writes aps5-trace-<commit>-<pid>.json.gz and appends one line to
aps5-summary.jsonl. Without either switch a PerformanceTimer costs one more
predictable branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:42:59 +02:00
Adrià cf4557e5d8
Merge pull request #4570 from AlxcBTW/feat/sampler-collapsed
feat(agc): write the worker sampler's stacks as collapsed stacks
2026-10-12 21:42:52 +02:00
Adrià 8027c749e5
Merge pull request #4707 from adriadam10/feat/progress-history
feat(progress): publish a progress history chart
2026-10-12 21:40:14 +02:00
Romans Pokrovskis b6f1a4a69d test(agc): verify display XOR array attachment transport
Run the existing array-layer draw, recompiled shader load, untouched-layer and guest-publication checks with D_X as well as R_X. This exercises the resident array path that carries slice-dependent address XOR; a direct per-slice copy is not an equivalent model.

AI-assisted implementation and validation: Codex.
2026-10-12 22:38:59 +03:00
Romans Pokrovskis 4326c7c212 fix(agc): validate display XOR bits before resident lookup
Apply the source-configured D_X XOR refusal during color decoding as well as direct layout construction. Regression tests reject each raw column-bit combination before storage or rendering is selected. Existing resident-array and packed-mip refusal paths remain in force.

AI-assisted implementation and validation: Codex.
2026-10-12 22:38:59 +03:00
Romans Pokrovskis f27bdf4d73 feat(agc): support 64 KiB display XOR color targets
Map SW_64KB_D_X color surfaces through the existing source-backed texture equations and mip geometry. Keep display and render-target equation caches separate. Reject unresolved raw column bits for the new mode instead of accepting them.

Cover the captured PPSA99902 descriptor, independent source offsets, pipe/bank tags, transfers, array/mip placement and resident/non-resident GPU draws. Record unmeasured console behavior in TechnicalDebt.

CPU validation on main e013f8c9: captured-descriptor failure 5/5; changed passes 5/5; pinned AMD addrlib reference 536940 addresses per run, five runs. Full build and GPU validation are tracked externally. AI-assisted implementation and validation: Codex.
2026-10-12 22:38:59 +03:00
Ondra97 85cf9c3ac7 fix(tools): preserve technical debt section moves 2026-10-12 21:37:58 +02:00
babyhuey db3726a868 feat(libkernel): export sceKernelMmap 2026-10-12 15:34:17 -04:00
milescoviello b8d71fc7f2 fix(libkernel): dissolve a UDP association on connect to AF_UNSPEC
connect() on a guest datagram socket checked the address family before
doing anything, so an AF_UNSPEC (null) address failed with EAFNOSUPPORT
and left the socket connected: getpeername() still reported the old
peer, send() without an address still went there, and datagrams from
any other sender were dropped by the host.

FreeBSD 11.4 disconnects a connected datagram socket first and only then
rejects the address. sys/kern/uipc_socket.c, soconnectat():

	 * If protocol is connection-based, can only connect once.
	 * Otherwise, if connected, try to disconnect first.  This allows
	 * user to disconnect by connecting to, e.g., a null address.
	 */
	if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
	    (error = sodisconnect(so)))) {

udp_disconnect() (sys/netinet/udp_usrreq.c) clears the peer and resets
only the local address, keeping the local port:

	in_pcbdisconnect(inp);
	inp->inp_laddr.s_addr = INADDR_ANY;

after which in_pcbconnect_setup() (sys/netinet/in_pcb.c) returns
EAFNOSUPPORT for the full-size address, as main already does. connect(2)
documents it: "Datagram sockets may dissolve the association by
connecting to an invalid address, such as a null address."

A connected internet datagram socket given an address whose family byte
is 0 is now disconnected on the host first (AF_UNSPEC on Linux, a zeroed
address on Windows) and the call then fails as before. Linux also drops
an ephemeral local port on that disconnect, so the port is bound again
on the wildcard address, which is what FreeBSD leaves. A host
failure during the disconnect is returned as the call's error. Stream
sockets keep the previous path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:33:20 +00:00
AlxcBTW ca219d1b67 feat(tools): digest a performance trace for an issue or a pull request
tools/trace_digest.py reads an APS5_TRACE_FRAMES capture (aps5-trace-*.json.gz)
and prints a markdown report of a few KB, or the same data as JSON: the
summary line from aps5-summary.jsonl, the slowest frames with their largest
slices, the busy, wait and untraced time of each thread, the largest slices
per frame, the longest waits, and with --stacks the hottest frames of an
APS5_SAMPLE_WORKER collapsed-stack file. Python standard library only; a
unittest checks every section on a small synthetic trace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:33:18 +02:00
PrintOrb b6ae7da1ed docs(videoout): preserve split debt layout after merging main 2026-10-12 14:32:58 -05:00
Jordan Hans bdb0be019e feat(libSceAgcDriver): export sceAgcDriverGetHsOffchipParam 2026-10-12 19:32:46 +00:00
xXPBSXx 0485570577 fix(exports): export the calls PPSA29866 imports
PPSA29866 (Tomba! 2) fails to load without these imports that no library exports. Their signatures are unknown, so they are declared without parameters and throw until they are implemented. Adds a TechnicalDebt entry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:32:38 +02:00
Adrià Franch 63e60db1a7 feat(progress): publish a progress history chart
tools/progress_history.py measures the first-parent history of main with
the rules of tools/progress.py, one point per day, and writes history.json,
history.svg and history.html. The Progress workflow runs it after
progress.py; the README shows the chart under the map and links the page.
Labels and points compact as days accumulate.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 21:31:09 +02:00
Dean 2f4cc8983b
Merge pull request #4431 from AlxcBTW/refactor/spirv-specializer-debris
refactor(shader): drop unused folded constants and unreachable blocks after specialization
2026-10-13 08:30:39 +13:00
PrintOrb 8024471225
Merge branch 'main' into fix/videoout-high-frame-rate 2026-10-12 14:27:28 -05:00
David-cmrg bed1b15096 fix(libkernel): create /data at start-up 2026-10-12 21:27:00 +02:00
Felipe Arce f3b8ef7abf feat(libSceLibcInternal): implement fdopendir 2026-10-12 16:23:42 -03:00
AlxcBTW 428963d3b3 feat(agc): write the worker sampler's stacks as collapsed stacks
APS5_SAMPLE_WORKER=<file> now also writes <file stem>.collapsed.txt: one
"frame;...;frame count" line per distinct stack of queue worker 0, root
first, frames as module+0xRVA, which speedscope.app and flamegraph.pl
open. Walks go up to 64 frames, and nothing allocates while the worker is
suspended.

The libraries are built with -fno-asynchronous-unwind-tables, so most of
their functions have no .pdata entry, only a DWARF record in .ehfram, and a
walk ended at the first such frame. The worker's walk now steps those
frames through their record, with the CIE/FDE decoding and the call frame
instructions of libc's unwinder, which move from Unwind.cpp into its
header unchanged. A module is indexed after the worker resumes, the first
time a walk meets it, and every register the step restores is read inside
the worker's stack. The process sampler (APS5_SAMPLE_THREADS) keeps its 12
frames through .pdata.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:23:11 +02:00
fdfawef324 f8257a112b feat(agc): return null from the workload builders for unregistered streams
sceAgcDcbSetWorkloadStreamInactive and the three ACB workload builders
(sceAgcAcbSetWorkloadsActive, sceAgcAcbSetWorkloadComplete,
sceAgcAcbSetWorkloadStreamInactive) threw for every call, and
sceAgcDcbSetWorkloadsActive read the workload ids before it checked the
stream.

The prosper console oracle (mattias800/prosper df074498,
prosper/tests/data/console_oracle/agcbuild.golden.tsv lines 48-56 and
196-204) measured all six calls on a PS5 with three argument patterns
and no workload stream registered: each returned null and wrote nothing.
Pattern b passes stream 1 with the workload ids at address 2, so the
console checks the stream before it reads the ids; here that call read
address 2 and crashed.

The four builders now return null for a null command buffer or a stream
that is not registered, and still throw for a registered stream: the
packet a registered stream gets is not measured, KytyPS5 has no body for
them, and its IT_NOP layout for the DCB calls differs from the
SET_UCONFIG_REG, WRITE_DATA and SET_WORKLOAD sequence OpenAGC
(3c7e8072, src/agc_workload_packet.c) derives from the driver. The
signatures are OpenAGC's DCB ones (src/game_compat.c), which says the
ACB calls use the same driver builders. sceAgcDcbSetWorkloadsActive now
checks the stream before the ids, as OpenAGC does. Recorded in
docs/dev/debt/libSceAgc.md.

agc_commands gains testWorkloadStreams: the three measured patterns for
all six calls (null, buffer untouched), an exhausted and a null buffer,
and a registered stream that throws without touching the buffer. On
upstream/main it fails with "sceAgcDcbSetWorkloadStreamInactive not
implemented", and the DCB active pattern b alone segfaults reading
0x2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:22:21 -04:00
Hannah Lyons 057a7317d1 feat(libc): implement expm1f, erff, lgamma and llroundl 2026-10-12 15:19:24 -04:00
Karan Vora 1dc99ba0a2 perf(shader): drop the input flush that rcp, rsq, sqrt, exp and log results never use
The SPIR-V emitters of FPRecip32, FPRecipSqrt32, FPSqrt, FPExp2 and FPLog2
flushed a denormal input to its signed zero before the host operation.
Since #537, unaryFloatSpecials replaces the host result for every input
below 2^-126 with the hardware's result for a zero of the same sign: rcp
and rsq give a signed infinity, sqrt the signed zero, log -inf and exp 1.0.
floatUnary and float16Unary both go through it, so the flushed input could
only change a value that is then discarded. v_rcp_iflag_f32 gets the same
special values and already went without the flush.

The pixel prologue computes PositionW as FPRecip32 of FragCoord.w without
those special values, so it now flushes FragCoord.w in the IR first and
gives the same result as before.
2026-10-12 15:17:41 -04:00
xXPBSXx 8f254e0623 docs(debt): move the resource tracker entries to debt/shader.md
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 7d508cf779 fix(recompiler): evaluate pruning conditions without throwing on null roots
A branch condition that reads through a null user-data pointer is now
treated as not evaluable instead of being caught, so every successor
stays active.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 37003b7a38 fix(recompiler): track which branch uses each image, not only each descriptor source
Several image instructions of different dimensions on the same T# address share
one descriptor source, so a source stays active as soon as one of them is taken
and the others are still checked against the T#. Record the images a block uses
as well, deactivate the images used only behind a branch the capture does not
take, and bind them as null descriptors.

PPSA15246 (Teardown) then passes the "incompatible with the static runtime
image interface" check of the three image_store variants that are not taken.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 0accdb487e style(recompiler): drop the comment above the resource control flow planner
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 7b9c7ca582 fix(recompiler): leave out descriptor sources behind a branch the capture decides
A compute kernel that picks a buffer or an image path from an SRT word reads
the same address as a V# in one path and as a T# in another. The resource
tracker now records which block uses each descriptor source and builds the
resource control flow from the structured block terminators, so a source whose
only uses sit behind a conditional branch with a clean, evaluable condition is
left inactive (bound as zero words, not validated) when the capture takes the
other path. A condition that reads inaccessible memory is not evaluable instead
of throwing.

PPSA15246 (Teardown) stopped on "runtime image descriptor is invalid" for the
untaken path of such a kernel.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:18 +02:00
xXPBSXx 4b6f463751 feat(libSceAmpr): answer sceAmprAmmGetUsageStatsData
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:14:14 +02:00
Adrià 682ef0c174
Merge pull request #4444 from fma-rc/docs/discussion-form-game-bring-up
docs: add a discussion form for game bring-up threads
2026-10-12 21:11:46 +02:00
Adrià Franch 908cd42880 docs: point the bring-up form to the blocker map
COMPATIBILITY.md no longer exists
2026-10-12 21:11:37 +02:00
Nesyth 2fb6dccd75 fix(agc): read comp-to-single DCC keys of sampled textures and storage images 2026-10-12 21:08:08 +02:00
Nesyth 78f8df053f perf(libc): share one snapshot of the registered guest ranges between leases until the registry changes 2026-10-12 21:07:13 +02:00
Dean Galvin 1d3f642760 fix(agc): report the test's page protection change to the page cache
The second import-window case of agc_write_tracking makes one page
unreadable with VirtualProtect or mprotect and expects an import that
stays watched to throw before it copies its range. On Windows CheckRange
answers from its table of committed arena pages, which is kept current
by the registry's invalidator. The first case had checked the same
allocation, so the table still said the page was readable, the check
passed and the copy faulted on the page.

ProtectPage now reports the change through GuestAllocationsInvalidate,
as the registry does for a guest protection change and as
tests/BdaResources.cpp does after VirtualProtect.
2026-10-13 08:04:52 +13:00
Dean Galvin 406c72d9be perf(agc): compare an import window's pages when the window closes
A host import that leaves its range watched can resolve the write
protection of the imported pages without storing anything, as NVIDIA's
Linux driver does for every page. ImportWatched stamped those pages as a
possible CPU store (BlockMaybeWritten), and only a storage image tracked
by 64 KiB unit could clear the stamp, by comparing with a copy of the
guest bytes it was uploaded from. Since #3372 a fast-cleared upload
therefore reads its whole surface from guest memory wherever such an
import is possible, although nothing but that compare uses the copy.

ImportWatched now takes whether the import leaves its range watched. If
it does, the range is copied before the stores made so far are
collected, and after the import each page whose write protection was
resolved is compared with that copy. A page with other bytes is stamped
as a CPU store. A page with the same bytes keeps only the CPU-side stamp
the label rules read, so UnchangedSince holds for it for every consumer:
storage images tracked by layer, snapshots and caches as well.

If it does not (a host-pointer import on a device whose imports are
compared, a direct import on Windows), nothing is copied or compared:
the pages resolved in the window get the CPU-side stamp and the range
leaves the watch before the tracker is unlocked. Where host-pointer and
dma-buf imports differ in this, the dma-buf import that follows a failed
host-pointer import is a window of its own, so each window knows its
kind before it starts.

BlockMaybeWritten, the written-stamp table, WatchedImportsStampPages and
the unit compare in StorageTexture::Refresh are gone. The fast-clear
upload skips the guest-byte copy whenever its range is watched, as
before #3372.
2026-10-13 08:04:52 +13:00
David 1c7a8305b5
feat(libSceContentSearch): implement the search calls over an empty library
Cult of the Lamb (PPSA06465) imports libSceContentSearch, including
sceContentSearchGetContentLastUpdateId, OpenMetadataByContentId,
GetTotalContentSize and GetNumOfContent (import audit in #2686), which
were declared without parameters and threw.

Give the nine pending calls the signatures of AlexAltea/orbital
db_types.json (cc408367, lines 555-569) and SharpProspero
Interop/Content/ContentSearch.cs (5dfdb4b0), and implement the content
library the host has, which is empty:

- GetNumOfContent, GetTotalContentSize and SearchContent report 0 rows
  and 0 bytes, GetContentLastUpdateId and the search calls report update
  id 0. A search limit above 92 returns 0x809D100A and every call before
  Init returns 0x809D1001, both from SharpProspero.
- Filters and sort conditions use SharpProspero's layouts (added to
  SceTypes.hpp with size checks) and are checked against its enums;
  unknown values, null outputs and null arrays with a length throw.
- OpenMetadata and OpenMetadataByContentId throw, as no source gives the
  error for content that is not in the library, so no metadata id is
  handed out and CloseMetadata, GetMetadataFieldInfo and
  GetMetadataValue throw too.

The update id value, the argument errors and the check order are
recorded in TechnicalDebt. The four ContentSearch calls leave the
unimplemented-imports test, and guest_content_search covers the library.
2026-10-13 00:34:12 +05:30
p3nicillin dbecde00e0 feat(libSceNetCtlApDialog): run the offline access-point dialog lifecycle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:03:59 +01:00
LeandroLP23 67c943f119 docs(debt): drop the dup and select clauses from the random device entry
dup and dup2 carry the random device over to the new descriptor, and
select reports /dev/random and /dev/urandom ready, as the select entry
already says.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:02:33 +02:00
LeandroLP23 3e3b0a27ca docs(debt): mention --windows-gui and its lost output on the console entry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:02:32 +02:00
Adrià d53960612f
Merge pull request #4686 from adriadam10/docs/roadmap-link
docs: replace COMPATIBILITY.md with a link to the blocker map
2026-10-12 21:02:25 +02:00
Eli Yarson f80a2f5535 fix(libkernel): resolve original .prx names to relinked .guest.prx in stat, open and reachability
Titles check for a plugin with sceKernelStat before loading it through the engine path. The relinker emits only X.prx.guest.prx, so the check failed and PSNCore started with the raw (0, NULL) arguments. Share the dlopen mapping with sceKernelStat, read-only sceKernelOpen and sceKernelCheckReachability when the original name is absent.
2026-10-12 16:02:14 -03:00
Adrià Franch 7f51a6bcae docs: replace COMPATIBILITY.md with a link to the blocker map 2026-10-12 21:01:37 +02:00
LeandroLP23 d3011ae113 fix(libkernel): share the directory state between duplicated descriptors on Windows
dup and dup2 of a Windows directory descriptor threw. FreeBSD's
kern_dup makes both descriptors refer to one open file, so they share
the read position; the duplicate now points at the same DirectoryState,
which getdents and lseek go through, and keeps it after the original
is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:01:33 +02:00
Yunus Shaikh a9c625acaf fix(shader): return 0 for 8-bit UNORM color comparison
image_sample_c on an 8-bit UNORM T# returns 0 for every compare
function on gfx1035, as on 8-bit sRGB (measured on the hardware oracle
in the review of #3438), but the emulated comparison still compared the
red channel. 8, 8_8 and 8_8_8_8 UNORM now return 0 like 8-bit sRGB.

Every texel comparison of those formats is 0, so a bilinear cube
without DISABLE_CUBE_WRAP and an anisotropic S# cannot change the
result; both are accepted for them, as the cube already was for 8-bit
sRGB. Other formats keep both refusals.

The test rows that check real comparisons move to 16_16 UNORM.
2026-10-12 12:00:21 -07:00
jagrat Singh dbce47244b feat(libkernel): implement sceKernelGetLibkernelTextLocation 2026-10-13 00:30:15 +05:30
fdfawef324 0ca047e60d fix(libc): size collate<char>::do_transform strings as the console does
collate<char>::do_transform gave the result std::string a capacity of
exactly its length and threw for results of 4095 characters or more.
The console's libc.prx grows the string from its inline capacity of 15
to length | 15, so a 26-character result has capacity 31 and a 32-byte
allocation. Once capacity + 1 reaches 0x1000 bytes it takes the
over-aligned large allocation that the guest's inlined deallocation
expects: operator new(bytes + 0x27), the data aligned to 32 bytes and
the raw pointer stored just before it. This is the scheme of
Dinkumware's basic_string (_Newsize | _ALLOC_MASK) and of its
_Allocate in <xmemory0> (_BIG_ALLOCATION_THRESHOLD 4096,
_BIG_ALLOCATION_ALIGNMENT 32, _NON_USER_SIZE sizeof(void*) + 31),
including its bad_alloc when bytes + 0x27 would wrap.

do_transform now uses that capacity and allocation; the throw for long
results is gone. The debt entry in docs/dev/debt/libc.md now records
the capacity and large storage as unverified on hardware. guest_collate
checks the capacity and allocation size of a 26-character result, the
last small result (4079 characters, a 4080-byte block) and the first
large one (4080 characters, capacity 4095, a 4096 + 0x27-byte block
with the data aligned to 32 and the raw pointer before it). With main's
LocaleSupport.cpp it aborts at the allocation-size check of the
26-character result (lastSize 27 instead of 32).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:59:36 -04:00
Yunus Shaikh d0cef9afdb test(libSceNet): define and run CheckEpollWritable before CheckPoolStats
#4673 adds a test after CheckEmptyEpoll and its call after CheckEmptyEpoll(), so placing the new check there made the two pull requests conflict.
2026-10-12 11:57:27 -07:00
Yunus Shaikh 78d594d9f6 Merge remote-tracking branch 'origin/main' into fix/libscenet-epoll-out 2026-10-12 11:56:48 -07:00
jagrat Singh 057b8108b3 feat(libkernel): implement setregid
Follow FreeBSD sys_setregid and root identity model: accept real and effective group id 0 or -1 (unchanged) and change nothing; any other id throws. Update GuestCredentials test and TechnicalDebt.
2026-10-13 00:25:13 +05:30
Adrià a60f52e318
Merge pull request #4400 from adriadam10/ci/stale-conflict-warning
ci: remind stale conflicting pull requests to rebase
2026-10-12 20:54:11 +02:00
Yunus Shaikh ac8106bda2 refactor(libSceNet): define timeout_option before receive_error
#4673 adds a function after receive_error, so defining timeout_option there made the two pull requests conflict.
2026-10-12 11:52:49 -07:00
Dean Galvin 59db660f7b test(agc): reserve the arena host hole the write-tracking checks query
On Windows the guest arena is reserved while libc loads, around whatever
the process has already mapped in its interval. About one start in forty
has nothing mapped there, so the arena has no host hole:
agc_driver_graphics then fails with "write-watched arena contained no
host exclusion to test" and agc_write_tracking prints that its host-hole
checks were skipped.

agc_write_tracking now starts itself suspended, reserves one allocation
granule in the middle of the child's arena interval before the child's
loader runs, and the child runs the host-hole checks on that range. The
range is a host hole on every start, so the checks no longer depend on
the address layout. The four queries agc_driver_graphics made on an
enumerated hole are among them and are removed there.
2026-10-13 07:47:05 +13:00
David f3a6edfccb feat(libSceShare): implement sceShareCaptureVideoClipExtended
Imported by the Share plugin of Hollow Knight: Silksong (PPSA12544).
Takes the extended parameter and request id like
sceShareCaptureScreenshotExtended, as KytyPS5 does: nothing is
captured, so it stores the invalid request id -1 when the pointer is
not null and returns NOT_SUPPORTED.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 245090904b64354f68fba3b8ef6fd6e232a3b1e6)
2026-10-12 18:46:45 +00:00
Adrià Franch 92aa5b971c docs(technical-debt): drop stale EAAccess unknown-info line and move the new one off the section end
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 18:46:44 +00:00
David e2954aa188 feat(libSceNpPartner001): implement sceNpEAAccessInitialize and sceNpEAAccessTerminate
UFC 6 (PPSA23566) imports both. Initialization marks the library
initialized and termination clears it, as in shadPS4. Termination
without initialization and a second initialization throw, since no
PS5 source settles their results.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit d2866a886a7702cdaad56d161460a3ce86ede629)
2026-10-12 18:46:44 +00:00
David 2dc4d9d65c feat(libSceAmpr): implement std::_Throw_Cpp_error
Throw a guest std::system_error for the Dinkumware _Cpp_error codes,
as in VC12 and microsoft/STL thread0.cpp: the error_code holds the
FreeBSD errno and a guest generic error category modelled in libc, and
what() is the table message followed by the category message. Codes
outside the table throw std::out_of_range. The system_error copy
constructor and assignment now copy the error_code too.

Imported by Deathloop (PPSA01670) and Minecraft Legends (PPSA05510).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit bed1deafdee4b50bea9683a1cead0d0320150eb7)
2026-10-12 18:46:43 +00:00
David 1d67426ede feat(libSceLibcInternal): implement getopt and getopt_long
Export getopt (VUtibKJCt1o) and getopt_long (8VVXJxB5nlk) from
libSceLibcInternal together with the guest variables optind
(zCnSJWp-Qj8), optarg (zG0BNJOZdm4), opterr (yaFXXViLWPw), optopt
(FwzVaZ8Vnus) and optreset (CZNm+oNmB-I). The NIDs were computed with
tools/nid_names.py and all seven are in aerolib; the PS5 payload SDK
f5d40f02 lists the functions and the five objects in
sce_stubs/libSceLibcInternal.c.

Titles (predicted stops from registry order, not observed runs):
- XPSemu PPSA97358, after execv/execve, the signal waits, the
  credential setters, mlockall and tcsendbreak:
  "FAIL: unresolved ELF import VUtibKJCt1o from libSceLibcInternal.prx"
  (getopt idx 204, 10 call sites from 0xF6C23D), then optind (207),
  optreset (208), optarg (209).
- RetroArch PPSA99169: getopt_long (idx 554, call sites 0xD2FC0C,
  0xD3002A), optind (553), optarg (555).

Behaviour follows FreeBSD release/11.4.0 8c639945: getopt is
lib/libc/stdlib/getopt.c (no permutation, GNU "::" optional argument,
lone "-" as an option when listed), getopt_long is getopt_internal
from lib/libc/stdlib/getopt_long.c with GNU_COMPATIBLE (permutation,
POSIXLY_CORRECT read through the guest getenv on the first call and on
optreset, "+" and "-" prefixes, "W;", optind 0 as a reset, abbreviation
and ambiguity rules and the GNU warning texts). Warnings are written to
the guest stderr stream as "<__progname>: <message>", as warnx does.
The struct option layout and argument kinds are include/getopt.h.
The code lives in libSceLibcInternal because __progname is a libkernel
export that libc cannot link.

A null getopt option string, a null argument vector or argument, a
negative optind and an optind moved inside a pending permutation throw
where FreeBSD faults. getopt_long_only is not exported.

guest_getopt runs the FreeBSD getopt_test cases (contrib/netbsd-tests
t_getopt.sh with the h_getopt and h_getopt_long result format) plus
grouped and optional arguments, "--", permutation order, "+" and "-"
prefixes, optreset and optind 0 restarts, POSIXLY_CORRECT, all three
long argument kinds, abbreviations, ambiguity, flag pointers, "W;",
optopt with opterr 0 and the exact stderr text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit a2740166d93920c301e1add23ceea762b4acad27)
2026-10-12 18:46:43 +00:00
MarcZX100 acd821f7ce fix(tools): audit sectionless guest modules 2026-10-12 18:46:25 +00:00
Leandro Soares 6054923cd5 fix(tools): audit the imports of --modules files
(cherry picked from commit 2d0d707802c190f4d8ed93d01427576accbc08b1)
2026-10-12 18:46:25 +00:00
Scramblrrrr 3824282e59 test(agc): keep queue zero alive after an interrupted storage idle wait 2026-10-12 18:46:24 +00:00
MarcZX100 afbc3c8eb5 fix(agc-driver): delay storage writeback until queue activity settles 2026-10-12 18:46:24 +00:00
MarcZX100 cf25df9a62 test(cmake): serialize load-sensitive Windows tests 2026-10-12 18:46:19 +00:00
Hussein Kadhim df8f3097be fix(libkernel): accepted sockets inherit the listener's non-blocking mode
FreeBSD accept() copies SS_NBIO from the listening socket (ACCEPT4_INHERIT in
kern_accept4). The accepted guest socket was always recorded as blocking:
fcntl(F_GETFL) never showed O_NONBLOCK, and the host socket kept the host's own
inheritance (kept on Windows, dropped on Linux). Apply the listener's mode to
the accepted socket.
2026-10-12 18:46:10 +00:00
MarcZX100 32b0c8d79a test(libc): cover null readdir_r result pointer 2026-10-12 18:46:05 +00:00
David ce4e6ce839 feat(libc): implement readdir_r
VLC PPSA85300 imports readdir_r (J0kng1yac3M) from
libSceLibcInternal.prx. Predicted stop after getlogin_r, from relink
registry order (idx 343, call site 0x16A0B91):
  FAIL: unresolved ELF import J0kng1yac3M from libSceLibcInternal.prx

The NID is readdir_r in aerolib (tools/nid_names.py --nid J0kng1yac3M:
rename-ready readdir_r); the title binds it to libSceLibcInternal, where
the PS5 payload SDK (ps5-payload-dev/sdk f5d40f02,
sce_stubs/libSceLibcInternal.c) lists it beside readdir, so it goes into
libc next to readdir.

FreeBSD 11.4 (freebsd-src 8c639945, lib/libc/gen/readdir.c:106-135):
save errno and clear it, take the stream lock, read the next entry and
copy _GENERIC_DIRSIZ(dp) bytes of it into the caller's entry, set
*result to the entry or NULL at the end, return errno when the read
fails, otherwise restore errno and return 0. The guest struct dirent is
the SDK's FreeBSD 11 layout (include/freebsd/sys/dirent.h, 264 bytes,
name at 8), and _GENERIC_DIRSIZ equals the recordLength readdir already
computes. The read itself is readdir's, moved into a helper that both
call under the per-stream mutex, so readdir and readdir_r share one
position. A null stream returns EBADF, as readdir sets it; a null entry
or result pointer throws instead of faulting.

The call site is libbluray 1.4.1 _dir_read_posix
(videolan/libbluray 7d94f266, src/file/dir_posix.c:73-88), reached when
VLC opens a Blu-ray folder or ISO. The disassembly at 0x16A0B91 passes a
264-byte stack buffer (sizeof(struct dirent)) and reads d_name at
offset 8.

Tested on Windows (MinGW): guest_directory 3/3 (readdir_r over a
temporary directory to the end gives the same names and types as
readdir, copies only recordLength bytes, keeps errno at the end,
continues the position of an interleaved readdir, EBADF for a null
stream, throw for a null entry).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 01085afe122d6d2c6af9e54c745e2a7c448f7b24)
2026-10-12 18:46:05 +00:00
Blazing Cook c20583bd34 fix(ampr): reject command buffer offsets beyond bound memory
(cherry picked from commit b92287dc68d538d44369d2a09500b4404ca53fca)
2026-10-12 18:45:58 +00:00
Yunus Shaikh 2c0d8d48bc Merge remote-tracking branch 'origin/main' into fix/libc-strtod-quiet-nan-windows
# Conflicts:
#	docs/dev/TechnicalDebt.md
2026-10-12 11:45:40 -07:00
milescoviello 953ae58bc8 fix(libkernel): hold back new rwlock readers while a writer waits
Guest rwlocks were a host std::shared_timed_mutex. On Linux that is a
glibc rwlock, which prefers readers: a reader is let in while a writer
is blocked, so a writer waiting on a lock that readers keep taking in
overlapping turns never gets it (a probe with four readers each holding
the lock for 4 ms still had scePthreadRwlockWrlock waiting after 5 s,
with 4924 read acquisitions meanwhile), and tryrdlock succeeds while a
writer waits.

FreeBSD 11.4 libthr gives waiting writers precedence. A rwlock starts
without URWLOCK_PREFER_READER (rwlock_init() callocs it), and
lib/libthr/thread/thr_umtx.h, _thr_rwlock_tryrdlock(), refuses a reader
while a writer waits:

	if ((flags & URWLOCK_PREFER_READER) != 0 ||
	    (rwlock->rw_flags & URWLOCK_PREFER_READER) != 0)
		wrflags = URWLOCK_WRITE_OWNER;
	else
		wrflags = URWLOCK_WRITE_OWNER | URWLOCK_WRITE_WAITERS;

as does do_rw_rdlock() in sys/kern/kern_umtx.c for the blocking wait.
The only exception is a thread that already holds a read lock, so that a
recursive read cannot deadlock (lib/libthr/thread/thr_rwlock.c,
rwlock_rdlock_common()):

	if (curthread->rdlock_count) {
	...
		flags = URWLOCK_PREFER_READER;

The rwlock is now a mutex and condition with a count of waiting writers:
a reader waits while a writer holds the lock or, unless the thread
already holds a read lock, while a writer waits; a timed writer that
gives up wakes the readers it held back. The TechnicalDebt note about
locking guest rwlocks once for winpthreads no longer applies to them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:43:43 +00:00
screamm 43ffba87e4 feat(libkernel): implement fcntl record locks on Windows
ProsperoRadio (PPSA99001) stops at 33.7 s on Windows, with #3649
applied so that it gets past poll, on the throw from #3840:

    what():  fcntl: record locks are not implemented on Windows, command=12, fd=3

F_GETLK, F_SETLK, F_SETLKW and the F_OGETLK, F_OSETLK and F_OSETLKW
forms now follow FreeBSD 11.4's kern_fcntl and lf_advlockasync
(8c639945): an SEEK_CUR start is resolved against the descriptor offset
(EOVERFLOW past OFF_MAX), F_RDLCK needs read access and F_WRLCK write
access (EBADF), another type is EINVAL, and the range is checked against
the file size for SEEK_END (EINVAL for a negative start or an unknown
whence, EOVERFLOW past OFF_MAX). The guest is one process, and
lf_blocks never lets locks of one owner conflict, so whichever
descriptor or thread asks, F_SETLK and F_SETLKW succeed without waiting
and F_GETLK reports F_UNLCK, as on the console. Nothing is held on the
host: Windows byte-range locks are mandatory and would fail the guest's
own reads and writes through other descriptors. A lock from the end of
a directory throws.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 18:42:53 +00:00
Adrià 1acfa7f788
Merge pull request #4411 from adriadam10/docs/debt-per-library
feat(tools): keep technical debt in one file per library
2026-10-12 20:39:57 +02:00
Adrià Franch 342a8c4590 docs(debt): move library entries to one file per library
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 20:38:29 +02:00
Adrià Franch 555099ae23 feat(tools): keep technical debt in one file per library
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 20:38:17 +02:00
Dean 1b92f7f5dc
Merge pull request #4605 from AlxcBTW/fix/agc-dword-pattern-fill-format
fix(agc): take the dword pattern-fill kernel only through a 32-bit output format
2026-10-13 07:35:32 +13:00
Superhe11 faf1cd77c6 fix(relinker): validate the ELF object version and header size 2026-10-12 21:35:13 +03:00
Dean 9d3629cc91
Merge pull request #4268 from AlxcBTW/fix/vop3p-int-neg
fix(shader): apply VOP3P neg_lo/neg_hi to packed 16-bit integer sources
2026-10-13 07:35:10 +13:00
Dean ed47df8ba9
Merge pull request #4385 from AlxcBTW/fix/host-import-retire-hold
fix(agc): keep a host import dropped mid-hold for every batch that hold opens
2026-10-13 07:34:49 +13:00
Dean c90404f97a
Merge pull request #4518 from AlxcBTW/fix/bda-unaligned-dword-write
fix(shader): store unaligned 32-bit BDA writes byte by byte
2026-10-13 07:34:34 +13:00
Dean 0c75934f5f
Merge pull request #4470 from overlordxrz-source/fix/vertex-one-lane-waves
fix(recompiler): run vertex programs as one-lane waves without vertex subgroups
2026-10-13 07:34:13 +13:00
Dean 9f7c40e36f
Merge pull request #4389 from ivica00/fix/rect-list-invariant-position
fix(shader): decorate rect-list gl_Position as Invariant
2026-10-13 07:33:51 +13:00
David 61fbec8ef1 fix(libkernel): fail lseek on a pipe with ESPIPE on Windows 2026-10-12 18:33:31 +00:00
Dean 5d933db02e
Merge pull request #4364 from SleglJan/fix/structurizer-return-join
fix(recompiler): give an early-return selection a merge when its parent's other arm reaches the same join
2026-10-13 07:33:30 +13:00
Dean 9df77462cc
Merge pull request #4544 from poonasor/feat/libkernel-module-info2
feat(libkernel): implement sceKernelGetModuleList2 and sceKernelGetModuleInfo2
2026-10-13 07:33:08 +13:00
Dean ee966bb747
Merge pull request #4542 from saxopa/fix/relinker-sce-relro
fix(relinker): load a PT_SCE_RELRO segment that no PT_LOAD covers
2026-10-13 07:32:37 +13:00
Dean 9c99f43440
Merge pull request #4537 from coltken95/claude/sleepy-dirac-e44kw4
fix(libc): sort with FreeBSD 11's qsort so equal elements keep its order
2026-10-13 07:32:15 +13:00
Dean 21c27e60a4
Merge pull request #4536 from orishamir/fix/debugout-host-stderr
fix(libkernel): keep sceKernelDebugOutText off a redirected guest stderr
2026-10-13 07:31:53 +13:00
Dean a29ea3a2e3
Merge pull request #4529 from NahuelGenchi/fix/libc-new-length-error
fix(libc): throw bad_alloc from operator new when the default heap rejects a size
2026-10-13 07:31:32 +13:00
Dean 455027e520
Merge pull request #4528 from kr4y4/test/agc-dot2-rounding-float64-skip
test(agc): skip the f32 dot2 rounding test on devices without shaderFloat64
2026-10-13 07:31:10 +13:00
Dean 3b5c96377d
Merge pull request #4516 from Ondra97/codex/file-lock-lifetime
fix(libs): share Windows flock state between duplicated descriptors
2026-10-13 07:30:48 +13:00
Dean f7b238e43b
Merge pull request #4513 from winstrex/fix/libkernel-open-directory-trailing-separator
fix(libkernel): open directories whose host path ends in a separator on Windows
2026-10-13 07:30:26 +13:00
Dean 66013d910b
Merge pull request #4511 from rebelittle/fix/windows-seh-unwind
fix(libc): unwind guest exceptions through Windows DLL frames
2026-10-13 07:30:11 +13:00
Dean 4756c051ef
Merge pull request #4504 from yunusshaikh7/fix/savedata-dir-name-trailing-wildcard
fix(savedata): let a trailing % in a directory name pattern match nothing
2026-10-13 07:29:49 +13:00
Dean acbaa8a16d
Merge pull request #4495 from SibteProf/fix/relinker-sdk-mman-syscalls
fix(relinker): recognize SDK memory syscall trap patterns
2026-10-13 07:29:18 +13:00
Dean 4a49cda98f
Merge pull request #4492 from ideabsassistenza/fix/pad-input-persistent-sequence
fix(pad-input): preserve command ordering across clock resets
2026-10-13 07:28:56 +13:00
Dean 2350fa5353
Merge pull request #4489 from Dripl1x/codex/guest-export-index
perf(relinker): index guest exports for dependency resolution
2026-10-13 07:28:34 +13:00
Dean e305bc4e4a
Merge pull request #4483 from fable-void/pr/relinker-jump-table-targets
fix(relinker): treat jump table targets as branch targets when moving instructions
2026-10-13 07:28:12 +13:00
Dean 9907993e5f
Merge pull request #4473 from arcwiser/fix/pr-overlap-fetch-batches
fix(tools): batch pr_overlap fetch refspecs
2026-10-13 07:27:51 +13:00
Dean 60f4c71608
Merge pull request #4466 from babyhuey/feat/libkernel-socid-cpumode
feat(libkernel): export sceKernelGetMainSocId, sceKernelGetCpumode and sceKernelGetCpuFrequency
2026-10-13 07:27:28 +13:00
Dean d4ccf91e03
Merge pull request #4462 from milescoviello/carry/libc-umodti3-4460
feat(libc): implement __umodti3
2026-10-13 07:27:06 +13:00
Dean b9f194a890
Merge pull request #4455 from SibteProf/fix/libc-secure-copy-size-limits
fix(libc): reject oversized secure copy destinations
2026-10-13 07:26:35 +13:00
Dean 91e263824e
Merge pull request #4452 from orishamir/fix/relinker-null-symbol-imports
fix(relinker): resolve relocations against null symbol entries to 0
2026-10-13 07:26:13 +13:00
Yunus Shaikh 0a1dff2fcc perf(agc): read the draw snapshot cap when resources are built
ShaderResources::PrepareDrawBindings called MaxDrawSnapshotBytes() on
every draw, and it read APS5_DRAW_SNAPSHOT_MAX_MIB with getenv each
time. The UCRT's getenv takes the environment lock and scans the whole
block. In Planet of Lana (about 90,000 draws per 10 s) that call was
2.6% of the queue-0 worker's samples.

The cap is now read once when a ShaderResources object is built and
kept in it, so a cached resource template uses it for every draw it
serves. In Planet of Lana the record phase drops from 21.2 to 18.7 us
per draw.

drawSnapshotCapTests also raises the variable after building the
resources and checks that they keep the cap they were built with. With
the previous ShaderResources.cpp it fails there.
2026-10-12 11:26:09 -07:00
Dean 715bfabdac
Merge pull request #4426 from babyhuey/feat/libc-overflow-error
feat(libc): export std::overflow_error type info, vtable and destructor
2026-10-13 07:25:42 +13:00
Dean 45e1b80636
Merge pull request #4421 from Lorkydey/fix/nid-names-invalid-cli-nids
fix(tools): reject invalid NIDs in the name lookup CLI
2026-10-13 07:25:21 +13:00
Dean 5d3e1740ff
Merge pull request #4420 from Lorkydey/fix/check-skips-disabled-tests
fix(ci): reject disabled CTest cases in skip validation
2026-10-13 07:25:00 +13:00
Dean 30e169127d
Merge pull request #4419 from Ondra97/codex/deferred-module-lifetime
fix(libkernel): bind deferred initialization to the loaded image
2026-10-13 07:24:39 +13:00
Dean 6f47d9117f
Merge pull request #4416 from muhzain05/fix/videodec2-picture-owner
fix(videodec2): answer picture info from the decoder that last wrote the buffer
2026-10-13 07:24:18 +13:00
Dean 3ede42aace
Merge pull request #4414 from AchrafReyani/fix/agc-single-sample-scan-into-msaa-target
fix(agc): rasterize a single-sample scan into a multisampled target at the pixel center
2026-10-13 07:23:57 +13:00
Dean 31ade9abc2
Merge pull request #4406 from Onny2/test/sce-net-absolute-invalid-name
test(net): look up the unresolvable name as an absolute name
2026-10-13 07:23:34 +13:00
Dean 83e6be8a69
Merge pull request #4394 from AaravMehta-07/codex/sce-net-stream-sender
fix(libSceNet): preserve successful stream recvfrom results
2026-10-13 07:23:13 +13:00
Dean 57f91221c4
Merge pull request #4380 from AzazelSensei/fix/libc-stream-errors
fix(libc): return stream errors from fwrite, fread and fputs instead of throwing
2026-10-13 07:22:52 +13:00
Dean b5546111b6
Merge pull request #4373 from milescoviello/fix/recvmsg-seqpacket-trunc
fix(libkernel): report MSG_TRUNC for SOCK_SEQPACKET records in recvmsg
2026-10-13 07:22:38 +13:00
Dean b701bc5d8d
Merge pull request #4372 from milescoviello/fix/fflush-read-stream
fix(libc): leave a stream that is reading untouched in fflush on Linux
2026-10-13 07:22:17 +13:00
Dean 3377986a60
Merge pull request #4371 from milescoviello/fix/net-socket-timeval-timeouts
fix(libSceNet): read setsockopt timeouts as struct timeval
2026-10-13 07:21:55 +13:00
Dean edddd6a17b
Merge pull request #4370 from milescoviello/fix/ipv6-only-sockets
fix(net): make new IPv6 sockets IPv6-only as FreeBSD does
2026-10-13 07:21:34 +13:00
Dean 59a2167a93
Merge pull request #4368 from SandstromPL/fix/conventions-run-from-subdirectory
fix(conventions): check the whole repository when run from a subdirectory
2026-10-13 07:21:13 +13:00
Dean f30e89ee7c
Merge pull request #4365 from Gashrod1/fix/libc-strxfrm-truncation
fix(libc): truncate strxfrm like FreeBSD's C locale
2026-10-13 07:20:51 +13:00
Dean ee2c0f9b92
Merge pull request #4361 from Roojool/contrib/round2-c20
test(tools): support Python 3.10 in encoding regressions
2026-10-13 07:20:30 +13:00
Dean dfe9ccf6a2
Merge pull request #4360 from milescoviello/fix/wide-printf-grouping-flag
fix(libc): accept the ' flag in the wide printf family
2026-10-13 07:20:09 +13:00
Dean 15e64abd77
Merge pull request #4359 from milescoviello/fix/syslog-wide-format
fix(libc): format guest wide arguments in syslog on Linux
2026-10-13 07:19:47 +13:00
Dean 655cba83d5
Merge pull request #4358 from milescoviello/fix/scanf-binary-prefix
fix(libc): read %i without the C23 binary prefix in the scanf family on Linux
2026-10-13 07:19:26 +13:00
Dean 2c579b55f9
Merge pull request #4357 from milescoviello/fix/wide-printf-positional
fix(libc): format positional arguments in the wide printf family
2026-10-13 07:19:05 +13:00
Dean b538b28d8b
Merge pull request #4354 from milescoviello/fix/fopen-append-position
fix(libc): start append-mode streams at the end of the file
2026-10-13 07:18:50 +13:00
Dean bde332a59c
Merge pull request #4353 from milescoviello/fix/printf-errno-message
fix(libc): print the guest errno message for %m in the printf family
2026-10-13 07:18:29 +13:00
milescoviello eb9da23e68 fix(libkernel): accept socket option values longer than an int
setsockopt() on a guest socket failed with EINVAL unless the option
length was exactly sizeof(int), so an option passed as a 64-bit value
(for example SO_BROADCAST or SO_RCVBUF from an int64_t or size_t) was
rejected and silently left unchanged.

FreeBSD 11.4 reads these integer options through sooptcopyin() with the
size of an int as both the wanted and the minimum length, and only a
shorter value fails. sys/kern/uipc_socket.c, sooptcopyin():

	 * If the user gives us more than we wanted, we ignore it, but if we
	 * don't get the minimum length the caller wants, we return EINVAL.
	...
	if ((valsize = sopt->sopt_valsize) < minlen)
		return EINVAL;
	if (valsize > len)
		sopt->sopt_valsize = valsize = len;

and sosetopt() uses it for SO_BROADCAST, SO_REUSEADDR, SO_SNDBUF and
SO_RCVBUF:

	error = sooptcopyin(sopt, &optval, sizeof optval,
	    sizeof optval);

A longer value now sets the option from its first int; a shorter one
still fails with EINVAL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:04:27 +00:00
Yunus Shaikh c55f377fb0 refactor(libc): place the NaN helpers and test includes apart from neighbouring changes 2026-10-12 11:03:36 -07:00
Aarav f11798413e fix(libkernel): guard SCE pthread semaphore count overflow
Reject invalid initial counts without replacing the semaphore and preserve a saturated count on post, matching the existing guest POSIX limits with encoded SCE errors. Add boundary and unchanged-value guest regressions.
2026-10-12 23:26:25 +05:30
Gustavo Ariel Gamarra Rojas 554e615524 test(agc): check that a buffer written over pending storage results keeps both
A storage image is written on the GPU and left pending, then a leased
address-based use writes the first 40800 bytes of the same memory as a
buffer and every pending image is flushed: guest memory must hold the
buffer's bytes where it wrote and the image's texels everywhere else.
Runs with watched and with compared imports and returns 77 when host
imports or write watching are unavailable.
2026-10-12 14:52:44 -03:00
Gustavo Ariel Gamarra Rojas f4ff589679 fix(agc): store pending storage results before a use writes the same memory as a buffer
Before a dispatch or draw that writes buffer ranges is recorded, store the
results of storage images still pending in those ranges, whatever path
serves the range (in place, copied, or an address-based heap, which the
per-region flush exempts), and include the written ranges in the pending
scan of a revalidated resource set.

Without it the image's write-back could come after the buffer write: a
partial write to a 64 KiB block made the write-back keep the whole block,
dropping the image's results in the rest of it, and a write-back recorded
after the buffer write stored the image's older texels over it. Titles
that reuse transient memory for textures and buffers in the same frame
(Unreal Engine's render graph transient allocator) read corrupted tile
data as a result.
2026-10-12 14:52:44 -03:00
lorkydey c052b7c720 feat(libc): query filesystem statistics by guest path 2026-10-12 19:42:52 +02:00
milescoviello 11bda1c6a8 feat(libc): implement ceil
Kart PS5 (PPSA99611) stops on gacfOmO8hNs (ceil, per tools/nid_names.py)
from libc.prx once __umodti3 resolves (title run on #4519). ceil rounds
toward positive infinity whatever the rounding mode, keeps the sign of
zero and of results that round to zero, and returns infinities and NaNs
unchanged, as std::ceil does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:27:00 +00:00
MiniRML 63022e23cc Merge remote-tracking branch 'upstream/main' into fix/nid-windows-unicode-paths 2026-10-12 10:05:59 -07:00
MiniRML 69b6cb665f Merge remote-tracking branch 'upstream/main' into fix/kernel-json-number-locale 2026-10-12 10:05:55 -07:00
MiniRML a7799c4315 Merge remote-tracking branch 'upstream/main' into fix/nid-buffer-validation 2026-10-12 10:05:51 -07:00
QRMOE 79d88af192
test(libc): cover wide transform output bounds 2026-10-12 19:04:00 +02:00
QRMOE 22d03d497f
fix(libc): terminate truncated wcsxfrm results 2026-10-12 19:03:18 +02:00
QRMOE 0950b19854
test(libc): cover exhausted tokenizer state 2026-10-12 19:01:27 +02:00
MiniRML ad43a486f0 test(nid): keep reference registration independent of other topics 2026-10-12 10:00:50 -07:00
QRMOE 3cb434715a
fix(libc): clear exhausted strtok continuation state 2026-10-12 19:00:50 +02:00
MiniRML 1c500fb298 test(nid): separate export-reference registration from path tests 2026-10-12 09:59:52 -07:00
MiniRML 4df7877334 test(nid): avoid competing hash test registration 2026-10-12 09:57:43 -07:00
MiniRML 89d89ac048 fix(nid): preserve each ELF string consumer independently 2026-10-12 09:53:17 -07:00
MiniRML f25eba673d fix(nid): bound version requirement chains to their section 2026-10-12 09:51:20 -07:00
Borooooo 39bed4b9d7 fix(relinker): accept paths longer than 260 characters on Windows
relinker.exe carried only MinGW's default manifest, so Windows kept MAX_PATH for it even with long paths enabled, and an input under a deep directory failed with 'Cannot open file'. Embed a manifest with longPathAware that keeps asInvoker and the Windows 10 supportedOS entry.
2026-10-12 18:50:50 +02:00
MiniRML 617e7acfa8 test(nid): avoid competing hash test registration 2026-10-12 09:46:59 -07:00
MiniRML abe8fb8193 fix(nid): validate ELF table layouts before patching 2026-10-12 09:45:30 -07:00
Claude 1285ee06f5
fix(shader): keep a folded selection's branch when SPIR-V 1.6 forbids one label
The previous commit pointed the untaken side of a folded branch that
keeps its selection merge at the merge block. That adds an edge from the
header to the merge block: the merge block's phis have no operand for it,
and values from the taken arm no longer dominate the merge block, as
fuzzing with validation on found once the repeated label stopped hiding
it.

For SPIR-V 1.6 the branch is now left as it is, so the control flow and
the untaken arm stay; earlier versions keep naming the taken label twice,
which they allow, and drop the untaken arm as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-12 16:40:43 +00:00
MiniRML bb443667ec fix(nid): validate GNU hash geometry before rewriting 2026-10-12 09:39:28 -07:00
MiniRML 6e0f942f0f fix(nid): resolve PE reference exports through their ordinals 2026-10-12 09:36:42 -07:00
Rujul Talekar 0c52b2a0b0 fix(tools): use configured CMAKE_MAKE_PROGRAM for test registration audit
tools/check_test_registration.py invoked bare ninja from PATH, so the audit failed with FileNotFoundError when the configured Ninja lived outside PATH. Read CMAKE_MAKE_PROGRAM from the build directory CMakeCache.txt and fail honestly when it is missing. Adapt the mocked built tests to a configured build and add a regression that runs a real Ninja from a path with spaces outside PATH.
2026-10-12 21:59:53 +05:30
MiniRML db577b4a34 fix(release): preflight asset destination types 2026-10-12 09:29:03 -07:00
Nithishvelan M 98e0fb0dcd
fix(libSceWebBrowserDialog): permit termination from finished state in sceWebBrowserDialogTerminate 2026-10-12 21:58:02 +05:30
MiniRML 17a3d8a1a0 fix(oracle): honor current target override 2026-10-12 09:27:33 -07:00
MiniRML 521cc95b18 fix(oracle): validate input words before tool lookup 2026-10-12 09:26:05 -07:00
Claude 52b679c552
fix(shader): keep the back-edge of a loop whose continue block is dead
Pipeline specialization replaced every unreachable block with
OpUnreachable. When a loop's continue target became unreachable, either
because the latch exited on a constant condition or because the emitter
had already folded it, that removed the header's only back-edge, and
validation rejects a loop header targeted by no back-edge block.

An unreachable continue target of a live loop header now keeps an
OpBranch to its header, as SPIRV-Tools' dead branch elimination does.
The header's phis keep an operand for it, naming the phi itself, since
the edge is never taken and the continue block's values are gone.

Found by recompiling mutated compute shaders with SPIR-V validation on,
for loops with a literal bound such as s_cmp_lt_u32 with two constants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-12 16:25:22 +00:00
MiniRML 53d10eac9a fix(tools): exclude cut helpers from NID accounting 2026-10-12 09:24:13 -07:00
AlxcBTW 81e4638254 docs: add a performance report issue form and ask for summary lines
.github/ISSUE_TEMPLATE/performance.yml asks for the title and scene, what is
slow or what changed, the summary lines and traces of APS5_TRACE_FRAMES
captures, the trace digest and optionally the worker's collapsed stacks,
and states the capture protocol: warm caches, the same scene and settings,
no other profiling switch, the same machine and session. The pull request
template and CONTRIBUTING ask a pull request that claims a change in speed
for the summary lines of main and of the change, captured the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 18:23:52 +02:00
PrintOrb 9c007805d6
Merge branch 'main' into fix/videoout-high-frame-rate 2026-10-12 11:15:48 -05:00
MiniRML a6f42f5119 fix(relinker): zero the unmapped section-name table address 2026-10-12 09:10:47 -07:00
Simon 50deecc122 fix(agc): count every CP data write of a dispatch group as a write of the run
The compute dispatch look-ahead noted WRITE_DATA and RELEASE_MEM stores only
as labels, and labels other than 4 or 8 bytes were dropped. A WRITE_DATA that
fills a descriptor or an argument block between two dispatches was invisible,
so the later dispatch was captured ahead with the old bytes. Every such store
now also enters the run's written ranges, which stop the look-ahead before a
dispatch that reads them.
2026-10-12 18:09:14 +02:00
MiniRML 7dfd790d08 fix(release): find the configured Windows runtime 2026-10-12 09:05:52 -07:00
Yunus Shaikh aa4a9b4faf fix(libc): return the default quiet NaN from the strtod family
C17 7.22.1.3 and 7.29.4.1.1 interpret NAN(n-char-sequence) as a quiet
NaN. With MinGW-w64, std::strtod, strtof and strtold are mingw-w64's
gdtoa functions, which copy the hex digits into the significand
without the quiet bit, and for long double without the x87 integer
bit: strtod("nan(0x123)") returned the signaling NaN
0x7ff0000000000123 and strtold a pseudo-NaN. A payload whose only set
bit is the implicit or x87 integer bit gave infinity:
strtof("nan(0x800000)"), strtod("nan(0x10000000000000)") and
strtold("nan(0x8000000000000000)") returned +inf. glibc returns quiet
NaNs that keep the payload.

C17 7.12.11.2p2 makes nan("n-char-sequence") equivalent to
strtod("NAN(n-char-sequence)", NULL), and nan and nanf already return
the console's fixed quiet NaNs, ignoring the tag. ParseFloat, which
strtod, strtof, strtold, atof and the wide variants share, now returns
the same values, 0x7ff8000000000000 and 0x7fc00000, negated after a
minus sign, on Windows and Linux. strtold returns the x87 default
quiet NaN, since no console nanl was observed.
2026-10-12 09:04:31 -07:00
Claude 7fd423dfe9
fix(shader): never branch conditionally to one label twice
SPIR-V 1.6 requires the true and false labels of OpBranchConditional to
differ. Two places produced the same label for both:

- The structured terminator emitter, for a conditional branch whose
  targets are the same block, such as s_cbranch_scc0 to the next
  instruction. It now emits OpBranch, keeping OpLoopMerge on a loop
  header.
- Pipeline specialization, when it folded a branch on a constant and
  kept the selection merge because the taken path is not linear. It
  wrote the taken label twice; the untaken side now names the
  selection's merge block, which the constant condition never takes.

Found by recompiling mutated compute shaders with SPIR-V validation on;
before this, 169 of 20000 were rejected for this rule, and builds
without SPIRV-Tools handed the module to the driver.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-12 16:04:13 +00:00
Yunus Shaikh 4f78bd534f fix(libSceNet): report copied socket address lengths
sceNetAccept, sceNetGetsockname, sceNetGetpeername, sceNetRecvfrom and
sceNetRecvmsg stored the full address length (16 or 28) even when the
caller's buffer was shorter and only part of the address was copied.
FreeBSD 11.4 stores the copied length, the smaller of the two
(kern_accept4, kern_recvit, kern_getsockname and kern_getpeername in
sys/kern/uipc_syscalls.c), as libkernel's socket calls do since #4295.
2026-10-12 09:00:04 -07:00
Yunus Shaikh feffc67acb fix(libSceNet): name an unbound socket with the zero address on Windows
sceNetGetsockname on a socket that is neither bound nor connected failed
with EINVAL on Windows, where Winsock reports WSAEINVAL. FreeBSD 11.4
returns the wildcard address with port 0 (in_getsockaddr and
in6_getsockaddr in sys/netinet/in_pcb.c and sys/netinet6/in6_pcb.c), and
Linux already does, so the call now builds that address for the socket's
family.
2026-10-12 09:00:04 -07:00
Yunus Shaikh 53cbdebe82 fix(libSceNet): report the registered socket as the epoll event ident
sceNetEpollWait copied the whole event a title passed to
sceNetEpollControl, so the ident and reserved fields came back as the
title had left them. The ident of a returned event is the id that was
registered and reserved is 0 (shadPS4 net.cpp sceNetEpollWaitImpl,
KytyPS5 network.cpp EpollWait); a title that registers a socket without
filling ident and looks the socket up by it now finds it.
2026-10-12 09:00:03 -07:00
Yunus Shaikh b19a74f5bf fix(libSceNet): sceNetEpollWait takes 2 as the OUT event
sceNetEpollWait used Linux's EPOLLOUT bit (4) for writability. The SCE
value is 2 (shadPS4 net_types.h ORBIS_NET_EPOLLOUT, KytyPS5 network.cpp
EPOLL_OUT, SharpProspero Socket.cs EpollOut); IN (1), ERR (8) and HUP
(0x10) already match. A socket registered for OUT was never polled for
writability, so the wait ran out its timeout or blocked forever, and an
IN|OUT registration never reported OUT.
2026-10-12 09:00:02 -07:00
Yunus Shaikh febc36223b fix(libSceNet): take 0x1105 and 0x1106 as the send and receive timeouts
sceNetSetsockopt and sceNetGetsockopt answered EOPNOTSUPP for 0x1105 and
0x1106, the SCE send and receive timeouts in microseconds (shadPS4
net_types.h ORBIS_NET_SO_SNDTIMEO/RCVTIMEO, SharpProspero Socket.cs
SoSndTimeo/SoRcvTimeo, KytyPS5 network.cpp). A title that set a receive
timeout this way then blocked in sceNetRecv for good. Both options now
set and read the timeouts that 0x1005 and 0x1006 already do.
2026-10-12 09:00:02 -07:00
Dean 2a5eca11cd
Merge pull request #4350 from milescoviello/fix/once-reset-on-exit
fix(libkernel): reset a once control when its initializer exits or is cancelled
2026-10-13 04:53:46 +13:00
Dean a7614930a4
Merge pull request #4349 from milescoviello/fix/open-nofollow
fix(libkernel): fail open with O_NOFOLLOW on a symbolic link
2026-10-13 04:53:25 +13:00
Dean 3881117feb
Merge pull request #4348 from milescoviello/fix/rmdir-symlink
fix(libkernel): fail rmdir on a symbolic link instead of removing it
2026-10-13 04:53:02 +13:00
Dean d3187dff7f
Merge pull request #4346 from SandstromPL/fix/relinker-registry-after-patch
fix(relinker): write registries after the executable
2026-10-13 04:52:40 +13:00
Dean 57f5ec232c
Merge pull request #4338 from ArjenS-LA/fix/diagnostic-input-preservation
fix(relinker): preserve inputs when writing Windows diagnostics
2026-10-13 04:52:18 +13:00
Mohamed Aziz Hadjkacem 4d37c93364 fix(agc,shader): prime the two host shared mutexes added after 48dcaace7
Commit 48dcaace7 introduced PrimedSharedMutex and converted every host std::shared_mutex that existed at the time, because winpthreads initializes a static rwlock on first use and a concurrent first lock returns EINVAL, which std::shared_mutex ignores (docs/dev/TechnicalDebt.md:10).

Two host mutexes were added afterward and missed the conversion:
- Recompiler.cpp registrationModule() static mutex (added by baf3437465)
- MultisampleTarget.cpp guardedMutex() static mutex (added by d109f3966f)

Apply the same priming. Recompiler.cpp reuses the PrimedSharedMutex already defined in that translation unit; MultisampleTarget.cpp gets the identical local helper to match the existing inline precedent.

Tested: Windows 11 x64, WinLibs GCC 15.2.0 (matches CI). Compiled Recompiler.cpp.obj and all three MultisampleTarget.cpp.obj targets with the project build flags; check_conventions.py passes.
2026-10-12 16:52:00 +01:00
Dean 0a6bb6ac51
Merge pull request #4337 from ArjenS-LA/fix/windows-origin-slash
fix(relinker): accept trailing separators on the Windows origin path
2026-10-13 04:51:57 +13:00
Dean 05f6460165
Merge pull request #4336 from ArjenS-LA/fix/guest-runpath-list
fix(relinker): rebase each origin in guest runpath lists
2026-10-13 04:51:37 +13:00
Dean 63d4a10999
Merge pull request #4335 from ArjenS-LA/fix/test-registration-reachability
fix(tools): ignore unreachable CTest registration files
2026-10-13 04:51:15 +13:00
Dean b8fff28bd0
Merge pull request #4330 from treuenten/claude/peaceful-darwin-etpdf0
fix(tools): follow Depends on chains in pr_overlap
2026-10-13 04:50:54 +13:00
Dean 97445386fe
Merge pull request #4328 from pipe1os/fix/agc-packed-mip-tail-fallback
fix(agc): render into non-resident packed mip tails
2026-10-13 04:50:40 +13:00
Dean 82b1b2e7a4
Merge pull request #4445 from LeandroLP23/fix/agc-library-modules-after-prebuild
fix(agc): keep shader modules of pipeline library parts alive for background linking
2026-10-13 04:50:18 +13:00
MiniRML bf6a054c52 fix(nid): preserve Unicode paths on Windows 2026-10-12 08:43:02 -07:00
MiniRML b4d7a3b6c9 fix(kernel): parse JSON numbers independently of host locale 2026-10-12 08:34:20 -07:00
Xabilimon1 aa88949e2e fix(libkernel): build the fcntl status flags and the getrlimit test on macOS
InitialEntry reads O_DIRECT from the host status flags, and macOS has
no O_DIRECT, so libkernel did not compile there. It is now read only
where the host defines it. GuestGetrlimit iterates over braced lists
without including <initializer_list>, which libc++ requires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:33:37 +02:00
Nixitov f1810c89ea feat(libc): implement nearbyintl
OutRun2006 (PPSA99106) imports nearbyintl and stops at start-up on main. Add the long double round-to-nearest-integer export next to nearbyintf; std::nearbyint rounds under the current direction and raises no inexact exception.
2026-10-12 17:29:41 +02:00
MiniRML ca67860168 fix(nid): validate buffer ranges and string terminators 2026-10-12 08:28:52 -07:00
poonasor 08824f3b90 feat(libkernel): implement sceKernelGetModuleList2 and sceKernelGetModuleInfo2
The title-module variants of sceKernelGetModuleList and
sceKernelGetModuleInfo, following shadPS4: the list leaves out system
libraries and the info call answers EPERM for one. A system library is
a loaded .prx image that is not a relinked title module.

NHL 27 (PPSA34063) imports both and fails to load without them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:15:26 -04:00
AlxcBTW 4309bffdb0 fix(agc): take the dword pattern-fill kernel only through a 32-bit output format
The kernel #2081 matches stores each pattern dword with
buffer_store_format_x, so what lands in memory depends on the output
descriptor's FORMAT (bits 12-18 of user[3]). Only 32_UINT, 32_SINT and
32_FLOAT (20-22) store the dword unchanged. Other formats convert it,
and an 8- or 16-bit one writes 1 or 2 bytes of every 4-byte element, so
a buffer fill would also overwrite the bytes in between. Such a dispatch
now goes to the recompiler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:09:31 +02:00
thuanquang 4feeef6981 test(tools): create hard links with os.link for Python 3.9
Path.hardlink_to was added in Python 3.10, so import_audit and stubgen
errored on Python 3.9.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:05:04 +07:00
AlxcBTW 676622fd78 fix(shader): count only id operands as uses when dropping specialization debris
referencedIds() took every operand word of every instruction as a
possible id, so a literal that happened to equal the id of a dead folded
constant or an unreachable block kept it: the OpTypeInt width 32 kept a
dead constant %32, LocalSize literals kept dead labels.

Visit only the <id> operands of each instruction. Literal operands are
skipped for the instructions that carry them: type widths, counts and
image parameters, constant values, storage classes, memory access and
image operand masks (their trailing ids still count), composite and
shuffle indices, OpArrayLength members, extended instruction numbers,
entry point names, execution modes, merge and function controls, branch
weights, OpSwitch case values (two words for a 64-bit selector) and
group operations. An instruction without a listed layout still counts
every word, so an unknown instruction can only keep debris, never drop
something in use.

The SpirvModule case now also folds a switch whose dead constant has id
32 and whose dead blocks' ids appear as LocalSize literals; it fails
without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:03:08 +02:00
Claude ef09cda468
fix(libc): sort with FreeBSD 11's qsort so equal elements keep its order
qsort was a heapsort. It sorted correctly, but elements that compare
equal ended in a different order from FreeBSD's Bentley-McIlroy qsort,
in 18947 of 20000 random arrays with repeated keys, and below 7
elements, where FreeBSD's insertion sort keeps equal elements in
place, a heapsort does not. qsort now follows FreeBSD 11 qsort.c,
including its comparator call sequence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-12 15:01:35 +00:00
AlxcBTW 63ddeb09f4 test(agc): keep the retire test's mock device out of the Linux import probe
On Linux with write watching (uffd write-protect), the first import asks the device whether a GPU
write clears the protection (ProbeImportWriteProtection): it records a vkCmdCopyBuffer and a
vkCmdFillBuffer on the import and submits them. The test's mock device records no commands, so
agc_host_import_retire failed with "missing Vulkan function: vkCmdCopyBuffer" before it reached
the retire (RADV, reported on #4515). The test now sets APS5_WRITE_WATCH_IMPORTS=unwatch, so its
imports are compared as on Windows and the probe, which a mock cannot answer, does not run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:00:45 +02:00
saxopa d0502b449d fix(relinker): load a PT_SCE_RELRO segment that no PT_LOAD covers
PS4 executables keep their relocated read-only data, GOT and init/fini
arrays in a PT_SCE_RELRO segment outside every PT_LOAD. The segment filter
dropped it, so Windows and macOS output stopped at the first relocation
into it, and Linux output placed the relinker's extra block over its
addresses without an error. The ELF reader now reports such a segment as a
read-write PT_LOAD. A PT_SCE_RELRO inside a PT_LOAD keeps being skipped,
and one that partially overlaps a PT_LOAD throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:54:13 +02:00
Ori Shamir fc0d46ba00 fix(libkernel): throw instead of aborting when there is no host standard error to keep
A Windows GUI build started without standard handles has no descriptor 2.
_dup(2) then reached the invalid-parameter handler, which aborts, before the
"cannot keep the host standard error" error could be thrown. It now goes
through WithoutParameterHandler like the other descriptor calls in this file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:53:41 +03:00
Scott Schuster 2585eeb7ee fix(libkernel): keep sceKernelDebugOutText off a redirected guest stderr
Guest descriptors are host descriptors, so a guest that moves standard
error with dup2 also moves the stream sceKernelDebugOutText writes to. A
title that forwards its standard error to the kernel log through a pipe
then reads its own debug output back and logs it again without end. Before
the guest's first dup2 onto descriptor 2, the host's standard error is kept,
and sceKernelDebugOutText writes there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 17:53:41 +03:00
Dean d592d54e11
Merge pull request #2802 from ThatsLeo/perf/agc-untracked-compare-memo
perf(agc): memoize untracked texture comparisons by the stores that reach the surface
2026-10-13 03:42:27 +13:00
Dean 8751a29407
Merge pull request #4150 from eljackjoestar-cmd/codex/ancillary-relative-ssa
fix(shader): preserve ancillary proofs across relative VGPR accesses
2026-10-13 03:42:06 +13:00
Dean f95619b8d2
Merge pull request #4108 from GustavoGamarra95/feat/agc-host-memory-fallback
feat(agc): place textures and storage images in host memory when device memory runs out
2026-10-13 03:41:44 +13:00
Dean acb13d1b39
Merge pull request #4030 from AlxcBTW/fix/recompiler-flat-p10-provoking-v2-2
fix(recompiler): read P10 of a flat input as the provoking vertex
2026-10-13 03:41:23 +13:00
krishnaraya bb028203db test(agc): skip the f32 dot2 rounding test on devices without shaderFloat64
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 10:32:54 -04:00
Nahuel Genchi 22d1fbbc73 fix(libc): throw bad_alloc from operator new when the default heap rejects a size 2026-10-12 11:32:47 -03:00
Simone bdf003a5e8 test(relinker): register weak_imports next to elf_header
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:31:10 +02:00
Kikoarl 94f06cee6f feat(libSceSysmodule): register libSceAcm (0x10B) and libScePsml (0x137) module IDs 2026-10-12 16:22:34 +02:00
Dean 8fa5cc4866
Merge pull request #3995 from delarun/fix/ngs2-dead-cells
feat(libSceNgs2): implement the Geom reset functions
2026-10-13 03:08:54 +13:00
Dean 052134100e
Merge pull request #3869 from gookie-dev/feat/http2-delete-cookie-box
feat(libSceHttp2): implement sceHttp2DeleteCookieBox
2026-10-13 03:08:32 +13:00
Dean fe66af13ef
Merge pull request #4038 from santhoshkumar-dev/fix/libkernel-symlink-file-size
fix(libkernel): report the target size of symbolic links on Windows
2026-10-13 03:08:09 +13:00
Dean 858f75dbee
Merge pull request #3725 from Recapi/carry/screamm-sinhf-asinl
feat(libc): implement sinhf and asinl (carried from #3519)
2026-10-13 03:07:47 +13:00
Dean cee0290c2c
Merge pull request #4331 from oneandonlydean/up4/fix-3504-shader-prep-time
fix(shader): prove masked lanes unread for every select in one backward pass
2026-10-13 03:07:16 +13:00
AlxcBTW 803a131275 fix(shader): store unaligned 32-bit BDA writes byte by byte
EmitBdaWrite, used for buffer stores through GPU-selected descriptors, recorded an
Unaligned fault and dropped every 32-bit store whose address is not 4-byte aligned.
EmitBdaStore and the BDA reads already take the byte path for unaligned addresses, and
raw buffer accesses use the exact bytes, as RDNA does in unaligned mode. The unaligned
case now stores the four bytes one by one like the 8- and 16-bit stores of the same
function.

Sword of the Sea hit this about once per frame from one compute store, so four bytes
were lost and a fault was reported each time.

The BDA store execution table now also runs through EmitBdaWrite; its unaligned dword
cases failed before this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:06:16 +02:00
Ondra97 bd842bc7d2 fix(libs): share Windows flock state between duplicated descriptors 2026-10-12 16:03:47 +02:00
Simone cb1c18d552 fix(relinker): keep weak undefined imports weak
Weak undefined imports in the input ELF became STB_GLOBAL in the relinked
output, so a title that weak-imports a function no module exports stopped
at startup instead of binding the import to null.

Keep STB_WEAK on the relinked dynamic symbols. On Windows, write 0 plus
the addend to the slot of a weak import that no searched module exports.

Fixes #4497

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:48:50 +02:00
Reagan Little 74392900d5 fix(libc): unwind guest exceptions through Windows DLL frames
The Windows unwinder stopped at the first frame of a module without the
project's .ehmeta or .ehfram tables, so an exception could not cross
libstdc++-6.dll and libwinpthread-1.dll (std::call_once through
pthread_once) or system DLLs. For such a frame it now looks up the
function's PE unwind data with RtlLookupFunctionEntry and takes the
caller's registers, including the nonvolatile rsi, rdi and xmm6-xmm15,
from RtlVirtualUnwind. A frame whose unwind data has an exception or
termination handler still stops the walk, since those handlers are not
run.

windows_exceptions gets back the std::call_once case of #1423.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:42:51 -04:00
fma-rc 8dcb4e5c6b
docs: mark the run fields of the bring-up form as a first report 2026-10-12 15:41:21 +02:00
Yunus Shaikh 12ef40e4f1 fix(savedata): let a trailing % in a directory name pattern match nothing
sceSaveDataDirNameSearch reads % in the directory name pattern as any
run of characters and _ as one character. The matcher stopped when the
name ran out and then required the pattern to have ended too, so a
trailing % with nothing left to match failed: SAVEDATA% did not find a
save named SAVEDATA, and TRAI_% did not find TRAIL. A % now matches no
characters at the end of a name as it already did in the middle.
2026-10-12 06:37:37 -07:00
Simon 0cd3f63803
Merge branch 'boykopovar:main' into fix/pad-input-persistent-sequence 2026-10-12 15:37:34 +02:00
Dean c6efe068a1
Merge pull request #4253 from codingreapermaps/fix/libkernel-sys-windows-readlink
fix(libkernel_sys): read relative symbolic links on Windows
2026-10-13 02:35:50 +13:00
Dean f1514adea3
Merge pull request #4060 from coltken95/claude/sleepy-dirac-e44kw4
fix(relinker): decode 3DNow! instructions with their ModRM and opcode…
2026-10-13 02:35:21 +13:00
Dean 3b83f71e0f
Merge pull request #3905 from aureau/aureau/carry-libc-strtod-no-conversion-zero
fix(libc): return positive zero when float parsing consumes no input
2026-10-13 02:34:59 +13:00
Dean b39b78a5ad
Merge pull request #3809 from aayushmishraaa/fix/relinker-reject-non-x86-64
fix(relinker): reject input executables that are not x86-64
2026-10-13 02:34:37 +13:00
Dean ddc713fbcf
Merge pull request #4004 from zmeizy/claude/mutex-timedlock-try-first
fix(libkernel): try the lock before checking the pthread_mutex_timedl…
2026-10-13 02:34:16 +13:00
Dean f4b395e28e
Merge pull request #3971 from arreina/fix/param-json-nesting
fix(libkernel): limit param.json nesting depth
2026-10-13 02:33:55 +13:00
Dean 1fb5635077
Merge pull request #3967 from MarcZX100/codex/carry-sjlj-mingw-guard
fix(build): reject MinGW compilers that select SjLj exceptions
2026-10-13 02:33:33 +13:00
Dean fefecc61c2
Merge pull request #3954 from MarcZX100/codex/carry-clock-error
fix(libkernel): return SCE errors when clock calls fail
2026-10-13 02:33:12 +13:00
Dean 9a01496582
Merge pull request #3939 from zaydzaari/research/rtc-named-zones-1342
fix(rtc): recognize additional measured named time zones
2026-10-13 02:32:51 +13:00
Dean 57dc136026
Merge pull request #3936 from MarcZX100/codex/carry-fopen-s-output-null
fix(libc): clear fopen_s output on invalid arguments
2026-10-13 02:32:30 +13:00
Dean 993f80a081
Merge pull request #3912 from GalaxyGamingFR/fix/pr-overlap-wrapped-exports
fix(tools): ignore wrapped declarations in pr_overlap exports
2026-10-13 02:31:59 +13:00
Dean 11c9522cf9
Merge pull request #3893 from MarcZX100/codex/carry-credential-setters-3862
feat(libkernel): export credential setters and supplementary groups
2026-10-13 02:31:36 +13:00
Dean a1a9c3db59
Merge pull request #3860 from excited-raccoon/perf/relinker-linear-control-flow
perf(relinker): walk fallthrough instructions without queueing
2026-10-13 02:31:15 +13:00
SibteProf c0967216bf fix(relinker): recognize SDK memory syscall trap patterns 2026-10-12 18:25:06 +05:00
Karan Vora 69d0dc2288 perf(shader): treat f32 min, max, med3, floor and ceil results as denormal-free
In the float modes that flush f32 denormals, writeOperand flushes the result
of v_min/max/min3/max3/med3_f32 and v_floor/ceil_f32. DenormalFlushEliminator
drops a flush only when it can show the value isn't a denormal, and it had
no rule for these opcodes, so their result flush stayed even when every
operand had already been flushed.

FPMin32, FPMax32, FPMinTri32, FPMaxTri32 and FPMedTri32 return one of their
operands' bits, or the and or or of two zeros, so the result is denormal-free
when every operand is. FPFloor32 and FPCeil32 return an integer, a zero, an
infinity or a NaN whatever their operand. In IEEE mode the min and max
results and the floor and ceil results go through selects with a quieted
operand, which the eliminator can't follow, so their flush stays there.

agc_denormal_flush_elimination covers the five min and max forms with
flushed and with unknown operands, floor and ceil of an unknown value, and a
max carried around a loop.
2026-10-12 09:24:29 -04:00
Simon 70481b0703 fix(pad-input): preserve command ordering across clock resets 2026-10-12 15:23:22 +02:00
fma-rc b6f81995fc
docs: add a discussion form for game bring-up threads 2026-10-12 15:23:16 +02:00
Evgeniy Kazarin 33c955a8e3 perf(relinker): index guest exports for dependency resolution 2026-10-12 16:15:06 +03:00
Abdullah 7e2832475f fix(libc): return stream errors from fwrite, fread and fputs instead of throwing 2026-10-12 16:14:25 +03:00
Alex Collini ff3eae6c9a feat(agc): sample non-sRGB textures through FORCE_DEGAMMA samplers as sRGB views
A sampler with FORCE_DEGAMMA decodes the texels of a non-sRGB 8-bit texture
as sRGB. Such a binding was refused; it now samples an sRGB view of the same
image. Snapshot images whose format has an sRGB twin (R8, R8G8, RGBA8, BGRA8,
ABGR8 packed, BC1/2/3/7 UNORM) are created with MUTABLE_FORMAT, and storage
images are already mutable. The view is made on first use for the full,
first-layer or base-level range, with sampled usage only and the descriptor's
minimum LOD. A format without an sRGB twin is still refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:11:34 +02:00
winstrex a47178b86f fix(libkernel): open directories whose host path ends in a separator on Windows
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:06:44 -03:00
fablevoid 1541244b78 fix(relinker): treat jump table targets as branch targets when moving instructions
The AMD-only converter moves the instructions next to a short AMD-only
instruction into its stub, and refuses to move one that a branch
enters. It knew only direct jump and call targets, so a switch case
entered through a jump table could land inside the jump written over
the moved instructions.

The converter now also reads relative jump tables: a RIP-relative LEA
whose address lies in a loaded segment starts a table of 32-bit
offsets from that address, read while each target starts an
instruction of the segment being converted. To read them, Convert
takes the program headers and converts their executable load
segments; the relinker passes all headers, and
ElfReader::ReadCodeSegments, now unused, is removed.

The converter test builds a dispatch through such a table whose second
case enters the instruction after a 4-byte 256-bit store, and checks
that the store takes the instruction before it instead. It also checks
that reading stops at an entry that does not start an instruction,
that only a LEA forms a table address, that tables are read only
inside loaded segments, that only executable load segments are
converted, and that a segment past the end of the file fails the
conversion.

PPSA12544: one 256-bit store in Il2cppUserAssemblies moved the
instruction after it, which is case 1 of a switch; it now goes through
an island. The other relinked files are unchanged.

AI-assisted: yes
2026-10-12 13:06:23 +00:00
Dean 33ae282e4e
Merge pull request #4325 from gravelas/feat/shader-replay-prepare
feat(agc-driver): replay a request as a prepared artifact with --prepare
2026-10-13 01:57:48 +13:00
Dean 9bc4356990
Merge pull request #4295 from AaravMehta-07/codex/kernel-socket-address-length
fix(libkernel): report copied socket address lengths
2026-10-13 01:57:25 +13:00
Dean 58ee7af7ae
Merge pull request #4231 from MarcZX100/codex/carry-gai-strerror-4055
fix(libScePosixForWebKit): match FreeBSD gai_strerror text
2026-10-13 01:57:10 +13:00
jweidenh b00bd9fa11 test(agc): skip import retirement without compatible host imports 2026-10-12 14:56:47 +02:00
Dean f4b47ee51f
Merge pull request #4014 from ahir061/fix/macos-executable-directory
fix(libs): find the executable directory on macOS
2026-10-13 01:56:47 +13:00
jweidenh 408fa9cbf5 fix(agc): defer host import retirement until the GPU lock is held 2026-10-12 14:56:47 +02:00
jweidenh c4faf55fc4 test(agc): cover unlocked host import retirement during mapping 2026-10-12 14:56:47 +02:00
Dean 4bf963d660
Merge pull request #3966 from MarcZX100/codex/carry-strcasecmp-lowered
fix(libc): compare strcasecmp results after lowercasing bytes
2026-10-13 01:56:24 +13:00
arcwiser 441d392c35 fix(tools): batch pr_overlap fetch refspecs 2026-10-12 23:53:47 +11:00
overlordxrz-source 0bcff53b2c fix(recompiler): run vertex programs as one-lane waves without vertex subgroups
lavapipe and MoltenVK leave the vertex stage out of subgroupSupportedStages,
so the driver's shader validation refuses every vertex program that declares
GroupNonUniform ("subgroup capability 61 is unsupported for shader stage 1").
Most do, since the entry EXEC is a ballot.

The device target now records whether the vertex stage has subgroups. When
it does not, vertex and local programs get a host subgroup width of 1: s3
reports one vertex and one primitive, the lane id is 0, a ballot is the
invocation's own bit, a shuffle returns the invocation's value, an elect is
true and a half-wave reduction read is the key itself. Reads of other lanes
keep the existing narrow-host rules and throw where they did. Devices with
vertex subgroups emit the same SPIR-V as before.
2026-10-12 08:50:05 -04:00
Gustavo Ariel Gamarra Rojas d56b22541a perf(agc): wait once per unit run when a storage image captures its guest bytes 2026-10-12 09:36:54 -03:00
Gustavo Ariel Gamarra Rojas f16a860227 feat(agc): read committed guest memory without the flush hook 2026-10-12 09:36:54 -03:00
ThatsLeo ca99658cef perf(agc): memoize untracked texture comparisons by the stores that reach the surface
The memo stamp held TrackerGeneration, which every collect and driver store
advances, so it almost never matched. Driver stores into uncovered ranges
record their generation per 64 KiB block, and NewestStamp of the surface
replaces TrackerGeneration in the stamp.
2026-10-12 14:34:14 +02:00
marcio-ch 3bc3e35a02 fix(agc): handle the fill kernel that reads its pattern from a buffer
The 16-byte fill kernel has a variant that loads its pattern with
s_buffer_load_dwordx4 through the V# in s[4:7] instead of taking it from
the user SGPRs. Vampire Crawlers (PPSA30703) clears its UI canvas HTILE
with it every frame; run as a shader, the depth surface never cleared and
every UI draw failed its GEQUAL depth test, leaving the screen black.
2026-10-12 09:32:17 -03:00
Hannah Lyons 4e3aa159d9 feat(libkernel): export sceKernelGetMainSocId, sceKernelGetCpumode and sceKernelGetCpuFrequency 2026-10-12 08:32:16 -04:00
Dean 0ef9dc9282
Merge pull request #4277 from BharatYadav007/bharat/libc-mktime-negative-one
fix(libc): normalize valid mktime minus-one results
2026-10-13 01:30:26 +13:00
Dean d58ba1010e
Merge pull request #4232 from MarcZX100/codex/carry-fdopen-modes-4022
fix(libc): accept FreeBSD fdopen modes
2026-10-13 01:30:04 +13:00
Dean 71c3dbb722
Merge pull request #4190 from ArchitDewan/fix/relinker-empty-path
fix(relinker): reject empty positional paths
2026-10-13 01:29:30 +13:00
Dean e50326224c
Merge pull request #4103 from yunusshaikh7/fix/libc-difftime-negative-times-windows
fix(libc): subtract negative times in difftime on Windows
2026-10-13 01:29:12 +13:00
Dean 90c7ab3390
Merge pull request #4102 from itsbo5/fix/libkernel-udp-send-edestaddrreq
fix(libkernel): return EDESTADDRREQ for send and sendto on unconnecte…
2026-10-13 01:28:39 +13:00
Dean 256cc9f976
Merge pull request #4099 from yunusshaikh7/fix/libc-asctime-years-before-1900-windows
fix(libc): format years before 1900 in asctime on Windows
2026-10-13 01:28:18 +13:00
Dean 104bb65c94
Merge pull request #4093 from kallmek3nny/fix/aio-precise-poll-sleep
fix(aio): use precise polling sleep
2026-10-13 01:27:47 +13:00
Dean f8dee05957
Merge pull request #4091 from jweidenh/codex/guest-noop-relocations
fix(relinker): ignore no-op relocations in guest modules
2026-10-13 01:27:26 +13:00
Dean 253ec3ffee
Merge pull request #4084 from hawwwwwk/pr/relinker-suprx-guest-alias-current
fix(relinker): resolve .suprx guest module aliases
2026-10-13 01:27:08 +13:00
Dean c5675762a2
Merge pull request #4069 from Kikoarl/feat/sysmodule-voicechat-module-id
feat(libSceSysmodule): register libSceVoiceChat module ID (0x114)
2026-10-13 01:26:25 +13:00
screamm 23ac439214 feat(libc): implement __umodti3
Kart PS5 (PPSA99611, Phi1ow/mkwii-ps5 v1.0.0-rc1) stops on it right
after __divti3. The related open PR #4037 reports: "with this change it
passes `__divti3` and stops at `ELSr5qm4K1M` (`__umodti3`)".
PS5CEMU-HAR (PPSA99360) imports it too: tools/import_audit.py lists
ELSr5qm4K1M __umodti3 as absent from libSceLibcInternal.prx (registry
index 359), so the predicted stop is
"FAIL: unresolved ELF import ELSr5qm4K1M from libSceLibcInternal.prx".
libSceLibcInternal imports fall back to libc.prx, so the export lives in
libc beside __udivti3. compute_nid and aerolib (zecoxao/sce_symbols
2883963a) both give ELSr5qm4K1M = __umodti3.

__umodti3 is compiler-rt's lib/builtins/umodti3.c (llvm-project
0ee439b705e82a4fe20e266bc8fea96d0e60e1ec): the remainder that
__udivmodti4 leaves. The restoring division of SonyUdivti3 already
computes it, so the loop moves to SonyUdivmod128 and both helpers use
it. A zero divisor reaches a 64-bit divide in __udivmodti4 and traps on
x86-64; this throws instead, as __udivti3 does, and names the dividend.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 12:26:15 +00:00
Dean a46ffb5e75
Merge pull request #4066 from hawwwwwk/pr/relinker-ff-callsite-current
fix(relinker): filter non-reference RIP-relative FF operations
2026-10-13 01:25:57 +13:00
Dean fb23e85f66
Merge pull request #4063 from MarcZX100/codex/carry-pthread-join-self-4052
fix(libkernel): return EDEADLK for self-join and EINVAL for null threads
2026-10-13 01:25:36 +13:00
Ori Shamir 1205d03daa fix(relinker): resolve relocations against null symbol entries to 0
A relocation whose dynamic symbol entry is all zero, like the null symbol,
now takes the value 0 plus its addend, as the gABI defines for STN_UNDEF.
The Linux output imports such an entry as an unnamed weak symbol, which the
host loader resolves to 0. The Windows and macOS outputs write the value
into the slot instead of failing with "Empty import symbol name". Such
references are left out of the call registry. An unnamed symbol entry that
is not all zero still fails the relink.

OliSe Player (PPSA01153) has seven of these entries and calls through the
slots only when they are not 0 (#4437).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:24:56 +03:00
Dean 029c1bc17d
Merge pull request #3926 from MarcZX100/codex/carry-fclose-error
fix(libc): return fclose failures to callers
2026-10-13 01:24:43 +13:00
Dean 906ce3f20c
Merge pull request #3599 from CouchRiv/fix-libc-exp2-errno
fix(libc): exp2 leaves errno unchanged
2026-10-13 01:24:15 +13:00
Dean 6ee0720ded
Merge pull request #4311 from leorossi2005/feat/libkernel-cpu-temperature
feat(libkernel): implement sceKernelGetCpuTemperature
2026-10-13 01:24:01 +13:00
Dean df055db921
Merge pull request #4256 from MarcZX100/codex/fix-avplayer-test-unwind-2709
test(avplayer): link guest_avplayer with the shared runtime libraries
2026-10-13 01:23:34 +13:00
Dean 9b20dd58c0
Merge pull request #4251 from Dextroid17/fix/libampr-usage-stats-stubs
fix(libSceAmpr): export sceAmprAmmSetPageTablePoolOccupancyNotificationThreshold
2026-10-13 01:23:06 +13:00
Dean c99cc11cf5
Merge pull request #4194 from fable-void/pr/rosetta-store-split
feat(relinker): split unaligned 256-bit stores for Rosetta
2026-10-13 01:22:45 +13:00
Dean a951cef8f4
Merge pull request #4272 from Ondra97/codex/socket-receive-host-write
fix(libs): allow socket receives into watched guest memory
2026-10-13 01:22:17 +13:00
Dean a88c4f272f
Merge pull request #4234 from solbjorg1245/upstream/savedata-search-info
fix(savedata): fill the search infos of sceSaveDataDirNameSearch
2026-10-13 01:21:48 +13:00
Dean 4d5fcef15b
Merge pull request #4203 from saxopa/fix/relinker-relative-section-symbol
fix(relinker): accept RELATIVE relocations against a zero-value section symbol
2026-10-13 01:21:34 +13:00
Dean 9a1922145d
Merge pull request #4080 from hugodearaujo/fix/rwlock-destroy-held
fix(libkernel): destroy rwlocks the way the console does
2026-10-13 01:21:08 +13:00
Dean ed14cb4e0a
Merge pull request #4053 from nikowolf123/fix/libkernel-close-guest-sockets
fix(libkernel): close guest sockets in sceKernelClose
2026-10-13 01:20:47 +13:00
Dean baee04e810
Merge pull request #4037 from Nixitov/feat/libc-divti3
feat(libc): implement __divti3
2026-10-13 01:20:26 +13:00
Gustavo Ariel Gamarra Rojas b35117c285 fix(agc): choose host memory from the heap budget instead of retrying
Read the device-local heap's VK_EXT_memory_budget figures before a texture
or storage image allocation and pick a host memory type when the
allocation does not fit, rather than allocating again after the driver
refused the first attempt. The allocation itself is made once and still
throws when it fails.
2026-10-12 09:13:34 -03:00
Gustavo Ariel Gamarra Rojas 85929389b5 test(agc): cover the host memory fallback of device allocations 2026-10-12 09:13:34 -03:00
Gustavo Ariel Gamarra Rojas eb03e0e154 feat(agc): place storage images in host memory when device memory runs out 2026-10-12 09:13:34 -03:00
Gustavo Ariel Gamarra Rojas 61180981ce feat(agc): place textures in host memory when device memory runs out 2026-10-12 09:13:34 -03:00
AlxcBTW eaf5ca240f fix(agc): read a parked import through its holder's map node
RetiredImport now owns the dropped import's map node (std::map::extract), so releaseParkedImports
takes the parked import's range from node.mapped().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 14:11:35 +02:00
AlxcBTW c54fb9d522 Merge remote-tracking branch 'origin/main' into fix/host-import-retire-hold 2026-10-12 14:08:34 +02:00
SibteProf 5ffafa920f fix(libc): reject oversized secure copy destinations 2026-10-12 17:06:54 +05:00
Dean ffa198407e
Merge pull request #3765 from pushkarreddyy/feat/libkernel-wait-rlimits
feat(libkernel): answer getrlimit for FreeBSD resources
2026-10-13 00:44:48 +13:00
Dean 2dfde65fa1
Merge pull request #3789 from xXPBSXx/fix/agc-workload-nop-packets
fix(libSceAgc): emit NOP packets for the DCB workload calls
2026-10-13 00:44:27 +13:00
Dean ac2ad045fc
Merge pull request #1357 from TibixDev/pr/shared-import-spans
fix(agc): retry failed host imports after range replacement
2026-10-13 00:43:28 +13:00
Dean 7300101fa2
Merge pull request #3700 from pushkarreddyy/feat/libkernel-sysctl-kern-arandom
feat(libkernel): support MIB 1.37 (kern.arandom) in sysctl
2026-10-13 00:43:14 +13:00
Del'aRun cac398cb96 feat(libSceNgs2): implement the Geom reset functions
Implement sceNgs2GeomResetListenerParam and sceNgs2GeomResetSourceParam,
taken from #1021 and limited to the two reset functions. The Geom
structures get their real layouts. sceNgs2GeomCalcListener and
sceNgs2GeomApply stay unimplemented.

Co-Authored-By: Dextroid17 <217507631+Dextroid17@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:39:27 +04:00
LeandroLP23 2919dd38cb fix(agc): keep shader modules of pipeline library parts alive for background linking
#4152 dropped the device-wide module list that #3173 added, so Pipeline
destroys the shader modules right after the library parts are created, also
for the parts prebuilt at registration. AMD's Windows driver still reads
them during the background link-time-optimized link and faults inside
amdvlk64.dll. Hand the modules of each new part to the device's library
store again and destroy them in ClearPipelineLibraries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:14:02 +02:00
Dean 3d8ffeb271
Merge pull request #4395 from oneandonlydean/up4/carry-1810
fix(agc): cache Linux describePages results
2026-10-12 23:54:52 +13:00
Dean 8eee5cfad5
Merge pull request #4118 from Mouad-Scriptz-0/feat/npauth-authorized-app-dialog
feat(libSceNpAuthAuthorizedAppDialog): add the dialog, with NpAuth and SaveData imports of Minecraft
2026-10-12 23:54:30 +13:00
Dean 0e61bedf9a
Merge pull request #4162 from Celegans12/fix/shader-wave64-lane-reductions
fix(shader): match half-wave reductions over relatively addressed vector registers
2026-10-12 23:54:08 +13:00
Dean 3356c28fd6
Merge pull request #4106 from fma-rc/feat/agc-present-mode
feat(agc): select the swapchain present mode with ANYPS5_PRESENT_MODE
2026-10-12 23:53:46 +13:00
Dean 5f3b94cb19
Merge pull request #3840 from ahir061/feat/libkernel-fcntl-files
feat(libkernel): support fcntl and ioctl on non-socket descriptors
2026-10-12 23:53:24 +13:00
Dean 5a031d49e8
Merge pull request #4324 from abhinav29102005/feat/libkernel-memory-pool-commit-type
fix(libkernel): propagate memory type in sceKernelMemoryPoolCommit and batch commit
2026-10-12 23:53:02 +13:00
Dean 090e26aac1
Merge pull request #4291 from diversao/fix/agc-release-parked-imports
fix(agc): release parked host imports a pinned guest range waits for
2026-10-12 23:52:40 +13:00
Dean dfb2033eb1
Merge pull request #3889 from devavijitsingh/fix/agc-write-tracker-lifetime
fix(agc): keep write tracker alive through driver teardown
2026-10-12 23:52:18 +13:00
Dean 9fca13b538
Merge pull request #4009 from dantdmdarealguy/feat/input-deadzone-sensitivity
feat(input): configure stick deadzones and mouse sensitivity
2026-10-12 23:51:54 +13:00
Dean b6aa03eae1
Merge pull request #3956 from GustavoGamarra95/feat/agc-storage-dump-selector
feat(agc): select storage dumps by shape and store them at each flip
2026-10-12 23:51:32 +13:00
Dean b40a0b17dc
Merge pull request #3946 from danzaio/fix/shader-failure-address
fix(agc): name the shader address and stage in preparation failures
2026-10-12 23:51:09 +13:00
Dean c390d81552
Merge pull request #4326 from winstrex/feat/libkernel-sceKernelEnableDmemAliasing
feat(libkernel): implement sceKernelEnableDmemAliasing
2026-10-12 23:50:39 +13:00
Dean 98341494df
Merge pull request #4308 from utkuhalis/pr/agc-acb-rendering-wait
feat(libSceAgc): implement sceAgcAcbWaitUntilSafeForRendering
2026-10-12 23:50:06 +13:00
Dean 7cb5238d53
Merge pull request #4281 from eljackjoestar-cmd/codex/reserve-direct-memory
perf(libkernel): commit Windows direct backing when mapped
2026-10-12 23:49:42 +13:00
Dean b117e31f39
Merge pull request #4186 from Scramblrrrr/codex/host-hole-query-cross-test
fix(memory): exclude arena host holes from every write-tracking query
2026-10-12 23:49:18 +13:00
Dean e9d35d6491
Merge pull request #3636 from JAB-dev/feat/agc-color-target-1-5-5-5
feat(agc): render 1_5_5_5 UNORM color targets
2026-10-12 23:48:42 +13:00
Dean 31839d413c
Merge pull request #4316 from gravelas/fix/agc-far-out-of-range-indices
fix(agc-driver): move indices far past every vertex fetch’s records to the first zero vertex
2026-10-12 23:48:23 +13:00
Dean c1d59a1125
Merge pull request #4245 from AchrafReyani/fix/agc-linear-2d-texture-default-pitch
fix(agc): accept a linear 2D texture whose DEPTH holds its default pitch
2026-10-12 23:48:00 +13:00
Dean 52973f89db
Merge pull request #4076 from Nesyth/fix/agc-cb-metadata-pass-without-depth-planes
fix(agc): accept a CB metadata pass beside depth state without depth or stencil planes
2026-10-12 23:47:38 +13:00
Dean f55e1117d4
Merge pull request #4058 from ee6aba54/fix/videoout-event-unopened-port
fix(libSceVideoOut): return INVALID_HANDLE for events on ports that are not open
2026-10-12 23:47:16 +13:00
Dean eee8541dae
Merge pull request #2904 from Scramblrrrr/codex/depth-storage-reuse
fix(graphics): allow storage textures in reused depth memory
2026-10-12 23:46:54 +13:00
Dean c3204078e4
Merge pull request #4270 from ErenYeager1836/fix/shm-unlink-windows-return
fix(libkernel): return after NotImplemented in Windows shm_unlink
2026-10-12 23:46:32 +13:00
AlxcBTW bb1325938e test(agc): compare the cost of more formatted loads with the first one, not with the module
agc_formatted_buffer_once required two more formatted loads from a known
descriptor to add less than a tenth of the module. The module was mostly
what specialization left of the folded format switch. Without those
leftovers it has 1,219 words and the two call sites add 225, so the
ratio failed while nothing was duplicated.

Require the two extra loads to add fewer words than the first one,
which also brings the shared function. A second copy of that function
per load still fails this check, as well as the function count checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:46:16 +02:00
lorkydey 86ce78c633 fix(tools): reject invalid NIDs in the name lookup CLI 2026-10-12 12:42:48 +02:00
lorkydey 4d29852a25 fix(ci): reject disabled CTest cases in skip validation 2026-10-12 12:39:52 +02:00
Ondra97 ec5688a666 fix(libkernel): bind deferred initialization to the loaded image 2026-10-12 12:34:35 +02:00
gookie ce0915e834 feat(libSceHttp2): implement sceHttp2DeleteCookieBox
PPSA35943 (Graveyard Keeper 2) imports sceHttp2DeleteCookieBox, which no
library exported. On current main the loader stops the title there:

  ./eboot.elf: symbol lookup error: ./eboot.elf: undefined symbol:
  O9ync3F-JVI

sceHttp2DeleteCookieBox(cookieBoxId) erases the box from the handle
registry sceHttp2CreateCookieBox adds it to and returns 0, or the
library's invalid-id error (0x817B1100) for an id that is not a live
cookie box. The signature follows SharpProspero and the behaviour
prosper's sceHttp2DeleteCookieBox; the error code is listed in
TechnicalDebt as not measured on a console.

guest_http2 deletes a box twice, a context id, a box whose context was
terminated, and the two boxes it uses before sceHttp2Term.

With this change the title loads and runs into its asset loading.
2026-10-12 12:24:57 +02:00
AlxcBTW 3ea89674d2 refactor(shader): drop unused folded constants and unreachable blocks after specialization
SpecializeSpirv hoists every instruction it folds into a module-level
constant and keeps that constant after its last use has folded away
too. It also leaves each block that a folded branch cuts off as
OpLabel; OpUnreachable. A formatted buffer helper's 77-case format
switch leaves about 1,800 such blocks.

Emit only the folded constants that something still refers to, and
remove OpLabel; OpUnreachable blocks that no instruction names. Merge
and continue targets of live constructs are named, so they stay.

The specialized modules lose words, not behaviour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:17:25 +02:00
eljackjoestar-cmd ae049e7757 fix(shader): restrict ancillary refolding to live builtin uses 2026-10-12 14:09:32 +04:00
john.maia 0b46084c9f fix(agc): build pipeline library shader parts for the draw's depth format
The pre-rasterization and fragment shader libraries were created with an
empty VkPipelineRenderingCreateInfo, so their depth and stencil formats
were VK_FORMAT_UNDEFINED even when the output library had a depth target.
On RADV (RX 7600, Mesa 25.2.8) a fast-linked pipeline built that way hangs
the GPU at its first draw: Epic Mickey loses the device as soon as the
level renders its translucency lighting volume, and with
APS5_NO_PIPELINE_LTO=1 it hangs within seconds of boot. The shader parts
now get the view mask and depth/stencil formats of the draw, and those
formats join their cache key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:09:00 +01:00
muhzain05 48919d01e4 fix(videodec2): answer picture info from the decoder that last wrote the buffer
sceVideodec2GetPictureInfo searched the live decoders in handle order and
returned the first one that had ever written the queried frame buffer. When
two decoders wrote the same address, the lower handle answered with stale
timestamps, a stale AVC decoder answered for an HEVC or VP9 picture, a stale
HEVC or VP9 decoder made an AVC query throw, and resetting or deleting the
decoder that wrote the buffer last exposed the older decoder's picture again.

Record the decoder that last published a picture into each frame buffer and
answer from that decoder only. Reset and Delete drop only the decoder's own
records, so another decoder's picture is never removed or brought back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:06:54 -06:00
Achraf Reyani 69728a0cb0 fix(agc): rasterize a single-sample scan into a multisampled target at the pixel center
With PA_SC_AA_CONFIG at one sample and MSAA_ENABLE off, the hardware scans
each pixel once and gives every sample of a multisampled color target its
result: radeonsi programs exactly this when GL multisampling is disabled on
a multisampled framebuffer (si_emit_msaa_config leaves sc_aa_config 0), and
GL defines it as single-sample rasterization with full coverage. Such a draw
without depth or alpha-to-coverage now rasterizes the target's sample count
with every sample location at the pixel center, which covers all samples
exactly when the center is covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 19:05:04 +09:00
Aarav 0eec3fd74a test(sce-net): avoid adjacent PR insertion points 2026-10-12 15:30:47 +05:30
Adrià Franch 8ccf9ee752 ci: remind stale conflicting pull requests to rebase
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 11:54:58 +02:00
Adrià e7c70a2df9
Merge pull request #4405 from adriadam10/ci/path-gate-tests
ci(build): run pull request jobs only when their files change
2026-10-12 11:54:45 +02:00
eljackjoestar-cmd b3587f9528 fix(shader): preserve ancillary proofs across relative VGPR accesses 2026-10-12 13:36:30 +04:00
Adrià Franch d9d00f0e34 ci(build): run pull request jobs only when their files change
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 11:35:11 +02:00
Jake Wang 09c0fa5daf fix(shader): release bundled optimizer traversal maps 2026-10-12 05:29:36 -04:00
Dean Galvin 9b91d2f66b fix(libc): round mspace block sizes to the 32-byte alignment 2026-10-12 22:21:01 +13:00
Hannah Lyons d316fcd8d5 feat(libc): export std::overflow_error type info, vtable and destructor 2026-10-12 05:20:17 -04:00
Adrià 9aa5fd2900
Merge pull request #4393 from adriadam10/docs/contributing-title-blockers
docs(contributing): name the title a pull request unblocks
2026-10-12 11:15:35 +02:00
Adrià Franch 7949388c0b docs(contributing): name the title a pull request unblocks
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 11:05:40 +02:00
Onny2 2bb8b83765 test(net): look up the unresolvable name as an absolute name
guest_sce_net expects sceNetResolverStartNtoa and
sceNetResolverStartNtoaMultipleRecordsEx to fail for
guest-sce-net.invalid. The resolver delegates to the host's
getaddrinfo, which retries a name that is not found with the host's
DNS search domains. On an ordinary home network whose router sets a
search domain that answers any name, the lookup then succeeds as
guest-sce-net.invalid.<search domain> and the test aborts.

Use the absolute name guest-sce-net.invalid. so the host applies no
search domain and the lookup fails on any network.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:05:39 +02:00
Mouad-Scriptz-0 295e6b501d feat(libSceNpAuthAuthorizedAppDialog): add the dialog, with NpAuth and SaveData imports of Minecraft
Minecraft (PPSA17221) fails to load on nine absent imports. The authorized-app
dialog runs the common-dialog state machine without UI and reports the user
as having cancelled, like the login and VR setup dialogs. The other two,
sceNpAuthGetAuthorizedAppCode and the unnamed libSceSaveData.native NID
RvID8BVeg+g, are declared as throwing stubs with unknown signatures.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 08:53:30 +00:00
screamm 5f3fb3dddd feat(libc): implement asinl
RetroArch 1.0.0-beta.1 (PPSA99169) stops on main 0f863c10 at

    FAIL: unresolved ELF import Nx-F5v0-qU8 from libSceLibcInternal.prx

Nx-F5v0-qU8 is asinl (tools/nid_names.py, aerolib).

The implementation follows the asinl of the libc.prx bundled with
PPSA28319, read from its disassembly: FreeBSD msun's e_asinl.c with
the ld80 invtrig.c coefficients (9f043204), which the binary holds as
the same 80-bit constants, computed in the x87 long double the guest
uses. +-1 returns +-pi/2 rounded to 64 bits and +-0 keeps its sign.
For |x| > 1, the infinities and NaN, Sony's error helper raises
FE_INVALID and sets errno to EDOM before returning (x - x) / (x - x),
so a NaN argument sets EDOM too.

20 million random arguments in (-1, 1) plus the branch edges give the
same bits as that code from the title's libc.prx run on a Windows
host. guest_math pins a set of those bits and checks the domain
errors' errno and FE_INVALID, and that errno is untouched otherwise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 10:51:59 +02:00
screamm 2c6e3f53ff feat(libc): implement sinhf
Ruffle Flash 1.2.0 (PPSA68091) stops on main 0f863c10 at

    FAIL: unresolved ELF import 1t1-JoZ0sZQ from libSceLibcInternal.prx

1t1-JoZ0sZQ is sinhf (tools/nid_names.py, aerolib). RetroArch
1.0.0-beta.1 (PPSA99169) imports it as well.

The implementation follows the sinhf of the libc.prx bundled with
PPSA28319, read from its disassembly: FreeBSD msun's e_sinhf.c as of
5aa554c7 (before __ldexp_expf, so expf(|x| / 2) is squared between
logf(FLT_MAX) and the overflow threshold), calling msun stable/9
expm1f and expf (59bc2f09), whose paths for the arguments sinhf passes
are inlined. Past the threshold Sony's error helper sets errno to
ERANGE before returning x * 1e37; NaN and the infinities return x + x
and leave errno alone.

Running that code from the title's libc.prx on a Windows host gives
the same bits as this implementation for every finite argument up to
the threshold, both signs (2,238,032,378 inputs). guest_math pins a
set of those bits, the overflow errno and FE_OVERFLOW, and checks that
errno is untouched otherwise.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 10:51:00 +02:00
ivica00 436e4191c7 fix(shader): decorate rect-list gl_Position as Invariant
The vertex stage already marks Position Invariant, and the rect-list stages have to match that interface.
2026-10-12 10:45:38 +02:00
milescoviello 98f6639e9e fix(net): make new IPv6 sockets IPv6-only as FreeBSD does
On Linux, socket() and sceNetSocket() create AF_INET6 sockets in the
host's default dual-stack mode (net.ipv6.bindv6only is 0), so a wildcard
IPv6 bind also claims the IPv4 port. A title that keeps an IPv4 and an
IPv6 socket on the same port fails: with 0.0.0.0:P bound, binding [::]:P
returns EADDRINUSE (48), and an IPv6 socket bound to [::] also receives
IPv4 traffic from v4-mapped peers.

FreeBSD makes every new IPv6 socket IPv6-only. sys/netinet6/in6_proto.c:

	VNET_DEFINE(int, ip6_v6only) = 1;

sys/netinet/in_pcb.c, in_pcballoc():

	if (INP_SOCKAF(so) == AF_INET6) {
		inp->inp_vflag |= INP_IPV6PROTO;
		if (V_ip6_v6only)
			inp->inp_flags |= IN6P_IPV6_V6ONLY;
	}

and sys/netinet6/in6_pcb.c, in6_pcbbind(), looks for an IPv4 owner of
the port only when the flag is clear:

	if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0 &&
	    IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {

socket() and sceNetSocket() now set IPV6_V6ONLY on every IPv6 socket
they create. Windows already defaults to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:44:27 +00:00
Dean c36fd0a0eb
Merge pull request #4321 from oneandonlydean/up4/fix-3308-watch-mapping
perf(libkernel): find direct mappings that share physical memory through an index
2026-10-12 21:43:41 +13:00
Dean a3b3cec05e
Merge pull request #4230 from MarcZX100/codex/carry-socket-flags-4028
fix(libkernel): accept socket type flags
2026-10-12 21:42:57 +13:00
Dean c22e2344ba
Merge pull request #4227 from MarcZX100/codex/carry-mbstowcs-4027
fix(libc): count full string with null mbstowcs destination
2026-10-12 21:42:35 +13:00
Dean 9fdddc8747
Merge pull request #4160 from hugodearaujo/fix/join-self-create-attr
fix(libkernel): join the calling thread and create from an unset attribute as the console does
2026-10-12 21:42:14 +13:00
Dean 82fc98f5e5
Merge pull request #4158 from hugodearaujo/fix/mutex-errors
fix(libkernel): return the console's errors from the SCE mutex calls
2026-10-12 21:41:52 +13:00
Dean 5ccf78e4e3
Merge pull request #4153 from hugodearaujo/fix/pthread-attr-errors
fix(libkernel): return the console's errors from the scePthreadAttr calls
2026-10-12 21:41:31 +13:00
Dean b2cc2df954
Merge pull request #4119 from hugodearaujo/fix/accessibility-defaults
fix(libSceUserService): report the console's default accessibility settings
2026-10-12 21:41:16 +13:00
Dean 7ea2022227
Merge pull request #4104 from hugodearaujo/fix/username-console
fix(libSceUserService): answer sceUserServiceGetUserName as the console does
2026-10-12 21:40:46 +13:00
Dean 69f7411ec9
Merge pull request #4096 from hugodearaujo/fix/solosched-values
fix(libkernel): accept only the solo scheduling values the console accepts
2026-10-12 21:40:24 +13:00
Dean 375e99ec33
Merge pull request #4095 from yunusshaikh7/fix/libc-towctrans-descriptors
fix(libc): map _Towctrans descriptors 1 and 2 to lower and upper case
2026-10-12 21:40:02 +13:00
Dean 49b6b6a5c8
Merge pull request #4092 from yunusshaikh7/fix/libscenet-datagram-receive-timeout
fix(libSceNet): report EAGAIN when a datagram receive times out on Windows
2026-10-12 21:39:40 +13:00
Dean 9b152c43d0
Merge pull request #4090 from yunusshaikh7/fix/libc-fopen-delete-sharing-windows
fix(libc): open fopen streams with delete sharing on Windows
2026-10-12 21:39:18 +13:00
Dean 10aff1c663
Merge pull request #4077 from hugodearaujo/fix/sched-priority-range
fix(libkernel): report the console's scheduling priority range
2026-10-12 21:38:56 +13:00
Dean 269a3453d1
Merge pull request #4059 from ee6aba54/feat/npwebapi2-add-web-trace-tag
feat(libSceNpWebApi2): export sceNpWebApi2AddWebTraceTag
2026-10-12 21:38:35 +13:00
Dean 57c90b3835
Merge pull request #4056 from Celegans12/fix/libc-terminate-full-what
fix(libc): write the whole what() when terminate reports an exception
2026-10-12 21:38:13 +13:00
milescoviello 89bc9d1082 fix(libc): read %i without the C23 binary prefix in the scanf family on Linux
On Linux sscanf, vsscanf, fscanf and sscanf_s hand their conversions to
glibc. Since glibc 2.38, code built as GNU C++ calls the C23 entry
points (__isoc23_vsscanf, __isoc23_vfscanf, __isoc23_sscanf), whose %i
reads a 0b/0B binary prefix. On main,
sscanf("0b101 x", "%i%n%7s", &value, &end, rest) stores value 5 and
end 5 and leaves "x" for the %s; " -0B11" gives -3. TechnicalDebt
records this.

FreeBSD's %i knows only the 0 and 0x prefixes. FreeBSD 11.4
lib/libc/stdio/vfscanf.c, parseint, lines 345-353 and 371-380:

	case '0':
		if (base == 0) {
			base = 8;
			flags |= PFXOK;
		}
	...
	/* letters ok iff hex */
	case 'A': case 'B': case 'C':
	case 'D': case 'E': case 'F':
	case 'a': case 'b': case 'c':
	case 'd': case 'e': case 'f':
		/* no need to fix base here */
		if (base <= 10)
			break;	/* not legal here */

so "0b101" stores 0, consumes the "0" and leaves "b101", as C17 does
and as the strtol family here already does.

With glibc the scanf family now calls __isoc99_vsscanf and
__isoc99_vfscanf, the entry points GNU C++ used before glibc 2.38
(exported since 2.7), which read %i without the binary prefix. Other
hosts keep std::vsscanf and std::vfscanf. The TechnicalDebt entry is
removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:38:06 +00:00
Dean 32e62f2f70
Merge pull request #4039 from ee6aba54/fix/ngs2-sampler-empty-state
fix(libSceNgs2): report EMPTY for a streaming sampler that played its queue
2026-10-12 21:37:52 +13:00
Dean 00fe732b2f
Merge pull request #4032 from fma-rc/feat/pad-input-file-sticks
feat(pad): hold the sticks from the controller command file
2026-10-12 21:37:30 +13:00
Dean 0511e86de9
Merge pull request #3984 from zmeizy/claude/scanf-wide-linux
fix(libc): throw for 16-bit wide scanf conversions on Linux
2026-10-12 21:37:08 +13:00
Dean 286d133a03
Merge pull request #3981 from Kikoarl/feat/sysmodule-audiopropagation-module-id
feat(libSceSysmodule): register libSceAudioPropagation module ID (0x120)
2026-10-12 21:36:45 +13:00
Dean 773f8de7b9
Merge pull request #3824 from xXPBSXx/fix/ces-cp1251-profile
feat(libSceCesCs): add sceCesRefersUcsProfileCp1251 and sceCesMbcsUcsContextInit
2026-10-12 21:36:23 +13:00
Dean 732fc01aaf
Merge pull request #3814 from xXPBSXx/fix/export-ppsa30601-imports
fix(exports): export the calls PPSA30601 imports
2026-10-12 21:36:02 +13:00
Dean Galvin d635d18967 test(agc): check that a Linux page query is answered from the page table
Revoke a checked arena page with mprotect behind the registry and check
that the stored answer comes back, so the test fails without the cache.
Pass the gpu argument that GuestAllocations::Mutation::Add and Protect
now take.
2026-10-12 21:35:24 +13:00
Dean 7b03a42185
Merge pull request #3659 from MarcZX100/carry/win-getdents-name-limit-3327
fix(libkernel): reject oversized Windows directory entry names
2026-10-12 21:35:24 +13:00
Ahir 49e9298706 fix(agc): store only the requested pages of a Linux page query
describePages stored the whole VMA that PROCMAP_QUERY or /proc/self/maps returned. Linux merges adjacent
anonymous mappings with equal flags, so an arena VMA can span GiBs, and the first table miss inside it after
an invalidation rewrote every page of it. Only the pages of the request, rounded out to whole pages, are
stored now, as #1791 describes.

When the guest arena is available, RunUnmappedGapTests checks that a check stores no pages outside its
range, and that protecting and unmapping through the registry clears the stored pages. The registry checks
follow the test on the perf/linux-page-cache branch linked from #1791.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:35:24 +13:00
Swastik-51 9f06176334 fix(agc): cache Linux describePages results 2026-10-12 21:35:13 +13:00
Dean 8f71e3010a
Merge pull request #4259 from Feuris/fix/libc-realloc-null-zero
fix(libc): allocate from realloc(NULL, 0) as malloc(0) does
2026-10-12 21:35:03 +13:00
Adrià 2979ae37a3
Merge pull request #4152 from adriadam10/feat/phase-b
perf(agc): build shader pipeline libraries when shaders are registered
2026-10-12 10:31:42 +02:00
AlxcBTW e785a902b5 fix(agc): keep a host import dropped mid-hold for every batch that hold opens
refreshImports and importAllocation drop an import whose registered run changed (a title growing a
heap) while the GpuMutex hold that drops it may still hold a HostImport pointer from before: a
build's in-place region (UploadFinish flushes pending images between regions, and a flush may
reconcile the imports), an indirect argument or a vertex import. The holder went to the batch open
at the drop, or was destroyed at once with the recorder idle, and the map node the pointer names
was erased. A Submit or Sync later in the hold put the command using the old buffer or address into
a later batch, so the import could be freed before that batch ran and the GPU read an unmapped
page (VK_ERROR_DEVICE_LOST; VK_EXT_device_fault reports READ_INVALID inside the dropped import).

The dropped import's map node now moves into its holder (std::map::extract), so those pointers stay
valid, and the holder is kept by every batch the same hold opens (Recorder::KeepThroughHold), so
whatever the hold records with them completes first. Drops for pages the guest releases (the lease
waiter) keep the old lifetime, and the waiter lets go of what an ended hold kept so it does not pin
the ranges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:30:13 +02:00
AlxcBTW ff349a9a50 test(agc): a host import dropped mid-hold must outlive the hold's later batches
Two deterministic, GPU-free checks (mock device) of the import lifetime when refreshImports drops
an import whose registered run grew, while the GpuMutex hold that took the import may still record
with it: with the recorder idle (the import was destroyed at once) and with a Submit between the
keeping batch and the batch that records the use (the import was destroyed when the keeping batch
completed, with the later batch still in flight).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:30:13 +02:00
Dean 9bc3623517
Merge pull request #4355 from oneandonlydean/up4/fix-4276-stencil-ref-export
fix(agc): export the stencil reference from pixel shaders with VK_EXT_shader_stencil_export
2026-10-12 21:22:09 +13:00
Dean 18028b4920
Merge pull request #4320 from oneandonlydean/up4/fix-frame-dump-queue
fix(agc-driver): queue frame dumps at the dump scale
2026-10-12 21:21:48 +13:00
Dean 3f80d529da
Merge pull request #4309 from oneandonlydean/up4/fix-2776-msaa-enable-single-sample
fix(agc): accept MSAA_ENABLE on a single-sample target sampled at the pixel center
2026-10-12 21:21:26 +13:00
Santhosh Kumar 6b38cdda8c fix(libkernel): report the target size of symbolic links on Windows
_wstat64, the FindFirstFileExW listing and std::filesystem::file_size
report the size of a file symbolic link itself (0). stat therefore gave
0 for linked files, and APR resolved them as empty, so Unity treated a
linked globalgamemanagers as empty and never read it.

stat takes the size from the handle it already opens, which follows the
link. The APR listing skips reparse points, and names missing from it
are sized through stat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:48:56 +05:30
Dean Galvin d37463e76f fix(shader): prove masked lanes unread for every select in one backward pass
MaskedSelectEliminator walked forward from each select to see whether the
lanes its mask excludes are read. Since fda4aa90 the walks pass explicit-LOD
gathers, size queries and guarded image writes, so in a large pixel shader
most of them cover the rest of the program, and the cost grows with the
square of its size (#3504).

The pass now computes, for every value, the set of masks under which it is
unread, from its uses backwards: a use guarded by a condition contributes the
masks that condition implies, a lane-local use adds what its result is unread
under, and any other use contributes nothing. A select is dropped when its
mask is in its own set. Loops are solved as strongly connected components.
The rules for guards, lane-local uses, loops, whole quad masks, selected
execs, rebuilt mask words and guarded first-lane reads are the ones the walk
applied. A first-lane read passes the masks its read mask implies when its
own result is unread outside that read mask, which is solved once per read in
the same way. The walk's step limit, its 64-level implication depth and its
first-lane nesting depth are gone, so a proof no longer depends on the order
of the questions.
2026-10-12 21:16:29 +13:00
milescoviello 19bfa3db29 fix(libkernel): report MSG_TRUNC for SOCK_SEQPACKET records in recvmsg
recvmsg() reads one byte more than the guest buffers hold to see
whether a record did not fit, but only for SOCK_DGRAM sockets. On a
SOCK_SEQPACKET socketpair a record longer than the buffers was cut
short with msg_flags 0: a 6-byte record received into 2 bytes returned
2 and no MSG_TRUNC, and the rest of the record was gone.

FreeBSD's SOCK_SEQPACKET protocol is atomic, sys/kern/uipc_usrreq.c:

	.pr_type =		SOCK_SEQPACKET,
	...
	.pr_flags =		PR_ADDR|PR_ATOMIC|PR_CONNREQUIRED|PR_WANTRCVD|
				    PR_RIGHTS,

and its receive routine, soreceive_generic() in sys/kern/uipc_socket.c,
flags the truncation of an atomic record and drops its rest:

	if (m != NULL && pr->pr_flags & PR_ATOMIC) {
		flags |= MSG_TRUNC;
		if ((flags & MSG_PEEK) == 0)
			(void) sbdroprecord_locked(&so->so_rcv);
	}

which kern_recvit() hands back in msg_flags. recvmsg() now probes for a
longer record on SOCK_SEQPACKET sockets as it does for SOCK_DGRAM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:03:38 +00:00
krishalivaibhav a55472cf9f build(agc): avoid copied-write test registration overlap 2026-10-12 13:31:41 +05:30
milescoviello 1337bd7e45 fix(libc): leave a stream that is reading untouched in fflush on Linux
fflush() passed every stream to glibc's fflush(). For a stream that is
reading, glibc seeks the descriptor back and drops the read buffer, and
with a pushed-back character it gets the position wrong: after fgetc()
returned 'a' from "abcdef\n" and ungetc('Z'), fflush() returned 0 and
the next fread() returned "bcdef\n\n". The pushed-back 'Z' was lost and
the last byte came back twice.

FreeBSD's fflush() writes out only a stream that is writing and leaves
every other stream as it is, lib/libc/stdio/fflush.c:

	if ((fp->_flags & (__SWR | __SRW)) == 0)
		retval = 0;
	else
		retval = __sflush(fp);

and __sflush():

	t = fp->_flags;
	if ((t & __SWR) == 0)
		return (0);

fflush() on Linux now returns 0 without calling the host for a stream
whose last operation was not a write (__fwriting() is 0). fflush(NULL)
and streams that are writing are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 08:00:21 +00:00
Adrià Franch b1ee794a66 test(agc): give the formatted buffer vertex case clip distances and the zero-position pixel shader its push slot
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:59:23 +02:00
milescoviello e9c5ae26be fix(libSceNet): read setsockopt timeouts as struct timeval
libkernel's setsockopt() hands options for a libSceNet socket to
sceNetSetsockopt(), whose SO_RCVTIMEO and SO_SNDTIMEO take an int of
microseconds. The POSIX call takes a struct timeval, so main read the
low half of tv_sec as microseconds: {1, 250000} set a 1 microsecond
receive timeout, {0, 500000} set 0, which disables the timeout, and an
out-of-range tv_usec was accepted.

FreeBSD reads a struct timeval, sys/kern/uipc_socket.c, sosetopt():

	case SO_SNDTIMEO:
	case SO_RCVTIMEO:
	...
		error = sooptcopyin(sopt, &tv, sizeof tv,
		    sizeof tv);
	if (error)
		goto bad;
	if (tv.tv_sec < 0 || tv.tv_usec < 0 ||
	    tv.tv_usec >= 1000000) {
		error = EDOM;
		goto bad;
	}

and sooptcopyin() fails a shorter value with EINVAL:

	if ((valsize = sopt->sopt_valsize) < minlen)
		return EINVAL;

setsockopt() on a libSceNet socket now converts the timeval for these
two options, with EINVAL for a short value and EDOM for a negative or
out-of-range one. sceNetSetsockopt() keeps taking microseconds; both
paths share the code that sets the host timeout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:58:06 +00:00
Dean 8c4a295ffb
Merge pull request #4083 from jweidenh/codex/fix-guest-array-callback-abi
fix(libc): preserve the guest ABI for array callbacks
2026-10-12 20:53:57 +13:00
Dean 4e6ef9077a
Merge pull request #3648 from hamb3r/fix/unity-libc-stubs
fix(libc): add missing Unity engine static imports
2026-10-12 20:53:14 +13:00
Adrià Franch f9b592806c perf(recompiler): keep the buffer base misalignment specialized in registration form
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 56d5a4985d perf(recompiler): read the formatted buffer helper's V# words as specialization constants when the format is known
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 60151b66b7 perf(recompiler): keep the format word specialized for buffers accessed with the descriptor's format
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 68164aee2a docs: note that compute shaders keep the specialized form
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 672e43ca67 perf(recompiler): keep compute shaders specialized; only prebuilt vertex and pixel shaders take the registration form
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch d809e6d0db fix(agc): close the library prebuild pool only if it was created
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch acc0a0487d docs: describe shader modules and pipeline libraries built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 361c7e2024 perf(agc): keep oversized shaders specialized and prebuild libraries on their own thread
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch a326fa84f7 fix(recompiler): give registration results a flattened SRT placeholder
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 9eb42ca2d2 fix(agc): destroy pipeline library shader modules once the library is built
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 67556722e1 perf(recompiler): read vertex attribute component selectors at run time when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch e9def903fd perf(agc): prebuild each stage's library alone and keep two descriptor sets with pipeline libraries
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 7f64d14772 perf(agc): build the shader pipeline libraries when graphics stages are resolved at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch d7e0f27d6b perf(agc): describe pipeline set layouts from the compiled shaders alone
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch 4d0b5c7898 perf(agc): give the fragment stage its own descriptor set when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:41 +02:00
Adrià Franch baf3437465 perf(recompiler): read depth-compare state and export mappings at run time when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:33 +02:00
Adrià Franch 765ea6a10f perf(recompiler): keep descriptor heaps uncompacted and read image modes at run time when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:32 +02:00
Adrià Franch ddb6680667 perf(recompiler): read buffer V# words in the shader when modules are built at registration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 09:50:32 +02:00
milescoviello 083c98a067 fix(libkernel): leave a once initializer's own cleanup handlers alone when it throws
pthread_once and scePthreadOnce removed their reset handler with a pop
that runs whatever handler is on top of the cleanup stack. An
initializer that pushed a handler of its own and then threw left that
handler on top, so the pop ran it with dangling state. Both now record
the stack top before pushing the reset handler; on a throw they restore
the stack to it and reset the control directly, and a normal return
restores it as well.

Reported in review by Hussein Kadhim, who fixed it the same way.

Co-authored-by: Hussein Kadhim <husseinkad.dev@gmail.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 07:49:09 +00:00
Tyler Westerberg a3cba2d1ff fix(agc-driver): move indices far past every vertex fetch's records to the first zero vertex
Goat Simulator 3 draws a triangle list whose indices are all 0xffffffff
with primitive restart off. Each fetch past a descriptor's records reads
zeros (out-of-bounds select 0 or 1), but the zero-padded vertex copies
were sized to the highest index: 160 GiB, which vkAllocateMemory refused.
When the highest index is at least 64Ki vertices past the largest
per-vertex record count, the draw now uses a copy of its indices with
every index past that count moved to it. Each attribute reads zeros
there as at the original index; only the vertex index the shader sees
changes, so indices closer to the records are left as they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:42:49 -04:00
m.peau e19e9145dd fix(libc): truncate strxfrm like FreeBSD's C locale
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 09:42:28 +02:00
krishalivaibhav dd4504ee43 test(agc): follow repository comment conventions 2026-10-12 13:11:36 +05:30
SandstromPL 872c3ba6ff fix(conventions): check the whole repository when run from a subdirectory
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 13:10:44 +05:30
krishalivaibhav 512ea3ec07 fix(agc): synchronize recorded fast clears with attachments 2026-10-12 13:10:17 +05:30
Rujul Talekar e991645c46 fix(tools): simulate cp1252 default encoding on Python 3.10 in pr_overlap tests
command_output patched subprocess._text_encoding, which does not exist on Python 3.10, so all eight tests errored before their assertions. Use subprocess._text_encoding where available and map io.TextIOWrapper locale to cp1252 on older interpreters, so a subprocess with no explicit encoding decodes as cp1252 on both. Add a behavioral check that default decodes as cp1252 while explicit utf-8 stays correct.

Authored by Rujul Talekar, with Muse Spark 1.3 assistance.
2026-10-12 13:02:14 +05:30
Paritosh Lahre 436e310ddb
fix(conventions): report 3rdparty submodules without a .gitmodules entry (#4304)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:31:24 +13:00
Bharat Yadav f961c52ad8
fix(release): exclude removed libraries from packages (#4297) 2026-10-12 20:31:13 +13:00
Paritosh Lahre 0f0291e39b
fix(tools): run pr_overlap on gh older than 2.48 (#4289)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:31:03 +13:00
Lorky 44e9050a58
fix(import-audit): parse export aliases as C++ tokens (#4196)
Co-authored-by: lorkydey <lorky.contact@gmail.com>
2026-10-12 20:30:54 +13:00
Rain 3048da7c04
docs(compatibility): record DOOM homebrew on Windows with AMD RDNA 4 and RDNA 2 (#4312)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:29:53 +13:00
MarcZX10 656c81ac8b
docs: clarify runtime code generation limits for --to-intel (#4257) 2026-10-12 20:29:46 +13:00
Dean 98c07d8e77
Merge pull request #4111 from jdmagic/fix/kernel-close-net-sockets
fix(libkernel): close() and setsockopt() reach libSceNet sockets
2026-10-12 20:28:00 +13:00
Dean 8b06e64176
Merge pull request #3854 from aryan0078/fix/relinker-debug-module
fix(relinker): resolve debug PRX dependencies by module identity
2026-10-12 20:27:31 +13:00
Dean 4b07baa2f2
Merge pull request #4314 from gravelas/feat/libscevoiceqos-set-connection-attribute
feat(libSceVoiceQoS): export sceVoiceQoSSetConnectionAttribute
2026-10-12 20:27:08 +13:00
Dean 5161070e67
Merge pull request #4273 from poonasor/fix/relinker-move-tls-branch-from-short-access
fix(relinker): move a short guest TLS access's follower branch into its stub
2026-10-12 20:26:47 +13:00
Dean aa2f05a285
Merge pull request #4266 from ducloc080202/fix/libkernel-apr-write-commands
fix(libkernel): record AmmModifyMtypeProtect and AmmRemapIntoPrt commands
2026-10-12 20:26:26 +13:00
Dean 5240dd46a5
Merge pull request #4255 from overlordxrz-source/feat/rudp-get-max-segment-size
feat(libSceRudp): implement sceRudpGetMaxSegmentSize
2026-10-12 20:26:05 +13:00
Dean fedcb3c813
Merge pull request #4117 from PrintOrb/fix/windows-positioned-invalid-descriptors
fix(libkernel): return positioned descriptor errors without CRT reports
2026-10-12 20:25:44 +13:00
Dean 2c51fdfba9
Merge pull request #4112 from jdmagic/fix/net-errors-set-kernel-errno
fix(libSceNet): failures also set libkernel's errno
2026-10-12 20:25:23 +13:00
Dean 4303f39b92
Merge pull request #4110 from jdmagic/fix/net-epoll-control-ops
fix(libSceNet): sceNetEpollControl takes 2 as MOD and 3 as DEL
2026-10-12 20:25:02 +13:00
Dean 84377033a5
Merge pull request #3831 from eljackjoestar-cmd/codex/opus-surround
feat(ajm): decode Opus surround mapping family
2026-10-12 20:24:32 +13:00
Dean 46f5b72e8e
Merge pull request #3623 from milescoviello/fix/pthread-key-numbering
fix(libkernel): number pthread keys from 1 as FreeBSD libthr does
2026-10-12 20:24:15 +13:00
SandstromPL 70f03dc5e5 fix(relinker): write registries after the executable
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:48:01 +05:30
Jan Šlégl 7f5cd4480d fix(recompiler): give an early-return selection a merge when its parent's other arm reaches the same join
A selection outside loops whose one arm only returns and whose other arm
branches to a join it does not dominate gets an empty block on that edge
as its merge (splitOneReturnJoin), but only when every other predecessor
of the join dominates the selection or is dominated by it. When the
parent selection's other arm is a region that ends in the same join, the
selection has no structured merge and the shader fails to recompile.

When every structurization attempt fails, Structurize now runs them again
and accepts that split for any join the selection does not dominate. The
split only inserts an empty block on the edge from the selection to the
join. Graphs that structurize on main keep their result.

A synthetic CFG and a synthetic program cover the shape; both fail on main
with "conditional block N has no structured merge".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
2026-10-12 09:11:25 +02:00
Dean 2ca6198e9a
Merge pull request #3606 from Lorkydey/feat/libkernel-getlogin-r
feat(libkernel): implement getlogin_r using the host account name
2026-10-12 20:01:56 +13:00
ArjenS-LA 100896c1dc fix(relinker): convert guest modules with GNU hash tables 2026-10-12 00:01:49 -07:00
Dean 9231a08b00
Merge pull request #3737 from MarcZX100/codex/json2-init-parameter2-3348
feat(libSceJson2): implement InitParameter2 constructors
2026-10-12 20:01:34 +13:00
ArjenS-LA 4b6bb2f059 fix(relinker): preserve inputs when writing Windows diagnostics 2026-10-12 00:01:25 -07:00
Dean de24bd616d
Merge pull request #4044 from xXPBSXx/fix/export-ppsa07411-vr2-imports
fix(exports): export the PS VR2 calls PPSA07411 imports that no library has
2026-10-12 20:01:13 +13:00
ArjenS-LA e2c24cf038 fix(relinker): accept trailing separators on the Windows origin path 2026-10-12 00:01:09 -07:00
ArjenS-LA 341dafb7ca fix(relinker): rebase each origin in guest runpath lists 2026-10-12 00:00:52 -07:00
ArjenS-LA 9504762edb fix(tools): ignore unreachable CTest registration files 2026-10-12 00:00:28 -07:00
Ahir 99b1b32f31 fix(libkernel): read the fcntl argument by command
fcntl and sceKernelFcntl read a third argument as a 64-bit integer for
every command on a non-socket descriptor. F_GETFD and F_GETFL pass no
third argument, and the commands that take one pass an int or a pointer.
They now read nothing, an int or a pointer, by command. An int read as
a 64-bit value kept whatever the caller left in the upper half of the
register, so F_READAHEAD -1 enabled read-ahead instead of disabling it.
2026-10-12 12:17:26 +05:30
screamm 01b7bb3e2f feat(libkernel): support ioctl on non-socket descriptors
ioctl and _ioctl answered EBADF for every descriptor outside the
libSceNet socket table. Decoded call sites: PS5MC (PPSA99420, FIONBIO x3,
FIONREAD x2, TIOCGWINSZ x3), ProsperoTV (PPSA99003, FIONBIO on a pipe
once poll on pipes is in), XPSemu (PPSA97358, 21 sites).

Following FreeBSD 11.4 kern_ioctl (sys/kern/sys_generic.c:758),
vn_ioctl (sys/kern/vfs_vnops.c:1488), pipe_ioctl (sys/kern/sys_pipe.c:1346)
and randomdev_ioctl (sys/dev/random/randomdev.c:301), release/11.4.0
8c639945: FIONBIO sets or clears O_NONBLOCK through the same status
flags as fcntl F_SETFL (EFAULT for a null argument before EBADF);
FIONREAD reports the size past the offset of a file, the queued bytes of
a pipe's read end, 0 on its write end, ENOTTY on the random device, and
throws on a directory or another character device; termios requests
(group 't', TIOCGWINSZ included) fail with ENOTTY, as tcgetattr already
takes these descriptors as not a terminal; any other request throws
naming the request and descriptor kind. Sockets keep their path
unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 12:17:26 +05:30
screamm 591e8ddadc feat(libkernel): support fcntl on non-socket descriptors
fcntl and sceKernelFcntl answered EBADF (sceKernelFcntl threw) for
every descriptor outside the libSceNet socket table, so files, pipes,
directories and descriptors 0 to 2 could not be queried or changed.

Observed: Yamagi Quake II (PPSA99007) stops with
"Sys_Error: change stdin to blocking Bad file descriptor", from
fcntl(fileno(stdin), F_SETFL, fcntl(0, F_GETFL, 0) & ~FNDELAY)
(yquake2 src/backends/unix/system.c:81-86).

Predicted from the titles' sources and decoded call sites: XPSemu
(PPSA97358, 97 fcntl sites: F_GETFD 12, F_SETFD 20, F_GETFL 8,
F_SETFL 9, F_DUPFD 1, F_DUPFD_CLOEXEC 7, F_GETLK 9) fails in
qemu_init_main_loop, where its pipe2 (pipe plus F_SETFD FD_CLOEXEC) and
glib's g_unix_set_fd_nonblocking (F_GETFL, F_SETFL) build the event
notifier (util/event_notifier-posix.c:44-67); VLC (PPSA85300, 43 sites,
F_RDAHEAD on every file it opens), RetroArch (PPSA99169, 17),
Porpoise (PPSA99764, 13) and Ruffle (9) call it on files and pipes.

The commands follow FreeBSD 11.4 kern_fcntl and kern_dup
(sys/kern/kern_descrip.c:479-799, 812, release/11.4.0 8c639945):
F_GETFD/F_SETFD keep the close-on-exec flag per descriptor, F_GETFL
reports the host access mode with the status flags, F_SETFL changes
only O_NONBLOCK, O_APPEND, O_ASYNC, O_FSYNC, O_DIRECT and FRDAHEAD,
F_DUPFD/F_DUPFD_CLOEXEC give the lowest free descriptor at or above the
start through dup (EINVAL for a negative start), F_DUP2FD goes through
dup2, F_RDAHEAD/F_READAHEAD set FRDAHEAD on files (EBADF on pipes,
ENOTTY elsewhere), and unknown commands fail with EINVAL. Status flags
live in a guest table shared by dup, dup2 and F_DUPFD like an open file
description. O_NONBLOCK switches a pipe (PIPE_NOWAIT on Windows, where an
empty read and a full write now report EAGAIN; the host flag on Linux).
Record locks use the host POSIX locks on Linux and throw on Windows.
Changes the host cannot model throw naming the flag and descriptor kind.
Sockets keep their path unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 12:17:25 +05:30
Dean Galvin 22d662eda7 fix(agc): export the stencil reference from pixel shaders with VK_EXT_shader_stencil_export 2026-10-12 19:44:41 +13:00
pushkarreddyy 2781077ace feat(libkernel): answer getrlimit for FreeBSD resources 2026-10-12 12:09:41 +05:30
Dean d06c065657
Merge pull request #3895 from CouchRiv/fix-shader-vcmpx-f32-family
fix(shader): keep f32 denormals in compares when denormals are kept
2026-10-12 19:39:34 +13:00
Dean 7f7f961bf8
Merge pull request #3739 from eljackjoestar-cmd/codex/masked-first-lane-proof
fix(shader): prove masked writes through guarded first-lane reads
2026-10-12 19:39:13 +13:00
pushkarreddyy d8b42473d7 feat(libkernel): support MIB 1.37 (kern.arandom) in sysctl 2026-10-12 12:07:45 +05:30
fma-rc 21b9e2efeb
feat(agc): select the swapchain present mode with ANYPS5_PRESENT_MODE 2026-10-12 08:30:00 +02:00
Felipe Arce 4dfe0526b8 fix(agc): link the texture mip layout into the BDA device tests 2026-10-12 03:26:02 -03:00
Celegans12 67ddb3624e fix(relinker): preserve load failures of present optional providers
Probe an optional provider before loading it and skip only direct file or
path absence. A present DLL with a missing dependency must report the
original loader failure instead of being treated as an absent provider.

Check the generated PE on every host: each optional provider's path
reference must reach a GetFileAttributesW call through its IAT slot whose
success branch continues to the LoadLibraryExW call.

Extend the native-provider regression with a generated DLL that imports
a missing dependency, while retaining the successful absent-file case.
2026-10-12 08:19:49 +02:00
Felipe Arce 6c94d31d77 test(agc): render into packed mip tails through the guest-memory fallback 2026-10-12 03:17:32 -03:00
Felipe Arce 9394c74301 fix(agc): keep untailed color targets on their own detiler layout 2026-10-12 03:17:32 -03:00
Avijit Singh 485c0f3346 style(agc): keep mip layout API comment-free
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 03:17:32 -03:00
Avijit Singh 89c128782c fix(agc): render nonresident packed mip targets
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 03:17:32 -03:00
Avijit Singh c58c9488e5 test(agc): cover packed color mip detiling
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 03:17:32 -03:00
Avijit Singh c914bcd721 feat(agc): describe color mip targets with parent layout
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 03:17:32 -03:00
Claude fd0d55ce21
fix(tools): follow Depends on chains in pr_overlap
pr_overlap only skipped a pair when one pull request named the other
directly. In a stack where #3 depends on #2 and #2 on #1, #1 and #3 were
reported as overlapping, and two pull requests built on the same #1 were
reported for the files and exports both inherit from it. Dependencies are
now followed through open pull requests, and what a shared dependency adds
is left out of the same-file and same-export columns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0189deyapHNNqKQJboZBJQei
2026-10-12 06:10:56 +00:00
Dean Galvin fdfbeba8ad fix(agc): accept MSAA_ENABLE on a single-sample target sampled at the pixel center 2026-10-12 19:07:15 +13:00
Dean 2ccbd376f8
Merge pull request #2169 from authurtabo-byte/feat/agc-uniform-sample-locations
feat(agc): support uniform custom MSAA sample locations
2026-10-12 19:06:44 +13:00
abhinav29102005 99a39859db test(libkernel): assert memory type propagation from batch commit entry 2026-10-12 11:34:03 +05:30
abhinav29102005 4f947dab16 test(libkernel): dispatch memory pool batch op 1 commit entry in test 2026-10-12 11:33:47 +05:30
abhinav29102005 ed3ca36b54 test(libkernel): construct batch commit entry for memory pool test 2026-10-12 11:33:19 +05:30
abhinav29102005 e106b3ba9b test(libkernel): assert neighbor page isolation after pool commit 2026-10-12 11:32:55 +05:30
abhinav29102005 f608965b47 test(libkernel): assert direct memory type update after pool commit 2026-10-12 11:32:37 +05:30
abhinav29102005 e04347ca60 test(libkernel): call sceKernelMemoryPoolCommit in direct memory test 2026-10-12 11:32:24 +05:30
abhinav29102005 899e7ddd0a test(libkernel): declare sceKernelMemoryPoolCommit in guest memory test harness 2026-10-12 11:32:07 +05:30
abhinav29102005 aeb131a5b7 fix(libkernel): forward entry type in sceKernelMemoryPoolBatch op 1 2026-10-12 11:31:52 +05:30
abhinav29102005 7b552bf143 fix(libkernel): forward type parameter from sceKernelMemoryPoolCommit 2026-10-12 11:31:39 +05:30
abhinav29102005 337db209fa fix(libkernel): route memory type to DoMtypeprotect in PoolCommit 2026-10-12 11:31:25 +05:30
abhinav29102005 21b0eb7fae refactor(libkernel): accept memory type in PoolCommit helper signature 2026-10-12 11:31:08 +05:30
JAB-dev deaa2e3ef6 test(agc): draw into a 1_5_5_5 target and read the 16 bits back
Draws 32_ABGR exports into a tiled and a linear format 17 target and compares every texel with
the layout X in bits 0-4, Y in 5-9, Z in 10-14, W in bit 15, including the X/Z-swapped pairs, rounding
edges and texels outside the scissor. Reverting the X/Z exchange fails it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 07:59:29 +02:00
Dean Galvin abc1cfa57d fix(agc-driver): queue frame dumps at the dump scale
A frame presented from guest memory with APS5_DUMP_FRAMES is read back
at the size of the display buffer, and that whole copy went into the
dump writer's queue with the scale beside it (31.6 MiB for a 3840x2160
buffer). The queue holds 64 MiB, so the third frame the writer had not
finished ended the run with "frame dump queue exceeded 64 MiB". A frame
blitted from the resident image is already reduced on the GPU and queued
at the reduced size.

The readback is now reduced by the dump scale when the slot retires and
queued at that size, the same pixels the writer kept before. With
APS5_DUMP_SCALE=1 nothing is reduced and the limit is reached as before.

video_out_frame_dump_queue presents four 3840x2160 frames from guest
memory while the writer is held on the first file and compares every
dump with the decoded display buffer.
2026-10-12 18:54:34 +13:00
winstrex 749541f653 feat(libkernel): implement sceKernelEnableDmemAliasing
Direct memory can already be mapped more than once, so there is no
aliasing to enable and the call returns 0. ProsperoEden (PPSA99008)
imports it and calls it before mapping the same direct memory again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:40:10 -03:00
Leonardo 216984afc6
feat(libkernel): implement sceKernelGetCpuTemperature 2026-10-12 07:37:44 +02:00
mir 89b4b2475e
Merge branch 'main' into aureau/carry-libc-strtod-no-conversion-zero 2026-10-12 01:34:06 -04:00
Dean Galvin 5b7cb8d126 perf(libkernel): find direct mappings that share physical memory through an index 2026-10-12 18:29:46 +13:00
Utku Halis 661f8e1e7c feat(libSceAgc): implement sceAgcAcbWaitUntilSafeForRendering 2026-10-12 08:26:47 +03:00
AlxcBTW b42ef24e07
fix(libs): keep thread_local objects with destructors out of module teardown on Windows (#4029)
* fix(agc): keep the driver's per-thread scratch out of thread_local teardown

With MinGW's emulated TLS, a thread_local with a destructor in a DLL is
destroyed after its storage is gone: when a std::thread ends, winpthread
runs libgcc's emutls key destructor, which frees the thread's thread_local
storage, and only then does the loader's DLL_THREAD_DETACH run the DLL's
tls_atexit list, so the destructors run on freed memory. The queue worker
touches four of them in libSceAgcDriver (the dispatch-ahead map, the
pending waits, the draw-snapshot compare vectors and the image decode
memo), and Driver::stop's join of the worker then corrupts the heap.
Under cdb's checking heap agc_driver_draw_cache_reuse stopped at the
worker's exit with heap corruption in all 127 runs, with graphics
pipeline libraries, with APS5_NO_PIPELINE_LTO=1 and with APS5_NO_GPL=1.
Without it the damage shows intermittently, as exit 0xc0000374 (2 of 143
runs with a cold NVIDIA shader cache) or as a fault in whatever allocates
next, such as NVIDIA's compiler on the optimized-link thread (#2980).

These objects now live in HostThreadLocal, fiber-local storage that owns
them, as the recorder's buffers do since e424b6b7b.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(libs): keep thread_local objects with destructors out of module teardown

The teardown order behind the driver's heap corruption holds in every
module: with MinGW's emulated TLS a thread's thread_local storage is freed
before the module's DLL_THREAD_DETACH runs its thread_local destructors,
so they run on freed objects. PreciseSleepUs's timer (used, among others,
by the video-out vblank thread) then closes a stale handle; libkernel's
directory path, socket waker and UUID random device, libSceRandom's random
device and libScePosixForWebKit's random device and host lookup free or
close stale objects. They move to HostThreadLocal. The other thread_locals
in the modules are trivially destructible or only built for Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(libs): fail when a module registers a thread_local destructor

prx_thread_local_destructors lists the symbols of every built module and,
where MinGW's thread_local destructor support is linked in, disassembles
the module for calls to __cxa_thread_atexit outside libc's registration of
guest destructors. A module that keeps a thread_local with a destructor
fails it with the function that registers one: with main's libSceRandom
it reports sceRandomGetRandomNumber, and with this branch all 122 modules
pass, libc being the only one that links the support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(agc): keep the image memo behind its pointer

The image decode memo went into HostThreadLocal as the whole array. On
Linux, where HostThreadLocal is a plain thread_local, that put 1024 memo
entries into every thread's TLS block instead of one pointer. It keeps
its unique_ptr as before, now owned by HostThreadLocal, so Linux builds
the same thread_local as main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dean Galvin <dean.galvin@live.com>
2026-10-12 18:25:29 +13:00
Aarav 74defef675 fix(libSceNet): preserve successful stream recvfrom results 2026-10-12 10:54:08 +05:30
Tyler Westerberg 2add25cea0 feat(agc-driver): replay a request as a prepared artifact with --prepare
agc_shader_replay compiles a request through Recompile, the per-draw path.
Shaders the driver prepares at registration go through PrepareShader
instead and produce a different, larger artifact, so a failure or the size
of that artifact could not be reproduced from the dumped request.
--prepare compiles the request with PrepareShader and prints the artifact's
SPIR-V size, or the first line of the failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:21:48 -04:00
Dean 6e653bef51
Merge pull request #3675 from MrMha/fix/oracle-cache-current
fix(hw-oracle): invalidate cached builds when inputs change
2026-10-12 18:21:47 +13:00
Tyler Westerberg bad49ba2c4 feat(libSceVoiceQoS): export sceVoiceQoSSetConnectionAttribute
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:21:46 -04:00
Dean f9984863e6
Merge pull request #3600 from CouchRiv/fix-libc-fmod-errno
fix(libc): fmod leaves errno unchanged
2026-10-12 18:20:20 +13:00
Dean 5d7be6bdc8
Merge pull request #4003 from zmeizy/claude/memory-pool-batch-type-protect
fix(libkernel): apply the memory type in sceKernelMemoryPoolBatch typ…
2026-10-12 18:20:05 +13:00
Dean 0ed0d3c9b6
Merge pull request #4033 from MarcZX100/codex/carry-shader-wqm-live-quad-bit-2548
fix(shader): fold current s_wqm pixel lane
2026-10-12 18:19:43 +13:00
Dean abd73fbc0d
Merge pull request #4002 from zmeizy/claude/barrier-destroy-after-release
fix(libkernel): let pthread_barrier_destroy wait for released waiters
2026-10-12 18:19:21 +13:00
Dean 6d9a680fbb
Merge pull request #3878 from fma-rc/fix/agc-zero-position-vertex
fix(shader): clip primitives with an all-zero clip position
2026-10-12 18:18:41 +13:00
Diversao cd067d1569 fix(agc): release parked host imports a pinned guest range waits for
A device replaced at AttachWindow keeps its host imports in
HostImports::parked until it is destroyed, and each parked import keeps
its guest ranges pinned. A guest munmap, mprotect or free of such a range
spun in the pin waiter until APS5_PIN_WAIT_MS and then failed, because the
waiter only looked at the live imports. The replaced device was already
waited idle by PrepareForReplacement, so the waiter now drops the parked
imports over the range.

agc_parked_import_unmap imports a range on one device, again on a second
one, and unmaps it with a 3 s pin wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:00:48 -03:00
Aarav 6197edea2b fix(libkernel): report copied socket address lengths 2026-10-12 10:21:17 +05:30
BharatYadav007 f0e852ae27 fix(libc): normalize valid mktime minus-one results 2026-10-12 10:18:20 +05:30
fma-rc 9db923e907
fix(shader): clip primitives with an all-zero clip position 2026-10-12 06:46:34 +02:00
Dean e013f8c9c5
Merge pull request #3834 from jackthereaper2006/docs/doom-linux-amd
docs: record DOOM homebrew on Linux with an AMD GPU
2026-10-12 17:35:57 +13:00
Dean 66ab8753bf
Merge pull request #3780 from MrMha/fix/unwind-record-bounds
fix(relinker): validate file-backed unwind record ranges
2026-10-12 17:35:37 +13:00
Dean 3eabeaaa81
Merge pull request #3754 from Amoenus/codex/test-app-info-child-result-maina11
test(libkernel): verify app info missing metadata errors
2026-10-12 17:35:16 +13:00
Dean 1a8fd39199
Merge pull request #3715 from Lorkydey/feat/libkernel-wait
feat(libkernel): implement wait through guest waitpid
2026-10-12 17:35:02 +13:00
Dean 7bcadb1e56
Merge pull request #3680 from droid2299/fix/libkernel-rusage-children
fix(libkernel): support RUSAGE_CHILDREN in getrusage
2026-10-12 17:34:48 +13:00
TibixDev 2441a4b0d9 fix(agc): retry failed host imports after range replacement
Retain weak identities and the full failed span so a replacement of either owner or a shorter remaining run retries the import. Keep unchanged failures cached across unrelated mapping changes without pinning their allocations. Existing upstream import lifetime, span windows and Windows fallback remain in place. AI-assisted implementation.
2026-10-12 07:34:15 +03:00
Dean 69138616c3
Merge pull request #3653 from QRMOE/fix-libc-strftime-literal-conversions
fix(libc): preserve strftime literal conversions
2026-10-12 17:34:15 +13:00
Dean a2c4176f7e
Merge pull request #3631 from arreina/pr/json-test-leak
test(json2): destroy the value before constructing over it
2026-10-12 17:34:01 +13:00
Dean 1d1acb1266
Merge pull request #3629 from QRMOE/fix-libc-dirname-result-length
fix(libc): check dirname result length
2026-10-12 17:33:41 +13:00
danzaio 56df893667 fix(agc): preserve dumps for non-standard shader failures
Capture the request once before rethrowing from the shared handler. Standard exceptions keep the shader address, stage, optional dump filename and full message; non-standard exceptions remain unchanged after capture.

AI-assisted: yes.
2026-10-12 01:33:37 -03:00
danzaio e71e3a4b3f fix(agc): name the shader address and stage in preparation failures
PrepareShaderWithDiagnostics dumped the request but rethrew the raw error, so a failure never said which shader it came from. Wrap it with the shader address and stage, keeping the dump. AI-assisted: yes.
2026-10-12 01:33:37 -03:00
danzaio 768929bb76 test(agc): preparation failures name the shader address and stage
FailureCapture now requires the code address and stage in the diagnosed failure, with and without the request dump. Fails before the wrap, passes after. AI-assisted: yes.
2026-10-12 01:33:37 -03:00
Dean bb67fe1a34
Merge pull request #3612 from karim200003/fix/libc-fgets-nonpositive-size
fix(libc): fail fgets with a size of 0 or less like FreeBSD
2026-10-12 17:33:20 +13:00
Dean e929880df4
Merge pull request #4008 from zmeizy/claude/getenv-invalid-name-errno
fix(libc): set EINVAL when getenv gets an invalid name
2026-10-12 17:33:03 +13:00
Dean 3fefe8a32f
Merge pull request #4010 from zmeizy/claude/getenv-stable-pointers
fix(libc): keep getenv results valid when other variables change
2026-10-12 17:32:42 +13:00
Dean 8b33507dc3
Merge pull request #4007 from zmeizy/claude/atexit-with-cxa-atexit
fix(libc): run atexit handlers in one order with __cxa_atexit destruc…
2026-10-12 17:32:21 +13:00
Dean fcb57611e4
Merge pull request #4221 from oneandonlydean/up4/in-place-index-highest-2
perf(agc): keep the highest index of a large in-place index buffer while it is unchanged
2026-10-12 17:32:00 +13:00
Dean 62bd3c9e68
Merge pull request #3841 from Kikoarl/feat/sysmodule-share-module-id
feat(libSceSysmodule): register libSceShare module ID (0x10F)
2026-10-12 17:31:46 +13:00
Dean 2da042eee0
Merge pull request #3933 from fma-rc/fix/system-service-language-param
fix(libSceSystemService): answer parameter 400 with the console language
2026-10-12 17:31:24 +13:00
Dean eb91f4e860
Merge pull request #4049 from Mouhand-Kaddo/fix/demons-readonly-resummarize
fix(agc): handle Demon's Souls read-only depth resummarize
2026-10-12 17:30:54 +13:00
Dean 6d1cee6d3f
Merge pull request #3593 from ahir061/fix/recompiler-prime-shared-mutex
fix(shader): lock recompiler shared mutexes once when created
2026-10-12 17:30:28 +13:00
Dean c22957f7b0
Merge pull request #3951 from Scramblrrrr/codex/constant-phi-definitions-current-main
fix(shader): preserve distinct SSA definitions when folding Phi inputs
2026-10-12 17:30:04 +13:00
Dean 32cb3fea95
Merge pull request #3932 from CouchRiv/fix-shader-f16-denorm-flush-family
fix(shader): flush f16 denormals in unary, frexp and conversion ops per the float mode
2026-10-12 17:29:43 +13:00
Dean 6b0ce1513d
Merge pull request #4018 from Mouhand-Kaddo/fix/demons-loop-continue
fix(shader): keep Demon's Souls loop shader valid after optimization
2026-10-12 17:29:21 +13:00
Dean 991378066e
Merge pull request #3661 from Celegans12/fix/agc-own-resolved-dispatch-state
fix(agc): own resolved dispatch state through thread exit
2026-10-12 17:29:00 +13:00
Dean 35ef0b7377
Merge pull request #3937 from hansjm10/fix/libkernel-munmap-unmapped
fix(libkernel): let munmap succeed over a range with nothing mapped
2026-10-12 17:28:36 +13:00
Dean 5aa0e15a4f
Merge pull request #3867 from tv7/fix/videoout-attribute-change-pending-flip
fix(videoout): accept a buffer attribute change while a flip is pending
2026-10-12 17:28:15 +13:00
Dean 18af217211
Merge pull request #3645 from alfalfa-star/fix/recompiler-mesh-grid-size-sgpr
fix(recompiler): read NumWorkgroups in every mesh shader so RADV sizes its user data
2026-10-12 17:27:54 +13:00
Ondra97 1e429dc23f test(libs): cover watched recvfrom sender outputs 2026-10-12 06:10:09 +02:00
eljackjoestar-cmd 9873fae1ef perf(libkernel): commit Windows direct backing when mapped 2026-10-12 07:59:34 +04:00
poonasor c6f934a3d0 fix(relinker): move a short guest TLS access's follower branch into its stub
A short fs-prefixed TLS access is patched with a 5-byte jump by
relocating the following instructions into the stub, but any branch
there was rejected outright. NHL 27 hits this on every stack-protector
epilogue: fs:xor rax,[rbx] immediately followed by jne __stack_chk_fail.

Allow relative conditional branches and short jumps: re-encode rel8
forms as their rel32 equivalents, rebase the displacement when the
stub is emitted, and keep rejecting loop/jrcxz, calls, returns, and
segment-prefixed followers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:44:54 -04:00
authurtabo-byte 605741d39f fix(agc): key GPL libraries by sample-location enablement 2026-10-11 20:43:05 -07:00
codingreapermaps c6be0e95cc fix(libkernel_sys): read relative symbolic links on Windows
readlink threw for every Windows reparse point, including the links
symlink creates. Read the target of a relative symbolic link from its
reparse data and return it with forward slashes. Absolute targets,
junctions and other reparse points still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:39:39 +01:00
Ondra97 8f33897275 fix(libs): allow socket receives into watched guest memory 2026-10-12 05:37:35 +02:00
authurtabo-byte fb7615e307 fix(agc): key GPL libraries by custom sample locations 2026-10-11 20:36:04 -07:00
Michon 33efe5ac5d fix(agc): the standard 8x pattern's last sample is at (7, -7)
Vulkan's standard 8x sample 7 is (0.9375, 0.0625), (7, -7) in signed sixteenths; the table had (7, -8), so the
standard pattern (PPSA99902's PA_SC_AA_SAMPLE_LOCS words 0xbd153fd1 / 0x9773f95b) was refused as nonstandard.
Regression row with those words.

Reported by @Amoenus.

Co-Authored-By: Amoenus <5391396+Amoenus@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 9a1a0672cac0f8e3dcaa6e28cbe6b7b684a40a1f)
2026-10-11 20:36:04 -07:00
authurtabo-byte 9bc72d105e fix(agc): preserve graphics behavior after rebase
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-10-11 20:36:04 -07:00
authurtabo-byte e28dcc0c12 fix(agc): validate custom sample location depth formats 2026-10-11 20:36:04 -07:00
authurtabo-byte c9e48e5ae2 feat(agc): support uniform custom MSAA sample locations 2026-10-11 20:36:04 -07:00
ducloc080202 2cf929db08 fix(libkernel): record AmmModifyMtypeProtect and AmmRemapIntoPrt commands 2026-10-12 10:33:45 +07:00
Nithishvelan M bc71f97667 fix(libkernel): return after NotImplemented in Windows shm_unlink 2026-10-12 09:03:06 +05:30
Dextroid17 ef3b121e44 fix(libSceAmpr): export sceAmprAmmSetPageTablePoolOccupancyNotificationThreshold
Resolves NID touqMEt6qXQ, the last import Trails in the Sky 2nd Chapter
(PPSA35169) is left missing on libSceAmpr.prx once the libkernel pair in #4214
is recorded. No reference describes the function's contract, so it throws via
the project's NotImplemented_nid_no_patch pattern rather than guessing a result
that a caller would act on.

sceAmprAmmGetUsageStatsData (KqiWXLgCVe0) is deliberately not included: #3814
exports it and #4207 answers it from a cited layout.

See issue #4214.
2026-10-11 23:23:44 -04:00
danzaio b390831a58 docs(user): relocate Windows standard handles logging note to Windows runtime layout section 2026-10-12 00:18:47 -03:00
danzaio ae96779914 test(relinker): verify Windows DLL CRT output handles 2026-10-12 00:15:49 -03:00
ChatGPT (local) 6effc5dcec test(relinker): verify GUI startup log replacement and unavailable log
Cover successful GUI stdout logging, replacement of stale log, and startup when log path is a directory. AI-assisted: ChatGPT.
2026-10-12 00:15:49 -03:00
Doston Abdullayev ab3e926058 feat(relinker): write the output of a Windows executable without standard handles to anyps5.log
A --windows-gui build started from Explorer, or any Windows executable
started without a standard output or error handle, lost everything it
printed: the entry stub's messages, the libraries' stdout and stderr,
uncaught exception messages and crash reports.

Before it enters its directory or loads a library, the entry stub now
checks the standard output and error handles, and gives each missing one
anyps5.log beside the executable. The C runtime of the libraries it then
loads adopts the file. Executables started with handles are unchanged,
and one that cannot create the file starts as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:15:49 -03:00
Dean d146ec1b66
Merge pull request #3640 from MarcZX100/feat/libc-math-predicate-exports-3624
feat(libc): export isnanf and __signbitl
2026-10-12 16:12:58 +13:00
Dean a18b088d39
Merge pull request #4005 from zmeizy/claude/posix-deadline-overflow
fix(libkernel): clamp POSIX deadlines beyond the clock range instead …
2026-10-12 16:12:38 +13:00
milescoviello 4ece6750b7 fix(libc): print the guest errno message for %m in the printf family
FreeBSD's printf formats %m as the message for the errno value at the
time of the call. On Linux a format with %m went to glibc, which looks
the number up in Linux's table, so guest errno values above 34 got
another error's text: with errno 35 (EAGAIN) "[%m]" printed
"[Resource deadlock avoided]" instead of "[Resource temporarily
unavailable]", and errno 63 (ENAMETOOLONG) printed "Out of streams
resources". On Windows, and on Linux when the format also had a wide
argument, the guest formatter threw on %m.

FreeBSD 11.4, lib/libc/stdio/vfprintf.c:

	saved_errno = errno;
	...
		case 'm':
			cp = strerror(saved_errno);
			size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
			sign = '\0';
			break;

The guest formatter now saves errno when it starts and formats %m as a
string with the guest strerror_r text, so width, precision and the -
flag apply as FreeBSD's string path applies them. On Linux a format with
%m goes to the guest formatter like one with a wide argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:12:27 +00:00
Dean 5a59d4e257
Merge pull request #3979 from zmeizy/claude/sema-remaining-timeout
fix(libkernel): report the remaining time from sceKernelWaitSema
2026-10-12 16:12:08 +13:00
Dean d2d4c07aab
Merge pull request #3978 from zmeizy/claude/asprintf-guest-wide
fix(libc): format 16-bit wide arguments in asprintf on Linux
2026-10-12 16:11:47 +13:00
Dean 1470522ff3
Merge pull request #3964 from MarcZX100/codex/carry-nanosleep-rmtp
fix(libkernel): leave remaining time untouched when nanosleep completes
2026-10-12 16:11:26 +13:00
Feuris d119945a45 fix(libc): allocate from realloc(NULL, 0) as malloc(0) does
realloc(NULL, 0) returned NULL without allocating anything, while malloc(0) returns a unique pointer. C11 7.22.3.5p3 says realloc with a null pointer behaves like malloc for the specified size, so code that grows an empty buffer from zero elements (p = realloc(p, n * size) with n == 0) and treats NULL as out of memory failed on its first call.

FreeBSD 11.4 never returns NULL from realloc for a zero size; je_realloc rounds the size up before choosing between resizing and allocating (contrib/jemalloc/src/jemalloc.c, je_realloc).

A null pointer with size 0 now reaches the allocator's realloc, which allocates as malloc(0) does. A non-null pointer with size 0 is still freed and NULL returned, as before.

Co-authored-by: milescoviello <kitslayer29@gmail.com>
2026-10-12 00:11:17 -03:00
Dean 9eb1486e39
Merge pull request #3957 from MarcZX100/codex/carry-recvfrom-stream
fix(libkernel): report no sender for stream recvfrom calls
2026-10-12 16:11:05 +13:00
Dean eb189f65d6
Merge pull request #3944 from hugodearaujo/fix/operation-mode-console
fix(libkernel): report the operation mode the console reports
2026-10-12 16:10:44 +13:00
Dean 5fe4bebec6
Merge pull request #3923 from AchrafReyani/fix/libkernel-close-ebadf
fix(libkernel): close an unopened descriptor on Windows without the invalid-parameter report
2026-10-12 16:10:27 +13:00
Dean e088868801
Merge pull request #3908 from GalaxyGamingFR/fix/conventions-wrapped-declarations
fix(conventions): treat wrapped declarations as declarations
2026-10-12 16:10:06 +13:00
Dean 796daf5ef0
Merge pull request #3906 from GalaxyGamingFR/fix/progress-render-sliver
fix(progress): skip cells for rectangles without area
2026-10-12 16:09:45 +13:00
AlxcBTW a7ed14c74b Merge branch 'main' into fix/recompiler-flat-p10-provoking-v2-2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:09:22 +02:00
Dean c12cb634f1
Merge pull request #4249 from oneandonlydean/up4/fix-skip-messages
test(agc): word the tessellation and oversized dispatch skips as device limitations, as check_skips expects
2026-10-12 16:09:15 +13:00
Dean 4dfa8506b3
Merge pull request #1680 from emirplus/fix/savedata-memory-metadata
fix(savedata): recover from metadata setup failure
2026-10-12 16:08:53 +13:00
Dean 01e5f3b8a8
Merge pull request #3977 from hansjm10/fix/libscenet-sockaddr-len
fix(libSceNet): accept guest socket addresses whatever their sa_len
2026-10-12 16:08:32 +13:00
Dean f0674d3c08
Merge pull request #3968 from babyhuey/feat/libkernel_sys-openat
feat(libkernel_sys): implement openat
2026-10-12 16:08:11 +13:00
Dean f3abf38307
Merge pull request #3855 from Lorkydey/fix/libkernel-utc-dst-width
fix(libkernel): limit UTC-to-local DST output to 32 bits
2026-10-12 16:07:49 +13:00
Dean 26d87eec20
Merge pull request #3850 from SandstromPL/fix/relinker-module-dir-root
fix(relinker): accept the input's directory as a module directory
2026-10-12 16:07:27 +13:00
Dean f5ce5957c1
Merge pull request #3797 from YossiYad/carry/audioout-hdmi-monitor-info
feat(libSceAudioOut): implement sceAudioOutSysGetHdmiMonitorInfo
2026-10-12 16:07:06 +13:00
AlxcBTW 48654bdc89 fix(shader): apply VOP3P neg_lo/neg_hi to packed 16-bit integer sources
The packed integer ops (v_pk_add/sub/mul_lo/mad/min/max/shift) read their
16-bit sources without the VOP3P negate bits. Hardware negates a 16-bit
source by flipping bit 15 for integer ops too, and Sony's compiler relies
on it: UE5's TSR history update clamps its -1 neighbour offsets with
v_pk_max_i16 v14, neg(lit 0x7fff), v14, i.e. max(-1, minY - y). Without
the modifier that became max(0x7fff, x), the x-1/y-1 taps of the 3x3
input filter read out of bounds as zero, and the history lost ~16 % of
its energy every frame (Sword of the Sea's missing glow).

The new execution test covers every packed integer op with neg_lo and
neg_hi, op_sel interplay, clamp and the TSR literal case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013fa2MbmdFLVr8YHuVjucHE
2026-10-12 04:57:52 +02:00
Gustavo Ariel Gamarra Rojas bff955a3e4 fix(agc): match any format when a storage dump shape names none 2026-10-11 23:55:12 -03:00
milescoviello 42bf2a2ad3 fix(libc): format guest wide arguments in syslog on Linux
vsyslog formats the message with the host's vsnprintf on Linux, so a
%ls, %S, %lc or %C argument is read as glibc's 32-bit wchar_t instead
of the guest's 16-bit units. glibc fails the conversion and the message
is dropped: on main syslog(LOG_DEBUG, "wide %ls %d %S", u"guest", 5,
u"text") writes "[syslog:15] " with nothing after it. Windows already
formats syslog messages with the guest formatter, and the narrow printf
family does so on Linux for formats with a guest wide conversion.

FreeBSD 11.4 lib/libc/gen/syslog.c line 229 formats the message with

	(void)vfprintf(fp, fmt, ap);

and lib/libc/stdio/vfprintf.c (lines 831-843) converts %S and %ls from
the guest's wchar_t:

	case 'S':
		flags |= LONGINT;
		/*FALLTHROUGH*/
	case 's':
		if (flags & LONGINT) {
			wchar_t *wcp;
	...
				convbuf = __wcsconv(wcp, prec);

vsyslog now formats through the guest's vsnprintf, which hands formats
with a guest wide conversion to the guest formatter (UTF-8 output, as
for printf) and every other format to glibc as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:52:07 +00:00
milescoviello 04c5c8da7a fix(libc): accept the ' flag in the wide printf family
The wide formatter behind swprintf, vswprintf, wprintf and snwprintf_s
does not know the ' (thousands grouping) flag: it takes ' for the
conversion and fails, so on main swprintf(buffer, 32, L"%'d", 1234567)
returns -1 with errno EINVAL and an empty buffer. The narrow guest
formatter accepts the flag since #3567.

FreeBSD 11.4 lib/libc/stdio/vfwprintf.c takes it as a flag
(lines 613-615):

	case '\'':
		flags |= GROUPING;
		goto rflag;

and grouping_init (lines 129-147) adds separators only while
*gs->grouping != CHAR_MAX. In the C locale, the only one the guest's
setlocale accepts, lib/libc/locale/lnumeric.c (lines 44-50) has

	static char	numempty[] = { CHAR_MAX, '\0' };
	...
		"",     	/* thousands_sep */
		numempty	/* grouping */

so the flag prints the number unchanged. The wide formatter now skips
' with the other flags before it hands the conversion to the host's
snprintf, as the narrow one does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:50:08 +00:00
milescoviello 7ad0736032 fix(libc): format positional arguments in the wide printf family
The wide formatter behind swprintf, vswprintf, wprintf and snwprintf_s
does not parse %n$ or *m$. It reads the position digits as a field
width, takes '$' for the conversion and fails, so on main
swprintf(buffer, 32, L"%2$d-%1$d", 1, 2) returns -1 with errno EINVAL
and an empty buffer. Localized format strings that reorder their
arguments this way print nothing.

FreeBSD formats wide output with __vfwprintf (vswprintf.c line 79),
which takes positions exactly as the narrow vfprintf does. FreeBSD 11.4
lib/libc/stdio/vfwprintf.c lines 635-652:

	case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
		n = 0;
		do {
			n = 10 * n + to_digit(ch);
			ch = *fmt++;
		} while (is_digit(ch));
		if (ch == '$') {
			nextarg = n;
			if (argtable == NULL) {
				argtable = statargtable;
				if (__find_warguments (fmt0, orgap,
						       &argtable)) {

and GETASTER (lines 509-531) reads a *m$ width or precision from
position m without moving nextarg.

The wide formatter now numbers its arguments with the narrow
formatter's NumberedArgumentClasses and ArgumentPosition, run over an
ASCII copy of the format, so the same FreeBSD rules apply: a position
selects the argument, *m$ does not move it, and a conversion without a
position takes the argument after the previous one. Formats without a
position are formatted as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:50:08 +00:00
milescoviello 6a19442f12 fix(libc): start append-mode streams at the end of the file
fopen and freopen leave an "a+" stream where the host CRT puts it, at
the start of the file. On main, after fopen(file, "a+") on a 7-byte
file, ftell returns 0 and the first fgetc returns the first byte of the
file. FreeBSD seeks every append-mode stream to the end when it opens
it, so ftell returns 7 and a read before any fseek returns EOF. Code
that opens a log or save file with "a+" and tests ftell(f) == 0 to see
whether the file is new treats every existing file as empty.

FreeBSD 11.4 lib/libc/stdio/fopen.c (fopen), and the same lines in
lib/libc/stdio/freopen.c (freopen):

	if (oflags & O_APPEND) {
		fp->_flags2 |= __S2OAP;
		(void)_sseek(fp, (fpos_t)0, SEEK_END);
	}

lib/libc/stdio/flags.c sets O_APPEND for every mode that starts with 'a':

	case 'a':	/* open for appending */
		ret = __SWR;
		m = O_WRONLY;
		o = O_CREAT | O_APPEND;

fopen and freopen now seek a stream opened with an 'a' mode to the end
of the file, ignoring a failed seek as FreeBSD does. Writes still go to
the end through O_APPEND.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:50:07 +00:00
fablevoid c763a78b67 feat(relinker): split unaligned 256-bit stores for Rosetta
Rosetta 2 ends the process when an unaligned 256-bit store crosses into
a page the store may not write ("rosetta error: unexpectedly need to
EmulateForward on a synchronous exception") instead of raising the
fault. A store inside such a page, and a 128-bit store across one,
fault normally. The macOS write watch keeps watched pages read-only
between collections, so a guest that stores 32 bytes across the start
of a watched page ends the process.

--to-rosetta now lowers VMOVUPS, VMOVUPD and VMOVDQU with a 256-bit
memory destination to a stub that stores the low half with the same
instruction at VEX.L=0 and the high half with VEXTRACTF128 at the
operand plus 16, so each half faults like any other store. RIP-relative
operands are relocated from the instruction's place in the site. A
store whose upper half has no 32-bit displacement fails the conversion.
Aligned stores (VMOVAPS, VMOVAPD, VMOVDQA and the non-temporal forms)
cannot cross a page and are left alone.

Every store is lowered. A 4-byte store has no room for the jump to its
stub, so its stub also takes one neighbouring instruction. The
following one: a return ends the stub, a call pushes its original
return address and jumps, a jump or conditional branch is re-encoded
with a 32-bit displacement, and another 256-bit store is lowered in the
same stub. Otherwise the preceding one, unless it is a call, a jump, a
return or a trap. Register calls, LOOP, JRCXZ and branches with a
prefix that changes them do not move. When neither neighbour can move,
the store becomes a short jump to an island: a run of movable
instructions within reach is moved to its own stub, and the bytes it
frees hold the jump to the store's stub. A store with neither fails the
conversion.

The converter tests compare the halves with clang's encodings for base,
SIB, disp8 overflowing to disp32, disp32, RSP-relative, FS-absolute,
r13-based and RIP-relative operands, and check that aligned stores,
loads, register forms and masked stores are not split. They check the
stub of a short store followed by a return, a call (direct, with an
address-size prefix, and through a RIP-relative pointer), short, near
and RIP-relative jumps, short and near conditional branches and another
store; that a following register call, LOOP, JRCXZ, prefixed branch or
CLWB makes the store take the instruction before it; islands past
instructions already moved, a store that needs its own stub, a branch
target, a register call and the short-jump reach; and that a store with
no room fails.

PPSA12544: 55295 stores split in the executable, 278 of them in the
stub of the store before them; every module converts with none kept.

AI-assisted: yes
2026-10-12 02:48:33 +00:00
fma-rc e0eef6c57f
feat(pad): hold the sticks from the controller command file 2026-10-12 04:47:59 +02:00
Yassine 5959506bbd test(avplayer): link guest_avplayer with the shared runtime libraries
guest_avplayer_tests imports __cxa_throw from libc.prx, which is loaded
before libstdc++, but -static-libgcc gave it a private copy of
_Unwind_Resume. A failed Check threw through libc.prx's unwinder and the
landing pad of the test function resumed with the private one, so on
Linux every failure aborted in _Unwind_RaiseException_Phase2 instead of
printing its message from main. This is the unexplained Linux abort in
the intermittent guest_avplayer failures.

The test is now linked without the static runtime flags, as the AgcDriver,
Agc and VideoOut tests already are, so the unwinder comes from the same
library as __cxa_throw. A forced Check failure in TestPlayback aborted
before and now prints its message and exits with 1.

(cherry picked from commit fa5199cd8fef7f9c7ab9e83a441e15d16364a81d)
2026-10-12 02:47:43 +00:00
Dean 34a16b7f08
Merge pull request #4159 from MarcZX100/codex/fix-sampled-cache-fixture-4141
test(agc): keep sampled cache fixture registrations separate
2026-10-12 15:47:18 +13:00
Dean 2263cefe25
Merge pull request #3601 from Scramblrrrr/codex/legacy-sse-reciprocals
fix(relinker): lower legacy packed reciprocal register instructions
2026-10-12 15:43:20 +13:00
overlordxrz-source 4b378a251f feat(libSceRudp): implement sceRudpGetMaxSegmentSize 2026-10-11 22:43:17 -04:00
Dean 6253883dee
Merge pull request #3881 from gravelas/fix/libscejson2-object-find
feat(libSceJson2): implement Object::find, Object::iterator::operator-> and String::compare
2026-10-12 15:42:47 +13:00
Dean 438ee8a517
Merge pull request #3652 from orishamir/fix/pad-analog-triggers
fix(pad): keep partial L2/R2 values from a controller
2026-10-12 15:42:14 +13:00
Dean f49f95873c
Merge pull request #3969 from hugodearaujo/fix/user-age-level
fix(libSceUserService): report an unrestricted age level
2026-10-12 15:34:36 +13:00
Dean d1758a91c8
Merge pull request #3494 from babyhuey/carry/pthread-attr-scope-3377
feat(libkernel): implement pthread_attr_getscope, setscope, getinheritsched and getstackaddr
2026-10-12 15:33:56 +13:00
Dean c2fdb9c71c
Merge pull request #2902 from oneandonlydean/carry/libkernel-sysctl-sdk-version
feat(libkernel): support kern.sdk_version in sysctlbyname
2026-10-12 15:33:29 +13:00
Scramblrrrr 29a843579d test(memory): query host holes beside warm collect memos 2026-10-11 23:27:02 -03:00
Karan Vora 4a4ec4c62c perf(shader): keep wave32 EXEC and VCC lane bits without a ballot round trip
In wave32 a compare that wrote vcc_lo kept only the ballot word and rebuilt the
lane's bit from it as (word >> lane) & 1, and every read of vcc_lo as a lane
mask (v_cndmask, carry-in) did the same. s_*saveexec_b32 and the 32-bit scalar
logic ops and s_mov_b32 that write exec_lo or vcc_lo also rebuilt the lane bit
from the new word. Wave64 already kept the lane bits.

Every writer keeps Vcc equal to the lane's bit of VccLo and Exec equal to the
lane's bit of ExecLo, so in wave32:
- a compare into vcc_lo sets Vcc to the compared value, and reading vcc_lo as
  a mask returns Vcc;
- s_*saveexec_b32 combines the old Exec with the source's lane bit, and tags
  the saved mask in the destination SGPR with the old Exec;
- s_and/or/xor/not/andn2/orn2/nand/nor/xnor_b32 and s_mov_b32 that involve
  exec_lo or vcc_lo combine the sources' lane bits the way sU64Mask does for
  the 64-bit forms, and tag an SGPR destination.
The words are still computed as before for scalar reads, SCC and branches;
where nothing reads them they are dead.

agc_driver_wave32_exec_lane_bits runs a wave32 program with v_cmp into vcc_lo,
v_cndmask, s_and_saveexec_b32 and exec restores through s_or_b32 and s_mov_b32,
checks every lane, and checks that its SPIR-V has no subgroup ballot left.
2026-10-11 22:23:44 -04:00
hypermist 8083dd1114 perf(agc): sort guest buffer regions through indices before merging them
UploadPrepare sorted the build's Region objects themselves, each a few
hundred bytes of vectors and shared pointers, and grew the merged list
without reserving it, moving every region many times per resource build.
Sorting an index array and reserving the merged list moves each region
once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
2026-10-12 15:08:34 +13:00
Dean Galvin 572e9d9f78 test(agc): word the tessellation and oversized dispatch skips as device limitations, as check_skips expects 2026-10-12 15:06:12 +13:00
Achraf Reyani 6b2432875d fix(agc): accept a linear 2D texture whose DEPTH holds its default pitch
On GFX10.3 the DEPTH field of a 1D or 2D non-array linear T# carries the
row pitch minus one (Mesa's ac_descriptors.c sets it for custom pitches,
which must be multiples of 256 bytes). A single-level, uncompressed 2D
linear texture whose DEPTH equals its default 256-byte-aligned pitch now
decodes as that 2D texture; any other pitch still throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:04:50 +09:00
Dean ceb342b947
Merge pull request #4240 from oneandonlydean/up4/fix-check-skips-packed-float16
test(agc): report the packed f16 native skip as a device limitation, as check_skips expects
2026-10-12 15:00:56 +13:00
MarcZX100 ce9fc689b7 docs(libkernel): record Windows directory name limit 2026-10-12 01:51:35 +00:00
Dean Galvin 20b801b7e8 test(agc): report the packed f16 native skip as a device limitation, as check_skips expects 2026-10-12 14:51:21 +13:00
ArjenS-LA b41c0b3da9 fix(libkernel): reject oversized Windows directory entry names
(cherry picked from commit b0e0c1c4bfd1487047873663115a94a46987b831)
2026-10-12 01:51:01 +00:00
milescoviello 589de27c17 fix(libc): accept the fdopen modes FreeBSD accepts
fdopen only took a fixed list of 15 mode strings and failed with EINVAL
for any other, so "rt", "r+t", "re", "rbe" or "rw" gave NULL. FreeBSD's
fdopen parses the mode with __sflags (lib/libc/stdio/flags.c): after
r, w or a it takes b, +, x, e and v and stops at the first other
character ("default: known = 0;"), and only a bad first character or x
on a read-only mode fail with EINVAL. fopen and freopen already parse
modes this way through ParseFileMode (#2033).

fdopen now uses ParseFileMode as well and passes the host its normalised
mode without x, which FreeBSD's fdopen ignores.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:50:11 +00:00
milescoviello 30d1e85283 fix(libScePosixForWebKit): use FreeBSD's gai_strerror message texts
gai_strerror returned its own wording for most codes, so a resolver
failure was described with text FreeBSD's libc does not use: EAI_NONAME
(8) gave "Name or service not known" instead of "hostname nor servname
provided, or not known", EAI_BADFLAGS (3) "Invalid flags" instead of
"Invalid value for ai_flags", and an unknown code "Unknown address
resolution error" instead of "Unknown error". Twelve of the fifteen
texts differed.

FreeBSD 11.4, lib/libc/net/gai_strerror.c:

	static const char *ai_errlist[] = {
		"Success",					/* 0 */
		"Address family for hostname not supported",	/* 1 */
		"Temporary failure in name resolution",		/* EAI_AGAIN */
		"Invalid value for ai_flags",			/* EAI_BADFLAGS */
		"Non-recoverable failure in name resolution",	/* EAI_FAIL */
		"ai_family not supported",			/* EAI_FAMILY */
		"Memory allocation failure", 			/* EAI_MEMORY */
		"No address associated with hostname",		/* 7 */
		"hostname nor servname provided, or not known",	/* EAI_NONAME */
		"servname not supported for ai_socktype",	/* EAI_SERVICE */
		"ai_socktype not supported", 			/* EAI_SOCKTYPE */
		"System error returned in errno", 		/* EAI_SYSTEM */
		"Invalid value for hints",			/* EAI_BADHINTS */
		"Resolved protocol is unknown",			/* EAI_PROTOCOL */
		"Argument buffer overflow"			/* EAI_OVERFLOW */
	};
	...
	if (ecode >= 0 && ecode < EAI_MAX)
		return ai_errlist[ecode];
	return "Unknown error";

The table and the unknown-code text now match, as 5830d8e44 did for
strerror.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:49:40 +00:00
milescoviello cba3b5a8b3 fix(libkernel): accept SOCK_NONBLOCK and SOCK_CLOEXEC in socket
socket only took a type of exactly SOCK_STREAM or SOCK_DGRAM, so a type
with SOCK_NONBLOCK (0x20000000) or SOCK_CLOEXEC (0x10000000) set failed
with EPROTONOSUPPORT, although socketpair already accepted both.

FreeBSD strips both flags before creating the socket (sys/kern/
uipc_syscalls.c, kern_socket: "if ((type & SOCK_CLOEXEC) != 0) { type
&= ~SOCK_CLOEXEC; oflag |= O_CLOEXEC; }" and the same for SOCK_NONBLOCK
with FNONBLOCK), and makes the new socket non-blocking with
"fo_ioctl(fp, FIONBIO, &fflag, ...)".

socket now does the same as socketpair: it creates the socket from the
plain type and, with SOCK_NONBLOCK, switches it to non-blocking.
SOCK_CLOEXEC needs nothing more, since a guest never executes another
program.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:48:40 +00:00
milescoviello 910363657d fix(libc): count the whole string in mbstowcs with a null destination
mbstowcs, and libSceAmpr's copy of it, counted at most n characters
when the destination was NULL, so the usual sizing call
mbstowcs(NULL, s, 0) returned 0 and mbstowcs(NULL, s, 1) returned 1 for
a three-character string. A caller that sizes its buffer that way got a
buffer too small for the string and no terminator.

POSIX says that with a null pwcs mbstowcs "shall return the length
required to convert the entire array regardless of the value of n", and
FreeBSD does that: lib/libc/locale/mbstowcs.c passes SIZE_T_MAX as the
source limit, and the C locale's _none_mbsnrtowcs (none.c) returns the
distance to the terminating NUL when "dst == NULL".

With a null destination, SonyMbstowcs now returns the length of the
whole string.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:47:11 +00:00
Dean d371ed3076
Merge pull request #4200 from winstrex/fix/host-entropy
fix(libs): take guest entropy from the host OS instead of std::random_device
2026-10-12 14:42:53 +13:00
Dean 695b8e552c
Merge pull request #4169 from oneandonlydean/up4/fix-mprotect-stack-bound
fix(libkernel): reject mprotect of host memory that is not a guest thread stack
2026-10-12 14:41:35 +13:00
Dean d0ca839cc9
Merge pull request #4057 from jweidenh/codex/vector-window-stage-inputs
fix(recompiler): include implicit inputs in relative vector windows
2026-10-12 14:30:23 +13:00
Karan Vora d759a43ae5 perf(shader): search a bindless image table once per access
EmitImage selects the table slot of a bindless image before it branches over
the image's runtime modes, and EmitImageMode then searched the table again in
every mode arm. Each search is a binary search of dependent flattened_srt
loads, so every bindless sample ran two. The mode arms now take the selection
EmitImage already made; the modes copy the base image, so the mapping offset,
root and iteration count the search reads are the same.
2026-10-11 21:22:52 -04:00
Nixitov 7d9966a8ee feat(libc): implement __divti3
Kart PS5 (PPSA99611) imports __divti3 and stops at start-up on main. The guest has no signed 128-bit division; add it alongside __udivti3, dividing the magnitudes and negating the quotient, and throwing on a zero divisor like the unsigned routine.
2026-10-12 03:22:38 +02:00
Dean 34ba918bc3
Merge pull request #4177 from oneandonlydean/up4/fix-registered-workgroup-sync
test(agc): read lane 63 of the vertex index in the registered workgroup fixture
2026-10-12 14:22:30 +13:00
pallp 4b125c0ca5 fix(savedata): fill the search infos of sceSaveDataDirNameSearch
SaveDataDirNameSearchResult.infos points at an array the title reads one
entry per returned directory name, but the search never wrote it, and
SaveDataSearchInfo was declared as 128 opaque bytes. A title that asks for
the infos read whatever its buffer held.

SaveDataSearchInfo is now the 48-byte record shadPS4 declares as
OrbisSaveDataSearchInfo (blocks, free blocks, 32 reserved bytes), and the
search fills one per returned name. Every native save reports
SAVE_DATA_BLOCKS_MAX blocks, as sceSaveDataGetMountInfo and the block sort
keys already assume, so both counts are SAVE_DATA_BLOCKS_MAX and the
reserved bytes are zero.

guest_savedata_search_tests checks the two records, their zero reserved
bytes and that the search writes nothing past them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VFVE5WbaUJ2vyj8NAW44CS
2026-10-12 02:18:33 +01:00
Scramblrrrr b45232dda6 test(memory): handle Windows layouts without arena host holes 2026-10-11 22:18:20 -03:00
fma-rc b843b16a0a
fix(libSceSystemService): answer parameter 400 with the console language 2026-10-12 03:18:00 +02:00
Nesyth d9fbaf767c fix(agc): accept a CB metadata pass beside depth state without depth or stencil planes 2026-10-12 03:14:12 +02:00
saxopa cded9ba8c3 fix(relinker): accept RELATIVE relocations against a zero-value section symbol
OpenOrbis create-fself adds one to the symbol index of every relocation it
copies from .rela.dyn, so each R_X86_64_RELATIVE entry of a homebrew built
with the OpenOrbis toolchain names symbol 1, a local STT_SECTION symbol with
value 0. FreeBSD rtld computes RELATIVE as base + addend without reading the
symbol. The pipeline, the code analysis and the strict filter now accept
index 0 or a STT_SECTION symbol with value 0, and still throw for any other
symbol. The relocation is written with symbol index 0, where the pipeline
previously dropped RELATIVE entries with a nonzero index.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:12:02 +02:00
winstrex 8f9efc25f2 fix(libs): take guest entropy from the host OS instead of std::random_device
std::random_device in libstdc++ reads RDSEED on x86 Linux. On an AMD
EPYC 9V45 (Zen 5) CI runner its 32-bit results repeat about 130 times
in 65536 draws, so guest_posix_random fails. libc now exports
HostEntropy_nid_no_patch, which uses getentropy (rand_s on Windows) and
throws when the OS cannot provide entropy. arc4random, arc4random_buf,
sceRandomGetRandomNumber, /dev/random and /dev/urandom reads,
std::_Random_device and sceKernelUuidCreate use it, and the arc4random
and random device TechnicalDebt entries say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:07:49 -03:00
jweidenh 38b05f4868 fix(agc): initialize every sparse buffer copy hole 2026-10-11 21:06:02 -04:00
jweidenh 8404d62be9 test(agc): reproduce stale bytes in sparse buffer holes 2026-10-11 21:06:02 -04:00
Scramblrrrr 875d730116 fix(agc): report sparse CPU copy spans after allocation 2026-10-11 21:54:42 -03:00
Dean Galvin 8938a257f2 test(agc): read lane 63 of the vertex index in the registered workgroup fixture
The fixture read lane 63 of v0, which the program never writes. Since #3546 a wave64 vertex program on a narrower host subgroup builds that read as 0, so the synchronous registration the test expects to fail succeeds. v5 holds the vertex index, which is not 0 in lanes 32-63, so the read still throws.
2026-10-12 13:44:23 +13:00
Kikoarl d906ee2f6d test(agc): add unit test for copied writer indirect overlap disambiguation 2026-10-12 02:42:14 +02:00
Oxide 1-6 3df53d9296
fix(shader): preserve integer narrowing in constant folding
Remove unsafe U16/U8 round-trip cancellation and cover nonconstant, immediate, bitcast and dead-code cleanup paths with Release-active IR regressions.
2026-10-12 06:08:56 +05:30
screamm 1b4baaee27 fix(memory): exclude arena host holes from every write-tracking query
On Windows agc_write_tracking has failed since #3594 with

    write tracking test failed: an unwatched range reads as not stored over

from the check in CheckSharedBlock:

    if (!Watched(outside, unwatched.size())) Require(StoredOver(outside, unwatched.size(), TrackerGeneration()), "an unwatched range reads as not stored over");

The host stack array lies in a host hole of the guest arena. #3594 made
Watched, collectWrites, UnchangedSince, UnchangedSinceAll and storeOwn
reject ranges overlapping such holes, but StoredOver, MarkWritten,
WrittenSince, ChangedBlocks and ImportWatched still accepted them through
WriteTracker::covers(), so StoredOver proved a range not stored over while
Watched called it unwatched (#4087).

The exclusion now lives in WriteTracker::covers() on Windows, which every
query already asks, and the per-function checks are removed. Host holes
are padded to the allocation granularity and the tracker blocks are 64 KiB
from the arena base, so no block is shared between a hole and arena
memory, and ranges outside the holes get the same answers as before.

A new case enumerates the arena for a host hole and checks every query
against it; on the previous code StoredOver, MarkWritten, StoreOwnBytes,
WrittenSince and ChangedBlocks fail it.

Fixes #4087

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 21:36:46 -03:00
Scramblrrrr ef218ef928 test(agc): distinguish GPU depth from reused storage bytes 2026-10-11 21:29:59 -03:00
Scramblrrrr b9b83cef2b style(graphics): follow the code comment convention 2026-10-11 21:29:25 -03:00
Scramblrrrr 4de5726d32 fix(graphics): distinguish reused memory from depth storage aliases 2026-10-11 21:29:25 -03:00
Dean 7a84b22267
Merge pull request #4131 from oneandonlydean/up4/fix-stored-over-unwatched
fix(agc): report arena host holes as stored over on Windows
2026-10-12 13:29:17 +13:00
Archit Dewan 5eb22de16d fix(relinker): reject empty positional paths 2026-10-11 20:28:56 -04:00
MarcZX100 c87c8c229b test(agc): keep sampled cache fixture registrations separate 2026-10-12 00:12:50 +00:00
hugodearaujo 0b7e4ddf9f fix(libkernel): join the calling thread and create from an unset attribute as the console does
On a PS5 (firmware 13.60) scePthreadJoin on the calling thread returns
EDEADLK and scePthreadCreate with a zeroed attribute creates a thread
with the default attributes. Here both threw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:09:08 -03:00
Celegans12 364d7cf29f fix(shader): match half-wave reductions over relatively addressed vector registers
A program that uses v_movrels_b32 or v_movreld_b32 keeps every vector
register from the lowest relatively addressed one up in a per-invocation
window array, so its plain register accesses become LoadVectorWindow and
StoreVectorWindow. The half-wave reduction matcher followed only SSA values,
so a wave64 scan, v_permlanex16_b32 join and lane 31/63 read through that
array threw that lane 63 is outside the 32-lane host subgroup.

The matcher now reads a window load at a constant slot as the value of the
last store to that slot earlier in its block, unless a store at a runtime
slot lies in between; each invocation owns its array, so that is the value
the load returns. A store in another block, or a v_movreld_b32 at a runtime
M0 in between, still throws. This was the only refused lane 32-63 read left
among Ghost of Yotei's wave64 graphics programs (one pixel program).
2026-10-12 02:07:50 +02:00
hugodearaujo b9b5bcc190 fix(libkernel): return the console's errors from the SCE mutex calls
On a PS5 (firmware 13.60) the SCE mutex attribute calls return EINVAL
for a null or uninitialized attribute, scePthreadMutexattrSetprotocol
accepts 0 to 2, scePthreadMutexInit takes an uninitialized attribute as
the default one, and destroying a destroyed mutex returns EINVAL. Here
these threw, including for the priority protection protocol.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:06:55 -03:00
Avijit Singh fc7a9650f9 style(agc): remove unnecessary shutdown explanation
Keep the tracker initialization fix without an explanatory code comment or technical-debt entry, as requested in review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 05:32:09 +05:30
hugodearaujo 2446a2648a fix(libkernel): return the console's errors from the scePthreadAttr calls
On a PS5 (firmware 13.60) the scePthreadAttr setters and getters return
EINVAL for a null or uninitialized attribute and for a null output,
ENOTSUP for a policy other than SCHED_FIFO and SCHED_RR, an inheritance
other than explicit and inherit, and a priority outside 256 to 767, and
EINVAL for an invalid detach state. Here they threw, or stored any
policy, inheritance and priority.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:59:17 -03:00
eljackjoestar-cmd fded8a7e7f fix(shader): prove masked writes through guarded first-lane reads 2026-10-12 03:57:04 +04:00
Kikoarl a057eede19 perf(agc): disambiguate direct GPU writes from copied write-backs in indirect draws 2026-10-12 01:53:33 +02:00
Dean Galvin 5b1868d23d fix(libkernel): reject mprotect of host memory that is not a guest thread stack
The Windows path from #3788 changed the protection of any committed private host region outside the guest registry, including a range crossing a guest stack's lower bound. Ranges inside a live guest thread stack keep the path from #2982.
2026-10-12 12:50:24 +13:00
lorkydey f88be06af2 fix(libkernel): resolve getlogin_r from the effective host account 2026-10-12 01:39:07 +02:00
lorkydey a4502cd4bd feat(libkernel): implement getlogin_r using the host login name 2026-10-12 01:39:07 +02:00
Dean Galvin 5864ee5839 fix(agc): report arena host holes as stored over on Windows
StoredOver still read block stamps for a range in a host region inside the guest arena interval, which Watched reports as unwatched since #3594.
2026-10-12 12:38:06 +13:00
xXPBSXx 2a7b38ab75 fix(libSceAgc): write the KytyPS5 workload packets and return null for unregistered streams
sceAgcDcbSetWorkloadsActive writes an 18-dword IT_NOP packet (stream id, 64-bit workload mask) and sceAgcDcbSetWorkloadComplete a 12-dword one (stream id, workload id, 64-bit clear mask), following KytyPS5. Both return null for a null buffer, an unregistered or out-of-range stream, a workload id above 63, and for active a duplicate id or a count of 0 or above 63. sceAgcDriverRegisterWorkloadStream and sceAgcDriverUnregisterWorkloadStream now record the stream ids the DCB calls check.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 01:37:56 +02:00
Claude c35d05395d
fix(libc): reject 16-bit wide conversions in sscanf_s and after the ' flag on Linux
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 23:37:17 +00:00
xXPBSXx 45fcf734ab fix(libSceAgc): emit NOP packets for the DCB workload calls
sceAgcDcbSetWorkloadsActive and sceAgcDcbSetWorkloadComplete threw. They now write a plain two-dword NOP and keep no workload state.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 01:36:51 +02:00
hugodearaujo 78abb2b46a fix(libSceUserService): report the console's default accessibility settings
On a PS5 (firmware 13.60) a user with every accessibility setting at
its default reads chat transcription 0, press-and-hold delay 0, trigger
effect 1, vibration 1, zoom 0 and zoom follow focus 1; all six read 0
here. A user who is not logged in returns 0x80960009 with or without an
output, as sceUserServiceGetAgeLevel does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:35:58 -03:00
PrintOrb 4680a52c1e fix(libkernel): return positioned descriptor errors without CRT reports 2026-10-11 18:35:17 -05:00
Simon 2fd05f47e2 feat(libSceNpWebApi2): export sceNpWebApi2AddWebTraceTag
PPSA33016 imports sceNpWebApi2AddWebTraceTag (NID MgsTa76wlEk), and no
library exported it, so the loader stopped before the title started.
The title does not call it at start-up or in play, so the export throws
like the other unimplemented functions.
2026-10-12 01:30:54 +02:00
Dean bca7a29d25
Merge pull request #4021 from Mouhand-Kaddo/fix/windows-select-host-poll
fix(libkernel): restore Windows select build for Demons Souls testing
2026-10-12 12:30:30 +13:00
ethan 63a27648a8 fix(relinker): resolve EIP-relative imports in strict filter 2026-10-11 16:24:12 -07:00
jdmagic d5805fb3e4 fix(libkernel): setsockopt sets options of libSceNet sockets
A title creating a socket with sceNetSocket sets its options with the BSD
setsockopt, which looked the descriptor up only among libkernel's own
sockets and returned EBADF. libSceNet now registers an option setter with
libkernel, as it does for close and select, and accepts SO_REUSEADDR,
SO_BROADCAST, SO_SNDBUF and SO_RCVBUF like libkernel's own sockets.

PPSA01289 (Sackboy: A Big Adventure) sets SO_BROADCAST on its UDP P2P
socket this way in UIpNetDriver::InitBase. The failure made every listen
fail, and the engine travelled back to its default map with ?closed,
reloading it about every two seconds behind the loading screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:24 -04:00
jdmagic 5d14cd02ff fix(libSceNet): failures also set libkernel's errno
libSceNet stored its errno only in its own slot (sceNetErrnoLoc). Titles
that use the BSD socket model read errno after a libSceNet call, as the
console's libSceNet calls are kernel calls that set it; shadPS4 mirrors its
net errno into the kernel errno the same way (SetKernelErrnoHook).

PPSA01289 (Sackboy: A Big Adventure) reads errno through Unreal's
GetLastErrorCode after sceNetRecvfrom. It never saw EWOULDBLOCK, so
UIpNetDriver::TickDispatch kept receiving and the game thread never left
its first networked frame.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:24 -04:00
jdmagic 12b18af550 fix(libkernel): close() closes libSceNet sockets
A libSceNet socket is a kernel descriptor on the console, so titles close it
with close(). libSceNet keeps its sockets in its own table, and libkernel's
close passed their numbers to the CRT, which reported an invalid parameter
and left the socket open. libSceNet now registers a closer with libkernel,
as it registers its poller for select. PPSA01289 (Sackboy: A Big Adventure)
closes its P2P socket this way each time it tears its network session down,
leaking one open socket per attempt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:24 -04:00
jdmagic 22827bf6ad fix(libSceNet): sceNetEpollControl takes 2 as MOD and 3 as DEL
sceNetEpollControl used Linux's EPOLL_CTL numbering (1 ADD, 2 DEL, 3 MOD).
The SCE values are 1 ADD, 2 MOD, 3 DEL (shadPS4 net_types.h,
ORBIS_NET_EPOLL_CTL_*). A title removing a socket with op 3 and a null
event got EINVAL, and op 2 deleted the registration it meant to modify.
PPSA01289 (Sackboy: A Big Adventure) removes its UDP socket from its epoll
this way before closing it, every time its network session is torn down.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:23:23 -04:00
jweidenh 5ec6cff3fa fix(recompiler): include implicit inputs in relative vector windows 2026-10-11 19:23:00 -04:00
jweidenh e4575c723e test(recompiler): keep vector window fixtures observable 2026-10-11 19:23:00 -04:00
jweidenh c0c86deeb8 test(recompiler): cover implicit inputs in relative vector windows 2026-10-11 19:23:00 -04:00
xXPBSXx 35a6df818f Merge remote-tracking branch 'upstream/main' into fix/export-ppsa30601-imports
# Conflicts:
#	core/libs/prx/libSceJson2/Export.cpp
#	docs/dev/TechnicalDebt.md
2026-10-12 01:19:36 +02:00
xXPBSXx 50ca599251 Merge remote-tracking branch 'upstream/main' into fix/ces-cp1251-profile
# Conflicts:
#	docs/dev/TechnicalDebt.md
2026-10-12 01:17:20 +02:00
hugodearaujo 802ec551c2 fix(libSceUserService): answer sceUserServiceGetUserName as the console does
On a PS5 (firmware 13.60) a null name returns INVALID_ARGUMENT first,
then a user id the console does not know returns 0x80960105 whatever
the buffer, and a buffer shorter than 17 bytes is zeroed before
BUFFER_TOO_SHORT is returned. Here an unknown user returned
INVALID_ARGUMENT and a short buffer was left untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:12:43 -03:00
virp-mta 8b04156bfd fix(libkernel): return EDESTADDRREQ for send and sendto on unconnected UDP sockets 2026-10-12 03:12:34 +04:00
Yunus Shaikh 1b009e1879 fix(libc): subtract negative times in difftime on Windows
difftime and libc_difftime passed their arguments to the host CRT.
UCRT's _difftime64 returns 0 and sets errno to EINVAL when either
argument is negative (common_difftime in ucrt/time/difftime.cpp), so
on Windows difftime(0, -1) returned 0 instead of 1 and every
difference involving a time before 1970 was 0. C17 7.27.2.2 defines
the result as time1 - time0, and FreeBSD 11's difftime
(contrib/tzcode/stdtime/difftime.c) and glibc on the Linux build
compute it for any arguments.

Subtract the smaller time from the larger as uint64_t, which cannot
overflow, convert the difference to double and negate it when time1
is the smaller one, as FreeBSD and glibc's fallback path do. The
result is time1 - time0 rounded once to double on both hosts.
difftime now forwards to libc_difftime like the other aliases in the
file.
2026-10-11 16:12:25 -07:00
krishnaraya e5f943e3b3 fix(shader): keep the output modifier of the f16 min3, max3, med3 and div_fixup ops
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 19:10:15 -04:00
nyavana 67cdc9edcc docs(relinker): correct help and exit-code usage
Document --help output and status, distinguish no-argument errors, and include help in the successful exit code description.

AI-assisted: yes (OpenAI Codex).
2026-10-11 16:09:56 -07:00
Yunus Shaikh 7cc5698094 fix(libc): format years before 1900 in asctime on Windows
asctime passed the guest tm to the host CRT. UCRT's asctime returns
null and sets errno to EINVAL when tm_year is negative or tm_mday is
past the end of the month (common_asctime_s in ucrt/time/asctime.cpp),
so on Windows asctime of any time in the years 1000 to 1899, which
gmtime converts, or of February 29 in a common year returned null.
C17 7.27.3.1 defines the text for every year from 1000 to 9999 when
each member is in its normal range (tm_mday 1 to 31), and glibc on
the Linux build and FreeBSD 11's asctime
(contrib/tzcode/stdtime/asctime.c) print it.

Format the text with the C17 algorithm's format into a thread-local
buffer that fits any member values. As in glibc and FreeBSD, a
weekday or month outside its range is printed as ???, and the year is
computed in 64 bits.
2026-10-11 16:08:53 -07:00
Adrià bab7b32852
Merge pull request #3772 from MrMha/perf/relinker-call-index
perf(relinker): use a flat sorted call-site index
2026-10-12 01:05:45 +02:00
Yunus Shaikh 433bf1ebc2 fix(libc): map _Towctrans descriptors 1 and 2 to lower and upper case
_Towctrans passed the guest's descriptor to the host towctrans.
Dinkumware's <wctype.h> (xwcc.h) defines towlower(wc) as
_Towctrans(wc, 1) and towupper(wc) as _Towctrans(wc, 2), the values
FreeBSD's wctrans.c also uses for "tolower" and "toupper". UCRT reads
1 as toupper and other nonzero values as tolower, so on Windows
towlower(L'A') returned L'A' and towupper(L'a') returned L'a', while
towlower(L'a') returned L'A'. The host wint_t is 16 bits there, which
also turned WEOF into 0xffff. glibc's wctrans_t is a pointer to a
table that towctrans reads, so on Linux descriptor 1 or 2 is
dereferenced as an address.

_Towctrans now takes the guest's 32-bit wint_t and size_t descriptor.
1 and 2 map 0..255 through the C locale tables behind _Getptolower and
_Getptoupper and return other values unchanged. Any other descriptor
sets errno to EINVAL and returns wc unchanged, as FreeBSD's
towctrans_l does.
2026-10-11 16:05:26 -07:00
jweidenh 72d5b8ce90 fix(relinker): ignore no-op relocations in guest modules 2026-10-11 19:02:06 -04:00
Yunus Shaikh f7a85491ef fix(libSceNet): report EAGAIN when a datagram receive times out on Windows
With SO_RCVTIMEO set, Winsock fails a receive that waits that long with
WSAETIMEDOUT, which native_error turned into ETIMEDOUT (60). sceNetRecv,
sceNetRecvfrom and sceNetRecvmsg on a datagram socket therefore failed
with 0x8041013C on Windows, where FreeBSD's sbwait returns EWOULDBLOCK
(35) to soreceive, Linux returns EAGAIN, and POSIX (XSH 2.10.16)
requires EAGAIN or EWOULDBLOCK when no data was received.

A datagram socket has no connection that can time out, so WSAETIMEDOUT
there only means that the receive timeout the guest set has expired:
report EAGAIN. Stream sockets keep ETIMEDOUT, since Winsock leaves a
connection in an indeterminate state after a timeout; TechnicalDebt
records that gap and the one for SO_SNDTIMEO.
2026-10-11 16:01:51 -07:00
hugodearaujo 65b6a4729b fix(libkernel): accept only the solo scheduling values the console accepts
On a PS5 (firmware 13.60) scePthreadAttrSetsolosched and
pthread_attr_setsolosched_np accept only 0 and 16; every other value
returns ENOTSUP and keeps the stored one. The SCE set and get calls
return EINVAL for a null or uninitialized attribute instead of throwing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:01:44 -03:00
Gustavo Ariel Gamarra Rojas a70ec3946d feat(agc): store the dumped storage images at each flip
An image whose results stay on the GPU is never written back, so
APS5_DUMP_STORAGE saw nothing; APS5_STORE_AT_FLIP=1 stores every pending
image and ran a 4 GB device out of memory at 3840x2160. The selected
images alone are now stored at each flip.
2026-10-11 19:59:42 -03:00
Gustavo Ariel Gamarra Rojas f2d6986741 feat(agc): select storage dumps by shape and dump every layer
APS5_DUMP_STORAGE also takes <width>x<height>x<layers>[:<format>], for
images whose address changes from run to run (a title's 32x32x32
color-grading LUT), and the dump holds the first mip of every layer or
3D slice instead of the first one only.
2026-10-11 19:59:35 -03:00
Yunus Shaikh 0245ba7d88 fix(libc): open fopen streams with delete sharing on Windows
fopen opened its stream with _wfopen, which shares read and write
access but not delete access. While the guest held a stream open,
rename of its file failed with EIO and remove with EBUSY. POSIX rename
and remove work on open files, as on Linux and FreeBSD, and since
#3677 the descriptors sceKernelOpen returns share delete access.

fopen now creates the handle with CreateFileW sharing read, write and
delete, with the access and creation disposition of the mode, and
wraps it with _open_osfhandle and _wfdopen, keeping append mode. A
failed CreateFileW sets errno through the error mapping #3677 added to
libkernel's NativeOpen, which moves to libc as
WindowsOpenError_nid_no_patch so that both use it. freopen still
reopens through _wfreopen; TechnicalDebt records it.
2026-10-11 15:56:20 -07:00
pushkarreddyy 726e773417 feat(libkernel): support kern.sdk_version in sysctlbyname 2026-10-12 11:53:41 +13:00
Alex de54527a8b fix(shader): keep f32 denormals in compares when denormals are kept
With the f32 denormal field of FLOAT_MODE set to keep, v_cmp_X_f32 and
v_cmpx_X_f32 read a denormal operand as zero: NVIDIA, RADV and lavapipe
all flush denormals in their float compares. Compare the operands as
sign-magnitude integers instead, with NaN handled apart. Compares with
flushed denormals are unchanged. This matches RDNA2 (gfx1036) on 256
operand pairs per opcode, with f32 denormals kept and flushed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 18:51:37 -04:00
jweidenh ecd4e90628 fix(libc): preserve the guest ABI for array callbacks 2026-10-11 18:51:28 -04:00
Adrià 325e0a8bdd
Merge pull request #3741 from jweidenh/codex/optimized-pipeline-readback
test(agc): verify graphics output after optimized pipeline binding
2026-10-12 00:50:45 +02:00
Adrià 22cecd5aac
Merge pull request #3779 from pushkarreddyy/feat/libkernel-sysctl-pathname-hw-physmem
feat(libkernel): handle sysctl queries for pathname, physmem and cacheline size
2026-10-12 00:48:35 +02:00
hugodearaujo 7b5ac83a2a fix(libkernel): destroy rwlocks the way the console does
On a PS5 (firmware 13.60) destroying a rwlock returns 0 even while it
is held, and leaves 1 in the handle; every later call on that handle
returns EINVAL until it is initialized again. A zero handle is
initialized on first use by the scePthreadRwlock calls too. Here a held
rwlock returned EBUSY, a destroyed handle became null and was silently
re-created by the next POSIX call, and the scePthreadRwlock calls threw
on a zero handle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:48:33 -03:00
kallmek3nny 5fd0e261c9 fix(aio): use precise polling sleep 2026-10-11 18:48:18 -04:00
hugodearaujo 3ba4751f98 fix(libkernel): report the console's scheduling priority range
On a PS5 (firmware 13.60) sched_get_priority_max returns 767 and
sched_get_priority_min 256 for SCHED_FIFO and SCHED_RR; the two were
swapped. Every other policy, SCHED_OTHER included, returns -1 with
EINVAL instead of a range or a host exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:47:18 -03:00
ethan 0a324d8ccb fix(relinker): resolve .suprx guest module aliases 2026-10-11 15:45:27 -07:00
Adrià ba08a1582a
Merge pull request #3876 from CnRJay/feat/savedata-convert
feat(savedata): implement sceSaveDataConvert
2026-10-12 00:43:58 +02:00
Adrià f9905068b7
Merge pull request #3730 from MarcZX100/codex/voiceqos-end-3331
feat(libSceVoiceQoS): implement sceVoiceQoSEnd
2026-10-12 00:42:41 +02:00
Adrià 6dbc0ea66a
Merge pull request #3829 from YossiYad/carry/agc-vieBRwlh1Lw
feat(libSceAgc): implement the vieBRwlh1Lw interpolant mapping update
2026-10-12 00:39:58 +02:00
Kikoarl 567898cd58 feat(libSceSysmodule): register libSceVoiceChat module ID (0x114) 2026-10-12 00:39:28 +02:00
Adrià 3905e7e2c9
Merge pull request #3827 from CouchRiv/fix-shader-combined-cvt-i16-f16-omod
fix(shader): accept output modifiers on v_cvt_i16_f16 and ignore them as the hardware does
2026-10-12 00:39:20 +02:00
PrintOrb 5c19601a12 fix(videoout): require bundled SDL XRandR support on Linux 2026-10-11 17:36:12 -05:00
Adrià 2c884140a5
Merge pull request #3721 from MarcZX100/codex/content-export-sessions-3374
feat(libSceContentExport): implement export sessions
2026-10-12 00:35:54 +02:00
Adrià 6426baa107
Merge pull request #3691 from MarcZX100/fix/sem-timedwait-available-first
fix(libkernel): handle available sem_timedwait counts first
2026-10-12 00:35:17 +02:00
Claude 36c78ba143
fix(relinker): report the ModRM of 3DNow! instructions in decoded instruction info
DecodeInstruction did not treat 0F 0F as having a ModRM byte, so a
RIP-relative 3DNow! operand was not reported to the analyses and the
--to-intel stub relocation that read HasRipRelativeDisp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-11 22:34:39 +00:00
Mouhand Kaddo ac4954c6e3 fix(libkernel): guard Unix host polling in Windows select 2026-10-12 02:34:11 +04:00
xXPBSXx 859d0b1e63 fix(exports): also export sceHmd2GetFieldOfView
main has sceHmd2GetFieldOfViewWithoutHandle only; PPSA07411 imports sceHmd2GetFieldOfView.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 00:33:41 +02:00
Adrià dd1cf5cb9d
Merge pull request #3771 from yunusshaikh7/fix/savedata-dir-name-search-sort
fix(savedata): sort sceSaveDataDirNameSearch results by the requested key and order
2026-10-12 00:30:16 +02:00
Adrià ae7dbfa9cb
Merge pull request #3770 from A-Akhil/fix/shader-sin-cos-series
fix(shader): take v_sin_f32 and v_cos_f32 from an exact series where the driver's result is more than 4 ulp off
2026-10-12 00:29:31 +02:00
Adrià e8169f49b3
Merge pull request #3756 from Lorkydey/feat/agc-acb-atomic-gds
feat(libSceAgc): build versioned ACB atomic GDS commands
2026-10-12 00:28:45 +02:00
eljackjoestar-cmd 9a0c59570c feat(ajm): decode Opus surround mapping family 2026-10-12 02:28:14 +04:00
Adrià f9e1d2541c
Merge pull request #3750 from fma-rc/feat/shader-subvector-loop
feat(shader): implement s_subvector_loop_begin and s_subvector_loop_end
2026-10-12 00:28:10 +02:00
Simon 233590462c fix(libSceNgs2): report EMPTY for a streaming sampler that played its queue
A playing sampler voice whose waveform still accepts blocks (CONTINUE)
reported only INUSE|PLAYING after it had played every queued block, so
a title that waits for EMPTY before it reuses the voice never did.
PPSA18391 (engine: Clickteam Fusion) reads state_flags & 0x22 != 2 from
sceNgs2VoiceGetState as the end of a sound; each of its sound effects
played once and never again.

The voice now also reports EMPTY (0x20) in that state and keeps
playing, so blocks added later still play. A waveform that was closed
without CONTINUE does not report it.
2026-10-12 00:26:12 +02:00
ethan 409f53eaed fix(relinker): filter non-reference RIP-relative FF operations 2026-10-11 15:24:53 -07:00
Adrià 5aaf26bd32
Merge pull request #3689 from MarcZX100/codex/libkernel-spin-reltimedwait-3554
feat(libkernel): implement pthread spinlocks and relative condition wait
2026-10-12 00:23:53 +02:00
Adrià d2fa71b706
Merge pull request #3682 from zmeizy/claude/kernel-fsync-error-codes
fix(libkernel): return SCE error codes from sceKernelFsync
2026-10-12 00:23:21 +02:00
Hussein Kadhim 351ca3bb12 fix(libkernel): return EDEADLK for a thread joining itself and EINVAL for a null thread
scePthreadJoin threw when a joinable thread joined itself and
scePthreadJoin and scePthreadDetach threw on a null thread, so
pthread_join and pthread_detach ended the process where FreeBSD libthr
returns EDEADLK and EINVAL.
2026-10-11 22:23:16 +00:00
Adrià c059590d8e
Merge pull request #3598 from Recapi/feat/libc-asinl
feat(libc): implement the remaining long double math functions RetroArch imports
2026-10-12 00:22:48 +02:00
Simon 43573fd48a fix(libSceVideoOut): return INVALID_HANDLE for events on ports that are not open
Registering a flip or vblank event on a handle that sceVideoOutOpen never
created, or that was closed, threw, which ended titles that register
their flip event before they open the port and ignore the result. The
documented error SCE_VIDEO_OUT_ERROR_INVALID_HANDLE is returned instead,
the same check sceVideoOutSetFlipRate and the other output calls use.

The flip test program gains an "unopened" mode.
2026-10-12 00:21:47 +02:00
Claude feb19a844d
fix(relinker): decode 3DNow! instructions with their ModRM and opcode suffix
0F 0F was decoded as a two-byte instruction. 3DNow! instructions are
0F 0F ModRM [SIB] [displacement] imm8, the trailing byte selecting the
operation (AMD64 Architecture Programmer's Manual Volume 3, Appendix A,
3DNow! opcodes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VZWLdTjf1S57iLgn2rcaFb
2026-10-11 22:21:32 +00:00
Dean fabbeb393b
Merge pull request #3522 from pushkarreddyy/feat/libc-reallocf
feat(libc): implement reallocf
2026-10-12 11:21:30 +13:00
PrintOrb 665cca43a0
Merge branch 'main' into fix/videoout-high-frame-rate 2026-10-11 17:21:16 -05:00
Adrià 8fc53cb325
Merge pull request #3853 from billythemenace2/feat/rudp-ppsa06888-imports
feat(libSceRudp): declare the calls PPSA06888 imports
2026-10-12 00:20:03 +02:00
Dean Galvin 1dc342dd57 perf(agc): keep the highest index of a large in-place index buffer while it is unchanged
An indexed draw that binds its index buffer in place from a host import
(#2253) scans the bound range for its highest index on every draw, where
the copied path reuses the value kept with its snapshot. A draw of many
indices pays for the scan each time, and an indirect draw, which binds
the whole declared index buffer, scans the whole buffer per draw.

The recorder now keeps the highest index of an in-place index range of
4096 indices or more with the write generation collected before the
scan, and a draw takes it while the range is unchanged since
(GuestMemory::UnchangedSince and the allocation registry generation, as
for a reused snapshot). A smaller range is scanned at every draw as
before: the scan costs 0.4 ns per index and the write collect and lookup
0.2 to 1.4 us per draw, so the scan is the cheaper of the two below
about 3,000 indices, as #2253 measured on small ranges.
2026-10-12 11:19:50 +13:00
Adrià Franch 8c00fe871e docs(TechnicalDebt): put the InitParameter2 line where the old one was, not at the end of the file
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 22:19:14 +00:00
MarcZX100 d004d11fa3 fix(libSceJson2): use signed special-float enum type 2026-10-11 22:19:14 +00:00
David 1d5f5eb6c8 feat(libSceJson2): implement the InitParameter2 constructors and setSpecialFloatFormatType
Resident Evil 4 (PPSA07411, #2776) and PRAGMATA (PPSA02530, #3043) stop
on the absent export with

  what():  _ZN3sce4Json14InitParameter225setSpecialFloatFormatTypeENS0_22SpecialFloatFormatTypeE not implemented

Deathloop, Minecraft Legends and Cyberpunk import the base-object
constructor InitParameter2::InitParameter2(), InitParameterRtti2::
setAllocatorRtti and InitParameterRtti::InitParameterRtti(AllocParamRtti*,
void*, unsigned long), which were NotImplemented stubs.

InitParameter2 is the 40-byte object of SharpEmu and KytyPS5: the
allocator, its user data, the file buffer size and the 32-bit special
float format type at 0x18. Both constructors zero it, setAllocatorRtti
writes the allocation parameters and user data where setAllocator writes
the allocator, and setSpecialFloatFormatType stores the type.
InitParameterRtti's constructor writes its three arguments to the first
24 bytes. The NID WVZBP4IyM+E recomputes from the mangled name and is in
the aerolib database. The enum's values are unknown, so no value is
rejected; the inferred layouts are recorded in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 258f2910808e5ef1636973aaefbdf51615873dcb)
2026-10-11 22:19:14 +00:00
pushkarreddyy 931aaa2a7e feat(libkernel): handle sysctl queries for pathname, physmem and cacheline size 2026-10-12 00:17:14 +02:00
milescoviello 972caa4f2a fix(libkernel): number pthread keys from 1 as FreeBSD libthr does
scePthreadKeyCreate and pthread_key_create handed out the slot index,
so the first key in a process was 0. FreeBSD libthr returns slot + 1
(_pthread_key_create: "*key = i + 1") and subtracts 1 again in
pthread_key_delete, pthread_getspecific and pthread_setspecific, so 0 is
never a valid key. Code that keeps 0 as "key not created yet" therefore
created a new key on every call and lost the values it stored under the
previous one.

Keys now start at 1; 0 and other out-of-range keys are rejected with
EINVAL by delete and set and read as NULL, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYhZu5HokHMo9mfi8QU83m
2026-10-11 22:16:59 +00:00
jweidenh 2b8f151b3f test(agc): verify optimized pipeline draws and readback 2026-10-12 00:16:28 +02:00
Alex c63294027b fix(libc): exp2 leaves errno unchanged
exp2 set errno (34) for 1024.0. FreeBSD msun never sets errno and
reports errors through floating-point exceptions only, so errno is now
saved and restored around the host call.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 18:15:15 -04:00
Adrià 43bc17f7ed
Merge pull request #3727 from mariusyo07/fix/pad-vibration-mode-3
fix(pad): accept vibration mode 3
2026-10-12 00:14:30 +02:00
Adrià 24865e8e22
Merge pull request #3887 from AchrafReyani/fix/libc-windows-backtrace-stack-candidates
fix(libc): list stack return address candidates in Windows backtrace reports
2026-10-12 00:13:59 +02:00
Adrià 11d9c00cf6
Merge pull request #3884 from hansjm10/fix/sysmodule-agc-internal-id
fix(libSceSysmodule): recognize the libSceAgc internal module id
2026-10-12 00:13:05 +02:00
Adrià c8ad983fd4
Merge pull request #3871 from fdfawef324/fix/libc-log-line-one-write
fix(libc): write each log line in one call
2026-10-12 00:12:34 +02:00
Adrià f803854a5c
Merge pull request #3670 from SibteProf/fix/json2-initparameter2-base-constructor
fix(json2): implement InitParameter2 base constructor
2026-10-12 00:12:05 +02:00
Niko 5731e02841 fix(libkernel): close guest sockets in sceKernelClose
sceKernelClose passed every descriptor to the host close. Guest socket
descriptors start at 0x10000000 and are not host descriptors, so closing
an open socket returned SCE_KERNEL_ERROR_EBADF and left it in the guest
socket table with its host socket open. Route them to GuestSockets::Close
as close, sceKernelRead and sceKernelWrite already do; a closed socket
still returns SCE_KERNEL_ERROR_EBADF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 00:11:53 +02:00
Adrià af0b52ebb4
Merge pull request #3592 from MarcZX100/codex/socket-errno-errors-3573
fix(libkernel): map host socket errors to guest errno
2026-10-12 00:11:40 +02:00
Adrià b18d4feddd
Merge pull request #3580 from Amr-Youssef/feat/libkernel-sigwait
feat(libkernel): export pause, sigwait and sigwaitinfo
2026-10-12 00:11:14 +02:00
MrMha 3687b2e214 perf(relinker): use a flat sorted call-site index 2026-10-12 00:11:00 +02:00
Adrià 33067ca0c7
Merge pull request #3856 from ErenYeager1836/carry/recompiler-vector-window
perf(recompiler): keep relatively addressed vector registers in a window array
2026-10-12 00:10:58 +02:00
Adrià 59584d58b1
Merge pull request #3858 from LeandroLP23/fix/libc-internal-force-tls-destructor-windows-test
test(libSceLibcInternal): run guest_force_tls_destructor on Windows
2026-10-12 00:10:39 +02:00
Adrià 362a1f4e96
Merge pull request #3857 from gookie-dev/fix/libscexml-for-titles-that-list-it
fix(libSceXml): add an export-free libSceXml for titles that list it
2026-10-12 00:10:31 +02:00
Adrià 825cbd4106
Merge pull request #3531 from karanvora2599/perf/shader-native-packed-f16
perf(shader): compute v_pk_add_f16 and v_pk_mul_f16 with native f16 math
2026-10-12 00:10:02 +02:00
Adrià 10afe59c7d
Merge pull request #3851 from itsbo5/feat/libc-drand48-lrand48-mrand48-srand48
feat(libc): implement drand48, lrand48, mrand48 and srand48
2026-10-12 00:09:44 +02:00
Adrià 7667f53232
Merge pull request #3845 from semirkabir/feat/umtx-nwake-private
feat(libkernel): wake each listed address in _umtx_op UMTX_OP_NWAKE_PRIVATE
2026-10-12 00:09:31 +02:00
Adrià 93ba44d750
Merge pull request #3847 from semirkabir/feat/usbd-device-calls
feat(libSceUsbd): declare the device and handle calls for the no-device path
2026-10-12 00:09:29 +02:00
Adrià a93947040c
Merge pull request #3836 from nyavana/docs/libs-build-testing-refresh-20261011
docs(build): clarify when explicit libs target is required
2026-10-12 00:09:13 +02:00
Adrià 972b45b807
Merge pull request #3835 from queeeef/fix/agc-rect-list-partial-primitive
fix(agc): draw the complete rectangles of a rect-list draw with a partial last one
2026-10-12 00:09:05 +02:00
Adrià 7e4074fcb9
Merge pull request #3838 from gookie-dev/fix/pad-special-port-not-connected
fix(pad): report special ports as not connected in scePadOpen
2026-10-12 00:09:03 +02:00
Adrià c7731cf4d4
Merge pull request #3843 from kr4y4/fix/agc-aniso-override-force-degamma
fix(agc): keep FORCE_DEGAMMA paired when ANISO_OVERRIDE rebuilds a sampler
2026-10-12 00:09:01 +02:00
Adrià 31bf997b53
Merge pull request #3833 from David-cmrg/test/agc-gated-dispatch-short-spins
test(agc): split the gated dispatch producer spin into short dispatches
2026-10-12 00:08:18 +02:00
Kikoarl 7cf2ee844a test(libSceSysmodule): verify handle and symbol resolution for libSceAudioPropagation 2026-10-12 00:08:15 +02:00
Adrià 6407626dd1
Merge pull request #3830 from winstrex/fix/agc-flat-apertures-kernel-parts
test(agc): split agc_driver_flat_apertures by shader instead of by row
2026-10-12 00:08:14 +02:00
Aryan singh 7c1b64b0ca fix(relinker): share module suffixes between identity and import matching
The guest identity predicate accepted .debug_prx while Domain::ImportModule
did not: a dependency named libGuest.debug_prx normalised to
libGuest_debug_prx, so a qualified import declared in module libGuest was
left unscoped. When another guest module exported the same symbol, the
conversion failed with an ambiguous guest import, while .prx, .sprx and
.suprx resolved to the declaring module.

Module suffixes are now one list in the domain layer, used by the identity
predicate, import module normalisation and the default .prx suffix.
Needed-name stem aliasing keeps its own list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:38:07 +05:30
Aryan singh d3fc14a50c fix(relinker): resolve debug PRX dependencies by module identity 2026-10-12 03:38:07 +05:30
Adrià e53c30bcdf
Merge pull request #3828 from CouchRiv/fix-shader-combined-pknorm-f16
fix(shader): accept abs, neg and op_sel source modifiers on v_cvt_pknorm_i16_f16 and v_cvt_pknorm_u16_f16
2026-10-12 00:07:49 +02:00
Adrià 4b355d4e68
Merge pull request #3825 from kr4y4/fix/shader-dpp8-pk-fmac-f16
fix(shader): accept DPP8 on v_pk_fmac_f16
2026-10-12 00:07:47 +02:00
Adrià 01b7bb68be
Merge pull request #3823 from xXPBSXx/fix/rudp-exports-ppsa03490-v3
fix(rudp): provide the calls PPSA03490 still lacks
2026-10-12 00:07:39 +02:00
Adrià 1a7b96b5bc
Merge pull request #3822 from object05/test/f32-denormal-flush-split-fma
test(shader): accept the split fma sum in F32DenormalFlush
2026-10-12 00:06:56 +02:00
Adrià 5317eeb54e
Merge pull request #3817 from omurc4/feat/videoout-add-buffer-attribute-option
feat(libSceVideoOut): export sceVideoOutAddBufferAttributeOption
2026-10-12 00:06:21 +02:00
Adrià 2c8122c9f2
Merge pull request #3813 from jweidenh/codex/storage-upload-lifetime
fix(agc): retain storage textures during initial uploads
2026-10-12 00:05:54 +02:00
Adrià 12d684f4e6
Merge pull request #3807 from AchrafReyani/fix/shader-srt-signed-less-than-uniform
fix(shader): accept signed less-than in runtime SRT values
2026-10-12 00:05:43 +02:00
Adrià ed90d97346
Merge pull request #3810 from MiniRML/pr/videoout-test-driver
test(videoout): make SDL video driver configurable
2026-10-12 00:05:41 +02:00
Adrià 66e87807d5
Merge pull request #3804 from MarcZX100/codex/relinker-trap-sdk-syscalls-2136
feat(relinker): trap exact SDK syscall stubs on request
2026-10-12 00:05:38 +02:00
Adrià 4a8efe0699
Merge pull request #3808 from MarcZX100/codex/apr-command-size-validation-1930
fix(libkernel): validate APR command payload sizes
2026-10-12 00:05:34 +02:00
Adrià 5130c66b88
Merge pull request #3794 from semirkabir/feat/cpuset-getaffinity-process
feat(libkernel): report the process affinity from cpuset_getaffinity
2026-10-12 00:04:42 +02:00
hamb3r b8a1cbe42b fix(libc): add missing Unity engine static imports 2026-10-12 01:04:39 +03:00
Adrià e10f91b305
Merge pull request #3777 from MarcZX100/codex/agc-store-idle-3272
fix(agc): store pending storage-image results when queues idle
2026-10-12 00:04:39 +02:00
Adrià 0694c57998
Merge pull request #3693 from CouchRiv/fix-libc-asinf-errno
fix(libc): match FreeBSD asinf errno
2026-10-12 00:03:54 +02:00
Adrià acc5d29498
Merge pull request #3673 from MrMha/fix/stub-output-current
fix(stubgen): protect input files and keep stdout valid C++
2026-10-12 00:03:02 +02:00
Adrià dc70a66cbd
Merge pull request #3788 from xXPBSXx/fix/libkernel-mprotect-host-memory-v3
fix(libkernel): allow sceKernelMprotect on committed host memory outside the guest registry
2026-10-12 00:01:51 +02:00
Adrià 3929c21fb9
Merge pull request #3782 from MrMha/fix/rewrite-instruction-boundaries
fix(codegen): reject rewrites inside instructions
2026-10-12 00:01:46 +02:00
Adrià 949cd55471
Merge pull request #3785 from MrMha/fix/shader-cache-read-allocation
fix(shader): handle cache file allocation failures
2026-10-12 00:01:43 +02:00
Adrià 90933951d9
Merge pull request #419 from MichonGoddijn231849/feat/msaa-targets
feat(agc): multisampled color and depth targets with CB resolves
2026-10-12 00:01:37 +02:00
Mouhand Kaddo 82ac3bd93e fix(agc): skip read-only depth resummarize draws 2026-10-12 02:01:36 +04:00
Adrià 6566c9c8c6
Merge pull request #3784 from MrMha/fix/param-json-utf8
fix(libkernel): reject malformed UTF-8 in param JSON
2026-10-12 00:01:32 +02:00
David 6ce1240aba feat(libSceContentExport): implement the export session calls
sceContentExportStart hands out positive session ids, at most 10 live
(SharpProspero MaxExecution), and sceContentExportFinish closes them.
sceContentExportGetProgress, sceContentExportCancel and
sceContentExportFromDataWithThumbnail take their Orbital signatures and
check the session. The host has no media library to export into, so
FromDataWithThumbnail throws before it calls either data callback, and
GetProgress and Cancel throw for a live session since no export ever
runs and their result for an idle session is unknown. An eleventh Start
and Term with live sessions throw.

Titles: Cult of the Lamb PPSA06465 imports GetProgress, Cancel and
FromDataWithThumbnail (#2686); PPSA21564 (#271) and Cyberpunk 2077
PPSA04029 (#266) import Start.

Sources: Orbital tools/ida/db_types.json (cc408367) for the signatures,
SharpProspero Interop/Content/ContentExport.cs (5dfdb4b0) for the
session model and MaxExecution, shadPS4 content_export.h (443f0224) for
the parameter and callback layouts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 473d6dd355d2c0ee22f9792927e0e465846569cf)
2026-10-12 00:01:17 +02:00
Hannah Lyons c154b8bff3 feat(libkernel_sys): implement openat 2026-10-11 18:01:11 -04:00
Adrià 974116d38b
Merge pull request #3783 from jweidenh/codex/windows-partial-write-tracking
fix(libc): preserve writes during partial shared-memory collection
2026-10-12 00:01:08 +02:00
xXPBSXx 1b7e842b9c fix(exports): export the PS VR2 calls PPSA07411 imports that no library has
PPSA07411 (Resident Evil 4) fails to load without these imports. Their signatures are unknown, so they are declared without parameters and throw until they are implemented. Adds a TechnicalDebt entry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 00:01:07 +02:00
David 5164f7875a feat(libSceVoiceQoS): implement sceVoiceQoSEnd
End clears the init flag set by sceVoiceQoSInit and returns 0, so the
library can be initialized again. Before an initialization it returns
0x804E0901 (LIBVOICEQOS_NOT_INIT), as measured on a PS5 (firmware 13.60)
in #1407.

LEGO Horizon Adventures (PPSA14632) calls it at shutdown and Ghost of
Yotei (PPSA26344) calls it on its sceVoiceQoSInit failure branch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 25fbc944faddc5fb3519c4ce4b8d3600b3bc9e1b)
2026-10-12 00:01:03 +02:00
Adrià bb48d60fc6
Merge pull request #3774 from MrMha/fix/program-layout-ranges
fix(relinker): validate program layout ranges before writing
2026-10-12 00:00:46 +02:00
Adrià 7646a3f3ba
Merge pull request #3775 from eliyarson/docs/doom-linux-nvidia
docs: record DOOM homebrew on Linux with an NVIDIA GPU
2026-10-12 00:00:42 +02:00
Adrià 7ee8992714
Merge pull request #3776 from MrMha/fix/section-header-ranges
fix(relinker): validate complete allocated section ranges
2026-10-12 00:00:39 +02:00
Adrià 51e2c511e7
Merge pull request #3773 from xXPBSXx/fix/export-ppsa07411-imports-v4
fix(exports): export the calls PPSA07411 imports
2026-10-11 23:59:49 +02:00
Adrià 5acbcd64ba
Merge pull request #3768 from MarcZX100/codex/docs-dozen-workaround-3255
docs: document Windows Dozen driver workaround
2026-10-11 23:59:39 +02:00
Adrià f60fe21bc3
Merge pull request #3761 from semirkabir/feat/fiber-underscore-frame-pointer
feat(libSceFiber): add the underscore thread frame pointer export
2026-10-11 23:58:58 +02:00
Adrià 34ceeb05d0
Merge pull request #3760 from semirkabir/feat/ult-underscore-exports
feat(libSceUlt): add underscore mutex and queue create exports
2026-10-11 23:58:47 +02:00
Adrià d4938e1534
Merge pull request #3753 from jweidenh/codex/pthread-destructor-lifetime
fix(libkernel): run pthread key destructors before releasing thread state
2026-10-11 23:58:33 +02:00
Adrià c7b9719951
Merge pull request #3744 from ArekatlaNishanthchowdary/fix/pthread-rwlock-unlock-unowned
fix(libkernel): return EPERM when unlocking an rwlock that is not held
2026-10-11 23:57:59 +02:00
Adrià 9f0618548d
Merge pull request #3667 from MrMha/fix/convention-paths-current
fix(conventions): handle unusual Git paths and escaped links
2026-10-11 23:57:27 +02:00
Adrià b997cecfec
Merge pull request #3740 from jagrat04/feat/libSceAcm-fft-panner
feat(libSceAcm): implement sceAcm_FFT and sceAcm_Panner
2026-10-11 23:57:24 +02:00
Adrià df64fa9382
Merge pull request #3738 from Mouad-Scriptz-0/feat/agc-merged-vertex-tables
fix(agc): read a merged program's vertex tables past its hidden user words
2026-10-11 23:56:46 +02:00
Adrià 2b4b8178f9
Merge pull request #3603 from itsbo5/feat/libSceNpEntitlementAccess-offline-requests
feat(libSceNpEntitlementAccess): implement offline entitlement info r…
2026-10-11 23:56:38 +02:00
Adrià 72e67c0e50
Merge pull request #3555 from lisn0/feat/linux-named-shared-memory
feat(libkernel): support named shared memory on Linux
2026-10-11 23:55:30 +02:00
Adrià b6555c8454
Merge pull request #3714 from MarcZX100/codex/libkernel-aio-set-param-3708
feat(libkernel): implement sceKernelAioSetParam
2026-10-11 23:55:27 +02:00
Adrià c4d3b3a743
Merge pull request #3726 from NFSS10/perf/srt-plan-use-replacement
perf(shader): avoid copying SRT read uses
2026-10-11 23:55:24 +02:00
Adrià 32f6414314
Merge pull request #3713 from SleglJan/fix/libkernel-flexible-module-charge
fix(libkernel): count guest module segments against the flexible memory budget
2026-10-11 23:54:59 +02:00
Adrià 06664a44cd
Merge pull request #3717 from jweidenh/codex/nvidia-pipeline-replay
feat(agc): capture and replay compute pipeline compilation
2026-10-11 23:54:52 +02:00
Adrià 4a49ed06f7
Merge pull request #3712 from MarcZX100/codex/test-creat-errors-3303
test(libkernel): cover creat error cases
2026-10-11 23:54:51 +02:00
MarcZX100 8935160160 fix(libkernel): handle available sem_timedwait counts first
Co-authored-by: milescoviello <kitslayer29@gmail.com>
2026-10-11 23:54:18 +02:00
Adrià 1be2ca2028
Merge pull request #3706 from MarcZX100/codex/http-abort-wait-3156
feat(libSceHttp): implement abortable epoll waits
2026-10-11 23:53:59 +02:00
Adrià 0cff844842
Merge pull request #3707 from overlordxrz-source/feat/net-in6addr-linklocal-allnodes
feat(libSceNet): export sce_net_in6addr_linklocal_allnodes
2026-10-11 23:53:51 +02:00
David 774f21845d feat(libkernel): implement pthread_cond_reltimedwait_np
Add pthread_cond_reltimedwait_np (K953PF5u6Pc; computed with
tools/nid_names.py and matching aerolib.csv) as scePthreadCondTimedwait
with POSIX error numbers: ETIMEDOUT when the relative timeout expires,
EPERM when the caller does not hold the mutex, and the mutex held again on
return in every waiting case.

Titles: Porpoise PPSA99764 and RetroArch PPSA99169 import it.
Predicted from relink registry order, not observed: Porpoise stops with
"FAIL: unresolved ELF import K953PF5u6Pc from libkernel.prx" (registry
index 384; RetroArch index 490). No call site is resolved in the registry.

The third argument is a relative timeout in microseconds, not a
struct timespec. FreeBSD 11.4 libthr (8c639945a44588f6cfdddfb057bb5aad88735da0)
has no such function. shadPS4 (3a9f57265d28975658f1ca941daa5c879c7a74bd,
src/core/libraries/kernel/threads/condvar.cpp:229-234, 445, 467-468)
registers posix_pthread_cond_reltimedwait_np(cond, mutex, u64 usec) for this
NID and the same function for scePthreadCondTimedwait (BmMjYxmew1w), whose
microsecond argument this repository already implements. The width follows
the SCE twin's 32-bit SceKernelUseconds, as sem_reltimedwait_np does, not
shadPS4's u64. There is no timespec to validate, so no EINVAL case.
Recorded in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit c86ec8417d12f5b12136b0f06eae06493b0e7a62)
2026-10-11 23:53:37 +02:00
David 092f7ef00a feat(libkernel): implement the pthread spinlock calls
Add pthread_spin_init (ZMn3clnAGBA), pthread_spin_destroy (IJIggoPZExk),
pthread_spin_lock (pw+70ClLYlY), pthread_spin_trylock (rCTGkBIHfPY) and
pthread_spin_unlock (LEfMMCT+SlM). The NIDs were computed with
tools/nid_names.py and match aerolib.csv. libkernel has no scePthreadSpin*
twin in aerolib, so the calls are implemented directly, like the barrier.

Titles: Porpoise PPSA99764 and RetroArch PPSA99169 import pthread_spin_lock.
Predicted from relink registry order, not observed: after
pthread_cond_reltimedwait_np, Porpoise stops with
"FAIL: unresolved ELF import pw+70ClLYlY from libkernel.prx" (registry
index 387; RetroArch index 493). No call site is resolved in the registry.

Behaviour follows FreeBSD 11.4 libthr thr_pspinlock.c and thr_umtx.h
(8c639945a44588f6cfdddfb057bb5aad88735da0). The guest pthread_spinlock_t
holds a pointer to an allocated lock. EINVAL for a null or destroyed lock and
for a pshared other than PTHREAD_PROCESS_PRIVATE or PTHREAD_PROCESS_SHARED,
EBUSY from trylock while any thread holds the lock, EPERM from unlock by a
thread that does not hold it. libthr spins forever when the holder locks
again; that call throws here. A shared lock is allocated like a private one,
since guest code runs in one process. Recorded in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 13ebf7f926fa2ee1c825cb1fcf18451a8781c045)
2026-10-11 23:53:36 +02:00
Adrià 4989809b13
Merge pull request #3705 from MarcZX100/fix/asan-guest-arena-window-3634
feat(build): support Linux AddressSanitizer builds
2026-10-11 23:53:19 +02:00
Adrià c8536ab100
Merge pull request #3703 from utkuhalis/fix/macos-write-watch-fresh
fix(libc): track never-collected pages in the macOS write watch
2026-10-11 23:53:08 +02:00
Claude db49377bef fix(libkernel): return SCE error codes from sceKernelFsync
sceKernelFsync returned the host fsync or _commit result, so a failure
returned -1 instead of an SCE error code, and a guest socket descriptor
reached the host call. fsync then set the guest errno from the host
errno rather than from that result.

Handle sockets and return SCE error codes as sceKernelFdatasync does
(EINVAL for an open socket, EBADF for a closed one or an unknown
descriptor), and have fsync take the errno from sceKernelFsync's result
like the other POSIX wrappers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 23:53:00 +02:00
Adrià 9ce99f8f4d
Merge pull request #3697 from JAB-dev/fix/font-writing-letter-step
fix(libSceFont): return the letter step of sceFontWritingRefersRenderStepCharacter through a pointer
2026-10-11 23:52:50 +02:00
Adrià 4bde70b4e2
Merge pull request #3702 from oneandonlydean/up4/depth-tests-without-planes
fix(agc): leave the depth surface out when no enabled test has its plane
2026-10-11 23:52:46 +02:00
Adrià 6fa58ec0ac
Merge pull request #3696 from JAB-dev/feat/font-syllable-string-state
feat(libSceFont): implement sceFontCharacterGetSyllableStringState
2026-10-11 23:52:45 +02:00
David 059e1319d1 feat(libkernel): export pause, sigwait and sigwaitinfo
Exports pause (EDyhWelj7UY), sigwait (mrbHXqK8wkg) and sigwaitinfo
(Uq5BGthgbl4) from libkernel. NIDs computed with tools/nid_names.py and
matched in aerolib; ps5-payload-dev/sdk f5d40f02 sce_stubs/libkernel.c
places all three in libkernel.

They throw like the merged sigtimedwait (#3430), with the same message
pattern: "sigwait: waiting for a pending signal", "sigwaitinfo: waiting
for a pending signal" and "pause: waiting for a delivered signal". In
FreeBSD 11.4 (8c639945) sigwaitinfo is kern_sigtimedwait without a
timeout (sys/kern/kern_sig.c:1212), sigwait is the same wait returning
the error number (kern_sig.c:1150, lib/libthr/thread/thr_sig.c:676-728)
and pause is sigsuspend of the current mask (lib/libc/gen/pause.c:46-56).
AnyPS5 never leaves a guest signal pending (blocked signals are dropped
in Dispatch, raise delivers at once), so there is nothing to take, and
raise, kill and pthread_kill run a handler only on the calling thread,
so nothing can end a pause.

Titles (predicted stops, registry order; not observed runs):
- Ruffle PPSA68091, observed today
  "FAIL: unresolved ELF import 1t1-JoZ0sZQ from libSceLibcInternal.prx"
  (sinhf, #3519), predicted next
  "FAIL: unresolved ELF import EDyhWelj7UY from libkernel.prx"
  (pause, call site 0x8F7266).
- RetroArch PPSA99169: pause (0x30DB1, an address table entry).
- PS5MC PPSA99420: pause (0x3BD80F6, 0x3C9E220), sigwait (0x3C9E4C7),
  sigwaitinfo (0x3C9E588).
- XPSemu PPSA97358: sigwait (0xE02AEC, 0x1101294, 0x1101368), QEMU's
  dummy vCPU thread and sigwait_compat thread.

guest_sigtimedwait now also checks the three throws, with an empty set,
a null info, a SIGINT raised while blocked, and that neither the output
nor the blocked mask changes. TechnicalDebt records the call sites.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 23:52:33 +02:00
Adrià ca4ab0d5d6
Merge pull request #3687 from Lorkydey/fix/libc-atoi-guest-width
fix(libc): parse atoi through guest-width strtol
2026-10-11 23:51:52 +02:00
SibteProf eaa9ce97ca test(json2): isolate base constructor regression from float format changes 2026-10-11 23:51:46 +02:00
Codex 3d8db7b5a8 fix(json2): implement InitParameter2 base constructor
Carry the implementation and regression from lucfx18's PR #2823, retaining
the original Git author. Extend the regression with a leading guard and
typed default-field checks, and record the unverified console layout.

Source: https://github.com/boykopovar/AnyPS5/pull/2823
2026-10-11 23:51:34 +02:00
Adrià 153a0a58d6
Merge pull request #3588 from tv7/fix/apr-async-queue
fix(libkernel): run APR buffers past an unsatisfied wait on their own queue
2026-10-11 23:51:13 +02:00
Adrià 4658391b1f
Merge pull request #3681 from zmeizy/claude/kernel-file-errno-translation
fix(libkernel): translate host errno in the file functions' SCE errors
2026-10-11 23:51:05 +02:00
Adrià 53b928cb04
Merge pull request #3686 from zmeizy/claude/truncate-test-root
test(libkernel): skip the read-only truncate cases when running as root
2026-10-11 23:50:44 +02:00
Adrià 562149d750
Merge pull request #3685 from zmeizy/claude/kernel-sys-errno-translation
fix(libkernel_sys): translate host errors to guest errno numbers
2026-10-11 23:50:40 +02:00
Adrià dba34804ec
Merge pull request #3684 from zmeizy/claude/libc-prx-keep-needed
fix(build): keep libc.prx needed by every executable that links a prx
2026-10-11 23:50:32 +02:00
Adrià 05c2509c61
Merge pull request #3678 from Ondra97/codex/relinker-desktop-input-preservation
fix(relinker): preserve inputs when writing desktop assets
2026-10-11 23:49:58 +02:00
Adrià Franch 7663d8479c docs(technical-debt): note glibc errno for logb and remainder 2026-10-11 23:49:39 +02:00
Renan Pires a36b9092d1 feat(libc): implement the long double math functions RetroArch imports 2026-10-11 23:49:38 +02:00
Adrià 6c6c5552dc
Merge pull request #3677 from MarcZX100/carry/libkernel-delete-sharing-3316
fix(libkernel): allow delete sharing for open Windows files
2026-10-11 23:49:25 +02:00
Adrià 85e2b31b56
Merge pull request #3676 from MrMha/fix/ci-test-accounting-current
fix(ci): preserve test discovery and skip diagnostics
2026-10-11 23:49:23 +02:00
Adrià 8d57710c95
Merge pull request #3674 from MrMha/fix/release-packaging-current
fix(packaging): validate and stage release archives
2026-10-11 23:49:15 +02:00
Adrià 45682f090d
Merge pull request #3672 from MrMha/fix/nid-inventory-current
fix(nid-names): ignore comments and literals in symbol inventories
2026-10-11 23:48:44 +02:00
Adrià d956de8425
Merge pull request #3596 from Lorkydey/feat/libc-fstatvfs
feat(libc): implement guest fstatvfs volume statistics
2026-10-11 23:48:10 +02:00
Adrià 00b6457610
Merge pull request #3671 from MrMha/fix/dependency-declarations-current
fix(pr-overlap): bound explicit dependency declarations
2026-10-11 23:47:27 +02:00
Adrià cda695db72
Merge pull request #3666 from MrMha/fix/export-audit-current
fix(import-audit): validate ELF and PE exports before reporting
2026-10-11 23:46:56 +02:00
Adrià 2605ad6c76
Merge pull request #3669 from MarcZX100/carry/audioin-open-variants-3340
feat(libSceAudioIn): implement sceAudioInHqOpen and sceAudioInAsyncOpen
2026-10-11 23:46:54 +02:00
Adrià e05a603771
Merge pull request #3668 from CouchRiv/fix-shader-float-compare-clamp
fix(shader): accept the clamp bit on VOP3 float compares
2026-10-11 23:46:41 +02:00
Adrià 2b988061ed
Merge pull request #3665 from itsbo5/feat/libSceSigninDialog-completion
feat(libSceSigninDialog): complete offline sign-in dialog lifecycle
2026-10-11 23:45:50 +02:00
pushkarreddyy 324669722e feat(libkernel): implement pthread_attr_getstackaddr 2026-10-11 17:45:35 -04:00
pushkarreddyy 91bfd6a18c feat(libkernel): implement pthread_attr_getinheritsched, pthread_attr_getscope and pthread_attr_setscope 2026-10-11 17:45:17 -04:00
Adrià cbd98bbdc1
Merge pull request #3540 from ivica00/fix/srgb-bilinear-cube-zero
fix(shader): return 0 for an 8-bit sRGB bilinear cube comparison
2026-10-11 23:45:00 +02:00
Adrià f35bcafbee
Merge pull request #3662 from Celegans12/perf/recompiler-iteration-selection-storage
perf(recompiler): reuse sorted sets for iteration selection merges
2026-10-11 23:44:59 +02:00
Adrià f6203c150b
Merge pull request #3497 from LightStorm72/https-ca-list-stubs
fix(libSceHttp): return an empty CA list from sceHttpsGetCaList/sceHttpsFreeCaList
2026-10-11 23:44:40 +02:00
Adrià 097b8d82a5
Merge pull request #3621 from goncalompontes/feat/shader-vop3-interp-f32-e64
feat(shader): decode GFX10 VOP3-encoded interpolation
2026-10-11 23:44:35 +02:00
Adrià fe03a7d126
Merge pull request #3660 from Celegans12/fix/cache-replace-open-windows-files
fix(cache): replace entries while Windows readers retain old files
2026-10-11 23:44:25 +02:00
Adrià 34c94e1d42
Merge pull request #3639 from AchrafReyani/fix/libc-mspace-32-byte-alignment
fix(libc): align mspace allocations to 32 bytes
2026-10-11 23:44:18 +02:00
Adrià Franch e8ea9a62bb test(libkernel): check that a socket call returns the mapped guest errno
Sending on an unconnected UDP socket must report the guest EDESTADDRREQ (39)
through NativeError, so a regression to EIO is caught on Linux.
2026-10-11 23:43:47 +02:00
Hussein Kadhim d9c2622655 docs(libkernel): record the unreported nonblocking connect refusal and the recv after SHUT_RD difference
(cherry picked from commit c1519d951b2b77346392ccc18204f37c587e7eda)
2026-10-11 23:43:47 +02:00
Hussein Kadhim d694aac7b4 fix(libkernel): translate every host socket error to the guest errno of its name
NativeError listed 17 Winsock and 17 POSIX errors and turned every other
one into EIO. A second connect while the first is pending (WSAEALREADY,
EALREADY) and an option the host rejects (WSAENOPROTOOPT, ENOPROTOOPT)
reached the guest as EIO; on Linux a nonblocking connect (EINPROGRESS),
a send with no destination (EDESTADDRREQ), ENOTSOCK, EAFNOSUPPORT,
EOPNOTSUPP and others did too.

The translation moves to SocketError.hpp and covers the socket errors of
both hosts by name, with the FreeBSD 11 numbers. WSAESHUTDOWN stays EPIPE
as before. socketpair's own table is gone, as NativeError covers it.

(cherry picked from commit a889c98e6a59d62da289f2e6a77355d2b41a1bde)
2026-10-11 23:43:47 +02:00
Adrià e2ea125926
Merge pull request #3605 from CouchRiv/fix-libc-asin-nan-sign
fix(libc): asin(|x| > 1) returns the x86 default NaN and leaves errno unchanged
2026-10-11 23:43:44 +02:00
Adrià 57ba0e7bca
Merge pull request #3568 from DariusHQ/feat/libkernel-sendfile
feat(libkernel): implement sendfile
2026-10-11 23:43:14 +02:00
Adrià aab41deda0
Merge pull request #3541 from MarcZX100/codex/amd-ordered-nan-compare-3536
fix(shader): handle ordered NaN comparisons
2026-10-11 23:43:11 +02:00
Dean abd318d9c0
Merge pull request #3683 from Zurisen/fix/amm-below-host-regions
fix(libkernel): reserve the AMM ranges below host regions at the top of the guest arena
2026-10-12 10:43:07 +13:00
Dean b5e6b458e5
Merge pull request #3976 from pipe1os/fix/agc-host-imports-device-switch
fix(agc): keep another device's host imports until its teardown
2026-10-12 10:43:04 +13:00
jweidenh 2ac8c72383 test(agc): bound packed f16 SPIR-V scanner reads
FindF16x2Math read spirv[offset + 1] for every instruction once the f16 vector type was known, so a final one-word OpFunctionEnd read past the module. Check the opcode and its minimum word count before reading operands.
2026-10-11 23:43:03 +02:00
Karan Vora 660e24eeac perf(shader): compute v_pk_add_f16 and v_pk_mul_f16 with native f16 math
v_pk_add_f16 and v_pk_mul_f16 computed each half in f32 and rounded it
back to f16, then fixed the NaN, FP16_OVFL and clamp results on the
bits. On a device with native f16 that rounds to nearest even and keeps
f16 denormals (the Float16, RoundingModeRTE and DenormPreserve
capabilities that #1252 already checks), the arithmetic is now one
f16vec2 OpFAdd or OpFMul decorated NoContraction, and the same NaN,
FP16_OVFL and clamp rules run on its result bits.

The results don't change. f32 has at least 2 * 11 + 2 significand bits,
so rounding an f16 sum or product to f32 and then to f16 gives the
correctly rounded f16 result. Vulkan requires f16 OpFAdd and OpFMul to
be correctly rounded, the module keeps f16 denormals, and any NaN they
produce is replaced by the NaN the existing rules select.

v_pk_fma_f16 and v_pk_fmac_f16 keep the f32 path: Vulkan gives GLSL Fma
only the precision of OpFMul followed by OpFAdd, so a native f16 Fma
may round twice.

New test agc_driver_packed_float16_native compiles one shader with
packed add, mul, fma and fmac (op_sel, op_sel_hi, neg_lo, neg_hi and
clamp variants) with and without Float16, checks that only add and mul
use f16vec2 math, and compares every result bit for bit on random and
special inputs (zeros, denormals, infinities, NaNs, values near the f16
maximum) in nine float modes.
2026-10-11 23:43:03 +02:00
Celegans12 58ee73f475 fix(libc): write the whole what() when terminate reports an exception
The default terminate handler formatted its whole report into a 1024-byte
buffer, so a long what() was cut off without a trailing newline. Driver
refusals that carry a serialized RecompileRequest lost most of it, and the
next log line ran into the truncated text.

The report header still goes through the bounded RawLog, and what() is
now written in full by a raw write to the stderr handle, followed by a
newline. Both keep avoiding the CRT stream lock, as before.

exception_runtime gains a forked child that throws an uncaught
runtime_error with a message over 3000 characters and checks that the
full what() reaches stderr before SIGABRT.
2026-10-11 23:42:53 +02:00
Adrià Franch 208b908238 test(agc): register the multisampled target guest-access test in ctest
The test only ran in the full recorder binary, which no ctest entry starts. It now has its own mode and entry (agc_multisample_guest_access); with the guard back at the single-sample size its new padding row fails.
2026-10-11 23:41:30 +02:00
Adrià Franch 5c5912c49e fix(agc): guard a multisampled target's whole sample planes; docs for host-only multisampled targets
The guest-access guard covers CB bytes times samples (the single-sample layout size alone missed the later sample planes' tile padding), with a test row in that padding. Remove two TechnicalDebt lines duplicated by the rebase, document the guard bound and the unevicted targets, and add the multisampled targets to ARCHITECTURE.md.
2026-10-11 23:41:30 +02:00
Michon d109f3966f fix(agc): guest access to a multisampled color target's memory throws
A multisampled target's samples are only on the host, so its guest memory
never holds them: a CPU read through GuestMemory::Read/ReadCommitted, a
DMA_DATA copy from it and a texture or storage image lookup at its address
throw instead of returning bytes that were never written there. Asked by
oneandonlydean; the test reads a resolved-only target's memory each way.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 23:41:30 +02:00
Michon b64da7caf7 fix(agc): GPL fragment libraries keyed by the sample count
The fragment shader and fragment output libraries are built with the
pipeline's rasterizationSamples, so a 1x and a 4x pipeline with the same
shaders and formats must not share them. Reported by adriadam10 (found by
Raghav314 in #2242).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 23:41:30 +02:00
Michon ae6dfd9073 fix(agc): the standard 8x pattern's last sample is at (7, -7)
Vulkan's standard 8x sample 7 is (0.9375, 0.0625), (7, -7) in signed sixteenths; the table had (7, -8), so the
standard pattern (PPSA99902's PA_SC_AA_SAMPLE_LOCS words 0xbd153fd1 / 0x9773f95b) was refused as nonstandard.
Regression row with those words.

Reported by @Amoenus.

Co-Authored-By: Amoenus <5391396+Amoenus@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 23:41:30 +02:00
Michon eeefb06549 fix(agc): multisampled targets use main's CMASK fields; their CMASK state refuses what it cannot model
The duplicate ColorTarget::cmaskAddress, decode and size helper are gone: multisampled targets
use main's cmaskAddress/cmaskBytes (whole metablocks). Single-sample targets keep main's rule
(CMASK under FAST_CLEAR); a multisampled one takes it under FAST_CLEAR or COMPRESSION. A zero
fill of the whole CMASK clears at the next use, 0xFFFFFFFF marks it expanded, any other or
partial fill refuses the next use; eliminate/DCC passes over a multisampled target and a CB
resolve into a fast-clear destination are refused. The draw key adds CMASK_BASE_EXT (0x398),
which main decodes but did not key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 23:41:30 +02:00
Michon 163947ef72 style(agc): drop code comments the conventions check rejects
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 23:41:30 +02:00
Michon ebf361103c feat(agc): multisampled color and depth targets with CB resolves
Draws with PA_SC_AA_CONFIG multisampling (2, 4 or 8 samples, every target with as many
fragments, the standard sample locations, MSAA_ENABLE) render into host-only multisampled
color images and multisampled depth surfaces; the pipeline rasterizes with that sample
count. A CB_COLOR_CONTROL resolve over a multisampled CB0 runs as a render pass whose
resolve attachment is CB1's resident image. A zero fill over a target's CMASK fast-clears
it to the CB_COLOR_CLEAR_WORD values of its next use. EQAA, coverage conversion,
nonstandard sample locations, sampling a multisampled surface and multisampled mips stay
refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 23:41:30 +02:00
Adrià 50d3f0691f
Merge pull request #3529 from oguzhanozfe/codex/windows-identical-function-export-aliases
fix(relinker): allow identical Windows function export aliases
2026-10-11 23:41:27 +02:00
ThatsLeo 58d59f6279 fix(shader): fold a lane's s_wqm bit of the starting pixel lanes to true
Vulkan starts only quads holding a non-helper lane, so the bit is set in every
lane it runs. Stray's pixel shader 0x2012f90000 keeps the packed ancillary
word where that bit is clear, and failed with an unsupported live use.

(cherry picked from commit 2068089b3d75fae55d0994a0916a22bffd7ee2a4)
2026-10-11 21:41:01 +00:00
Adrià 752db7da76
Merge pull request #3569 from DariusHQ/fix/libc-format-positional-arguments
fix(libc): format positional arguments on Windows
2026-10-11 23:40:52 +02:00
Adrià Franch 9ac63c0af8 style: restore blank lines touched by accident 2026-10-11 23:40:51 +02:00
bilel 11f329fd4b fix(libkernel): reject identity changes before ordinary-file fallback
A descriptor replaced before the shared-memory path probe can otherwise
bypass the pinned-object identity check through native fallback. Compare
the negative probe's current file identity to the initial snapshot and
reject a mismatch. Exercise the earlier window with an ordinary file for
all six descriptor operations.

Confidence: high
Scope-risk: narrow
Tested: 18 Linux runtime tests with zero skips; 18 deterministic reuse scenarios; affected three patched modules; standalone ASan/UBSan; Clang analyzer; conventions and diff checks
Not-tested: Full project build/suite, Windows runtime, console hardware and titles
Directive: Identity checks do not synchronize the entire descriptor table or identify separate opens of the same inode
2026-10-11 23:40:46 +02:00
bilel f6ef5a77e2 fix(libkernel): preserve patched loading and shared-memory object identity
Keep guest creation-mask state in libc behind C-linkage helpers so NID
patching preserves the shared exports. Reject a pinned descriptor whose
file identity differs from the snapshot taken before classification.
Exercise close and reuse from another thread at both acquisition windows
for writes, resize and seek, and create temporary ordinary-file fixtures
so runtime tests work without the source tree.

Constraint: NID exemptions require the literal suffix at the end of the exported symbol
Confidence: high
Scope-risk: narrow
Tested: 18 targeted Linux runtime tests with zero skips; patched_libraries for libc/libkernel/libkernel_sys; standalone ASan/UBSan with warnings as errors; source-prefix-remapped fixtures; Clang analyzer and conventions
Not-tested: Full project build and library set, Windows runtime, console hardware and titles
Directive: Device/inode validation establishes file identity, not open-file-description identity
2026-10-11 23:40:46 +02:00
bilel 72b3a2abdd fix(libkernel): keep shared-memory identity and guest permissions consistent
Pin the descriptor before mmap validation so guest descriptor reuse cannot
redirect the eventual mapping. Apply the canonical guest creation mask when
creating named objects, distinguishing new objects before setting permissions
without changing the host process umask. Add regressions that reproduce both
failures and document the remaining host requirements.

Constraint: Guest umask must not mutate the process-wide host mask
Confidence: high
Scope-risk: narrow
Tested: 17 targeted Linux runtime tests passed with zero skips; conventions and diff checks; Clang analyzer on three changed production translation units
Not-tested: Full project build and suite, Windows runtime, console hardware and titles
2026-10-11 23:40:46 +02:00
bilel 2aaeaa3f05 feat(libkernel): make Linux shared memory usable through guest APIs (#3923)
Complete the object lifecycle through guest mapping and unlink APIs, and
preserve FreeBSD shared-object size and seek rules across scalar, vector
and asynchronous descriptor writes. Pin descriptors during operations and
coordinate size-sensitive operations across cooperating emulator processes.

Constraint: Linux host support with existing dependencies and allocation tracking
Rejected: Bare descriptor registry | close, duplication and reuse invalidate identity
Rejected: Per-object OFD locks | forked descriptors share the same lock description
Confidence: high
Scope-risk: moderate
Directive: Keep guest resize, truncating open and bounded writes under the same namespace guard
Tested: Ten Linux runtime suites; focused shared-memory ASan/UBSan suite with warnings as errors
Not-tested: Windows build, PS5 hardware and title compatibility
2026-10-11 23:40:45 +02:00
bilel zenned f274ceb18d feat(libkernel): allow Linux guests to reopen named shared memory (#3921)
Translate guest named-open flags and permissions to POSIX shared-memory descriptors. Bound guest names and use the existing digest implementation to keep a stable AnyPS5 namespace, including long names and internal slashes. Preserve anonymous and Windows behavior, and record the separate unlink and guest mapping gaps.

Constraint: Builds and tests run only through the enrolled 191 compute service.
Rejected: Direct Linux guest names | cannot represent FreeBSD internal slashes and 1023-byte names.
Tested: Source review and diff checks; baseline job100619 accepted, result retrieval currently times out.
Not-tested: Runtime verification pending on191; full build, Windows and titles.
Confidence: medium
Scope-risk: narrow
2026-10-11 23:40:45 +02:00
bilel zenned eeb647c6b5 test(libkernel): expose the missing named shared-memory path (#3921)
Exercise named object creation, reopening, descriptor permissions, truncation and cross-process identity before adding Linux support. The standalone target compiles the production implementation without graphics dependencies.

Tested: Source and staged diff reviewed; failing baseline will run on 191.
Not-tested: Full runtime build and Windows.
2026-10-11 23:40:45 +02:00
Adrià 951fd9afcb
Merge pull request #3590 from karanvora2599/perf/shader-f16-minmax-bits
perf(shader): pick f16 min, max and med3 results on the bits
2026-10-11 23:39:47 +02:00
Adrià 8a13a1b8ef
Merge pull request #3594 from Scramblrrrr/codex/windows-host-write-watch-exclusion
fix(agc): exclude host arena holes from write-watch proofs
2026-10-11 23:39:34 +02:00
Adrià 07eacac68c
Merge pull request #3591 from QRMOE/fix-libkernel-clock-getres-null
fix(libkernel): honor null clock_getres output
2026-10-11 23:39:27 +02:00
Adrià da07b30b26
Merge pull request #3589 from MrMha/fix/relinker-elf-reader-validation
fix(relinker): validate ELF section tables and file mappings
2026-10-11 23:39:02 +02:00
Adrià 372fb03284
Merge pull request #3586 from jradelmo/feat/http-uri-authority-delimiter
fix(libSceHttp): retain authority delimiter for partial URI builds
2026-10-11 23:38:55 +02:00
Adrià 87d7ab7950
Merge pull request #3551 from ducloc080202/fix/libc-unhandled-throw-site
fix(libc): name the throw site in unhandled exception reports
2026-10-11 23:38:35 +02:00
Adrià 7c8df1f0e4
Merge pull request #3583 from DariusHQ/feat/libc-wcstok
feat(libc): implement wcstok
2026-10-11 23:38:25 +02:00
Adrià c60f65a227
Merge pull request #3535 from sktharunbalaji13-cmd/contrib/libSceHttp2-invalid-id
fix(libSceHttp2): validate SSL setter handles
2026-10-11 23:37:50 +02:00
Adrià f5824092cf
Merge pull request #3545 from babyhuey/feat/posixforwebkit-fnmatch
feat(libScePosixForWebKit): implement fnmatch
2026-10-11 23:37:41 +02:00
Adrià 011c26cd5e
Merge pull request #3578 from LeandroLP23/fix/libkernel-fsync-read-only-windows
fix(libkernel): sync read-only and directory descriptors on Windows
2026-10-11 23:37:24 +02:00
Adrià 1342c4b9df
Merge pull request #3539 from MarcZX100/codex/agc-write-data-dst3-3534
fix(agc): accept WRITE_DATA destination 3 for memory fences
2026-10-11 23:36:57 +02:00
Adrià 0b7b479a04
Merge pull request #3576 from parl0v/refactor/agc-dcb-atomic-gds-0900
refactor(agc): name zARR5aCmkoY sceAgcDcbAtomicGds_0900
2026-10-11 23:36:55 +02:00
Adrià 24ba6b1764
Merge pull request #3577 from parl0v/refactor/audioin-pad-embedded-mic
refactor(audioin): name X+4jdIS75P0 sceAudioInIsPadEmbeddedMic
2026-10-11 23:36:48 +02:00
Adrià a672688e94
Merge pull request #3523 from XairRrR/fix/agc-default-unexported-pixel-inputs
fix(agc): read zero for pixel inputs of parameters the vertex stage does not export
2026-10-11 23:36:20 +02:00
Adrià 7de666a937
Merge pull request #3512 from LeandroLP23/fix/shader-mix-f16-directed-rounding
fix(shader): round v_fma_mixlo_f16 and v_fma_mixhi_f16 in every f16 rounding mode
2026-10-11 23:36:15 +02:00
Adrià 4d55bf10be
Merge pull request #3493 from AchrafReyani/feat/libkernel-ioctl
feat(libkernel): export _ioctl and getsid
2026-10-11 23:35:53 +02:00
Adrià 195f55eb97
Merge pull request #3572 from Lorkydey/feat/libkernel-getitimer
feat(libkernel): query real-time alarms with getitimer
2026-10-11 23:35:45 +02:00
Adrià b6b7deb0f4
Merge pull request #3574 from marshmll/fix/linux-sdl-audio-backends
fix(build): require an SDL audio backend on Linux
2026-10-11 23:35:42 +02:00
Adrià 41f013f664
Merge pull request #3567 from DariusHQ/fix/libc-format-grouping-flag
fix(libc): accept the ' flag in Windows formatting
2026-10-11 23:34:57 +02:00
Adrià ac1c340392
Merge pull request #3571 from EdouardSence/feat/json-parser-stream-parse
feat(libSceJson2): add sce::Json::Parser::parse with a read callback
2026-10-11 23:34:48 +02:00
Adrià dafc085adb
Merge pull request #3566 from DariusHQ/fix/libc-format-alternate-octal-zero
fix(libc): print 0 for %#.0o of zero on Windows
2026-10-11 23:34:44 +02:00
Adrià 7450e4dae6
Merge pull request #3570 from EdouardSence/feat/json-array-copy-clear-front-stream-parse
feat(libSceJson2): add the sce::Json::Array copy constructor, clear, front and iterator::operator->
2026-10-11 23:34:22 +02:00
Adrià ada499b635
Merge pull request #3561 from XairRrR/fix/agc-swap-alt-review-guards
fix(agc): reject constant color factors and memory-writing stages in split SWAP_ALT blends
2026-10-11 23:34:15 +02:00
Adrià 4085df4667
Merge pull request #3565 from ivica00/feat/libc-powisf2
feat(libc): implement __powisf2
2026-10-11 23:34:08 +02:00
Adrià 3ccef6abcd
Merge pull request #3564 from Aathish-D/feat/libc-ftrylockfile
feat(libc): implement ftrylockfile
2026-10-11 23:34:00 +02:00
Mouhand Kaddo 121b16fb4b fix(libkernel): guard Unix host polling in Windows select 2026-10-12 01:33:53 +04:00
Adrià c892804260
Merge pull request #3560 from EdouardSence/feat/razorcpu-push-marker
feat(libSceRazorCpu): add sceRazorCpuPushMarker
2026-10-11 23:33:24 +02:00
Adrià e44b7cf78c
Merge pull request #3546 from AlxcBTW/fix/vertex-narrow-wave-entry-2
fix(recompiler): give wave64 vertex programs on 32-lane hosts a 32-lane s3
2026-10-11 23:33:16 +02:00
Adrià 9d5c6afcd6
Merge pull request #3559 from EdouardSence/feat/ngs2-report-handler-exports
feat(libSceNgs2): export sceNgs2ReportRegisterHandler and sceNgs2ReportUnregisterHandler
2026-10-11 23:33:12 +02:00
Adrià 62f8758711
Merge pull request #3544 from babyhuey/feat/libkernel-execv
feat(libkernel): export execv
2026-10-11 23:33:07 +02:00
Adrià c56ea3b343
Merge pull request #3537 from hugodearaujo/feat/ppsa15878-vr2-exports
feat(libs): declare the PS VR2 exports PPSA15878 imports
2026-10-11 23:32:40 +02:00
Adrià 2ebe73da53
Merge pull request #3543 from Lorkydey/fix/relinker-none-relocations
fix(relinker): ignore no-op executable relocations
2026-10-11 23:32:26 +02:00
Adrià ceab34ad63
Merge pull request #3538 from hugodearaujo/fix/sysmodule-id-zero
fix(libSceSysmodule): return INVALID_VALUE for module id 0
2026-10-11 23:32:24 +02:00
Adrià b2477d9804
Merge pull request #3530 from MarcZX100/codex/libkernel-inet-text-rules-3126
fix(libkernel): follow FreeBSD inet text rules
2026-10-11 23:32:18 +02:00
Adrià 28400c02d1
Merge pull request #3542 from BharatYadav007/bharat/libc-setlogmask
feat(libc): implement setlogmask
2026-10-11 23:32:17 +02:00
milescoviello 9421484572 fix(libkernel): fail open with O_NOFOLLOW on a symbolic link
sceKernelOpen built the host flags from the bits it knows and dropped
O_NOFOLLOW (0x100), so open and sceKernelOpen with O_NOFOLLOW followed
a symbolic link and opened its target, and with O_CREAT created the
missing target of a dangling link.

FreeBSD looks up the last component without following it when
O_NOFOLLOW is set (sys/kern/vfs_vnops.c, vn_open_cred:
"((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF", and with
O_CREAT "if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0)
ndp->ni_cnd.cn_flags |= FOLLOW;"), and then refuses the link itself in
vn_open_vnode: "if (vp->v_type == VLNK) return (EMLINK);".

open now fails with EMLINK when O_NOFOLLOW is set and the path names a
symbolic link. O_CREAT | O_EXCL keeps failing with EEXIST, and a
trailing slash still resolves through the link.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:30:23 +00:00
milescoviello e8cb97e6e2 fix(libkernel): fail rmdir on a symbolic link instead of removing it
sceKernelRmdir, and rmdir through it, checked the path with
std::filesystem::is_directory and is_empty, which follow a symbolic
link, and then removed the path itself. So rmdir on a link to an empty
directory returned 0 and deleted the link, while the target stayed.

FreeBSD looks up the last component of rmdir's path without following
it (sys/kern/vfs_syscalls.c, kern_rmdirat: NDINIT_ATRIGHTS with
LOCKPARENT | LOCKLEAF and no FOLLOW) and then fails with ENOTDIR for
anything that is not a directory: "if (vp->v_type != VDIR) { error =
ENOTDIR; goto out; }". A link, whether it points to a directory or
nowhere, is a VLNK there.

rmdir now returns ENOTDIR for a symbolic link and leaves it alone, as
unlink already looks at the link itself through symlink_status. A
trailing slash still resolves through the link, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:30:23 +00:00
milescoviello 3ba4532352 fix(libkernel): reset a once control when its initializer exits or is cancelled
pthread_once and scePthreadOnce marked the control as running, called
the initializer and marked the control done only after it returned. A
thread that left the initializer through pthread_exit or a
cancellation point never came back, so the control stayed in progress
and every later call on it waited forever.

FreeBSD libthr runs the initializer under a cleanup handler
(lib/libthr/thread/thr_once.c, _pthread_once:
"THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control);
init_routine(); THR_CLEANUP_POP(curthread, 0);"). once_cancel_handler
stores ONCE_NEVER_DONE and wakes the waiters, and thread exit and
cancellation run the cleanup handlers. POSIX requires this for
cancellation: "if init_routine is a cancellation point and is canceled,
the effect on once_control shall be as if pthread_once() was never
called."

Both functions now push such a handler around the initializer, so the
control returns to its initial state and the next caller, or one
already waiting, runs the initializer. scePthreadOnce waiters now retry
when the control goes back to its initial state, and an exception from
its initializer resets the control as it already did for pthread_once.
CleanupHandler moves to Cancel.hpp so that Once.cpp can use it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:30:23 +00:00
Adrià 9bee015035
Merge pull request #3524 from XairRrR/perf/agc-attach-2d-array-layers
fix(agc): render a slice of a 2D array color target into that layer of the array
2026-10-11 23:30:07 +02:00
Adrià 039d21ead8
Merge pull request #3527 from Sanjaysrinivas/fix/agc-buffer-memory-recovery
fix(agc): reclaim idle buffers on allocation OOM
2026-10-11 23:30:03 +02:00
ivica00 cd814b8c6f fix(shader): return 0 for an 8-bit sRGB bilinear cube comparison
An 8-bit sRGB compare is already 0 for every texel, so the selected face cannot change the result.
2026-10-11 23:29:39 +02:00
Adrià 989146c3b9
Merge pull request #3514 from gookie-dev/perf/kernel-module-info-frame-tables
perf(libkernel): read a loaded image's unwind tables once in sceKernelGetModuleInfoFromAddr
2026-10-11 23:29:38 +02:00
Adrià b0fd7c017e
Merge pull request #3517 from ee6aba54/fix/trophy2-null-outputs
fix(libSceNpTrophy2): accept a null details or data in GetTrophyInfo and GetGroupInfo
2026-10-11 23:29:33 +02:00
Adrià d2e1f20edb
Merge pull request #3499 from blawesom/carry/ampr-cnd-signal
feat(libSceAmpr): implement _Cnd_signal and _Cnd_init_with_name
2026-10-11 23:29:11 +02:00
Adrià d1f198d468
Merge pull request #3509 from Lorkydey/fix/relinker-module-dir-boundaries
fix(relinker): normalize additional module directory paths
2026-10-11 23:29:09 +02:00
Adrià 59559c306c
Merge pull request #3496 from MarcZX100/codex/primstate-normalize-disabled-vgpr-3457
fix(agc): preserve hand-built user VGPR offsets
2026-10-11 23:29:06 +02:00
Adrià a9a34e91cc
Merge pull request #3412 from pushkarreddyy/feat/agc-draw-snapshot-cap
perf(libSceAgcDriver): cap draw input snapshot allocation size
2026-10-11 23:28:42 +02:00
Ahir Barman Maji 52c7470b15 fix(shader): find the shader cache directory on macOS
ShaderCacheDirectory located the executable through /proc/self/exe,
which macOS does not have, so it returned an empty path there and
the shader and pipeline disk caches were silently disabled unless
ANYPS5_SHADER_CACHE_DIR was set. Use _NSGetExecutablePath and
resolve the path with std::filesystem::canonical on macOS. Linux
and Windows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:56:34 +05:30
Mouhand Kaddo 9d8ee3de7d fix(shader): isolate single-block loop continue targets
Route semantic self-loops through a dedicated empty back-edge block. This prevents block merging from folding the continue target into the loop header when memory instructions emit nested selections. Add a synthetic loop-store regression that fails SPIR-V validation after optimization on main.
2026-10-12 01:26:17 +04:00
Adrià 14a7e64dcd
Merge pull request #3485 from santhoshkumar-dev/codex/windows-path-traversal
fix(filesystem): reject invalid parent traversal on Windows
2026-10-11 23:24:37 +02:00
Adrià 7562b4b05e
Merge pull request #3473 from superhe11/fix/resource-cache-storage-eviction
fix(agc): keep uncached storage images out of the resource cache
2026-10-11 23:24:32 +02:00
Adrià 90896b5cc9
Merge pull request #3462 from exefer/fix/libkernel-select-host-descriptors
fix(libkernel): select descriptors that are not sockets
2026-10-11 23:24:27 +02:00
Adrià 1770f352d7
Merge pull request #2471 from Scramblrrrr/codex/shared-loop-exit
fix(shader): guard shared loop exit routing and preserve valid fallbacks
2026-10-11 23:24:02 +02:00
Adrià 4cacc78d61
Merge pull request #3391 from tibzejoker/fix/masked-select-wqm-superset
fix(shader): drop masked selects proven unobserved through whole quad mode
2026-10-11 23:23:58 +02:00
Adrià 9865c57f76
Merge pull request #2261 from Sanjaysrinivas/feat/registered-ngg-workgroup
fix(agc): route registered NGG workgroups through mesh
2026-10-11 23:23:54 +02:00
Adrià fc5faefc44
Merge pull request #3159 from XairRrR/perf/agc-shader-prep-lock
perf(agc-driver): prepare shader ABI variants before taking the preparation lock
2026-10-11 23:23:53 +02:00
Adrià 515464844d
Merge pull request #1250 from kr4y4/fix/shader-fract-sub-floor
fix(shader): compute V_FRACT_F32 from the exact fraction of |x|
2026-10-11 23:23:29 +02:00
Adrià c5da8233a5
Merge pull request #3515 from Winvoker/fix/progress-stub-call-whitespace
fix(progress): match stub calls across whitespace
2026-10-11 23:16:46 +02:00
Adrià 9c75aa87c3
Merge pull request #3513 from jweidenh/codex/test-bda-store-boundaries
test(recompiler): cover BDA store boundary faults and written pages
2026-10-11 23:15:57 +02:00
Adrià Franch 7ca6b63905 docs(technical-debt): note the CA list stubs skip context validation
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 23:15:05 +02:00
LightStorm72 1d5f699439 fix(libSceHttp): return an empty CA list from sceHttpsGetCaList/sceHttpsFreeCaList 2026-10-11 23:15:05 +02:00
Ahir Barman Maji f30f3e8a84 fix(libs): find the executable directory on macOS
libSceFont and libSceNpEntitlementAccess located the executable
through /proc/self/exe, which macOS does not have, so finding the
system fonts and the entitlements file threw there. Use
_NSGetExecutablePath and resolve the path with
std::filesystem::canonical on macOS. Linux and Windows are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:38:05 +05:30
Claude 11fa3ab843
fix(libc): keep getenv results valid when other variables change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 21:07:20 +00:00
Claude 1489400189
fix(libc): set EINVAL when getenv gets an invalid name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 21:06:35 +00:00
Adrià Franch 76d43c525e fix(libkernel): drop _ioctl, already exported by #3487
Co-authored-by: Achraf Reyani <noreply@users.noreply.github.com>
2026-10-11 23:06:10 +02:00
Achraf Reyani 6c7a2cbe69 feat(libkernel): export _ioctl and getsid
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:06:10 +02:00
Adrià b3ae112012
Merge pull request #3528 from MarcZX100/codex/agc-shutdown-3506
fix(agc): catch shutdown in shader ABI resolution
2026-10-11 23:06:08 +02:00
Claude fe4a2cf7d8
fix(libc): run atexit handlers in one order with __cxa_atexit destructors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 21:05:51 +00:00
Adrià 9d9f2cf094
Merge pull request #3516 from Feuris/carry/windows-udp-short-receive
fix(net): preserve truncated UDP receives on Windows
2026-10-11 23:05:37 +02:00
Adrià 68ac73f3df
Merge pull request #3452 from LeandroLP23/fix/kernel-windows-directory-rewind
fix(libkernel): rewind Windows directory descriptors on lseek
2026-10-11 23:05:29 +02:00
Adrià d80a52b75d
Merge pull request #3500 from notaxeltv/feat/tts2-unavailable-exports
feat(libSceTextToSpeech2): return EOPNOTSUPP from remaining speech exports
2026-10-11 23:04:31 +02:00
Claude e463c5d11f
fix(libkernel): clamp POSIX deadlines beyond the clock range instead of overflowing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 21:01:57 +00:00
Claude 74d44b8e6c
fix(libkernel): try the lock before checking the pthread_mutex_timedlock deadline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 21:00:30 +00:00
Claude 29af23e718
fix(libkernel): apply the memory type in sceKernelMemoryPoolBatch type-protect entries
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 20:58:50 +00:00
Claude 602a76fc26
fix(libkernel): let pthread_barrier_destroy wait for released waiters
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 20:57:22 +00:00
dantdmdarealguy 3c581619d3 feat(input): configure stick deadzones and mouse sensitivity
anyps5-input.ini accepts LeftStickDeadzone and RightStickDeadzone (whole
percent, 0 to 90), a radial deadzone on the controller sticks whose
remaining travel is rescaled to the full range, and MouseSensitivity
(above 0, at most 10), which replaces the fixed factor of 1 for mouse
look. The defaults keep the current mapping exactly.
2026-10-11 15:57:12 -05:00
Adrià Franch 8e5091c2f0 docs(technical-debt): name the SPIR-V rule behind keeping the loop back edge
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 22:57:06 +02:00
XairRrR 0ad1a46884 test(agc): reject a split SWAP_ALT draw whose vertex shader stores to memory
The 8_8 SWAP_ALT blend test now also draws with a vertex shader that
stores a dword to a buffer (buffer_store_dword through user data 4-7)
and a separate alpha blend, and requires the draw to be rejected for
its shaders writing memory. With the former pixel-only check the draw
went through and the test fails.
2026-10-11 22:44:57 +02:00
Felipe Arce 4966917282 fix(agc): park retired host imports through shared_ptr so Imports() stays in place 2026-10-11 17:38:22 -03:00
jweidenh 3740c233bc fix(agc): accept core Vulkan capabilities during compute replay 2026-10-11 16:34:26 -04:00
Kikoarl 5d298b6d95 feat(libSceSysmodule): register libSceAudioPropagation module ID (0x120) 2026-10-11 22:33:22 +02:00
Felipe Arce 25f5afe645 fix(agc): keep another device's host imports until its teardown 2026-10-11 17:25:29 -03:00
jweidenh 598e991bca test(agc): provide the binding flag fixture placeholder buffer 2026-10-11 16:20:28 -04:00
Jordan Hans 2d3dd337ad fix(libSceNet): accept guest socket addresses whatever their sa_len 2026-10-11 20:17:41 +00:00
Alberto Ruiz e31b757c1e fix(libkernel): limit param.json nesting depth
parseJsonValue recursed without a limit, so a param.json with tens of
thousands of nested arrays overflowed the stack. Reject nesting beyond 512
levels, the limit libSceJson2 uses.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5WJS4ZdQynAJesCZKH5hb
2026-10-11 22:15:15 +02:00
jweidenh a4ae6d7e31 fix(agc): capture larger compute modules and binding flags 2026-10-11 16:14:44 -04:00
jweidenh decbac9bb4 fix(agc): link capture tests with the Windows exception runtime 2026-10-11 16:09:48 -04:00
jweidenh 8fbd0eecc1 feat(agc): capture and replay compute pipeline compilation 2026-10-11 16:09:48 -04:00
Claude f40bb479e2
fix(libkernel): report the remaining time from sceKernelWaitSema
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 20:09:33 +00:00
jweidenh 3b32e54ed9 test(agc): skip partial collection when write watching is unavailable 2026-10-11 16:09:08 -04:00
jweidenh e5e8c232e8 fix(libc): preserve uncollected shared memory writes on Windows 2026-10-11 16:09:08 -04:00
jweidenh bf8a6bc2ff test(libc): cover partial shared write collection 2026-10-11 16:09:08 -04:00
hugodearaujo d5f11b156b fix(libSceUserService): report an unrestricted age level
sceUserServiceGetAgeLevel reported 0. On a PS5 (firmware 13.60) the
value is the age of the user's parental restriction (0, 7, 12 and 15
measured) and 0xFFFFFFFF without one, so 0 is a user restricted to age
0. A user who is not logged in returns 0x80960009 (not logged in) with or
without an output, before the null output check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:07:45 -03:00
Claude d358374ac8
fix(libc): throw for 16-bit wide scanf conversions on Linux
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 20:07:20 +00:00
Arekatla Nishanth Chowdary c13d3b474a fix(build): reject MinGW compilers that select SjLj exceptions for the libraries
The libraries are built with -fno-asynchronous-unwind-tables and unwind
through their own DWARF tables. MinGW-w64 GCC 11.4 and 13.2 answer that
flag by switching to SjLj exceptions. Configuration succeeded and, with
13.2, the build stopped at the libc.prx link with undefined references
to __gxx_personality_sj0 and _Unwind_SjLj_Register, which does not name
the cause. Configuration now checks the exception model the flag selects
and stops with the supported toolchain's location.
2026-10-11 20:03:53 +00:00
fma-rc ba9f903e3f
feat(shader): implement s_subvector_loop_begin and s_subvector_loop_end 2026-10-11 22:03:26 +02:00
Claude 6b72296ef5
fix(libc): format 16-bit wide arguments in asprintf on Linux
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 20:02:20 +00:00
Dean 0a8b1d32b1
Merge pull request #3415 from MarcZX100/codex/fix-windows-runtime-unwind-3411
test(libc): keep Windows exception cleanup coverage on guest frames
2026-10-12 08:59:34 +13:00
Santhosh Kumar 2ad916566f fix(libkernel_sys): return the resolver's error from symlink
symlink, added in #3491 while this branch was open, still resolved its
new path with ResolvePath_nid_no_patch, which now throws for an invalid
"..". Use ResolvePathError_nid_no_patch as link does, so
"missing/../x" returns ENOENT and "file/../x" ENOTDIR.

Reported by @Silverash-i8 on #3485.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:25:02 +05:30
Santhosh Kumar 17827dd9ab fix(filesystem): reject invalid parent traversal on Windows 2026-10-12 01:25:02 +05:30
milescoviello ad14190c13 fix(libc): resolve ".." in guest paths against the directory before it
Guest paths were normalized lexically before they reached the host, so
"missing/../file" and "file/../x" dropped the component before "..":
stat and open succeeded on the file, unlink deleted it and rmdir removed
a directory, where FreeBSD's lookup() resolves ".." with VOP_LOOKUP in
the directory reached so far and fails with ENOENT for a missing
component and ENOTDIR for a file. POSIX path resolution (XBD 4.13) says
the same.

".." is still collapsed lexically when the path before it is an existing
directory, so case-insensitive matching and aliases behave as before.
When it is not, the rest of the path is passed to the host unchanged and
the host resolves it. Linux then reports ENOENT or ENOTDIR as FreeBSD
does; Windows collapses ".." itself, so its results are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYhZu5HokHMo9mfi8QU83m
2026-10-12 01:24:53 +05:30
Claude 4a419397cb
fix(libkernel_sys): translate host errors to guest errno numbers
GuestError passed host error values up to 34 through and reported every
other value as EIO, so readlink failed with EIO for a name that is too
long or a symbolic link loop, and a host EAGAIN would have been reported
as the guest's EDEADLK. The values whose numbering differs are now
mapped to FreeBSD's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 19:50:38 +00:00
Hussein Kadhim aaf49fb40f fix(libc): compare strcasecmp and strncasecmp results after lowering
FreeBSD returns the difference of the lowercased bytes where the strings
differ, including where one of them ends. The guest wrappers returned the raw
difference of the terminator and the unfolded byte, so strcasecmp("ab", "A")
gave 66 instead of 98.
2026-10-11 19:49:35 +00:00
Hussein Kadhim 87e9c70157 fix(libkernel): recvfrom on a stream socket reports no sender address
FreeBSD's soreceive leaves the sender unset for a stream socket, so recvfrom
returns an address length of 0 (kern_recvit). The guest call formatted the empty
host address as a 28-byte AF_INET6 sockaddr and reported length 28. Report
length 0 when the host returns no AF_INET or AF_INET6 sender, as recvmsg does.
2026-10-11 19:48:51 +00:00
Hussein Kadhim 8db0f89e7b fix(libkernel): return SCE error codes from sceKernelClockGettime and sceKernelClockGetres
A thread CPU clock whose thread has finished made clock_gettime fail with errno EINVAL, but the sce wrappers passed the raw -1 through to the title. They now return the encoded errno like the other sce kernel calls.
2026-10-11 19:47:03 +00:00
PrintOrb cef97424a8 test(videoout): pin high-frame-rate display rejection 2026-10-11 14:46:14 -05:00
Dean 12c2266281
Merge pull request #3922 from oneandonlydean/up4/fix-implicit-lod-test-wqm
test(agc): enter whole quad mode in the implicit-LOD 1D sample test
2026-10-12 08:45:56 +13:00
Dean a061f5b5ac
Merge pull request #3510 from babyhuey/fix/libkernel-sys-symlink-windows
fix(libkernel_sys): create symlinks with CreateSymbolicLinkW on Windows
2026-10-12 08:45:52 +13:00
Dean 70eba72ad6
Merge pull request #3518 from Silverash-i8/fix/filesystem-test-directory-time
test(libkernel): don't require restoring the directory time on Windows
2026-10-12 08:45:48 +13:00
Dean f7d623528a
Merge pull request #3532 from MarcZX100/codex/srgb-compare-zero-3526
fix(shader): allow constant-zero sRGB comparisons
2026-10-12 08:45:45 +13:00
Hussein Kadhim 942b5945d1 fix(libkernel): leave the remaining time untouched when nanosleep completes
FreeBSD's kern_clock_nanosleep writes the remaining time only when the sleep is cut short. A completed sleep zeroed rmtp, so nanosleep(&ts, &ts) in a polling loop slept zero time from the second call on.
2026-10-11 19:45:27 +00:00
Scramblrrrr 2a58fb7997 fix(shader): preserve distinct definitions while folding phi inputs
Fold identical resolved values or equal immediate constants, retaining distinct branch-local SSA definitions. AI-assisted: yes (Codex).
2026-10-11 16:44:34 -03:00
Dean 4be9c25519
Merge pull request #3520 from MarcZX100/codex/gcc13-unwind-3459
fix(libc): resume host foreign exceptions with host unwinder
2026-10-12 08:37:59 +13:00
GalaxyGamingFR ede6fad8b2 fix(conventions): treat wrapped declarations as declarations
Check.duplicates reported a duplicate export for a hit in another
library unless that single grep line ended with a semicolon, so a
prototype wrapped over two lines was reported as a duplicate
definition. The hit file is now read at the head commit and the
existing function_body decides whether the hit is a definition.

The git grep pattern also used the GNU escapes \b and \s, which the
BSD regex library on macOS does not support, so the check never
matched there. The pattern now uses POSIX classes instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 12:20:36 -07:00
MarcZX100 cc7343daf5 fix(libc): clear fopen_s output on invalid arguments
(cherry picked from commit 387279ee941e5a203325852b24cdb87e2f835e43)
2026-10-11 19:16:51 +00:00
MarcZX100 fdf288f3fc fix(libc): return fclose failures to callers
(cherry picked from commit 610d6cdda098e9adfcd3814b52f8eba01caaa4d1)
2026-10-11 19:12:46 +00:00
MrMha 3742fa136e test(relinker): cover extra block bounds and header address overflow 2026-10-11 23:11:42 +04:00
AlxcBTW 99edf9435c fix(recompiler): read P10 of a flat input as the provoking vertex
A FLAT_SHADE input without pass-through that a pixel shader reads
through v_interp_mov_f32 P10 became an explicit per-vertex input and
read vertex 1 minus vertex 0. With the last provoking vertex the
pipeline then threw ("last provoking vertex with explicit per-vertex
flat interpolation is unsupported"), which stopped all three
ps5-opengl titles in the catalog.

ps5-opengl reads every flat input through P10 when GL's last-vertex
convention is in effect, and its driver passes the GL 4.6 CTS on a PS5
(#1413). Such a read now returns the provoking vertex's value, as P0
does, and only a P20 read keeps a flat input per vertex. The lane was
not measured on the console itself, so FragmentParameter records it
and a pipeline using the first provoking vertex throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:09:55 +02:00
Achraf Reyani 6eacac2e96 test(libkernel): check close of an unopened descriptor among the other bad-descriptor cases
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 04:08:23 +09:00
GalaxyGamingFR 08169eae25 fix(tools): ignore wrapped declarations in pr_overlap exports
exports() only checked whether the matching line ended with a semicolon, so a
prototype wrapped over several lines was counted as an implemented export and
two PRs adding the same forward declaration were flagged as overlapping. It now
joins the following added lines up to the first semicolon or brace and treats
the match as a declaration when the semicolon comes first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 12:03:55 -07:00
GalaxyGamingFR a89aa5c0d0 fix(progress): skip cells for rectangles without area
When squarify gave a group a sliver narrower than 2px, treemap passed cells a zero or negative width, so the row count became complex or divided by zero and render crashed. cells now returns no cells for rectangles without area. A regression test renders a 300 and 1 item split.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 12:00:10 -07:00
MarcZX100 b6f96f8e90 test(libkernel): extend existing credential regression coverage 2026-10-11 18:55:56 +00:00
Mouad-Scriptz-0 bc5bfd0ed6 feat(libkernel): export setuid, setreuid, setgroups and getgroups
`setuid`, `setreuid` and `setgroups` are absent imports of XPSemu
(PPSA97358) and PS5MC (PPSA99420) on `main`. The guest process is modelled
as root with ids 0, as `seteuid` and `setegid` already assume: `setuid`
accepts 0 and `setreuid` 0 or -1 per id and change nothing, any other id
throws. `setgroups` keeps the supplementary group list as FreeBSD's
`kern_setgroups` does (`NGROUPS_MAX` 1023, an empty list leaves the
effective group as the only entry, a first entry other than 0 throws) and
the new `getgroups` reports it. `guest_credentials` covers the accepted
ids, the default list, a set list, an empty list and the EINVAL/EFAULT
cases.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit d12b062074dcf64bff694800d77f7261adf6f59e)
2026-10-11 18:55:22 +00:00
MrMha 5740cd6c20 fix(build): separate UTF-8 and control-flow test registration 2026-10-11 22:52:58 +04:00
Avijit Singh 2e0dcd7f57 docs(agc): record write tracker shutdown ordering
Document why the Driver singleton must initialize the guest write tracker before its static construction completes on Windows.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 00:19:27 +05:30
Avijit Singh 471d9eced2 fix(agc): keep write tracker alive through driver teardown
Initialize the guest write tracker during Driver construction so its function-local static is destroyed after Driver on process exit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-12 00:19:21 +05:30
Achraf Reyani b610642c61 fix(libkernel): close an unopened descriptor on Windows without the invalid-parameter report
close() of a descriptor that is not open calls _close, which raises the
UCRT invalid-parameter handler before returning EBADF. In a relinked title
that handler is libc's crash reporter, so an ordinary close of a closed
descriptor printed a FATAL report and every thread's stack. The call now
runs with the thread-local no-op handler, as dup and dup2 already do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:47:39 +09:00
Achraf Reyani 10740f85f9 refactor(libc): define the stack candidate helper beside ReportBacktrace
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:42:52 +09:00
Achraf Reyani cf2ca2b103 fix(libc): list stack return address candidates in Windows backtrace reports
The abort() and UCRT invalid-parameter reports capture the stack with
RtlCaptureStackBackTrace, which stops at the first frame without unwind
data (guest code, or our libraries built without asynchronous unwind
tables), so they often end two or three frames in, before any caller.
The fault report already scans the stack for code addresses; that scan
is now a helper the backtrace reports use as well.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 03:41:02 +09:00
PrintOrb 63434686bf fix(videoout): support permitted high-frame-rate output 2026-10-11 13:40:31 -05:00
Mouad-Scriptz-0 876c4ecacf test(recompiler): pin that an unwritten window register reads 0
Two more agc_driver_relative_indexing rows read a register of the window
the program never wrote, directly and through v_movrels_b32, and expect 0,
which the zeroed window array gives like an unwritten SSA register did;
the TechnicalDebt entry says the console's value is undefined.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 18:36:09 +00:00
Dean Galvin d9afe1078e test(agc): enter whole quad mode in the implicit-LOD 1D sample test
A pixel wave starts with only its covered pixels in EXEC, so a shader that
takes an implicit LOD has to run s_wqm first or the helper lanes of a partly
covered quad hold no coordinate. The test's pixel shader did not, which goes
unnoticed while its one triangle covers every quad whole. llvmpipe clips that
triangle at twice the viewport into three, and in the quads on the two inner
edges the helpers' zero coordinate gave the last mip instead of the expected
one.

The shader now starts with s_wqm_b64 exec, exec, as a compiled pixel shader
that samples with an implicit LOD does.
2026-10-12 07:35:46 +13:00
xXPBSXx 471e12e484 fix(libSceCesCs): drop the unused module target and move the TechnicalDebt line
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 20:35:09 +02:00
Jordan Hans b1104a2c65 fix(libSceSysmodule): recognize the libSceAgc internal module id 2026-10-11 18:33:48 +00:00
xXPBSXx 4eb3340dcb fix(rudp): throw from sceRudpGetMaxSegmentSize and justify the success of the pump calls
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 20:31:52 +02:00
Adrià Franch 1462312ebf docs(savedata): record the inferred sceSaveDataConvert contract 2026-10-11 20:28:50 +02:00
Mouad-Scriptz-0 f9fd0f125c fix(recompiler): give out-of-range relative vector accesses the measured semantics
M0 is unsigned, so an index at or past the window reads v0 and drops its
write, v_swaprel_b32 writes neither side when either is out of range, and
the window array starts zeroed like unwritten SSA registers; the window
load counts as a lane source so a wave64 program's second half keeps its
own registers. agc_driver_relative_indexing runs in wave32 and wave64 and
pins the rows measured on gfx1035 in the review of #3856, and the
TechnicalDebt entry states the measured behaviour, the window's end at the
last named register and the sizes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 18:28:02 +00:00
Alex 95988c9c34 fix(shader): flush f16 denormals in unary, frexp and conversion ops per the float mode
The f16 unary ops, v_frexp_mant_f16, v_frexp_exp_i16_f16, the norm
conversions and v_cvt_f32_f16 read denormal halves as they are and
rounded denormal results as they are, whatever the denormal field of
FLOAT_MODE says. On RDNA2 a clear bit 0 flushes a denormal half input
to its signed zero and a clear bit 1 flushes a denormal half result.

One shared rule now drives all of them, and v_ldexp_f16 uses it for its
input too.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 14:27:58 -04:00
Chad eedf7a2671 feat(savedata): implement sceSaveDataConvert
PPSA28416 calls it on its existing save at boot (after sceSaveDataDirNameSearch finds it) and aborted on the missing implementation. The call takes one parameter block: user id at +0, the directory name pointer at +8. Host saves have no size limit, so the conversion completes at once: an existing save returns OK, a missing one NOT_FOUND, an invalid name PARAMETER. The title polls sceSaveDataGetEventResult once afterwards and continues on 'no event'.
2026-10-11 13:23:58 -05:00
Tyler Westerberg 565e8e9c4a feat(libSceJson2): implement Object::find, Object::iterator::operator-> and String::compare
Goat Simulator 3 (PPSA04159) imports sce::Json::Object::find(String const&)
const (ehN1Biw3tw8), Object::iterator::operator->() const (EnesV4gXDtE) and
String::compare(char const*) const (Yfw63FVJgtU); without them the Windows
entry stub stops before the game starts with "unresolved ELF import
ehN1Biw3tw8".

find returns an iterator to the first pair whose key equals the argument,
or end(), using the same key match as Object::operator[]. operator->
returns the pair like operator*. compare orders the string against a C
string as std::string::compare does; a null argument throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:11:39 -04:00
Lexroid 5cd25dedbd fix(videoout): accept a buffer attribute change while a flip is pending
sceVideoOutSubmitChangeBufferAttribute2 threw when a buffer of the group
had a flip pending, and Marvel's Wolverine makes that call with flips in
the queue. A flip now takes its buffer group when it is reserved, in
submission order, so flips submitted before the change keep the
attributes their buffers were rendered with and later flips use the
new ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:06:04 +03:00
Adrià Franch c61f07409f test(shader): cover clamp alone and inf/NaN/fraction rows in v_cvt_i16_f16 output modifiers
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 19:58:44 +02:00
gookie d33dad00b8 fix(libSceXml): add an export-free libSceXml for titles that list it
PPSA19546 (Cookie Clicker) and PPSA34959 (The King is Watching) list
libSceXml.prx among their needed libraries but import nothing from it.
AnyPS5 builds no such library, so on Linux the dynamic loader stops both
before they run:

  ./eboot.elf: error while loading shared libraries: libSceXml.prx:
  cannot open shared object file: No such file or directory

On Windows the start-up stub has skipped a missing library that nothing
imports from since #3129; the Linux loader resolves DT_NEEDED itself
and cannot skip one. libSceXml now exists with no exports, like
libSceAgcVsh and libSceAudio, so it loads on both. A title that imports
from it still stops at the missing import.

Both titles now load and stop at their next missing import:
setSpecialFloatFormatType (PPSA19546, #3575) and sceHttpsFreeCaList
(PPSA34959, #3497).
2026-10-11 19:58:13 +02:00
Adrià Franch 07af520c1c docs(technical-debt): move the vieBRwlh1Lw entry off the end of Functional
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 19:58:11 +02:00
goncalompontes fa726efe94 test(shader): cover the VOP3 interpolation result and source modifiers
Extend the decode test to mul:4, div:2, -|v2|, -|v2| div:2, a p2
coordinate with neg and a non-zero attribute channel, a clamped p2, and
the mov form with clamp and mul:2. All 18 accepted words are the
assembler output (llvm-mc -mcpu=gfx1010).
2026-10-11 18:57:55 +01:00
Lorkydey 36d37a062a fix(libkernel): limit UTC-to-local DST output to 32 bits 2026-10-11 19:56:47 +02:00
Adrià Franch fdf8246f5e docs(architecture): describe APR and AMM command buffer queues 2026-10-11 19:56:25 +02:00
Ayan d700567008 perf(relinker): walk fallthrough instructions without queueing 2026-10-11 17:55:39 +00:00
SandstromPL 7f0c5d9d4d fix(relinker): accept the input's directory as a module directory
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:21:34 +05:30
krishnaraya b3be79ffed fix(agc): keep FORCE_DEGAMMA paired when ANISO_OVERRIDE rebuilds a sampler
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 13:50:25 -04:00
Adrià Franch 896c958236 docs(technical-debt): record that sceSaveDataDirNameSearch rejects sort key 4
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 19:49:46 +02:00
virp-mta 302a8774ac feat(libc): implement drand48, lrand48, mrand48 and srand48 2026-10-11 21:48:56 +04:00
Semir Kabir e92547a7a1 feat(libkernel): wake each listed address in _umtx_op UMTX_OP_NWAKE_PRIVATE 2026-10-11 17:48:06 +00:00
Yunus Shaikh 9b1e1ecccd fix(savedata): sort sceSaveDataDirNameSearch results by the requested key and order
The search returned saves in host directory order and ignored the condition's key and order. A title that asks for the newest save first (modification time, descending) got whichever directory the host listed first: the alphabetically first on NTFS, an arbitrary one on Linux. Sort by directory name, user parameter or modification time, reverse for a descending order, and reject unknown keys and orders with the parameter error, as shadPS4 does.
2026-10-11 10:46:53 -07:00
Kikoarl 7714a5c2db feat(libSceSysmodule): register libSceShare module ID (0x10F) 2026-10-11 19:45:25 +02:00
Daan Dehing 780303049b test(agc): cover indexed rect-list draws with a partial last rectangle
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:39:47 +02:00
billythemenace2 e2bfacefa3 feat(libSceRudp): declare the calls PPSA06888 imports
PPSA06888 stops on main at start-up because its bundled
Media/Plugins/SceRudp.prx imports sceRudpEnableInternalIOThread2,
sceRudpGetOption and sceRudpGetRemoteInfo, which no library exports.

The signatures are unknown, so they are declared without parameters and
throw. The title does not call them during start-up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:39:22 +01:00
Zayd Zaari 4d15f9ef24 fix(rtc): recognize additional measured named time zones
Add thirteen offsets measured repeatedly on PS5 firmware 13.42, keeping DST names ahead of their shorter prefixes. Cover case folding, prefix matching and a second valid date.

Written with AI assistance; validated against real hardware and the guest RTC tests.
2026-10-11 17:36:41 +00:00
Lexroid fbb4d3771d fix(libkernel): give AMM buffers their own queue
One queue for APR and AMM buffers kept an AMM buffer behind an APR buffer
waiting on an address that the AMM buffer writes, so neither ran.
Marvel's Wolverine does this: an APR buffer waits on a counter that the
WriteAddress commands of later AMM buffers advance, and its loading
stopped. APR and AMM buffers now keep their order within their own queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:32:11 +03:00
LeandroLP23 c27e67ceb5 test(libSceLibcInternal): run guest_force_tls_destructor on Windows
sceKernelGetModuleInfoFromAddr has a Windows path since 77ef75c1d, so
_sceLibcInternalForceTlsDestructor finds each dso's module there too.
The test takes the other module's address with GetProcAddress where
Linux uses dlsym(RTLD_NEXT), and the TechnicalDebt entry no longer says
the call throws on Windows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:31:41 +02:00
nyavana 42062da564 docs(build): clarify when explicit libs target is required 2026-10-11 10:28:32 -07:00
Daan Dehing 0418aaaad2 fix(agc): draw the complete rectangles of a rect-list draw with a partial last one
A rect-list draw whose count is not a multiple of 3 threw "incomplete rect-list
primitive". Rect lists are drawn as patch lists of 3 control points, and Vulkan
generates floor(vertexCount/3) patches, so the draw now records as is and the
trailing 1 or 2 vertices are left out. Palworld (PPSA23126) draws 4 vertices x 9
instances as a rect list when character creation opens (#3832).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:28:27 +02:00
gookie 73866db755 fix(pad): report special ports as not connected in scePadOpen
scePadOpen returned the standard pad's handle for a special port
(type 2) as well, and scePadGetHandle found it there. Both ports then
read the one input queue, so a press queued between two frames could be
drained by the special port's read and never reach the standard port.
PPSA16827 (Harmony's Odyssey) opens both ports for the initial user and
reads both every frame, also polling controller information and device
class data for the special one.

Special ports now return DEVICE_NOT_CONNECTED (0x80920007) from
scePadOpen, as scePadOpenExt already does because the host has no
special controller, and scePadGetHandle has no handle for them.
TechnicalDebt's scePadOpenExt entry now covers both calls.

guest_pad opens a special port before and after the standard one and
expects DEVICE_NOT_CONNECTED, and DEVICE_NO_HANDLE from scePadGetHandle
for the special port while the standard handle stays valid. It fails on
main.

PPSA16827 over 130 s of its title screen: 6812 scePadRead, 3408
scePadGetControllerInformation and 3406 scePadDeviceClassParseData calls
before, two reads of the same queue per frame; 2702 reads, one
information call and no parse calls after.
2026-10-11 19:26:30 +02:00
omurc4 d40189d707 feat(libSceVideoOut): export sceVideoOutAddBufferAttributeOption
Elden Ring Nightreign (PPSA25380) imports the function, which no library
exported, so the title failed to load. The option is ORed into the buffer
attribute and a null attribute throws. Options other than 0 and 8 still
throw when the buffers are registered.
2026-10-11 13:26:22 -04:00
Mouad-Scriptz-0 f8ed012e3e perf(recompiler): keep relatively addressed vector registers in a window array
`v_movrels_b32`, `v_movreld_b32`, `v_movrelsd_b32`, `v_movrelsd_2_b32`
and `v_swaprel_b32` lowered to a compare-and-select chain over every
register from the base to the program's last allocated register, so a
program addressing 154 registers 104 times turned 3176 ISA words into
649,515 SPIR-V words that Intel's compiler did not finish. The registers
from the lowest relative base up now live in a function-scope array per
invocation (`LoadVectorWindow`/`StoreVectorWindow`), so an indexed access
is one load or store; plain accesses to those registers go through the
same array, DPP-modified relative writes keep their `DppUpdateU32` merge,
an index past the window reads 0 and drops its write, and programs
without relative addressing are unchanged. The same program recompiles
to 392,333 words with 4,992 selects instead of 32,383.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 17:26:18 +00:00
fdfawef324 58669dc134 fix(libc): write each log line in one call
mingw's fprintf writes an unbuffered stream a character at a time and
holds the stream's lock for the whole line. The APS5_LOG_* lines that
carry a RecompileRequest ("Shader ... could not be prepared after
registration", 15-20 KB each) took one WriteFile per character, and every
other thread writing to stderr waited behind them, guest calls that log
included. Demon's Souls (PPSA01342 v1.005, Windows) stops on a black
screen after two frames on main: a BPE job worker waits for the stderr
lock in sceSaveDataDirNameSearch's trace while the shader preparation
pool prints its dumps, so GameCoreLoop never finishes a frame.

The log macros now format the line into memory and write it with one
fwrite, as Driver::reportSkip does since 8cf2f6580. The UCRT writes an
unbuffered stream's fwrite as one even-length block and the last byte,
so a line takes at most two writes on Windows and one on Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:23:21 -04:00
jackthereaper2006 2c769288c5 docs: record DOOM homebrew on Linux with an AMD GPU
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:22:45 +05:00
hugodearaujo d1631862f5 fix(libkernel): report the operation mode the console reports
sceKernelGetOperationMode returned 0 for both values, which were
unknown. A payload on a PS5 (firmware 13.60) reads mode 2 and submode 0;
nothing past the two ints is written and the result register holds no
status, so the function now returns nothing. PPSA12544 logs both values
and ignores the result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:20:54 -03:00
David f4424b5f1e feat(libSceAgc): implement the vieBRwlh1Lw interpolant mapping update
Hollow Knight Silksong (PPSA12544) imports libSceAgc NID vieBRwlh1Lw
(#699). Its name is unknown: it is not in aerolib, and the
sceAgcUpdateInterpolantMapping label other projects give it hashes to
SbuY2jN+axQ.

OpenAGC (src/game_compat.c at 3c7e807, sceAgcUnknownVieBRwlh1Lw) reads
it from FW 11.60 as the dbOlWdppb4o transform with the same (regs, gs,
ps) arguments, without the identity tail: it writes one register per
pixel shader input, none for a pixel shader without inputs, and rejects
a null pixel shader. It now shares the dbOlWdppb4o path and throws for
the rejected arguments.

For is_f16 1 and 3 inputs whose default_value and default_value_hi
differ, OpenAGC takes DEFAULT_VAL from default_value_hi, while KytyPS5,
sharpemu and dbOlWdppb4o here take it from default_value; these inputs
throw. On every other input the dbOlWdppb4o values equal OpenAGC's.

agc_interpolant_mapping runs the dbOlWdppb4o vectors and OpenAGC's
enhanced-mapping vectors through it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 20:16:47 +03:00
krishnaraya 8a0395381e fix(shader): accept DPP8 on v_pk_fmac_f16
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 13:16:11 -04:00
MarcZX100 42ac947f90 fix(agc): wake queue zero when workers become idle 2026-10-11 17:14:27 +00:00
lorkydey 329b9d63bb fix(relinker): normalize additional module directory paths 2026-10-11 19:12:52 +02:00
object05 6f527fa956 test(shader): accept the split fma sum in F32DenormalFlush
agc_driver_f32_denormal_flush fails on RADV RENOIR (Mesa 26.2.4):

  lane 20 no float mode v_fma_f32 v14, v4, v5, v6 is 0x00800000, expected 0x00ffffff

The driver lowers Fma to a multiply and an add: the compiled shader has
v_mad_f32 and no v_fma_f32. The product 0x1fffffff * 0x1fffffff is below
2^-126 and is flushed before 0x00800000 is added. Lane 20 is the only
row with a tiny product and a non-zero addend. v_fma_f32 and v_fmac_f32
fail on it in all three float modes; every other check passes.

When both return that sum, the two values are not checked. FmaRounding
already leaves out its fused forms on a device that splits fma.
2026-10-11 19:10:52 +02:00
MarcZX100 6693cd6301 fix(agc): scope idle writeback and test queue worker 2026-10-11 17:09:43 +00:00
gookie 28320acf2c perf(libkernel): read a loaded image's unwind tables once in sceKernelGetModuleInfoFromAddr
sceKernelGetModuleInfoFromAddr found the size of the image's .eh_frame
on every call by walking all of its CIE and FDE records, checking each
against every PT_LOAD header. The guest libc's unwinder calls it for
each frame it unwinds, so a title that unwinds often walked its whole
.eh_frame hundreds of times per second.

The tables read from a .eh_frame_hdr are now kept per header address
and reused while the image stays loaded. The cache is dropped whenever
dl_iterate_phdr reports that an object was unloaded (dlpi_subs), so an
address reused by another image is read again.

guest_module_info queries a module twice and expects the same tables,
then unloads it, loads it again and expects tables of the same size at
the same offset in the new image.

PPSA16827 (Harmony's Odyssey), title screen, over 500 calls per second
counted under gdb, which slows the title: the process used 9269 CPU
samples per 10 s, 53.7% of them in this walk, and 3306 with this change,
3.2% in the lookup. The title still runs at 27 FPS; its frame rate is
limited elsewhere.
2026-10-11 19:07:37 +02:00
xXPBSXx f854e52b26 docs: place the PPSA07411 TechnicalDebt entry away from the end of the file
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:06:38 +02:00
Semir Kabir 33c6e825ab feat(libSceUsbd): declare the device and handle calls for the no-device path 2026-10-11 10:06:27 -07:00
xXPBSXx ed0ea5e66b docs: place the PPSA30601 TechnicalDebt entry away from the end of the file
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:06:24 +02:00
winstrex 88e5a2e407 test(agc): split agc_driver_flat_apertures by shader instead of by row
Rows that run the same kernel at the same wave size compile the same
pipeline, so each part now takes whole groups of them, and the test is
split into fourteen parts. Each shader is compiled once per CI run
instead of in up to three parts at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:05:40 -03:00
xXPBSXx d7bf16a054 fix(exports): export the calls PPSA30601 imports
PPSA30601 (Resonance: A Plague Tale Legacy) fails to load without these imports that no library exports. Their signatures are unknown, so they are declared without parameters and throw until they are implemented. Adds a TechnicalDebt entry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 19:02:18 +02:00
David-cmrg 6200b8c7b5 test(agc): split the gated dispatch producer spin into short dispatches 2026-10-11 19:01:47 +02:00
MarcZX100 7c2eba0a6d fix(libkernel): validate APR command payload sizes 2026-10-11 16:57:06 +00:00
aayushmishraaa f0bca23deb fix(relinker): reject input executables that are not x86-64
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:26:47 +05:30
MarcZX100 0a6247532e feat(relinker): trap exact SDK syscall stubs on request 2026-10-11 16:54:02 +00:00
xXPBSXx 34688d3a69 test(exports): drop the PPSA07411 export test
CONTRIBUTING asks for no tests that only check that a symbol is exported.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 18:51:54 +02:00
jweidenh 804fcb41e3 test(agc): keep storage probe callbacks out of buffer pools 2026-10-11 12:50:37 -04:00
jweidenh 9f567c227e test(agc): report recorder test Vulkan device 2026-10-11 12:49:32 -04:00
jweidenh 747321d0bb fix(agc): retain storage textures before recording initial uploads 2026-10-11 12:48:00 -04:00
jweidenh a1e01e1850 test(agc): cover storage upload lifetime before submission 2026-10-11 12:47:32 -04:00
Alex fba96d6422 fix(libc): asin(|x| > 1) returns the x86 default NaN and leaves errno unchanged
On main asin(2.0) returns 0x7ff8000000000000 and sets errno to 33;
FreeBSD msun (lib/msun/src/e_asin.c) returns 0xfff8000000000000 and
never sets errno. asin now handles |x| > 1 itself with quiet
comparisons, so a NaN input still raises no exception. For |x| <= 1
and for NaN the host function is still called.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 12:46:30 -04:00
screamm f51180973e feat(libSceAudioOut): implement sceAudioOutSysGetHdmiMonitorInfo
Surround Sound Studio (PPSA99051), catalog build on main a11c8f7f with
the flat-interpolation fix, stops 16 s in, before its first frame
(orishamir, #3581 comment 6109839314):

    sceAudioOutSysGetHdmiMonitorInfo not implemented

The call takes (type, out, size); out must be non-null, size exactly
0x180 and type 1 (HDMI) or 0x10 (sainsaji/PS5-Audio-Passthrough-Research
c3be9c2a, docs/FINDINGS.md section 3 and AGENTS.md:25, firmware 12.70).
Titles read the monitor name at +0x0B, the descriptor count at +0x94 and
8-byte descriptors from +0x98 (same, FINDINGS.md hardware log item 1 and
sample-app/src/platform/ps5/bitstream_out.cpp:92-128).

Audio reaches the host through SDL as PCM and there is no EDID, so type 1
with a 0x180-byte buffer reports a sink without a name or descriptors:
the buffer is zeroed and 0 is returned. Surround Sound Studio's
Passthrough page then lists no formats; EVO Player's code treats an empty
list like a negative return. A null buffer returns 0x80260004, as the
other AudioOut calls do. Type 0x10, other types and other sizes throw:
their results and error codes are not known.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 19:45:00 +03:00
Adrià Franch 28962787b3 docs(technical-debt): describe the v_sin and v_cos series fallback
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 18:44:14 +02:00
Semir Kabir 2124cfba5f feat(libkernel): report the process affinity from cpuset_getaffinity 2026-10-11 16:43:36 +00:00
Alex 2f5d166e2b fix(shader): accept output modifiers on v_cvt_i16_f16 and ignore them as the hardware does
v_cvt_i16_f16 with mul:2, mul:4, div:2 or clamp in the VOP3 encoding
stops the recompiler with "VOP3 source modifiers are not implemented".
On gfx1036 (Radeon 610M) the result is identical with and without
mul:2, mul:4, div:2 and clamp combined with mul:2, for all 66 measured
inputs; clamp on its own is not a measured case.

The decoder now accepts omod and clamp on v_cvt_i16_f16 and drops them,
as measured. Clamp without an output multiplier is accepted by the same
rule: clamp with mul:2 gives the same hardware result as mul:2 alone.
Matches 528 of 528 rows in two float modes, 0 differing.
v_cvt_u16_f16 is not measured and stays refused.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 12:40:48 -04:00
MarcZX100 d57d3f0000 fix(agc): store pending results when queues go idle 2026-10-11 16:39:36 +00:00
A-Akhil 3a3504850a
fix(shader): take v_sin_f32 and v_cos_f32 from an exact series where the driver's result is more than 4 ulp off 2026-10-11 22:06:22 +05:30
MarcZX100 63a45aacdf docs: document Windows Dozen driver workaround 2026-10-11 16:35:54 +00:00
xXPBSXx a68bc9dfdc fix(libkernel): allow sceKernelMprotect on committed host memory outside the guest registry
Thread stacks are host-private memory; the call threw instead of setting the native protection.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 18:35:35 +02:00
xXPBSXx a03afed4c3 fix(exports): export the calls PPSA07411 imports
PPSA07411 (Resident Evil 4) fails to load without these imports that no library exports. Their signatures are unknown, so they are declared without parameters and throw until they are implemented. Adds one test and a TechnicalDebt entry.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 18:35:00 +02:00
Eli Yarson 8d72065b68 docs: record DOOM homebrew on Linux with an NVIDIA GPU 2026-10-11 13:34:27 -03:00
MiniRML 7a82dd4879 chore: sync upstream before VideoOut driver review 2026-10-11 09:34:01 -07:00
Adrià Franch 38cb3b12d1 docs(technical-debt): record the single source of sceAgcAcbAtomicGds_0900
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 18:32:21 +02:00
Achraf Reyani 5027dcedc7 fix(shader): accept signed less-than in runtime SRT values
#2072 taught the SRT evaluator SLessThan32, but IsRuntimeUniformOp still
left it out, so the runtime validator rejected any descriptor dword computed
through a signed comparison before the evaluator could compute it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 01:32:12 +09:00
Semir Kabir 5f123fd1b2 feat(libSceFiber): add the underscore thread frame pointer export 2026-10-11 09:30:02 -07:00
fdfawef324 9d96752fcd fix(libkernel): let munmap succeed over a range with nothing mapped
munmap and sceKernelMunmap over a range in which no page is mapped
failed: munmap returned EINVAL and sceKernelMunmap let the registry's
exception escape. Both now return 0. The console returns 0 for
sceKernelMunmap of a range it just unmapped and of a never-mapped
address, and EINVAL only for a zero length, as measured by prosper's
console oracle (kernelx.golden.tsv, kx_fmem_unmap_again,
kx_fmem_munmap_unmapped).

The other half of #3196, a range with an unmapped page in the middle, is
already fixed on main by 04ed61957; the test covers both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:59:32 +05:30
Semir Kabir 54b9b93240 feat(libSceUlt): add underscore mutex and queue create exports 2026-10-11 09:29:28 -07:00
Lorkydey 43783f725c feat(libSceAgc): build versioned ACB atomic GDS commands 2026-10-11 18:28:46 +02:00
screamm b2da41e542 test(libkernel): accept host stacks above the AMM range in guest_amm_placement
On Windows, real thread stacks that exist before the guest arena is
reserved can sit near the arena top, so the AMM range correctly lands
9-60 GiB below it and the 1 GiB bound failed about 1 run in 10. The test
now requires that no free 64 GiB window exists above the AMM range.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 18:26:44 +02:00
jweidenh 503043ca00 fix(libkernel): run pthread key destructors before thread release 2026-10-11 12:21:40 -04:00
Hussein Kadhim d4f1303d47 test(libc): cover the strtod sign cases for every variant and atof 2026-10-11 21:51:31 +05:30
Hussein Kadhim 216ced6c27 fix(libc): return positive zero when strtod, strtof, strtold and wide variants perform no conversion 2026-10-11 21:51:20 +05:30
jweidenh 6ae2a784af test(libkernel): cover pthread key destructor lifetime 2026-10-11 12:20:59 -04:00
Romans Pokrovskis 1e40599f24 test(libkernel): verify app info missing metadata errors 2026-10-11 19:17:25 +03:00
jagrat Singh 86bd3b6621 feat(libSceAcm): implement sceAcm_FFT and sceAcm_Panner 2026-10-11 21:40:58 +05:30
Adrià Franch 4f013785dd docs(debt): record unverified scePadSetVibrationMode mode 3
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 18:06:39 +02:00
mariusyo07 1e3a9345f0 fix(pad): accept vibration mode 3
Broken Sword Reforged (PPSA21480) calls scePadSetVibrationMode(handle, 3) after
scePadOpen and treats a failure as fatal (ps5_controls.cpp:19 "Sony call failed
with code 80920001"). Modes up to 3 are accepted, 4 and above stay invalid.

Co-Authored-By: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com>
2026-10-11 18:02:39 +02:00
MrMha 656005c2a4 fix(shader): handle cache file allocation failures 2026-10-11 19:57:45 +04:00
MrMha 474c2b9ff1 fix(libkernel): reject malformed UTF-8 in param JSON 2026-10-11 19:56:55 +04:00
MrMha e5c280fed4 fix(codegen): reject rewrites inside instructions 2026-10-11 19:56:00 +04:00
Hussein Kadhim 700f017eb6 test(libkernel): check that timed-out rwlock reads leave no reader count 2026-10-11 21:25:20 +05:30
Hussein Kadhim b02881cf0e fix(libkernel): fail pthread_rwlock_unlock with EPERM when the rwlock is not held
scePthreadRwlockUnlock called unlock_shared on the host mutex whenever the
caller was not the writer, which is undefined behaviour for an unlocked rwlock
or one write-locked by another thread. Track the reader count and return
EPERM like FreeBSD libthr when the caller does not own the write lock and no
read lock is held.
2026-10-11 21:25:20 +05:30
MrMha b443760ca4 fix(relinker): validate file-backed unwind record ranges 2026-10-11 19:55:09 +04:00
Lorkydey d71c51c22d feat(libkernel): implement wait through guest waitpid 2026-10-11 17:53:21 +02:00
MrMha 653eb704c2 fix(relinker): validate complete allocated section ranges 2026-10-11 19:52:18 +04:00
MrMha d85d1c268f fix(relinker): validate program layout ranges before writing 2026-10-11 19:51:17 +04:00
David 60a7f28112 feat(libkernel): implement sceKernelAioSetParam
Dying Light 2 (PPSA02262) imports sceKernelAioSetParam (9WK-vhNXimw) from
its eboot. In #1794 tv7 reported on main b5f5362d3 + #2129 (2026-10-10):

  FAIL: unresolved ELF import -q-vbO859Tw from libSceVideoOut.prx

with "12 other imports are still unresolved on `main`. In the eboot,
`-q-vbO859Tw`, `GTfJ64qFCPw`, `vwtVpHQKelI` (no known names),
`sceKernelAioSetParam` and `sceNpWebApi2GetMemoryPoolStats`." The import
is observed; its position in the loader queue after the nameless NIDs is
predicted.

The function fills one SceKernelAioSchedulingParam (five 4-byte fields,
20 bytes): Orbital cc408367 tools/ida/db_types.json:1103, SharpProspero
5dfdb4b0 Interop/Kernel/KernelAio.cs ("Sets every field of param at
once") and ampr_emu cfa85df3 ps5/include/sce/_fs.h agree on
(param, schedulingWindowSize, delayedCountLimit, enableSplit, splitSize,
splitChunkSize). No source gives an error code, so a null struct and
values outside SharpProspero's maximums (window and delayed count 0..128,
split flag 0 or 1, sizes up to 0x1000000) throw. The engine does not use
the settings, as sceKernelAioInitializeImpl ignores its struct; this is
recorded in TechnicalDebt.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit e45a2b950afe594212db2764529ccae32f84be8c)
2026-10-11 15:50:42 +00:00
Jan Šlégl e753080391 fix(libkernel): count guest module segments against the flexible memory budget
sceKernelAvailableFlexibleMemorySize and the flexible maps counted only
explicit flexible mappings, so the full 448 MiB was reported while the
title's executable and modules were loaded. A title that derives its code
and data size from the budget minus the available size got an unsigned
underflow.

The segments of the main executable and of every .guest.prx module that
the title's ELF gives permissions now count, rounded to 16 KiB pages, as
shadPS4 maps every non-system module segment as flexible memory. The
relinker's own blocks and the host system libraries do not count. The
charge ends when a module is unloaded.

Refs #2883

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
2026-10-11 17:48:05 +02:00
Alex b00efdb261 fix(shader): accept abs, neg and op_sel source modifiers on v_cvt_pknorm_i16_f16 and v_cvt_pknorm_u16_f16
v_cvt_pknorm_i16_f16 and v_cvt_pknorm_u16_f16 with |x|, -x or op_sel in
the VOP3 encoding stop the recompiler with "VOP3 source modifiers are
not implemented", although the RDNA2 hardware executes them.

These two opcodes now use the two-source f16 modifier path of
v_pack_b32_f16: op_sel picks the high half of either source, then abs
and neg apply. Other op_sel bits, clamp and omod are still refused.
Matches 15632 of 15632 rows measured on gfx1036 (Radeon 610M) in two
float modes, 0 differing.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 11:45:26 -04:00
Tharun Balaji a0a7155964 test(libkernel): cover creat error cases
Carry the additional GuestFilesystem checks from issue #3303 after its implementation landed on main. The declaration already exists on main, so keep one prototype.
2026-10-11 15:43:40 +00:00
overlordxrz-source 4891d625af feat(libSceNet): export sce_net_in6addr_linklocal_allnodes
Co-authored-by: eliasbruno124-dev <230422461+eliasbruno124-dev@users.noreply.github.com>
2026-10-11 11:41:05 -04:00
MarcZX100 e56a727375 feat(build): support Linux AddressSanitizer builds 2026-10-11 15:40:36 +00:00
MarcZX100 776cff0efb fix(libSceHttp): use native Windows condition waits
Use an SRW lock and native Windows condition variable for epoll waits, preserving the shared handle lifecycle. This avoids the scheduler-tick overshoot measured with std::condition_variable on Windows.

Suggested-by: winstrex
2026-10-11 15:40:29 +00:00
David 4c7d83a2bf feat(libSceHttp): implement sceHttpAbortWaitRequest and the unlimited sceHttpWaitRequest
Deathloop (PPSA01670) imports sceHttpAbortWaitRequest; Planet Coaster
(PPSA01736) and Palworld import sceHttpWaitRequest.

Epoll handles are kept in a registry with an abort flag, a destroyed flag
and a condition variable. A wait with a negative timeout blocks until
sceHttpAbortWaitRequest, sceHttpDestroyEpoll or the sceHttpTerm of the
last context wakes it; a bounded wait still returns 0 events. An abort
stays set and makes every later wait return SCE_HTTP_ERROR_ABORTED
(0x80431080); a wait woken by destroy or term returns 0 events.
sceHttpDestroyEpoll no longer frees a handle a waiter is blocked on.
sceHttpInit registers its context, and the epoll calls and sceHttpTerm
return BEFORE_INIT without a live context and INVALID_ID for an unknown
context or handle.

Behaviour and error codes: shadPS4 src/core/libraries/network/http.cpp
and http_error.h at a8a70da595da2a96e0410309472ba4a7eb558144.
Signature (void* eh): Orbital tools/ida/db_types.json at
b321fcdbd700cc02da1cb1ad25412781d5ec73c6 and SharpProspero
src/SharpProspero/Interop/Net/Http.cs at
60977b29103452b9681ae10c7a9011d44965bf16.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 766997df3f8329fd6e8e7aac2b5d83daf88c8292)
2026-10-11 15:40:17 +00:00
Darryl Fernandes c2f16e0e4d fix(libkernel): support RUSAGE_CHILDREN in getrusage
Co-authored-by: milescoviello <kitslayer29@gmail.com>
2026-10-11 11:39:12 -04:00
Utku Halis 99ddfa762a fix(libc): track never-collected pages in the macOS write watch
On macOS, GuestWriteWatchCollectFresh reported nothing and returned true, so
MarkWritten took pages no collection had armed yet for clean, and they stayed
writable without being tracked. GuestWriteWatchCollectArmed was a full collect,
which also took those pages.

Pages now carry a fresh bit from registration until a collection takes them, as
the Linux watch keeps its fresh ranges: CollectFresh reports and arms only fresh
pages, CollectArmed skips them, and Collect takes both. Protection changes, host
writes and faults keep the bit.

The write_watch fresh and armed tests check both collections.
2026-10-11 18:34:59 +03:00
Adrià Franch 6293e6ae63 docs(technical-debt): cover the single-item NpEntitlementAccess info requests 2026-10-11 17:34:43 +02:00
Adrià Franch 86da3e9d74 fix(libc): include cerrno in Math.cpp 2026-10-11 17:34:21 +02:00
JAB-dev e127a525d1 feat(libSceFont): implement sceFontCharacterGetSyllableStringState
The call was an unimplemented stub. It now writes syllable state 0 and
returns OK for every character outside the Thai block, where a PS4
firmware 11.00 libSceFont measures 1, 3 or 4; Thai characters throw as
not implemented. A null character or result returns
SCE_FONT_ERROR_INVALID_PARAMETER, and a null character writes 0 to a
non-null result, as measured. The signature follows SharpProspero.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 17:32:23 +02:00
MrMha cf1195ad20 fix(hw-oracle): narrow cache PR and normalize runtime paths 2026-10-11 19:31:39 +04:00
MrMha 19344d661a test(packaging): restore cwd before temporary cleanup 2026-10-11 19:30:59 +04:00
virp-mta e6dcb46f3a feat(libSceNpEntitlementAccess): implement offline entitlement info requests 2026-10-11 19:30:34 +04:00
MrMha 2f6db82da7 fix(nid-names): normalize Windows inventory paths 2026-10-11 19:30:28 +04:00
exefer 3ae2583802 fix(libkernel): select descriptors that are not sockets 2026-10-11 17:25:32 +02:00
Alex 1a6f6a9f13 fix(libc): match FreeBSD asinf errno
asinf returned 0xffc00000 (errno 33) for 2.0 (0x40000000), where FreeBSD
lib/msun/src/math.h returns 0xffc00000 (errno 0).

It now returns the FreeBSD result for that case. A guest test covers it
and fails on main.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 11:22:51 -04:00
Lorkydey 6744880450 fix(libc): parse atoi through guest-width strtol 2026-10-11 17:20:39 +02:00
JAB-dev b576273225 fix(libSceFont): return the letter step of sceFontWritingRefersRenderStepCharacter through a pointer
The second argument is an out pointer to the writing's letter step, as
KytyPS5 and the SharpProspero bindings declare it. It was filled as a
letter step struct, which wrote 64 bytes over the caller's 8-byte
pointer. The letter step now lives in the writing and the call stores
its address, or null when there is no current step.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 17:12:47 +02:00
David dc022d8418 fix(libkernel): let a guest unlink and rename files it has open on Windows
FreeBSD removes or renames a file that is still open; the descriptor stays usable. _wopen opens without FILE_SHARE_DELETE, so sceKernelUnlink returned EACCES and rename failed while the guest held a descriptor. sceKernelOpen now opens through CreateFileW with delete sharing and keeps the flag, mode and error mapping of _wopen.

(cherry picked from commit 2f5ada5c86a427505804f22def209e054b3571e4)
2026-10-11 15:11:38 +00:00
virp-mta 4b91755d9a feat(libSceSigninDialog): complete offline sign-in dialog lifecycle 2026-10-11 19:11:37 +04:00
Adrià Franch 8b73fe7a76 fix(stubgen): give new output files the default umask permissions 2026-10-11 17:11:35 +02:00
Ondra97 1f34f6e8d4 fix(relinker): preserve inputs when writing desktop assets 2026-10-11 17:11:26 +02:00
Adrià Franch a5465007fa test(conventions): merge duplicate import 2026-10-11 17:10:33 +02:00
Zurisen 3c6c5d5331 fix(libkernel): reserve the AMM ranges below host regions at the top of the guest arena
The AMM ranges were reserved at the last 64 GiB of the guest arena and
the reservation threw when that window was not free. On Windows the
stacks of the threads that exist before the arena is reserved (the main
thread and the loader's workers) are placed at random addresses and are
left in the arena as host regions; when one fell in the last 64 GiB, the
first AMM use threw "fixed guest mapping ... overlaps a host region in
the guest arena". Reserve the highest free 64 GiB window instead: the
last 64 GiB when it is free, and the window just below an occupied part
of it otherwise.
2026-10-11 17:09:12 +02:00
Alex 9ff132d9a5 fix(shader): accept the clamp bit on VOP3 float compares
The decoder throws "VOP3 source modifiers are not implemented" for
VOP3-encoded float compares (v_cmp_*_f16/f32/f64) that set the clamp
bit, so shaders using them fail to compile.

On RDNA2 hardware the clamp bit does not change the result of a float
compare: the measured results with and without it are the same. The
decoder now accepts the bit on float compares; clamp on other carry-out
and scalar-destination opcodes still throws.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 11:04:38 -04:00
goncalompontes 61d4494863 fix(shader): accept the inline constant third source of VOP3 interpolation
The assembler encodes the unused third source of the VOP3-encoded
interpolation forms as the inline constant 0 (src2 = 0x80, bit 25 of the
second word), which the decoder refused. Accept that encoding and refuse
any other third source. The decode test now uses the assembler words for
every accepted form and keeps the refusals one field away from a valid
word.
2026-10-11 16:03:46 +01:00
David 9ac583bddd feat(libSceAudioIn): implement sceAudioInHqOpen and sceAudioInAsyncOpen
Both open the same SDL capture port as sceAudioInOpen, so Input,
GetSilentState and Close work on their handles. HqOpen accepts only a
128-sample grain at 48 kHz (SharpProspero, KytyPS5); AsyncOpen returns
INVALID_SIZE above 384 samples (SharpProspero). Values the sources
disagree on throw. Format 0x10 still throws: only KytyPS5 accepts it.

Imported by FMOD-based titles, The Smurfs Dreams (PPSA21607) and Astro
Bot (PPSA21564); Ghost of Yotei (PPSA26344) calls sceAudioInOpen.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 16bfdf928d0a2da0b4473db2633a2e270538cbf5)
2026-10-11 15:03:16 +00:00
Nuno Silva 199225225f test(shader): cover SRT read use patching 2026-10-11 16:02:49 +01:00
Adrià Franch c81ee9288c docs(technical-debt): record fstatvfs deviations from FreeBSD 2026-10-11 17:01:55 +02:00
Claude 54e5aad214
test(libkernel): skip the read-only truncate cases when running as root
Root ignores the file mode, so truncating a file with only the owner
read bit succeeds and guest_truncate aborted when the suite ran as root,
as it does in containers. The read-only cases now run only for other
users and the test says on stderr when it skips them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 14:59:10 +00:00
MarcZX100 f942de360e test(libc): keep Windows unwind coverage within guest frames 2026-10-11 14:58:39 +00:00
Adrià Franch 9758607197 docs(technical-debt): record invalid-id returns of the other Http2 handle exports 2026-10-11 16:57:18 +02:00
Tharun Balaji c426143712 fix(libSceHttp2): clear completion queues for cascade-deleted requests
sceHttp2DeleteRequest erased the request's completion queue, but
sceHttp2DeleteTemplate and sceHttp2Term removed descendant request
handles through ReleaseOwnedHandles without clearing their completion
queues. A request that completed asynchronously before its template or
context was deleted kept a stale completion that sceHttp2WaitAsync could
still consume.

ReleaseOwnedHandles now returns every transitively removed handle id, and
DeleteTemplate and Term erase the completion queue of each removed
request. CompleteAsync validates that the request is live while holding
the handle lock and inserts the completion under it, so a send that races
a deletion either returns the invalid-id error without queuing or is
erased by the deletion; a completion is no longer re-created after a
deletion returns. The only lock nesting stays handles before completions,
and no mutex is held while the user event is triggered.

GuestHttp2 covers a queued completion before sceHttp2DeleteTemplate and
before sceHttp2Term: the completion is no longer consumable, a late send
returns the invalid-id error without queuing, and the removed handles are
rejected.
2026-10-11 16:57:12 +02:00
Tharun Balaji 4337b43f54 fix(libSceHttp2): validate SSL setter handles 2026-10-11 16:57:12 +02:00
Claude bd0998a7a8
fix(build): keep libc.prx needed by every executable that links a prx
Ubuntu's GCC links with --as-needed, so a test that calls nothing in
libc.prx directly dropped it from DT_NEEDED and satisfied the prx
libraries' __cxa_* and __gxx_personality_v0 references from the static
libstdc++ instead. Those copies interpose libc.prx's, so exceptions
thrown inside the prx libraries were searched by libgcc and resumed by
the guest unwinder, which aborted guest_kernel_stack_protect. libc now
propagates to every consumer of a prx with --no-as-needed on Linux.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 14:56:57 +00:00
lorkydey 5bd43e3e8e build(libc): register filesystem statistics source separately 2026-10-11 16:55:03 +02:00
QRMOE a06728b566
docs(libc): update remaining strftime gaps
Record literal handling while retaining unimplemented padding and timezone gaps.

AI-assisted: yes
2026-10-11 16:53:00 +02:00
Ori Shamir 3f4438dcca fix(pad): keep partial L2/R2 values from a controller
An SDL controller sets the L2/R2 bit whenever its trigger axis is above
zero, and BuildData then reported 255 for any set bit, so a partly
pressed trigger read as fully pressed. Report the analog value whenever
there is one; 255 remains for an injected press and for a bit without
an analog value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:52:56 +03:00
QRMOE 90a14e6a1f
test(libc): cover strftime literal conversions
Exercise unknown, repeated and incomplete conversions, percent escaping and buffer boundaries.

AI-assisted: yes
2026-10-11 16:52:30 +02:00
QRMOE 1b9179501f
fix(libc): preserve strftime literal conversions
Normalize FreeBSD 11.4 literal conversions before host formatting.

AI-assisted: yes
2026-10-11 16:51:42 +02:00
Dean Galvin c1287d73c5 fix(agc): leave the depth surface out when no enabled test has its plane
DecodeState bound the depth surface whenever DB_DEPTH_CONTROL enabled a
depth, stencil or depth bounds test and either plane was valid. A stencil
test beside a depth-only surface (DB_STENCIL_INFO format invalid, depth
test off) tests nothing, but the surface was still attached and the
render extent was clamped to it. A draw into a 1920x1080 target with a
960x540 depth surface left bound then covered only the top-left quarter.

A test whose plane is present but invalid no longer binds the surface.
Such a draw renders without a depth attachment, as it already does when
both planes are invalid. DrawRejection follows the same rule.
2026-10-12 03:43:27 +13:00
Adrià Franch a1d11ba4a2 test(relinker): drop unregistered evidence script for export aliases
The script needs a stock and a patched converter binary as arguments and is not
run by ctest; the C++ test covers the same cases.
2026-10-11 16:43:10 +02:00
Oguzhan Ozdemir 5ea9ce2e40 fix(relinker): allow identical Windows function export aliases 2026-10-11 16:43:07 +02:00
xXPBSXx abf6f06be4 fix(rudp): provide the calls PPSA03490 still lacks
PPSA03490 (Demon Slayer) imports sceRudpGetMaxSegmentSize, sceRudpProcessEvents, sceRudpNetFlush and sceRudpNetReceived, which no library exports. The first three answer by the library's initialised state; sceRudpNetReceived is declared without an implementation because its signature is unknown. Adds one test and TechnicalDebt entries.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 16:37:32 +02:00
LeandroLP23 d668b7c2b1 fix(shader): check NaN on the bits for ordered less-than and greater-than
On the AMD Windows driver 32.0.13058.8001 (Radeon 860M), a NaN compared
less than +infinity, or +infinity greater than a NaN, gives 1 when the
infinity reaches the shader as a constant, as in
agc_driver_f32_compare_constant's register operand case (#3536). The
same driver returns 1 for a NaN in an ordered not-equal (#825), which is
presumably what its compiler turns these compares into. FPOrdLessThan32
and FPOrdGreaterThan32 now AND their result with the integer NaN check
FPOrdNotEqual32 already uses, which gives the same result for every
input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 1cfbde5d69fe3b5c0aa482d4ff3551f0e87c3915)
2026-10-11 14:36:11 +00:00
MarcZX100 b0d7d8254f fix(shader): handle NaN below positive infinity 2026-10-11 14:36:11 +00:00
MarcZX100 e7c20b3033 feat(libc): export isnanf and __signbitl 2026-10-11 14:35:01 +00:00
Claude d7f1a6d3d5
fix(libkernel): translate host errno in the file functions' SCE errors
SceErrorFromErrno in the libkernel file functions passed host errno
values 1-34 through and turned every other value into EIO. Linux numbers
EAGAIN 11, which is FreeBSD's EDEADLK, so a read from an empty
non-blocking pipe returned SCE_KERNEL_ERROR_EDEADLK, and ENAMETOOLONG,
ELOOP, ENOTEMPTY, EDQUOT and EOVERFLOW, whose host values differ from
FreeBSD's, all became EIO.

Map those host values to the guest's FreeBSD numbers, as
SceIoErrorFromErrno in Open.cpp already does for EAGAIN, EDEADLK,
EDQUOT and EOVERFLOW. The guest constants the callers pass are all at
most 34 and never 11, or GUEST_ENOTEMPTY, so they are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ha6Tsy62AFUyp9a5Mf7vjA
2026-10-11 14:32:44 +00:00
Achraf Reyani 27599ccfbf fix(libc): align mspace allocations to 32 bytes
Clang's PS4 and PS5 targets set the default new alignment and the suitable
alignment to 32 bytes, so PS5 code may store to a fresh mspace block with
32-byte-aligned AVX instructions, as the guest heap already allows since
#1621. The mspace allocator aligned blocks to 16 bytes only. Every mspace
allocation is now aligned to at least 32 bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:32:41 +09:00
alfalfa-star 31de083006 test(agc): require every compiled mesh program to read NumWorkgroups
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:30:37 +02:00
alfalfa-star 4d0d3c06e8 fix(recompiler): read NumWorkgroups in every mesh shader so RADV sizes its user data
RADV (Mesa 26.2.4, GFX12) updates the user SGPR count and draw-id flag
it uses for a mesh dispatch only when the bound mesh shader has the
grid-size/draw-id slot. A mesh shader reading neither NumWorkgroups nor
DrawIndex keeps the previous vertex pipeline's values: the dispatch
opens a 3-register SET_SH_REG, writes one value, and the CP parses the
following DISPATCH_MESH_DIRECT as register data. The gfx ring then
times out and the GPU is reset with VRAM lost.

Kena: Bridge of Spirits (PPSA01802) hits this on its layered volume
draws, which interleave with vertex draws in one command buffer.
Confirmed with RADV_DEBUG=hang (the dispatch header shows up as
SPI_SHADER_USER_DATA_GS_4) and the CP header history in the amdgpu
devcoredump.

Every mesh shader now loads NumWorkgroups.x and sets zero vertices
when it is 0, which no dispatch produces, so RADV gives it the
grid-size slot and writes as many values as it announces.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:30:37 +02:00
MrMha 0ffd244af2 fix(ci): preserve test discovery and skip diagnostics 2026-10-11 18:25:36 +04:00
QRMOE 97409a6d08
test(libc): cover dirname result length boundaries
Cover 16 paths with long leaves, exact-fit and overlong directory results, repeated separators, input preservation and guest errno. The regression reports 12 failures on unchanged main and passes with the accompanying fix.

AI-assisted: yes
2026-10-11 16:25:14 +02:00
MrMha 563b4738a5 fix(hw-oracle): invalidate cached builds when inputs change 2026-10-11 18:24:42 +04:00
MrMha 5ccc11642f fix(packaging): validate and stage release archives 2026-10-11 18:23:59 +04:00
QRMOE f133aaa4b0
fix(libc): check dirname result length
Find the directory component before checking the result buffer limit, matching FreeBSD 11.4. Preserve input and guest errno on success; overlong directory results still report ENAMETOOLONG.

AI-assisted: yes
2026-10-11 16:23:53 +02:00
Adrià Franch 4abf052341 docs(debt): record zero default for unexported pixel inputs 2026-10-11 16:23:32 +02:00
MrMha 83be8dfb27 fix(stubgen): protect input files and keep stdout valid C++ 2026-10-11 18:23:13 +04:00
MrMha 08b1b7808a fix(nid-names): ignore comments and literals in symbol inventories 2026-10-11 18:22:21 +04:00
MrMha 14bbb1e00f fix(pr-overlap): bound explicit dependency declarations 2026-10-11 18:21:44 +04:00
Alberto Ruiz 029430690f test(json2): destroy the value before constructing over it
ValueClear constructs a Value, then calls the Value(const Object&)
constructor on the same object. The constructor allocates a new node and
overwrites the pointer, so the first node (56 bytes) is never freed.
LeakSanitizer reports it; destroy the value first.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R5WJS4ZdQynAJesCZKH5hb
2026-10-11 16:21:15 +02:00
MrMha 45f349df9b fix(conventions): handle unusual Git paths and escaped links 2026-10-11 18:21:09 +04:00
xXPBSXx 66165443c0 feat(libSceCesCs): add sceCesRefersUcsProfileCp1251 and sceCesMbcsUcsContextInit
PPSA15246 (Teardown) imports both from the module-named libSceCesCs-module and
stops at the loader without them. Cp1251 joins Cp1252 in sceCesSbcToUtf8 and
sceCesUtf8ToSbc; the context initializer succeeds without storing anything
because its layout is unknown (listed in TechnicalDebt.md).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 16:20:47 +02:00
MrMha d6bc502765 fix(import-audit): validate ELF and PE exports before reporting 2026-10-11 18:20:18 +04:00
Nuno Silva baf376862f perf(shader): avoid copying SRT read uses 2026-10-11 15:19:53 +01:00
XairRrR 2f140cb8b0 fix(agc): read zero for pixel inputs of parameters the vertex stage does not export
A pixel input whose SPI_PS_INPUT_CNTL offset names a parameter the
previous stage does not export failed validation ("graphics interfaces
disagree"), so the draw threw. No export writes that parameter slot for
the draw. radeonsi programs a pixel input without a matching vertex
output as OFFSET 0x20 with DEFAULT_VAL 0, so that it reads (0, 0, 0, 0)
(SI_PS_INPUT_CNTL_UNUSED in si_shader.h, applied in si_shader.c), and
rect-list draws already pass zero for such inputs.

Before the pixel stage of a draw is prepared, inputs that read a
parameter the previous stage's program does not export now read zero:
OFFSET bit 5 is set, DEFAULT_VAL and FLAT_SHADE are cleared, and an FP16
input also reads zero in its high half (USE_DEFAULT_ATTR1 set,
DEFAULT_VAL_ATTR1 cleared). Inputs that already read a default value
are unchanged.

Fixes #2975.
2026-10-11 16:14:12 +02:00
Alex 632338cd65 fix(libc): fmod leaves errno unchanged
fmod set errno (33) for 1.0, 0.0. FreeBSD msun never sets errno and
reports errors through floating-point exceptions only, so errno is now
saved and restored around the host call.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 10:07:19 -04:00
Celegans12 76fa2beceb perf(recompiler): reuse sorted sets for iteration selection merges
Seed each iteration-selection update from its first eligible successor
instead of rebuilding and intersecting the universe of block IDs.
Reuse the existing in-place intersection and sorted-insertion helpers
while preserving the original empty-successor and fixed-point behavior.
Keep a cache-neutral switch for same-binary output and timing comparisons.
Run the existing structurizer and control-flow fixtures in a separate
process with this optimization disabled as well as in their default mode.
2026-10-11 16:06:57 +02:00
Celegans12 f32bbf2e9e fix(cache): replace entries while Windows readers retain old files
Publish Windows cache entries with the POSIX rename operation so an
open delete-sharing reader keeps its original file after replacement.
Close the rename handle before returning and retain the existing
fallback only when the extended operation is unsupported.
Exercise old and new reader contents, deletion lifetime and temporary
file cleanup in the registered disk-cache regression on both platforms.
2026-10-11 16:03:37 +02:00
Celegans12 09894076d4 fix(agc): own resolved dispatch state through thread exit
The resolved-dispatch map was a nontrivial thread_local object.
MinGW can run its destructor after emutls frees the backing storage,
causing an access violation when a queue worker exits.
Use the existing ThreadOwned helper to destroy the heap-owned map
through its pthread key while keeping a trivial TLS pointer.
Reject FATAL output in both draw-cache test modes so a background
shutdown failure cannot pass only because the process returns zero.
2026-10-11 16:01:34 +02:00
EdouardSence b4b2e8843a docs(TechnicalDebt): list sceRazorCpuPushMarker under silent stubs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:59:53 +02:00
Scramblrrrr 363d17985e fix(relinker): lower legacy packed reciprocal register instructions 2026-10-11 10:58:14 -03:00
lorkydey 1ead4a120c feat(libc): implement guest fstatvfs volume statistics 2026-10-11 15:57:20 +02:00
Scramblrrrr 56d08f2547 fix(agc): exclude host arena holes from write-watch proofs 2026-10-11 10:55:48 -03:00
QRMOE 10e822cf04
test(libkernel): cover omitted clock resolution
Cover 20 clock IDs, preserved guest errno, non-null unsupported clocks and the SCE null-output fault. The regression fails against main and passes with the accompanying fix.

AI-assisted: yes
2026-10-11 15:53:15 +02:00
Ahir 48dcaace78 fix(shader): lock recompiler shared mutexes once when created
winpthreads initializes a statically initialized rwlock on its first
lock and fails a concurrent first lock with EINVAL, which
std::shared_mutex ignores, so two threads can hold the lock at once.
The source, binding plan and specialized module caches take their
first locks from several threads, so their mutexes now take and
release the lock in their constructor, as guest rwlocks do.

The GuestWriteWatch mutex the technical debt entry also named is in
the Linux-only watch, so it is not affected.
2026-10-11 19:22:13 +05:30
QRMOE 7fdc5b4528
fix(libkernel): honor null clock_getres output
Return success for a null POSIX resolution output before inspecting the clock ID, matching FreeBSD 11.4 sys_clock_getres.

AI-assisted: yes
2026-10-11 15:51:43 +02:00
Sanjaysrinivas 3d31a8464a fix(agc): reclaim idle buffers on allocation OOM 2026-10-11 15:50:46 +02:00
fablevoid 17e03d3c0a fix(libc): fail fgets with a size of 0 or less like FreeBSD
The guest fgets handed the size straight to the host. With a size of 0
glibc and the Windows runtime return NULL without setting errno or the
stream error indicator, and a negative size goes to the Windows
invalid-parameter path. FreeBSD's fgets checks n <= 0 before reading:
it sets __SERR on the stream, sets errno to EINVAL and returns NULL,
leaving the buffer and the file position untouched.

fgets_nid_postfix now does the same before calling the host. The
FileStream flag it uses was only set for encoding errors so far and is
renamed from SetEncodingError/encodingError to SetError/guestError; it
already stays set until clearerr like __SERR.

guest_standard_streams checks sizes 0, -1 and INT_MIN (NULL, errno 22,
error indicator set and cleared by clearerr, buffer and position
untouched) and that a following read still returns the line; it fails
without the change.

Fixes #3084

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:49:43 +00:00
MrMha 2eed99e770 fix(relinker): validate ELF section tables and file mappings 2026-10-11 17:47:50 +04:00
jweidenh 07297a54aa test(agc): verify rejected resource sets release storage images 2026-10-11 16:44:45 +03:00
XairRrR eba67d4130 fix(agc): render a slice of a 2D array color target into that layer of the array
A color view of slice N of a 2D array (CB_COLOR_VIEW SLICE_START, with
CB_COLOR_ATTRIB3 MIP0_DEPTH as the last slice) was rendered as a 2D
surface at the slice's address. The swizzle equations of the XOR tile
modes take the slice index (the detiler's slice bits), so the texels of
slice N of an array are not where a 2D surface at that address puts
them: a title that renders into array slices and then samples the array
read them scrambled.

The decoded target now keeps the array's address, its last slice and the
view's slice. A resident target that is a slice of an array attaches
that layer of the array's storage image, so the texels follow the
array's layout; storage images of 2D arrays are color attachments for
this, and an attachment view selects a layer. A slice whose array has
DCC, CMASK or an attachment proxy has no resident array image, and a
non-resident slice of an XOR-tiled array now throws instead of being
written in the 2D layout.
2026-10-11 15:44:20 +02:00
pushkarreddyy c3db0b0d15 perf(libSceAgcDriver): cap draw input snapshot allocation size
In #3338, Ratchet & Clank: Rift Apart (PPSA01474) hits a 36 GB / 5 s
memcpy stall at frame ~380 right after intro logos because the GPU
worker thread is blocked copying multi-gigabyte storage buffers on every
draw snapshot miss (specifically a 3.01 GiB streaming range
0x118f800000+0xbc200000 and a 232 MiB range 0x1171258040+0xe7fffc0
across 130,000 draws).

ShaderResources::PrepareDrawBindings now caps both per-allocation and
cumulative draw input buffer snapshot copies at 64 MiB by default,
overridable via APS5_DRAW_SNAPSHOT_MAX_MIB. Inputs exceeding the threshold
skip snapshotting and bind directly in place, preventing multi-gigabyte
memory copies while preserving standard small uniforms and draw arguments.
Budget allocation prioritises earlier descriptor bindings in deterministic
pipeline layout order.

Adds drawSnapshotCapTests in core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
verifying individual and cumulative capping, cross-platform cache behavior on
watched and compared hosts, and direct observation of subsequent CPU writes
on in-place bound ranges exceeding the cap. Exercised via --draw-snapshot-cap-only
registered as agc_draw_snapshot_cap in libSceAgcDriver/CMakeLists.txt.
2026-10-11 19:11:56 +05:30
DariusHQ 6ef110af48 feat(libc): implement wcstok
PS5MC (PPSA99420) stops at start-up on wcstok once sendfile is in. It
follows FreeBSD's three-argument wcstok over the guest's 16-bit wchar_t:
leading delimiters are skipped, the token ends at the next delimiter,
which is overwritten with 0, and *last is set to null when the string
runs out.
2026-10-11 16:41:50 +03:00
Lexroid 32a3047b5d fix(libkernel): run APR buffers past an unsatisfied wait on their own queue
sceKernelAprSubmitCommandBuffer ran the whole buffer on the submitting
thread, so a WaitOnAddress or WaitOnCounter that the submitter itself
satisfies after the submit returned never returned. A buffer now runs on
the submitting thread until such a wait; the rest of it, and every APR or
AMM buffer submitted after it, runs in order on one APR thread.
sceKernelAprWaitCommandBuffer and sceAmprAmmWaitCommandBufferCompletion
wait for that completion.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wu3nuU87RJwjkkmxqft3sD
2026-10-11 16:37:25 +03:00
Josip Parlov 93f5220d11 refactor(audioin): name X+4jdIS75P0 sceAudioInIsPadEmbeddedMic 2026-10-11 15:36:25 +02:00
Simon 69e3c1d115 fix(libSceNpTrophy2): accept a null details or data in GetTrophyInfo and GetGroupInfo
sceNpTrophy2GetTrophyInfo and sceNpTrophy2GetGroupInfo rejected a call
unless both outputs were given, although both already fill each output
only when it is not null. PPSA18391 (Clickteam Fusion) queries trophies
with a null details. Only a call with neither output is rejected now, as
in sceNpTrophy2GetGameInfo.
2026-10-11 15:35:56 +02:00
marshmll 3d83999350 fix(build): require an SDL audio backend on Linux
The bundled SDL builds its PipeWire, PulseAudio and ALSA drivers only
when their development files are found, and otherwise keeps just the
silent dummy and disk drivers without an error. The Linux CI and
release jobs install none of them, so the v0.1.1 Linux libraries
(libSceAudioOut, libSceAgcDriver, libfmod) contain no audible driver.

Fail configuration on Linux when SDL found none of the three, install
the Debian development packages in the Linux build and release jobs,
and list the packages in the build instructions.
2026-10-11 10:34:29 -03:00
Josip Parlov 265c982eaa refactor(agc): name zARR5aCmkoY sceAgcDcbAtomicGds_0900 2026-10-11 15:33:55 +02:00
MarcZX100 afcaf91019 test(agc): cover hand-built VGPR register offset 2026-10-11 13:32:20 +00:00
Hannah Lyons 81e3e34637 test(libScePosixForWebKit): pin fnmatch bracket class syntax to FreeBSD 11 2026-10-11 09:31:23 -04:00
lorkydey 1fc9b4d93f feat(libkernel): query real-time alarms with getitimer 2026-10-11 15:30:33 +02:00
EdouardSence 95a92f35e1 feat(libSceJson2): add sce::Json::Parser::parse with a read callback
PPSA04540 (Rise of the Ronin) imports it. The characters the callback
returns are parsed as by the (text, size) overload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:28:58 +02:00
Adelmo Junior 5334bd178c fix(libSceHttp): retain authority delimiter for partial URI builds 2026-10-11 10:28:39 -03:00
EdouardSence 3faaa4b8f0 feat(libSceJson2): add the sce::Json::Array copy constructor, clear, front and iterator::operator->
PPSA04540 (Rise of the Ronin) imports all four.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:28:30 +02:00
ducloc080202 ae07d4a7a1 fix(libc): quote the executable path in the throw-site test 2026-10-11 20:25:42 +07:00
ivica00 9b439b30e9 feat(libc): implement __powisf2
PPSA02456's FMOD plugin imports this float integer-power helper, which libc does not export.
2026-10-11 15:25:25 +02:00
JAB-dev 61a9f5dc14 feat(agc): render 1_5_5_5 UNORM color targets
CB_COLOR_INFO format 17 with UNORM and the standard component swap renders
into VK_FORMAT_A1R5G5B5_UNORM_PACK16, whose attachment and blend support
Vulkan requires. AMD keeps X in the low bits and Vulkan keeps R in the
high bits, so the exports are mapped with X and Z exchanged and the stored
bits follow the guest layout. PPSA06699 renders into this format.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 15:21:46 +02:00
Sanjaysrinivas 1fe527bbec fix(agc): route registered NGG workgroups
Defer registered NGG workgroup preparation until routing is known. Share the routing decision with static ABI resolution and draws. Gate the wave64 fixture on supported mesh subgroups; retain device-independent routing coverage.
2026-10-11 15:20:24 +02:00
DariusHQ bad19037a6 feat(libkernel): implement sendfile
PS5MC (PPSA99420) stops at start-up on sendfile. It follows FreeBSD 11:
headers, the file range and trailers go to a connected stream socket, the
file offset does not move, a non-blocking socket fails with EAGAIN after
a partial send, and *sbytes reports what was sent. The checks and the
point where *sbytes is written follow kern_sendfile.c.
2026-10-11 16:19:13 +03:00
DariusHQ b9fc448343 fix(libc): format positional arguments on Windows
The guest formatter threw on %n$ and *m$, which FreeBSD's vfprintf and
glibc accept. It now reads the arguments a numbered format names into a
table first, with the types their conversions give them, and follows
FreeBSD's rules for unnumbered conversions and unnamed arguments.
2026-10-11 16:19:13 +03:00
DariusHQ d9fe7ee614 fix(libc): accept the ' flag in Windows formatting
FreeBSD's vfprintf takes ' as the thousands-grouping flag. The Windows
formatter threw on it as an unsupported conversion. The guest's setlocale
only accepts the C locale, which has no thousands separator, so the flag
changes nothing: it is dropped before the host's printf sees it, and the
host's locale cannot add separators.
2026-10-11 16:19:12 +03:00
DariusHQ b1fe54e331 fix(libc): print 0 for %#.0o of zero on Windows
C and FreeBSD's vfprintf print a single 0 when # meets octal zero with a
precision of 0. The Windows formatter passed the conversion to the host's
printf, which printed nothing; it now asks for one digit instead.
2026-10-11 16:19:12 +03:00
David 88cd5bbde5 fix(libSceAgc): accept a hand-built GE_USER_VGPR register offset in sceAgcCreatePrimState
EVO Player (PPSA99039), Jelly5 (PPSA99505), Emby5 (PPSA99515) and
Nuvio PS5 (PPSA99997) stop on main 0f863c10 with

  what():  sceAgcCreatePrimState: invalid user configuration register offsets

All four build their shader headers by hand (EVO-PLAYER-PS5
evo_agc_shader_header.c, ported from ps5-xash3d-halflife) and store the
ge_user_vgpr_en special at 0x25c (GE_USER_VGPR1, value 0) instead of
0x262 (GE_USER_VGPR_EN), as analysed in #3457. They run on the console.

OpenAGC's reconstruction of the FW 5.50 sceAgcCreatePrimState
(src/game_compat.c at 3c7e8072) copies the four special register pairs
verbatim without checking their offsets; KytyPS5's offset checks
(src/libs/agc.cpp at 16a7fe68) are EXIT_NOT_IMPLEMENTED guards. The
special is now copied at the offset the header stores, not moved to
0x262; the driver reads neither register. GE_CNTL, VGT_SHADER_STAGES_EN
and a used VGT_GS_OUT_PRIM_TYPE are still required at their own
offsets because the driver reads them there, and the error now names
the special and the offset found.

With the change all four titles get past sceAgcCreatePrimState and stop
in the shader recompiler (GetImageResource dword 3 is not a valid
runtime value), as #3457 reported for EVO Player.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit aeaa5080d9595305aa73e74d966f989c88b79839)
2026-10-11 13:19:03 +00:00
David 6db0a4df7c fix(libkernel): sync read-only and directory descriptors on Windows
FreeBSD's fsync and fdatasync succeed on a descriptor opened read-only and on a directory. _commit needs write access, so both returned EBADF there. A read-only descriptor is now flushed through a write handle from ReOpenFile and a directory through a handle opened for it, as sceKernelSync already does.
2026-10-11 15:15:00 +02:00
Mouad-Scriptz-0 b63996f1ff fix(agc): read a merged program's vertex tables past its hidden user words
A merged (NGG geometry path) program's user data starts with eight
hidden words, so the vertex buffer and attribute table offsets of its
AGC header, which count from the first user SGPR, were read eight words
too early and found zeros. Killzone: Liberation (PPSA11142) stopped on
`AGC graphics: null vertex buffer table address` at the first frame of
its PSP game. The decoder now adds the hidden word count to the table
reads; the registers reported to the recompiler are unchanged, as it
already maps them from the first user SGPR. `agc_driver_graphics` gains
a merged and a plain layout case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 13:13:01 +00:00
David 9c7aa0d61a feat(libc): implement ftrylockfile
Porpoise PPSA99764 stops after pthread_attr_* (#3494) with the
predicted `FAIL: unresolved ELF import h05OHOMZNMw from
libSceLibcInternal.prx` (idx 311, call site 0x8EC37); RetroArch
PPSA99169 imports it at idx 448 (call site 0xA4A27). h05OHOMZNMw is
ftrylockfile by tools/nid_names.py and aerolib.csv (zecoxao/sce_symbols
2883963a); ps5-payload-dev/sdk f5d40f02 declares
`int ftrylockfile(FILE *)` in include/freebsd/stdio.h.

ftrylockfile tries the same host CRT stream lock that flockfile,
funlockfile and every locking host stdio call on the stream take, so
no second lock exists beside the one stdio uses. It returns 0 when the
lock is acquired or already owned by the caller (recursive, released by
as many funlockfile calls) and -1 when another thread holds it, as
FreeBSD 8c639945 lib/libc/stdio/_flock_stub.c:89-104 does. Linux calls
glibc ftrylockfile. UCRT has no try variant of _lock_file, so Windows
calls TryEnterCriticalSection on the CRITICAL_SECTION that _lock_file
enters (__crt_stdio_stream_data::_lock, ucrt/inc/corecrt_internal_stdio.h,
same layout in Windows SDK 10.0.19041, 10.0.22621 and 10.0.26100). A
null or closed stream throws. The UCRT layout dependency is recorded in
TechnicalDebt.

guest_standard_streams covers trylock on an unlocked stream, re-entry
by the owner, failure from another thread while held (also after one of
two unlocks and while another thread holds flockfile), stdio calls
from another thread blocking on the trylock-held lock, release order,
and null and closed streams.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 18:42:23 +05:30
Superhe11 83c9eb7af1 fix(agc): keep uncached storage images out of the resource cache 2026-10-11 16:12:02 +03:00
EdouardSence 295e48f059 feat(libSceNgs2): export sceNgs2ReportRegisterHandler and sceNgs2ReportUnregisterHandler
PPSA04540 (Rise of the Ronin) imports both and now loads past them;
calling either throws until NGS2 produces reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:11:24 +02:00
EdouardSence 9f3cc31f77 feat(libSceRazorCpu): add sceRazorCpuPushMarker
PPSA04540 (Rise of the Ronin) imports it. Like sceRazorCpuPushMarkerStatic
it does nothing without a capture and returns 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:09:35 +02:00
MarcZX100 a0e3d2098d fix(shader): omit sRGB variants for comparisons 2026-10-11 13:09:21 +00:00
thibaut db1f3c38ef fix(shader): drop masked selects proven unobserved through whole quad mode
A lane set contained in X is contained in WQM(X). Containment is proven
through BitwiseAnd and through ballots evaluated in the uniform entry
blocks, where every invocation is active. Under a whole quad mode mask the
masked lanes form whole quads, so implicit-LOD pixel samples, which only
read their own quad, do not observe them.

This removes the selects that kept the packed pixel ancillary input alive
after its fields were extracted in GTA V (PPSA04264) pixel shaders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-11 15:05:49 +02:00
Scramblrrrr 3943287d63 refactor(shader): drop the overlapping global loop join fallback 2026-10-11 10:00:07 -03:00
Scramblrrrr 3a80442635 fix(shader): validate conditional exits before accepting every fallback 2026-10-11 10:00:07 -03:00
Scramblrrrr b23b54a6d7 perf(shader): avoid rerouting synthetic loop-exit forwarders 2026-10-11 10:00:07 -03:00
Scramblrrrr 5781203184 test(shader): expect main to merge the shared exit tail without routing 2026-10-11 10:00:07 -03:00
Scramblrrrr 901c627862 fix(recompiler): preserve the guarded loop exit fallback before shared routing
Keep the AlxcBTW #24 fallback as a separate guarded attempt before enabling shared-exit and nested-continuation repairs. Add the reduced padded shader reported by orishamir on #2523 as an end-to-end regression; the abstract graph does not reproduce the same failure.
2026-10-11 10:00:07 -03:00
Scramblrrrr b0c86ec07f fix(recompiler): privatize shared continuations before loop gateways 2026-10-11 10:00:07 -03:00
Scramblrrrr e6c8e1e9c7 fix(recompiler): route shared loop exit selections in the guarded fallback 2026-10-11 10:00:07 -03:00
AlxcBTW 4df387d0c1 fix(recompiler): reject a loop-exit pass result that is not structured
The third structurize pass, which routes loop exits that leave the loop
before they meet, runs only when the plain and the privatized pass
throw, and its result was used without a check. Merged with #2471, it
turns some of those budget failures into graphs that spirv-val
rejects. In this one, selection 12 of the result lies in the selection
at 7 and branches to 7's merge block 13:

  {{1}, {2, 3}, {4}, {5}, {6, 7}, {}, {8, 9}, {10, 11}, {1}, {12, 4},
   {5}, {13}, {8, 14}, {15, 16}, {7}, {13}, {4}}

  block 12 exits the selection headed by block 12, but not via a
  structured exit

hasStructuredConstructs checks the third pass's result against the
structured control-flow rules of the SPIR-V specification (2.11), with
dominance and post-dominance taken over branch, merge and continue
edges. Back edges come only from a loop's continue block. A conditional
without a merge breaks or continues its innermost loop. A header
strictly dominates its merge, and a loop header its continue block. A
construct contains the merge of every header inside it and is entered
only through its header. It is left only to its own merge or to the
innermost loop's merge or continue block, and a continue construct only
to its loop's header or merge. When the result fails, the second pass's
error is reported, as when the third pass throws. The first two passes
are unchanged.

Differential fuzz with the structured-program generator and random
reducible graphs. Oracle: exact post-dominators,
requireStructuredBranches, merges dominated by their headers, the
original's executions, and spirv-val (SPIRV-Tools v2026.4).
- Against 9fa7d06a: the same outcome and output on every graph. That
  covers 100,000 random graphs at 0 words and with one block at 1000,
  and 30,000 programs at 16 and at 1000 words per block, each also with
  one block at 1000.
- Merged with #2471, the same 30,000 programs: the third pass had made
  41, 42 and 45 of them invalid (16 words, 16 with one block at 1000,
  1000 words). These end in #2471's own error again, and nothing else
  changes. The 1, 1 and 18 graphs that d59a9167 merged with #2471
  already structures invalidly come from the first two passes and stay
  as they are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:00:06 -03:00
AlxcBTW 6500daa012 fix(recompiler): route loop exits that leave the loop before they meet
A loop whose exit paths leave the loop construct before they meet got
the nearest common post-dominator of its exits as merge, and
splitSharedMergeBlocks never separated it. An exit continues the
enclosing loop, so its path to that merge runs through the outer
header again, or an exit enters a block that a branch before the loop
also reaches. splitOneLoopMerge gave the loop a forwarding merge block
each round, the other path still bypassed it, and the post-dominator
stayed the same block until "CFG shared merge splitting exceeded
budget". These shapes from #2642 fail that way on main, on d59a9167,
with #2471 and with both:

  {{1}, {2}, {3, 4}, {2, 5}, {}, {1}}
  {{1}, {2, 3}, {4, 5}, {}, {3, 6}, {1}, {2}}
  {{1, 3}, {2, 4}, {3, 1}, {4}, {}}
  {{1, 2}, {3, 2}, {4}, {4, 1}, {}}
  {{1, 2}, {4}, {3, 1}, {4, 2}, {}}

When the plain and the privatized pass both fail, Structurize now runs
a third pass. It routes such a loop's exits through one junction with
routeLoopExits, as loops whose exits have no common post-dominator are
routed already, and gives an outer loop whose only latch lies in an
inner loop its own continue block. In that pass a loop exit conditional
that is neither a direct break or continue nor has an exit tail of its
own is structured as a selection, and an exit tail that the other arm
reaches is not used as one. Without that, graphs the routing unblocks
keep a conditional without merge (the rule from #2762) or a selection
entered past its header. Graphs that the first two passes structure
are unchanged, and when the third pass fails too, the second pass's
error is reported.

Differential fuzz against main, reducible graphs with 5 to 9 blocks,
each with 0 estimated words per block and again with one block at 1000
words (the privatizeSharedReturns path). Oracle: exact post-dominators,
requireStructuredBranches, merges dominated by their headers, the same
executions as the original graph, and spirv-val (SPIRV-Tools v2026.4,
Vulkan 1.3) on a module emitted from the structured graph.

- 200,000 random graphs. main structures 178,743 (0 words) and 178,750
  (1000 words); the output is identical on all of them. Of main's
  14,256 and 14,311 budget failures 13,527 and 13,596 now structure,
  and 2,576 and 2,565 of its refusals. No new output is invalid. The
  invalid outputs are main's 530, plus at 1000 words one graph that
  d59a9167 already turns from a budget failure into a back edge to a
  block that is not a loop header.
- 24,000 structured programs from orishamir/anyps5-cfgfuzz's
  generator (if/else, loops, break, continue, return). main structures
  21,093 and 21,022; the output is identical. 619 of 2,448 and 634 of
  2,429 budget failures now structure. No new output is invalid; the
  invalid outputs are main's 92.

agc_structurizer has the five shapes; requireOriginalBranches now
follows route variables. agc_shader_control_flow has two RDNA programs
of the first two shapes. The non-GPU tests and the 253 agc_driver GPU
tests pass on Windows (RTX 5070 Laptop).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:00:06 -03:00
AlxcBTW 5182c031a7 fix(recompiler): merge a loop selection where its arms meet in the iteration
A selection in a loop whose arms both reach their nearest common
post-dominator within the iteration got that post-dominator as its
merge. When an arm can also continue the loop, the post-dominator
follows the back edge: the continuing path re-enters the loop header
and reaches the outer selection's merge without passing the block where
the arms meet. In the loop {1}, {2, 4}, {3, 4}, {4, 5}, {1, 6}, {1}, {}
from #2642 (latches 4 and 5) the selections at 1 and 2 both merged at 4.
splitSharedMergeBlocks gave 2 a forwarding merge block, recomputed 4 for
it because 3 continues through 5, and repeated until "CFG shared merge
splitting exceeded budget: originalBlocks=9 currentBlocks=45
splitBudget=36".

When that post-dominator is inside the loop construct, findSelectionMerge
now takes the arms' nearest common post-dominator over the paths of the
iteration that reach it, so paths that leave through the continue block
or the loop's merge no longer move the merge past the arms' join. When
no arm leaves early the result is the same block as before. The loop
structures with two forwarding blocks and no clones or route variables;
so do three latches, the selections on the false side, and an if-else
whose one arm continues.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 10:00:06 -03:00
LeandroLP23 ce0239c940 fix(libkernel): return lseek errors for directory descriptors instead of throwing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:59:57 +02:00
Hussein Kadhim 6db8c81aae docs(technical-debt): describe the Windows directory snapshot lifetime exactly 2026-10-11 14:59:57 +02:00
Hussein Kadhim cb6dcbf844 fix(libkernel): keep directory lseek off the shared lock and report offset overflow
lseek on a non-directory descriptor no longer takes the directory table lock when no directory is open, and a listing is read under the descriptor's own lock. SEEK_CUR with offset 0 only reports the position, and an overflowing SEEK_CUR offset reports EOVERFLOW.
2026-10-11 14:59:57 +02:00
Hussein Kadhim 7a68e11ab4 fix(libkernel): rewind Windows directory descriptors on lseek
lseek on a directory descriptor returned 0 without moving the listing, so getdents after lseek(fd, 0, SEEK_SET) reported nothing.
2026-10-11 14:59:57 +02:00
LeandroLP23 23dad48559 docs(debt): record the RDNA2 sweep for every f16 rounding mode of v_fma_mixlo/mixhi_f16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:59:56 +02:00
AlxcBTW bc517d6058 fix(shader): give an exact zero mix f16 sum -0 when rounding toward -infinity
With f16 rounding toward -infinity, v_fma_mixlo_f16 and v_fma_mixhi_f16
gave +0 for an exact cancellation such as 1.0 * 1.0 + -1.0, because the
f64 sum is computed to nearest. RDNA2 gives -0 there, as IEEE 754 does
for an exact zero sum rounded toward -infinity, measured on gfx1036 in
#2631. An exact zero sum now gives -0 in that mode unless both the
product and the addend are +0. A nonzero result that rounds to zero
keeps its sign, so a tiny positive sum still gives +0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:59:56 +02:00
AlxcBTW f22b9fa02a fix(shader): round v_fma_mixlo_f16 and v_fma_mixhi_f16 in every f16 rounding mode
The f16 forms of v_fma_mix threw for any FLOAT_MODE with an f16 rounding
other than to nearest even. ps5-opengl's shaders run with FLOAT_MODE
0xcc, f16 rounding toward zero with f16 denormals kept, so PPSA99005
stopped at its first draw (#2631).

The exact product-sum, already computed in f64 with round to odd, is now
rounded once in the mode's direction: scaled by the f16 step at its
exponent, truncated, floored or ceiled, and scaled back. A result beyond
65504 becomes infinity or 65504 as IEEE 754 gives for the direction. On
RDNA2 (gfx1036) -1.5 * 2^-24 + 2^-126 gives 0x8002 with round toward
-infinity, and this gives the same. Flushed f16 denormals still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 14:59:55 +02:00
ducloc080202 e9736282f7 fix(libc): name the throw site in unhandled exception reports 2026-10-11 19:59:31 +07:00
XairRrR 79c8d36901 fix(agc): reject constant color factors and memory-writing stages in split SWAP_ALT blends
Review follow-up to the 8_8 SWAP_ALT blending. The second channel of
such a target holds the guest's alpha. Vulkan applies the green blend
constant to it for CONSTANT_COLOR and ONE_MINUS_CONSTANT_COLOR, where the
guest's alpha equation uses the alpha constant. Decoding now throws for
these factors on a SWAP_ALT 8_8 target, unless the equation is MIN or
MAX, which ignore factors.

The second pass of a split draw runs every stage again, not only the
pixel shader, so a vertex or geometry stage that writes memory would
write twice. The memory-write check (now ShadersWriteMemory) covers all
the draw's stages.
2026-10-11 14:58:57 +02:00
jweidenh 82c04e4027 fix(shader): keep sRGB comparisons in the emulated runtime mode
(cherry picked from commit 5377951c552f2313c8f7a52b57bd7a54c5f7fb7f)
2026-10-11 12:54:54 +00:00
XairRrR 94cffec3a7 perf(agc-driver): prepare shader ABI variants before taking the preparation lock
A shader preparation transaction took the global writers lock when it
was constructed and kept it to its end. ResolveShaderAbi, reached from
sceAgcCreateInterpolantMapping and sceAgcLinkShaders, compiled its
variants inside such a transaction, so every game thread creating or
linking shaders waited behind one compile: Uncharted's stalled frames
spent about half a second each in shader registration.

The root transaction now takes the lock on its first Read, Edit or
Hold. ResolveShaderAbi prepares the variants before reading the
prepared state when its ABI was not committed yet. Registry
publication holds the lock as before: PublishRegisteredShader holds
it, and RegisterShader holds it before the driver mutex, so the lock
order is unchanged. Commit takes no lock: a transaction with changes
already holds it.
2026-10-11 14:54:48 +02:00
lorkydey cb9932007a fix(relinker): ignore no-op executable relocations 2026-10-11 14:53:59 +02:00
Hannah Lyons bf4cb4974d feat(libScePosixForWebKit): implement fnmatch 2026-10-11 08:52:48 -04:00
hugodearaujo bdf67fcc49 feat(libs): declare the PS VR2 exports PPSA15878 imports
Propagation: Paradise Hotel (PPSA15878) does not load on main: its
executable imports 15 PS VR2 functions that no library exports, the
first reported being sceVrTracker2GetPlayAreaOrientedBoundingBox
(snYs7Nf-RKk). Declare the nine libSceHmd2 reprojection and gaze
exports, three libSceVrTracker2 exports and three scePadVrController
trigger effect exports, without parameters, as throwing stubs. Names
come from the aerolib NID list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:51:00 -03:00
hugodearaujo a61e2d35dd fix(libSceSysmodule): return INVALID_VALUE for module id 0
Measured on a PS5 (firmware 13.60): sceSysmoduleIsLoaded,
sceSysmoduleLoadModule and sceSysmoduleUnloadModule with id 0, and the
Internal and InternalWithArg calls with id 0 or 0x80000000, return
0x805A1000 and leave the WithArg result untouched. They threw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 09:51:00 -03:00
Hannah Lyons 0e9a8dd949 feat(libkernel): export execv 2026-10-11 08:50:20 -04:00
Adrià Franch b33d2e3736 docs(debt): record the disabled user VGPR enable alias in sceAgcCreatePrimState
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 12:49:38 +00:00
MarcZX100 a9575dc70b fix(agc): normalize disabled user VGPR special offset 2026-10-11 12:49:38 +00:00
Abdulrahman Elkordy 8b0b78f529 test(libkernel): don't require restoring the directory time on Windows
The empty-path utimes check restores the working directory's write time
afterwards and requires the restore to succeed. With MinGW,
std::filesystem::last_write_time fails with EACCES when setting a
directory's time (a file's works), so guest_filesystem aborts there on
Windows. The restore stays, only its result is no longer checked.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 15:49:25 +03:00
MarcZX100 9e8b5a21bc fix(shader): allow constant-zero sRGB comparisons 2026-10-11 12:49:23 +00:00
ArjenS-LA 859736aa0e fix(net): preserve truncated UDP receives on Windows 2026-10-11 09:48:50 -03:00
MarcZX100 b316c9885d fix(libc): resume host foreign exceptions with host unwinder 2026-10-11 12:48:47 +00:00
MarcZX100 5550ca63dd fix(agc): wait for shutdown at shader ABI entrypoints 2026-10-11 12:48:40 +00:00
Hussein Kadhim d4301cf9b3 fix(libkernel): parse and print inet text addresses by the guest rules
inet_pton and inet_ntop no longer call the host resolver library, whose
accepted forms and output differ from the guest's (leading zeros,
zone ids, IPv4-embedded printing).

(cherry picked from commit 058d808ae7c43042d0ffd5fad76062a2df16b871)
2026-10-11 12:46:21 +00:00
pushkarreddyy 06f84010bb feat(libc): implement reallocf
Two catalog titles stop on the absent libSceLibcInternal export reallocf
(YMZO9ChZb0E) on main 0f863c10:

- PPSA97510 PSXS5 2.2.0
- PPSA98510 SwanStationPS5 1.1.3

    FAIL: unresolved ELF import YMZO9ChZb0E from libSceLibcInternal.prx

reallocf follows FreeBSD 11 lib/libc/stdlib/reallocf.c (freebsd-src
stable/11 5ed7eb0d97ba4436218e810f61bd059acba984c2) on top of the guest
realloc: it reallocates through the application heap, and when realloc
fails (the application allocator returns null or the default heap
rejects the size) it frees the block through the same allocator, sets
ENOMEM and returns null. Size 0 frees and returns null as realloc does
here, so the block is not freed twice. Other heap failures throw, as
for realloc. The NID matches tools/nid_names.py and aerolib.

The application heap tests cover grow, shrink, a null pointer, size 0
and failure with both the replacement and the default allocator.

Co-Authored-By: screamm <noreply@github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 18:16:07 +05:30
Mouad a553e22638 feat(agc): write memory for WRITE_DATA with DST_SEL 3
Killzone: Liberation (PPSA11142) writes its frame fence with
`sceAgcDcbWriteData(dst 6, cache policy 3, write confirm)`, which the
SPRX-confirmed encoding turns into WRITE_DATA with DST_SEL 3 and a label
address in the title's memory that RELEASE_MEM and WAIT_REG_MEM then
use. The validator rejected DST_SEL 3 as the GDS; it now writes guest
memory like DST_SEL 5, resolves as a label, and the title runs past its
first frame. TechnicalDebt records where the semantics come from.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 9c53b7f6e8ce2ca81d8f0fa551d12632acfeb60d)
2026-10-11 12:45:26 +00:00
Adrià a11c8f7f70
Merge pull request #3511 from krishalivaibhav/codex/fix-imgui-submodule-config
build: remove accidental imgui gitlink
2026-10-11 14:42:34 +02:00
BharatYadav007 8355b1f7b8 feat(libc): implement setlogmask 2026-10-11 18:07:14 +05:30
Hannah Lyons 6aa0b4fb22 fix(libkernel_sys): create symlinks with CreateSymbolicLinkW on Windows 2026-10-11 08:31:59 -04:00
krishalivaibhav fab9f576ae build: remove accidental imgui gitlink 2026-10-11 17:51:09 +05:30
Adrià 1e8fff68c8
Merge pull request #3481 from sypher69/fix/fchdir-search-permission
fix(libkernel_sys): check fchdir search permission on Linux
2026-10-11 14:09:47 +02:00
Winvoker c9038f7d8d fix(progress): match stub calls across whitespace 2026-10-11 15:06:06 +03:00
Adrià 2220effbe4
Merge pull request #3482 from MarcZX100/codex/rx-cross-block-3475
fix(agc): retain cross-block R_X coordinate XORs
2026-10-11 14:02:02 +02:00
Adrià 8b679d8d9e
Merge pull request #3498 from MarcZX100/codex/progress-inert-reporting
feat(tools): mark behaviourally empty functions in progress reports
2026-10-11 13:59:56 +02:00
Adrià 03708282b3
Merge pull request #3495 from Lorkydey/fix/relinker-registry-input-preservation
fix(relinker): preserve input binaries when writing registries
2026-10-11 13:56:53 +02:00
Adrià c253de1165
Merge pull request #3428 from MarcZX100/codex/fix-libkernel-open-empty-path-3308
fix(libkernel): reject empty sceKernelOpen paths
2026-10-11 13:55:48 +02:00
Adrià e783f19bc1
Merge pull request #3441 from MarcZX100/codex/fold-unordered-infinity-compares-3355
fix(recompiler): fold true unordered comparisons against f32 infinity
2026-10-11 13:55:17 +02:00
Adrià a5d75aa24e
Merge pull request #3490 from MarcZX100/codex/image-dimension-boundary-3460
test(agc): remove ambiguous 1D V wrap expectation
2026-10-11 13:54:57 +02:00
Adrià 7d7df0560e
Merge pull request #3491 from babyhuey/feat/libkernel-sys-symlink
feat(libkernel_sys): implement symlink
2026-10-11 13:54:51 +02:00
Adrià 6c4a84fbc6
Merge pull request #3492 from fable-void/pr/macos-module-start-args
fix(relinker): pass module start arguments to macOS guest initializers
2026-10-11 13:54:49 +02:00
Adrià eaffdc402f
Merge pull request #3489 from MarcZX100/codex/carry-equeue-level-once-3115
fix(libkernel): report level-triggered equeue events once per wait
2026-10-11 13:54:44 +02:00
Adrià bc193544f4
Merge pull request #3487 from DariusHQ/feat/libkernel-underscore-ioctl
feat(libkernel): export _ioctl
2026-10-11 13:54:41 +02:00
Adrià 84f43dcdac
Merge pull request #3486 from MarcZX100/codex/carry-sigsetjmp-3432
feat(libkernel): implement sigsetjmp
2026-10-11 13:53:51 +02:00
Adrià fd1a66cab6
Merge pull request #3484 from AchrafReyani/feat/libkernel-pthread-mutexattr-gettype
feat(libkernel): implement pthread_mutexattr_gettype
2026-10-11 13:53:23 +02:00
Adrià 2e2c531441
Merge pull request #2258 from krishalivaibhav/codex/spirv13-image-regression
test(shader): cover SPIR-V 1.3 image compilation without a GPU
2026-10-11 13:53:14 +02:00
Adrià c5a304a824
Merge pull request #3478 from fable-void/pr/macos-write-watch-remap
fix(libc): watch direct memory mapped over its reservation on macOS
2026-10-11 13:52:58 +02:00
Adrià c68b887799
Merge pull request #3479 from MarcZX100/codex/carry-arc4random-3450
feat(libScePosixForWebKit): implement arc4random
2026-10-11 13:52:52 +02:00
Adrià 673d1e65ca
Merge pull request #3476 from alfalfa-star/carry/select-non-socket-descriptors
fix(libkernel): answer select on the standard and random device descriptors
2026-10-11 13:52:21 +02:00
Adrià 6f7a12b0ee
Merge pull request #3472 from oneandonlydean/up4/fix-bda-note-write-nvidia-hang
fix(shader): set a BDA-written page's bit inside the slot probe loop
2026-10-11 13:51:42 +02:00
Adrià 23f274e785
Merge pull request #3467 from MarcZX100/codex/rudp-context-queries-3073
feat(libSceRudp): report disconnected context queue state
2026-10-11 13:51:11 +02:00
Adrià 6731a610e7
Merge pull request #3470 from Techx3/trophy2-show-trophy-list
fix(libSceNpTrophy2): return success from sceNpTrophy2ShowTrophyList
2026-10-11 13:51:05 +02:00
Adrià 712ae61a02
Merge pull request #3465 from MarcZX100/codex/fix-empty-kernel-paths-3308
fix(libkernel): reject empty filesystem paths
2026-10-11 13:50:57 +02:00
Adrià 544b38d094
Merge pull request #3474 from 014-code/fix/agc-resource-cache-storage-eviction
fix(agc): evict resource-cache images with storage entries
2026-10-11 13:50:49 +02:00
Adrià daa13905cb
Merge pull request #3434 from Yuva1024/fix/agc-nvidia-pipeline-link-optimization
fix(agc): skip the background pipeline relink on NVIDIA's proprietary driver
2026-10-11 13:49:43 +02:00
Adrià 6fd350f075
Merge pull request #3464 from GustavoGamarra95/test/agc-a2c-normalized-exports
test(agc): cover UNORM16 and SNORM16 exports with alpha-to-coverage
2026-10-11 13:49:40 +02:00
MarcZX100 8a390b06df fix(agc): XOR overlapping R_X coordinate contributions 2026-10-11 11:49:23 +00:00
Adrià b33b1b8e66
Merge pull request #3458 from alfalfa-star/fix/agc-dcc-writeback-keys-reused
fix(agc): store a storage image's DCC keys at write-back only when not stored yet
2026-10-11 13:49:15 +02:00
Adrià 3a37a896fa
Merge pull request #3455 from YossiYad/carry/select-non-socket-descriptors
fix(libkernel): answer select on non-socket descriptors
2026-10-11 13:49:06 +02:00
jweidenh 6199661506 test(recompiler): verify BDA store boundary faults and written pages 2026-10-11 07:46:41 -04:00
Adrià 08e0079e4d
Merge pull request #3451 from LeandroLP23/fix/libc-time-strftime-freebsd-conversions
fix(libc): format %k, %l, %s, %v and %+ in strftime
2026-10-11 13:46:24 +02:00
Adrià e38b5e95d2
Merge pull request #3300 from lendinglibrary/fix/3212-dispatch-split
fix(agc): split a compute dispatch over the workgroup count limit
2026-10-11 13:46:20 +02:00
Adrià cafc074e1a
Merge pull request #3454 from GustavoGamarra95/refactor/share-procmap-query
refactor(agc): share the PROCMAP_QUERY lookup with GuestMemory
2026-10-11 13:45:42 +02:00
Adrià 7ff417f8b9
Merge pull request #3449 from hypermoist/feat/np-entitlement-game-trials-flag
feat(libSceNpEntitlementAccess): declare sceNpEntitlementAccessGetGameTrialsFlag
2026-10-11 13:45:07 +02:00
Adrià 8f47444853
Merge pull request #3446 from MarcZX100/codex/tolerate-process-time-clock-skew-3422
test(libkernel): tolerate process-time clock skew
2026-10-11 13:45:05 +02:00
Adrià 83929de897
Merge pull request #3444 from Pipodi/fix/fused-hull-header
fix(agc): select the registered back half for a fused hull header
2026-10-11 13:44:59 +02:00
Adrià 612e93651c
Merge pull request #3453 from LeandroLP23/fix/libc-scanf-suppressed-eof
fix(libc): return 0 from scanf when input fails after a suppressed conversion on Windows
2026-10-11 13:44:57 +02:00
Adrià 97fbc7e629
Merge pull request #3447 from sypher69/fix/thread-process-affinity-inheritance
fix(libkernel): inherit process affinity in new guest threads
2026-10-11 13:44:45 +02:00
MarcZX100 05504bb05e fix(tools): retain literals when classifying inert returns 2026-10-11 11:44:32 +00:00
Leandro Soares 90a82da283 feat(tools): mark behaviourally empty bodies in the progress report
Cherry-picked from commit f1be92e95c02be451971fc1a8eec139e738e936d.
2026-10-11 11:44:28 +00:00
Adrià 077eeb05ea
Merge pull request #3448 from Feuris/carry/agc-debug-raise-exception
feat(libSceAgc): implement sceAgcDebugRaiseException
2026-10-11 13:43:54 +02:00
notaxeltv ae425ed920 feat(libSceTextToSpeech2): return EOPNOTSUPP from remaining speech exports
Titles that call Open/Speak after a failed initialize should get the same unavailable-engine error instead of a throw.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 13:43:46 +02:00
Adrià e5d4f59dac
Merge pull request #3438 from ivica00/fix/srgb-bilinear-cube-compare
fix(shader): return 0 for 8-bit sRGB color comparison
2026-10-11 13:43:27 +02:00
Adrià ea31cff19c
Merge pull request #3436 from MarcZX100/codex/rmdir-dot-einval-3116
fix(filesystem): reject final dot removal paths
2026-10-11 13:42:56 +02:00
Adrià 22b1782e01
Merge pull request #3442 from babyhuey/feat/libkernel-pthread-getthreadid-np
feat(libkernel): implement pthread_getthreadid_np
2026-10-11 13:42:48 +02:00
David dce174749d feat(libSceAmpr): implement _Cnd_signal and _Cnd_init_with_name
Deathloop (PPSA01670), Minecraft Legends (PPSA05510) and GTA III
Definitive Edition import both from libSceAmpr. They take a pointer to
the handle slot and act on the conditions created by the libc _Cnd_init:
_Cnd_signal wakes one waiter through a libc helper, _Cnd_init_with_name
is _Cnd_init with the name ignored. _Thrd_id still throws.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 13:41:49 +02:00
lorkydey 0cfdc79e59 fix(relinker): preserve input binaries when writing registries 2026-10-11 13:38:27 +02:00
MarcZX100 668a6db50d test(agc): avoid ambiguous row-boundary filtering 2026-10-11 11:34:47 +00:00
Hannah Lyons 6bc537ce01 feat(libkernel_sys): implement symlink 2026-10-11 07:31:49 -04:00
Hussein Kadhim 59517ef5f5 test(libkernel): cover blocking wait, edge and repeated AMPR equeue events
Document the level-triggered user event semantics of sceKernelWaitEqueue in TechnicalDebt.

(cherry picked from commit db79bee1e597effff34c72493ed893d4e750b95d)
2026-10-11 11:28:34 +00:00
Hussein Kadhim a9b7b20f8f fix(libkernel): report a level-triggered equeue event once per wait
A user event added with sceKernelAddUserEvent stays triggered until it is
deleted. The collection loop kept re-emitting it until the caller's event
buffer was full, so a wait with num > 1 returned the same event num times.
Like kevent, each event is now reported at most once per call; distinct
queued completions of one event are still delivered in order.

(cherry picked from commit db28ebbb7f078cb593cf6b7bb34b8db49dd94630)
2026-10-11 11:28:34 +00:00
AlxcBTW 66236674f0 fix(recompiler): keep lane 31 a shuffle without subgroup arithmetic
The half-wave scan rules for 32-lane hosts made lane 31 of a matched
scan a subgroup reduction and threw on a device without subgroup
arithmetic, where the read used to build as a shuffle of the scan. Such
a device now keeps the shuffle for lane 31; lane 63 still reads the
identity, which needs no arithmetic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:24:14 +02:00
AlxcBTW 98ce96a8b9 test(agc): run a wave64 NGG full-wave OR on a 32-lane device
A wave64 vertex program with the culling scan of #2783 (lane < s3's
primitive count, row_shr bound_ctrl:1 OR scan, v_permlanex16_b32,
v_readlane_b32 of lanes 31 and 63) compares the wave OR with the ballot
of its keys and collapses its triangle when they differ. On a 32-lane
device every pixel of the grid must be covered.

Before the s3 change the program does not build on 32 lanes; with a
shuffle from lane 31 instead of the subgroup reduction no pixel is
covered, as the device leaves lanes of each vertex warp idle. Other
subgroup widths skip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:24:14 +02:00
AlxcBTW 24894376ab fix(recompiler): give wave64 vertex programs on 32-lane hosts a 32-lane s3
A wave64 vertex program runs at one guest lane per invocation, so on a
32-lane host subgroup its lanes 32-63 have no invocation. The vertex
entry still set s3 = 64 | 64 << 8, telling the program that its wave
holds 64 vertices and 64 primitives. An NGG culling program then keys a
full-wave OR on lane < s3's primitive count, lanes 32-63 hold real cull
results, and its v_readlane_b32 of lane 63 cannot be built (#2783).

The entry now sets s3 = 32 | 32 << 8 for a wave64 program on a 32-lane
host, the wave the host runs; the console launches waves with fewer
vertices and primitives. The host subgroup width reaches the vertex
input the way it reaches the compute and mesh inputs
(BuildShaderStageInputInfo from HostSubgroupSize), and the source,
prepared-shader and disk cache keys already carry it for every stage.
Programs that do not read s3 are unchanged; wave32 programs and other
host widths keep the full wave.

The keys are then 0 in lanes 32-63, so the read is sound. On a 32-lane
host, when the existing half-wave scan rules fail, the scan may also:
- run under s_mov_b64 exec, -1 (EXEC constant true);
- use bound_ctrl:1 row_shr steps when 0, the fill, is the identity (OR,
  add, unsigned max), also into another register, whose old value never
  shows since such a step writes every lane;
- take keys shown to be 0 in lanes 32-63: built from 0 through selects
  under a lane id or v_mbcnt of -1 below at most 32, v_cmp_ne_u32 against
  0, ORs, DPP and permlanes, in stages whose entry EXEC is every lane.
Lane 63 is then the identity and lane 31 the subgroup reduction of the
host's invocations. A shuffle from lane 31 is not enough: an RTX 5070
runs the vertex warps of a non-indexed triangle list with 30 of their 32
invocations. A v_readlane_b32 outside the host subgroup of another value
that is 0 in lanes 32-63 reads 0. Only reads that used to throw change,
besides lane 31 of such scans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:24:14 +02:00
MarcZX100 a6cb65e481 fix(agc): retain cross-block R_X coordinate XORs 2026-10-11 11:23:47 +00:00
David b79c8e2143 feat(libkernel): implement sigsetjmp
XPSemu (PPSA97358) stops next at sigsetjmp (aRo9AhFUXcM) from libkernel,
per the body of #3323 "feat(libkernel): implement cpuset_setaffinity for
the calling thread or process": "XPSemu: with this PR, the next stop is
`sigsetjmp` (`aRo9AhFUXcM`)". RetroArch and PS5MC list it under libkernel
in the absent-import tables of #1578; aerolib names aRo9AhFUXcM sigsetjmp.

sigsetjmp is FreeBSD's amd64 sigsetjmp (lib/libc/amd64/gen/sigsetjmp.S,
release/11.4.0 8c639945), the counterpart of the merged siglongjmp (#2935)
and its sigjmp_buf layout (sys/x86/include/setjmp.h, _JBLEN 12): it stores
savemask at offset 88 and, when it is nonzero, saves the current mask at 72
through _sigprocmask(SIG_BLOCK, NULL, oset); then it records the return
address, rbx, rsp, rbp, r12-r15 and the x87 control word, and returns 0.

guest_signal_sets now jumps through the export instead of its copy of
FreeBSD's sigsetjmp, and checks that savemask 0 leaves the mask slot
untouched, a nonzero savemask records the current mask and its own value,
and the callee-saved registers come back after siglongjmp.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 8a609a5a1f189dce107a871e7da0fd55c746921f)
2026-10-11 11:23:10 +00:00
fablevoid 1b865fc3ea fix(relinker): pass module start arguments to macOS guest initializers
The macOS image initializer called a guest module's DT_INIT with no
arguments, so a module started with sceKernelLoadStartModule never saw
its args and argp, and its result was dropped. The Linux writer already
calls DT_INIT through libkernel's pending argument handoff. On the
macOS port, the PSN plugin of PPSA12544 refuses to start without its
argument block ("PSN is an old version that cannot be used by the
current player runtime") and the title then crashes.

A guest module with DT_INIT now gets two slots bound to
__aps5_get_pending_module_args_nid_no_patch and
__aps5_set_module_init_result_nid_no_patch, and its initializer calls
DT_INIT with the args and argp that sceKernelLoadStartModule records,
as on Linux, then hands the result back. The TechnicalDebt sentence
about the missing arguments on macOS is removed.

guest_module_args also relinks its fixture with --macos and checks the
two binds; on x86-64 macOS it loads the module and checks that DT_INIT
receives the arguments and that its result is returned.

AI-assisted: yes
2026-10-11 11:22:38 +00:00
DariusHQ b4a59e34c4 feat(libkernel): export _ioctl
Stremio Plus (PPSA74126) stops at start-up on _ioctl, which its statically
linked tc* functions call. FreeBSD's _ioctl is the same system call as
ioctl, so it forwards to ioctl.
2026-10-11 14:19:02 +03:00
Dani 0e4d94ce39 fix(libkernel_sys): check fchdir search permission on Linux 2026-10-11 13:18:30 +02:00
David 7083b2941a feat(libScePosixForWebKit): implement arc4random
Implement arc4random (UISQ-dQzw28) in libScePosixForWebKit on top of the
existing arc4random_buf, so both draw from the same thread-local host
std::random_device. Like FreeBSD 11.4 lib/libc/gen/arc4random.c
(8c639945) it returns a uniformly distributed 32-bit value and has no
failure result.

Cloud Play (PPSA99600) imports it: the import audit in #3249 on main
ccf9ff90 lists `UISQ-dQzw28` `arc4random` among nine absent imports
("no PR or issue found"). The ps5-payload-dev SDK stubs (f5d40f02,
sce_stubs/libScePosixForWebKit.c) place arc4random, arc4random_buf and
fnmatch in libScePosixForWebKit; arc4random_uniform is in neither that
set nor libSceLibcInternal, so it is not added.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 95da68017f6817abec785864e682530e9b135a2b)
2026-10-11 11:14:30 +00:00
Donson 16cfe85a38 fix(agc): evict resource-cache images with storage entries 2026-10-11 19:08:29 +08:00
Adrià Franch ddf6b8c984 test(libSceNpTrophy2): cover sceNpTrophy2ShowTrophyList, take the context argument 2026-10-11 13:08:03 +02:00
MarcZX100 58bf763e85 feat(libSceRudp): implement context query calls 2026-10-11 11:07:33 +00:00
Achraf Reyani 466c86255f feat(libkernel): implement pthread_mutexattr_gettype
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:05:46 +09:00
fablevoid 195682e190 fix(libc): watch direct memory mapped over its reservation on macOS
Register copied the protection it had recorded for pages it already
watched over the protection the kernel had just reported. A guest that
reserves address space, which registers it as PROT_NONE, then maps
direct memory over it read-write and registers it again kept the stale
PROT_NONE. Collect only write-protects pages with PROT_WRITE, so writes
to that memory were reported once and never again. With PPSA12544 on
the macOS port, the title menu text blinked and glyphs went missing.

Register now keeps a recorded protection only where its own write
protection explains what the kernel reports, and takes the kernel's
protection otherwise. A protected page that was mapped again also loses
its protected mark, so erase no longer restores the old protection over
the new mapping. The kernel is read under the lock, so a concurrent
Collect cannot protect a page between the read and the merge.

Test write_watch_mapped: a reservation, a mapping over it and a second
registration keep reporting writes, and a protected page reserved again
stays PROT_NONE.

AI-assisted: yes
2026-10-11 11:04:02 +00:00
Suery 6c96c2ee1e
fix(libSceNpTrophy2): return success from sceNpTrophy2ShowTrophyList
There is no system trophy UI, so the call returns success without showing a
list, like the other silent NpTrophy2 entry points; recorded in
docs/dev/TechnicalDebt.md. PPSA15552 (Dead Cells) calls it and
aborted on the NotImplemented exception.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Dqgm9nUKygsXzr6poJsHJ
2026-10-11 07:02:34 -04:00
MarcZX100 d26c9bcabc fix(libkernel): reject empty filesystem paths 2026-10-11 11:00:35 +00:00
Gustavo Ariel Gamarra Rojas a07ac32a77 test(agc): cover UNORM16 and SNORM16 exports in the alpha-to-coverage GPU test
The pixel program packs its color for the export format, and the test now draws with FP16, UNORM16 and SNORM16 exports: an alpha of 0.75 keeps the pixel, 0.25 drops it, and so does -0.5 with SNORM16. Passes on an RTX 3050, a Radeon 660M and lavapipe.
2026-10-11 08:00:05 -03:00
Gustavo Ariel Gamarra Rojas 109eb81d62 test(agc): check the MRT0 export formats alpha-to-coverage accepts
Formats 4, 5, 6 and 9 (FP16, UNORM16, SNORM16 and 32-bit float ABGR) decode with alpha-to-coverage; formats 1, 2 and 3, which carry no floating-point alpha, still throw.
2026-10-11 08:00:05 -03:00
Adrià 0f863c1022
Merge pull request #353 from adriadam10/feat/relinker-linux-desktop-entry
feat(relinker): write a desktop entry with the game icon for Linux executables
2026-10-11 12:59:31 +02:00
lendinglibrary 288ee89d2d fix(agc): split a compute dispatch over the workgroup count limit
A dispatch whose group count exceeds maxComputeWorkGroupCount aborted the
process; a software device (lavapipe) reports the spec minimum 65535, and
Ghost of Yotei (PPSA26344) dispatches 129199x1x1 in its presentation path.
The count is now recorded as consecutive vkCmdDispatchBase chunks of at
most the limit per dimension, each continuing the grid at its base offset
(a plain vkCmdDispatch would restart group ids at 0 and re-execute the
first chunk's workgroups), with a barrier between chunks so a later chunk
sees an earlier one's writes. Compute pipelines are created with
VK_PIPELINE_CREATE_DISPATCH_BASE_BIT, which a nonzero base requires.

A split on NVIDIA throws instead of dispatching: measured on an RTX 5070
Ti, the driver drops the first workgroups of a vkCmdDispatchBase dispatch
with a nonzero base (reproduced with a standalone Vulkan program outside
this repository), so its 65535 Y/Z limits cannot be split through. A split
of a dispatch whose LDS lives in device memory also throws: its buffer
slot base is computed from gl_NumWorkGroups, which inside a chunk is the
chunk's count, so workgroups of different chunks would alias (a 1D X split
only works because the base degenerates to the group id).

Fixes #3212
2026-10-11 05:58:29 -05:00
Adrià Franch 33a1ca5d33 fix(relinker): skip the desktop entry for macOS output and use native paths
The Linux desktop entry was written for every non-Windows output, macOS
included since --macos landed. It is now written only for Linux output,
and its paths go through Io::NativePath/Utf8Path like the rest of the
relinker. An end-to-end test relinks for Linux, macOS and Windows.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 12:55:22 +02:00
Gustavo Ariel Gamarra Rojas 94f05530d9 refactor(agc): use the shared PROCMAP_QUERY lookup in GuestMemory
GuestMemory.cpp carried its own procmap_query fallback and a ProcMapsQueryFd that opened /proc/self/maps once, probed PROCMAP_QUERY and honoured APS5_NO_PROCMAP_QUERY, the same as HostMapping::QueryFd. It now includes HostMapping.hpp and calls HostMapping::QueryFd.
2026-10-11 07:55:11 -03:00
alfalfa-star 00648f71b0 test(agc): keep key bytes reused after a first write through the write-back
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:51:36 +02:00
alfalfa-star 63f06f2935 fix(agc): store a storage image's DCC keys at write-back only when not stored yet
MarkDirty stores uncompressed keys at an image's first write and marks
its uploaded keys uncompressed. The write-back stored them again,
whenever the key bytes did not read uncompressed. By then the title may
have reused the metadata memory: RIDE 4 (PPSA01599) frees its 1920x1088
render targets after the legal screen and Unreal Engine's small-block
allocator takes the 64 KiB key block that followed one of them. The
deferred write-back then filled the allocator's free lists with 0xff
and the title crashed popping one.

The write-back now stores keys only when the image's uploaded keys are
not uncompressed already, that is when no store at the first write or
an earlier write-back made them so since the keys were last read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:51:36 +02:00
hypermist ef5d2130f4 feat(libSceNpEntitlementAccess): declare sceNpEntitlementAccessGetGameTrialsFlag
PPSA08427 (Killing Floor 3) stops on main at start-up because its bundled
pros.sdk.Prospero.prx imports it:

pros.sdk.Prospero.prx.guest.prx: undefined symbol: y7JD1gfwyfA

The signature is unknown, so it is declared without parameters and
throws, like the other entitlement flag queries the title does not reach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
2026-10-11 23:50:45 +13:00
Dean Galvin e7b4c05549 fix(shader): set a BDA-written page's bit inside the slot probe loop
note_bda_write loaded the chunk's page mask and set the page's bit after the probe loop, through a function variable holding the slot. NVIDIA 610.57.04 (Linux) never finishes a dispatch that takes that path: agc_driver_bda_device and agc_driver_flat_apertures_0, _2, _3, _4, _5 lose the device. The same load and conditional OpAtomicOr now run in the loop body, on the iteration that finds or claims the slot.
2026-10-11 23:50:11 +13:00
MarcZX100 b1b996b243 fix(libkernel): reject empty sceKernelOpen paths 2026-10-11 10:49:17 +00:00
Abdulrahman Elkordy abed1c4314 fix(libkernel): answer select on the standard descriptors
Dungeons (PPSA16064) calls select on descriptor 0 and stopped with
"select on descriptor 0 (not a libSceNet socket) not implemented".
Descriptor 0 is now never readable and descriptors 1 and 2 are always
writable, as for a console title with no input. The assumption is in
TechnicalDebt next to the termios entry for the same descriptors.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:48:29 +03:00
Abdulrahman Elkordy cbbccb4930 fix(libkernel): name the descriptor select cannot poll
The abort only said "select on descriptors that are not libSceNet
sockets", which leaves no way to tell from a title's log what it waited
on. It now names the descriptor and says whether libSceNet was loaded.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:48:29 +03:00
Abdulrahman Elkordy 1f50b91280 fix(libkernel): report /dev/urandom descriptors ready in select
select passed every descriptor it was given to the libSceNet socket poller
and aborted on any it did not know. A title that selects on its
/dev/urandom descriptor (Dungeons, PPSA16064, aborts right after opening
it) stopped with "select on descriptors that are not libSceNet sockets".
On FreeBSD the random device is always readable and writable, so these
descriptors are now answered without the poller, and a ready one makes
the socket poll return at once.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:48:29 +03:00
Adrià 9a9bd3fb3c
Merge pull request #2369 from karanvora2599/perf/shader-coherent-alias
perf(shader): load from buffers without Coherent when the load has no GLC or DLC
2026-10-11 12:47:46 +02:00
David 9bec190733 feat(libSceAgc): implement sceAgcDebugRaiseException
sceAgcDebugRaiseException (NID T6xuVw0KUJo) now logs the exception id
with APS5_LOG_OUT and returns 0, as libkernel's
sceKernelDebugRaiseException does.

Palworld PPSA23126 (#2598) and 007 First Light PPSA11386 (#2523) import
it; neither is shown calling it.

OpenAGC 3c7e8072 (src/game_compat.c) returns 0 and says the function is
a no-op on retail hardware; the ud2 trap it describes for development
hardware is not reproduced. KytyPS5 6f0cddd4 (src/libs/agc.cpp) takes a
uint32 exception_id, logs it and returns 0. OpenAGC declares no
arguments, but a body that only traps reads none, so the uint32 is kept
and the conflict is recorded in TechnicalDebt. The aerolib name is from
zecoxao/sce_symbols 2883963a; ps5-payload-dev/sdk f5d40f02 has only an
untyped stub.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 07:47:33 -03:00
MarcZX100 1a793b3698 test(libkernel): tolerate process-time clock skew 2026-10-11 10:46:48 +00:00
Adrià 74f51a4c7d
Merge pull request #3440 from alfalfa-star/fix/guest-unmap-hole-inside-allocation
fix(libc): skip holes inside an allocation when unmapping a range
2026-10-11 12:46:05 +02:00
ivica00 cb52ca10c9 fix(shader): return 0 from 8-bit sRGB color comparison
image_sample_c_lz on gfx1035 returns 0 for 8-bit sRGB, so the unorm clamp was the wrong result. Bilinear cube comparison without DISABLE_CUBE_WRAP keeps throwing because that filter blends across faces.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-11 12:44:53 +02:00
Adrià Franch 7a0d708e70 docs(technical-debt): record the NVIDIA proprietary pipeline relink skip 2026-10-11 12:44:52 +02:00
Yuvaraj b29133c74e fix(agc): skip the background pipeline relink on NVIDIA's proprietary driver
On an RTX 4060 (Ada, driver 617.42) the optimized link of a pipeline built
from libraries loses the device (VK_ERROR_DEVICE_LOST) as soon as it is
bound, which fails agc_driver_pixel_valid_mask in 16 of 20 runs. With
APS5_NO_PIPELINE_LTO=1 it passes 20 of 20. Reporters see the same loss on an
Ampere card with a depth attachment; two Blackwell cards do not lose it.

A device whose driver is VK_DRIVER_ID_NVIDIA_PROPRIETARY keeps the
fast-linked pipeline and no longer queues the link-time-optimized relink.
Other drivers are unchanged.

Refs #2866

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 12:44:46 +02:00
Gustavo Ariel Gamarra Rojas 4bc36a4865 refactor(libc): define the PROCMAP_QUERY permission flags in HostMapping
GuestMemory.cpp reads PROCMAP_QUERY_VMA_READABLE and PROCMAP_QUERY_VMA_WRITABLE from the query result. The fallback for kernel headers without PROCMAP_QUERY now defines them too, so GuestMemory can use this header instead of its own copy.
2026-10-11 07:44:34 -03:00
Dani 21f4be11ad fix(libkernel): inherit process affinity in new guest threads 2026-10-11 12:44:26 +02:00
Adrià 74981fabe1
Merge pull request #3439 from babyhuey/feat/libkernel-sys-fstatfs
feat(libkernel_sys): export fstatfs
2026-10-11 12:42:21 +02:00
MarcZX100 8c06d20f6a fix(filesystem): reject final dot removal paths
Co-authored-by: milescoviello <kitslayer29@gmail.com>
2026-10-11 10:42:14 +00:00
Adrià 03a84d02a9
Merge pull request #3392 from MarcZX100/codex/fchdir-3288
feat(libkernel_sys): implement fchdir
2026-10-11 12:40:41 +02:00
MarcZX100 85b60b65d1 fix(recompiler): fold unordered infinity comparisons 2026-10-11 10:40:37 +00:00
Adrià 2e40ade881
Merge pull request #3402 from GalaxyGamingFR/fix/conventions-system-dependency-spelling
fix(conventions): match ordinary CMake spellings of system lookups
2026-10-11 12:39:31 +02:00
Adrià Franch ca330dfefb fix(relinker): validate the desktop entry before writing the executable
The icon, title and output path are checked before the ELF is written,
so a malformed icon0.png or param.json leaves no output behind. Paths
with control characters are refused instead of producing an invalid
entry.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 12:39:14 +02:00
Adrià Franch 1ff7b534fe feat(relinker): write a desktop entry with the game icon for Linux executables
ELF has no icon resource, so when sce_sys/icon0.png sits beside the input
the relinker copies it next to the output and writes <output>.desktop
(freedesktop Desktop Entry) with that icon. Name is the param.json title
without trademark signs or line breaks, or the output name without
param.json. Exec is quoted per the spec and the entry is marked executable
so file managers launch it. A malformed icon0.png or param.json fails the
relink; without icon0.png nothing extra is written.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 12:39:14 +02:00
Hannah Lyons 2741e96172 feat(libkernel): implement pthread_getthreadid_np 2026-10-11 06:38:14 -04:00
Adrià 6554fcb68a
Merge pull request #3430 from AchrafReyani/feat/libkernel-sigtimedwait
feat(libkernel): export sigtimedwait
2026-10-11 12:36:53 +02:00
Adrià f8d9b770c5
Merge pull request #3431 from fable-void/pr/macos-guest-absolute-symbols
fix(relinker): accept absolute symbols in macOS guest modules
2026-10-11 12:36:48 +02:00
Adrià f6d156a72e
Merge pull request #3435 from alfalfa-star/fix/playgo-package-chunk-table
fix(libScePlayGo): take the chunk set from sce_sys/playgo-chunk.dat
2026-10-11 12:36:22 +02:00
Adrià 0e442a4fee
Merge pull request #3433 from Feuris/carry/libkernel-truncate
feat(libkernel): export truncate and report EISDIR for a directory
2026-10-11 12:36:19 +02:00
Alex Collini 687f73c951 fix(agc): select the registered back half for a fused hull header
sceAgcFuseShaderHalves returns a fused hull as a copy of its back half
(type 7) that is not registered: type 3, no user data, and shader
registers copied to scratch memory with the checksum, RSRC1/RSRC2 and
the LS program address rewritten. sceAgcCreatePrimState accepts only that
type-3 header, and graphics ABI resolution refused it as a replaced
shader header.

SameHeader now also takes a type-3 header as the registered type-7
header for the same code when every other field is equal and its shader
registers differ only in the fused ones. Graphics ABI resolution then
applies the bound header's shader registers over the registered state.

PPSA26344 fuses its hull halves during its loading screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:36:01 +02:00
Adrià c064e6898e
Merge pull request #3425 from oneandonlydean/up4/fix-3158-span-limit
perf(agc-driver): join host-import spans only over whole aligned windows
2026-10-11 12:35:57 +02:00
Adrià a94f426a75
Merge pull request #3429 from CouchRiv/fix-libc-hypot-special-value
fix(libc): hypot returns +inf when one operand is infinite
2026-10-11 12:35:51 +02:00
Adrià 02030f8a62
Merge pull request #3427 from AchrafReyani/fix/pr-overlap-fetch-head-sha
fix(tools): fetch each open pull request by the head commit the API reports
2026-10-11 12:35:24 +02:00
alfalfa-star f3a2dcff7d test(libkernel): map fixed over a hole inside a direct allocation
One 4-page direct mapping loses its second page to munmap, then pages
1-2 are mapped fixed. The range starts in the hole; before the fix the
unmap of the overlap threw "guest protection or unmap range has a
hole". The pages around it keep their data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:34:12 +02:00
Abdulrahman Elkordy 2ba139df67 fix(libkernel): answer select on the standard descriptors
Dungeons (PPSA16064) calls select on descriptor 0 and stopped with
"select on descriptor 0 (not a libSceNet socket) not implemented".
Descriptor 0 is now never readable and descriptors 1 and 2 are always
writable, as for a console title with no input. The assumption is in
TechnicalDebt next to the termios entry for the same descriptors.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:34:07 +03:00
Abdulrahman Elkordy a670e93a88 fix(libkernel): name the descriptor select cannot poll
The abort only said "select on descriptors that are not libSceNet
sockets", which leaves no way to tell from a title's log what it waited
on. It now names the descriptor and says whether libSceNet was loaded.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:34:07 +03:00
Abdulrahman Elkordy 259b9ca630 fix(libkernel): report /dev/urandom descriptors ready in select
select passed every descriptor it was given to the libSceNet socket poller
and aborted on any it did not know. A title that selects on its
/dev/urandom descriptor (Dungeons, PPSA16064, aborts right after opening
it) stopped with "select on descriptors that are not libSceNet sockets".
On FreeBSD the random device is always readable and writable, so these
descriptors are now answered without the poller, and a ready one makes
the socket poll return at once.

Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
2026-10-11 13:34:07 +03:00
ivica00 fc7446590a fix(shader): compare sRGB colors and bilinear cube faces
Subnautica Below Zero stops while compiling a menu shader that compares an sRGB color texture or a bilinear cube face. sRGB uses the unorm reference clamp, and a bilinear cube sample stays on the face it hits.

Written with AI assistance.
2026-10-11 12:32:28 +02:00
alfalfa-star 04ed619574 fix(libc): skip holes inside an allocation when unmapping a range
GuestAllocationsUnmap took the piece containing the cursor from the
allocation's original extent. After part of an allocation was unmapped,
a later unmap or fixed map starting in that hole still matched the
allocation, and replaceRange threw on the hole. Match against the
registered range itself and end each piece at its end. RIDE 4
(PPSA01599) maps fixed over a partly unmapped direct memory range.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:32:12 +02:00
alfalfa-star 6a4ac0c629 docs(libScePlayGo): record where the chunk set comes from
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:30:52 +02:00
alfalfa-star 3ef8556ada test(libScePlayGo): take the chunk set from a package chunk table
A child process runs from a root whose app0/sce_sys/playgo-chunk.dat
declares 3 chunks: ids 0-2 are local, id 3 is rejected with
SCE_PLAYGO_ERROR_BAD_CHUNK_ID and scePlayGoGetChunkId counts 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:30:52 +02:00
Hannah Lyons 81075a66c6 feat(libkernel_sys): export fstatfs 2026-10-11 06:30:50 -04:00
alfalfa-star eb7da6680f fix(libScePlayGo): take the chunk set from sce_sys/playgo-chunk.dat
Without playgo-chunkdefs.xml every chunk id was accepted, so a title
that probes chunk ids until SCE_PLAYGO_ERROR_BAD_CHUNK_ID never stops.
RIDE 4 (PPSA01599, Unreal Engine 4) grows its chunk list until it runs
out of memory. The package's chunk table header holds the chunk count
at offset 0x0A; ids below it are valid. Every id is still accepted only
when neither file exists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:29:45 +02:00
MarcZX100 1a73d0165e fix(libkernel_sys): use C ABI for fchdir helpers 2026-10-11 10:26:53 +00:00
MarcZX100 dc818b3ca6 fix(libc): keep chdir resolution atomic 2026-10-11 10:26:53 +00:00
Tharun Balaji 5a8091fafd feat(libkernel_sys): implement fchdir
FreeBSD fchdir(2) changes the working directory to the directory referenced by a descriptor. The guest imports fchdir from libkernel_sys (PPSA99764, issue #1578) and the module did not export it.

The descriptor's directory is resolved from the path recorded when the directory was opened on Windows, and from the /proc/self/fd link target on Linux. The guest working directory is then set through the libc routine shared with chdir. A descriptor that is not open returns EBADF and one that is not a directory returns ENOTDIR.

AI-assisted: yes
(cherry picked from commit 76329cbb08781e13d757c3ff955acf71f8bd506f)
2026-10-11 10:26:53 +00:00
Adrià 99c1c26347
Merge pull request #3426 from MathiasDevelopes/codex/agc-clear-state-mask
fix(agc): mask clear-state command to four bits
2026-10-11 12:24:18 +02:00
Adrià 8fcd352f04
Merge pull request #3420 from gookie-dev/perf/agc-lazy-constant-ram
perf(agc): allocate a queue's constant RAM on its first write
2026-10-11 12:23:06 +02:00
Adrià ce4263b1a6
Merge pull request #3423 from hawwwwwk/pr/relinker-instruction-length-current
fix(relinker): reject x86 instructions exceeding 15 bytes
2026-10-11 12:22:21 +02:00
Adrià 88636aaf02
Merge pull request #3419 from gookie-dev/perf/agc-registers-foreach
perf(agc): walk register sets by their bit words when resolving ABIs
2026-10-11 12:21:53 +02:00
Adrià 0809bcc8ae
Merge pull request #3421 from DariusHQ/feat/libkernel-ucontext-freebsd
feat(libkernel): implement getcontext, setcontext and makecontext
2026-10-11 12:21:46 +02:00
GalaxyGamingFR 0e7ccce2ec fix(conventions): match ordinary CMake spellings of system lookups
The system-dependency rule was case-sensitive, so FIND_PACKAGE(SDL2) passed,
and its trailing word boundary failed when whitespace followed the opening
parenthesis. find_path, find_program and find_file were not covered at all.
The pattern is now case-insensitive, anchors each command name, and covers
the find_* family.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 12:21:30 +02:00
Adrià 4611e9435c
Merge pull request #3424 from AchrafReyani/feat/libc-rand48
feat(libc): implement erand48, nrand48 and jrand48
2026-10-11 12:21:04 +02:00
Adrià dcca02896e
Merge pull request #3406 from GalaxyGamingFR/fix/stubgen-debt-links
fix(tools): make stubgen TechnicalDebt links resolve from docs/dev
2026-10-11 12:20:37 +02:00
Achraf Reyani 2a2696bd9c feat(libkernel): export sigtimedwait
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:20:16 +09:00
Mouad a584bc6a01 feat(libkernel): export truncate and report EISDIR for a directory
`truncate` (`ayrtszI7GBg`) is an absent import of PS5MC (PPSA99420),
XPSemu (PPSA97358) and Porpoise (PPSA99764) on `main`. It resolves
through `sceKernelTruncate`, which now returns `EISDIR` for a directory
and the host errno for other failures instead of `EIO`, as FreeBSD 11
`kern_truncate` does. The guest-relative test `guest_truncate` covers
shrinking, growing with zero fill, a negative length, a missing path,
an empty path, a null path, a directory and a read-only file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 07:19:35 -03:00
Adrià 68ae2a895d
Merge pull request #3386 from GalaxyGamingFR/fix/conventions-path-with-spaces
fix(conventions): keep per-line rules for file names with spaces
2026-10-11 12:19:02 +02:00
Alex fc0994876d fix(libc): hypot returns +inf when one operand is infinite
hypot(inf, NaN) returned a NaN on main. C Annex F returns +inf when
either operand is infinite, even if the other is a NaN, and FreeBSD
msun (lib/msun/src/e_hypot.c) does so for a signalling NaN too. The
infinite case is now checked before std::hypot; other inputs do not
change.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 06:17:21 -04:00
fablevoid 3462427519 fix(relinker): accept absolute symbols in macOS guest modules
#698 taught the reader, the module builder and the Windows writer to
resolve relocations against SHN_ABS symbols and to leave them out of the
exports, but the macOS writer still threw "Unsupported guest export
section" for them. PPSA12544 bundles modules with such an export
(RKDbTdN8TO0), so --macos stopped before writing anything.

The macOS writer now resolves a relocation against an absolute symbol to
its value plus the addend, without a rebase, and skips absolute symbols
when exporting, as the Windows writer does. guest_absolute_symbols covers
the macOS output too.

AI-assisted: yes
2026-10-11 10:16:59 +00:00
Adrià e7827eb4d4
Merge pull request #3416 from AchrafReyani/fix/nodiscard-warnings
fix(shader): discard two nodiscard results explicitly
2026-10-11 12:16:54 +02:00
Adrià a7d80e7413
Merge pull request #2410 from karanvora2599/perf/shader-pack-conversions-f32
perf(shader): round v_cvt_pknorm and v_cvt_pk_u8 in f32
2026-10-11 12:16:07 +02:00
Adrià f0c6cf27b2
Merge pull request #3408 from GalaxyGamingFR/fix/pr-overlap-template-placeholder
fix(tools): read Depends on through the template placeholder in pr_overlap
2026-10-11 12:13:59 +02:00
Achraf Reyani 2b234efff3 fix(tools): fetch each open pull request by the head commit the API reports
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:13:35 +09:00
Hussein Kadhim 60b1318226 docs(technical-debt): list the float exponent quirk with the scanf host differences 2026-10-11 12:12:48 +02:00
Hussein Kadhim 09921dd376 fix(libc): return 0 from scanf when input fails after a suppressed conversion on Windows
C17 7.21.6.2p16 returns EOF only if input fails before the first conversion completes, and a suppressed conversion completes. FreeBSD counts it in nconversions (vfscanf.c, releng/11.4, lines 799-805). The host scanf returned EOF there, so sscanf("12", "%*d%d", &v) reported EOF instead of 0. Each suppressed conversion now records its end with a %n and a result of EOF becomes 0 when one was recorded.
2026-10-11 12:12:48 +02:00
Adrià 2ac9f1b317
Merge pull request #3414 from AchrafReyani/fix/process-time-test-wait
test(libkernel): wait until 50 ms have passed before the first process time read
2026-10-11 12:12:44 +02:00
Adrià 74c0f1bd14
Merge pull request #3409 from GalaxyGamingFR/docs/contributing-depends-on
docs: show the Depends on form the conventions check accepts
2026-10-11 12:11:49 +02:00
MathiasDevelopes 46d8c587c6 fix(agc): mask clear-state command to four bits 2026-10-11 12:11:03 +02:00
Adrià f100f6c7fc
Merge pull request #2786 from tototomate123/fix/hfw-loader-split
fix(loader): resolve sceKernelDlsym on the main module
2026-10-11 12:10:04 +02:00
Adrià b42b29128b
Merge pull request #3407 from GalaxyGamingFR/fix/import-audit-per-library-stubs
fix(tools): classify import_audit stubs per providing library
2026-10-11 12:09:27 +02:00
Adrià 3bf63fadce
Merge pull request #3401 from AchrafReyani/carry/pipodi-3350
fix(recompiler): split descriptor webs selected per lane
2026-10-11 12:08:57 +02:00
Adrià 6cee0bb02a
Merge pull request #3400 from AchrafReyani/carry/pipodi-3349
fix(recompiler): evaluate an out-of-range constant buffer read as zero
2026-10-11 12:08:15 +02:00
boykopovar cfbd516c24
Merge pull request #3397 from boykopovar/feat/show-guest-prx-processing
feat(relinker): show guest prx processing
2026-10-11 13:07:42 +03:00
Adrià Franch 68f60cf348 fix(conventions): spell the stripped tab as an escape 2026-10-11 12:07:35 +02:00
GalaxyGamingFR 3a1deb48a8 fix(conventions): strip trailing tab from spaced diff paths
Git appends a tab after file names containing spaces in the +++ header, so the
added-lines key kept the tab and its suffix no longer matched. Comment,
extension, title and silent-stub rules then skipped such files. The tab is
now stripped, with a test covering core/a b.cpp.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 12:07:34 +02:00
Adrià 434f290def
Merge pull request #3403 from babyhuey/feat/libkernel-link
feat(libkernel_sys): implement link
2026-10-11 12:06:51 +02:00
Adrià a4d5d3ce53
Merge pull request #545 from Pouare514/feat/ampr-c-runtime
feat(libs): implement C runtime re-exports for libSceAmpr
2026-10-11 12:06:18 +02:00
Adrià cdef5b7fce
Merge pull request #3405 from GalaxyGamingFR/fix/pr-overlap-encoding
fix(tools): decode pr_overlap git output with errors=replace
2026-10-11 12:05:54 +02:00
Adrià 106d446a33
Merge pull request #3380 from Feuris/feat/libc-abs
feat(libc): implement abs
2026-10-11 12:05:14 +02:00
Hussein Kadhim 7195e355b4 docs(technical-debt): list the doubled strftime modifier among the host differences 2026-10-11 12:05:00 +02:00
Hussein Kadhim 84926887e9 fix(libc): keep the literal percent after E and O in strftime
A format such as %E%k is a modified percent sign followed by a literal k, so the second percent must not start a new conversion.
2026-10-11 12:05:00 +02:00
Hussein Kadhim c5bc118f4b fix(libc): accept E and O before new strftime conversions and take %+ zone from tm 2026-10-11 12:05:00 +02:00
Hussein Kadhim 7e3a409022 fix(libc): format %k, %l, %s, %v and %+ in strftime 2026-10-11 12:05:00 +02:00
Achraf Reyani f85ae8d6f2 feat(libc): implement erand48, nrand48 and jrand48
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 19:02:32 +09:00
Adrià 24cb0d2412
Merge pull request #3389 from semirkabir/feat/usbd-no-devices
feat(libSceUsbd): implement transfer allocation and event handling for the no-device path
2026-10-11 12:02:06 +02:00
ethan f2ef0f9557 fix(relinker): reject x86 instructions exceeding 15 bytes
(cherry picked from commit 7243caa56ed79fab09edd23d92851e2508671775)
2026-10-11 03:02:05 -07:00
gookie 8b550b81fc perf(agc): allocate a queue's constant RAM on its first write
QueueState held the 48 KiB constant RAM inline, so every QueueState
cleared 48 KiB when constructed and copied it when copied.
ResolveShaderAbi and ResolveGraphicsStagesAbi construct one on each call
(sceAgcLinkShaders, sceAgcCreatePrimState, interpolant mapping),
resolveGroupAhead copies the live state, and a queue reset builds a new
one, although few command buffers write the constant RAM.

The constant RAM is now a vector allocated when WRITE_CONST_RAM first
writes it. ConstantRam returns a shared all-zero view until then, so
DUMP_CONST_RAM of unwritten words still stores zeros, and the bounds use
QueueState::ConstantRamWords. The store view ResolveStore returns is
copied by the recorder within the packet, as before.

testMemory dumps unwritten constant RAM and checks for zeros, and checks
that a copied queue state keeps the words written before the copy.

A QueueState is 336 bytes instead of 49464; constructing one takes 0.3
to 0.8 us instead of 1.5 to 2.1 us, and copying one 0.4 to 0.6 us
instead of 1.9 to 3.8 us (GCC 13 -O3). On the title screen of PPSA21402
(Balatro) the clears from the two resolvers took 30 to 129 of 3459 to
7838 CPU samples per 10 s.
2026-10-11 12:01:27 +02:00
Adrià Franch 52a5b4869a fix(tools): drop unused depends lookup in open_prs 2026-10-11 12:01:08 +02:00
GalaxyGamingFR 26e0b30df3 fix(tools): ignore PR template placeholder when reading dependencies
The PR template's untouched "Depends on: <!-- #PR, or none -->" line
matched the checklist regex, so prose such as "Depends on #1400" was never
consulted and the declared dependency was lost. HTML comments are now
stripped and the prose form is used when the checklist line is blank. An
explicit "Depends on: none" still takes precedence.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 12:01:08 +02:00
DariusHQ cf1545d751 feat(libkernel): implement getcontext, setcontext and makecontext
PSXS5, SwanStationPS5 and Nuvio stop at start-up on setcontext on main,
and EVO Player and ProsperoStore do on Windows. The three are imported
by the statically linked OpenSSL's ASYNC code.

The three use FreeBSD 11's amd64 ucontext_t (mcontext at 0x10, mc_len
800, uc_link at 0x330, 0x370 bytes), which the titles are built
against: EVO Player's ASYNC_is_capable captures into a 0x370-byte stack
buffer, and async_fibre_makecontext sets uc_link at 0x330 and uc_stack
at 0x338. getcontext saves every register at its call in assembly and
stores the context as FreeBSD's sys_getcontext does: rax and rdx as 0,
the carry flag clear, the FXSAVE image and the signal mask, and nothing
from uc_link on. setcontext checks mc_len and mc_flags as set_mcontext
does, installs the signal mask, masks MXCSR with the host's MXCSR_MASK
and returns through iretq, so every general register and the flags are
restored and nothing is written below the target stack pointer.
makecontext follows FreeBSD 11's amd64 libc.

Windows checks guard-page growth and exception unwinding against the
stack bounds in the TEB. setcontext and longjmp now set them to the
stack they switch to, which makecontext registers and pins writable as
libSceFiber does for fiber stacks. OpenSSL enters a context with
setcontext and then switches with _setjmp and _longjmp.
2026-10-11 13:00:38 +03:00
gookie f596bde6f6 perf(agc): walk register sets by their bit words when resolving ABIs
Iterating a Registers advances with nextPresent, which recomputes the
word, mask and next bit from the index on every step, about 3.5 ns per
register. BuildRegisteredAbiKey walks all of a state's registers, about
300 with the context, and ResolveGraphicsStagesAbi walks each stage's
registered state to merge it, on every sceAgcLinkShaders,
sceAgcCreatePrimState and interpolant mapping call.

Registers::ForEach visits the set registers in ascending order straight
from the presence words, and both paths use it. The key and the merged
state are unchanged.

testRegisterFile checks that ForEach visits what iteration visits, in
order, and skips an erased register.

Building the key for 297 registers takes 200 ns instead of 1030 ns (GCC
13 -O3). On the title screen of PPSA21402 (Balatro) the two functions
took 0.18 to 0.29 CPU samples per sample of the title's own code, and
0.06 to 0.12 with this change.
2026-10-11 12:00:28 +02:00
Adrià 8ec0e13109
Merge pull request #3404 from GalaxyGamingFR/fix/conventions-checklist-bullets
fix(conventions): count * and + checklist bullets as unchecked
2026-10-11 11:59:14 +02:00
GalaxyGamingFR efbf36f637 fix(tools): make stubgen debt links resolve from docs/dev
stubgen emitted TechnicalDebt links as repo-relative paths, but
TechnicalDebt.md lives in docs/dev and the conventions checker resolves
links relative to it, so pasted lines failed CI. The derived default path
now carries a ../../ prefix; an explicit --td-path is used as given.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 11:58:25 +02:00
Adrià b44eeb967a
Merge pull request #3393 from LeandroLP23/fix/libc-printf-pointer-form
fix(libc): format %p as FreeBSD does on Windows
2026-10-11 11:58:03 +02:00
Adrià 708ffcd2ef
Merge pull request #3394 from LeandroLP23/fix/kernel-signal-unmapped-numbers
fix(libkernel): accept signal() for every valid signal number
2026-10-11 11:56:52 +02:00
Adrià 58a581d983
Merge pull request #3399 from MathiasDevelopes/codex/feat-npwebapi2-push-event-abort
feat(libSceNpWebApi2): export sceNpWebApi2PushEventAbortHandle
2026-10-11 11:56:32 +02:00
Adrià ec77a616b6
Merge pull request #3395 from LeandroLP23/fix/kernel-mkdir-enotdir
fix(libkernel): report ENOTDIR from mkdir and rmdir when a path component is a file
2026-10-11 11:55:48 +02:00
Adrià 76f2d72d40
Merge pull request #3384 from sypher69/fix/sce-dynlibdata-relocation-offsets
fix(relinker): resolve SCE relocation tables relative to dynlibdata
2026-10-11 11:55:28 +02:00
Achraf Reyani 0eaf2ec159 test(libkernel): wait until 50 ms have passed before the first process time read
sleep_for can return up to a scheduler tick early against the
performance counter the guest clock reads, so the first reading could be
just under 50 ms on a loaded machine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:51:47 +09:00
Adrià 5c4bc7d607
Merge pull request #3390 from GalaxyGamingFR/fix/check-skips-device-messages
fix(ci): recognise every device-limitation skip message in check_skips
2026-10-11 11:51:46 +02:00
Adrià b70a0e278f
Merge pull request #1491 from Celegans12/feat/shader-tessellation-triangle-patches
feat(shader): implement triangle-patch tessellation
2026-10-11 11:50:50 +02:00
tototomate123 4a62b6220b fix(build): export the Windows dynamic loader test executable 2026-10-11 11:50:44 +02:00
tototomate123 19b0359f13 fix(loader): support main-module sceKernelDlsym 2026-10-11 11:50:44 +02:00
Adrià Franch af9dfbfab9 test(libs): report the failing guest_crt check and compare localeconv per module
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:49:34 +02:00
Hannah Lyons 0e8dc6f20f feat(libkernel_sys): implement link 2026-10-11 05:47:59 -04:00
Achraf Reyani 30a3cbea7d fix(shader): discard two nodiscard results explicitly
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:45:44 +09:00
Adrià a73a1af9c8
Merge pull request #3382 from babyhuey/feat/libkernel-readv
feat(libkernel): implement readv and writev
2026-10-11 11:43:45 +02:00
MathiasDevelopes 6d70a46f74 docs: remove gap between technical debt entries 2026-10-11 11:43:36 +02:00
MathiasDevelopes 0f45675768 feat(libSceNpWebApi2): export sceNpWebApi2PushEventAbortHandle
Titles that import it now load; calling it throws until push event
handles have operations that can be aborted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f50cb08c988736d645f0cc1c329f911f732fa935)
(cherry picked from commit ae3227dbfa2c972b36242056df5971d858ac65c3)
(cherry picked from commit 3df66a6a5dd9f0cd73daaee1342c22742c1915e4)
2026-10-11 11:43:35 +02:00
GalaxyGamingFR 9c42733c46 docs: show the Depends on form the conventions check accepts
CONTRIBUTING.md told authors to write `Depends on #N` in the
description, but the pr-template rule in tools/check_conventions.py
only accepts the checklist line `Depends on: #N` or `Depends on: none`
from the pull request template. A pull request that followed the
document failed the Conventions job with "fill Depends on with #N or
none". The sentence now shows the checklist form.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 02:43:06 -07:00
Alex Collini f3e654a4f5 fix(recompiler): split descriptor webs selected per lane
A T# picked per lane with SelectU32 and made uniform with
v_readfirstlane, as in a waterfall loop, left a descriptor web without
Phis that the web walk could not split. CollectDescriptorPhis now steps
through ReadFirstLane, and a Phi-free tuple containing SelectU32 or
ReadFirstLane is split on the shared condition (SelectU32) or wrapped in
a matching ReadFirstLane. The new nodes are placed after the last
original node of the tuple in its block and inserted only when the web
is split; otherwise they are invalidated like the web's Phis.

PPSA26344 compute shader 0x800037f200 selects its T# per lane between
SRT reads or zero and reads the first lane of the result.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:42:56 +09:00
Alex Collini 5a9d731540 fix(recompiler): evaluate an out-of-range constant buffer read as zero
s_buffer_load returns zero for dwords outside the V#'s range. The SRT
evaluator refused such a read instead, so evaluating a descriptor failed
as soon as one arm of a split T# read through a null or short V#, even
when that arm is never taken. The evaluator now returns zero for those
reads, as the hardware does.

PPSA26344 compute shader 0x800037f200 reads one arm's T# through a null
V# and stopped at "SrtWalker::EvaluateRuntimeSources failed to evaluate
runtime sources".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:42:47 +09:00
GalaxyGamingFR 50c288810e fix(tools): key import audit stubs by library
source_stubs pooled done NIDs across all libraries, so a throwing stub
in libSceShare.native counted as implemented when libSceShare had the
same function. Stubs are now keyed by (library.prx, nid) and audit marks
an import stub only when every provider of it is a stub.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 02:42:36 -07:00
Pouare514 5290f92876 fix(libs): share Sony CRT semantics between libc and libSceAmpr with tests 2026-10-11 11:42:31 +02:00
GalaxyGamingFR 15f34d3043 fix(tools): decode pr_overlap git output with errors=replace
One PR diff containing a byte that is not valid UTF-8 made git() and
merge() raise, which aborted the hourly run and dropped the overlap
comment on every PR. Both subprocess calls now pass errors="replace",
matching check_conventions.py. A test commits a Latin-1 byte and checks
the export is still found.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 02:42:08 -07:00
GalaxyGamingFR 2f1b4aaf97 fix(conventions): count * and + checklist bullets as unchecked
The pull request check only matched unchecked items written with a dash,
so a checklist rewritten with * or + bullets passed with every box empty.
GitHub renders all three bullet styles as task items. The pattern now
accepts any of them, and new tests cover each bullet style.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 02:41:54 -07:00
Adrià dbc3b0679a
Merge pull request #2541 from maoxin1234/fix/sysmodule-trophy2-module-id
fix(libSceSysmodule): register Trophy2 module ID
2026-10-11 11:41:43 +02:00
Feuris e2b11c2dd8 feat(libc): implement abs 2026-10-11 06:41:00 -03:00
Adrià 946ffa08d4
Merge pull request #3372 from alfalfa-star/fix/agc-fast-clear-import-window
fix(agc): keep a fast-clear copy where an import window can stamp watched memory
2026-10-11 11:40:15 +02:00
Semir Kabir cbd18363bd feat(libSceUsbd): implement transfer allocation and event handling for the no-device path 2026-10-11 09:40:14 +00:00
Adrià a31732c5ff
Merge pull request #3365 from MathiasDevelopes/codex/fix-windows-unscoped-imports
fix(relinker): load needed providers for unscoped Windows imports
2026-10-11 11:40:05 +02:00
Adrià 9a1f6f44ac
Merge pull request #3379 from CouchRiv/fix-shader-fold-bitreverse32
fix(shader): fold BitReverse32 of an immediate
2026-10-11 11:39:04 +02:00
Adrià 66d577d428
Merge pull request #3378 from GalaxyGamingFR/fix/conventions-quoted-paths
fix(conventions): read non-ASCII paths unquoted from git
2026-10-11 11:37:57 +02:00
Adrià Franch c8f8014ea2 docs(debt): local programs get one artifact per tessellation configuration
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:37:19 +02:00
Hannah Lyons 63a4014236 feat(libkernel): implement readv and writev 2026-10-11 05:36:38 -04:00
Adrià 097f4e2284
Merge pull request #3373 from MathiasDevelopes/codex/feat-coredump-thread-context-export
feat(libkernel): export sceCoredumpGetThreadContextInfo
2026-10-11 11:36:29 +02:00
Dean Galvin 8da461ba51 perf(agc-driver): fix the host-import window at 8 MiB
The window was read from APS5_HOST_IMPORT_WINDOW_MIB on the device.
It is now always 8 MiB, the value the previous commit was measured
with; SetHostImportWindow stays for the tests. TechnicalDebt records
the window as a choice.
2026-10-11 22:35:37 +13:00
maoxin1234 5ee3ef8cb8 fix(libSceSysmodule): recognize Trophy2 module id 2026-10-11 11:35:37 +02:00
Cesar Rodriguez a947303731 perf(agc-driver): join host-import spans only over whole aligned windows
one span. A title that grows a heap 16 KiB at a time then re-imports the
whole chain on every growth: Ratchet & Clank: Rift Apart re-imported about
1.5 TiB while loading its first scene, and NVIDIA refused grown spans with
VK_ERROR_OUT_OF_DEVICE_MEMORY, which leaves those ranges copied per draw.

Ranges are now joined only when a chain covers a whole aligned window (8 MiB
by default, APS5_HOST_IMPORT_WINDOW_MIB on the device); until the window is
complete each range is imported on its own, and a span never crosses a
window edge. Growth therefore re-imports a range once more at most when its
window completes, instead of the chain on every growth. Joining any part of
a window was tried first and still re-imported 2 GB per second in Ratchet,
as each growth re-imported the window's partial span. A single range larger
than the window is imported whole. SetHostImportWindow(0) keeps the
unbounded spans, which is what the existing tests use.

The new importWindowTests (agc_driver_graphics) checks that a full window is
one span, that a partly filled window imports its range alone, that a view
crossing a window edge gets no import, and that completing one window leaves
the other window's import in place; with the window ignored it fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 22:35:28 +13:00
Adrià af45296363
Merge pull request #1780 from Roojool/fix/avplayer-eof-wakeup
fix(avplayer): use precise Windows notified waits for EOF polling
2026-10-11 11:35:23 +02:00
Dani cdfc11cf56 fix(relinker): resolve SCE relocation tables relative to dynlibdata 2026-10-11 11:34:59 +02:00
Adrià f4b7151097
Merge pull request #2510 from Edd016/fix/hw-oracle-mingw-lld
fix(hw-oracle): select the AMDGPU ELF emulation when linking kernels
2026-10-11 11:33:58 +02:00
GalaxyGamingFR 73b4bdb600 fix(tools): accept more device limitation skips in check_skips
The Linux skip check only recognised subgroup, "the device has no",
"the device reports" and "no display", so tests that skip with
"the device lacks ...", "does not import", "imports no", "flushes",
"binds fewer", "no usable Vulkan device" or "host imports are
unavailable" failed the job. The regex now covers those messages, and
unrelated reasons such as "flaky" still fail. A unit test covers the
messages and an end-to-end run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 02:33:31 -07:00
Adrià Franch de2f7aca22 test(relinker): drop docstring from loader path check 2026-10-11 11:33:17 +02:00
MathiasDevelopes 3f5c6e8336 test(relinker): remove disallowed loader test comments 2026-10-11 11:33:13 +02:00
MathiasDevelopes c245e3abc4 test(relinker): verify unscoped Windows provider load paths 2026-10-11 11:33:13 +02:00
MathiasDevelopes 2faa5dd6ed fix(relinker): load needed providers for unscoped Windows imports 2026-10-11 11:33:13 +02:00
Adrià 92ceadff7f
Merge pull request #3376 from adriadam10/ci/pr-open-limit
ci(pr): limit open pull requests by the author's merged count
2026-10-11 11:33:10 +02:00
GalaxyGamingFR 8365766e93 fix(conventions): read non-ASCII paths unquoted from git
With git's default core.quotePath=true, paths with non-ASCII characters
were printed quoted and octal-escaped. The checker stored the quoted
string as the path and then failed in git show with a traceback. git()
now passes core.quotePath=false so raw UTF-8 paths are used. A test
covers a diff containing a non-ASCII file name.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9UU71SP1UKGR51Sowbgmq
2026-10-11 02:32:41 -07:00
Adrià e608d40265
Merge pull request #2051 from esatturan/fix/libkernel-umtx-interruptible-waits
fix(libkernel): let exception delivery interrupt mutex and rwlock waits
2026-10-11 11:31:40 +02:00
Adrià 65d4a8b189
Merge pull request #3375 from MarcZX100/codex/libc-unlocked-streams-lockfree-1765
feat(libc): implement unlocked character I/O
2026-10-11 11:31:03 +02:00
Adrià Franch 48c9156d10 docs: remove stray blank line in the TechnicalDebt list 2026-10-11 11:28:44 +02:00
Adrià Franch 14f585a439 ci(pr): limit open pull requests by the author's merged count
Authors with no merged pull requests can have 1 open, with at least 1
merged 20, and from 20 merged there is no limit. Reopening a pull
request runs the check again.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:28:43 +02:00
MathiasDevelopes 92303a2ee5 feat(libkernel): export sceCoredumpGetThreadContextInfo
Titles that import it now load; calling it throws like the other
coredump queries, since host crashes are not turned into guest dumps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit e0e5fd9ffbbaffdc065d1471ce1c91b144af5f98)
(cherry picked from commit 2ac0c88fdbb9e380b6eb5a37d5f98d557940f131)
(cherry picked from commit 1279010b1bc123d3b87664ede15ec6ab2ced806a)
2026-10-11 11:28:39 +02:00
Adrià c6ca798fe6
Merge pull request #1655 from mohdfaour03/fix/videoout-present-precise-wait
fix(videoout): wait for flips with the high-resolution condition on Windows
2026-10-11 11:27:48 +02:00
Adrià 0333ec91ed
Merge pull request #1482 from awake-devel/fix/libkernel-umtx-op
feat(libkernel): implement _umtx_op wait and wake operations
2026-10-11 11:26:12 +02:00
Adrià Franch a898efdb41 fix(shader): index hull control-point inputs with constant slots
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:25:56 +02:00
Adrià Franch e53e1762e3 test(agc): adapt the tessellation tests to the fixed push slots and preparation at use
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:25:56 +02:00
Celegans12 77dcb74ccd feat(shader): implement triangle-patch tessellation
DecodeShaderStages accepts LS, HS and an NGG passthrough ES, reads the
3-bit VGT_TF_PARAM fields, refuses its other bits but DISTRIBUTION_MODE,
and needs HOS levels that keep [1, 63]; 0x286-0x287 join the draw key.
Local and domain programs are prepared with their stage group: its
headers are merged over one set of defaults, and a local program left
unbound by the hull header is prepared at its first draw.

The local program ends at its jump into the hull half.
TessellationLowering lowers LDS and ring addresses that are affine in
the control point and relative index to tessellation interfaces with
strides from the code, and refuses accesses outside the patch, per-patch
writes from other control points and a modified primitive export. Hull
lanes read their own saved exec bit, also of host-subgroup-masked
copies; other masks are refused, gl_InvocationID loads as a scalar and
factors clamp to 63.
2026-10-11 11:25:56 +02:00
Alex d809958b27 fix(shader): fold BitReverse32 of an immediate
A descriptor dword built by S_BREV_B32, V_BFREV_B32 or S_BREV_B64 from a
constant stayed a runtime value, so the resource could not be resolved
and the shader failed to prepare. The constant folder now reverses the
bits of an immediate the way the hardware does (D[31-i] = S0[i]), like
the existing BitCount32 fold. ConstantBitTest covers the fold.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-11 05:23:40 -04:00
Adrià 09bd4aa1a3
Merge pull request #1431 from Zaid-Talib/claude/focused-turing-ivfmjn
feat(libkernel): deliver sceKernelRaiseException on Linux
2026-10-11 11:22:31 +02:00
Adrià 4c9686fe2e
Merge pull request #1388 from gjonn/feat/agc-color-compare-constant-swizzle
feat(agc): color compare through a view whose X channel is constant
2026-10-11 11:21:17 +02:00
esatturan be4ab94642 fix(libkernel): let exception delivery interrupt timed mutex and rwlock waits on Windows 2026-10-11 11:20:40 +02:00
Roojool 23b65eb553 fix(avplayer): use precise Windows notified waits for EOF polling
Waits on eventCondition go through libkernel's TimedWait::Condition (precise
timed waits on Windows) instead of std::condition_variable::wait_for, as
requested in review.
2026-10-11 11:20:24 +02:00
Adrià e272b92e03
Merge pull request #3368 from AchrafReyani/carry/libkernel-process-time-start
fix(libkernel): count process time from library load
2026-10-11 11:19:48 +02:00
Adrià Franch b41db48b7a fix(videoout): build TimedWait into libSceVideoOut instead of importing it from libkernel
libkernel.prx does not export TimedWait::*, so libSceVideoOut.prx had an
unresolved import and titles stopped at load. Compile TimedWait.cpp into
libSceVideoOut, and use NotifyAll for the notify added on main.
2026-10-11 11:19:05 +02:00
Mohd Faour 96890dee37 fix(videoout): wait for flips with the high-resolution condition on Windows
The flip queue's condition variable went through winpthreads, whose
timed waits end on GetTickCount64 steps: the present thread's 10 ms poll
for SDL events and the pad, taken while no flip is queued, ended 11 to
24 ms later on Windows, and WaitForFlipRoom's 60 s bound had the same
granularity. Use TimedWait::Condition, which waits on an event and a
high-resolution timer, so the poll ends at 10 ms and a submitted flip
still wakes the present thread at once.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 11:19:05 +02:00
Adrià 0490ca18dd
Merge pull request #1505 from Sanjaysrinivas/test/shader-half-expansion
test(shader): cover half expansion bit patterns
2026-10-11 11:18:31 +02:00
Romans Pokrovskis 228532e9a8 fix(libc): keep unlocked stream status queries lock-free
Use unlocked POSIX EOF/error queries after unlocked byte operations so status synchronization cannot reacquire the stream lock. Keep the verified UCRT status calls on Windows, and cover byte, status, error, and held-lock behavior.

Co-authored-by: MarcZX100 <marczxrr@gmail.com>
2026-10-11 09:18:23 +00:00
MarcZX100 753b856526 feat(libc): implement unlocked character I/O 2026-10-11 09:18:23 +00:00
milescoviello ee720ee004 test(agc): reproduce an import window over a pending storage image
agc_driver_dcc_keys_first_write only exercises the import window when the
device's own host import resolves the write protection of the imported
pages, as the NVIDIA driver does on Linux. CI's devices do not, so the
comparison that #2984 adds for BlockMaybeWritten units is not run there.

RunImportWindow opens an import window itself over a pending storage
image with GuestMemory::ImportWatched and stores the byte already there,
which leaves the same stamp without depending on the driver: the image
must stay pending and unchanged. A store of a new byte inside a window
must still be seen. Devices whose imports are compared print that the
window is not tested, as the recorder's (w) test does. Linux only: the
Windows path compares imports.

TechnicalDebt records that an identical-byte CPU store inside a window
cannot be told apart from the import, and that images tracked by layer
still upload again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:18:04 +02:00
milescoviello f774e1cd98 fix(agc): keep a fast-clear copy where an import window can stamp watched memory
range is write-watched. Since #2984, Refresh turns a BlockMaybeWritten
unit into BlockUnchanged only through compareUntracked, which needs that
copy. Where a host import leaves its range watched and making it stamps
the imported pages (the probe's writtenAtImport, 4 of 4 on NVIDIA's
Linux driver), the import window stamps the surface, so a pending
fast-cleared image counted as changed again and
agc_driver_dcc_keys_first_write failed on main (#3179).

HostImports now keeps whether making each kind of import stamped the
probe's pages, and WatchedImportsStampPages reports whether an import
that leaves its range watched can stamp it. The clear path skips the
copy only when no such import can, as oneandonlydean proposed in #3179.
Devices whose probe saw no pages written at the import, devices without
host imports and Windows (whose alias imports don't disarm the guest
views) keep #2699's saving.
An unprobed watch (APS5_WRITE_WATCH_IMPORTS=watch) keeps the copy.

The fast-clear check in agc_driver_dcc_keys_first_write now expects the
copy exactly when the range is unwatched or such a stamp is possible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:18:04 +02:00
Adrià 5ad2cb8cd0
Merge pull request #1377 from gjonn/fix/agc-unnormalized-base-level-view
fix(agc): sample the base level of mipmapped views with unnormalized coordinates
2026-10-11 11:17:05 +02:00
Adrià e8e4dfdb36
Merge pull request #3337 from AchrafReyani/carry/export-ppsa01530-ppsa13356-imports
fix(exports): export the calls PPSA01530, PPSA23827 and PPSA13356 import
2026-10-11 11:16:45 +02:00
Adrià bf5458b760
Merge pull request #1423 from gjonn/perf/libc-unwind-eh-frame-index
perf(libc): index .eh_frame and find unwind modules without a syscall
2026-10-11 11:16:13 +02:00
Adrià 3e1fa82cf2
Merge pull request #1416 from gjonn/fix/recompiler-sample-missing-coordinates
fix(recompiler): sample the first row when the address omits coordinates
2026-10-11 11:14:21 +02:00
Adrià Franch 3879e7ed54 fix(libkernel): throw on a size-0 _umtx_op timeout that is not a valid timespec 2026-10-11 11:13:33 +02:00
Zurisen 9ebc657f82 fix(libkernel): count process time from library load
sceKernelGetProcessTime and sceKernelGetProcessTimeCounter measured from
a start captured on their first call, so the first reading was about 0
however long the process had run, and exactly 0 when both reads fell in
one tick of the host clock (100 ns on Windows). Capture the start when
libkernel loads.
2026-10-11 18:13:30 +09:00
Hannah Lyons 93cb3a1b0d fix(libkernel): compare the full WAIT_UINT value and throw on _umtx_op operation 1 2026-10-11 11:13:09 +02:00
clem-gb 79f90f3d9b feat(libkernel): implement _umtx_op wait and wake operations
Titles that ship their own libc.prx or Unity's PS5Util.prx import
_umtx_op (04AjkP0jO9U) for atomic waits, call_once and futex-style
waits. Implement UMTX_OP_WAIT, UMTX_OP_WAKE, UMTX_OP_WAIT_UINT,
UMTX_OP_WAIT_UINT_PRIVATE and UMTX_OP_WAKE_PRIVATE as FreeBSD 11's
kern_umtx.c does, on the sceKernelSyncOnAddressWait waiter queue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
2026-10-11 11:13:08 +02:00
Claude c356112f32 fix(libkernel): keep raises off unregistered threads and clamp the handler's MXCSR
Two defects found in review of the Linux delivery:

A raise right after scePthreadCreate reached the new thread before
RunThread set currentThread, so scePthreadSelf() in the handler adopted
a second handle, and could deadlock on glibc's thread lock when the
signal interrupted pthread_getattr_np. The creator now blocks all
signals around the std::thread construction, so the child starts with
them blocked, and RunThread restores the creator's mask once the thread
is registered, as libthr does. A raise in that window stays pending and
is delivered just before the entry runs.

A handler that set reserved MXCSR bits made rt_sigreturn fail with
SIGSEGV. The MXCSR the handler leaves is now masked with the frame's
MXCSR_MASK (0xffbf when it reads 0), as FreeBSD's fpusetregs does, on
both platforms; Windows also keeps CONTEXT.MxCsr equal to
FltSave.MxCsr.

guest_raise_exception raises right after each of 50 thread creations and
checks scePthreadSelf() in the handler, and its context round trip sets
a reserved MXCSR bit and FZ and checks that only FZ is kept.

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwJx7AHrSUaMGSpEGhL1Kb
2026-10-11 11:12:02 +02:00
Adrià 90c152e840
Merge pull request #1333 from TibixDev/pr/descriptor-pool-recycling
perf(agc): recycle descriptor pools after their last set is released
2026-10-11 11:11:24 +02:00
Claude dca5a88ef2 feat(libkernel): deliver sceKernelRaiseException on Linux
sceKernelRaiseException threw on Linux, so a title whose garbage
collector stops threads with it (Unity's PS5Util raises SIGUSR1 on each
thread and reads mc_rsp from the handler's context) could not run there.

The raise now sends the host SIGUSR1 to the target thread with
pthread_kill. The host handler, installed on first use with SA_SIGINFO
and SA_RESTART, builds the FreeBSD-layout ucontext from the interrupted
registers and the FXSAVE area, calls the guest handler on the target's
own stack and writes the registers back, so the handler's changes reach
the interrupted code, as they do on Windows. errno is left as the
handler leaves it, as libthr does.

A raise on another thread takes the target's join lock with the host
signal blocked and returns ESRCH once the thread has finished, so a
detached thread's pthread_t is never signalled after it exits. The
pthread_t is recorded when the thread is created or adopted. Handlers
are now lock-free atomics, as the host handler reads them.

guest_raise_exception now runs on every platform. It also checks that
r13 and xmm8 reach the handler and that r12 and xmm8 written by the
handler reach the interrupted code.

AI-assisted: yes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwJx7AHrSUaMGSpEGhL1Kb
2026-10-11 11:11:23 +02:00
Adrià Franch a0e03ab48d fix(agc): write a color compare's channels to their own registers 2026-10-11 11:10:41 +02:00
Adrià Franch 6ac5cad133 fix(agc): compare the red channel of a swizzled view 2026-10-11 11:10:41 +02:00
Gjon Gojcevic d32a7881f0 feat(agc): apply the view's channel selects to a color compare
Comparison sampling of a color texture required the view's X channel to select red. A title runs image_sample_c_lz on a 1x1 R8 unorm placeholder whose dst_sel is constant 0 on all four channels.

On hardware the compare result is placed in every channel and each returned channel then applies its own dst_sel: 0 gives 0, 1 gives 1, R/G/B/A give the compare result. Return each dmask register from its own select, and throw only for the reserved selects 2 and 3.
2026-10-11 11:10:41 +02:00
Adrià Franch 3ac63a6d4a fix(libc): key the .eh_frame index on the image, drop the VirtualQuery fallback, test unwinding through runtime DLLs
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:09:18 +02:00
Gjon Gojcevic 2122d7f308 perf(libc): index .eh_frame and find unwind modules without a syscall
On Windows every unwind step called VirtualQuery to find the frame's
module, and a module with a plain .eh_frame (libstdc++, libgcc, libwinpthread)
was searched by decoding each FDE in turn. A C++ exception crossing those
frames cost about a millisecond. Look the module up with RtlPcToFileHeader
first, falling back to VirtualQuery for images the loader does not know,
and binary-search a per-module index of FDE start addresses built on first
use.
2026-10-11 11:09:12 +02:00
Adrià Franch 60a14b5be6 fix(recompiler): bind a 2D texture to a 1D image access
A 1D sample or load of a 2D T# had no runtime image mode after the mode table landed, so it was still refused at materialization. Sampled 1D accesses now also accept a 2D view; the address completion in the emitter reads the missing row as 0.
2026-10-11 11:07:41 +02:00
Gjon Gojcevic 8f44aa656a fix(recompiler): read sampled image address components the instruction omits as 0
Measured on hardware: a position component the instruction omits reads as 0, like an explicit 0, and the next VGPR is ignored. Missing array layers and derivative components are 0 too. Cube coordinates still need all components.
2026-10-11 11:07:41 +02:00
Adrià Franch ac1c3b775b fix(agc): read the base level of a mipmapped view through unnormalized coordinates only where the hardware does
Multi-level views, and views starting above mip 0, are bound as their base
level only when every use of the image is image_sample_lz through an
unnormalized S# without minimum LOD or LOD bias, the case measured exact on
RDNA2. Selected-LOD, implicit-LOD and offset uses keep throwing.

The recompiler records selected-LOD uses (SamplerUseNonZeroLod) and proves
the case per image (imageUnnormalizedBaseLevel); the shader disk cache
format goes to 27.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 11:07:25 +02:00
Gjon Gojcevic 0fe4d66b7e fix(agc): sample the base level of mipmapped views with unnormalized coordinates
Vulkan only accepts unnormalized samplers on single-level views, while the
guest binds full mip chains and samples their base level. Bind a lazily
created one-level view of the descriptor's base mip instead of refusing
mipmapped views and views that start above mip 0.
2026-10-11 11:07:25 +02:00
Adrià ac2c5c08c8
Merge pull request #1349 from TibixDev/pr/rgba16-component-order
fix(agc): support alternate channel order in 16-bit color targets
2026-10-11 11:07:22 +02:00
Adrià Franch 3c8c02f194 docs(debt): move the PPSA01530 imports line into Unknown function info and note the inferred Ult forwards 2026-10-11 11:07:12 +02:00
Achraf Reyani c9f4689248 fix(exports): drop the sceNpWebApi2GetMemoryPoolStats stub and register the test with the shared guest tests
include from Equeue.cpp.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:07:03 +02:00
xXPBSXx c60c123f37 fix(libScePad): export scePadUnknown_n3kSX62fgNo under its NID
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 11:07:03 +02:00
xXPBSXx c4d29f6a99 fix(exports): export the calls PPSA01530, PPSA23827 and PPSA13356 import
Forwards _sceUltWaitingQueueResourcePoolCreate and _sceUltSemaphoreCreate to the existing calls, like the other underscore Ult exports. Declares sceNetShowIfconfig, sceNpWebApi2GetMemoryPoolStats, Json::Value::setNullAccessCallBack, kqueue, kevent and one pad call of unknown name without an implementation.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-11 11:07:03 +02:00
Karan Vora 17267336af fix(shader): get the pknorm tie error from exact subtractions
normF32 took the error of p = |x| * scale from fma(|x|, scale, -p).
Vulkan only guarantees GLSL Fma on f32 the precision of a multiply
followed by an add, and lavapipe in Mesa 25.2.8 splits it, so the error
was 0 there and products that round to k + 0.5 were resolved by
roundEven: v_cvt_pknorm_u16_f32 of 0x37c000c0 gave 0x00020002 instead of
0x00010001.

scale + 1 is a power of two, so A = |x| * (scale + 1) is exact and the
product is A - |x|. Since A >= |x|, p = A - |x| and the error
(A - p) - |x| are computed with correctly rounded subtractions only
(Fast2Sum), which every driver gives exactly.
2026-10-11 05:03:58 -04:00
Adrià a0165d6774
Merge pull request #1307 from ebarr24/codex/luna-posix-file-errors
fix(libkernel): translate scalar POSIX I/O errors
2026-10-11 11:03:50 +02:00
Adrià 4f8d4017c7
Merge pull request #1296 from TibixDev/pr/pad-file-input
feat(pad): accept timed controller pulses from a local command file
2026-10-11 11:01:22 +02:00
Adrià Franch de89b88900 test(agc): expect the reversed-order blend rejection by its message 2026-10-11 10:59:37 +02:00
TibixDev 058b7e8f94 fix(agc): support alternate channel order in 16-bit color targets
Map red and blue exports, write masks and constant-color blend factors together. Reject conflicting simultaneous target constants.

Semantics: Mesa 26.2.2 src/amd/common/ac_formats.c ac_translate_colorswap; console equivalence remains unverified. Graphics tests passed. Written with AI assistance.
2026-10-11 10:59:37 +02:00
Karan Vora e0be950774 perf(shader): pick f16 min, max and med3 results on the bits
v_min_f16, v_max_f16, v_min3_f16, v_max3_f16, v_med3_f16, v_pk_min_f16
and v_pk_max_f16 converted their sources to f32, ran the f32 min, max
or med3 with its NaN and signed-zero rules, and converted the result
back to f16 before applying the f16 NaN rules on the bits. The result
is always one of the sources, or for two zeros their sign bits
combined, so it is now chosen on the f16 bits: an integer comparison of
the bits with the sign folded into an ordering key, the same AND/OR
rule for two zeros, then the existing NaN, IEEE_MODE, denormal flush
and clamp rules.

The integer comparison doesn't depend on how the driver converts f16
denormals to f32. Comparing the f32 conversions instead fails on an
Intel UHD, whose UnpackHalf2x16 makes f16 denormals zero when the
module declares no f16 DenormPreserve, as these instructions no longer
convert to f16.

New test agc_driver_float16_min_max_bits checks the seven instructions
(VOP2 and VOP3 forms with abs, neg, op_sel and clamp; packed op_sel,
op_sel_hi and neg) on random and special inputs against a model of
those rules, in seven float modes including the f16 input denormal
flush. Current main gives the same results as the model on an RTX
4060, an Intel UHD and lavapipe.
2026-10-11 04:59:08 -04:00
Hussein Kadhim 14446548a9 fix(libkernel): keep mkdir errno stable and handle trailing separators in sceKernelMkdir
Capture errno before the ENOTDIR lookup, translate EINVAL from a name
under a regular file to ENOTDIR, check the parent only once and strip a
trailing separator before taking it, and split the dense rmdir return.
2026-10-11 10:58:38 +02:00
Hussein Kadhim e4b6a7b919 fix(libkernel): report ENOTDIR from mkdir and rmdir when a path component is a file
On Windows mkdir_nid_postfix returned ENOENT and sceKernelMkdir and
sceKernelRmdir returned ENOENT or EIO when a regular file was used as a
directory in the path. FreeBSD mkdir(2) and rmdir(2) return ENOTDIR for
a path component that is not a directory.
2026-10-11 10:58:21 +02:00
Karan Vora 2069fd1262 Merge remote-tracking branch 'origin/main' into perf/shader-coherent-alias 2026-10-11 04:57:31 -04:00
Adrià fd50c632a9
Merge pull request #690 from nicolasabosch/docs/doom-homebrew-compatibility
docs: record DOOM homebrew integration compatibility
2026-10-11 10:57:13 +02:00
Karan Vora 8d0a0504ae Merge remote-tracking branch 'origin/main' into perf/shader-pack-conversions-f32 2026-10-11 04:57:04 -04:00
Adrià Franch bd79dd4898 test(agc): register the descriptor pool recycling test in ctest 2026-10-11 10:54:58 +02:00
TibixDev e645d13048 test(agc): install the guest terminate handler for pool failures 2026-10-11 10:54:52 +02:00
TibixDev 4e7ca3c760 fix(agc): recycle descriptor pools after their last set is released
Allocate cached sets without individual-free pools. Reset a pool after its
last owner releases it. Keep stable pool indices, skip full pools, and
terminate if reset fails. No GPU wait is added.

This avoids RADV 26.2.2's affected individual-free allocation path.

Validation: Fedora 44 Release graphics tests pass. The reset-failure
regression fails on the previous behavior and passes on this change.
llvmpipe and RX 6600 pass the 3,301-set lifecycle test with core and
synchronization validation. Windows was not tested.

AI-assisted: yes.
2026-10-11 10:54:44 +02:00
Hussein Kadhim 1100d41a36 fix(kernel): accept the default handler for SIGKILL and SIGSTOP in signal
FreeBSD's kernel_sigaction rejects SIGKILL and SIGSTOP only for a handler other than SIG_DFL, so signal(9, SIG_DFL) and signal(17, SIG_DFL) succeed. Drop the SIG_ERR rejection, which has no kernel basis and which sigaction does not apply.
2026-10-11 10:54:27 +02:00
TibixDev 3a35841aad feat(pad): accept timed controller pulses from a local command file
Add an optional input helper for test runs without desktop input injection. Preserve physical input and release injected buttons automatically.

Validation: guest_pad tests and Python helper CLI smoke checks passed. Written with AI assistance.
2026-10-11 10:54:18 +02:00
Hussein Kadhim a8cc561f82 fix(libkernel): accept signal() for every valid signal number
signal() returned SIG_ERR with EINVAL for any signal without a host
equivalent, such as SIGHUP, SIGPIPE and SIGUSR1, while sigaction()
accepted them. FreeBSD's signal() is sigaction() with SA_RESTART and fails
only for numbers outside 1..128 and for SIGKILL and SIGSTOP.
2026-10-11 10:53:09 +02:00
Adrià Franch ba46784e16 test(libkernel): pin direct sceKernelRead/Write/Lseek errors and a read on a directory 2026-10-11 10:51:03 +02:00
Ethan Barr 79baad41f4 fix(libkernel): translate scalar POSIX I/O errors 2026-10-11 10:50:48 +02:00
Adrià Franch 0efa96271c docs: update DOOM homebrew compatibility with merged dependencies and third-party results
Drops the stale pending-dependency note (#553 #683 #684 #685 are merged), adds the
package source and the Linux, extended-gameplay, save/load and audio results
reported in the thread, and replaces the cold-start note with the measured cause.
The UCRT diagnostic note is removed: no report on current main.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-11 10:47:25 +02:00
Nicolas Abosch 63ddfcbb51 docs: record DOOM homebrew integration compatibility 2026-10-11 10:47:11 +02:00
Hussein Kadhim d67ad1fc14 docs(technical-debt): record the Linux narrow %p difference 2026-10-11 10:43:41 +02:00
Hussein Kadhim 44f00e2be8 fix(libc): format %p as FreeBSD does on Windows
The Windows formatter passed %p to the host CRT, which prints 16
zero-padded digits without a prefix. FreeBSD prints 0x followed by the
lowercase hex digits (0x0 for a null pointer), as if by %#lx.
2026-10-11 10:43:41 +02:00
goncalompontes 54c839959f feat(shader): decode GFX10 VOP3-encoded interpolation
v_interp_p1_f32_e64, v_interp_p2_f32_e64 and v_interp_mov_f32_e64
(VOP3Interp opcodes 0x200, 0x201 and 0x202) reach the decoder as VOP3
and were refused as unimplemented VOP3 opcodes. Decode them into the
existing VInterpP1F32, VInterpP2F32 and VInterpMovF32 opcodes with the
VOP3Interp operand layout, so the interpolation translation already
used by the VINTRP encodings handles them.

Encodings and operand layout follow LLVM's gfx10 MC tests for
VOP3Interp_Real_gfx10 (D69348). The interpolation parameter is the low
six bits of src0, the channel bits 6:7, the coordinate (or the
interpolation parameter of the mov form) is src1, and clamp and omod
reach the destination. op_sel, the packed-half selector, a third source
and abs/neg on operands that do not exist throw.
2026-10-10 21:07:55 +01:00
emirplus 6a7cce415a fix(savedata): recover from metadata setup failure 2026-10-10 21:09:00 +03:00
Karan Vora b6442c2137 Merge remote-tracking branch 'origin/main' into perf/shader-pack-conversions-f32 2026-10-10 12:01:10 -04:00
Karan Vora c3742a3995 Merge remote-tracking branch 'origin/main' into perf/shader-coherent-alias
# Conflicts:
#	core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/ProgramMetadata.hpp
2026-10-10 11:34:02 -04:00
Karan Vora 5a16ed15b1 docs(shader): list plain buffer loads beside GLC accesses as not coherent 2026-10-10 11:33:29 -04:00
Mini 781e049e50
Merge branch 'boykopovar:main' into pr/videoout-test-driver 2026-10-10 01:43:53 -07:00
Mini d43c2d8750
Merge branch 'boykopovar:main' into pr/videoout-test-driver 2026-10-10 01:24:06 -07:00
MiniRML 97dfbfe4e0 test(videoout): make SDL video driver configurable 2026-10-10 01:08:19 -07:00
ed a83bfa6813 fix(hw-oracle): select the AMDGPU ELF emulation when linking kernels
LLD built for a MinGW host runs the COFF linker when ld.lld gets no -m option,
so it rejects the AMDGPU object with "unknown file type" and the
hardware_oracle test fails on Windows with WinLibs LLVM in PATH. Passing
-m elf64_amdgpu selects the ELF linker on every host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:09:53 -07:00
Karan Vora 3a16e5e54e perf(shader): round v_cvt_pknorm and v_cvt_pk_u8 in f32
v_cvt_pknorm_i16/u16_f32 rounded |x| * 32767 or 65535 to nearest even
with a 64-bit integer product, variable 64-bit shifts and a sticky bit.
In f32, p = |x| * scale is the f32 nearest to the exact product, so
roundEven(p) is the answer unless p is exactly k + 0.5, where the sign of
the exact error fma(|x|, scale, -p) decides. Lanes that select a
constant (NaN, |x| >= 1) multiply 0 instead.

v_cvt_pk_u8_f32 truncated with the saturating conversion and rounded the
fraction by hand; it is now roundEven(x) between the 0 and 255 clamps,
NaN giving 0, read without the f32 denormal flush.

New test agc_driver_f32_pack_rounding: 563,232 inputs around every
rounding tie, checked against the exact product in double.
2026-10-09 14:05:07 -04:00
Karan Vora d220f48e92 perf(shader): load from buffers without Coherent when the load has no GLC or DLC
A program with one GLC or DLC buffer load or store decorated the whole
guest buffer array Coherent, so its other loads were coherent too. On
NVIDIA that keeps them out of the L1 cache: one GLC store made the
re-reads of a 4 KiB window about 2.7 times slower.

RDNA keeps those loads incoherent. A load without GLC or DLC can hit the
per-CU L0 and per-SA L1 caches, which are not coherent between
work-groups, and code that needs another work-group's data reads it
with GLC/DLC or invalidates the caches first (LLVM AMDGPU memory model,
GFX10: an acquiring load is followed by buffer_gl1_inv; buffer_gl0_inv).

The program metadata now records whether a program invalidates a cache
(buffer_gl0_inv, buffer_gl1_inv, s_gl1_inv, s_dcache_inv). Without one,
a program with GLC/DLC accesses gets a second array on the same binding,
buffers_coherent, decorated Coherent, and both arrays are Aliased as
buffers_u64 already is. Loads without GLC or DLC read buffers; GLC/DLC
loads, stores and atomics use buffers_coherent. A program that
invalidates a cache keeps the whole array Coherent.
2026-10-09 12:22:04 -04:00
krishalivaibhav 4361d8c53a test(shader): validate image compilation on SPIR-V 1.3 targets 2026-10-09 16:46:37 +05:30
emirplus a8c2100f59 fix(savedata): ensure metadata precedes blob commit and handle rollback recovery
Write initial SaveDataParam metadata before replacing the memory blob so metadata failure cannot leave a prematurely grown blob. Handle blob write failure with metadata rollback, and allow retries to recover slots with missing metadata. Expand tests with nonzero data, blob failure, and fresh readback.
2026-10-08 21:41:09 +03:00
emirplus c4c3ed7711 fix(savedata): recover from metadata setup failure
Report SAVE_DATA_ERROR_INTERNAL when setupSaveDataMemory2 fails to persist initial SaveDataParam metadata, and restore previous state so retrying setup can initialize cleanly. Closes #1667.
2026-10-08 19:18:36 +03:00
Sanjaysrinivas d9afb0af62 test(shader): cover half expansion bit patterns 2026-10-08 12:19:59 +02:00
krishnaraya 480b04801c fix(shader): compute V_FRACT_F32 from the exact fraction of |x|
Co-authored-by: Hashaam Zafar <68960089+hashaam1217@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 22:06:57 -04:00
1254 changed files with 96754 additions and 7648 deletions

View File

@ -0,0 +1,81 @@
title: "Game name (PPSA00000)"
body:
- type: markdown
attributes:
value: |
One thread per title, for every platform: search this category for the title ID first. If a thread exists, reply there, also when your OS or GPU differs from the one it reports.
Name the discussion `Game name (title ID)`, for example `DOOM Shareware (PPSA99666)`, without the platform.
Bug reports and implementation work belong in issues and pull requests; link them here as the blocker.
Per-game progress is also tracked on the [blocker map](https://alxcbtw.github.io/anyps5-roadmap/), built from public GitHub data. Only share material you have the right to distribute.
- type: checkboxes
id: searched
attributes:
label: Existing thread
options:
- label: I searched this category for the title ID and no thread exists for this title on any platform.
required: true
- type: input
id: title
attributes:
label: Title
description: Name and title ID from `sce_sys/param.json`.
placeholder: DOOM Shareware (PPSA99666)
validations:
required: true
- type: markdown
attributes:
value: |
### First report
The fields below describe one run. Other systems and content versions are added to the thread as replies.
- type: input
id: version
attributes:
label: Content version tested
description: The content version from `sce_sys/param.json`.
placeholder: 01.001.001
validations:
required: true
- type: input
id: commit
attributes:
label: AnyPS5 commit
description: The `main` commit that was built, or the release that was used, and any pull requests merged on top.
validations:
required: true
- type: input
id: system
attributes:
label: System tested
description: OS, GPU and driver version, and the compiler if you built it yourself.
placeholder: Arch Linux, GCC 16.2.1, RTX 5080 (NVIDIA 615.78.08)
validations:
required: true
- type: dropdown
id: reached
attributes:
label: Furthest point
options:
- Does not convert
- Stops before the first frame
- Logos or loading screen
- Menu
- In game
validations:
required: true
- type: textarea
id: seen
attributes:
label: What is shown
description: The last screen that was seen, the frame rate there, and what works or is missing (picture, audio, input).
validations:
required: true
- type: textarea
id: blocker
attributes:
label: Current blocker
description: The line where it stops, the issue that tracks it, and the open pull requests and local changes the title still needs.
- type: textarea
id: steps
attributes:
label: How it was run
description: Commands and environment variables, and what the title needed beyond the usage guide.

65
.github/ISSUE_TEMPLATE/performance.yml vendored Normal file
View File

@ -0,0 +1,65 @@
name: Performance report
description: A slow scene or a change in speed, measured with a performance trace so that reports can be compared.
title: "perf: "
body:
- type: markdown
attributes:
value: |
Capture the scene with `APS5_TRACE_FRAMES=<start>:<count>` (presents; `APS5_TRACE_SECONDS=<start>:<length>` also works), as described under [Performance trace](https://github.com/boykopovar/AnyPS5/blob/main/docs/dev/BUILD.md#performance-trace). It writes `aps5-trace-<commit>-<pid>.json.gz` and appends one line to `aps5-summary.jsonl`.
- Run the scene once and restart the title, so the shader and pipeline caches are warm.
- Use the same scene, window, resolution and settings for every capture you compare.
- Set no other `APS5_TRACE_*` or `APS5_PROFILE_*` switch and do not redirect the log; they cost time.
- Capture the base and the change on the same machine in the same session.
A trace holds timings, scope and thread names and counts, no game data. The summary line names the GPU, driver and OS.
- type: input
id: scene
attributes:
label: Title and scene
description: Title ID and what the window covers (menu, level, position, cutscene).
placeholder: PPSA01325, main menu after the intro
validations:
required: true
- type: textarea
id: observed
attributes:
label: What is slow or what changed
description: What you see (frame time, stutter, a phase that grew) and, for a comparison, the two commits.
validations:
required: true
- type: textarea
id: summary
attributes:
label: Summary lines
description: The line of aps5-summary.jsonl for each capture. It holds the commit, the APS5_* switches, GPU, driver, OS, resolution and frame times.
render: json
validations:
required: true
- type: textarea
id: trace
attributes:
label: Traces
description: Attach each aps5-trace-<commit>-<pid>.json.gz (drag it into this field). They open in ui.perfetto.dev.
validations:
required: true
- type: textarea
id: digest
attributes:
label: Digest
description: The output of `python3 tools/trace_digest.py <trace>` (with `--stacks <file>.collapsed.txt` if you sampled the worker).
- type: textarea
id: stacks
attributes:
label: Worker stacks
description: Optional. The collapsed-stack file of APS5_SAMPLE_WORKER (<file stem>.collapsed.txt), attached. It opens in speedscope.app.
- type: checkboxes
id: protocol
attributes:
label: Capture
options:
- label: The caches were warm (the scene ran once and the title was restarted)
required: true
- label: No other APS5_TRACE_* or APS5_PROFILE_* switch was set and the log was not redirected
required: true
- label: Captures compared here come from the same machine, scene and settings

View File

@ -1,16 +1,18 @@
### What
### Tested
<!-- OS, title or homebrew, what was checked. "Not tested" is a valid answer. -->
<!-- OS, title or homebrew, what was checked. "Not tested" is a valid answer. A performance change: the summary lines of main and of this PR from the same machine and scene, with the traces attached, captured as the Performance report issue form describes. -->
### Checklist
<!-- Rules behind each item: https://github.com/boykopovar/AnyPS5/blob/main/CONTRIBUTING.md -->
- [ ] Based on current `main`; no other open PR implements the same functions
- [ ] One topic per PR; follow-ups go in a new PR
- [ ] No comments in code except [technical debt](https://github.com/boykopovar/AnyPS5/blob/main/docs/dev/CONVENTIONS.md)
- [ ] Unimplemented paths throw (`NotImplemented_nid_no_patch`); silent stubs are listed in [TechnicalDebt](https://github.com/boykopovar/AnyPS5/blob/main/docs/dev/TechnicalDebt.md#silent-stubs)
- [ ] Unimplemented paths throw (`NotImplemented_nid_no_patch`); silent stubs are listed under Silent stubs in their library's [technical debt](https://github.com/boykopovar/AnyPS5/tree/main/docs/dev/debt) file
- [ ] No notes, investigation `.md` files or images added to the repository (attach them to this PR)
- [ ] New third-party code is a submodule built from source
- [ ] Generic behaviour, not specific to one title
- [ ] Depends on: <!-- #PR, or none -->
- [ ] Unblocks: <!-- title ID and Game bring-up discussion, e.g. PPSA01325 #3232, or none -->
- [ ] GPUs tested: <!-- for AGC, shader recompiler or VideoOut changes: vendor, model, driver and OS of each GPU the GPU tests ran on; otherwise n/a -->
- [ ] AI-assisted: <!-- yes / no -->

View File

@ -14,18 +14,34 @@ jobs:
contents: read
pull-requests: write
steps:
- uses: actions/cache/restore@v6
with:
path: ${{ runner.temp }}/area-state.json
key: area-labels-${{ github.run_id }}
restore-keys: area-labels-
- id: prs
env:
GH_TOKEN: ${{ github.token }}
run: |
state=$RUNNER_TEMP/area-state.json
[ -s "$state" ] || echo '{}' > "$state"
config=$(gh api "repos/$GITHUB_REPOSITORY/contents/.github/labeler.yml" --jq .sha)
gh pr list -R "$GITHUB_REPOSITORY" --base "${{ github.event.repository.default_branch }}" -L 200 --json number,headRefOid \
--jq "map({key: (.number | tostring), value: (.headRefOid + \"-$config\")}) | from_entries" > "$RUNNER_TEMP/area-open.json"
jq -r --slurpfile old "$state" 'to_entries[] | select($old[0][.key] != .value) | .key' "$RUNNER_TEMP/area-open.json" | head -n 40 > "$RUNNER_TEMP/area-todo"
jq -c --slurpfile open "$RUNNER_TEMP/area-open.json" --rawfile todo "$RUNNER_TEMP/area-todo" \
'with_entries(select(.key | in($open[0]))) + ($open[0] | with_entries(select(.key | IN($todo | split("\n")[]))))' "$state" > "$RUNNER_TEMP/area-next.json"
{
echo 'numbers<<EOF'
gh pr list -R "$GITHUB_REPOSITORY" --base "${{ github.event.repository.default_branch }}" -L 200 --json number --jq '.[].number'
cat "$RUNNER_TEMP/area-todo"
echo EOF
} >> "$GITHUB_OUTPUT"
- if: steps.prs.outputs.numbers != ''
uses: actions/checkout@v7
- if: steps.prs.outputs.numbers != ''
uses: actions/labeler@v7
with:
pr-number: ${{ steps.prs.outputs.numbers }}
- run: mv "$RUNNER_TEMP/area-next.json" "$RUNNER_TEMP/area-state.json"
- uses: actions/cache/save@v6
with:
path: ${{ runner.temp }}/area-state.json
key: area-labels-${{ github.run_id }}

View File

@ -12,6 +12,11 @@ on:
description: Pull request number to build
required: true
type: string
linux:
description: Also build and test on Linux
required: false
default: true
type: boolean
permissions:
contents: read
@ -39,6 +44,8 @@ jobs:
pr: ${{ steps.gate.outputs.pr }}
head: ${{ steps.gate.outputs.head }}
relinker: ${{ steps.gate.outputs.relinker }}
register-context: ${{ steps.gate.outputs.register-context }}
write-watch: ${{ steps.gate.outputs.write-watch }}
full: ${{ steps.gate.outputs.full }}
steps:
- uses: actions/github-script@v8
@ -55,6 +62,8 @@ jobs:
core.setOutput('pr', '');
core.setOutput('head', '');
core.setOutput('relinker', true);
core.setOutput('register-context', true);
core.setOutput('write-watch', true);
core.setOutput('full', true);
await core.summary.addRaw(`Building main at ${context.sha}: all builds and tests enabled`).write();
return;
@ -97,16 +106,32 @@ jobs:
});
relinker = commits.length > 0;
}
const files = await github.paginate(github.rest.pulls.listFiles, { owner, repo, pull_number, per_page: 100 });
const paths = files.flatMap((file) => [file.filename, file.previous_filename].filter(Boolean));
const touches = (...prefixes) => files.length >= 3000 ||
paths.some((path) => prefixes.some((prefix) => prefix.endsWith('/') ? path.startsWith(prefix) : path === prefix));
const workflow = '.github/workflows/build.yml';
const libc = 'core/libs/prx/libc/';
relinker = relinker && touches(
'core/relinker/', libc, 'core/libs/prx/libkernel/AppMetadata/', 'core/libs/tests/ParamJsonParser.cpp',
'core/shader/recompiler/tests/CacheFileReadTests.cpp', 'core/shader/recompiler/ShaderCacheDirectory.hpp',
'tools/', 'cmake/', 'CMakeLists.txt', workflow
);
const registerContext = touches('core/libs/tests/register_context/', 'core/libs/tests/RegisterContext.cpp', libc, workflow);
const writeWatch = touches('core/libs/tests/write_watch/', 'core/libs/tests/WriteWatch.cpp', libc, workflow);
const { data: main } = await github.rest.repos.getBranch({ owner, repo, branch: 'main' });
core.setOutput('sha', main.commit.sha);
core.setOutput('pr', String(pull_number));
core.setOutput('head', pr.head.sha);
core.setOutput('relinker', relinker);
core.setOutput('register-context', registerContext);
core.setOutput('write-watch', writeWatch);
core.setOutput('full', manual);
core.info(`PR #${pull_number} at ${pr.head.sha} merged into main at ${main.commit.sha}`);
await core.summary.addRaw(
`PR #${pull_number} at ${pr.head.sha} merged into main at ${main.commit.sha}: full builds ${manual ? 'enabled' : 'disabled'}, ` +
`relinker ${relinker ? 'enabled' : 'skipped because the author has no commit in main'}`
`relinker ${relinker ? 'enabled' : 'skipped: no relinker file changed or the author has no commit in main'}, ` +
`register context tests ${registerContext ? 'enabled' : 'skipped'}, write watch tests ${writeWatch ? 'enabled' : 'skipped'}`
).write();
relinker:
@ -162,6 +187,8 @@ jobs:
- run: ctest --test-dir build-relinker --output-on-failure --timeout 120
register-context:
needs: eligibility
if: ${{ needs.eligibility.outputs.register-context == 'true' }}
strategy:
fail-fast: false
matrix:
@ -176,6 +203,8 @@ jobs:
- run: ctest --test-dir build/register-context --output-on-failure --repeat until-fail:3 --no-tests=error
write-watch:
needs: eligibility
if: ${{ needs.eligibility.outputs.write-watch == 'true' }}
strategy:
fail-fast: false
matrix:
@ -190,7 +219,7 @@ jobs:
linux:
needs: eligibility
if: ${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && needs.eligibility.outputs.full == 'true' }}
if: ${{ (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.linux)) && needs.eligibility.outputs.full == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
@ -215,8 +244,17 @@ jobs:
key: linux
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
evict-old-files: job
- name: Install lavapipe for the GPU execution tests
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libvulkan1 mesa-vulkan-drivers
- name: Install lavapipe and the SDL audio and XRandR development files
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libvulkan1 mesa-vulkan-drivers libpipewire-0.3-dev libpulse-dev libasound2-dev libxrandr-dev
- id: mesa
run: |
echo "version=$(dpkg-query -W -f='${Version}' mesa-vulkan-drivers)" >> "$GITHUB_OUTPUT"
echo "cpu=$(grep -m1 '^flags' /proc/cpuinfo | sha256sum | cut -c1-12)" >> "$GITHUB_OUTPUT"
- uses: actions/cache/restore@v6
with:
path: .mesa-shader-cache
key: mesa-${{ steps.mesa.outputs.version }}-${{ steps.mesa.outputs.cpu }}-${{ github.sha }}
restore-keys: mesa-${{ steps.mesa.outputs.version }}-${{ steps.mesa.outputs.cpu }}-
- run: cmake -S . -B build $CONFIGURE -DANYPS5_ENABLE_SPIRV_TOOLS=ON
- name: Check that every test executable is registered with ctest
run: python3 tools/check_test_registration.py build
@ -226,6 +264,12 @@ jobs:
env:
ANYPS5_REQUIRE_VULKAN: 1
GALLIUM_OVERRIDE_CPU_CAPS: avx
MESA_SHADER_CACHE_DIR: ${{ github.workspace }}/.mesa-shader-cache
- if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && !cancelled() }}
uses: actions/cache/save@v6
with:
path: .mesa-shader-cache
key: mesa-${{ steps.mesa.outputs.version }}-${{ steps.mesa.outputs.cpu }}-${{ github.sha }}
- run: python3 tools/check_skips.py build/ctest.xml
- name: Report tests slower than 30 s
if: ${{ !cancelled() }}
@ -280,7 +324,7 @@ jobs:
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
evict-old-files: job
- shell: cmd
run: cmake -S . -B build %CONFIGURE%
run: cmake -S . -B build %CONFIGURE% "-DANYPS5_AGC_EXECUTION_TESTS=F16ToF32Unpack;F64FmaSubnormals;RegistrationPrepareShutdown"
- name: Check that every test executable is registered with ctest
shell: cmd
run: python tools/check_test_registration.py build

View File

@ -9,6 +9,10 @@ on:
permissions: {}
concurrency:
group: conflict-label
cancel-in-progress: true
jobs:
label:
runs-on: ubuntu-latest

View File

@ -24,7 +24,7 @@ jobs:
force_label() { gh label create "$1" --color "$2" --description "$3" --force; }
force_label 'needs rebase' b60205 'Conflicts with main, rebase needed'
force_label 'intake paused' d93f0b 'Closed while PR intake is limited to authors with at least 10 PRs merged into main'
force_label 'open PR limit' d93f0b 'Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20'
force_label ci 5319e7 '.github'
force_label agc 1d76db 'libSceAgc*'
force_label shader 1d76db 'core/shader'
@ -35,3 +35,4 @@ jobs:
label prx c5def5 'Other PRX libraries'
label build bfd4f2 'CMake, submodules, 3rdparty'
label docs 0075ca 'Documentation'
label stale ededed 'Conflicting with main and no activity for 7 days'

55
.github/workflows/pages.yml vendored Normal file
View File

@ -0,0 +1,55 @@
name: Pages
on:
push:
branches: [main]
workflow_dispatch:
release:
types: [published]
workflow_run:
workflows: [Release]
types: [completed]
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
pages:
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v7
with:
ref: main
fetch-depth: 0
- name: Build web converter
shell: bash
run: |
git clone --depth 1 https://github.com/emscripten-core/emsdk.git "$RUNNER_TEMP/emsdk"
"$RUNNER_TEMP/emsdk/emsdk" install 6.0.3
"$RUNNER_TEMP/emsdk/emsdk" activate 6.0.3
source "$RUNNER_TEMP/emsdk/emsdk_env.sh"
emcmake cmake -S . -B build-wasm -DANYPS5_BUILD_WASM=ON -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release
cmake --build build-wasm --target relinker_web --parallel 2
- name: Test web converter
run: python3 tools/test_web.py --build build-wasm
- name: Download latest release libraries
env:
GITHUB_TOKEN: ${{ github.token }}
run: python3 tools/build_web.py --build build-wasm --libraries
- run: python3 tools/progress.py build-wasm/web
- run: python3 tools/progress_history.py build-wasm/web
- uses: actions/upload-pages-artifact@v5
with:
path: build-wasm/web
- id: deployment
uses: actions/deploy-pages@v5

View File

@ -3,7 +3,7 @@ name: PR intake
on:
pull_request_target:
branches: [main]
types: [opened]
types: [opened, reopened]
permissions: {}
@ -27,7 +27,6 @@ jobs:
if (pr.state !== 'open' || pr.base.ref !== 'main') {
return;
}
const minimumMergedPRs = 10;
const { data: mergedPRs } = await github.rest.search.issuesAndPullRequests({
q: `repo:${owner}/${repo} is:pr is:merged base:main author:${pr.user.login}`,
per_page: 1
@ -35,10 +34,20 @@ jobs:
if (mergedPRs.incomplete_results || !Number.isSafeInteger(mergedPRs.total_count) || mergedPRs.total_count < 0) {
throw new Error('Could not reliably count merged pull requests');
}
if (mergedPRs.total_count >= minimumMergedPRs) {
const merged = mergedPRs.total_count;
if (merged >= 20) {
return;
}
const name = 'intake paused';
const allowedOpen = merged >= 1 ? 20 : 1;
const openPRs = await github.paginate(github.rest.pulls.list, {
owner, repo, state: 'open', base: 'main', per_page: 100
});
const open = new Set(openPRs.filter((p) => p.user.login === pr.user.login).map((p) => p.number));
open.add(pull_number);
if (open.size <= allowedOpen) {
return;
}
const name = 'open PR limit';
try {
await github.rest.issues.getLabel({ owner, repo, name });
} catch (error) {
@ -48,7 +57,7 @@ jobs:
try {
await github.rest.issues.createLabel({
owner, repo, name, color: 'd93f0b',
description: 'Closed while PR intake is limited to authors with at least 10 PRs merged into main'
description: 'Closed over the open PR limit: 1 without merged PRs, 20 with at least 1, none from 20'
});
} catch (createError) {
if (createError.status !== 422) {
@ -58,7 +67,7 @@ jobs:
}
}
await github.rest.issues.addLabels({ owner, repo, issue_number: pull_number, labels: [name] });
const body = 'Thank you for your interest in the project! We are sorry, but we cannot accept more PRs while we work through the existing backlog. This PR has been automatically closed and labeled "intake paused". This is not a judgment of the quality of your changes.';
const body = 'Thank you for your contribution! Authors with no pull requests merged into `main` can have 1 open pull request at a time, authors with at least 1 merged can have 20, and from 20 merged there is no limit. This PR has been automatically closed and labeled "open PR limit" because that limit was reached. This is not a judgment of the quality of your changes: reopen it once one of your open pull requests is merged or closed.';
const comments = await github.paginate(github.rest.issues.listComments, {
owner, repo, issue_number: pull_number, per_page: 100
});

View File

@ -22,15 +22,30 @@ jobs:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/cache/restore@v6
with:
path: ${{ runner.temp }}/overlap-state.json
key: pr-overlap-${{ github.run_id }}
restore-keys: pr-overlap-
- env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
out=$RUNNER_TEMP/overlap
state=$RUNNER_TEMP/overlap-state.json
python3 tools/pr_overlap.py --base "${{ github.event.repository.default_branch }}" --out "$out"
[ -s "$state" ] || echo '{}' > "$state"
record() { jq --arg pr "$1" --arg h "$2" '.[$pr] = $h' "$state" > "$state.new" && mv "$state.new" "$state"; }
shopt -s nullglob
marker='<!-- pr-overlap -->'
prev=
for file in "$out"/*.md; do
[ -z "$prev" ] || record "$prev" "$hash"
pr=$(basename "$file" .md)
hash=$(sha256sum "$file" | cut -d' ' -f1)
prev=
[ "$(jq -r --arg pr "$pr" '.[$pr] // empty' "$state")" = "$hash" ] && continue
prev=$pr
comment=$(gh api "repos/$GH_REPO/issues/$pr/comments" --paginate \
--jq ".[] | select(.user.login == \"github-actions[bot]\" and (.body | startswith(\"$marker\"))) | [.id, .body] | @json" | head -n1)
id=$(jq -r '.[0] // empty' <<< "${comment:-null}")
@ -45,3 +60,11 @@ jobs:
gh api -X DELETE "repos/$GH_REPO/issues/comments/$id"
fi
done
[ -z "$prev" ] || record "$prev" "$hash"
reports=$(for file in "$out"/*.md; do basename "$file" .md; done | jq -R . | jq -sc .)
jq -c --argjson keep "$reports" 'with_entries(select(.key | IN($keep[])))' "$state" > "$state.new" && mv "$state.new" "$state"
- if: always()
uses: actions/cache/save@v6
with:
path: ${{ runner.temp }}/overlap-state.json
key: pr-overlap-${{ github.run_id }}

View File

@ -1,30 +0,0 @@
name: Progress
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
progress:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v7
- run: python3 tools/progress.py out
- uses: actions/upload-pages-artifact@v5
with:
path: out
- id: deployment
uses: actions/deploy-pages@v5

View File

@ -60,9 +60,9 @@ jobs:
with:
key: ${{ matrix.platform }}
save: false
- name: Install lavapipe for the GPU execution tests
- name: Install lavapipe for the GPU execution tests and the audio development files
if: matrix.platform == 'linux'
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libvulkan1 mesa-vulkan-drivers
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends libvulkan1 mesa-vulkan-drivers libpipewire-0.3-dev libpulse-dev libasound2-dev libxrandr-dev
- name: Configure Linux
if: matrix.platform == 'linux'
run: cmake -S . -B build $CONFIGURE

28
.github/workflows/stale-conflicts.yml vendored Normal file
View File

@ -0,0 +1,28 @@
name: Stale conflicts
on:
schedule:
- cron: '30 3 * * *'
workflow_dispatch:
permissions: {}
jobs:
warn:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v11
with:
only-pr-labels: needs rebase
days-before-issue-stale: -1
days-before-issue-close: -1
days-before-pr-stale: 7
days-before-pr-close: -1
stale-pr-label: stale
stale-pr-message: 'This pull request has conflicted with `main` and had no activity for 7 days. Rebase it on current `main` so it can be reviewed and merged. It will not be closed automatically.'
remove-pr-stale-when-updated: true
exempt-draft-pr: true
operations-per-run: 200

6
.gitmodules vendored
View File

@ -28,6 +28,12 @@
[submodule "3rdparty/stb"]
path = 3rdparty/stb
url = https://github.com/nothings/stb.git
[submodule "3rdparty/tracy"]
path = 3rdparty/tracy
url = https://github.com/wolfpld/tracy.git
[submodule "3rdparty/libjpeg-turbo"]
path = 3rdparty/libjpeg-turbo
url = https://github.com/libjpeg-turbo/libjpeg-turbo.git
[submodule "3rdparty/libusb-cmake"]
path = 3rdparty/libusb-cmake
url = https://github.com/libusb/libusb-cmake.git

1
3rdparty/libusb-cmake vendored Submodule

@ -0,0 +1 @@
Subproject commit bea6567b63796ab27c1c33257d230284fb2d8316

1
3rdparty/tracy vendored Submodule

@ -0,0 +1 @@
Subproject commit 30997d5ca6bb632cc10807a1da8a6d3de0aeeb3c

View File

@ -10,6 +10,19 @@ endfunction()
option(BUILD_TESTING "Build and register relinker tests" OFF)
option(ANYPS5_RELINKER_ONLY "Build the relinker without system libraries or third-party dependencies" OFF)
option(ANYPS5_BUILD_WASM "Build the browser relinker" OFF)
if(ANYPS5_BUILD_WASM)
if(NOT EMSCRIPTEN)
message(FATAL_ERROR "ANYPS5_BUILD_WASM requires emcmake and Emscripten")
endif()
if(BUILD_TESTING)
message(FATAL_ERROR "Use the native build for C++ tests and the browser tests for WASM")
endif()
set(ANYPS5_RELINKER_CLI OFF CACHE BOOL "Build the native relinker command line" FORCE)
add_subdirectory(core/relinker)
return()
endif()
if(BUILD_TESTING)
enable_testing()
if(MINGW)
@ -24,7 +37,9 @@ if(BUILD_TESTING)
endif()
option(ANYPS5_ENABLE_SPIRV_TOOLS "Enable SPIRV-Tools validation and optimization" OFF)
option(ANYPS5_ENABLE_ASAN "Build Linux targets with AddressSanitizer and use an ASan-compatible guest arena range" OFF)
option(APS5_ENABLE_TIMING_LOG "Write frame timings asynchronously to frame-timing.log" OFF)
option(APS5_ENABLE_TRACY "Build the Tracy profiler client and report performance timer scopes as Tracy zones" OFF)
option(ANYPS5_USE_SYSTEM_DEPS "Resolve SDL2, freetype, Vulkan-Headers, SPIRV-Headers, glslang and SPIRV-Tools from the system instead of the bundled submodules" OFF)
add_compile_definitions(APS5_ENABLE_TIMING_LOG=$<BOOL:${APS5_ENABLE_TIMING_LOG}>)
include(cmake/SystemDeps.cmake)
@ -32,11 +47,35 @@ include(cmake/SystemDeps.cmake)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT APPLE)
if(ANYPS5_ENABLE_ASAN)
if(NOT CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(FATAL_ERROR "ANYPS5_ENABLE_ASAN is supported only on Linux")
endif()
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
message(FATAL_ERROR "ANYPS5_ENABLE_ASAN requires GCC or Clang")
endif()
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_link_options(-fsanitize=address)
add_compile_definitions(ANYPS5_ENABLE_ASAN=1)
elseif(NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
endif()
if(BUILD_TESTING)
add_test_executable(shader_cache_file_read_tests
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/tests/CacheFileReadTests.cpp)
target_include_directories(shader_cache_file_read_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler)
add_test(NAME shader_cache_file_read COMMAND shader_cache_file_read_tests)
set_tests_properties(shader_cache_file_read PROPERTIES TIMEOUT 20)
add_test_executable(request_serializer_tests
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/tests/RequestSerializerTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp)
target_include_directories(request_serializer_tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler
${CMAKE_CURRENT_SOURCE_DIR}/core/shader/recompiler/ControlFlow/include)
add_test(NAME request_serializer COMMAND request_serializer_tests)
set_tests_properties(request_serializer PROPERTIES TIMEOUT 20)
find_package(Python3 QUIET COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
add_test(NAME pr_overlap_encoding COMMAND ${Python3_EXECUTABLE}
@ -45,22 +84,57 @@ if(BUILD_TESTING)
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_dependencies.py)
add_test(NAME pr_overlap_tools COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap.py)
add_test(NAME progress_history COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_history.py)
add_test(NAME progress_report COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress.py)
add_test(NAME progress_encoding COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_encoding.py)
add_test(NAME progress_render_sliver COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_render_sliver.py)
add_test(NAME progress_wrappers COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_wrappers.py)
add_test(NAME progress_read_encoding COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_progress_read_encoding.py)
add_test(NAME import_audit COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_import_audit.py)
add_test(NAME conventions_function_bodies COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_comments.py)
add_test(NAME conventions_wrapped_declarations COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_wrapped_declarations.py)
add_test(NAME conventions_ci_gate COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_ci_gate.py)
add_test(NAME test_registration COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_test_registration.py)
add_test(NAME check_skips COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_skips.py)
add_test(NAME stubgen COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_stubgen.py)
add_test(NAME debt_split COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_debt_split.py)
add_test(NAME stubgen_stdout_newlines COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_stubgen_stdout_newlines.py)
add_test(NAME hardware_oracle COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/hw-oracle/test_hw_oracle.py)
add_test(NAME pr_overlap_exports COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_exports.py)
add_test(NAME pr_overlap_wrapped_exports COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_wrapped_exports.py)
add_test(NAME pr_overlap_own_branch COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pr_overlap_own_branch.py)
add_test(NAME nid_database_cache COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_nid_names.py)
add_test(NAME release_package COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_package_release.py)
add_test(NAME pad_input COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_pad_input.py)
set_tests_properties(pad_input PROPERTIES TIMEOUT 20)
add_test(NAME trace_digest COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_trace_digest.py)
add_test(NAME conventions_body_bom COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_body_bom.py)
add_test(NAME conventions_agent_files COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/tools/tests/test_check_conventions_agent_files.py)
endif()
endif()
@ -89,6 +163,22 @@ if(NOT ANYPS5_USE_SYSTEM_DEPS)
set(CMAKE_EXE_LINKER_FLAGS "")
add_subdirectory(3rdparty/SDL2)
set(CMAKE_EXE_LINKER_FLAGS "${ANYPS5_EXE_LINKER_FLAGS}")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
get_directory_property(ANYPS5_SDL_X11_XRANDR DIRECTORY 3rdparty/SDL2 DEFINITION HAVE_X11_XRANDR)
if(NOT ANYPS5_SDL_X11_XRANDR)
message(FATAL_ERROR "SDL has no XRandR support, so X11 display refresh rates cannot be queried. "
"Install libxrandr-dev (Debian, Ubuntu) or libXrandr-devel (Fedora), "
"enable SDL_X11_XRANDR, then rerun configuration.")
endif()
get_directory_property(ANYPS5_SDL_PIPEWIRE DIRECTORY 3rdparty/SDL2 DEFINITION HAVE_PIPEWIRE)
get_directory_property(ANYPS5_SDL_PULSEAUDIO DIRECTORY 3rdparty/SDL2 DEFINITION HAVE_PULSEAUDIO)
get_directory_property(ANYPS5_SDL_ALSA DIRECTORY 3rdparty/SDL2 DEFINITION HAVE_ALSA)
if(NOT ANYPS5_SDL_PIPEWIRE AND NOT ANYPS5_SDL_PULSEAUDIO AND NOT ANYPS5_SDL_ALSA)
message(FATAL_ERROR "SDL found no PipeWire, PulseAudio or ALSA development files, so audio output would be silent. "
"Install libpipewire-0.3-dev, libpulse-dev and libasound2-dev (Debian, Ubuntu) "
"or pipewire-devel, pulseaudio-libs-devel and alsa-lib-devel (Fedora), then rerun configuration.")
endif()
endif()
endif()
set(ARCHITECTURE x86_64)
add_subdirectory(3rdparty/ffmpeg-core)
@ -98,6 +188,10 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_options(ffmpeg INTERFACE LINKER:--exclude-libs,ALL)
endif()
if(APS5_ENABLE_TRACY)
include(cmake/Tracy.cmake)
endif()
set(FT_DISABLE_ZLIB ON CACHE BOOL "" FORCE)
set(FT_DISABLE_BZIP2 ON CACHE BOOL "" FORCE)
set(FT_DISABLE_PNG ON CACHE BOOL "" FORCE)
@ -114,6 +208,14 @@ if(NOT ANYPS5_USE_SYSTEM_DEPS)
endif()
endif()
set(LIBUSB_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(LIBUSB_INSTALL_TARGETS OFF CACHE BOOL "" FORCE)
set(LIBUSB_BUILD_TESTING OFF CACHE BOOL "" FORCE)
set(LIBUSB_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(LIBUSB_ENABLE_UDEV OFF CACHE BOOL "" FORCE)
add_subdirectory(3rdparty/libusb-cmake EXCLUDE_FROM_ALL)
set_target_properties(usb-1.0 PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_subdirectory(core/shader/recompiler)
add_subdirectory(core/Decoder/Jpeg)
add_subdirectory(core/Decoder/Png)

View File

@ -5,7 +5,7 @@
- Follow the [coding conventions](docs/dev/CONVENTIONS.md): naming, no comments except [technical debt](docs/dev/TechnicalDebt.md), Conventional Commits.
- Every function either does exactly what it is supposed to or throws. Unimplemented exports call `NotImplemented_nid_no_patch(__func__)` (see [libSceAudioIn](core/libs/prx/libSceAudioIn/Export.cpp)).
- Pull requests that add or change shader instruction semantics say where they come from: measured on hardware (which GPU and what was checked, e.g. with the [hardware oracle](docs/dev/HW_ORACLE.md)) or the exact source (ISA section, LLVM, ACO or Mesa file). Reviewers check the semantics on hardware. Cases the source doesn't settle throw, and behaviour not verified on hardware is recorded in [technical debt](docs/dev/TechnicalDebt.md).
- A silent stub is allowed only when it unblocks a title and only affects the UI; add it to [silent stubs](docs/dev/TechnicalDebt.md#silent-stubs).
- A silent stub is allowed only when it unblocks a title and only affects the UI; add it under Silent stubs in its library's [technical debt](docs/dev/debt/) file.
- Missing imports fail at startup with a clear error; don't replace them with fallbacks that keep running.
- Implement the general behaviour of a function, not what one title happens to need.
- Don't add replacements for modules that titles ship themselves in `sce_module/`, `sce_modules/` or `prx/`: engine or middleware modules (Cohtml, FMOD, GOG Galaxy) and SDK libraries that only wrap other system libraries (NpCppWebApi over NpWebApi2). The relinker converts and loads the title's own module, and a host library with the same name is left out of `DT_NEEDED`. Only system libraries, which reach the kernel or the hardware, are reimplemented in [core/libs/prx](core/libs/prx).
@ -37,9 +37,9 @@ For pull requests, full Linux and Windows builds and tests run only when a colla
gh workflow run build.yml --repo boykopovar/AnyPS5 --ref main --field pr=1234
```
This manual run also builds and tests the relinker on Linux, Windows and macOS, regardless of the author's previous contributions or the changed files. The relinker matrix also runs automatically when a pull request targeting `main` is opened, updated or reopened, if its author already has a commit in `main`; full builds never run automatically for pull requests. All build jobs build the selected pull request merged into the current `main` (GitHub's merge commit), so a pull request that no longer merges cleanly must be rebased first. New commits require another manual run for full builds.
This manual run also builds and tests the relinker on Linux, Windows and macOS, regardless of the author's previous contributions or the changed files. The relinker matrix also runs automatically when a pull request targeting `main` is opened, updated or reopened, if its author already has a commit in `main` and it changes a file the relinker-only build uses; full builds never run automatically for pull requests. Every job that runs automatically for pull requests runs only when the pull request changes its files: a `paths` filter on the `pull_request` trigger, or `needs: eligibility` with an `if` on the files output it sets in `build.yml`. The Conventions check fails a workflow change that adds a job without one. All build jobs build the selected pull request merged into the current `main` (GitHub's merge commit), so a pull request that no longer merges cleanly must be rebased first. New commits require another manual run for full builds.
The Conventions check runs automatically for every contributor, including first-time contributors. It uses the base branch checker and reads pull request Git objects without executing pull request code. The Conventions check runs on every pull request and fails when a rule on this page is broken. It accepts code comments only when the pull request also changes [TechnicalDebt](docs/dev/TechnicalDebt.md), and only UTF-8 text files. Run it locally before pushing:
The Conventions check runs automatically for every contributor, including first-time contributors. It uses the base branch checker and reads pull request Git objects without executing pull request code. The Conventions check runs on every pull request and fails when a rule on this page is broken. It accepts code comments only when the pull request also changes [technical debt](docs/dev/TechnicalDebt.md), the index or a file in `docs/dev/debt/`, and only UTF-8 text files. Run it locally before pushing:
```
python3 tools/check_conventions.py --base origin/main
@ -50,13 +50,20 @@ python3 tools/check_conventions.py --base origin/main
The [pull request template](.github/pull_request_template.md) is the checklist for these rules.
- One branch per topic, based on current `main`. Follow-up work goes in a new pull request, not into an open one.
- Open pull requests per author are limited by how many of theirs are merged into `main`: 1 with none merged, 20 with at least 1, no limit from 20. A pull request opened or reopened over the limit is closed automatically; reopen it once another one is merged or closed.
- Before starting, check that no open pull request already implements the same functions.
- Keep the branch up to date with `main` and resolve conflicts yourself; rebasing and force-pushing is fine.
- If a pull request needs another one first, say so in the description (`Depends on #N`).
- A pull request that conflicts with `main` and has no activity for 7 days is labeled `stale` with a reminder. It is not closed.
- If a pull request needs another one first, say so on the checklist line (`Depends on: #N`; `none` otherwise).
- If a pull request fixes a stop or a wrong result seen on a title, name it on the checklist line with the title ID and its [Game bring-up](https://github.com/boykopovar/AnyPS5/discussions/categories/game-bring-up) discussion (`Unblocks: PPSA01325 #3232`; `none` otherwise). Reviewers review these first.
- Fixes a title needs go to `main` as pull requests. A Game bring-up discussion lists the open pull requests and local changes the title still needs; update the list when one is merged, closed or replaced.
- The PR overlap comment lists open pull requests that change the same lines or exports; agree with their authors which one goes first and declare it with `Depends on`.
- Run the tests before opening the pull request and describe what was tested (OS, title or homebrew).
- A pull request that claims a change in speed shows the summary lines of `main` and of the change, from the same machine and scene, and attaches the traces, captured as the [Performance report](.github/ISSUE_TEMPLATE/performance.yml) issue form describes. A slow scene is reported with the same form.
- Changes to the AGC driver, the shader recompiler or VideoOut name each GPU the GPU tests ran on (vendor, model, driver, OS) on the `GPUs tested` checklist line. CI runs these tests only on lavapipe, and hardware drivers fail where lavapipe and other vendors pass, so a reviewer can ask for a run on another vendor before merging.
- Investigation notes, reports, screenshots and logs go in the pull request, not in the repository. Images for documentation go in the [gist](https://gist.github.com/boykopovar/0e53f2e1426f29ecd41e3b51540b8a90) comments.
- Say whether the change was written with AI assistance. The author of the pull request is responsible for every line of it.
## Documentation
Text in the repository is dry, strict and concise. Record known gaps, unknown NIDs and signatures in [technical debt](docs/dev/TechnicalDebt.md) instead of new files.
Text in the repository is dry, strict and concise. Record known gaps, unknown NIDs and signatures in [technical debt](docs/dev/TechnicalDebt.md) instead of new files. An entry goes in `docs/dev/debt/<name>.md`, named after the first `core/` path it links (`<name>` for `core/libs/prx/<name>/...`, otherwise for `core/<name>/...`), under the same `###` sections as the index; entries that link neither stay in the index. A pull request whose `TechnicalDebt.md` change conflicts after the split rebases and runs `python3 tools/debt_split.py resolve` during the stopped rebase, which moves its added, edited and removed entries to their files; `python3 tools/debt_split.py split` moves entries left in the index.

View File

@ -2,29 +2,33 @@
Tool for automatic executables porting to Linux and Windows.
[**Web converter**](https://boykopovar.github.io/AnyPS5/) - files stay on your device.
![anyps5_collage1](https://github.com/user-attachments/assets/0f8e13e8-1cb7-4c69-8075-39978e138994)
[Discord](https://discord.gg/BHFztBPUe)
Includes a [relinker](core/relinker) that converts executable to the target system's native format and implementations of [system prx libraries](core/libs/prx) suitable for dynamic linking. No emulation or separate runtime process.
[Usage](docs/user/USAGE.md), [Build instructions](docs/dev/BUILD.md), [Architecture](docs/dev/ARCHITECTURE.md), [Technical debt of the project](docs/dev/TechnicalDebt.md), [code style conventions](docs/dev/CONVENTIONS.md), [contributing](CONTRIBUTING.md)
## Status
[![libraries](https://boykopovar.github.io/AnyPS5/badge-libraries.svg)](https://boykopovar.github.io/AnyPS5/) [![shaders](https://boykopovar.github.io/AnyPS5/badge-shaders.svg)](https://boykopovar.github.io/AnyPS5/)
[![libraries](https://boykopovar.github.io/AnyPS5/badge-libraries.svg)](https://boykopovar.github.io/AnyPS5/progress.html) [![shaders](https://boykopovar.github.io/AnyPS5/badge-shaders.svg)](https://boykopovar.github.io/AnyPS5/progress.html)
[![progress map](https://boykopovar.github.io/AnyPS5/progress.svg)](https://boykopovar.github.io/AnyPS5/)
[![progress map](https://boykopovar.github.io/AnyPS5/progress.svg)](https://boykopovar.github.io/AnyPS5/progress.html)
[Progress history](https://boykopovar.github.io/AnyPS5/history.html)
<sub>* System libraries: percentage of the functions known to the project so far (declared in [core/libs/prx](core/libs/prx)), not of every PS5 system function. The total grows as more functions are declared.</sub>
[List of verified games](docs/user/COMPATIBILITY.md)
Dreaming Sarah (2D platformer) runs at a stable 60 fps on a GTX 1050 Ti / i5-7500 3.4GHz.
Unsupported or unexpected states strictly throw `std::runtime_error`. `what()` is printed to stderr and the process terminates.
The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully produces SPIR-V (validated via [Spirv-Tools](3rdparty/SPIRV-Tools) when built with `ANYPS5_ENABLE_SPIRV_TOOLS`).
## Compatibility
See the [game compatibility list](docs/user/COMPATIBILITY.md) for tested games and known issues.
Per-game progress (stage reached, open blockers, libraries and GPU coverage) is tracked on the [blocker map](https://alxcbtw.github.io/anyps5-roadmap/), a community project built from public GitHub data ([source](https://github.com/AlxcBTW/anyps5-roadmap)). Test reports go in the [Game bring-up](https://github.com/boykopovar/AnyPS5/discussions/categories/game-bring-up) discussions, one thread per title.
## Input mapping

21
cmake/Tracy.cmake Normal file
View File

@ -0,0 +1,21 @@
set(TRACY_ENABLE ON CACHE BOOL "" FORCE)
set(TRACY_STATIC OFF CACHE BOOL "" FORCE)
set(TRACY_ON_DEMAND ON CACHE BOOL "" FORCE)
set(TRACY_ONLY_LOCALHOST ON CACHE BOOL "" FORCE)
set(TRACY_NO_CRASH_HANDLER ON CACHE BOOL "" FORCE)
set(TRACY_NO_CODE_TRANSFER ON CACHE BOOL "" FORCE)
set(TRACY_NO_SAMPLING ON CACHE BOOL "" FORCE)
set(TRACY_NO_SYSTEM_TRACING ON CACHE BOOL "" FORCE)
set(TRACY_NO_CALLSTACK ON CACHE BOOL "" FORCE)
set(TRACY_NO_FRAME_IMAGE ON CACHE BOOL "" FORCE)
set(TRACY_NO_VERIFY ON CACHE BOOL "" FORCE)
set(CMAKE_DISABLE_FIND_PACKAGE_rocprofiler-sdk ON)
add_subdirectory(${CMAKE_SOURCE_DIR}/3rdparty/tracy ${CMAKE_BINARY_DIR}/3rdparty/tracy EXCLUDE_FROM_ALL)
if(MINGW)
target_sources(TracyClient PRIVATE ${CMAKE_CURRENT_LIST_DIR}/TracyThreadExit.cpp)
target_compile_definitions(TracyClient PRIVATE TRACY_DELAYED_INIT)
get_target_property(tracyInterfaceDefinitions TracyClient INTERFACE_COMPILE_DEFINITIONS)
list(FILTER tracyInterfaceDefinitions EXCLUDE REGEX "^(WINVER|_WIN32_WINNT)=")
set_target_properties(TracyClient PROPERTIES INTERFACE_COMPILE_DEFINITIONS "${tracyInterfaceDefinitions}")
endif()

View File

@ -0,0 +1,3 @@
extern "C" int __cxa_thread_atexit(void (*)(void*), void*, void*) {
return 0;
}

View File

@ -25,7 +25,14 @@ target_include_directories(nid_patcher PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nid/i
target_compile_features(nid_patcher PRIVATE cxx_std_20)
if(MINGW)
target_link_options(nid_patcher PRIVATE -static -static-libgcc -static-libstdc++)
target_link_options(nid_patcher PRIVATE -municode -static -static-libgcc -static-libstdc++)
include(CheckCXXSourceCompiles)
set(CMAKE_REQUIRED_FLAGS -fno-asynchronous-unwind-tables)
check_cxx_source_compiles("#ifdef __USING_SJLJ_EXCEPTIONS__\n#error\n#endif\nint main() { return 0; }" ANYPS5_MINGW_DWARF_EXCEPTIONS)
unset(CMAKE_REQUIRED_FLAGS)
if(NOT ANYPS5_MINGW_DWARF_EXCEPTIONS)
message(FATAL_ERROR "${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} selects SjLj exceptions with -fno-asynchronous-unwind-tables; the libraries need DWARF unwinding. Use the MinGW-w64 toolchain listed in docs/dev/BUILD.md.")
endif()
add_compile_options(-fno-asynchronous-unwind-tables)
endif()
@ -55,6 +62,7 @@ target_include_directories(libc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
configure_windows_unwind(libc)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_options(libc PRIVATE LINKER:-z,now)
target_link_options(libc INTERFACE LINKER:--no-as-needed)
endif()
set(UNPATCHED_DIR ${CMAKE_CURRENT_BINARY_DIR}/libs/unpatched)
@ -101,9 +109,9 @@ foreach(LIB_DIR ${LIBS_DIRS})
endif()
if(LIB_NAME STREQUAL "libkernel")
target_link_libraries(${LIB_NAME} PRIVATE libc)
target_link_libraries(${LIB_NAME} PUBLIC libc)
else()
target_link_libraries(${LIB_NAME} PRIVATE libkernel libc)
target_link_libraries(${LIB_NAME} PRIVATE libkernel PUBLIC libc)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_options(${LIB_NAME} PRIVATE LINKER:-z,now)
@ -141,26 +149,79 @@ foreach(LIB_DIR ${LIBS_DIRS})
endif()
endforeach()
if(APS5_ENABLE_TRACY)
set_target_properties(TracyClient PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${UNPATCHED_DIR}
LIBRARY_OUTPUT_DIRECTORY ${UNPATCHED_DIR}
ARCHIVE_OUTPUT_DIRECTORY ${IMPLIB_DIR}
)
set(TRACY_CLIENT_COPIED ${CMAKE_CURRENT_BINARY_DIR}/TracyClient.copied)
add_custom_command(
OUTPUT ${TRACY_CLIENT_COPIED}
COMMAND ${CMAKE_COMMAND} -E make_directory ${PATCHED_DIR}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:TracyClient> ${PATCHED_DIR}
COMMAND ${CMAKE_COMMAND} -E touch ${TRACY_CLIENT_COPIED}
DEPENDS TracyClient
COMMENT "Copying the Tracy client next to the patched libraries"
)
list(APPEND PATCHED_OUTPUTS ${TRACY_CLIENT_COPIED})
endif()
add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS})
if(BUILD_TESTING)
add_test_executable(guest_posix_scalar_io_tests tests/GuestPosixScalarIo.cpp)
target_include_directories(guest_posix_scalar_io_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_posix_scalar_io_tests PRIVATE libkernel libc)
configure_windows_unwind(guest_posix_scalar_io_tests)
add_test(NAME guest_posix_scalar_io COMMAND guest_posix_scalar_io_tests)
if(WIN32)
set_tests_properties(guest_posix_scalar_io PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(nid_buffer_validation_tests nid/tests/BufferValidation.cpp)
target_include_directories(nid_buffer_validation_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/nid/include)
target_compile_features(nid_buffer_validation_tests PRIVATE cxx_std_20)
if(MINGW)
target_compile_options(nid_buffer_validation_tests PRIVATE -fasynchronous-unwind-tables)
endif()
add_test(NAME nid_buffer_validation COMMAND nid_buffer_validation_tests)
set_tests_properties(nid_buffer_validation PROPERTIES TIMEOUT 10)
find_package(Python3 QUIET COMPONENTS Interpreter)
if(Python3_Interpreter_FOUND)
add_test(NAME nid_gnu_hash COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_gnu_hash.py $<TARGET_FILE:nid_patcher>)
add_test(NAME nid_patcher_macho COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_macho_nids.py $<TARGET_FILE:nid_patcher>)
add_test(NAME nid_patcher_native_paths COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_native_paths.py $<TARGET_FILE:nid_patcher>)
set_tests_properties(nid_patcher_native_paths PROPERTIES TIMEOUT 20)
endif()
add_test_executable(patched_libraries_tests tests/PatchedLibraries.cpp)
add_dependencies(patched_libraries_tests libs)
if(Python3_Interpreter_FOUND)
add_test(NAME nid_pe_references COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_pe_references.py $<TARGET_FILE:nid_patcher>)
endif()
if(MINGW)
target_compile_options(patched_libraries_tests PRIVATE -fasynchronous-unwind-tables)
target_link_options(patched_libraries_tests PRIVATE -static -static-libgcc -static-libstdc++)
endif()
add_test(NAME patched_libraries COMMAND patched_libraries_tests ${PATCHED_DIR})
if(Python3_Interpreter_FOUND)
add_test(NAME nid_elf_layout COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_elf_layout.py $<TARGET_FILE:nid_patcher>)
endif()
add_test_executable(guest_kernel_sys_tests tests/GuestKernelSys.cpp)
target_include_directories(guest_kernel_sys_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_kernel_sys_tests PRIVATE libkernel_sys libkernel libc)
configure_windows_unwind(guest_kernel_sys_tests)
add_test(NAME guest_kernel_sys COMMAND guest_kernel_sys_tests)
if(Python3_Interpreter_FOUND)
add_test(NAME nid_verneed COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_verneed.py $<TARGET_FILE:nid_patcher>)
endif()
if(WIN32)
set_tests_properties(guest_kernel_sys PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel_sys>")
@ -168,17 +229,61 @@ if(BUILD_TESTING)
if(APPLE)
add_subdirectory(tests/write_watch)
endif()
if(WIN32)
add_test_executable(windows_write_watch_tests tests/WindowsWriteWatch.cpp)
target_include_directories(windows_write_watch_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(windows_write_watch_tests PRIVATE _WIN32_WINNT=0x0A00)
if(MINGW)
target_compile_options(windows_write_watch_tests PRIVATE -fasynchronous-unwind-tables)
endif()
add_test(NAME windows_write_watch COMMAND windows_write_watch_tests)
set_tests_properties(windows_write_watch PROPERTIES TIMEOUT 20)
endif()
add_test_executable(guest_offline_imports_tests tests/GuestOfflineImports.cpp)
target_include_directories(guest_offline_imports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libc)
configure_windows_unwind(guest_offline_imports_tests)
add_test(NAME guest_offline_imports COMMAND guest_offline_imports_tests)
if(Python3_Interpreter_FOUND)
add_test(NAME nid_shared_strings COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/nid/tests/test_shared_strings.py $<TARGET_FILE:nid_patcher>)
endif()
if(WIN32)
set_tests_properties(guest_offline_imports PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceHttp>")
endif()
add_test_executable(guest_signin_dialog_tests tests/GuestSigninDialog.cpp)
target_include_directories(guest_signin_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_signin_dialog_tests PRIVATE libSceSigninDialog libc)
configure_windows_unwind(guest_signin_dialog_tests)
add_test(NAME guest_signin_dialog COMMAND guest_signin_dialog_tests)
if(WIN32)
set_tests_properties(guest_signin_dialog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceSigninDialog>")
endif()
add_test_executable(guest_netctl_ap_dialog_tests tests/GuestNetCtlApDialog.cpp)
target_include_directories(guest_netctl_ap_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_netctl_ap_dialog_tests PRIVATE libSceNetCtlApDialog libc)
configure_windows_unwind(guest_netctl_ap_dialog_tests)
add_test(NAME guest_netctl_ap_dialog COMMAND guest_netctl_ap_dialog_tests)
if(WIN32)
set_tests_properties(guest_netctl_ap_dialog PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNetCtlApDialog>")
endif()
add_test_executable(guest_np_auth_tests tests/GuestNpAuth.cpp)
target_include_directories(guest_np_auth_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_np_auth_tests PRIVATE libSceNpAuth libc)
configure_windows_unwind(guest_np_auth_tests)
add_test(NAME guest_np_auth COMMAND guest_np_auth_tests)
if(WIN32)
set_tests_properties(guest_np_auth PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceNpAuth>")
endif()
add_test_executable(guest_rudp_status_tests tests/GuestRudpStatus.cpp)
target_include_directories(guest_rudp_status_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_rudp_status_tests PRIVATE libSceRudp libc)
@ -189,6 +294,26 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceRudp>")
endif()
add_test_executable(guest_rudp_context_queries_tests tests/GuestRudpContextQueries.cpp)
target_include_directories(guest_rudp_context_queries_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_rudp_context_queries_tests PRIVATE libSceRudp libc)
configure_windows_unwind(guest_rudp_context_queries_tests)
add_test(NAME guest_rudp_context_queries COMMAND guest_rudp_context_queries_tests)
if(WIN32)
set_tests_properties(guest_rudp_context_queries PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceRudp>")
endif()
add_test_executable(guest_rudp_pump_tests tests/GuestRudpPump.cpp)
target_include_directories(guest_rudp_pump_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_rudp_pump_tests PRIVATE libSceRudp libc)
configure_windows_unwind(guest_rudp_pump_tests)
add_test(NAME guest_rudp_pump COMMAND guest_rudp_pump_tests)
if(WIN32)
set_tests_properties(guest_rudp_pump PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceRudp>")
endif()
add_test_executable(guest_memory_pool_stats_tests tests/GuestMemoryPoolStats.cpp)
target_include_directories(guest_memory_pool_stats_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_memory_pool_stats_tests PRIVATE libSceSsl libSceHttp2 libSceNet libkernel libc)
@ -250,6 +375,11 @@ if(BUILD_TESTING)
endif()
add_test(NAME host_thread_local COMMAND host_thread_local_tests)
set_tests_properties(host_thread_local PROPERTIES TIMEOUT 15)
if(MINGW)
add_test(NAME prx_thread_local_destructors COMMAND ${CMAKE_COMMAND} -DNM=${CMAKE_NM} -DOBJDUMP=${CMAKE_OBJDUMP}
-DMODULES=$<TARGET_FILE_DIR:libc> -P ${CMAKE_CURRENT_SOURCE_DIR}/tests/ThreadLocalDestructors.cmake)
set_tests_properties(prx_thread_local_destructors PROPERTIES TIMEOUT 300)
endif()
add_test_executable(savedata_core_write_replacement_tests tests/SaveDataWriteReplacement.cpp)
target_include_directories(savedata_core_write_replacement_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
@ -269,6 +399,16 @@ if(BUILD_TESTING)
configure_windows_unwind(savedata_replace_file_failure_tests)
add_test(NAME savedata_replace_file_failure COMMAND savedata_replace_file_failure_tests)
add_test_executable(guest_crt_tests tests/GuestCrt.cpp)
target_include_directories(guest_crt_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_crt_tests PRIVATE libSceAmpr libc)
configure_windows_unwind(guest_crt_tests)
add_test(NAME guest_crt COMMAND guest_crt_tests)
if(WIN32)
set_tests_properties(guest_crt PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAmpr>")
endif()
if(MINGW)
target_compile_options(savedata_core_write_replacement_tests PRIVATE -fasynchronous-unwind-tables)
target_compile_options(savedata_native_write_replacement_tests PRIVATE -fasynchronous-unwind-tables)
@ -281,6 +421,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceSaveData.native>")
endif()
add_test_executable(guest_ppsa01530_exports_tests tests/GuestPPSA01530Exports.cpp)
target_include_directories(guest_ppsa01530_exports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_ppsa01530_exports_tests PRIVATE libkernel libSceJson2 libSceNet libScePad libc)
configure_windows_unwind(guest_ppsa01530_exports_tests)
add_test(NAME guest_ppsa01530_exports COMMAND guest_ppsa01530_exports_tests)
if(WIN32)
set_tests_properties(guest_ppsa01530_exports PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
add_test_executable(guest_posix_random_tests tests/GuestPosixRandom.cpp)
target_include_directories(guest_posix_random_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_posix_random_tests PRIVATE libScePosixForWebKit)
@ -290,4 +440,24 @@ if(BUILD_TESTING)
set_tests_properties(guest_posix_random PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libScePosixForWebKit>")
endif()
add_test_executable(MemoryPoolTest
tests/MemoryPoolTest.cpp
prx/libkernel/DirectMemory/MemoryPool.cpp
)
target_include_directories(MemoryPoolTest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
if(MINGW)
target_compile_options(MemoryPoolTest PRIVATE -fasynchronous-unwind-tables)
endif()
add_test(NAME MemoryPoolTest COMMAND MemoryPoolTest)
add_test_executable(guest_acm_tests tests/GuestAcm.cpp)
target_include_directories(guest_acm_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(guest_acm_tests PRIVATE libSceAcm libc)
configure_windows_unwind(guest_acm_tests)
add_test(NAME guest_acm COMMAND guest_acm_tests)
if(WIN32)
set_tests_properties(guest_acm PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceAcm>")
endif()
endif()

View File

@ -16,6 +16,23 @@ struct alignas(16) __m128 { float v[4]; };
using Bool = std::uint8_t;
struct SonyLconv {
char* monetary[7];
char chars[14];
char pad[2];
char* decimal_point;
char* grouping;
char* thousands_sep;
char* frac_grouping;
char* frac_sep;
char* false_text;
char* true_text;
char* no_text;
char* yes_text;
};
static_assert(offsetof(SonyLconv, decimal_point) == 0x48);
static_assert(sizeof(SonyLconv) == 0x90);
using KernelModule = std::int32_t;
using KernelCpumask = std::uint64_t;
using KernelUseconds = unsigned int;
@ -78,6 +95,14 @@ struct KernelAioRwRequest {
std::int32_t fd;
};
struct KernelAioSchedulingParam {
std::int32_t schedulingWindowSize;
std::int32_t delayedCountLimit;
std::uint32_t enableSplit;
std::uint32_t splitSize;
std::uint32_t splitChunkSize;
};
struct KernelBatchMapEntry {
void* start;
std::uint64_t offset;
@ -255,6 +280,12 @@ struct ModuleInfoForUnwind {
std::uint64_t seg0_size;
};
struct InternalSegmentInfo {
std::uint64_t address;
std::uint64_t size;
};
static_assert(sizeof(InternalSegmentInfo) == 0x10);
struct MallocReplace {
std::uint64_t size = sizeof(MallocReplace);
void* malloc_initialize = nullptr;
@ -1234,9 +1265,16 @@ struct NetCtlNatInfo { std::uint8_t opaque[128]; };
union NetCtlInfo { std::uint8_t opaque[256]; };
struct NetCtlIfStat {
std::uint32_t device;
std::uint64_t tx_bytes;
std::uint64_t rx_bytes;
std::uint32_t reserved[8];
};
using NetCtlCallback = void (*)(int, void*);
struct HttpEpoll {};
struct HttpEpoll;
using HttpEpollHandle = HttpEpoll*;
using HttpsCallback = int (*)(int, unsigned int, void* const*, int, void*);
using HttpRedirectCallback = int (*)(int, std::int32_t, std::int32_t*, const char*, void*);
@ -1423,7 +1461,12 @@ struct SaveDataMountInfo {
struct SceSaveDataTitleId { char data[10]; char pad[2]; };
struct SceSaveDataDirName { char data[32]; };
struct SaveDataSearchInfo { std::uint8_t opaque[128]; };
struct SaveDataSearchInfo {
std::uint64_t blocks;
std::uint64_t free_blocks;
std::uint8_t reserved[32];
};
static_assert(sizeof(SaveDataSearchInfo) == 48);
struct SaveDataMemoryData { void* buf; std::size_t buf_size; std::size_t offset; };
struct SaveDataMount3 {
@ -1546,6 +1589,13 @@ struct SaveDataBackup {
std::uint8_t reserved[32];
};
struct SaveDataConvert {
std::int32_t user_id;
std::int32_t pad;
const SceSaveDataDirName* dir_name;
std::uint8_t unknown[48];
};
struct AppContentInitParam { char reserved[32]; };
struct AppContentBootParam {
@ -1565,8 +1615,50 @@ struct ContentExportInitParam2 {
std::int64_t reserved1;
};
struct ContentExportParam {
char title[257];
char comment[257];
char content_type[65];
};
static_assert(sizeof(ContentExportParam) == 579);
struct ContentExportCallbackParam {
std::size_t content_length;
void* func;
void* user_data;
};
struct ContentSearchInitParam { std::size_t memory_size; };
struct ContentSearchColumnValue {
std::uint32_t size;
std::int64_t value;
};
struct ContentSearchContentColumn {
std::int32_t column_type;
std::int32_t column_operator;
const ContentSearchColumnValue* value;
std::int32_t logical_connector;
};
struct ContentSearchContentColumnSet {
const ContentSearchContentColumn* columns;
std::int32_t columns_length;
std::int32_t logical_connector;
};
struct ContentSearchContentOrderByCondition {
std::int32_t column_type;
std::int32_t sort_type;
};
static_assert(sizeof(ContentSearchColumnValue) == 16);
static_assert(sizeof(ContentSearchContentColumn) == 24);
static_assert(offsetof(ContentSearchContentColumn, logical_connector) == 16);
static_assert(sizeof(ContentSearchContentColumnSet) == 16);
static_assert(sizeof(ContentSearchContentOrderByCondition) == 8);
struct PngEncCreateParam {
std::uint32_t this_size;
std::uint32_t attribute;

View File

@ -0,0 +1,15 @@
#ifndef NID_NATIVEPATH_HPP
#define NID_NATIVEPATH_HPP
#include <filesystem>
#include <string>
namespace Nid::Internal {
inline std::filesystem::path NativePath(const std::string& path) {
return std::filesystem::path(std::u8string(path.begin(), path.end()));
}
}
#endif

View File

@ -61,7 +61,7 @@ inline std::string StripNidPostfix(const std::string& name) {
template<typename T>
T Read(const std::vector<std::uint8_t>& buf, std::size_t offset) {
if (offset + sizeof(T) > buf.size()) throw std::runtime_error("read out of bounds");
if (offset > buf.size() || sizeof(T) > buf.size() - offset) throw std::runtime_error("read out of bounds");
T v;
std::memcpy(&v, buf.data() + offset, sizeof(T));
return v;
@ -69,7 +69,7 @@ T Read(const std::vector<std::uint8_t>& buf, std::size_t offset) {
template<typename T>
void Write(std::vector<std::uint8_t>& buf, std::size_t offset, const T& v) {
if (offset + sizeof(T) > buf.size()) throw std::runtime_error("write out of bounds");
if (offset > buf.size() || sizeof(T) > buf.size() - offset) throw std::runtime_error("write out of bounds");
std::memcpy(buf.data() + offset, &v, sizeof(T));
}
@ -78,6 +78,7 @@ inline std::string ReadCStr(const std::vector<std::uint8_t>& buf, std::size_t of
std::string s;
while (offset < buf.size() && buf[offset] != 0u)
s.push_back(static_cast<char>(buf[offset++]));
if (offset == buf.size()) throw std::runtime_error("unterminated cstr");
return s;
}

View File

@ -15,6 +15,11 @@ namespace Nid {
namespace {
void RequireElfRange(const std::vector<std::uint8_t>& elf, std::uint64_t offset, std::uint64_t size, const char* table) {
if (offset > elf.size() || size > elf.size() - offset)
throw std::runtime_error(std::string("invalid ELF ") + table + " range");
}
std::uint32_t GnuHash(const std::string& name) {
std::uint32_t h = 5381u;
for (const unsigned char c : name)
@ -36,6 +41,18 @@ GnuHashLayout ReadGnuHashLayout(const std::vector<std::uint8_t>& elf, std::size_
return layout;
}
void ValidateGnuHash(const std::vector<std::uint8_t>& elf, std::size_t offset, std::size_t size, std::size_t symCount) {
if (offset > elf.size() || size > elf.size() - offset || size < 16u)
throw std::runtime_error("invalid gnu.hash section range");
const auto layout = ReadGnuHashLayout(elf, offset);
if (layout.SymOffset > symCount || !layout.BloomSize || (layout.BloomSize & (layout.BloomSize - 1u)) ||
layout.BloomShift >= 32u || (!layout.NBuckets && layout.SymOffset != symCount))
throw std::runtime_error("invalid gnu.hash geometry");
const std::uint64_t required = 16u + std::uint64_t{layout.BloomSize} * 8u +
std::uint64_t{layout.NBuckets} * 4u + (symCount - layout.SymOffset) * 4u;
if (required > size) throw std::runtime_error("gnu.hash section too small to rebuild in place");
}
void ReorderDynSymForGnuHash(
std::vector<std::uint8_t>& elf,
std::size_t dynSymOffset,
@ -204,49 +221,65 @@ void RemapDynamicNeededOffsets(
}
}
struct VersionNameField {
std::size_t Offset;
std::uint32_t Name;
};
std::vector<VersionNameField> ReadVersionNameFields(const std::vector<std::uint8_t>& elf, const Elf64_Ehdr& ehdr) {
using namespace Internal;
std::vector<VersionNameField> fields;
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
const auto section = Read<Elf64_Shdr>(elf, static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr));
if (section.sh_type != kShtGnuVerneed || section.sh_size == 0u) continue;
if (section.sh_offset > elf.size() || section.sh_size > elf.size() - section.sh_offset)
throw std::runtime_error("invalid .gnu.version_r section range");
const auto location = [&](std::uint64_t relative, std::size_t size) {
if (relative > section.sh_size || size > section.sh_size - relative)
throw std::runtime_error(".gnu.version_r record outside section");
return static_cast<std::size_t>(section.sh_offset + relative);
};
const auto advance = [&](std::uint64_t current, std::uint32_t step, std::size_t size) {
if (step < size || step > section.sh_size - current)
throw std::runtime_error("invalid .gnu.version_r record link");
return current + step;
};
std::uint64_t current = 0u;
while (true) {
const auto offset = location(current, sizeof(Elf64_Verneed));
const auto need = Read<Elf64_Verneed>(elf, offset);
if (need.vn_version != 1u) throw std::runtime_error("unsupported .gnu.version_r structure version");
fields.push_back({offset + offsetof(Elf64_Verneed, vn_file), need.vn_file});
if (need.vn_cnt != 0u) {
auto auxiliary = advance(current, need.vn_aux, sizeof(Elf64_Verneed));
for (std::uint16_t index = 0u; index < need.vn_cnt; ++index) {
const auto auxOffset = location(auxiliary, sizeof(Elf64_Vernaux));
const auto aux = Read<Elf64_Vernaux>(elf, auxOffset);
fields.push_back({auxOffset + offsetof(Elf64_Vernaux, vna_name), aux.vna_name});
if (index + 1u == need.vn_cnt) {
if (aux.vna_next != 0u) throw std::runtime_error(".gnu.version_r auxiliary count mismatch");
} else {
auxiliary = advance(auxiliary, aux.vna_next, sizeof(Elf64_Vernaux));
}
}
}
if (need.vn_next == 0u) break;
current = advance(current, need.vn_next, sizeof(Elf64_Verneed));
}
}
return fields;
}
void RemapVerneedOffsets(
std::vector<std::uint8_t>& elf,
const Elf64_Ehdr& ehdr,
const std::vector<std::pair<std::uint32_t, std::uint32_t>>& oldToNewOffset
) {
using namespace Internal;
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr);
const auto shdr = Read<Elf64_Shdr>(elf, shOffset);
if (shdr.sh_type != kShtGnuVerneed) continue;
const std::size_t verneedOffset = static_cast<std::size_t>(shdr.sh_offset);
std::size_t entryOffset = verneedOffset;
while (true) {
const auto verneed = Read<Elf64_Verneed>(elf, entryOffset);
const auto mappedFile = std::find_if(
oldToNewOffset.begin(), oldToNewOffset.end(),
[&](const auto& entry) { return entry.first == verneed.vn_file; }
);
if (mappedFile != oldToNewOffset.end())
Write(elf, entryOffset + offsetof(Elf64_Verneed, vn_file), mappedFile->second);
std::size_t auxOffset = entryOffset + verneed.vn_aux;
for (std::uint16_t a = 0u; a < verneed.vn_cnt; ++a) {
const auto vernaux = Read<Elf64_Vernaux>(elf, auxOffset);
const auto mappedName = std::find_if(
oldToNewOffset.begin(), oldToNewOffset.end(),
[&](const auto& entry) { return entry.first == vernaux.vna_name; }
);
if (mappedName != oldToNewOffset.end())
Write(elf, auxOffset + offsetof(Elf64_Vernaux, vna_name), mappedName->second);
if (vernaux.vna_next == 0u) break;
auxOffset += vernaux.vna_next;
}
if (verneed.vn_next == 0u) break;
entryOffset += verneed.vn_next;
}
for (const auto& field : ReadVersionNameFields(elf, ehdr)) {
const auto mapped = std::find_if(oldToNewOffset.begin(), oldToNewOffset.end(),
[&](const auto& entry) { return entry.first == field.Name; });
if (mapped != oldToNewOffset.end()) Write(elf, field.Offset, mapped->second);
}
}
@ -275,35 +308,8 @@ std::vector<std::uint32_t> CollectDynamicNameOffsets(const std::vector<std::uint
}
std::vector<std::uint32_t> CollectVerneedNameOffsets(const std::vector<std::uint8_t>& elf, const Elf64_Ehdr& ehdr) {
using namespace Internal;
std::vector<std::uint32_t> offsets;
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr);
const auto shdr = Read<Elf64_Shdr>(elf, shOffset);
if (shdr.sh_type != kShtGnuVerneed) continue;
std::size_t entryOffset = static_cast<std::size_t>(shdr.sh_offset);
while (true) {
const auto verneed = Read<Elf64_Verneed>(elf, entryOffset);
offsets.push_back(verneed.vn_file);
std::size_t auxOffset = entryOffset + verneed.vn_aux;
for (std::uint16_t a = 0u; a < verneed.vn_cnt; ++a) {
const auto vernaux = Read<Elf64_Vernaux>(elf, auxOffset);
offsets.push_back(vernaux.vna_name);
if (vernaux.vna_next == 0u) break;
auxOffset += vernaux.vna_next;
}
if (verneed.vn_next == 0u) break;
entryOffset += verneed.vn_next;
}
}
for (const auto& field : ReadVersionNameFields(elf, ehdr)) offsets.push_back(field.Name);
return offsets;
}
@ -316,6 +322,9 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
if (elf[4] != 2) throw std::runtime_error("not ELF64");
const auto ehdr = Read<Elf64_Ehdr>(elf, 0u);
if (ehdr.e_ident[5] != 1 || ehdr.e_shentsize != sizeof(Elf64_Shdr))
throw std::runtime_error("unsupported ELF byte order or section header size");
RequireElfRange(elf, ehdr.e_shoff, std::uint64_t{ehdr.e_shnum} * sizeof(Elf64_Shdr), "section table");
std::size_t dynSymOffset = 0u, dynSymSize = 0u;
std::size_t gnuHashOffset = 0u, gnuHashSize = 0u;
@ -326,6 +335,9 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + i * sizeof(Elf64_Shdr);
const auto shdr = Read<Elf64_Shdr>(elf, shOffset);
if (shdr.sh_type == kShtDynsym) {
if (shdr.sh_entsize != sizeof(Elf64_Sym) || shdr.sh_size % sizeof(Elf64_Sym) || shdr.sh_link >= ehdr.e_shnum)
throw std::runtime_error("invalid ELF dynsym layout");
RequireElfRange(elf, shdr.sh_offset, shdr.sh_size, "dynsym");
dynSymOffset = static_cast<std::size_t>(shdr.sh_offset);
dynSymSize = static_cast<std::size_t>(shdr.sh_size);
dynSymSectionIndex = i;
@ -333,7 +345,6 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
if (shdr.sh_type == kShtGnuHash) {
gnuHashOffset = static_cast<std::size_t>(shdr.sh_offset);
gnuHashSize = static_cast<std::size_t>(shdr.sh_size);
gnuHashSymOffset = Read<std::uint32_t>(elf, gnuHashOffset + 4u);
}
if (shdr.sh_type == kShtGnuVersym)
versymOffset = static_cast<std::size_t>(shdr.sh_offset);
@ -345,6 +356,10 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
const std::size_t symCount = dynSymSize / sizeof(Elf64_Sym);
if (symCount == 0u) throw std::runtime_error(".dynsym is empty");
if (gnuHashOffset != 0u) {
ValidateGnuHash(elf, gnuHashOffset, gnuHashSize, symCount);
gnuHashSymOffset = Read<std::uint32_t>(elf, gnuHashOffset + 4u);
}
const std::size_t dynStrSectionLink = [&]() -> std::size_t {
for (std::uint16_t i = 0u; i < ehdr.e_shnum; ++i) {
@ -359,6 +374,8 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
{
const std::size_t shOffset = static_cast<std::size_t>(ehdr.e_shoff) + dynStrSectionLink * sizeof(Elf64_Shdr);
const auto shdr = Read<Elf64_Shdr>(elf, shOffset);
if (shdr.sh_type != 3) throw std::runtime_error("invalid ELF dynstr section type");
RequireElfRange(elf, shdr.sh_offset, shdr.sh_size, "dynstr");
dynStrOffset = static_cast<std::size_t>(shdr.sh_offset);
dynStrSize = static_cast<std::size_t>(shdr.sh_size);
}
@ -427,7 +444,7 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
const auto addName = [&](std::uint32_t oldOffset, std::string value) {
const auto existing = std::find_if(
names.begin(), names.end(),
[&](const auto& entry) { return entry.first == oldOffset; }
[&](const auto& entry) { return entry.first == oldOffset && entry.second == value; }
);
if (existing == names.end()) names.emplace_back(oldOffset, std::move(value));
};
@ -463,8 +480,9 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
newDynStr.push_back(0u);
}
std::vector<std::pair<std::uint32_t, std::uint32_t>> oldToNewOffset;
for (const auto& name : names) oldToNewOffset.emplace_back(name.first, valueOffsets.at(name.second));
std::vector<std::pair<std::uint32_t, std::uint32_t>> preservedToNewOffset;
for (const auto offset : preservedOffsets)
preservedToNewOffset.emplace_back(offset, valueOffsets.at(ReadCStr(origDynStr, offset)));
if (newDynStr.size() > dynStrSize) {
throw std::runtime_error(
@ -475,25 +493,15 @@ void ElfNidPatcher::PatchNids(std::vector<std::uint8_t>& elf, const std::string&
newDynStr.resize(dynStrSize, 0u);
std::vector<std::string> newSymbolNames(symCount);
for (std::size_t i = 1u; i < symCount; ++i) {
const std::size_t symOffset = dynSymOffset + i * sizeof(Elf64_Sym);
const auto sym = Read<Elf64_Sym>(elf, symOffset);
if (sym.st_name == 0u) continue;
const auto mapped = std::find_if(
oldToNewOffset.begin(), oldToNewOffset.end(),
[&](const auto& entry) { return entry.first == sym.st_name; }
);
if (mapped == oldToNewOffset.end()) continue;
newSymbolNames[i] = ReadCStr(newDynStr, mapped->second);
Write(elf, symOffset + offsetof(Elf64_Sym, st_name), mapped->second);
for (const auto& use : uses) {
newSymbolNames[use.symIndex] = use.newValue;
Write(elf, dynSymOffset + use.symIndex * sizeof(Elf64_Sym) + offsetof(Elf64_Sym, st_name), valueOffsets.at(use.newValue));
}
std::memcpy(elf.data() + dynStrOffset, newDynStr.data(), newDynStr.size());
RemapDynamicNeededOffsets(elf, ehdr, oldToNewOffset);
RemapVerneedOffsets(elf, ehdr, oldToNewOffset);
RemapDynamicNeededOffsets(elf, ehdr, preservedToNewOffset);
RemapVerneedOffsets(elf, ehdr, preservedToNewOffset);
if (gnuHashOffset != 0u) {
const auto bucketCount = Read<std::uint32_t>(elf, gnuHashOffset);

View File

@ -3,6 +3,7 @@
#include <nid/PeNidPatcher.hpp>
#include <nid/MachONidPatcher.hpp>
#include <nid/NidPatcherUtils.hpp>
#include <nid/NativePath.hpp>
#include <bit>
#include <cstring>
#include <fstream>
@ -49,7 +50,12 @@ std::unordered_set<std::string> ReadPeExports(const std::vector<std::uint8_t>& b
if (!directory.VirtualAddress || directory.Size < sizeof(PeExportDirectory)) throw std::runtime_error("missing reference PE exports");
const auto sections = optional + optionalSize;
RequireRange(binary, sections, std::uint64_t{sectionCount} * sizeof(PeSectionHeader));
const auto headersSize = ReadValue<std::uint32_t>(binary, optional + 60);
const auto mapRva = [&](std::uint32_t rva, std::uint64_t size) -> std::uint64_t {
if (rva < headersSize && size <= headersSize - rva) {
RequireRange(binary, rva, size);
return rva;
}
for (std::uint16_t index = 0; index < sectionCount; ++index) {
const auto section = ReadValue<PeSectionHeader>(binary, sections + std::uint64_t{index} * sizeof(PeSectionHeader));
if (rva < section.VirtualAddress) continue;
@ -64,14 +70,20 @@ std::unordered_set<std::string> ReadPeExports(const std::vector<std::uint8_t>& b
const auto table = ReadValue<PeExportDirectory>(binary, mapRva(directory.VirtualAddress, sizeof(PeExportDirectory)));
if (!table.NumberOfNames) throw std::runtime_error("reference PE has no named exports");
const auto names = mapRva(table.AddressOfNames, std::uint64_t{table.NumberOfNames} * 4);
const auto ordinals = mapRva(table.AddressOfNameOrdinals, std::uint64_t{table.NumberOfNames} * 2);
const auto functions = mapRva(table.AddressOfFunctions, std::uint64_t{table.NumberOfFunctions} * 4);
std::unordered_set<std::string> result;
for (std::uint32_t index = 0; index < table.NumberOfNames; ++index) {
const auto ordinal = ReadValue<std::uint16_t>(binary, ordinals + std::uint64_t{index} * 2);
if (ordinal >= table.NumberOfFunctions) throw std::runtime_error("invalid reference PE export ordinal");
if (!ReadValue<std::uint32_t>(binary, functions + std::uint64_t{ordinal} * 4)) continue;
const auto rva = ReadValue<std::uint32_t>(binary, names + std::uint64_t{index} * 4);
const auto offset = mapRva(rva, 1);
auto name = ReadName(binary, offset, binary.size() - offset);
mapRva(rva, name.size() + 1);
if (!result.insert(name).second) throw std::runtime_error("duplicate reference export: " + name);
}
if (result.empty()) throw std::runtime_error("reference PE has no named exports");
return result;
}
@ -110,7 +122,7 @@ std::string NormalizeExportName(const std::string& name) {
std::unordered_set<std::string> ReadExportExclusions(const std::string& path) {
if constexpr (std::endian::native != std::endian::little) throw std::runtime_error("export references require a little-endian host");
std::ifstream input(path, std::ios::binary | std::ios::ate);
std::ifstream input(Internal::NativePath(path), std::ios::binary | std::ios::ate);
if (!input) throw std::runtime_error("cannot open export reference: " + path);
const auto size = input.tellg();
if (size <= 0) throw std::runtime_error("empty or unreadable export reference: " + path);

View File

@ -1,5 +1,6 @@
#include <nid/BinaryPatcherFactory.hpp>
#include <nid/ExportExclusions.hpp>
#include <nid/NativePath.hpp>
#include <fstream>
#include <iostream>
#include <stdexcept>
@ -9,20 +10,20 @@
namespace {
std::vector<std::uint8_t> _readFile(const std::string& path) {
std::ifstream f(path, std::ios::binary);
std::ifstream f(Nid::Internal::NativePath(path), std::ios::binary);
if (!f) throw std::runtime_error("cannot open: " + path);
return {std::istreambuf_iterator<char>(f), {}};
}
void _writeFile(const std::string& path, const std::vector<std::uint8_t>& data) {
std::ofstream f(path, std::ios::binary);
std::ofstream f(Nid::Internal::NativePath(path), std::ios::binary);
if (!f) throw std::runtime_error("cannot write: " + path);
f.write(reinterpret_cast<const char*>(data.data()), static_cast<std::streamsize>(data.size()));
}
}
int main(int argc, char* argv[]) {
int Run(int argc, char* argv[]) {
if (argc < 3) {
std::cerr << "Usage: nid_patcher <library_name> [--preserve-exports <unpatched-library>] <file> [file2 ...]\n";
return 1;
@ -65,3 +66,27 @@ int main(int argc, char* argv[]) {
return 0;
}
#ifdef _WIN32
int wmain(int argc, wchar_t* argv[]) {
try {
std::vector<std::string> values;
values.reserve(static_cast<std::size_t>(argc));
for (int index = 0; index < argc; ++index) {
const auto utf8 = std::filesystem::path(argv[index]).u8string();
values.emplace_back(utf8.begin(), utf8.end());
}
std::vector<char*> arguments;
arguments.reserve(values.size());
for (auto& value : values) arguments.push_back(value.data());
return Run(argc, arguments.data());
} catch (const std::exception& error) {
std::cerr << "FAIL: " << error.what() << '\n';
return 2;
}
}
#else
int main(int argc, char* argv[]) {
return Run(argc, argv);
}
#endif

View File

@ -0,0 +1,60 @@
#include <nid/NidPatcherUtils.hpp>
#include <cstdint>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <vector>
namespace {
void Require(bool condition) {
if (!condition) throw std::runtime_error("NID buffer validation failed");
}
template<typename TAction>
void RequireRejected(TAction action) {
try { action(); }
catch (const std::runtime_error&) { return; }
throw std::runtime_error("Malformed NID buffer was accepted");
}
}
int main() {
try {
using namespace Nid::Internal;
const std::vector<std::uint8_t> unterminated{'n', 'a', 'm', 'e'};
RequireRejected([&] { ReadCStr(unterminated, 0); });
RequireRejected([&] { ReadCStr(unterminated, 3); });
const std::vector<std::uint8_t> strings{'a', 0, 'b', 'c', 0};
Require(ReadCStr(strings, 0) == "a");
Require(ReadCStr(strings, 1).empty());
Require(ReadCStr(strings, 2) == "bc");
Require(ReadCStr(strings, 4).empty());
RequireRejected([&] { ReadCStr(strings, strings.size()); });
RequireRejected([&] { ReadCStr({}, 0); });
std::vector<std::uint8_t> bytes(8, 0xa5);
const std::uint64_t value = 0x1020304050607080;
Write(bytes, 0, value);
Require(Read<std::uint64_t>(bytes, 0) == value);
Write<std::uint32_t>(bytes, 4, 0x12345678);
Require(Read<std::uint32_t>(bytes, 4) == 0x12345678);
const auto before = bytes;
for (const auto offset : {std::size_t{5}, std::size_t{8}, std::size_t{9},
std::numeric_limits<std::size_t>::max(), std::numeric_limits<std::size_t>::max() - 1,
std::numeric_limits<std::size_t>::max() - 3}) {
RequireRejected([&] { Read<std::uint32_t>(bytes, offset); });
RequireRejected([&] { Write<std::uint32_t>(bytes, offset, 0); });
Require(bytes == before);
}
RequireRejected([&] { Read<std::uint64_t>(bytes, 1); });
std::vector<std::uint8_t> empty;
RequireRejected([&] { Read<std::uint8_t>(empty, 0); });
RequireRejected([&] { Write<std::uint8_t>(empty, 0, 0); });
std::cout << "NID buffer validation passed\n";
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';
return 1;
}
}

View File

@ -0,0 +1,48 @@
import struct
import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, SHOFF
class ElfLayoutTests(unittest.TestCase):
def test_declared_layout_must_match_supported_tables(self):
changes = ((5, "B", 2), (0x3a, "H", 32),
(SHOFF + 64 + 56, "Q", 8),
(SHOFF + 64 + 32, "Q", len(symbols(False)) * 24 + 1),
(SHOFF + 128 + 4, "I", 1))
for offset, kind, value in changes:
with self.subTest(offset=offset, value=value):
image = build_image(symbols(False), False)
struct.pack_into("<" + kind, image, offset, value)
self.reject(image)
def test_table_ranges_and_links_are_checked(self):
changes = ((0x28, "Q", 0x10000),
(SHOFF + 64 + 24, "Q", 0xff0),
(SHOFF + 64 + 40, "I", 100),
(SHOFF + 128 + 24, "Q", 0xff0),
(SHOFF + 128 + 32, "Q", 0xffffffffffffffff))
for offset, kind, value in changes:
with self.subTest(offset=offset, value=value):
image = build_image(symbols(False), False)
struct.pack_into("<" + kind, image, offset, value)
self.reject(image)
def reject(self, image):
result, after = patch(PATCHER, bytes(image), "layout.so")
self.assertEqual(result.returncode, 2, (result.returncode, result.stderr))
self.assertRegex(result.stderr.decode(), "ELF|dynsym|dynstr")
self.assertEqual(after, image)
def test_valid_supported_layout_still_patches(self):
image = build_image(symbols(False), False)
result, after = patch(PATCHER, bytes(image), "valid-layout.so")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertNotEqual(after, image)
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()

View File

@ -0,0 +1,75 @@
import struct
import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, HASH_OFFSET, SHOFF, read_dynsym, gnu_hash
class GnuHashTests(unittest.TestCase):
def image(self, buckets=4, bloom=1, shift=6, symoffset=3):
image = build_image(symbols(False), False)
count = len(symbols(False))
struct.pack_into("<IIII", image, HASH_OFFSET, buckets, symoffset, bloom, shift)
size = 16 + 8 * bloom + 4 * buckets + 4 * max(0, count - symoffset)
struct.pack_into("<Q", image, SHOFF + 3 * 64 + 32, size)
return image
def test_non_power_of_two_bloom_is_rejected(self):
self.reject(self.image(bloom=3))
def test_hash_shift_outside_word_is_rejected(self):
self.reject(self.image(shift=32))
def test_unsafe_geometry_is_rejected(self):
for fields in ({"bloom": 0}, {"buckets": 0}, {"symoffset": 100}):
with self.subTest(fields=fields):
self.reject(self.image(**fields))
def test_declared_section_extent_is_checked(self):
for size in (12, 16, 0x10000):
with self.subTest(size=size):
image = self.image()
struct.pack_into("<Q", image, SHOFF + 3 * 64 + 32, size)
self.reject(image)
def reject(self, image):
result, after = patch(PATCHER, bytes(image), "geometry.so")
self.assertEqual(result.returncode, 2, (result.returncode, result.stderr))
self.assertIn(b"gnu.hash", result.stderr)
self.assertEqual(after, image)
def test_rebuilt_hash_finds_every_export(self):
image = self.image(bloom=2)
result, after = patch(PATCHER, image, "valid.so")
self.assertEqual(result.returncode, 0, result.stderr)
entries = read_dynsym(after)
buckets, first, bloom, shift = struct.unpack_from("<4I", after, HASH_OFFSET)
bucket_offset = HASH_OFFSET + 16 + bloom * 8
chain_offset = bucket_offset + buckets * 4
for index in range(first, len(entries)):
name = entries[index][0]
hashed = gnu_hash(name)
word = struct.unpack_from("<Q", after, HASH_OFFSET + 16 + ((hashed // 64) % bloom) * 8)[0]
self.assertTrue(word & (1 << (hashed % 64)))
self.assertTrue(word & (1 << ((hashed >> shift) % 64)))
start = struct.unpack_from("<I", after, bucket_offset + (hashed % buckets) * 4)[0]
found = False
for candidate in range(start, len(entries)):
value = struct.unpack_from("<I", after, chain_offset + (candidate - first) * 4)[0]
if (value | 1) == (hashed | 1) and entries[candidate][0] == name:
found = True
if value & 1:
break
self.assertTrue(found, name)
def test_empty_hash_with_no_buckets_is_supported(self):
image = self.image(buckets=0, symoffset=len(symbols(False)))
result, after = patch(PATCHER, image, "empty.so")
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(struct.unpack_from("<I", after, HASH_OFFSET)[0], 0)
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()

View File

@ -0,0 +1,56 @@
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from test_macho_nids import build_dylib, exports_of, nid
PATCHER = Path(sys.argv.pop(1)).resolve()
class NativePathTests(unittest.TestCase):
def invoke(self, *arguments):
return subprocess.run([str(PATCHER), "libtest", *map(str, arguments)],
capture_output=True, encoding="utf-8", errors="replace", timeout=10)
def test_non_ascii_directory_and_filename_patch_the_intended_file(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary) / "\u6e2c\u8a66"
root.mkdir()
file = root / "\u043c\u043e\u0434\u0443\u043b\u044c.prx"
file.write_bytes(build_dylib([("_puts_nid_postfix", 0, 0x100)], []))
result = self.invoke(file)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(exports_of(file.read_bytes()), {"_" + nid("puts"): (0, 0x100)})
self.assertIn(str(file), result.stdout)
self.assertEqual(list(root.iterdir()), [file])
def test_non_ascii_export_reference_preserves_the_named_export(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
reference = root / "\u6e2c\u8a66-reference.prx"
original = build_dylib([("_puts", 0, 0x100)], [])
reference.write_bytes(original)
file = root / "target.prx"
file.write_bytes(build_dylib([("_puts_nid_postfix", 0, 0x100), ("_exit_nid_postfix", 0, 0x108)], []))
result = self.invoke("--preserve-exports", reference, file)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(exports_of(file.read_bytes()), {"_puts_nid_postfix": (0, 0x100), "_" + nid("exit"): (0, 0x108)})
self.assertEqual(reference.read_bytes(), original)
def test_missing_unicode_input_does_not_modify_another_file(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
existing = root / "existing.prx"
existing.write_bytes(b"unchanged")
missing = root / "\u6e2c\u8a66-missing.prx"
result = self.invoke(missing)
self.assertEqual(result.returncode, 2)
self.assertIn(str(missing), result.stderr)
self.assertFalse(missing.exists())
self.assertEqual(existing.read_bytes(), b"unchanged")
if __name__ == "__main__":
unittest.main()

View File

@ -0,0 +1,69 @@
import struct
import subprocess
import sys
import tempfile
import unittest
from pathlib import Path
from test_macho_nids import build_dylib, exports_of, nid
def reference(*, headers=False, address=0x2000, ordinal=0):
image = bytearray(1280)
image[:2] = b"MZ"
struct.pack_into("<I", image, 0x3c, 0x40)
struct.pack_into("<IHHIIIHH", image, 0x40, 0x4550, 0x8664, 2, 0, 0, 0, 240, 0x2022)
optional = 0x58
struct.pack_into("<H", image, optional, 0x20b)
struct.pack_into("<I", image, optional + 60, 512)
struct.pack_into("<I", image, optional + 108, 16)
start, rva = (416, 416) if headers else (512, 0x1000)
struct.pack_into("<II", image, optional + 112, rva, 80)
for index, (name, va, offset, size) in enumerate(((b".edata", 0x1000, 512, 256), (b".text", 0x2000, 768, 512))):
struct.pack_into("<8sIIIIIIHHI", image, optional + 240 + index * 40,
name, size, va, size, offset, 0, 0, 0, 0, 0x40000040)
struct.pack_into("<IIHHIIIIIII", image, start, 0, 0, 0, 0, rva + 50, 1, 1, 1, rva + 40, rva + 44, rva + 48)
struct.pack_into("<IIH", image, start + 40, address, rva + 50, ordinal)
name = b"puts_nid_postfix\0"
image[start + 50:start + 50 + len(name)] = name
return image
class ReferenceTests(unittest.TestCase):
def patch_with_reference(self, data):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
source, ref = root / "target.dylib", root / "reference.bin"
original = build_dylib([("_puts_nid_postfix", 0, 0x100), ("_exit_nid_postfix", 0, 0x108)], [])
source.write_bytes(original)
ref.write_bytes(data)
result = subprocess.run([str(PATCHER), "libc", str(source), "--preserve-exports", str(ref)], capture_output=True, text=True)
self.assertEqual(ref.read_bytes(), data)
return result, source.read_bytes(), original
def test_header_resident_export_tables(self):
result, image, _ = self.patch_with_reference(reference(headers=True))
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(exports_of(image), {"_puts_nid_postfix": (0, 0x100), "_" + nid("exit"): (0, 0x108)})
def test_section_resident_export_tables(self):
result, image, _ = self.patch_with_reference(reference())
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("_puts_nid_postfix", exports_of(image))
def test_null_function_slot_does_not_preserve_a_name(self):
result, image, original = self.patch_with_reference(reference(address=0))
self.assertNotEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("no named exports", result.stdout + result.stderr)
self.assertEqual(image, original)
def test_invalid_name_ordinal_is_rejected_without_changing_target(self):
result, image, original = self.patch_with_reference(reference(ordinal=1))
self.assertNotEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("ordinal", result.stdout + result.stderr)
self.assertEqual(image, original)
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()

View File

@ -0,0 +1,58 @@
import struct
import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSYM_OFFSET, read_dynsym, sections, DEFINITIONS
class SharedStringTests(unittest.TestCase):
def test_dynamic_names_keep_original_string_when_export_shares_offset(self):
for tag in (1, 14, 15, 29):
with self.subTest(tag=tag):
data = build_image(symbols(False), False)
name_offset = struct.unpack_from("<I", data, DYNSYM_OFFSET + 3 * 24)[0]
struct.pack_into("<H", data, 0x3c, 5)
struct.pack_into("<IIQQQQIIQQ", data, SHOFF + 4 * 64, 0, 6, 2, 0, 0x580, 32, 2, 0, 8, 16)
struct.pack_into("<qQqQ", data, 0x580, tag, name_offset, 0, 0)
result, after = patch(PATCHER, bytes(data), "shared-dynamic.so")
self.assertEqual(result.returncode, 0, result.stderr)
strings = sections(after)[2]
new_offset = struct.unpack_from("<Q", after, 0x588)[0]
start = strings[4] + new_offset
self.assertEqual(after[start:after.index(0, start)].decode(), DEFINITIONS[0][0])
renamed = next(entry[0] for entry in read_dynsym(after) if entry[2] == DEFINITIONS[0][2])
self.assertNotEqual(renamed, DEFINITIONS[0][0])
def test_version_requirement_strings_keep_original_names(self):
data = build_image(symbols(False), False)
name_offset = struct.unpack_from("<I", data, DYNSYM_OFFSET + 3 * 24)[0]
struct.pack_into("<H", data, 0x3c, 5)
struct.pack_into("<IIQQQQIIQQ", data, SHOFF + 4 * 64, 0, 0x6ffffffe, 2, 0, 0x580, 32, 2, 1, 8, 0)
struct.pack_into("<HHIII", data, 0x580, 1, 1, name_offset, 16, 0)
struct.pack_into("<IHHII", data, 0x590, 0, 0, 2, name_offset, 0)
result, after = patch(PATCHER, bytes(data), "shared-version.so")
self.assertEqual(result.returncode, 0, result.stderr)
strings = sections(after)[2]
for field in (0x584, 0x598):
offset = struct.unpack_from("<I", after, field)[0]
start = strings[4] + offset
self.assertEqual(after[start:after.index(0, start)].decode(), DEFINITIONS[0][0])
renamed = next(entry[0] for entry in read_dynsym(after) if entry[2] == DEFINITIONS[0][2])
self.assertNotEqual(renamed, DEFINITIONS[0][0])
def test_local_symbol_keeps_name_shared_with_renamed_global(self):
entries = symbols(False)
entries[1] = (DEFINITIONS[0][0], 1, 9, 2, 0x1500)
data = build_image(entries, False)
data[DYNSYM_OFFSET + 24 + 4] = 2
result, after = patch(PATCHER, bytes(data), "shared-local.so")
self.assertEqual(result.returncode, 0, result.stderr)
names = {value: name for name, _, value in read_dynsym(after)}
self.assertEqual(names[0x1500], DEFINITIONS[0][0])
self.assertNotEqual(names[DEFINITIONS[0][2]], DEFINITIONS[0][0])
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()

View File

@ -0,0 +1,76 @@
import struct
import sys
import unittest
from pathlib import Path
from test_symbol_versions import build_image, symbols, patch, SHOFF, DYNSTR_OFFSET, sections
VERNEED = 0x580
def image(*, auxiliary=16, count=1, size=32, version=1, auxnext=0, nextrecord=0):
data = build_image(symbols(False), False)
strings = sections(data)[2]
filename = strings[5]
names = b"libfixture.so\0FIXTURE_1\0"
data[DYNSTR_OFFSET + filename:DYNSTR_OFFSET + filename + len(names)] = names
struct.pack_into("<Q", data, SHOFF + 2 * 64 + 32, filename + len(names))
struct.pack_into("<H", data, 0x3c, 5)
struct.pack_into("<IIQQQQIIQQ", data, SHOFF + 4 * 64, 0, 0x6ffffffe, 2, 0, VERNEED, size, 2, 1, 8, 0)
struct.pack_into("<HHIII", data, VERNEED, version, count, filename, auxiliary, nextrecord)
struct.pack_into("<IHHII", data, VERNEED + auxiliary, 0, 0, 2, filename + len(b"libfixture.so\0"), auxnext)
return data
class VersionRequirementsTests(unittest.TestCase):
def test_auxiliary_record_cannot_leave_declared_section(self):
self.reject(image(auxiliary=32))
def test_auxiliary_count_must_match_chain(self):
self.reject(image(count=2))
def test_unsupported_structure_version_is_rejected(self):
self.reject(image(version=2))
def test_invalid_forward_links_and_section_ranges_are_rejected(self):
for fields in ({"auxnext": 16}, {"nextrecord": 64}, {"size": 15},
{"size": 0x10000}, {"auxiliary": 8}, {"count": 2, "auxnext": 8}):
with self.subTest(fields=fields):
self.reject(image(**fields))
def test_valid_multiple_auxiliaries_and_requirements(self):
data = image(count=2, size=80, auxnext=16, nextrecord=48)
original_name = struct.unpack_from("<I", data, VERNEED + 24)[0]
original_file = struct.unpack_from("<I", data, VERNEED + 4)[0]
struct.pack_into("<IHHII", data, VERNEED + 32, 0, 0, 3, original_name, 0)
struct.pack_into("<HHIII", data, VERNEED + 48, 1, 1, original_file, 16, 0)
struct.pack_into("<IHHII", data, VERNEED + 64, 0, 0, 4, original_name, 0)
result, after = patch(PATCHER, bytes(data), "multiple-versions.so")
self.assertEqual(result.returncode, 0, result.stderr)
strings = sections(after)[2]
for position, expected in ((4, b"libfixture.so"), (24, b"FIXTURE_1"),
(40, b"FIXTURE_1"), (52, b"libfixture.so"), (72, b"FIXTURE_1")):
offset = struct.unpack_from("<I", after, VERNEED + position)[0]
start = strings[4] + offset
self.assertEqual(after[start:after.index(0, start)], expected)
def reject(self, data):
result, after = patch(PATCHER, bytes(data), "versions.so")
self.assertEqual(result.returncode, 2, (result.returncode, result.stderr))
self.assertIn(b"version_r", result.stderr)
self.assertEqual(after, data)
def test_valid_names_are_preserved_when_strings_are_rebuilt(self):
data = image()
result, after = patch(PATCHER, bytes(data), "valid-versions.so")
self.assertEqual(result.returncode, 0, result.stderr)
strings = sections(after)[2]
for position, expected in ((VERNEED + 4, b"libfixture.so"), (VERNEED + 16 + 8, b"FIXTURE_1")):
offset = struct.unpack_from("<I", after, position)[0]
start = strings[4] + offset
self.assertEqual(after[start:after.index(0, start)], expected)
if __name__ == "__main__":
PATCHER = Path(sys.argv.pop(1)).resolve()
unittest.main()

View File

@ -1,54 +0,0 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI ErrorDialogClose(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ErrorDialogOpen(const void* param) {
(void)param;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ShareGetCurrentStatus(uint32_t feature_flag, ShareCurrentStatus* status) {
(void)feature_flag;
(void)status;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ShareInitialize(size_t heap_size, int thread_priority, uint64_t affinity_mask) {
(void)heap_size;
(void)thread_priority;
(void)affinity_mask;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ShareTerminate(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SystemServiceParamGetInt(int param_id, int* value) {
(void)param_id;
(void)value;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI SystemServiceParamGetString(int param_id, char* buf, size_t buf_size) {
(void)param_id;
(void)buf;
(void)buf_size;
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -1,35 +0,0 @@
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI RemotePlayGetConnectionStatus(int user_id, int* status) {
(void)user_id;
(void)status;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ShareGetCurrentStatus(uint32_t feature_flag, ShareCurrentStatus* status) {
(void)feature_flag;
(void)status;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ShareInitialize(size_t heap_size, int thread_priority, uint64_t affinity_mask) {
(void)heap_size;
(void)thread_priority;
(void)affinity_mask;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI ShareTerminate(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -53,12 +53,15 @@ int APS5_VABI sceAcm_ConvReverb_SharedInput(void) {
return 0;
}
int APS5_VABI sceAcm_FFT() {
NotImplemented_nid_no_patch(__func__);
int APS5_VABI sceAcm_FFT(void) {
return 0;
}
int APS5_VABI sceAcm_Panner() {
int APS5_VABI sceAcm_Panner(void) {
return 0;
}
int APS5_VABI sceAcmBatchJobPriority(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}

View File

@ -4,17 +4,34 @@
#include "prx/libSceAgc/Command/include/Memory.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include "prx/libSceAgc/DcbState/include/Marker.hpp"
#include "prx/libSceAgcDriver/Execution/include/VideoOutput.hpp"
#include "prx/libSceAgcDriver/Resource/include/Registration.hpp"
#include <cstdint>
#include <cstddef>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
std::uint32_t* RejectUnregisteredWorkloadStream(CommandBuffer* buf, std::uint32_t streamId, const char* function) {
if (buf == nullptr || !AgcDriverWorkloadStreamRegistered_nid_postfix(streamId)) return nullptr;
NotImplemented_nid_no_patch(function);
return nullptr;
}
}
extern "C" {
// unknown signature
void* APS5_VABI sceAgcAcbAtomicGds_0900(void) {
NotImplemented_nid_no_patch(__func__);
return nullptr;
std::uint32_t* APS5_VABI sceAgcAcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData) {
const auto operation = (control & 0xffu) | ((control & 0x200u) << 7u) |
((control & 0x20u) << 13u) | ((control & 0x40u) << 14u) |
((control & 0x20u) == 0 && mode == 1 ? 0x20000u : 0u);
return Agc::Command::Emit(buf, 0x1du, {
operation, ((gdsOp & 1u) << 8u) | (src & 0x3fu), offset, index,
mask & 0x00ff00ffu, data, static_cast<std::uint32_t>(compareData),
static_cast<std::uint32_t>(compareData >> 32u), static_cast<std::uint32_t>(extraData),
static_cast<std::uint32_t>(extraData >> 32u)}, __func__);
}
std::uint32_t* APS5_VABI sceAgcAcbJump(CommandBuffer* buf, std::uint8_t cachePolicy, const std::uint32_t* target, std::uint32_t sizeInDwords) {
@ -46,11 +63,12 @@ std::uint32_t APS5_VABI sceAgcAcbRewindGetSize() {
}
std::uint32_t* APS5_VABI sceAgcAcbWaitUntilSafeForRendering(CommandBuffer* buf, std::uint32_t videoOutHandle, std::uint32_t displayBufferIndex) {
(void)buf;
(void)videoOutHandle;
(void)displayBufferIndex;
NotImplemented_nid_no_patch(__func__);
return nullptr;
auto* packet = Agc::Command::Allocate(buf, AgcDriver::RenderingWaitPacketWords, __func__);
packet[0] = AgcDriver::RenderingWaitPacketHeader;
packet[1] = videoOutHandle;
packet[2] = displayBufferIndex;
packet[3] = 0;
return packet;
}
std::uint32_t* APS5_VABI sceAgcAcbSetFlip(CommandBuffer* buf, std::uint32_t videoOutHandle, std::int32_t displayBufferIndex, std::uint32_t flipMode, std::int64_t flipArg) {
@ -63,6 +81,21 @@ std::uint32_t* APS5_VABI sceAgcAcbSetFlip(CommandBuffer* buf, std::uint32_t vide
return nullptr;
}
std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadsActive(CommandBuffer* buf, std::uint32_t streamId, const std::uint32_t* workloadIds, std::uint32_t workloadCount) {
(void)workloadIds;
(void)workloadCount;
return RejectUnregisteredWorkloadStream(buf, streamId, __func__);
}
std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadComplete(CommandBuffer* buf, std::uint32_t streamId, std::uint32_t workloadId) {
(void)workloadId;
return RejectUnregisteredWorkloadStream(buf, streamId, __func__);
}
std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadStreamInactive(CommandBuffer* buf, std::uint32_t streamId) {
return RejectUnregisteredWorkloadStream(buf, streamId, __func__);
}
uint32_t* APS5_VABI sceAgcAcbPushMarker(CommandBuffer* buf, const char* str, uint32_t color) {
(void)color;
return Agc::Marker::Push(buf, str, __func__);

View File

@ -29,9 +29,11 @@ add_library(libSceAgc SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/DcbState/src/Predication.cpp
${CMAKE_CURRENT_SOURCE_DIR}/DcbState/src/Registers.cpp
${CMAKE_CURRENT_SOURCE_DIR}/DcbState/src/Workload.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/Debug.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/PacketInfo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/Platform.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/RegisterDefaults.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/Semaphore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/ShaderFusion.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Misc/src/Suspend.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Patch/src/CondExec.cpp
@ -46,6 +48,7 @@ add_library(libSceAgc SHARED EXCLUDE_FROM_ALL
${CMAKE_CURRENT_SOURCE_DIR}/Predication/src/Predication.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/CreateShader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/GsOversubscription.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/GsPrimPayload.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/InterpolantMapping.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/LinkShaders.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Shader/src/PrimState.cpp
@ -67,7 +70,7 @@ endforeach()
add_test_executable(agc_command_tests EXCLUDE_FROM_ALL tests/Commands.cpp)
target_include_directories(agc_command_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_command_tests PRIVATE libSceAgc libc)
target_link_libraries(agc_command_tests PRIVATE libSceAgc libSceAgcDriver libc)
configure_windows_unwind(agc_command_tests)
if(BUILD_TESTING)
set_target_properties(agc_command_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
@ -93,6 +96,19 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_semaphore_tests EXCLUDE_FROM_ALL tests/Semaphore.cpp)
target_include_directories(agc_semaphore_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_semaphore_tests PRIVATE libSceAgc libc)
configure_windows_unwind(agc_semaphore_tests)
if(BUILD_TESTING)
set_target_properties(agc_semaphore_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
add_test(NAME agc_semaphore_tests COMMAND agc_semaphore_tests)
if(WIN32)
set_tests_properties(agc_semaphore_tests PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_packet_info_tests EXCLUDE_FROM_ALL tests/PacketInfo.cpp)
target_include_directories(agc_packet_info_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_packet_info_tests PRIVATE libSceAgc libc)
@ -290,6 +306,22 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_gs_prim_payload_tests EXCLUDE_FROM_ALL
tests/GsPrimPayload.cpp
Shader/src/GsPrimPayload.cpp
)
target_include_directories(agc_gs_prim_payload_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_gs_prim_payload_tests PRIVATE libc)
configure_windows_unwind(agc_gs_prim_payload_tests)
if(BUILD_TESTING)
set_target_properties(agc_gs_prim_payload_tests PROPERTIES EXCLUDE_FROM_ALL FALSE)
add_test(NAME agc_gs_prim_payload_tests COMMAND agc_gs_prim_payload_tests)
if(WIN32)
set_tests_properties(agc_gs_prim_payload_tests PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_dma_data_tests EXCLUDE_FROM_ALL
tests/DmaData.cpp
Command/src/Packet.cpp

View File

@ -13,8 +13,7 @@
extern "C" {
// unknown signature
APS5_EXPORT("zARR5aCmkoY", sceAgcDcbA_zARR5aCmkoY);
void* APS5_VABI sceAgcDcbA_zARR5aCmkoY(void) {
void* APS5_VABI sceAgcDcbAtomicGds_0900(void) {
NotImplemented_nid_no_patch(__func__);
return nullptr;
}
@ -35,8 +34,7 @@ std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t o
}
std::uint32_t* APS5_VABI sceAgcDcbClearState(CommandBuffer* buf, std::uint32_t command) {
Agc::Command::CheckBits(command, 0xfu, __func__);
return Agc::Command::Emit(buf, 0x12u, {command}, __func__);
return Agc::Command::Emit(buf, 0x12u, {command & 0xfu}, __func__);
}
uint32_t* APS5_VABI sceAgcDcbRewind(CommandBuffer* buf, uint32_t initial_state) {

View File

@ -36,7 +36,6 @@ std::uint32_t* APS5_VABI sceAgcDcbEventWrite(CommandBuffer* buf, std::uint8_t ev
Agc::Command::CheckAddress(guestAddress, 8, __func__);
return Agc::Command::Emit(buf, 0x46u, {0x100u | eventType, static_cast<std::uint32_t>(guestAddress), static_cast<std::uint32_t>(guestAddress >> 32u)}, __func__);
}
Agc::Command::Require(address == nullptr, __func__, "this event does not use an address");
const auto eventIndex = eventType == 7 || eventType == 15 || eventType == 16 ? 0x400u : 0u;
return Agc::Command::Emit(buf, 0x46u, {eventIndex | eventType}, __func__);
}

View File

@ -7,6 +7,8 @@
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
static constexpr int SCE_AGC_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80890001);
extern "C" {
int APS5_VABI sceAgcInit_0090(std::uint32_t* state, std::uint32_t version) {
@ -16,6 +18,7 @@ int APS5_VABI sceAgcInit_0090(std::uint32_t* state, std::uint32_t version) {
}
int APS5_VABI sceAgcInit(std::uint32_t version) {
if (version >= 14) return SCE_AGC_ERROR_INVALID_ARGUMENT;
Agc::Command::GetRegisterDefaults(version, false, __func__);
Agc::Command::GetRegisterDefaults(version, true, __func__);
return 0;

View File

@ -1,26 +1,62 @@
#include "prx/libSceAgc/DcbState/include/Workload.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include "prx/libSceAgcDriver/Resource/include/Registration.hpp"
#include <cstdint>
#include <cstddef>
#include <cstring>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr std::uint32_t OpcodeNop = 0x10;
constexpr std::uint32_t ActivePacketWords = 18;
constexpr std::uint32_t CompletePacketWords = 12;
constexpr std::uint32_t MaxWorkloadId = 63;
constexpr std::uint32_t MaxActiveCount = 63;
uint32_t* NewPacket(CommandBuffer* buf, std::uint32_t words, const char* function) {
auto* packet = Agc::Command::Allocate(buf, words, function);
std::memset(packet, 0, words * sizeof(std::uint32_t));
packet[0] = Agc::Command::Header(OpcodeNop, words, 0);
return packet;
}
}
extern "C" {
uint32_t* APS5_VABI sceAgcDcbSetWorkloadComplete(CommandBuffer* buf, uint32_t stream_id, uint32_t workload_id) {
(void)buf;
(void)stream_id;
(void)workload_id;
NotImplemented_nid_no_patch(__func__);
return nullptr;
if (buf == nullptr || workload_id > MaxWorkloadId || !AgcDriverWorkloadStreamRegistered_nid_postfix(stream_id)) return nullptr;
const std::uint64_t clearMask = ~(std::uint64_t{1} << workload_id);
auto* packet = NewPacket(buf, CompletePacketWords, __func__);
packet[1] = stream_id;
packet[2] = workload_id;
packet[3] = static_cast<std::uint32_t>(clearMask);
packet[4] = static_cast<std::uint32_t>(clearMask >> 32);
return packet;
}
uint32_t* APS5_VABI sceAgcDcbSetWorkloadsActive(CommandBuffer* buf, uint32_t stream_id, const uint32_t* workload_ids, uint32_t workload_count) {
(void)buf;
(void)stream_id;
(void)workload_ids;
(void)workload_count;
if (buf == nullptr || workload_ids == nullptr || workload_count == 0 || workload_count > MaxActiveCount || !AgcDriverWorkloadStreamRegistered_nid_postfix(stream_id)) return nullptr;
std::uint64_t mask = 0;
for (std::uint32_t index = 0; index < workload_count; ++index) {
const auto id = workload_ids[index];
if (id > MaxWorkloadId) return nullptr;
const std::uint64_t bit = std::uint64_t{1} << id;
if ((mask & bit) != 0) return nullptr;
mask |= bit;
}
auto* packet = NewPacket(buf, ActivePacketWords, __func__);
packet[1] = stream_id;
packet[2] = static_cast<std::uint32_t>(mask);
packet[3] = static_cast<std::uint32_t>(mask >> 32);
return packet;
}
uint32_t* APS5_VABI sceAgcDcbSetWorkloadStreamInactive(CommandBuffer* buf, uint32_t streamId) {
if (buf == nullptr || !AgcDriverWorkloadStreamRegistered_nid_postfix(streamId)) return nullptr;
NotImplemented_nid_no_patch(__func__);
return nullptr;
}

View File

@ -0,0 +1,4 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_DEBUG_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_DEBUG_HPP
#endif

View File

@ -0,0 +1,4 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_SEMAPHORE_HPP
#define CORE_LIBS_PRX_LIBSCEAGC_MISC_INCLUDE_SEMAPHORE_HPP
#endif

View File

@ -0,0 +1,14 @@
#include "prx/libSceAgc/Misc/include/Debug.hpp"
#include <cstdint>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
extern "C" {
int APS5_VABI sceAgcDebugRaiseException(std::uint32_t exceptionId) {
APS5_LOG_OUT("sceAgcDebugRaiseException exceptionId=0x%08x", static_cast<unsigned>(exceptionId));
return 0;
}
}

View File

@ -0,0 +1,61 @@
#include "prx/libSceAgc/Misc/include/Semaphore.hpp"
#include "prx/libSceAgc/Command/include/Packet.hpp"
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <limits>
#include <mutex>
#include "SceTypes.hpp"
#include "prx/libc/include/General.hpp"
namespace {
constexpr std::uint64_t semaphoreMemoryAlignment = 0x4000u;
constexpr std::uint64_t semaphoreLabelBytes = 32u;
constexpr std::uint64_t semaphoreCounterBytes = 8u;
std::mutex semaphoreMutex;
std::uint8_t* semaphoreMemory = nullptr;
std::uint64_t semaphoreBytes = 0;
bool hasOnlyZeroLabelTails(const std::uint8_t* memory, std::uint64_t sizeInBytes) {
for (std::uint64_t label = 0; label < sizeInBytes; label += semaphoreLabelBytes) {
for (std::uint64_t byte = semaphoreCounterBytes; byte < semaphoreLabelBytes; ++byte) {
if (memory[label + byte] != 0) return false;
}
}
return true;
}
}
extern "C" {
int APS5_VABI sceAgcSetAmmSemaphoreMemory(void* memory, std::uint64_t sizeInBytes) {
const auto address = reinterpret_cast<std::uintptr_t>(memory);
std::lock_guard lock(semaphoreMutex);
Agc::Command::Require(semaphoreMemory == nullptr, __func__, "the AMM semaphore memory is already registered; the console's error is unknown");
Agc::Command::Require(memory != nullptr && sizeInBytes != 0, __func__, "null or empty AMM semaphore memory; the console's error is unknown");
Agc::Command::Require(((address | sizeInBytes) & (semaphoreMemoryAlignment - 1u)) == 0, __func__, "AMM semaphore memory or size is not 16 KiB aligned; the console's error is unknown");
Agc::Command::Require(sizeInBytes - 1u <= std::numeric_limits<std::uintptr_t>::max() - address, __func__, "AMM semaphore memory wraps the address space");
auto* labels = static_cast<std::uint8_t*>(memory);
Agc::Command::Require(hasOnlyZeroLabelTails(labels, sizeInBytes), __func__, "AMM semaphore labels hold data past their first 8 bytes; whether registration clears it is unknown");
for (std::uint64_t label = 0; label < sizeInBytes; label += semaphoreLabelBytes) {
std::memset(labels + label, 0, semaphoreCounterBytes);
}
semaphoreMemory = labels;
semaphoreBytes = sizeInBytes;
return 0;
}
int APS5_VABI sceAgcGetSemaphoreLabel(std::uint32_t index, void** label) {
std::lock_guard lock(semaphoreMutex);
Agc::Command::Require(semaphoreMemory != nullptr, __func__, "no AMM semaphore memory is registered; the console's error is unknown");
Agc::Command::Require(index < semaphoreBytes / semaphoreLabelBytes, __func__, "the semaphore label lies outside the AMM semaphore memory; the console's error is unknown");
Agc::Command::Require(label != nullptr, __func__, "null semaphore label output; the console's error is unknown");
*label = semaphoreMemory + static_cast<std::uint64_t>(index) * semaphoreLabelBytes;
return 0;
}
}

View File

@ -19,11 +19,13 @@ constexpr std::uint32_t VGT_SHADER_STAGES_EN = 0x2D5u;
constexpr std::uint32_t VGT_PRIMITIVE_TYPE = 0x242u;
constexpr std::uint32_t GE_CNTL = 0x25Bu;
constexpr std::uint32_t GE_USER_VGPR1 = 0x25Cu;
constexpr std::uint32_t GE_USER_VGPR_EN = 0x262u;
constexpr std::uint32_t GE_PC_ALLOC = 0x260u;
constexpr std::uint32_t SPI_SHADER_PGM_RSRC4_GS = 0x081u;
constexpr std::uint32_t SPI_VS_OUT_CONFIG = 0x1B1u;
constexpr std::uint32_t SPI_SHADER_IDX_FORMAT = 0x1C2u;
constexpr std::uint32_t GE_MAX_OUTPUT_PER_SUBGROUP = 0x1FFu;
constexpr std::uint32_t PA_CL_VS_OUT_CNTL = 0x207u;
constexpr std::uint32_t VGT_GS_ONCHIP_CNTL = 0x291u;

View File

@ -0,0 +1,39 @@
#include <cstdint>
#include <stdexcept>
#include <string>
#include <prx/libc/include/General.hpp>
#include "SceShaders.hpp"
#include "prx/libSceAgc/Shader/include/ShaderConstants.hpp"
namespace {
constexpr std::uint32_t IDX0_EXPORT_FORMAT_MASK = 0xFu;
constexpr std::uint32_t SPI_SHADER_2COMP = 2u;
constexpr std::uint32_t TwoComponentPayloadBytes = 8u;
}
extern "C" {
int APS5_VABI sceAgcGetGsPrimPayload(std::uint32_t* payload, const Shader* gs) {
if (payload == nullptr || gs == nullptr) {
throw std::runtime_error(std::string(__func__) + ": payload or gs is null");
}
if (gs->num_cx_registers != 0 && gs->cx_registers == nullptr) {
throw std::runtime_error(std::string(__func__) + ": gs->cx_registers is null but num_cx_registers != 0");
}
std::uint32_t bytes = 0;
for (std::uint32_t i = 0; i < gs->num_cx_registers; ++i) {
const ShaderRegister& reg = gs->cx_registers[i];
if (reg.offset == ShaderRegs::SPI_SHADER_IDX_FORMAT && (reg.value & IDX0_EXPORT_FORMAT_MASK) == SPI_SHADER_2COMP) {
bytes = TwoComponentPayloadBytes;
break;
}
}
*payload = bytes;
return 0;
}
}

View File

@ -53,10 +53,21 @@ std::uint32_t CreateInterpolantValueSplitF16(std::uint32_t psWord, const ShaderS
: CreateInterpolantValue(psWord, gsSemantic);
}
int CreateInterpolantMapping(const char* fn, ShaderRegister* regs, const Shader* gs, const Shader* ps, CreateInterpolantValueFn createValue) {
std::uint32_t UpdateInterpolantValue(std::uint32_t psWord, const ShaderSemantic* gsSemantic) {
const std::uint32_t f16 = (psWord >> 20u) & 0x3u;
if (f16 != 0 && f16 != InterpolantF16HiOnly && ((psWord >> 28u) & 0x3u) != ((psWord >> 30u) & 0x3u)) {
throw std::runtime_error(std::string(__func__) + ": DEFAULT_VAL of an f16 input with different low and high defaults is unknown");
}
return CreateInterpolantValueSplitF16(psWord, gsSemantic);
}
int CreateInterpolantMapping(const char* fn, ShaderRegister* regs, const Shader* gs, const Shader* ps, CreateInterpolantValueFn createValue, bool fillIdentityTail) {
if (regs == nullptr) {
throw std::runtime_error(std::string(fn) + ": regs is null");
}
if (ps == nullptr && !fillIdentityTail) {
throw std::runtime_error(std::string(fn) + ": ps is null");
}
if (ps != nullptr && ps->num_input_semantics > 32) {
throw std::runtime_error(std::string(fn) + ": input semantic count exceeds 32");
}
@ -66,13 +77,14 @@ int CreateInterpolantMapping(const char* fn, ShaderRegister* regs, const Shader*
auto* output = regs;
regs = values.data();
if (ps == nullptr || ps->num_input_semantics == 0) {
FillIdentityInterpolants(regs, 0);
const std::uint32_t count = fillIdentityTail ? InterpolantRegisterCount : 0;
if (fillIdentityTail) FillIdentityInterpolants(regs, 0);
if (ps != nullptr) {
AgcDriverResolveShaderAbi_nid_postfix(ps, {regs, 32}, {});
AgcDriverResolveShaderAbi_nid_postfix(ps, {regs, count}, {});
if (gs != nullptr) AgcDriverResolveGraphicsAbi_nid_postfix(gs, ps, 0);
}
transaction.Commit();
std::copy(values.begin(), values.end(), output);
std::copy_n(values.begin(), count, output);
return 0;
}
@ -98,11 +110,12 @@ int CreateInterpolantMapping(const char* fn, ShaderRegister* regs, const Shader*
SetInterpolantRegister(regs, i, createValue(ShaderSemanticWord(psSemantic), gsSemantic));
}
FillIdentityInterpolants(regs, ps->num_input_semantics);
AgcDriverResolveShaderAbi_nid_postfix(ps, {regs, 32}, {});
const std::uint32_t count = fillIdentityTail ? InterpolantRegisterCount : ps->num_input_semantics;
if (fillIdentityTail) FillIdentityInterpolants(regs, ps->num_input_semantics);
AgcDriverResolveShaderAbi_nid_postfix(ps, {regs, count}, {});
AgcDriverResolveGraphicsAbi_nid_postfix(gs, ps, 0);
transaction.Commit();
std::copy(values.begin(), values.end(), output);
std::copy_n(values.begin(), count, output);
return 0;
}
@ -111,12 +124,17 @@ int CreateInterpolantMapping(const char* fn, ShaderRegister* regs, const Shader*
extern "C" {
int APS5_VABI sceAgcCreateInterpolantMapping_0100(ShaderRegister* regs, const Shader* gs, const Shader* ps) {
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValue);
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValue, true);
}
APS5_EXPORT("dbOlWdppb4o", sceAgcUnknownCreateInterpolantMapping);
int APS5_VABI sceAgcUnknownCreateInterpolantMapping(ShaderRegister* regs, const Shader* gs, const Shader* ps) {
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValueSplitF16);
return CreateInterpolantMapping(__func__, regs, gs, ps, CreateInterpolantValueSplitF16, true);
}
APS5_EXPORT("vieBRwlh1Lw", sceAgcUnknown_vieBRwlh1Lw);
int APS5_VABI sceAgcUnknown_vieBRwlh1Lw(ShaderRegister* regs, const Shader* gs, const Shader* ps) {
return CreateInterpolantMapping(__func__, regs, gs, ps, UpdateInterpolantValue, false);
}
}

View File

@ -11,6 +11,18 @@
#include "prx/libSceAgc/Shader/include/ShaderUtils.hpp"
#include "prx/libSceAgc/Shader/include/ShaderConstants.hpp"
namespace {
void RequireSpecialOffset(const ShaderRegister& reg, std::uint32_t expected, const char* name) {
if (reg.offset != expected) {
char message[160];
std::snprintf(message, sizeof(message), "sceAgcCreatePrimState: %s special register at offset 0x%x, expected 0x%x", name, static_cast<unsigned>(reg.offset), static_cast<unsigned>(expected));
throw std::runtime_error(message);
}
}
}
extern "C" {
int APS5_VABI sceAgcCreatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_regs, const Shader* hs, const Shader* gs, std::uint32_t prim_type) {
@ -25,21 +37,17 @@ int APS5_VABI sceAgcCreatePrimState(ShaderRegister* cx_regs, ShaderRegister* uc_
throw std::runtime_error(std::string(__func__) + ": invalid shader type or missing special registers");
}
(void)GraphicsPrimTypeToGsOut(prim_type);
const auto validStages = [](const Shader* shader) { return shader->specials->vgt_shader_stages_en.offset == ShaderRegs::VGT_SHADER_STAGES_EN; };
if (!validStages(gs) || (hs != nullptr && !validStages(hs))) {
throw std::runtime_error(std::string(__func__) + ": invalid context register offsets");
}
if (gs->specials->ge_cntl.offset != ShaderRegs::GE_CNTL || gs->specials->ge_user_vgpr_en.offset != ShaderRegs::GE_USER_VGPR_EN || (hs != nullptr && hs->specials->ge_user_vgpr_en.offset != ShaderRegs::GE_USER_VGPR_EN)) {
throw std::runtime_error(std::string(__func__) + ": invalid user configuration register offsets");
}
RequireSpecialOffset(gs->specials->vgt_shader_stages_en, ShaderRegs::VGT_SHADER_STAGES_EN, "VGT_SHADER_STAGES_EN");
if (hs != nullptr) RequireSpecialOffset(hs->specials->vgt_shader_stages_en, ShaderRegs::VGT_SHADER_STAGES_EN, "VGT_SHADER_STAGES_EN");
RequireSpecialOffset(gs->specials->ge_cntl, ShaderRegs::GE_CNTL, "GE_CNTL");
std::array<ShaderRegister, 2> contextValues{gs->specials->vgt_shader_stages_en, gs->specials->vgt_gs_out_prim_type};
if (hs != nullptr) {
contextValues[0].value |= hs->specials->vgt_shader_stages_en.value;
}
if ((contextValues[0].value & ShaderRegs::VGT_SHADER_STAGES_GS_BIT) != 0) {
if (gs->specials->vgt_gs_out_prim_type.offset != ShaderRegs::VGT_GS_OUT_PRIM_TYPE) throw std::runtime_error(std::string(__func__) + ": invalid context register offsets");
RequireSpecialOffset(gs->specials->vgt_gs_out_prim_type, ShaderRegs::VGT_GS_OUT_PRIM_TYPE, "VGT_GS_OUT_PRIM_TYPE");
} else if (hs != nullptr) {
if (hs->specials->vgt_gs_out_prim_type.offset != ShaderRegs::VGT_GS_OUT_PRIM_TYPE) throw std::runtime_error(std::string(__func__) + ": invalid context register offsets");
RequireSpecialOffset(hs->specials->vgt_gs_out_prim_type, ShaderRegs::VGT_GS_OUT_PRIM_TYPE, "VGT_GS_OUT_PRIM_TYPE");
contextValues[1] = hs->specials->vgt_gs_out_prim_type;
} else {
contextValues[1] = {ShaderRegs::VGT_GS_OUT_PRIM_TYPE, GraphicsPrimTypeToGsOut(prim_type)};

View File

@ -15,21 +15,6 @@ int APS5_VABI sceAgcAcbSetMarkerSpan() {
return 0;
}
int APS5_VABI sceAgcAcbSetWorkloadComplete() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcAcbSetWorkloadStreamInactive() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcAcbSetWorkloadsActive() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcAcquireMemSetEngine() {
NotImplemented_nid_no_patch(__func__);
return 0;
@ -45,31 +30,11 @@ int APS5_VABI sceAgcDcbSetMarkerSpan() {
return 0;
}
int APS5_VABI sceAgcDcbSetWorkloadStreamInactive() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcDebugRaiseException() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcGetDefaultCxStateFlat() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcGetSemaphoreLabel() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcSetAmmSemaphoreMemory() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcSetSemaphoreMemory() {
NotImplemented_nid_no_patch(__func__);
return 0;
@ -80,11 +45,6 @@ int APS5_VABI sceAgcCbMemsetExclusive() {
return 0;
}
int APS5_VABI sceAgcGetGsPrimPayload() {
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceAgcSetShaderInstrumentation() {
NotImplemented_nid_no_patch(__func__);
return 0;
@ -95,10 +55,4 @@ int APS5_VABI sceAgcGetShaderInstrumentation() {
return 0;
}
APS5_EXPORT("vieBRwlh1Lw", sceAgcUnknown_vieBRwlh1Lw);
int APS5_VABI sceAgcUnknown_vieBRwlh1Lw(void) {
NotImplemented_nid_no_patch(__func__);
return 0;
}
}

View File

@ -10,9 +10,12 @@
#include <cstring>
#include <string>
extern "C" std::uint32_t* APS5_VABI sceAgcDcbEventWrite(CommandBuffer* buf, std::uint8_t eventType, const volatile void* address);
extern "C" std::uint64_t APS5_VABI sceAgcDcbEventWriteGetSize(std::uint8_t eventType);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbResetQueue(CommandBuffer* buf, std::uint32_t op, std::uint32_t state);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbClearState(CommandBuffer* buf, std::uint32_t command);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetFlip(CommandBuffer* buf, std::uint32_t handle, std::int32_t index, std::uint32_t mode, std::int64_t argument);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbWaitUntilSafeForRendering(CommandBuffer* buf, std::uint32_t handle, std::uint32_t index);
extern "C" int APS5_VABI sceAgcSuspendPoint();
extern "C" int APS5_VABI sceAgcInit(std::uint32_t version);
extern "C" void* APS5_VABI sceAgcGetRegisterDefaults();
@ -29,6 +32,14 @@ extern "C" std::uint32_t* APS5_VABI sceAgcCbSetShRegisterRangeDirect(CommandBuff
extern "C" std::uint32_t* APS5_VABI sceAgcDcbContextStateOp_0100(CommandBuffer* buf, std::uint32_t operation);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbPushMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbPopMarker(CommandBuffer* buf);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetWorkloadComplete(CommandBuffer* buf, std::uint32_t stream_id, std::uint32_t workload_id);
extern "C" int APS5_VABI sceAgcDriverRegisterWorkloadStream(std::uint32_t stream_id, const void* stream);
extern "C" int APS5_VABI sceAgcDriverUnregisterWorkloadStream(std::uint32_t stream_id);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetWorkloadsActive(CommandBuffer* buf, std::uint32_t stream_id, const std::uint32_t* workload_ids, std::uint32_t workload_count);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetWorkloadStreamInactive(CommandBuffer* buf, std::uint32_t streamId);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadsActive(CommandBuffer* buf, std::uint32_t streamId, const std::uint32_t* workloadIds, std::uint32_t workloadCount);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadComplete(CommandBuffer* buf, std::uint32_t streamId, std::uint32_t workloadId);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetWorkloadStreamInactive(CommandBuffer* buf, std::uint32_t streamId);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbPushMarker(CommandBuffer* buf, const char* str, std::uint32_t color);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbPopMarker(CommandBuffer* buf);
@ -36,9 +47,13 @@ extern "C" std::uint32_t* APS5_VABI sceAgcAcbSetMarker(CommandBuffer* buf, const
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetIndexBuffer(CommandBuffer* buf, std::uint64_t indexAddress);
extern "C" std::uint32_t* APS5_VABI sceAgcDcbSetBaseIndirectArgs(CommandBuffer* buf, std::uint32_t shaderType, const volatile void* indirectBaseAddr);
extern "C" std::uint32_t* APS5_VABI sceAgcSetNop(CommandBuffer* buf, std::uint32_t sizeDw);
extern "C" int APS5_VABI sceAgcDebugRaiseException(std::uint32_t exceptionId);
extern "C" std::uint32_t* APS5_VABI sceAgcAcbAtomicGds_0900(CommandBuffer* buf, std::uint32_t control, std::uint32_t mode, std::uint32_t gdsOp, std::uint32_t src, std::uint16_t offset, std::uint16_t index, std::uint32_t mask, std::uint32_t data, std::uint64_t compareData, std::uint64_t extraData);
namespace {
constexpr int SCE_AGC_ERROR_INVALID_ARGUMENT = static_cast<int>(0x80890001);
void check(bool condition, const char* message) {
if (!condition) {
throw std::runtime_error(message);
@ -133,12 +148,14 @@ void testClearState() {
auto* packet = sceAgcDcbClearState(&storage.buffer, command);
check(packet == storage.words.data() + command * 2 && packet[0] == 0xc0001200u && packet[1] == command, "incorrect CLEAR_STATE packet");
}
check(storage.buffer.cursor_up == storage.words.data() + 32 && storage.words[32] == 0xdeadbeefu, "incorrect CLEAR_STATE cursor advance");
auto* maskedZero = sceAgcDcbClearState(&storage.buffer, 0x10u);
check(maskedZero == storage.words.data() + 32 && maskedZero[0] == 0xc0001200u && maskedZero[1] == 0, "CLEAR_STATE did not mask the command to four bits");
auto* maskedF = sceAgcDcbClearState(&storage.buffer, 0xffffffffu);
check(maskedF == storage.words.data() + 34 && maskedF[0] == 0xc0001200u && maskedF[1] == 0xfu, "CLEAR_STATE did not mask the command to four bits");
check(storage.buffer.cursor_up == storage.words.data() + 36 && storage.words[36] == 0xdeadbeefu, "incorrect CLEAR_STATE cursor advance");
const auto before = storage.words;
expectFailure([&] { sceAgcDcbClearState(&storage.buffer, 0x10u); });
expectFailure([&] { sceAgcDcbClearState(&storage.buffer, 0xffffffffu); });
expectFailure([] { sceAgcDcbClearState(nullptr, 0); });
check(storage.words == before && storage.buffer.cursor_up == storage.words.data() + 32, "invalid CLEAR_STATE modified the buffer");
check(storage.words == before && storage.buffer.cursor_up == storage.words.data() + 36, "invalid CLEAR_STATE modified the buffer");
}
struct ContextGrowth {
@ -203,6 +220,91 @@ void testMarkers() {
expectFailure([] { sceAgcAcbSetMarker(nullptr, "frame", 0); });
}
void testWorkloads() {
Storage dcb;
const std::uint32_t ids[2] = {3, 4};
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, ids, 2) == nullptr, "workloads active packet for an unregistered stream");
check(sceAgcDcbSetWorkloadComplete(&dcb.buffer, 1, 3) == nullptr, "workload complete packet for an unregistered stream");
check(dcb.buffer.cursor_up == dcb.words.data(), "unregistered stream advanced the cursor");
const std::uint32_t record[8] = {};
check(sceAgcDriverRegisterWorkloadStream(1, record) == 0, "workload stream registration failed");
const auto* active = sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, ids, 2);
check(active == dcb.words.data() && active[0] == Agc::Command::Header(0x10, 18, 0) && active[1] == 1 && active[2] == 0x18 && active[3] == 0, "workloads active packet");
for (std::uint32_t word = 4; word < 18; ++word) check(active[word] == 0, "workloads active packet tail is not zero");
const auto* complete = sceAgcDcbSetWorkloadComplete(&dcb.buffer, 1, 33);
check(complete == dcb.words.data() + 18 && complete[0] == Agc::Command::Header(0x10, 12, 0) && complete[1] == 1 && complete[2] == 33 && complete[3] == 0xffffffffu && complete[4] == 0xfffffffdu, "workload complete packet");
for (std::uint32_t word = 5; word < 12; ++word) check(complete[word] == 0, "workload complete packet tail is not zero");
check(dcb.buffer.cursor_up == dcb.words.data() + 30, "incorrect workload cursor advance");
const std::uint32_t duplicate[2] = {5, 5};
const std::uint32_t outOfRange[1] = {64};
const std::uint32_t top[1] = {63};
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, duplicate, 2) == nullptr, "duplicate workload ids accepted");
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, outOfRange, 1) == nullptr, "workload id 64 accepted");
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, top, 1) != nullptr, "workload id 63 rejected");
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, ids, 0) == nullptr, "workload count 0 accepted");
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 1, nullptr, 2) == nullptr, "null workload ids accepted");
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 0, ids, 2) == nullptr, "stream id 0 accepted");
check(sceAgcDcbSetWorkloadsActive(&dcb.buffer, 32, ids, 2) == nullptr, "stream id 32 accepted");
check(sceAgcDcbSetWorkloadComplete(&dcb.buffer, 1, 64) == nullptr, "workload complete id 64 accepted");
check(sceAgcDcbSetWorkloadsActive(nullptr, 1, ids, 2) == nullptr, "null command buffer accepted");
check(sceAgcDcbSetWorkloadComplete(nullptr, 1, 3) == nullptr, "null command buffer accepted for complete");
check(sceAgcDriverUnregisterWorkloadStream(1) == 0, "workload stream unregistration failed");
check(sceAgcDcbSetWorkloadComplete(&dcb.buffer, 1, 3) == nullptr, "workload complete packet after unregistration");
}
void testWorkloadStreams() {
struct Vector {
std::uint32_t streamId;
std::uintptr_t workloadIds;
std::uint32_t workloadCount;
std::uint32_t workloadId;
};
const std::array<Vector, 3> vectors{{
{0x11111111u, 0x22222222u, 0x33333333u, 0x22222222u},
{1, 2, 3, 2},
{0x22222222u, static_cast<std::uintptr_t>(0x3333333344444444ull), 0, 0x44444444u}}};
for (const auto& vector : vectors) {
Storage storage;
storage.words.fill(0xdeadbeefu);
const auto before = storage.words;
const auto* ids = reinterpret_cast<const std::uint32_t*>(vector.workloadIds);
check(sceAgcDcbSetWorkloadsActive(&storage.buffer, vector.streamId, ids, vector.workloadCount) == nullptr, "DCB workloads active returned a packet for an unregistered stream");
check(sceAgcDcbSetWorkloadComplete(&storage.buffer, vector.streamId, vector.workloadId) == nullptr, "DCB workload complete returned a packet for an unregistered stream");
check(sceAgcDcbSetWorkloadStreamInactive(&storage.buffer, vector.streamId) == nullptr, "DCB workload stream inactive returned a packet for an unregistered stream");
check(sceAgcAcbSetWorkloadsActive(&storage.buffer, vector.streamId, ids, vector.workloadCount) == nullptr, "ACB workloads active returned a packet for an unregistered stream");
check(sceAgcAcbSetWorkloadComplete(&storage.buffer, vector.streamId, vector.workloadId) == nullptr, "ACB workload complete returned a packet for an unregistered stream");
check(sceAgcAcbSetWorkloadStreamInactive(&storage.buffer, vector.streamId) == nullptr, "ACB workload stream inactive returned a packet for an unregistered stream");
check(storage.words == before && storage.buffer.cursor_up == storage.words.data(), "a workload call for an unregistered stream wrote to the buffer");
}
const std::uint32_t ids[2] = {3, 4};
Storage exhausted;
exhausted.buffer.cursor_down = exhausted.words.data() + 3;
const auto before = exhausted.words;
check(sceAgcDcbSetWorkloadStreamInactive(&exhausted.buffer, 1) == nullptr && sceAgcAcbSetWorkloadsActive(&exhausted.buffer, 1, ids, 2) == nullptr &&
sceAgcAcbSetWorkloadComplete(&exhausted.buffer, 1, 3) == nullptr && sceAgcAcbSetWorkloadStreamInactive(&exhausted.buffer, 1) == nullptr,
"a workload call for an unregistered stream on an exhausted buffer returned a packet");
check(exhausted.words == before && exhausted.buffer.cursor_up == exhausted.words.data(), "a workload call for an unregistered stream modified an exhausted buffer");
check(sceAgcDcbSetWorkloadStreamInactive(nullptr, 1) == nullptr && sceAgcAcbSetWorkloadsActive(nullptr, 1, ids, 2) == nullptr &&
sceAgcAcbSetWorkloadComplete(nullptr, 1, 3) == nullptr && sceAgcAcbSetWorkloadStreamInactive(nullptr, 1) == nullptr,
"a workload call with a null command buffer returned a packet");
const std::uint32_t record[8] = {};
check(sceAgcDriverRegisterWorkloadStream(1, record) == 0, "workload stream registration failed");
Storage registered;
registered.words.fill(0xdeadbeefu);
const auto untouched = registered.words;
expectFailure([&] { sceAgcDcbSetWorkloadStreamInactive(&registered.buffer, 1); });
expectFailure([&] { sceAgcAcbSetWorkloadsActive(&registered.buffer, 1, ids, 2); });
expectFailure([&] { sceAgcAcbSetWorkloadComplete(&registered.buffer, 1, 3); });
expectFailure([&] { sceAgcAcbSetWorkloadStreamInactive(&registered.buffer, 1); });
check(registered.words == untouched && registered.buffer.cursor_up == registered.words.data(), "a workload call that threw modified the buffer");
check(sceAgcDcbSetWorkloadStreamInactive(nullptr, 1) == nullptr && sceAgcAcbSetWorkloadsActive(nullptr, 1, ids, 2) == nullptr &&
sceAgcAcbSetWorkloadComplete(nullptr, 1, 3) == nullptr && sceAgcAcbSetWorkloadStreamInactive(nullptr, 1) == nullptr,
"a workload call with a null command buffer for a registered stream returned a packet");
check(sceAgcDriverUnregisterWorkloadStream(1) == 0, "workload stream unregistration failed");
}
void testIndexBuffer() {
Storage storage;
alignas(4) std::uint16_t indices[2]{};
@ -313,6 +415,17 @@ void testFlip() {
exhausted.buffer.cursor_down = exhausted.words.data() + 5;
expectFailure([&] { sceAgcDcbSetFlip(&exhausted.buffer, 1, 0, 1, 0); });
check(exhausted.buffer.cursor_up == exhausted.words.data(), "failed flip allocation advanced cursor");
Storage acb;
acb.words.fill(0xdeadbeefu);
auto* wait = sceAgcAcbWaitUntilSafeForRendering(&acb.buffer, 0xfedcba98u, 2);
const std::array<std::uint32_t, 4> expectedWait{0xc0021018u, 0xfedcba98u, 2, 0};
check(wait == acb.words.data() && std::equal(expectedWait.begin(), expectedWait.end(), wait), "compute rendering wait packet mismatch");
check(acb.buffer.cursor_up == wait + 4 && wait[4] == 0xdeadbeefu, "compute rendering wait overran allocation");
expectFailure([] { sceAgcAcbWaitUntilSafeForRendering(nullptr, 1, 0); });
Storage full;
full.buffer.cursor_down = full.words.data() + 3;
expectFailure([&] { sceAgcAcbWaitUntilSafeForRendering(&full.buffer, 1, 0); });
check(full.buffer.cursor_up == full.words.data(), "failed compute rendering wait advanced cursor");
check(sceAgcSuspendPoint() == 0, "empty suspend failed");
}
@ -401,6 +514,111 @@ void testMemory() {
check(storage.words == beforeMask, "invalid mask patch modified packet memory");
}
struct AtomicGdsAllocation {
Storage storage;
std::uint32_t capacity = 15;
std::uint32_t calls = 0;
bool accepted = true;
};
bool APS5_VABI growAtomicGds(CommandBuffer* buffer, std::uint32_t count, void* userData) {
auto& allocation = *static_cast<AtomicGdsAllocation*>(userData);
check(count == 15, "atomic GDS allocation omitted packet or reserved words");
++allocation.calls;
if (!allocation.accepted) return false;
buffer->bottom = allocation.storage.words.data();
buffer->top = buffer->bottom + allocation.capacity;
buffer->cursor_up = buffer->bottom;
buffer->cursor_down = buffer->top;
return true;
}
void testAtomicGds() {
Storage storage;
storage.words.fill(0xdeadbeefu);
storage.buffer.cursor_up += 2;
auto* packet = sceAgcAcbAtomicGds_0900(&storage.buffer, 0xffffffffu, 1, 0xffffffffu, 0xffffffffu,
0xabcd, 0xfedc, 0x12345678u, 0x87654321u, 0xfedcba9876543210ull, 0x0123456789abcdefull);
const std::array<std::uint32_t, 11> expected{
0xc0091d00u, 0x001500ffu, 0x13fu, 0xabcdu, 0xfedcu, 0x00340078u,
0x87654321u, 0x76543210u, 0xfedcba98u, 0x89abcdefu, 0x01234567u};
check(packet == storage.words.data() + 2, "atomic GDS returned the wrong packet address");
check(std::equal(expected.begin(), expected.end(), packet), "atomic GDS packet or guest ABI mismatch");
check(storage.buffer.cursor_up == packet + expected.size(), "atomic GDS did not advance by eleven words");
check(storage.words[0] == 0xdeadbeefu && storage.words[1] == 0xdeadbeefu &&
std::all_of(storage.words.begin() + 13, storage.words.end(), [](auto word) { return word == 0xdeadbeefu; }),
"atomic GDS wrote outside its packet");
const std::array<std::array<std::uint32_t, 3>, 9> controls{{
{0, 0, 0}, {0, 1, 0x00020000u}, {0, 2, 0}, {0, 0xffffffffu, 0},
{0x20u, 1, 0x00040020u}, {0x40u, 1, 0x00120040u},
{0x200u, 1, 0x00030000u}, {0xfffffd00u, 0, 0}, {0xffu, 1, 0x001400ffu}}};
for (const auto& entry : controls) {
Storage controlStorage;
auto* controlPacket = sceAgcAcbAtomicGds_0900(&controlStorage.buffer, entry[0], entry[1],
0xfffffffeu, 0xffffffc0u, 0, 0, 0xff00ff00u, 0, 0, 0);
std::array<std::uint32_t, 11> controlExpected{};
controlExpected[0] = 0xc0091d00u;
controlExpected[1] = entry[2];
check(std::equal(controlExpected.begin(), controlExpected.end(), controlPacket), "atomic GDS control or field masking mismatch");
}
auto emit = [](CommandBuffer* buffer) {
return sceAgcAcbAtomicGds_0900(buffer, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
};
Storage exact;
exact.buffer.cursor_down = exact.words.data() + 15;
exact.buffer.reserved_dw = 4;
check(emit(&exact.buffer) == exact.words.data() && exact.buffer.cursor_up == exact.words.data() + 11,
"atomic GDS rejected an exact fit with reserved space");
const auto exactBefore = exact.words;
expectFailure([&] { emit(&exact.buffer); });
check(exact.words == exactBefore && exact.buffer.cursor_up == exact.words.data() + 11,
"atomic GDS overwrote reserved space");
Storage exhausted;
exhausted.buffer.cursor_down = exhausted.words.data() + 10;
const auto before = exhausted.words;
expectFailure([&] { emit(&exhausted.buffer); });
check(exhausted.words == before && exhausted.buffer.cursor_up == exhausted.words.data(),
"failed atomic GDS allocation modified the buffer");
expectFailure([&] { emit(nullptr); });
Storage noDown;
noDown.buffer.top = noDown.words.data() + 11;
noDown.buffer.cursor_down = nullptr;
check(emit(&noDown.buffer) == noDown.words.data() && noDown.buffer.cursor_up == noDown.buffer.top,
"atomic GDS did not honor a buffer with no down cursor");
AtomicGdsAllocation allocation;
CommandBuffer growing{nullptr, nullptr, nullptr, nullptr, growAtomicGds, &allocation, 4};
check(emit(&growing) == allocation.storage.words.data() && allocation.calls == 1 &&
growing.cursor_up == allocation.storage.words.data() + 11, "atomic GDS did not use the guest allocation callback");
const std::array<std::uint32_t, 11> zeroPacket{0xc0091d00u};
check(std::equal(zeroPacket.begin(), zeroPacket.end(), allocation.storage.words.begin()),
"atomic GDS callback allocation produced the wrong packet");
for (const bool accepted : {false, true}) {
AtomicGdsAllocation failure;
failure.accepted = accepted;
failure.capacity = 14;
const auto failureBefore = failure.storage.words;
CommandBuffer failed{nullptr, nullptr, nullptr, nullptr, growAtomicGds, &failure, 4};
expectFailure([&] { emit(&failed); });
check(failure.calls == 1 && failure.storage.words == failureBefore &&
failed.cursor_up == (accepted ? failure.storage.words.data() : nullptr),
"atomic GDS wrote a packet after failed or insufficient allocation");
}
}
void testDebugRaiseException() {
int result = -1;
try {
result = sceAgcDebugRaiseException(0x1234abcdu);
} catch (const std::exception& error) {
throw std::runtime_error(std::string("debug exception stopped the caller: ") + error.what());
}
check(result == 0, "debug exception did not return 0");
}
void testDefaults() {
std::uint32_t state = 0x12345678;
check(sceAgcInit_0090(&state, 8) == 0 && state == 0x12345678, "AGC initialization failed or modified caller state");
@ -408,8 +626,10 @@ void testDefaults() {
expectFailure([] { sceAgcInit_0090(nullptr, 8); });
expectFailure([&] { sceAgcInit_0090(&state, 14); });
check(sceAgcInit(8) == 0, "AGC version initialization failed");
expectFailure([] { sceAgcInit(14); });
expectFailure([] { sceAgcInit(0xffffffffu); });
check(sceAgcInit(13) == 0, "AGC version 13 initialization failed");
check(sceAgcInit(14) == SCE_AGC_ERROR_INVALID_ARGUMENT, "unsupported AGC version did not return invalid argument");
check(sceAgcInit(0xffffffffu) == SCE_AGC_ERROR_INVALID_ARGUMENT, "maximum AGC version did not return invalid argument");
check(sceAgcInit(0xffffc278u) == SCE_AGC_ERROR_INVALID_ARGUMENT, "pointer-valued AGC argument did not return invalid argument");
for (std::uint32_t version = 0; version < 14; ++version) {
for (const bool internal : {false, true}) {
auto* first = Agc::Command::GetRegisterDefaults(version, internal, __func__);
@ -423,6 +643,47 @@ void testDefaults() {
expectFailure([] { Agc::Command::GetRegisterDefaults(0xffffffffu, true, __func__); });
}
void testEventWrite() {
Storage storage;
const auto* samplePacket = sceAgcDcbEventWrite(&storage.buffer, 0x38, reinterpret_cast<const void*>(0x1000));
check(samplePacket == storage.words.data(), "incorrect event 0x38 return pointer");
check(storage.words[0] == 0xc0024600u, "incorrect event 0x38 packet header");
check(storage.words[1] == (0x100u | 0x38u), "incorrect event 0x38 first payload word");
check(storage.words[2] == 0x1000u, "incorrect event 0x38 low address word");
check(storage.words[3] == 0u, "incorrect event 0x38 high address word");
check(sceAgcDcbEventWriteGetSize(0x38) == 16, "incorrect event 0x38 size");
check(sceAgcDcbEventWriteGetSize(0x39) == 16, "incorrect event 0x39 size");
Storage sample39;
sceAgcDcbEventWrite(&sample39.buffer, 0x39, reinterpret_cast<const void*>(0x2008));
check(sample39.words[0] == 0xc0024600u && sample39.words[1] == (0x100u | 0x39u) && sample39.words[2] == 0x2008u && sample39.words[3] == 0u, "incorrect event 0x39 packet");
expectFailure([&] { sceAgcDcbEventWrite(&storage.buffer, 0x38, reinterpret_cast<const void*>(0x1001)); });
expectFailure([&] { sceAgcDcbEventWrite(&storage.buffer, 0x40, nullptr); });
Storage partialFlush;
sceAgcDcbEventWrite(&partialFlush.buffer, 7, nullptr);
check(partialFlush.words[0] == 0xc0004600u && partialFlush.words[1] == 0x407u, "incorrect event 7 partial flush packet");
check(sceAgcDcbEventWriteGetSize(7) == 8, "incorrect event 7 size");
Storage partialFlush15;
sceAgcDcbEventWrite(&partialFlush15.buffer, 15, nullptr);
check(partialFlush15.words[0] == 0xc0004600u && partialFlush15.words[1] == 0x40fu, "incorrect event 15 partial flush packet");
Storage partialFlush16;
sceAgcDcbEventWrite(&partialFlush16.buffer, 16, nullptr);
check(partialFlush16.words[0] == 0xc0004600u && partialFlush16.words[1] == 0x410u, "incorrect event 16 partial flush packet");
Storage cacheFlushNull;
sceAgcDcbEventWrite(&cacheFlushNull.buffer, 0x16, nullptr);
check(cacheFlushNull.words[0] == 0xc0004600u && cacheFlushNull.words[1] == 0x16u, "incorrect event 0x16 null address packet");
check(sceAgcDcbEventWriteGetSize(0x16) == 8, "incorrect event 0x16 size");
Storage cacheFlushWithAddress;
sceAgcDcbEventWrite(&cacheFlushWithAddress.buffer, 0x16, reinterpret_cast<const void*>(0x2d2a10000ull));
check(cacheFlushWithAddress.words[0] == 0xc0004600u && cacheFlushWithAddress.words[1] == 0x16u, "incorrect event 0x16 with address packet");
}
}
int main(int argc, char** argv) {
@ -434,10 +695,13 @@ int main(int argc, char** argv) {
return 0;
}
testPackets();
testEventWrite();
testNop();
testClearState();
testIndexedIndirectDraws();
testMarkers();
testWorkloads();
testWorkloadStreams();
testIndexBuffer();
testIndirectBase();
testContextState();
@ -446,6 +710,8 @@ int main(int argc, char** argv) {
testRegisterRange();
testPacketPayloadAddress();
testMemory();
testAtomicGds();
testDebugRaiseException();
testDefaults();
LibcRunShutdown_nid_postfix();
std::puts("AGC command tests passed");

View File

@ -0,0 +1,119 @@
#include "SceShaders.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libSceAgc/Shader/include/ShaderConstants.hpp"
#include <array>
#include <cstdint>
#include <cstdio>
#include <stdexcept>
#include <vector>
extern "C" int APS5_VABI sceAgcGetGsPrimPayload(std::uint32_t* payload, const Shader* gs);
namespace {
constexpr std::uint32_t Sentinel = 0xccccccccu;
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
template <typename TAction>
void expectFailure(TAction action, const char* message) {
try {
action();
} catch (const std::exception& error) {
check(error.what()[0] != '\0', "empty exception message");
return;
}
throw std::runtime_error(message);
}
Shader makeGs(std::vector<ShaderRegister>& cx) {
Shader gs{};
gs.cx_registers = cx.empty() ? nullptr : cx.data();
gs.num_cx_registers = static_cast<std::uint8_t>(cx.size());
return gs;
}
void expectPayload(const Shader& gs, std::uint32_t expected, const char* message) {
std::array<std::uint32_t, 2> out{Sentinel, Sentinel};
check(sceAgcGetGsPrimPayload(out.data(), &gs) == 0, message);
check(out[0] == expected, message);
check(out[1] == Sentinel, "the payload write went past four bytes");
}
void testNoRegisters() {
std::vector<ShaderRegister> cx;
expectPayload(makeGs(cx), 0, "a shader without context registers reported a payload");
}
void testTwoComponentIndexFormat() {
std::vector<ShaderRegister> cx{{ShaderRegs::SPI_SHADER_IDX_FORMAT, 2u}};
expectPayload(makeGs(cx), 8, "a two-component index export did not report 8 bytes");
}
void testOnlyTheLowNibbleCounts() {
std::vector<ShaderRegister> high{{ShaderRegs::SPI_SHADER_IDX_FORMAT, 0xfffffff2u}};
expectPayload(makeGs(high), 8, "bits above IDX0_EXPORT_FORMAT changed the payload");
for (const std::uint32_t format : {0u, 1u, 3u, 4u, 0x20u}) {
std::vector<ShaderRegister> cx{{ShaderRegs::SPI_SHADER_IDX_FORMAT, format}};
expectPayload(makeGs(cx), 0, "an index export format other than two components reported a payload");
}
}
void testOtherRegistersAreIgnored() {
std::vector<ShaderRegister> cx{{ShaderRegs::SPI_VS_OUT_CONFIG, 2u}, {ShaderRegs::GE_MAX_OUTPUT_PER_SUBGROUP, 2u}};
expectPayload(makeGs(cx), 0, "a register other than SPI_SHADER_IDX_FORMAT reported a payload");
}
void testSearchContinuesPastAMismatch() {
std::vector<ShaderRegister> cx{
{ShaderRegs::SPI_VS_OUT_CONFIG, 0u},
{ShaderRegs::SPI_SHADER_IDX_FORMAT, 4u},
{ShaderRegs::SPI_SHADER_IDX_FORMAT, 2u},
};
expectPayload(makeGs(cx), 8, "a non-matching SPI_SHADER_IDX_FORMAT entry stopped the search");
}
void testCountBoundsTheSearch() {
std::vector<ShaderRegister> cx{{ShaderRegs::SPI_VS_OUT_CONFIG, 0u}, {ShaderRegs::SPI_SHADER_IDX_FORMAT, 2u}};
Shader gs = makeGs(cx);
gs.num_cx_registers = 1;
expectPayload(gs, 0, "an entry past num_cx_registers was read");
}
void testRejections() {
std::vector<ShaderRegister> cx{{ShaderRegs::SPI_SHADER_IDX_FORMAT, 2u}};
const Shader gs = makeGs(cx);
std::uint32_t out = Sentinel;
expectFailure([&] { sceAgcGetGsPrimPayload(nullptr, &gs); }, "a null payload pointer was accepted");
expectFailure([&] { sceAgcGetGsPrimPayload(&out, nullptr); }, "a null shader was accepted");
Shader missing{};
missing.num_cx_registers = 1;
expectFailure([&] { sceAgcGetGsPrimPayload(&out, &missing); }, "a register count without a register array was accepted");
check(out == Sentinel, "a rejected call wrote the payload");
}
}
int main() {
try {
testNoRegisters();
testTwoComponentIndexFormat();
testOnlyTheLowNibbleCounts();
testOtherRegistersAreIgnored();
testSearchContinuesPastAMismatch();
testCountBoundsTheSearch();
testRejections();
LibcRunShutdown_nid_postfix();
std::puts("AGC GS primitive payload tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
try { LibcRunShutdown_nid_postfix(); }
catch (const std::exception& shutdown) { std::fprintf(stderr, "shutdown: %s\n", shutdown.what()); }
return 1;
}
}

View File

@ -13,12 +13,14 @@
extern "C" int APS5_VABI sceAgcCreateInterpolantMapping_0100(ShaderRegister* regs, const Shader* gs, const Shader* ps);
extern "C" int APS5_VABI sceAgcUnknownCreateInterpolantMapping(ShaderRegister* regs, const Shader* gs, const Shader* ps);
extern "C" int APS5_VABI sceAgcUnknown_vieBRwlh1Lw(ShaderRegister* regs, const Shader* gs, const Shader* ps);
namespace {
using Registers = std::array<ShaderRegister, 32>;
const Shader* mappedPixel = nullptr;
Registers preparedMapping{};
std::size_t preparedCount = 0;
unsigned mappings = 0;
unsigned links = 0;
@ -65,6 +67,11 @@ void testIdentity() {
regs = filled();
check(sceAgcUnknownCreateInterpolantMapping(regs.data(), nullptr, &ps) == 0, "mapping without inputs failed");
checkIdentity(regs, 0, "mapping without inputs is not the identity");
regs = filled();
check(sceAgcUnknown_vieBRwlh1Lw(regs.data(), nullptr, &ps) == 0, "update without inputs failed");
check(preparedCount == 0u, "update without inputs prepared registers");
const auto untouched = filled();
check(std::memcmp(regs.data(), untouched.data(), sizeof(regs)) == 0, "update without inputs wrote registers");
}
void testMapping() {
@ -99,6 +106,15 @@ void testMapping() {
check(split[4].value == 0x03180002u, "full f16 interpolant mapping changed");
checkIdentity(split, static_cast<std::uint32_t>(inputs.size()), "unused interpolants are not the identity");
auto update = filled();
const auto untouched = filled();
const std::size_t written = inputs.size() * sizeof(ShaderRegister);
check(sceAgcUnknown_vieBRwlh1Lw(update.data(), &gs, &ps) == 0, "update mapping failed");
check(preparedCount == inputs.size(), "update prepared registers past its inputs");
check(std::memcmp(update.data(), preparedMapping.data(), written) == 0, "prepared update differs from published registers");
check(std::memcmp(update.data(), split.data(), written) == 0, "update mapping differs from the split f16 mapping");
check(std::memcmp(update.data() + inputs.size(), untouched.data(), sizeof(update) - written) == 0, "update wrote past its inputs");
auto regular = filled();
check(sceAgcCreateInterpolantMapping_0100(regular.data(), &gs, &ps) == 0, "mapping failed");
check(std::memcmp(regular.data(), preparedMapping.data(), sizeof(regular)) == 0, "prepared mapping differs from published registers");
@ -106,6 +122,51 @@ void testMapping() {
checkIdentity(regular, static_cast<std::uint32_t>(inputs.size()), "unused interpolants are not the identity");
}
void testUpdateMapping() {
std::array<ShaderSemantic, 4> inputs{semantic(1, 0, 0), semantic(2, 0, 1), semantic(3, 0, 2), semantic(4, 0, 3)};
inputs[0].is_custom = 1;
inputs[0].default_value = 2;
inputs[1].default_value = 1;
inputs[1].default_value_hi = 1;
inputs[2].default_value_hi = 2;
inputs[3].is_custom = 1;
inputs[3].default_value = 3;
inputs[3].default_value_hi = 3;
std::array<ShaderSemantic, 3> outputs{semantic(1, 5, 0), semantic(2, 7, 1), semantic(4, 9, 3)};
Shader gs{};
gs.output_semantics = outputs.data();
gs.num_output_semantics = static_cast<std::uint16_t>(outputs.size());
Shader ps{};
ps.input_semantics = inputs.data();
ps.num_input_semantics = static_cast<std::uint32_t>(inputs.size());
auto update = filled();
const auto untouched = filled();
check(sceAgcUnknown_vieBRwlh1Lw(update.data(), &gs, &ps) == 0, "update mapping failed");
check(update[0].value == 0x00000625u, "custom interpolant update changed");
check(update[1].value == 0x01380107u, "low f16 half update changed");
check(update[2].value == 0x02480220u, "unmatched high f16 half update changed");
check(update[3].value == 0x03680709u, "full f16 interpolant update changed");
for (std::uint32_t i = 0; i < inputs.size(); ++i) {
check(update[i].offset == ShaderRegs::SPI_PS_INPUT_CNTL_0 + i, "updated register offset changed");
}
const std::size_t written = inputs.size() * sizeof(ShaderRegister);
check(std::memcmp(update.data() + inputs.size(), untouched.data(), sizeof(update) - written) == 0, "update wrote past its inputs");
auto create = filled();
check(sceAgcUnknownCreateInterpolantMapping(create.data(), &gs, &ps) == 0, "split f16 mapping failed");
check(std::memcmp(create.data(), update.data(), written) == 0, "update differs from the split f16 mapping");
checkIdentity(create, static_cast<std::uint32_t>(inputs.size()), "unused interpolants are not the identity");
for (const std::uint32_t index : {1u, 3u}) {
inputs[index].default_value = 0;
update = filled();
expectFailure([&] { sceAgcUnknown_vieBRwlh1Lw(update.data(), &gs, &ps); });
check(std::memcmp(update.data(), untouched.data(), sizeof(update)) == 0, "rejected update wrote registers");
inputs[index].default_value = inputs[index].default_value_hi;
}
}
void testRejections() {
std::array<ShaderSemantic, 33> inputs{};
Shader gs{};
@ -117,18 +178,23 @@ void testRejections() {
const auto saved = regs;
expectFailure([&] { sceAgcCreateInterpolantMapping_0100(regs.data(), &gs, &ps); });
expectFailure([&] { sceAgcUnknownCreateInterpolantMapping(regs.data(), &gs, &ps); });
expectFailure([&] { sceAgcUnknown_vieBRwlh1Lw(regs.data(), &gs, &ps); });
check(std::memcmp(regs.data(), saved.data(), sizeof(regs)) == 0, "rejected mapping wrote registers");
ps.num_input_semantics = 1;
expectFailure([&] { sceAgcUnknownCreateInterpolantMapping(regs.data(), nullptr, &ps); });
expectFailure([&] { sceAgcUnknownCreateInterpolantMapping(nullptr, &gs, &ps); });
expectFailure([&] { sceAgcUnknown_vieBRwlh1Lw(regs.data(), nullptr, &ps); });
expectFailure([&] { sceAgcUnknown_vieBRwlh1Lw(nullptr, &gs, &ps); });
expectFailure([&] { sceAgcUnknown_vieBRwlh1Lw(regs.data(), &gs, nullptr); });
}
}
extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
check(shader != nullptr && context.size() == preparedMapping.size() && primitive.empty(), "invalid mapping preparation call");
check(shader != nullptr && context.size() <= preparedMapping.size() && primitive.empty(), "invalid mapping preparation call");
mappedPixel = shader;
std::copy(context.begin(), context.end(), preparedMapping.begin());
preparedCount = context.size();
++mappings;
}
@ -141,8 +207,9 @@ int main() {
try {
testIdentity();
testMapping();
testUpdateMapping();
testRejections();
check(mappings == 3u && links == 2u, "mapping preparation call count changed");
check(mappings == 7u && links == 5u, "mapping preparation call count changed");
LibcRunShutdown_nid_postfix();
std::puts("AGC interpolant mapping tests passed");
return 0;

View File

@ -0,0 +1,164 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include "prx/libc/include/Shutdown.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdio>
#include <limits>
#include <memory>
#include <new>
#include <stdexcept>
#include <vector>
extern "C" int APS5_VABI sceAgcSetAmmSemaphoreMemory(void* memory, std::uint64_t sizeInBytes);
extern "C" int APS5_VABI sceAgcGetSemaphoreLabel(std::uint32_t index, void** label);
namespace {
constexpr std::uint64_t blockBytes = 0x4000u;
constexpr std::uint64_t labelBytes = 32u;
constexpr std::uint64_t counterBytes = 8u;
constexpr std::uint64_t memoryBytes = 2u * blockBytes;
constexpr std::uint64_t storageBytes = 4u * blockBytes;
constexpr std::uint32_t labelCount = static_cast<std::uint32_t>(memoryBytes / labelBytes);
constexpr std::uint8_t guardByte = 0xa5u;
constexpr std::uint8_t counterByte = 0x5au;
struct AlignedRelease {
void operator()(std::uint8_t* memory) const { ::operator delete(memory, std::align_val_t{blockBytes}); }
};
std::unique_ptr<std::uint8_t, AlignedRelease> storage;
void check(bool condition, const char* message) {
if (!condition) throw std::runtime_error(message);
}
template <typename TAction>
void expectFailure(TAction action) {
try {
action();
} catch (const std::runtime_error& error) {
check(error.what()[0] != '\0', "empty exception message");
return;
}
throw std::runtime_error("expected an exception");
}
std::uint8_t* memory() {
return storage.get() + blockBytes;
}
std::vector<std::uint8_t> snapshot() {
return {storage.get(), storage.get() + storageBytes};
}
void checkUnchanged(const std::vector<std::uint8_t>& before, const char* message) {
check(std::equal(before.begin(), before.end(), storage.get()), message);
}
void fillStorage() {
std::fill(storage.get(), storage.get() + storageBytes, guardByte);
for (std::uint64_t label = 0; label < memoryBytes; label += labelBytes) {
std::fill(memory() + label, memory() + label + counterBytes, counterByte);
std::fill(memory() + label + counterBytes, memory() + label + labelBytes, std::uint8_t{0});
}
}
void testUnregistered() {
std::array<void*, 3> labels{storage.get(), storage.get(), storage.get()};
expectFailure([&] { sceAgcGetSemaphoreLabel(0, &labels[1]); });
expectFailure([] { sceAgcGetSemaphoreLabel(0, nullptr); });
check(labels[0] == storage.get() && labels[1] == storage.get() && labels[2] == storage.get(), "a rejected semaphore label changed the output");
}
void testRegistrationRejections() {
const auto before = snapshot();
expectFailure([] { sceAgcSetAmmSemaphoreMemory(nullptr, memoryBytes); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(nullptr, 0); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), 0); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory() + 1, memoryBytes); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory() + labelBytes, blockBytes); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), blockBytes - 1u); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), blockBytes + labelBytes); });
expectFailure([] {
const auto top = std::numeric_limits<std::uintptr_t>::max() & ~static_cast<std::uintptr_t>(blockBytes - 1u);
sceAgcSetAmmSemaphoreMemory(reinterpret_cast<void*>(top), memoryBytes);
});
checkUnchanged(before, "a rejected registration changed memory");
testUnregistered();
}
void testUnsettledClearing() {
for (const std::uint64_t offset : {5u * labelBytes + counterBytes, memoryBytes - 1u}) {
memory()[offset] = 0x77u;
const auto before = snapshot();
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes); });
checkUnchanged(before, "a registration with unsettled clearing changed memory");
testUnregistered();
memory()[offset] = 0;
}
}
void testRegistration() {
const auto before = snapshot();
check(sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes) == 0, "registering the AMM semaphore memory failed");
check(std::all_of(memory(), memory() + memoryBytes, [](std::uint8_t byte) { return byte == 0; }), "registration did not clear the semaphore counters");
check(std::equal(before.begin(), before.begin() + blockBytes, storage.get()), "registration wrote before the semaphore memory");
check(std::equal(before.begin() + blockBytes + memoryBytes, before.end(), memory() + memoryBytes), "registration wrote past the semaphore memory");
}
void testLabels() {
for (const std::uint32_t index : {0u, 1u, 7u, labelCount / 2u, labelCount - 1u}) {
std::array<void*, 3> labels{storage.get(), nullptr, storage.get()};
check(sceAgcGetSemaphoreLabel(index, &labels[1]) == 0, "a semaphore label lookup failed");
check(labels[1] == memory() + index * labelBytes, "incorrect semaphore label address");
check(labels[0] == storage.get() && labels[2] == storage.get(), "a semaphore label lookup wrote past its output");
}
}
void testLabelRejections() {
for (const std::uint32_t index : {labelCount, labelCount + 1u, std::numeric_limits<std::uint32_t>::max()}) {
void* label = storage.get();
expectFailure([&] { sceAgcGetSemaphoreLabel(index, &label); });
check(label == storage.get(), "a rejected semaphore label changed the output");
}
expectFailure([] { sceAgcGetSemaphoreLabel(0, nullptr); });
}
void testRepeatedRegistration() {
std::fill(memory(), memory() + memoryBytes, std::uint8_t{0x3c});
const auto before = snapshot();
expectFailure([] { sceAgcSetAmmSemaphoreMemory(memory(), memoryBytes); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(storage.get(), blockBytes); });
expectFailure([] { sceAgcSetAmmSemaphoreMemory(nullptr, 0); });
checkUnchanged(before, "a repeated registration changed memory");
void* label = nullptr;
check(sceAgcGetSemaphoreLabel(labelCount - 1u, &label) == 0 && label == memory() + memoryBytes - labelBytes, "a repeated registration replaced the semaphore memory");
}
}
int main() {
try {
storage.reset(static_cast<std::uint8_t*>(::operator new(storageBytes, std::align_val_t{blockBytes})));
fillStorage();
testUnregistered();
testRegistrationRejections();
testUnsettledClearing();
testRegistration();
testLabels();
testLabelRejections();
testRepeatedRegistration();
storage.reset();
LibcRunShutdown_nid_postfix();
std::puts("AGC semaphore tests passed");
return 0;
} catch (const std::exception& error) {
std::fprintf(stderr, "%s\n", error.what());
try { LibcRunShutdown_nid_postfix(); }
catch (const std::exception& shutdown) { std::fprintf(stderr, "shutdown: %s\n", shutdown.what()); }
return 1;
}
}

View File

@ -7,6 +7,7 @@
#include <array>
#include <span>
#include <stdexcept>
#include <string>
#include <iostream>
extern "C" int APS5_VABI sceAgcLinkShaders(ShaderRegister*, ShaderRegister*, const void*, const Shader*, const Shader*, std::uint32_t);
@ -48,6 +49,30 @@ void OptionalGeometryOutputRegister(ShaderSpecialRegs& special, const Shader& ve
special.vgt_shader_stages_en.value = 0;
special.vgt_gs_out_prim_type = {VGT_GS_OUT_PRIM_TYPE, 2};
}
void HandBuiltUserVgprRegister() {
using namespace ShaderRegs;
ShaderSpecialRegs handBuilt{};
handBuilt.ge_cntl = {GE_CNTL, 0x100u};
handBuilt.vgt_shader_stages_en = {VGT_SHADER_STAGES_EN, 0x2412010u};
handBuilt.vgt_gs_out_prim_type = {0x2ceu, 2};
handBuilt.ge_user_vgpr_en = {GE_USER_VGPR1, 0};
Shader preRaster{};
preRaster.type = static_cast<std::uint8_t>(ShaderBinaryType::Gs);
preRaster.specials = &handBuilt;
std::array<ShaderRegister, 2> context{};
std::array<ShaderRegister, 3> primitive{};
preparations = 0;
Require(sceAgcCreatePrimState(context.data(), primitive.data(), nullptr, &preRaster, 4) == 0);
Require(context[0].offset == VGT_SHADER_STAGES_EN && context[0].value == 0x2412010u && context[1].offset == VGT_GS_OUT_PRIM_TYPE && context[1].value == static_cast<std::uint32_t>(GsOutputPrimitiveType::Triangles));
Require(primitive[0].offset == GE_CNTL && primitive[0].value == 0x100u && primitive[1].offset == GE_USER_VGPR1 && primitive[1].value == 0 && primitive[2].offset == VGT_PRIMITIVE_TYPE && primitive[2].value == 4);
Require(preparations == 1 && stageCount == 1 && preparedPrimitive[1].offset == GE_USER_VGPR1 && preparedPrimitive[1].value == 0);
handBuilt.ge_cntl.offset = 0x25cu;
std::string message;
try { static_cast<void>(sceAgcCreatePrimState(context.data(), primitive.data(), nullptr, &preRaster, 4)); }
catch (const std::runtime_error& error) { message = error.what(); }
Require(message.find("GE_CNTL") != std::string::npos && message.find("0x25c") != std::string::npos);
}
}
extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
@ -168,6 +193,7 @@ int main() {
Require(sceAgcCreateInterpolantMapping_0100(interpolants.data(), &vertex, nullptr) == 0);
Require(preparations == 0 && mappings == 0 && links == 0);
OptionalGeometryOutputRegister(special, vertex, hull);
HandBuiltUserVgprRegister();
std::cout << "shader helper preparation tests passed\n";
} catch (const std::exception& error) {
std::cerr << error.what() << '\n';

View File

@ -111,6 +111,7 @@ add_custom_command(
set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Unimplemented.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/ComputePipelineCapture.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/DisplayBuffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/DisplayFormat.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/DeviceAccess.cpp
@ -161,6 +162,8 @@ set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Synchronization/WaitMemory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Synchronization/WaitStatistics.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/WorkerSampler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/PerformanceTrace.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/TraceFile.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/ThreadPriority.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/GuestMemory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/ShaderMemory.cpp
@ -173,6 +176,7 @@ set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/PresentationScaler.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Draw.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/DepthSurface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/MultisampleTarget.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/ColorTargetLayout.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/ColorTargetTransfer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Graphics/src/Pipeline.cpp
@ -217,9 +221,22 @@ set(agcDriverSources
${CMAKE_CURRENT_SOURCE_DIR}/Submit/src/Dcb.cpp
)
find_package(Git QUIET)
set(agcTraceRevisionHeader ${agcTextureDetileGeneratedRoot}/prx/libSceAgcDriver/TraceRevision.hpp)
add_custom_target(agc_trace_revision
COMMAND ${CMAKE_COMMAND} -DGIT=${GIT_EXECUTABLE} -DSOURCE=${CMAKE_SOURCE_DIR} -DOUTPUT=${agcTraceRevisionHeader} -P ${CMAKE_CURRENT_SOURCE_DIR}/trace_revision.cmake
BYPRODUCTS ${agcTraceRevisionHeader}
VERBATIM
)
set(agcZlibIncludeDir ${CMAKE_SOURCE_DIR}/3rdparty/libjpeg-turbo/src/spng/zlib)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/TraceFile.cpp PROPERTIES
INCLUDE_DIRECTORIES ${agcZlibIncludeDir}
)
add_library(libSceAgcDriver SHARED EXCLUDE_FROM_ALL
${agcDriverSources}
)
add_dependencies(libSceAgcDriver agc_trace_revision)
target_include_directories(libSceAgcDriver PRIVATE
${LIBS_INCLUDE_DIR}
@ -245,8 +262,12 @@ target_link_libraries(libSceAgcDriver PRIVATE
SDL2-static
libc
libkernel
jpeg_turbo
${CMAKE_DL_LIBS}
)
if(APS5_ENABLE_TRACY)
target_link_libraries(libSceAgcDriver PUBLIC TracyClient)
endif()
set_target_properties(libSceAgcDriver PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(libSceAgcDriver)
@ -272,16 +293,61 @@ if(BUILD_TESTING)
set_tests_properties(agc_profile_output PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(agc_stack_walk_tests tests/StackWalk.cpp)
target_include_directories(agc_stack_walk_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_stack_walk_tests PRIVATE libc)
configure_windows_unwind(agc_stack_walk_tests)
add_test(NAME agc_stack_walk COMMAND agc_stack_walk_tests)
set_tests_properties(agc_stack_walk PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(agc_stack_walk PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(agc_performance_trace_tests tests/PerformanceTrace.cpp Execution/src/TraceFile.cpp)
target_include_directories(agc_performance_trace_tests PRIVATE ${LIBS_INCLUDE_DIR} ${agcZlibIncludeDir})
target_link_libraries(agc_performance_trace_tests PRIVATE libc jpeg_turbo)
configure_windows_unwind(agc_performance_trace_tests)
add_test(NAME agc_performance_trace COMMAND agc_performance_trace_tests)
set_tests_properties(agc_performance_trace PROPERTIES TIMEOUT 20)
if(WIN32)
set_tests_properties(agc_performance_trace PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
if(WIN32)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/GuestMemory.cpp PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0602)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/GuestMemory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Execution/src/Driver/Lifecycle.cpp PROPERTIES COMPILE_DEFINITIONS _WIN32_WINNT=0x0602)
endif()
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
endforeach()
add_test_executable(agc_driver_register_key_tests EXCLUDE_FROM_ALL tests/RegisterKey.cpp)
target_include_directories(agc_driver_register_key_tests PRIVATE
${LIBS_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}
)
target_link_libraries(agc_driver_register_key_tests PRIVATE
libSceAgcDriver
libc
libkernel
)
set_target_properties(agc_driver_register_key_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_register_key_tests)
if(BUILD_TESTING)
set_target_properties(agc_driver_register_key_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_driver_register_key COMMAND agc_driver_register_key_tests)
set_tests_properties(agc_driver_register_key PROPERTIES TIMEOUT 30 ENVIRONMENT "APS5_VERIFY_REGISTER_KEYS=1")
if(WIN32)
set_tests_properties(agc_driver_register_key PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_driver_tests EXCLUDE_FROM_ALL
tests/Submit.cpp
)
@ -313,8 +379,10 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_wait_shutdown PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_GPU_WAIT_TIMEOUT_MS=20"
TIMEOUT 10 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_driver_resolution_shutdown COMMAND agc_driver_wait_shutdown_tests --resolution-shutdown)
set_tests_properties(agc_driver_resolution_shutdown PROPERTIES TIMEOUT 10 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_wait_shutdown PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_wait_shutdown agc_driver_resolution_shutdown PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
@ -390,6 +458,15 @@ if(BUILD_TESTING)
add_test(NAME agc_fragment_barycentric COMMAND agc_fragment_barycentric_tests)
endif()
add_test_executable(agc_pipeline_link_optimization_tests EXCLUDE_FROM_ALL tests/PipelineLinkOptimization.cpp)
target_include_directories(agc_pipeline_link_optimization_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
set_target_properties(agc_pipeline_link_optimization_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_pipeline_link_optimization_tests)
if(BUILD_TESTING)
set_target_properties(agc_pipeline_link_optimization_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_pipeline_link_optimization COMMAND agc_pipeline_link_optimization_tests)
endif()
add_test_executable(agc_subgroup_clock_tests EXCLUDE_FROM_ALL
tests/SubgroupClock.cpp
Execution/src/SubgroupClock.cpp
@ -430,6 +507,10 @@ set_target_properties(agc_spirv_narrow_constant_store_tests PROPERTIES CXX_EXTEN
configure_windows_unwind(agc_spirv_narrow_constant_store_tests)
if(ANYPS5_ENABLE_SPIRV_TOOLS)
target_link_libraries(agc_spirv_narrow_constant_store_tests PRIVATE SPIRV-Tools-static)
add_test_executable(agc_spirv13_image_tests EXCLUDE_FROM_ALL tests/Spirv13Image.cpp)
target_link_libraries(agc_spirv13_image_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_spirv13_image_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_spirv13_image_tests)
add_test_executable(agc_spirv_optimizer_tests EXCLUDE_FROM_ALL tests/SpirvOptimizer.cpp)
target_link_libraries(agc_spirv_optimizer_tests PRIVATE shader_recompiler_agc_driver libc SPIRV-Tools-static)
set_target_properties(agc_spirv_optimizer_tests PROPERTIES CXX_EXTENSIONS OFF)
@ -449,6 +530,19 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_spirv_branch_targets_tests EXCLUDE_FROM_ALL tests/SpirvBranchTargets.cpp)
target_link_libraries(agc_spirv_branch_targets_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_spirv_branch_targets_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_spirv_branch_targets_tests)
if(BUILD_TESTING)
set_target_properties(agc_spirv_branch_targets_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_spirv_branch_targets COMMAND agc_spirv_branch_targets_tests)
set_tests_properties(agc_spirv_branch_targets PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1")
if(WIN32)
set_tests_properties(agc_spirv_branch_targets PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_spirv_lds_atomic_lock_tests EXCLUDE_FROM_ALL tests/SpirvLdsAtomicLock.cpp)
target_link_libraries(agc_spirv_lds_atomic_lock_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_spirv_lds_atomic_lock_tests PROPERTIES CXX_EXTENSIONS OFF)
@ -472,11 +566,26 @@ target_link_libraries(agc_masked_select_tests PRIVATE shader_recompiler_agc_driv
set_target_properties(agc_masked_select_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_masked_select_tests)
add_test_executable(agc_ir_reclaim_tests EXCLUDE_FROM_ALL tests/IrReclaim.cpp)
target_link_libraries(agc_ir_reclaim_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_ir_reclaim_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_ir_reclaim_tests)
add_test_executable(agc_vector_window_tests EXCLUDE_FROM_ALL tests/VectorWindow.cpp)
target_link_libraries(agc_vector_window_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_vector_window_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_vector_window_tests)
add_test_executable(agc_constant_bit_test_tests EXCLUDE_FROM_ALL tests/ConstantBitTest.cpp)
target_link_libraries(agc_constant_bit_test_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_constant_bit_test_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_constant_bit_test_tests)
add_test_executable(agc_integer_narrowing_tests EXCLUDE_FROM_ALL tests/IntegerNarrowing.cpp)
target_link_libraries(agc_integer_narrowing_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_integer_narrowing_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_integer_narrowing_tests)
add_test_executable(agc_scalar_select_mask_tests EXCLUDE_FROM_ALL tests/ScalarSelectMask.cpp)
target_link_libraries(agc_scalar_select_mask_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_scalar_select_mask_tests PROPERTIES CXX_EXTENSIONS OFF)
@ -526,6 +635,19 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_vop3_interp_decode_tests EXCLUDE_FROM_ALL tests/Vop3InterpDecode.cpp)
target_link_libraries(agc_vop3_interp_decode_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_vop3_interp_decode_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_vop3_interp_decode_tests)
if(BUILD_TESTING)
set_target_properties(agc_vop3_interp_decode_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_vop3_interp_decode COMMAND agc_vop3_interp_decode_tests)
if(WIN32)
set_tests_properties(agc_vop3_interp_decode PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_pass_hazards_tests EXCLUDE_FROM_ALL tests/PassHazards.cpp Graphics/src/PassHazards.cpp)
target_include_directories(agc_pass_hazards_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_pass_hazards_tests PRIVATE libc)
@ -560,6 +682,20 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_device_memory_fallback_tests EXCLUDE_FROM_ALL tests/DeviceMemoryFallback.cpp)
target_include_directories(agc_device_memory_fallback_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/core/shader/recompiler ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_device_memory_fallback_tests PRIVATE libSceAgcDriver libc)
set_target_properties(agc_device_memory_fallback_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_device_memory_fallback_tests)
if(BUILD_TESTING)
set_target_properties(agc_device_memory_fallback_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_device_memory_fallback COMMAND agc_device_memory_fallback_tests)
if(WIN32)
set_tests_properties(agc_device_memory_fallback PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_display_buffer_dcc_tests EXCLUDE_FROM_ALL tests/DisplayBufferDcc.cpp)
target_include_directories(agc_display_buffer_dcc_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/core/shader/recompiler ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_display_buffer_dcc_tests PRIVATE libSceAgcDriver libc)
@ -584,6 +720,24 @@ target_link_libraries(agc_formatted_buffer_once_tests PRIVATE shader_recompiler_
set_target_properties(agc_formatted_buffer_once_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_formatted_buffer_once_tests)
add_test_executable(agc_rect_list_position_invariant_tests EXCLUDE_FROM_ALL tests/RectListPositionInvariant.cpp)
target_link_libraries(agc_rect_list_position_invariant_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_rect_list_position_invariant_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_rect_list_position_invariant_tests)
if(BUILD_TESTING)
set_target_properties(agc_rect_list_position_invariant_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_rect_list_position_invariant COMMAND agc_rect_list_position_invariant_tests)
if(WIN32)
set_tests_properties(agc_rect_list_position_invariant PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_formatted_buffer_byte_stores_tests EXCLUDE_FROM_ALL tests/FormattedBufferByteStores.cpp)
target_link_libraries(agc_formatted_buffer_byte_stores_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_formatted_buffer_byte_stores_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_formatted_buffer_byte_stores_tests)
add_test_executable(agc_shader_disk_cache_tests EXCLUDE_FROM_ALL tests/ShaderDiskCache.cpp)
target_include_directories(agc_shader_disk_cache_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(agc_shader_disk_cache_tests PRIVATE shader_recompiler_agc_driver libc)
@ -622,6 +776,7 @@ add_test_executable(agc_driver_flip_tests EXCLUDE_FROM_ALL
target_include_directories(agc_driver_flip_tests PRIVATE
${LIBS_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/3rdparty/Vulkan-Headers/include
)
target_link_libraries(agc_driver_flip_tests PRIVATE
@ -712,6 +867,19 @@ target_link_libraries(agc_driver_cmask_fast_clear_tests PRIVATE
set_target_properties(agc_driver_cmask_fast_clear_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_cmask_fast_clear_tests)
set(agcBufferUpdateSource ${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/BufferUpdate.comp)
set(agcBufferUpdateSpv ${agcTestShaderGeneratedRoot}/BufferUpdate.spv)
set(agcBufferUpdateHeader ${agcTestShaderGeneratedRoot}/BufferUpdate_spv.h)
add_custom_command(
OUTPUT "${agcBufferUpdateHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -o "${agcBufferUpdateSpv}" "${agcBufferUpdateSource}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${agcBufferUpdateSpv} -DOUTPUT=${agcBufferUpdateHeader}
-DSYMBOL=BUFFER_UPDATE_SPV -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${agcBufferUpdateSource}" glslang-standalone
VERBATIM
)
add_test_executable(agc_driver_recorder_tests EXCLUDE_FROM_ALL
tests/Recorder.cpp
tests/ResidentPresent.cpp
@ -719,6 +887,7 @@ add_test_executable(agc_driver_recorder_tests EXCLUDE_FROM_ALL
${agcSampleLodShaderHeader}
${agcSampleArrayShaderHeader}
${agcCoverShaderHeaders}
${agcBufferUpdateHeader}
)
target_include_directories(agc_driver_recorder_tests PRIVATE
@ -762,17 +931,62 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_storage_baseline_settled COMMAND agc_driver_recorder_tests --settled-baseline-only)
add_test(NAME agc_storage_integer_clear COMMAND agc_driver_recorder_tests --integer-clear-only)
set_tests_properties(agc_storage_integer_clear PROPERTIES SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_storage_integer_clear PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_storage_upload_lifetime COMMAND agc_driver_recorder_tests --storage-upload-lifetime-only)
add_test(NAME agc_storage_upload_failure COMMAND agc_driver_recorder_tests --storage-upload-failure-only)
set_tests_properties(agc_storage_upload_lifetime agc_storage_upload_failure PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_storage_upload_lifetime agc_storage_upload_failure PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_storage_baseline_settled PROPERTIES SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_storage_baseline_settled PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_storage_written_buffer_order COMMAND agc_driver_recorder_tests --written-buffer-order-only)
set_tests_properties(agc_storage_written_buffer_order PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_storage_written_buffer_order PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_copied_direct_ordering COMMAND agc_driver_recorder_tests --copied-direct-only)
set_tests_properties(agc_copied_direct_ordering PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_copied_direct_ordering PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_host_import_unmap COMMAND agc_driver_recorder_tests --host-import-unmap-only)
set_tests_properties(agc_host_import_unmap PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_parked_import_unmap COMMAND agc_driver_recorder_tests --parked-import-unmap-only)
set_tests_properties(agc_parked_import_unmap PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000")
add_test(NAME agc_host_import_retirement COMMAND agc_driver_recorder_tests --host-import-retirement-only)
set_tests_properties(agc_host_import_retirement PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
add_test(NAME agc_unit_shadow COMMAND agc_driver_recorder_tests --unit-shadow-only)
set_tests_properties(agc_unit_shadow PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30 ENVIRONMENT "APS5_PIN_WAIT_MS=3000")
if(WIN32)
set_tests_properties(agc_host_import_unmap agc_unit_shadow PROPERTIES ENVIRONMENT_MODIFICATION
add_test(NAME agc_storage_cpu_snapshot COMMAND agc_driver_recorder_tests --storage-cpu-snapshot-only)
set_tests_properties(agc_storage_cpu_snapshot PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 30 SKIP_RETURN_CODE 77
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_descriptor_pool_recycling COMMAND agc_driver_recorder_tests --descriptor-pool-only)
set_tests_properties(agc_descriptor_pool_recycling PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_draw_snapshot_cap COMMAND agc_driver_recorder_tests --draw-snapshot-cap-only)
set_tests_properties(agc_draw_snapshot_cap PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_multisample_guest_access COMMAND agc_driver_recorder_tests --multisample-only)
set_tests_properties(agc_multisample_guest_access PROPERTIES SKIP_RETURN_CODE 77)
add_test(NAME agc_draw_inputs_in_place COMMAND agc_driver_recorder_tests --draw-inputs-in-place-only)
set_tests_properties(agc_draw_inputs_in_place PROPERTIES SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_host_import_unmap agc_parked_import_unmap agc_host_import_retirement agc_unit_shadow agc_descriptor_pool_recycling agc_draw_snapshot_cap agc_multisample_guest_access agc_draw_inputs_in_place PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
add_test(NAME agc_host_import_second_mapping COMMAND agc_driver_recorder_tests --host-import-second-mapping-only)
set_tests_properties(agc_host_import_second_mapping PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
@ -791,6 +1005,20 @@ if(BUILD_TESTING)
endif()
endif()
add_test_executable(agc_host_import_retire_tests EXCLUDE_FROM_ALL tests/HostImportRetireHold.cpp)
target_include_directories(agc_host_import_retire_tests PRIVATE ${LIBS_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/core/shader/recompiler ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_host_import_retire_tests PRIVATE libSceAgcDriver libc)
set_target_properties(agc_host_import_retire_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_host_import_retire_tests)
if(BUILD_TESTING)
set_target_properties(agc_host_import_retire_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_host_import_retire COMMAND agc_host_import_retire_tests)
if(WIN32)
set_tests_properties(agc_host_import_retire PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_driver_pm4_tests EXCLUDE_FROM_ALL
tests/Pm4.cpp
)
@ -820,46 +1048,11 @@ add_test_executable(agc_driver_graphics_tests EXCLUDE_FROM_ALL
tests/GuestSamplerResource.cpp
tests/TextureDetiler.cpp
tests/DepthSurfaceReuse.cpp
Graphics/src/Draw.cpp
Graphics/src/DepthSurface.cpp
Graphics/src/Pipeline.cpp
Graphics/src/PipelineLibrary.cpp
Graphics/src/State.cpp
Graphics/src/ColorTargetLayout.cpp
Graphics/src/ColorTargetTransfer.cpp
Graphics/src/Resources.cpp
Graphics/src/PlaceholderImages.cpp
Graphics/src/ShaderResources.cpp
Graphics/src/ShaderAddressResources.cpp
Graphics/src/GuestBufferMemory.cpp
Graphics/src/BdaResources.cpp
Graphics/src/BufferAddress.cpp
Graphics/src/ShaderValidation.cpp
Graphics/src/GuestTextureResource.cpp
Graphics/src/GuestSamplerResource.cpp
Graphics/src/TextureFormat.cpp
Graphics/src/DccMetadata.cpp
Graphics/src/TextureTiling.cpp
Graphics/src/TextureDetiler.cpp
Graphics/src/Texture.cpp
Graphics/src/PassHazards.cpp
Graphics/src/UnitShadow.cpp
Graphics/src/Sampler.cpp
Graphics/src/Recorder.cpp
Graphics/src/ParallelCompare.cpp
Graphics/src/ShaderInputState.cpp
${agcTextureDetileShaderHeader}
${agcCmaskClearShaderHeader}
${agcMeshArgumentsShaderHeader}
${agcTextureDetileImageShaderHeader}
${agcSampleCounterShaderHeader}
${agcSampleDumpsShaderHeader}
Execution/src/GuestMemory.cpp
Execution/src/Pm4.cpp
)
target_include_directories(agc_driver_graphics_tests PRIVATE
${LIBS_INCLUDE_DIR}
$<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>
${agcTextureDetileGeneratedRoot}
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${CMAKE_SOURCE_DIR}/core/shader/recompiler/IntermediateRepresentation/include
@ -870,7 +1063,7 @@ target_include_directories(agc_driver_graphics_tests PRIVATE
)
target_link_libraries(agc_driver_graphics_tests PRIVATE
shader_recompiler_agc_driver
libSceAgcDriver
libc
libkernel
)
@ -878,44 +1071,71 @@ target_link_libraries(agc_driver_graphics_tests PRIVATE
set_target_properties(agc_driver_graphics_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_graphics_tests)
foreach(agcVertexRangeShader IN ITEMS VertexRange.vert VertexRangeProducer.frag)
string(REPLACE "." "_" agcVertexRangeName "${agcVertexRangeShader}")
string(TOUPPER "${agcVertexRangeName}_SPV" agcVertexRangeSymbol)
set(agcVertexRangeSpv ${agcTestShaderGeneratedRoot}/${agcVertexRangeName}.spv)
set(agcVertexRangeHeader ${agcTestShaderGeneratedRoot}/${agcVertexRangeName}_spv.h)
add_custom_command(
OUTPUT "${agcVertexRangeHeader}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${agcTestShaderGeneratedRoot}"
COMMAND "$<TARGET_FILE:glslang-standalone>" -V --target-env vulkan1.1 -o "${agcVertexRangeSpv}" "${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/${agcVertexRangeShader}"
COMMAND ${CMAKE_COMMAND} -DINPUT=${agcVertexRangeSpv} -DOUTPUT=${agcVertexRangeHeader}
-DSYMBOL=${agcVertexRangeSymbol} -P "${CMAKE_CURRENT_SOURCE_DIR}/embed_spirv.cmake"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/${agcVertexRangeShader}" glslang-standalone
VERBATIM
)
list(APPEND agcVertexRangeHeaders ${agcVertexRangeHeader})
endforeach()
target_sources(agc_driver_recorder_tests PRIVATE ${agcVertexRangeHeaders})
if(BUILD_TESTING)
add_test(NAME agc_driver_vertex_target_range_copied COMMAND agc_driver_recorder_tests --vertex-target-range-copied)
add_test(NAME agc_driver_vertex_target_range_imported COMMAND agc_driver_recorder_tests --vertex-target-range-imported)
set_tests_properties(agc_driver_vertex_target_range_copied agc_driver_vertex_target_range_imported PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_driver_vertex_target_range_copied agc_driver_vertex_target_range_imported PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_driver_bda_device_tests EXCLUDE_FROM_ALL
tests/BdaDevice.cpp
tests/BdaShader.cpp
tests/BdaExecution.cpp
tests/BdaContracts.cpp
Execution/src/BdaFeatures.cpp
Graphics/src/Resources.cpp
Graphics/src/PlaceholderImages.cpp
Graphics/src/BufferAddress.cpp
)
target_include_directories(agc_driver_bda_device_tests PRIVATE
${LIBS_INCLUDE_DIR}
$<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}
)
target_link_libraries(agc_driver_bda_device_tests PRIVATE SDL2-static libc shader_recompiler_agc_driver)
target_link_libraries(agc_driver_bda_device_tests PRIVATE libSceAgcDriver SDL2-static libc)
set_target_properties(agc_driver_bda_device_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_bda_device_tests)
add_test_executable(agc_driver_mesh_tests EXCLUDE_FROM_ALL
tests/Mesh.cpp
${agcDriverSources}
)
add_test_executable(agc_driver_mesh_tests EXCLUDE_FROM_ALL tests/Mesh.cpp)
target_include_directories(agc_driver_mesh_tests PRIVATE
${LIBS_INCLUDE_DIR}
${agcTextureDetileGeneratedRoot}
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}
${ANYPS5_SPIRV_HEADERS_INCLUDE_DIR}
)
target_link_libraries(agc_driver_mesh_tests PRIVATE shader_recompiler_agc_driver SDL2-static libc libkernel ${CMAKE_DL_LIBS})
target_link_libraries(agc_driver_mesh_tests PRIVATE libSceAgcDriver SDL2-static libc)
set_target_properties(agc_driver_mesh_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_mesh_tests)
set(ANYPS5_AGC_EXECUTION_TESTS "" CACHE STRING "tests/execution sources to build, by file name without extension; the others build a stub that reports a skip. Empty builds all")
file(GLOB agcExecutionTestSources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tests/execution/*.cpp)
set(agcExecutionTests)
foreach(agcExecutionTestSource IN LISTS agcExecutionTestSources)
get_filename_component(agcExecutionTestName ${agcExecutionTestSource} NAME_WE)
if(ANYPS5_AGC_EXECUTION_TESTS AND NOT agcExecutionTestName IN_LIST ANYPS5_AGC_EXECUTION_TESTS)
set(agcExecutionTestSource ${CMAKE_CURRENT_SOURCE_DIR}/tests/ExecutionTestNotBuilt.cpp)
endif()
string(REGEX REPLACE "([a-z0-9])([A-Z])" "\\1_\\2" agcExecutionTestName "${agcExecutionTestName}")
string(TOLOWER "agc_driver_${agcExecutionTestName}_tests" agcExecutionTest)
add_test_executable(${agcExecutionTest} EXCLUDE_FROM_ALL ${agcExecutionTestSource})
@ -930,6 +1150,13 @@ foreach(agcExecutionTestSource IN LISTS agcExecutionTestSources)
configure_windows_unwind(${agcExecutionTest})
list(APPEND agcExecutionTests ${agcExecutionTest})
endforeach()
foreach(agcExecutionTestName IN LISTS ANYPS5_AGC_EXECUTION_TESTS)
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/execution/${agcExecutionTestName}.cpp)
message(FATAL_ERROR "ANYPS5_AGC_EXECUTION_TESTS names ${agcExecutionTestName}, which has no source in tests/execution")
endif()
endforeach()
target_link_libraries(agc_driver_gpu_map_on_fiber_tests PRIVATE libSceFiber)
target_include_directories(agc_driver_alpha_to_coverage_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
@ -939,6 +1166,8 @@ target_include_directories(agc_driver_swap_alt_blend_tests PRIVATE $<TARGET_PROP
target_include_directories(agc_driver_packed_unorm_color_target_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
target_include_directories(agc_driver_color1555_target_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
foreach(stage IN ITEMS vert frag)
set(agcProvokingShaderSource ${CMAKE_CURRENT_SOURCE_DIR}/tests/shaders/ProvokingVertex.${stage})
set(agcProvokingShaderSpv ${agcTestShaderGeneratedRoot}/ProvokingVertex_${stage}.spv)
@ -959,6 +1188,8 @@ target_include_directories(agc_driver_provoking_vertex_tests PRIVATE ${agcTestSh
target_include_directories(agc_driver_prepared_exports_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
target_include_directories(agc_driver_buffer_descriptor_runtime_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
target_include_directories(agc_driver_prepared_publication_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
target_include_directories(agc_driver_prepared_graphics_tests PRIVATE $<TARGET_PROPERTY:shader_recompiler_agc_driver,INTERFACE_INCLUDE_DIRECTORIES>)
@ -972,6 +1203,12 @@ target_include_directories(agc_driver_prepared_shaders_tests PRIVATE
if(BUILD_TESTING)
set_target_properties(agc_shader_mode_register_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_driver_readonly_resummarize COMMAND agc_driver_tests --readonly-resummarize)
set_tests_properties(agc_driver_readonly_resummarize PROPERTIES TIMEOUT 10)
if(WIN32)
set_tests_properties(agc_driver_readonly_resummarize PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_shader_mode_register COMMAND agc_shader_mode_register_tests)
set_tests_properties(agc_shader_mode_register PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 20)
if(WIN32)
@ -979,15 +1216,17 @@ if(BUILD_TESTING)
endif()
add_test(NAME agc_shader_bindless COMMAND agc_shader_memory_tests --bindless)
set_tests_properties(agc_shader_bindless PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_shader_srt_read_patching COMMAND agc_shader_memory_tests --srt-read-patching)
set_tests_properties(agc_shader_srt_read_patching PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_shader_frontend_pair COMMAND agc_shader_memory_tests --frontend-pair)
add_test(NAME agc_shader_frontend_pair_off COMMAND agc_shader_memory_tests --frontend-pair)
set_tests_properties(agc_shader_frontend_pair PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
set_tests_properties(agc_shader_frontend_pair_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_FRONTEND_PAIR=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_shader_bindless agc_shader_frontend_pair agc_shader_frontend_pair_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_shader_bindless agc_shader_srt_read_patching agc_shader_frontend_pair agc_shader_frontend_pair_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
foreach(agcTest IN ITEMS agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_constant_bit_test_tests agc_scalar_select_mask_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_pass_hazards_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_placeholder_images_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests agc_formatted_buffer_once_tests ${agcExecutionTests})
if(agcTest STREQUAL "agc_driver_constant_store_tests" OR agcTest STREQUAL "agc_driver_flat_apertures_tests")
foreach(agcTest IN ITEMS agc_shared_write_tests agc_driver_tests agc_shader_memory_tests agc_structurizer_tests agc_spirv_module_tests agc_dpp_write_tests agc_masked_select_tests agc_ir_reclaim_tests agc_vector_window_tests agc_constant_bit_test_tests agc_integer_narrowing_tests agc_scalar_select_mask_tests agc_vop1_control_decode_tests agc_validate_program_tests agc_pass_hazards_tests agc_shader_disk_cache_tests agc_shader_device_profile_tests agc_placeholder_images_tests agc_shader_control_flow_tests agc_driver_flip_tests agc_driver_pm4_tests agc_driver_graphics_tests agc_driver_bda_device_tests agc_driver_mesh_tests agc_driver_cmask_fast_clear_tests agc_formatted_buffer_once_tests agc_formatted_buffer_byte_stores_tests ${agcExecutionTests})
if(agcTest STREQUAL "agc_driver_constant_store_tests" OR agcTest STREQUAL "agc_driver_flat_apertures_tests" OR agcTest STREQUAL "agc_driver_pipeline_optimized_readback_tests")
continue()
endif()
string(REGEX REPLACE "_tests$" "" agcTestName "${agcTest}")
@ -999,15 +1238,83 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
add_test(NAME agc_driver_partially_resident_pending_stores COMMAND agc_driver_partially_resident_snapshot_tests --pending-stores)
set_tests_properties(agc_driver_partially_resident_pending_stores PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_driver_partially_resident_pending_stores PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_sparse_buffer_holes COMMAND agc_driver_graphics_tests --sparse-buffer-holes)
set_tests_properties(agc_sparse_buffer_holes PROPERTIES TIMEOUT 30)
set_tests_properties(agc_driver_sparse_buffer_holes PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_sparse_buffer_holes PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_emulated_color_compare_srgb_decode COMMAND agc_driver_emulated_color_compare_tests)
set_tests_properties(agc_driver_emulated_color_compare_srgb_decode PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_SRGB_SHADER_DECODE=1"
SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
set_tests_properties(agc_driver_emulated_color_compare agc_driver_emulated_color_compare_srgb_decode PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_driver_emulated_color_compare_srgb_decode PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_shared_write_partial_collect COMMAND agc_shared_write_tests --partial-collect)
set_tests_properties(agc_shared_write_partial_collect PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 30)
if(WIN32)
add_test(NAME agc_shared_write_partial_collect_two_pass COMMAND agc_shared_write_tests --partial-collect)
set_tests_properties(agc_shared_write_partial_collect_two_pass PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_SINGLE_PASS_COLLECT=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 30)
set_tests_properties(agc_shared_write_partial_collect agc_shared_write_partial_collect_two_pass PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_target_properties(agc_driver_pipeline_optimized_readback_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
foreach(pipelineReadbackCase IN ITEMS d32 d16 color32 color64)
foreach(pipelineReadbackMode IN ITEMS default fast)
set(pipelineReadbackTest agc_driver_pipeline_optimized_readback_${pipelineReadbackCase})
set(pipelineReadbackEnvironment "ANYPS5_NO_SHADER_CACHE=1")
if(pipelineReadbackMode STREQUAL "fast")
string(APPEND pipelineReadbackTest "_fast")
list(APPEND pipelineReadbackEnvironment "APS5_NO_PIPELINE_LTO=1")
endif()
add_test(NAME ${pipelineReadbackTest} COMMAND agc_driver_pipeline_optimized_readback_tests ${pipelineReadbackCase})
set_tests_properties(${pipelineReadbackTest} PROPERTIES ENVIRONMENT "${pipelineReadbackEnvironment}" SKIP_RETURN_CODE 77 TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(${pipelineReadbackTest} PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endforeach()
endforeach()
add_test(NAME agc_vector_window_stage_inputs COMMAND agc_vector_window_tests --stage-inputs-only)
set_tests_properties(agc_vector_window_stage_inputs PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
set_tests_properties(agc_vector_window_stage_inputs agc_driver_relative_stage_inputs PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_vector_window_stage_inputs PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_layered_volume_vertex COMMAND agc_driver_layered_volume_draw_tests --vertex-only)
add_test(NAME agc_driver_layered_volume_vertex_no_gpl COMMAND agc_driver_layered_volume_draw_tests --vertex-only)
set_tests_properties(agc_driver_layered_volume_vertex agc_driver_layered_volume_vertex_no_gpl PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
set_property(TEST agc_driver_layered_volume_vertex_no_gpl APPEND PROPERTY ENVIRONMENT "APS5_NO_GPL=1")
if(WIN32)
set_tests_properties(agc_driver_layered_volume_vertex agc_driver_layered_volume_vertex_no_gpl PROPERTIES
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_f64_denorm_preserve_spirv COMMAND agc_driver_f64_fma_subnormals_tests --verify-spv-denorm-mode)
set_tests_properties(agc_driver_f64_denorm_preserve_spirv PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_f64_denorm_preserve_spirv PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_f16_to_f32_unpack_spirv COMMAND agc_driver_f16_to_f32_unpack_tests --verify-spirv)
set_tests_properties(agc_driver_f16_to_f32_unpack_spirv PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_f16_to_f32_unpack_spirv PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_structurizer_intersection_off COMMAND agc_structurizer_tests)
set_tests_properties(agc_structurizer_intersection_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_DOM_INTERSECTION=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_structurizer_iteration_selection_off COMMAND agc_structurizer_tests)
set_tests_properties(agc_structurizer_iteration_selection_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_ITERATION_SELECTION=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
add_test(NAME agc_shader_control_flow_iteration_selection_off COMMAND agc_shader_control_flow_tests)
set_tests_properties(agc_shader_control_flow_iteration_selection_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_PERF_ITERATION_SELECTION=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_structurizer_intersection_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_structurizer_intersection_off agc_structurizer_iteration_selection_off agc_shader_control_flow_iteration_selection_off PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_gated_dispatch_capture_short_timeout COMMAND agc_driver_gated_dispatch_capture_tests)
set_tests_properties(agc_driver_gated_dispatch_capture_short_timeout PROPERTIES
@ -1018,10 +1325,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_driver_provoking_vertex PROPERTIES TIMEOUT 30)
add_test(NAME agc_driver_uint8x4_color_target_no_resident COMMAND agc_driver_uint8x4_color_target_tests)
set_tests_properties(agc_driver_uint8x4_color_target_no_resident PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_RESIDENT_TARGETS=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_driver_uint8x4_color_target_no_resident PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
set_tests_properties(agc_driver_flat_store PROPERTIES RUN_SERIAL TRUE)
set_target_properties(agc_driver_flat_apertures_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
foreach(flatAperturesPart RANGE 5)
add_test(NAME agc_driver_flat_apertures_${flatAperturesPart} COMMAND agc_driver_flat_apertures_tests --part ${flatAperturesPart}/6)
foreach(flatAperturesPart RANGE 13)
add_test(NAME agc_driver_flat_apertures_${flatAperturesPart} COMMAND agc_driver_flat_apertures_tests --part ${flatAperturesPart}/14)
set_tests_properties(agc_driver_flat_apertures_${flatAperturesPart} PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_flat_apertures_${flatAperturesPart} PROPERTIES ENVIRONMENT_MODIFICATION
@ -1031,14 +1344,29 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_storage_cache_budget PROPERTIES TIMEOUT 30)
add_test(NAME agc_driver_sampled_cache_budget_memory COMMAND agc_driver_sampled_cache_budget_tests memory)
set_tests_properties(agc_driver_sampled_cache_budget_memory PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77)
add_test(NAME agc_driver_resource_cache_storage_admission COMMAND agc_driver_sampled_cache_budget_tests resource-admission)
add_test(NAME agc_driver_resource_cache_oversized_storage_admission COMMAND agc_driver_sampled_cache_budget_tests resource-oversized)
add_test(NAME agc_driver_resource_cache_storage_clear COMMAND agc_driver_sampled_cache_budget_tests resource-clear)
set_tests_properties(agc_driver_resource_cache_storage_admission agc_driver_resource_cache_oversized_storage_admission agc_driver_resource_cache_storage_clear PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_driver_sampled_cache_budget_memory PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_sampled_cache_budget_memory agc_driver_resource_cache_storage_admission agc_driver_resource_cache_oversized_storage_admission agc_driver_resource_cache_storage_clear PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
# The driver draw test again with the draw key off: the draw cache then keys on the decoded vertex
# stage, which must include PA_CL_VS_OUT_CNTL.
add_test(NAME agc_driver_vertex_clip_cull_draw_no_key COMMAND agc_driver_vertex_clip_cull_draw_tests no-key)
set_tests_properties(agc_driver_vertex_clip_cull_draw_no_key PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_DRAW_KEY=1" SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_driver_vertex_clip_cull_draw_no_key PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_pm4_failure COMMAND agc_driver_pm4_tests failure)
set_tests_properties(agc_driver_pm4_failure PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77)
add_test(NAME agc_descriptor_pool_reset_failure COMMAND agc_driver_graphics_tests --descriptor-pool-reset-failure)
add_test(NAME agc_driver_address_space_teardown COMMAND agc_driver_graphics_tests --address-space-teardown)
set_tests_properties(agc_driver_address_space_teardown PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(WIN32)
set_tests_properties(agc_driver_pm4_failure PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_pm4_failure agc_descriptor_pool_reset_failure agc_driver_address_space_teardown PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
if(NOT WIN32)
@ -1046,20 +1374,22 @@ if(BUILD_TESTING)
set_tests_properties(agc_driver_pm4_priority_refused PROPERTIES ENVIRONMENT
"ANYPS5_NO_SHADER_CACHE=1;APS5_THREAD_PRIORITY=high;DBUS_SYSTEM_BUS_ADDRESS=unix:path=/nonexistent/aps5-no-rtkit" SKIP_RETURN_CODE 77)
endif()
add_test(NAME agc_driver_present_mode COMMAND agc_driver_flip_tests present-mode)
add_test(NAME agc_driver_flip_compute COMMAND agc_driver_flip_tests compute)
set_tests_properties(agc_driver_flip_compute PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77)
if(WIN32)
set_tests_properties(agc_driver_flip_compute PROPERTIES ENVIRONMENT_MODIFICATION
set_tests_properties(agc_driver_flip_compute agc_driver_present_mode PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_driver_draw_cache_reuse_off COMMAND agc_driver_draw_cache_reuse_tests)
set_tests_properties(agc_driver_draw_cache_reuse_off PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1;APS5_NO_GPL=1" SKIP_RETURN_CODE 77)
set_tests_properties(agc_driver_draw_cache_reuse agc_driver_draw_cache_reuse_off PROPERTIES FAIL_REGULAR_EXPRESSION "FATAL:")
if(WIN32)
set_tests_properties(agc_driver_draw_cache_reuse_off PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
foreach(agcTest IN ITEMS agc_spirv_narrow_constant_store_tests agc_spirv_optimizer_tests agc_driver_constant_store_tests)
foreach(agcTest IN ITEMS agc_spirv_narrow_constant_store_tests agc_spirv13_image_tests agc_spirv_optimizer_tests agc_driver_constant_store_tests)
if(NOT TARGET ${agcTest})
continue()
endif()
@ -1077,6 +1407,9 @@ if(BUILD_TESTING)
${agcSpirvEnvironment} "$<TARGET_FILE:${agcTest}>")
set_tests_properties(${agcTestName} PROPERTIES
ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" TIMEOUT 60 SKIP_RETURN_CODE 77)
if(agcTest STREQUAL "agc_spirv13_image_tests")
set_tests_properties(${agcTestName} PROPERTIES TIMEOUT 30)
endif()
if(agcTest STREQUAL "agc_driver_constant_store_tests")
set_tests_properties(${agcTestName} PROPERTIES RUN_SERIAL TRUE)
endif()
@ -1086,7 +1419,7 @@ if(BUILD_TESTING)
endif()
endforeach()
endforeach()
foreach(preparedMode IN ITEMS indirect uncached debug debug_failure deferred deferred_undecodable)
foreach(preparedMode IN ITEMS indirect uncached debug debug_failure deferred deferred_undecodable registered_workgroup registered_workgroup_sync)
set(preparedTest "agc_driver_prepared_shaders_${preparedMode}")
if(preparedMode STREQUAL "indirect")
add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --indirect)
@ -1094,6 +1427,8 @@ if(BUILD_TESTING)
add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --deferred)
elseif(preparedMode STREQUAL "deferred_undecodable")
add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --deferred-undecodable)
elseif(preparedMode STREQUAL "registered_workgroup" OR preparedMode STREQUAL "registered_workgroup_sync")
add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --registered-workgroup)
elseif(preparedMode STREQUAL "debug_failure")
add_test(NAME ${preparedTest} COMMAND agc_driver_prepared_shaders_tests --fail-before-registration)
set_tests_properties(${preparedTest} PROPERTIES WILL_FAIL TRUE TIMEOUT 30)
@ -1103,6 +1438,8 @@ if(BUILD_TESTING)
set_tests_properties(${preparedTest} PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1" SKIP_RETURN_CODE 77 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
if(preparedMode STREQUAL "uncached")
set_property(TEST ${preparedTest} APPEND PROPERTY ENVIRONMENT "APS5_NO_DISPATCH_CACHE=1")
elseif(preparedMode STREQUAL "registered_workgroup_sync")
set_property(TEST ${preparedTest} APPEND PROPERTY ENVIRONMENT "APS5_SYNC_SHADER_PREPARE=1")
elseif(preparedMode STREQUAL "debug" OR preparedMode STREQUAL "debug_failure")
set_property(TEST ${preparedTest} APPEND PROPERTY ENVIRONMENT "APS5_PROFILE_DRAW=1" "APS5_NO_UNLOCKED_DEVICE=1")
endif()
@ -1170,7 +1507,9 @@ if(AGC_BUILD_VISUAL_TEST)
SDL2-static
libc
libkernel
jpeg_turbo
)
add_dependencies(agc_driver_visual_test agc_trace_revision)
set_target_properties(agc_driver_visual_test PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_driver_visual_test)
@ -1222,6 +1561,63 @@ endif()
set_target_properties(agc_shader_replay PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_shader_replay)
add_executable(agc_compute_pipeline_replay EXCLUDE_FROM_ALL tools/ComputePipelineReplay.cpp)
target_include_directories(agc_compute_pipeline_replay PRIVATE
${LIBS_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}
${ANYPS5_SPIRV_HEADERS_INCLUDE_DIR}
)
target_link_libraries(agc_compute_pipeline_replay PRIVATE libSceAgcDriver libc)
set_target_properties(agc_compute_pipeline_replay PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_compute_pipeline_replay)
if(BUILD_TESTING)
add_test_executable(agc_compute_pipeline_capture_tests tests/ComputePipelineCapture.cpp Execution/src/ComputePipelineCapture.cpp)
target_include_directories(agc_compute_pipeline_capture_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
target_link_libraries(agc_compute_pipeline_capture_tests PRIVATE libc)
set_target_properties(agc_compute_pipeline_capture_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_compute_pipeline_capture_tests)
add_test(NAME agc_compute_descriptor_layout_flags COMMAND agc_driver_graphics_tests --descriptor-layout-flags)
set_tests_properties(agc_compute_descriptor_layout_flags PROPERTIES TIMEOUT 30)
add_test(NAME agc_driver_covered_centroid_state COMMAND agc_driver_graphics_tests --covered-centroid-state)
set_tests_properties(agc_driver_covered_centroid_state PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_compute_descriptor_layout_flags PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
set_tests_properties(agc_driver_covered_centroid_state PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test(NAME agc_compute_pipeline_capture COMMAND agc_compute_pipeline_capture_tests)
set_tests_properties(agc_compute_pipeline_capture PROPERTIES TIMEOUT 30)
if(WIN32)
set_tests_properties(agc_compute_pipeline_capture PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
if(BUILD_TESTING AND ANYPS5_ENABLE_SPIRV_TOOLS)
add_test_executable(agc_compute_pipeline_replay_tests tests/ComputePipelineReplay.cpp)
target_include_directories(agc_compute_pipeline_replay_tests PRIVATE
${LIBS_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/core/shader/recompiler
${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR}
${ANYPS5_SPIRV_HEADERS_INCLUDE_DIR}
)
target_link_libraries(agc_compute_pipeline_replay_tests PRIVATE libSceAgcDriver libc)
set_target_properties(agc_compute_pipeline_replay_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_compute_pipeline_replay_tests)
add_test(NAME agc_compute_pipeline_replay COMMAND agc_compute_pipeline_replay_tests)
set_tests_properties(agc_compute_pipeline_replay PROPERTIES TIMEOUT 30 SKIP_RETURN_CODE 77)
add_dependencies(agc_compute_pipeline_replay_tests agc_compute_pipeline_replay agc_driver_branch_past_endpgm_tests)
add_test(NAME agc_compute_pipeline_capture_run COMMAND ${CMAKE_COMMAND}
-DFIXTURE=$<TARGET_FILE:agc_driver_branch_past_endpgm_tests>
-DREPLAY=$<TARGET_FILE:agc_compute_pipeline_replay>
-DOUTPUT_ROOT=${CMAKE_CURRENT_BINARY_DIR}/capture-tests
-P ${CMAKE_CURRENT_SOURCE_DIR}/tests/ComputePipelineCaptureRun.cmake)
set_tests_properties(agc_compute_pipeline_capture_run PROPERTIES TIMEOUT 30 SKIP_REGULAR_EXPRESSION "compute pipeline capture run: skipped, no usable Vulkan device")
if(WIN32)
set_tests_properties(agc_compute_pipeline_replay agc_compute_pipeline_capture_run PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
if(BUILD_TESTING)
add_test_executable(texture_format_lookup_tests ${LIBS_INCLUDE_DIR}/tests/TextureFormatLookup.cpp)
target_include_directories(texture_format_lookup_tests PRIVATE ${LIBS_INCLUDE_DIR} ${ANYPS5_VULKAN_HEADERS_INCLUDE_DIR})
@ -1250,6 +1646,15 @@ if(BUILD_TESTING)
set_tests_properties(guest_agc_driver_resources PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_agc_driver_debug_unavailable_tests ${LIBS_INCLUDE_DIR}/tests/GuestAgcDriverDebugUnavailable.cpp)
target_include_directories(guest_agc_driver_debug_unavailable_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_agc_driver_debug_unavailable_tests PRIVATE libSceAgcDriver libc)
configure_windows_unwind(guest_agc_driver_debug_unavailable_tests)
add_test(NAME guest_agc_driver_debug_unavailable COMMAND guest_agc_driver_debug_unavailable_tests)
if(WIN32)
set_tests_properties(guest_agc_driver_debug_unavailable PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
add_test_executable(guest_rendering_wait_tests ${LIBS_INCLUDE_DIR}/tests/GuestRenderingWait.cpp)
target_include_directories(guest_rendering_wait_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_rendering_wait_tests PRIVATE libSceAgcDriver libc)
@ -1484,6 +1889,21 @@ add_test_executable(agc_position_invariance_tests EXCLUDE_FROM_ALL tests/Positio
target_link_libraries(agc_position_invariance_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_position_invariance_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_position_invariance_tests)
add_test_executable(agc_vertex_layer_export_tests EXCLUDE_FROM_ALL tests/VertexLayerExport.cpp)
target_link_libraries(agc_vertex_layer_export_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_vertex_layer_export_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_vertex_layer_export_tests)
if(BUILD_TESTING)
set_target_properties(agc_vertex_layer_export_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_vertex_layer_export COMMAND agc_vertex_layer_export_tests)
set_tests_properties(agc_vertex_layer_export PROPERTIES ENVIRONMENT "ANYPS5_NO_SHADER_CACHE=1")
if(WIN32)
set_tests_properties(agc_vertex_layer_export PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
if(BUILD_TESTING)
set_target_properties(agc_position_invariance_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_position_invariance COMMAND agc_position_invariance_tests)
@ -1548,3 +1968,16 @@ if(BUILD_TESTING)
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()
add_test_executable(agc_constant_phi_tests EXCLUDE_FROM_ALL tests/ConstantPhi.cpp)
target_link_libraries(agc_constant_phi_tests PRIVATE shader_recompiler_agc_driver libc)
set_target_properties(agc_constant_phi_tests PROPERTIES CXX_EXTENSIONS OFF)
configure_windows_unwind(agc_constant_phi_tests)
if(BUILD_TESTING)
set_target_properties(agc_constant_phi_tests PROPERTIES EXCLUDE_FROM_ALL OFF)
add_test(NAME agc_constant_phi COMMAND agc_constant_phi_tests)
if(WIN32)
set_tests_properties(agc_constant_phi PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
endif()
endif()

View File

@ -0,0 +1,77 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_COMPUTEPIPELINECAPTURE_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_COMPUTEPIPELINECAPTURE_HPP
#include <vulkan/vulkan.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <span>
#include <string>
#include <vector>
namespace AgcDriver {
struct ComputeDescriptorBinding {
std::uint32_t binding = 0;
VkDescriptorType type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
std::uint32_t count = 1;
VkShaderStageFlags stages = VK_SHADER_STAGE_COMPUTE_BIT;
VkDescriptorBindingFlags flags = 0;
};
struct ComputeDescriptorSetLayout {
VkDescriptorSetLayoutCreateFlags flags = 0;
std::vector<ComputeDescriptorBinding> bindings;
};
struct ComputeSpecialization {
std::uint32_t id = 0;
std::uint32_t value = 0;
};
struct ComputePipelineCapture {
static constexpr std::uint32_t Version = 1;
static constexpr std::uint32_t MaxSpirvWords = 8u * 1024u * 1024u;
static constexpr std::size_t MaxFileBytes = 36u * 1024u * 1024u;
std::vector<std::uint32_t> spirv;
std::vector<ComputeDescriptorSetLayout> sets;
std::vector<VkPushConstantRange> pushConstants;
std::vector<ComputeSpecialization> specialization;
std::uint32_t requiredSubgroupSize = 0;
VkPipelineCreateFlags flags = 0;
VkPipelineShaderStageCreateFlags stageFlags = 0;
std::string deviceName;
std::uint32_t vendorId = 0;
std::uint32_t deviceId = 0;
std::uint32_t driverVersion = 0;
std::uint32_t apiVersion = 0;
std::array<std::uint8_t, VK_UUID_SIZE> pipelineCacheUuid{};
void Validate() const;
std::vector<std::byte> Encode() const;
static ComputePipelineCapture Decode(std::span<const std::byte> bytes);
static ComputePipelineCapture Read(const std::filesystem::path& path);
std::filesystem::path Write(const std::filesystem::path& directory) const;
std::string Identity() const;
};
struct ComputePipelineReplayResult {
double moduleMilliseconds = 0;
double layoutMilliseconds = 0;
double pipelineMilliseconds = 0;
double cacheMilliseconds = 0;
double totalMilliseconds = 0;
std::string deviceName;
std::uint32_t vendorId = 0;
std::uint32_t deviceId = 0;
std::uint32_t driverVersion = 0;
std::uint32_t apiVersion = 0;
std::array<std::uint8_t, VK_UUID_SIZE> pipelineCacheUuid{};
std::size_t initialCacheBytes = 0;
std::vector<std::byte> cache;
};
}
#endif

View File

@ -8,6 +8,8 @@ namespace AgcDriver::DriverDetail {
double TraceMs();
std::uint64_t SkippedShader();
void require(bool condition, const char* reason);
std::uint32_t readRegister(const Registers& registers, std::uint32_t offset);

View File

@ -37,6 +37,9 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
struct PreparedGraphicsStage {
std::shared_ptr<const ShaderSnapshot> snapshot;
PreparedShaders::Entry entry;
ShaderRecompiler::ShaderStage stage = ShaderRecompiler::ShaderStage::Vertex;
std::uint32_t pushOffset = 0;
std::shared_ptr<const ShaderRecompiler::RecompileResult> registration;
};
std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decoded, const ShaderRecompiler::SpirvTarget& target);
@ -72,6 +75,19 @@ struct DrawEntryCounters {
std::chrono::steady_clock::time_point lastReport = std::chrono::steady_clock::now();
};
class DrawKeySightings {
public:
static constexpr std::size_t Ways = 4;
explicit DrawKeySightings(std::size_t keys);
bool SeenBefore(std::uint64_t key);
private:
std::vector<std::uint64_t> keys;
std::vector<std::uint8_t> filled;
std::vector<std::uint8_t> next;
std::size_t bucketMask = 0;
};
enum class DrawVerdict { Drawn, Nothing, Rejected };
struct StageCapture {

View File

@ -0,0 +1,12 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_DRIVER_DRAW_DRAWREGISTERKEY_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_DRIVER_DRAW_DRAWREGISTERKEY_HPP
#include "prx/libSceAgcDriver/Execution/include/QueueState.hpp"
namespace AgcDriver::DriverDetail {
std::uint64_t RegisterStateKey(const QueueState& queue, bool allUserWords = false, bool incremental = true);
}
#endif

View File

@ -44,6 +44,7 @@ namespace AgcDriver::DriverDetail {
class Driver {
public:
static Driver& Get();
static std::uint64_t GpuMapImportThreads();
~Driver();
void Shutdown();
void WaitIdle();
@ -114,6 +115,7 @@ private:
static std::size_t drawCacheEntries();
void accountDrawVariant(const DispatchVariant& variant, bool added);
void insertDrawEntry(std::uint64_t key, std::vector<std::shared_ptr<DispatchVariant>>& fresh, std::shared_ptr<const DrawDecode> decode);
bool admitDrawKey(std::uint64_t key);
void eraseDrawEntry(std::uint64_t key, const std::shared_ptr<DrawEntry>& expected);
std::shared_ptr<const DrawRecipe> findDrawRecipe(std::uint64_t key, const std::vector<std::shared_ptr<DispatchVariant>>& stages);
void attachDrawRecipe(std::uint64_t key, const std::vector<std::shared_ptr<DispatchVariant>>& stages, std::shared_ptr<const DrawRecipe> recipe);
@ -309,6 +311,7 @@ private:
std::list<std::uint64_t> drawOrder;
std::mutex drawCacheMutex;
std::uint64_t drawCacheHits = 0, drawCacheEvictions = 0, drawCacheVariants = 0, drawCacheVariantBytes = 0;
std::optional<DrawKeySightings> drawKeySightings;
DrawEntryCounters drawEntryCounters;
@ -343,6 +346,7 @@ private:
std::uint64_t queue0Executing = 0;
std::atomic<std::uint32_t> orderHolders{0};
std::atomic<std::uint32_t> runningWorkers{0};
std::atomic<std::uint64_t> idleEpoch{0};
std::atomic<std::uint64_t> queue0Awaited{0};
std::atomic<std::uint32_t> flipsAhead{0};
std::atomic<std::uint32_t> flipHolders{0};

View File

@ -2,6 +2,8 @@
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_DRIVER_MEMORY_DWORDPATTERNFILL_HPP
#include "Recompiler.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DccMetadata.hpp"
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
@ -18,6 +20,10 @@ struct DwordPatternFill {
};
std::optional<DwordPatternFill> MatchDwordPatternFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
std::optional<DwordPatternFill> MatchZeroDwordFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
std::optional<Graphics::MemoryValueFill> MatchMemoryValueBufferFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute);
bool IsByteFill(const std::array<std::uint32_t, 4>& pattern);
std::optional<Graphics::DccKeys> KeysFillMemo(const std::array<std::uint32_t, 4>& pattern, bool coversStorageKeys, bool memoryValueFill);
}

View File

@ -5,6 +5,7 @@
#include "prx/libSceAgcDriver/Execution/include/QueueState.hpp"
#include <array>
#include <optional>
#include <span>
#include <atomic>
#include <condition_variable>
#include <cstddef>
@ -18,6 +19,8 @@
#include <string>
#include <vector>
struct Shader;
namespace ShaderRecompiler {
class ShaderPreparationContext;
}
@ -64,6 +67,7 @@ struct PreparedShaders : PreparedShaderState {
std::mutex mutex;
std::condition_variable settled;
std::exception_ptr failure;
std::atomic<bool> demanded{false};
bool pending = false;
};
@ -82,6 +86,7 @@ struct ShaderSnapshot {
std::shared_ptr<PreparedShaders> prepared = std::make_shared<PreparedShaders>();
std::shared_ptr<const RegisteredShaderState> registeredState;
std::shared_ptr<std::atomic<std::uint8_t>> workgroupUse = std::make_shared<std::atomic<std::uint8_t>>(0u);
std::shared_ptr<std::atomic<std::uint8_t>> positionVectorUse = std::make_shared<std::atomic<std::uint8_t>>(0u);
};
@ -90,15 +95,22 @@ std::shared_ptr<const ShaderSnapshot> ProgramSnapshot(const ShaderRegistry& shad
void NoteRawGraphicsDraw();
bool RawGraphicsDrawn();
bool ProgramUsesWorkgroup(const ShaderSnapshot& snapshot, std::size_t codeOffset);
// The PA_CL_VS_OUT_CNTL position export bits a vertex program is compiled with: none for a program
// without pos1..pos3 exports, so its artifact does not depend on them.
std::uint32_t PositionExportLayout(const ShaderSnapshot* snapshot, std::size_t codeOffset, std::span<const std::uint32_t> code, std::uint32_t paClVsOutCntl);
bool VertexWorkgroupRequired(const QueueState& queue, const ShaderRegistry& registry);
std::shared_ptr<const ShaderRecompiler::SourceHandle> PrepareShaderWithDiagnostics(const ShaderRecompiler::RecompileRequest& request, ShaderRecompiler::ShaderPreparationContext* preparation = nullptr);
std::uint64_t NullPixelProgramAddress();
ShaderSnapshot PrepareNullPixelProgram(const VulkanDevice& device);
std::optional<ShaderRecompiler::ShaderFloatMode> RegisteredFloatMode(const ShaderSnapshot& snapshot);
std::shared_ptr<const ShaderSnapshot> RegisteredHeader(const ShaderRegistry& registry, const Shader* shader);
std::shared_ptr<const ShaderSnapshot> RegisteredProgram(const ShaderRegistry& registry, std::uint64_t address, std::initializer_list<std::uint8_t> types);
void PublishRegisteredShader(std::shared_ptr<ShaderRegistry>& registry, const std::shared_ptr<const ShaderSnapshot>& snapshot);
void SubmitRegistrationPreparation(std::function<void()> task);
void SubmitRegistrationPreparation(std::function<void()> task, const PreparedShaders* prepared = nullptr);
void PrioritizeRegistrationPreparation(PreparedShaders& prepared);
void SubmitLibraryPrebuild(std::function<void()> task);
void CloseRegistrationPreparation();
void ResolvePreparedGraphics(const ShaderSnapshot& front, const std::shared_ptr<const ShaderSnapshot>& fragment, std::uint32_t primitiveType, const ShaderRecompiler::SpirvTarget& target);

View File

@ -30,6 +30,7 @@ void Read(std::uint64_t address, std::span<std::byte> destination, std::size_t a
// Variants for resources in heaps the guest commits on demand (partially resident textures, GPU
// scratch heaps): uncommitted pages read as zeros, are not compared, and are never stored.
void ReadCommitted(std::uint64_t address, std::span<std::byte> destination);
void ReadCommittedUnsynced(std::uint64_t address, std::span<std::byte> destination);
bool EqualsCommitted(std::uint64_t address, std::span<const std::byte> bytes);
// EqualsCommitted without the flush hook: the caller has decided that the recorded GPU work
// writing the range need not be waited for (the dispatch-cache validation in Driver.cpp).
@ -59,7 +60,7 @@ void WriteChanged(std::uint64_t address, std::span<const std::byte> current, std
// UnchangedSince is always false, so callers fall back to comparing bytes.
bool WriteWatched();
void Unwatch(std::uint64_t address, std::size_t bytes);
bool ImportWatched(std::uint64_t address, std::size_t bytes, const std::function<bool()>& import);
bool ImportWatched(std::uint64_t address, std::size_t bytes, const std::function<bool()>& import, bool staysWatched = true);
bool Watched(std::uint64_t address, std::size_t bytes);
std::uint64_t CollectWrites(std::uint64_t address, std::size_t bytes);
bool UnchangedSince(std::uint64_t address, std::size_t bytes, std::uint64_t generation);
@ -71,6 +72,8 @@ struct UnchangedQuery {
};
bool WrittenSince(std::uint64_t address, std::size_t bytes, std::uint64_t generation);
bool UnchangedSinceAll(std::span<const UnchangedQuery> queries);
std::uint64_t CollectCommittedWrites(std::uint64_t address, std::size_t bytes, std::vector<std::pair<std::uint64_t, std::uint64_t>>& ranges);
bool UnchangedCommittedSince(std::span<const std::pair<std::uint64_t, std::uint64_t>> ranges, std::uint64_t generation);
// Returns the generation the blocks were stamped with (0 when the arena is not write-watched):
// UnchangedSince(range, it) holds until the next store over the range.
std::uint64_t MarkWritten(std::uint64_t address, std::size_t bytes);
@ -101,6 +104,7 @@ std::uint64_t CollectWritesUncached(std::uint64_t address, std::size_t bytes);
// tracker mutex, so every collect that bumped before it has finished its walk and every later
// collect stamps newer.
std::uint64_t TrackerGeneration();
std::uint64_t NewestStamp(std::uint64_t address, std::size_t bytes);
// Whether no CPU write touched the range since `generation`: an uncached resetting collect of the
// range's pages first (a game store not collected yet becomes a block stamp), then the compare over
// the stamps collects make (dirty pages), not the MarkWritten stamps of the driver's own GPU label
@ -114,7 +118,6 @@ bool UnchangedSinceCollected(std::uint64_t address, std::size_t bytes, std::uint
// watched arena, or for a generation of 0, a block reads as changed.
constexpr std::uint8_t BlockUnchanged = 0;
constexpr std::uint8_t BlockWritten = 1;
constexpr std::uint8_t BlockMaybeWritten = 2;
bool ChangedBlocks(std::uint64_t address, std::size_t bytes, std::span<const std::uint64_t> generations, std::span<std::uint8_t> changed, std::span<std::uint8_t> cpu = {});
// Serializes device work: draws, dispatches, presentation and the deferred write-backs below. The
@ -149,6 +152,7 @@ GpuMutexType& GpuMutex();
// work the hold deferred to run without it (the recorder's release of the objects completed batches
// kept). One hook, set once; it must not take GpuMutex itself.
void SetGpuUnlockHook(void (*hook)());
std::uint64_t GpuHoldSerial();
// Debug aid (APS5_ASSERT_GPU_LOCK=1): aborts with a [lock] line naming `where` when the calling
// thread does not hold GpuMutex. Recording, submits, command batches and the detiler's pools must
// only be reached under it (a resource build's stage A runs without it, see ShaderResources).
@ -183,6 +187,7 @@ unsigned long long CodeOffset(const void* address);
// every read and store through this interface calls first. Code that reads guest memory directly
// calls FlushGpuWrites itself.
void SetFlushHook(void (*hook)(std::uint64_t address, std::size_t bytes));
void SetReadGuard(void (*guard)(std::uint64_t address, std::size_t bytes));
void FlushGpuWrites(std::uint64_t address, std::size_t bytes);
// Moves with every ForgetPages call (the libc invalidator: memory unmapped or re-registered): odd
// while the call stores its page states, moved past it after, so a reader that loads an even

View File

@ -1,6 +1,8 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PERFORMANCETIMER_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PERFORMANCETIMER_HPP
#include "prx/libSceAgcDriver/Execution/include/PerformanceTrace.hpp"
#include "prx/libSceAgcDriver/Execution/include/TracyZone.hpp"
#include <algorithm>
#include <chrono>
#include <cstddef>
@ -258,11 +260,11 @@ private:
class PerformanceTimer {
public:
explicit PerformanceTimer(const char* scope) : frame(PerformanceContext::Current()), scope(scope) {
explicit PerformanceTimer(const char* scope) : frame(PerformanceContext::Current()), trace(PerformanceTrace::Recording() ? PerformanceTrace::Thread() : nullptr), scope(scope) {
if (frame != nullptr) {
total = frame->Get(scope, "total");
}
if (frame != nullptr) start = Clock::now();
if (frame != nullptr || trace != nullptr) start = Clock::now();
previous = start;
}
@ -274,8 +276,12 @@ public:
}
void Finish() {
if (frame == nullptr) return;
zone.End();
if (frame == nullptr && trace == nullptr) return;
const auto end = Clock::now();
if (trace != nullptr) trace->Add(scope, nullptr, start, end);
trace = nullptr;
if (frame == nullptr) return;
if (tail != nullptr) frame->Add(tail, end - previous);
frame->Add(total, end - start);
frame = nullptr;
@ -287,10 +293,13 @@ public:
}
void Mark(const char* stage, std::uint64_t bytes = 0) {
if (frame == nullptr) return;
if (frame == nullptr && trace == nullptr) return;
const auto now = Clock::now();
if (tail == nullptr) tail = frame->Get(scope, "tail");
frame->Add(frame->Get(scope, stage), now - previous, bytes);
if (trace != nullptr) trace->Add(scope, stage, previous, now);
if (frame != nullptr) {
if (tail == nullptr) tail = frame->Get(scope, "tail");
frame->Add(frame->Get(scope, stage), now - previous, bytes);
}
previous = now;
}
@ -298,7 +307,9 @@ private:
using Clock = FrameTiming::Clock;
FrameTiming* frame;
TraceThread* trace;
const char* scope;
[[no_unique_address]] TracyZone zone{scope};
FrameTiming::Metric* total = nullptr;
FrameTiming::Metric* tail = nullptr;
Clock::time_point start{};

View File

@ -0,0 +1,437 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PERFORMANCETRACE_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PERFORMANCETRACE_HPP
#include <algorithm>
#include <array>
#include <atomic>
#include <charconv>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace AgcDriver {
class VulkanDevice;
class TraceWindow {
public:
using Clock = std::chrono::steady_clock;
enum class Edge { None, Open, Close };
static std::optional<TraceWindow> Parse(const char* frames, const char* seconds) {
if (frames == nullptr && seconds == nullptr) return std::nullopt;
if (frames != nullptr && seconds != nullptr) throw std::invalid_argument("APS5_TRACE_FRAMES and APS5_TRACE_SECONDS cannot be set together");
const std::string_view name = frames != nullptr ? "APS5_TRACE_FRAMES" : "APS5_TRACE_SECONDS";
const std::string_view text = frames != nullptr ? frames : seconds;
const auto colon = text.find(':');
std::uint64_t start = 0;
std::uint64_t length = 0;
if (colon == std::string_view::npos || !number(text.substr(0, colon), start) || !number(text.substr(colon + 1), length) || length == 0)
throw std::invalid_argument(std::string(name) + "=" + std::string(text) + " is not <start>:<length> with a length above 0");
return TraceWindow(frames != nullptr, start, length);
}
Edge Present(std::uint64_t present, Clock::duration elapsed) {
if (state == State::Closed) return Edge::None;
if (state == State::Waiting) {
if (frames ? present < start : wholeSeconds(elapsed) < start) return Edge::None;
state = State::Open;
openedPresent = present;
openedAt = elapsed;
return Edge::Open;
}
if (frames ? present - openedPresent < length : wholeSeconds(elapsed - openedAt) < length) return Edge::None;
state = State::Closed;
return Edge::Close;
}
std::string Describe() const {
return std::string(frames ? "frames " : "seconds ") + std::to_string(start) + ":" + std::to_string(length);
}
private:
enum class State { Waiting, Open, Closed };
TraceWindow(bool frames, std::uint64_t start, std::uint64_t length) : frames(frames), start(start), length(length) {}
static bool number(std::string_view text, std::uint64_t& value) {
const auto end = text.data() + text.size();
const auto [last, error] = std::from_chars(text.data(), end, value);
return !text.empty() && error == std::errc{} && last == end;
}
static std::uint64_t wholeSeconds(Clock::duration elapsed) {
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::seconds>(elapsed).count());
}
bool frames;
std::uint64_t start;
std::uint64_t length;
State state = State::Waiting;
std::uint64_t openedPresent = 0;
Clock::duration openedAt{};
};
class TraceThread {
public:
using Clock = std::chrono::steady_clock;
static constexpr std::size_t DefaultCapacity = 262144;
static constexpr Clock::duration MinimumSlice = std::chrono::microseconds(20);
struct Slice {
const char* scope;
const char* stage;
Clock::time_point begin;
Clock::duration length;
};
struct Total {
const char* scope;
const char* stage;
Clock::duration length;
std::uint64_t count;
};
struct Snapshot {
std::string name;
std::span<const Slice> slices;
std::vector<Total> totals;
bool dropped = false;
};
TraceThread(std::string name, const std::atomic<std::uint64_t>& presents, std::size_t capacity = DefaultCapacity)
: name(std::move(name)), presents(presents), slices(std::make_unique<Slice[]>(capacity)), capacity(capacity) {}
TraceThread(const TraceThread&) = delete;
TraceThread& operator=(const TraceThread&) = delete;
void Add(const char* scope, const char* stage, Clock::time_point begin, Clock::time_point end) {
const auto length = end - begin;
account(scope, stage, length);
const auto present = presents.load(std::memory_order_relaxed);
if (!sampled || present != sampledPresent) {
if (sampled) append({nullptr, nullptr, sampleBegin, folded});
sampled = true;
sampledPresent = present;
sampleBegin = begin;
folded = {};
}
if (length < MinimumSlice) folded += length;
else append({scope, stage, begin, length});
}
Snapshot Collect() const {
Snapshot snapshot{name, {slices.get(), used.load(std::memory_order_acquire)}, {}, false};
for (const auto& entry : entries) {
const auto* scope = entry.scope.load(std::memory_order_acquire);
if (scope == nullptr) continue;
snapshot.totals.push_back({scope, entry.stage.load(std::memory_order_relaxed), Clock::duration(entry.length.load(std::memory_order_relaxed)), entry.count.load(std::memory_order_relaxed)});
}
snapshot.dropped = dropped.load(std::memory_order_relaxed);
return snapshot;
}
private:
struct Entry {
std::atomic<const char*> scope{nullptr};
std::atomic<const char*> stage{nullptr};
std::atomic<Clock::rep> length{0};
std::atomic<std::uint64_t> count{0};
};
void append(const Slice& slice) {
const auto index = used.load(std::memory_order_relaxed);
if (index == capacity) {
dropped.store(true, std::memory_order_relaxed);
return;
}
slices[index] = slice;
used.store(index + 1, std::memory_order_release);
}
void account(const char* scope, const char* stage, Clock::duration length) {
const auto key = reinterpret_cast<std::uintptr_t>(scope) ^ (reinterpret_cast<std::uintptr_t>(stage) >> 3);
const auto hash = static_cast<std::size_t>((static_cast<std::uint64_t>(key) * 0x9e3779b97f4a7c15ull) >> 55);
for (std::size_t probe = 0; probe < entries.size(); ++probe) {
auto& entry = entries[(hash + probe) % entries.size()];
const auto* current = entry.scope.load(std::memory_order_relaxed);
if (current == nullptr) {
entry.stage.store(stage, std::memory_order_relaxed);
entry.scope.store(scope, std::memory_order_release);
} else if (current != scope || entry.stage.load(std::memory_order_relaxed) != stage) {
continue;
}
entry.length.store(entry.length.load(std::memory_order_relaxed) + length.count(), std::memory_order_relaxed);
entry.count.store(entry.count.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);
return;
}
dropped.store(true, std::memory_order_relaxed);
}
const std::string name;
const std::atomic<std::uint64_t>& presents;
const std::unique_ptr<Slice[]> slices;
const std::size_t capacity;
std::atomic<std::size_t> used{0};
std::atomic<bool> dropped{false};
std::array<Entry, 512> entries;
bool sampled = false;
std::uint64_t sampledPresent = 0;
Clock::time_point sampleBegin{};
Clock::duration folded{};
};
struct TraceSummary {
std::string commit;
std::optional<bool> dirty;
std::vector<std::pair<std::string, std::string>> environment;
std::string gpu;
std::string driver;
std::string os;
std::uint32_t width = 0;
std::uint32_t height = 0;
std::string window;
std::uint64_t firstPresent = 0;
double gpuBusyMs = 0;
std::string trace;
};
struct TraceGpuRange {
const char* name;
double beginNs;
double endNs;
};
struct TraceClockSample {
double gpuNs = 0;
std::chrono::steady_clock::time_point cpu{};
};
class TraceFormat {
public:
using Clock = std::chrono::steady_clock;
static constexpr std::array<std::string_view, 5> WorkerBusyScopes{"Driver.Packet", "Driver.Suspend", "Driver.EndSubmission", "Driver.Rewind", "Driver.Completion"};
static constexpr std::string_view WorkerWaitScope = "Driver.WorkerWait";
static constexpr std::string_view WorkerPrefix = "queue worker ";
static constexpr std::size_t TopPhases = 8;
static void AppendString(std::string& output, std::string_view text) {
output += '"';
for (const char character : text) {
const auto byte = static_cast<unsigned char>(character);
if (character == '"' || character == '\\') {
output += '\\';
output += character;
} else if (byte < 0x20 || byte >= 0x7f) {
char escaped[8];
std::snprintf(escaped, sizeof escaped, "\\u%04x", static_cast<unsigned>(byte));
output += escaped;
} else {
output += character;
}
}
output += '"';
}
static std::string Name(const char* scope, const char* stage) {
return stage == nullptr ? std::string(scope) : std::string(scope) + "." + stage;
}
static std::string Events(std::uint64_t process, Clock::time_point origin, std::span<const TraceThread::Snapshot> threads, std::size_t presenter, std::span<const Clock::time_point> presents, std::uint64_t firstPresent) {
std::string output = "{\"traceEvents\":[\n";
bool first = true;
const auto event = [&](std::string_view name, const char* phase, Clock::time_point at, std::size_t tid) {
if (!first) output += ",\n";
first = false;
output += "{\"name\":";
AppendString(output, name);
char text[96];
std::snprintf(text, sizeof text, ",\"ph\":\"%s\",\"ts\":%.3f,\"pid\":%llu,\"tid\":%llu", phase, microseconds(at - origin), static_cast<unsigned long long>(process), static_cast<unsigned long long>(tid));
output += text;
};
event("process_name", "M", origin, 0);
output += ",\"args\":{\"name\":\"AnyPS5\"}}";
for (std::size_t index = 0; index < threads.size(); ++index) {
const auto& thread = threads[index];
const auto tid = index + 1;
event("thread_name", "M", origin, tid);
output += ",\"args\":{\"name\":";
AppendString(output, thread.name);
output += "}}";
std::vector<TraceThread::Slice> slices(thread.slices.begin(), thread.slices.end());
std::stable_sort(slices.begin(), slices.end(), [](const auto& a, const auto& b) { return a.begin != b.begin ? a.begin < b.begin : a.length > b.length; });
const auto folded = thread.name + " folded";
for (const auto& slice : slices) {
char text[64];
if (slice.scope == nullptr) {
event(folded, "C", slice.begin, tid);
std::snprintf(text, sizeof text, ",\"args\":{\"ms\":%.3f}}", milliseconds(slice.length));
} else {
event(Name(slice.scope, slice.stage), "X", slice.begin, tid);
std::snprintf(text, sizeof text, ",\"dur\":%.3f}", microseconds(slice.length));
}
output += text;
}
}
for (std::size_t index = 0; index + 1 < presents.size(); ++index) {
event("present", "i", presents[index], presenter + 1);
output += ",\"s\":\"g\",\"args\":{\"present\":" + std::to_string(firstPresent + index) + "}}";
}
output += "\n]}\n";
return output;
}
static std::vector<TraceThread::Slice> GpuSlices(std::span<const TraceGpuRange> ranges, const TraceClockSample& first, const TraceClockSample& last, Clock::time_point origin) {
const double gpuSpan = last.gpuNs - first.gpuNs;
const double scale = gpuSpan > 0 ? std::chrono::duration<double, std::nano>(last.cpu - first.cpu).count() / gpuSpan : 1.0;
const auto map = [&](double gpuNs) { return first.cpu + std::chrono::duration_cast<Clock::duration>(std::chrono::duration<double, std::nano>((gpuNs - first.gpuNs) * scale)); };
std::vector<TraceThread::Slice> slices;
for (const auto& range : ranges) {
const auto begin = map(range.beginNs);
const auto end = map(range.endNs);
if (end <= begin || begin < origin) continue;
slices.push_back({range.name, nullptr, begin, end - begin});
}
return slices;
}
static std::string Summary(const TraceSummary& summary, std::span<const TraceThread::Snapshot> threads, std::span<const Clock::time_point> presents) {
const auto count = presents.empty() ? std::size_t{0} : presents.size() - 1;
const auto span = presents.empty() ? Clock::duration{} : presents.back() - presents.front();
std::vector<double> frames;
for (std::size_t index = 1; index < presents.size(); ++index) frames.push_back(milliseconds(presents[index] - presents[index - 1]));
std::sort(frames.begin(), frames.end());
const auto percentile = [&](double fraction) { return frames.empty() ? 0.0 : frames[std::min(frames.size() - 1, static_cast<std::size_t>(fraction * static_cast<double>(frames.size())))]; };
const double perPresent = count != 0 ? 1.0 / static_cast<double>(count) : 0.0;
std::string output = "{\"commit\":";
AppendString(output, summary.commit);
output += ",\"dirty\":";
output += summary.dirty.has_value() ? (*summary.dirty ? "true" : "false") : "null";
output += ",\"env\":{";
for (std::size_t index = 0; index < summary.environment.size(); ++index) {
if (index != 0) output += ',';
AppendString(output, summary.environment[index].first);
output += ':';
AppendString(output, summary.environment[index].second);
}
output += "},\"gpu\":";
AppendString(output, summary.gpu);
output += ",\"driver\":";
AppendString(output, summary.driver);
output += ",\"os\":";
AppendString(output, summary.os);
output += ",\"resolution\":";
AppendString(output, std::to_string(summary.width) + "x" + std::to_string(summary.height));
output += ",\"window\":{\"spec\":";
AppendString(output, summary.window);
output += ",\"first_present\":" + std::to_string(summary.firstPresent);
output += ",\"ms\":" + number(milliseconds(span)) + "}";
output += ",\"presents\":" + std::to_string(count);
output += ",\"frame_ms\":{\"p50\":" + number(percentile(0.5)) + ",\"p90\":" + number(percentile(0.9)) + ",\"p99\":" + number(percentile(0.99)) + ",\"max\":" + number(frames.empty() ? 0.0 : frames.back()) + "}";
output += ",\"gpu_busy_ms_per_present\":" + number(summary.gpuBusyMs * perPresent);
output += ",\"worker_busy_pct\":{";
bool firstWorker = true;
std::map<std::string, Clock::duration> phases;
bool dropped = false;
for (const auto& thread : threads) {
dropped = dropped || thread.dropped;
Clock::duration busy{};
for (const auto& total : thread.totals) {
const std::string_view scope = total.scope;
if (total.stage == nullptr && std::find(WorkerBusyScopes.begin(), WorkerBusyScopes.end(), scope) != WorkerBusyScopes.end()) busy += total.length;
if (scope != WorkerWaitScope) phases[Name(total.scope, total.stage)] += total.length;
}
if (!thread.name.starts_with(WorkerPrefix)) continue;
if (!firstWorker) output += ',';
firstWorker = false;
AppendString(output, thread.name);
output += ':' + number(span != Clock::duration{} ? 100.0 * static_cast<double>(busy.count()) / static_cast<double>(span.count()) : 0.0);
}
output += "},\"top_phases_ms_per_frame\":{";
std::vector<std::pair<std::string, Clock::duration>> top(phases.begin(), phases.end());
std::stable_sort(top.begin(), top.end(), [](const auto& a, const auto& b) { return a.second > b.second; });
for (std::size_t index = 0; index < top.size() && index < TopPhases; ++index) {
if (index != 0) output += ',';
AppendString(output, top[index].first);
output += ':' + number(milliseconds(top[index].second) * perPresent);
}
output += "},\"trace_dropped\":";
output += dropped ? "true" : "false";
output += ",\"trace\":";
AppendString(output, summary.trace);
output += "}\n";
return output;
}
private:
static double microseconds(Clock::duration elapsed) {
return std::chrono::duration<double, std::micro>(elapsed).count();
}
static double milliseconds(Clock::duration elapsed) {
return std::chrono::duration<double, std::milli>(elapsed).count();
}
static std::string number(double value) {
char text[32];
std::snprintf(text, sizeof text, "%.3f", value);
return text;
}
};
class PerformanceTrace {
public:
static bool Configured() {
static const bool configured = std::getenv("APS5_TRACE_FRAMES") != nullptr || std::getenv("APS5_TRACE_SECONDS") != nullptr;
return configured;
}
static void BindThread(const char* name, std::size_t capacity = TraceThread::DefaultCapacity);
static void Present(std::uint32_t width, std::uint32_t height, const VulkanDevice* device);
static void GpuRange(const char* name, double beginNs, double endNs) {
if (!Recording()) return;
std::lock_guard lock(gpuMutex);
if (gpuRanges.size() == gpuRanges.capacity()) {
gpuDropped = true;
return;
}
gpuRanges.push_back({name, beginNs, endNs});
}
static bool Recording() {
return recording.load(std::memory_order_relaxed);
}
static TraceThread* Thread() {
return local;
}
static void StopOutput();
private:
inline static std::atomic<bool> recording{false};
inline static std::atomic<std::uint64_t> presentCount{0};
inline static thread_local TraceThread* local = nullptr;
inline static std::mutex gpuMutex;
inline static std::vector<TraceGpuRange> gpuRanges;
inline static bool gpuDropped = false;
};
}
#endif

View File

@ -0,0 +1,14 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PIPELINELINKOPTIMIZATION_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PIPELINELINKOPTIMIZATION_HPP
#include <vulkan/vulkan.h>
namespace AgcDriver {
inline bool PipelineLinkTimeOptimizationUsable(VkDriverId driver) {
return driver != VK_DRIVER_ID_NVIDIA_PROPRIETARY;
}
}
#endif

View File

@ -0,0 +1,27 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PRESENTMODE_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_PRESENTMODE_HPP
#include <vulkan/vulkan.h>
#include <algorithm>
#include <span>
#include <stdexcept>
#include <string_view>
namespace AgcDriver {
inline VkPresentModeKHR SelectPresentMode(const char* requested, std::span<const VkPresentModeKHR> supported) {
VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
if (requested != nullptr) {
const std::string_view name(requested);
if (name == "mailbox") mode = VK_PRESENT_MODE_MAILBOX_KHR;
else if (name == "immediate") mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
else if (name == "fifo_latest_ready") mode = VK_PRESENT_MODE_FIFO_LATEST_READY_KHR;
else if (name != "fifo") throw std::runtime_error("Vulkan presentation: ANYPS5_PRESENT_MODE must be fifo, fifo_latest_ready, mailbox or immediate");
}
if (std::find(supported.begin(), supported.end(), mode) == supported.end()) throw std::runtime_error("Vulkan presentation: the surface does not support the requested present mode");
return mode;
}
}
#endif

View File

@ -9,8 +9,10 @@
#include <stdexcept>
#include <utility>
#include <map>
#include <memory>
#include <array>
#include <optional>
#include <span>
#include <string>
#include <vector>
@ -22,6 +24,29 @@ public:
using mapped_type = std::uint32_t;
using value_type = std::pair<const std::uint32_t, std::uint32_t>;
class Mask {
public:
explicit Mask(std::vector<std::uint64_t> bits) : bits(std::move(bits)) {}
bool Contains(std::uint32_t offset) const { return offset / 64 < bits.size() && ((bits[offset / 64] >> (offset % 64)) & 1u) != 0; }
private:
std::vector<std::uint64_t> bits;
};
class Reference {
public:
Reference(Registers& owner, std::uint32_t offset) : owner(owner), offset(offset) {}
operator std::uint32_t() const { return owner.values[offset]; }
Reference& operator=(std::uint32_t value) { owner.insert_or_assign(offset, value); return *this; }
Reference& operator=(const Reference& other) { return *this = static_cast<std::uint32_t>(other); }
Reference& operator|=(std::uint32_t value) { return *this = static_cast<std::uint32_t>(*this) | value; }
Reference& operator&=(std::uint32_t value) { return *this = static_cast<std::uint32_t>(*this) & value; }
Reference& operator^=(std::uint32_t value) { return *this = static_cast<std::uint32_t>(*this) ^ value; }
Reference& operator+=(std::uint32_t value) { return *this = static_cast<std::uint32_t>(*this) + value; }
private:
Registers& owner;
std::uint32_t offset;
};
class const_iterator {
public:
using iterator_category = std::forward_iterator_tag;
@ -56,6 +81,18 @@ public:
using iterator = const_iterator;
Registers() = default;
Registers(const Registers&) = default;
Registers& operator=(const Registers&) = default;
Registers(Registers&& other) noexcept : values(std::move(other.values)), present(std::move(other.present)), entries(std::exchange(other.entries, 0)), mask(std::move(other.mask)), fingerprint(std::exchange(other.fingerprint, 0)) {}
Registers& operator=(Registers&& other) noexcept {
if (this == &other) return *this;
values = std::move(other.values);
present = std::move(other.present);
entries = std::exchange(other.entries, 0);
mask = std::move(other.mask);
fingerprint = std::exchange(other.fingerprint, 0);
return *this;
}
Registers(std::initializer_list<std::pair<std::uint32_t, std::uint32_t>> entries) {
for (const auto& [offset, value] : entries) emplace(offset, value);
}
@ -67,33 +104,48 @@ public:
const_iterator upper_bound(std::uint32_t offset) const { return {this, nextPresent(static_cast<std::size_t>(offset) + 1)}; }
bool contains(std::uint32_t offset) const { return offset < values.size() && ((present[offset / 64] >> (offset % 64)) & 1u) != 0; }
std::size_t count(std::uint32_t offset) const { return contains(offset) ? 1 : 0; }
template <class TVisit>
void ForEach(TVisit&& visit) const {
for (std::size_t word = 0; word < present.size(); ++word) {
for (auto bits = present[word]; bits != 0; bits &= bits - 1) {
const auto offset = word * 64 + static_cast<std::size_t>(std::countr_zero(bits));
visit(static_cast<std::uint32_t>(offset), values[offset]);
}
}
}
std::size_t size() const { return entries; }
bool empty() const { return entries == 0; }
void clear() {
values.clear();
present.clear();
entries = 0;
fingerprint = 0;
}
std::pair<const_iterator, bool> emplace(std::uint32_t offset, std::uint32_t value) {
if (contains(offset)) return {const_iterator{this, offset}, false};
mark(offset) = value;
changeFingerprint(offset, value);
return {const_iterator{this, offset}, true};
}
std::pair<const_iterator, bool> insert_or_assign(std::uint32_t offset, std::uint32_t value) {
if (contains(offset)) {
if (values[offset] == value) return {const_iterator{this, offset}, false};
changeFingerprint(offset, values[offset]);
values[offset] = value;
changeFingerprint(offset, value);
return {const_iterator{this, offset}, false};
}
mark(offset) = value;
changeFingerprint(offset, value);
return {const_iterator{this, offset}, true};
}
std::uint32_t& operator[](std::uint32_t offset) {
if (contains(offset)) return values[offset];
return mark(offset) = 0;
Reference operator[](std::uint32_t offset) {
if (!contains(offset)) emplace(offset, 0);
return {*this, offset};
}
std::uint32_t& at(std::uint32_t offset) {
Reference at(std::uint32_t offset) {
if (!contains(offset)) throw std::out_of_range("register is not set");
return values[offset];
return {*this, offset};
}
const std::uint32_t& at(std::uint32_t offset) const {
if (!contains(offset)) throw std::out_of_range("register is not set");
@ -101,6 +153,7 @@ public:
}
std::size_t erase(std::uint32_t offset) {
if (!contains(offset)) return 0;
changeFingerprint(offset, values[offset]);
present[offset / 64] &= ~(std::uint64_t{1} << (offset % 64));
--entries;
return 1;
@ -113,8 +166,31 @@ public:
}
return a == end() && b == other.end();
}
std::uint64_t Fingerprint(const std::shared_ptr<const Mask>& selected) const {
if (mask != selected) {
fingerprint = RecomputeFingerprint(*selected);
mask = selected;
}
return fingerprint;
}
std::uint64_t RecomputeFingerprint(const Mask& selected) const {
std::uint64_t result = 0;
for (const auto [offset, value] : *this) {
if (selected.Contains(offset)) result ^= contribution(offset, value);
}
return result;
}
private:
static std::uint64_t contribution(std::uint32_t offset, std::uint32_t value) {
auto hash = ((static_cast<std::uint64_t>(offset) << 32u) | value) + 0x9e3779b97f4a7c15ull;
hash = (hash ^ (hash >> 30u)) * 0xbf58476d1ce4e5b9ull;
hash = (hash ^ (hash >> 27u)) * 0x94d049bb133111ebull;
return hash ^ (hash >> 31u);
}
void changeFingerprint(std::uint32_t offset, std::uint32_t value) {
if (mask != nullptr && mask->Contains(offset)) fingerprint ^= contribution(offset, value);
}
static constexpr std::size_t End = ~std::size_t{0};
std::uint32_t& mark(std::uint32_t offset) {
if (offset >= values.size()) {
@ -137,6 +213,8 @@ private:
std::vector<std::uint32_t> values;
std::vector<std::uint64_t> present;
std::size_t entries = 0;
mutable std::shared_ptr<const Mask> mask;
mutable std::uint64_t fingerprint = 0;
};
inline Registers BuildInitialContextRegisters() {
@ -162,6 +240,7 @@ inline Registers BuildInitialContextRegisters() {
result.emplace(0x31c + 0xfu * i, 0);
}
for (std::uint32_t i = 0; i < 16; ++i) {
result.emplace(0x2fe + i, 0);
result.emplace(0x94 + 2 * i, 0x80000000);
result.emplace(0x95 + 2 * i, 0x40004000);
result.emplace(0xb4 + 2 * i, 0);
@ -183,11 +262,12 @@ struct Predication {
};
struct QueueState {
static constexpr std::size_t ConstantRamWords = 0x3000;
Registers shader;
Registers context = InitialContextRegisters();
Registers userConfig{{0x24a, 0}, {0x24b, 0}};
std::optional<Registers> savedContext;
std::array<std::uint32_t, 0x3000> constantRam{};
std::vector<std::uint32_t> constantRam;
std::uint64_t indexBase = 0;
std::uint64_t drawIndirectBase = 0;
std::uint64_t dispatchIndirectBase = 0;
@ -200,6 +280,17 @@ struct QueueState {
void ClearContext() {
context = InitialContextRegisters();
}
std::span<const std::uint32_t> ConstantRam() const {
static const std::array<std::uint32_t, ConstantRamWords> unwritten{};
if (constantRam.empty()) return unwritten;
return constantRam;
}
std::span<std::uint32_t> WritableConstantRam() {
if (constantRam.empty()) constantRam.resize(ConstantRamWords);
return constantRam;
}
};
}

View File

@ -84,6 +84,7 @@ struct DrawRecipe {
std::optional<Graphics::State> masked;
std::set<std::uint32_t> fragmentOutputs;
VkPipelineStageFlags shaderStages = 0;
bool hasDepth = false;
};
}

View File

@ -16,6 +16,7 @@ public:
ShaderPreparationTransaction& operator=(const ShaderPreparationTransaction&) = delete;
PreparedShaderState& Edit(const ShaderSnapshot& snapshot);
const PreparedShaderState& Read(const ShaderSnapshot& snapshot) const;
void Hold();
void Commit();
private:

View File

@ -0,0 +1,155 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_STACKWALK_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_STACKWALK_HPP
#include "prx/libc/include/exceptions/Unwind.hpp"
#include <algorithm>
#include <array>
#include <cstdint>
#include <iterator>
#include <map>
#include <span>
#include <string>
#include <utility>
#include <vector>
namespace AgcDriver {
class DwarfFrames {
public:
using Byte = LibcUnwind::Byte;
using Word = LibcUnwind::Word;
using Registers = std::array<Word, 17>;
enum class Step { Stepped, NoRecord, Failed };
DwarfFrames(Word base, Word end, const Byte* frames, const Byte* framesEnd) : base(base), end(end) {
for (const Byte* p = frames; p != nullptr && framesEnd - p >= 8;) {
const Byte* record = p;
const auto length = LibcUnwind::Read<std::uint32_t>(p);
if (length == 0) continue;
if (length == 0xffffffff || length < 4 || static_cast<Word>(framesEnd - p) < length) break;
const Byte* next = p + length;
if (LibcUnwind::Read<std::uint32_t>(p) != 0) {
LibcUnwind::Frame frame{};
const Byte* cursor = nullptr;
const char* augmentation = nullptr;
Byte lsdaEncoding = 0;
if (LibcUnwind::DecodeRange(frame, LibcUnwind::Lookup{0, record}, cursor, augmentation, lsdaEncoding)) index.emplace_back(frame.start, record);
}
p = next;
}
std::sort(index.begin(), index.end());
}
Word Base() const {
return base;
}
bool Contains(Word pc) const {
return pc >= base && pc < end;
}
bool Covers(Word pc) const {
LibcUnwind::Frame frame{};
return find(pc, frame) == Step::Stepped;
}
template<typename TRead>
Step Unwind(Registers& registers, bool leaf, TRead&& read) const {
const Word pc = registers[16] - (leaf ? 0 : 1);
LibcUnwind::Frame frame{};
if (const auto found = find(pc, frame); found != Step::Stepped) return found;
LibcUnwind::Rules initial;
if (!LibcUnwind::Instructions(frame.cieBegin, frame.cieEnd, frame, ~Word(0), initial, {})) return Step::Failed;
if constexpr (LibcUnwind::RuleCount > 32) {
if (frame.returnRegister == 32) {
initial.registers[16] = initial.registers[32];
initial.registers[32] = {};
frame.returnRegister = 16;
}
}
LibcUnwind::Rules rules = initial;
if (!LibcUnwind::Instructions(frame.begin, frame.end, frame, pc, rules, initial)) return Step::Failed;
if (rules.cfaExpression != nullptr || rules.cfaRegister >= 16 || frame.returnRegister >= 17) return Step::Failed;
const Word cfa = registers[rules.cfaRegister] + rules.cfaOffset;
if (cfa <= registers[7]) return Step::Failed;
Registers next = registers;
for (unsigned column = 0; column < next.size(); ++column) {
const auto& rule = rules.registers[column];
if (rule.kind == 0) continue;
if (rule.kind == 1) {
if (!read(cfa + rule.value, next[column])) return Step::Failed;
} else if (rule.kind == 2 && rule.value >= 0 && rule.value < 17) {
next[column] = registers[rule.value];
} else if (rule.kind == 4) {
next[column] = cfa + rule.value;
} else if (rule.kind == 5) {
next[column] = 0;
} else {
return Step::Failed;
}
}
next[7] = cfa;
next[16] = next[frame.returnRegister];
if (next[16] == 0 || (next[7] == registers[7] && next[16] == registers[16])) return Step::Failed;
registers = next;
return Step::Stepped;
}
private:
Step find(Word pc, LibcUnwind::Frame& frame) const {
const auto found = std::upper_bound(index.begin(), index.end(), pc, [](Word value, const auto& entry) { return value < entry.first; });
if (found == index.begin()) return Step::NoRecord;
const Byte* p = nullptr;
const char* augmentation = nullptr;
Byte lsdaEncoding = 0;
if (!LibcUnwind::DecodeRange(frame, LibcUnwind::Lookup{pc, std::prev(found)->second}, p, augmentation, lsdaEncoding)) return Step::Failed;
if (pc < frame.start || pc - frame.start >= frame.length) return Step::NoRecord;
if (*augmentation == 'z') {
const auto size = LibcUnwind::Uleb(p);
p += size;
}
frame.begin = p;
return Step::Stepped;
}
Word base;
Word end;
std::vector<std::pair<Word, const Byte*>> index;
};
class CollapsedStacks {
public:
void Add(std::span<const std::uint64_t> leafFirst, bool truncated) {
std::vector<std::uint64_t> stack(leafFirst.begin(), leafFirst.end());
if (truncated) {
stack.push_back(0);
++truncatedStacks;
}
++stacks[std::move(stack)];
}
std::uint64_t Truncated() const {
return truncatedStacks;
}
template<typename TName>
std::string Format(TName&& name) const {
std::string output;
for (const auto& [stack, count] : stacks) {
for (auto frame = stack.rbegin(); frame != stack.rend(); ++frame) {
if (frame != stack.rbegin()) output += ';';
output += *frame == 0 ? std::string("[truncated]") : name(*frame);
}
output += ' ' + std::to_string(count) + '\n';
}
return output;
}
private:
std::map<std::vector<std::uint64_t>, std::uint64_t> stacks;
std::uint64_t truncatedStacks = 0;
};
}
#endif

View File

@ -0,0 +1,17 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_TRACEFILE_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_TRACEFILE_HPP
#include <filesystem>
#include <string_view>
namespace AgcDriver {
class TraceFile {
public:
static void WriteGzip(const std::filesystem::path& path, std::string_view text);
static void AppendLine(const std::filesystem::path& path, std::string_view line);
};
}
#endif

View File

@ -0,0 +1,81 @@
#ifndef CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_TRACYZONE_HPP
#define CORE_LIBS_PRX_LIBSCEAGCDRIVER_EXECUTION_INCLUDE_TRACYZONE_HPP
#ifdef TRACY_ENABLE
#include <tracy/TracyC.h>
#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <stdexcept>
#endif
namespace AgcDriver {
class TracyZone {
public:
explicit TracyZone([[maybe_unused]] const char* name) {
#ifdef TRACY_ENABLE
context = ___tracy_emit_zone_begin(location(name), 1);
#endif
}
TracyZone(const TracyZone&) = delete;
TracyZone& operator=(const TracyZone&) = delete;
~TracyZone() {
End();
}
void End() {
#ifdef TRACY_ENABLE
if (context.active == 0) return;
___tracy_emit_zone_end(context);
context.active = 0;
#endif
}
static void MarkFrame() {
#ifdef TRACY_ENABLE
___tracy_emit_frame_mark(nullptr);
#endif
}
static void NameThread([[maybe_unused]] const char* name) {
#ifdef TRACY_ENABLE
___tracy_set_thread_name(name);
#endif
}
private:
#ifdef TRACY_ENABLE
static const ___tracy_source_location_data* location(const char* name) {
static constexpr std::size_t capacity = 256;
static std::array<std::atomic<const char*>, capacity> names{};
static std::array<___tracy_source_location_data, capacity> locations{};
static std::mutex mutex;
const auto start = static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(name) >> 4);
for (std::size_t probe = 0; probe < capacity; ++probe) {
const auto index = (start + probe) % capacity;
const auto* known = names[index].load(std::memory_order_acquire);
if (known == name) return &locations[index];
if (known != nullptr) continue;
std::lock_guard lock(mutex);
known = names[index].load(std::memory_order_relaxed);
if (known == name) return &locations[index];
if (known != nullptr) continue;
locations[index] = {name, name, "", 0, 0};
names[index].store(name, std::memory_order_release);
return &locations[index];
}
throw std::runtime_error("Tracy: too many zone names");
}
TracyCZoneCtx context;
#endif
};
}
#endif

View File

@ -7,9 +7,11 @@
#include "prx/libSceAgcDriver/Execution/include/Presentation.hpp"
#include "prx/libSceAgcDriver/Execution/include/DisplayBuffer.hpp"
#include "prx/libSceAgcDriver/Execution/include/Recipe.hpp"
#include "prx/libSceAgcDriver/Execution/include/ComputePipelineCapture.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Draw.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include <chrono>
#include <functional>
#include <memory>
#include <optional>
@ -31,12 +33,19 @@ struct RecordedDispatch;
class VulkanDevice {
public:
explicit VulkanDevice(const PresentationWindow* window = nullptr);
struct CompileOnly {};
explicit VulkanDevice(CompileOnly);
ComputePipelineReplayResult ReplayComputePipeline(const ComputePipelineCapture& capture, std::span<const std::byte> initialCache = {});
~VulkanDevice();
VulkanDevice(const VulkanDevice&) = delete;
VulkanDevice& operator=(const VulkanDevice&) = delete;
std::string DeviceName() const;
std::string DriverInfo() const;
bool CalibrateClock(double& gpuNs, std::chrono::steady_clock::time_point& cpu) const;
ShaderRecompiler::SpirvTarget Target() const;
std::optional<ShaderRecompiler::GeometryStageLimits> GeometryLimits() const;
const std::uint32_t* WorkGroupCountLimit() const;
std::uint32_t VendorId() const;
ShaderRecompiler::SpirvTarget ComputeTarget(std::uint32_t waveSize) const;
// Distinguishes this device from every earlier one in the process (a memo keyed by the device
// cannot rely on the pointer, which a replacement may reuse).
@ -55,6 +64,7 @@ public:
// mutex through the flush hook, so a thread submitting on another queue's behalf (a WAIT_REG_MEM
// poller, a compute worker's between-packet flush) passes false: it must only submit, never wait.
void SubmitRecorded(bool reapFirst = true);
void StoreIdleResults(bool reapFirst = true);
// Unlocked drain: SubmitAndEpoch (under the mutex) returns the serial covering every recorded
// batch (0: nothing in flight); WaitRecorded(serial) waits for it WITHOUT the mutex (the caller
// holds a shared_ptr to this device); ReapRecorded(serial) (under the mutex) runs the completions
@ -212,7 +222,7 @@ public:
// blit-submit times after the flip packet; noted in-place reads checked
// (APS5_FLIP_READ_CHECK=1) and found overwritten before the blit's fence.
struct PresentStatistics {
std::uint64_t presents, gpuUnread, batchesAheadOfBlit, batchesAfterFlip, readsChecked, readsOverwritten;
std::uint64_t presents, gpuUnread, batchesAheadOfBlit, batchesAfterFlip, readsChecked, readsOverwritten, acquireDrops;
double gpuBusyMs, gpuGapMs, lastSubmitAfterFlipMs, blitSubmitAfterFlipMs;
};
static PresentStatistics PresentCounts();
@ -292,8 +302,11 @@ public:
};
IndirectDrawSupport DrawIndirectSupport() const;
void EnqueueDraw(const Graphics::State& graphics, const Pm4::DrawParameters& draw, std::span<const Graphics::CompiledShader> shaders, std::span<const Graphics::GuestMemorySnapshot> snapshots = {});
bool PrebuildGraphicsLibraries(const Graphics::ShaderStages& stages, std::span<const Graphics::CompiledShader> shaders);
private:
VulkanDevice(const PresentationWindow* window, bool compileOnly);
friend struct VulkanDeviceTestAccess;
// The device's Graphics::Context: a copy of the one built at setup (its instance functions
// resolved then, its function table filled then), or with APS5_NO_CONTEXT_CACHE=1 built anew.
Graphics::Context graphicsContext() const;

View File

@ -0,0 +1,265 @@
#include "prx/libSceAgcDriver/Execution/include/ComputePipelineCapture.hpp"
#include <algorithm>
#include <fstream>
#include <iomanip>
#include <limits>
#include <mutex>
#include <set>
#include <sstream>
#include <stdexcept>
namespace AgcDriver {
namespace {
void RequireCapture(bool condition, const char* message) {
if (!condition) throw std::runtime_error(std::string("compute pipeline capture: ") + message);
}
std::string Hash(std::span<const std::byte> bytes) {
std::uint64_t hash = 14695981039346656037ull;
for (const auto value : bytes) {
hash ^= std::to_integer<std::uint8_t>(value);
hash *= 1099511628211ull;
}
std::ostringstream result;
result << std::hex << std::setfill('0') << std::setw(16) << hash;
return result.str();
}
class Encoder {
public:
void Word(std::uint32_t value) {
for (unsigned shift = 0; shift < 32; shift += 8) bytes.push_back(static_cast<std::byte>((value >> shift) & 0xffu));
}
void String(const std::string& value) {
Word(static_cast<std::uint32_t>(value.size()));
for (const auto character : value) bytes.push_back(static_cast<std::byte>(static_cast<unsigned char>(character)));
}
std::vector<std::byte> bytes;
};
class Decoder {
public:
explicit Decoder(std::span<const std::byte> bytes) : bytes(bytes) {}
std::uint32_t Word() {
RequireCapture(bytes.size() - cursor >= 4, "truncated integer");
std::uint32_t value = 0;
for (unsigned shift = 0; shift < 32; shift += 8) value |= std::to_integer<std::uint32_t>(bytes[cursor++]) << shift;
return value;
}
std::uint32_t Count(std::uint32_t maximum, std::size_t minimumBytes) {
const auto count = Word();
RequireCapture(count <= maximum && count <= (bytes.size() - cursor) / minimumBytes, "count exceeds record bounds");
return count;
}
std::string String() {
const auto count = Count(VK_MAX_PHYSICAL_DEVICE_NAME_SIZE - 1, 1);
std::string result;
for (std::uint32_t index = 0; index < count; ++index) result.push_back(static_cast<char>(std::to_integer<unsigned char>(bytes[cursor++])));
return result;
}
bool Finished() const { return cursor == bytes.size(); }
private:
std::span<const std::byte> bytes;
std::size_t cursor = 0;
};
std::vector<std::byte> ReadBytes(const std::filesystem::path& path) {
const auto size = std::filesystem::file_size(path);
RequireCapture(size <= ComputePipelineCapture::MaxFileBytes, "file exceeds 36 MiB");
std::ifstream input(path, std::ios::binary);
RequireCapture(input.good(), "cannot open capture");
std::vector<std::byte> bytes(static_cast<std::size_t>(size));
input.read(reinterpret_cast<char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
RequireCapture(input.good() && input.peek() == std::char_traits<char>::eof(), "capture changed while reading");
return bytes;
}
}
void ComputePipelineCapture::Validate() const {
RequireCapture(spirv.size() >= 5 && spirv.size() <= MaxSpirvWords, "SPIR-V word count out of bounds");
RequireCapture(spirv[0] == 0x07230203u && spirv[1] >= 0x00010000u && spirv[1] <= 0x00010600u && (spirv[1] & 0xffu) == 0 && spirv[3] != 0 && spirv[3] <= 0x003fffffu && spirv[4] == 0, "invalid SPIR-V header");
bool mainEntry = false;
for (std::size_t cursor = 5; cursor < spirv.size();) {
const auto count = spirv[cursor] >> 16u;
RequireCapture(count != 0 && count <= spirv.size() - cursor, "truncated SPIR-V instruction");
if ((spirv[cursor] & 0xffffu) == 15u) {
RequireCapture(count >= 5 && spirv[cursor + 1] == 5u && spirv[cursor + 2] != 0 && spirv[cursor + 2] < spirv[3] && spirv[cursor + 3] == 0x6e69616du && spirv[cursor + 4] == 0, "only a compute main entry point is supported");
RequireCapture(!mainEntry, "multiple entry points are unsupported");
mainEntry = true;
}
cursor += count;
}
RequireCapture(mainEntry, "compute main entry point missing");
RequireCapture(deviceName.size() < VK_MAX_PHYSICAL_DEVICE_NAME_SIZE && deviceName.find('\0') == std::string::npos, "invalid device name");
RequireCapture((flags & ~(VK_PIPELINE_CREATE_DISPATCH_BASE_BIT | VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR)) == 0, "unsupported pipeline flags");
RequireCapture(stageFlags == 0, "unsupported shader stage flags");
RequireCapture(requiredSubgroupSize == 0 || (requiredSubgroupSize <= 128 && (requiredSubgroupSize & (requiredSubgroupSize - 1u)) == 0), "invalid required subgroup size");
RequireCapture(sets.size() <= 8, "too many descriptor sets");
std::size_t totalBindings = 0;
for (const auto& layout : sets) {
RequireCapture((layout.flags & ~VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT) == 0, "unsupported descriptor layout flags");
RequireCapture(layout.bindings.size() <= 4096, "too many descriptor bindings");
totalBindings += layout.bindings.size();
std::set<std::uint32_t> used;
const auto highest = layout.bindings.empty() ? 0u : std::ranges::max(layout.bindings, {}, &ComputeDescriptorBinding::binding).binding;
for (const auto& binding : layout.bindings) {
RequireCapture(used.insert(binding.binding).second, "duplicate descriptor binding");
RequireCapture(binding.type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || binding.type == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || binding.type == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE || binding.type == VK_DESCRIPTOR_TYPE_SAMPLER, "unsupported descriptor type");
RequireCapture(binding.count != 0 && binding.count <= 1048576, "invalid descriptor count");
RequireCapture(binding.stages == VK_SHADER_STAGE_COMPUTE_BIT, "unsupported descriptor stage flags");
constexpr VkDescriptorBindingFlags supported = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT;
RequireCapture((binding.flags & ~supported) == 0, "unsupported descriptor binding flags");
RequireCapture((binding.flags & VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT) == 0 || (binding.type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER && (layout.flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT) != 0), "unsupported update-after-bind descriptor");
RequireCapture((binding.flags & VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT) == 0 || binding.binding == highest, "variable descriptor count must use the highest binding");
}
}
RequireCapture(totalBindings <= 4096, "too many total descriptor bindings");
RequireCapture(pushConstants.size() <= 1, "too many compute push constant ranges");
for (const auto& range : pushConstants) RequireCapture(range.stageFlags == VK_SHADER_STAGE_COMPUTE_BIT && range.size != 0 && range.size <= 4096 && range.offset <= 4096 - range.size && (range.offset % 4) == 0 && (range.size % 4) == 0, "invalid push constant range");
RequireCapture(specialization.size() <= 4096, "too many specialization values");
std::set<std::uint32_t> specializationIds;
for (const auto& value : specialization) RequireCapture(specializationIds.insert(value.id).second, "duplicate specialization ID");
}
std::vector<std::byte> ComputePipelineCapture::Encode() const {
Validate();
Encoder output;
output.Word(0x35535041u);
output.Word(0x504d4f43u);
output.Word(Version);
output.Word(flags);
output.Word(stageFlags);
output.Word(requiredSubgroupSize);
output.Word(vendorId);
output.Word(deviceId);
output.Word(driverVersion);
output.Word(apiVersion);
for (const auto value : pipelineCacheUuid) output.Word(value);
output.String(deviceName);
output.Word(static_cast<std::uint32_t>(sets.size()));
for (const auto& layout : sets) {
output.Word(layout.flags);
output.Word(static_cast<std::uint32_t>(layout.bindings.size()));
for (const auto& binding : layout.bindings) {
output.Word(binding.binding);
output.Word(static_cast<std::uint32_t>(binding.type));
output.Word(binding.count);
output.Word(binding.stages);
output.Word(binding.flags);
}
}
output.Word(static_cast<std::uint32_t>(pushConstants.size()));
for (const auto& range : pushConstants) {
output.Word(range.stageFlags);
output.Word(range.offset);
output.Word(range.size);
}
output.Word(static_cast<std::uint32_t>(specialization.size()));
for (const auto& value : specialization) {
output.Word(value.id);
output.Word(value.value);
}
output.Word(static_cast<std::uint32_t>(spirv.size()));
for (const auto word : spirv) output.Word(word);
RequireCapture(output.bytes.size() <= MaxFileBytes, "encoded capture exceeds size bound");
return std::move(output.bytes);
}
ComputePipelineCapture ComputePipelineCapture::Decode(std::span<const std::byte> bytes) {
RequireCapture(bytes.size() <= MaxFileBytes, "record exceeds 36 MiB");
Decoder input(bytes);
RequireCapture(input.Word() == 0x35535041u && input.Word() == 0x504d4f43u, "invalid file signature");
RequireCapture(input.Word() == Version, "unsupported file version");
ComputePipelineCapture result;
result.flags = input.Word();
result.stageFlags = input.Word();
result.requiredSubgroupSize = input.Word();
result.vendorId = input.Word();
result.deviceId = input.Word();
result.driverVersion = input.Word();
result.apiVersion = input.Word();
for (auto& value : result.pipelineCacheUuid) {
const auto word = input.Word();
RequireCapture(word <= 255, "invalid cache UUID byte");
value = static_cast<std::uint8_t>(word);
}
result.deviceName = input.String();
result.sets.resize(input.Count(8, 8));
std::size_t totalBindings = 0;
for (auto& layout : result.sets) {
layout.flags = input.Word();
const auto count = input.Count(4096, 20);
totalBindings += count;
RequireCapture(totalBindings <= 4096, "too many total descriptor bindings");
layout.bindings.resize(count);
for (auto& binding : layout.bindings) {
binding.binding = input.Word();
binding.type = static_cast<VkDescriptorType>(input.Word());
binding.count = input.Word();
binding.stages = input.Word();
binding.flags = input.Word();
}
}
result.pushConstants.resize(input.Count(1, 12));
for (auto& range : result.pushConstants) {
range.stageFlags = input.Word();
range.offset = input.Word();
range.size = input.Word();
}
result.specialization.resize(input.Count(4096, 8));
for (auto& value : result.specialization) {
value.id = input.Word();
value.value = input.Word();
}
result.spirv.resize(input.Count(MaxSpirvWords, 4));
for (auto& word : result.spirv) word = input.Word();
RequireCapture(input.Finished(), "trailing bytes after capture");
result.Validate();
return result;
}
ComputePipelineCapture ComputePipelineCapture::Read(const std::filesystem::path& path) {
return Decode(ReadBytes(path));
}
std::string ComputePipelineCapture::Identity() const {
return Hash(Encode());
}
std::filesystem::path ComputePipelineCapture::Write(const std::filesystem::path& directory) const {
const auto bytes = Encode();
const auto recordDirectory = directory / ("compute-" + Hash(bytes));
const auto path = recordDirectory / "capture.aps5cp";
static std::mutex mutex;
std::lock_guard lock(mutex);
std::filesystem::create_directories(directory);
RequireCapture(!std::filesystem::is_symlink(std::filesystem::symlink_status(directory)), "capture directory is a symbolic link");
if (std::filesystem::exists(recordDirectory)) {
RequireCapture(std::filesystem::is_directory(recordDirectory) && !std::filesystem::is_symlink(std::filesystem::symlink_status(recordDirectory)) && !std::filesystem::is_symlink(std::filesystem::symlink_status(path)), "existing capture path is not a regular capture");
RequireCapture(ReadBytes(path) == bytes, "existing capture differs or is incomplete");
return path;
}
std::uintmax_t totalBytes = 0;
std::size_t count = 0;
for (const auto& entry : std::filesystem::directory_iterator(directory)) {
RequireCapture(++count < 128, "capture directory reached 128 entries");
RequireCapture(entry.is_directory() && !entry.is_symlink(), "capture directory contains an unexpected entry");
const auto capture = entry.path() / "capture.aps5cp";
RequireCapture(std::filesystem::is_regular_file(capture) && !std::filesystem::is_symlink(std::filesystem::symlink_status(capture)), "capture directory contains an incomplete record");
const auto size = std::filesystem::file_size(capture);
RequireCapture(size <= MaxFileBytes, "existing capture exceeds size bound");
totalBytes += size;
}
RequireCapture(totalBytes + bytes.size() <= 256u * 1024u * 1024u, "capture directory exceeds 256 MiB");
RequireCapture(std::filesystem::create_directory(recordDirectory), "capture directory was concurrently created");
std::ofstream output(path, std::ios::binary);
RequireCapture(output.good(), "cannot create capture");
output.write(reinterpret_cast<const char*>(bytes.data()), static_cast<std::streamsize>(bytes.size()));
output.close();
RequireCapture(output.good(), "cannot finish capture");
return path;
}
}

View File

@ -1,5 +1,6 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Diagnostics.hpp"
#include <cstdlib>
#include <stdexcept>
namespace AgcDriver::DriverDetail {
@ -9,6 +10,11 @@ double TraceMs() {
return std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - origin).count();
}
std::uint64_t SkippedShader() {
static const std::uint64_t skipped = [] { const char* text = std::getenv("APS5_SKIP_SHADER"); return text ? std::strtoull(text, nullptr, 16) : 0ull; }();
return skipped;
}
void require(bool condition, const char* reason) {
if (!condition) {
throw std::runtime_error(std::string("AGC driver: ") + reason);

View File

@ -16,6 +16,7 @@ namespace AgcDriver::DriverDetail {
void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet, const Submission& submission, std::uint64_t indirectArguments) {
PerformanceTimer timing("Driver.Dispatch");
const auto address = (static_cast<std::uint64_t>(readRegister(queue.shader, 0x20c)) << 8u) | (static_cast<std::uint64_t>(readRegister(queue.shader, 0x20d) & 0xffu) << 40u);
if (address == SkippedShader() && address != 0) return;
auto registeredShader = RegisteredProgram(*submission.shaders, address, {0});
if (!registeredShader) registeredShader = ReadRawShader(address);
const auto& snapshot = *registeredShader;
@ -75,6 +76,9 @@ void Driver::dispatch(QueueState& queue, std::span<const std::uint32_t> packet,
if (!partialLastGroup && indirectArguments == 0 && matchesCopyKernel(std::span(snapshot.code).subspan(codeOffset), userData, compute)) return;
} else {
if (const auto htile = Graphics::HtileDepthClearAddress(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads); htile != 0) Graphics::NoteHtileDepthClear(htile);
if (indirectArguments == 0 && (packet[4] & 0x20u) == 0) {
if (const auto fill = Graphics::MatchMemoryValueFill(std::span(snapshot.code).subspan(codeOffset), userData, compute.numThreads, {packet[1], packet[2], packet[3]})) Graphics::NoteMemoryValueFill(*fill);
}
if (!partialLastGroup && fillBuffer(queue, submission.queue, packet, std::span(snapshot.code).subspan(codeOffset), userData, compute, localDevice)) {
pendingDispatchPhases().outcome = DispatchOutcome::FillHle;
return;

View File

@ -1,4 +1,5 @@
#include "prx/libSceAgcDriver/Execution/include/ProfileOutput.hpp"
#include "ThreadOwned.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/DeferredLabels.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
@ -10,8 +11,8 @@
namespace AgcDriver::DriverDetail {
std::map<std::size_t, Driver::ResolvedDispatch>& Driver::resolvedAhead() {
static thread_local std::map<std::size_t, ResolvedDispatch> resolved;
return resolved;
static thread_local std::map<std::size_t, ResolvedDispatch>* resolved = nullptr;
return ShaderRecompiler::ThreadOwned(resolved);
}
Driver::GroupCaptureStats& Driver::groupCaptureStats() {
@ -178,6 +179,7 @@ void Driver::resolveGroupAhead(const Submission& submission, const QueueState& l
const auto label = Pm4::DecodeLabelWrite(packet);
if (!label) break;
NoteLabel(labels, label->address, label->Bytes());
writes.emplace_back(label->address, label->address + label->Bytes().size());
} else if (opcode != 0x3f && opcode != 0x42 && opcode != 0x46 && opcode != 0x58) {
if (Pm4::AccessesMemory(header)) break;
try {

View File

@ -4,6 +4,7 @@
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "Optimization/ResourceProgram.hpp"
#include <algorithm>
#include <cstdlib>
namespace AgcDriver::DriverDetail {
@ -92,8 +93,9 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
resolveDrawDecode(queue, submission, decode, registerKey, drawKey, profile);
const auto& graphics = decode->state;
const auto& pixel = decode->pixel;
auto pixel = decode->pixel;
std::vector<DrawProgram> programs = decode->programs;
if (const auto skipped = SkippedShader(); skipped != 0 && std::any_of(programs.begin(), programs.end(), [skipped](const DrawProgram& program) { return program.binary.codeAddress == skipped; })) return drawn();
const auto setMeshIndexBuffer = [&](const Pm4::DrawParameters& parameters) {
if (!graphics.stages.mesh) return;
auto& words = programs.front().userData;
@ -144,7 +146,8 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
const auto& program = programs[i];
if (program.binary.stage == Stage::Fragment || roles[i] == Role::GeometryBack) return;
decodeReads[i].clear();
vertexInfos[i] = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, &decodeReads[i]);
vertexInfos[i] = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, &decodeReads[i], false, 8u - program.firstUserSgpr);
if (vertexInfos[i]) vertexInfos[i]->paClVsOutCntl = PositionExportLayout(program.snapshot.get(), program.codeOffset, program.binary.code, readRegister(queue.context, 0x207)) | graphics.layerExports;
};
if (!registerKey) {
for (std::size_t i = 0; i < programs.size(); ++i) decodeVertexInfo(i);
@ -204,6 +207,13 @@ DrawVerdict Driver::draw(QueueState& queue, std::span<const std::uint32_t> packe
memory.insert(memory.end(), matchedRegions[i].begin(), matchedRegions[i].end());
} else {
resultIndex[i] = results.size();
if (program.binary.stage == ShaderRecompiler::ShaderStage::Fragment) {
for (auto previous = i; previous-- > 0;) {
if (programResults[previous] == nullptr) continue;
pixel = Graphics::DefaultUnexportedInputs(decode->pixel, programResults[previous]->parameterExports);
break;
}
}
results.push_back(materializeDrawStage(i, pushCursorBytes, queue, submission, programs, graphics, pixel, vertexInfos, memory, linked, drawParameters, localDevice, shaderMemory, stageCaptures, recompiled, drawHit, matched, matchedRegions, profile, dumpTarget, dumpSlot1, captures, phaseTiming, phaseMs, rejected));
if (!rejected.empty()) return DrawVerdict::Rejected;
programResults[i] = &results.back();

View File

@ -1,6 +1,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawCache.hpp"
#include <algorithm>
#include <bit>
#include <cstdlib>
namespace AgcDriver::DriverDetail {
@ -56,6 +57,36 @@ void Driver::accountDrawVariant(const DispatchVariant& variant, bool added) {
}
}
DrawKeySightings::DrawKeySightings(std::size_t keyCount) {
Graphics::Require(keyCount >= Ways, "draw key sightings need at least one bucket");
const auto buckets = std::bit_ceil(keyCount / Ways);
keys.assign(buckets * Ways, 0);
filled.assign(buckets, 0);
next.assign(buckets, 0);
bucketMask = buckets - 1;
}
bool DrawKeySightings::SeenBefore(std::uint64_t key) {
auto mixed = key;
mixed = (mixed ^ (mixed >> 30u)) * 0xbf58476d1ce4e5b9ull;
mixed = (mixed ^ (mixed >> 27u)) * 0x94d049bb133111ebull;
mixed ^= mixed >> 31u;
const auto bucket = static_cast<std::size_t>(mixed) & bucketMask;
auto* ways = keys.data() + bucket * Ways;
if (std::find(ways, ways + filled[bucket], key) != ways + filled[bucket]) return true;
ways[next[bucket]] = key;
next[bucket] = static_cast<std::uint8_t>((next[bucket] + 1u) % Ways);
if (filled[bucket] < Ways) ++filled[bucket];
return false;
}
bool Driver::admitDrawKey(std::uint64_t key) {
std::lock_guard cacheLock(drawCacheMutex);
if (drawCache.contains(key)) return true;
if (!drawKeySightings) drawKeySightings.emplace(drawCacheEntries() * DrawKeySightings::Ways);
return drawKeySightings->SeenBefore(key);
}
void Driver::insertDrawEntry(std::uint64_t key, std::vector<std::shared_ptr<DispatchVariant>>& fresh, std::shared_ptr<const DrawDecode> decode) {
std::lock_guard cacheLock(drawCacheMutex);
auto& counters = drawEntryCounters;

View File

@ -20,7 +20,7 @@ ShaderRecompiler::RecompileResult Driver::materializeDrawStage(std::size_t i, st
program.binary,
{waveSize, program.firstUserSgpr, program.userData, std::nullopt, program.binary.stage == Stage::Fragment ? std::optional(pixel) : std::nullopt, vertexInfos[i], memory, RegisteredFloatMode(*program.snapshot)},
localDevice->Target(),
{0, 0, pushOffset, (graphics.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushSlotBytes) - pushOffset % Graphics::PipelinePushSlotBytes},
{0, 0, pushOffset, (graphics.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : graphics.stages.tessellation && pushOffset < Graphics::PipelinePushSlotBytes ? ShaderRecompiler::TessellationLevelPushOffsetBytes : Graphics::PipelinePushSlotBytes) - pushOffset % Graphics::PipelinePushSlotBytes},
ShaderRecompiler::GraphicsCompileContext{program.firstUserSgpr, linked, graphics.stages.mesh, graphics.stages.tessellation, {drawParameters.indexAddress, drawParameters.indexCount, drawParameters.indexSize, drawParameters.instanceCount}}
};
const auto waitedBefore = traceCapSync() || profile ? Graphics::Recorder::ThreadWaitedMs() : 0.0;
@ -83,7 +83,7 @@ ShaderRecompiler::RecompileResult Driver::materializeDrawStage(std::size_t i, st
}
void Driver::cacheDrawStages(bool useDrawEntries, bool drawHit, const Pm4::DrawParameters& drawParameters, const std::optional<Graphics::IndirectDrawPath>& indirectCpu, const std::vector<DrawProgram>& programs, const std::vector<StageCapture>& stageCaptures, const std::vector<std::optional<ShaderRecompiler::ShaderVertexStageInfo>>& vertexInfos, const std::vector<std::vector<Graphics::DecodeRead>>& decodeReads, bool verifyHit, const std::vector<std::shared_ptr<DispatchVariant>>& matched, std::vector<std::shared_ptr<DispatchVariant>>& fresh, std::uint64_t drawKey, bool registerKey, const std::shared_ptr<const DrawDecode>& decode, DrawPhaseTiming& phaseTiming) {
if (useDrawEntries && !drawHit && !(drawParameters.indirect && indirectCpu)) {
if (useDrawEntries && !drawHit && !(drawParameters.indirect && indirectCpu) && (verifyHit || admitDrawKey(drawKey))) {
phaseTiming.Phase(DrawRowVectors);
std::uint64_t unstable = 0, mismatches = 0;
for (std::size_t i = 0; i < programs.size(); ++i) {

View File

@ -81,7 +81,7 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
};
const auto& graphics = product->state;
if (graphics.stages.path == Graphics::ShaderPath::Tessellation) {
append(0x148, 5, Stage::Local, 0x10b, 0x10c, Role::Local);
if (!staticAbi || programAddress(0x148) != 0) append(0x148, 5, Stage::Local, 0x10b, 0x10c, Role::Local);
append(0x108, 7, Stage::TessellationControl, 0x10b, 0x10c, Role::Hull);
initializeMerged(programs.back(), 0x102, true);
append(0x0c8, 2, Stage::TessellationEvaluation, 0x08b, 0x08c, Role::Domain);
@ -109,21 +109,14 @@ void DecodeGraphicsPrograms(DrawDecode& decoded, const QueueState& queue, const
std::shared_ptr<DrawDecode> Driver::decodeDraw(const QueueState& queue, const Submission& submission) {
auto product = std::make_shared<DrawDecode>();
bool vertexWorkgroup = false;
const auto stages = queue.context.find(0x2d5);
const auto low = queue.shader.find(0xc8);
const auto high = queue.shader.find(0xc9);
if (stages != queue.context.end() && (stages->second & 0x02002024u) == 0x2000u && low != queue.shader.end() && high != queue.shader.end()) {
const auto address = (static_cast<std::uint64_t>(low->second) << 8u) | (static_cast<std::uint64_t>(high->second & 0xffu) << 40u);
const auto front = ProgramSnapshot(*submission.shaders, address, {2});
vertexWorkgroup = front->header.empty() && ProgramUsesWorkgroup(*front, static_cast<std::size_t>((address - front->codeAddress) / sizeof(std::uint32_t)));
}
product->state = Graphics::DecodeState(queue, vertexWorkgroup);
product->state = Graphics::DecodeState(queue, VertexWorkgroupRequired(queue, *submission.shaders));
DecodeGraphicsPrograms(*product, queue, *submission.shaders, false, true);
product->pixel = Graphics::DecodePixelStageInfo(queue.context, Graphics::ExportMappings(product->state), Graphics::PixelProgramSkipped(queue));
product->pixel.targetExportPacking = Graphics::ExportPackings(product->state);
product->pixel.dualSourceBlend = product->state.dualSourceBlend;
product->pixel.alphaToCoverage = product->state.alphaToCoverage;
if (product->pixel.ancillary) product->pixel.rasterPrimitive = Graphics::RasterPrimitiveOf(product->state);
if (product->pixel.ancillary) product->pixel.layerExported = product->state.layerExports != 0;
return product;
}

View File

@ -26,7 +26,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
mix(graphics.stages.tessellation.has_value());
if (graphics.stages.tessellation) {
const auto& tess = *graphics.stages.tessellation;
for (const auto value : {tess.inputControlPoints, tess.outputControlPoints, tess.domain, tess.partitioning, tess.outputTopology}) mix(value);
for (const auto value : {tess.inputControlPoints, tess.outputControlPoints, tess.domain, tess.partitioning, tess.outputTopology, tess.patchesPerGroup}) mix(value);
}
mix(graphics.rectList);
mix(programs.size());
@ -48,6 +48,7 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
mix(vertex.fetchAttribReg);
mix(vertex.fetchBufferReg);
mix(vertex.fetchEmbedded);
mix(vertex.paClVsOutCntl);
for (std::uint32_t r = 0; r < vertex.resourcesNum; ++r) {
for (const auto field : vertex.resources[r].fields) mix(field);
const auto& destination = vertex.resourcesDst[r];
@ -64,11 +65,13 @@ void Driver::lookupDraw(const Submission& submission, const std::shared_ptr<Vulk
for (const bool flag : {pixel.wave32, pixel.hasPerspectiveCenterVgpr, pixel.perspectiveCentroid, pixel.posX, pixel.posY, pixel.posZ, pixel.posW, pixel.frontFace, pixel.ancillary, pixel.sampleShading, pixel.noPerspective, pixel.linearCentroid, pixel.pixelKillEnable, pixel.depthExportEnable, pixel.sampleMaskExportEnable, pixel.earlyZ, pixel.executeOnNoop}) mix(flag);
mix(static_cast<std::uint64_t>(pixel.conservativeZExport));
mix(pixel.orderedPixelShader);
mix(pixel.stencilRefExportEnable);
for (const auto value : pixel.targetOutputMode) mix(value);
for (const auto value : pixel.targetExportMapping) mix(value);
for (const auto value : pixel.targetExportPacking) mix(static_cast<std::uint64_t>(value));
mix(pixel.dualSourceBlend);
mix(pixel.alphaToCoverage);
mix(static_cast<std::uint64_t>(pixel.rasterPrimitive));
std::lock_guard cacheLock(drawCacheMutex);
++drawEntryCounters.lookups;
const auto found = drawCache.find(drawKey);

View File

@ -23,6 +23,7 @@ std::optional<DrawVerdict> Driver::precheckDraw(const QueueState& queue, const S
localDevice->ColorMetadataPass(*pass);
return DrawVerdict::Drawn;
}
if (Graphics::DepthResummarizeDrawsNothing(queue)) return DrawVerdict::Nothing;
rejected = Graphics::DepthMaintenanceRejection(queue);
if (!rejected.empty()) return DrawVerdict::Rejected;
static const bool traceIndirectEnabled = std::getenv("APS5_TRACE_INDIRECT_DRAWS") != nullptr;

View File

@ -1,4 +1,5 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Draw/DrawRegisterKey.hpp"
#include <algorithm>
#include <array>
#include <cstdlib>
@ -6,36 +7,69 @@
namespace AgcDriver::DriverDetail {
namespace {
bool runtimeRegister(std::uint32_t offset) {
return offset - 0x00cu < 32u || offset - 0x08cu < 32u || offset - 0x10cu < 32u || offset == 0x082u || offset == 0x083u || offset == 0x102u || offset == 0x103u;
}
const auto& registerMasks() {
static const auto masks = [] {
std::array<std::vector<std::uint64_t>, 3> bits;
for (const auto& range : Graphics::DrawKeyRegisters) {
auto& bank = bits[static_cast<std::size_t>(range.bank)];
bank.resize(std::max(bank.size(), (static_cast<std::size_t>(range.first) + range.count + 63) / 64));
for (auto offset = range.first; offset < range.first + range.count; ++offset) {
if (range.bank == Graphics::RegisterBank::Shader && runtimeRegister(offset)) continue;
bank[offset / 64] |= std::uint64_t{1} << (offset % 64);
}
}
std::array<std::shared_ptr<const Registers::Mask>, 3> result;
for (std::size_t i = 0; i < bits.size(); ++i) result[i] = std::make_shared<const Registers::Mask>(std::move(bits[i]));
return result;
}();
return masks;
}
}
std::uint64_t RegisterStateKey(const QueueState& queue, bool allUserWords, bool incremental) {
const auto& masks = registerMasks();
const auto fingerprint = [&](const Registers& registers, Graphics::RegisterBank bank) {
const auto& mask = masks[static_cast<std::size_t>(bank)];
return incremental ? registers.Fingerprint(mask) : registers.RecomputeFingerprint(*mask);
};
auto shape = (0xcbf29ce484222325ull ^ fingerprint(queue.context, Graphics::RegisterBank::Context)) * 0x100000001b3ull;
shape = (shape ^ fingerprint(queue.shader, Graphics::RegisterBank::Shader)) * 0x100000001b3ull;
shape = (shape ^ fingerprint(queue.userConfig, Graphics::RegisterBank::UserConfig)) * 0x100000001b3ull;
auto exact = shape;
const auto words = [&](std::uint32_t base, std::uint32_t count) {
for (auto it = queue.shader.lower_bound(base); it != queue.shader.end() && it->first < base + count; ++it) {
exact = (exact ^ it->first) * 0x100000001b3ull;
exact = (exact ^ it->second) * 0x100000001b3ull;
}
};
for (const auto base : {0x00cu, 0x08cu, 0x10cu}) {
const auto resources = queue.shader.find(base - 1);
const auto count = resources == queue.shader.end() ? 0u : ((resources->second >> 1u) & 0x1fu) | (((resources->second >> 27u) & 1u) << 5u);
words(base, allUserWords ? 32u : std::min(count, 32u));
}
words(0x082u, 2);
words(0x102u, 2);
return exact;
}
std::uint64_t Driver::drawRegisterKey(const QueueState& queue, const ShaderRegistry& registry, std::uint64_t deviceSerial) {
static const bool allUserWords = std::getenv("APS5_DRAW_KEY_ALL_USER_WORDS") != nullptr;
std::uint64_t key = 0xcbf29ce484222325ull;
static const bool verify = std::getenv("APS5_VERIFY_REGISTER_KEYS") != nullptr;
auto key = RegisterStateKey(queue, allUserWords);
if (verify && key != RegisterStateKey(queue, allUserWords, false)) throw std::runtime_error("incremental register fingerprint disagrees with full state");
const auto mix = [&](std::uint64_t value) {
key ^= value;
key *= 0x100000001b3ull;
};
const auto userEnd = [&](std::uint32_t base) {
if (allUserWords) return base + 32u;
const auto resources = queue.shader.find(base - 1);
if (resources == queue.shader.end()) return base;
const auto count = ((resources->second >> 1u) & 0x1fu) | (((resources->second >> 27u) & 1u) << 5u);
return base + std::min(count, 32u);
};
const std::array<std::pair<std::uint32_t, std::uint32_t>, 3> users{{{0x00cu, userEnd(0x00cu)}, {0x08cu, userEnd(0x08cu)}, {0x10cu, userEnd(0x10cu)}}};
const auto unread = [&](std::uint32_t offset) {
return std::any_of(users.begin(), users.end(), [&](const auto& user) { return offset >= user.second && offset < user.first + 32u; });
};
mix(deviceSerial);
for (const auto& range : Graphics::DrawKeyRegisters) {
const auto& bank = range.bank == Graphics::RegisterBank::Context ? queue.context : range.bank == Graphics::RegisterBank::Shader ? queue.shader : queue.userConfig;
mix((static_cast<std::uint64_t>(range.bank) << 32u) | range.first);
const auto end = range.first + range.count;
const bool shader = range.bank == Graphics::RegisterBank::Shader;
for (auto it = bank.lower_bound(range.first); it != bank.end() && it->first < end; ++it) {
if (shader && unread(it->first)) continue;
mix(it->first);
mix(it->second);
}
}
for (const auto base : {0x008u, 0x088u, 0x0c8u, 0x108u, 0x148u}) {
const auto low = queue.shader.find(base);
const auto high = queue.shader.find(base + 1);
@ -57,7 +91,7 @@ std::uint64_t Driver::drawRegisterKey(const QueueState& queue, const ShaderRegis
}
bool Driver::sameVertexInfo(const ShaderRecompiler::ShaderVertexStageInfo& a, const ShaderRecompiler::ShaderVertexStageInfo& b) {
if (a.resourcesNum != b.resourcesNum || a.fetchAttribReg != b.fetchAttribReg || a.fetchBufferReg != b.fetchBufferReg || a.fetchEmbedded != b.fetchEmbedded) return false;
if (a.resourcesNum != b.resourcesNum || a.fetchAttribReg != b.fetchAttribReg || a.fetchBufferReg != b.fetchBufferReg || a.fetchEmbedded != b.fetchEmbedded || a.paClVsOutCntl != b.paClVsOutCntl) return false;
for (std::uint32_t i = 0; i < a.resourcesNum && i < a.resources.size(); ++i) {
if (a.resources[i].fields != b.resources[i].fields) return false;
const auto& x = a.resourcesDst[i];
@ -84,7 +118,7 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
const auto sameTess = [](const std::optional<ShaderRecompiler::TessellationConfiguration>& x, const std::optional<ShaderRecompiler::TessellationConfiguration>& y) {
if (x.has_value() != y.has_value()) return false;
if (!x) return true;
return x->inputControlPoints == y->inputControlPoints && x->outputControlPoints == y->outputControlPoints && x->domain == y->domain && x->partitioning == y->partitioning && x->outputTopology == y->outputTopology;
return x->inputControlPoints == y->inputControlPoints && x->outputControlPoints == y->outputControlPoints && x->domain == y->domain && x->partitioning == y->partitioning && x->outputTopology == y->outputTopology && x->patchesPerGroup == y->patchesPerGroup;
};
if (s.stages.path != t.stages.path || s.stages.registerValue != t.stages.registerValue || s.stages.vertexWaveSize != t.stages.vertexWaveSize || s.stages.fragmentWaveSize != t.stages.fragmentWaveSize || !sameMesh(s.stages.mesh, t.stages.mesh) || !sameTess(s.stages.tessellation, t.stages.tessellation)) return false;
if (!sameColor(s.color, t.color) || s.colors.size() != t.colors.size() || s.blends.size() != t.blends.size()) return false;
@ -98,14 +132,14 @@ bool Driver::sameDecode(const DrawDecode& a, const DrawDecode& b) {
if (std::memcmp(&s.viewport, &t.viewport, sizeof(VkViewport)) != 0 || std::memcmp(&s.scissor, &t.scissor, sizeof(VkRect2D)) != 0) return false;
const auto& p = a.pixel;
const auto& q = b.pixel;
if (p.interpolatorCount != q.interpolatorCount || p.interpolatorSettings != q.interpolatorSettings || p.wave32 != q.wave32 || p.inputAddr != q.inputAddr || p.hasPerspectiveCenterVgpr != q.hasPerspectiveCenterVgpr || p.perspectiveCentroid != q.perspectiveCentroid || p.posX != q.posX || p.posY != q.posY || p.posZ != q.posZ || p.posW != q.posW || p.frontFace != q.frontFace || p.ancillary != q.ancillary || p.sampleShading != q.sampleShading || p.noPerspective != q.noPerspective || p.linearCentroid != q.linearCentroid || p.pixelKillEnable != q.pixelKillEnable || p.depthExportEnable != q.depthExportEnable || p.sampleMaskExportEnable != q.sampleMaskExportEnable || p.earlyZ != q.earlyZ || p.executeOnNoop != q.executeOnNoop || p.conservativeZExport != q.conservativeZExport || p.orderedPixelShader != q.orderedPixelShader || p.targetOutputMode != q.targetOutputMode || p.targetExportMapping != q.targetExportMapping) return false;
if (p.interpolatorCount != q.interpolatorCount || p.interpolatorSettings != q.interpolatorSettings || p.wave32 != q.wave32 || p.inputAddr != q.inputAddr || p.hasPerspectiveCenterVgpr != q.hasPerspectiveCenterVgpr || p.perspectiveCentroid != q.perspectiveCentroid || p.posX != q.posX || p.posY != q.posY || p.posZ != q.posZ || p.posW != q.posW || p.frontFace != q.frontFace || p.ancillary != q.ancillary || p.sampleShading != q.sampleShading || p.noPerspective != q.noPerspective || p.linearCentroid != q.linearCentroid || p.pixelKillEnable != q.pixelKillEnable || p.depthExportEnable != q.depthExportEnable || p.sampleMaskExportEnable != q.sampleMaskExportEnable || p.earlyZ != q.earlyZ || p.executeOnNoop != q.executeOnNoop || p.conservativeZExport != q.conservativeZExport || p.orderedPixelShader != q.orderedPixelShader || p.stencilRefExportEnable != q.stencilRefExportEnable || p.targetOutputMode != q.targetOutputMode || p.targetExportMapping != q.targetExportMapping) return false;
if (a.roles != b.roles || a.programs.size() != b.programs.size()) return false;
for (std::size_t i = 0; i < a.programs.size(); ++i) {
const auto& x = a.programs[i];
const auto& y = b.programs[i];
if (x.binary.stage != y.binary.stage || x.binary.codeAddress != y.binary.codeAddress || x.userDataBase != y.userDataBase || x.firstUserSgpr != y.firstUserSgpr || x.userData != y.userData || x.snapshot != y.snapshot || x.codeOffset != y.codeOffset) return false;
}
if (p.targetExportPacking != q.targetExportPacking || p.dualSourceBlend != q.dualSourceBlend || p.alphaToCoverage != q.alphaToCoverage) return false;
if (p.targetExportPacking != q.targetExportPacking || p.dualSourceBlend != q.dualSourceBlend || p.alphaToCoverage != q.alphaToCoverage || p.rasterPrimitive != q.rasterPrimitive) return false;
return true;
}

View File

@ -123,13 +123,19 @@ extern "C" void AgcDriverReportFailure_nid_postfix(std::exception_ptr error) {
AgcDriver::ReportFailure(error);
}
extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
extern "C" void AgcDriverResolveShaderAbi_nid_postfix(const Shader* shader, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) try {
AgcDriver::DriverDetail::Driver::Get().ResolveShaderAbi(shader, context, primitive);
} catch (const ProcessShutdown&) {
LibcAwaitExit_nid_postfix();
}
extern "C" void AgcDriverResolveGraphicsAbi_nid_postfix(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType) {
extern "C" void AgcDriverResolveGraphicsAbi_nid_postfix(const Shader* vertex, const Shader* pixel, std::uint32_t primitiveType) try {
AgcDriver::DriverDetail::Driver::Get().ResolveGraphicsAbi(vertex, pixel, primitiveType);
} catch (const ProcessShutdown&) {
LibcAwaitExit_nid_postfix();
}
extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) {
extern "C" void AgcDriverResolveGraphicsStagesAbi_nid_postfix(std::span<const Shader* const> stages, std::span<const ShaderRegister> context, std::span<const ShaderRegister> primitive) try {
AgcDriver::DriverDetail::Driver::Get().ResolveGraphicsStagesAbi(stages, context, primitive);
} catch (const ProcessShutdown&) {
LibcAwaitExit_nid_postfix();
}

View File

@ -3,13 +3,68 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/WorkerSampler.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTrace.hpp"
#include "prx/libSceAgcDriver/Execution/include/ProfileOutput.hpp"
#include "prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp"
#include "prx/libc/include/GuestAllocations.hpp"
#include <atomic>
#include <cstdint>
#include <cstdio>
#include <system_error>
#include <thread>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <pthread.h>
#endif
namespace AgcDriver::DriverDetail {
namespace {
constexpr std::uintptr_t InlineImportStackBytes = 256 * 1024;
std::atomic<std::uint64_t> gpuMapImportThreads{0};
bool StackHoldsImport() {
#ifdef _WIN32
ULONG_PTR low = 0;
ULONG_PTR high = 0;
GetCurrentThreadStackLimits(&low, &high);
#else
struct Bounds {
std::uintptr_t low = 0;
std::uintptr_t high = 0;
Bounds() {
pthread_attr_t attributes;
if (pthread_getattr_np(pthread_self(), &attributes) != 0) return;
void* base = nullptr;
std::size_t size = 0;
if (pthread_attr_getstack(&attributes, &base, &size) == 0 && base != nullptr) {
low = reinterpret_cast<std::uintptr_t>(base);
high = low + size;
}
pthread_attr_destroy(&attributes);
}
};
thread_local const Bounds bounds;
const auto low = bounds.low;
const auto high = bounds.high;
#endif
const volatile unsigned char marker = 0;
const auto frame = reinterpret_cast<std::uintptr_t>(&marker);
return frame >= low && frame < high && frame - low >= InlineImportStackBytes;
}
}
std::uint64_t Driver::GpuMapImportThreads() {
return gpuMapImportThreads.load(std::memory_order_relaxed);
}
Driver& Driver::Get() {
static Driver driver;
return driver;
@ -50,11 +105,13 @@ void Driver::stop() {
device.Reset();
replacedDevices.clear();
Graphics::ShutdownGuestBufferWorkers();
PerformanceTrace::StopOutput();
StopProfileOutput_nid_no_patch();
stopped = true;
}
Driver::Driver() {
static_cast<void>(GuestMemory::TrackerGeneration());
ShaderMemory::SetWaitedMsProvider(&Graphics::Recorder::ThreadWaitedMs);
try {
LibcRegisterShutdown_nid_postfix([] { Driver::Get().Shutdown(); });
@ -63,14 +120,29 @@ Driver::Driver() {
throw;
}
GuestAllocations::GuestAllocationsSetGpuMapObserver_nid_postfix([](const GuestAllocations::Mapped& ranges, std::uint64_t generation) {
try {
const auto current = Driver::Get().device.Load();
if (current == nullptr || current->ImportGuestMemory(ranges, generation, false)) return;
std::lock_guard lock(GuestMemory::GpuMutex());
if (Driver::Get().device.Load() == current) current->ImportGuestMemory(ranges, generation, true);
} catch (const std::exception& error) {
std::fprintf(stderr, "[gpu] import of GPU memory at its mapping failed: %s\n", error.what());
const auto import = [&] {
try {
const auto current = Driver::Get().device.Load();
if (current == nullptr || current->ImportGuestMemory(ranges, generation, false)) return;
std::lock_guard lock(GuestMemory::GpuMutex());
if (Driver::Get().device.Load() == current) current->ImportGuestMemory(ranges, generation, true);
} catch (const std::exception& error) {
std::fprintf(stderr, "[gpu] import of GPU memory at its mapping failed: %s\n", error.what());
}
};
if (GuestMemory::GpuMutex().HeldByThisThread() || StackHoldsImport()) {
import();
return;
}
std::thread host;
try {
host = std::thread(import);
} catch (const std::system_error& error) {
std::fprintf(stderr, "[gpu] import of GPU memory at its mapping failed: no host thread to run it on: %s\n", error.what());
return;
}
gpuMapImportThreads.fetch_add(1, std::memory_order_relaxed);
host.join();
});
}

View File

@ -6,6 +6,7 @@
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include <algorithm>
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp"
#include <bit>
#include <cstdlib>
#include <optional>
@ -13,13 +14,22 @@
namespace AgcDriver::DriverDetail {
namespace {
bool matchesBufferPatternFillKernel(std::span<const std::uint32_t> code) {
static constexpr std::array<std::uint32_t, 12> kernel{0xd7460004u, 0x04010c08u, 0xf4280302u, 0xfa000000u, 0xbf8cc07fu, 0x7e00020cu, 0x7e02020du, 0x7e04020eu, 0x7e06020fu, 0xe01c2000u, 0x80000004u, 0xbf810000u};
return code.size() >= kernel.size() && std::equal(kernel.begin(), kernel.end(), code.begin());
}
}
bool Driver::matchesFillKernel(std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute) {
static const bool enabled = std::getenv("APS5_NO_FILL_HLE") == nullptr;
static const bool patternEnabled = std::getenv("APS5_NO_PATTERN_FILL_HLE") == nullptr;
if (!enabled || userData.size() < 8 || compute.numThreads[0] != 64 || compute.numThreads[1] != 1 || compute.numThreads[2] != 1) return false;
if (patternEnabled && MatchesPatternFillKernel(code, userData, compute)) return true;
static constexpr std::array<std::uint32_t, 9> fillKernel{0xd7460004u, 0x04010c08u, 0x7e000204u, 0x7e020205u, 0x7e040206u, 0x7e060207u, 0xe01c2000u, 0x80000004u, 0xbf810000u};
if (code.size() < fillKernel.size() || !std::equal(fillKernel.begin(), fillKernel.end(), code.begin())) return false;
if (!matchesBufferPatternFillKernel(code) && (code.size() < fillKernel.size() || !std::equal(fillKernel.begin(), fillKernel.end(), code.begin()))) return false;
const auto stride = (userData[1] >> 16u) & 0x3fffu;
const bool swizzled = ((userData[1] >> 31u) & 1u) != 0;
@ -48,8 +58,9 @@ std::optional<DwordPatternFill> MatchDwordPatternFill(std::span<const std::uint3
const bool swizzled = ((userData[1] >> 31u) & 1u) != 0;
const bool addTid = ((userData[3] >> 23u) & 1u) != 0;
const auto type = userData[3] >> 30u;
const auto format = (userData[3] >> 12u) & 0x7fu;
const auto period = userData[9];
if (type != 0 || stride != 4 || swizzled || addTid || (period != 1u && period != 2u && period != 4u) || (packet[4] & 0x20u) != 0 || packet[2] != 1 || packet[3] != 1) return std::nullopt;
if (type != 0 || stride != 4 || swizzled || addTid || format < 20u || format > 22u || (period != 1u && period != 2u && period != 4u) || (packet[4] & 0x20u) != 0 || packet[2] != 1 || packet[3] != 1) return std::nullopt;
const auto dwords = std::min<std::uint64_t>({static_cast<std::uint64_t>(packet[1]) * 64u, userData[8], userData[2]});
const auto base = userData[0] | (static_cast<std::uint64_t>(userData[1] & 0xffffu) << 32u);
if (dwords == 0 || dwords % 4u != 0 || base % 16u != 0) return std::nullopt;
@ -58,6 +69,50 @@ std::optional<DwordPatternFill> MatchDwordPatternFill(std::span<const std::uint3
return fill;
}
std::optional<DwordPatternFill> MatchZeroDwordFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute) {
static const bool enabled = std::getenv("APS5_NO_FILL_HLE") == nullptr;
if (!enabled || userData.size() != 4 || packet.size() < 5 || !compute.groupIdEnable[0] || compute.numThreads[0] != 64 || compute.numThreads[1] != 1 || compute.numThreads[2] != 1) return std::nullopt;
static constexpr std::array<std::uint32_t, 6> kernel{0xd7460000u, 0x04010c04u, 0x7e020280u, 0xe0102000u, 0x80000100u, 0xbf810000u};
if (code.size() < kernel.size() || !std::equal(kernel.begin(), kernel.end(), code.begin())) return std::nullopt;
const auto stride = (userData[1] >> 16u) & 0x3fffu;
const bool swizzled = ((userData[1] >> 31u) & 1u) != 0;
const bool addTid = ((userData[3] >> 23u) & 1u) != 0;
const auto outOfBounds = (userData[3] >> 28u) & 3u;
const auto format = (userData[3] >> 12u) & 0x7fu;
const auto type = userData[3] >> 30u;
if (type != 0 || stride != 4 || swizzled || addTid || outOfBounds != 0 || format < 20u || format > 22u || (packet[4] & 0x20u) != 0 || packet[2] != 1 || packet[3] != 1) return std::nullopt;
const auto dwords = std::min<std::uint64_t>(static_cast<std::uint64_t>(packet[1]) * 64u, userData[2]);
const auto base = userData[0] | (static_cast<std::uint64_t>(userData[1] & 0xffffu) << 32u);
if (dwords == 0 || dwords % 4u != 0 || base % 16u != 0) return std::nullopt;
return DwordPatternFill{base, static_cast<std::size_t>(dwords * 4u), {}};
}
std::optional<Graphics::MemoryValueFill> MatchMemoryValueBufferFill(std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute) {
static const bool enabled = std::getenv("APS5_NO_FILL_HLE") == nullptr;
if (!enabled || userData.size() != 8 || packet.size() < 5 || (packet[4] & 0x20u) != 0 || !compute.groupIdEnable[0]) return std::nullopt;
const auto fill = Graphics::MatchMemoryValueFill(code, userData, compute.numThreads, {packet[1], packet[2], packet[3]});
if (!fill) return std::nullopt;
if (fill->address % 16u != 0 || fill->bytes % 16u != 0) return std::nullopt;
return fill;
}
bool IsByteFill(const std::array<std::uint32_t, 4>& pattern) {
return std::all_of(pattern.begin(), pattern.end(), [&](std::uint32_t word) { return word == (pattern[0] & 0xffu) * 0x01010101u; });
}
std::optional<Graphics::DccKeys> KeysFillMemo(const std::array<std::uint32_t, 4>& pattern, bool coversStorageKeys, bool memoryValueFill) {
if (!IsByteFill(pattern) || !(coversStorageKeys || memoryValueFill)) return std::nullopt;
switch (pattern[0] & 0xffu) {
case 0x00: return Graphics::DccKeys::Clear0000;
case 0x40: return Graphics::DccKeys::Clear0001;
case 0x80: return Graphics::DccKeys::Clear1110;
case 0xc0: return Graphics::DccKeys::Clear1111;
case 0x20: return Graphics::DccKeys::ClearRegister;
case 0xff: return Graphics::DccKeys::Uncompressed;
default: return std::nullopt;
}
}
bool Driver::fillClearEnabled() {
static const bool enabled = std::getenv("APS5_NO_FILL_CLEAR") == nullptr;
return enabled;
@ -123,8 +178,11 @@ void Driver::fillClearCount(const Graphics::StorageTexture::FillCoverage& covera
}
bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<const std::uint32_t> packet, std::span<const std::uint32_t> code, const std::vector<std::uint32_t>& userData, const ShaderRecompiler::ShaderComputeStageInfo& compute, const std::shared_ptr<VulkanDevice>& localDevice) {
const auto dwordFill = MatchDwordPatternFill(packet, code, userData, compute);
if (!dwordFill && !matchesFillKernel(code, userData, compute)) return false;
auto dwordFill = MatchDwordPatternFill(packet, code, userData, compute);
if (!dwordFill) dwordFill = MatchZeroDwordFill(packet, code, userData, compute);
std::optional<Graphics::MemoryValueFill> memoryFill;
if (!dwordFill) memoryFill = MatchMemoryValueBufferFill(packet, code, userData, compute);
if (!dwordFill && !memoryFill && !matchesFillKernel(code, userData, compute)) return false;
std::uint64_t base;
std::size_t bytes;
std::array<std::uint32_t, 4> pattern;
@ -133,6 +191,15 @@ bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<cons
base = dwordFill->base;
bytes = dwordFill->bytes;
pattern = dwordFill->pattern;
} else if (memoryFill) {
GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Fill);
inputLock.lock();
recordLabelsForPacket(localDevice.get(), queueId);
if (!GuestMemory::Accessible(reinterpret_cast<const void*>(memoryFill->valueAddress), 4)) return false;
GuestMemory::Read(memoryFill->valueAddress, std::as_writable_bytes(std::span(pattern).first(1)), 4);
std::fill(pattern.begin() + 1, pattern.end(), pattern[0]);
base = memoryFill->address;
bytes = memoryFill->bytes;
} else if (MatchesPatternFillKernel(code, userData, compute)) {
const auto range = DecodePatternFillRange(userData, packet);
if (!range) return false;
@ -164,7 +231,16 @@ bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<cons
const auto records = std::min<std::uint64_t>(static_cast<std::uint64_t>(groups[0]) * 64u, numRecords);
base = userData[0] | (static_cast<std::uint64_t>(userData[1] & 0xffffu) << 32u);
bytes = static_cast<std::size_t>(records * 16u);
pattern = {userData[4], userData[5], userData[6], userData[7]};
if (matchesBufferPatternFillKernel(code)) {
const auto source = userData[4] | (static_cast<std::uint64_t>(userData[5] & 0xffffu) << 32u);
GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Fill);
inputLock.lock();
recordLabelsForPacket(localDevice.get(), queueId);
if (!GuestMemory::Accessible(reinterpret_cast<const void*>(source), sizeof(pattern))) return false;
GuestMemory::Read(source, std::as_writable_bytes(std::span(pattern)), 4);
} else {
pattern = {userData[4], userData[5], userData[6], userData[7]};
}
}
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
static std::atomic<std::uint64_t> fills{0}, filledBytes{0}, cpuFills{0};
@ -201,7 +277,7 @@ bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<cons
static const bool traceKeys = std::getenv("APS5_TRACE_DCC_KEYS") != nullptr;
if (traceKeys && coverage.cover == Graphics::StorageTexture::FillCover::Keys) std::fprintf(stderr, "[dcc-keys] title fills keys 0x%llx+0x%zx with %08x %08x %08x %08x (queue 0x%x)\n", static_cast<unsigned long long>(base), bytes, pattern[0], pattern[1], pattern[2], pattern[3], queueId);
const bool uniformKeysFill = coverage.cover == Graphics::StorageTexture::FillCover::Keys && std::all_of(pattern.begin(), pattern.end(), [&](std::uint32_t word) { return word == (pattern[0] & 0xffu) * 0x01010101u; });
const bool uniformKeysFill = coverage.cover == Graphics::StorageTexture::FillCover::Keys && IsByteFill(pattern);
if (uniformKeysFill) {
Graphics::StorageTexture::NoteKeysFill(base, bytes, static_cast<std::uint8_t>(pattern[0]));
Graphics::StorageTexture::ClearByKeysFill(base, bytes, static_cast<std::uint8_t>(pattern[0]));
@ -234,18 +310,8 @@ bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<cons
phase(FillFlush);
const bool stored = cleared || localDevice->FillBuffer(base, bytes, pattern);
phase(FillDevice);
if (uniformKeysFill && stored && !cleared) {
Graphics::DccKeys filled = Graphics::DccKeys::Mixed;
switch (pattern[0] & 0xffu) {
case 0x00: filled = Graphics::DccKeys::Clear0000; break;
case 0x40: filled = Graphics::DccKeys::Clear0001; break;
case 0x80: filled = Graphics::DccKeys::Clear1110; break;
case 0xc0: filled = Graphics::DccKeys::Clear1111; break;
case 0x20: filled = Graphics::DccKeys::ClearRegister; break;
case 0xff: filled = Graphics::DccKeys::Uncompressed; break;
default: break;
}
if (filled != Graphics::DccKeys::Mixed) Graphics::NoteKeysFillOnGpu(base, bytes, filled);
if (stored && !cleared) {
if (const auto filled = KeysFillMemo(pattern, uniformKeysFill, memoryFill.has_value())) Graphics::NoteKeysFillOnGpu(base, bytes, *filled);
}
if (profile && stored && !cleared) {
const bool uniform = pattern[0] == pattern[1] && pattern[1] == pattern[2] && pattern[2] == pattern[3];
@ -265,7 +331,10 @@ bool Driver::fillBuffer(QueueState& queue, std::uint32_t queueId, std::span<cons
for (std::size_t done = 0; done < bytes; done += chunk) GuestMemory::Write(base + done, std::span<const std::byte>(block).first(std::min(chunk, bytes - done)), 16);
phase(FillCpu);
}
if (pattern[0] == pattern[1] && pattern[1] == pattern[2] && pattern[2] == pattern[3]) Graphics::NoteDepthMetadataFill(base, bytes, pattern[0]);
if (pattern[0] == pattern[1] && pattern[1] == pattern[2] && pattern[2] == pattern[3]) {
Graphics::NoteDepthMetadataFill(base, bytes, pattern[0]);
Graphics::NoteColorMetadataFill(base, bytes, pattern[0]);
}
if (profile) {
const auto now = std::chrono::steady_clock::now();
const auto cover = static_cast<std::size_t>(coverage.cover);

View File

@ -205,6 +205,7 @@ void Driver::execute(const Submission& submission) {
} else if (header == FlipPacketHeader) {
CheckFailure();
std::uint64_t batchesAtFlip = 0, unsignaledAtFlip = 0;
bool flipInterruptDeferred = false;
if (!drains) {
GuestMemory::TagGpuLockSite(GuestMemory::GpuLockSite::Flush);
@ -212,8 +213,13 @@ void Driver::execute(const Submission& submission) {
const auto localDevice = device.Load();
if (!recordLabelsForPacket(localDevice.get(), submission.queue) && localDevice != nullptr) localDevice->SubmitRecorded(submission.queue == 0);
if (localDevice != nullptr) localDevice->FlipBatches(batchesAtFlip, unsignaledAtFlip);
if (localDevice != nullptr) {
localDevice->FlipBatches(batchesAtFlip, unsignaledAtFlip);
const auto queueId = submission.queue;
flipInterruptDeferred = localDevice->AfterRecordedWork([queueId] { AgcDriverDeliverEopInterrupt(queueId); }, false);
}
}
if (!flipInterruptDeferred) AgcDriverDeliverEopInterrupt(submission.queue);
++flipsCounted;
if (batchesAtFlip != 0) flipSerial = batchesAtFlip;
flipBatchesUnsignaled += unsignaledAtFlip;

View File

@ -8,6 +8,7 @@
#include "prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp"
#include "prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp"
#include "prx/libSceAgcDriver/Graphics/include/Recorder.hpp"
#include "prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp"
#include <cstdlib>
#include <cstring>
#include <limits>
@ -60,7 +61,12 @@ bool Driver::preparePacketMemory(const Submission& submission, QueueState& queue
}
}
if (opcode == 0x50 && packet.size() >= 7) {
if (Pm4::DmaSource(packet) == 2 && Pm4::DmaDestination(packet) != 1) Graphics::NoteDepthMetadataFill(packet[4] | (static_cast<std::uint64_t>(packet[5]) << 32u), packet[6] & 0x3ffffffu, packet[2]);
if (Pm4::DmaSource(packet) == 0 || Pm4::DmaSource(packet) == 3) Graphics::RequireNoMultisampleTarget(packet[2] | (static_cast<std::uint64_t>(packet[3]) << 32u), packet[6] & 0x3ffffffu, "a DMA copy");
if (Pm4::DmaSource(packet) == 2 && Pm4::DmaDestination(packet) != 1) {
const auto address = packet[4] | (static_cast<std::uint64_t>(packet[5]) << 32u);
Graphics::NoteDepthMetadataFill(address, packet[6] & 0x3ffffffu, packet[2]);
Graphics::NoteColorMetadataFill(address, packet[6] & 0x3ffffffu, packet[2]);
}
}
if (!drainAll && !endOfPipeInterrupt && (opcode == 0x49 || opcode == 0x37)) {
if (const auto label = Pm4::DecodeLabelWrite(packet)) {

View File

@ -4,6 +4,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Queues/WorkerAffinity.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include "prx/libSceAgcDriver/Execution/include/TracyZone.hpp"
#include <cstdlib>
namespace AgcDriver::DriverDetail {
@ -37,7 +38,7 @@ void Driver::AttachWindow(const PresentationWindow& window) {
}
void Driver::Present(const PresentationWindow& window, const DisplayBuffer* buffer, bool opaque, void (*gpuReady)(void*), void* context) {
if (PerformanceTrace::Configured()) PerformanceTrace::Present(window.width, window.height, device.Load().get());
PerformanceContext timingContext(window.timing.get());
PerformanceTimer timing("Driver.Present");
static thread_local bool pinned = false;
@ -127,6 +128,7 @@ void Driver::Present(const PresentationWindow& window, const DisplayBuffer* buff
}
gpuReady(context);
timing.Mark("release_and_callback");
TracyZone::MarkFrame();
if (profile) reportPresents(waitedMs, inFlight);
CheckFailure();
} catch (const ProcessShutdown&) {
@ -152,10 +154,12 @@ void Driver::reportPresents(double waitedMs, std::size_t inFlight) {
const auto counts = VulkanDevice::PresentCounts();
const auto presents = counts.presents - previous.presents;
const double perPresent = presents != 0 ? 1.0 / static_cast<double>(presents) : 0.0;
const auto dropped = counts.acquireDrops - previous.acquireDrops;
const double perFrame = presents + dropped != 0 ? 1.0 / static_cast<double>(presents + dropped) : 0.0;
const auto flips = flipsCounted.load() - previousFlips;
const double perFlip = flips != 0 ? 1.0 / static_cast<double>(flips) : 0.0;
const auto serial = flipSerial.load();
AgcDriver::ProfilePrint_nid_no_patch( "[present] %llu presents over 10 s (%zu may trail on the GPU); %s p50 %.2f ms, p90 %.2f ms, max %.2f ms over %zu; per present: GPU busy %.1f ms, idle gaps %.1f ms (%llu batches without a completion record); per flip: %.1f batches recorded (%.1f unsignaled at the flip); per present: %.1f batches ahead of the blit, %.1f after it; after the flip packet: last submit %.1f ms, blit submit %.1f ms; in-place reads overwritten before execution: %llu of %llu checked\n", static_cast<unsigned long long>(presents), inFlight, inFlight != 0 ? "inflight_wait" : "render_fence_wait", percentile(0.5), percentile(0.9), waits.empty() ? 0.0 : waits.back(), waits.size(), (counts.gpuBusyMs - previous.gpuBusyMs) * perPresent, (counts.gpuGapMs - previous.gpuGapMs) * perPresent, static_cast<unsigned long long>(counts.gpuUnread - previous.gpuUnread), static_cast<double>(serial - windowSerial) * perFlip, static_cast<double>(flipBatchesUnsignaled.load() - previousUnsignaled) * perFlip, static_cast<double>(counts.batchesAheadOfBlit - previous.batchesAheadOfBlit) * perPresent, static_cast<double>(counts.batchesAfterFlip - previous.batchesAfterFlip) * perPresent, (counts.lastSubmitAfterFlipMs - previous.lastSubmitAfterFlipMs) * perPresent, (counts.blitSubmitAfterFlipMs - previous.blitSubmitAfterFlipMs) * perPresent, static_cast<unsigned long long>(counts.readsOverwritten - previous.readsOverwritten), static_cast<unsigned long long>(counts.readsChecked - previous.readsChecked));
AgcDriver::ProfilePrint_nid_no_patch( "[present] %llu presents over 10 s (%llu dropped: no free swapchain image; %zu may trail on the GPU); %s p50 %.2f ms, p90 %.2f ms, max %.2f ms over %zu; per frame, presented or dropped: GPU busy %.1f ms, idle gaps %.1f ms (%llu batches without a completion record); per flip: %.1f batches recorded (%.1f unsignaled at the flip); per present: %.1f batches ahead of the blit, %.1f after it; after the flip packet: last submit %.1f ms, blit submit %.1f ms; in-place reads overwritten before execution: %llu of %llu checked\n", static_cast<unsigned long long>(presents), static_cast<unsigned long long>(dropped), inFlight, inFlight != 0 ? "inflight_wait" : "render_fence_wait", percentile(0.5), percentile(0.9), waits.empty() ? 0.0 : waits.back(), waits.size(), (counts.gpuBusyMs - previous.gpuBusyMs) * perFrame, (counts.gpuGapMs - previous.gpuGapMs) * perFrame, static_cast<unsigned long long>(counts.gpuUnread - previous.gpuUnread), static_cast<double>(serial - windowSerial) * perFlip, static_cast<double>(flipBatchesUnsignaled.load() - previousUnsignaled) * perFlip, static_cast<double>(counts.batchesAheadOfBlit - previous.batchesAheadOfBlit) * perPresent, static_cast<double>(counts.batchesAfterFlip - previous.batchesAfterFlip) * perPresent, (counts.lastSubmitAfterFlipMs - previous.lastSubmitAfterFlipMs) * perPresent, (counts.blitSubmitAfterFlipMs - previous.blitSubmitAfterFlipMs) * perPresent, static_cast<unsigned long long>(counts.readsOverwritten - previous.readsOverwritten), static_cast<unsigned long long>(counts.readsChecked - previous.readsChecked));
previous = counts;
previousFlips = flipsCounted.load();
previousUnsignaled = flipBatchesUnsignaled.load();

View File

@ -6,10 +6,18 @@
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/WorkerSampler.hpp"
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
#include <algorithm>
#include <chrono>
#include <cstdlib>
namespace AgcDriver::DriverDetail {
namespace {
constexpr auto IdleResultsQuietPeriod = std::chrono::milliseconds(50);
}
FrameTiming* Driver::frameTiming() {
if (!APS5_ENABLE_TIMING_LOG) return nullptr;
if (pendingFrameTiming == nullptr) pendingFrameTiming = std::make_shared<FrameTiming>(++frameSerial, true);
@ -66,11 +74,9 @@ void Driver::reapCompletionLabels() {
void Driver::run(std::uint32_t id) noexcept {
onWorkerThread() = true;
{
char role[32];
std::snprintf(role, sizeof role, "queue worker 0x%x", id);
PinWorkerThread(role);
}
char role[32];
std::snprintf(role, sizeof role, "queue worker 0x%x", id);
PinWorkerThread(role);
GuestMemory::TagGpuLockThread(id);
if (id == 0) StartWorkerSampler();
@ -78,8 +84,27 @@ void Driver::run(std::uint32_t id) noexcept {
static const bool profile = std::getenv("APS5_PROFILE_DRAW") != nullptr;
auto& costs = submissionCosts(id);
try {
if (PerformanceTrace::Configured()) PerformanceTrace::BindThread(role);
for (;;) {
submission = Submission{};
struct Running {
std::atomic<std::uint32_t>& count;
std::atomic<std::uint64_t>& idleEpoch;
std::condition_variable& changed;
bool active = false;
Running(std::atomic<std::uint32_t>& count, std::atomic<std::uint64_t>& idleEpoch, std::condition_variable& changed)
: count(count), idleEpoch(idleEpoch), changed(changed) {}
void Start() {
count.fetch_add(1, std::memory_order_acq_rel);
active = true;
}
~Running() {
if (active && count.fetch_sub(1, std::memory_order_acq_rel) == 1) {
idleEpoch.fetch_add(1, std::memory_order_release);
changed.notify_all();
}
}
} running{runningWorkers, idleEpoch, changed};
static const bool traceGpu = std::getenv("APS5_TRACE_GPU") != nullptr;
{
PerformanceContext timingContext(id == 0 ? frameTiming() : FrameTiming::Async());
@ -91,6 +116,13 @@ void Driver::run(std::uint32_t id) noexcept {
workerQueued() = &worker.queued;
if (traceGpu && pending.empty()) std::fprintf(stderr, "[gpu] %.1f idle queue=0x%x\n", TraceMs(), id);
const auto ready = [&] { return failure || stopping || !pending.empty(); };
const auto allQueuesIdle = [&] {
return runningWorkers.load(std::memory_order_acquire) == 0
&& std::all_of(workers.begin(), workers.end(), [](const auto& item) { return item.second.pending.empty(); });
};
const auto idleResultsPending = [&] {
return id == 0 && Graphics::StorageTexture::AnyPending() && allQueuesIdle() && !completionsPending();
};
const auto poll = [&](const auto& done) {
#ifdef _WIN32
lock.unlock();
@ -104,9 +136,35 @@ void Driver::run(std::uint32_t id) noexcept {
while (!ready()) {
if (!completionsPending() || (id != 0 && Graphics::Recorder::PendingCompletionLabels() == 0)) {
const auto idleEpochAtCheck = idleEpoch.load(std::memory_order_acquire);
const bool storeIdleResults = idleResultsPending();
if (storeIdleResults) {
queue0Dormant.store(true, std::memory_order_relaxed);
const auto quietUntil = std::chrono::steady_clock::now() + IdleResultsQuietPeriod;
const auto idleEpochAtWait = idleEpoch.load(std::memory_order_acquire);
changed.wait_until(lock, quietUntil, [&] {
return ready() || idleEpoch.load(std::memory_order_acquire) != idleEpochAtWait;
});
queue0Dormant.store(false, std::memory_order_relaxed);
if (ready()) break;
if (idleEpoch.load(std::memory_order_acquire) != idleEpochAtWait) {
continue;
}
if (!idleResultsPending()) continue;
}
lock.unlock();
if (storeIdleResults) {
if (const auto localDevice = device.Load()) localDevice->StoreIdleResults();
}
lock.lock();
if (ready()) break;
if (id == 0 && allQueuesIdle() && !completionsPending()
&& (!storeIdleResults || idleEpoch.load(std::memory_order_acquire) != idleEpochAtCheck)) continue;
if (id == 0) queue0Dormant.store(true, std::memory_order_relaxed);
changed.wait(lock, ready);
const auto idleEpochAtWait = idleEpoch.load(std::memory_order_acquire);
changed.wait(lock, [&] { return ready() || (id == 0 && idleEpoch.load(std::memory_order_acquire) != idleEpochAtWait); });
if (id == 0) queue0Dormant.store(false, std::memory_order_relaxed);
if (id == 0 && !ready() && idleEpoch.load(std::memory_order_acquire) != idleEpochAtWait) continue;
break;
}
if (poll(ready)) break;
@ -133,16 +191,10 @@ void Driver::run(std::uint32_t id) noexcept {
if (APS5_ENABLE_TIMING_LOG) submission.orderedAt = std::chrono::steady_clock::now();
timing.Mark("dequeue_order");
worker.queued.fetch_sub(1, std::memory_order_acq_rel);
running.Start();
if (profile && submission.enqueuedAt != std::chrono::steady_clock::time_point{}) costs.dequeueNs += static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now() - submission.enqueuedAt).count());
}
{
struct Running {
std::atomic<std::uint32_t>& count;
explicit Running(std::atomic<std::uint32_t>& count) : count(count) { count.fetch_add(1, std::memory_order_acq_rel); }
~Running() { count.fetch_sub(1, std::memory_order_acq_rel); }
} running{runningWorkers};
execute(submission);
}
execute(submission);
if (traceGpu) std::fprintf(stderr, "[gpu] %.1f done serial=%llu queue=0x%x\n", TraceMs(), static_cast<unsigned long long>(submission.serial), id);
const auto completeStart = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{};
PerformanceContext completionContext(id == 0 ? frameTiming() : FrameTiming::Async());

View File

@ -332,8 +332,12 @@ bool Driver::orderReleased(std::uint32_t queue, std::uint64_t received) const {
}
void Driver::noteWaitBlocked(std::uint32_t queue, std::uint64_t awaited, bool blocked) {
if (blocked) runningWorkers.fetch_sub(1, std::memory_order_acq_rel);
else runningWorkers.fetch_add(1, std::memory_order_acq_rel);
if (blocked) {
if (runningWorkers.fetch_sub(1, std::memory_order_acq_rel) == 1) {
idleEpoch.fetch_add(1, std::memory_order_release);
changed.notify_all();
}
} else runningWorkers.fetch_add(1, std::memory_order_acq_rel);
if (queue == 0) queue0Awaited.store(blocked ? awaited : 0, std::memory_order_release);
if (queue == 0 && !blocked) queue0AwaitsTitle.store(false, std::memory_order_release);
if (blocked && orderHolders.load(std::memory_order_acquire) != 0) {

View File

@ -1,5 +1,6 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Driver.hpp"
#include "prx/libSceAgcDriver/Execution/include/Driver/Queues/WorkerAffinity.hpp"
#include "prx/libSceAgcDriver/Execution/include/TracyZone.hpp"
#include "prx/libc/include/CpuTopology.hpp"
#include <cstdlib>
@ -20,6 +21,7 @@ std::uint64_t WorkerAffinityMask() {
}
void PinWorkerThread(const char* role) {
TracyZone::NameThread(role);
const auto mask = WorkerAffinityMask();
if (mask == 0) return;
static std::once_flag summary;

View File

@ -19,9 +19,12 @@
#include <cstdlib>
#include <cstring>
#include <deque>
#include <exception>
#include <functional>
#include <list>
#include <atomic>
#include <pthread.h>
#include <sstream>
#include <stdexcept>
#include <thread>
#include "RdnaDecoder/RdnaInstructionDecoder.hpp"
@ -29,13 +32,25 @@
namespace AgcDriver::DriverDetail {
std::shared_ptr<const ShaderRecompiler::SourceHandle> PrepareShaderWithDiagnostics(const ShaderRecompiler::RecompileRequest& request, ShaderRecompiler::ShaderPreparationContext* preparation) {
std::exception_ptr failure;
std::string dump;
try {
return ShaderRecompiler::PrepareShader(request, preparation);
auto handle = ShaderRecompiler::PrepareShader(request, preparation);
return handle;
} catch (...) {
failure = std::current_exception();
try {
if (std::getenv("APS5_DUMP_SHADERS") != nullptr) static_cast<void>(Driver::dumpRequest(request.shader.codeAddress, request));
if (std::getenv("APS5_DUMP_SHADERS") != nullptr) dump = Driver::dumpRequest(request.shader.codeAddress, request);
} catch (...) {}
throw;
}
try {
std::rethrow_exception(failure);
} catch (const std::exception& error) {
std::ostringstream where;
where << "shader 0x" << std::hex << request.shader.codeAddress << " stage " << static_cast<unsigned>(request.shader.stage);
if (!dump.empty()) where << " (" << dump << ")";
where << ": ";
throw std::runtime_error(where.str() + error.what());
}
}
@ -126,10 +141,17 @@ bool RawGraphicsDrawn() {
namespace {
void AwaitRegisteredPreparation(PreparedShaders& prepared, std::unique_lock<std::mutex>& lock) {
if (prepared.pending) PrioritizeRegistrationPreparation(prepared);
prepared.settled.wait(lock, [&] { return !prepared.pending; });
if (prepared.failure != nullptr) std::rethrow_exception(prepared.failure);
}
bool CommittedAbi(const ShaderSnapshot& snapshot, std::span<const std::uint64_t> key) {
std::unique_lock lock(snapshot.prepared->mutex);
AwaitRegisteredPreparation(*snapshot.prepared, lock);
return std::ranges::any_of(snapshot.prepared->registeredAbis, [&](const auto& abi) { return std::ranges::equal(abi, key); });
}
}
bool ProgramUsesWorkgroup(const ShaderSnapshot& snapshot, std::size_t codeOffset) {
@ -146,6 +168,39 @@ bool ProgramUsesWorkgroup(const ShaderSnapshot& snapshot, std::size_t codeOffset
return used;
}
std::uint32_t PositionExportLayout(const ShaderSnapshot* snapshot, std::size_t codeOffset, std::span<const std::uint32_t> code, std::uint32_t paClVsOutCntl) {
constexpr std::uint8_t Unknown = 0, Unused = 1, Used = 2;
const auto bits = paClVsOutCntl & Graphics::PositionExportLayoutBits;
if (bits == 0) return 0;
const bool cached = snapshot != nullptr && codeOffset == 0;
if (cached) {
const auto use = snapshot->positionVectorUse->load(std::memory_order_acquire);
if (use != Unknown) return use == Used ? bits : 0;
}
const auto decoded = ShaderRecompiler::RdnaInstructionDecoder{}.Decode(code);
const bool used = std::any_of(decoded.instructions.begin(), decoded.instructions.end(), [](const auto& instruction) {
return instruction.family == ShaderRecompiler::RdnaInstructionFamily::EXP && instruction.exportTarget >= 0x0du && instruction.exportTarget <= 0x0fu && instruction.exportEnableMask != 0;
});
if (cached) snapshot->positionVectorUse->store(used ? Used : Unused, std::memory_order_release);
return used ? bits : 0;
}
bool VertexWorkgroupRequired(const QueueState& queue, const ShaderRegistry& registry) {
Graphics::NoteRegisterRead(Graphics::RegisterBank::Context, 0x2d5);
const auto routing = queue.context.find(0x2d5);
if (routing == queue.context.end() || (routing->second & 0x02002024u) != 0x2000u) return false;
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, 0xc8);
Graphics::NoteRegisterRead(Graphics::RegisterBank::Shader, 0xc9);
const auto low = queue.shader.find(0xc8);
const auto high = queue.shader.find(0xc9);
if (low == queue.shader.end() || high == queue.shader.end()) return false;
require((high->second & ~0xffu) == 0, "reserved graphics program address bits are set");
const auto address = (static_cast<std::uint64_t>(low->second) << 8u) | (static_cast<std::uint64_t>(high->second) << 40u);
const auto snapshot = ProgramSnapshot(registry, address, {2});
require((address - snapshot->codeAddress) % sizeof(std::uint32_t) == 0, "graphics entry point is not dword aligned");
return (snapshot->header.empty() || snapshot->type == 2) && ProgramUsesWorkgroup(*snapshot, (address - snapshot->codeAddress) / sizeof(std::uint32_t));
}
struct ShaderPreparationTransaction::State {
struct Change {
@ -157,9 +212,13 @@ struct ShaderPreparationTransaction::State {
};
static std::mutex writers;
static thread_local State* active;
std::unique_lock<std::mutex> lock{writers};
std::unique_lock<std::mutex> lock{writers, std::defer_lock};
std::map<PreparedShaders*, Change> changes;
bool failed = false;
void Hold() {
if (!lock.owns_lock()) lock.lock();
}
};
std::mutex ShaderPreparationTransaction::State::writers;
@ -180,6 +239,7 @@ ShaderPreparationTransaction::~ShaderPreparationTransaction() {
PreparedShaderState& ShaderPreparationTransaction::Edit(const ShaderSnapshot& snapshot) {
require(!committed, "shader preparation transaction is already committed");
root->Hold();
auto& changes = root->changes;
const auto found = changes.find(snapshot.prepared.get());
if (found != changes.end()) {
@ -193,6 +253,7 @@ PreparedShaderState& ShaderPreparationTransaction::Edit(const ShaderSnapshot& sn
const PreparedShaderState& ShaderPreparationTransaction::Read(const ShaderSnapshot& snapshot) const {
require(!committed, "shader preparation transaction is already committed");
root->Hold();
auto& changes = root->changes;
const auto found = changes.find(snapshot.prepared.get());
if (found != changes.end()) return found->second.prepared;
@ -201,6 +262,11 @@ const PreparedShaderState& ShaderPreparationTransaction::Read(const ShaderSnapsh
return changes.emplace(snapshot.prepared.get(), State::Change{snapshot.prepared, *snapshot.prepared, false, snapshot.prepared->entries.size(), snapshot.prepared->registeredAbis.size()}).first->second.prepared;
}
void ShaderPreparationTransaction::Hold() {
require(!committed, "shader preparation transaction is already committed");
root->Hold();
}
void ShaderPreparationTransaction::Commit() {
require(!committed && !root->failed, "shader preparation transaction was aborted");
if (state != nullptr) {
@ -230,6 +296,34 @@ void ShaderPreparationTransaction::Commit() {
committed = true;
}
bool FusedHullHeader(const ShaderSnapshot& snapshot, Shader used, const Shader& registered) {
if (used.type != 3u || registered.type != 7u) return false;
const auto* bound = used.sh_registers;
used.type = registered.type;
used.sh_registers = registered.sh_registers;
if (std::memcmp(&used, &registered, offsetof(Shader, num_sh_registers) + sizeof(Shader::num_sh_registers)) != 0) return false;
const auto address = reinterpret_cast<std::uintptr_t>(registered.sh_registers);
const std::size_t bytes = registered.num_sh_registers * sizeof(ShaderRegister);
if (bytes == 0 || bound == nullptr || address < snapshot.headerAddress || address - snapshot.headerAddress > snapshot.header.size() || snapshot.header.size() - (address - snapshot.headerAddress) < bytes) return false;
GuestMemory::CheckRange(bound, bytes, alignof(ShaderRegister));
for (std::size_t index = 0; index < registered.num_sh_registers; ++index) {
ShaderRegister expected;
std::memcpy(&expected, snapshot.header.data() + (address - snapshot.headerAddress) + index * sizeof(ShaderRegister), sizeof(ShaderRegister));
const auto reg = bound[index];
if (reg.offset != expected.offset) return false;
const bool fused = reg.offset == 0x100u || reg.offset == 0x10au || reg.offset == 0x10bu || reg.offset == 0x148u || reg.offset == 0x149u;
if (!fused && reg.value != expected.value) return false;
}
return true;
}
static Shader GuestShaderFields(const Shader* shader) {
GuestMemory::CheckRange(shader, sizeof(Shader), 1);
Shader fields;
std::memcpy(&fields, static_cast<const void*>(shader), sizeof(Shader));
return fields;
}
bool SameHeader(const ShaderSnapshot& snapshot, const Shader* shader) {
if (snapshot.headerAddress == reinterpret_cast<std::uintptr_t>(shader)) return true;
if (snapshot.header.size() < sizeof(Shader)) return false;
@ -239,11 +333,11 @@ bool SameHeader(const ShaderSnapshot& snapshot, const Shader* shader) {
std::memcpy(&used, static_cast<const void*>(shader), sizeof(Shader));
std::memcpy(&registered, snapshot.header.data(), sizeof(Shader));
used.user_data = registered.user_data;
return std::memcmp(&used, &registered, offsetof(Shader, num_sh_registers) + sizeof(Shader::num_sh_registers)) == 0;
return std::memcmp(&used, &registered, offsetof(Shader, num_sh_registers) + sizeof(Shader::num_sh_registers)) == 0 || FusedHullHeader(snapshot, used, registered);
}
std::shared_ptr<const ShaderSnapshot> RegisteredHeader(const ShaderRegistry& registry, const Shader* shader) {
const auto found = registry.find(reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code)));
const auto found = registry.find(reinterpret_cast<std::uintptr_t>(const_cast<const void*>(GuestShaderFields(shader).code)));
if (found == registry.end()) return {};
for (const auto& snapshot : found->second) {
if (snapshot->headerAddress == reinterpret_cast<std::uintptr_t>(shader)) return snapshot;
@ -273,6 +367,7 @@ std::shared_ptr<const ShaderSnapshot> RegisteredProgram(const ShaderRegistry& re
void PublishRegisteredShader(std::shared_ptr<ShaderRegistry>& registry, const std::shared_ptr<const ShaderSnapshot>& snapshot) {
ShaderPreparationTransaction transaction;
transaction.Hold();
auto published = snapshot;
if (registry != nullptr) {
const auto found = registry->find(snapshot->codeAddress);
@ -304,6 +399,10 @@ void ReportPreparedAtUse(const ShaderSnapshot& snapshot, const ShaderRecompiler:
APS5_LOG_ERR("The null pixel program has no wave%u artifact for this draw; preparing it at draw", request.context.waveSize);
return;
}
if (request.context.vertex && (request.context.vertex->paClVsOutCntl & (1u << 18u)) != 0) {
APS5_LOG_OUT_DEBUG("Shader 0x%llx is drawn into a layered target; preparing its layered variant at draw", static_cast<unsigned long long>(snapshot.codeAddress));
return;
}
if (!snapshot.header.empty()) {
if (!snapshot.prepared->deferred) APS5_LOG_ERR("Shader 0x%llx stage %u has no artifact prepared at registration for the state of this draw or dispatch; preparing it at use", static_cast<unsigned long long>(request.shader.codeAddress), static_cast<std::uint32_t>(request.shader.stage));
return;
@ -474,9 +573,8 @@ Shader ReadHeader(const ShaderSnapshot& snapshot) {
return header;
}
RegisteredShaderState DecodeRegisteredState(const ShaderSnapshot& snapshot) {
RegisteredShaderState DecodeRegisteredState(const ShaderSnapshot& snapshot, RegisteredShaderState state = {{}, InitialContextRegisters(), {{0x24a, 0}, {0x24b, 0}}}) {
const auto header = ReadHeader(snapshot);
RegisteredShaderState state{{}, InitialContextRegisters(), {{0x24a, 0}, {0x24b, 0}}};
for (const auto reg : ReadHeaderArray(snapshot, header.sh_registers, header.num_sh_registers)) {
state.shader.insert_or_assign(reg.offset, reg.value);
}
@ -505,7 +603,7 @@ void BuildRegisteredAbiKey(const QueueState& state, const VulkanDevice& device,
key.insert(key.end(), {device.Serial(), ShaderRecompiler::DebugProbeActive(), ShaderRecompiler::RayTracingStrict(), ShaderRecompiler::RayTracingMiss()});
for (const auto* registers : {&state.shader, &state.context, &state.userConfig}) {
key.push_back(registers->size());
for (const auto& [offset, value] : *registers) key.push_back((static_cast<std::uint64_t>(offset) << 32u) | value);
registers->ForEach([&](std::uint32_t offset, std::uint32_t value) { key.push_back((static_cast<std::uint64_t>(offset) << 32u) | value); });
}
}
@ -517,6 +615,7 @@ struct RegisteredPreparation {
std::array<ShaderRecompiler::MemoryRegion, 2> memory{};
std::optional<ShaderRecompiler::RecompileRequest> request;
std::vector<std::uint64_t> abiKey;
std::shared_ptr<VulkanDevice> prebuildDevice;
};
std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snapshot, const VulkanDevice& device, const QueueState& state, bool registration, bool* deferred = nullptr) {
@ -531,7 +630,7 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
case 1: stage = Stage::Fragment; programRegister = 0x008; resourceRegister = 0x00b; break;
case 2: stage = Stage::Vertex; programRegister = 0x0c8; resourceRegister = 0x08b; firstUser = 8; break;
case 4: stage = Stage::Mesh; programRegister = 0x0c8; resourceRegister = 0x08b; break;
case 5: stage = Stage::Local; programRegister = 0x148; resourceRegister = 0x10b; firstUser = 8; break;
case 5: return {};
case 6: stage = Stage::Mesh; programRegister = 0x088; resourceRegister = 0x08b; break;
case 7: stage = Stage::TessellationControl; programRegister = 0x108; resourceRegister = 0x10b; break;
default:
@ -586,8 +685,9 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
} else {
const auto routing = RegisterValue(state.context, 0x2d5);
wave = (routing & 0x00400000u) != 0 ? 32u : 64u;
if ((routing & 0x20u) != 0 || stage == Stage::Mesh || (routing & 4u) != 0) {
if (registration) return nullptr;
if (registration && stage == Stage::Vertex && (routing & 0x02002024u) == 0x2000u && ProgramUsesWorkgroup(snapshot, codeOffset)) return nullptr;
if ((routing & 0x20u) != 0 || stage == Stage::Mesh || (routing & 4u) != 0 || ((routing >> 3u) & 3u) == 1u) {
if (registration) return {};
auto stageState = state;
stageState.context[0x1b6] = 0;
const auto stages = Graphics::DecodeShaderStages(stageState);
@ -598,7 +698,11 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
if (firstUser == 0) userCount += 8;
}
plan->userData.resize(userCount);
if (stage != Stage::Compute && stage != Stage::Fragment && snapshot.type != 6) vertex = Graphics::DecodeVertexStageInfo(snapshot.header, snapshot.headerAddress, plan->userData, nullptr, true);
if (stage != Stage::Compute && stage != Stage::Fragment && snapshot.type != 6) {
vertex = Graphics::DecodeVertexStageInfo(snapshot.header, snapshot.headerAddress, plan->userData, nullptr, true, 8u - firstUser);
const auto outputs = state.context.find(0x207u);
if (vertex && outputs != state.context.end()) vertex->paClVsOutCntl = PositionExportLayout(&snapshot, codeOffset, std::span(snapshot.code).subspan(codeOffset), outputs->second);
}
plan->swappc = {vertex.has_value(), firstUser, userCount};
if (deferred != nullptr) {
if (const auto counter = ShaderRecompiler::UnresolvableSwappcTarget(plan->decoded, &plan->swappc)) {
@ -610,10 +714,23 @@ std::unique_ptr<RegisteredPreparation> PlanRegistered(const ShaderSnapshot& snap
plan->memory = {{{snapshot.codeAddress, std::as_bytes(std::span(snapshot.code))}, {snapshot.headerAddress, snapshot.header}}};
auto& request = plan->request.emplace(ShaderRecompiler::RecompileRequest{{stage, address, code, snapshot.headerAddress, snapshot.header}, {wave, firstUser, plan->userData, compute, pixel, vertex, plan->memory, RegisteredFloatMode(snapshot)}, stage == Stage::Compute ? device.ComputeTarget(wave) : device.Target(), {0, 0, 0, 128}, graphics});
if (graphics && graphics->mesh) request.layout.pushConstantSizeBytes = ShaderRecompiler::MeshDrawPushOffsetBytes;
if (graphics && graphics->tessellation) request.layout.pushConstantSizeBytes = ShaderRecompiler::TessellationLevelPushOffsetBytes;
if (pixel && device.GraphicsPipelineLibraries()) request.layout.pushConstantOffsetBytes = Graphics::FixedPushOffset(Stage::Fragment);
return plan;
}
void PrebuildStageLibrary(const std::shared_ptr<VulkanDevice>& device, const Graphics::ShaderStages& stages, ShaderRecompiler::ShaderStage stage, std::shared_ptr<const ShaderRecompiler::RecompileResult> result, std::uint32_t pushOffset) {
if (result == nullptr || (stage != ShaderRecompiler::ShaderStage::Vertex && stage != ShaderRecompiler::ShaderStage::Fragment)) return;
SubmitLibraryPrebuild([device, stages, stage, result = std::move(result), pushOffset] {
const std::array<Graphics::CompiledShader, 1> shaders{{{stage, result.get(), pushOffset}}};
try {
static_cast<void>(device->PrebuildGraphicsLibraries(stages, shaders));
} catch (const std::exception& error) {
APS5_LOG_OUT("shader library prebuild skipped: %s", error.what());
}
});
}
std::vector<PreparedShaders::Entry> PrepareRegistered(RegisteredPreparation& plan) {
auto& request = *plan.request;
std::vector<PreparedShaders::Entry> entries;
@ -623,6 +740,13 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(RegisteredPreparation& pla
entries.push_back({plan.codeOffset, PrepareShaderWithDiagnostics(request, &preparation)});
};
append();
if (plan.prebuildDevice != nullptr && request.target.registrationModules && request.shader.stage == ShaderRecompiler::ShaderStage::Fragment) {
try {
PrebuildStageLibrary(plan.prebuildDevice, {Graphics::ShaderPath::Vertex, 0u, 32u, request.context.waveSize, {}, {}}, request.shader.stage, ShaderRecompiler::RegistrationResult(request, *entries.front().handle), request.layout.pushConstantOffsetBytes);
} catch (const std::exception& error) {
APS5_LOG_OUT("shader library prebuild skipped: %s", error.what());
}
}
if (request.context.compute) {
request.context.compute->partialThreads = {1, 1, 1};
append();
@ -641,15 +765,51 @@ std::vector<PreparedShaders::Entry> PrepareRegistered(const ShaderSnapshot& snap
class RegistrationPreparePool {
public:
static RegistrationPreparePool& Instance() {
static auto* pool = new RegistrationPreparePool();
static auto* pool = [] {
auto* created = new RegistrationPreparePool(std::max(2u, std::thread::hardware_concurrency() / 2u));
PreparationSlot().store(created);
return created;
}();
return *pool;
}
void Submit(std::function<void()> task) {
static RegistrationPreparePool* Existing() {
return PreparationSlot().load();
}
static RegistrationPreparePool& Prebuild() {
static auto* pool = [] {
const char* text = std::getenv("APS5_PREBUILD_THREADS");
auto* created = new RegistrationPreparePool(text != nullptr ? std::max(1u, static_cast<unsigned>(std::strtoul(text, nullptr, 10))) : 1u);
PrebuildSlot().store(created);
return created;
}();
return *pool;
}
static RegistrationPreparePool* ExistingPrebuild() {
return PrebuildSlot().load();
}
private:
static std::atomic<RegistrationPreparePool*>& PreparationSlot() {
static std::atomic<RegistrationPreparePool*> slot{nullptr};
return slot;
}
static std::atomic<RegistrationPreparePool*>& PrebuildSlot() {
static std::atomic<RegistrationPreparePool*> slot{nullptr};
return slot;
}
public:
void Submit(std::function<void()> task, const PreparedShaders* prepared = nullptr) {
{
std::unique_lock lock(mutex);
if (threads != 0 && !closed) {
tasks.push_back(std::move(task));
if (prepared != nullptr && prepared->demanded.load()) tasks.push_front({std::move(task), prepared});
else tasks.push_back({std::move(task), prepared});
lock.unlock();
ready.notify_one();
return;
@ -658,6 +818,14 @@ public:
task();
}
void Prioritize(const PreparedShaders& prepared) {
std::lock_guard lock(mutex);
const auto found = std::ranges::find_if(tasks, [&](const auto& task) { return task.prepared == &prepared; });
if (found == tasks.end()) return;
if (std::getenv("APS5_TRACE_PREPARATION_WAIT") != nullptr) std::fprintf(stderr, "[shader-prepare] demanded queued task at position %zu of %zu\n", static_cast<std::size_t>(std::distance(tasks.begin(), found)), tasks.size());
std::rotate(tasks.begin(), found, std::next(found));
}
void Close() {
std::unique_lock lock(mutex);
closed = true;
@ -665,8 +833,7 @@ public:
}
private:
RegistrationPreparePool() {
const auto count = std::max(2u, std::thread::hardware_concurrency() / 2u);
explicit RegistrationPreparePool(unsigned count) {
pthread_attr_t attributes;
pthread_attr_init(&attributes);
pthread_attr_setstacksize(&attributes, 64u << 20u);
@ -682,8 +849,9 @@ private:
static void* Main(void* argument) {
auto& pool = *static_cast<RegistrationPreparePool*>(argument);
if (PerformanceTrace::Configured()) PerformanceTrace::BindThread("shader prepare", TraceThread::DefaultCapacity / 8);
for (;;) {
std::function<void()> task;
Task task;
{
std::unique_lock lock(pool.mutex);
pool.ready.wait(lock, [&] { return !pool.tasks.empty(); });
@ -691,8 +859,8 @@ private:
pool.tasks.pop_front();
++pool.running;
}
task();
task = nullptr;
task.function();
task = {};
{
std::lock_guard lock(pool.mutex);
--pool.running;
@ -702,10 +870,15 @@ private:
return nullptr;
}
struct Task {
std::function<void()> function;
const PreparedShaders* prepared = nullptr;
};
std::mutex mutex;
std::condition_variable ready;
std::condition_variable idle;
std::deque<std::function<void()>> tasks;
std::deque<Task> tasks;
unsigned threads = 0;
unsigned running = 0;
bool closed = false;
@ -717,6 +890,7 @@ bool AsyncRegistrationPrepare() {
}
void PrepareRegisteredInBackground(std::shared_ptr<const ShaderSnapshot> snapshot, std::shared_ptr<RegisteredPreparation> plan) {
const auto* preparedKey = snapshot->prepared.get();
SubmitRegistrationPreparation([snapshot = std::move(snapshot), plan = std::move(plan)] {
PerformanceContext timingContext(FrameTiming::Preparation());
std::vector<PreparedShaders::Entry> entries;
@ -738,17 +912,27 @@ void PrepareRegisteredInBackground(std::shared_ptr<const ShaderSnapshot> snapsho
prepared.pending = false;
}
prepared.settled.notify_all();
});
}, preparedKey);
}
}
void SubmitRegistrationPreparation(std::function<void()> task) {
RegistrationPreparePool::Instance().Submit(std::move(task));
void SubmitRegistrationPreparation(std::function<void()> task, const PreparedShaders* prepared) {
RegistrationPreparePool::Instance().Submit(std::move(task), prepared);
}
void PrioritizeRegistrationPreparation(PreparedShaders& prepared) {
prepared.demanded.store(true);
if (auto* pool = RegistrationPreparePool::Existing()) pool->Prioritize(prepared);
}
void SubmitLibraryPrebuild(std::function<void()> task) {
RegistrationPreparePool::Prebuild().Submit(std::move(task));
}
void CloseRegistrationPreparation() {
RegistrationPreparePool::Instance().Close();
if (auto* prebuild = RegistrationPreparePool::ExistingPrebuild()) prebuild->Close();
}
std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decoded, const ShaderRecompiler::SpirvTarget& target) {
@ -756,8 +940,10 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
require(decoded.programs.size() == decoded.roles.size(), "graphics ABI program roles are incomplete");
for (const auto& program : decoded.programs) require(program.snapshot != nullptr, "graphics ABI has no registered shader snapshot");
std::vector<ShaderRecompiler::ProgramRole> expected;
if (decoded.state.stages.path == Graphics::ShaderPath::Tessellation) expected = {ShaderRecompiler::ProgramRole::Local, ShaderRecompiler::ProgramRole::Hull, ShaderRecompiler::ProgramRole::Domain};
else {
if (decoded.state.stages.path == Graphics::ShaderPath::Tessellation) {
if (!decoded.roles.empty() && decoded.roles.front() == ShaderRecompiler::ProgramRole::Local) expected.push_back(ShaderRecompiler::ProgramRole::Local);
expected.insert(expected.end(), {ShaderRecompiler::ProgramRole::Hull, ShaderRecompiler::ProgramRole::Domain});
} else {
expected.push_back(ShaderRecompiler::ProgramRole::Main);
if (decoded.state.stages.path == Graphics::ShaderPath::Geometry && !decoded.programs.empty() && decoded.programs.front().snapshot->type == 4) expected.push_back(ShaderRecompiler::ProgramRole::GeometryBack);
}
@ -776,14 +962,15 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
std::vector<PreparedGraphicsStage> prepared;
std::uint32_t pushOffset = 0;
const bool fixedSlots = target.fixedPushSlots;
const auto capacity = decoded.state.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : Graphics::PipelinePushSlotBytes;
const auto capacity = decoded.state.stages.mesh ? ShaderRecompiler::MeshDrawPushOffsetBytes : decoded.state.stages.tessellation ? ShaderRecompiler::TessellationLevelPushOffsetBytes : Graphics::PipelinePushSlotBytes;
for (std::size_t index = 0; index < decoded.programs.size(); ++index) {
if (decoded.roles[index] == ShaderRecompiler::ProgramRole::GeometryBack) continue;
const auto& program = decoded.programs[index];
const bool fragment = program.binary.stage == ShaderRecompiler::ShaderStage::Fragment;
const auto wave = fragment ? decoded.state.stages.fragmentWaveSize : decoded.state.stages.vertexWaveSize;
std::optional<ShaderRecompiler::ShaderVertexStageInfo> vertex;
if (!fragment) vertex = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, nullptr, true);
if (!fragment) vertex = Graphics::DecodeVertexStageInfo(program.binary.header, program.binary.headerAddress, program.userData, nullptr, true, 8u - program.firstUserSgpr);
if (vertex) vertex->paClVsOutCntl = PositionExportLayout(program.snapshot.get(), program.codeOffset, program.binary.code, decoded.state.paClVsOutCntl) | decoded.state.layerExports;
pushOffset = Graphics::StagePushOffset(pushOffset, program.binary.stage, fixedSlots);
const auto offset = pushOffset;
const auto room = (fixedSlots && fragment ? Graphics::PipelinePushSlotBytes : capacity) - pushOffset % Graphics::PipelinePushSlotBytes;
@ -803,7 +990,15 @@ std::vector<PreparedGraphicsStage> PrepareGraphicsStages(const DrawDecode& decod
}
const auto bytes = handle->artifact->bindings.pushConstantSizeBytes;
require(bytes <= room, "prepared graphics stages exceed the push constant block");
prepared.push_back({program.snapshot, {program.codeOffset, handle}});
std::shared_ptr<const ShaderRecompiler::RecompileResult> registration;
if (target.registrationModules) {
try {
registration = ShaderRecompiler::RegistrationResult(request, *handle);
} catch (const std::exception& error) {
APS5_LOG_OUT("shader library prebuild skipped: %s", error.what());
}
}
prepared.push_back({program.snapshot, {program.codeOffset, handle}, program.binary.stage, offset, std::move(registration)});
pushOffset += bytes;
}
transaction.Commit();
@ -823,18 +1018,23 @@ void Driver::ResolveGraphicsStagesAbi(std::span<const Shader* const> stages, std
std::lock_guard lock(mutex);
registry = shaders;
for (const auto* shader : stages) {
GuestMemory::CheckRange(shader, sizeof(Shader), alignof(Shader));
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code));
const auto fields = GuestShaderFields(shader);
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(fields.code));
require(registry != nullptr && registry->contains(address), "graphics ABI refers to an unregistered shader");
const auto selected = RegisteredHeader(*registry, shader);
require(selected != nullptr, "graphics ABI refers to a replaced shader header");
if (owner == nullptr) owner = selected;
const auto& snapshot = *selected;
require(snapshot.registeredState != nullptr, "registered shader state is missing");
const auto& registered = *snapshot.registeredState;
for (const auto& [offset, value] : registered.shader) state.shader.insert_or_assign(offset, value);
for (const auto& [offset, value] : registered.context) state.context.insert_or_assign(offset, value);
for (const auto& [offset, value] : registered.userConfig) state.userConfig.insert_or_assign(offset, value);
const auto registered = DecodeRegisteredState(snapshot, {});
registered.shader.ForEach([&](std::uint32_t offset, std::uint32_t value) { state.shader.insert_or_assign(offset, value); });
registered.context.ForEach([&](std::uint32_t offset, std::uint32_t value) { state.context.insert_or_assign(offset, value); });
registered.userConfig.ForEach([&](std::uint32_t offset, std::uint32_t value) { state.userConfig.insert_or_assign(offset, value); });
if (fields.type != snapshot.type) {
const std::size_t count = fields.num_sh_registers;
GuestMemory::CheckRange(fields.sh_registers, count * sizeof(ShaderRegister), alignof(ShaderRegister));
for (std::size_t index = 0; index < count; ++index) state.shader.insert_or_assign(fields.sh_registers[index].offset, fields.sh_registers[index].value);
}
}
}
for (const auto reg : context) state.context.insert_or_assign(reg.offset, reg.value);
@ -856,7 +1056,7 @@ void Driver::ResolveGraphicsStagesAbi(std::span<const Shader* const> stages, std
return;
}
DrawDecode decoded{};
decoded.state.stages = Graphics::DecodeShaderStages(state);
decoded.state.stages = Graphics::DecodeShaderStages(state, VertexWorkgroupRequired(state, *registry));
DecodeGraphicsPrograms(decoded, state, *registry, true, false);
auto prepared = PrepareGraphicsStages(decoded, localDevice->Target());
for (auto& stage : prepared) {
@ -868,6 +1068,11 @@ void Driver::ResolveGraphicsStagesAbi(std::span<const Shader* const> stages, std
if (stage.snapshot->type == 1) continue;
ResolvePreparedGraphics(*stage.snapshot, {}, primitiveType == state.userConfig.end() ? 0u : primitiveType->second, localDevice->Target());
}
if (decoded.state.stages.path == Graphics::ShaderPath::Vertex) {
for (const auto& stage : prepared) {
if (stage.registration != nullptr) PrebuildStageLibrary(localDevice, decoded.state.stages, stage.stage, stage.registration, stage.pushOffset);
}
}
PreparedShaderState::GraphicsAbi abi{key, registry, {}};
for (const auto& stage : prepared) abi.stages.push_back(stage.snapshot);
auto& graphicsAbis = transaction.Edit(*owner).graphicsAbis;
@ -881,8 +1086,8 @@ void Driver::ResolveShaderAbi(const Shader* shader, std::span<const ShaderRegist
PerformanceTimer timing("Shader.ResolveAbi");
ShaderPreparationTransaction transaction;
CheckFailure();
GuestMemory::CheckRange(shader, sizeof(Shader), alignof(Shader));
if (shader->type != 0 && shader->type != 1) {
const auto fields = GuestShaderFields(shader);
if (fields.type != 0 && fields.type != 1) {
const std::array<const Shader*, 1> stages{shader};
ResolveGraphicsStagesAbi(stages, context, primitive);
transaction.Commit();
@ -891,7 +1096,7 @@ void Driver::ResolveShaderAbi(const Shader* shader, std::span<const ShaderRegist
std::shared_ptr<const ShaderSnapshot> snapshot;
{
std::lock_guard lock(mutex);
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code));
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(fields.code));
require(shaders != nullptr && shaders->contains(address), "static ABI refers to an unregistered shader");
snapshot = RegisteredHeader(*shaders, shader);
require(snapshot != nullptr, "static ABI refers to a replaced shader header");
@ -908,12 +1113,20 @@ void Driver::ResolveShaderAbi(const Shader* shader, std::span<const ShaderRegist
struct RegisteredAbiKeyStorage {};
auto& key = HostThreadLocal<std::vector<std::uint64_t>, RegisteredAbiKeyStorage>();
BuildRegisteredAbiKey(state, *localDevice, key);
const auto prepare = [&] {
const auto plan = PlanRegistered(*snapshot, *localDevice, state, false);
if (plan == nullptr) return std::vector<PreparedShaders::Entry>{};
plan->prebuildDevice = localDevice;
return PrepareRegistered(*plan);
};
const bool committedAbi = CommittedAbi(*snapshot, key);
auto entries = committedAbi ? std::vector<PreparedShaders::Entry>{} : prepare();
const auto& registeredAbis = transaction.Read(*snapshot).registeredAbis;
if (std::ranges::find(registeredAbis, key) != registeredAbis.end()) {
transaction.Commit();
return;
}
auto entries = PrepareRegistered(*snapshot, *localDevice, state, false);
if (committedAbi) entries = prepare();
auto& prepared = transaction.Edit(*snapshot);
for (auto& entry : entries) {
const auto duplicate = std::ranges::any_of(prepared.entries, [&](const auto& existing) { return existing.handle->artifact == entry.handle->artifact; });
@ -950,10 +1163,8 @@ void Driver::ResolveGraphicsAbi(const Shader* vertex, const Shader* pixel, std::
std::shared_ptr<const ShaderSnapshot> fragment;
{
std::lock_guard lock(mutex);
GuestMemory::CheckRange(vertex, sizeof(Shader), alignof(Shader));
GuestMemory::CheckRange(pixel, sizeof(Shader), alignof(Shader));
const auto lookup = [&](const Shader* shader) {
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(shader->code));
const auto address = reinterpret_cast<std::uintptr_t>(const_cast<const void*>(GuestShaderFields(shader).code));
require(shaders != nullptr && shaders->contains(address), "rectangle ABI refers to an unregistered shader");
const auto snapshot = RegisteredHeader(*shaders, shader);
require(snapshot != nullptr, "rectangle ABI refers to a replaced shader header");
@ -1067,6 +1278,7 @@ void Driver::RegisterShader(const Shader* shader) {
snapshot.header.resize(fields.header_size);
std::memcpy(snapshot.header.data(), static_cast<const void*>(shader), fields.header_size);
transaction.Hold();
{
std::lock_guard lock(mutex);
rethrowFailure();
@ -1099,6 +1311,7 @@ void Driver::RegisterShader(const Shader* shader) {
std::shared_ptr<RegisteredPreparation> preparation;
if (AsyncRegistrationPrepare()) {
preparation = PlanRegistered(snapshot, *localDevice, registered, true, &snapshot.prepared->deferred);
if (preparation != nullptr) preparation->prebuildDevice = localDevice;
if (preparation != nullptr && (snapshot.type == 0 || snapshot.type == 1)) BuildRegisteredAbiKey(registered, *localDevice, preparation->abiKey);
snapshot.prepared->pending = preparation != nullptr;
} else {

View File

@ -5,6 +5,7 @@
#include "prx/libSceAgcDriver/Execution/include/Driver/Synchronization/SynchronizationStatistics.hpp"
#include "prx/libSceAgcDriver/Execution/include/GuestMemory.hpp"
#include "prx/libSceAgcDriver/Execution/include/Pm4.hpp"
#include "prx/libc/include/HostThreadLocal.hpp"
#include <cstdlib>
#include <mutex>
#include <set>
@ -54,8 +55,8 @@ bool Driver::traceLateLabels() {
}
std::vector<Driver::PendingWait>& Driver::pendingWaits() {
static thread_local std::vector<PendingWait> waits;
return waits;
struct WaitsTag {};
return HostThreadLocal<std::vector<PendingWait>, WaitsTag>();
}
void Driver::notePendingWait(std::span<const std::uint32_t> packet, std::uint32_t queue, std::uint64_t received) {

Some files were not shown because too many files have changed in this diff Show More