Skip to content

perf(agc): import adjacent host ranges as aligned blocks and stop re-trying budget refusals - #6457

Merged
oneandonlydean merged 22 commits into
boykopovar:mainfrom
EagleBearerYS:perf/agc-host-import-aligned-blocks
Oct 19, 2026
Merged

oneandonlydean merged 22 commits into
boykopovar:mainfrom
EagleBearerYS:perf/agc-host-import-aligned-blocks

Conversation

@EagleBearerYS

@EagleBearerYS EagleBearerYS commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

What

A title that maps its GPU heap in 64 KiB pieces got one host import per piece. Resident Evil Village holds about 27,600 such ranges by its first scene and fills the import budget. At a full budget, every rebuild of the address-based build's address space asked again for every range the budget had refused.

  • Diagnostics:
    • each import request is tagged with its requester (map, build, access, join, segment) in the APS5_TRACE_HOST_IMPORT lines;
    • the driver prints the live, made, refused (and remembered) and failed counts per requester at shutdown: [gpu] host imports at shutdown: ….
  • Aligned blocks: adjacent registered ranges are imported as the largest aligned power-of-two block, up to the 8 MiB window, that holds the address and that they fill exactly.
    • A block over the budget steps down to the largest block that fits; a held or failed block steps down one size.
    • A failed block is remembered until its ranges change. It no longer pins the span to single ranges for the session.
  • Remembered refusals: a budget refusal for a request that may not evict is remembered until live imports drop or the budget grows. It is still counted as a refusal; requests that may evict always try again.
  • Test fix: the storage write-back overhang test reads its surfaces after FlushGpuWrites, as readers of guest memory must.

The worst case. A window that fills, or empties, one piece at a time with builds in between pays for each block upgrade: every doubling imports the block again, the old half included.

  • In the mock tests, filling one window piece by piece takes 64 imports and 16 MiB, against main's 4 MiB. A window shrinking the same way takes 32 imports and 7 MiB, against main's 0.
  • It is bounded by the window: at most 7 upgrades per address.

Overlap with #6499 (parl0v, the #6060 follow-up): it keeps an import whose head or tail a guest protect or unmap cuts off, re-attached to the surviving range with a new VkBuffer on the same memory.

  • This PR decides which runs are imported and when: adjacent registered ranges inside an aligned 8 MiB window as power-of-two blocks, and budget refusals remembered. It does not change ImportWatched, replaceRange or what a split keeps.
  • The two compose. A block upgrade here (a block replaced by the aligned block twice its size, at most 7 times per address while its window fills) is a new import of a larger run, which perf(agc): keep a host import across a split of its guest range #6499 does not keep, so the bound above still applies.
  • They conflict in GuestBufferMemory.cpp in three places: the replaced-bytes sum in importAllocation, the fields set on a new import, and the start of refreshImports. Whichever merges second keeps both sides, with the replaced-bytes sum counting perf(agc): keep a host import across a split of its guest range #6499's pinnedBytes. Either order works.
  • A single large range whose start moves or that grows is imported whole on main and here alike.

With #7339 and #6678 (both merged): this branch's merge of main 3de84fa (399cc69) takes #7339's guard over a host import while it is built, with this PR's signature and requester counts. On the failure path that retries as smaller blocks, the failed attempt's alias is unmapped before the retry and handed to the guard as null, so it is unmapped once. #6678's settle structures and calls sit beside this PR's refusals, as its description lists. #7339's copy lifetime check mapped its source and destination back to back, which this PR imports as one aligned block, so the source's protection change retired both; 1b304c8 maps them with an unmapped block between them, so the check still covers two separate imports. The merge of main 66e71b6 (0b84427) takes #5390's HostImportOffset wording in HostImportFor's declaration with this PR's requester parameter; this PR's own code computes no buffer offset as address - base, so #5390's lead reaches it through HostImportOffset.

Related: #4260 (its first cause: a buffer crossing adjacent ranges of a window that is not full is now imported in place when one filled aligned block holds it; a buffer crossing a block or window edge is still copied. #4367 fixed its second cause, the Windows alias).

Tested

Resident Evil Village (PPSA01556), Windows 11, RTX 3070 Laptop GPU (driver 617.42), on main 6ea62f9 with #6366, #5703 and #5815 (open) and local commits for its sample-count stops, at main's default budget:

  • The budget filled before the title ([gpu] host import budget: 5840 MiB live of 5840 MiB (APS5_HOST_IMPORT_MIB)).
  • It then kept refusing the same requests at every rebuild: [gpu] host import budget: 5375 MiB live of 5840 MiB (APS5_HOST_IMPORT_MIB); 21138 imports (71872 MiB) refused so far, last 0x200200000+0x30000000. The last is the 768 MiB heap, retried again and again.
  • On our test tree with perf(agc): keep Windows host imports of write-watched private memory watched #6060, at the default budget, imports were refused 304,394 times by the end of a 6-minute title hold, over 27,636 distinct 64 KiB ranges.
  • A Resident Evil Village pair with this PR follows as an edit.

Linux, lavapipe (CI's job mirror): ^agc 598/598, and the new tests 10 of 10 at each commit.

  • Mock tests (BdaResources.cpp), each red on main's import code and green here:
    • 64 pieces mapped one by one → 1 import (main 64);
    • an address-based build → 1 (64);
    • a window missing its last piece → 7 (64);
    • 48 pieces at 768 KiB → 5 (48);
    • a block whose import fails → two halves (0);
    • a budget for 3 MiB of 4 → 2 MiB + 1 MiB (48);
    • 12,000 ranges over a 60 % budget → 0 retries per rebuild (4,800).
  • Storage write-back (lavapipe): a 4 MiB image over 64 pieces → 1 import (main: 64 segment imports).
  • Cost (30,000 64 KiB pieces, lavapipe):
    • AcquireRegistered per rebuild: 57.8 → 43.2 ms with the budget at 60 % of the registry, and 50.0 → 19.7 ms at 100 %;
    • importRun: +111 ns per call.

Windows (GitHub's runner, MinGW, no Vulkan device), on this branch before its rebase onto main (0f85236, the same code; run): it builds, and 591 of 592 ^agc tests pass (460 skip without Vulkan). The one failure, agc_driver_pm4_replay_tool (0xc0000139), and libkernel's guest_kqueue also fail on main on that runner.

Windows 11, RTX 3070 Laptop GPU (driver 617.42), at this head: the new and changed tests (agc_storage_writeback_blocks, agc_driver_graphics, agc_driver_storage_write_back_overhang) pass 5 of 5. With the import code before the aligned blocks, agc_storage_writeback_blocks fails with "a 4 MiB image over 64 adjacent 64 KiB pieces: the write-back made 64 imports, expected 1", and agc_driver_graphics with the host import report line (no remembered count).

Merged with main 66e71b6 (0b84427, after #5390), on GitHub's runners (run): the host import, settle, lifetime and sparse tests pass 3 runs in a row on Linux, as do the ^agc tests (709, 0 failed), and the libc and libkernel suites pass; on Windows only the two replay tools named below fail. The earlier merge with main 3de84fa (1b304c8) ran the same way (run): the host import, settle and lifetime tests pass 3 runs in a row on Linux, as do the ^agc tests (709, 0 failed), and the libc and libkernel suites pass. On Windows the only failures are agc_driver_pm4_replay_tool and agc_shader_replay_tool, which fail the same way on that runner for an unrelated branch (run). Before the test commit, the merge with main 3b887b5 failed agc_driver_guest_memory_lifetime_copy on Linux with "source retirement unnecessarily retired the copy destination" (run); with it, that test and the host import tests passed 10 runs in a row (run).

Checklist

Every host import now names the path that asked for it:
- map: the map-time import of a GPU mapping;
- build: an address-based build's walk and its region imports;
- access: a lookup through HostImportFor;
- join: a join at bind;
- segment: a storage image write-back segment;
- expand: a DB expand's store.

The APS5_TRACE_HOST_IMPORT ok line, the import failure line and the
budget refusal report end with the requester, so a traced run splits its
imports by path. HostImportCounters returns the imports made, refused
and failed per requester, with the live count and bytes.

agc_driver_graphics checks that each requester's imports, refusals and
failures are counted under it.
A run without APS5_TRACE_HOST_IMPORT had no count of the imports it made
or refused. When the driver stops, one line now gives the live imports
and the imports made, refused and failed, in total and per requester:

[gpu] host imports at shutdown: N live (M MiB); made ... (... MiB),
refused ... (... MiB), failed ...; by requester (made/refused/failed):
map a/b/c, build ..., access ..., join ..., segment ..., expand ...

It prints only when an import was made, refused or failed.
agc_driver_graphics checks the line's text.
The storage write-back overhang test read both surfaces straight from
guest memory after FlushAllPending and WaitIdle. A write-back through a
host import is only recorded there; a reader of guest memory calls
FlushGpuWrites, which waits for the batch that holds it. The test passed
because the split surface took the CPU write-back, whose SubmitAndWait
also drained the first surface's recorded store. With both surfaces
imported, neither store had landed when the test read them.
…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.
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.
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 5372 docs/dev/debt/libSceAgcDriver.md L137
PR 6951 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1253-1256, L1272-1276, L1347, L1489
PR 7292 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1522-1523, L3447-3448, L3451
PR 7457 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp L7, L75
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1603-1608, L1841-1843, L3205-3206, L3223
PR 7533 docs/dev/debt/libSceAgcDriver.md L137-138
PR 7647 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1506
PR 7844 docs/dev/debt/libSceAgcDriver.md L195
PR 7849 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp L1801-1810

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 1191 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 1324 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4040 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 4448 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 4449 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 5058 docs/dev/debt/libSceAgcDriver.md
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5426 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5441 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5499 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 5500 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 5609 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 5656 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6177 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 6185 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6194 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6197 core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
PR 6202 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6223 docs/dev/debt/libSceAgcDriver.md
PR 6235 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6252 docs/dev/debt/libSceAgcDriver.md
PR 6273 docs/dev/debt/libSceAgcDriver.md
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6292 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6539 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6548 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6549 docs/dev/debt/libSceAgcDriver.md
PR 6557 docs/dev/debt/libSceAgcDriver.md
PR 6611 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6615 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6628 core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
PR 6718 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6727 docs/dev/debt/libSceAgcDriver.md
PR 6747 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6759 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6772 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6804 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6820 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6867 docs/dev/debt/libSceAgcDriver.md
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6937 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6946 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6947 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 6960 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 6970 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6971 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 6972 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
PR 6977 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7154 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
PR 7223 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7256 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7310 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7332 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/Driver/Lifecycle.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/BdaResources.cpp
core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7427 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7449 docs/dev/debt/libSceAgcDriver.md
PR 7466 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
PR 7473 core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
PR 7505 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7514 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/include/GuestBufferMemory.hpp
core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
PR 7579 core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7600 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7615 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7746 docs/dev/debt/libSceAgcDriver.md
PR 7800 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Graphics/src/Texture.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7927 core/libs/prx/libSceAgcDriver/tests/Graphics.cpp
docs/dev/debt/libSceAgcDriver.md
PR 7978 docs/dev/debt/libSceAgcDriver.md
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
docs/dev/debt/libSceAgcDriver.md

…ite-back test

Its registration and its command-line option followed the split write-back test's, where another open pull request adds its own. They now follow the copied-direct ordering and CMASK tests. No test changes.
EagleBearerYS added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 15, 2026
Expand named a DB expand's store, but nothing passes it: main has no DB
expand, and the open DB expand change stores its planes only where host
imports already hold them, through HostImportOverlaps and the storage
write-back, without making or retiring an import. The requester could
never count, so the shutdown line always ended with expand 0/0/0.

agc_driver_graphics checks the requester names and the shutdown report
without it.
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 16, 2026
EagleBearerYS added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 17, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 17, 2026
main 52798f7 declares ExistingHostImportFor next to HostImportFor
(boykopovar#6892). Keep it beside this branch's HostImportFor, which takes a
requester.
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 17, 2026
Main keeps an import settle's objects per device in ImportSettle, next
to where this branch adds ImportRefusal. Keep both. Main's fallback for
a refused host-write alias returns after this branch's failure records.
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 17, 2026
oneandonlydean added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 19, 2026
# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
#	docs/dev/debt/libSceAgcDriver.md
oneandonlydean added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 19, 2026
# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
#	docs/dev/debt/libSceAgcDriver.md
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 19, 2026
# Conflicts:
#	docs/dev/debt/libSceAgcDriver.md
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 19, 2026
# Conflicts:
#	docs/dev/debt/libSceAgcDriver.md
# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
oneandonlydean added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 19, 2026
# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
oneandonlydean added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 19, 2026
# Conflicts:
#	core/libs/prx/libSceAgcDriver/Graphics/src/GuestBufferMemory.cpp
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 19, 2026
@oneandonlydean
oneandonlydean merged commit 5242599 into boykopovar:main Oct 19, 2026
10 checks passed
oneandonlydean pushed a commit that referenced this pull request Oct 19, 2026
…-import-blocks

#6457's branch now carries main's per-device import settles, which this
branch's GuestBufferMemory.cpp conflicted with.
oneandonlydean pushed a commit that referenced this pull request Oct 19, 2026
…mber-evicting-refusals

#6457's branch now carries main's per-device import settles. Its
ImportSettle sits where this branch adds EvictingRefusal; keep both.
wachi35676 added a commit to wachi35676/AnyPS5 that referenced this pull request Oct 20, 2026
Since host imports group adjacent ranges into aligned blocks (boykopovar#6457), an
import can span 64 KiB blocks an earlier unwatched import left behind,
and resident multisampled DCC targets with their metadata.

ImportWatched with staysWatched took the whole import out of the write
watch when any part of it was not watched before. It now walks and
compares each run of watched blocks of the import on its own, and
unwatches only the blocks that were not watched or whose walk failed.

A resident DCC target took the generation of its metadata from a walk
of the metadata's pages alone. CPU stores to the rest of the metadata's
64 KiB block made before that walk were stamped later by the import's
walk, so the block read as written, and the import window marked the
metadata page as possibly stored over: the next draw refused the
target. The generations now come from a walk of the whole blocks.

Fixes boykopovar#8103

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wachi35676 added a commit to wachi35676/AnyPS5 that referenced this pull request Oct 20, 2026
Since host imports group adjacent ranges into aligned blocks (boykopovar#6457), an
import can span both 64 KiB blocks that an earlier import left unwatched
and the metadata of resident multisampled DCC targets.

ImportWatched with staysWatched took the whole import out of the write
watch when any part of it was not watched before. It now walks and
compares each run of watched blocks of the import on its own, and
unwatches only the blocks that were not watched or whose walk failed.

A resident DCC target took the generation of its metadata from a walk
of the metadata's pages alone. CPU stores to the rest of the metadata's
64 KiB block made before that walk were stamped later by the import's
walk, so the block read as written, and the import window marked the
metadata page as possibly stored over: the next draw refused the
target. The generations now come from a walk of the whole blocks.

agc_write_tracking and agc_driver_msaa_dcc_clear_* gain regression
tests for both.

Fixes boykopovar#8103

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EagleBearerYS added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 21, 2026
…atch

On Windows a host import under 1 MiB leaves the write watch when it is
made, and comes back only when its deferred settle read completes.
retireImport drops the pending settle and never restores the watch, so
an import retired before it settled left its range outside the write
watch for good. Once no import fit the budget, an address-based build
could neither import such a range nor heap mirror it (CollectWrites
returned 0), and copied it.

Since aligned block imports (boykopovar#6457), this happens to every 64 KiB piece
a title maps one at a time: the piece is imported by the map, and when
its neighbour is registered its run grows into a larger aligned block,
so refreshImports retires the piece's import ("the range was mapped
again", though the range is unchanged). Resident Evil Village
(PPSA01556) on main 6696def aborted in its intro at 0:43 to 6:37
with "an address-based build copies 185 MiB of 2960 registered ranges,
past 64 MiB (APS5_ADDRESS_COPY_MAX_MIB); the largest: 0x20c0c20000+
0x10000 (0.1 MiB committed, outside the write-watched arena) ...". An
import trace showed each copied page imported by the map, retired when
its run grew, and never imported or watched again.

refreshImports and the budget eviction now note the ranges of each
unwatched import they retire that are still registered as the same
range, and rewatch them once the import is gone: as a settled import
does, except where another unwatched import or a live storage texture
still covers them. Rewatch stamps the range as written, so the next
build reads it in full. Ranges the guest mapped again or released keep
the existing path.

Tested: agc_driver_graphics gains retiredImportWatchTests (Windows):
a 64 KiB piece imported on its own, then its neighbour registered and
the budget cut below one piece. The piece must be back in the write
watch and both pieces heap mirrored. Without the fix the piece stays
unwatched and is copied.
EagleBearerYS added a commit to EagleBearerYS/AnyPS5 that referenced this pull request Oct 21, 2026
…atch

On Windows a host import under 1 MiB leaves the write watch when it is
made, and comes back only when its deferred settle read completes.
retireImport drops the pending settle and never restores the watch, so
an import retired before it settled left its range outside the write
watch for good. Once no import fit the budget, an address-based build
could neither import such a range nor heap mirror it (CollectWrites
returned 0), and copied it.

Since aligned block imports (boykopovar#6457), this happens to every 64 KiB piece
a title maps one at a time: the piece is imported by the map, and when
its neighbour is registered its run grows into a larger aligned block,
so refreshImports retires the piece's import ("the range was mapped
again", though the range is unchanged). Resident Evil Village
(PPSA01556) on main 6696def aborted in its intro at 0:43 to 6:37
with "an address-based build copies 185 MiB of 2960 registered ranges,
past 64 MiB (APS5_ADDRESS_COPY_MAX_MIB); the largest: 0x20c0c20000+
0x10000 (0.1 MiB committed, outside the write-watched arena) ...". An
import trace showed each copied page imported by the map, retired when
its run grew, and never imported or watched again.

refreshImports and the budget eviction now note the ranges of each
unwatched import they retire that are still registered as the same
range, and rewatch them once the import is gone: as a settled import
does, except where another unwatched import or a live storage texture
still covers them. Rewatch stamps the range as written, so the next
build reads it in full. Ranges the guest mapped again or released keep
the existing path.

Tested: agc_driver_graphics gains retiredImportWatchTests (Windows):
a 64 KiB piece imported on its own, then its neighbour registered and
the budget cut below one piece. The piece must be back in the write
watch and both pieces heap mirrored. Without the fix the piece stays
unwatched and is copied.

Co-authored-by: keravyn-ops <342686842+keravyn-ops@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build docs perf numbers missing perf pull request without a before and after summary line perf Performance change unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants