Repository navigation
feat(agc): blend into 11_11_10 float color targets in the pixel shader - #9020
Conversation
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
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
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.
|
…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
CnRJay
left a comment
There was a problem hiding this comment.
Replacing a thrown refusal with a working blend is worth having (GT7 gets past the 11_11_10 stop to its main menu), and building on #8564's interlock merge is reasonable. The cost of that mechanism is not in either PR.
- A blended draw into this target now runs its pixel shader in a fragment-shader-interlock critical section with early fragment tests forced, and for multisampled targets with per-sample shading. Interlock serializes overlapping fragments of the same pixel, and forcing early fragment tests changes when depth is tested relative to the shader's discards; on a blend-heavy pass (particles, UI, fog) that can be much slower than fixed-function blending. The description's only speed figure is "main menu at 4.4 FPS" in GT7 on an RTX 4090 with an earlier state of the commits. Please post the summary lines of the same scene with and without the blend path (the unsupported draw can be skipped with a switch for the comparison), and
Graphics.Drawper frame, so the cost is known before titles depend on it. - State which GPUs support
fragmentShaderPixelInterlockand what happens on those that do not (the previous behavior was a clear throw; the replacement should be too, with a message that names the missing feature), including lavapipe, which the tests run on. - The 2,961 lines include #8563 and #8564 "unchanged". Rebase onto them once they merge so this PR's two commits are what is reviewed; as it stands this PR is marked conflicting and cannot be tested as a unit.
…ed-float-blend
…ost blending (--benchmark-overdraw)
|
@CnRJay thanks, all three points are fair. In order: Cost in GT7. Here is the same scene with and without the blend path, from the same build: Gran Turismo 7 (PPSA01317 01.710.000) idling on its main menu with no input, RTX 4090 (NVIDIA 616.92), Windows 11, Ryzen 7 5800X, 3840x2160 output, warm shader cache. The build is this PR before its
About 12 draws per frame go through the merge here, out of about 1,880, and all of them are this PR's path: blended, layered draws into an 11_11_10 array target, from one pixel shader (0x120af97800). GPU busy per present is the same either way, so in this scene the interlocked merge costs less than the run-to-run spread. But the merged draws are few, and the frame is CPU-bound (queue worker 0 is about 95% busy, For that bound I added a timed overdraw case to the GPU test, aab4fb4:
So when every fragment overlaps 63 others in the same draw, the merge blends at about half the host's rate on this GPU: 2.07x the time per layer, the same in all three runs. At GT7's 12 merged draws per frame that disappears in the noise, as above. A full-screen particle or fog pass with heavy overdraw into an 11_11_10 target would take about twice as long as fixed-function blending. I only measured the RTX 4090. One summary line of each configurationOn the mechanism:
Devices without
So on an AMD Windows driver from before early 2025, GT7 now stops on the interlock message instead of the old 11_11_10 blend message. #8563 and #8564. Agreed. The branch carries them unchanged so that it builds and tests as a whole. This PR's own change is the two commits in the compare link in the description. I merged current AI-assisted: yes (Claude Code) |
|
Ghost of Yōtei (PPSA26344) on plain Your blend path already passes the packing through Would you rather fold 10_11_11 unorm into #9020, or should I open a small follow-up with |
|
@Celegans12 thanks for the Yōtei report. Please open it as a follow-up with Your list matches what I see in #9020's head (aab4fb4):
Two limits of #9020 to keep in mind for Yōtei. First, its blend refuses a multisampled 11_11_10 float target, so a multisampled 10_11_11 unorm target would need that case too. The merge as a whole also refuses volume targets and mipmapped or array multisampled ones. Second, the merge needs Post the Yōtei result here or on your PR, whichever you prefer. I can run your branch on an RTX 4090 if that helps. AI-assisted: yes (Claude Code) |
✅ NVIDIA GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (2, not counted against this PR)Validation IDs already present on main (4 IDs in 425 tests)Skipped (42)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
|
Thanks for the merge-main. Two things before this can go in, reviewed at
|
Adopt the complete boykopovar#9020 net change on the current main API. Preserve its per-target merges, normalized blend path and array views. Keep main replacement writes, volume refusal and compressed journals. Use request 52, disk 51 and journal 14 for the combined layouts. This private prerequisite is source adoption, not a new implementation. Based on frankea/AnyPS5@aa1d452 core/libs/prx/libSceAgcDriver and core/shader (author: frankea). Original history, authors, dates and messages are retained in the ledger. AI-assisted: yes
Adopt the complete boykopovar#9020 net change on the current main API. Preserve its per-target merges, normalized blend path and array views. Keep main replacement writes, volume refusal and compressed journals. Use request 52, disk 51 and journal 14 for the combined layouts. This private prerequisite is source adoption, not a new implementation. Based on frankea/AnyPS5@aa1d452 core/libs/prx/libSceAgcDriver and core/shader (author: frankea). Original history, authors, dates and messages are retained in the ledger. AI-assisted: yes
Adopt the complete boykopovar#9020 net change on the current main API. Preserve its per-target merges, normalized blend path and array views. Keep main replacement writes, volume refusal and compressed journals. Use request 52, disk 51 and journal 14 for the combined layouts. This private prerequisite is source adoption, not a new implementation. Based on frankea/AnyPS5@aa1d452 core/libs/prx/libSceAgcDriver and core/shader (author: frankea). Original history, authors, dates and messages are retained in the ledger. AI-assisted: yes
# Conflicts: # core/libs/prx/libSceAgcDriver/tests/ShaderMemory.cpp # core/shader/recompiler/tests/RequestSerializerTests.cpp
|
@oneandonlydean thanks, both done in 4ba1676, which merges
Tested on an M5 Max with MoltenVK, each 3 times: the 7 focused entries pass and the multisampled 4_4_4_4 test skips there (details in the description). I'll repeat the RTX 4090 runs on this head. |
❌ NVIDIA GPU test: build failedPR head
build: first errorsReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
ColorTarget's partialWriteMask is now 32 bits and mergeBlend was added, so State grows from 944 to 952 bytes; the recipe codec already writes, reads and canonicalizes both fields. Co-authored-by: frankea <frankea@users.noreply.github.com>
Both points are met on the current head a5730d0: it now merges cleanly with main (ad8e79f), and it moved to free version numbers (request serializer 55 over main's 54, ShaderDiskCache FormatVersion 57 over main's 56), with the PipelineJournal State size assert updated to 952. Windows build run 42311605065 passed on this head.
The recipe codec now stores partialWriteMask as 32 bits and adds mergeBlend, so a format 16 journal written before this change would be read with the wrong field layout. Bump the journal format to 17 so those files are discarded instead. Co-authored-by: frankea <frankea@users.noreply.github.com>
|
Hi @XairRrR, a correction to my dismissal of your review: your pipeline journal point was still open. The size guard was updated ( |
What
A blended or partial write into a one-sample 11_11_10 float color target used to throw (
AGC graphics: blending into an 11_11_10 float color target is unsupported). No host format holds this target's bits (red in 31..21, blue in 9..0, the mirror ofB10G11R11), so it renders as R32_UINT and fixed-function blending can't apply. This PR blends in the pixel shader instead, through the pixel-interlock merge that JAB-dev's #8564 adds for partial 4_4_4_4 writes. The merge mechanism is theirs; this PR adds blending to it and opens it to mips and 2D array slices.#8564 and #8563 are in
mainsince dcd7e3a, and this branch mergesmain3bd88ca. The changes of this PR are its two commits, 9a7a202 (the blend) and 6f240cb (mips and array slices), plus the resolution of the merge commits, which number this PR's serializer fields after #8564's: frankea/AnyPS5@f5edd53...6f240cbDecodeStateturns a blended or partial write into a one-sample 11_11_10 float target into a merge.TargetMergecarries the target's packing and the color equation ofCB_BLEND_CONTROL, and its mask is 32 bits wide;ColorTarget.partialWriteMaskis 32 bits as well, next to a newmergeBlend. With feat(agc): merge a draw into more than one unclamped normalized color target #12916 inmaina draw carries oneTargetMergeper merged target, and these fields sit on each entry; a packed merge still merges alone, so an 11_11_10 blend or partial write next to another merged target throws, as a 4_4_4_4 partial write does.TargetMergeSurfacedescribes the resident image of the whole array (ArraySurfaceForTarget, moved fromDraw.cpptoState.cppand shared) at the drawn mip, so the merge's storage image is the same image the target renders into.EncodeTextureResourceencodes a mip range and 2D arrays, and the shader addresses the view's first slice plusgl_Layer, clamped to the view's last slice as the console clamps the render target index toSLICE_MAX, so a guest layer index past the view never reaches outside the image. This applies to one-sample 4_4_4_4 merges too.fragmentShaderPixelInterlock. The refusal now names the target's shape.mainreads feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564's merge record from 46 (target, mask, samples, descriptor and the two depth and stencil flags), feat(agc): blend into an unclamped normalized color target in the pixel shader #11684's target blend from 46 as well (bit 16 of the mask word, then its fields after the two flags), feat(agc): merge a draw into more than one unclamped normalized color target #12916's further merges (their count in bits 17-19 of the first record's mask word, then one record each), its depth export bounds from 49, and target flags at the end of the stream from 51 to 54; it rejects 50. At 55 this PR writes each record's mask as a full 32-bit word and, after the two flags, a flag byte that holds what the 46-54 word kept above its mask (bit 0 the blend, bits 1-3 the number of further merges, on the first record), then feat(agc): blend into an unclamped normalized color target in the pixel shader #11684's blend fields when it is set, then this PR's packing, blend control, layered flag and the view's first and last slice; the depth export bounds follow the last record. A version 46 to 54 record (asmainwrites it) takes the flags from the upper half of its mask word, keeps the 16-bit mask limit, and without a blend reads a merge as a one-slice 4_4_4_4 merge without blending.request_serializerandagc_shader_memoryread version 51, 53 and 54 streams asmainwrites them. Shader disk cache 57 (mainis at 56; fix(agc): retire a multisampled color target a write-only storage image overwrites #10049 takes 58; this branch's cache key adds the merge's packing, blend and slice fields). Pipeline journal 16 withsizeof(State)944 (mainis at 15 and 936; feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564's 16-bitpartialWriteMaskbecomes 32 bits andmergeBlendfollows it).mainon 10/29): its blend into an unclamped normalized target uses the same merge with a float storage image and a stored texel, and this PR's packed merges keep the uint image and the stored word;MergeColorTargettakes feat(agc): blend into an unclamped normalized color target in the pixel shader #11684's path first when its blend is set.ShaderInputInfoBuilderaccepts a merge without packing only when it carries feat(agc): blend into an unclamped normalized color target in the pixel shader #11684's blend.TargetMergeSurfacenow gives every merge, feat(agc): blend into an unclamped normalized color target in the pixel shader #11684's included, the drawn mip and, for a 2D array view, the array's image with the view's slices; the 11_11_10 debt entry says so. feat(agc): blend into an unclamped normalized color target in the pixel shader #11684'sUnclampedBlendMask(0xffff) stays unambiguous with the 32-bit mask: a 4_4_4_4 merge with all four nibbles is a full write and never merges, and no 11_11_10 field mask gives 0xffff.main's ONE/ZERO/ADD rule (such a color equation into a packed target is a plain write) the merge only takes other equations, and withmain's staged multisampled 4_4_4_4 partial writes (StagedMerge.cpp) a multisampled 4_4_4_4 partial write still takes that path; 11_11_10 merges stay one-sample.Tested
main3cb1cd2 and 3bd88ca (head 4ba1676), Apple M5 Max (macOS 27.0.1, x86-64 under Rosetta 2, MoltenVK 1.4.2 from Vulkan SDK 1.4.363.0), each test run three times:request_serializer,agc_shader_memory,agc_driver_graphics,agc_driver_packed_float_blend,agc_driver_packed_float_color_target,agc_driver_packed_unorm4444_color_targetandagc_driver_packed_unorm4444_partial_writepass 7/7, andagc_driver_packed_unorm4444_multisample_partial_writeskips (no multisampled storage images on MoltenVK).mainno longer buildslibkernelon macOS (pthread_getattr_np,pthread_getcpuclockid, the Linux signal context,sched_getcpu,RUSAGE_THREAD,environ), so these builds carry a local, uncommitted macOS patch for those calls. The RTX 4090 runs below predate this merge; I will repeat them on it.mainee9a9a6 (head aa1d452; feat(agc): merge a draw into more than one unclamped normalized color target #12916's multi-target merges, see the comments), RTX 4090 (NVIDIA 616.92, Windows 11), each test run three times: the 75 ctest entries of the same 14 targets pass 75/75, and 75/75 under the validation layer (VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation,VK_LOADER_DEBUG=layer,ANYPS5_NO_SHADER_CACHE=1,ANYPS5_REQUIRE_VULKAN=1,ctest -V): 115Insert instance layerlines, noVUID-line, 39Undefined-Value-ShaderOutputNotConsumed-DynamicRenderingwarnings (in the testsmainalso runs, the countsmainshows; 10 inagc_driver_packed_float_blend).mainee9a9a6 passes a 236-entry set holding all of these butagc_driver_packed_float_blendin both runs, with noVUID-line. The shader memory test now also checks one and two merges at serializer versions 49 and 50. Not run on Linux.mainbb04a4f (head 237ede9; see the comments for the renumbering and feat(agc): blend into an unclamped normalized color target in the pixel shader #11684), RTX 4090, each test run three times: the 72 ctest entries of 14 targets (request_serializer,agc_shader_memory,agc_shader_disk_cache,agc_driver_pipeline_journal,agc_driver_graphics,agc_driver_key_miss,agc_driver_unclamped_normalized_blend,agc_driver_depth_export_clamp,agc_driver_layered_array_color_draw,agc_driver_packed_float_blend,agc_driver_packed_float_color_target,agc_driver_packed_unorm4444_color_target,agc_driver_packed_unorm4444_partial_write,agc_driver_packed_unorm4444_multisample_partial_write) pass 72/72, and 72/72 under the validation layer (VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation,VK_LOADER_DEBUG=layer,ANYPS5_NO_SHADER_CACHE=1,ANYPS5_REQUIRE_VULKAN=1,ctest -V): 115Insert instance layerlines, noVUID-line, nowas not found but was requested by env var VK_INSTANCE_LAYERS, 39Undefined-Value-ShaderOutputNotConsumed-DynamicRenderingwarnings.mainbb04a4f passes its 71 entries (noagc_driver_packed_float_blend) in both runs: 110Insert instance layerlines, noVUID-line, 29 of those warnings.sizeof(State): a macOS probe gives 920 formain(its Linux guard) and 928 for this head (the new guard). Not run on Linux.tests/execution/PackedFloatBlend.cpp(new) against a CPU reference, bit for bit on every pixel: 11 equations, a blended and a plain partial write, a back-to-back chain, two same-draw overdraws of 16 alternating layers, and three array merges (mip 0 of all six slices of a 128x128 array of 8 mips, one layer per slice, the shape Gran Turismo 7 renders; mip 1 of slices 1 to 3 of a 256x256 array; one slice drawn without a layer export).agc_driver_packed_float_blend,agc_driver_packed_unorm4444_partial_write,agc_driver_packed_unorm4444_multisample_partial_write,agc_driver_packed_float_color_target,agc_driver_layered_array_color_draw,agc_driver_graphicsandagc_shader_memorypass.request_serializer,agc_shader_disk_cache,agc_shader_memory,agc_shader_control_flow,agc_driver_bda_device,agc_shader_replay_requests,agc_driver_graphics,agc_pipeline_journalandagc_driver_prepared_shaderspass.mainmerges, head e554d27 on the RTX 4090:agc_shader_memory,agc_driver_graphics,agc_driver_layered_array_color_draw,agc_driver_packed_float_blend,agc_driver_packed_float_color_target,agc_driver_packed_unorm4444_partial_writeandagc_driver_packed_unorm4444_multisample_partial_writepass. The same 35 Linux tests pass on 374556c, themainmerge before it.main7e69be6 (head 4fad5fd) on the RTX 4090: the 49 ctest entries ofagc_driver_graphics_tests,agc_shader_memory_tests,agc_driver_pipeline_journal_tests,agc_driver_packed_float_blend_tests,agc_driver_packed_float_color_target_tests,agc_driver_packed_unorm4444_partial_write_tests,agc_driver_packed_unorm4444_multisample_partial_write_testsandagc_driver_layered_array_color_draw_testspass 49/49, each test run three times (ctest --repeat until-fail:3).VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation(Vulkan SDK 1.4.363.0),ANYPS5_NO_SHADER_CACHE=1andANYPS5_REQUIRE_VULKAN=1withctest -V, onmain83462cc (7e69be6 plus eight commits totools/only) (46/46:mainlacks the float blend and both 4_4_4_4 partial-write tests) and on this head (49/49). Neither log haswas not found but was requested by env var VK_INSTANCE_LAYERS, and neither has aVUID-line. The layer's only messages on this head areUndefined-Value-ShaderOutputNotConsumed-DynamicRenderingwarnings from the merge tests, where the merged target has no color attachment and the fragment output goes unused: 10 inagc_driver_packed_float_blend, 10 inagc_driver_packed_unorm4444_partial_writeand 9 inagc_driver_packed_unorm4444_multisample_partial_write.mainlogs no validation message in its 46.main2259f05 (head 634b3dc) on the RTX 4090: the 56 ctest entries ofagc_driver_graphics_tests,agc_shader_memory_tests,agc_driver_pipeline_journal_tests,agc_driver_packed_float_blend_tests,agc_driver_packed_float_color_target_tests,agc_driver_packed_unorm4444_partial_write_tests,agc_driver_packed_unorm4444_multisample_partial_write_tests,agc_driver_packed_unorm4444_color_target_tests,agc_driver_layered_array_color_draw_tests,request_serializer_tests,agc_shader_disk_cache_testsandagc_driver_key_miss_testspass 56/56, each test run three times. Under the validation layer, with the settings above: this head 56/56 andmain9d6adeb 54/54 (mainlacks the float blend test and feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564's three); noVUID-line in either log, and this head's only messages are the same 29Undefined-Value-ShaderOutputNotConsumed-DynamicRenderingwarnings.main0db47b2 (head a51e479; the only conflict was the disk cache format), RTX 4090: the same 56 ctest entries pass 56/56, each test run three times, and 56/56 under the validation layer with noVUID-line (the same 29 warnings);main0db47b2 54/54 and 54/54 under the layer, noVUID-line.main) andmain513ca7e (head 6806092; no textual conflicts, and the disk cache format moves to 47), RTX 4090: the same 56 ctest entries pass 56/56, each test run three times, and 56/56 under the validation layer with noVUID-line (the same 29 warnings). The head also merges cleanly withmain58ceeb8.main379b1dc (head 5e04550), which has feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564's final head: this PR's serializer fields move to version 47 and the disk cache stays at 47 overmain's 46; the journal is 10 andsizeof(State)832 overmain's 9 and 824.request_serializerandagc_shader_memorynow also check that a version 47 record carries the packing, blend control and slice bytes, that a version 46 record with a merge reads as 4_4_4_4 without blending, and that a version 46 record whose mask is wider than 16 bits is refused. RTX 4090: the 59 ctest entries of the same targets (mainadded three since the last run) pass 59/59, each test run three times, and 59/59 under the validation layer with noVUID-line (the same 29 warnings). Built with Apple clang on macOS,main'sRequestSerializerTests.cppaborts against this head's serializer (it expects version 46), and this head's passes. The head also merges cleanly withmain9c85790.main732ab06 (a657def): the only conflict was the journal'ssizeof(State)guard, now 848.mainhad meanwhile moved the pipeline journal to format 10 and the shader disk cache to 47 for its own changes, the numbers this PR used, so the merge kept both lines without a conflict. 5f72874 moves them to 11 and 49. The request serializer stays at 47;main's is still 46. RTX 4090, head 5f72874: the 65 ctest entries of the same targets (mainadded six since the last run) pass 65/65, each test run three times, and 65/65 under the validation layer with noVUID-line (the same 29 warnings).main3af09d9, on the targets it has plusagc_driver_sample_mask_draw_testsandagc_driver_stencil_known_replace_tests: 66/66, three times each, and 66/66 under the layer with noVUID-line (19Undefined-Value-ShaderOutputNotConsumed-DynamicRenderingwarnings, from its own two 4_4_4_4 partial-write tests). The head also merges cleanly withmain504ea3b, whose newer commits change no AGC code.VUID-errors. The layer's one message there isUndefined-Value-ShaderOutputNotConsumed-DynamicRendering, 10 per merge test, because the merged target has no color attachment and the fragment output goes unused.sizeof(State)is 848 on Linux x86-64 after the latest merge:main's 840 plus a 32-bitpartialWriteMaskandmergeBlend(feat(agc): merge partial writes into 4_4_4_4 UNORM color targets #8564's 16-bit mask keepsmain's size). A probe built on macOS gives the Linux guards' values formain732ab06 (840) and this PR's previous head 5e04550 (832), and 848 for this head. Not built on Linux since the merge.mainmerge and the layer clamp) and 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 and fix(shader): answer image_get_resinfo on any T# dimension #7550 merged locally: past the 11_11_10 blend stop and the cube-map target, the main menu at 4.4 FPS. With scripted input it goes on to the World Map, the Used Cars dealer and a car purchase, then stops in the car delivery scene oncolor targets with fewer fragments than samples (EQAA) are unsupported, which this PR doesn't touch.Checklist
main; no other open PR implements the same functions (the ONE/ZERO/ADD case is left to fix(agc): write a replace blend into a packed color target without blending #7243)NotImplemented_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)main). fix(agc): retire a multisampled color target a write-only storage image overwrites #10049 (mine) takes shader disk cache format 58 and this PR 57, so if both land, the second one's version line conflicts instead of merging silently. feat(agc): draw alpha-tested packed-target merges that write depth as a depth pass and an EQUAL merge pass #9335 (mine) is stacked on this PR.