Commit Graph
548 Commits
Author SHA1 Message Date
xXPBSXxandClaude Sonnet 5.5 cfbf714d87 docs(debt): list the unprepared AGC shader types
ShaderRegistry.cpp prepares the registered shader types 0, 1, 2, 4, 6 and 7, skips 5, and logs and leaves every other type unprepared at registration; Hello Neighbor 2, Ad Infinitum and Ghostrunner register type 8.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-16 05:55:24 +02:00
Yunus Shaikh aa2b7ce7ef fix(agc): keep a refused heap's mirror once its range has left the write watch
A heap mirror whose range had left the write watch was dropped and its
range planned again. When the host import budget refused the range, that
plan was a per-build copy, and past APS5_ADDRESS_COPY_MAX_MIB (64 MiB) the
copy stopped the process ("an address-based build copies ... past ..."),
where refreshing the mirror in full would have carried on.

Plan such a range again only when the import path takes it now. A cached
address space whose heap or sparse mirror has left the watch is rebuilt
only if importing the mirror's range succeeds, and the rebuild then finds
that import; otherwise the space is kept and the mirror refreshed in full,
every block counted as written. A build that plans its ranges tries the
import first, so acquireMirror keeps the existing mirror of a range that
reaches it, and a sparse mirror whose pieces are unwatched is refreshed in
full instead of planned again.

SetAddressCopyLimit sets the copy cap in-process, as SetHostImportBudget
sets the budget. The heap and sparse unwatch tests set it below the heap
the old plan would copy: the refused heap's mirror is kept and refreshed in
full (its unwatched block and a watched block the CPU wrote both reach it),
and once the budget holds the heap the next build imports it and releases
the mirror.
2026-10-16 03:55:11 +00:00
Yunus Shaikh bc27ba7d07 docs(agc): describe the settle of Windows imports under 1 MiB
ARCHITECTURE.md and the debt entry describe how imports under 1 MiB are
settled without waiting and return to the write watch, which the debt
entry listed as not implemented.
2026-10-15 19:42:17 -07:00
Yunus Shaikh 356d2a9dfa fix(agc): plan a sparse heap again when its mirror's range has left the write watch
A sparse heap mirror, made for a heap with uncommitted pages, is
refreshed from the write watch through its committed pieces. The check
that a reused heap mirror's range is still watched covered the heap
mirrors acquireMirror reuses and the cached space's heap runs, not
sparse ones: once a committed piece left the watch, refreshSparseMirror
threw "a sparse heap mirror's range is no longer write-watched" from
acquireMirror's reuse and from a cache hit's sparse refresh.

refreshSparseMirror now returns false when a piece is no longer
watched. Both callers already take false as not reusable: acquireMirror
makes the mirror again, finds the range unwatched, drops the stale
mirror and leaves the range to a copy, and a cache hit gives up the
cached space.

agc_sparse_heap_mirror_unwatch mirrors a 1 MiB reservation with 256 KiB
committed, takes one committed block off the write watch and stores new
bytes there from the CPU; the next build must not reuse the mirror and
must serve the new bytes. agc_sparse_heap_mirror_unwatch_no_space_cache
runs the same without the address-space cache. Without the fix both
throw. They are registered on Linux; the Windows path uses the guest
arena as the existing sparse heap test does and has not run on Windows.
2026-10-16 01:52:33 +00:00
Adrià 7b20318e89 Merge pull request #5882 from billythemenace2/fix/agc-msaa-target-without-msaa-enable
fix(agc): rasterize a multisampled target without MSAA_ENABLE at the pixel center
2026-10-15 23:58:24 +02:00
MarcZX100 881facc4fa fix(agc): refuse native MSAA index buffer reads 2026-10-15 21:35:59 +00:00
Yunus Shaikh ac9ccfb89c fix(agc): keep the host import budget off the device report until an import fails
The host heap's VK_EXT_memory_budget report lowered a set
APS5_HOST_IMPORT_MIB of 6400 MiB to 4480 MiB on Resident Evil Village
(PPSA01556), where 6400 MiB runs without a vkAllocateMemory failure, and
85,047 imports were refused. A set APS5_HOST_IMPORT_MIB is now never
lowered by the report, and the default is held to the report only once
an import's vkAllocateMemory has failed. The failure ceiling is
unchanged. The first time the report would lower the budget, a [gpu]
host import budget line prints the heap's reported budget and usage and
the live imports, and the refusal line names where its budget came from.
2026-10-15 21:04:31 +00:00
Adrià c10aa036d2 Merge pull request #5821 from SandstromPL/fix/agc-sampler-aniso-minmax-reduction
fix(agc): accept a min or max reduction sampler with anisotropic filtering
2026-10-15 22:58:19 +02:00
Yunus Shaikh e9ff3b1768 fix(agc): plan a heap again when its mirror's range has left the write watch
A heap mirror is refreshed from the write watch. acquireMirror checked
the watch only when it made a mirror and reused an existing one as it
was. Once part of the range left the watch, the next address-based build
threw "a heap mirror's range is no longer write-watched" from the
refresh, and a build served from the address-space cache threw the same
from its heap runs. A range leaves the watch for good when a host import
that does not keep the watch covers it: on Windows a direct import of
private memory that is not settled (below the 1 MiB settle floor, or any
when imports cannot be settled), elsewhere any import while imports are
compared.

A heap mirror now records the unwatch serial at which its range was last
seen fully watched. Its reuse, and a cache hit over it, compare that
serial with GuestMemory::UnwatchSerial and look at the watch again only
when a range has left it since. A mirror whose range is no longer
watched is not reused, the cache is not taken (before its imports are
stamped), and the pass plans the range again: an import if the budget
allows, else a copy. A refused heap mirror also drops the registry's
stale one, whose buffer would stay allocated otherwise.
2026-10-15 20:40:32 +00:00
billybobpicklioandClaude Opus 5.5 a0347cfed5 feat(agc): draw line-strip output of NGG geometry programs as mesh lines
A geometry program with VGT_GS_OUT_PRIM_TYPE 1 was refused as an
unsupported output assembly. Each primitive export of an NGG line-strip
program is one line whose indices are packed like a triangle's, so the
mesh module now declares OutputLinesEXT with PrimitiveLineIndicesEXT for
it. A subgroup gets VGT_GS_MAX_VERT_OUT - 1 lines per input primitive and
at least GE_MAX_OUTPUT_PER_SUBGROUP line slots; fast launch keeps using
PRIM_AMP_FACTOR. The output size is part of the cache, draw, validation
and pipeline keys, and the request serialization version becomes 31.
Triangle-strip geometry is unchanged.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 15:39:35 -04:00
Adrià 44df216321 Merge pull request #5834 from xXPBSXx/fix/agc-register-default-owner-v2
fix(libSceAgcDriver): return 0 from sceAgcDriverRegisterDefaultOwner
2026-10-15 20:34:25 +02:00
Adrià c503db8d87 Merge pull request #5800 from fdfawef324/codex/frame-dump-io-best-effort
fix(agc): disable optional BMP dumps after host I/O failures
2026-10-15 20:32:15 +02:00
Yunus Shaikh 57ceb9e370 fix(agc): keep the guest's sample order where pixel (0,0)'s locations stand in for a 2x2 grid
Village's 2x checkerboard puts its two samples at x = +4/16 and -4/16
and swaps their indices on odd rows. On a device without a 2x2 sample
location grid every pixel takes pixel (0,0)'s locations, so the odd
rows hold the console's sample 1 in sample 0 and the reverse: a pass
that reads the samples by index, as checkerboard reconstruction does,
exchanges the two half-pixels of every odd row, and SampleID, sample
masks and exported coverage name the wrong samples there.

Such a pattern holds one set of positions in every pixel, so drawing
with pixel (0,0)'s locations is exact up to the order of each pixel's
samples. The draw now keeps the guest's order: the pixel shader reads
SampleID, applies the sample mask and exports coverage through each
pixel's order, and every render pass drawing with the stand-in puts the
samples of its multisampled attachments in the stand-in's order before
it and back in the guest's after it. Color targets do that through a
storage view of their texels as unsigned integers, depth through a copy
and a per-sample pass writing FragDepth, and stencil through
VK_EXT_shader_stencil_export or one pass per bit.

It needs shaderStorageImageMultisample with a storage view of the color
format, sampleRateShading, and a depth format FragDepth writes back
exactly (16-bit or 32-bit float); otherwise, and for patterns whose
pixels hold different positions, the stand-in alone is used, and the
log line says why. Devices with the grid are unchanged.

Recompile requests carry the order as serialization version 32 (#6470
takes 31), and the shader cache format becomes 40 (open pull requests
take 37 and 39).
2026-10-15 17:53:11 +00:00
Alex ColliniandClaude Opus 5.5 349420af7f docs(agc): record the measured effect of dropping entries with released textures
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 19:28:43 +02:00
Alex ColliniandClaude Opus 5.5 49919d7f32 fix(agc): drop resource-cache entries that hold released sampled textures
A resource-cache entry keeps every sampled texture of its descriptor sets alive. The sampled texture cache passes over textures something else holds and, past its ceiling, drops them anyway; textures it makes without caching are never in it. Entries holding either kept them alive with no budget counting them, bounded only by the 1,024-entry and 65,536-element counts, until device memory ran out.

The texture cache now marks such textures released. ResourceCache::Insert refuses descriptor sets holding one and hands the entries that hold one back to the caller, like RemoveImage does for evicted storage images.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 18:56:30 +02:00
Adrià 2cefed5499 Merge pull request #5778 from meh7an/fix/agc-buffer-range-limit-by-mapped-memory
fix(agc): bind a V# past the range limit by the memory behind it
2026-10-15 18:50:36 +02:00
Yunus Shaikh 98c72e9f83 perf(agc): take the host import budget from the device's memory budget
Imports were held to a configured budget only. With APS5_HOST_IMPORT_MIB
at 6864 on a 15.4 GB PC, Village's imports reached the NVIDIA driver's
pinning ceiling of about 6.56 GiB, and from there every import was tried
and failed at vkAllocateMemory, 992 times.

Where the device enables VK_EXT_memory_budget, the budget is now also at
most the host heap's reported budget less its other usage and an eighth,
in 64 MiB steps, read at most once a second. A vkAllocateMemory failure
within an eighth of the budget, or the third in a row, lowers the budget
to the live imports, so later imports are refused or evict instead of
failing; an isolated failure further below only marks its range failed,
as before. A remembered refusal stays remembered while the budget does
not grow.
2026-10-15 09:49:04 -07:00
Yunus Shaikh 4a84bbf917 perf(agc): remember budget refusals until the budget frees
An address-based build asks for every registered GPU range whenever it
rebuilds its address space, and at a full budget it was refused each
one again: a title held at its default budget counted 304,394 refusals
over about 12,000 ranges at its title screen.

A request that may not evict (the build's walk, an import when a range
is mapped) now remembers a refused run with the live bytes and the
budget at the refusal. Until live imports drop below those bytes, the
budget changes or one of the run's ranges changes, a request for that
range is answered from memory, still counted as refused and also as
remembered, and a remembered block is skipped, so a block stepped down
for the budget keeps its import across registry changes. A request
that may evict always tries again, and a new import forgets the
refusals of the runs it covers. The shutdown report and the budget
line print how many refusals were remembered.
2026-10-15 09:10:35 -07:00
Yunus Shaikh a24aafb75c perf(agc): import adjacent registered ranges as aligned power-of-two blocks
An import covered one registered range unless adjacent ranges filled a
whole aligned 8 MiB window. A title that maps its GPU heap in 64 KiB
pieces got one import per piece from every requester: Village held
about 27,000 of them at its first scene, enough to fill the budget.

The run of an address is now the largest aligned power-of-two block,
from twice the import alignment up to the window, that holds the
address and that continuing readable ranges fill exactly.

A block over the budget steps down to the largest block that fits what
is left of it, counting what the requester may evict, and nothing is
tried when not even the address's own range fits, so a full budget
costs one refusal per request as before. A block that would replace an
import a build holds, or that fails (a refused Windows alias,
vkAllocateMemory), steps down to the next smaller block. A failed block
is remembered until one of its ranges changes, instead of pinning the
span to single ranges for the session. An address-based build no
longer asks for the ranges inside the block it just imported.

A storage image over 64 adjacent pieces now finds its block import at
write-back instead of importing every piece as a segment. The split
write-back test keeps its two ranges apart with a 64 KiB window.
2026-10-15 09:10:34 -07:00
Yunus Shaikh 2cd0c9107a fix(recompiler): run a pixel shader the guest does not iterate once per pixel
The ancillary input's sample number (bits 11:8) was the SampleId
builtin in every pixel shader. Vulkan enables sample shading for a
shader that uses SampleId, so a shader that reads the sample field or
the whole ancillary word ran once per sample on a multisampled draw
even when the guest runs it once per pixel (PS_ITER_SAMPLE clear), and
FragCoord, and with it POS_X_FLOAT and POS_Y_FLOAT, moved to the
sample. The sample number is now SampleId only when the request says
the guest iterates the shader, and otherwise 0, the iterated sample
number of a pixel the hardware does not iterate.

The other inputs that depend on the sample follow where the console
evaluates them:
- PERSP_SAMPLE and LINEAR_SAMPLE are interpolated with
  InterpolateAtOffset at sample 0's offset in a shader the guest does
  not iterate, and in an iterated one at the offset of the host's
  SampleId, read from a table of the guest's locations, because Vulkan
  leaves plain barycentrics under sample shading at an
  implementation-defined position (lavapipe uses the pixel centre).
- PERSP_CENTER and LINEAR_CENTER are interpolated at offset 0 in an
  iterated shader.
- POS_X_FLOAT and POS_Y_FLOAT follow SPI_BARYC_CNTL: the centre is
  floor(FragCoord) + 0.5 under sample shading, a sample position in a
  shader the guest does not iterate is the centre plus sample 0's
  offset, and POS_FLOAT_ULC gives the upper-left corner.

The offsets need InterpolationFunction (sampleRateShading) and the
fragment-shader barycentric path; devices without them keep the host's
default positions. The shader disk cache format goes to 39.
2026-10-15 16:06:38 +00:00
Claude 76cc70c245 fix(agc): overestimate single-sample draws with centroid inputs
A draw with PA_SC_CONSERVATIVE_RASTERIZATION_CNTL 0x6001 and
SPI_PS_INPUT_ENA.PERSP_CENTROID or LINEAR_CENTROID threw in DecodeState,
whatever its rasterized sample count. With PA_SC_AA_CONFIG.MSAA_NUM_SAMPLES
0 the single rasterized sample is the pixel centre, which is where the
recompiler already puts the centroid barycentrics, so such a draw now
decodes to overestimation. More rasterized samples, and a single-sample
draw raised to a multisampled color target, still throw.

Extends agc_driver_graphics and records the inference in the driver's
technical debt.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XJ3RTcWpqBBdVE8QRtKxJD
2026-10-15 15:47:02 +00:00
Yunus Shaikh 668ecaeca2 fix(agc): fall back to pixel (0,0)'s sample locations where the device has no 2x2 grid
A draw whose sample locations vary between the pixels of the guest's
2x2 pattern threw "sample locations varying between pixels require a
2x2 sample location grid for the draw's sample count" on a device
whose maxSampleLocationGridSize for that sample count is not a
multiple of 2x2. An RTX 3070 Laptop GPU reports 1x1 for every count,
so Resident Evil Village's 2x checkerboard pattern stopped there
before its title.

The pipeline now decides the grid when it is created: such a pattern
is drawn through a 1x1 grid with pixel (0,0)'s locations, the first
registers of the pattern and the pixel the guest's grid is anchored
at, and the first such pipeline of each sample count logs it. The
render pass begin takes the pipeline's grid, so a sample-location-
compatible depth image is begun with the locations it is drawn with.
Devices with the grid keep it, and uniform patterns are unchanged.
2026-10-15 14:36:22 +00:00
billythemenace2andClaude Opus 5.5 01d3594c20 fix(agc): treat a TYPE 1 T# in a read-only static image slot as null
A static, read-only image slot whose T# has TYPE 1 is now bound as a null
image, like TYPE 0 since #5473 and #4779. On gfx1010, image_sample_lz,
image_sample_b, image_load and image_get_resinfo through a format-22
TYPE 1 T# return zeros without a fault, and gfx1035 reads it as zeros
too. Written, atomic and table slots still throw, and the reserved
types 2-7 stay refused.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-10-15 15:16:56 +01:00
Yunus Shaikh 4344c1dc29 Merge branch 'main' into perf/shader-direct-cpu-evaluable-images 2026-10-15 06:38:09 -07:00
hugodearaujo e8b7361a1c fix(agc): settle dma-buf imports on Linux on the settle queue
The kernel maps a dma-buf import's pages for the GPU at the first submission that uses it. On RADV
that took about 2 s for a 3 GiB import, in the second frame of Hades (PPSA03355), and its staging
pool ran out while the frame was held.

Linux now makes the settle queue from #6060 when the device imports dma-buf memory, and settles
every dma-buf import of 1 MiB or more right after it is made with settleImport's 4-byte copy. The
settle queue needs neither the GPU mutex nor the main queue's work to finish. A dma-buf import's
buffer is shared with the settle queue's family instead of aliased, since a sparse import is bound
to many memory objects. Where the sparse queue already takes that family, the settle queue is a
second queue of it.
2026-10-15 10:19:46 -03:00
Adrià FranchandClaude 17ed1218a4 docs(debt): record the measured min/max anisotropic sampler results
Co-Authored-By: Claude <[email protected]>
2026-10-15 15:17:18 +02:00
SandstromPLandClaude Opus 5.5 474ec771bf fix(agc): accept a min or max reduction sampler with anisotropic filtering
A guest S# with FILTER_MODE min or max and an anisotropic XY filter
threw "combines a min or max reduction with anisotropic filtering",
which stops High on Life (PPSA15388) in its home base. It now decodes
to a Vulkan sampler with both anisotropy and the reduction mode, as a
weighted-average anisotropic S# already does. RADV programs the same
S# for such a Vulkan sampler.

Tamalero's gfx1030 measurement on #5156 covers a sample without
derivatives through a 2x anisotropic S# over a single-level texture:
min or max with anisotropic bilinear returns the bilinear minimum or
maximum, and min with anisotropic point the point result. The
execution test checks the bilinear case on the host GPU; the debt
entry records what is not measured.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 15:16:41 +02:00
Adrià 489466d3f9 Merge pull request #5734 from YossiYad/fix/strip-last-provoking
fix(agc): refuse last-vertex strips only for reads of a vertex slot
2026-10-15 15:03:44 +02:00
Adrià 3ee5371915 Merge pull request #5627 from AlxcBTW/fix/agc-htile-written-by-any-kernel
fix(agc): take depth fast clears from HTILE written by any kernel
2026-10-15 14:52:34 +02:00
Romans Pokrovskis 764f0ace02 test(agc): exercise native HTILE producers and clear refusals 2026-10-15 14:00:57 +02:00
fdfawef324andClaude Opus 5.5 6a9b3d6fad docs(agc): record the BMP open failure measured on presentation
VideoOut flips on an RTX 3060 now show that a first frame dump that cannot be opened no longer stops presentation. Header, pixel and close failures remain measured only by the CPU fixture.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 07:56:13 -04:00
AlxcBTW 139e3b86ed fix(agc): route native-owned depth binds before the HTILE read and store-back
A multisampled depth target is created as, or already is, the native depth owner, and a target whose HTILE overlaps a resident depth guard aliases one. Neither may have its HTILE read or rewritten by the generic store-back: the expanded words would be a guest write over the owner's range. DepthSurfaceView decides this before the store-back, which still runs before GpuMutex() and the surfaces mutex are taken. NativeMultisampleDepth binds the owner and a single-sample alias of its HTILE and checks the words stay untouched.
2026-10-15 13:55:39 +02:00
AlxcBTW cc022df348 fix(agc): take a slice of a depth array from its own HTILE only when its words are uniform
A fill or clear the driver recognizes over an HTILE drops the range's last-read generation, so the
next bind reads the words and stores them back even when the write tracker did not see the fill's
stores. A depth view of slice 1 or later reads the HTILE at its own address
(DB_HTILE_DATA_BASE + slice * HtileMetadataBytes); the HTILE word layout is only modelled for slice 0,
so such a slice takes the clear and the store-back only when all its words are equal and keeps its
fill-time fast clears otherwise. DepthTarget::slice carries DB_DEPTH_VIEW SLICE_START.

agc_driver_depth_fast_clear_submit: an unrecognized kernel clears the HTILE on two frames in a row and
both clears are taken; slice 1 with words that are not all equal is refused and stored back untouched;
slice 1 with equal words takes the clear. agc_driver_graphics checks the decoded slice.
2026-10-15 13:53:50 +02:00
AlxcBTW 3e61fcf1f9 fix(agc): read and store back the whole pipe-aligned HTILE outside the surfaces lock
The bind-time HTILE read covered one word per 8x8 tile from DB_HTILE_DATA_BASE, but main models the
HTILE as the pipe-aligned SW_64KB_Z_X metadata (HtileMetadataBytes, the extent HtileFillCovers takes):
at 1920x1080, 1856 of the 32400 tile words lie past the first tiles*4 bytes, so they kept ZMask 0
after a store-back. The read and the store-back now cover the whole metadata and run before the
surfaces mutex is taken, since both wait for recorded work over the range. The tests' HTILE arrays
hold the whole metadata.

PPSA25344 (Unreal Engine 5) decodes its depth through HTILE in its Nanite depth export after the
depth prepass and takes ZMask 0 as a tile holding the clear value; the new
agc_driver_depth_fast_clear_submit case checks that a draw after a fast clear leaves every word
expanded.
2026-10-15 13:53:50 +02:00
esatturan 4ddff7c6e5 fix(agc): take depth fast clears from HTILE written by any kernel 2026-10-15 13:53:49 +02:00
Adrià fdea36b836 Merge pull request #5524 from Amoenus/codex/native-depth-allocation-retirement
fix(agc): retire fully released native depth owners
2026-10-15 13:50:13 +02:00
fdfawef324andCodex 6541db004b fix(agc): disable optional BMP dumps after host I/O failures
Use checked host stdio for BMP output and stop requesting dumps after the first write failure. Keep pixel validation, allocation and non-I/O failures on the existing fatal path.

Co-Authored-By: Codex <[email protected]>
2026-10-15 07:39:36 -04:00
Adrià 5050a23f38 Merge pull request #5473 from MarcZX100/type0-recompiler-followup-4779
fix(shader): accept TYPE=0 images during materialization
2026-10-15 13:37:52 +02:00
Adrià 39d3ae13eb Merge pull request #5304 from tv7/fix/stencil-ref-export-unused
fix(agc): drop an unused stencil test value export from draws without a stencil test
2026-10-15 13:24:56 +02:00
Adrià FranchandClaude 91878f51f9 docs(debt): describe the TYPE=0 null image scope as implemented
Co-Authored-By: Claude <[email protected]>
2026-10-15 13:12:00 +02:00
Adrià Franch 6f0aa97f6c docs(debt): describe the unused stencil export drop next to the stencil replay 2026-10-15 12:51:37 +02:00
NikoandClaude Opus 5.5 c434467f6c fix(agc): form reversed-order blend factors from copies of the export
The reversed-order blend computed the factors Vulkan cannot apply per
channel in the pixel shader (1 - x in fp32, a clamp for UNORM targets
and, when both positions needed it, a premultiplied export) and passed
them to the blend as a dual-source second color. A plain blend converts
the export once and forms 1 - x and the products from that converted
value, so the two paths can round differently. On UNORM8 the difference
reaches one step per substituted factor: an Intel iGPU stored 161 where
the exact blend is 159.75 for SRC_COLOR + ONE_MINUS_SRC_COLOR with an
export of 0.5 over 192 (160 through SWAP_STD), and on an RTX 5080 and
lavapipe the split path differed from the same draw into a SWAP_STD
target by up to 2 steps on other inputs.

The pixel shader now only copies values. Each channel of the second
color is the exported alpha, the channel's own export or the constant 0
or 1, and a channel of the first color whose source factor is ONE may
become 1 with its export moved into the factor. Every factor, 1 - x and
product comes from the blend through SRC1_COLOR, SRC1_ALPHA and their
ONE_MINUS forms, applied to copies of the same export values that the
first color carries. TranslateReversedBlend searches for Vulkan factors
and copy routes that reproduce each channel's guest factors and throws
for combinations no routing reproduces, such as ONE_MINUS_SRC_ALPHA in
the alpha equation next to SRC_COLOR in the color equation: of the 1995
factor combinations the shader used to compute, 932 are routed and the
others throw again, as on main. GT7's 0x65010504 is among the routed
ones. The code is now four 2-bit routes for each color, the same for
every format, and the post-pass emits no arithmetic.

agc_driver_reversed_order_blend also draws each case into a SWAP_STD
target, which Vulkan blends alone, and requires identical stored bits;
the comparison with the exact blend keeps its 1-step tolerance.
agc_driver_graphics checks every factor combination TranslateReversedBlend
accepts against the guest equations and that the post-pass adds no
arithmetic instructions.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:51:27 +02:00
NikoandClaude Opus 5.5 16ea32c97d feat(agc): blend into color targets with a reversed component order
8_8_8_8 and 16_16_16_16 targets with COMP_SWAP 2 or 3 keep the exported
alpha in their first channel and red or blue in their fourth, so a plain
Vulkan blend would run the alpha equation on a color channel. Such draws
threw "blending into a color target with a reversed component order is
not implemented" (Gran Turismo 7, #4782).

The AMD SI and CIK register references describe CB_TARGET_MASK in the
order of components after blending and before COMP_SWAP is applied, and
RADV programs CB_BLENDn_CONTROL without regard to the swap while it
renders R4G4B4A4, B4G4R4A4, R5G5B5A1 and B5G5R5A1 through SWAP_STD_REV
and SWAP_ALT_REV and passes the CTS blend tests on Navi10. The color
block is therefore taken to blend in export order: the alpha equation
and source-alpha factors use the exported alpha and the destination
alpha is the first channel.

TranslateReversedBlend keeps the factor pairs Vulkan applies per channel
in the pipeline. Other source factors are computed by the pixel shader
from its MRT0 export (clamped for UNORM targets, as BLEND_CLAMP says)
and handed to the blend as a dual-source second color, which the
fragment output post-pass adds at location 0 index 1; when both
positions need it, the export is premultiplied by its source factor.
The code is a pipeline specialization, serialized in request version
26, and stays out of the prepared shader key.

sRGB, SNORM and integer targets, constant and second-source factors,
destination alpha in the color equation, destination factors the two
equations do not share, different color and alpha operations, MIN or
MAX with factors other than ONE, other color exports when the shader
computes factors, and devices without dualSrcBlend still throw, now
with the blend control, mapping and format in the message.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:51:27 +02:00
Yunus Shaikh 0b099964f0 Merge branch 'main' into perf/shader-direct-cpu-evaluable-images 2026-10-15 03:19:52 -07:00
Romans Pokrovskis da9fe3c708 fix(agc): retire fully released native depth owners
Track successful releases cumulatively across the complete depth and HTILE ownership ranges. Preserve color retirement and share its single callback; keep retired image contents alive through sampled views and recorded work. Add registry, partial/failure/protection, remap and mixed teardown regressions. Depends on native depth sampling in PR5496 and its descriptor prerequisite PR4820.
2026-10-15 13:08:13 +03:00
SP4C3B4R-8andClaude Opus 5.5 626cff7d26 fix(libSceAgcDriver): retire a depth surface once another writer covers its memory
A depth surface lives only in its Vulkan image, so a view of its memory
with another layout could only throw, and a matching view kept reading
the image after other writes over its memory. Retire the surface when a
color target or a written storage image it does not serve overlaps its
planes, or when a view it cannot serve finds guest memory over the planes
written since the surface was last bound; lookups then skip it and the
view reads the memory like any texture. Binding the target again brings
the surface back.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 11:00:36 +02:00
Adrià 0afa6a607e Merge pull request #6060 from parl0v/feat/watched-private-imports
perf(agc): keep Windows host imports of write-watched private memory watched
2026-10-15 10:54:43 +02:00
Adrià c4f03522a7 Merge pull request #5496 from Amoenus/codex/native-msaa-depth
feat(agc): sample native multisampled depth attachments
2026-10-15 10:54:18 +02:00
LeandroLP23andClaude Opus 5.5 79d72b6836 fix(shader): kill demoted quad-mask pixels when the pixel program returns
A pixel that PA_SC_AA_MASK leaves uncovered is demoted to a helper
invocation so the covered pixels keep their derivatives. When the guest
exports under an EXEC mask that clears that lane, the colour output is
never written. On the Radeon 860M (Windows driver 32.0.13058.8001) such
a demoted invocation still writes undefined colour to the target, so
agc_driver_quad_pixel_mask fails at its first implicit-LOD sample under a
partial mask (2). Killing the uncovered invocations at the return, after every
derivative is taken, discards them on every driver.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:09:52 +02:00
Adrià FranchandClaude 290be56265 test(agc): skip the dma-buf import probe when /dev/udmabuf can't be opened
Co-Authored-By: Claude <[email protected]>
2026-10-15 10:05:09 +02:00