Commit Graph

2376 Commits

Author SHA1 Message Date
Niko a69f5e0371 test(recompiler): compare stored resource plans field by field
The plan tests compared a decoded plan with the original only through
the codec itself (re-encoding must give the same bytes) and through
materialization. Neither catches a field that EncodePlan and DecodePlan
both leave out: the bytes still agree, and the walk does not read every
field. Leaving userDataBase, the plan's sRGB formats, the value ids or
several MemoryInfo fields (secondaryOffset, dataDwords, dataFormat,
planningOnly, idxen) out of both sides passed every test. The hand-built
plan also never materializes (its uniform values cannot be evaluated),
so its 1000 runtimes all failed with the same error, and no plan held an
immediate wider than 32 bits.

samePlan compares every field of two plans without the codec: the value
graph by storage index (opcode, type, id, flags, register, immediate,
arguments, phi blocks), the block ids, the roots, and the plain data
through the structures' own equality. The round trip now requires it
for the 14 fixtures and the hand-built plan, which sets every MemoryInfo
field, all indirect table fields, a second buffer, the whole uniform
fill and a 64-bit immediate to values other than their defaults. The
second process also builds each fixture's plan with its own front end
and requires it to equal the plan it used, so a stored plan must match
what the front end builds in another process, not only its own
re-encoding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko ed3f2776ed fix(recompiler): reject a stored plan whose image table names a missing source
DecodePlan checked every descriptor source index a plan holds except the
two inside an indirect image table: the heap source and the material
source. A damaged entry could therefore decode with a table that points
past the plan's descriptor sources, and only the materializer's own
bounds checks stood between it and an out-of-range read. The front end
interns both sources before the image source that names them, so a plan
it builds always passes the new check.

agc_shader_disk_cache now requires a plan whose table heap or material
source lies past the sources not to decode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 1ed6c21067 perf(recompiler): load the resource plan from the disk cache before the front end
getSource built ResourceProgram (PrepareResourceProgram and ExtractPlan)
for the first request of every shader a process saw, before
findOrCompileVariant looked in the disk cache. With a warm cache every
launch still decoded, built the CFG, structurized, translated and ran
SSA, the fold passes, SrtWalker and ResourceTracker for every shader;
only SPIR-V emission was skipped.

getSource now builds the plan key and tries LoadPlan first. On a hit it
sets the plan and leaves the program null: findOrCompileVariant already
rebuilds the program when no stored variant fits the layout. The loaded
plan takes the request's code address as its shader hash, as a plan
built in this process would. On a miss it builds the plan as before and
stores it. A plan the front end refuses is not stored. The disk is not
used when the cache is off, under the debug probe, with APS5_DUMP_IR
(which prints the front-end passes), or with APS5_NO_PERF_DISK_PLANS=1,
a cache-neutral switch for comparisons.

FrontEndBuilds() counts PrepareResourceProgram calls. The new
cross-process test prepares, captures and materializes 14 shader
fixtures in one process and repeats it in a second process sharing the
cache directory: the second process must run the front end 0 times, hit
14 plans and 14 variants, and produce byte-identical SPIR-V, bindings,
push constants, snapshots, read traces and plans. A damaged plan entry
is rejected and rebuilt alone with the same output and stored again; a
changed code word and another switch value miss.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Niko 0c144502e5 feat(recompiler): store front-end resource plans in the shader disk cache
The disk cache keeps compiled variants (SPIR-V, shader info, binding
layout) but not the IrResourcePlan the front end extracts for the
resource walk, so a process can only get a plan by running the whole
front end. Since #471 the plan owns its values (ownPlanValues clones the
graph behind every root into valueStorage and blockStorage), so it can
be written out and rebuilt.

- EncodePlan writes the value graph in storage order, each value as
  LEB128 integers: opcode, type, id, flags, register, operand count,
  the immediate if it has one, each argument as the zigzag distance to
  the value's own index (0 for null) and, for phis, the index of each
  predecessor block. The roots (descriptor source dwords, SRT reads,
  control-flow conditions, uniform-fill values) are value indices; the
  plain data (memory info, indirect image tables, control-flow edges,
  materialization sources, guarded, clean and pure flat slots, the
  flags, the ShaderInfo through the variant's encoder) follows. A plan
  whose roots or arguments point outside its storage is not encoded.
  Control-flow conditions can pull most of a program into the plan:
  a synthetic 2,038-dword compute shader with a branch every 20 dwords
  has 42,775 plan values, which take 518 KB this way and 2.5 MB with
  fixed-width fields; its compiled variant entry is 935 KB.
- DecodePlan rebuilds the graph the way ownPlanValues does and rejects
  an unknown opcode, an overlong integer, an index past the values or
  blocks, a descriptor source, image, buffer or sampler past the
  sources, a branch without two successors, an edge past the blocks,
  and a uniform fill of more than four words.
- BuildPlanKey is the in-memory source key (RecompileCacheKey::Build and
  the host subgroup size), every code word and the switch key, after a
  plan entry tag. It leaves out the binding layout, so all layouts of a
  shader share one plan.
- EncodePlanEntry/DecodePlanEntry use the variant's header and hashes;
  LoadPlan and StorePlan use the same directory and background writer
  and count plan hits, misses, writes and load failures on their own.
- Size asserts cover the plan and the structures it is written from.
- FormatVersion 30.

agc_shader_disk_cache now round-trips the plans of 14 in-tree shader
fixtures (SRT reads, guarded and chained pointers, control-flow
conditions, an indirect image table, a loop phi, a fragment shader) and
a hand-built plan with a uniform fill. For each it materializes the
original and the decoded plan over 1000 random user-data and memory
runtimes and requires the same snapshot, poison, null-root count and
read trace, or the same error. Truncated, bit-flipped and
other-format entries are rejected, malformed plans do not decode, and
the key follows the code, the target and the host subgroup size but not
the layout, addresses or user data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:31:17 +02:00
Adrià 67e86d93d5
Merge pull request #4250 from shahulmubeen-a/perf/shader-byte-buffer-stores
perf(shader): store 8- and 16-bit buffer components as bytes
2026-10-13 17:47:54 +02:00
Adrià e6fae88a42
Merge pull request #4684 from XairRrR/fix/agc-acquire-without-waiting
fix(agc): drop a presentation instead of waiting for a swapchain image
2026-10-13 17:36:29 +02:00
billybobpicklio d7818c9e55 fix(agc): accept integer tessellation partitioning
A draw with VGT_TF_PARAM.PARTITIONING 0 (PART_INTEGER) was refused by the
state decoder and its hull and domain programs by the recompiler. It is now
drawn with Vulkan's equal spacing, the mode radeonsi programs as
PART_INTEGER, with tessellation factors bounded at 64 instead of
fractional-odd's 63. Power-of-two and fractional-even partitioning still
throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:11:46 -04:00
Adrià 8bf10e51f4
Merge pull request #4554 from queeeef/fix/agc-sampled-dcc-key-writers
fix(agc): wait for recorded writes over a sampled texture's DCC keys before reading them
2026-10-13 17:04:12 +02:00
Adrià a74f2efe84
Merge pull request #5097 from TideOPL/fix/ancillary-layer-export
fix(shader): read the ancillary render target index from Layer when the draw exports it
2026-10-13 17:01:11 +02:00
Lexroid 7370aaf6d0 feat(agc): decode GS_FAST_LAUNCH geometry with one point per subgroup
VGT_SHADER_STAGES_EN.GS_FAST_LAUNCH = 1 (mesh-style NGG, as RADV programs
it on gfx10.3) was refused. With a point list and GE_CNTL groups of one
point, it launches one subgroup per input point and the first thread
reads that point's index in the ES vertex id VGPR, which the mesh
prologue already provides. The subgroup's primitive limit comes from
GE_NGG_SUBGRP_CNTL.PRIM_AMP_FACTOR, and the GS and ES VGPR counts are
not required to be full.

Fast launch 2, other group sizes or inputs, and indexed fast-launch
draws still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 17:00:45 +02:00
Dean Galvin 0adbdcf45c perf(agc): cache a draw key the second time it is seen
A draw whose key has no entry captured its stages, built a variant per
stage (a copy of every captured region) and inserted a draw entry. A key
that never comes back paid for the variants and the entry for nothing,
and its entry pushed a live one out of the cache. Draws whose user data
changes every frame are such keys.

The driver now remembers the keys it has seen once, in a table of
four-way buckets holding four times the cache's size in keys. A key gets
its entry at its second sight; a key that already has an entry still
takes new variants at once. A full bucket replaces its oldest key, so
keys that share a bucket do not push each other out until more than four
of them come between two sights of one.

Test: agc_driver_draw_cache_reuse draws three times. The first two draws
find no entry, one entry is inserted, and the third draw hits it. A
sighting table of one bucket keeps two keys drawn in turn and replaces
its oldest key when full.
2026-10-14 03:53:28 +13:00
Adrià 60fb618708
Merge pull request #4551 from eljackjoestar-cmd/codex/preserve-color-slice-xor
fix(agc): preserve array slice XOR in color target views
2026-10-13 16:52:35 +02:00
Adrià f2c314e902
Merge pull request #4577 from Moaziz667/fix/prime-remaining-host-mutexes
fix(agc,shader): prime the remaining host shared mutexes
2026-10-13 16:45:17 +02:00
Adrià b4d76ba609
Merge pull request #4556 from Amoenus/codex/fix-inactive-covered-centroid
fix(agc): admit inactive covered-centroid mode
2026-10-13 16:43:05 +02:00
Adrià 69b4ca5d9b
Merge pull request #4215 from Mouad-Scriptz-0/fix/agc-flip-eop-event
fix(libSceAgcDriver): raise the graphics end-of-pipe event from flip packets
2026-10-13 16:39:04 +02:00
JJenkx f7fd7569fa fix(agc): retire fully unmapped multisample targets 2026-10-13 10:37:07 -04:00
Shahul Mubeen A 066a8fbe30 perf(shader): store 8- and 16-bit buffer components as bytes in compute
A formatted store of an 8- or 16-bit component into a bound buffer
patched the dword around it with a compare-exchange loop (writeBuffer
in SpirvRuntimeBuffer.cpp). Ghost of Yotei's light-culling compute
pass (program 0x800038be00, 240x135 groups per frame) stores millions
of such components per dispatch and took 426 ms of GPU time per
dispatch on an RTX 4070 Ti SUPER.

Compute stores now go through a byte view of the bound buffers
(buffers_u8, an aliased declaration of the buffers binding, defined
on the first narrow store): one OpStore per byte under
StorageBuffer8BitAccess, which the shader device profile already
requires. Without the capability, and in every other stage, the
compare-exchange path remains: the draw-time validator accepts only
u32 and u64 aliased views. Loads, dword stores and stores through
GPU-selected descriptors (BDA) are unchanged.

Ghost of Yotei (PPSA26344 02.002.000), Windows 11, RTX 4070 Ti SUPER,
logo phase: that dispatch 426 -> 10.7 ms of GPU time, 1.9 -> 11 fps.

agc_formatted_buffer_byte_stores compiles a formatted 8_8_8_8 store
as compute with and without StorageBuffer8BitAccess and as a vertex
shader with it, and checks the byte view, the byte stores, the
compare-exchange loops and the fallbacks. agc_driver_formatted_store_unaligned
executes 8- and 16-bit formatted stores at misaligned offsets,
including stores that cross a dword, and checks every byte of the
output including the untouched neighbours.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 16:36:56 +02:00
Adrià 80e70b4292
Merge pull request #4408 from santhoshkumar-dev/codex/agc-render-target-vertex-aliasing
fix(agc): exclude skipped vertex records from target alias checks
2026-10-13 16:29:52 +02:00
eljackjoestar-cmd 13818efc3b fix(agc): preserve array slice XOR in color target views 2026-10-13 16:25:05 +02:00
TideOPL de5279bc0c fix(shader): serialize the pixel layer export flag from request version 23
main allocates request version 22 to the tessellation patches per group, so a version 22 request has no layer export byte.
2026-10-13 15:23:21 +01:00
Adrià 146f171549
Merge pull request #4271 from eljackjoestar-cmd/codex/reclaim-source-ir
perf(shader): reclaim unreachable cached source IR values
2026-10-13 16:22:26 +02:00
Daan Dehing a910dee09f fix(agc): wait for recorded writes over a sampled texture's DCC keys before reading them
textureClearKeys reads the keys through a raw pointer, past the flush hook, so writes the
recorder holds for the key range are not in the bytes yet. A title that clears the keys of a
target with a kernel and samples it in the same batch had its keys read from the memory's
previous contents: in Palworld (PPSA23126) these were the float data of a reused heap, so the
keys read as mixed and the texels were read as stored.

When the keys read do not decode (not uncompressed, not a clear the format can fill),
WaitForKeyWriters answers from the pending fills and stores it knows, or waits for the batch
that writes the keys, and the keys are read again. Keys that decode are used as before.

RunSampledKeysBehindKernel (agc_driver_dcc_keys_first_write): key memory holding float bytes,
then the 0000 clear kernel recorded without a wait; the sampled texture's keys read Clear0000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 16:18:50 +02:00
Adrià 759c66564f
Merge pull request #4428 from hypermoist/perf/address-space-import-runs
perf(agc): keep adjacent ranges one import serves as one region of the cached address space
2026-10-13 16:18:14 +02:00
TideOPL 7015c0443e fix(shader): read the ancillary render target index from Layer when the draw exports it 2026-10-13 15:15:55 +01:00
Adrià ac6d1b2013
Merge pull request #4163 from Celegans12/feat/agc-tessellation-patch-inputs
feat(agc): read hull patch constants in tessellation domain programs
2026-10-13 16:15:23 +02:00
Adrià 8c772ad342
Merge pull request #4265 from fisherjoey/fix/fiber-host-stack
fix(libSceFiber): run fibers on host stacks with room for host calls
2026-10-13 16:12:14 +02:00
Adrià d0711248b1
Merge pull request #4279 from karanvora2599/fix/shader-coherent-gds
fix(shader): declare the GDS buffer coherent
2026-10-13 16:04:55 +02:00
Mouad-Scriptz-0 326e0bc29f fix(agc): raise the graphics end-of-pipe event when a flip completes
A flip packet now delivers the queue's end-of-pipe event once the work
recorded before it completes, as a RELEASE_MEM with INT_SEL does. Minecraft
(PPSA17221) registers the graphics event, never requests an interrupt in its
releases, and waits on the event after every flip while holding its render
lock, so it presented one frame and then waited forever.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 15:49:45 +02:00
eljackjoestar-cmd 57b2dc8584 perf(shader): reclaim unreachable cached source IR values 2026-10-13 15:48:04 +02:00
Adrià e152ba8f69
Merge pull request #4209 from karanvora2599/fix/shader-cube-gradients
fix(shader): keep cube image gradients out of the face coordinate bias
2026-10-13 15:45:43 +02:00
Adrià 6cd052848a
Merge pull request #4210 from karanvora2599/fix/agc-depth-before-shader
fix(agc): test depth before pixel shaders that ask for it
2026-10-13 15:44:24 +02:00
Karan Vora 0998eb4f23 fix(shader): declare the GDS buffer coherent
GDS is one memory for the whole GPU on AMD hardware: every work-group's
ds_read and ds_write with gds see the others' writes, with no cache in
between. The recompiler keeps it in a storage buffer that was not decorated
Coherent, so on a GPU whose L1 caches storage buffer loads, NVIDIA's from
Turing on among them, a plain GDS read could return a value cached before
another work-group's GDS write. Atomics were not affected. The gds buffer is
now declared Coherent.

verifyCoherentGds in agc_shader_memory compiles a ds_read_b32 and a
ds_write_b32 on GDS and checks that the gds buffer is Coherent.
2026-10-13 15:43:31 +02:00
Adrià 2cf23fcc56
Merge pull request #4114 from Celegans12/fix/agc-tessellation-num-patches
fix(agc): bound tessellation patch records by the draw's patch count
2026-10-13 15:40:48 +02:00
fdfawef324 81364388a0 fix(libSceFiber): run fibers on host stacks on Windows
#4265 gives each fiber an mmap'd host stack on Linux and leaves Windows
as it was: there a fiber still runs host calls inside the context the
title passed to sceFiberInitialize. Demon's Souls packs its 16 KiB job
fiber contexts back to back. A job that calls
sceAgcCreateInterpolantMapping reaches shader preparation on the fiber
(Driver::ResolveShaderAbi, PrepareRegistered, the RDNA decoder,
BuildShaderStageInputInfo), goes more than 16 KiB deep and overwrites
the frames at the top of the context below it. On the loading screen
after the opening cinematic the fiber that owns that context then
failed the title's stack check, or returned to 0x7fffffff.

On Windows each fiber now gets a host stack from VirtualAlloc, the
context size plus 1 MiB, committed like the 1 MiB host margin of guest
threads, with a PAGE_NOACCESS page at the bottom. While the fiber runs,
the TIB stack base and limit and the deallocation stack describe that
host stack, as Windows does for its own fibers, so stack probes and
GetCurrentThreadStackLimits see the stack the fiber runs on. A host
stack that cannot be allocated, protected or released throws instead
of falling back to the context.

agc_driver_gpu_map_on_fiber: GetCurrentThreadStackLimits on a fiber now
returns its host stack, which has room for the import, so on Windows a
GPU mapping made on a fiber imports on the fiber's stack instead of a
host thread. The fiber's context is still not written.

Test: on Windows, fiber checks that the TIB stack bounds inside a fiber
cover its host stack, at least the context plus 256 KiB. With #4265
alone it fails at "the thread's stack bounds are the fiber's host
stack", and without that check at "a host call from a 16 KiB fiber
writes nothing below its context"; with this change fiber and
agc_driver_gpu_map_on_fiber pass (Windows Server 2025, RTX 3060,
WinLibs GCC 15.2.0, Release).

Demon's Souls (PPSA01342) on that host: at the loading screen the two
16 KiB job fibers above the overwritten one now use 17,592 and 16,936
bytes of their host stacks, the title's stack check no longer fails
there, and the load goes on until a separate Vulkan allocation failure
(vkAllocateMemory of a 4 MiB host-visible buffer, out of device memory).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:37:23 +02:00
Adrià 3a60a0d054
Merge pull request #4011 from karanvora2599/perf/shader-f16-compare-any-nan
perf(shader): skip the f16 NaN rebuild for operands that are only compared
2026-10-13 15:34:02 +02:00
Karan Vora 73eaa1f18b fix(shader): keep cube image gradients out of the face coordinate bias
image_sample_d and image_sample_cd on a cube pass their gradients through the
same conversion as the face coordinates, which subtracts 1.0 to move AMD's
[1, 2] face range to [0, 1]. A gradient is a difference of coordinates, so the
bias turned a one-texel gradient into one of about -1 and the sample read the
smallest mip. Gradients now skip the bias.

agc_driver_image_sample_cube_derivatives samples every face of a 16x16 cube at
five levels through image_sample_d and checks the level and face it read.

TechnicalDebt records that the gradient handling follows Mesa and was not
measured on hardware.
2026-10-13 15:26:04 +02:00
Adrià a252fcd75f
Merge pull request #4012 from queeeef/perf/agc-zero-dword-fill
perf(agc): handle the zero dword fill kernel as a buffer fill
2026-10-13 15:21:00 +02:00
Karan Vora 320bd37c92 fix(agc): test depth before pixel shaders that ask for it
DB_SHADER_CONTROL.DEPTH_BEFORE_SHADER (bit 12) is what a pixel shader compiled
for early depth/stencil sets: the depth and stencil tests and writes happen
before the shader runs, even when it kills pixels or writes memory. The bit
was accepted but never read, so such shaders got EarlyFragmentTests only when
Z_ORDER was EARLY_Z_THEN_LATE_Z and they had no kill or exports. Their killed
pixels then left depth unwritten, and occluded pixels still wrote memory.
EARLY_Z_THEN_RE_Z (Z_ORDER 3) was not treated as early either.

DecodePixelStageInfo now sets earlyZ for DEPTH_BEFORE_SHADER, and for Z_ORDER
1 or 3 under the existing kill and export conditions, including the stencil
reference export. The backend no longer repeats those conditions; it keeps
only the alpha-to-coverage one, so a shader with alpha-to-coverage still runs
its tests late. earlyZ keeps its place in the request, so the serialization
version does not change.

earlyFragmentTestsTests in agc_driver_graphics checks which DB_SHADER_CONTROL
values declare EarlyFragmentTests, and that alpha-to-coverage keeps Z_ORDER 1
and DEPTH_BEFORE_SHADER late, directly and after a serialization round trip.
stencilRefExportTests checks that DEPTH_BEFORE_SHADER with a stencil reference
export still decodes as early.

TechnicalDebt records that the bit follows Mesa and that its combination with
a Z export, a mask export, a stencil export or alpha-to-coverage was not
measured.
2026-10-13 15:19:33 +02:00
Karan Vora 352bf304fe perf(shader): skip the f16 NaN rebuild for operands that are only compared
With float16DenormPreserve, EmitF16BitsToF32 rebuilds the bits of a NaN
half after UnpackHalf2x16, because NVIDIA returns the default NaN and
Intel quiets it. When every use of the converted operand is an f32
comparison or a NaN test, directly or through abs and neg, only whether
it is NaN matters, so the conversion returns UnpackHalf2x16's result as
is. v_cmp_*_f16 goes from 11.5 to 4 SASS on an RTX 4060.

agc_driver_f16_to_f32_unpack adds v_cmp_u_f16 and v_cmp_lt_f16 over all
65,536 halves with each decode the device supports. Its SPIR-V check
requires the v_cvt_f32_f16 conversion to rebuild the NaN bits and the
compared conversions not to.
2026-10-13 15:11:54 +02:00
xXPBSXx c6a4223420 fix(agc): sample 10_10_10_2 SNorm and SInt textures
Guest texture formats 51 (SNorm) and 55 (SInt) of the 10-bit packed group were missing from the texture lookup and the descriptor format table, so a descriptor using them threw 'unsupported guest texture format' while the UNorm (50) and UInt (54) siblings worked. Map them to A2B10G10R10_SNORM_PACK32 / A2B10G10R10_SINT_PACK32, classify them for sampling (float / signed integer), and list SINT among the integer formats.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 14:56:03 +02:00
Adrià Franch dd49e1df93 build(agc): put libc's directory on PATH for the register key test on Windows
The sibling AGC driver tests get this through the agcTest loop; without it the test exits 0xc0000135 (DLL not found) on Windows.
2026-10-13 14:49:31 +02:00
Adrià Franch 89414d3039 build(agc): link register key test with libc and libkernel like its sibling tests
The Windows build failed to start it (0xc0000135, DLL not found).
2026-10-13 14:49:24 +02:00
TibixDev 5942b8c7f1 perf(graphics): track draw register fingerprints incrementally
Track mutations against the current draw dependency mask and preserve device and shader registry identity in the draw key. Validate mutations, projected masks, PM4 packets and prepared graphics against full recomputation.

Written with AI assistance.
2026-10-13 14:49:24 +02:00
Romans Pokrovskis 44c4b3f8bf fix(agc): admit inactive covered-centroid mode
Admit PA_SC_AA_CONFIG bit29 only without active perspective or linear centroid inputs, or with a skipped pixel program. Active/unspecified centroid inputs and unrelated AA fields remain refused. Source: pinned PS5_Mesa 0b2d6d1a GFX10.3 schema/RADV producer; existing ENA/ADDR intersection. Console covered-centroid interpolation remains unmeasured.

Full build/all 124 runtime libraries, conventions and registration pass. Final Windows suite: 965 pass, zero fail, seven skips of 972. Isolated state test passes 20 repeats and fails 5/5 with main State.cpp; four guard mutants fail 5/5 each. Original two title register captures reproduce the main refusal and pass this check with candidate, five times each. NVIDIA relevant groups pass; AMD draw-cache failure also occurs on unchanged main and is reported in issue4435.

AI-assisted: Codex.
2026-10-13 15:34:41 +03:00
Adrià a164b1d711
Merge pull request #1359 from TibixDev/pr/copied-write-ordering
fix(agc): order direct reads after copied writes
2026-10-13 14:25:22 +02:00
Adrià 398ccaaf96
Merge pull request #5108 from adriadam10/ci/agc-graphics-tests-link-driver
build(agc): link the graphics and BDA device tests against libSceAgcDriver
2026-10-13 14:19:16 +02:00
Adrià 96f3e9fe5d
Merge pull request #4041 from ee6aba54/fix/agc-cmask-unknown-codes
fix(agc): keep the texels of CMASK tiles with codes other than 0 and 15
2026-10-13 14:11:16 +02:00
Adrià Franch 14f2092c9e build(agc): link the graphics and BDA device tests against libSceAgcDriver
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:53:51 +02:00
Celegans12 247162dc4f feat(agc): read hull patch constants in tessellation domain programs
TessellationLowering refused every domain-program read of the off-chip
ring outside its patch's control points, so domain programs that read
the hull's patch constants, like the quad terrain domain in Ghost of
Yotei at 0x8000 - 208 * (rel + 1), aborted.

Such reads now lower to a new PatchInput kind and become Patch inputs.
Hull outputs and domain inputs place a constant by its off-chip address
modulo the record stride, in a record-sized array at location
ceil(hsStride / 16); each program derives both on its own, so the two
interfaces match although they compile separately. Domain reads at an
address that does not move with the relative patch, across more than
one record or inside the control-point region still throw, as does a
domain program that reads patch constants but no control point. Hull
patch-constant writes now take the same bound: above the control points
of all NUM_PATCHES patches, not just one patch's.
2026-10-13 13:50:53 +02:00
Celegans12 25a83722c7 fix(agc): bound tessellation patch records by the draw's patch count
TessellationLowering proved ring and LDS addresses affine over a
relative patch index of up to 256. A hull that lays its patch-constant
records out downwards, at 0x8000 - 208 * (rel + 1) like the quad
particle hull in Ghost of Yotei, then reached a negative address for
the last slot and aborted as "not affine". The relative patch index of
hull and domain programs stays below VGT_LS_HS_CONFIG.NUM_PATCHES
(bits 7:0, patches per threadgroup), which is 63 for that draw.

DecodeShaderStages now carries NUM_PATCHES as patchesPerGroup in the
tessellation configuration (draw keys, hull and domain cache keys,
recompile request version 22) and refuses zero, and the lowering bounds
the relative patch index by it. Configurations without it keep 256.
2026-10-13 13:50:27 +02:00
Adrià ff8fe4dbc1
Merge pull request #3988 from karanvora2599/perf/shader-f16-unpack-half
perf(shader): convert f16 operands with UnpackHalf2x16 where f16 denormals are kept
2026-10-13 13:48:59 +02:00
Adrià Franch f20a32bec3 build(agc): link agc_driver_mesh_tests against libSceAgcDriver
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:46:45 +02:00
AlxcBTW 230b5c2683 perf(agc-driver): join the imports of adjacent large ranges when a buffer is bound across them
8 MiB window, so a range larger than the window is always imported alone. A
buffer bound across two such ranges then has no import and is copied whole on
every use: PPSA25344 binds a 368 MiB buffer across a 320.75 MiB and a 47.3 MiB
range and copies it on almost every frame.

When UploadFinish meets such a region, it imports the run of the ranges the
region crosses, provided every one of them is larger than the window, and
retires their own imports. It does this before any region of the build
confirms an import, so the retire moves the epoch ahead of every pointer the
build would use. A range that large is registered whole, not grown in 64 KiB
pieces like a heap, so the joined import is not re-imported as a heap grows; it
stands in refreshImports while exactly its ranges stand. A later join that
reaches a joined import takes it in, so two joins that share a range never
retire each other in turn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:45:21 +02:00
Adrià Franch 1137c57572 ci(windows): build only the execution tests that run without a Vulkan device
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 13:44:08 +02:00
XairRrR 95f34a0504 fix(agc): report GPU time per presented or dropped frame
The [present] line divided the GPU busy and idle time of a window by the
presentations that got a swapchain image. That time covers the GPU work
of every frame, so with frames dropped for want of an image it read
several times too high (278.9 ms instead of about 52 ms on a hidden
window, reported on #4684). Those two figures are now divided by the
presented and dropped frames; the per-present blit figures are unchanged.
2026-10-13 13:34:19 +02:00
XairRrR 37d9ef133f fix(agc): drop a presentation instead of waiting for a swapchain image
The presenter acquired the next swapchain image with a 5 s timeout
before completing the guest's flip. With FIFO on Wayland, NVIDIA's
driver hands out about one image per second while the window is hidden
(another workspace or occluded): vkAcquireNextImageKHR blocked for
~1 s per flip, and the title ran at exactly 1 frame per second. Stack
snapshots of Uncharted: Legacy of Thieves Collection (PPSA05684) showed
the presenter in drmSyncobjTimelineWait under AcquireImage in every
snapshot taken during such a stall, and in none outside them.

The acquire no longer waits: VK_NOT_READY or VK_TIMEOUT drops that
presentation, as an out-of-date swapchain already does, and the flip
completes. APS5_PROFILE_DRAW's [present] line counts the drops.

Menu with the window moved to an empty workspace for 50 s: 7.5 fps and
about 65 drops per 10 s while hidden (1 fps before), no drops while
visible.
2026-10-13 13:30:47 +02:00
Dean 5d50886ee4
Merge pull request #2076 from jdmagic/feat/agc-stencil-plane-storage
feat(libSceAgcDriver): take a one-layer 2D array view of a depth surface plane as the plane
2026-10-14 00:11:01 +13:00
Dean 6f7cda184b
Merge pull request #4315 from JAB-dev/feat/agc-dcc-packed-float-clear
feat(agc): encode packed R11G11B10 float DCC fast clears
2026-10-14 00:10:16 +13:00
lorkydey d4b59ce9fa fix(agc): accept inactive vertex point size on non-point draws 2026-10-13 12:56:44 +02:00
Dean 075169f7b3
Merge pull request #4404 from eljackjoestar-cmd/codex/demanded-shader-preparation
fix(agc): prioritize demanded shader preparation
2026-10-13 23:42:40 +13:00
Dean 768cd93977
Merge pull request #4082 from Celegans12/feat/agc-quad-tessellation
feat(agc): implement quad tessellation with counter-clockwise output and level clamps
2026-10-13 23:42:25 +13:00
Dean 1d7b075610
Merge pull request #4496 from Kikoarl/perf/agc-gds-dma-and-completion-label-sync
perf(agc): disambiguate completion label overlaps in CopyBuffer
2026-10-13 23:42:11 +13:00
Adrià Franch 3949fb9d44 test(agc): skip copied-direct ordering without Vulkan and find libc on Windows
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-13 12:34:08 +02:00
TibixDev 29b227bf35 fix(agc): complete copied writes before direct consumers
Wait for overlapping CPU writebacks and completion labels before importing buffers or accepting cached image resources. Cover texture data and compression metadata, retain asynchronous disjoint and GPU-only dependencies, and avoid recursive completion waits.

Reproduced fresh-buffer and cached-image failures with CPU Vulkan before fixing them. Regression covers fresh/reused bindings, dispatch/draw copied writers, texel/metadata updates, labels and no-wait cases; passes CPU Vulkan and RX 6600 synchronization validation. Graphics and guest-memory suites pass. The Skyrim loading GPU fault remains unattributed.

AI-assisted implementation and independent code review.
2026-10-13 12:34:03 +02:00
Dean 53aea1ff34
Merge pull request #5024 from oneandonlydean/up4/storage-store-keeps-untracked-writes
fix(agc): keep CPU writes when a pending storage result is stored over untracked memory
2026-10-13 23:33:08 +13:00
Dean 387b8a0faa
Merge pull request #3763 from eljackjoestar-cmd/codex/dynamic-rendering-layers
fix(agc): render all attachment layers with dynamic rendering
2026-10-13 23:32:53 +13:00
Adrià Franch 99ebc650c3 fix(agc): zero dword fill only for 32-bit output formats
The kernel stores through the V# FORMAT; with an 8- or 16-bit format it writes 1 or 2 bytes per element, which a zero fill of the whole range would also clear between. Same check as MatchDwordPatternFill (format 20-22), with test rows.
2026-10-13 12:29:00 +02:00
Daan Dehing 501453573c perf(agc): handle the zero dword fill kernel as a buffer fill
A wave64 compute kernel that stores dword 0 at index group * 64 + thread through a 4-byte
stride buffer is run as a buffer fill. Palworld (PPSA23126) clears its DCC keys with it every
frame; as a shader dispatch its writes were unknown to the DCC key proofs, so every refresh of
those targets waited for the GPU.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:27:46 +02:00
Dean Galvin cc3b47dda0 fix(libSceAgcDriver): take a one-layer 2D array plane view as a narrow exception to the non-2D refusal
Main refuses every storage view of a depth or stencil plane that is not
2D. A 2D array view is now taken as the plane only at layer 0 with one
layer; a view with more layers or another base layer still throws.

A one-layer 2D array view replaces a 2D view of the plane like any other
view, so a store back through the replaced 2D view throws. The depth
storage dispatch test runs one-layer views of both planes and checks
that the depth and stencil tests see what they stored, and that a
two-layer view still throws.
2026-10-13 23:17:51 +13:00
jdmagic e4a7cbfc83 feat(libSceAgcDriver): take a one-layer 2D array view of a depth surface plane as the plane
A storage view of a depth or stencil plane was taken for the depth surface
only as a 2D image. A 2D array view of layer 0 with one layer and the
plane's extent and format is the same plane, and now gets the plane's
storage image too; a view with more layers still takes the ordinary path.
The plane's storage image is recreated when the view's dimension changes.

PPSA01289 (Sackboy: A Big Adventure) binds the stencil plane of its
2880x1620 D32S8 surface as a one-layer R8_UINT 2D array from a compute
shader during presentation, and read guest memory instead of the stencil.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:15:15 +13:00
Karan Vora e4f212b2e2 perf(shader): convert f16 operands with UnpackHalf2x16 where f16 denormals are kept
Since #1463, every f16 operand read decodes sign, exponent and mantissa
with integer operations, 8 to 12 instructions more per read than
UnpackHalf2x16 on NVIDIA. The decode works around an AMD Windows driver
that folds the mask of a byte-selected half into UnpackHalf2x16.

A new target flag, float16DenormPreserve, is set when the device reports
shaderDenormPreserveFloat16 and denormBehaviorIndependence ALL, the same
rule as float64DenormPreserve. With it, EmitF16BitsToF32 uses
UnpackHalf2x16 and declares DenormPreserve for 16-bit floats, under
which SPV_KHR_float_controls keeps unpacked subnormal halves. NVIDIA
returns the default NaN for every NaN half, so the NaN bits are rebuilt
from the half: its sign at bit 31, its payload at bits 22:13. The
DenormPreserve 16 mode is declared once, shared with the native f32 to
f16 conversion. Without the flag (lavapipe; RADV reports 32_BIT_ONLY)
the integer decode stays. Halves of fields narrower than 16 bits, such
as SDWA byte selects, keep the integer decode on every device.

The flag is serialized (request version 17) and part of the cache key.

New test agc_driver_f16_to_f32_unpack converts all 65,536 halves, with
nonzero high bits, through v_cvt_f32_f16, and byte-selected halves
through v_add_f16 SDWA, with each decode the device supports, and
compares the bits with an exact decode. agc_driver_f16_to_f32_unpack_spirv
checks which decode the SPIR-V uses with and without the flag.
2026-10-13 12:15:00 +02:00
JAB-dev a96f8bd640 feat(agc): encode packed R11G11B10 float DCC fast clears
A fast clear of a packed float color target (format 36) was refused as unencodable. Give the format
a DCC layout (11, 11 and 10 bit float channels) and its one value, so 0000, 0001, 1110 and 1111
clear codes fill the texels and a texture keeps those keys.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 23:14:10 +13:00
Adrià 8bcd97cd5b
Merge pull request #1418 from gjonn/feat/agc-unnormalized-implicit-lod
feat(agc): sample the base level through unnormalized samplers
2026-10-13 11:49:07 +02:00
Dean Galvin 4a4bea9d1b test(agc): skip the clip and cull distance draw without a Vulkan device 2026-10-13 22:26:59 +13:00
Sanjaysrinivas 0612c181ff fix(agc): avoid storage snapshot overlap
Create uploads before publishing snapshot validity. Keep allocation
edits separate from the per-unit validity changes in #5024.
2026-10-13 11:11:58 +02:00
eljackjoestar-cmd dedb869fa2 test(agc): shut down the layered submission driver before exit 2026-10-13 13:07:18 +04:00
eljackjoestar-cmd 2ae227ef85 fix(agc): integrate merged position exports and request version 21 2026-10-13 13:03:12 +04:00
eljackjoestar-cmd 78ad5d9dfb fix(agc): validate dynamic layer limits and host subgroup size 2026-10-13 13:01:17 +04:00
eljackjoestar-cmd 13c4a2a6c6 fix(agc): preserve main interfaces in the layered rendering rebase 2026-10-13 13:00:51 +04:00
eljackjoestar-cmd a103e48c8b test(agc): use the current fragment push slot in layered readback 2026-10-13 13:00:28 +04:00
eljackjoestar-cmd 93a090ed8d test(agc): retain array-view checks after merge 2026-10-13 13:00:28 +04:00
eljackjoestar-cmd 60a1649e76 fix(agc): render all attachment layers with dynamic rendering 2026-10-13 13:00:28 +04:00
alfalfa-star 425a4daf16 fix(agc): report layer exports as ignored only when a draw does not route them
The warning fired for every draw with PA_CL_VS_OUT_CNTL layer bits,
including layered draws that now route the render target index. It is
checked after the color targets are decoded and leaves out the routed
bits and the misc vector enable. The comment that said layer exports
are never routed is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:28 +04:00
Dean 02d4839b60
Merge pull request #4584 from xXPBSXx/fix/agc-line-strip-primitive
fix(agc): decode primitive type 3 as a line strip
2026-10-13 22:00:05 +13:00
alfalfa-star dd70286889 test(agc): draw a layered volume through the driver
agc_driver_layered_volume_submit submits a DCB whose context registers
alone describe a 3D render target (CB_COLOR_ATTRIB3 resource type 3D,
CB_COLOR_VIEW slices 1-3) and set PA_CL_VS_OUT_CNTL USE_VTX_RENDER_
TARGET_INDX, with both shaders registered before the draw. It checks
that each band lands in its own slice. Removing the layer export from
the vertex stage info in Driver::draw, or the bit-18 read in
DecodeState, fails it. Two more runs draw the volume under DCC keys:
uncompressed keys stay uncompressed, and 0000 fast-clear keys leave
every texel the draw does not write at the clear color and end
uncompressed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:04 +04:00
alfalfa-star 97d192eeae test(agc): pass the GPU flag to Mutation::Add in the layered volume test
Main added the GPU-visible argument to GuestAllocations::Mutation::Add.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:04 +04:00
alfalfa-star 1055617751 feat(agc): render layered draws into 3D color targets
A color view of several depth slices of a 3D target (CB_COLOR_VIEW
SLICE_START < SLICE_MAX) was rejected as "color views of several array
slices are unsupported". Titles render such volumes with one draw: an
instanced draw whose vertex stage picks each primitive's slice through
the misc position export (PA_CL_VS_OUT_CNTL USE_VTX_RENDER_TARGET_INDX
with VS_OUT_MISC_VEC_ENA).

Depends on #423, which carries PA_CL_VS_OUT_CNTL to the recompiler.

- DecodeColorBuffer takes the slice range of a 3D target
  (ColorTarget::layers, clamped to the surface's last slice), allows DCC
  on 3D targets, and sizes a tiled 3D target by its whole surface
  (SurfaceForTarget moves to State.cpp for that). DecodeState sets
  State::renderLayers and, for a layered draw, USE_VTX_RENDER_TARGET_INDX
  (State::layerExports); color targets of different slice counts and
  layered draws with a depth target throw.
- The driver adds State::layerExports to the PA_CL_VS_OUT_CNTL bits #423
  passes (it already passes VS_OUT_MISC_VEC_ENA), so the render target
  index reaches gl_Layer only for layered draws and stays ignored
  elsewhere as before.
- A vertex stage writing gl_Layer declares ShaderViewportIndexLayerEXT
  and SPV_EXT_shader_viewport_index_layer when the device offers them
  (the device enables VK_EXT_shader_viewport_index_layer when present),
  instead of SPIR-V 1.5 ShaderLayer, which a Vulkan 1.1 device cannot
  take. Graphics validation accepts the capability, the extension and a
  vertex Layer output when it is enabled, and a per-primitive Layer
  output of a mesh stage.
- The resident target's attachment view is a 2D array view of the
  slices, and the framebuffer has that many layers (Framebuffer and
  AcquireFramebuffer take the layer count).

The execution test agc_driver_layered_volume_draw draws three bands,
one per layer, into slices 1..3 of a 4-slice 64 KiB-tiled volume and
reads every slice back: slice 0 stays untouched and slice 1 + L holds
only band L. It also recompiles a mesh program with the layer export and
validates it against the layered state. The mesh case does not draw:
lavapipe's LLVM backend aborts compiling that mesh pipeline ("Cannot
emit physreg copy instruction"), which a test cannot catch. On RADV the
same mesh draw renders the bands into the right slices.

PPSA14632 (LEGO Horizon Adventures) renders its 64x64x64 RGBA16F
translucency lighting volumes (two DCC targets, 64 instances) this way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:00:04 +04:00
Sanjaysrinivas 3f314030b4 fix(agc): use storage factory in snapshot test 2026-10-13 10:59:20 +02:00
Sanjaysrinivas a296b9cacf fix(agc): register snapshot fixture GPU memory 2026-10-13 10:59:20 +02:00
Sanjaysrinivas 65eb466ddd fix(agc): avoid snapshot upload PR conflict 2026-10-13 10:59:20 +02:00
Sanjaysrinivas e2ee9430bf perf(agc): allocate CPU snapshots on demand 2026-10-13 10:59:20 +02:00
Celegans12 23342340cc feat(agc): implement quad tessellation with counter-clockwise output and level clamps
DecodeShaderStages accepted only clockwise triangle patches and no HOS
level clamp narrower than [1, 63], so terrain drawn as TRI_CCW quad
patches with VGT_HOS_MAX_TESS_LEVEL 9 aborted. Quad domains and TRI_CCW
now decode and emit Quads and VertexOrderCcw; isolines, other
partitionings and point or line output still throw.

VGT_HOS_MIN/MAX_TESS_LEVEL are read per draw and pushed in the last 8
bytes of the pre-rasterization push constant slot, where the hull reads
them to clamp its factors; they stay out of every key and the recompile
request, so one hull serves every level. Zero, negative and NaN outer
factors still cull; clamps outside 0 <= min <= 63, max >= 1,
min <= max throw. Quad hulls write four outer and two inner factors at
a 24-byte stride, the layout Mesa's TF ring store uses.
2026-10-13 10:56:16 +02:00
Dean 4bb2c7f394
Merge pull request #423 from MichonGoddijn231849/feat/clip-cull-distances
feat(agc): vertex clip and cull distances
2026-10-13 21:47:43 +13:00
Dean Galvin e5449936f4 fix(agc): keep CPU writes when a pending storage result is stored over untracked memory
On memory outside the write watch a write-back compares guest memory with
the bytes saved at the upload and leaves the 64 KiB blocks that differ to
the CPU. A store of some of an image's units marked the whole saved copy
invalid, and a fill turned into an image clear or an image copy never
saved the bytes under it, so the next store had nothing to compare and
wrote its layers whole over what the CPU had written since.

The saved copy is now valid per tracked unit. A store, a drop or a stale
eviction invalidates only its own units, a CPU-path store makes the units
it stored valid again, and the write-back compares each run of selected
units that still has saved bytes. FillClear and CopyFrom save the bytes
of the units they fill when the surface is not watched, as an upload does.

Results over a range that left the watch while they were pending still
have no saved bytes; storing them now throws instead of overwriting the
CPU's writes (technical debt).
2026-10-13 21:46:12 +13:00
Dean Galvin 8c04efcfbc fix(shader): load a BDA span in place from its own function
read_bda_span chooses between the span's in-place dword loads and its
per-dword byte fallback, which calls read_bda_dword_bytes. Each shape of
that choice so far hangs a group of NVIDIA drivers: a dispatch never
finishes and the device is lost.

As the two arms of one selection, with the loads emitted in the arm,
it hung drivers 591.86 and 590.48.01 when lanes took both arms (#3291).
As two selections, the loads under the span's condition and the fallback
under its negation (#4759), it hangs 595.97, 596.36, 610.62, 610.74 and
610.57.04 (#4975).

The loads now live in load_bda_span, a DontInline function that calls
nothing, and read_bda_span is one selection again: one arm calls
load_bda_span, the other is the fallback as before. Probe, loads,
lookups, faults and stops are unchanged for every lane.

On an RTX 4090 (Linux, driver 610.57.04) the two-selection shape hangs
agc_driver_scalar_wide_loads and agc_driver_flat_apertures_0..13 in a
build with SPIRV-Tools, and agc_driver_bda_span_reads and
agc_driver_pixel_interlock in a build without. All pass with this
change in both builds. Drivers 591.86, 590.48.01, 595.97, 596.36,
610.62 and 610.74 are not tested; a shape with the loads in their own
DontInline function was reported passing on 591.86 in #3291.

agc_shader_memory now requires that a span read function holds no load
and calls its load function once, and that the load function holds the
four loads and no call.

Refs #4975
2026-10-13 21:40:18 +13:00
Michon a72dcb93aa test(agc): clip and cull distances drawn through the device and the driver
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:37:49 +13:00
Michon 04138f50ad feat(agc): vertex clip and cull distances
PA_CL_VS_OUT_CNTL's clip and cull enables and its misc and distance vector bits reach the recompiler's vertex info (request version 20), the draw key, the vertex-info comparison and the recompile cache key. Registration and draw-time preparation set them the same way the draw does; a program without pos1..pos3 exports gets none, so its artifact does not depend on the register. Guest clip distances share main's gl_ClipDistance array with the all-zero position distance; cull distances need shaderCullDistance. Up to eight clip and cull distances are accepted.

Co-Authored-By: john.maia <hi@johnmaia.dev>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 21:37:49 +13:00
Santhosh Kumar 44623222c9 fix(agc): exclude skipped vertex records from target alias checks 2026-10-13 14:00:11 +05:30
Kikoarl 396b5189a9 perf(agc): disambiguate completion label overlaps in CopyBuffer 2026-10-13 09:56:09 +02:00
eljackjoestar-cmd 68c2b0f746 fix(agc): prioritize demanded shader preparation 2026-10-13 11:54:29 +04:00