Skip to content

fix(agc): allocate Windows page permissions on demand - #8139

Merged
adriadam10 merged 5 commits into
boykopovar:mainfrom
Izamaru:perf/sparse-page-permissions
Oct 23, 2026
Merged

adriadam10 merged 5 commits into
boykopovar:mainfrom
Izamaru:perf/sparse-page-permissions

Conversation

@Izamaru

@Izamaru Izamaru commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

What

On Windows, PageSpan::allocate commits one byte per 4-KiB guest page even when most of the arena has never been queried. Replace that dense allocation with a small atomic directory and 64-KiB metadata chunks allocated when readable pages are recorded.

Missing chunks remain unknown and take the ordinary query path. Recording an inaccessible run does not allocate missing chunks; inaccessible entries in existing chunks are still cached. Invalidation, accessSerial, initialization publication and access flags are retained. Chunks have process lifetime, like the previous flat allocation. Linux retains its flat allocation.

This is a memory-allocation change, independent of merged #6701's write-generation stamps and #7788's Linux image caching. It overlaps the PageSpan implementation in #6921 (run scanning). Sequencing is agreed with @milescoviello: #8139 goes first; after it merges, @milescoviello will rebase #6921 and adapt its summaries to the sparse chunks, preserving the fences and the Linux flat allocation. This PR has no prerequisite and does not bundle #6921. We can help validate the author's rebased #6921 on Windows.

Tested

Current-integration Windows revalidation, 2026-10-23

  • Pinned upstream main: 342ac2eda0696b40775e09eb8c928b180758bc1d.
  • Exact final PR head: 0ef6f9fc9a53f2962b6fa238b0dcb5e6ae1e04c6.
  • Full final-head tree: b655ff3e4424e765ba0d64581f81449067cfabdd.
  • Main-production control: 0ec002d3ddcd5717c68f234527baa82212b470d0, tree d63906895db5de0962eecb33083a02f28840b652.

The main branch advanced during preparation, so the final paired comparison was repeated on the pinned main above. The only merge conflict was in core/libs/prx/libSceAgcDriver/CMakeLists.txt: preserve both main's agc_locks_tests block and this PR's Windows page-state test block. The production GuestMemory.cpp merged automatically; the sparse implementation and PageStates.cpp fixture are unchanged. The diff against the pinned main still contains only the original three files. #6921 is not bundled.

The control uses the pinned main's production code, byte for byte, with only the same CMake registration and PageStates.cpp fixture added so both sides run identical cases. It is a fixture-only control, not a claim that unmodified main already contains these new tests. The exact final commit was created in the fork, fetched by SHA, checked against the complete locally resolved tree, rebuilt and tested before moving the PR branch to it.

Same environment on both sides: Release, Windows 11 build 26200, WinLibs MinGW-w64 GCC 15.2.0 r7; NVIDIA GeForce RTX 4090, driver 617.14; Khronos validation 1.4.363.0.

The same eight behavioral cases pass on both:
agc_page_states, agc_driver_graphics, agc_sparse_buffer_holes, agc_copy_input_kept_bytes, agc_depth_copy_kept_bytes, agc_draw_inputs_copied, agc_space_pending_writes, agc_pending_write_snapshot.

Selection Behavioral cases Metadata regression Khronos instance-layer insertions VUIDs / missing layer / skips
main-production control 0ec002d3d 8/8 expected dense-table failure in a separate run 3 0 / none / none
exact final head 0ef6f9fc9 8/8; 9/9 including metadata pass 3 0 / none / none

Run serially with ctest -V --timeout 30, ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1, VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation and loader layer diagnostics. Both behavioral selections confirm three actual Khronos instance-layer insertions and the RTX 4090 device. Their VUID sets are empty; neither log reports the requested layer missing or skipped cases. The page-state and metadata fixtures themselves are CPU tests and create no Vulkan instance.

Fresh-process metadata control on this pinned integration

With main production, agc_page_state_metadata fails as expected with page permissions committed a dense arena table and 262,664,192 bytes incremental private commit. With the exact final head it passes at 102,400 bytes, a 262,561,792-byte (250.40-MiB) reduction in this fixture. Querying an untouched reserved chunk also passes its no-allocation check.

The behavioral fixture covers concurrent first publication, chunk boundaries, guest bytes, reserved holes, protection changes, decommit and recommit through the access observer.

git diff --check and conventions against the pinned main pass. The literal origin/main conventions check was also run; its unrelated upstream findings come from that ref tracking the older fork main.

Limitations: focused synthetic Windows/NVIDIA validation only; no full-suite, Linux runtime, other-vendor GPU, title-runtime, title-unblock, FPS or whole-game memory claim. The 250.40-MiB figure is a metadata-initialization measurement, not a saving measured in Yōtei. Full Windows/Linux CI still requires a collaborator to dispatch the project build workflow for the updated head. Subsequent main commits are outside these pinned results.

The independent Windows 10 reports below retain their original heads and are separate from this author's 2026-10-23 revalidation.

Independent Windows 10 validation

TheSovietWaffle reports that agc_page_states and agc_page_state_metadata pass 5 of 5 on the earlier head 20ddc95c5: Windows 10 Pro 22H2 build 19045, WinLibs GCC 15.2.0, Release, Ryzen system with an RTX 3080 and Radeon iGPU.

This independently extends the page-state and metadata test coverage to Windows 10. These two fixtures exercise CPU-side memory-permission and metadata paths; the listed adapters do not establish Vulkan/GPU coverage. No exact private-commit measurements were supplied, so this report is not an independent measurement of the 250.40-MiB reduction above.

Earlier published head with a baseline

xXPBSXx reports a paired Windows run on the earlier head 2533730bf2: Windows 10 Pro 10.0.19045, Ryzen 7 5800X3D, WinLibs GCC 15.2.0 (MinGW-w64 UCRT), Release. Only agc_page_state_tests was built, with ctest -N confirming the two cases.

  • With the PR, agc_page_states and agc_page_state_metadata both pass in all three runs.
  • For the baseline, the PR's tests/PageStates.cpp was retained and only production Execution/src/GuestMemory.cpp was replaced by merge-base cabd80f472. The behavioral test passes; the metadata test fails in all three runs with page permissions committed a dense arena table and 262,664,192 bytes of initialization private commit.
  • The PR's production source was restored after the control.

This independently reproduces the dense baseline on Windows 10 for that earlier published head. These are CPU permission/metadata fixtures only; no other tests, platforms, full suite or titles were run. No PR-side private-commit value was supplied, so the exact 250.40-MiB reduction above has not been independently remeasured by this report.

Checklist

@github-actions

github-actions Bot commented Oct 20, 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 6921 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp L374, L381, L383-386, L394, L397-398, L407, L411, L421, L452

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 1368 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1395 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 1896 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 2434 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 4448 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 5021 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 5371 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5431 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 5925 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6044 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6186 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6187 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6188 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6192 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6209 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6273 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6280 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6300 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6412 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6481 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6501 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6788 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6808 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6820 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6868 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6960 core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 6973 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6987 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6989 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 6995 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7002 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7048 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7201 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 7275 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7307 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7457 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7464 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7604 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 7642 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 7677 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7705 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 7833 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8014 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8246 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8365 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8484 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8542 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8552 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8564 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8592 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8740 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp
PR 8874 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 8999 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9020 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9335 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9521 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9748 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9884 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9892 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9897 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 9898 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10000 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10064 core/libs/prx/libSceAgcDriver/CMakeLists.txt
PR 10193 core/libs/prx/libSceAgcDriver/CMakeLists.txt
core/libs/prx/libSceAgcDriver/Execution/src/GuestMemory.cpp

@github-actions github-actions Bot added build agc libSceAgc* labels Oct 20, 2026
@TheSovietWaffle

Copy link
Copy Markdown
Contributor

Windows 10 data point (Windows 10 Pro 22H2 19045, WinLibs GCC 15.2.0, Release, Ryzen with RTX 3080 and Radeon iGPU), head 20ddc95c5: agc_page_states and agc_page_state_metadata pass 5 of 5.

AI-assisted: yes (Claude Code).

@Izamaru

Izamaru commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@milescoviello, could we agree on sequencing between #8139 and #6921? The changes are complementary: #8139 makes Windows page-state storage sparse, while #6921 accelerates scans using block summaries.

Local merge-tree checks found conflicts in GuestMemory.cpp in either order. The combined implementation needs to preserve sparse allocation, summary invalidation and the concurrency fences.

Would merging #8139 first work for you? We can help adapt and validate the Windows path afterward. If you and the maintainer prefer #6921 first, we can instead rebase #8139 onto it and preserve both behaviors.

Could we agree on the merge order and who handles the combined adaptation before taking #8139 out of draft?

@milescoviello

Copy link
Copy Markdown
Contributor

@Izamaru Yes, #8139 first works for me, and I'll handle the combined version in #6921. #8139 fixes how much memory the Windows table commits, while #6921 only speeds up scans, so it makes sense for the fix to go in first.

After #8139 is merged, I'll rebase #6921 onto it:

I'll add a case to your PageStates test with a run that crosses a chunk boundary into an unallocated chunk. If you can, a Windows check of the rebased #6921 would help: agc_page_states plus #6921's GuestAllocations cases.

Thanks also, @TheSovietWaffle, for the Windows run here.

AI-assisted: yes (Claude Code)

@Izamaru
Izamaru marked this pull request as ready for review October 20, 2026 14:00
@Izamaru

Izamaru commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor Author

@milescoviello Thanks, #8139 first works for us too. I refreshed it against main cabd80f; the standalone PR still contains only its original three files.

The experiment is now published in our fork: lab/compat-8139-6921 (diff against the pinned main). It combines #8139 at 20ddc95c5 and #6921 at f05f64ab3, based on main 44e3389c8. This is an independent integration experiment for review, not validation of your future rebase.

Pinned published tip: 70cc3909b92c4b3b07defb0c9058c1f94dd0893b. Its complete Git tree 68c20938ba97225d6ddac0ca94b652097603e43e exactly matches the locally tested candidate c6e0b6854756ebf8ee950dd86041214984f68eaf; the original upstream commits are retained in the merge history. The additional Windows fixture is included.

On Windows 11 build 26200, GCC 15.2.0 r7, Release, RTX 4090 driver 617.14:

  • The combined selection passed 10/10. The same nine behavioral tests passed with main's production GuestMemory.cpp and identical fixtures.
  • Our additional agc_page_state_runs case exercises 64/512-page and chunk boundaries, read-only pages, decommit/recommit, synchronized reader/writer transitions and churn. agc_page_states and agc_page_state_runs each passed 20 fresh-process repetitions.
  • Removing only summary clearing in forget made the new test fail on stale writable access; restoring the committed source passed 10/10 again. This checks that the fixture detects that integration defect.
  • The synthetic metadata fixture retained the sparse-allocation saving: 262,664,192 to 102,400 incremental private-commit bytes (250.40 MiB). This is not a whole-game memory measurement.
  • The focused Vulkan runs used ANYPS5_REQUIRE_VULKAN=1, ANYPS5_NO_SHADER_CACHE=1 and VK_LAYER_KHRONOS_validation. All three Vulkan processes per selection confirmed layer insertion; zero VUIDs, missing layers or skipped tests.

The experiment puts the 1024 summary bytes in each 65536-entry chunk, skips missing chunks and preserves the settle/forget fences and Linux flat allocation, consistent with your proposed direction. Linux and the full suite were not run. The agreed order remains #8139 first, then your adapted #6921. We can test agc_page_states plus your GuestAllocations cases when you publish the rebase. No loading-time, FPS or gameplay claim.

AI-assisted: yes (OpenAI Codex); author reviewed and is responsible for the results.

@milescoviello

Copy link
Copy Markdown
Contributor

Thanks @Izamaru, that is useful ahead of the rebase. The check that removing the summary clear in forget makes your test fail is exactly the integration defect I would worry about. Yes, please share agc_page_state_runs (a gist or a branch is fine). I will carry it in the rebased #6921 with you credited in the description, next to my GuestAllocations cases, and ping you here when it is up so you can run the Windows side.

AI-assisted: yes (Claude Code)

@Izamaru

Izamaru commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

@milescoviello I updated my earlier comment after your reply with the published lab/compat-8139-6921 branch. The agc_page_state_runs fixture is included; the pinned commit, test results and limitations are all in that updated comment. We can run the Windows checks when your rebase is ready.

AI-assisted: yes (OpenAI Codex).

@milescoviello

Copy link
Copy Markdown
Contributor

Thanks @Izamaru, I will take agc_page_state_runs from lab/compat-8139-6921 for the rebase and credit it.

AI-assisted: yes (Claude Code)

@xXPBSXx

xXPBSXx commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Independent Windows run of this PR with a baseline (data only).

Environment: Windows 10 Pro 10.0.19045, Ryzen 7 5800X3D, WinLibs GCC 15.2.0 (MinGW-w64 UCRT), Release. PR head 2533730bf2; merge-tree against main is clean. I built only agc_page_state_tests; the two tests are agc_page_states and agc_page_state_metadata (ctest -N lists both; the GPU on this machine plays no part in them).

Results (3 runs each):

  • PR head: both tests pass 3/3.
  • Baseline: I kept the PR's tests/PageStates.cpp and replaced only Execution/src/GuestMemory.cpp with its version from the merge base cabd80f472, then rebuilt. agc_page_state_metadata fails 3/3 with page permissions committed a dense arena table and page-permission initialization: 262664192 bytes of private commit; agc_page_states passes. I restored the PR's GuestMemory.cpp afterwards.

This adds the failing baseline to the earlier pass-only Windows data point.

Not run: other platforms, the other tests, the full repository CTest, and any title or console run.

AI-assisted (Claude Code).

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 23, 2026
@adriadam10 adriadam10 self-assigned this Oct 23, 2026
@adriadam10
adriadam10 merged commit 156d033 into boykopovar:main Oct 23, 2026
12 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the sparse chunk directory in PageSpan (runEnd over absent chunks, range store, forget, CAS publication, inaccessible runs not allocating), the new agc_page_states/agc_page_state_metadata tests (the metadata one fails on main's dense table, 262,664,192 bytes vs 102,400), and the Windows 10 runs by @TheSovietWaffle and @xXPBSXx; merged.

@Izamaru
Izamaru deleted the perf/sparse-page-permissions branch October 23, 2026 18:30
@milescoviello

Copy link
Copy Markdown
Contributor

@Izamaru #6921 is rebased onto main with the summaries inside each Chunk (head b50a5d5cea), and it carries your agc_page_state_runs fixture, credited as co-author, plus a case for a run that ends where an unallocated chunk begins. On my Windows 11 VM (GT 1030) it passes 6 of 6, 3 of 3, and a mutant without the summary clear in forget fails your fixture 3 of 3. If you can run the Windows side you offered (agc_page_states, agc_page_state_metadata, agc_page_state_runs), please post it on #6921.

AI-assisted: yes (Claude Code)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agc libSceAgc* build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants