Commit Graph
548 Commits
Author SHA1 Message Date
xXPBSXxandClaude Sonnet 5.5 5a7f183a3d fix(libSceAgcDriver): return 0 from sceAgcDriverRegisterDefaultOwner
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
2026-10-14 13:52:32 +02:00
Sasha PapoandClaude Opus 5.5 d756f525fc feat(agc): store pending storage-image results before each GPU label with APS5_STORE_AT_LABEL
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:40:18 +02:00
pushkarreddyy e1d402c6ca feat(libSceAgcDriver): support secondary LOD bias and relax unnormalized coordinate filters 2026-10-14 17:07:46 +05:30
Adrià 82f112d20f Merge pull request #4451 from Amoenus/codex/feat-alpha-to-coverage-dither8
feat(agc): emulate default eight-sample dithered alpha coverage
2026-10-14 12:55:07 +02:00
Adrià 29f4891e21 Merge pull request #5696 from gjonn/hdr-pq-display-buffers
feat(agc): present BT.2100 PQ display buffers on an SDR output
2026-10-14 12:49:21 +02:00
Jan ŠléglandClaude Opus 5.5 d34564be4a perf(agc): bind a sparse descriptor heap's committed run in place from its import
A descriptor heap that a shader binds whole while only the pieces from its
base are mapped (a sparse heap) is copied on the CPU for every build that
binds it unless a mirror serves it. The sparse heap mirror takes a heap whose
needed extent lies in one releasable registered range. A heap mapped piece by
piece, each mapping its own allocation and the pieces back to back from the
heap's base, is several registered ranges, so every build copied its mapped
run again.

Such a region is now bound in place from an import of that run: an import
already covering it, the run's own import, or one joined over exactly the run
(#4517). This applies to any sparse heap no mirror took, including under
APS5_NO_LEASE_MIRROR or APS5_NO_SPARSE_HEAP_MIRROR. A join is made only for a
region that starts at a registered range, once the previous bind of that
address saw the same last mapped byte: a heap that grows between every two of
its binds keeps the copy, and one bound more often than it grows is joined
again after each growth. Joins stop after 64 per joined run base, with a
message: the heap is then bound in place while an existing import covers its
run and copied otherwise. The records go with their range. The region's descriptor and BDA ranges end at the import's end, which
covers the mapped run and the last view, as the copy does. A region whose
mapped part does not start at its base, with a view past the run, written by
its shader, or whose run is not imported is copied as before. A direct
region whose import ends before it outside this path still fails in
Descriptor, as before.

A set over such a region stays reusable: DirectRegions and InPlaceReads name
the region up to its import's end, so the import's serial proves it, and a
reuse also requires the heap to be mapped exactly up to that end
(CutRegions), checked whenever the imports or the registry changed, so a
mapping past the cut makes the set miss. DirectRegions also refuses a set any
of whose regions bound in place no longer holds the import found at its base
(a later join in the same build retired it); this applies to every region
bound in place.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 12:42:01 +02:00
Dean 6ade14f52d Merge pull request #5679 from moong15/fix/agc-save-guest-bytes-before-untracked-results
fix(agc): save the guest bytes of units without a write generation when results land outside the write watch
2026-10-14 23:28:44 +13:00
mrfelek dd7d649c36 feat(agc): storage image access to depth planes through a 2D array view from a later slice
A storage view of the depth plane that starts above slice 0 was refused. The
layered storage image already spans slices 0..last, and the view starts at its
base slice, so the refusal is dropped and only the slices from the base slice to
the last are copied in and out. The base slice joins the cache key.

DepthStorageDispatch stores through a view from base slice 2 and checks the
other slices stay untouched; it fails without the change.
2026-10-14 12:13:52 +02:00
Mehran e9c98900e7 fix(agc): bind a V# past the range limit by the memory behind it 2026-10-14 13:09:59 +03:00
Romans Pokrovskis c8b3a37533 feat(agc): emulate default eight-sample dithered alpha coverage 2026-10-14 12:58:43 +03:00
Dean 115dc95784 Merge pull request #5347 from MathiasDevelopes/codex/pr-agc-stencil-clear-state
fix(agc): initialize stencil control and reference masks
2026-10-14 22:48:14 +13:00
Dean 5744094e79 Merge pull request #5506 from codingreapermaps/feat/agc-z64kbx-color-targets
feat(agc): render into SW_64KB_Z_X color targets
2026-10-14 22:40:55 +13:00
Dean 64380313b5 Merge pull request #5521 from codingreapermaps/feat/agc-alpha-coverage-without-color
fix(agc): alpha-to-coverage from a 32_AR MRT0 export without a color target
2026-10-14 22:37:55 +13:00
Dean 2582ec1193 Merge pull request #5309 from hugodearaujo/feat/agc-sparse-dmabuf-import
feat(agc): import shared ranges above 64 MiB as sparse buffers over udmabuf chunks
2026-10-14 21:50:50 +13:00
Gjon Gojcevic c9ba43bfb7 feat(videoout): present PQ display buffers through an HDR10 swapchain
Detect HDR output on the host: on Windows the monitor showing the window
must run in HDR10 (DXGI IDXGIOutput6), and on every host the window's
Vulkan surface must offer a 10-bit VK_COLOR_SPACE_HDR10_ST2084_EXT
swapchain with VK_EXT_swapchain_colorspace and VK_EXT_hdr_metadata. The
monitor's luminance comes from DXGI; hosts that report none get 1000
nits peak and full frame, 0 nits black.

While HDR output is available, sceVideoOutGetOutputStatus reports
dynamic range 2 and flag bit 0, sceSystemServiceGetHdrToneMapLuminance
reports the monitor's luminance, and every open port's output mode
event fires when that changes. PQ display buffers are presented
unchanged through an HDR10 swapchain with HDR metadata; any other
display buffer switches back to the SDR swapchain. Without HDR output
the PQ to SDR conversion uses the peak the title was last told as its
white level.

AI-assisted: yes
2026-10-14 02:55:21 -04:00
Gjon Gojcevic 76c0ba53e3 feat(agc): present BT.2100 PQ display buffers on an SDR output
Accept the 10-bit BT.2100 PQ display formats 0x8100070422000000 (red
in the low bits) and 0x8100070400000000 (blue in the low bits) and
convert them to 8-bit sRGB when presented: PQ EOTF, BT.2020 to BT.709
primaries, a luminance tone map whose white level is the tone-map
luminance sceSystemServiceGetHdrToneMapLuminance reports (reference
white min(203 nits, white level)), and the sRGB encoding.

The conversion runs on the CPU for linear buffers and DCC clear values,
in the color transfer shader for tiled guest buffers, and in the
presentation pass for resident images. The three share the PQ EOTF
table computed once in double precision.

AI-assisted: yes
2026-10-14 02:55:21 -04:00
Dean d000959a6c Merge pull request #5313 from Raghav314/perf/agc-msaa-fill-host-only
perf(agc): do not store a compute fill a multisampled target takes as its clear
2026-10-14 19:28:50 +13:00
umangandClaude Opus 5.5 f05086278d fix(agc): save the guest bytes of units without a write generation when results land outside the write watch
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 11:53:41 +05:30
Dean a94675fb17 Merge pull request #5039 from YossiYad/fix/agc-sample-array-target-layer
fix(agc): sample a slice-0 array color target as a 2D texture through its image
2026-10-14 19:20:40 +13:00
Yossi Yadgar 70d7497a27 docs(debt): note the array layer views of slice-0 color targets 2026-10-14 18:56:46 +13:00
NikoandClaude Opus 5.5 83787e692a perf(agc): compare Windows direct imports only after a write-watch mark
On Windows every direct host import leaves the write watch, and a storage
image over it compares its whole surface with its guest-byte copy at every
collect epoch. #4432 measured 7-8.6 s of every 10 s in those compares on a
GTA V scene, with 98% of the compared surfaces unchanged.

Tracker blocks that leave the watch now keep their write-watch marks as a
hint. Leaving the watch gives a block a fresh hint stamp, and
UntrackedHint walks a range of such blocks, at most once per collect epoch
and thread, stamping the blocks whose pages a CPU store marked; it
returns the newest stamp of the range. compareUntracked puts that stamp in
its memo key in place of the collect epoch, so an image is compared again
after a mark in its range, a driver store, a mapping change or a recorded
GPU write. The byte compare still decides whether anything changed: a mark
the driver makes without a CPU store costs one compare and drops nothing.
Nothing else reads the hint. Collects, Watched, UnchangedSince,
ChangedBlocks and NewestStamp still treat those blocks as untracked, and
Linux is unchanged.

agc_driver_dcc_keys_first_write refreshes an imported storage image over
50 collect epochs with no store: it compared the image 50 times before and
0 times now. A CPU store is still uploaded on the next refresh, and a store
of the bytes already there costs one compare and keeps the pending
results. agc_write_tracking checks the hint itself.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 18:56:27 +13:00
Dean e46c695054 Merge pull request #5657 from oneandonlydean/fix/storage-save-at-unwatch
fix(agc): save the guest bytes of pending storage results when their range leaves the write watch
2026-10-14 18:23:11 +13:00
Dean Galvin 4e136dd323 fix(agc): decide the sparse dma-buf import before importing and throw after
A shared range larger than one chunk now takes the sparse path only when
the device binds sparse memory, udmabuf is open, the range's backing
offset is on the sparse block size, the sparse buffer accepts every
memory type a chunk import can have and the backing file covers the
last chunk. All of that is known before a chunk is imported. Once the
path is chosen, a failed chunk import, fence creation, bind, fence wait or device
address throws instead of going on to the whole-range udmabuf and copies.

agc_host_import_sparse checks the import against a mocked device with
the real udmabuf: a range above size_limit_mb becomes one sparse buffer
bound to one chunk import per 64 MiB, an unaligned backing offset
imports no chunk, and failures of UDMABUF_CREATE and vkQueueBindSparse
after the decision throw.
2026-10-14 18:16:17 +13:00
Dean d4efd33978 Merge pull request #4895 from fdfawef324/feat/agc-index-attributes-indirect
feat(agc): execute INDEX_ATTRIBUTES_INDIRECT (PM4 0x91)
2026-10-14 18:12:18 +13:00
Dean Galvin db4fd5f29c fix(agc): take the GPU lock when saving pending storage results before the range leaves the write watch
A draw made outside the GPU lock (agc_driver_recorded_kept_bytes) imports a target's memory and leaves the watch there; the hook threw instead of saving. It now takes the lock, as FlushPending does.
2026-10-14 18:04:20 +13:00
MathiasDevelopes 8d89265c7a fix(agc): initialize stencil control and reference masks
(cherry picked from commit 85e0d98bba896002539365bb29a7a39a8e43f501)
2026-10-14 17:57:06 +13:00
codingreapermapsandClaude Opus 5.5 c8662b6bca fix(agc): accept alpha-to-coverage from a 32_AR MRT0 export without a color target
radeonsi exports MRT0 as 32_AR when alpha-to-coverage is on and no color
buffer is bound, so a depth-only draw can depend on that alpha. The
decoder now accepts 32_AR and an absent MRT0 target, and reads the MRT0
export format from SPI_SHADER_COL_FORMAT, which it masked to 0 for
draws without a color target.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:57:00 +13:00
hugodearaujoandClaude Opus 5.5 5894feff7e feat(agc): import shared ranges above 64 MiB as sparse buffers over udmabuf chunks
On Linux a shared-backed guest range is imported through one udmabuf
covering the whole range, and udmabuf refuses buffers above its
size_limit_mb (64 MiB by default). Larger ranges fell back to copies; an
address-based dispatch then synced its whole mirror, storing back every
pending storage image inside it each time.

Such a range is now one sparse buffer bound to the existing 64 MiB chunk
imports. The bind runs on a sparse binding queue of its own when the
device has one, so imports made at map time (without the GPU mutex) can
use it; otherwise only under the GPU mutex on the main queue.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 17:55:39 +13:00
Dean Galvin e56a7c2351 fix(agc): save the guest bytes of pending storage results when their range leaves the write watch
On Windows, singly mapped direct memory is private since #5596, so its host
import is a direct import that leaves the write watch instead of a shared
alias that stays watched. A storage image whose results were pending when
its memory was imported had no saved guest bytes, and storing them threw.

GuestMemory now calls a hook before Unwatch and before an ImportWatched
that does not stay watched. Every image with results pending over the
range saves the guest bytes of its pending units that have none, then
collects and records the pieces the keep rule leaves to the CPU for the
writes the watch saw. The next store and Refresh leave those pieces to the
CPU and compare the rest with the saved bytes. A map-time import made
without the GPU lock is deferred to the locked retry when pending results
overlap the range.
2026-10-14 17:43:41 +13:00
fdfawef324andClaude Opus 5.5 dd3c13ffd7 feat(agc): execute INDEX_ATTRIBUTES_INDIRECT (PM4 0x91)
The driver had no handler for the PFP INDEX_ATTRIBUTES_INDIRECT packet,
so a submission with it threw "opcode is not known in the reference".
The packet now reads the 16-byte record at attribute_base +
attribute_index * 16 when it executes and loads INDEX_BASE,
INDEX_BUFFER_SIZE and INDEX_TYPE from it as the direct packets do.
The packet encoding follows PAL's BuildIndexAttributesIndirect and the
record layout PAL's IndexAttribIndirect (gfx9Chip.h, c5e80007).
indirect_mode 1, reserved bits and record index types above 3 throw.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 22:41:57 -04:00
Dean 803cbf2f81 Merge pull request #4715 from Lorkydey/fix/agc-inactive-vertex-point-size
fix(agc): accept inactive vertex point size on non-point draws
2026-10-14 15:31:48 +13:00
Dean c5f839f651 Merge pull request #4702 from tv7/feat/gs-fast-launch
feat(agc): decode GS_FAST_LAUNCH geometry with one point per subgroup
2026-10-14 15:31:33 +13:00
raghav314andClaude Opus 5.5 5acd68f949 perf(agc): do not store a compute fill a multisampled target takes as its clear
A multisampled color target keeps its samples only in its host image, and every guest read of its
color memory that the driver checks is refused (RequireNoMultisampleTarget). A uniform compute fill
over exactly its color memory with an exact clear value becomes the target's clear through
NoteColorMetadataFill, so BufferFill no longer stores it into the host import as well.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 07:37:58 +05:30
Michon e9249a0a47 feat(agc): host import windows, least-recent eviction under the budget
A registered range larger than 64 MiB is imported one 64 MiB window at a
time for buffer and fill accesses instead of whole, and the live imports
stay under a budget: APS5_HOST_IMPORT_MIB, else the larger of 6 GiB and
half the physical memory, at most the largest host heap less 2 GiB
(imports and type-3 mirrors share it; #167's rule). An import past the budget
retires the least recently used imports not used in the last 256
lookups.

- An access across window edges imports the cells it touches, grown to
  the union of the range's imports it overlaps, so uses across
  neighbouring edges share one import instead of replacing each other.
- An address-based build (AcquireRegistered) imports the range whole; a
  window over its first byte does not serve it.
- Every refusal (alignment, a failed run, a held import, the budget with
  what may be evicted) is decided before anything is retired.
- An import made when a range is mapped never evicts: the map observer
  holds no GPU lock.

The window size and the budget have setters for the tests.
2026-10-14 14:51:48 +13:00
pushkarreddyy acbc573212 feat(libSceAgcDriver): skip CMASK decode on single-sample views with viewMip > 0 2026-10-14 14:15:25 +13:00
Dean 604ce71864 Merge pull request #4947 from Kikoarl/perf/agc-draw-recipe-depth-expansion
perf(agc): expand draw recipes to support depth targets
2026-10-14 14:00:34 +13:00
AlxcBTWandClaude Opus 5.5 ca95f74097 perf(agc-driver): join the imports of adjacent ranges of any size that a buffer is bound exactly over
A bound buffer is joined across adjacent registered ranges only when every
one of them is larger than the 8 MiB window. PPSA25344's loading binds 16 MiB
and 4 MiB buffers whose backing was registered in two to six adjacent
pieces with the same access, some of them smaller than the window, so each
use of such a buffer was copied on the CPU: up to 348 GB in the first 300 s
of loading.

A bound buffer that starts at the first crossed range's start and ends at the
last one's end now joins those ranges whatever their size. A heap grown in
pieces matches that only for a binding over exactly its current pieces, so
growth beside a joined import does not re-import it. joinedCovers, used when a
joined import retires, accepts any adjacent run of ranges.

importJoinTests binds exactly over two 64 KiB pieces of a partly filled
window and requires one import of both; a binding across them that starts
inside the first piece stays apart.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 13:34:31 +13:00
Dean 29d5f067b9 Merge pull request #5003 from tibzejoker/feat/depth-storage-array-views
feat(agc): storage image access to depth planes through 2D array views
2026-10-14 13:32:41 +13:00
Dean cb320fd884 Merge pull request #4603 from AlxcBTW/perf/agc-memory-value-fill
perf(agc): take the memory-value dword fill kernel as a buffer fill
2026-10-14 13:31:43 +13:00
Kikoarl 3ebad9045e perf(agc): expand draw recipes to support depth targets 2026-10-14 02:00:40 +02:00
AlxcBTWandClaude Opus 5.5 6ffc99bedb perf(agc): take the memory-value dword fill kernel as a buffer fill
The 9-word kernel #3816 matches stores one dword per thread, loaded with
s_buffer_load_dword through the descriptor in user[4..7]. Run as a
compute dispatch, its stores are pending writes of unknown value, so a
later read of DCC keys in that range waits for the GPU.

fillBuffer now takes it as a uniform buffer fill when all of these hold:
- the output descriptor's format is 32_UINT, 32_SINT or 32_FLOAT (20-22),
  so buffer_store_format_x stores the loaded dword unchanged;
- the value descriptor covers the loaded dword: num_records times the
  stride (num_records alone when the stride is 0) is at least 4 bytes,
  the range the recompiler gives a scalar buffer load;
- exactly 8 user SGPRs and group id X enabled, so the group id is s8;
- a direct, non-partial dispatch, 64x1x1 threads, one group in Y and Z,
  and a 16-byte aligned range of whole 16-byte units.

The value is read when the fill is recorded, after the labels queued
before it, as the pattern-fill kernel's source is. When the value is one
byte repeated and that byte is a DCC key code, the fill goes into the GPU
key-store memo whatever ClassifyFill reports: the memo answers a key
read only while this fill is still the newest pending write over the
keys, and a fill over a color target's keys is not a storage image's
keys fill.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 00:44:41 +02:00
thibautandClaude Opus 5.5 696d1660c1 feat(agc): storage image access to depth planes through 2D array views
A 2D array storage view of a depth plane from slice 0 threw. It now gets
a storage image over all its slices: each slice with a depth surface at
the plane's address plus a whole depth slice per layer is copied from
that surface before every use and back after the work; the other slices
hold guest memory like any storage image. Cubes, stencil arrays, later
base slices and layer pitches other than the depth slice size still
throw.

GTA V (PPSA04264) stops at compute shader 0x242d4a200 on a 5-slice R32
Z_64KB_X array over its shadow depth surfaces.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01V1W4jdNSKTV7xwuS6xQPJV
2026-10-13 23:38:51 +02:00
codingreapermapsandClaude Opus 5.5 f6aa98b296 feat(agc): render into SW_64KB_Z_X color targets
SW_64KB_Z_X (0x18) color targets use the texture equations and 64 KiB
block geometry of that swizzle mode, including the equation bits above
the block that R_X targets already apply. PPSA12361 renders the R32
float copy of its depth into one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-13 22:36:27 +01:00
Dean 8d98e8851f Merge pull request #4474 from JJenkx/fix/multisample-target-retirement
fix(agc): retire fully unmapped multisample targets
2026-10-14 10:23:12 +13:00
Dean 61b8742b6b Merge pull request #4515 from AlxcBTW/perf/host-import-join
perf(agc-driver): join the imports of adjacent large ranges when a buffer is bound across them
2026-10-14 05:55:27 +13:00
Adrià Franch 1a2a4265c1 docs(debt): record the GS_FAST_LAUNCH decode assumptions 2026-10-13 17:00:51 +02: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à 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