Commit Graph
214 Commits
Author SHA1 Message Date
Dean d43e6bd663 Merge pull request #12185 from Gonic0/fix/recompiler-user-data-call-across-branches
fix(recompiler): link user-data calls whose target copy precedes forward branches
2026-10-29 07:27:53 +13:00
Nicolas GonzalezandClaude Opus 5.5 6037a12ee7 fix(recompiler): link user-data calls whose target copy precedes forward branches
FindUserDataCalls required the s_mov copy of the user-data callee pointer to
sit in the call's basic block, so a compute shader that copies s[0:1] at entry
and calls after an execz guard was left with an unresolvable s_swappc_b64.
Fall back to reachingWriter, as FindTableCalls already does.

The UserDataCallAfterBranch execution test dispatches such a shader with two
callees and checks every lane's result.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 22:43:10 -03:00
Dean Galvin d4a296d863 Merge main into perf-D2-v47: depth export tail moves to request version 49
Main took request serialization version 48 for the fragment barycentric
built-ins flag (#9193). The depth export clamp bounds are now written at
version 49 and read only for versions from 49, so a version 48 request as main
writes it still decodes, with default bounds. Tests: version 49 is the
emitted prefix, 50 the first unsupported one, a depth exporting request
without its bounds decodes at 48, 47 and 46 (the last two without main's
barycentric byte), and a version 48 two-sample dithered request decodes as
written.
2026-10-28 08:24:34 +13:00
Dean Galvin 1bf274a817 Merge main into perf-D2-v47: depth export tail moves to request version 48
Main took request serialization version 47 for the two-sample dithered
alpha-to-coverage value. The depth export clamp bounds are now written at
version 48 and read only for versions from 48, so a version 47 request as main
writes it still decodes, with default bounds. Tests: version 48 is the
emitted prefix, 49 the first unsupported one, and a version 47 two-sample
dithered request (with and without depth export) decodes as written.
2026-10-27 22:37:31 +13:00
Emanueleandyripper a95ced504c feat(recompiler): read I/J from barycentric built-ins on MoltenVK
MoltenVK exposes VK_KHR_fragment_shader_barycentric but its MSL step
rejects PerVertexKHR, so the extension is disabled there and a pixel
shader whose I/J VGPRs outlive their v_interp_p1/p2 pairs takes the
generated barycentric stages, which accept only triangle lists without
primitive restart on a device without geometry shaders.

A new target flag, fragmentBarycentricBuiltins, enables the extension
for its built-ins only. The host interpolation check then keeps the
leftover I/J reads (all perspective or all linear, at the fragment's
default position) and reads them from BaryCoordKHR or
BaryCoordNoPerspKHR, while the interpolated attributes stay hardware
varyings. Raw vertex parameters and I/J at an offset or a sample
position still take the generated stages. Apple GPUs return
unnormalized perspective weights unless the fragment function reads
[[position]], so each perspective read keeps a neutral use of
gl_FragCoord.w, loaded as a float. Devices with the full extension are
unaffected; the request serialization moves to version 45.

Co-authored-by: yripper <[email protected]>
2026-10-27 07:25:48 +01:00
Adrià 4c486ca322 Merge pull request #10515 from JJenkx/fix/agc-two-sample-dithered-a2c
fix(agc): emulate two-sample DB_ALPHA_TO_MASK 0xe701 dithered alpha-to-coverage
2026-10-27 04:52:02 +01:00
Dean d471a12b82 Merge pull request #11112 from Bram910/fix/shader-skip-unreachable-words
fix(shader): skip undecodable words that only follow s_branch or s_endpgm
2026-10-27 15:00:43 +13:00
Adrià 7992f03293 Merge pull request #9703 from frankea/perf/structurizer-construct-walk
refactor(shader): walk each construct when checking the structured constructs
2026-10-26 13:48:49 +01:00
Adrià 6641f48356 Merge pull request #9789 from rooklz/fix/agc-uint-color-export
fix(libSceAgcDriver): export raw 32-bit values into UINT color targets as integers
2026-10-26 12:48:04 +01:00
Adrià 97934af20b Merge pull request #9702 from frankea/perf/structurizer-split-search
fix(shader): structurize a 4,750-block graph before Unreal's render thread watchdog fires
2026-10-26 11:54:10 +01:00
Bram910 433aa1dac9 Merge origin/main into fix/shader-skip-unreachable-words 2026-10-26 10:57:34 +01:00
Adrià 3a930342d5 Merge pull request #9701 from frankea/fix/structurizer-scc-iterative
fix(shader): find strongly connected components without recursion
2026-10-25 23:03:07 +01:00
Bram910andClaude Opus 5.5 2158c56a88 fix(shader): end the control-flow block at s_code_end
A block that ended in s_code_end fell through to the next block. With
undecodable words skipped after s_code_end, that made a later branch
target a fall-through successor of s_code_end. The graph now starts a
block at the next decoded instruction after s_code_end and ends the
s_code_end block with a return, as it does for s_endpgm.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 19:38:58 +01:00
Bram910andClaude Opus 5.5 e1213fd473 fix(shader): skip undecodable words that only follow s_branch or s_endpgm
The RDNA program decode sweeps linearly up to the last s_endpgm past the
furthest direct branch target, so it also decoded words that no path
reaches and threw on any it could not decode. A word directly after
s_branch, s_endpgm or s_code_end that fails to decode is now skipped;
the decode still throws that word's error when a direct branch or
s_call_b64 targets it, and a failure on a word reachable by fall-through
is unchanged. The control-flow graph starts the block after s_branch and
s_endpgm at the next decoded instruction instead of the next word.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 19:15:40 +01:00
JJenkxandClaude Opus 5.5 2ff14f34e3 fix(agc): emulate two-sample DB_ALPHA_TO_MASK 0xe701 dithered alpha-to-coverage
Bugsnax (PPSA01503) draws with DB_ALPHA_TO_MASK 0xe701 (quad offsets
3, 1, 2, 3 without OFFSET_ROUND) on a two-sample target and stopped on
the refusal of every dither but the eight-sample 0x18701. The pixel
shader now applies an eight-level per-quad table for it, measured
natively on gfx1032, and the request's dither byte selects that table
(serialization version 47). It is accepted only for two matched raster
and DB_EQAA samples, the two measured location pairs and FP16_ABGR or
32_ABGR exports.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 16:40:02 -04:00
Dean Galvin c8f6a33164 Merge main into perf-D2-v47 fix (request serializer version 47 on top of main's 46) 2026-10-25 08:55:48 +13:00
Rookie b15a58d802 fix(libSceAgcDriver): export raw 32-bit values into UINT color targets as integers 2026-10-24 20:14:50 +01:00
Adrià 18e85391b4 Merge pull request #9746 from imellag/fix/shader-empty-loop-continue
fix(shader): route a loop's back edge through an empty continue block when its latch holds instructions
2026-10-24 14:53:54 +02:00
Adrià 798c908fc4 Merge pull request #9068 from frankea/table-callee-range
fix(shader): link table callees that together exceed s_call_b64 range
2026-10-24 01:58:48 +02:00
frankea ef2b164b15 fix(shader): relay later table calls past earlier calls' callee copies 2026-10-23 22:13:23 +02:00
frankea a1ae72a2e8 fix(shader): a 64-bit SALU result overwrites both dwords of a call link pair 2026-10-23 22:13:23 +02:00
frankea 0a4027df9a fix(shader): link each table callee next to its own call so large callee sets stay in branch range 2026-10-23 22:13:23 +02:00
JAB-devandClaude Sonnet 5.5 8d54357e79 Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/agc-4-4-4-4-partial-writes
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-23 21:18:00 +02:00
Adrià d7948b09f9 Merge pull request #8563 from JAB-dev/feat/agc-4-4-4-4-color-target
feat(agc): render 4_4_4_4 UNORM color targets through packed R16_UINT exports
2026-10-23 21:12:19 +02:00
Dean Galvin 13727eebd1 Merge main into perf-D2-v47 2026-10-24 07:59:40 +13:00
Maz 913e458369 fix(shader): deserialize pixel function storage dwords at version 45
The SM-budget threshold writes the storage size after the LDS flag; read it back and keep the fixtures on the fused+pointList trailer.
2026-10-23 20:53:29 +04:00
Maz 40c7ff888c fix(shader): scale pixel LDS Function threshold by SM budget
Max measured a 4 GiB private-memory ceiling on a 4090; use
VK_NV_shader_sm_builtins so denser cards take the buffer path below 4096.
2026-10-23 20:53:24 +04:00
mazen dba7f5466c fix(shader): keep a pixel LDS overflow until the GPU is idle
The slot counter is reset only after earlier fragment writes, and the overflow flag stays until WaitIdle. The storage buffer is limited to NVIDIA's proprietary driver, which is where the large function array does not finish.
2026-10-23 20:53:21 +04:00
Adrià 0940da2424 Merge pull request #8070 from thuanquang/fix/table-call-wide-descriptor
fix(shader): link a table call whose V# is a slice of an s_load_dwordx8 or s_load_dwordx16
2026-10-23 18:40:11 +02:00
Dean Galvin 7ec9ce9132 fix(agc): clamp an exported pixel depth to the viewport depth range
The DB clamps a pixel shader's exported depth to PA_SC_VPORT_ZMIN_0 and
ZMAX_0 unless DB_RENDER_OVERRIDE.DISABLE_VIEWPORT_CLAMP is set. Vulkan only
clamps with depthClamp, which also drops clipping, so the exported value
reached the depth attachment unclamped.

The pixel stage info now carries the two bounds when the shader exports
depth (0 and 1 otherwise), and the exported value is NClamped to them
before it is stored to gl_FragDepth. With
DISABLE_VIEWPORT_CLAMP the value is stored as exported; non-finite or
inverted bounds are rejected. DB_RENDER_OVERRIDE joins the draw key
registers. The bounds
are part of the recompile cache key (only for depth-exporting shaders, so
other keys do not change), the draw key and its register check, and
recompile requests (serialization version 47).

Tests: the pixel stage decode reads the bounds, passes the disable flag through
and rejects bad bounds, and leaves a draw without depth export at 0 and 1; a
recompiled depth export stores the NClamped value, and its bounds survive
serialization and key the cache.

Request version 47: main is at 44 (#8612) and open PRs also claim 44 to 46 (#8564, #7888, #9193 and the #9020, #9335, #9748 stack). Whichever of these lands second renumbers.
2026-10-24 01:38:01 +13:00
EmanueleandYcaro Gabriel 7410c37b6c perf(shader): emit exact f32 add, subtract and multiply as fma on MoltenVK
The recompiler marks every guest f32 add, subtract and multiply
NoContraction, because the guest rounds each one separately.
SPIRV-Cross turns each of them into a call to a helper that Metal
compiles without optimization, which dominates fragment time on
MoltenVK, and its multiply helper, fma(a, b, +0.0), turns a -0.0
product into +0.0.

On MoltenVK they are now GLSL Fma whose second step is exact, which
gives the separately rounded result: a * b is fma(a, b, -0.0), a + b
is fma(1.0, a, b) and a - b is fma(-1.0, b, a). The sin, cos and log2
series and the offset image coordinates and gather texel scale use the
same forms. The choice is a SpirvTarget flag set for
VK_DRIVER_ID_MOLTENVK, part of the shader cache key and of the request
serialization (version 44); other drivers are unchanged.

agc_driver_f32_separate_rounding checks a multiply feeding an add or a
subtract on rows where fusing them changes the result. It,
agc_driver_f32_arithmetic_nan and agc_driver_f32_denormal_flush also
run the fused forms on devices that do not use them, so lavapipe checks
them against the same rows.

Co-authored-by: Ycaro Gabriel <[email protected]>
2026-10-23 12:13:18 +02:00
JAB-devandClaude Sonnet 5.5 8b7dd6beb7 Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/agc-4-4-4-4-color-target
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-23 12:00:34 +02:00
Emanuele 75fdf64721 fix(shader): route a loop's back edge through an empty continue block when its latch holds instructions 2026-10-23 06:46:55 +02:00
frankea 958f3f960d Merge branch 'perf/structurizer-split-search' into perf/structurizer-construct-walk 2026-10-22 22:40:29 -04:00
frankea 969aadab62 Merge branch 'fix/structurizer-scc-iterative' into perf/structurizer-split-search 2026-10-22 22:40:28 -04:00
frankea 050af89f1a Merge remote-tracking branch 'origin/main' into fix/structurizer-scc-iterative 2026-10-22 22:40:28 -04:00
JAB-devandClaude Sonnet 5.5 d201de3f32 Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/agc-4-4-4-4-partial-writes
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-23 04:09:40 +02:00
Dean Galvin 6990044dfb Merge main into feat/geometry-points-sized 2026-10-23 12:43:00 +13:00
JAB-devandClaude Sonnet 5.5 9ab242a6da Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/agc-4-4-4-4-color-target
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-22 19:47:24 +02:00
JAB-devandClaude Sonnet 5.5 4aaf26e3f3 Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/agc-4-4-4-4-partial-writes
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-22 14:24:38 +02:00
Get-fix a13d01bcc9 fix(shader): keep the PS5 user-data pointer check unchanged outside PS4 shaders
unchangedUntil skipped a write by the reading instruction itself for every shader, so an RDNA s_load that overwrites its own address pair was taken as a user-data pointer load where main refused it. The skip now applies only to PS4 (GCN) shaders, where it is needed; PS5 shaders take main's path.
2026-10-22 12:19:13 +03:00
Get-fix 4b717912d7 feat(shader): decode PS4 (GCN) shaders by re-encoding them to RDNA
Each GCN instruction is re-encoded to its RDNA form and decoded by the
existing RDNA decoder, so the PS4 path shares the recompiler. Covers the
legacy VS stage (program at the VS slot, user SGPRs from s0, vertex and
instance index in v0-v3), fetch shaders linked as user-data calls, MIMG
(image_sample and friends), PS4 buffer descriptors (GCN DATA_FORMAT /
NUM_FORMAT, 44-bit base) and T# image descriptors (GCN tiling in word 7).
The GCN switch is keyed into the shader disk cache. PS5 behaviour is
unchanged: every PS4-only rule is gated on GcnGuestShaders().
2026-10-22 12:17:44 +03:00
frankea 6ba72890ad perf(shader): walk each construct when checking the structured constructs
hasStructuredConstructs tested every block of the graph against each merge header, and searched every block for a block's innermost loop, with dominator binary searches each time. Under APS5_FORCE_STRUCTURIZER_FALLBACK, Ghostrunner's compute shader 0x200c520000 becomes a 32,490-block reach-flag graph with 6,647 merge headers, and the check took 44 to 50 s of the 49 to 55 s.

The innermost loop is now searched among the loop headers only. Every other block failed the loop-header test first, so the candidates and their order are unchanged.

When constructsFollowDominance holds for the structural graph, the member checks visit only the blocks dominatedBlocks finds from the header, stopping at its merge, and for a loop also from its continue block, in block order. Only a block in the header's construct or in the loop's continue construct can fail a member check. A construct block is dominated by the header and not by the merge, so some path from the entry reaches it without the merge, and from the last header on that path every block is dominated by the header, so the walk reaches it. A continue-construct block is dominated by the continue block and is reached from it the same way. The walk finds every such block, and the blocks it skips passed every check, so the result is unchanged. Otherwise every block is checked as before, and APS5_NO_PERF_DOMINATOR_TREE keeps the old loop.

On an M4 Pro the check takes 0.27 s instead of 44 to 50 s on that graph, and the forced fallback takes 5.1 to 5.4 s instead of 49 to 55 s. The default structurization of the shader makes one check, which takes 0.03 s instead of 0.36 s.
2026-10-22 04:27:00 -04:00
frankea 231be5cc7c perf(shader): test a selection construct's members by dominance
After each split, every selection header that needs no work still walks its construct (dominatedBlocks) to learn which predecessors of its merge lie inside. On Ghostrunner's compute shader 0x200c520000 that is 1.7 million walks.

When every block is reachable from the entry and the predecessor lists match the successor lists, and the merge M neither is the header H nor dominates it, the walk finds exactly the blocks that H dominates and M does not, other than M. A block the walk reaches is dominated by H and reached from the entry on a path that avoids M, because some path to H avoids M. A block that H dominates and M does not lies on a path from the entry that avoids M, and from the last H on that path every block is dominated by H, so the walk reaches it. splitOneSelectionMerge checks those conditions once per pass and then asks the dominator sets directly; otherwise it walks as before. Only membership in the construct is used, so the split and its order are unchanged.

On an M4 Pro the shader's structurization takes 12 to 15 s instead of 18 s, and 176 s before this series.
2026-10-22 04:27:00 -04:00
frankea 3606824314 fix(shader): find strongly connected components without recursion
computeStronglyConnectedComponents visited blocks recursively, one stack frame per block on the deepest path. The reach-flag fallback turns Ghostrunner's compute shader 0x200c520000 into a graph of 32,490 blocks, and with APS5_FORCE_STRUCTURIZER_FALLBACK the visit overflowed agc_shader_replay's 2 MiB stack after 4 s.

The visit now keeps its frames in a vector. It enters successors in the same order and closes components in the same order, so the components, their entry blocks and the irreducibility verdict are unchanged.
2026-10-22 04:27:00 -04:00
frankea d30b78f47b perf(shader): build the structurizer's dominator trees instead of iterating dominator sets
recomputeAnalyses runs after every merge split, and its dominator and post-dominator fixpoints intersected a bitset of every block for every block until nothing changed. They now build the tree with the Cooper-Harvey-Kennedy iteration over a reverse post-order from a virtual root above the entry block, or above every exit for post-dominators, and write each block's sorted set as its parent's set plus the block itself.

The sets are the same. When every block is reachable from the virtual root, the greatest fixpoint of the old equations is the dominance relation of that rooted graph, which the tree describes. When a block is unreachable from the entry, or cannot reach an exit, the old fixpoint runs as before.

The post-dominator tree also keeps each block's immediate post-dominator, and FindNearestCommonPostDominator climbs it when both blocks have one: the sets are the tree's paths, so the deepest common block is the one the set intersection found. Blocks added or cloned after the analysis have no parent and take the set path, as before.

APS5_NO_PERF_DOMINATOR_TREE turns both off, and the structurizer and control-flow tests also run with it set. On an M4 Pro the structurization of Ghostrunner's compute shader 0x200c520000 takes 18 s instead of 37 s.
2026-10-22 04:27:00 -04:00
frankea 32be418b33 perf(shader): look up the structurizer's sorted block lists instead of scanning them
Merge splitting re-examines every selection header after each split, so the lookups behind each examination run millions of times on a large graph. On Ghostrunner's compute shader 0x200c520000 (4,750 blocks growing to 10,170 over 1,302 splits) agc_shader_replay spent 173 s in splitSharedMergeBlocks. A profile put most of it in linear membership tests and in FindNearestCommonPostDominator, not in the analyses.

Loop bodies, construct blocks, selection regions and strongly connected components are sorted, so membership in them is now a binary search. The traversals that tested membership in the list they were building (selectionRegion, reachableWithin, naturalLoopBody) and externalEntryEdges test a bitset instead, and selectionRegion reads its sorted result from the bitset. dominatedBlocks tests the header once and pushes a successor only before it is visited. FindNearestCommonPostDominator skips a candidate whose post-dominator set is smaller than the other common post-dominators and checks the rest in one pass over the set. remapIds sorts only when the map broke the order.

Each change keeps the same sets in the same order and the same first candidate, so the structured graph is unchanged. On an M4 Pro the shader's structurization takes 37 s instead of 176 s.
2026-10-22 04:27:00 -04:00
JAB-devandClaude Sonnet 5.5 8ed185a51c Merge branch 'main' of https://github.com/boykopovar/AnyPS5 into feat/agc-4-4-4-4-partial-writes
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-21 20:13:17 +02:00
thuanquangandClaude 01e7d158b6 fix(shader): link a table call whose V# is a slice of an s_load_dwordx8 or s_load_dwordx16
Co-Authored-By: Claude <[email protected]>
2026-10-22 00:13:39 +07:00
milescovielloandClaude Opus 5.5 11bc7b1686 fix(shader): move the vertex point fields to request version 43
main took request versions 33 to 42 for other fields, the last of them
the pixel sample order, so requests of those versions from main have no
point-list tail. The point fields now come with version 43, after them,
and the tests expect 43 and reject 44.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 09:13:32 -04:00