Repository navigation
Conversation
… exports CB_COLOR_INFO format 19 with UNORM and COMP_SWAP 2 lists A, B, G, R from the least significant bits. No Vulkan device is required to render into the 4-bit formats and NVIDIA exposes none of them as color attachments, so the target is an R16_UINT image and the pixel shader packs the exports into the guest layout, as the 10_11_11 target does. Writes of fewer than four components, blending, DCC and truncating rounding are refused. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ed words Draws 32_ABGR and FP16_ABGR exports into a linear 4_4_4_4 target and checks every pixel against the guest layout (A in bits 3..0, B in 7..4, G in 11..8, R in 15..12), including NaN, negative, infinite, denormal and rounded values, and the pixels outside the scissor. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A CB resolve from a packed color target throws, so a 2-sample 4_4_4_4 target cannot be resolved by the color block. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A CB_TARGET_MASK that writes fewer than four components of a 4_4_4_4 target leaves the other nibbles of each packed word as they were, which an R16_UINT attachment cannot do. Such draws now close the attachment's color write mask, and the pixel shader merges the written nibbles into the target through a storage image: inside a pixel interlock it reads the word, keeps the bits the mask leaves alone and stores it back. The merge (export index, word mask, sample count) is part of the static shader key, request version 40 and shader disk cache format 42; the storage image is appended to the shader's images with a single runtime mode, and its T# is encoded from the target's surface at draw time, so the draw resolves it to the target's resident image, which Draw requires. Merging shaders declare early fragment tests; shaders that kill under late tests, export depth, stencil or coverage, use alpha-to-coverage or a quad sample mask are refused, as are multisampled, mipmapped, volume and array targets and more than one merged target per draw. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…echeck Merged and full writes into one target are now also drawn without a wait between them and checked once at the end, so the render pass that follows a merging draw has to end on the storage write to its attachment. The unit test also passes the partial target masks through DrawRejection. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… depth or stencil A packed 4_4_4_4 partial write merges inside the pixel shader after early depth and stencil tests. Moving the tests ahead of a pixel kill only changes the result when the draw writes depth or stencil, so the target merge now carries whether the draw does (depth write, or stencil with a write mask and a non-keep operation), and the late-test kill refusal applies only then. The flag is part of the static shader key, the serialized request (now version 40) and the pixel input info. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e shader The merge used to write the target at the color export, so a pixel killed later by a valid-mask export still changed its word. The merged word is now kept until the shader returns and written inside the pixel interlock only when the pixel's valid mask is still set. The execution test kills a checkerboard both before and after the merging export. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A merging draw read and wrote its packed target as a storage image while the same image stayed a color attachment of the pass, a feedback loop that NVIDIA only tolerates. The merged slot is now VK_ATTACHMENT_UNUSED in the subpass and VK_FORMAT_UNDEFINED in the rendering formats, its view is left out of the render pass and VkFramebuffer, and dynamic rendering binds no view there. The target stays in the draw's tracked and dirty images. Pipelines and continued passes are keyed on the merged slot. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…aw and depth The partial write test now names the word mask of every CB_TARGET_MASK case instead of taking it from PackedWriteMask, draws 16 overlapping layers in one draw whose merged nibble must come from the last layer, and merges behind and in front of a depth target filled by a checkerboard-killing draw: a depth-rejected fragment leaves its word. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ites Occlusion counts are taken at the forced early tests, the refused merge draws and why a late depth write was not used, and the one GPU the merge was measured on. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… image OpBeginInvocationInterlockEXT and OpEndInvocationInterlockEXT order the critical sections of overlapping fragment invocations but perform no availability or visibility operations (Vulkan memory model, fragment shader interlock), so without Coherent a merge could read a word that an earlier overlapping invocation stored but did not make visible. The image variable the merged target is bound through is now decorated Coherent. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A one-sample target under a multisampled rasterizer whose OVERRASTERIZATION_AMOUNT matches (radeonsi line and polygon smoothing) decodes with mergedCoverage and is drawn through mixed-samples coverage reduction onto its one-sample attachments. A merging draw leaves the 4_4_4_4 target out of the attachments, a combination that is not tested, so a partial write of such a draw now throws; the limit is listed in the technical debt file. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… under late tests A partial 4_4_4_4 write merges inside a pixel shader that declares early fragment tests. When the guest runs the shader under late tests, pixels that the depth or stencil test rejects still execute it on the console, so their buffer and image stores, atomics and GDS operations happen there. Under forced early tests they would be dropped. Such a merge now throws, unless the guest asked for early tests itself. The merged target's own image write does not count. A recompile test checks two things for a merging shader that adds to a buffer counter: under late tests it throws, and under the guest's early tests it compiles with its one atomic add beside the merge. The same shader without a merge still compiles under late tests. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… samples A merged 4_4_4_4 write runs its pixel shader after early depth and stencil tests. Vulkan counts a sample for an occlusion query at those tests, so pixels the guest shader kills under late tests would be counted, and on the console they are not. Once the game has read an occlusion counter the recorder counts samples for every later draw, so from then on such a merge throws. The check runs where the recorded and recipe draw paths prepare their inputs. The state records DB_SHADER_CONTROL.KILL_ENABLE for it, which fits in the existing padding. The execution test starts sample counting and then checks two cases: a merge that kills a checkerboard under late tests is refused, and a merge that kills nothing still draws. A decode test reads KILL_ENABLE. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…eject pixels The refusal of a merging shader that writes guest memory under late tests fired in two cases where the early tests change nothing. The first was a draw with no depth, stencil or depth-bounds test. Early tests reject no pixel there, so every write the console makes still happens. The target merge now carries whether the draw has such a test, and the refusal applies only then. This adds one byte to the version 40 merge fields of the recompile request. The second was any GDS binding, including a shader that only reads GDS. The refusal now looks for GDS writes, atomics, appends and consumes. Tests: the depth and stencil decode checks the new flag, and the static shader key depends on it. A counter-adding shader under late tests compiles without a test and throws with one. A late-test merge that reads GDS compiles, and one that adds to GDS throws. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A partial write of a multisampled 4_4_4_4 target now merges per sample: the draw shades per sample, and the pixel shader reads and writes its own sample (gl_SampleID) of the target through a multisampled storage image whose T# describes the target's surface. Draw makes the multisampled target storable before it takes the attachment view, so the image swap of the first storage access cannot leave the framebuffer on the retired image, and it accepts the target among the draw's multisampled sources only as that one merge binding. An execution test draws a full write into a 2-sample target, merges an X write of a triangle that cuts through pixels, and loads every sample: covered samples hold the merged word, uncovered ones the first draw's. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A multisampled target's storage writes are ordered for later draws only through MultisampleTarget::Storage(), which a reused resource build would skip. The merging draw now takes the storage view once more after its resources are resolved, so it is the last access before recording; multisampled draws make no draw recipe. The multisampled test draws a full write right after the merge and checks that it covers the merged samples it overlaps. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The 2-sample test decodes its draw states from registers and checks the word mask, the closed write mask, per-sample shading and the exported merge, and a skip names the missing device feature. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Per-sample shading of a merging draw makes centroid and sample-located inputs and the ancillary sample number follow the invocation's sample (centre-located inputs too on devices without interpolation functions and fragment shader barycentrics), and the 2-sample merge was measured on one GPU only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A partial write of a multisampled 4_4_4_4 target forces per-sample shading after DecodeState checked SAMPLE_COVERAGE against the guest's own iteration, so a pixel-rate shader reading SAMPLE_COVERAGE got only its invocation's sample in gl_SampleMaskIn instead of the whole pixel's coverage. The forced shading now runs the same check and throws its message; a full write of the same target still decodes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…rates A partial write of a multisampled 4_4_4_4 target makes the draw shade per sample. For a shader the guest runs per pixel, every invocation of a pixel must then see the same inputs. Otherwise its colours and kill decisions follow each sample, while the console runs one invocation. The decode now throws in two cases when the guest did not ask for per-sample shading. The first is a shader that reads the ancillary word, whose sample number would become the invocation's. The second is a shader that reads centroid, sample-located or pull-model barycentrics, Z, 1/W, or a position at the centroid or a sample. That case is allowed when every sample sits at the pixel centre, as in a draw raised from a one-sample rasterizer. The recompiler throws for centre-located barycentrics on a device without interpolation functions and fragment shader barycentrics, where they would not be rebuilt at the centre. Decode tests run each moving input on a 2-sample rasterizer, where it throws, and with PS_ITER_SAMPLE, where it decodes. The raised draw keeps the located inputs and refuses the ancillary word. Centre and upper-left positions, centre barycentrics, the front face and the fixed-point position decode, and a recompile test covers the missing barycentrics. The debt entry now lists what is kept and what throws. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A merge into a multisampled 4_4_4_4 target runs the pixel shader once per sample. A shader that stores to guest memory, runs atomics or uses GDS would then repeat those writes for every covered sample. An 8-sample pixel would add to a counter eight times. Such a merge now throws. The recompile request carries the merge's sample count, not whether the guest asked for per-sample shading, so this also refuses shaders the guest iterates itself, where the console repeats the writes too. A recompile test checks that a 2-sample merging shader adding to a buffer counter throws, and that the same shader without the merge compiles. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The 2-sample test now draws two merges back to back with no read-back in between: an X merge over one triangle and a W merge over a crossing one. It then loads every sample. Samples inside both triangles must hold both nibbles, so the second merge has to read the word the first one wrote. Each merging draw takes the target's storage view before it records. That view ends any open render pass and records a shader-write barrier, so two merges never share a pass. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…it at the centre The recompiler refused centre-located barycentrics in a multisampled merge on a device without interpolation functions and fragment shader barycentrics. It did so even when every sample sits at the pixel centre, as in a draw raised from a one-sample rasterizer, where the barycentrics are the centre's anyway. The refusal now applies only when the merge's sample locations are off the centre. The pixel request carries those locations for such a merge. The recompile test runs on two states. On a 2-sample rasterizer, the missing barycentrics throw, and the shader compiles once they are present. On a raised draw, the shader compiles without them. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The multisampled merge accepts 2, 4 and 8 samples, but the execution test drew only 2-sample targets. It now runs the same draws on 2, 4 and 8 samples, each count with its own target: a cutting merge under a full write, then an X and a W merge drawn back to back. Every sample is read back through a T# of the target's sample count and checked against the standard sample locations. A partly merged pixel is now any pixel with some, but not all, of its samples merged. A count whose R16_UINT colour attachment with storage use the device does not offer is skipped and printed. The test is skipped when the device offers none of the three. The debt entry on measured hardware now names the 4- and 8-sample merges. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A blended or partial write into a one-sample 11_11_10 float target goes through the pixel-interlock merge: the pixel shader unpacks the stored word, evaluates the CB_BLEND_CONTROL color equation in fp32 and packs the result toward zero before the write mask. Constant color and dual-source factors still throw, and so does a mipmapped, volume, array or multisampled target, now with the target's shape in the message.
A one-sample 4_4_4_4 partial write or 11_11_10 blend into a mip of a mipmapped target, or into a slice range of a 2D array, now goes through the pixel-interlock merge instead of throwing. The merge descriptor names the same whole-array surface as the resident color image at the drawn mip, and the shader addresses the slice as the view's first slice plus gl_Layer, clamped to the view's last slice as the console clamps the render target index to SLICE_MAX. Volume targets and multisampled mips or arrays still throw.
…ed-float-blend
…ed-float-blend # Conflicts: # core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp # core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp # core/shader/recompiler/CacheKey.hpp # core/shader/recompiler/ControlFlow/src/RequestSerializer.cpp # core/shader/recompiler/Optimization/include/Optimization/ShaderStageInputInfo.hpp # core/shader/recompiler/Recompiler.hpp # core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp # core/shader/recompiler/tests/RequestSerializerTests.cpp # docs/dev/debt/libSceAgcDriver.md
… writes depth as a depth pass and an EQUAL merge pass
|
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.
|
12 tasks done
12 tasks done
Open
12 tasks done
12 tasks done
12 tasks done
12 tasks done
This was referenced Oct 22, 2026
12 tasks done
12 tasks done
12 tasks done
This was referenced Oct 22, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A partial or blended write into a packed color target merges inside the pixel shader through #8564's pixel-interlock read-modify-write, which forces early fragment tests. That throws when the shader kills pixels under late tests and the draw writes depth (
kills under late tests, draw writes depth or stencil): with early tests the killed fragments would still write depth, so alpha-tested holes would occlude what is drawn behind them. Gran Turismo 7 stops on this in the World Map with an alpha-tested draw into an 11_11_10 target (#9020's blend), reverse-Z:DB_DEPTH_CONTROLZFUNC GEQUAL with depth writes, no stencil, one target.This branch is #9020 as merged with current
main(e554d27), plus one commit, 5451af8, and the merge of #9020's branch into it (7df24ce). The change of this PR alone: frankea/AnyPS5@374556c...5451af8CB_TARGET_MASK, so it has no merge: the title's own late tests, kill and depth write run as they are, and depth ends up as on the console. The second pass keeps only those 4 bits and setsDB_DEPTH_CONTROLZ_WRITE_ENABLEto 0 andZFUNCto EQUAL (2). It writes no depth or stencil, which is the shape feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564 already merges with a kill under late tests, so it blends exactly the fragments the kill keeps whose depth equals what the first pass left. Field positions and theCompareFragencoding are from Mesa's gfx103.json (DB_DEPTH_CONTROLbits 2 and 6:4,CB_TARGET_MASK4 bits per target,DB_SHADER_CONTROLKILL_ENABLEbit 6 andDEPTH_BEFORE_SHADERbit 12).Driver::drawdecides after decoding the state and before capturing any shader (LateTestMergeSplitTargets), returns a newDrawVerdict::Split, andPacketExecutionthen runs both passes through the unchangedDriver::draw, so each goes through the usual decode, capture, pipeline and draw caches under its own register key. EQUAL needs bit-identical depth in both passes: they run the same vertex program with the same viewport and depth bias, and positions are already decoratedInvariant.ZFUNCNEVER,DEPTH_BEFORE_SHADER, alpha-to-coverage, multisampled targets, and draws while an occlusion query counts samples (the second pass would count them again). A shader that writes guest memory throws in the second pass as before, since feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564 already refuses memory writes under forced early tests in a draw with a depth test.Tested
tests/Graphics.cpp: the split decision over the nine existing 4_4_4_4 depth, stencil and depth-bounds cases withKILL_ENABLEset: only the one that tests and writes depth splits, and none splits without a kill; no split for a stencil write, ZFUNC NEVER orDEPTH_BEFORE_SHADER; a merged target in slot 1 splits onCB_TARGET_MASKbits 4..7.tests/execution/LateTestMergeSplit.cpp(new), through DCB submission: a 64x16 tiled 11_11_10 target over a Z32 depth target. A background depth draw, an occluder over the left half, then the split draw (a checkerboard kill, premultiplied-alpha blend), then a probe with the same compare and no depth write. Checked bit for bit on every pixel against a CPU reference: behind the occluder unchanged, kept pixels blended once, killed pixels covered by the probe, so they wrote no depth. LESS, GREATER and GEQUAL, two rounds each (the second through the caches). With the split disabled the test stops on the refusal above.agc_driver_graphics,agc_driver_late_test_merge_split,agc_driver_packed_float_blend,agc_driver_packed_unorm4444_partial_write,agc_driver_packed_unorm4444_multisample_partial_write,agc_driver_sample_mask_draw,agc_driver_stencil_known_replaceandagc_driver_layered_array_color_drawpass.mainmerge, fix(shader): link table callees that together exceed s_call_b64 range #9068's two fixes, fix(agc): raise no end-of-pipe event from flips and carry the release's interrupt context #7978, fix(agc): write a replace blend into a packed color target without blending #7243, feat(agc): render layered draws into a depth view of several slices #7256, fix(shader): answer image_get_resinfo on any T# dimension #7550, and three local changes: one draws a partially resident depth target whose pages are all committed, one skips the compute dispatches refused in Recompiler: a T# reached through two loads after a dynamically indexed read (pointer → pointer → T#, pointer → V# → buffer → T#) stops Ratchet & Clank (PPSA01474) and Guardians of the Galaxy (PPSA01748) #6191, one draws dithered alpha-to-mask as undithered). The World Map's alpha-tested draw into the 11_11_10 target with pixel shader 0x126bcf0300 (GEQUAL with depth writes, no stencil) used to stop the load on the refusal above. It now draws through the split, and the load goes on tocolor targets with fewer fragments than samples (EQAA) are unsupported(4 samples, 2 fragments, 512x512), which this PR doesn't touch.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)