Skip to content

fix(agc): store and read DCC keys over the console's metadata extent - #788

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
Celegans12:feat/videoout-dcc-visible-keys
Oct 8, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
Celegans12:feat/videoout-dcc-visible-keys

Conversation

@Celegans12

@Celegans12 Celegans12 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What

@merwie's PPSA12544 report on #648 (1920x1080: 33552 0xff, 1008 0x00 of 34560 keys, first differing 32784) comes from the extent of the driver's key stores, not from key order or padding (my row-major reading here was wrong).

  • Extent: gfx10 SW_64KB_R_X 4-byte DCC is ceil(W/512) * ceil(H/512) * 4096 bytes (addrlib; PPSA26344's 0x161d760; GTA V's 0x6000 at 1280x720), 49152 at 1920x1080. MarkDccUncompressed stored 34560.
  • Reproduction: PPSA26344 registers a cs_dcc_retile_32bpp copy of its render DCC; that retile (addrlib patterns 25 to 2) of a 34560-key store gives merwie's numbers.
  • Change: DccKeyCount gives the extent for single-level, single-layer 4-byte SW_64KB_R_X surfaces. Color target and storage image key stores cover it for pipe-aligned DCC (CB_COLOR_ATTRIB3 bit 30, T# word 6 bit 19; libSceAgc's size query returns it, and at 128x128, 1920x1080 and 3840x2160 its CB keys end within 240 bytes of it), throwing where it is not writable; unaligned DCC keeps bytes/256. DisplayBufferKeys reads the extent and presents only uniform keys.
  • Pending image: compared with the flip's keys over that extent. Versus feat(agc): present DCC display buffers whose keys are uncompressed or fast-cleared #648, only a display whose keys past the first bytes/256 differ or are unreadable now throws; pipe-aligned driver stores leave no differing tail, unaligned ones can. Texture and CB key reads keep bytes/256 (TechnicalDebt).
  • feat(agc): multisampled color and depth targets with CB resolves #419 edits DecodeColorBuffer and RunColorMetadataPass next to code changed here and refuses multisampled DCC.

Tested

  • Windows 11, MinGW-w64 GCC 15.2.0, based on main c73ee44: configure -DBUILD_TESTING=ON, full build, --target libs, ctest:
  • 403/403 tests pass (ctest --output-on-failure)
  • Same build on lavapipe (Mesa 26.2.4 llvmpipe, 8-wide subgroups, VK_DRIVER_FILES): 402/402 pass; agc_driver_flat_store is left out there because it takes several minutes on Windows llvmpipe
  • Extended agc_display_buffer_dcc and agc_driver_graphics (do not build on main): merwie's keys reproduce, the driver's store, retiled, presents, and the flip reads keys past 34560; pipe-aligned stores cover the extent (or throw if not writable), unaligned ones bytes/256. Pipe-aligned rows fail on the old extent, unaligned ones without the gate.
  • By hand (not part of ctest): agc_driver_recorder_tests, video_out_flip_tests dcc.
  • Not tested: PPSA12544 (@merwie offered to test), PPSA26344 (no flip yet), Linux (CI), console behaviour.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Thanks. The 8x8-block mapping checks out: the low byte of tileOffset depends only on x & 7 and y & 7, so each block fills exactly one 256-byte key block. Cross-checking the runs against DecodeDisplayBuffer's per-pixel keys is a good oracle. Two questions:

  1. As the description says, PPSA12544 still stops (key 32784 covers presented pixels 1032 to 1039). merwie's numbers fit an ordering mismatch: no 0000 key in the first 32400 keys, and 32400 is exactly the number of presented 8x8 blocks. That suggests the console orders keys differently from the driver's linear model. Is the plan to model that order next, and does this PR still help once it's modelled?
  2. With a pending image on the same metadata (VulkanDevice.cpp:1845-1850), the whole-surface ProvedClearKeys is compared with keys read only over the presented runs. So a surface with mixed padding still throws there, and a case that presented under feat(agc): present DCC display buffers whose keys are uncompressed or fast-cleared #648 can now throw. Could the comparison use the same runs?

@Celegans12

Copy link
Copy Markdown
Contributor Author

Thanks, both points are right.

  1. merwie's numbers fit a row-major order of 8x8 blocks exactly.

    • 1920x1152 is 240 x 144 blocks = 34560 keys. The first 240 x 135 = 32400 keys are block rows 0..134, which are exactly visible rows 0..1079.
    • The first 0000 key, 32784, is block row 136 and column 144 (y 1088..1095, x 1152..1159), inside the padding rows. The zeros end near block row 140.
    • So the keys the title produces seem to follow the console's metadata order, not the driver's model, where key k is the k-th 256-byte block of the 64 KiB tiled surface. Under the driver's model, key 32784 lands on presented pixels, as the description says.

    The plan is to model that order next. I'd derive it from the gfx10 DCC meta addressing for this swizzle, rather than assume row-major from one title's histogram. I'll also check which writers produce the display keys: the driver's own key stores use its model, so both orders cannot meet on the same buffer unchecked.

    The point of this PR survives that change. In a row-major order the padding blocks are still there: 240 x 9 blocks below row 135, plus any columns past width. They still have to be ignored. Only the key-to-pixel mapping changes. I'll rework fix(agc): store and read DCC keys over the console's metadata extent #788 on that basis rather than have the tile-order mapping land.

  2. Yes. The pending-image comparison will use the same presented runs as the classification. A surface with mixed padding then presents there too, and no case that presented under feat(agc): present DCC display buffers whose keys are uncompressed or fast-cleared #648 throws.

I'll convert this to a draft until the reworked version is up.

@Celegans12
Celegans12 marked this pull request as draft October 6, 2026 01:36
@Celegans12

Copy link
Copy Markdown
Contributor Author

Correction to my earlier reply: the row-major reading was wrong, and neither order nor padding is the cause.

  • No gfx10 order is row-major. For this surface (SW_64KB_R_X, 4 bytes per element, 16 pipes; tileOffset matches addrlib's GFX10_SW_64K_R_X_1xaa pattern), addrlib places keys in 4 KiB meta blocks of 512x512 pixels. The blocks run row-major with a pitch of ceil(W/512), and keys are swizzled inside each block. So the metadata is ceil(W/512) * ceil(H/512) * 4096 bytes: 49152 for 1920x1080, against the 34560 the driver models. The title's own libSceAgc DCC info function (PPSA26344 eboot 0x161d760) computes the same size.
  • What fits merwie's numbers. Titles retile the render DCC, which is pipe-aligned, into a displayable DCC with libSceAgc's retile shader, and register that copy with VideoOut. In PPSA26344, the helper at 0x1604ef0 is called at 0xcd21f6 and 0xcd41c1, and [rt+0xa8] is the metadata passed to sceVideoOutRegisterBuffers2 at 0xcd004a.
    • The driver's MarkDccUncompressed stores 0xff only over bytes/256 keys of the render DCC, so the rest of the 49152 bytes stays 0.
    • Simulating that retile with addrlib's patterns 25 and 2 gives exactly 33552 x 0xff, 1008 x 0x00 and a first differing key of 32784 over the 34560 keys the flip reads. Row-major, an aligned-to-aligned copy, and a copy limited to the surface's rows do not reproduce them.

So the fix is to store and read keys over the console's metadata size for these surfaces, and to classify all of them. Nothing about padding is needed. I'll rework this PR that way, with a test that reproduces the three numbers, and the pending-image comparison will use the same range. The DCC addressing derivation goes in the PR text.

@github-actions github-actions Bot added docs prx agc libSceAgc* labels Oct 6, 2026
@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch from 40729c8 to 173518f Compare October 6, 2026 04:11
@Celegans12 Celegans12 changed the title feat(agc): ignore the DCC keys of display buffer padding at a flip fix(agc): store and read DCC keys over the console's metadata extent Oct 6, 2026
@Celegans12
Celegans12 marked this pull request as ready for review October 6, 2026 04:11
@Celegans12

Copy link
Copy Markdown
Contributor Author

Reworked as described above, and ready for review again. The new title is "store and read DCC keys over the console's metadata extent". It is one commit on main 109a328, and the padding runs are gone.

  • Extent: for single-level, single-layer 4-byte SW_64KB_R_X surfaces, DccKeyCount is ceil(W/512) * ceil(H/512) * 4096 bytes. Color target and storage image key stores cover that extent, and the flip classifies all of it.
  • Your question 2: the pending-image check now compares against the keys the flip read over the same extent, and an image on the same metadata presents as on main.
  • Test: agc_display_buffer_dcc reproduces merwie's numbers with a test-side copy of addrlib's patterns 25 and 2 and a retile: 33552 / 1008 / first differing key 32784 over 34560 keys. The driver's store over the extent, retiled, presents; with the old extent the same test fails with exactly those numbers.
  • CI: Windows ctest 352/352; lavapipe 351/351.

@merwie, if you can run PPSA12544 on this version, it should present without the padding experiment. If it still throws, the message now names the first differing key and its pixels in the displayable order, and APS5_TRACE_DCC_KEYS=1 prints the run lengths.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Thanks for the rework. Both earlier points are covered: the padding runs are gone, and the pending-image check now reads the display keys over the same extent as the flip. Reproducing merwie's 33552 / 1008 / 32784 numbers from a retile in agc_display_buffer_dcc is a convincing test.

One question about the store side. DccKeyCount applies the 512x512-per-4 KiB extent to every single-level, single-layer 4-byte SW_64KB_R_X color target and storage image, not only to display buffers. So MarkDccUncompressed now writes past one key per 256 bytes on render targets as well:

  • 1920x1080: 49152 bytes instead of 34560.
  • 3840x2160: 163840 instead of 130560.
  • 128x128 (the new metadataPassTests case): 4096 instead of 256.

StoredKeyCount only checks that the larger range is writable, so if a title's render DCC is smaller than that, the extra bytes land in whatever follows it rather than throwing. The 49152 figure comes from the displayable copy and the PPSA26344 DCC info function at 1080p. Does the same size hold for the pipe-aligned render metadata and for small targets? If it's only confirmed for the displayable case, could the larger store be limited to surfaces whose metadata is known to have that size (or throw when it can't be confirmed), with the rest kept at one key per 256 bytes?

I'll run this through PPSA21564 before it lands, since its color target and storage image write-backs take the new store path.

@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch from 173518f to 8710433 Compare October 6, 2026 06:54
@Celegans12

Copy link
Copy Markdown
Contributor Author

Good question. I checked the render (pipe-aligned) metadata and small targets. The size holds for them too, and one key per 256 bytes does not describe that metadata.

What the CB addresses. For the render DCC (16 pipes, non-RB+, GFX10_DCC_64K_R_X_SW_PATTERN[25]), the pipe bits sit at key-address bits 8..11. So at the sizes below, even a 128x128 target's keys are spread over its whole 4 KiB meta block. These are the keys of the surface's own 8x8 blocks (x < W, y < H):

Surface Extent bytes/256 CB keys Highest key + 1 CB keys below bytes/256
128x128 4096 256 256 3856 16
256x256 4096 1024 1024 3904 256
1280x720 24576 15360 14400 24382 11488
1920x1080 49152 34560 32400 48968 30916
3840x2160 163840 130560 129600 163616 122624
259x137 4096 1536 594 3915 224
  • At these sizes, over all 16 pipe XORs, the highest key + 1 is never more than 240 bytes below the extent (the worst case is 128x128).
  • Not at every size: below about 64x64, and at some sizes whose last meta block holds only a few pixels of the surface, the CB's keys end up to 4095 bytes earlier (32x32: 772 of 4096; 520x520: 12289 of 16384; 1032x1032: 32769 of 36864). There the size rests on libSceAgc's query below (always the extent, at least 4096), not on the CB.
  • So the console's own CB already writes into the last 4 KiB meta block. At these sizes, a render DCC much smaller than the extent would be overwritten on hardware.
  • The display (unaligned, pattern 2) copy reaches less far: 256 for 128x128, 46206 for 1920x1080.

How libSceAgc sizes it (PPSA26344 eboot):

  • The metadata size query 0x1611dc0 (component 3, at 0x1611f64) returns the DCC info result (0x161d760): size ceil(W/512)*ceil(H/512)*4096 per slice and alignment 4096. The meta block is 4096 bytes for swizzle 27 on both branches (0x161d7ca, 0x161d98d), so the minimum is 4096, also for 128x128.
  • The render-target initializer 0x1611770 checks only the DCC address alignment against that (0x1611a18-0x1611a45, error 0x8a6c0002). It always sets DCC_PIPE_ALIGNED (or 0x44000000, 0x1611c0e).
  • The title's render-target builder (0xfe3100) allocates the CB DCC with exactly that size and alignment (0xfe3f5e, then 0xfe4198). It allocates the retile destination [rt+0xa8] the same way (0xfe4353), then memsets both to 0 over that size (0xfe4289-0xfe42ba).
  • libSceAgc's own writers also cover the whole extent:
    • the DCC clear 0x1608380 fills slices * sliceSize;
    • the title's DCC-enable path (0xfe4d40) fills 0xffffffff over the same size (size query at 0xfe4e60, fill at 0xfe4ea3);
    • the retile helper (0x1604ef0) reads and writes the whole size.
  • GTA V fills 0x6000 bytes, the 1280x720 extent (fix(agc): make DCC key fills clear what reads through the surface's keys #163).

Why not store only the CB's keys. I simulated the retile at 1920x1080 with the padding left at the allocation's 0x00:

  • With the store over the extent, the display copy is all 0xff and presents.
  • With only the 32400 CB keys stored, the copy has 16752 0x00 keys, the first at display key 13568 (pixels 1920..1927 x 0..7, the first padding column). The flip throws, and main's prefix check throws too (3392 zeros). The texture readers' bytes/256 prefix of the render DCC also turns mixed (3644 padding keys).
  • So the CB's own keys are not a safe store range.

Proposal.

  • Keep the store over the extent, but only where the DCC is pipe-aligned: CB_COLOR_ATTRIB3.DCC_PIPE_ALIGNED (bit 30) for color targets, and META_PIPE_ALIGNED (T# word 6 bit 19) for storage images. That is where the hardware's own addressing confirms the size for most sizes; libSceAgc's query covers the rest. merwie's numbers reproduce only with a pipe-aligned render DCC, so PPSA12544 stays covered.
  • Unaligned DCC keeps one key per 256 bytes; an extent store over metadata that is not writable still throws.
  • The cost: a display the driver writes with unaligned DCC keeps its keys past bytes/256 as they were (at 1920x1080 its CB writes keys up to 46205), so its flip throws if they differ. libSceAgc's render-target initializer always sets the bit, so that takes title-built CB registers or an unaligned T#.
  • I would not gate on a recorded key fill. PPSA26344 initialises its DCC with a CPU memset and a compute fill that Driver::fillBuffer does not match, so the store would stay at bytes/256, and merwie's case would come back.

What changed in the PR. I amended the commit that way (now on main 481c85e):

  • ColorTarget and GuestTextureResource keep the bit as dccPipeAligned. The color target and storage image key stores cover the extent only when it is set, and one key per 256 bytes otherwise. DisplayBufferKeys is unchanged.
  • New test rows, each for pipe-aligned and unaligned DCC: DccKeyCount of descriptors from 128x128 to 3840x2160, a 128x128 fast clear eliminate, and a decoded 1920x1080 T# store (49152 or 34560 keys). An unaligned store over metadata smaller than the extent stores 34560 keys and does not throw.
  • Without the gate the unaligned rows fail. merwie's PPSA12544 reproduction still presents.

@github-actions

github-actions Bot commented Oct 6, 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
#935 core/libs/prx/libSceAgcDriver/tests/GuestTextureResource.cpp L6
#1051 docs/dev/TechnicalDebt.md L245-246
#1065 docs/dev/TechnicalDebt.md L245-246
#1072 core/libs/prx/libSceAgcDriver/Graphics/include/State.hpp L43
core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L97-102
docs/dev/TechnicalDebt.md L245-246
#1073 docs/dev/TechnicalDebt.md L245-246
#1077 core/libs/prx/libSceAgcDriver/Graphics/src/Draw.cpp L43-66
docs/dev/TechnicalDebt.md L245-246
#1122 docs/dev/TechnicalDebt.md L246

@adriadam10

Copy link
Copy Markdown
Collaborator

@oneandonlydean ready from our side; you said you'd run it through PPSA21564 before it lands, so the merge is yours once that's done.

@ps54all ps54all mentioned this pull request Oct 7, 2026
9 tasks done
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 7, 2026
@ps54all

ps54all commented Oct 7, 2026

Copy link
Copy Markdown

A PPSA12544 data point for this head (8710433f), since the description starts from merwie's Silksong numbers.

Windows 11, Intel i9-10850K, NVIDIA RTX 3080. PPSA12544 v1.0.30021 on 0d3c1eec plus this PR and merwie's Silksong stack (#760, #846, #842, #844, #839, #904), #903 and a few export PRs. Two runs without any local workaround for Mixed display keys (40 s and 90 s) reach the main menu at 60 FPS with no reads Mixed DCC keys stop. The presents come from the resident image ([flip] ... from the resident image, through guest memory: 0), so the display-buffer key read in DisplayBufferKeys is not reached in these runs.

AI-assisted (Claude Code) testing and write-up.

@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch from 8710433 to f6d6675 Compare October 7, 2026 22:02
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 7, 2026
@Celegans12

Copy link
Copy Markdown
Contributor Author

Rebased onto main (f6d6675) after #935 and #903. The #935 conflict was only the test includes. There's one line beyond the rebase, so you know before the regression run: #903 added a key store at the first write in StorageTexture::MarkDirty, and it still wrote one key per 256 bytes. On a pipe-aligned 1920x1080 surface that would cover only 34,560 of the 49,152 keys and leave the stale tail this PR removes, so that call now uses DccKeyCount(descriptor, guestBytes) like the other three storage-image stores. #903's own test uses DCC that isn't pipe-aligned, so it's unaffected, and no test pins that particular line yet. CI here: 403/403, lavapipe 402/402.

@Celegans12

Copy link
Copy Markdown
Contributor Author

The windows failure is guest_raise_exception (libkernel) hitting its 30 s timeout, the same flaky timeout #1267 got; it passes standalone in about 7 s here. A re-run of the windows job should clear it.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch 2 times, most recently from 4765284 to e3406c2 Compare October 8, 2026 06:54
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 8, 2026
@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch from e3406c2 to 766e948 Compare October 8, 2026 07:19
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 8, 2026
@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch from 766e948 to 0165070 Compare October 8, 2026 08:04
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 8, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Tested 0165070 on main 73ac9ef: ctest 433/433, and the full game check passes the same with and without this (no new skipped draws, no visual differences). I couldn't do the second, heavier title on main yet: it stops before the intro on a separate issue unrelated to this PR. From our side this is good to merge. It now conflicts with main in docs/dev/TechnicalDebt.md only, so it needs one more rebase first.

The console's DCC of a single-level 4-byte SW_64KB_R_X surface is
ceil(W/512) * ceil(H/512) * 4096 bytes, 49152 for 1920x1080, but the
driver stored and read one key per 256 surface bytes (34560).
PPSA26344 retiles its render DCC into the displayable copy it
registers (cs_dcc_retile_32bpp); a retile of the stale tail gives the
mixed keys PPSA12544 stops on.

DccKeyCount gives that extent. Color target and storage image key
stores cover it when the DCC is pipe-aligned (CB_COLOR_ATTRIB3 bit 30,
T# word 6 bit 19), whose CB keys reach its last meta block, and throw
where it is not writable; unaligned DCC keeps one key per 256 bytes,
as do texture and color target key reads. DisplayBufferKeys reads the
display DCC over the extent, presents only when every key agrees,
names the first differing key's pixels and checks a pending image
against those keys.
@Celegans12
Celegans12 force-pushed the feat/videoout-dcc-visible-keys branch from 0165070 to 1816aea Compare October 8, 2026 13:30
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@oneandonlydean
oneandonlydean merged commit 74b5c91 into boykopovar:main Oct 8, 2026
4 checks passed
@Celegans12
Celegans12 deleted the feat/videoout-dcc-visible-keys branch October 8, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants