Commit Graph

51 Commits

Author SHA1 Message Date
boykopovar 7c5f8b6e12 Revert "Merge pull request #143 from SamG-Coder/codex/shader-geometry-validation"
This reverts commit de49f81697, reversing
changes made to cfb1d76e60.
2026-10-01 22:55:27 +03:00
boykopovar de49f81697
Merge pull request #143 from SamG-Coder/codex/shader-geometry-validation
fix(shader): preserve NGG passthrough and wave32 launch inputs
2026-10-01 21:17:52 +03:00
SamG-Coder 4cd8e35366 fix(shader): preserve NGG passthrough launch and primitive inputs 2026-10-01 20:43:17 +10:00
SamG-Coder c329ad26b7 fix(shader): preserve upstream parameter validation during interpolation 2026-10-01 20:15:52 +10:00
SamG-Coder a8311e9d1a fix(shader): preserve interpolation instruction operands 2026-10-01 20:11:53 +10:00
boykopovar db5ce0f83c
Merge pull request #204 from oneandonlydean/fix/agc-misaligned-buffers-in-shader-data
fix(agc): patch the memory offsets of misaligned guest buffers in the shader data buffer
2026-10-01 11:31:57 +03:00
Dean Galvin aa9a5c02a7 feat(agc): run CB fast-clear eliminate and DCC decompress passes within the DCC model
Draws with CB_COLOR_CONTROL MODE ELIMINATE_FAST_CLEAR (2) or DCC_DECOMPRESS
(6) were rejected as "only normal color rendering with copy ROP". On
hardware these full-target blits write the fast-clear value into the
fast-cleared pixels (or store the pixels uncompressed); the shader's output
never lands. In this driver's DCC model (surfaces stored uncompressed,
uniform fast-clear keys make every read return the clear value) that is:

- keys that are a clear code: the target's texels become that value and
  its keys read uncompressed. Reads see the same values before and after.
- uncompressed keys, or a target without DCC: nothing changes.
- mixed keys (per-block metadata), a mipmapped DCC target (its keys are not
  decoded) or a register clear of a texel over 64 bits: throw.

The texels go into the target's resident image (its refresh already
cleared it to a code's value; the register code is cleared on the GPU with
FillClear) and reach memory through its write-back, or into guest memory
on the CPU when there is no resident image. A resident image made under
other DCC keys is remade under the target's keys by the storage cache
before the pass reads it. DecodeColorMetadataPass
requires the draw to be only that pass: copy ROP, no depth or stencil
work, no multisampling, a viewport and scissor covering each target.
Indirect passes throw. The CB_COLOR decode moves into DecodeColorBuffer,
which also reads CB_COLOR_CLEAR_WORD0/1.

The register clear code (DCC key 0x20) had no value before: its keys were
read as the stored texels. A target under register keys now reads as its
CB_COLOR_CLEAR_WORD texel: a resident target is cleared to it before a draw
renders into it (then its keys read uncompressed), and a non-resident
target's copy starts from it.

Every decision that materializes a clear reads the keys through
CurrentDccKeys, which first waits for the batches up to the newest recorded
writer of the key range. The title's fast-clear fill of the metadata can
still be pending on the GPU when the next draw is decoded; reading the
stale bytes there made a later draw clear the target over the results of
the earlier ones.

The remaining CB modes (resolve, decompress, FMASK decompress) keep the
rejection, which now names the mode.
2026-10-01 20:59:03 +13:00
Dean Galvin 81d6f94ceb fix(agc): patch the memory offsets of misaligned guest buffers in the shader data buffer
A guest buffer view off minStorageBufferOffsetAlignment is bound from the
aligned offset below it, and the difference goes into the shader's memory
byte offsets (3dec6ced), but only when those live in the push constants:
a shader whose shader data is a buffer failed with "a guest buffer off the
storage buffer offset alignment in a shader whose shader data is a buffer
is not implemented".

The adjustment now goes into the shader data buffer's byte at
memoryOffsetDword * 4 + element, and a template's data refresh writes the
dispatch's words with the same patches applied.
2026-10-01 16:09:16 +13:00
Dean Galvin 6a33a3d371 fix(recompiler): declare pixel shader inputs at their parameter slots
SPI_PS_INPUT_CNTL_n.OFFSET (bits 0-5) names the parameter slot pixel
input n reads, so several inputs may read one slot and an input may read
a slot the vertex stage never exports. With OFFSET bit 5 set and
FLAT_SHADE clear the input is the DEFAULT_VAL constant instead (bits 8-9:
0000, 0001, 1110, 1111). With OFFSET bit 5 and FLAT_SHADE both set the
slot in the low bits is passed through with its three vertices unchanged:
v_interp_mov P0, P10 and P20 read vertices 0, 1 and 2 rather than P0,
P1-P0 and P2-P0. The PS5 compiler writes such inputs as 0x422 and 0x423.

The old mapping masked OFFSET to five bits, so a defaulted input landed
on a real slot, and gave the second of two inputs reading one slot either
the same Location (two variables, "duplicate shader interface location")
or, when one was flat, a free Location the vertex stage never wrote
("graphics interfaces disagree at location N").

Now one input variable is declared per slot actually read, at Location =
slot, and inputs reading that slot share it. A slot read both flat and
interpolated (or through the perspective and linear pairs) is read per
vertex: flat readers take the provoking vertex, the others interpolate
with the barycentrics of their own pair. Pass-through inputs are read per
vertex at their slot without subtracting vertex 0, and reading one with
v_interp_p1/p2 is refused. Defaulted inputs read their DEFAULT_VAL
constant and declare nothing; inputs the program never reads are no
longer declared.
2026-10-01 08:38:47 +13:00
Dean Galvin cb66ec2004 feat(agc): centroid pixel inputs and NoPerspective by I/J pair
SPI_PS_INPUT_ENA/ADDR with PERSP_CENTROID (bit 2) or LINEAR_CENTROID
(bit 6) was rejected; PPSA21564 binds 0x326 (perspective center and
centroid, linear center, POS_X/Y).

The PS input VGPR layout now follows the hardware rule: SPI_PS_INPUT_ADDR
places every input it names in SPI order (two VGPRs per I/J pair, three
for the pull model, one otherwise), loaded or not, and ENA & ADDR selects
the loaded ones. ShaderPixelStageInfo carries inputAddr (replacing
perspectiveCenterVgpr) plus perspectiveCentroid and linearCentroid; the
recompiler places each loaded input with PixelInputVgpr. This also fixes
the system inputs, which were always written from v0 (psSystemInputBase
was never set), so POS_X overwrote the center I in any draw with
barycentrics enabled.

Draws are rasterized single-sampled, where the centroid is the pixel
center: with fragment barycentrics the centroid pairs carry the center
barycentrics (BaryCoordKHR for perspective; the linear center and
centroid pairs now get BaryCoordNoPerspKHR).

NoPerspective was decided for every parameter by one flag (LINEAR_CENTER
loaded). v_interp_p2 now records whether it reads the J of a linear or a
perspective pair, and a parameter is NoPerspective when it is read
through a linear pair; parameters read through neither keep the old
rule. A parameter read through both kinds of pair throws. Only pixel
stages consult this.

The request serializer moves to version 5 (older requests read back with
their loaded inputs as the layout); the cache key and the driver's draw
key compare and hash the new fields. The unsupported-input error names
ENA and ADDR.
2026-10-01 08:37:45 +13:00
boykopovar 1c1d8fb9df
Merge pull request #135 from oneandonlydean/fix/agc-drop-exports-past-attachments
fix(agc): drop pixel shader exports past the draw's attachments
2026-09-30 18:55:39 +03:00
boykopovar 14b883dbbb
Merge pull request #133 from oneandonlydean/feat/shader-ngg-mesh
feat(shader): translate NGG geometry programs to mesh shaders
2026-09-30 18:54:44 +03:00
boykopovar 6b731a9ee2 fix(agc): update graphics test to use pixel export mappings 2026-09-30 18:42:16 +03:00
boykopovar 3b1fc95ff4
Merge pull request #119 from oneandonlydean/feat/packed-vertex-formats
feat(agc): decode the packed 2_10_10_10 and 10_11_11 float vertex buffer formats
2026-09-30 18:06:04 +03:00
boykopovar 837a222b54
Merge pull request #117 from oneandonlydean/feat/cdbg-branches
feat(recompiler): conditional debug branches translate as never taken
2026-09-30 18:04:55 +03:00
boykopovar eaa47d66ed
Merge pull request #114 from oneandonlydean/fix/inert-depth-write
fix(agc): accept depth writes that the disabled depth test makes inert
2026-09-30 17:56:37 +03:00
Dean Galvin 33f9109999 feat(shader): translate NGG geometry programs to mesh shaders
The geometry path's merged ES/GS programs (VGT_SHADER_STAGES_EN.GS_EN)
were compiled as mesh-stage programs whose input metadata was never
filled in, so every such draw failed with "mesh shader translation
configuration is not supported".

Recompiler:
- BuildShaderStageInputInfo takes the draw's MeshConfiguration; the mesh
  input helpers (primitive size, step and counts) are implemented.
- The prologue emulates one NGG subgroup per workgroup: s2 GS_TG_INFO,
  s3 the merged wave info per wave, the GS vertex offsets (ES thread
  index times VGT_ESGS_RING_ITEMSIZE, odd strip triangles swapped), the
  primitive id, and the ES vertex and instance ids. An indexed draw's
  vertex id is read from the index buffer through a raw V# the driver
  puts in hidden user words 4..7.
- s_sendmsg GS_ALLOC_REQ becomes MeshAllocate. The epilogue zeroes the
  allocation first, clamps it to the output limits, culls primitives no
  thread exported and decodes the 9-bit vertex indices.
- The draw parameters live in the last six push dwords. A BDA fault in
  a mesh program no longer stops the invocation (as for other programs
  with barriers, the fault is recorded and the read returns zero), so
  every invocation reaches the subgroup barriers and the epilogue.
- The cache keys cover the mesh configuration; requests serialize its
  new esgsItemSize field (version 4).

Driver:
- DecodeShaderStages reads VGT_ESGS_RING_ITEMSIZE (now in the draw key)
  and sizes the subgroup for its ES vertices, GS primitives and outputs.
- Stages compile below the mesh draw parameters, which every mesh draw
  pushes; the mesh stage always takes part in the push range.
- The index-buffer V# is set per draw (per record on the CPU-resolved
  indirect path); the index-buffer alias check spares read-only
  elements.
- The workgroup and LocalSize checks count two guest lanes per
  invocation on 32-wide subgroups; mesh draws may carry a first vertex
  or instance.

agc_driver_mesh_tests (EXCLUDE_FROM_ALL, needs VK_EXT_mesh_shader)
recompiles a hand-assembled merged ES/GS program in the PS5 compiler's
layout and draws indexed and non-indexed triangle lists in one- and
two-wave subgroups and a triangle strip across odd-starting subgroups,
reading the pixels back. agc_shader_replay prints a request's draw and
mesh configuration.
2026-09-30 23:57:47 +13:00
Dean Galvin 52ffef8255 fix(agc): accept depth writes that the disabled depth test makes inert 2026-09-30 23:38:09 +13:00
Dean Galvin 4ad7103e56 test(agc): run agc_driver_graphics_tests under ctest and build it outside Windows
Nothing built the graphics test target, so it is now registered with the other AGC tests. It set
APS5_PIN_WAIT_MS through _putenv_s only, which does not exist outside Windows; it now uses setenv
there. The heap mirror tests need a write-watched guest arena and are skipped with a notice
without one, as the recorder tests already do.
2026-09-30 23:37:40 +13:00
boykopovar fc0596240d
Merge pull request #115 from oneandonlydean/feat/primitive-restart-strips
feat(agc): primitive restart with an all-ones index for strips, and for lists with VK_EXT_primitive_topology_list_restart
2026-09-30 12:27:42 +03:00
Dean Galvin 5a257bee52 feat(recompiler): conditional debug branches translate as never taken
S_CBRANCH_CDBGSYS, S_CBRANCH_CDBGUSER, S_CBRANCH_CDBGSYS_OR_USER and S_CBRANCH_CDBGSYS_AND_USER
(SOPP 0x17-0x1a) branch only while a debugger sets the conditional debug bits in the wave's
status, which never happens on a retail console, so they decode to one no-op opcode.
2026-09-30 10:56:31 +13:00
STL1te 6b36c050fe style: drop the branch's code comments per CONVENTIONS.md
Only the lines this branch added lose their comments; the commits before
keep them, with the reasoning behind each change, for whoever digs into
that code later.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:25:29 +03:00
STL1te 119560206d feat(agc-driver): heap ranges the host imports did not take are mirrored
An address-based build maps every readable registered range. A heap
range past APS5_HOST_IMPORT_MIB (or refused by the driver) was copied by
every such build: about 3200 ranges, 670 MiB and 500 ms per build on the
story-mode load, which then stalled. Such ranges are now served by
persistent host-cached mirrors, as the main image is, and the load
reaches its next failure.

- a heap mirror keeps no shadow; it keeps a write-tracking generation
  per 64 KiB block and a build copies only the blocks stamped since. The
  generation is stored by the copy, so a build that throws before it
  leaves no block marked current with stale bytes
- the build gathers its heap mirrors, sorts them and collects each run
  of adjacent ranges once (a collect costs about 4 us whatever its size;
  ~3000 mirrors took 16 ms per build, now about 3 ms)
- writable ranges are mirrored too: each build keeps the bytes its merged
  written ranges held at upload and write-back stores only what the GPU
  changed from them, so a CPU store made meanwhile stays; recorded work
  writing a range completes before a refresh reads it
- the mirrors are limited by APS5_HEAP_MIRROR_MIB (24 GiB by default); a
  mirror past it, or one whose buffer the driver refuses, stops the
  process. Heap mirrors are not given to descriptor sub-ranges, image
  mirror serials or reusable direct regions
- past APS5_ADDRESS_COPY_MAX_MIB (64 MiB by default) of committed bytes
  still copied, a build stops the process, naming the largest ranges and
  why each is copied (unreadable pages, an image mirror the driver
  refused, a range outside the write-watched arena, mirrors disabled)
- a direct memory page mapped at several addresses is read-only in every
  view while its range stays writable: copies, descriptor regions and
  mirrors take every readable page, write-back stores the writable ones
  and fails on a GPU change inside a read-only page instead of dropping it
- a store through the BDA table into a mirrored or copied range faults
  as before (only writable ranges imported in place are writable there);
  the fault now names the table range and that rule
- the BDA resource tests run with a storage range limit that holds the
  fault buffer and cover a mirror's fill, reuse, per-block refill, an
  interrupted refresh, a descriptor store beside a concurrent CPU store,
  and the copy limit

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:22:23 +03:00
STL1te c8913314bc test(agc-driver): the graphics and BDA device tests link and run to the end
Both suites stopped at their first stale expectation, so nothing after
it ran; the BDA device tests did not link.

- the BDA device tests define the one recorder entry Resources.cpp
  reaches (no recorder) and fail loudly on the others
- Buffer::Invalidate refuses memory that is not host mapped again (lost
  in 8a69fef); the device tests expect it for a device-local buffer
- APS5_PIN_WAIT_MS (60000 by default, unchanged) bounds the wait before
  a mutation of a leased range is refused, and the refusal names it; the
  graphics tests set 200 ms, as they expect about a dozen refusals
- the expectations follow the behaviour the code chose since: a buffer's
  four-DWORD V# is refused as a T#, a sampler exceeds the mock's zero
  sampler slots, a V# at unmapped memory binds its committed pages only,
  a rect-list fragment input without a vertex export reads as zero,
  detiled rows span the padded block width, mip ranges are checked in
  address order, block-compressed and 128/129/132 formats use render
  target tiling, an XOR swizzle is refused only over a base that is not
  64 KiB aligned, DCC and partial mip chain views decode, a cube reads as
  a 2D array, the sampler fixture's max LOD fits the 4.8 field, a BDA
  program with barriers is accepted, a tessellation control stage is
  refused, a corrupt request fails on its signature and a newer one on
  its version
- the DCC comment in the texture decoder no longer claims metadata
  compression is refused

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:22:23 +03:00
STL1te 3d3f71224e feat(shader): V#s at address 0 access nothing
A V# with records but a base address of 0 threw at bind time ("null shader
buffer descriptor address"), although a shader that never reaches that slot
works on hardware; an access through it would fault there, so a working
title does not make one.

Such a V# is specialized empty like one without records (every access out of
bounds) and binds the placeholder buffer; the draw cache's alias check skips
it as the build does. The tests expecting the old rejections are dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:22:20 +03:00
STL1te 2476bfda56 feat(agc-driver): depth/stencil and mip-chain targets, more color formats
- Depth/stencil: draws that test depth or stencil against a bound surface
  render with a GPU-only depth/stencil attachment (DepthSurface), cleared
  to DB_DEPTH_CLEAR/DB_STENCIL_CLEAR when made. Z test/write/function and
  per face the stencil function, operations and masks (BACKFACE_ENABLE);
  replacements by another value where Vulkan's single reference can carry
  them. Clear and copy draws, add/sub other than 1, array or mip views
  and MSAA throw, and texture reads of a surface's memory are refused.
  Scaleform menus mask with the stencil (REPLACE 0, ADD_CLAMP, EQUAL);
  their masked content was skipped before.
- Mipmapped color targets render through the resident image of the whole
  chain with a view of the mip, so mips in the packed tail are drawn too;
  CB_COLOR_VIEW's mip level is read from bits 26-29 (GFX10).
- Color: SNORM targets, UNORM16/SNORM16 exports, one-component formats
  honouring COMP_SWAP (A8), 32_R/32_GR/32_AR exports, linear rows padded
  to 256 bytes, BLEND_BYPASS.
- INDIRECT_BUFFER jumps are followed when copying a submission; thin 3D
  textures in display and rotated XOR swizzles; only the bytes the GPU
  changed are written back; resource registration.
- VideoOut: a flip stalls the GPU queue while the flip queue is full and
  holds the submitter back.
- APS5_DUMP_FRAMES_EVERY dumps every k-th presented frame.
- The graphics test target builds again (missing sources, a hand-aligned
  64 KiB buffer, stale expectations updated) and gains DepthStencilTests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 21:22:15 +03:00
Dean Galvin 2b0fa0bb74 fix(agc): drop pixel shader exports past the draw's attachments
An MRT export to a slot past the draw's color attachments (outside
CB_TARGET_MASK & CB_SHADER_MASK, e.g. MRT1 with only slot 0 enabled) is
dropped by the color backend. Vulkan discards a fragment output without
an attachment the same way, but validation rejected the draw ("fragment
shader must export float4 colors at locations below the attachment
count"). Such outputs are now left out of the written locations, and
only the exports that reach an attachment must be float4.
2026-09-29 13:26:41 +13:00
Dean Galvin 8588b551a4 feat(agc): decode the packed 2_10_10_10 and 10_11_11 float vertex buffer formats
Vertex buffer formats 50-55 (2_10_10_10 UNORM, SNORM, USCALED, SSCALED, UINT, SINT: X in the low
10 bits, the 2-bit W on top) map to Vulkan's A2B10G10R10 packings, and format 36 (10_11_11_FLOAT:
X in the low 11 bits) to B10G11R11_UFLOAT, the same mapping TextureFormat uses for textures.
11_11_10_FLOAT (43), whose 10-bit X has no Vulkan packing, stays rejected.
2026-09-29 13:07:47 +13:00
Dean Galvin 181457a0a6 feat(agc): primitive restart with an all-ones index for strips, and for lists with VK_EXT_primitive_topology_list_restart
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
2026-09-29 13:00:42 +13:00
Dean Galvin 0479a5f5f0 fix(agc): reject primitive restart only for indexed draws
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ButHENCbXXt8h4EZuWUabJ
2026-09-28 16:49:29 +13:00
devilismyfriend 29b4601564 perf(agc): dispatch and draw recipes, data-only cache hits, unit shadows, bindless image tables
Dispatch cache with per-key value variants and data-only hits (flat-SRT leaves the recompiler
proves pure are refreshed into the resource template instead of re-captured), dispatch and draw
recipes replaying a proven prepare, register-keyed draw entries, template data refresh, unit
shadows in device-local slabs, 64 KiB block-unit storage tracking with windowed detile, DCC
clear-key proofs, per-class GPU timing, a CPU copy path with batch read tracking, present slot
ring, rate-limited reaps and lazy completion counting, plan-failure and poisoned source memos,
bindless image tables (runtime-indexed descriptor arrays from a CPU-read table) so the
per-frame compute program that failed its plan now compiles. Worker affinity opt-in, per-draw
logs behind APS5_TRACE_DRAWS, wait-timeout reports include the recorder's view of the label.
Fixes a use-after-free behind BDA-miss dispatch snapshots and the CPU-write stamp of
UnchangedSinceCollected. Intro-video stage: 154 -> ~500 presents per 10 s at 4K.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0177gthrWEBBfBu5xJDXgBfo
2026-09-26 19:00:40 -07:00
boykopovar fa6d023b1b feat(agc): support rendertarget64kb shader textures 2026-09-24 11:18:35 +03:00
boykopovar f129f6cde5 fix(agc): accept valid hardware screen offset values 2026-09-23 16:22:20 +03:00
boykopovar 069ee78158 feat(shader): generate tessellation shaders for rect-list primitives 2026-09-23 16:13:33 +03:00
boykopovar bbde0f4680 feat(agc): support tiled color target layout 2026-09-23 13:15:30 +03:00
boykopovar 8cd216e9d7 fix(agc): remove raw buffer restriction and decode guest buffer descriptors 2026-09-23 11:44:46 +03:00
boykopovar b521e1f811 test(agc): add texture resource and detiling coverage 2026-09-23 10:57:27 +03:00
boykopovar 77f44a1a8d refactor(agc): report unimplemented image/sampler resources 2026-09-22 21:51:35 +03:00
boykopovar f7aaa2fb86 feat(agc): add DRAW_INDEX_AUTO handling impl 2026-09-22 20:07:09 +03:00
boykopovar 79c9aa587b feat(agc): add fragment shader barycentric support 2026-09-22 11:43:39 +03:00
boykopovar 5e3a13041c feat(agc): implement vertex attribute input and buffer binding 2026-09-22 11:34:54 +03:00
boykopovar 5b424406a5 fix(agc): enable float controls and validate subgroup capabilities 2026-09-22 11:23:58 +03:00
boykopovar 3028c87491 feat(agc): add bda memory mapping and resource management (wip) 2026-09-21 18:54:06 +03:00
boykopovar 1490c0386c refactor(agc): unify shader resource bindings and push constants 2026-09-21 13:48:44 +03:00
boykopovar 9c78d10044 fix(agc): support unrestricted vulkan depth range 2026-09-16 07:24:00 +03:00
boykopovar f0b13b6652 fix(agc): support negative depth range and inactive color targets 2026-09-16 07:21:13 +03:00
boykopovar 05ec432f75 fix(agc): non-homogeneous positions with all viewport transforms enabled 2026-09-16 07:11:00 +03:00
boykopovar f73834ce2f fix(agc): complete initial context state and tighten shader validation 2026-09-16 07:04:23 +03:00
boykopovar 74613f7e0f feat(agc): extend graphics compiler contract and add mesh and tessellation paths 2026-09-15 21:19:06 +03:00
boykopovar 215e3cbf48 fix(agc): fix missing register at DWORD 677 (wip) 2026-09-15 12:38:01 +03:00