Skip to content

fix(agc): retire a multisampled color target the title wrote over - #8223

Merged
oneandonlydean merged 10 commits into
boykopovar:mainfrom
savvaniss:fix/agc-retire-overwritten-msaa
Oct 30, 2026
Merged

oneandonlydean merged 10 commits into
boykopovar:mainfrom
savvaniss:fix/agc-retire-overwritten-msaa

Conversation

@savvaniss

@savvaniss savvaniss commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What

A multisampled color target keeps its samples on the host. Its guest memory stays guarded until an unmap or an allocation removal covers it: any access of that memory throws "overlaps multisampled color target … guest access to it is not implemented". A title with its own memory pool never unmaps. It reuses the memory of a render target it no longer needs, and the next access of the new data throws, although the target is dead. This PR takes two kinds of reuse as evidence that a target is dead:

  • The CPU wrote over it. Each target keeps a write-tracker generation, taken with CollectWrites over its color memory each time it is used. When a guarded access overlaps a target in write-watched memory, and the CPU wrote the overlapped bytes since that generation (UnchangedSinceCollected), the title has put other data there.
  • Another target was bound over it. In PPSA03208 the title reuses the memory through the GPU (trace). A 2-sample color target is drawn once. Later, a native 2-sample D32S8 depth target whose stencil plane sits at the same address is bound, and the stencil plane's import reads inside the color guard. Each bind over guarded memory now marks the targets it overlaps as claimed. That covers a depth target's depth plane, stencil plane and HTILE (at the top of DepthSurfaceView, before any import), a single-sample color target (where Draw already retires the depth surfaces it overlaps), and a draw of a multisampled color target with another address, extent, sample count or format. A draw through the target's sRGB twin format is served by the same target, so it is a draw of that target, not a claim.

A target's use clears its own mark and takes a new write generation. A use is a draw, and, as #9186 already holds a target host-only there, a dispatch's storage view of it or a color fill it absorbs. A target used again after the claim keeps its guard.

Since #9186, a guarded access also retires a target whose samples are not only on the host, that is a texture sampled before any draw, until a draw, a storage write or an absorbed fill. The three conditions sit together in RequireNoMultisampleColorTarget, in #9186's order: when every guarded range that an access overlaps can retire, they are all retired the way an unmap retires them, and the access goes on; when any of them cannot, the access refuses and nothing is retired. A later draw to the same address creates and guards a new target. A target is only retired at an access that main refuses with an exception, so nothing that runs on main behaves differently.

Each target keeps this state in one TargetUse (hostOnly, writes, claimed), and every guarded range of the target points to it, the DCC, CMASK and 8-sample FMASK ranges included. The one-fragment FMASK range of GuardOneFragmentFmask keeps its fmask flag and gets a TargetUse of its own, but claims skip it, so it refuses every guest access as on main. guard() returns void as on main; the color footprint it computes is guardedColorBytes(), which the use stamp also calls.

The debt entry for multisampled color targets records the rule and its gap: a target that is only sampled after another target was bound over it loses its samples. #6987, #6989 and #6995 work toward reading live samples from guest memory, which this does not do. Amoenus's adaptation of this branch onto main 5a2744c (25907aa1fb) was the reference for the one-fragment FMASK check in the test.

Tested

Windows 11 Pro 26300, WinLibs GCC 15.2.0 r7, Release, AMD Radeon RX 9070 XT, on main b7488e1 (merged into this branch on 30 Oct).

  • agc_driver_sample_multisample_target runs CheckOverwrittenTarget and CheckClaimedTargets after main's sampling test. The first is the CPU-write case: a write over a drawn 4x target in watched memory retires it at the next guarded read, and a later draw guards the new target. The second has the bind cases, in plain (unwatched) memory:
    1. It draws a 2x target, checks that a read of it is refused, and binds a 2x D32S8 target whose stencil plane is the color memory. A read of 0x1000 bytes of that memory must pass.
    2. Control: the same, but the color target is drawn again after the depth bind. The read must still be refused.
    3. It draws a 2x target and then a single-sample color target over the same memory. A read of that memory must pass.
    4. It draws a 128x64 2x target over two 64 KiB blocks, then a 16x16 4x target over the first block. A read of the second block must pass, and a read of the first block is still refused by the 4x target.
    5. Control: a dispatch stores to the 2x target after the stencil bind. The read must still be refused.
    6. A stencil plane bound over a one-fragment target's FMASK guard leaves it refusing.
  • With one part of the change removed at a time, each case fails at its own check, 7 of 7 (on the 25 Oct head; the later merges change no logic of this PR):
    • without the depth claims, case 1: "a 2x target that a D32S8 target's stencil plane was bound over after its last draw is still guarded";
    • when a use keeps its own mark, case 2: "a 2x target drawn again after a D32S8 target's stencil plane was bound over it is not guarded";
    • without the single-sample claim, case 3: the single-sample draw itself is refused ("a CPU read of …+0x10000 overlaps multisampled color target …+0x10000 (2 samples)");
    • without the claim from a multisampled draw, case 4: "a 2x target that a 4x target was drawn over is still guarded";
    • when holdHostOnly stamps no use, case 5: "a 2x target that a dispatch stored to after a D32S8 target's stencil plane was bound over it is not guarded";
    • when claims also mark the one-fragment FMASK range, case 6: "a stencil plane bound over a one-fragment target's FMASK retired its guard";
    • without the CPU-write evidence, CheckOverwrittenTarget: "a read of target memory the title wrote over … overlaps multisampled color target".
  • agc_driver_sample_multisample_target on main's production code stops 3 of 3 at the CPU-write case ("a read of target memory the title wrote over … overlaps multisampled color target").
  • Validation layer on main and on this head, on the same machine, with ANYPS5_GPU=AMD, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation, 31 tests: agc_driver_sample_multisample_target, agc_driver_native_msaa_vertex_input, agc_driver_native_fmask, agc_multisample_guest_access (with main's one-fragment FMASK guard tests), agc_driver_msaa_dcc_state, agc_driver_msaa_dcc_clear_2, _4 and _8, agc_driver, agc_driver_cached_image_multisample_guard, agc_driver_multisample_fill_host_only, agc_driver_multisample_retirement, agc_driver_multisampled_pass_order, agc_driver_multisample_readback and its eight cases, agc_driver_multisample_srgb_twin, agc_driver_multisample_centered_lines, agc_driver_msaa_layout, agc_driver_depth_copy_draw, and the native depth tests agc_driver_native_multisample_depth, agc_driver_native_depth_retirement, _clears, _clear_refusals and agc_driver_native_depth_bda_clear. On this head, on main b7488e1, all 31 pass, and agc_driver_sample_multisample_target passes 3 of 3. On main 07da04f (25 Oct, not rerun for the latest merge), all 31 pass, and its agc_driver_sample_multisample_target (without this PR's cases) passes 3 of 3. Neither log has a VUID- line or was not found but was requested by env var VK_INSTANCE_LAYERS, and every Selected GPU and Vulkan device: line names the RX 9070 XT. main's new agc_driver_packed_unorm4444_multisample_partial_write does not build in this tree on either side: its object's dependency file path passes Windows' 260-character limit.
  • Merging main cecb540 (21 Oct): the only conflict was the depthWritten parameter that main added to DepthSurfaceView. main had moved the guard loop into RequireNoMultisampleColorTarget, and the retirement moved with it.
  • Merging main 1da03c6 (22 Oct, after feat(agc): expose expanded FMASK for tracked native owners #7948): feat(agc): expose expanded FMASK for tracked native owners #7948's FMASK guarded() range takes the target's use, like the DCC and CMASK ranges. In the test's Block, both sides had added the same watched parameter, and main's version is kept.
  • Merging main 07da04f (25 Oct, after fix(agc): retire an undrawn filled multisampled color target that a guest access overlaps #9186 and the one-fragment FMASK guard): conflicts in MultisampleTarget.hpp, Draw.cpp, MultisampleTarget.cpp, the test and the debt file, resolved as described above. In Draw.cpp and the header, the claim comes before GuardOneFragmentFmask.
  • Merging main b7488e1 (30 Oct, after fix(agc): refuse buffer aliases of native MSAA targets #6498 and fix(agc): write host-only multisample memory as zeros in a capture snapshot #12566): the debt doc keeps this PR's entry and fix(agc): refuse buffer aliases of native MSAA targets #6498's shader buffer entry, and the test file keeps both sides' test blocks. HostOnlySampleRanges from fix(agc): write host-only multisample memory as zeros in a capture snapshot #12566 reads range.use->hostOnly (found by hugodearaujo).
  • Not run here: PPSA03208. On main, a native multisample stencil plane is not imported, so the test reads the overlapped bytes through RequireNoMultisampleTarget instead of through that import. The claim happens before the import.
  • hugodearaujo ran Assassin's Creed Mirage (PPSA13960) on Linux with an RX 9070 XT, RADV: on main bb04a4f it aborts at 86 s on a CPU read over a 4x target, and with this PR that stop is gone (comment). It then aborts at 79 s on a cached image access over another 4x target. Their trace shows that access is a dispatch's store-only clear over part of the target, a GPU write that is neither a CPU write nor a bind, so it still refuses and is a follow-up.
  • JJenkx ran a private integration of the 25 Oct head on Linux (RX 6650 XT, RADV): the same 19 focused checks pass with and without the PR under the validation layer (comment). In Bendy (PPSA27624), one run got past a CPU read over an 8-sample target and an identical repeat stopped at the same read, so this PR is not a fix for that stop.
  • First revision, not rerun for this one: Boggle (CUSA03312), on feat: run PS4 games on the AGC driver — Limbo (CUSA01664) in game (integration branch) #6977's branch with local support for PS4 multisampled color targets, read a texture that the title had uploaded into the memory of a 4x render target it no longer used. The target was retired there and once more at a storage image access, and the game went on to its menus and a round. The Intel UHD 770 runs and simonbbby's native Linux control (RTX 3090, RADV gfx1036, lavapipe: the PR passes 3 of 3, main's MultisampleTarget.cpp fails 3 of 3, comment) also covered the CPU-write case only.
  • tools/check_conventions.py --base origin/main passes, and tools/pr_overlap.py finds no shared exports.

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
  • Fixes: none
  • Unblocks: CUSA03312 Hasbro Family Fun Pack (CUSA03312) #7198; PPSA03208 at its stop at 1.349 s is expected, not run here
  • GPUs tested: AMD Radeon RX 9070 XT (driver 32.0.31041.1004), Windows 11 Pro 26300; the first revision also on Intel UHD Graphics 770 (driver 32.0.101.7088)
  • AI-assisted: yes

🤖 Generated with Claude Code

@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 20, 2026
@github-actions

github-actions Bot commented Oct 20, 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 10497 docs/dev/debt/libSceAgcDriver.md L107-109
PR 10906 docs/dev/debt/libSceAgcDriver.md L106-107
PR 13146 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp L998-1000

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 1368 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 1395 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 2434 docs/dev/debt/libSceAgcDriver.md
PR 4448 docs/dev/debt/libSceAgcDriver.md
PR 4449 docs/dev/debt/libSceAgcDriver.md
PR 5372 docs/dev/debt/libSceAgcDriver.md
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 5925 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6185 docs/dev/debt/libSceAgcDriver.md
PR 6186 docs/dev/debt/libSceAgcDriver.md
PR 6188 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 6194 docs/dev/debt/libSceAgcDriver.md
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6280 docs/dev/debt/libSceAgcDriver.md
PR 6300 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6412 docs/dev/debt/libSceAgcDriver.md
PR 6481 docs/dev/debt/libSceAgcDriver.md
PR 6548 docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6804 docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6971 core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7048 docs/dev/debt/libSceAgcDriver.md
PR 7085 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7275 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7369 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 7449 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7604 docs/dev/debt/libSceAgcDriver.md
PR 7642 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7844 docs/dev/debt/libSceAgcDriver.md
PR 7978 docs/dev/debt/libSceAgcDriver.md
PR 8014 docs/dev/debt/libSceAgcDriver.md
PR 8246 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 8319 docs/dev/debt/libSceAgcDriver.md
PR 8542 docs/dev/debt/libSceAgcDriver.md
PR 8592 docs/dev/debt/libSceAgcDriver.md
PR 9020 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9335 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9356 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9387 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9432 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9489 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9592 docs/dev/debt/libSceAgcDriver.md
PR 9700 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9712 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9748 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 9852 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9875 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 9915 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10026 docs/dev/debt/libSceAgcDriver.md
PR 10049 core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10193 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10233 core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10249 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 10442 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 10472 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/debt/libSceAgcDriver.md
PR 10492 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
core/libs/prx/libSceAgcDriver/tests/execution/SampleMultisampleTarget.cpp
docs/dev/debt/libSceAgcDriver.md
PR 11126 docs/dev/debt/libSceAgcDriver.md
PR 11444 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
PR 11452 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp
PR 12120 core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
docs/dev/debt/libSceAgcDriver.md
PR 12246 docs/dev/debt/libSceAgcDriver.md
PR 12840 docs/dev/debt/libSceAgcDriver.md

@simonbbby

Copy link
Copy Markdown
Contributor

Native Linux write-watch control for exact head eb9807f341a11320a50d8d188ab7ff902598c089, parent c9c7e6cfaf602d3d9a83b13251e14a810831fce9. I rebuilt the execution fixture and runtime dependencies, then ran the complete agc_driver_sample_multisample_target_tests in three fresh processes per restricted ICD.

Production code, with this PR's fixture throughout RTX 3090 RADV gfx1036 lavapipe
Exact PR 3/3 pass 3/3 pass 3/3 pass
Only MultisampleTarget.cpp restored to the exact parent 3/3 fail 3/3 fail 3/3 fail
Exact PR restored and rebuilt 3/3 pass 3/3 pass 3/3 pass

All nine parent controls stop at a read of target memory the title wrote over ... overlaps multisampled color target ... whose samples are only on the host. All 18 PR processes execute the new watched-memory case, including the refusal before the CPU overwrite, access after the middle 256-byte overwrite, access to the remaining prefix after retirement, and the refusal after drawing the target again. None prints overwritten target retirement not tested, skips, or times out. Thus this is a Linux before/after result for the new behavior, not a green run that bypasses write watching.

After restoration, the seven registered tests matching agc_multisample_guest_access, agc_driver_native_msaa_vertex_input, agc_driver_msaa_dcc_state, agc_driver_msaa_dcc_clear_[248], and agc_driver_sample_multisample_target pass 7/7 on NVIDIA and 7/7 on RADV. Lavapipe passes five and skips the 2x/8x DCC cases with the device lacks the requested multisample color attachments; no failures. The source tree is clean. Raw logs and JUnit results are retained.

Ubuntu 24.04, kernel 7.0.0-38, Ryzen 9950X, GCC 13.3, Release; NVIDIA 595.99.02, gfx1036 1002:13c0 / Mesa 25.2.8, lavapipe LLVM 20.1.2. Actual devices verified from loader output, required Vulkan, shader cache and pipeline journal disabled, other AnyPS5/Vulkan overrides removed. No Khronos validation layer is installed. Boggle, Windows, performance and the full suite were not tested here.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 20, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@savvaniss
savvaniss force-pushed the fix/agc-retire-overwritten-msaa branch from eb9807f to 79b42bd Compare October 20, 2026 12:30
@savvaniss

Copy link
Copy Markdown
Contributor Author

Rebased onto main 56b299c3a; the head is now 79b42bd76. The only textual conflict was the debt line next to the stencil entry. The rebase also needed one code change: #7641 added a guard() call where a target is recreated with another pipe-bank XOR. That call now also records the recreated target's color bytes and use stamp, as the two other guard() calls do.

On the RX 9070 XT, Windows, with the validation layer:

  • agc_driver_sample_multisample_target, agc_driver_native_msaa_vertex_input, agc_multisample_guest_access, agc_driver_msaa_dcc_state and agc_driver_msaa_dcc_clear_2, _4 and _8 pass 3 of 3 on main and on this head.
  • No log has a VUID- line.
  • The new case still fails with main's MultisampleTarget.cpp.

@simonbbby thank you for the Linux control with write watching on three ICDs. I added it to the description.

AI-assisted: yes (Claude Code)

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@savvaniss thanks for #8223. We hit a second form of the same reuse in PPSA03208, one that the CPU-write evidence can't see: the title reuses the memory through the GPU.

From a trace on our bring-up tree (main plus open multisample-depth PRs, with a diagnostic at colour-target registration):

  • At 0.002 s, a 2-sample colour target is registered at 0x1546eb0000 (format 13, 1920x2160, 0x870000 bytes) and drawn once.
  • Later, a native 2-sample D32S8 depth target is bound whose stencil plane is that same address (stencilAddress=0x1546eb0000, depth-only HTILE at 0x1547720000).
  • At 1.349 s, the stencil plane's initial import reads 0x1546eb0000+0x1000. That read lands inside the colour target's guard (last use 0.002 s, 1 use). Without a retire, it is refused and the title stops there.

Nothing wrote over the colour bytes from the CPU, so the write-tracker generation never changes, and as we read #8223 it would keep the guard here. To get past it, we used an unconditional retire-on-read as a probe. The title then runs about 20 s further, to its next, unrelated stop. We don't think that rule is safe for a live target, so we're not proposing it.

Could #8223 accept a second piece of evidence? When a native depth/stencil target (or a colour target with a different layout) claims a range that overlaps a guarded multisampled colour target, and the colour target's last use came before that claim, retire the colour guard through the same retirement #8223 adds for overwritten targets. That's the GPU-side counterpart of "the title wrote over it". A colour target that is still drawn after the claim would keep its guard and still refuse.

For the test, the existing fixture could:

  1. draw a 2x colour target;
  2. bind a D32S8 target whose stencil plane overlaps it;
  3. read the overlapped bytes, which should succeed with the guard retired;

plus a control where the colour target is drawn again after the depth bind and the read still refuses. We're happy to run the title on the result.

@fma-rc

fma-rc commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

@savvaniss #9186 changes the same function for a different case: a multisampled texture that a shader sampled before any draw, later accessed by the GPU with no CPU write in between (Death Stranding 2 stops there even with #8223). The two conflict in guard() and RequireNoMultisampleColorTarget. I merged them locally, so each target keeps your write stamp and the new host-only flag, and the test cases from both PRs pass together on an RTX 5080.

I'm happy for #8223 to land first and then rebase #9186 onto it. If you'd rather go second, I can point you at the merged version. Which order works for you?

savvaniss and others added 3 commits October 21, 2026 19:19
… over

A title can reuse the memory of a multisampled color target through the
GPU instead of the CPU: it binds a depth or stencil plane, an HTILE or a
color target of another layout over it, with no CPU write in between.
Each bind now marks the guarded targets it overlaps as claimed, and a
draw of a target clears its own mark. The next guarded access of a
claimed target retires it the way a CPU write over it does.

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

# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@savvaniss

Copy link
Copy Markdown
Contributor Author

@oneandonlydean thanks for the trace. The rule is in 93a7629 (with main merged in f4352b5), close to how you described it:

  • Each bind over guarded memory marks the multisampled color targets it overlaps as claimed. That covers a depth target's depth plane, stencil plane and HTILE, a single-sample color target, and a draw of a multisampled color target with another address, format, extent or sample count. The depth claims happen at the top of DepthSurfaceView, before any import reads guest memory.
  • A draw of a color target clears its own mark. A target drawn again after the claim keeps its guard and still refuses.
  • At the next guarded access, a claimed target is retired through the same path as the CPU-write case. The access then goes on, unless another live target still overlaps it.

SampleMultisampleTarget has the cases you suggested: it draws a 2x target, binds a 2x D32S8 target whose stencil plane is that memory, and reads 0x1000 bytes of it, which passes. In the control, the color target is drawn again after the bind, and the read is still refused. Two more cases cover the color-layout claims: a single-sample draw over a 2x target, and a 4x draw over part of a 2x target. With the previous head's production code, the test fails 3 of 3 at the first case. When I removed one part of the change at a time, each case failed at its own check. On the RX 9070 XT the MSAA and native-depth set (23 tests) passes under the validation layer on main and on this head, with no VUID- line. The PR description has the details.

On main, a native multisample stencil plane is not imported yet, so the test reads the overlapped bytes directly instead of going through the import from your trace. The claim comes before that import, so I expect PPSA03208 to get past its stop at 1.349 s, but I could not check that here. Thanks for offering to run it.

One trade-off is in the debt entry: a bind is taken as reuse. So if a title binds another target over a live multisampled target and afterwards only samples that target, without drawing to it, the samples are lost at the next guarded access.

@savvaniss

Copy link
Copy Markdown
Contributor Author

@fma-rc thanks for merging the two. Yes: #8223 first, and #9186 rebases onto it, as you offered.

Your merged version needs one more pass, though. Today's push changes the same code again for oneandonlydean's request:

  • The write stamp is no longer a separate usedAt atomic. Each target has one TargetUse (writes, claimed), and every guarded range of the target points to it, the DCC and CMASK ranges included. Your host-only flag can go in the same struct.
  • The retirement loop is now in main's RequireNoMultisampleColorTarget. It retires a target there when it is claimed (another target was bound over it after its last draw) or when the CPU wrote over it.

This does not cover your case. DS2's storage access over a texture that was sampled before any draw has no bind over it, so #9186 is still needed after #8223.

@EagleBearerYS

Copy link
Copy Markdown
Contributor

@savvaniss a merge-order note: #9533 (a draft) overlaps this PR in DepthSurfaceView and in MultisampleTarget.cpp's guard. It imports native multisampled D32S8 targets with a separate stencil plane, and before reading that plane it asks whether a host-only multisampled colour target covers it: the PPSA03208 shape your claim-and-retire rule handles. #9533 goes after this PR and rebases onto it, keeping your claims and retires. Its import then needs a colour target you retire to stay known to it: a retired target over the plane keeps its known fill value, or else leaves the stencil plane unproven, so stale guest bytes there are never taken as the clear value. That's a change on #9533's side; nothing changes here.

@savvaniss

Copy link
Copy Markdown
Contributor Author

@EagleBearerYS thanks for the merge-order note. Understood: #9533 goes after this pull request and rebases onto it, keeping its claims and retires, and the handling of a retired colour target over the stencil plane is on #9533's side. Nothing changes here.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@savvaniss we'd like to merge this before the MSAA stack (#6973 → #7002), which needs a rebase of its own after #7948, so its author can build your retirement into the stack's publish path in the same pass (I've asked them on #6973).

That needs this branch updated first: it now conflicts with main after #7948. It's small and mechanical:

Could you merge main (or rebase) with that? Once the new head is up we'll run full CI on it and one more Astro gate pass (the last gate tree predates #7948), and then merge.

A dispatch's store to a 2x target after a stencil plane was bound over
it keeps the target guarded. A stencil plane bound over a one-fragment
target's FMASK leaves its guard refusing, as on main. The FMASK check
follows Amoenus's adaptation of this branch (25907aa).

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

Copy link
Copy Markdown
Contributor Author

@oneandonlydean I merged main 07da04f into this branch as merge commit aa02d49 (no rebase, no force-push). The head is 331399d. To your questions:

CheckOverwrittenTarget and CheckClaimedTargets run after main's sampling test, which is unchanged. Two cases are new: a dispatch's store after a claim keeps the target guarded, and a stencil plane bound over a one-fragment FMASK guard leaves it refusing.

Tests on the RX 9070 XT, Windows 11, with ANYPS5_GPU=AMD, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation:

  • 31 MSAA, FMASK, native-depth and sRGB-twin tests, agc_driver and agc_multisample_guest_access (with main's one-fragment FMASK guard tests) among them. All 31 pass on main 07da04f and on the head, with no VUID- line in either log. Every Vulkan device: and Selected GPU line names the RX 9070 XT, and agc_driver_sample_multisample_target passes 3 of 3 on both.
  • With one part of the change removed at a time, each of the seven cases fails at its own check. With main's production code, the test stops 3 of 3 at the CPU-write case.
  • main's new agc_driver_packed_unorm4444_multisample_partial_write doesn't build in this tree on either side, because its object's dependency file path passes Windows' 260-character limit.
  • check_conventions.py passes, pr_overlap.py finds no shared exports, and the head merges cleanly with main 88f9967.

The PR description has the full list and each failure message.

AI-assisted: yes (Claude Code)

@savvaniss

Copy link
Copy Markdown
Contributor Author

@Amoenus thanks for the adaptation. I compared today's merge with 25907aa:

@hugodearaujo thanks for the Mirage run. Getting past the texture read at 89 s is what this PR is for. The second stop is not in its scope:

AI-assisted: yes (Claude Code)

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 25, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 26, 2026
@hugodearaujo

Copy link
Copy Markdown
Contributor

@savvaniss I traced that range, and a dispatch wrote it: the refused access is that write. Diagnostic build: main 26ba9b6 with #7532, a local guard that logs and retires instead of throwing, and a print for every driver write, DMA destination, color target and guard check that touches 0x10dc1d0000 to 0x10dc8d0000.

  • Every frame runs in the same order:

    1. draws into a 1x 1920x1080 color target at 0x10dc1d0000+0x480000, then a texture read of it;
    2. a draw into the 4x 960x540 target at 0x10dc450000, guarded for 0x480000 bytes, so its first 0x200000 bytes are the 1x image's last 0x200000;
    3. compute shader 0x1000810a00 over 1920x1080 (240x135 groups of 8x8);
    4. draws into the 1x target again.

    Between the 4x draw and the dispatch, nothing in the range was logged: no driver write, no DMA, and no other color target. I didn't trace CPU writes.

  • The only image 0x1000810a00 uses is a storage image at 0x10dc1d0000+0x480000 (1920x1080, format 13, tile mode 7), plus a 16-byte constant buffer. The shader is a clear. Its 14 instructions compute x and y from the group and thread ids, load the color, and image_store it, with no image load. A few submissions earlier, the same shader clears a 128x128 image at 0x10d6a80000. The "cached image access" in the error is the resource cache revalidating that storage image.

  • So this is the GPU-write case. fix(agc): retire a multisampled color target a write-only storage image overwrites #10049's rule, where a store-only image retires a target it covers completely, wouldn't fire here. The clear covers only 0x200000 of the target's 0x480000 bytes, and the 4x target is drawn again in the next frame.

With the diagnostic retiring every refusal, the next refusal in the run is the same case. It is a storage image access of 0x10e2f80000+0xbf0000 over the 4x target 0x10e3a70000+0x480000 (960x540, format 13). The image is 1920x1080, format 36, SW_64KB_S, with 4 mips. 0xbf0000 is exactly mips 3, 2, 1 and 0 (0x40000 + 0xc0000 + 0x280000 + 0x870000), with mip 0 last, so the 0x100000 bytes it shares with the target are the bottom rows of mip 0. Every frame:

  1. compute 0x10b4d71d00 reads the 4x target as a texture;
  2. in the next submission, compute 0x10b4c89700 runs over 1920x1080 (240x135 groups of 8x8). Its view of 0x10e2f80000 has base and last level 0. Its two image_loads read 0x10d6400000 and a format 36 texture (0x10ddac0000 in the frames that have the 4x target, 0x10d7b90000 before it appears). Its two image_stores write 0x10d7080000 and 0x10e2f80000. It has no branch and never loads 0x10e2f80000;
  3. 0x10b4c8ad00 then writes mips 1 to 3, which don't reach the target.

So the target's last reader has run when the store overwrites it, and the refusal comes back every frame, so the target is drawn again each frame. This overlap is partial too (0x100000 of 0x480000), so #10049's rule wouldn't fire here either. After that the run reaches 150 s, but with the guard retiring everything that says nothing about the rendering.

@JJenkx

JJenkx commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Linux validation of exact peer head 331399d903d7a4454685a3f446598cab86196dcc, preserving its history in private integration 84432c7f27f78b42d856ab56f4c3280a1ceb3eac. Baseline 444d40a7f989 is main f9b2db1d414 plus Bendy prerequisites #6782/#6913/#11690/#11692 (including the within-unit page-hole revision)/#11704. The code merges cleanly; the only conflict is the debt paragraph, resolved while preserving current main's descriptor measurements. This is an integration test, not unmodified-main validation.

RX6650XT / RADV Mesa26.2.4, Linux7.2.9, Ryzen7950X, Release. Same named19 focused checks pass on baseline and candidate: the sampled-target, native vertex-input and FMASK cases plus the16 prerequisite/clear/depth checks. Candidate sampling executes CheckOverwrittenTarget and CheckClaimedTargets. Both arms use ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, explicit RADV ICD and VK_LAYER_KHRONOS_validation:15 loader insertions each, zero VUIDs, no missing layer, no skips. Full logs are in the artifact.

PPSA27624 Bendy01.000.003: the baseline Linux fault-recording run reached the office corridor, then a right camera turn hit a CPU-side16MiB read overlapping a32MiB host-only8-sample color target. The runtime error propagated through presentation/submission and aborted. With this private integration, eight freshly converted outputs and the same ordinary doorway save/load, approach, save-before-OPEN and corridor-turn sequence completed420s with native exit0, no guard/driver failure in the log, reviewed post-turn X11 image, and complete save/config/kernel restoration.

Limits: one successful candidate run; the baseline fault capture used GDB and the candidate replay did not. This does not establish broad stability or a performance gain, and does not resolve the separate earlier intermittent SEGV. Live-sample readback and the retirement-rule gaps documented here remain outside this result.

Source composition, baseline/candidate focused logs, conversion hashes and reviewed scene images, downloaded SHA256 verified: 0d2faba0387ba3c12e057b16705a3ef6a4d1feb3b5e26417ae513987735d538e. Raw game data, saves, shader requests, fault stacks and process mappings are excluded.

Identical native repeat: the second run on the same candidate 84432c7f27f7, without GDB, reached the corridor but aborted immediately after the right camera input at328.6s. It hit the same host-only8-sample CPU-read guard (16MiB read overlapping a32MiB target), native exit134. Saves, memory saves, config and kernel settings were restored; no native processes remained. Thus #8223 is not a reliable fix for this Bendy blocker. The earlier successful run is retained as an observation, not fix evidence. Repeat result, failure excerpt and reviewed screenshots, SHA256 9924a68e6ddf15636819afbf652ee758ad3c6b9fe5dc52eae440106b5e0f3689. Raw stacks/registers and game data are excluded.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@savvaniss merged main into your branch to resolve the conflict in docs/dev/debt/libSceAgcDriver.md; no other changes. Please pull before your next push.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head d488531 merged onto main ba5b533 · tested 2026-10-29 14:24 UTC · 32 min

GPU NVIDIA RTX 2000 Ada Generation, driver 595.99.02, Vulkan 1.4.329
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ✅ 2,211 passed · 3 failed (0 new, 3 also fail on main) · 39 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 2,253 of 2,891 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (855 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) none
Failing on main too (3, not counted against this PR)
agc_compute_pipeline_inspection
guest_mmap_executable
guest_socket_options_multicast
Validation IDs already present on main (4 IDs in 404 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (404 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alpha_to_coverage, agc_driver_array_layer_target_cpu, agc_driver_bc_block_copy, and 396 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (39)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_msaa_dcc_compared_import_2: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_4: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_msaa_dcc_compared_import_8: skipped, the device reports host imports that keep write protection, so the write-watch probe compares none of them
agc_driver_sample_order_grid_reference: skipped, the device does not keep depth drawn at custom sample locations at those locations, even after a transition that names them: the composite of uniform renders holds the guest's depth in 0 of 3005 sloped samples, so no path can be compared with it bit for bit
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_stencil_ref_export: skipped, the device lacks VK_EXT_shader_stencil_export
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_cpuflip_in_order: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_eop_main: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_fresh: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_labels: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SD
Reproduce
git fetch origin d4885317668608260ae4ee67e73b3aced9545d53 ba5b5330b37724bca4b99f30faab504653635a04
git checkout ba5b5330b37724bca4b99f30faab504653635a04 && git merge --no-ff d4885317668608260ae4ee67e73b3aced9545d53
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 32 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 4 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1

Automated 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.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@savvaniss heads-up: #6498 just merged and now conflicts with this PR in tests/execution/SampleMultisampleTarget.cpp and the AgcDriver debt doc. Could you merge main and keep both sides there (both test blocks and both debt lines)? After that we'll run fresh CI on the new head and the game gate.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 29, 2026
@hugodearaujo

Copy link
Copy Markdown
Contributor

A test on Assassin's Creed Mirage (PPSA13960) on current main. Linux, RX 9070 XT (RADV, Mesa 26.2.6), Release.

Build Result
main bb04a4f aborts at 86 s: [videoout] presentation failed: AGC graphics: a CPU read of 0x10dfd20000+0x1fe0000 overlaps multisampled color target 0x10e0720000+0x870000 (4 samples), whose samples are only on the host: guest access to it is not implemented (preceded by color target 0x10dfd20000 stays non-resident: ... a storage image access of 0x10dfd20000+0x1fe0000 overlaps multisampled color target ...)
main bb04a4f + this PR's head d488531 (clean merge) that stop is gone; it aborts at 79 s on another target: compute shader 0x1000810a00: AGC graphics: a cached image access of 0x10dc1d0000+0x480000 overlaps multisampled color target 0x10dc450000+0x480000 (4 samples), whose samples are only on the host: guest access to it is not implemented

So this PR removes the title's first multisampled-target stop on main; the next one is a cached image access over a different 4x target. I'm looking at that one separately. (#10049 alone didn't change the first stop, and it conflicts with this PR in MultisampleTarget.cpp.)

For the refresh oneandonlydean asked for: merging current main (66b8e90) into this head by hand, the only textual conflict was the debt entry #6498 added next to the one this PR rewrites, but the merge also needed range.hostOnly changed to range.use->hostOnly in HostOnlySampleRanges (after #12566) to build. With that, the Mirage result above is the same on 66b8e90.

AI-assisted: yes (Claude Code)

Keeps both sides of the debt doc: this branch's multisampled color
target entry and boykopovar#6498's shader buffer entry. The test file merges with
both sides' test blocks. HostOnlySampleRanges from boykopovar#12566 reads the
host-only flag from the range's TargetUse.

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

Copy link
Copy Markdown
Contributor Author

@oneandonlydean this is ready for CI and the game gate. I merged main b7488e1 into your merge as adc9673 (no rebase, no force-push), and that is the new head. It also merges cleanly with the later main a5939f1.

Tests on the RX 9070 XT, Windows 11, with ANYPS5_GPU=AMD, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation:

  • All 31 tests of the MSAA, FMASK, native-depth and sRGB-twin set pass on this head, with no VUID- line. That set includes agc_driver and agc_driver_native_fmask.
  • agc_driver_sample_multisample_target passes 3 of 3.
  • Every Vulkan device: and Selected GPU line names the RX 9070 XT.
  • check_conventions.py passes.
  • I did not rerun the main baseline or the single-part removals for this merge. The last ones were on 25 Oct, and this merge changes no logic of this PR apart from the HostOnlySampleRanges read.

AI-assisted: yes (Claude Code)

@savvaniss

Copy link
Copy Markdown
Contributor Author

@hugodearaujo thanks for the Mirage run and for tracing the second stop, and for the HostOnlySampleRanges fix, which is in adc9673.

The second stop is not in this PR's scope. Your trace settles what it is: the refused access is compute shader 0x1000810a00's own store-only clear over 0x200000 of the 4x target's 0x480000 bytes, and nothing else touches the range in between. That is a GPU write, while this PR's evidence is a CPU write or a bind of a depth or stencil plane, HTILE or color target. So the guard refuses there as this PR's rule says it should, and your second case (0x10b4c89700) has the same shape.

A store-only storage image would be a third kind of evidence, so I'd keep it for a PR of its own after this one lands. As you found, #10049's full-coverage rule wouldn't fire on these partial overlaps. Such a PR could call the ClaimMultisampleTargetMemory this PR adds, once it has proved the image is store-only: the store would then retire the target, and the next frame's draw would guard a new one. Since you're already looking at it, I'll leave it with you.

@JJenkx thanks for the Linux run on the RX 6650 XT, and for repeating the Bendy sequence. Your repeat is the useful part: the 16 MiB CPU read over the 8-sample target still stops there, so I'm not listing this PR as a fix for that stop. This PR only retires a target when there is evidence the title reused its memory. A read of a target with no such evidence still refuses, and publishing live samples is what #6987, #6989 and #6995 work toward.

AI-assisted: yes (Claude Code)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* docs unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants