Skip to content

AGC: compute buffer read of a native 4-sample D32 depth target needs sample-preserving publication (Onimusha: Way of the Sword, PPSA27836) #6570

Open

Description

@simonbronk

What

With #6557, Onimusha: Way of the Sword (PPSA27836) passes the native multisample depth import of #6362 and stops a few commands later, during the first scene load:

compute shader 0x211200de00: AGC graphics: sample-preserving guest publication of native multisample depth is not implemented

The refusal is RequireNoResidentDepthRead_nid_no_patch (DepthSurface.cpp), reached from the guest read guard (RequireNoMultisampleTarget(..., "a CPU read")) while the compute dispatch snapshots a buffer.

Data (local diagnostic prints, not in any branch)

  • Depth target: 0x2000e60000, 128x64, 4 samples, VK_FORMAT_D32_SFLOAT, no stencil, HTILE 0x2000e80000 (pipe-aligned, ITERATE_256 off). Guarded ranges: depth 0x2000e60000+0x20000, HTILE 0x2000e80000+0x8000.
  • The read: 0x2000e60000+0x200000, starting at the depth base and covering both ranges. It is the first guest read of these ranges after the guard is created. Whether a draw wrote the target in between was not measured.
  • Earlier in the same run, 0x2000e60000 held a 960x1080 single-sample texture (format 22, tile mode 4), so the title reuses this memory for different surfaces.
  • Same stop in 3 of 3 runs. The shader address changes between runs (0x211200d000, 0x2111c16c00, 0x211200de00).

Build: main 4eb91ea + #5907 + #5841 + #5884 + #5915 + #6557, APS5_NO_HOST_IMPORT=1 (#6374), Windows 11 Pro 26200, NVIDIA GeForce RTX 5070 (617.42).

Existing work

No open PR implements this. #6498 (fixing #6480) adds refusals for buffer aliases of native MSAA targets and does not publish. @Amoenus's fork has MultisampleSwizzleEquations.hpp (Z_X equations for 1/2/4-byte elements and 2/4/8 fragments) and a color publication path on codex/integration-main03-msaa-publication. On that branch the depth refusal above is unchanged.

Question

@Amoenus, do you plan to upstream the multisample swizzle equations and the readback/retile path? Publishing native depth (host samples to Z_X memory, HTILE set to expanded) would build on them. I can write the depth part and test it on this title once the foundation is in main, or test your branch here if you prefer to do it yourself. @Gashrod1, this touches the native multisample depth surfaces your #6297, #6300 and #6423 work on.

Activity

  1. Amoenus commented on Oct 15, 2026

    @Amoenus
    Contributor

    @simonbronk Yes, I plan to extract the shared layout/equations and sample-preserving publication work into focused upstream PRs. Your report gives the depth consumer a concrete title and stop to validate against. Happy for you to take the depth implementation and title testing; I will focus on making the shared foundation reviewable on current main.

    The older branch you linked is preserved. A newer published checkpoint is fe0a86b6, on codex/integration-6060-floor290. Useful pieces at that exact revision:

    • Swizzle equations and CPU layout/Tile/Detile implementation: Z_X supports 1/2/4-byte elements with 2/4/8 fragments, including pipe/bank XOR within the implemented 16-pipe configuration.
    • Layout tests: address vectors, fragment/layer round trips, inverse equations, padding preservation and explicit refusals. These are CPU layout checks, not depth-publication hardware validation.
    • Color readback/publication path: ReadSamples / PublishGuest currently handle R_X color targets. The depth refusal remains; this checkpoint will not fix your reported stop. It is an integration reference, not a focused patch to cherry-pick wholesale.

    For the depth part, the remaining validation needs to cover distinct per-sample D32 values, the exact expanded HTILE encoding for this configuration, producer completion, and reuse/retirement of the previous surface. The 2 MiB buffer read also needs to publish the owned depth/HTILE ranges while preserving unrelated bytes. The layout helper preserves padding; the publication caller must deliberately handle any backing/padded samples it exposes. A resolve or removing the guard would not establish those semantics.

    I have not reproduced Onimusha here. Our color-path validation does not establish D32/HTILE correctness. Please keep the depth work coordinated here; you can inspect or adapt these pinned helpers now rather than waiting for the broad integration branch to land. I will link the focused foundation PRs here when ready.

    AI-assisted source inspection and reply.

    Focused foundations are available: #6588 provides the CPU R_X/Z_X layout/equations and retile helpers; #6601 provides the sampled-image reader, including per-sample D32 float bits. A combined validation commit is now pushed to our fork. The API and validation handoff has its exact composition and test scope. Native depth publication, HTILE and title testing remain the coordinated follow-up; neither foundation changes the reported refusal. AI-assisted: Codex.

  2. simonbronk commented on Oct 15, 2026

    @simonbronk
    ContributorAuthor

    @Amoenus thanks, that split works for me. I'll take the depth publication and the title testing, and use the helpers at fe0a86b as the layout reference. The PR will depend on your foundation PRs instead of copying them. Before writing it I'll measure the points you list: whether a draw writes the target before the read, distinct per-sample D32 values, the expanded HTILE word for this configuration, and how the 2 MiB read lines up with the previous surface at that address. Results will go here.

  3. Amoenus commented on Oct 15, 2026

    @Amoenus
    Contributor

    Submitted foundation status, 2026-10-17

    The shared color foundation #6973 → #6987 → #6989 → #6995 → #7002 is now rebased to main 21a0672ab2, pushed and clean against inspected main 47a266cf97; #6498 remains the buffer-guard prerequisite. The five PR descriptions record their exact new heads, repeated NVIDIA/AMD/llvmpipe tests and declared dependencies. #6480 maintains the consolidated stack, retained integration and scope. No whole-suite claim.

    This remains R_X color publication and caller wiring. Native depth ownership, Z_X guest commit and expanded HTILE publication are not implemented by these PRs; the Onimusha stop remains unvalidated here. The connected D32-to-Z_X evidence below retains its original pin. AI-assisted: Codex; author responsible.

    @simonbronk Updated shared-foundation composition: c4d6d3cc8, branch codex/test-msaa-reader-retile-main143, contains main 143f13055, #6588 head f1a733dd5, #6601 head 4b24b3ca0, and #6887's connected tests. #6452 is excluded; earlier pins remain preserved.

    New connected D32 coverage: #6887 now renders separate R32_UINT and D32_SFLOAT 129x67, 17-layer, 4x images. Actual readback feeds R_X/Z_X tile and detile with XOR 0/0x8100. Each mode checks 587,724 rendered elements, padding, source/outer sentinels and 28 pinned Addrlib fragment addresses. The depth case uses distinct finite D32 values below 1.0. This tests the D32-to-Z_X handoff directly without implementing a native owner or HTILE publisher.

    At this exact pin, all eight reader groups pass three fresh repetitions on each verified RTX 5080, AMD Radeon Graphics and Ubuntu 24.04 llvmpipe device: 72 passes, maximum0.51s. Windows GCC15.2r7 runs all 2x/4x/8x reader subcases; Linux GCC13.3 / Mesa25.2.8 LLVM20.1.2 runs 4x and excludes the reported 36 unavailable 2x/8x subcases. Both connected modes execute fully, no whole-test skips. A bounded D32 host-sample ordering control fails the intended assertion on NVIDIA; restoring this head passes. Selected targets rebuild on both OSes; previous full app/libs builds retain pin bfae8feb4. Conventions, registration, diff and selected Cppcheck/clang-tidy checks pass. GPU/OS/driver details and historical controls are in #6887.

    The sampled-array view must be in GENERAL; the caller still owns barriers, completion and resource lifetimes. This test connects helpers through an ordinary host buffer. It does not publish native depth ownership, stencil or expanded HTILE. In particular, the reader still rejects a VK_FORMAT_D32_SFLOAT_S8_UINT argument; written-stencil handling needs separate attention for the later title report. No Onimusha run or title unblock is claimed. Native publication remains your coordinated follow-up.

    AI-assisted: Codex. This handoff is edited in place.

    Earlier main896 handoff and retained reader validation

    @simonbronk Foundation handoff (source pins below): branch codex/validate-msaa-foundations-maine0, pinned at 185ef412f, now includes main 896e1b2b72f30f5196c1a600fd7aaf1165b9f44d.

    The helpers and fixtures remain byte-identical to #6588 head f506edb26 and #6601 head 4b24b3ca0; the composition also retains #6452's separate Windows test-alignment correction. No new production behavior is introduced, and the two submission PRs stay focused. The small foundation branch excludes our broader experimental integration.

    Fresh affected-path validation at 7275bcd02 on main e0e3ced1e: selected reader/layout targets and dependencies build on Windows 11 Enterprise 26200 / GCC 15.2r7 and Ubuntu 24.04 WSL2 / GCC 13.3. CPU layout passes three fresh processes per OS. All six reader modes pass three fresh processes on each of NVIDIA RTX 5080, AMD Radeon Graphics and Linux llvmpipe: 54 reader + 6 CPU passes, longest 0.51 s. Every reader process verifies its selected device. Windows runs 2x/4x/8x with no unavailable subcases; llvmpipe runs 4x and reports 36 unavailable 2x/8x subcases, excluded from coverage. No whole test skips or timeouts. All 32,768 distinct finite values in the 128x64 4x D32 case and the uint/depth pipeline transitions pass each repetition. Conventions and registration checks pass.

    The final 185ef412f differs from that tested pin only in .github/workflows/build.yml, adopting main's Linux CI wild-linker setup. Local source/build settings are unchanged, so the matrix was retained rather than repeated. These are local selected builds using our existing toolchains, not a wild-linker CI result or new full-application build. Earlier full builds, oracle comparisons and controls retain their original pins below. Windows GPU drivers: NVIDIA 610.62 / 32.0.16.1062 and AMD 32.0.21036.18; Linux Mesa 25.2.8 llvmpipe (LLVM 20.1.2), vendor 10005:0000.

    Independent reader follow-up: jotitadev's corrected-head retest confirms #6601 head 4b24b3ca0 on Ubuntu 26.04 / GCC 15.2, merged with main 0615cd2b8: all six modes pass 3/3 on Mesa 26.0.3 lavapipe (LLVM 21.1.8), running 4x/8x and reporting 2x unavailable; RTX 3070 driver 595.99.02 passes all six with 2x/4x/8x. This is the reviewer's reader validation, not another run of our combined branch or a connected depth-publication/title test. The requested Mesa 26.0.3 retest is complete.

    API/remaining scope: TextureDetiler::DispatchMultisampleReadback takes a sampled 2D-array view in GENERAL and emits layer/y/x/fragment elements. Its caller owns producer/readback barriers, completion and resource lifetime; #6588 supplies CPU retile/layout. Neither helper publishes guest ranges or expanded HTILE. The later written-stencil report also needs distinct attention: the current fixture tests D32 depth, not stencil extraction, and explicitly refuses a VK_FORMAT_D32_SFLOAT_S8_UINT argument. That is a confirmed helper/test boundary, not a newly reproduced title blocker. No Onimusha run, native-depth publication, stencil/HTILE correctness or title-unblock claim here.

    AI-assisted: Codex. This existing handoff is edited in place.

    Earlier main5d API handoff, full-build evidence and connected color example

    @simonbronk The shared foundations are proposed in #6588 (CPU R_X/Z_X layout and retile) and #6601 (sampled-image readback, including D32).

    TextureDetiler::DispatchMultisampleReadback takes VkFormat, a 2D-array sampled view in GENERAL, an output storage buffer, dimensions/layers/fragments and a firstElement/elements window. Output order is layer/y/x/fragment. It supports unsigned 1/2/4/8/16-byte element views and D32 float bits, preserving unused bytes in the final packed buffer word. The caller owns producer/readback barriers, completion and resource lifetimes. This reader does not require #5881 or storage usage on the source image.

    Combined branch ready for your work: codex/validate-msaa-foundations-main5d, now pinned at 82155cf97. It contains main 5d9dcc42d + #6601 head 4b24b3ca0 + the separate #6452 Windows test-alignment correction + #6588's unchanged layout implementation/fixture. Relative to the previous 6f91c1b6 pin, only the reader fixture changes: unsupported sample counts are reported while supported counts continue. The reader API and both production helpers are unchanged. The two submitted PRs remain focused and do not include #6452.

    On this exact combined pin, the full Windows app/libs build, conventions and test registration pass, as do five CPU-layout processes and 60 fresh reader processes (six modes x five repetitions x two GPUs). Physical selection is verified for every reader process: RTX 5080 and AMD Radeon integrated graphics. Reader cases cover 2/4/8 samples, two layers, partial windows, sentinels, forced 2D dispatch, descriptor reuse, and all 32,768 distinct finite D32 values in the 128x64 4x fixture. These exercise the two helpers separately; they do not test a connected guest-publication path. Independent layout-oracle comparisons, fault controls, broader regression matrices and static-analysis results are detailed in the respective PRs.

    Local Linux confirmation of this combined pin: Ubuntu 24.04 WSL2, GCC 13.3, Release, bundled dependencies/SPIRV-Tools ON. Both fixture targets and their dependencies build; the CPU layout test passes five fresh processes, and all six reader modes pass five fresh processes each on Mesa 25.2.8 lavapipe (LLVM 20.1.2, 256 bits). All 30 reader processes verify the software device. They run supported 4x and explicitly report unavailable 2x/8x subcases; no whole tests skip. Maximum of the 35 processes: 0.15 s. This is selected-target Linux coverage, not a full Linux app/suite build. The exact updated combined pin also passes the full Windows build/checks and the 65 Windows processes described above (maximum 0.44 s). The original-reader/fixed-reader lavapipe comparison is recorded in #6601.

    Your remaining depth work is resident-surface ownership/producer completion, publication of Z_X depth and expanded HTILE, range/padding preservation and reuse/retirement, followed by title validation. The native depth refusal remains. No Onimusha reproduction, HTILE correctness or title-unblock claim here. AI-assisted: Codex.

    Connected color-consumer example: integration commit eb3c113a6 wires the shared reader into the retained color readback/CPU-retile/publication path. The raw array view now declares sampled usage as well as storage usage. The device-feature-disabled fixture passes all 48 color configurations, 5/5 fresh processes on each NVIDIA/AMD GPU; the old consumer fails all ten controls. Full Windows builds and a 163-group matrix per GPU selector pass. This broader branch is an adaptation reference; use the small combined foundation branch above for the depth work. Source contracts for extended usage and view usage apply. Depth/HTILE/title status is unchanged. AI-assisted: Codex.

  4. simonbronk commented on Oct 16, 2026

    @simonbronk
    ContributorAuthor

    Measured on the title (main 5d9dcc4 + #5907 + #5884 + #5915 + #6557, APS5_NO_HOST_IMPORT=1, RTX 5070, 3 runs, local prints only):

    • Producer: between the native import and the failing read there is exactly one draw bound to the target. It has depthTest on with GREATER_OR_EQUAL, depthWrite off, no stencil test, no depth bounds and no color targets, rasterized at 4 samples. So nothing on the host writes the surface after the import; its samples are still the clear value the import took from HTILE.
    • Consumer: compute shader 0x2111838e00 (the address moves between runs), wave64, 128 threads. It reads the memory through a buffer V# in s[8:11]: six buffer_load_dwordx4 with idxen at offsets 0 to 0x50, so 96-byte records. It then buffer_store_dwords into the same V#, does a returning buffer_atomic_add on s[12:15] and an image_store. The snapshot of that buffer is the 2 MiB CPU read that hits the guard. The title is reusing the depth's memory as a structured buffer, not reading depth samples.

    So for this stop, the guest bytes are already exact: a draw that neither writes depth nor stencil leaves the native surface equal to what was imported. Publication is not needed here. Retiring the native surface on a guest access is enough, as long as the host never wrote it after the import. I plan a small PR for that:

    • track whether a native multisample depth surface was written on the host after its import (draws with depth or stencil writes, clears, copies, storage write-back);
    • on a guest read or write over an unwritten one, retire it and its guards instead of throwing;
    • keep the existing refusal for surfaces the host did write. That case still needs your foundations and the depth publication, which stays the follow-up here.

    One point I can't settle from a source yet: whether the DB leaves HTILE untouched for a depth-test-only draw. I'll look for it in Mesa/PAL before writing it down, and record it as technical debt if no source settles it.

    AI-assisted: yes (Claude Code).

  5. simonbronk commented on Oct 16, 2026

    @simonbronk
    ContributorAuthor

    #6727 implements the unwritten-surface case described above. A guest access over a native multisample depth surface that the host did not write after its import now retires the surface. A surface the host did write keeps the refusal. With it the title passes this stop and goes further into the first scene load, until depth export, shader coverage or ordered fragment execution is unsupported (register 0x203 = 0x00000045).

    This issue stays open for the written-surface case: publishing host samples to Z_X memory with expanded HTILE, on top of #6588 and #6601.

  6. simonbronk commented on Oct 16, 2026

    @simonbronk
    ContributorAuthor

    Update from PPSA27836. With #6727 and #6762 the title now hits the written-surface case of this issue:

    1. The DB_SHADER_CONTROL 0x45 draw writes the stencil of the native 4-sample D32 target: REPLACE_OP on depth pass, write mask 0xff.
    2. The next compute dispatch (0x211183a700) reads that memory.
    3. It throws sample-preserving guest publication of native multisample depth is not implemented.

    So retiring unwritten surfaces is not enough for this title; it needs the publication on top of #6588 and #6601. When either of them moves, I can test it on the title.

  7. Amoenus commented on Oct 16, 2026

    @Amoenus
    Contributor

    @simonbronk Shared-reader prototype, component-tested: commit daf5484e2, fork branch. Base d917ab2f3 is main 93e54cedd merged with exact #6601 head 4b24b3ca0; it excludes the broader publisher and #6588/#6887. Own diff: six files, +94/-19.

    The new overload explicitly selects the depth or stencil aspect of a sampled D32S8 array view. Depth emits D32 float bits; stencil emits one byte per fragment. The old format-only overload still refuses combined D32S8. Invalid aspect/format combinations throw. The caller retains responsibility for matching the view, GENERAL layout, sampled usage, barriers and completion. Native ownership, guest Z_X and HTILE publication are unchanged.

    Validation at this exact pin: application (relinker), libraries and reader target build on Windows 11 Enterprise26200 / GCC15.2r7 and Ubuntu24.04 WSL / GCC13.3. All seven reader groups pass three fresh repetitions per verified NVIDIA RTX5080 (32.0.16.1062), AMD Radeon Graphics (32.0.21036.18) and Mesa25.2.8 llvmpipe LLVM20.1.2: 63 passes, maximum0.48s. Windows runs2x/4x/8x; llvmpipe runs4x and excludes42 unavailable2x/8x subcases. No whole-test skips. The stencil case checks two layers, four spatial regions, varying fragment values, partial/packed tails, sentinels,65-dispatch reuse and stencil/depth/stencil pipeline reuse. Vulkan required; project shader cache disabled. A bounded post-completion byte-flip control fails the sample assertion; restoration passes. Conventions, registration, diff and selected Cppcheck/clang-tidy checks pass.

    Producer diagnostic retained separately

    The initial scissor-based producer disagrees with the expected region pattern on our llvmpipe. An unpacked comparison agrees with all680 packed results; both have86 differences from the expected spatial pattern. Moving the scissor boundaries moves the first mismatch. The final producer uses viewports with the same expected regions/values and passes; no reader code changed for that correction. Cause of the scissor-dependent result remains unconfirmed. No driver defect is asserted.

    Inactive-plane check: the main93 decoder clears stencil testing when the plane is absent, and requires depth testing before enabling depth writes. The surface key includes format and stencil address. Those predicates match our integration. Existing CPU/mock graphics cases pass once per OS at a428aba8f (Windows5.52s/Linux3.32s); two unavailable Linux heap-mirror checks are excluded. No inactive-plane defect is established, and this does not validate #6727 or a title run.

    Before an upstream PR: are you already supplying this extraction, and can you confirm the actual host VkFormat, stencil-plane address and dirty-mark call site at the later written-stencil stop? The latest report gives the stencil operation but not those details. Main93's native import accepts D16/D32; #6300 adds D32S8. That distinction matters before claiming this additional API is needed by the title. No new title run or unblock is claimed. AI-assisted: Codex; author responsible.

  8. simonbronk commented on Oct 17, 2026

    @simonbronk
    ContributorAuthor

    @Amoenus answers from a run on the title today. Tree: main 17cfb1f + #5915 + #6557 + #6727 + #6762, APS5_NO_HOST_IMPORT=1, RTX 5070, plus local prints only (not pushed).

    The written surface has no stencil plane. It is plain D32 depth:

    [diag-dsw] draw writes depth target 0x2003bc0000: depthTest 1 depthWrite 1 stencilTest 0 writeMask 0/0 ... samples 4
    [diag-dsw] note write: address 0x2003bc0000 stencilAddress 0x0 guest format 126 host format 126 samples 4 extent 128x64 htile 0x2003be0000 htileStencil 0 already 0
    [diag-dsw] refusal: guard 0x2003bc0000+0x20000 access 0x2003ba0000+0x200000
    
    • Host VkFormat 126 (VK_FORMAT_D32_SFLOAT), stencilAddress 0, HTILE without stencil.
    • The dirty mark comes from the draw path (Draw.cpp: writesDepthTarget → NoteDepthSurfaceWrite), from a draw with depth test and depth write on and no stencil test.
    • The read that is refused is a 2 MiB range starting 128 KiB below the target and covering all of it (the compute's buffer V#).

    So the title doesn't need your D32S8 stencil reader. It needs Z_X depth publication (D32, 4 samples, 128x64) and HTILE expansion. My earlier comment, which tied this stop to the DB_SHADER_CONTROL 0x45 stencil draw, was wrong. That draw targets another depth surface (its DB_STENCIL_INFO has a stencil format), and it isn't the producer here. Sorry for the noise.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions