Repository navigation
fix(shader): act on the containing dword for unaligned 32-bit buffer atomics - #6094
Conversation
|
I ran the unaligned case on real RDNA2 with
So on gfx1036 a misaligned atomic faults in UNALIGNED mode too, not only in the strict modes. If gfx1013 behaves the same, DWORD mode is the only one where MXGP's atomics would run, which is what this PR models. With this PR on |
|
MXGP 26 (PPSA30839), the title in the description, on Linux: Ubuntu 24.04, GCC 13.3, Release, RTX 4070 Ti (NVIDIA 595.99),
Not checked on Windows, and nothing here says what the console does with the misaligned atomic. AI-assisted: yes |
|
Windows run, for the "Not tested: ... Windows" line. Windows 11 (26200), RTX 3060 Laptop GPU (NVIDIA 617.42), WinLibs GCC 15.2.0 r7 (UCRT), Release,
Not run: MXGP 26, the hardware oracle. AI-assisted: yes (Claude Code) |
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
|
Code Violet (PPSA26528, Unreal Engine 5.5) on Windows: Windows 11 (10.0.22631), RTX PRO 6000 Blackwell Workstation Edition (NVIDIA 597.16), WinLibs GCC 15.2.0 r7, Release.
Nothing here says what the console does with the misaligned atomic either. AI-assisted: yes |
|
Checked the change against the RDNA2 ISA §8.1.7 (DWORD alignment mode), built and ran Minor, not blocking: the |
|
AMD / Windows run of this PR at 6c18863: Radeon 780M (RDNA 3, 0x15bf), AMD driver 25.10.20.01 (32.0.21020.1007), Windows 11 Home 10.0.26100, WinLibs GCC 15.2.0 r7, Release.
AI-assisted: yes (runs made on the device above). |
…atomics The RDNA2 ISA (8.1.7) ignores the two low address bits of dword or wider non-formatted accesses in alignment mode DWORD. MXGP 26 (PPSA30839) sets bits with buffer_atomic_or at odd byte offsets, which recorded an unaligned BDA fault and dropped the atomic. A 32-bit buffer atomic now acts on the dword that holds its address; 64-bit atomics off an 8-byte boundary still fault. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n their containing dword Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6c18863 to
96c692c
Compare
…lation, and AGC resource fixes (#565) * fix(libkernel): take NOTE_LOWAT on socket read and write events kevent threw for NOTE_LOWAT. On a socket's EVFILT_READ it now sets the low-water mark as FreeBSD 11's filt_soread does: the event fires once the buffered bytes reach it, or at end of file or on a listening socket, and a later change of the knote replaces it. EVFILT_WRITE takes a mark of 1, where the host's writability stands in for a free byte; other write marks and marks below 1 still throw. A pipe ignores the mark, as filt_piperead does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e1d4ccb) * fix(libkernel): read ITIMER_VIRTUAL and ITIMER_PROF as disarmed in getitimer setitimer refuses to arm the CPU interval timers and already reads and disarms them as zero, so they are never armed. getitimer threw for them instead of reporting that state, as FreeBSD's kern_getitimer does for a timer that is not set. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 9f704dc) * fix(libkernel): return EFAULT from fstat for a null stat buffer FreeBSD's fstat looks the descriptor up before it copies the result out, so a bad descriptor still fails with EBADF and an open one fails with EFAULT when sb is null. fstat and sceKernelFstat threw std::invalid_argument instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit be17961) * fix(libkernel): return EINVAL from scePthreadCondattrSetclock and Destroy for a null attribute Both threw std::invalid_argument for a null or uninitialized attribute. FreeBSD's libthr returns EINVAL for one, as the other scePthread attribute calls here already do. The clock id is still stored as given. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 62ac8d6) * fix(libkernel): keep refusing a read low-water mark above 1 on non-stream sockets On Linux, FIONREAD on a datagram socket reports the next datagram only, where FreeBSD's sbavail counts every queued byte, so a read mark above 1 there could never fire. kevent now throws for it as before; stream sockets and marks of 1 are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 91fbafd) * fix(libkernel): end guest thread, fiber and context frame chains at a zero record Unreal Engine walks the frame-pointer chain until it reads a return address of 0 and never checks the frame pointer itself. Guest threads started with whatever rbp the host code held, so the walk ran into host start-up frames (glibc's saved rbp 0x21 in a LEGO Batman core dump, address 8 in Returnal on Windows) and faulted. Fibers ended their chain with a null frame, and makecontext started its function with rbp 0, so the same walker read address 8 there too. Guest threads now start through a thunk that points rbp at a {saved rbp 0, return address 0} record, as the relinker's entry stub does for the main thread; the fiber trampolines push the same record, and the makecontext start function is built with frame pointers so its frame, over the zero return address makecontext stores, ends the chain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit ff9bf22) * fix(libkernel): return EFAULT from sceKernelStat and sceKernelUnlink for a null path (cherry picked from commit 274f092) * fix(libkernel): report EINVAL for getdirentries on a non-directory sceKernelGetdirentries on a descriptor that is not a directory returned ENOTDIR from the host getdents64 on Linux, and ENOTDIR for every descriptor missing from the directory table on Windows, including invalid ones. FreeBSD 11.4's kern_getdirentries returns EINVAL when the vnode is not a directory. Return EINVAL for a regular file on both hosts and EBADF for an invalid descriptor on Windows. Test getdents and getdirentries on a regular file and list the pipe case, which still fails with ESPIPE on Linux, in the library's technical debt file. Written with AI assistance (Claude). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 9dc6542) * fix(libkernel): return EPROTOTYPE for a socket type inet does not have socket() returned EPROTONOSUPPORT for every socket type it does not create. FreeBSD 11.4's socreate returns EPROTOTYPE when no protocol is found, the protocol is 0 and the type is not 0, and EPROTONOSUPPORT when a protocol was given. Its inet domain (in_proto.c) has SOCK_RAW entries in every kernel and a SOCK_SEQPACKET entry only with SCTP, and no SOCK_RDM or other type. Return EPROTOTYPE for a type the inet domain never has, such as SOCK_RDM, with protocol 0. SOCK_RAW and SOCK_SEQPACKET keep EPROTONOSUPPORT, since whether the console creates them is not known. Test the unknown types and mismatched protocols for AF_INET and AF_INET6, and list the socket types and families that are not created in the library's technical debt file. Written with AI assistance (Claude). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit a6c123c) * fix(libkernel): return EFAULT from truncate, utimes, mkdir, rmdir, and rename for a null path (cherry picked from commit 7bb7608) * feat(libkernel): implement pthread_condattr_setpshared PSFlyCast (PPSA99247) imports pthread_condattr_setpshared (3BpP850hBT4) from libkernel and fails to load without it: "./app.elf: symbol lookup error: ./app.elf: undefined symbol: 3BpP850hBT4". The attribute stores PTHREAD_PROCESS_PRIVATE (0) or PTHREAD_PROCESS_SHARED (1); a null or uninitialized attribute and any other value return EINVAL without changing it, following FreeBSD 11 libthr thr_condattr.c and pthread_mutexattr_setpshared here. Sharing across processes is not modeled, as for mutexes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sovthwyu4H63RMbRVha2Sy (cherry picked from commit 05b457d) * fix(libkernel): export sysarch so modules that import it load The Wine port (PPSA99995) ships an ntdll module that imports sysarch (b7uXQmnfB2s). libkernel did not export it, so loading stopped with "undefined symbol: b7uXQmnfB2s" before the title ran. Export sysarch as a stub that reports that it is not implemented: the title loads and runs its launcher without calling it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 162f4af) * feat(libkernel): read and set the GS base through sysarch sysarch's AMD64_GET_FSBASE, AMD64_GET_GSBASE and AMD64_SET_GSBASE now follow FreeBSD 11's amd64 sysarch on Linux x86-64 through arch_prctl: the GS base is the calling thread's, a base at or above VM_MAXUSER_ADDRESS returns EINVAL and a null argument EFAULT. AMD64_SET_FSBASE, whose base guest and host code share for their TLS, other operations and other hosts still throw. Wine sets its TEB this way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit d7c83db) * feat(libkernel): implement three coredump exports from the console module Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 70ff799) * feat(libkernel): add pthread_rwlockattr_getpshared and pthread_rwlockattr_setpshared Measured on a PS5 (firmware 12.70): the getter reports 0, and of the values 0, 1, 2 and -1 the setter accepts only 0 and checks the value before the attribute. A null or uninitialized attribute, or a null output, crashes the console; here it throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit bdf3adb) * fix(libkernel): reject PTHREAD_PROCESS_SHARED in pthread_condattr_setpshared and add pthread_condattr_getpshared On a PS5 (firmware 12.70), of the values 0, 1, 2 and -1, pthread_condattr_setpshared accepts only 0 and leaves the attribute unchanged otherwise. pthread_condattr_getpshared reports 0 and returns EINVAL for a null, uninitialized or destroyed attribute, also with a null output. With an initialized attribute a null output crashes the console; here it throws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit c00c58a) * feat(libkernel): poll regular files and directories as vop_stdpoll does poll threw for any descriptor that is neither a socket nor a pipe, so a title polling a file it also reads stopped there. FreeBSD 11.4 hands a vnode to VOP_POLL, and both vop_stdpoll and vop_nopoll return POLLIN, POLLRDNORM and POLLOUT of the requested events for every event poll accepts and never POLLPRI, POLLRDBAND or POLLWRBAND. A file, a directory or a file read from app0.zar now answers that without a host call. File::IsVnode tells such a descriptor apart from the host file type without throwing, so a host socket lent by libSceNet still reaches its own path. (cherry picked from commit 7e26c1a) * feat(libkernel): poll the random device as randomdev_poll does poll threw for /dev/random and /dev/urandom, which select already treats as ready. FreeBSD 11.4's randomdev_poll, once the device is seeded, narrows a request that has POLLIN or POLLRDNORM to those bits and returns any other request whole, POLLOUT and the band bits included. poll now answers that, and the test that relied on the random device throwing uses a kqueue descriptor, which poll still does not support. (cherry picked from commit d0c3385) * fix(libkernel): answer select on files and the random device as FreeBSD's poll routines do selscan asks a descriptor once, with the flags of every set it is in (POLLRDNORM for the read set, POLLWRNORM for the write set, POLLRDBAND for the exception set, plus POLLHUP and POLLERR), and marks each set whose flags the answer shares. A seeded random device's randomdev_poll narrows a request with POLLRDNORM to that bit and returns any other request whole, so in the read set it is readable and in no other set, and otherwise it is in each write and exception set it was asked in; select reported it readable and writable together and never exceptional. A regular file or directory went to the host poll, which on Windows threw; vop_stdpoll makes it readable and writable and never exceptional, which select now answers in process on every host, as poll does. (cherry picked from commit aabd0e7) * feat(libkernel): poll descriptors 0 to 2 with the console model select uses select takes descriptors 0 to 2 as a console title's terminal with no input: 0 is never readable and 1 and 2 are always writable. poll threw for them unless the host happened to attach a pipe, so the same title could select on its standard streams but not poll them. poll now gives the same answer, with POLLOUT, the one bit ttydev_poll reports for a writable terminal, for 1 and 2 and nothing for 0. (cherry picked from commit 7462945) * fix(libkernel): select on kqueues as kqueue_poll answers A kqueue descriptor is readable while one of its events is triggered and is never writable or exceptional, as FreeBSD's kqueue_poll answers, on both hosts. On Windows select threw for it, and on Linux its /dev/null placeholder was reported readable and writable. A descriptor that stops being a socket, a kqueue or a pipe during the wait now fails the call with EBADF when it was closed and is selected again with the rest of the timeout when its number was reused, as FreeBSD's kern_select looks every descriptor up again in each pass, instead of throwing. On Windows a host descriptor that is not a pipe throws when select starts, before waiting. Depends on boykopovar#7139. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 6d4c7c3) * fix(relinker): keep test artifacts in the build tree The tests build their working directories with tempfile.TemporaryDirectory() and std::filesystem::temp_directory_path(), so every executable they generate lands in the user's system temp directory. On Windows, Microsoft Defender quarantines those generated binaries as Trojan:Win32/Wacatac.H!ml, and the nine tests that run one fail with WinError 5 (access denied) on a clean checkout: autorun_exit_status, optional_plt, empty_tls, tls_function_coverage, executable_segments, windows_icon, guest_needed_modules, guest_module_directories and windows_import_modules. Point TMPDIR, TEMP and TMP at a build-local directory for every test registered here, so generated executables stay inside the build tree. Test artifacts are then removed with the build directory, and a contributor who needs an antivirus exclusion can scope it to the build tree instead of the system temp directory. (cherry picked from commit 39dd854) * fix(relinker): append the test temp directory instead of replacing the environment set_tests_properties replaced the ASAN_OPTIONS that add_asan_test_options appends to guest_module_args, guest_deferred_dependencies and guest_module_fini in ASan builds. Append the TMPDIR, TEMP and TMP settings instead. (cherry picked from commit 5e340fa) * fix(libkernel): report the accept queue length for a listening socket's read event A read event on a listening socket took its data from FIONREAD, which a listening socket refuses on Linux, so kevent and sceKernelAddReadEvent reported 0 connections while saying one was ready. FreeBSD 11.4 gives a listening socket filt_solisten, which reports so_qlen, the completed connections waiting for accept. Ready now checks SO_ACCEPTCONN and on Linux reads a TCP listener's accept backlog from TCP_INFO, where tcp_get_info puts it in tcpi_unacked; with no host call for the count (Windows, macOS, a listener that is not TCP) it reports 1, the lower bound the readiness already implies. (cherry picked from commit b40e0bf) * fix(relinker): report processor 0 from RDTSCP under --to-rosetta Rosetta sets RDTSCP's ECX to its constant TSC_AUX, 0xAAAAAAAB, which is not a processor index. --to-rosetta already makes RDPID return processor 0 for the same reason, so code reading the processor through RDTSCP saw a different value from code reading it through RDPID. --to-rosetta now moves RDTSCP into an optional stub that runs the instruction unchanged and then loads 0 into ECX with a MOV, which leaves the counter in EDX:EAX and the flags as they were. A site with no movable instruction after it for the jump is kept, as for RDPID. The Intel target and a plain relink are unchanged. (cherry picked from commit 57e52c1) * fix(relinker): run guest preinit arrays before the module initializer Converted guest modules never ran their DT_PREINIT_ARRAY entries: the reader validated the array and the unused-NID filter kept its targets, but no writer emitted or ran it. ld.so runs a shared object's preinit array only for the main executable, so the Linux output runs the entries from the DT_INIT stub before the module initializer; the macOS and Windows outputs run them before the initializer in their startup stubs, and the deferred Windows output exports a thunk that runs them before the initializer for runtime-loaded modules. A module with a preinit array and no initializer is still rejected, since the Linux stub exists only with one. (cherry picked from commit 9c43211) * fix(relinker): pop the guest preinit thunk registers in reverse order The Windows deferred preinit thunk pushed r12, r13 and r14 and then popped r12, r13 and r14, so the caller got r12 and r14 back swapped. InitializeDeferredModule keeps the module base, table and count live across the call to the thunk, so a compiler that keeps any of them in r12 or r14 sees corrupted loader state after opening a deferred module that has a preinit array. Pop in reverse push order instead and pin the new bytes in test_guest_preinit. (cherry picked from commit 6574ade) * test(relinker): provide libkernel.prx to the native guest preinit load Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit e751268) * test(libkernel): drop the guest socket fstat case, which needs GuestSockets::Stat Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgW4n3qYgi5jAAohEmXRQj * test(agc): cover Intel single-queue devices in recorder fixtures (cherry picked from commit 418d385) * fix(libSceSystemService): park guest threads during LoadExec's shutdown as exit does Metal Gear Solid 4 (PPSA18194) aborts on main when its launcher starts the game: sceSystemServiceLoadExec runs the guest shutdown, a render thread that then calls into the stopped AGC driver gets ProcessShutdown back from LibcAwaitExit, and the exception unwinds into title code, which terminates the process before the new executable starts. LibcAwaitExit parks a thread only once an exit has been requested, and LoadExec ran the shutdown without requesting one. It now starts the shutdown the way exit does, so such threads wait until the process is replaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5b072bc) * fix(agc): retain submitted batches when profiling fails (cherry picked from commit 4076fce) * fix(agc): wake an idle queue 0 for completions the draw pipeline adds With pipelined draws, queue 0 hands the end-of-pipe labels of a submission to the draw pipeline and can enter its idle wait before the pipeline commits them. When such a label raises an end-of-pipe interrupt behind recorded work, the pipeline attaches the delivery to the batch as a completion and submits it, but queue 0 only reaps when it saw completions pending before it went to sleep, and nothing woke it. The batch was then reaped, and the interrupt delivered, only when the next submission arrived, so a title that waits for that interrupt before submitting more work stopped. Queue 0's idle wait now also ends when completions are pending, and the pipeline's end of a submission (and the REWIND path's open-work submit) wakes it when they are. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit bd2197e) * fix(agc): suppress the flip end-of-pipe event on queues that raise release interrupts (cherry picked from commit 2aa6bfc) * test(agc): order the late interrupt's commit after queue 0 waits The test relied on queue 0 reaching its idle wait before the draw pipeline committed the end-of-pipe label. It now forces that order: a first submission runs the dispatch and leaves its batch unreaped (APS5_NO_OPPORTUNISTIC_REAP), queue 0 must be waiting, then the label is submitted while the test holds the GPU mutex, so the pipeline commits it only after queue 0 handed it over and waits again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit ff4ecc6) * fix(agc): count pipeline jobs after queue admission (cherry picked from commit 4f639d5) * fix(libc): allocate the getifaddrs list on the application heap FreeBSD's freeifaddrs is a plain free of the list, and Doom 3 BFG (PPSA99313) releases the list with free. The list came from host malloc, so the guest free stopped on an unregistered allocation; it now comes from the application heap, and freeifaddrs returns it there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 18cf27a) * feat(libc): implement getw and putw (cherry picked from commit 7c07ebd) * fix(libc): take sceLibcMspaceReallocalign's alignment before its size Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 3802f88) * perf(agc): share unchanged shader registry chunks between published versions PublishRegisteredShader copied the whole code-address map, with every header vector and snapshot reference, for each registration under the driver mutex, so a title registering its shaders while it loads paid O(n^2). The registry now keeps its entries in address-ordered chunks of at most 64 shared by every version that has not changed them; a write clones only the chunk it touches. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 5b12f1c) * fix(libSceJobManager): refuse shutdown with compute work in flight and check the handler wait (cherry picked from commit 394ac01) * test(agc): create fixture command pools with the reset flag Seven test fixtures create their own command pool without VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, while the driver's own pool sets it and Recorder begins a used command buffer again without an explicit reset. The Khronos validation layer reports VUID-vkBeginCommandBuffer-commandBuffer-00050 in depth_storage and partially_resident_snapshot --pending-stores for this. The fixtures now create the pool the way VulkanDevice does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 563cdd6) * feat(libc): implement wcscspn, wcstoimax, wcstoumax, strtouq, rindex and flsl Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit f68ab45) * fix(agc): clear a queue's flip event latch once its own interrupts stop A queue that delivered an end-of-pipe interrupt from its own release stopped delivering the flip's event for the rest of the run, so a title that raises a release interrupt once and then waits on the flip event never saw it again. Count the flips a queue has taken since its last delivered release interrupt: four consecutive flips keep the latch, a fifth clears it and raises the event, so the queue recovers within five flips. Gran Turismo 7 (PPSA01317) pairs two flips with one release interrupt per frame, which stays under the limit, and a title that flips without further release interrupts recovers. Extend agc_driver_eop_interrupts: a latched queue still delivers its own releases, flips keep reaching the video output while their event is suppressed, an event returns once the latch clears, and a release followed by two flips raises none. (cherry picked from commit 241429c) * fix(libc): keep the host standard stream when a guest freopen of it fails (cherry picked from commit 2d8d3b9) * fix(libc): format %p the way the console's libc does The console's libSceLibcInternal.sprx (measured on firmware 12.70 in a null pointer, ignores #, + and the space flag, counts only the digits towards the width, pads with spaces on the left (on the right for - and with zeros for 0 when there is no precision), treats the precision as the minimum number of digits, prints nothing for a null pointer under %.0p, and accepts the l length. FormatPointer instead followed FreeBSD and printed 0x..., which is what a title does not see on hardware. FormatPointer now prints the digits alone, the narrow Windows formatter accepts %lp, and Linux routes %p and %lp to the guest formatter instead of glibc, which prints the FreeBSD form and (nil) for a null pointer. The wide family still refuses %lp, because the console's swprintf was not measured with it; it returns -1 as it does for any other conversion the formatter rejects. The debt note records the pointer cases the console was not measured with and what the Linux routing means for a format that mixes %p with a refused conversion. (cherry picked from commit 536fef3) * feat(libc): implement fdim and fdimf (cherry picked from commit c33cf11) * feat(libc): implement erf, erfc, and erfcf (cherry picked from commit 5509b6c) * fix(libc): ilogb special cases match PS5 libc.prx (0 -> INT_MIN, inf/NaN -> INT_MAX) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 3b5c41b) * feat(libc): implement fabsf and fabsl (cherry picked from commit 831ecb0) * fix(libc): use std::fabs overload for fabsl (cherry picked from commit d5eeed0) * test(agc): skip the emulated color compare clamp cases without MinLod The offset cases compile `image_sample_cb_cl_o`, whose LOD clamp the emitter reaches only through the device's `shaderResourceMinLod`; without it the emitter refuses the shader (the gate #388 left in place). The test had no capability guard, so on such a device every one of those cases failed at the first of them, `point, wrap with negative offsets`, instead of skipping them as `ImageSampleLodClamp` and `ImageSampleBiasA16` already do. Found on llvmpipe (Mesa 25.0.7, Debian 13), which lacks the feature that lavapipe reports from Mesa 25.2.8: `agc_driver_emulated_color_compare` and its `_srgb_decode` variant both failed. The other cases still run; the note names the cases that were skipped. (cherry picked from commit 3855136) * test(agc): accept any NaN where the f16 fma and fma_mix tests expect one agc_driver_f16_fma_denormal_modes and agc_driver_fma_mix_f16_denormal_modes compared NaN results (inf * 0) bit for bit with the RX 6700 XT's, but the NaN bits of f16 arithmetic and of v_fma_mix_f32 with f16 sources are left to the driver, so the tests fail on NVIDIA (0x7fff for 0xfe00). Rows whose expected value is a NaN now accept any NaN; every other row is still exact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit e5b4b32) * test(agc): check that 16-bit interpolation takes the barycentric emulation Without barycentrics the test shader now recompiles with the emulation active instead of throwing, and with them it does not take it. The offline target declares Float64, which the exact interpolation sums use. (cherry picked from commit f397af4) * feat(agc): link calls through the AGC FS table (cherry picked from commit 8f37cdd) * perf(shader): judge first-lane and constant reads by what they read MayEvaluate took ReadFirstLane and ReadConst as evaluable without looking at their sources, so plans kept conditions behind per-lane values. (cherry picked from commit 745cdb5) * fix(shader): stop first-lane reads that reach themselves from recursing MayEvaluate and the evaluator track the first-lane reads they are inside, so a read reached again through a loop-carried phi or a constant slot is not evaluable instead of recursing. (cherry picked from commit e14a4e5) * fix(main): unique directory for the large cache file test Culprit: a03ccc6 (large cache files in chunks); failing run 39057904312. The test used a fixed directory in the temp dir; the runners of one host share it, so a concurrent run left a second entry behind. (cherry picked from commit b54018e) * fix(main): run the large cache file test alone with a longer hang limit Windows run 39061030084: agc_shader_disk_cache_large_file wrote 2 GiB in 141 s next to 1399 parallel tests (limit 120 s), and conventions_function_bodies_shard_4 timed out at 30 s on the same loaded runner. The test is now RUN_SERIAL and its hang limit is 200 s (ctest timeout stays 240 s). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 28b3ce0) * fix(shader): collect packed ancillary readers without recursion lowerPackedAncillary follows the selects, phis and identities that forward the packed ancillary word to its readers by recursing once per link (collectReaders), with a linear scan of the visited list at each step. A long forwarding chain overflowed the stack of the thread that compiles the shader: PPSA01317 crashes about 30 s into boot in that recursion on a guest job thread, with a 0xc00000fd stack overflow inside malloc. The readers are now collected with an explicit stack and a visited set, in the same order. AI-assisted: yes (cherry picked from commit dce7784) * fix(shader): use 64-bit buffer atomics for device-memory LDS Compute LDS beyond the device's shared-memory limit lives in a storage buffer, and its 64-bit atomics took a lock in that buffer. AMD's Windows driver compiles the lock's acquire and release to s_barrier, which inside the divergent lock loop pairs with the program's own barriers, so the device-memory case of agc_driver_lds_lock_budget read half its sum. With shaderBufferInt64Atomics the device-memory LDS now uses a 64-bit view of the buffer instead: 64-bit atomics are compare-exchange loops on it and 32-bit atomics stay native, so no lock is taken. Without the feature the lock is kept. The per-workgroup slice is rounded up to an even dword count so that the 64-bit view stays aligned. (cherry picked from commit af0af83) * fix(shader): fault on misaligned 64-bit device-memory LDS atomics (cherry picked from commit cb28abe) * fix(shader): bind an unnormalized S# normalized when it samples only null images An S# that forces unnormalized coordinates was refused when it samples a T# the shader declares 3D, 2D-array, cube or multisampled, since Vulkan allows unnormalized coordinates only with a single-level 1D or 2D view. A null T# reads zeros at any coordinate, and a T# whose channels all select constants is emitted as those constants without a sample, so when the S# samples no other T# its coordinates cannot change a result. Such an S# is now marked in its binding (samplerCoordinatesIgnored) and the driver creates its sampler without FORCE_UNNORMALIZED. The flag is part of the binding content key and of the shader disk cache, whose format version is raised to 38. An S# that also samples a real T# while a null T# of such a shape shares it is still refused. PPSA01317 pairs an unnormalized clamped bilinear S# with a null 3D T# and a 3D T# whose channels all select 0 for image_sample_lz in a compute shader during boot. AI-assisted: yes (cherry picked from commit a2e79fb) * fix(shader): act on the containing dword for unaligned 32-bit buffer atomics The RDNA2 ISA (8.1.7) ignores the two low address bits of dword or wider non-formatted accesses in alignment mode DWORD. MXGP 26 (PPSA30839) sets bits with buffer_atomic_or at odd byte offsets, which recorded an unaligned BDA fault and dropped the atomic. A 32-bit buffer atomic now acts on the dword that holds its address; 64-bit atomics off an 8-byte boundary still fault. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 7e4ce1a) * test(agc): expect 32-bit buffer atomics off a DWORD boundary to act on their containing dword Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> (cherry picked from commit 96c692c) * test(agc): cover native vertex aliases after index guards (cherry picked from commit 8980d77) * fix(agc): refuse native MSAA vertex buffer reads (cherry picked from commit 15eb120) * feat(agc): find the programs that store through the BDA table A program writes guest memory through the BDA table only with a store or an atomic through a PhysicalStorageBuffer pointer. SpirvStoresThroughAddresses scans a module for such an instruction: it collects the PhysicalStorageBuffer pointer types, every value of those types, and the stores, copies and atomics whose pointer operand is one of them. A module it can't read to its end counts as storing. An address-based build now records whether its programs only read through the table (ShaderResources::BdaReadOnly). Each module is scanned once; the answer is kept by its content hash. (cherry picked from commit 0300bb0) * fix(libSceAgcDriver): accept unclamped normalized color target formats (cherry picked from commit 9fb2820) * fix(agc): count every physical pointer write form in the BDA store scan The scan listed stores, memory copies and atomics, and took only typed OpTypePointer declarations as physical pointer types. A module that wrote guest memory through an untyped PhysicalStorageBuffer pointer (SPV_KHR_untyped_pointers), through a GLSL.std.450 Modf or Frexp output pointer, or with a cooperative matrix store would have been taken for one that only reads through the BDA table, and its draws would have continued a render pass they must end. The recompiler emits none of these today, so no module's answer changes. Untyped physical pointer types now count as physical pointer types, an extended instruction with a physical pointer operand counts as a store, and the cooperative matrix stores store through their pointer operand. Pointer type declarations no longer add their storage class to the pointer values. The tests build modules for every write form the scan knows, the reads it must not take for writes, and the three forms above. (cherry picked from commit 4ff3b05) * test(agc): order draws reading through the BDA table against buffer descriptor stores A draw that only reads through the table now leaves its pass open to the next draw, so its ordering against guest memory written through a buffer descriptor rests on the pass hazard tracker's unknown-read rule alone. Two cases cover it in agc_driver_address_passes: a draw that reads through the table what the previous draw stored through a buffer descriptor begins its own pass and sees the store, and a draw storing through a buffer descriptor after one that read through the table begins its own pass. Both fail when the tracker drops that rule. (cherry picked from commit f063282) * test(agc): check that a read through the BDA table sees a target as with its pass broken Continuing a pass across a draw that only reads through the table is right only if no pass end records a target write-back that the read depends on. agc_driver_address_passes --targets renders a color whose bytes spell 1.0f into the target, then reads the target's first dword through the table into the same target, once with a tiled target and once with a linear one. The tiled read continues the rendering draw's pass and reads the bytes from before it: tiled results reach guest memory only by explicit write-backs, none of which falls between the two draws. The linear read reads the rendered color: a linear target over a host import is stored back right after its draw (the store ends the pass), and one without an import is drawn synchronously. agc_driver_address_passes_targets_split runs the same cases with APS5_NO_ADDRESS_READ_PASSES=1 and must read the same bytes; a scratch build that writes the targets back whenever a draw reading through the table breaks the pass fails it. (cherry picked from commit 4796eac) * feat(agc): implement 11_11_10 float color targets through 32-bit uint attachments Color format 7 (COLOR_11_11_10, float) keeps its 10-bit component in the low bits; with the reversed component order (COMP_SWAP 2) that is blue, with red in the high 11 bits. Vulkan has no such layout, so the target was refused at decode. Render it the way 10_11_11 unorm targets are rendered: an R32_UINT attachment, the reversed export mapping 0xc6, and a new ColorExportPacking::Float11_11_10 that makes the pixel shader pack each export with the format-43 image store conversion (toward zero, overflow to the largest finite value, negatives to zero, NaN kept). Blending, partial writes, 16-bit integer exports, DCC and the other component orders throw. AI-assisted: yes (cherry picked from commit 49b0e80) * fix(agc): refuse rounding 11_11_10 float targets and resolves of packed color targets An 11_11_10 float color target with CB_COLOR_INFO ROUND_MODE (bit 18) clear asks the CB to round, but the export packing truncates toward zero, so such a target now throws. A CB resolve whose source is a packed 10_11_11 unorm or 11_11_10 float target would resolve the raw R32_UINT words, which an integer resolve cannot average, so it now throws at decode. AI-assisted: yes (cherry picked from commit a518a90) * test(agc): refuse an additive blend into an 11_11_10 float target The blend-refusal case used CB_BLEND_CONTROL 0x60010001, which is ONE * source + ZERO * destination: a replace blend that writes the source unchanged. A packed target can draw such a blend with blending disabled, so the case now uses the additive blend 0x60010101, which a packed target must still refuse. AI-assisted: yes (cherry picked from commit 45c78bb) * refactor(shader): gather the emulated barycentric interface in one place (cherry picked from commit f4b3f03) * feat(shader): build the barycentric emulation stages from tessellation On a device without geometry shaders, each triangle becomes a three-point patch at tessellation level 1. The evaluation stage takes the corner gl_TessCoord points at as its vertex and writes it as the weights, and passes every vertex's parameters for raw reads. The vertex clip distances are forwarded, so MoltenVK's stage interface matches the vertex output. (cherry picked from commit 02a4bdf) * feat(agc): emulate barycentrics through tessellation without geometry shaders A triangle list without primitive restart whose pixel shader emulates barycentrics draws through the generated tessellation stages when the device has tessellation but no geometry shaders, as on MoltenVK. The pipeline uses a three-point patch list for it, keeps it out of pipeline libraries and validates the generated stages' modes and interface. (cherry picked from commit 03f4f67) * test(agc): cover the tessellation route of the barycentric emulation (cherry picked from commit 48b7d3a) * feat(agc): support thick 4 KiB standard XOR volumes (cherry picked from commit b2d9880) * test(agc): allow native depth fixture command buffer resets (cherry picked from commit d6ad5b7) * fix(shader): match barycentric tessellation clip interfaces (cherry picked from commit 06379ce) * fix(shader): preserve clip arrays for tessellation control points (cherry picked from commit ba9aec5) * fix(agc): draw geometry programs with the last provoking vertex Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 8f03282) * fix(shader): keep clip distances in the blocks between the barycentric tessellation stages boykopovar#7330 passes the vertex clip distances as separate ClipDistance arrays so the control stage's input matches the vertex shader's output. SPIRV-Cross, which MoltenVK uses, cannot translate a separate ClipDistance array in a tessellation evaluation stage, so on MoltenVK every pipeline of the barycentric tessellation route failed to compile. The control stage now reads the separate array the vertex shader writes and passes it on inside its per-vertex block, which the evaluation stage reads and writes. (cherry picked from commit 6491e12) * test(agc): check where the barycentric tessellation stages keep clip distances (cherry picked from commit f982c1c) * feat(libSceAgcDriver): support polygon primitive type 21 (cherry picked from commit f07df07) * test(agc): honor the GPU selector in pooled buffer checks (cherry picked from commit 3f1dcad) * fix(agc): restrict lazy sampled view usage Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 724b166) * fix(agc): keep cached pipelines across lazy buffer pool creation Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> (cherry picked from commit 86b05a0) * fix(agc): discard device buffers after memory binding fails (cherry picked from commit 432fe5c) * test(agc): count mock Vulkan objects atomically (cherry picked from commit 5070322) * fix(agc): release unpublished descriptor cache objects (cherry picked from commit 256da96) * perf(agc): replace the plane copies around a depth plane's UAV clear by a depth clear A compute kernel that stores one colour to every texel of a storage image over a depth plane (a shadow atlas clear) copied the whole plane depth image -> staging buffer -> mirror before the dispatch and mirror -> staging buffer -> depth image after it, although the kernel reads nothing and writes every texel. A dispatch of the 15-word 8x8x1 image clear kernel (direct, no partial last group) whose T# lies on a depth surface now reads its colour from guest memory under the GPU mutex, the place the kernel reads it. When the dispatch covers the whole plane, the plane is one layer of level 0, the colour is a value the plane's format holds exactly (0 or 1 for a 16-bit unorm view, 0 or 0xffff for a 16-bit uint view, 0 or a normal float in (0, 1] for a 32-bit view) and nothing is waiting to write the colour, the dispatch still writes the mirror but the copy-in is left out, and the copy-out becomes a clear of the depth image to that value. The colour is read again when the copy-out is replaced; a changed value keeps the copy-out. Every other dispatch, stencil planes and declined values keep both copies. agc_driver_depth_clear_hle_tests runs the clear sequences with the clear taken and with the copies, and compares the depth image texel by texel through the mirror, the guest memory and the depth tests. (cherry picked from commit c05b256) * fix(agc): retain import settle resources per device (cherry picked from commit a3ed32e) * perf(agc): let the resource cache bound come back down Halve the grown bound (not below 1024 entries) once a window of the bound's uses times a shrink window was served by at most a quarter of it, and reset the bound on Clear(). The shrink window starts at 8 and doubles (up to 1024) each time growth undoes the last shrink, so a set of keys that comes back over a longer span stops the bound from oscillating. The entries over the bound leave at the next insert, with the other evictions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P (cherry picked from commit bea4be8) * test(agc): keep the resource cache growth test short The test now sets a base bound of 32 entries and a first shrink window of 2 (ResourceCache::SetBase, applied at the next Clear()) and turns dispatch recipes off, so every dispatch goes through the cache: grow, shrink, regrow, backoff and the Clear() reset take about 1.2k dispatches instead of about 100k. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBXM4pgkMHYEtqN1EQni2P (cherry picked from commit f27ad68) * fix(agc): harden host import construction and mutation waits Guard incomplete imports and preserve mutation waiter errors and progress. Follow-up to boykopovar#2575; refs boykopovar#2352. (cherry picked from commit 3047980) * fix(libSceAgcDriver): normalize reserved bit 30 in guest buffer descriptors (cherry picked from commit c3c400a) * fix(agc): account recorded storage images in keep budget (cherry picked from commit 902b051) * fix(agc): account for batch-retained evicted storage images (cherry picked from commit 27497f0) * fix(agc): create legal sampled cache placeholders (cherry picked from commit 5394684) * fix(agc): omit unavailable storage MSAA placeholders (cherry picked from commit fd87226) * revert(libc): drop the duplicate wcstoimax/wcstoumax/strtouq pick f68ab45 duplicates definitions the fork already has (redefinition errors). This reverts commit 17ab20050c90dc27998251019972ea3df1de2dac. * fix(shader): add the clip distance helper header the tessellation barycentric stages include Brings only SpirvClipDistances.hpp from upstream 2b9c131 (its rect-list stage changes stay skipped), and adds samplerCoordinatesIgnored to the Metal ConversionKey now that DescriptorBinding carries it (size assert 448 -> 472). * fix(agc): build the Metal driver and libkernel select against the Batch 40 picks - Split the chunked ShaderRegistry container members into ShaderRegistryChunks.cpp so the Metal replay build (which does not compile ShaderRegistry.cpp) links them. - MetalDriver rebuilds the filtered registry instead of erasing through the const entries the chunked registry hands out. - Socket.cpp: declare GuestEbadf, used by the Unknown-revents select path. * fix(agc): link the chunked shader registry into the Rosetta Metal driver * revert(shader): drop the unaligned 32-bit buffer atomic picks (upstream boykopovar#6094) cde860417 stops the direct 32-bit buffer atomic path from referencing the BDA fault buffer, so modules keep a BDA ABI version without page-table or fault-buffer metadata and the Metal shader bridge refuses them (anyps5_metal_rect_replay and anyps5_metal_guest_draw_replay fail). Reverted with its test d35a29a92 until the bridge's BDA contract accounts for it. This reverts commits d35a29a92 and cde860417. * fix(agc): apply the Batch 40 review findings - tests: the "%p|%0" count is 5 characters, not 4. - Metal: suppress the flip end-of-pipe event on a queue that raises its own RELEASE_MEM interrupts, as the Vulkan driver does since 2aa6bfc; the interrupt callback now reports whether it reached the title. - tests: drop the includes and the declaration the review found unused. * fix(agc): reconcile the Batch 40 rebase with main The rebase kept the pre-rebase side of four conflicting files and re-added one test block main already had. Restore the main-side content the picks had replaced (the ADX lowering include and matcher, the shader cache format version, the Metal driver's pending-write APIs) and keep one copy of the pthread frame-chain test target. * test(agc): report the end-of-pipe delivery from the replay interrupt callbacks The Metal driver's interrupt callback now answers whether the event reached the title, so every Configure call site in the Metal replays returns true as the game's callback does. --------- Co-authored-by: milescoviello <kitslayer29@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: thuanquang <ghoulbulldog@gmail.com> Co-authored-by: pushkarreddyy <212133856+pushkarreddyy@users.noreply.github.com> Co-authored-by: mattogepeto <mattogepeto@gmail.com> Co-authored-by: Nicolas Falesy <112726756+nicolasfalesy@users.noreply.github.com> Co-authored-by: T3cm0 <srodriguez7604@gmail.com> Co-authored-by: ionut27001 <194206737+ionut27001@users.noreply.github.com> Co-authored-by: Alexej Szpevák <szpevakalexej@gmail.com> Co-authored-by: HQasper <52220032+HQasper@users.noreply.github.com> Co-authored-by: blu <131330772+ycbuzi@users.noreply.github.com> Co-authored-by: Karim Masmoudi <125830216+Kernel-Hunter@users.noreply.github.com> Co-authored-by: Ayamullah Khan <ayamullahkhan04@gmail.com> Co-authored-by: Adrià Franch <adriadam10@gmail.com> Co-authored-by: akkadbakkad89 <best.dv1996@gmail.com> Co-authored-by: Ondra97 <47064087+Ondra97@users.noreply.github.com> Co-authored-by: Ori Shamir <orishamir04@gmail.com> Co-authored-by: Lberonio <103309975+Lberonio@users.noreply.github.com> Co-authored-by: lorkydey <57268822+Lorkydey@users.noreply.github.com> Co-authored-by: fma-rc <188743521+fma-rc@users.noreply.github.com> Co-authored-by: babyhuey <7751607+babyhuey@users.noreply.github.com> Co-authored-by: Slick Daddy <129640104+slick-daddy@users.noreply.github.com> Co-authored-by: BIGFERRY999 <bigferry24@gmail.com> Co-authored-by: David Sera <david.sera@proton.me> Co-authored-by: Yossi Yadgar <yadgar360@gmail.com> Co-authored-by: ThatsLeo <leonardorocco.monaco@studio.unibo.it> Co-authored-by: Gjon Gojcevic <gjonn@hotmail.com> Co-authored-by: Crimson <144462348+CrimsonMac@users.noreply.github.com> Co-authored-by: Nahid Delic <nahe.delic84@gmail.com> Co-authored-by: Romans Pokrovskis <motivated.it@gmail.com> Co-authored-by: Yunus Shaikh <75296019+EagleBearerYS@users.noreply.github.com> Co-authored-by: David <David.Samir.WD@gmail.com> Co-authored-by: MarcZX100 <MarcZX100@users.noreply.github.com> Co-authored-by: Sam <jb006d6855@gmail.com> Co-authored-by: Sam <85789460+Sam-mon7@users.noreply.github.com> Co-authored-by: Dextroid17 <217507631+Dextroid17@users.noreply.github.com> Co-authored-by: AlxcBTW <187763517+AlxcBTW@users.noreply.github.com> Co-authored-by: JJenkx <josh_jenkins@hotmail.com> Co-authored-by: thibaut <thibaut-leaux-angeli@live.fr> Co-authored-by: Nikhil Panchal <37629345+nikhilpanchal1@users.noreply.github.com> Co-authored-by: MarcZX100 <marczxrr@gmail.com>
What
A 32-bit buffer atomic whose address is not a multiple of 4 now acts on the dword that holds the address. On
mainit records an unaligned BDA fault and is dropped. 64-bit buffer atomics are unchanged: off an 8-byte boundary they still fault.Source: section 8.1.7 ("Alignment") of the RDNA2 ISA:
That is mode DWORD of
SH_MEM_CONFIG.alignment_mode. The console's mode is unknown (as the existing "V# bases off a DWORD boundary" entry already notes).buffer_atomic_or(MUBUF 0x3a) at odd byte offsets in a compute shader. In the strict modes, section 3.11 reports a misaligned atomic as a memory violation, so the console is not in one of those.All of this is recorded in
docs/dev/debt/shader.md.Tested
Linux (Pop!_OS, kernel 7.1.5), GCC, Release.
New
agc_driver_buffer_atomic_unaligned: 32 lanes each runbuffer_atomic_add(glc) of 1 at byte offsetlane * 4 + k, for k = 0 to 3. The test checks that:On
mainit fails at k = 1 (lane 0 returned 3735928559, expected 0).agc_shader_memory: the specialized 32-bitbuffer_atomic_incno longer emits arecord_bda_faultcall.agc_driver_buffer_atomics,_float,_inc_dec64,_or_swap64: pass.ctest -R 'agc|shader': 563 of 563 pass.agc_driver_preparation_scope_settlefailed once and passed on reruns; it also failed intermittently on the rebased feat(shader): add GDS ordered counts and ordered-append dispatches #5176 branch, which does not touch it.MXGP 26: with this change (on a branch with other local fixes), the title runs with 0 BDA faults, where it reported
reason=7(unaligned) faults before.tools/check_conventions.py --base upstream/mainpasses.Not tested: AMD hardware, the hardware oracle, Windows.
Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code