Skip to content

fix(agc): import a native multisample D32S8 target over uneven planes once its first draw wrote every sample - #16363

Open
hugodearaujo wants to merge 4 commits into
boykopovar:mainfrom
hugodearaujo:fix/agc-prove-overwritten-depth-planes
Open

hugodearaujo wants to merge 4 commits into
boykopovar:mainfrom
hugodearaujo:fix/agc-prove-overwritten-depth-planes

Conversation

@hugodearaujo

@hugodearaujo hugodearaujo commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

What

Assassin's Creed Mirage (PPSA13960) reuses the memory of its 4-sample D32S8 depth target (no HTILE) every frame:

  1. A compute chain writes an R11G11B10F mip chain over both planes after the depth passes.
  2. On the next frame the depth target is bound again over those results, at that frame's dynamic resolution (1872x1056, 1772x996, 1720x968...).
  3. The import (prepareUniformPlaneSampling) finds planes that do not hold one value each and records native multisample depth plane without HTILE does not hold one depth value in [0, 1]. The target is still drawn.
  4. Later, the compute that reads mip 0 of the new chain at the same address throws that refusal. It does that through the depth owner lookup in DepthSurfaceTexture.

The first draw of each frame on that target is a full-screen triangle. It has depth test ALWAYS with depth writes on, and on both faces stencil compare ALWAYS, pass op REPLACE, reference 0 and write mask 0xff. DB_RENDER_CONTROL is 0x60 and the scissor is the target extent. On the console that draw overwrites every sample, so the guest bytes under the planes no longer matter. But the registers alone cannot show that the geometry covers every sample.

This change proves it after the draw instead:

  • Mark the candidate. DepthSurfaceViewWithStencilWriteIntent takes the stencil value that the draw writes to every sample it covers. Draw.cpp passes it only for depth ALWAYS with writes and no depth bounds, plus stencil ALWAYS/REPLACE with write mask 0xff and one reference on both faces. If such a draw creates an owner whose import was refused only because the planes are uneven, the owner starts with stencil ~reference in every sample. The refusal stays recorded.
  • Prove it after the draw. The proof runs at the owner's next use after that draw: its next attachment, a check in CheckCurrent, or a guest access over its planes in RequireNoResidentDepthRead_nid_no_patch. The driver syncs the recorder and stores pending storage results over the planes. Then it reads the stencil samples back, with the readback PublishToGuest uses (now a shared helper). If every sample holds the reference, every sample passed that draw's tests, so its depth was written too.
  • Import on success. If the planes are also unchanged since the import, the refusal is dropped. The owner gets its depth guards and its publication capture, like an owner over uniform planes (fix(agc): publish a written native multisample D32S8 owner without HTILE to its depth and stencil planes #15688). The padding keeps the guest bytes.
  • Refuse otherwise, as today. That covers a sample the draw did not reach (it still holds ~reference), another stencil value, guest writes, and storage results stored over the planes since the import.

The debt entry for D32S8 without HTILE now says which uneven planes are imported.

Cost: one proof per created owner, which is a recorder sync plus a readback of the stencil plane (7 MB at 1772x996x4).

Tested

Linux 7.2.11, AMD Radeon RX 9070 XT, RADV (Mesa 26.2.6), Release with tests.

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: fix(agc): publish a written native multisample D32S8 owner without HTILE to its depth and stencil planes #15688 (stacked on it)
  • Fixes: none
  • Unblocks: PPSA13960 together with the changes listed under Tested
  • GPUs tested: AMD Radeon RX 9070 XT, RADV Mesa 26.2.6, Linux 7.2.11
  • AI-assisted: yes

@JJenkx

JJenkx commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Correction (rerun): my first run rebuilt only the main test target, but the test pattern also matched agc_driver_native_depth_bda_clear (from agc_driver_native_depth_bda_clear_tests), which was left from an earlier build and so didn't include this PR. I reran with every matching executable rebuilt from a fresh merge onto main d60ebec0c2 (39c9c9da6b): same result, same counts, same single skip, no VUID- errors.

Windows check of head b61aa1e53b, merged onto main 2d2d60a959 (d441ed9450, clean merge).

Setup: Windows 11 Enterprise 22621, WinLibs GCC 15.2 UCRT, Release, Radeon RX 6700 XT, AMD driver 26.10.1. Run through the Windows test queue with ANYPS5_REQUIRE_VULKAN=1 and Vulkan validation layers 1.4.363. The hosted Windows check does not run these GPU tests on pull requests.

  • 18 of the 19 agc_driver_native_depth_* tests pass in 3 of 3 runs on the real device, including this PR's overwritten_planes and publication_without_htile. The Vulkan device was created in each run, and there were no VUID- errors.
  • agc_driver_native_depth_driver_queued_fill skips on every run: "host imports are compared here, not write-watched". That is the existing Windows condition, unrelated to this change.

AI-assisted: yes.

@TechLevelZero

Copy link
Copy Markdown
Contributor

✅ NVIDIA GPU test: passed

PR head b61aa1e merged onto main 5299939 · tested 2026-11-06 12:49 UTC · 26 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,980 passed · 5 failed (0 new, 4 also fail on main, 1 unstable on main) · 53 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 3,038 of 4,210 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 (981 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.2 s, ⚠️ agc_driver_image_sample_unnormalized 34.3 s
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 1 of the last 10 main runs
Failing on main too (4, not counted against this PR)
agc_driver_image_table
guest_mmap_executable
guest_socket_options_multicast
web_library_assets
Validation IDs already present on main (9 IDs in 588 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (588 tests): agc_driver, agc_driver_address_draw_space_reuse, agc_driver_address_passes, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, agc_driver_bc_block_copy, and 580 more
VUID-VkShaderModuleCreateInfo-pCode-08737 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08739 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-VkShaderModuleCreateInfo-pCode-08741 (14 tests): agc_driver_dpp16_operands, agc_driver_dpp8_pk_fmac, agc_driver_dx10_clamp, agc_driver_f16_fma_denormal_modes, agc_driver_f16_fma_invalid_nan, agc_driver_float16_fma_rounding, agc_driver_float16_modifiers, agc_driver_float16_native, and 6 more
VUID-vkCmdCopyImage-srcImage-01548 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-vkCmdCopyImage-srcImage-01551 (7 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_sampling, agc_driver_depth_storage_dispatch, agc_driver_depth_surface_from_storage, agc_driver_dispatch_move_depth
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (1 test): agc_driver_prepared_exports
VUID-VkPipelineShaderStageCreateInfo-module-parameter (1 test): agc_driver_float16_unused_nan_payloads
VUID-VkPipelineShaderStageCreateInfo-pSpecializationInfo-06849 (1 test): agc_driver_float16_unused_nan_payloads
Skipped (53)
agc_driver_gcn_display_present: skipped, no display: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
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_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_net_ctl_disconnected: 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_device_capability: 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_host_resolution_disabled: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_four_k: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_height_only: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_limit: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_malformed: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_host_resolution_width_only: 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_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_backtoback: 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_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_
Reproduce
git fetch origin b61aa1e53bbf5dfa128b06c82f8da7b2505bf194 5299939ecfa9e186e1eb457c6f4bd133bdb087c7
git checkout 5299939ecfa9e186e1eb457c6f4bd133bdb087c7 && git merge --no-ff b61aa1e53bbf5dfa128b06c82f8da7b2505bf194
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.

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* build 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