129 Commits
Author SHA1 Message Date
mrfelek 3e7a0ae671 fix(shader): keep the f32 floor and ceil flush where the device flushes by default
NVIDIA's proprietary driver flushes f32 add, multiply and fma denormals by default but not the inputs of floor and ceil, so its startup probe failed only on those two. Drop their software flush only where the device declares DenormFlushToZero 32.
2026-10-26 08:57:06 +00:00
mrfelek 415e302c9c perf(agc): probe the f32 denormal flush on NVIDIA's proprietary driver
The driver declares no shaderDenormFlushToZeroFloat32, so every f32 add, mul and fma kept the software flush. Run the same probe as on MoltenVK and use the driver's default handling when it matches the emulated flush.
2026-10-26 08:57:06 +00:00
Adrià 5d48c6233e Merge pull request #9401 from tibzejoker/perf/grow-pipeline-store
perf(agc): size the pipeline store to the title's working set
2026-10-25 19:07:59 +01:00
Adrià 29ce6d87eb Merge pull request #8735 from hamb3r/perf/agc-compute-prewarm
perf(agc): prebuild compute pipelines and prewarm dispatch shaders ahead of execution
2026-10-25 13:20:12 +01:00
Adrià 654cb0bb4c Merge pull request #9965 from ebarr24/ws/ppsa01746-step1
perf(agc): pipeline queue-0 DMA_DATA by default
2026-10-25 11:06:26 +01:00
thibautandClaude Opus 5.5 ad047899bb perf(agc): grow the pipeline store from 1024 up to 4096 entries
The graphics pipeline store kept a fixed 1024 entries and the pipeline journal replayed at most
1024 records, and a replayed pipeline that found the store full was dropped. A title whose
working set is larger rebuilt the rest at the draw on every run, and evicted pipelines it kept
using were rebuilt again and again.

The bound now starts at 1024 and doubles up to 4096: a journal replay that does not fit grows it
instead of dropping the pipeline, and so does eviction churn (a ghost list of evicted keys, checked
every bound/32 evictions: more than half of them came back). It never shrinks during a run;
APS5_PIPELINE_STORE_ENTRIES still fixes it. The journal loads its last 4096 records by default
(APS5_PIPELINE_JOURNAL_ENTRIES). Resident pipelines are bounded by count, not bytes.

agc_driver_recorder_tests --benchmark-pipeline-store-memory [count] (opt-in, not registered)
builds count graphics pipelines with one fragment module each through CachedPipeline and prints
process private bytes and VK_EXT_memory_budget heap usage before and after: with the triangle
shaders about 59 KiB host and no device memory per pipeline on an RTX 4090, 100 KiB host and
8 KiB device-local on a UHD 770.

Tests: agc_pipeline_store_growth (fails on main), agc_pipeline_store_fixed_bound,
agc_pipeline_store_replay_growth (replays on one thread: the mock Vulkan is not thread-safe).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_017SNUYRd7sRKZrMYpquAsnU
2026-10-25 02:02:41 +02:00
Yunus Shaikh 02f6f8e66f Merge main into fix/agc-sync-settle-initial-dcc-main2 2026-10-24 11:49:50 -07:00
milescovielloandClaude Opus 5.5 88718239a7 fix(agc): read the pipeline journal's modules at replay instead of holding the file
The journal was read whole into one buffer that the decoded modules
viewed, so a replay held the entire file (1 GiB at the default
APS5_PIPELINE_JOURNAL_MAX_MIB) until its last entry was built. The load
now parses the file a record at a time through positioned reads and keeps
it open; a replay thread reads an entry's modules again when it builds the
entry and rejects it when a record no longer reads back as loaded. The
replay report gains the peak module bytes held at once.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 14:18:09 +02:00
Ethan Barr ce6865fc0d perf(agc): pipeline queue-0 DMA_DATA by default
Queue-0 DMA_DATA packets drained the draw pipeline unless APS5_PIPELINE_DMA was set, so the packet worker waited on the committer at every small immediate store. Kena PPSA01746 issues 17-23k DMA_DATA packets per 10 s and 11329 of its 20134 pipeline drains per 10 s are opcode 0x50. enqueueDmaPacket now queues DMA_DATA stores and fills unconditionally, and keepsDrawPipeline keeps the pipeline for a copy or fill whose source and destination miss every queued write. The APS5_PIPELINE_DMA switch and its documentation are removed and the tests run the default path.
2026-10-24 02:35:37 +02:00
Yunus Shaikh 3e3808e55b fix(agc): rewatch only a settled import's own pages
rewatchSettledImports passed an import's 64 KiB-rounded span to
Rewatch. For an import whose size is not a block multiple the span's
last block holds pages past the allocation, the walk fails on them, and
the import stayed compared for good. Pass the import's own pages; the
shared-block and live storage image checks still use the block span.
2026-10-24 01:49:43 +02:00
hamb3r cb29f63d43 perf(agc): address review on exception catches, docs, and test entry 2026-10-24 00:59:49 +03:00
Adrià ea695358ee Merge pull request #8527 from eliasbruno124-dev/fix/host-import-settle-order-20261020
fix(agc): order deferred import settles with main-queue accesses
2026-10-23 20:59:54 +02:00
Adrià e94545a592 Merge pull request #7644 from EagleBearerYS/perf/agc-lazy-resolve-shader-abi
perf(agc): leave a lazily registered shader's resolved ABI to its first use
2026-10-23 16:14:38 +02:00
eliasbruno124-dev 9d4a0ebfcd docs(agc): describe deferred settle submission ordering 2026-10-23 13:10:59 +02:00
raghav314andClaude Opus 5.5 c0dbd1871c perf(agc): draw finisher pipeline (APS5_DRAW_PIPELINE)
The second half of a pipelined draw (stage materialization, cache insert,
enqueue) runs on a finisher thread, overlapping the next draw's packet
work; draws whose stages could write memory are held back. Commits skip
work whose inputs did not change since the last commit, color metadata
passes are ordered through the pipeline, resource and storage-image proofs
are reused within one collect epoch until a write breaks them, and a shape
entry is refreshed in place at the second sight of the same read user words.
APS5_VERIFY_DRAW_PIPELINE checks reused proofs against a full check.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-23 03:46:57 +05:30
Adrià cf2c667060 Merge pull request #9203 from mazen320/docs/entry-register-contract
docs(architecture): record main-module entry register contracts
2026-10-21 20:22:04 +02:00
MarcZX10 2a12152f2b test(agc): clarify imports after partial unmap 2026-10-21 16:42:05 +00:00
Maz 0b411ccee0 docs(architecture): record main-module entry register contracts
Answer #9048 from the host stubs: Linux, Windows and macOS differ, and the console is not measured.
2026-10-21 19:08:57 +04:00
Yunus Shaikh 3253ab49bb perf(agc): leave a lazily registered shader's resolved ABI to its first use
Under APS5_LAZY_SHADER_PREPARE=1 registration prepares nothing, but
ResolveShaderAbi still planned and prepared every compute or pixel stage
whose ABI the title resolved, so a title that resolves every shader's
ABI prepared them all at boot whether they ever drew.

With lazy preparation ResolveShaderAbi now plans the stage with the
ABI's state (its register and entry-point checks, as lazy registration
keeps them), records the ABI key, and queues the ABI's context and
primitive registers on the shader. The shader's first draw or dispatch
plans and prepares the queued ABIs with that state before it looks for
its artifact, so a shader that never draws or dispatches stays
unprepared and one that does gets the entries eager resolution would
have made. A stage whose plan fails is deferred as before. Without the
switch nothing changes; graphics-stage ABIs are still prepared when
they are resolved.

Test: agc_driver_lazy_resolved_abi resolves the ABI of four lazily
registered shaders and draws two. None holds prepared entries after
resolution, the two that never drew still hold none, and a drawn one
draws the same bytes as with eager preparation in a child process.
2026-10-21 04:18:23 +00:00
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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