Skip to content

fix(agc): publish a reused native multisample depth target with the guest's undecided bytes, then retire its owner - #15941

Draft
EagleBearerYS wants to merge 4 commits into
boykopovar:mainfrom
EagleBearerYS:fix/agc-native-depth-reuse-publication
Draft

EagleBearerYS wants to merge 4 commits into
boykopovar:mainfrom
EagleBearerYS:fix/agc-native-depth-reuse-publication

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

What

Since #9533, a multisampled D32S8 target whose stencil has its own plane and whose HTILE is depth-only is imported as a native target. #9533 was merged together with #9717, in a4b76b2c0a.

Four titles that ran before that merge stop on main. JJenkx bisected each one to a4b76b2c0a: Curved Space (PPSA02817, #8691), NINJA GAIDEN: Ragebound (PPSA28002, #8671), Blue Prince (PPSA25009) and New Joe & Mac: Caveman Ninja (PPSA02801, #9091). On main they stop with:

AGC graphics: native multisample depth publication cannot distinguish an unwritten byte from a same-value guest store

What reaches the publication. JJenkx traced it in all four (#8671). Each title reuses the memory of a host-written native target for something new:

  • draw input, in NINJA GAIDEN and Blue Prince;
  • a colour target and a storage texture, in New Joe & Mac;
  • another depth target, in Curved Space.

Why it stops. Building the new resource publishes the old target's planes to the guest (#13021).

  • Guest writes are tracked per 4 KiB page. Once the title has stored into a page the host drew, every byte of that page that still holds its snapshot value is undecided, and the publication refuses.
  • Curved Space and New Joe & Mac also meet a second refusal: a native multisample depth attachment over the planes of a host-written owner of another identity is not implemented.

The change:

  • A cause on each depth read. It's DepthReadCause::Access, the default, or DepthReadCause::Reuse.
    • Reuse marks resources built over the memory: Draw.cpp's vertex and index buffer reads; the two storage-image accesses in ShaderResources.cpp, which also cover the colour-target lookup from refreshResidentTarget; and a new depth surface's three planes.
    • Everything else stays Access: CPU reads, DMA copies, texture reads, shader buffer accesses, dispatch-written ranges and the native sampling path.
  • The undecided bytes. Where main refuses an undecided byte, a Reuse publication takes the guest's byte. Every byte the publication can decide is decided as before:
    • the host's bytes on pages the guest didn't write;
    • the guest's bytes where it changed them;
    • bytes covered by driver stores, as recorded.
  • Retiring the old owner. After a Reuse publication commits, the old owner is retired: its guards and its owner entries are removed. It is then inactive and no longer host-written, so releaseRetired frees it at the next depth-surface creation (fix(agc): free native multisampled depth surfaces after their guest release #15300).
    • A later bind of its identity imports afresh.
    • A new depth attachment over its planes no longer meets the second refusal.
    • An Access publication leaves the owner resident, as before.
  • The AGC debt entry for the publication now says which accesses take the guest's undecided bytes, and that those bytes are inferred.

Why the guest's byte. Before #9533 these targets were not native, so a resource built over their memory read the guest's bytes everywhere.

What isn't proven. The untouched bytes on a page the title wrote are inferred, not proven. The write tracking can't tell a byte the title never touched from one it stored with the same value.

  • Exact store ranges for guarded planes would decide them. That's the exact follow-up, and it's out of scope here.
  • CPU reads and DMA copies keep refusing.

Tested

Linux, on a cloud machine: Ubuntu 24.04, GCC 13.3, CI's configure flags, lavapipe (Mesa 25.2.8, LLVM 20.1.2). The tests ran as an unprivileged user with ANYPS5_REQUIRE_VULKAN=1 GALLIUM_OVERRIDE_CPU_CAPS=avx.

New cases in agc_driver_native_depth_publication (CheckReusePublication):

  • The fixture: a 4-sample D32S8 target with a separate stencil plane and a depth-only HTILE. The host draws depth and stencil, then one CPU store leaves the rest of the stencil plane's first page undecided.

  • Red on main acf7ac208a, green here. On main all four fail with the refusal above.

    • (0) a vertex buffer read over the page, and (1) an index buffer read;
    • (2) a storage-image lookup, the colour-target path;
    • (3) a new depth target of another identity over the planes.

    The checks: the guest's bytes on the undecided page, the host's bytes elsewhere in the plane, every depth sample, and the old owner retired. A rebind of its identity takes a fresh import. In (3) the new surface imports.

  • The same on both:

    • (4) a CPU read and (5) a DMA copy still refuse, and keep the owner and the guest's bytes;
    • (6) a rebind of the same identity publishes nothing;
    • (7) a shader buffer access and (8) a texture read, with nothing undecided, publish the host's bytes, keep the owner and retire nothing.

The existing tests, 49 in all: the native depth, write-tracking, guest memory and multisample tests (ctest -R 'native_depth|native_multisample|depth_publication|write_track|guest_memory|multisample' --repeat until-fail:3).

  • On main and here: 47 pass and 2 skip, ×3. The skips are agc_driver_multisample_centered_lines and agc_driver_native_depth_bda_clear, which skip themselves on lavapipe.
  • No existing test was changed.

Mutations:

  • Making every publication Reuse fails (4)-(8).
  • Dropping the retirement fails (0)-(3). (3) then stops at the second refusal.

Checks: tools/check_test_registration.py build and tools/check_conventions.py --base origin/main pass.

The red/green is from the cloud run (lavapipe, CI flags). The GitHub ×10 runs the branch only, because main lacks DepthReadCause.

On today's main: the four commits were rebased onto 9ab6a281ea with the same lines. GitHub's runners, ×10 on that tree (run), the same tests: on Linux, 52 tests, 50 pass and 2 skip; on Windows, 53 tests, 8 pass and 45 skip. The libc and libkernel suites pass on both.

With #15300, #15301 and #15855: the branch merged with main 9f35c937da and #15855 (since merged), the same tests, ×10 on GitHub's runners (run): on Linux, 52 tests, 50 pass and 2 skip; on Windows, 53 tests, 8 pass and 45 skip. The libc and libkernel suites pass on both.

GitHub's Linux and Windows runners, ×10, on acf7ac208a (run). The tests matching native_depth|write_track|guest_memory|multisample:

  • Linux: 49 tests, 47 pass and 2 skip, ×10. The skips are the same two lavapipe ones. agc_driver_native_depth_publication passed 10 of 10.
  • Windows: 50 tests, 8 pass and 42 skip, ×10.
    • The runner has no Vulkan device, so the AGC driver tests skip themselves, the new cases included.
    • The five agc_write_tracking tests and three guest_memory tests run and pass.
  • Both: the libc and libkernel suites pass.

Validation layers (CONTRIBUTING's AGC rule; run). VK_LAYER_KHRONOS_validation ran on GitHub's Linux runner, with main's tree and this branch each running the 35 tests matching native_depth|multisample.

  • Each had 33 passes and 2 skips. The layer loaded in both.
  • The only VUID is VUID-VkDeviceCreateInfo-pNext-pNext, on main and here. This change adds none.
  • llvmpipe (LLVM 20.1.2), Mesa 25.2.8, Ubuntu 24.04.5.

The titles, run by JJenkx on this head merged onto main c4469b60da: Linux 7.2.11, AMD Radeon RX 6650 XT (RADV, Mesa 26.2.6), Release, APS5_PLAYGO_UNLISTED_CHUNKS=installed, 125 s with Cross every 15 s from 30 s. On that main each title stops with the refusal above. With this PR each one gets past it and runs to the scheduled close, exit 0:

  • NINJA GAIDEN: Ragebound (PPSA28002) (#8671): stops at 50 s on main. Here it runs 122 s, and the story intro plays at 60 s.
  • Curved Space (PPSA02817) (#8691): stops at 18 s on main. Here it runs 121 s, and the "Keep Going?" campaign screen is rendered at 60 s.
  • New Joe & Mac: Caveman Ninja (PPSA02801) (#9091): stops at 8 s on main. Here it runs 123 s, and at 60 s the "Classic Modes" menu, opened with Cross, is shown.

AMD Radeon RX 570 (RADV POLARIS10, GFX8), run by ryanc2112 (comment): this head merged onto main 7ad9749ed0, Mesa 26.2.5, Release, with VK_LAYER_KHRONOS_validation.

Pending: JJenkx's run of Blue Prince (PPSA25009) on this branch: how far it gets, any new stop line, and whether its intro renders. Blue Prince is the title whose intro renders only with the guest's byte (above), so its run checks the byte this PR takes. This PR stays a draft until it's in.

Overlap

Checklist

@ryanc2112

Copy link
Copy Markdown
Contributor

A GFX8 data point: AMD Radeon RX 570 (RADV POLARIS10), Mesa 26.2.5, SteamOS 3.8.28 (Arch distrobox), GCC 16.2.1, Release. Head 7d377e40ba merged onto main 7ad9749ed0 (clean merge), with ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation and ctest -V --no-tests=error. No log reports was not found but was requested by env var VK_INSTANCE_LAYERS.

On GFX8, 11 of the 17 agc_driver_native_depth_* tests fail on main because the sample shadow lacks depth-attachment usage. That includes agc_driver_native_depth_publication, which fails with native D32S8 publication lost live samples, HTILE, padding or guest writes. #15750 fixes it, so I also ran this PR on top of #15750 to let the new CheckReusePublication cases run here. The two merge together cleanly.

agc_driver_native_depth_* (17 tests), 3 runs each:

Result VUID- errors
main 6 pass, 11 fail (the GFX8 failures #15750 fixes) 0
This PR the same 6 pass and 11 fail 0
main + #15750 17 of 17 pass 0
main + #15750 + this PR 17 of 17 pass, publication included 0

The 1,327 AGC, shader, VideoOut and Vulkan tests, this PR on main, once each:

AI-assisted: yes (Claude Code)

@EagleBearerYS

Copy link
Copy Markdown
Contributor Author

@JJenkx thank you for the three title runs, and @ryanc2112 for the RX 570 run. I've added both to the description.

  • NINJA GAIDEN: Ragebound (PPSA28002), Curved Space (PPSA02817) and New Joe & Mac: Caveman Ninja (PPSA02801) stop at the publication refusal on main c4469b60da, at 50 s, 18 s and 8 s. With this PR each one runs to the scheduled close of the 125 s run and exits 0.
  • On the RX 570, this PR on top of fix(agc): give a multisampled depth-stencil sample shadow attachment usage #15750 passes all 17 agc_driver_native_depth_* tests, the new reuse cases included, with no VUID- errors, and the wider AGC, shader, VideoOut and Vulkan set matches main.

The PR stays a draft until Blue Prince (PPSA25009) is in, since its intro is the case that needs the guest's byte.

@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Nov 5, 2026
@AlfaRojo

AlfaRojo commented Nov 6, 2026

Copy link
Copy Markdown

Windows check of head 7d377e40ba, merged onto main 3cb1cd29ec (cfcaf07ecf, clean merge), on a discrete NVIDIA and an integrated AMD GPU, since the PR's own runs were on lavapipe.

Setup: Windows 11 Pro (10.0.26200), WinLibs GCC 15.2.0 r7 UCRT, Release, ANYPS5_AGC_EXECUTION_TESTS including NativeMultisampleDepth, ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1, ANYPS5_GPU selecting the device (confirmed by Selected GPU); no validation layer on this machine.

GPU agc_driver_native_depth_publication ctest -R 'native_depth|native_multisample' (18 tests)
NVIDIA GeForce RTX 4070 (10de:2786, driver 32.0.16.1692) 3 of 3 pass 17 pass, 1 skip
AMD Radeon Graphics 1002:13c0 (RDNA 2 integrated, driver 32.0.21043.5001) 3 of 3 pass 17 pass, 1 skip

The skip is the pre-existing skipped native HTILE write-coverage assertion: the write tracker does not cover the imported metadata range in CheckDepthTarget, which main prints on these GPUs too. No red arm: the new cases use DepthReadCause from this PR's headers, so the test does not build against main's code. No title run.

AI-assisted: yes.

This branch has not been deployed

No deployments
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.

3 participants