Commit Graph
110 Commits
Author SHA1 Message Date
XairRrR ed599f525b fix(agc): serve sampled views of arrays past maxImageArrayLayers from their viewed slices
The sampled lookup kept the whole array's layer count: a texture over an
array of more than maxImageArrayLayers layers read and snapshotted the
whole surface and then threw in the Texture constructor, on every lookup,
even when the descriptor viewed a few slices. A menu that samples a
16x16 array of up to 8065 layers through its image tables spent
~55 ms per frame reading those surfaces only to reject them.

cachedTexture now applies the window the storage path uses
(StorageArrayWindow, renamed ArrayLayerWindow since both paths use it):
a view whose slices fit the limit is served as an array of its own
starting at its first slice, and a view whose slices alone exceed the
limit throws before anything is read. A 2D image is one layer whatever
its last array field holds and is no longer refused by the storage path.

agc_driver_image_array_window_past_layer_limit samples and loads three
slices at 8100-8102 of an 8103-layer 1D array (before: "a texture array
with more layers than maxImageArrayLayers is not implemented") and checks
that a view of all its layers is refused with the window's error on a
device whose limit is lower.

AI-assisted: yes
2026-10-19 21:37:26 +02:00
XairRrR ae8500deb8 fix(agc): pass 3D storage volumes through StorageArrayWindow unchanged
A volume's depthOrLastArray is its depth, bounded by maxImageDimension3D,
not by maxImageArrayLayers: a 2049 to 4096 deep volume is a valid image
and threw since the window was introduced. Reported by Dextroid17.

AI-assisted: yes
2026-10-19 21:36:46 +02:00
XairRrR 3b6f6359f7 fix(agc): serve storage views of arrays past maxImageArrayLayers from their viewed slices
A PS5 array may hold up to 8192 layers; Vulkan devices allow fewer (2048 on
NVIDIA). A storage view of such an array created its whole image, with more
layers than the device allows: vkCreateImage is then invalid
(VUID-VkImageCreateInfo-arrayLayers-02256) and a store into a layer the device
did not back faults the GPU. Uncharted: Legacy of Thieves Collection (PPSA05684) views a 16x16 RGBA8
array of up to 7809 layers in slices of a few hundred to 2048 in its
orphanage chapter, which lost the device (Xid 109, write invalid) a few minutes in.

StorageArrayWindow describes the viewed slices as an array of their own
starting at the first viewed slice when the whole array is past the limit;
a non-XOR swizzle does not depend on the slice index, so the slices detile
alike. Views whose slices alone exceed the limit, XOR swizzles that mix in
the slice, and any other image past the limit now throw instead of creating
an invalid image.

AI-assisted: yes
2026-10-19 21:36:46 +02:00
billybobpicklioandClaude Opus 5.5 73983f1c29 perf(agc): queue CPU-path indirect draws on the draw pipeline
An indirect draw drained queue 0's draw pipeline on entry and recorded every
record on the worker, so titles that draw mostly through DRAW_INDIRECT,
DRAW_INDEX_INDIRECT and DRAW_INDEX_INDIRECT_MULTI got no overlap between the
worker's capture of one draw and the committer's recording of the previous.

The draw now drains only when its records or its count overlap a write still
queued on the pipeline. Records drawn inline drain first, so they stay in
order, and the last drawn record is queued like a direct draw, carrying its
write ranges. APS5_NO_PIPELINED_INDIRECT=1 restores the drain on every
indirect draw.

agc_driver_pipelined_indirect_draw holds the committer with a blocking item and
checks that two indirect draws queue behind it and draw what a direct draw
does, and that a draw whose record a queued label writes waits for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:23:10 +02:00
SP4C3B4R-8andClaude Opus 5.5 4a6069481f perf(agc): copy draw inputs on NVIDIA's Windows driver instead of binding host imports in place
NVIDIA's Windows driver reads a host import of guest memory far slower than a buffer it allocated.
Since draw vertex and index ranges are bound in place from host imports (#2253, measured on Linux), Astro
Bot's Crash Site on Windows 11 with an RTX 5070 Ti spends 164 ms of every frame in draws on the GPU, and
45 ms with the ranges copied as before; with the same build, 4.71 fps in place against 7.52 copied. An
RTX 3060 shows the same (draws 32.3 against 24.3 ms per frame), while AMD's Windows driver reads them in
place about as fast as a copy (RX 9070 XT: 69.8 against 73.1 ms), so copying there only costs CPU.

The device decides once (DrawInputsInPlaceUsable, Context::drawInputsInPlace): ranges are copied with
VK_DRIVER_ID_NVIDIA_PROPRIETARY on Windows and bound in place everywhere else. APS5_DRAW_INPUTS_IN_PLACE=1
binds them in place and APS5_NO_DRAW_INPUTS_IN_PLACE=1 copies them on any device, the second winning.
agc_draw_inputs_in_place_driver checks the decision per driver and host; agc_draw_inputs_copied checks
that an aligned imported range is copied and its read not noted when the context copies, whatever the
inherited environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GN8FJFTedayRXLHythXkZ4
2026-10-19 11:48:36 +02:00
Josip Parlov df72da5dbf docs(agc): describe trimmed host imports and backing serials 2026-10-19 08:02:13 +02:00
ThatsLeo ac14d6317d perf(shader): flush f32 denormals in hardware when a startup probe matches the emulation
Devices with DenormFlushToZero for f32 run the mixed denormal-flush programs with and
without the hardware mode at startup; when every result matches, add, sub, mul, fma,
floor and ceil in the default rounding mode drop their software flush.
2026-10-19 07:00:13 +02:00
Adrià 23629c2847 Merge pull request #6454 from EagleBearerYS/perf/shader-image-table-reuse-v3-pr
perf(agc): share one image table per heap and write epoch through capture, compare, resolution and descriptors
2026-10-19 03:39:03 +02:00
Adrià 7978a4b585 Merge pull request #6366 from EagleBearerYS/fix/agc-defer-unpreparable-abi-stages-v3
fix(agc): leave a stage the title's pipeline ABI cannot prepare to its first use
2026-10-19 03:26:59 +02:00
Dean 60fb8078c7 Merge pull request #6067 from meh7an/perf/libs-mimalloc-windows
feat(libs): let relinked Windows games opt into mimalloc
2026-10-19 13:09:36 +13:00
Yunus Shaikh 106b79e007 Merge main into perf/shader-image-table-reuse-v3-pr
The data-hit verification keeps this branch's pending-span query on its ShaderMemory and takes #5958's CaptureInvocation, which retries a capture with converted samples.
2026-10-18 17:04:23 -07:00
Yunus Shaikh 10b798fb99 Merge main into perf/shader-image-table-reuse-v3-pr
Keeps this branch's tagVariantRuns declaration next to main's dumpRequest suffix parameter in Driver.hpp, and reads #6232's pointer-chain test words from its table's snapshot, where this branch keeps them.
2026-10-18 17:03:39 -07:00
Adrià eb1a15b5cc Merge pull request #6314 from eliyarson/feat/relinker-game-patches
feat(relinker): add conversion-time game patches
2026-10-19 00:37:51 +02:00
Dean e1248700d9 Merge pull request #7646 from oneandonlydean/revert/6112
revert(relinker): run guest preinit arrays before the module initializer (#6112)
2026-10-19 10:10:20 +13:00
Mehran 9824c46fe0 feat(libs): let relinked Windows games opt into mimalloc 2026-10-19 00:08:47 +03:00
Dean Galvin c3b9001592 revert(relinker): run guest preinit arrays before the module initializer (#6112)
This reverts commit 376c040294.
2026-10-19 09:38:31 +13:00
Adrià FranchandClaude Sonnet 5.5 5b4dea8a36 docs: APS5_NO_PRESENT_EPOCH row and the presenter's epoch adoption
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 21:21:24 +02:00
Dean 376c040294 Merge pull request #6112 from ayam04/fix/relinker-guest-preinit
fix(relinker): run guest preinit arrays before the module initializer
2026-10-19 07:33:14 +13:00
Yunus Shaikh d0325058f7 Merge main into perf/shader-image-table-reuse-v3-pr
#7527's selected-record limit test reads a record's descriptor words through the
table snapshot's shared words list; this branch keeps a snapshot's words per column,
so the test reads them from its column instead.
2026-10-18 11:33:00 -07:00
Dean e3c46940b3 Merge pull request #7527 from EagleBearerYS/fix/shader-selected-records-image-pair-limits
fix(shader): keep a shader's selected image-table records within the image and pair limits
2026-10-19 07:22:18 +13:00
Eli Yarson 346b0a92ca feat(relinker): add conversion-time game patches
A per-game relinker-patches.toml (or --patches) replaces verified bytes in
the input executable and bundled modules before conversion. Entries are
checked against the expected bytes and against every in-place writer,
the ELF header range and relocation targets. An <output-stem>.patches.toml
manifest records the applied entries, and --verify-patches checks an input
or a converted output. With no patch file the output is unchanged.
2026-10-18 14:57:13 -03:00
Ayamullah Khan 9c43211664 fix(relinker): run guest preinit arrays before the module initializer
Converted guest modules never ran their DT_PREINIT_ARRAY entries: the
reader validated the array and the unused-NID filter kept its targets,
but no writer emitted or ran it. ld.so runs a shared object's preinit
array only for the main executable, so the Linux output runs the
entries from the DT_INIT stub before the module initializer; the macOS
and Windows outputs run them before the initializer in their startup
stubs, and the deferred Windows output exports a thunk that runs them
before the initializer for runtime-loaded modules. A module with a
preinit array and no initializer is still rejected, since the Linux
stub exists only with one.
2026-10-18 19:09:46 +02:00
Yunus Shaikh 54f2cf31b1 Merge main into perf/shader-image-table-reuse-v3-pr 2026-10-18 05:06:50 -07:00
Yunus Shaikh 364fb665c1 docs(shader): note that selected image-table records merge past the image and pair limits 2026-10-18 04:33:39 -07:00
Yunus Shaikh 3ea0be6488 docs(agc): say a settled import under a storage image stays compared
ARCHITECTURE.md and the libSceAgcDriver debt entry now say that a
settled Windows import under 1 MiB returns to the write watch only when
no storage image overlaps it, and why.
2026-10-18 00:47:31 -07:00
Yunus Shaikh db622e1c4f Merge main into perf/shader-image-table-reuse-v3-pr 2026-10-17 21:26:16 -07:00
Dean Galvin 70e444582d Merge main into perf/agc-async-settle 2026-10-18 16:04:01 +13:00
Yunus Shaikh 8f03535c8f Merge main into perf/agc-async-settle
Main's #7269 keeps the synchronous settle's buffer, memory and fence
per device in ImportSettle, so clearing one device's imports releases
only that device's objects. This branch's settle batches move there
too: each device's ImportSettle holds its batch target, memory and
batches with their fences, so switching devices no longer drops a
device's batch handles, and clearing a device waits for and destroys
only its own batches. The per-import settle state is renamed
pendingSettles to stay clear of main's per-device map, and a submitted
settle records the device whose batch it is in.
2026-10-17 16:36:23 -07:00
Yunus Shaikh a53d23f810 Merge main into perf/shader-image-table-reuse-v3-pr
Main's #6402 stores to image tables: a table with a written entry takes
the fresh path, as it does on main, and so does one without a sampled
table binding, whose entries the fresh path rejects. The shared place
step uses the binding as a pointer. Main's #6614 frees descriptor sets
from shared pools one by one, while this branch keeps only dedicated
pools, so both stay. #7101's bulk key read counts its unmapped-page
skips as unmapped, and its key words sit next to this branch's write
stamp and compared runs.
2026-10-17 16:05:44 -07:00
Yunus Shaikh 4cb0512329 Merge main into perf/shader-direct-cpu-evaluable-images
Main's #7101 reads a key buffer's words in one call where this branch
names the reason a narrowing is skipped. Keep main's bulk read and
count both of its unmapped-page skips as unmapped.
2026-10-17 15:58:42 -07:00
Dean b4f05ab328 Merge pull request #6631 from AlxcBTW/perf/dcc-key-presync
perf(agc): pre-sync the DCC key writers of a dispatch's images before the GPU mutex
2026-10-18 11:18:56 +13:00
Dean 0381ac76d1 Merge pull request #6632 from AlxcBTW/perf/handoff-spin
perf(agc): spin before blocking on the GPU mutex and the draw committer hand-offs
2026-10-18 11:18:40 +13:00
ThatsLeo e14a4e5138 fix(shader): stop first-lane reads that reach themselves from recursing
MayEvaluate and the evaluator track the first-lane reads they are inside, so a read reached again through a loop-carried phi or a constant slot is not evaluable instead of recursing.
2026-10-17 18:11:28 +02:00
ThatsLeo 745cdb5bf7 perf(shader): judge first-lane and constant reads by what they read
MayEvaluate took ReadFirstLane and ReadConst as evaluable without looking at
their sources, so plans kept conditions behind per-lane values.
2026-10-17 18:03:33 +02:00
Yunus Shaikh 5f822a5008 feat(agc): keep Windows imports under 1 MiB compared with APS5_NO_DEFERRED_SETTLES
The deferred settle of small Windows imports could only be turned off
through the test hook. The variable turns it off for a run, so a title can
be compared with and without it on one build; unset, nothing changes.
2026-10-16 12:38:16 -07:00
Yunus Shaikh c7e07cae44 Merge main into perf/agc-async-settle
Main's #5905 settles Linux dma-buf imports with settleImport, which is
now built on both platforms. The batched settle of Windows imports under
1 MiB, its counters and adoptSettleDevice's batch reset stay Windows-only,
and ClearHostImports drains the batches only there.
2026-10-16 12:37:41 -07:00
Adrià daf8913128 Merge pull request #5905 from hugodearaujo/fix/agc-prewarm-mapped-dma-buf
fix(agc): settle dma-buf imports on Linux on the settle queue
2026-10-16 21:05:30 +02:00
AlxcBTW 23a804e393 perf(agc): count the processors the process may run on for the hand-off spin guard
The automatic window needed 8 logical processors, which a 4-core CPU with SMT passes while the waited-for thread may run on a sibling thread, and ignored the process affinity. It now needs more than 8 processors in the affinity mask (CpuTopology), falling back to hardware_concurrency.
2026-10-16 19:52:11 +02:00
Yunus Shaikh 8cd0b37a5f Merge main into fix/agc-defer-unpreparable-abi-stages-v3
Brings #5872 (shader preparation on first use), main's async driver log and its geometry subgroup checks. The prepared-shader modes are the union of both lists. With this branch, the pipeline ABI of a lazily registered shader it cannot prepare leaves the stage deferred instead of throwing, so LazyRegistration now requires the stage to stay deferred with no prepared entry after that call; its dispatch and shutdown failure checks are unchanged. StderrCapture flushes the async log when it starts and before it reads, since a deferral's log line can otherwise still be queued. The undecodable-state check now matches the decode's VGT_SHADER_STAGES_EN reason rather than one message, which main's subgroup output check now gives first.
2026-10-16 06:25:28 -07:00
Adrià FranchandClaude 37db6a80b7 fix(agc): bound the async log's exit flush
On Windows the atexit handlers of a DLL run after the process has ended
its other threads, so the logger thread can be gone with lines still
queued and the unbounded Flush waits forever (guest_shader_link timed
out on 4 of the last 7 completed main builds since #6371). Run the bounded
FlushAtEnd there instead.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-16 14:30:15 +02:00
AlxcBTW e74484e0fb perf(agc): spin before blocking on the GPU mutex and the draw committer hand-offs
At the Minecraft Dungeons II title the queue-0 worker, the draw committer and the compute worker
hand work to each other 49,000 times per 10 s through the GPU mutex, the committer's condition
variable and DrawPipeline::Drain, and each hand-off that has to wait costs a kernel wake-up. The
three workers are busy 21-34 % of the time: the GPU idles because the chain of hand-offs is slow,
not because a thread runs out of CPU.

GpuMutexType::lock retries try_lock for a window before it blocks, the committer polls its item
count for the window before it waits on its condition variable, and Drain polls for twice the
window before it waits for the committer. A hand-off that completes within the window costs no
wake-up. The window is 150 us on machines with at least 8 logical processors and 0 below that;
APS5_HANDOFF_SPIN_US sets it (0 turns it off). Ordering and the lock's semantics are unchanged.
The test agc_handoff_spin covers the window policy, the spin helpers and contended locks, and
docs/dev/ARCHITECTURE.md describes the spin.
2026-10-16 14:19:43 +02:00
AlxcBTW 2a15be09c9 perf(agc): pre-sync the DCC key writers of a dispatch's images before the GPU mutex
The stage-A pre-sync of a dispatch listed only the surfaces that stage B reads on the CPU. The DCC
keys of an image that a recorded kernel writes were not in the list, so stage B waited for that
kernel in WaitForKeyWriters with the GPU mutex held and every other worker queued behind the wait.
At the Minecraft Dungeons II title the pre-sync fired once in 296,000 dispatch checks while
WaitForKeyWriters synchronized 400 times per 10 s.

PresyncSurfaces now also returns the key range of every image binding (PresyncSurface::dccKeys).
presync waits for a key range only while recorded work writes it, the key-store memo does not
already give its value (DccKeyWritersPending) and, under the mutex it takes for a moment anyway, the
pending stores do not give the keys either (DccKeyWriterNeedsSync, the test WaitForKeyWriters makes
before it synchronizes). Which keys the dispatch reads, and when, is unchanged: the locked lookup runs
as before and finds nothing pending.

APS5_NO_DCC_PRESYNC=1 leaves the key ranges out. The [presync] line counts key ranges checked,
pre-syncs that only the keys required and the outcomes of WaitForKeyWriters. The test
agc_driver_dcc_key_presync covers a kernel that writes the keys, a recorded fill of the keys and
nothing pending, and skips with the reasons check_skips.py expects when the device has no timeline
semaphores or no host import.
2026-10-16 14:18:46 +02:00
Yunus Shaikh 51d4babc64 Merge main into perf/shader-image-table-reuse-v3-pr 2026-10-16 05:09:29 -07:00
Adrià 127e5bf826 Merge pull request #5872 from Sam-mon7/perf/lazy-shader-prepare
perf(agc): allow shader preparation on first use
2026-10-16 14:01:24 +02:00
Adrià 0615cd2b82 Merge pull request #5875 from adriadam10/perf/agc-pipeline-journal
perf(agc): journal the pipelines of a run and build them ahead on the next one
2026-10-16 13:19:18 +02:00
Adrià Franch ebc2342d1e docs(architecture): describe dispatches queued as pipeline items 2026-10-16 12:53:38 +02:00
Adrià FranchandClaude Fable 5.1 4aafda8e3b perf(agc): journal the pipelines of a run and build them ahead on the next one
A graphics pipeline the store builds or a compute pipeline a dispatch builds is
appended to pipelines.journal next to the shader cache entries, as the
materialized result of each stage (the specialized SPIR-V, bindings and push
constants) plus the recipe the pipeline was built from: the stages'
identities, vertex input layout, descriptor set layout keys and the state
fields the Pipeline constructor reads. When a VulkanDevice is created with a
window (a headless device waits for its first draw or dispatch, since the
window's device usually replaces it first) the journal is loaded and its
pipelines are built on a pool of threads while the title loads, so the first
draw or dispatch of a configuration seen on an earlier run finds its pipeline
resident instead of compiling it on the queue worker.

The pipeline stores key on RecompileResult::Identity, a hash of the module
words, specialization constants, subgroup width and bindings that is the same
on every run (SharedSpirv::ContentHash, computed once per shared module),
rather than on the process-lifetime variant ids; a generated stage is keyed
like any other. CachedPipeline builds outside the store's mutex and the
library parts and link are built outside the library store's mutex, so replay
threads and queue workers build in parallel; a concurrent build of the same
key keeps the first inserted. A replayed pipeline enters the store at its
least recently used end and a full store takes none, and its optimized relink
waits for the first draw that binds it, so the title's own pipelines come
first in both. The compute pipeline creation moves into
VulkanDevice::createComputePipeline, shared by dispatches and the replay. The
store key also covers the color component mapping, the depth target's sample
count and a depth bias clamp, which the constructor reads.

The file names the device it was written for (the properties the driver's
pipeline cache file is named by) and starts over for another; a torn tail is
cut at a record boundary; two runs sharing the directory take turns on a lock
file and keep each other's records; a read failure never starts the file
over. A device cancels its replay before it is replaced or destroyed, and a
replaced device destroys its pipelines then rather than at the driver's stop.

APS5_NO_PIPELINE_JOURNAL=1 disables it; APS5_PIPELINE_JOURNAL_THREADS,
APS5_PIPELINE_JOURNAL_ENTRIES (the first recorded), APS5_PIPELINE_STORE_ENTRIES
and APS5_PIPELINE_JOURNAL_MAX_MIB bound it; APS5_PROFILE_DRAW prints a
[pipeline-journal] summary. APS5_VERIFY_SET_LAYOUTS=1 now also checks a
dispatch's set layout against the one described from its shader. The lavapipe
test suite, which shares one cache directory, runs with the journal off except
for the journal's own tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P6hw3kRAuQdfcU9YCZgaoK
2026-10-16 12:52:28 +02:00
Yunus Shaikh 19907d6ed4 Merge main into perf/shader-image-table-reuse-v3-pr
Resolves #4943's draw relocation against this branch: tagVariantRuns, which now builds a draw variant's runs and words, also records the captured words it shares with a table's supplied blocks, per region as before. A variant whose runs relocation splits keeps sharing for the draw cache's word compare, and skips the vouched validation, which already requires one run per region.
2026-10-16 03:25:29 -07:00
Yunus Shaikh 22f8c14801 Merge #6243's head into perf/shader-image-table-reuse-v3-pr 2026-10-16 02:40:10 -07:00
Yunus Shaikh ca174bc7e6 docs(agc): drop the disk cache's format version from ARCHITECTURE.md
The line said FormatVersion 36 while the code is at 37, as main's says 33 against 36. Without the number it no longer goes stale on each bump or conflicts between PRs that bump it.
2026-10-16 02:39:51 -07:00