Skip to content

feat(agc): draw radeonsi line and polygon smoothing (SAMPLE_COVERAGE, merged mixed samples) - #6530

Merged
adriadam10 merged 4 commits into
boykopovar:mainfrom
milescoviello:feat/smoothing-mixed-samples
Oct 19, 2026
Merged

adriadam10 merged 4 commits into
boykopovar:mainfrom
milescoviello:feat/smoothing-mixed-samples

Conversation

@milescoviello

@milescoviello milescoviello commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

OliSe Player (PPSA01153, catalog 02.001.000) draws through a radeonsi-based GL driver (ps5-gallium in its log). Its MilkDrop visualizer picks a random preset. With "Rovastar - Dark Ritual (Star Of Destiny Mix)" it smooths lines and polygons the way radeonsi does, and main stops 20 s in:

terminate called after throwing an instance of 'std::runtime_error'
  what():  AGC graphics: unsupported SPI_PS_INPUT_ENA/ADDR bit combination (ena 0x4002 addr 0x4002)

The draw (from a throwaway print):

Register Value
PA_SC_AA_CONFIG 0x0020c002: 4 samples, 4 exposed
Color target one, 1 sample, VK_FORMAT_R8G8B8A8_UNORM; no depth target
PA_SC_MODE_CNTL_0 / _1 0x23 (MSAA_ENABLE) / 0x0
DB_EQAA 0x02130000: OVERRASTERIZATION_AMOUNT 2
SPI_PS_INPUT_ENA / ADDR 0x4002: PERSP_CENTER, SAMPLE_COVERAGE

This is radeonsi's smoothing setup. With a one-sample framebuffer, si_emit_msaa_config rasterizes SI_NUM_SMOOTH_AA_SAMPLES = 4 coverage samples and sets OVERRASTERIZATION_AMOUNT(log2 4), so the pixel is written when any of them is covered. si_shader.c enables SAMPLE_COVERAGE_ENA for nir_lower_poly_line_smooth, which scales alpha by the number of covered samples.

Two commits:

  1. feat(shader): SAMPLE_COVERAGE. SPI_PS_INPUT_ENA/ADDR bit 14 gives the pixel shader one VGPR after ANCILLARY. The decoder refused it, and the recompiler never loaded that VGPR. It is now loaded from gl_SampleMaskIn[0] (new input kind SampleMask). It shares the variable the partial quad sample mask already declares, since a module may hold only one SampleMask input. On a multisampled draw the hardware word is the whole pixel's coverage, but when the shader runs per sample gl_SampleMaskIn holds only that invocation's samples. So per-sample shading is refused, and so are PERSP_SAMPLE, LINEAR_SAMPLE and ANCILLARY beside the coverage input (reading the sample index makes the host shade per sample).
  2. feat(agc): merged coverage. A draw with MSAA_ENABLE set, no depth target, no per-sample shading, only one-sample color targets and OVERRASTERIZATION_AMOUNT equal to log2 of the raster samples now decodes with State::mergedCoverage. Before, it threw "a color target's sample count differs from the rasterizer's". Its pipeline keeps the raster sample count, gives the color attachments one sample (VkAttachmentSampleCountInfoNV, or the render pass attachments) and merges coverage (VK_COVERAGE_REDUCTION_MODE_MERGE_NV). A one-sample color pixel is then covered when any raster sample is, and gl_SampleMaskIn carries the four bits. The device enables VK_NV_framebuffer_mixed_samples and VK_NV_coverage_reduction_mode when vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV reports merge with one color sample and no depth. On other devices the pipeline throws, so the draw still stops, as it does on main. Pipeline keys and the draw state comparison include the flag.

Tests:

  • agc_driver_pixel_sample_coverage (new, GPU). A full-target triangle into a one-sample target reads 1 from SAMPLE_COVERAGE alone, after the perspective center pair, and after ANCILLARY. A triangle over half of the target, rasterized with 4 samples into the one-sample target, writes only masks from 1 to 15, at least one 15 and one partial edge mask, and leaves the rest untouched. On a device without merged coverage that part prints a skip line. On main's sources the first case fails at once: the first pixel holds (0, 0, 0, 1), as the VGPR is never loaded.
  • agc_driver_graphics. 0x4002 decodes with the coverage VGPR at 2 and is read from gl_SampleMaskIn. Beside a partial quad sample mask, the module declares one SampleMask input. On a 2x target, SAMPLE_COVERAGE is refused with PS_ITER_SAMPLE and with an addressed PERSP_SAMPLE, LINEAR_SAMPLE or ANCILLARY. The register set above decodes as merged coverage, and a wrong overrasterization amount, PS_ITER_SAMPLE or MSAA_ENABLE clear are still refused. On main's State.cpp this stops at "a color target's sample count differs from the rasterizer's".

Tested

Linux (Ubuntu 24.04, GCC 13.3, Release), RTX 4070 Ti, NVIDIA 595.99:

Build Result
main 0029e62 2 of 2 stop at 19–20 s on the SAMPLE_COVERAGE refusal above
main 03c4cb5 + the first commit only the same draw stops at 19 s: a color target's sample count differs from the rasterizer's
main 03c4cb5 + both commits 3 of 3 reach the limit (one 90 s run with screenshots, two 120 s runs). The screenshots show the preset's tunnel drawn with smooth outlines, at about 4 FPS, before the player's menu returns at 60 FPS

With the full preset folder (a random preset each run), 2 of 2 180 s runs with both commits reach the limit. PPSA99600, the other app here that uses ps5-gallium, reaches its 120 s limit on main and with this PR. The branch is rebased onto main 44df216, where both test binaries pass 3 of 3.

On Windows 11 (RTX 4070, NVIDIA 596.21), @screamm built 087b633, ran both test binaries 3 of 3, and confirmed that they fail with this PR's sources reverted. With OliSe, main stopped 2 of 2 on the same refusal and this PR reached the 150 s limit 3 of 3. There, though, the visualiser drew black: the menu at half brightness after one Square press, a black window after the second. In my Linux runs the tunnel is visible. Neither setup had a music module playing. screamm then traced the black frame. With APS5_NO_RESIDENT_TARGETS=1 the three preset targets fill with the tunnel, and the final frame shows it behind the menu. Only the default resident-target path is black, and only there does the log show guest memory changed while GPU results were pending; keeping the CPU's blocks, together with 1,353 storage-image uploads with the reason untracked. My Linux runs of this head never log that line (the memory no longer registered; 135 pending ranges dropped line shows up 3 to 4 times on both). So the black frame looks like the Windows host-import comparison discarding the targets' GPU results, not these draws.

screamm also caught that this PR's agc_driver_graphics failed when merged with current main. #5882 now lets a multisampled scan without MSAA_ENABLE past DecodeState when there is no depth target, so my last smoothing case is refused at the color sample-count check instead. Rebased onto main 5d9dcc4 (1fd45dc), with that expectation updated. agc_driver_graphics passes 3 of 3, and the 469 area tests give 468 passes, the remaining one being main's agc_driver_store_over_untracked (#6385).

@johnmaia ran the full suite on AMD (Radeon RX 7600, RADV NAVI33, Mesa 25.2.8, Ubuntu 24.04) with 1fd45dc on main 82ddae7. 1287 of 1288 tests pass, and the one failure is main's agc_driver_graphics (#6792). agc_driver_pixel_sample_coverage passes, and with #6789 merged too, agc_driver_graphics passes with and without this PR, so the new Graphics.cpp cases run there.

Rebased onto main 1b1f848 (faea7a2), which now has #5907 (simonbronk): multisampled draws into one-sample targets rasterize at sample 0's location, and a sample-coverage input is refused there. The two meet in the color sample-count check. An overrasterized draw (OVERRASTERIZATION_AMOUNT equal to log2 of the raster samples) still decodes as merged coverage, and any other one-sample draw takes #5907's sample-0 path. Two expectations changed with that:

agc_driver_graphics, agc_driver_covered_centroid_state and agc_driver_pixel_sample_coverage pass 3 of 3. Of the 898 area tests (the regex above plus ^guest_|libc), all pass except main's agc_driver_store_over_untracked.

6972f7a, after @screamm's recheck: State::mergedCoverage is part of the pipeline store key and is read when the pipeline is created, but the pipeline journal's state codec and CanonicalPipelineState dropped it. A journalled merged-coverage pipeline therefore replayed as a plain multisampled one, a lost prewarm as screamm noted. The codec and the canonical state now carry it, and the journal format becomes 4. A new agc_pipeline_journal case round-trips a merged-coverage state and requires the decoded state to key the same pipeline. With the previous PipelineJournal.cpp it fails with the recipe dropped merged coverage; with the change it passes 3 of 3. The AGC area suite (504 tests) passes except agc_driver_store_over_untracked, as before. #6639 also moves the format to 4 for its own fields, so whichever of the two merges second will move it to 5.

@screamm rechecked 6972f7a38 on Windows (RTX 4070, 596.21). The journal and coverage tests pass 3 of 3. Without the encodeState line, or without the CanonicalPipelineState line, the journal tests fail. #6959 also moves the journal format to 4, like #6639, so whichever of these merges later takes the next number.

Not checked in a title on AMD or Intel (where the merged path throws), or on a console.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 4070 Ti, driver 595.99, Ubuntu 24.04; NVIDIA GeForce RTX 4070, driver 596.21, Windows 11 (by @screamm); AMD Radeon RX 7600, Mesa 25.2.8 RADV, Ubuntu 24.04 (tests, by @johnmaia)
  • AI-assisted: yes

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

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.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6801 core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp L316

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.

Pull request Shared files not compared
PR 1191 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
PR 1368 core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 2434 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 4040 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4072 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
PR 4472 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
PR 4891 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5021 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5431 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5441 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5480 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 5499 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5500 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/StageIO.hpp
core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 5656 core/libs/prx/libSceAgcDriver/Graphics/src/PipelineJournal.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/PipelineJournal.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6177 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6188 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6194 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6235 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp
PR 6249 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6280 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 6360 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6457 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6479 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6481 core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6503 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
PR 6529 core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6535 core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6539 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 6548 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6563 core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6615 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
PR 6647 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6679 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/StageIO.hpp
core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp
PR 6683 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp
PR 6718 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6727 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6730 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6762 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
core/shader/recompiler/Translation/src/ShaderInputInfoBuilder.cpp
PR 6763 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6786 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6788 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6804 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6808 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6820 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6941 core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 6946 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/PipelineJournal.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
PR 6947 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/PipelineJournal.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 6970 core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6972 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Execution/src/Driver/Draw/DrawRegisterKey.cpp
core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
PR 6989 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
PR 6995 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
PR 7002 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
PR 7085 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7201 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7233 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7254 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7292 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7310 core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 7332 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/State.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/Optimization/src/ShaderInfoCollector.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7340 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7369 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7371 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
PR 7390 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7410 core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/shader/recompiler/Translation/src/InstructionTranslator.cpp
PR 7445 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp
PR 7484 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
PR 7618 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/Context.hpp
PR 7622 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/PipelineJournal.cpp
PR 7642 core/libs/prx/libSceAgcDriver/Execution/src/VulkanDevice.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Pipeline.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/PipelineJournal.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/ShaderInputState.cpp
core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvEmitterHelpers.cpp
core/shader/recompiler/SpirvBackend/src/SpirvModuleEmitter.cpp

@screamm

screamm commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Cross-validation on Windows 11 Pro 26220, NVIDIA GeForce RTX 4070 (driver 596.21), WinLibs MinGW-w64 GCC 15.2, PR head 087b6337a on main 44df21632 (fast-forward, no conflicts):

  • Build: full build and libs pass, except agc_driver_gpu_indirect_arguments_misaligned_writer_ahead_tests (64 KiB alignment on MinGW), which fails the same way on main (test(agc): align the indirect arguments block at run time #6452/fix(agc): allocate indirect arguments test block dynamically on Windows #6478).
  • agc_driver_graphics and agc_driver_pixel_sample_coverage, --repeat until-fail:3: 3 of 3 each. The coverage test printed no "merged coverage not tested" line, so the RTX 4070 on Windows takes the VK_NV_framebuffer_mixed_samples + VK_NV_coverage_reduction_mode MERGE path.
  • Negative control: the 11 changed .cpp files reverted to 44df21632 (tests and the three headers kept): both tests fail. agc_driver_pixel_sample_coverage: SAMPLE_COVERAGE alone: pixel (0, 0) holds (0.000000, 0.000000, 0.000000, 1.000000); agc_driver_graphics: expected graphics rejection: SAMPLE_COVERAGE in a multisampled pixel shader that runs per sample. Restored and rebuilt: both pass.
  • GPU suite (-j1 --timeout 300 -R '^agc_driver_', minus the unbuildable test): 448 of 452 pass, 2 skipped, 2 fail. agc_driver_preparation_scope_settle fails on main too. agc_driver_sample_mask_draw crashed once (SegFault) while I ran the non-GPU suite in parallel; it passes 7 of 7 serially on this PR and 7 of 7 on main, so I could not reproduce it.
  • Non-GPU suite (-j8 --timeout 120, loaded host): 814 of 836. Of the 22 failures, 12 are the Python conventions/infra tests timing out (same on main). Of the 10 C++ ones, 9 pass serially; guest_none_relocations times out on main too.
  • tools/check_conventions.py --base origin/main and git diff --check: pass.

OliSe Player PPSA01153 (02.001.000), the same setup as in the description: presets trimmed to Rovastar - Dark Ritual (Star Of Destiny Mix).milk, Square pressed through scripted pad input once the menu renders, 150 s limit:

Build Result
main 44df21632 2 of 2 stop 4 to 7 s after Square (10.9 s and 16.1 s), exit 0xC0000409: terminate called after throwing an instance of 'std::runtime_error' / what(): AGC graphics: unsupported SPI_PS_INPUT_ENA/ADDR bit combination (ena 0x4002 addr 0x4002)
this PR 3 of 3 reach the limit with no stop line (Square once in two runs, twice in one)

The draw no longer stops the title, but the preset is not visible on this machine. After one Square press (the app's help says VISUALISER: OFF / BEHIND / FULL) the "VISUALISER / TUNNEL" toast appears and the whole frame is the menu at half brightness. 30 frame dumps over about 140 s change only where the menu animates. After a second press ("VISUALISER FULL") the window is completely black apart from the toast (every pixel outside it is 0) for over 60 s. The guest's own fps: is 0.5 to 1.4 in the visualiser and 28 to 60 in the menu. The host was heavily loaded by parallel builds, so I can't compare the frame rate with your 4 FPS. No module played in my runs: the app's download fails (radio: DOWNLOAD FAILED (-2)) and music/ is empty. So I can't tell whether the black output is this preset with silent input or a rendering problem. Was a module playing when you took the tunnel screenshots?

Cloud Play PPSA99600: main 1 run, this PR 2 runs, all reach 150 s with no stop line (2640; 2270 and 2597 frames). None of the three compiles a smooth=1 shader, so this title does not reach the changed path; it only shows no regression.

Not checked: AMD or Intel (where the merged path throws by design), a console, a run with a module playing, the untrimmed preset folder, the frame rate on an idle host, and Linux.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

@milescoviello

Copy link
Copy Markdown
Contributor Author

@screamm thanks for the Windows run and the negative control.

No module played in my runs either. My logs show the same lines as yours: library: /app0/music: 0 module(s) and radio: DOWNLOAD FAILED (-2). So the tunnel in my screenshots came from silent input.

On Linux (RTX 4070 Ti, NVIDIA 595.99) the preset was visible a few seconds after the first Square press. After "VISUALISER" the menu sits over the tunnel. After "VISUALISER FULL" the tunnel fills the window, at a guest fps: of 4.2 to 5.1. So the black visualiser you see doesn't happen here, and I don't know yet why. Your agc_driver_pixel_sample_coverage pass on the MERGE path suggests the smoothed draws work on your driver, but I haven't checked which draw goes black there, so that part is a guess.

I'll add your run to the description, including the black output on Windows, so it isn't presented as checked there. If you get to it, a frame dump would show whether the preset's own targets stay black or only the final composite does. No pressure, you've already covered a lot.

AI-assisted: yes (Claude Code)

@screamm

screamm commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Follow-up with the frame dumps, on Windows 11 Pro 26220, NVIDIA GeForce RTX 4070 (596.21), WinLibs MinGW-w64 GCC 15.2: this PR (087b6337a) merged on main 5d9dcc42d (clean). OliSe Player PPSA01153, presets trimmed to the Rovastar preset, Square pressed once after the menu renders, 150 s per run.

Short answer: the preset's own targets are not black. When the render targets bypass the resident-image path (APS5_NO_RESIDENT_TARGETS=1), the three preset targets fill with the tunnel and the final frame shows it behind the menu. On the default path the final frame is black. So the black comes from how the driver keeps resident render targets on this machine, not from the merged-coverage draws.

Run (this PR) Switches Frame dumps after Square
default APS5_TRACE_DRAWS=1 APS5_PROFILE_DRAW=1 13 dumps, max pixel 140, mean 17.6-18.1: the menu at half brightness, no tunnel
default APS5_STORE_AT_FLIP=1 29 dumps, max 140, mean 18.3-19.0: unchanged
default APS5_DUMP_STORAGE=22b200000 26 dumps, max 140, mean 18.1-19.0: unchanged, and no storage_*.raw was written, so I could not read the resident image itself
non-resident targets APS5_NO_RESIDENT_TARGETS=1 18 dumps, max 255, mean 61.5-138.2, 91-99 % non-zero pixels: tunnel with the smoothed green outline visible

All of these reach the 150 s limit with no stop line (260 to 508 frames, guest fps: 0.3 to 2.0 in the visualiser).

Draws after Square (default path, about 140 s): 3145 draws, almost all into three 1920x1080 R8G8B8A8_UNORM targets (0x228c00000 1019, 0x229600000 1008, 0x22b200000 999 draws). 129 of them also write a linear 1920x1080 R16G16_SFLOAT target (0x22a000000), and 119 go to the two display buffers.

Per-draw target dumps (APS5_DUMP_TARGETS=400, which reads only non-resident targets, so together with APS5_NO_RESIDENT_TARGETS=1): 241, 213 and 214 dumps of the three preset targets. Only the first one or two of each are all zero. After that they hold the tunnel: values up to 255, the mean of R, G and B reaches 93-98, and 97 % of the pixels are non-zero in the last dumps. Main 5d9dcc42d with the same switches (APS5_DUMP_TARGETS=40) shows the same start in the 24 renders before it stops 5.5 s after Square on AGC graphics: unsupported SPI_PS_INPUT_ENA/ADDR bit combination (ena 0x4002 addr 0x4002).

Only on the default path, the log has [gpu] storage image 0x22b200000: guest memory changed while GPU results were pending; keeping the CPU's blocks and [gpu] storage image 0x22b200000+0x10e0000: memory no longer registered; 135 pending ranges dropped. With APS5_PROFILE_DRAW=1, the [storage] lines count 1353 storage-image uploads with the reason untracked (22.8 GB) during the run. That looks like the preset images being refilled from guest memory that never received the GPU results, but I have not proven it. Do your Linux logs show these lines?

Separate, for the rebase: on current main this PR's agc_driver_graphics fails with AGC graphics: unexpected failure: AGC graphics: a color target's sample count differs from the rasterizer's (--covered-centroid-state, i.e. multisampleTests, fails on the merge and passes on main). #5882, merged after 44df21632, now accepts a multisampled target without MSAA_ENABLE for filled primitives. Your last smoothing case (smooth.context[0x292] = 2u, expecting multisampled targets rasterized without MSAA_ENABLE) therefore reaches the colour-target check. The draw is still refused, just with the other message.

Not checked: reading the resident images directly on the default path, whether other presets go black on main for the same reason, Square pressed twice (FULL), Linux, AMD or Intel.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

@milescoviello
milescoviello force-pushed the feat/smoothing-mixed-samples branch from 087b633 to 1fd45dc Compare October 16, 2026 07:59
@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks @screamm, both findings were useful.

On your question: my Linux runs of this head (RTX 4070 Ti, Dark Ritual preset, the visualiser showing the tunnel) never log keeping the CPU's blocks. They do log storage image 0x22b800000+0x10e0000: memory no longer registered; 135 pending ranges dropped 3 to 4 times per run, like yours. Those runs didn't have APS5_PROFILE_DRAW, so I have no [storage] upload counts to compare. Together with your APS5_NO_RESIDENT_TARGETS=1 result, that points at the Windows path where resident targets are compared against guest memory instead of write-tracked, which #6189 also describes. I added this to the description.

The rebase: you were right. 1fd45dc is on main 5d9dcc4, and the last smoothing case now expects a color target's sample count differs from the rasterizer's. The draw is still refused. agc_driver_graphics passes 3 of 3, and the area tests give 468 of 469, the remaining one being main's agc_driver_store_over_untracked.

AI-assisted: yes

@johnmaia

Copy link
Copy Markdown
Contributor

AMD run with this head (1fd45dc) merged onto main 82ddae7: AMD Radeon RX 7600 (RDNA 3, RADV NAVI33, Mesa 25.2.8), Ubuntu 24.04, GCC 13.3, Release, full ctest -j4.

AI-assisted: yes (Claude Code)

@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks @johnmaia, the AMD run is very useful, especially confirming that the new Graphics.cpp cases run once #6789 is in. I added it to the description.

AI-assisted: yes

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@milescoviello
milescoviello force-pushed the feat/smoothing-mixed-samples branch from 1fd45dc to faea7a2 Compare October 16, 2026 19:49
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
@screamm

screamm commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Rechecked on head faea7a221 (rebased over #5907), Windows 11 Pro 26220, RTX 4070 (596.21), WinLibs GCC 15.2. Stacked as main 5f61879a9 + this PR + #6639 + #6669 (the only conflict is the adjacent Require lines with #6639 in Pipeline.cpp; I kept both). Full build: no failures.

  • agc_driver_graphics, agc_driver_covered_centroid_state and agc_driver_pixel_sample_coverage pass 3 of 3, with no skip line, so this GPU takes the merged-coverage path. The GPU pass -R '^agc_driver_' passes 484 of 487 with 2 skipped. The one failure, agc_driver_f16_fma_denormal_modes, also fails on plain main here. Non-GPU suite: no real failures.
  • OliSe Player, Che - Escape (which needs this PR and feat(agc): draw point lists on the vertex path with their point size #6639), 120 s: main stops 2 of 2 on layer, viewport or auxiliary vertex exports is unsupported (register 0x207 = 0x01210000). The stack reaches the limit 2 of 2 with no driver failure. That matches my earlier stacked run.
  • One thing I noticed while reading the feat(agc): draw point lists on the vertex path with their point size #6639 rebase: State::mergedCoverage is in the pipeline store key and read by the Pipeline constructor, but PipelineJournal.cpp neither encodes it nor keeps it in CanonicalPipelineState. So a journalled merged-coverage pipeline would replay as a non-merged one, and the later draw would miss it and build it again. That is a lost prewarm, not a wrong pipeline. feat(agc): draw point lists on the vertex path with their point size #6639's ea05b7804 fixed the same gap for its own fields. I found this by reading the code and did not run a replay.

Not checked: the Rovastar preset this time, AMD/Intel, Linux.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks @screamm, good catch on the journal. PipelineJournal.cpp dropped State::mergedCoverage in both the state codec and CanonicalPipelineState, so a journalled merged-coverage pipeline came back as a plain multisampled one. 6972f7a carries it in both, moves the journal format to 4, and adds an agc_pipeline_journal round trip that requires the decoded state to key the same pipeline (it fails with the old codec). Its lines sit before quadSampleMask, so they don't collide with #6639's. Whichever of the two merges second will bump the format to 5.

AI-assisted: yes (Claude Code)

@screamm

screamm commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Rechecked on head 6972f7a38 (scratch-merged onto main 673f7406c; this PR merges cleanly alone, #6639 is not needed for the merge). Windows 11 Pro 26220, RTX 4070 (596.21), WinLibs GCC 15.2. Targeted build only (the three AGC driver test targets plus what they link), not a full build. My tree also contained #7035, which touches only libkernel.

  • agc_pipeline_journal, agc_driver_pipeline_journal, agc_driver_graphics, agc_driver_pixel_sample_coverage and agc_driver_covered_centroid_state pass 3 of 3 with ctest --repeat until-fail:3, no skip line.
  • The merged-coverage round trip is covered by the new block in pipelineJournalTests (run by agc_pipeline_journal): encode, decode, mergedCoverage kept, and the store key from the decoded state equals the original and differs from the non-merged one. It covers the recipe codec and the key, not a journal file write and replay of such a pipeline.
  • Negative controls: without the encodeState line, agc_pipeline_journal fails (the second run did not load the two recorded pipelines and their three modules) and so does agc_driver_pipeline_journal (the recipe does not decode). Without the line in CanonicalPipelineState, agc_pipeline_journal fails with the recipe dropped merged coverage. Restored, all pass.
  • FileFormat goes 3 to 4 and is used only for the header write and check, so no test pins the number. feat(agc): draw point lists on the vertex path with their point size #6639 (ea05b7804) and fix(agc): render layered mesh targets within device limits #6959 also bump 3 to 4; if their codecs differ, whichever merges later needs the next number, or two codecs share format 4 (a dropped journal tail, not a crash).
  • tools/check_conventions.py --base origin/main passes and git diff --check is clean.

That closes the gap I pointed out.

Not checked: the GPU pass, the non-GPU suite, title runs for this head, AMD/Intel, Linux.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks @screamm for rechecking these on Windows (#6530 journal fix, #7035 timing fix, #6681 relinker side). I added each run to its description, and noted #6959 on #6530.

AI-assisted: yes (Claude Code)

@screamm

screamm commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

@milescoviello, about the storage uploads and #6189. I stacked this PR (6972f7a38) with #6202 (the #6189 fix) on main 673f7406c, both clean merges, and compared it with this PR alone. Same OliSe setup as before: Rovastar preset only, Square once, 120 s, APS5_PROFILE_DRAW=1 APS5_PROFILE_GPU=1, Windows 11, RTX 4070 (596.21).

Build Frame dumps after Square untracked uploads per 10 s Write-backs per 10 s
this PR + #6202 26 dumps, max 140, mean 17.4-18.4 (still black) 656-768 (10.8-12.7 GiB) linear target 58-63
this PR alone 14 dumps, max 140, mean 17.9-18.3 530-608 (8.7-10.0 GiB) linear target 46-54, refresh 54-63, storage refresh 38-45

Both runs reach the limit with no stop line, and both log storage image 0x22b200000+0x10e0000: memory no longer registered; 135 pending ranges dropped four times.

So #6202 removes the refresh write-backs but not the black visualiser and not the untracked uploads. Those are still about 23 per present. All of them take the CPU path, at 16.875 MiB each. That is 0x10e0000 bytes, the size of one two-slice 1920x1080 image, so these are uploads of the whole image. The [gputime] storage-upload class stays at 0 in both runs, because it times only GPU-direct uploads. Use the [storage] line to count these.

The preset targets do have #6189's two-slice array layout, but #6189 doesn't explain the black output.

Written with Claude Code assistance (validation by a subagent, reviewed in this session).

@milescoviello

Copy link
Copy Markdown
Contributor Author

Thanks @screamm. Here is the same setup on Linux, to compare against your untracked count. Rovastar Dark Ritual preset only, Square once, 120 s, APS5_PROFILE_DRAW=1 APS5_PROFILE_GPU=1. The build is main 40315fc plus this PR (6972f7a), on Ubuntu 24.04 with an RTX 4070 Ti (NVIDIA 595.99), 2 runs. The rows are 10 s intervals after the first one:

Linux, this PR (2 runs) Your Windows run, this PR alone
[storage] CPU uploads per 10 s 130–134 (2.19–2.26 GiB), 16.875 MiB each 530–608 (8.7–10.0 GiB)
by reason flushed 72–74, store 57–60, untracked 0 (1–2 only in the first 10 s) untracked
write-backs per 10 s linear target 86–90, refresh 130–134, storage refresh 72–75 linear target 46–54, refresh 54–63, storage refresh 38–45
presents per 10 s 44–45
memory no longer registered; 135 pending ranges dropped 1–2 per run 4 per run

So Linux uploads the same whole two-slice 1920x1080 image (0x10e0000 bytes), about 3 times per present. None of those uploads is untracked: they are flushed CPU writes or stores. Your ~23 untracked uploads per present don't occur here.

That supports the earlier reading: on Windows, the resident targets are compared against guest memory rather than write-tracked (#6189's description of that path), and the black frame comes from there. It doesn't come from the merged-coverage draws in this PR. The 2.2 GiB per 10 s of whole-image CPU uploads on Linux is a cost worth looking at too. It's separate from this PR, which doesn't change storage handling.

I didn't dump frames in these runs, so this table has no mean brightness. Earlier Linux runs on this preset showed the tunnel.

AI-assisted: yes (Claude Code)

@milescoviello

Copy link
Copy Markdown
Contributor Author

Rebased onto main 400baa2 (head d1b6f08).

AI-assisted: yes (Claude Code)

@milescoviello
milescoviello force-pushed the feat/smoothing-mixed-samples branch from 89d14bd to d1b6f08 Compare October 18, 2026 19:10
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 18, 2026
milescoviello and others added 4 commits October 19, 2026 01:25
SPI_PS_INPUT_ENA/ADDR bit 14 (SAMPLE_COVERAGE_ENA) gives the pixel shader one
VGPR, after ANCILLARY, holding the samples the primitive covers. The decoder
refused it and the recompiler never loaded the VGPR. It is now loaded from
gl_SampleMaskIn[0] (sharing the input the partial quad sample mask already
declares). On a multisampled draw the hardware word is the whole pixel's
coverage while gl_SampleMaskIn holds only the invocation's samples when the
pixel shader runs per sample, so that case, and sample-rate or ancillary inputs
beside it, are refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xed samples

A radeonsi-style GL driver smooths lines and polygons by rasterizing four
samples into a one-sample color target: PA_SC_AA_CONFIG has 4 samples,
DB_EQAA.OVERRASTERIZATION_AMOUNT is log2(4), and the pixel shader reads
SAMPLE_COVERAGE to scale alpha by the covered samples. The pixel is written when
any of the four samples is covered. DecodeState refused the one-sample target as
a sample-count mismatch.

Such a draw (MSAA_ENABLE set, no depth target, no per-sample shading, every
color target one-sample, OVERRASTERIZATION_AMOUNT matching the raster samples)
now decodes with mergedCoverage. Its pipeline keeps the raster sample count,
gives the color attachments one sample (VkAttachmentSampleCountInfoNV or the
render pass) and merges coverage (VK_COVERAGE_REDUCTION_MODE_MERGE_NV). The
device enables VK_NV_framebuffer_mixed_samples and VK_NV_coverage_reduction_mode
when it reports that combination; elsewhere the pipeline throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pipeline creation and the pipeline store key read State::mergedCoverage,
but the journal's state codec and CanonicalPipelineState dropped it, so a
journalled merged-coverage pipeline replayed as a plain multisampled one
and the draw built its pipeline again. The codec and the canonical state
now carry it and the journal format becomes 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t MSAA_ENABLE

Since boykopovar#6196, a one-sample target under a multisampled rasterizer with
MSAA_ENABLE clear is drawn as one sample instead of refused, so the
overrasterized draw with MSAA_ENABLE clear now decodes as one sample without
merged coverage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@milescoviello

Copy link
Copy Markdown
Contributor Author

Rebased onto main 017f215 (f0b208a). Three conflicts:

  • StageIO.hpp: main added four barycentric input kinds. This PR's SampleMask now comes after them, so main's values don't change.
  • DrawRegisterKey.cpp: main added iteratesSamples, positionLocation and sampleLocations to the pixel key line. This PR's mergedCoverage stays in the sample line.
  • tests/Graphics.cpp: main now rasterizes PS_ITER_SAMPLE with sample-index and position inputs as sample 0 alone instead of refusing it, and that expectation is kept. This PR's SAMPLE_COVERAGE case keeps its own refusal.

The 596 tests matching ^agc_driver_|^agc_vertex_barrier|recompiler|shader_cache|request_serializer|spirv|^agc_shader|bda|journal|prepared pass, except agc_driver_bda_device, which fails on main here too (#7571).

AI-assisted: yes (Claude Code)

@milescoviello
milescoviello force-pushed the feat/smoothing-mixed-samples branch from d1b6f08 to f0b208a Compare October 19, 2026 01:54
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@adriadam10
adriadam10 merged commit e0b8be9 into boykopovar:main Oct 19, 2026
10 checks passed
oneandonlydean pushed a commit that referenced this pull request Oct 21, 2026
Resolves sameDecode in DrawRegisterKey.cpp, keeping this branch's sample-order comparison and #6530's merged-coverage comparison.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants