Skip to content

fix(libc): track writes to singly mapped direct memory without faults on Windows - #637

Closed
DotDebian wants to merge 2 commits into
boykopovar:mainfrom
DotDebian:fix/windows-direct-memory-write-watch
Closed

DotDebian wants to merge 2 commits into
boykopovar:mainfrom
DotDebian:fix/windows-direct-memory-write-watch

Conversation

@DotDebian

@DotDebian DotDebian commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

What

Part of #361: on Windows, writes to direct memory mapped at a single guest address are now tracked without raising a fault on the guest thread.

Problem. Direct memory is mapped as section views, and WindowsMappings::Collect write-protects the views it collects. The next guest write faults and is resumed by the vectored handler. Windows builds the exception frame directly below rsp, over the System V red zone, so a leaf function resumes with its locals overwritten when its AVX state is live (mechanism and standalone reproduction in #361). The bytes are gone before any handler runs, so the fault has to be avoided.

Change.

  • A direct memory page mapped at one guest address lives in private MEM_WRITE_WATCH memory at that address ("resident"). Collect reads its writes with GetWriteWatch, as it already does for flexible memory. No page is write-protected, no fault is raised.
  • Resident pages are allocated in runs of at most 2 MiB, so a collect is one GetWriteWatch call per run.
  • A run has to be broken when one of its pages is mapped at a second address, when part of it is unmapped, or when MapAlias needs views of it. The run is made read-only, copied into the section and replaced by views of the section, tracked by write protection as before. Nothing is copied back into live memory. A write that hits the fence, and a read or write that hits the placeholder between the release and the views, is retried by the fault handler once the views exist.
  • Before a run is broken, sceKernelMapDirectMemory and the unmap paths wait for the leases and host imports of every registered range that shares the run (RequireUnpinned), as an unmap of those ranges does, so no import keeps the replaced pages.
  • When a whole run is unmapped, its non-zero pages are kept in private memory. They are copied back and released when the physical pages are mapped again, and released when the backing is destroyed, so direct memory keeps its contents across unmap and remap and is charged once.
  • A page that was once in the section stays a view when it is mapped again.
  • A page that was never collected still reports as written on its first collect, as a view does.
  • The direct memory section is created with SEC_RESERVE: it commits only the pages of broken runs.

Not changed, still open in #361.

  • Pages mapped at two or more addresses still take the fault path. So do the pages that shared a run (up to 2 MiB) with a page that was mapped a second time or unmapped while they stayed mapped.
  • A thread that touches a run while it is being broken takes one fault on its own stack.
  • HandleSse4a still resumes an exception on the guest stack.

Behaviour to know about.

  • Collected writes of resident pages are reported per 4 KiB page instead of per 16 KiB page.
  • A host import of singly mapped direct memory imports the guest pages themselves, like flexible memory, so on Windows it is compared instead of write-tracked.
  • MapAlias returns null when a resident run of the range extends outside it, and the import falls back to copies.
  • A committed SEC_RESERVE page cannot be decommitted, which is why unmapped pages are not stored in the section.
  • Three existing tests assumed that singly mapped direct memory is write-protected by a collect: CheckPinnedSharedPages and the shared block of agc_driver_dcc_keys_first_write now map their memory twice, and CheckFailedCollectKeepsWrites expects one 4 KiB page.

Tested

Windows 11 Pro 10.0.26200, Intel Core Ultra 9 285K (no AVX-512, so the red zone overwrite from #361 reproduces here), RTX 5080, MinGW-w64 GCC 15.2.0. Head dcffc098, on main 5edae353.

  • ctest: 710 of 710 run pass, 5 skipped (four video_out_flip_*, guest_video_out_wqhd_detection_no_vulkan). guest_memory, agc_host_import_second_mapping and agc_driver_dcc_keys_first_write pass 10 of 10 repeats.
  • New tests, each failing without its fix:
    • CheckSingleDirectMappingTracksWritesWithoutFaults (guest_memory): two writes to a singly mapped range raise no access violation and are collected as two pages; contents and tracking survive a partial unmap, a second mapping and a full unmap and remap.
    • CheckSecondDirectMappingWaitsForLeases (guest_memory): a second mapping of a page returns only after a lease on the first mapping is dropped. Without the wait it returns at once.
    • agc_host_import_second_mapping (agc_driver_recorder_tests --host-import-second-mapping-only): a host import of singly mapped direct memory is replaced after its first page is mapped again. Without the wait it fails with the import of direct memory whose pages were replaced was kept.
    • CheckBreakingAResidentRunKeepsConcurrentAccesses (guest_memory): 20 rounds; one thread writes every byte of four runs once and another reads them while the main thread maps a page of each run a second time, or unmaps it. On the previous head the reader faults; with the reader off, bytes are lost in 3 of 3 runs.
    • CheckRemappedDirectMemoryIsChargedOnce (guest_memory): mapping, filling, unmapping and remapping 256 MiB raises the system commit by less than 384 MiB. On the previous head it fails.
  • Dreaming Sarah (PPSA02929) on this head: 40 s without a fault, default write watching. FPS not measured this time.
  • Not run on this head: Astro Bot (PPSA21564, measured on d6bb6c5b only, see below) and Linux. The change is inside #ifdef _WIN32 code, apart from the new tests, which are compiled out on Linux.

Measured on the first version (d6bb6c5b) on top of the astrobot branch (b58df66c, discussion #357), not repeated since:

Not measured: titles that map most of their direct memory twice, and the cost of unmapping large resident ranges.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • AI-assisted: yes

🤖 Generated with Claude Code

@oneandonlydean

Copy link
Copy Markdown
Collaborator

#650 and #652 have landed, and each conflicts with this on one line. Both resolutions keep both sides:

#650 also added a paragraph to docs/user/USAGE.md saying the backing is "committed in full when the title allocates it, not when its pages are first used". With SEC_RESERVE that's no longer the case, so could you reword that sentence in the same rebase?

@Celegans12

Celegans12 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Tested d6bb6c5b on Windows 11 Pro 10.0.26200, i9-11900H, RTX 3080 Laptop (NVIDIA 610.62), MinGW-w64 GCC 15.2.0.

ctest: Release, -DBUILD_TESTING=ON, full build, --target libs: 284/284 pass. That includes the two AGC tests that failed on your RTX 5080. With this branch's GuestMemory.cpp on main 33e9a121, guest_memory fails at GuestMemory.cpp:400 (g_accessViolations == 0). On the branch it passes 10 of 10 repeats.

PPSA26344 v1.008 on our integration branch, with and without this PR. It allocates 13.5 GiB of direct memory and makes 835 maps (4.7 GiB). No page is mapped twice and nothing is unmapped.

  • Both builds stop at the same point (compute shader 0x800027c200: AGC driver: null or misaligned address). Neither has an access violation, so this does not test Windows: resuming a write fault overwrites the guest red zone when AVX state is live #361.
  • At that point, commit charge drops from about 15.3 GiB to 6.6-7.0 GiB. The 3.3 GiB map at 0x5000000000 takes under 2 s instead of about 6 s. On an idle host, the time to reach that point drops from about 22 s to about 11 s.
  • New in 5 of 5 runs with the PR, 0 of 4 without: [gpu] storage image 0x504a7c0000: guest memory changed while GPU results were pending; keeping the CPU's blocks, right after host import of 0x5000000000+0xd3400000. Likely cause:
    • The range is now private read-write, so importAllocation imports the guest address itself (GuestBufferMemory.cpp:295, :312).
    • On this driver, importing MEM_WRITE_WATCH memory sets every page's watch bit. Standalone, 64 of 64 pages are set after vkAllocateMemory.
    • So ImportWatched (GuestMemory.cpp:1050) probably stamps all 3.3 GiB as written.

Problems. Each has a standalone repro against this branch's libc/libkernel. Main's libs pass all three.

  1. Stale GPU import. A second mapping of a page in a resident run goes through share()/cut() (WindowsMappings.hpp:124, :405, :429). These replace the pages of the whole run. An existing import of the first mapping survives refreshImports, because its registered range is unchanged (GuestBufferMemory.cpp:391). In a 64 KiB repro, after page 0 is mapped again:
    • The GPU reads 0x11111111 where the CPU wrote 0x22222222.
    • GPU fills do not reach the CPU.
    • The same happens on page 1, which is mapped once but is in the same run.
  2. Lost writes. cut()/share() copy the pages while they are still writable (:414, :437), then release them (:415, :440). cut() also recommits the run read-write before it copies the bytes back (:417-:419). A write from another thread in those windows does not fault, so it never reaches HandleWrite and is lost. Repro: one thread writes each slot of a 2 MiB run once while another thread maps the run's last page a second time, or munmaps it. Writes were lost in 14-16 of 20 rounds in each mode.
  3. Charged twice after a remap. Pages stored in the SEC_RESERVE section stay committed when they are mapped again as resident copies (:105-:108). Mapping, filling, unmapping and remapping 2 GiB gives +2.1 GiB private plus +2.1 GiB section. On main it is 2.1 GiB.

For the USAGE.md rewording: running out of commit no longer fails at allocation. It fails in sceKernelMapDirectMemory with replace guest placeholder with resident direct memory (:386). On a second mapping or a munmap, it can also fail with commit a direct memory page (:381).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Do you plan to address Celegans12's Windows report from 10-05 here? It found three problems, each with a repro:

  • a stale GPU import after a second mapping;
  • writes lost in cut()/share();
  • a double commit charge after a remap.

The branch also conflicts with main in GuestArena.cpp and DirectMemory.cpp. If you'd rather stop here, please say so.

@SP4C3B4R-8

Copy link
Copy Markdown
Contributor

Tested d6bb6c5b on Windows 11, Ryzen 7 7800X3D (AVX-512, so this host never hit blocker B), RTX 5070 Ti, MinGW-w64 GCC 15.2.0, PPSA21564 v01.007.000. Cherry-picked cleanly onto oneandonlydean/astrobot 80c8adae; APS5_HOST_IMPORT_MIB=20480 APS5_PROFILE_DRAW=1, write watching on, warm shader and pipeline caches, -lvl underwater_aerial_garden, 300 s per run.

without with this PR
presents in the first 10 s (logos, loading) 162 249
in the level (presents per 10 s) 5–8 6–8
queue 0 per draw packet in the level 177–217 us 193–194 us
errors none none

No regression here; in the level the queue 0 worker is CPU-bound on draws either way, so the frame rate there doesn't change.

Tested with AI assistance (Claude Code).

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work. Not repeating @oneandonlydean's question: the three problems @Celegans12 reproduced on d6bb6c5b are still open, and the diff confirms them. They need to be fixed before this can merge:

  • Stale GPU import after a second mapping: share()/cut() (WindowsMappings.hpp:124, :405, :429) replace the pages of the whole run, but the existing import keeps its registered range. Either invalidate imports of every page in the run, or avoid touching siblings.
  • Lost writes: cut()/share() copy pages while still writable (:414, :437) and cut() recommits read-write before copying back (:417-:419). A write from another thread in those windows never faults, so it is lost. Keep the pages protected (or otherwise fenced) until the copy is done.
  • Double commit charge after remap: pages stored in the SEC_RESERVE section stay committed once mapped again as resident copies (:105-:108). Decommit the section page after copying it out.

Also:

  • Rebase: conflicts with main in GuestArena.cpp and DirectMemory.cpp, and docs/user/USAGE.md needs the commit-charge wording updated (as @oneandonlydean noted).
  • Collect calls GetWriteWatch for resident runs even when watchResident is false (:281); that memory isn't MEM_WRITE_WATCH. Please guard it or explain why it can't happen.
  • The new test is single-threaded and never imports into the GPU or remaps a large run, so it can't catch any of the three problems above. Please add a test per problem (the repros in @Celegans12's comment are a good start; each must fail without the fix).

The test and the Windows paths can't run on Linux CI; once pushed, a Windows run of guest_memory linked in the PR would help.

@Celegans12

Copy link
Copy Markdown
Contributor

@DotDebian are you still planning to work on this? It matters a lot for Ghost of Yōtei: the process now peaks at about 37 GiB private on a 32 GiB machine and pages heavily, and with this branch its commit charge dropped from 15.3 to about 7 GiB in my test on 10-05.

If you don't have time, I'd be glad to continue it: rebase onto main (including the #650/#652 resolutions oneandonlydean described), fix the three problems from my report (the stale import after a second mapping, the lost writes in cut()/share(), the double commit charge after a remap), and open it as a follow-up that keeps your commits and authorship and supersedes this one. I'll wait a day or two for your answer before starting.

DotDebian and others added 2 commits October 10, 2026 16:42
… on Windows

Write tracking of a section view write-protects it and resumes the write
fault on the guest thread. Windows builds the exception frame directly below
rsp, over the System V red zone, so the guest resumes with its leaf locals
overwritten when its AVX state is live (boykopovar#361).

A direct memory page mapped at one guest address now lives in private
MEM_WRITE_WATCH memory at that address, and its writes are collected with
GetWriteWatch like flexible memory: no fault is raised. A page mapped at a
second address is moved into the section and both addresses become views of
it, tracked by write protection as before. Pages are stored back into the
section when they are unmapped. The section is reserved rather than
committed, so only shared or stored pages are charged twice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… out of the section

Review of boykopovar#637 found three problems in the resident direct memory path.

A second mapping or a partial unmap split a resident run by copying it out
while it was still writable and committing it again, so a write from another
thread in that window was lost. A run that has to be broken is now made
read-only, copied into the section and replaced by views of it: there is no
copy back into live memory. A write that hits the fence, and a read or write
that hits the placeholder between the release and the views, is retried by
the fault handler once the views exist.

The pages of the whole run are replaced, so a GPU import of the first mapping
kept pinning the old pages. Mapping a physical page a second time and
unmapping part of a run now wait for the leases and imports of every range
that shares the run, as an unmap of those ranges does.

A page stored in the SEC_RESERVE section stayed committed after it was copied
back out, and Windows cannot decommit it. Unmapped resident pages are now
kept in private memory that is released when they are mapped again or when
their backing is destroyed; a page that was once in the section stays a view.

Collect refuses resident pages when the arena has no write watch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DotDebian
DotDebian force-pushed the fix/windows-direct-memory-write-watch branch from d6bb6c5 to dcffc09 Compare October 10, 2026 15:28
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed build docs agc libSceAgc* and removed needs rebase Conflicts with main, rebase needed labels Oct 10, 2026
@DotDebian

Copy link
Copy Markdown
Contributor Author

Sorry for the silence. Rebased onto main (dcffc098) and pushed fixes for the three problems @Celegans12 reported, plus the review items.

  • Lost writes: a run that must be broken is fenced read-only, copied into the section and replaced by views; there is no copy back into live memory. A write that hits the fence, and a read or write that hits the placeholder gap, is retried by the fault handler.
  • Stale import: a second mapping or a partial unmap waits for the leases and imports of every range sharing the run (RequireUnpinned), as an unmap of those ranges does.
  • Double charge: a committed SEC_RESERVE page cannot be decommitted with VirtualFree, so decommitting the section page after the copy is not available. Unmapped resident pages are kept in private memory instead, released when they are mapped again or when the backing is destroyed. A page that was once in the section stays a view.
  • Collect: resident pages are refused when the arena has no write watch.
  • Rebase: both conflicts resolved as @oneandonlydean described, USAGE.md reworded with the new failure messages.

Cost: pages that share a run (up to 2 MiB) with a page mapped twice or partly unmapped become views and are back on the fault path. Three existing tests assumed a collect write-protects singly mapped direct memory and were adapted; details in the description.

Four new tests, each failing without its fix (a lease wait, a GPU import replaced after a second mapping, concurrent writes and reads over 20 rounds, a 256 MiB remap charged once). Windows ctest on this head: 710 of 710 run pass, 5 skipped. Astro Bot was not rerun on this head.

@Celegans12 thanks for the offer and for the repros. A rerun of your three and of the Yōtei commit numbers on this head would help.

Written with AI assistance (Claude Code).

@orishamir

Copy link
Copy Markdown
Contributor

Windows run of dcffc098 merged onto main 262c031 (3d5546c7, clean merge): Windows 11 Pro 26300, RTX 4080 (driver 616.56), WinLibs MinGW-w64 GCC 15.2.0 r7, Release, BUILD_TESTING=ON, the CI windows job's configure line.

Revert-pin (non-test files back to main, the PR's tests, CMake and docs kept):

  • agc_host_import_second_mapping fails with AGC graphics: the import of direct memory whose pages were replaced was kept, so it pins the stale-import fix.
  • guest_memory fails at CheckFailedCollectKeepsWrites (GuestMemory.cpp:1246 in the merged tree), one of the adapted existing checks, before any of the four new checks runs. Running each new check on its own (a local env-var switch in main(), not part of the PR): CheckSingleDirectMappingTracksWritesWithoutFaults fails at Require(g_accessViolations == 0) and CheckSecondDirectMappingWaitsForLeases fails at Require(released), so those two pin. CheckBreakingAResidentRunKeepsConcurrentAccesses and CheckRemappedDirectMemoryIsChargedOnce pass without the fix, 5 of 5 runs each. That follows from what main is: it has no resident runs, so there is nothing to break, and it commits the direct memory section in full in CreateFileMappingW, so a remap cannot add charge. The two tests guard against the d6bb6c5b behaviour Celegans12 reproduced, not against main; a reviewer who reverts the non-test files will see them pass. Worth a sentence in the description, or a variant that also fails on main if there is one.
  • The three adapted agc tests (agc_storage_baseline_settled, agc_host_import_unmap, agc_driver_dcc_keys_first_write) pass both with and without the fix.

With the fix: full ctest 720 of 720 pass (6 skipped: the SDL-offscreen video_out_flip_* and the two no-Vulkan video-out tests), guest_memory 10 of 10 with --repeat until-fail:10, and the new Windows-only agc_host_import_second_mapping passes on the NVIDIA driver.

Head vs reviewed commit: d6bb6c5b changed 4 files, dcffc098 changes 11 (+564/-18). Each review item is present in the merged source: resident views refuse Collect when watchResident is false (WindowsMappings.hpp:319, :398), fence resident guest pages makes a run read-only before it is copied (:497, breakRun at :517), RequireResidentUnpinned/RequireAliasesUnpinned run before the map and unmap paths in DirectMemory.cpp, the section is SEC_RESERVE with GuestArenaForgetSection, and USAGE.md now says commit happens at map time and names the three failure messages.

Merges: clean onto main 262c031. #2864 (the overlap bot's hit) merges clean on its own but conflicts in tests/Recorder.cpp after this PR; whichever lands second needs a rebase.

Not checked: a title run (no dump here) and Linux, where the maintainers build.

Thanks for the quick turnaround on the three problems and the rebase, @DotDebian.

AI-assisted: yes (Claude Code Fable 5.1 xhigh)

@adriadam10

Copy link
Copy Markdown
Collaborator

Thanks for the rebase and the fixes; @orishamir's Windows run of dcffc098 is a good confirmation.

This one overlaps #2054, which makes the same change (singly mapped direct memory as private MEM_WRITE_WATCH pages until it is aliased) in WindowsMappings.hpp, DirectMemory.cpp, GuestArena.cpp, CrashReport.cpp and GuestMemory.cpp. The two conflict with each other in GuestArena.cpp, DirectMemory.cpp and GuestMemory.cpp, so only one can land. @boykopovar @oneandonlydean: which design do you want?

Open points on this PR whichever way it goes:

  • CheckBreakingAResidentRunKeepsConcurrentAccesses and CheckRemappedDirectMemoryIsChargedOnce pass on main (see @orishamir's revert-pin), so they don't pin the change; a variant that fails on main, or a sentence in the description saying so, would help.
  • Linux is not built by CI on this PR; the only runtime evidence is the Windows runs above.

@orishamir

Copy link
Copy Markdown
Contributor

Linux evidence for dcffc098, since CI's linux job is gated on push/workflow_dispatch and never ran here. Same replica as on #374 (the CI image rebuilt locally: ubuntu:24.04, GCC 13.3, Mesa 25.2.8 llvmpipe LLVM 20.1.2, SPIRV-Tools on, CI env, write watch active), the head as pushed on its base 5edae35, ctest --parallel 4 --timeout 120.

  • Build and libs: clean. Full suite, 701 tests, 3 runs: two pass 701/701.
  • The first run had one failure, agc_driver_flat_store (a serial test), and not a timeout: after [gpu] recorded batch still running on the GPU after 10 s the process ended with *** stack smashing detected ***: terminated at 11.4 s. It did not recur in 30 serial runs of the test on this tree, 30 on the base commit with the PR reverted, or 2 further full suites on each tree (1 abort in 65 runs across both trees; our seven full runs on the fix(agc): depth fast clears through the HTILE metadata #374 tree last night also passed it). Not attributable to this PR; noted because it is a stack-protector abort rather than a hang, and the repo has no report of one.
  • What Linux exercises here is small by construction: every hunk in the shared files (GuestArena.cpp/.hpp, DirectMemory.cpp, GuestBufferMemory.cpp) is under #ifdef _WIN32 or calls RequireResidentUnpinned/RequireAliasesUnpinned, whose non-Windows bodies are empty; the new GuestMemory.cpp, Recorder.cpp and DccKeysFirstWrite.cpp cases are _WIN32-only, and agc_host_import_second_mapping is registered under if(WIN32). So Linux here means "compiles, and nothing changed", which the clean runs confirm; the Windows runs remain the behavioural evidence.

AI-assisted: yes (Claude Code Fable 5.1 xhigh)

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 10, 2026
@oneandonlydean

Copy link
Copy Markdown
Collaborator

#637: it's rebased, each of the three problems has a fix and a test, and it has Windows and Linux runs; #2054 still has open asks and conflicts. Two things before it can go in: it now conflicts with main in tests/Recorder.cpp, and CheckBreakingAResidentRunKeepsConcurrentAccesses and CheckRemappedDirectMemoryIsChargedOnce pass on main, so either make them fail there or say in the description that they guard the earlier d6bb6c5b behaviour.

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rebase and the fixes; @orishamir's Windows and Linux runs of dcffc098 are a good confirmation. It now conflicts with main, and one of the conflicts needs a change in your code:

  • GuestBufferMemory.cpp (importAllocation): main now makes WindowsMappings::MapAlias throw std::runtime_error on every refusal, and the caller catches it, adds the base to state.unaliasedSpans and falls back to copies. MapAlias still does return nullptr when a resident run extends outside the range (WindowsMappings.hpp:259). Please make it throw through the same refuse(...) helper, keep the caller as on main, and drop the entry.alias == nullptr branch. Without that, entry.alias stays null after the merge.
  • tests/Recorder.cpp: keep both --unit-shadow-only/--cmask-only and --host-import-second-mapping-only.
  • As @oneandonlydean asked: CheckBreakingAResidentRunKeepsConcurrentAccesses and CheckRemappedDirectMemoryIsChargedOnce pass on main. Make them fail there, or say in the description that they guard the earlier d6bb6c5b behaviour.

We can't build or run the Windows paths here, so we didn't resolve the MapAlias change ourselves. This overlaps #2054 (same change, conflicting too), so only one of them can land; once this one is rebased and green on Windows it is the one with runs on several titles.

@Mouhand-Kaddo

Copy link
Copy Markdown
Contributor

Windows real-title result for head dcffc098c, integrated with main 989146c3b, #4018 and the Windows build guard #4021. Both commits from this PR were applied. Merge conflicts were resolved by retaining current main's multi-range alias handling and test command switches, and adding this PR's null-alias check and second-mapping test switch.

Demon's Souls PPSA01342 v1.04 normally stops after approximately 14 s on this host with an audio DSP access violation at DemonsSouls.exe+0x1f91a90, reading 0x2f8410000. With this PR, that stop did not recur: initialization reached the main loop at 14.28 s and the run continued through shader preparation and GPU texture creation. It ended at 89.07 s with a later explicit unsupported operation:

[videoout] presentation failed: DB_RENDER_CONTROL depth copy, resummarize or decompress is unsupported (register 0x0 = 0x00000010)

No main menu or presented frame verified. This is one bounded real-game run, not a claim that all remaining #361 paths are fixed.

The full libs build succeeded. guest_memory, fiber, windows_exceptions, agc_host_import_second_mapping, and agc_driver_dcc_keys_first_write: 5/5 pass (2.72 s). Windows 11 x64, i7-13700K (AVX2, no AVX-512), RX 6900 XT, MinGW GCC 15.2 Release, ANYPS5_ENABLE_SPIRV_TOOLS=OFF (default), default write tracking and host import. No Linux tests for this comparison. Original game files preserved; no game code or payload attached. AI-assisted with Codex.

@tibzejoker

Copy link
Copy Markdown
Contributor

@DotDebian GTA V (PPSA04264) still needs this on current main (665ad7d). Without it, with write watch on, the title crashes in the RAGE main thread right after the save loads (access violation at eboot 0x28aacee). #5052's relink-time framing doesn't cover GTA: it frames 0 of the 875 red-zone leaves, because none of them has an RBP prologue.

WindowsMappings.hpp has moved a lot since dcffc098c: shared views now track 4 KiB subpages. Are you still planning to rebase this? If not, would you mind if we tried a port onto current main that addresses the review points, with you kept as the author? We can test it on GTA either way.

AI-assisted (Claude Code).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@tibzejoker yes, please port it onto current main and keep DotDebian as the author. The points from the 10-11 review still apply: MapAlias throws on every refusal on main, so the return nullptr at WindowsMappings.hpp:259 and the entry.alias == nullptr branch go, and the two tests that already pass on main should go or be marked as guards. A guest_memory run on Windows of the ported head is needed.

@tibzejoker

Copy link
Copy Markdown
Contributor

@oneandonlydean @DotDebian the port is up as #5596, on main ce5c839. DotDebian is the author of the main commit, and our changes are separate commits. It covers the 10-11 review points (MapAlias throws through refuse, the nullptr branch is gone, and the two tests now fail on main). guest_memory and the full ctest (1112/1112) pass on Windows 11.

AI-assisted (Claude Code).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Closing in favour of #5596, the port of this onto current main with DotDebian's commit kept as the author's; follow-ups go there.

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

Labels

agc libSceAgc* build docs libc libkernel needs rebase Conflicts with main, rebase needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants