Commit Graph

183 Commits

Author SHA1 Message Date
boykopovar 2ec144d4d3 perf(shader): avoid redundant resource validation + eliminate dead spirv computations 2026-10-08 17:29:48 +03:00
boykopovar 05fa426112 perf(shader): eliminate prepared-path overhead in data, descriptors, dispatch, bda 2026-10-08 17:29:17 +03:00
boykopovar 7431e5a6e1 perf(shader): restore prepared vertex inputs + harden buffer specialization 2026-10-08 17:28:55 +03:00
boykopovar 7f56e04157 fix(shader): validate and serialize pipeline specialization constants (wip) 2026-10-08 17:28:35 +03:00
boykopovar 73816838dc perf(shader): add pipeline specialization and direct buffer access (wip) 2026-10-08 17:28:34 +03:00
boykopovar f524bbedc3 refactor(shader): separate artifact preparation from draw and dispatch 2026-10-08 17:26:40 +03:00
boykopovar ab0f9985fa feat(shader): resolve bindless resources through runtime heap metadata 2026-10-08 17:26:36 +03:00
boykopovar dccfa08de9 feat(shader): add fixed runtime abi + typed descriptor heaps 2026-10-08 17:26:04 +03:00
Celegans12 1816aea91c fix(agc): store and read DCC keys over the console's metadata extent
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.
2026-10-08 15:24:43 +02:00
Adrià 07c2405c35
Merge pull request #1371 from TibixDev/pr/depth-maintenance-precheck
fix(agc): reject depth maintenance before shader compilation
2026-10-08 14:37:01 +02:00
Adrià 072804fd33
Merge pull request #1288 from Celegans12/fix/agc-dcc-filled-keys-refresh
fix(agc): forget a DCC key fill once a refresh sees other keys
2026-10-08 14:35:47 +02:00
Adrià cba8d86da8
Merge pull request #1270 from Celegans12/fix/agc-unaligned-buffer-views
fix(agc): bind guest buffer views that start off a DWORD boundary
2026-10-08 14:34:45 +02:00
Adrià 0b3cc73e1c
Merge pull request #1112 from Celegans12/feat/agc-conservative-rasterization
feat(agc): implement overestimating conservative rasterization
2026-10-08 14:31:38 +02:00
Adrià d393c3e754
Merge pull request #1346 from TibixDev/pr/disabled-color-targets
fix(agc): honor disabled color targets and their reset state
2026-10-08 13:36:03 +02:00
Celegans12 29c22442dc feat(agc): implement overestimating conservative rasterization
A draw whose PA_SC_CONSERVATIVE_RASTERIZATION_CNTL (context 0x313) was
not the default 0x6000 threw in DecodeState and the draw precheck.
0x6001 sets OVER_RAST_ENABLE and now decodes to Vulkan's overestimation
mode, chained to the pipeline's rasterization state and its key.

The device enables VK_EXT_conservative_rasterization only when its
primitiveOverestimationSize is at most 1/256 pixel and it rasterizes
degenerate triangles; elsewhere the pipeline throws. Overestimation is
accepted only when triangles reach the rasterizer: VGT_GS_OUT_PRIM_TYPE
behind a geometry shader, VGT_TF_PARAM's output topology behind
tessellation, VGT_PRIMITIVE_TYPE otherwise. Other 0x313 values and
centroid interpolation still throw.

Adds the agc_driver_conservative_rasterization GPU test and extends
agc_driver_graphics.
2026-10-08 13:20:22 +02:00
Celegans12 38e89df1a5 fix(agc): bind guest buffer views that start off a DWORD boundary
A V# whose base is not a multiple of 4 bytes from the start of its GPU
owner threw in GuestBufferMemory::Descriptor, because the shader added
the offset below the view only in whole dwords.

Descriptor now binds from the offset alignment below the view and
passes the whole remainder. Such a view's range covers the dwords
around it, and its descriptor region starts and ends on dword
boundaries, so a lone view copied to its own buffer binds the same way.

The buffer specialization records the V# base's low two bits. Raw
vector loads through such a V# join two dwords, glc dwordx2 loads
included, stores merge their bytes into both with compare-exchange
loops, and bounds checks include the second dword. s_buffer_load
ignores the low two bits, as SrtEvaluator does. Formatted accesses
whose element alignment (min(element bytes, 4)) the base breaks throw,
as they fault the wave on RDNA2, and so do atomics.
2026-10-08 13:01:27 +02:00
Celegans12 ccddd4f89b fix(agc): forget a DCC key fill once a refresh sees other keys
StorageTexture::MarkDirty skips the first write's uncompressed key
store while the image's filledKeys holds a clear code, and only an
upload or clearByKeysFill reset that fill. After a 0001 key fill, a
kernel storing 0000 over the keys and a storage write, the refresh of
write-watched memory read 0000, the keys the image was uploaded under,
so it did not upload again and kept the stale 0001. The write then
left the 0000 keys in memory, and a kernel reading them saw the clear
colour.

StorageTexture::ProvedKeys, the key read of Refresh and the fast
revalidation, now forgets the fill when it reads other keys while the
image has no results pending. An image with pending results keeps it,
since its write-backs treat those results as killed by that fill.
2026-10-08 12:55:45 +02:00
Adrià 30e89cc6a0
Merge pull request #1266 from Celegans12/fix/agc-oversized-descriptor-sets
fix(agc): allocate oversized descriptor sets from a dedicated pool
2026-10-08 12:09:59 +02:00
Adrià 96614df544
Merge pull request #1239 from awake-devel/fix/agc-release-host-imports
fix(agc): release host imports before the device is destroyed
2026-10-08 11:27:40 +02:00
Adrià 3ce729fe86
Merge pull request #1182 from MichonGoddijn231849/feat/compute-scratch
feat(agc): compute dispatches with scratch memory
2026-10-08 11:15:45 +02:00
Adrià a5df1a874d
Merge pull request #1177 from MichonGoddijn231849/feat/standard-64k-color-targets
feat(agc): 64 KiB standard color targets
2026-10-08 10:44:52 +02:00
Michon 20e055c244 feat(agc): compute dispatches with scratch memory
A compute program with COMPUTE_PGM_RSRC2.SCRATCH_EN takes its per-lane private size from the AGC
shader header (scratch_size_dw_per_thread) instead of being refused. The size travels in
ShaderComputeStageInfo (cache key, request serialization version 10) to the translator, whose
scratch accesses already lower to a per-invocation Function array of that many dwords.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 10:22:40 +02:00
Adrià e705d430d6
Merge pull request #1139 from alfalfa-star/fix/agc-inert-pixel-stage
fix(agc): run draws whose pixel stage cannot run without its program
2026-10-08 10:20:09 +02:00
Michon 6aa8e58de7 feat(agc): 64 KiB standard color targets
Color targets with the SW_64KB_S swizzle (CB_COLOR_ATTRIB3 tile mode 9) use the standard element
order of the 4 KiB mode extended by the texture detiler's 64 KiB bits, and are resident like the
other tiled targets. The standard blocks are now wider than tall for 2- and 8-byte elements, as the
standard swizzle and the texture layout have them (they were transposed, aliasing texels).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 10:16:22 +02:00
Adrià 8e22498c20
Merge pull request #1134 from alfalfa-star/fix/agc-mesh-unindexed-index-vsharp
fix(agc): bind a null index buffer to non-indexed mesh draws
2026-10-08 10:15:09 +02:00
Adrià 72bcc33606
Merge pull request #1113 from Celegans12/fix/agc-batch-kept-bytes
fix(agc): bound the upload buffers that recorder batches keep alive
2026-10-08 09:39:56 +02:00
TibixDev 1841f3c6e7 fix(agc): honor disabled color-buffer formats
Exclude COLOR_INVALID attachments from the effective write mask while preserving later MRT export indices and depth-only rendering. Validated with the graphics tests and Skyrim menu draws.

AI-assisted implementation and testing by OpenAI Codex.
2026-10-08 10:22:13 +03:00
Chad c17337b6d9 fix(agc): size CMASK by whole 1024x512 metablocks
A CMASK holds 4 KiB per 1024x512-pixel metablock, not one nibble per 8x8
tile of the target: 1920x1080 spans 0x6000 bytes, as titles clear it,
not 16200. The swizzled nibbles of the lower-right tiles lie past the
packed size, so the fast-clear check missed them and the expanded store
left them cleared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:53:02 +13:00
alfalfa-star 61a61016d8 feat(agc): render into CMASK fast-clear color targets
Draws into a color target with CB_COLOR_INFO FAST_CLEAR (bit 13) were
rejected. The CMASK keeps one nibble per 8x8 tile: 0 means the tile is
fast-cleared and reads as CB_COLOR_CLEAR_WORD, 0xF means expanded (the
texels hold the color). The texture units and display scanout do not read
it, so a title runs a fast-clear eliminate pass before sampling or
flipping such a target.

DecodeColorBuffer now takes the CMASK address (CB_COLOR0_CMASK and
CB_COLOR0_CMASK_BASE_EXT) and the bytes one nibble per 8x8 tile spans.
Before a draw renders into the target, and in a fast-clear eliminate
pass, a CMASK whose nibbles are all 0 is materialized: the target is
filled with the clear texel (FillClear on its resident image, else
guest memory on the CPU) and the CMASK bytes are stored as expanded,
through the same GPU-ordered store as the DCC "uncompressed" keys
(0xFF either way). The result equals the draw followed by the eliminate
pass. An all-expanded CMASK changes nothing; a mixed one throws, as do
fast clears of DCC, mipmapped, 3D and array targets.

PPSA14632 (LEGO Horizon Adventures) fast-clears its two 3840x2160
display buffers through CMASK, so its final composite draw was dropped
on every frame and only black was presented.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 07:58:31 +02:00
Adrià 058dead176
Merge pull request #1058 from Celegans12/feat/agc-r8-srgb-color-target
feat(agc): implement 8 srgb color targets through rgba8 srgb proxies
2026-10-08 07:52:45 +02:00
Dean 5afffae1e0
Merge pull request #1235 from CnRJay/fix/agc-host-import-remapped-range
fix(agc): retire host imports of ranges mapped again
2026-10-08 15:17:35 +13:00
TibixDev f2fe3f7165 fix(agc): reject depth maintenance before shader compilation 2026-10-08 03:39:05 +03:00
Adrià c73ee448d5
Merge pull request #903 from Celegans12/fix/agc-dcc-keys-on-first-write
fix(agc): store uncompressed DCC keys at the first write of a fast-cleared image
2026-10-07 23:42:16 +02:00
clem-gb c411489c9b fix(agc): release host imports before the device is destroyed
The host import registry is a function-local static that device
teardown never emptied, so every import still registered kept its
VkBuffer and VkDeviceMemory past vkDestroyDevice
(VUID-vkDestroyDevice-device-05137 in 23 execution tests).
ClearHostImports destroys the device's imports and resets the
registry; ~State calls it next to ClearImageMirrors.

Closes #1236

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018RNrNcixatky8U4LfC7Ew5
2026-10-07 23:41:49 +02:00
Adrià 539cbd881d
Merge pull request #943 from alfalfa-star/feat/linux-direct-memory-dmabuf-import
feat(agc): import shared direct memory through udmabuf on Linux
2026-10-07 23:38:09 +02:00
Celegans12 b8d3060351 fix(agc): store uncompressed DCC keys at the first write of a fast-cleared image
A storage image uploaded under fast-clear DCC keys left the clear code
in guest memory until its write-back, so a title kernel that read the
keys after a draw or dispatch into the image saw the clear code.
PPSA26344's DCC retile kernel copies such keys into its display
buffer's metadata every frame, and the flip presented the clear color.

MarkDirty now stores uncompressed keys the first time an image uploaded
under a clear code is written, unless the title filled the keys with a
clear code since, and the image's uploaded keys become uncompressed.
While they are, Refresh and the fast revalidation read the keys as the
recorded work writing them leaves them: from the bytes pending uniform
fills and driver key stores note (NotePendingFill) when those already
make the keys read uncompressed (bytes that differ, 0xff, or a byte the
format can't use as a clear code), otherwise after waiting for that work.
2026-10-07 23:24:14 +02:00
Chad b950ecd7e4 docs(agc): keep the swizzle equation table header as on main
The conventions check rejects changed code comments without a
TechnicalDebt change; the header line only listed the new modes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:11:05 -05:00
Claude ace10fdba3 feat(agc): detile the display, T and 4 KiB XOR swizzle modes
Textures in SW_256B_D, SW_4KB_D, SW_64KB_D, SW_64KB_S_T, SW_64KB_D_T,
SW_4KB_S_X and SW_4KB_D_X (tile modes 2, 6, 10, 17, 18, 21 and 22)
threw while their T# was decoded. They are detiled with address
equations generated from AMD addrlib's GFX10 swizzle patterns for the
same 16-pipe non-RB+ configuration as the 64 KiB XOR table, and share
the block shape and mip tail placement of the standard mode of their
block size. The pipe/bank XOR base check of the XOR and T modes now
uses the block size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:11:05 -05:00
alfalfa-star 00e6d078cb fix(agc): keep dma-buf imports write-watched when they leave write protection intact
The import write-watch probe imports private anonymous scratch pages
through VK_EXT_external_memory_host. On amdgpu that path resolves the
pages' write protection (the userptr is revalidated at every submission),
so the probe decides that imported ranges are compared instead of
watched, and every import is unwatched. Shared direct memory is never
imported that way: its imports go through udmabuf, which takes the
memfd's pages without touching the process page tables. Unwatching them
anyway left every surface in direct memory without write stamps: each
storage image refresh re-uploaded the whole surface ("untracked"), and
draw snapshots of imported ranges could never be reused. On PPSA14632
that was over 800 GiB of uploads in 90 s.

- HostImport records whether it is a dma-buf import, and the import
  decides watch or unwatch after the import by its kind (ImportWatched
  already absorbs the pages the import itself touches).
- A second probe, ProbeDmaBufImportWriteProtection, imports a sealed
  shared memfd scratch range through udmabuf: dma-buf imports stay
  watched when a GPU read of it marks no page written and a CPU store
  after that is still collected. Otherwise, or when the probe fails,
  they are compared like host-pointer imports.
- Both probes also report whether a CPU store after the GPU read was
  collected (ImportProbe::writtenByCpu).
- APS5_WRITE_WATCH_IMPORTS=watch or unwatch overrides both kinds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:09:26 +02:00
alfalfa-star 815d274736 feat(agc): import shared direct memory through udmabuf on Linux
On Linux, direct memory is a memfd mapped MAP_SHARED. amdgpu accepts only
private anonymous memory for a host pointer import (libdrm sets
AMDGPU_GEM_USERPTR_ANONONLY), so every import of direct memory failed with
VK_ERROR_INVALID_EXTERNAL_HANDLE and its ranges were copied.

When a host pointer import fails, the range's memfd slice is turned into a
dma-buf through /dev/udmabuf and imported with VK_EXT_external_memory_dma_buf.
The direct memory backing is sealed against shrinking and growing, as
udmabuf requires. libkernel reports the backing of a guest range through
GuestArenaSharedBacking. Without /dev/udmabuf, or past its size limit, the
range is copied as before. APS5_TRACE_HOST_IMPORT now prints the mappings
behind a failed import on Linux too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:09:16 +02:00
Adrià 2b9cd26dbe
Merge pull request #933 from CnRJay/feat/agc-3d-xor-swizzle
feat(agc): detile 3D textures in SW_64KB_Z_X and SW_64KB_S_X
2026-10-07 22:53:31 +02:00
Adrià 629724f103
Merge pull request #931 from CnRJay/feat/agc-1d-array-textures
feat(agc): implement 1D array textures
2026-10-07 22:43:42 +02:00
Celegans12 9af8e9e9f6 fix(agc): bound the upload buffers that recorder batches keep alive
A sampled texture upload keeps its staging, tiled and linear buffers
until the batch that recorded it completes. A draw whose image table
uploads thousands of textures records them all into one open batch,
so in PPSA26344 6.6 GiB of device buffers were live when the next
texture allocation failed with VK_ERROR_OUT_OF_DEVICE_MEMORY.

Recorder::Keep now takes the bytes an object holds, and the sampled
upload and the storage upload and write-back scratch paths pass theirs.
Before each sampled or storage texture lookup, BoundKeptBytes submits
the open batch once it keeps 512 MiB. While the batches in flight and
those a sync or reap of this hold already finished keep more than twice
that, it destroys the finished ones' objects, then waits for the oldest
in flight and releases its objects at once instead of at the unlock.
agc_recorder_kept_bytes checks these bounds on a mock device.
2026-10-07 22:37:12 +02:00
Celegans12 14b4edda8d fix(agc): allocate oversized descriptor sets from a dedicated pool
DescriptorCache::Allocate returned no set when a set needed more
descriptors of one type than a chain pool holds (4,096 storage
buffers, 1,024 sampled or storage images, 512 samplers). A build's
own set then fell back to a pool of its own, but PrepareDrawBindings
has no fallback and threw "draw snapshot descriptor allocation
failed", so a recorded draw with such a set and a snapshotted input
was skipped.

Such a set now gets a pool sized to it (maxSets 1, no
FREE_DESCRIPTOR_SET_BIT), which Free destroys. Every set's count of
each type is first checked against the device's maxDescriptorSet*
limit; other descriptor types throw. The [descriptors] profile line
counts the dedicated pools.
2026-10-07 22:37:10 +02:00
Chad d89076e9e7 fix(agc): retire host imports of ranges mapped again
A guest range mapped again at the same base and size (a fixed remap into
a registered range) kept its import, which still shows the pages pinned
or aliased before the remap. The GPU then read stale memory. Each import
now remembers its registry range (weakly, since a held range is a lease
the remap waits for) and is retired when the registry has a different
range at its base.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:56:21 -05:00
Michon 29aa275653 fix(agc): accept fragment Geometry and SampleRateShading only when the device enabled them
The validator takes the geometryShader and sampleRateShading enables from the
context (set where #295 enables them), so a device without them refuses the
draw instead of creating a module that needs features it never enabled.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 18:18:47 +02:00
alfalfa-star 98405e8f25 fix(agc): run draws whose pixel stage cannot run without its program
A depth-only pass can leave a stale pixel program address in
SPI_SHADER_PGM_LO/HI_PS while never setting the pixel registers it does
not need. The draw then failed with "missing register at DWORD 0x1b3"
(SPI_PS_INPUT_ENA), although the hardware never launches that program:
with no color written (CB_TARGET_MASK & CB_SHADER_MASK == 0), no depth,
stencil or mask export (SPI_SHADER_Z_FORMAT 0 and those enables of
DB_SHADER_CONTROL clear), and neither KILL_ENABLE, EXEC_ON_HIER_FAIL,
EXEC_ON_NOOP nor EXEC_IF_OVERLAPPED set, the pixel shader has no effect
and is not executed.

PixelProgramUnset becomes PixelProgramSkipped: true for a zero program
address as before, and for such an inert pixel stage. Both use the
driver's null pixel program; the precheck and the decode agree.

PPSA14632 (LEGO Horizon Adventures) issues an HTILE expand this way
(DB_RENDER_CONTROL DEPTH/STENCIL_COMPRESS_DISABLE, CB disabled, depth
test and writes off) once per run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:31:39 +02:00
alfalfa-star 5822c26b59 fix(agc): bind a null index buffer to non-indexed mesh draws
A non-indexed mesh draw (DRAW_INDEX_AUTO, or a non-indexed indirect
record) gave the mesh program's hidden index buffer V# (user words 4..7)
a 4-byte range at the program's own code address, so the descriptor had
a guest address although the prologue never loads through it (the load
is predicated on the draw's index size). That range is bound from live
guest memory. When the program also reads guest memory by address, the
address build captures the program's code as an immutable snapshot, and
the two overlapped: the draw failed with "writable guest memory
overlaps an immutable snapshot", or, when the live bytes at the code
address had changed since the capture, "guest snapshot differs from the
writable memory it overlaps".

MeshIndexBufferDescriptor now returns a null V# (base and size 0, same
format word) for a non-indexed draw, which ShaderResources binds to its
empty placeholder buffer, and takes no unread address any more.

PPSA14632 (LEGO Horizon Adventures) hit this on its layered mesh-path
draws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 15:18:10 +02:00
Adrià a0fa346f40
Merge pull request #944 from Celegans12/feat/agc-sint-storage-images
feat(agc): implement sint storage images through uint views
2026-10-07 13:56:17 +02:00
Celegans12 6e34e8614e feat(agc): implement sint storage images through uint views
Storage images of a SINT format threw "storage image descriptor uses
an unsupported format"; only write-only 32_SINT was accepted.

The recompiler now writes 16_SINT, 8_8_SINT, 32_SINT, 16_16_SINT,
8_8_8_8_SINT, 32_32_SINT, 16_16_16_16_SINT and 32_32_32_32_SINT
storage images with the Uint numeric class. 8- and 16-bit components
are saturated (EmitFormatStoreComponent) and 32-bit ones stored
unchanged, as measured on RDNA2 (gfx1035); image_store_pck stores
32-bit components unchanged too. The driver binds these surfaces
through a cached view of the same-size UINT format.

8_SINT, 32_32_32_SINT, the packed SINT formats (11_11_10, 10_11_11,
2_10_10_10, 10_10_10_2), D16 stores, packed stores of 8- and 16-bit
components and atomics other than 32-bit ones on 32_SINT and 64-bit
ones on 32_32_SINT still throw. New test: agc_driver_image_store_sint,
with DST_SEL X,Y,0,1 on the two-channel formats, as with X,Y,Z,W the
hardware writes channel 1 from the W data slot (TechnicalDebt).
2026-10-07 11:56:04 +02:00