fix(memory): give commit back near Windows' commit limit instead of crashing #77

Merged
HumanBean merged 1 commit from HumanBean/Longshot:fix/commit-limit into main 2026-11-13 02:34:45 +00:00
Owner

Why

On Windows a PC with a small page file can hit the commit limit while an area loads, and the game crashes (Windows is out of memory, then an access violation on ARes Loading 1). This gives commit back before the limit, and waits at the limit instead of crashing.

Port of NotRexed/Hookshot#2972 by NotRexed, who wrote it and is kept as the commit's author. The patch applied as is once the comment tags were renamed (hookshot → longshot). The only addition is the docs row for the new switch.

What changed

  • textures: a commit rule in texture_frame() in gpu/vk/vk_texture.cpp, beside the driver's-limit rules (commit_reserve(), commit_headroom()). When commit headroom (GlobalMemoryStatusEx's ullAvailPageFile, Windows only) drops below RX_VK_COMMIT_RESERVE_MB, the shortfall comes off the textures' budget, and eviction and compaction give the blocks back. While it is active it logs vk: commit limit near: at most every 600 frames.
  • memory: hos_commit and the first-touch handler in host/mkw_glue.c wait up to 5 s at the commit limit (error 1455), retrying every 50 ms, before giving up. Before, the failed commit left the pages reserved and the game's next write crashed. The failure log and the out-of-memory notice now live in one place, commit_refused.
  • tests: tests/test_commit_limit.py checks the rule, the wait, and that the first-touch handler doesn't wait twice.
  • docs: a row for RX_VK_COMMIT_RESERVE_MB in docs/development.md, "Switches for testing and debugging".

Flags and settings

Flag New or changed What it does Default now Goal: on by default? Helps on Doesn't help on Untested on Untested, but should help on
RX_VK_COMMIT_RESERVE_MB=<MB> New Commit the texture cache leaves free before Windows' commit limit; 0 turns the rule off 2560 (on, Windows only) Yes: it does nothing until headroom drops below the reserve Windows PCs near the commit limit (16 GB RAM with a fixed page file) Linux (it overcommits; the rule never reads headroom there) All hardware, in the game Low-RAM Windows PCs with a fixed or small page file

Measurements

NotRexed's in-game results, posted on the original PR after it was opened. They were measured on Hookshot, where this is the same code (the only difference is the hookshot → longshot comment tags). These numbers were not taken again on Longshot.

main: e0e60920 (Hookshot main, 2026-11-12; Longshot main here: ef8b784f)
this PR: fix/commit-limit @ 905cb5e1 (Hookshot; 87a5e424 here)
Baseline: Hookshot main @ e0e60920 (the base of NotRexed/Hookshot#2972); this PR's code is its head 905cb5e1, here as 87a5e424 on Longshot main ef8b784f. Longshot main wasn't measured separately because the code under test didn't change in the port.
Hardware: RTX 4060 Ti, 31 GB RAM, 115 GB commit limit, Windows
Scene: the route run (Kokiri, Hyrule Field, Kakariko), 3440x1440, muted
Method: main and this branch alternated, 2 runs each with the rule idle (this PC never gets near its limit); then a run with the rule forced (RX_VK_COMMIT_RESERVE_MB=38397, about 25 GB of the game's commit before it kicks in) against the run before it, where the rule never fired. Median fps, p99 frame time, private commit, VRAM blocks, and the vk: commit limit near: lines from the log.
How to reproduce: on Windows, run the route once on main and once on this PR, twice each, alternating; then set RX_VK_COMMIT_RESERVE_MB to about your free commit less 25 GB and run it again.

Rule idle (the default on a PC far from its limit):

Run main @ e0e60920 this PR @ 905cb5e1
Median fps, run 1 51 53
Median fps, run 2 50 57
Median fps, median of runs 50.5 55
p99, run 1 / run 2 43.2 / 42.1 ms 40.5 / 36.1 ms
Private commit at end, run 1 / run 2 33.4 / 34.8 GB 28.9 / 28.6 GB
commit limit near lines - 0

The rule never fired on this branch's runs, so the fps and commit differences are run-to-run variation, not the change. What these runs show is that it costs nothing when idle.

Rule forced (RX_VK_COMMIT_RESERVE_MB=38397):

this PR @ 905cb5e1, rule not firing (the run before) this PR @ 905cb5e1, RX_VK_COMMIT_RESERVE_MB=38397
Median fps 58 54
p99 35.6 ms 38.7 ms
Max frame time 881 ms 881 ms
Private commit peak 30.3 GB 26.7 GB
VRAM blocks peak 12.9 GB 9.0 GB
commit limit near lines 0 52

With the rule forced, the texture budget came down by the shortfall each time (for example 3661 → 2812 MB), and headroom stayed between 37.4 and 38.4 GB, within about 1 GB of the reserve. The compaction gave blocks back. No flashes in the earlier forced attempt's flash check, but its Kokiri walk never reached the reserve, so that check doesn't show the trim active.

Why the fix is needed (Hookshot main, a route run on the RTX 4060 Ti, and the crash report from a 16 GB PC with a fixed 32 GB page file, 47.9 GB commit limit): private commit went from 19.3 GB at the start to 35.4 GB in Hyrule Field, while the VRAM blocks grew from 2.7 to 13.3 GB. Private commit less the blocks stayed flat at ~21 GB, and the working set at ~11 GB. The 16 GB PC crashed at 33.9 GB of its own commit:

mem: commit of 0xf0000 bytes ... failed (error 1455)
Windows is out of memory
CRASH: exception 0xc0000005 (write ...)   (ARes Loading 1)

Testing

Tested: in the game by NotRexed on Hookshot (same code), RTX 4060 Ti, 31 GB RAM, Windows, on the route run (Kokiri, Hyrule Field, Kakariko): 2 interleaved runs each of main and this branch with the rule idle, and a run with the rule forced, which logged 52 commit limit near lines and lowered both peak commit and VRAM blocks (see Measurements). Game-free on Linux for this port: test_commit_limit.py, test_texture_squeeze.py, test_blocks_at_start.py and test_patch_hooks_defined.py pass. tests/run_all.py gives 454 passed, 7 failed, 71 skipped, rebased on main @ ef8b784f. All 7 also fail on main without this change: test_bake_portability, test_builder_log_read_only, test_program_cache_cleanup, test_program_cache_one_variant, test_control, test_longshot_ctl, test_vram_budget_row.

Not tested: the 5 s wait at the commit limit itself (reaching it means really filling a PC's commit; tests/test_commit_limit.py covers it), and a PC that actually reaches its limit in play, like the 16 GB one with the fixed page file from the crash report. Nobody has run the Longshot build of this in the game.

Needs other testers:

  • Windows PC with 16 GB RAM and a fixed or small page file: a route run on the defaults, expecting vk: commit limit near: lines near the limit and no out-of-memory crash while an area loads (no request opened; anyone with such a PC is welcome)

Screenshots

Nothing on screen.

Risk and rollback

With headroom under the reserve, textures could be evicted more often (more streaming and pop-in) on a PC close to its commit limit. RX_VK_COMMIT_RESERVE_MB=0 turns the rule off. The 5 s wait at the limit only happens where the game would have crashed. Reverting the commit is enough.

Reviewer notes

The new block in texture_frame() is the core. It reuses the driver's-limit rule's over arithmetic. The related upstream lazy-heap draft (HOS_HEAP_LAZY), which goes after the guest heap committed whole up front, is not part of this.

Issues

None.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Flags table filled in; README / docs/development.md switch reference updated.
  • Screenshots for a change that shows on screen. (n/a)
  • A row in docs/tested-configurations.md for each measured run.
  • MKWVK_API_VERSION bumped (gpu/vk/vk_api.h's MkwVkHost or MkwVkRenderer changed). (n/a)
  • The SPIR-V headers regenerated with glslang (a shader changed). (n/a)
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened. (n/a)
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. (syntax-checked upstream only)
  • Docs updated.

🤖 Generated with Claude Code

## Why On Windows a PC with a small page file can hit the commit limit while an area loads, and the game crashes (`Windows is out of memory`, then an access violation on `ARes Loading 1`). This gives commit back before the limit, and waits at the limit instead of crashing. **Port of [NotRexed/Hookshot#2972](https://github.com/NotRexed/Hookshot/pull/2972) by NotRexed**, who wrote it and is kept as the commit's author. The patch applied as is once the comment tags were renamed (hookshot → longshot). The only addition is the docs row for the new switch. ## What changed - textures: a commit rule in `texture_frame()` in `gpu/vk/vk_texture.cpp`, beside the driver's-limit rules (`commit_reserve()`, `commit_headroom()`). When commit headroom (`GlobalMemoryStatusEx`'s `ullAvailPageFile`, Windows only) drops below `RX_VK_COMMIT_RESERVE_MB`, the shortfall comes off the textures' budget, and eviction and compaction give the blocks back. While it is active it logs `vk: commit limit near:` at most every 600 frames. - memory: `hos_commit` and the first-touch handler in `host/mkw_glue.c` wait up to 5 s at the commit limit (error 1455), retrying every 50 ms, before giving up. Before, the failed commit left the pages reserved and the game's next write crashed. The failure log and the out-of-memory notice now live in one place, `commit_refused`. - tests: `tests/test_commit_limit.py` checks the rule, the wait, and that the first-touch handler doesn't wait twice. - docs: a row for `RX_VK_COMMIT_RESERVE_MB` in `docs/development.md`, "Switches for testing and debugging". ## Flags and settings | Flag | New or changed | What it does | Default now | Goal: on by default? | Helps on | Doesn't help on | Untested on | Untested, but should help on | |---|---|---|---|---|---|---|---|---| | `RX_VK_COMMIT_RESERVE_MB=<MB>` | New | Commit the texture cache leaves free before Windows' commit limit; `0` turns the rule off | 2560 (on, Windows only) | Yes: it does nothing until headroom drops below the reserve | Windows PCs near the commit limit (16 GB RAM with a fixed page file) | Linux (it overcommits; the rule never reads headroom there) | All hardware, in the game | Low-RAM Windows PCs with a fixed or small page file | ## Measurements NotRexed's in-game results, [posted on the original PR](https://github.com/NotRexed/Hookshot/pull/2972) after it was opened. They were measured on Hookshot, where this is the same code (the only difference is the hookshot → longshot comment tags). These numbers were not taken again on Longshot. main: e0e60920 (Hookshot main, 2026-11-12; Longshot main here: ef8b784f) this PR: fix/commit-limit @ 905cb5e1 (Hookshot; 87a5e424 here) Baseline: Hookshot main @ e0e60920 (the base of NotRexed/Hookshot#2972); this PR's code is its head 905cb5e1, here as 87a5e424 on Longshot main ef8b784f. Longshot main wasn't measured separately because the code under test didn't change in the port. Hardware: RTX 4060 Ti, 31 GB RAM, 115 GB commit limit, Windows Scene: the route run (Kokiri, Hyrule Field, Kakariko), 3440x1440, muted Method: main and this branch alternated, 2 runs each with the rule idle (this PC never gets near its limit); then a run with the rule forced (`RX_VK_COMMIT_RESERVE_MB=38397`, about 25 GB of the game's commit before it kicks in) against the run before it, where the rule never fired. Median fps, p99 frame time, private commit, VRAM blocks, and the `vk: commit limit near:` lines from the log. How to reproduce: on Windows, run the route once on main and once on this PR, twice each, alternating; then set `RX_VK_COMMIT_RESERVE_MB` to about your free commit less 25 GB and run it again. **Rule idle (the default on a PC far from its limit):** | Run | main @ e0e60920 | this PR @ 905cb5e1 | |---|---|---| | Median fps, run 1 | 51 | 53 | | Median fps, run 2 | 50 | 57 | | Median fps, median of runs | 50.5 | 55 | | p99, run 1 / run 2 | 43.2 / 42.1 ms | 40.5 / 36.1 ms | | Private commit at end, run 1 / run 2 | 33.4 / 34.8 GB | 28.9 / 28.6 GB | | `commit limit near` lines | - | 0 | The rule never fired on this branch's runs, so the fps and commit differences are run-to-run variation, not the change. What these runs show is that it costs nothing when idle. **Rule forced (`RX_VK_COMMIT_RESERVE_MB=38397`):** | | this PR @ 905cb5e1, rule not firing (the run before) | this PR @ 905cb5e1, `RX_VK_COMMIT_RESERVE_MB=38397` | |---|---|---| | Median fps | 58 | 54 | | p99 | 35.6 ms | 38.7 ms | | Max frame time | 881 ms | 881 ms | | Private commit peak | 30.3 GB | 26.7 GB | | VRAM blocks peak | 12.9 GB | 9.0 GB | | `commit limit near` lines | 0 | 52 | With the rule forced, the texture budget came down by the shortfall each time (for example 3661 → 2812 MB), and headroom stayed between 37.4 and 38.4 GB, within about 1 GB of the reserve. The compaction gave blocks back. No flashes in the earlier forced attempt's flash check, but its Kokiri walk never reached the reserve, so that check doesn't show the trim active. **Why the fix is needed** (Hookshot main, a route run on the RTX 4060 Ti, and the crash report from a 16 GB PC with a fixed 32 GB page file, 47.9 GB commit limit): private commit went from 19.3 GB at the start to 35.4 GB in Hyrule Field, while the VRAM blocks grew from 2.7 to 13.3 GB. Private commit less the blocks stayed flat at ~21 GB, and the working set at ~11 GB. The 16 GB PC crashed at 33.9 GB of its own commit: ``` mem: commit of 0xf0000 bytes ... failed (error 1455) Windows is out of memory CRASH: exception 0xc0000005 (write ...) (ARes Loading 1) ``` ## Testing **Tested:** in the game by NotRexed on Hookshot (same code), RTX 4060 Ti, 31 GB RAM, Windows, on the route run (Kokiri, Hyrule Field, Kakariko): 2 interleaved runs each of main and this branch with the rule idle, and a run with the rule forced, which logged 52 `commit limit near` lines and lowered both peak commit and VRAM blocks (see Measurements). Game-free on Linux for this port: `test_commit_limit.py`, `test_texture_squeeze.py`, `test_blocks_at_start.py` and `test_patch_hooks_defined.py` pass. `tests/run_all.py` gives 454 passed, 7 failed, 71 skipped, rebased on main @ ef8b784f. All 7 also fail on main without this change: test_bake_portability, test_builder_log_read_only, test_program_cache_cleanup, test_program_cache_one_variant, test_control, test_longshot_ctl, test_vram_budget_row. **Not tested:** the 5 s wait at the commit limit itself (reaching it means really filling a PC's commit; `tests/test_commit_limit.py` covers it), and a PC that actually reaches its limit in play, like the 16 GB one with the fixed page file from the crash report. Nobody has run the Longshot build of this in the game. **Needs other testers:** - Windows PC with 16 GB RAM and a fixed or small page file: a route run on the defaults, expecting `vk: commit limit near:` lines near the limit and no out-of-memory crash while an area loads (no request opened; anyone with such a PC is welcome) ## Screenshots Nothing on screen. ## Risk and rollback With headroom under the reserve, textures could be evicted more often (more streaming and pop-in) on a PC close to its commit limit. `RX_VK_COMMIT_RESERVE_MB=0` turns the rule off. The 5 s wait at the limit only happens where the game would have crashed. Reverting the commit is enough. ## Reviewer notes The new block in `texture_frame()` is the core. It reuses the driver's-limit rule's `over` arithmetic. The related upstream lazy-heap draft (HOS_HEAP_LAZY), which goes after the guest heap committed whole up front, is not part of this. ## Issues None. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Flags table filled in; README / docs/development.md switch reference updated. - [ ] Screenshots for a change that shows on screen. (n/a) - [x] A row in `docs/tested-configurations.md` for each measured run. - [ ] `MKWVK_API_VERSION` bumped (`gpu/vk/vk_api.h`'s `MkwVkHost` or `MkwVkRenderer` changed). (n/a) - [ ] The SPIR-V headers regenerated with glslang (a shader changed). (n/a) - [x] A game-free test added for the fix, failing on `main` and passing here. - [ ] A check under `tests/` that reads changed source updated in this PR, not weakened. (n/a) - [ ] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. (syntax-checked upstream only) - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
HumanBean force-pushed fix/commit-limit from 0bc76e407a to 1533bbd9d0 2026-11-13 02:06:28 +00:00 Compare
HumanBean force-pushed fix/commit-limit from 1533bbd9d0 to 87a5e4247a
Some checks failed
PR description / Description follows the template (pull_request_target) Failing after 3s
2026-11-13 02:18:21 +00:00
Compare
HumanBean changed title from WIP: fix(memory): give commit back near Windows' commit limit instead of crashing to fix(memory): give commit back near Windows' commit limit instead of crashing 2026-11-13 02:30:54 +00:00
HumanBean deleted branch fix/commit-limit 2026-11-13 02:34:46 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!77
No description provided.