Commit Graph
164 Commits
Author SHA1 Message Date
Dean Galvin 6990044dfb Merge main into feat/geometry-points-sized 2026-10-23 12:43:00 +13: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
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
milescovielloandClaude Opus 5.5 e7ea490325 feat(agc): draw point lists on the vertex path with their point size
A point list routed through the vertex path was refused, and so was
PA_CL_VS_OUT_CNTL.USE_VTX_POINT_SIZE on it. OliSe Player (PPSA01153)
stops on the latter with two of its MilkDrop presets.

The vertex path now draws point lists as Vulkan points. The vertex stage
writes PointSize: the misc export's X clamped to PA_SU_POINT_MINMAX when
USE_VTX_POINT_SIZE is set, and the PA_SU_POINT_SIZE height otherwise.
The point state travels in the recompile request (version 31) and the
cache and draw keys. Non-square register sizes, inverted clamps, point
sprite texture coordinates and, without largePoints, sizes other than
one pixel throw.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 09:13:09 -04:00
Yunus Shaikh 6d09797d6b Merge main into fix/agc-sample-location-grid-exact 2026-10-21 05:39:42 +00:00
Dean 494182e051 Merge pull request #8201 from savvaniss/fix/shader-call-link-wave-size
fix(shader): check call link registers for both wave sizes when the size is unknown
2026-10-21 15:02:27 +13:00
Dean Galvin a07ad30578 feat(shader): request DenormPreserve 32 where the device preserves f32 denormals
The recompiler asked for DenormPreserve only for 16-bit and 64-bit floats,
so a device that flushes 32-bit denormals by default (RADV, MoltenVK) flushed
them whatever the guest's float mode said. target.float32DenormPreserve is set
from shaderDenormPreserveFloat32 when denormBehaviorIndependence allows a 32-bit
mode, is part of the cache key and of the request serialization (version 41),
and makes the module declare DenormPreserve 32 unless the 32-bit flush is
already taken in hardware. APS5_NO_HW_F32_PRESERVE=1 turns it off.
2026-10-21 10:45:24 +13:00
Savvanis SpyrosandClaude Opus 5.5 267d211318 fix(shader): refuse a call link escape only at the wave size it escapes under
When the graph is built without the wave size and the return address
escapes under only one of wave32 and wave64, the graph records that size
and its error instead of throwing, and translation throws it only at
that size. A wave64 program that reads a compare mask written over a
call's link pair from a scalar instruction is now recompiled, while the
same program at wave32, where the high dword still holds the return
address, is refused.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 18:13:18 +03:00
Dean 5ccf1824df Merge pull request #6679 from SleglJan/pr/merged-hull
feat(agc): run registered merged hull programs (binary type 3)
2026-10-21 04:12:03 +13:00
Savvanis SpyrosandClaude Opus 5.5 cf12795014 fix(shader): check call link registers for both wave sizes when the size is unknown
The control-flow builder does not know the wave size (the front end is
shared across wave sizes), and with an unknown size it took a v_cndmask
mask to read two registers but a VOPC result to write only one. A wave64
compare that overwrote a call's link pair before a v_cndmask read it was
then rejected as a link register escaping the call/return model. The
check now runs once for wave32 and once for wave64 when the size is
unknown, so each read is matched with writes of the same width.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 18:07:21 +03:00
Jan ŠléglandClaude Opus 5.5 8b8219dcef feat(agc): run registered merged hull programs (binary type 3)
A registered type-3 hull program holds the local and the hull part of a
tessellation draw in one binary, addressed through SPI_SHADER_PGM_LO_LS
(SH 0x148) with no SPI_SHADER_PGM_LO_HS: the local part runs on the lanes
below s3[7:0] and writes LDS, an s_barrier follows, and the hull part runs
on the lanes below s3[15:8] and may store the registers the local part
filled. The draw decode asked 0x148 for a type-5 local half, and
PPSA11386 (007 First Light) stopped in its level load with "registered
program refers to an incompatible shader binary type".

The draw decode now gives a registered type-3 program both the local and
the hull role and marks the tessellation configuration mergedHull. The
local stage only passes gl_VertexIndex and gl_InstanceIndex in a 16-byte
record. The hull stage runs the whole program, one invocation per output
control point: v2 and v5 come from the record of control point
min(gl_InvocationID, inputs - 1), v3 is the relative vertex id, and the
LDS becomes a per-control-point output array that a control point writes
in its own record and reads in any record of its patch after the barrier.
Registers live across the barrier stay in the invocation.

The hull stage of a merged program runs at one lane per invocation on
every device (HostSubgroupSize 1). Its lanes are gl_InvocationID, not host
subgroup invocations, so the s_cbranch_execz around the local part tests
the invocation's own lane. With the device's subgroup it took a subgroup
ballot, and lavapipe, whose subgroup stages lack the tessellation control
stage, rejected the draw ("subgroup capability 61 is unsupported for
shader stage 2").

The lowering refuses more input than output control points, LDS writes
outside the own record, reads outside the patch's records, barriers that
some invocations do not reach or that sit in a loop, reads or writes of
another lane's register, outputs beyond the device limits, and fetch
subroutines. Type 3 is prepared with its stage group like type 5 and
takes its float mode from RSRC1_HS. The hull user-data address (SH
0x102/0x103) is optional for a merged program: s[0:1] take it, unchecked,
when the draw sets it and are zero otherwise.

Both stages refer to one binary, so a draw lists each code and header
region once (AppendProgramMemory); a request's memory regions must not
overlap. An indirect draw's record words go into every program whose user
data holds the register, so the hull stage of a merged program reads
them too.

mergedHull is part of the recompile cache key, the draw decode comparison
and the request serialization (version 39).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-20 15:55:50 +02:00
Adrià 809aab71d8 Merge pull request #7630 from JJenkx/fix/shader-scheduled-table-index
fix(shader): follow scheduled jump table index writes
2026-10-20 13:04:54 +02:00
Adrià 9b4b3cf463 Merge pull request #6941 from kq62/fix/mesh-barycentric-emulation
feat(shader): emulate barycentrics and per-vertex parameters on the mesh path
2026-10-20 00:17:49 +02:00
Adrià f745930f03 Merge pull request #7413 from thuanquang/fix/table-call-hoisted-descriptor
fix(shader): link a table call whose descriptor load sits before its loop
2026-10-19 21:50:42 +02:00
sussyandClaude Sonnet 5.5 dd41527ce2 feat(shader): emulate smooth barycentrics on the mesh path
A pixel shader that reads barycentrics on a device without
VK_KHR_fragment_shader_barycentric used to need a vertex shader so a
generated geometry stage could follow it, which a mesh pipeline cannot
have. The mesh program is now compiled a second time with the layout
its pixel shader needs: it writes its clip position as a varying and the
three corner positions of each primitive as per-primitive outputs, and
the pixel shader solves for the weights, for both smooth and linear
interpolation.

The layout is part of the mesh cache key, the stored artifact and the
captured request. Without VK_EXT_mesh_shader the draw still throws, now
saying so. Per-vertex parameters on the mesh path still throw.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-19 17:26:37 +02:00
simonbronkandClaude Opus 5.5 7563b4a480 fix(agc): replace with a stencil operation value the pixel shader exports
With DB_SHADER_CONTROL.STENCIL_OP_VAL_EXPORT_ENABLE the pixel shader
supplies the value that REPLACE_OP and the add and subtract operations
use in place of DB_STENCILREFMASK.STENCILOPVAL. The bit was refused as a
whole, which stopped Onimusha: Way of the Sword (PPSA27836) at a
DB_SHADER_CONTROL 0x45 draw with DB_STENCIL_CONTROL 0x40040 (REPLACE_OP
on depth pass, both faces), write mask 0xff and compare ALWAYS.

The export is accepted with the 32_GR and 32_ABGR Z formats. When no
reachable operation through a non-zero write mask reads the value, it
cannot change the result and is ignored. Without a test value export, a
reachable REPLACE_OP becomes REPLACE with the exported value as
FragStencilRefEXT (or the bounded replay on devices without the
extension), provided both faces compare without the test value and use
no ONES or REPLACE_TEST; early tests throw. The add and subtract
operations, and REPLACE_OP with a test value export, still throw.

The value is read from bits 8-15 of the second MRTZ channel: the
title's pixel shader shifts it left by 8 right before exp mrtz
(v_lshlrev_b32 v0, 8, v0). That is the only evidence for the layout and
it is listed as technical debt. The pixel stage info carries the new
flag into the cache key, the draw keys and request serialization
version 31.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 12:53:13 +02:00
Yunus Shaikh 53cc193a3c Merge main into fix/agc-sample-location-grid-exact
#6503 took request serialization 36 for its float32 flush flag, so this branch's sample order moves to 37. The version relabels in ShaderMemory.cpp and RequestSerializerTests.cpp drop both trailing flags and the sample order.
2026-10-18 22:52:06 -07:00
ThatsLeo ac14d6317d perf(shader): flush f32 denormals in hardware when a startup probe matches the emulation
Devices with DenormFlushToZero for f32 run the mixed denormal-flush programs with and
without the hardware mode at startup; when every result matches, add, sub, mul, fma,
floor and ceil in the default rounding mode drop their software flush.
2026-10-19 07:00:13 +02:00
Yunus Shaikh 18463ff5c2 Merge main into fix/agc-sample-location-grid-exact
#4650 took request serialization version 35 for its depth remap flag, so this branch's sample order moves to 36. The serialization tests drop #4650's trailing byte from every older version they build, as main's do, along with the iteration fields and the sample order.
2026-10-18 20:56:57 -07:00
Dean Galvin 4d4ae6e5eb Merge main into pr-4650
# Conflicts:
#	core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp
#	core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp
#	core/shader/recompiler/tests/RequestSerializerTests.cpp
2026-10-19 14:32:36 +13:00
Yunus Shaikh 745f199d5a Merge main into fix/agc-sample-location-grid-exact
Combines this branch's sample order with #6470's once-per-pixel and once-per-sample inputs. The pixel request keeps both sides' fields: #6470's iteration, position location and sample locations are serialized at version 34, and the sample order after them at version 35. The serialization tests move the sample-order bytes after the iteration fields and drop both from older versions. The pixel info collector adds sample order to the SampleLocationsVary inputs, and the draw keys compare and hash both. EmitSampleOffset's iterated index already goes through GuestSampleIndex, since the SampleId builtin does. The shader cache stays at FormatVersion 40, and the sample-location debt entry takes #6470's sentence on the position inputs.
2026-10-18 18:30:37 -07:00
Adrià 00666eeb36 Merge pull request #6470 from EagleBearerYS/fix/recompiler-once-per-pixel-sample-inputs
fix(recompiler): run a pixel shader the guest does not iterate once per pixel
2026-10-19 03:21:34 +02:00
Adrià 55a3ac6440 Merge pull request #6405 from fdfawef324/claude/shader-structurizer-branching-returns
fix(shader): give a selection a merge when one arm returns through nested branches
2026-10-19 02:07:54 +02:00
Dean Galvin cf4d1f4a97 Merge main into pr-4650
# Conflicts:
#	core/shader/recompiler/ShaderDiskCache.cpp
2026-10-19 11:15:57 +13:00
thuanquangandClaude cc69016858 fix(shader): follow long branches in the hoisted descriptor search
reachingWriter followed only fall-through and direct branches, so a V#
load on a path that reaches the loop through an s_setpc_b64 jump the
control flow analysis resolves was not seen, and another load could be
taken as the only writer. The walk now follows those jumps. It also
fails at s_call_b64 targets, whose callers it does not follow, instead
of walking from a function entry into the code above it.

Co-Authored-By: Claude <[email protected]>
2026-10-19 02:18:43 +07:00
Yunus Shaikh a74f343e58 Merge main into fix/agc-sample-location-grid-exact 2026-10-18 11:30:40 -07:00
Yunus Shaikh dc47a81b02 Merge main into fix/recompiler-once-per-pixel-sample-inputs 2026-10-18 11:30:31 -07:00
JJenkx eaa6d89ff9 fix(shader): follow scheduled jump table index writes 2026-10-18 14:02:37 -04:00
thuanquangandClaude Opus 5.5 499e2db905 fix(shader): link a table call whose descriptor load sits before its loop
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 00:53:38 +07:00
Adrià c34e9d855c Merge pull request #6160 from fma-rc/feat/agc-fs-table-calls
feat(agc): link calls through the AGC FS table
2026-10-18 19:44:10 +02:00
Dean Galvin 5db206aba0 fix(agc): read the depth remap scale and offset from push constants at run time
The remap mode stays a specialization constant and cache key input. Scale and
offset are two push words at byte 104, patched per draw, so draws that differ
only in depth range share one pipeline. The recipe, journal and serializer
carry the mode only.
2026-10-18 19:01:14 +13:00
Yunus Shaikh 8f6f7d7500 Merge main into fix/agc-sample-location-grid-exact 2026-10-17 19:10:21 -07:00
Mouad-Scriptz-0andClaude Fable 5.1 53212b18ac feat(agc): remap a viewport depth range the device cannot express into the vertex shader
Without VK_EXT_depth_range_unrestricted a viewport depth range outside
[0, 1] threw. Minecraft (PPSA17221) draws its world with scale 1 and
offset 0 in negative-one-to-one clip space, so its depth is the raw z/w
clamped by PA_SC_VPORT_ZMIN/ZMAX. On such a device the vertex shader now
scales clip z so z/w is the console's depth, the pipeline clamps depth at
the console's bounds, and two clip distances restore the near and far
planes that depth clamping removes. Mode, scale and offset travel as
pipeline specialization constants from the draw's vertex stage info, in
both the per-draw and the registration-module path; mesh and tessellation
draws with such a range still throw.

The console's depth clamp bounds are clipped to [0, 1]: exact for a
normalized depth target or none, and reported once for a tested or
written float depth target, where a depth below 0 is stored as 0
(Minecraft clamps its float depth target to [-1, 1]). The shader
validator accepts the remap's two extra clip distances next to the one
a program may already export.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-18 14:27:48 +13:00
Yunus Shaikh ddf072e6f3 Merge main into fix/recompiler-once-per-pixel-sample-inputs
Main's EmitSetAttribute debt entry now says the generated rect-list and
barycentric tessellation stages forward the vertex shader's clip array.
It sits next to this branch's PERSP_SAMPLE and pixel position entries,
which main left as they were; keep main's entry and this branch's two.
2026-10-17 17:03:27 -07:00
Gjon Gojcevic 49b0e805f0 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
2026-10-17 17:29:21 -04:00
fma-rc 8f37cddff0 feat(agc): link calls through the AGC FS table 2026-10-17 16:47:09 +02:00
Yunus Shaikh 3d42f60978 Merge branch 'fix/agc-sample-location-grid-fallback' into fix/agc-sample-location-grid-exact
The fallback branch's merge of main brings the single-sample scan entry
after the custom MSAA sample locations entry in the AGC debt file. Keep
it after this branch's version of that entry.
2026-10-17 03:05:00 -07:00
Yunus Shaikh 7b3826a42d Merge main into fix/recompiler-once-per-pixel-sample-inputs
main d7262bd6d6 adds the single-sample scan entry after the custom MSAA
sample locations entry in the AGC debt file. Keep it after this
branch's version of that entry.
2026-10-17 03:04:20 -07:00
Adrià ea5c27b53e Merge pull request #6025 from wh1trope/perf/dominator-bitsets
perf(shader): intersect dominator sets as bitsets
2026-10-17 05:30:45 +02:00
Yunus Shaikh f7e1f5d2e8 Merge branch 'fix/agc-sample-location-grid-fallback' into fix/agc-sample-location-grid-exact
Takes #6451's merge of main. Main's spanned-waves flag took request
serialization version 32, so the sample order moves to version 33; a
version 32 request keeps its spanned-waves flag and reads no sample order.
Multisampled color targets now gain storage usage on their first storage
binding; a target whose samples may be reordered still gets it at creation,
and the recorder's storage-usage checks expect that.
2026-10-16 12:46:37 -07:00
Yunus Shaikh bd09c49374 Merge main into fix/recompiler-once-per-pixel-sample-inputs
Main took request serialization versions 31 (output primitive size) and
32 (spanned waves), and #6683 uses 33, so the pixel iteration fields move
to version 34. A version 32 request keeps its spanned-waves flag and reads
no pixel iteration.
2026-10-16 12:41:06 -07:00
Yunus Shaikh 418d5abb88 Merge main into fix/agc-sample-location-grid-exact
Brings #5884's request field (output primitive size, read from serialization version 31). This branch's sample order stays at version 32, read after it, so a version-31 request reads as before.
2026-10-16 06:37:54 -07:00
Alex ColliniandClaude Opus 5.5 7a6cc71786 feat(agc): carry spanned waves in the target and require full subgroups
APS5_SPANNED_WAVE is now read once by the driver, which sets
SpirvTarget::spannedWaves only on a device with 32-lane compute subgroup
control and computeFullSubgroups. The recompiler no longer reads the
environment. The field is part of the cache key and of the request
serialization (version 32), so a cached or replayed module keeps the mode
it was built in.

A spanned module numbers its lanes SubgroupId * 32 +
SubgroupLocalInvocationId, which needs two full 32-lane subgroups. The
artifact records this as fullSubgroups (disk cache and pipeline identity
included), and the driver creates those pipelines with
REQUIRE_FULL_SUBGROUPS. Compute pipeline captures accept the flag with a
required subgroup size.

The two-lane fallback now catches only SpannedWaveUnsupported, thrown for
a shuffle wider than 32 bits; other emit errors surface as before.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 14:12:58 +02:00
billybobpicklioandClaude Opus 5.5 a0347cfed5 feat(agc): draw line-strip output of NGG geometry programs as mesh lines
A geometry program with VGT_GS_OUT_PRIM_TYPE 1 was refused as an
unsupported output assembly. Each primitive export of an NGG line-strip
program is one line whose indices are packed like a triangle's, so the
mesh module now declares OutputLinesEXT with PrimitiveLineIndicesEXT for
it. A subgroup gets VGT_GS_MAX_VERT_OUT - 1 lines per input primitive and
at least GE_MAX_OUTPUT_PER_SUBGROUP line slots; fast launch keeps using
PRIM_AMP_FACTOR. The output size is part of the cache, draw, validation
and pipeline keys, and the request serialization version becomes 31.
Triangle-strip geometry is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 15:39:35 -04:00
Yunus Shaikh 57ceb9e370 fix(agc): keep the guest's sample order where pixel (0,0)'s locations stand in for a 2x2 grid
Village's 2x checkerboard puts its two samples at x = +4/16 and -4/16
and swaps their indices on odd rows. On a device without a 2x2 sample
location grid every pixel takes pixel (0,0)'s locations, so the odd
rows hold the console's sample 1 in sample 0 and the reverse: a pass
that reads the samples by index, as checkerboard reconstruction does,
exchanges the two half-pixels of every odd row, and SampleID, sample
masks and exported coverage name the wrong samples there.

Such a pattern holds one set of positions in every pixel, so drawing
with pixel (0,0)'s locations is exact up to the order of each pixel's
samples. The draw now keeps the guest's order: the pixel shader reads
SampleID, applies the sample mask and exports coverage through each
pixel's order, and every render pass drawing with the stand-in puts the
samples of its multisampled attachments in the stand-in's order before
it and back in the guest's after it. Color targets do that through a
storage view of their texels as unsigned integers, depth through a copy
and a per-sample pass writing FragDepth, and stencil through
VK_EXT_shader_stencil_export or one pass per bit.

It needs shaderStorageImageMultisample with a storage view of the color
format, sampleRateShading, and a depth format FragDepth writes back
exactly (16-bit or 32-bit float); otherwise, and for patterns whose
pixels hold different positions, the stand-in alone is used, and the
log line says why. Devices with the grid are unchanged.

Recompile requests carry the order as serialization version 32 (#6470
takes 31), and the shader cache format becomes 40 (open pull requests
take 37 and 39).
2026-10-15 17:53:11 +00:00
Yunus Shaikh d5cbfd1e12 feat(agc): carry pixel iteration and sample locations in recompile requests
A pixel shader's request now says whether the guest iterates it per
sample (PA_SC_MODE_CNTL_1.PS_ITER_SAMPLE on a multisampled draw), where
SPI_BARYC_CNTL puts POS_X_FLOAT and POS_Y_FLOAT (POS_FLOAT_LOCATION,
bits 17:16, and POS_FLOAT_ULC, bit 20), and the guest's sample
locations for each pixel of the 2x2 quad when the shader reads
PERSP_SAMPLE, LINEAR_SAMPLE or a position at the sample: sample 0's
once per pixel, every sample's when iterated. The fields join the
shader cache key, the draw cache key and the request serialization
(version 31). A missing SPI_BARYC_CNTL reads as the pixel centre and
the reserved location 3 throws.

Devices with sampleRateShading now advertise the InterpolationFunction
capability, which the pixel shader validator accepts.
2026-10-15 16:06:38 +00:00
fdfawef324andClaude Opus 5.5 a2f497a05c fix(shader): give a selection a merge when one arm returns through nested branches
mergeBesideReturns ignores the edges into blocks that only lead to a return
when it looks for the merge of a selection whose arms do not meet. It only
recognized a straight run of single-predecessor blocks ending in a return,
so an arm whose return path branches again (an early return inside a
nested if/else) still counted as a block of its own, the arms had no
common post-dominator, and the whole program fell back to flag
structurization ("conditional block 0 has no structured merge").

A block now returns when it has one predecessor, is dominated by the
header and every successor returns. Flag structurization turns every
original block into a guarded region, and the register phis at the region
joins carry values from paths that never reach a later use: in Resident
Evil 4 (PPSA07411) pixel program 0x20762add00 the T# of an image_load
became Phi(0, load through Phi(user data, compare mask)) and resource
analysis refused it. Structured, the T# is the load through the user-data
pointer and the program prepares, captures and materializes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:11:54 -04:00
NikoandClaude Opus 5.5 16ea32c97d feat(agc): blend into color targets with a reversed component order
8_8_8_8 and 16_16_16_16 targets with COMP_SWAP 2 or 3 keep the exported
alpha in their first channel and red or blue in their fourth, so a plain
Vulkan blend would run the alpha equation on a color channel. Such draws
threw "blending into a color target with a reversed component order is
not implemented" (Gran Turismo 7, #4782).

The AMD SI and CIK register references describe CB_TARGET_MASK in the
order of components after blending and before COMP_SWAP is applied, and
RADV programs CB_BLENDn_CONTROL without regard to the swap while it
renders R4G4B4A4, B4G4R4A4, R5G5B5A1 and B5G5R5A1 through SWAP_STD_REV
and SWAP_ALT_REV and passes the CTS blend tests on Navi10. The color
block is therefore taken to blend in export order: the alpha equation
and source-alpha factors use the exported alpha and the destination
alpha is the first channel.

TranslateReversedBlend keeps the factor pairs Vulkan applies per channel
in the pipeline. Other source factors are computed by the pixel shader
from its MRT0 export (clamped for UNORM targets, as BLEND_CLAMP says)
and handed to the blend as a dual-source second color, which the
fragment output post-pass adds at location 0 index 1; when both
positions need it, the export is premultiplied by its source factor.
The code is a pipeline specialization, serialized in request version
26, and stays out of the prepared shader key.

sRGB, SNORM and integer targets, constant and second-source factors,
destination alpha in the color equation, destination factors the two
equations do not share, different color and alpha operations, MIN or
MAX with factors other than ONE, other color exports when the shader
computes factors, and devices without dualSrcBlend still throw, now
with the blend control, mapping and format in the message.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:51:27 +02:00
fdfawef324 1ffc5f672b feat(agc): emulate per-pixel multisample coverage masks
Decode the four PA_SC_AA_MASK fields at the rasterized sample count. Use the native Vulkan mask when all four pixels agree and apply varying masks in the fragment shader, preserving helpers and combining coverage with alpha-to-coverage and shader mask exports. Add the normalized masks to pipeline, draw and shader keys and the versioned replay request.

Refuse unsupported conservative-raster, centroid and early depth/stencil memory-write cases. Synthetic readbacks cover driver submission, depth/stencil suppression, fragment stores and one-sample and four-sample coverage.
2026-10-15 13:00:34 +13:00