Skip to content

fix(shader): store through DIM 2D image_store to a cube T#'s BASE_ARRAY face - #6799

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
milescoviello:fix/cube-face-image-store
Oct 19, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
milescoviello:fix/cube-face-image-store

Conversation

@milescoviello

@milescoviello milescoviello commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

What

A compute shader in Crimson Desert (PPSA25286, 01.000.568) writes a cube map through DIM 2D image_stores and stops recompiling:

compute shader 0x229470c100: image descriptor is incompatible with the static runtime image interface: descriptor format 71 decodes to {class 1, dim 5, conversion 0, packed 0, cube 1, ...}; modes: {class 1, dim 3, ...} {class 1, dim 1, ...} ...; T# 209a6200 c4700000 003fc03f b1b00fac 00000005 00700080 007b0000 00209aa4

The T# is a 256x256 16_16_16_16_FLOAT cube (TYPE 11, DEPTH 5, BASE_ARRAY 0) with one level. The program runs 8x8 threads per group, reads 2D images and writes its results with image_store (0xf0200f08 and 0xf0200108, DIM 2D, no mip). #4139 already reads a cube T# through a DIM 2D image_load or sample as the BASE_ARRAY face, measured on RDNA1. A store had no such mode.

How

  • ResourceTracker: a mip-less ImageWrite keeps cubeFaceCompatible.
  • ResourceMaterializer: the face mode (the 2D-array cube view) is added for an image the program only stores to, as well as for reads. Its integer coordinates are (x, y, 0), and the storage view starts at BASE_ARRAY, so the store writes texel (x, y) of the BASE_ARRAY face, the face the measured load reads.
  • @simonbbby measured the stores on RDNA2 (gfx1036, not the PS5's gfx1013) with the hardware oracle. 32_UINT and 32_FLOAT stores through 8x8 mip-less cube arrays (DEPTH 11) with BASE_ARRAY 0 and 6, in wave32 and wave64, write texel (x, y) of that face and leave every other byte of the 64 KiB backing unchanged. That is the same layout and the same every-other-byte check agc_driver_image_store_face_on_cube makes. simonbbby then measured this title's 16_16_16_16_FLOAT the same way. Four-channel stores of exactly representable half values pass 20 of 20 dispatches (BASE_ARRAY 0 and 6, wave32 and wave64), and only the 64 stored eight-byte texels change in the backing. The debt entry records both measurements (273675b), says that other formats and the title's own 256x256 tiled descriptor are not measured, and lists what still throws: atomics, an image the program also reads, formats written through a conversion such as 32_SINT, and mip loads and stores.
  • The mode selector is a specialization constant, so a specialized pipeline keeps only the mode its T# selects. The two extra modes (float and uint) only enlarge the unspecialized artifact of a write-only DIM 2D storage image.

Tested

Linux (Ubuntu 24.04, GCC 13.3, Release), RTX 4070 Ti, NVIDIA 595.99.

  • agc_driver_image_store_face_on_cube (new, GPU): 64 threads store their value at (tid & 7, tid >> 3) into an 8x8 cube array T# (DEPTH 11) with BASE_ARRAY 0 and 6, for 32_UINT and 32_FLOAT. Every texel of that face must hold its thread's value, and every other byte of the 12 faces must be unchanged. With main's ResourceTracker.cpp and ResourceMaterializer.cpp it fails 3 of 3 with the error above (descriptor format 20 decodes to {class 2, dim 5, ..., cube 1, ...}). With this change it passes 3 of 3.
  • agc_driver_image_plane_address_on_cube, agc_driver_image_store_line_through_plane, agc_driver_image_store_array_through_volume: 3 of 3.
  • ctest (^agc_driver_|^agc_vertex_barrier|recompiler|shader_cache|request_serializer|spirv|^agc_shader|bda|journal, 502 tests) on main eb901bc: 500 pass. agc_driver_store_over_untracked fails the same way on main (test(agc): keep waiting for the neighbour copy case's result #6385 addresses it), and so does agc_driver_graphics in a case fix(agc): stop pixel invocations on BDA faults when supported #5804 added (test(agc): keep the vertex terminate case's code in static storage #6789 fixes it).

Crimson Desert (PPSA25286, 01.000.568), 900 s limit, 16 GiB cap, scripted pad input. On main the title stops 14 s in (#6612), so these runs use my test stack: #6612, #6613, #6706, #6708, #6748, #6178 and #6785.

Without this change (two runs) With it
End the error above at 603 / 593 s, after 26 draws 665 s, after 58 draws, on an unrelated SrtWalker::EvaluateRuntimeSources refusal in another compute shader

Rebased onto main 82ddae7 (d697c92): the four cube and volume store tests pass 3 of 3, and the 502 area tests pass except main's agc_driver_graphics (#6792) and agc_driver_store_over_untracked (#6385).

Not checked on Windows or on a console. On AMD, only the oracle measurement above was run, not this emulator build.

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
  • Unblocks: none
  • GPUs tested: NVIDIA GeForce RTX 4070 Ti, driver 595.99, Ubuntu 24.04; AMD gfx1036 hardware oracle for the store layout (by @simonbbby)
  • AI-assisted: yes

🤖 Generated with Claude Code

@MarcZX10

Copy link
Copy Markdown
Contributor

The regression proves the emulator’s selected Vulkan view writes the requested face, but the PR explicitly says the corresponding guest image_store behavior was not measured. The image_load result from #4139 does not by itself establish store semantics or format behavior. Could you attach a hardware-oracle measurement for DIM 2D storage writes (especially BASE_ARRAY 0 and 6, with the supported UINT/FLOAT formats), or otherwise keep this behavior gated/refused until that contract is established? I would not treat the emulator-side expected values alone as proof of guest equivalence.

@github-actions

github-actions Bot commented Oct 16, 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 7381 core/shader/recompiler/Optimization/src/ResourceTracker.cpp L1803
docs/dev/debt/shader.md L57-58

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 907 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 1341 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 5021 docs/dev/debt/shader.md
PR 5371 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 5426 docs/dev/debt/shader.md
PR 5500 docs/dev/debt/shader.md
PR 5782 docs/dev/debt/shader.md
PR 5941 docs/dev/debt/shader.md
PR 6009 docs/dev/debt/shader.md
PR 6235 docs/dev/debt/shader.md
PR 6280 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 6604 docs/dev/debt/shader.md
PR 6924 docs/dev/debt/shader.md
PR 6947 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
PR 6977 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
docs/dev/debt/shader.md
PR 7074 docs/dev/debt/shader.md
PR 7085 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 7223 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 7332 docs/dev/debt/shader.md
PR 7392 docs/dev/debt/shader.md
PR 7395 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
docs/dev/debt/shader.md
PR 7426 docs/dev/debt/shader.md
PR 7429 docs/dev/debt/shader.md
PR 7484 docs/dev/debt/shader.md
PR 7550 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
PR 7642 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp
core/shader/recompiler/Optimization/src/ResourceTracker.cpp
PR 7689 core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp

@milescoviello

Copy link
Copy Markdown
Contributor Author

That's fair: the test only shows that the Vulkan view I chose writes the face I expect, not what the hardware writes. I don't have an RDNA GPU here, so I can't run the oracle myself, and this PR shouldn't merge on the analogy to #4139 alone. I'll leave it waiting for a measurement rather than add an opt-in gate, since a gate that stays off would leave the title stopping either way.

To make the measurement quick for anyone with an RDNA2 card (or @billythemenace2, who measured #4139), here is an oracle kernel and runner. Each of 64 lanes stores its value at (x, y) = (lane & 7, lane >> 3) through a DIM 2D image_store to an 8x8 cube array T# (DEPTH 11, BASE_ARRAY 0 or 6, 32_UINT or 32_FLOAT). It then reads all 12 slices back through a 2D-array view of the same memory, so only the hardware's own addressing is involved. It hasn't been assembled here, because this machine has no AMDGPU toolchain, so the syntax may need a touch.

cube_store_body.s
  ; rows (wave32, 64 rows): v4 = value, v5 = x, v6 = y, v7 = BASE_ARRAY (the same in every row)
  ; extra: 12 faces of 8x8 texels, linear, right after the 64 rows (s[4:5] + 1024)
  s_add_u32 s8, s4, 1024
  s_addc_u32 s9, s5, 0
  s_lshr_b64 s[10:11], s[8:9], 8
  v_readfirstlane_b32 s12, v7
  s_lshl_b32 s12, s12, 16
  ; cube T# in s[16:23]: FORMAT from s13 (20 = 32_UINT, 22 = 32_FLOAT), 8x8, TYPE 11, DEPTH 11, BASE_ARRAY s12
  s_mov_b32 s13, FORMAT_BITS
  s_mov_b32 s16, s10
  s_and_b32 s17, s11, 0xff
  s_or_b32 s17, s17, s13
  s_mov_b32 s18, 0x1c001
  s_mov_b32 s19, 0xb0000fac
  s_or_b32 s20, s12, 11
  s_mov_b32 s21, 0
  s_mov_b32 s22, 0
  s_mov_b32 s23, 0
  ; 2D-array view of the same 12 slices in s[24:31]: TYPE 13, DEPTH 11, BASE_ARRAY 0
  s_mov_b32 s24, s10
  s_mov_b32 s25, s17
  s_mov_b32 s26, 0x1c001
  s_mov_b32 s27, 0xd0000fac
  s_mov_b32 s28, 11
  s_mov_b32 s29, 0
  s_mov_b32 s30, 0
  s_mov_b32 s31, 0
  image_store v4, v[5:6], s[16:23] dmask:0x1 dim:SQ_RSRC_IMG_2D unorm
  s_waitcnt_vscnt null, 0
  s_barrier
  buffer_gl0_inv
  buffer_gl1_inv
  v_mov_b32 v26, v5
  v_mov_b32 v27, v6
  v_mov_b32 v28, 0
  image_load v10, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 1
  image_load v11, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 2
  image_load v12, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 3
  image_load v13, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 4
  image_load v14, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 5
  image_load v15, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 6
  image_load v16, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 7
  image_load v17, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 8
  image_load v18, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 9
  image_load v19, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 10
  image_load v20, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  v_mov_b32 v28, 11
  image_load v21, v[26:28], s[24:31] dmask:0x1 dim:SQ_RSRC_IMG_2D_ARRAY unorm
  s_waitcnt vmcnt(0)
cube_store_run.py (python3 cube_store_run.py <AnyPS5 checkout>)
# Runs cube_store_body.s for BASE_ARRAY 0 and 6 and formats 32_UINT (20) and 32_FLOAT (22); prints, per run, the faces that
# changed and whether each lane's texel on the BASE_ARRAY face holds its value. Usage: python3 cube_store_run.py <AnyPS5 checkout>
import json, struct, sys
sys.path.insert(0, sys.argv[1] + "/tools/hw-oracle")
import hw_oracle
body = open(__file__.replace("cube_store_run.py", "cube_store_body.s")).read()
pattern = lambda index: 0xa5a50000 | (index & 0xffff)
extra = b"".join(struct.pack("<I", pattern(i)) for i in range(16384))
modes = dict(ieee=0, denorm32=3, denorm16=3, dx10_clamp=0, round32=0, round16=0, fp16_overflow=0)
for fmt in (20, 22):
    for base in (0, 6):
        rows = [(0x3f000000 + lane * 0x1357, lane & 7, lane >> 3, base) for lane in range(64)]
        out = hw_oracle.run(body.replace("FORMAT_BITS", hex((fmt << 20) | 0xc0000000)), rows, extra, **modes)
        faces = [[o[face] for o in out] for face in range(12)]
        ok = all(out[l][base] == rows[l][0] for l in range(64))
        print(json.dumps({"format": fmt, "base_array": base, "base_face_holds_values": ok, "faces": faces}))

If the hardware writes texel (x, y) of the BASE_ARRAY face and leaves the other 11 alone, every printed run has base_face_holds_values: true and only that face differs from the fill pattern. I'll pin the measured rows in agc_driver_image_store_face_on_cube. If it writes somewhere else, I'll change the mode to match or close this PR.

AI-assisted: yes

@github-actions github-actions Bot added shader core/shader docs agc libSceAgc* labels Oct 16, 2026
@simonbbby

Copy link
Copy Markdown
Contributor

Measured the requested DIM 2D image_store on real gfx1036: it writes the BASE_ARRAY face in these UINT/FLOAT cases, including BASE_ARRAY=6.

Hardware/toolchain: AMD gfx1036, PCI 1002:13c0, Ryzen 9 9950X iGPU; Ubuntu 24.04, amdgpu kernel 7.0.0-38-generic, ROCr/HSA 5.7.1-2build1, clang/LLD 18.1.3. This is an RDNA2 measurement, not PS5/gfx1013 evidence. PR head checked: 4ffb8bcf47d2171eb8fb7716c4d43a50fa6b1efe.

I ran your posted assembly without syntax changes, with 64 rows and the 64 KiB pattern-filled extra, through the existing oracle. Only the runner's module/body paths were adapted. Explicit controls for every run: ieee=0, dx10_clamp=0, denorm32=3, denorm16=3, round32=0, round16=0, fp16_overflow=0. Fine-grained memory, default 4096-byte LDS.

T# FORMAT BASE_ARRAY wave32, 5 fresh dispatches wave64, 5 fresh dispatches
20 / 32_UINT 0 all pass all pass
20 / 32_UINT 6 all pass all pass
22 / 32_FLOAT 0 all pass all pass
22 / 32_FLOAT 6 all pass all pass

For every dispatch, all 64 texels on the selected face equal the input bits 0x3f000000 + lane*0x1357; each of the other 704 texels read through the 12-slice view matches a corresponding no-store baseline. Those baselines contain none of the expected stored values at the target positions.

I also checked the backing memory independently of the image-load descriptor: after the store/wait/barrier/cache invalidation, replace the image reads with global_load_dwordx4 reads from the extra-buffer address. Each lane reads 16 consecutive dwords at chunk*4096 + lane*64, for 16 fresh 64-lane dispatches covering the complete 64 KiB. For each of the four format/base combinations, exactly 64 dwords change and every other byte, including row/slice padding, is unchanged. The no-store raw control reproduces the entire original buffer.

Exact rows to pin (lane=0..63): (value, x, y, base) = (0x3f000000 + lane*0x1357, lane&7, lane>>3, base). For this descriptor's linear-aligned layout, raw dword index base*512 + y*64 + x holds value; every other dword retains 0xa5a50000 | index. Thus the measured row pitch is 256 bytes and slice stride 2048 bytes. The image readback's target face is base, not always face zero. Both observation paths agree.

These controls rule out a missing store, a BASE_ARRAY-ignored store and an apparent result caused only by the readback view. Assembly SHA-256: a22fa915ce0a4513789e0fef3d9735121bd65f38160b879a20517b761761c943 (the body in your comment, extracted with its trailing newline). Raw per-lane rows and full readbacks are retained locally.

Limits: the supplied 8x8, 12-slice, mip-less descriptors, formats 20/22 and the finite value bits above. No atomics, other formats (including the title's 16_16_16_16_FLOAT), mip behavior, PS5 measurement, emulator build or Crimson Desert replay. This establishes the requested face-selection/store behavior on gfx1036; it does not validate those other paths.

@milescoviello
milescoviello force-pushed the fix/cube-face-image-store branch from 4ffb8bc to d697c92 Compare October 16, 2026 14:32
@milescoviello

Copy link
Copy Markdown
Contributor Author

Thank you @simonbbby, that is exactly the measurement this needed, and the raw-buffer check that only the 64 target dwords change rules out more than I asked for.

@MarcZX100: with that, the store path is no longer unmeasured for the case the test pins. On RDNA2 (gfx1036, not the PS5's gfx1013), a DIM 2D image_store through 8x8 mip-less cube arrays of 32_UINT and 32_FLOAT, with BASE_ARRAY 0 and 6, wave32 and wave64, writes texel (x, y) of the BASE_ARRAY face and nothing else. That is the layout agc_driver_image_store_face_on_cube expects. I rebased onto main 82ddae7 and changed the debt entry to say what was measured, linking simonbbby's comment (d697c92). It still says that other formats, including the title's 16_16_16_16_FLOAT, are not measured. The throws for atomics, read-and-store, conversion formats and mips are unchanged. The new head passes the four cube/volume store tests 3 of 3 and the 502 area tests pass except the two that fail on main (#6792, #6385).

AI-assisted: yes

@simonbbby

Copy link
Copy Markdown
Contributor

Adding FORMAT 71 / 16_16_16_16_FLOAT to the gfx1036 measurement, since the current description explicitly leaves the title's format unmeasured. Head refreshed: d697c92ecfbf4cde5f147712eeb10431ca035d1a.

Same native RDNA2 oracle setup as the earlier result: gfx1036, Ubuntu 24.04, kernel 7.0.0-38, ROCr 5.7.1, clang/LLD 18.1.3. Controls: ieee=0, dx10_clamp=0, denorm32=3, denorm16=3, round32=0, round16=0, fp16_overflow=0; fine-grained memory, 4096-byte LDS.

Kept the supplied 8×8 mip-less cube-array descriptors, TYPE 11, DEPTH 11, BASE_ARRAY 0 or 6, and the 64 KiB patterned backing. Changed FORMAT_BITS to 0xc4700000 and the store to four channels:

  v_mov_b32 v40, v4
  v_mov_b32 v41, 0xc0000000
  v_mov_b32 v42, 0x40800000
  v_mov_b32 v43, 0x3e800000
  image_store v[40:43], v[5:6], s[16:23] dmask:0xf dim:SQ_RSRC_IMG_2D unorm

Rows use v4=0x3f000000 + lane*0x20000, x=lane&7, y=lane>>3. Thus the RGBA values are (0.5 + lane/128, -2, 4, 0.25), all exactly representable in binary16. Expected half words are (0x3800 + lane*16, 0xc000, 0x4400, 0x3400); I independently checked these with Python's IEEE-half struct.pack('<4e', ...).

  • BASE_ARRAY 0 and 6, wave32 and wave64, five fresh dispatches per combination: 20/20 pass. A 2D-array image_load of all four channels expands every target texel back to the exact four input f32 bit patterns. No-store image baselines do not contain those payloads.
  • Independent global_load_dwordx4 snapshots cover the complete 64 KiB, without using image-load addressing. All four base/wave combinations match the expected half words at byte offset base*2048 + y*256 + x*8, and every other byte remains unchanged, including padding. Exactly 128 dwords change (64 eight-byte RGBA texels). Raw no-store controls preserve the entire backing in both wave sizes.
  • For BASE_ARRAY 6, the first stored texel is at byte 12,288 and face zero is untouched. This distinguishes BASE_ARRAY face selection from an always-face-zero store; the raw checks also distinguish a readback-view artifact or a one-channel-only store.

The generated store-prefix assembly SHA-256 is daf66e09cd1d38f4e82d420bfec3160af1de8d942a64f84fbf9ed2b6c977f023; scripts, raw image rows and all four full backing snapshots are retained locally.

Scope: this adds the four-channel half-float format with these finite, exactly representable values and this linear-aligned geometry. It does not measure the title's full 256×256 tiled descriptor, exceptional/rounding-edge values, mips, atomics, PS5/gfx1013 or an emulator/title run. No repository source changed.

@milescoviello

Copy link
Copy Markdown
Contributor Author

Added this to the description and to the debt entry (273675b): the debt entry no longer lists 16_16_16_16_FLOAT as unmeasured, and still names the title's 256x256 tiled descriptor as unmeasured.

AI-assisted: yes

milescoviello and others added 3 commits October 17, 2026 18:48
…AY face

A DIM 2D image_load through a cube T# already reads texel (x, y) of the
T#'s BASE_ARRAY face (boykopovar#4139, measured on RDNA1). A DIM 2D image_store to
a cube T# had no matching runtime mode and threw "image descriptor is
incompatible with the static runtime image interface". Crimson Desert
writes a 16_16_16_16_FLOAT cube this way from a compute shader.

A mip-less image_store to an image the program only stores to now takes
the same face mode, writing texel (x, y) of the BASE_ARRAY face, the
face the measured load reads. Stores were not measured on hardware; the
debt entry says so. Formats that store through a conversion (SINT) keep
throwing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simonbbby measured DIM 2D image_store through 8x8 cube arrays on RDNA2
(gfx1036): 32_UINT and 32_FLOAT stores with BASE_ARRAY 0 and 6 write that
face and nothing else.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simonbbby measured four-channel 16_16_16_16_FLOAT stores through the same
8x8 cube arrays on gfx1036: they land on the BASE_ARRAY face and leave the
rest of the backing unchanged, so the debt entry no longer lists the
title's format as unmeasured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@milescoviello
milescoviello force-pushed the fix/cube-face-image-store branch from 273675b to c3a7e4f Compare October 17, 2026 18:54
@adriadam10 adriadam10 self-assigned this Oct 19, 2026
@adriadam10
adriadam10 merged commit 3bb15be into boykopovar:main Oct 19, 2026
8 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the diff: the tracker only keeps the cube-face mode for mip-less ImageWrite, and the materializer adds it only for images the program stores to without reading, atomics or dynamic mips, and without conversion or packed formats, so everything else still throws. The new test pins texel (x, y) of the BASE_ARRAY face for 32_UINT and 32_FLOAT, base 0 and 6, and every other byte unchanged; on main the descriptor is rejected. The debt entry matches the code and the RDNA2 oracle measurements from @simonbbby (gfx1036; not the PS5's gfx1013), which satisfy the earlier request for hardware evidence; merged.

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

Labels

agc libSceAgc* docs shader core/shader

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants