fix(vk): free on the GPU's progress, not on a time or a frame count: read-back timeouts and the upscalers' frees #788

Open
mooki wants to merge 1 commit from mooki/Longshot:fix/async-safe-frees into main
Member

Type: fix · Risk: low · Tested on: macOS 26.7, Apple M4 Max (game-free checks only, arm64 and x86_64) · Needs: Windows and Linux, any GPU

Why

When the GPU runs late, the renderer can free or reset things the GPU is still using. Two places go by a clock or a frame count instead of the GPU's progress:

  • After a texture write-back's read-back waits 5 s without finishing, the next read-back resets its command pool and fence and records into its command buffer again, while the GPU may still be running it.
  • With no window (HOS_HEADLESS), the upscalers' images are freed 4 frames after they were last used. Nothing in that mode waits for the GPU each frame, so the GPU can still be using them.

Either can end in a GPU fault or a lost device, on any GPU. Neither has been seen in a game; they were found by reading the source, and the new test shows each one on main's code. The issue tracker's titles (snapshot of 2026-11-14 20:01) have nothing that fits: #130 and #583 are device losses with other causes, #241 is the read-back's cost.

How to reproduce

Seen on: not seen in a game; shown on main's own functions by tests/test_async_safe_frees.py and tests/test_ui_texture_upload.py (case headless), on any OS
Settings and switches: for the read-back: any settings, a GPU stall of more than 5 s during a texture write-back (vk: a batch of N texture write-backs did not complete in time); for the frees: HOS_HEADLESS=1 with DLSS, FSR 3.1 or FSR 4 as the upscaler or RX_VK_REACTIVE=1, or RX_VK_SHOT_MENU=1
Steps: run python -I -B tests/test_async_safe_frees.py --headers <Vulkan-Headers/include> on main; in a game run, set RX_VK_VALIDATION=1 with the settings above and play while the GPU is busy (an area load)
Expected: nothing reset, begun or destroyed while a GPU command may still use it
Actual: the test reports a pool and a fence reset, and a command buffer begun, under a read-back that has not finished, and each of the three free lists freeing before the GPU passed the work that used it. The validation layer would name it (for example VUID-vkResetCommandPool-commandPool-00040, VUID-vkDestroyImage-image-01000) when the GPU is that far behind

What changed

The read-back waits for a submit whose 5 s wait ran out before its pool or fence is reset. The free lists counted in frames also wait for the timeline value that followed the work, as vk_fsr3.cpp (drop_image, free_dead) already does. Nothing is freed earlier than before; it is only freed later where the GPU had not finished.

  • read-backs, gpu/vk/vk_texture.cpp:
    • readback_begin (main :3077) reset rb_pool, and readback_submit_wait (:3111) reset rb_fence, with no wait after the 5 s wait (:3117) had given up. Vulkan forbids both resets while the command buffer or the submit is pending, and drivers reuse a reset pool's command memory.
    • Now rb_pending stays set from the submit until its fence is seen signalled, and readback_settle() waits for it without a limit before either reset.
    • flush_textures's timeout branch (:3337) already kept the batch's staging buffers until the next submission (:3348). The images the copies read were left free: a texture's death waits only for its last_value on the timeline (:4671, :4684), and the read-back's own submit is not on it. Each copied texture's last_value is now raised to that same submission.
  • frees counted in frames, gpu/vk/vk_reactive.cpp, gpu/vk/vk_temporal.cpp, gpu/vk/vk_fsr3_inputs.cpp:
    • later and free_dead (main vk_reactive.cpp:76, :81; vk_temporal.cpp:291, :296; vk_fsr3_inputs.cpp:98, :103) freed an image, view, buffer or released feature 4 frames on. This includes DLSS's and FSR 4's, through temporal_later (vk_temporal.cpp:821).
    • With no window, do_present returns before present_image (gpu/vk/vk_replay.cpp:4499), so nothing waits for the present ring's fences. Only the 48-slot recording ring (vk_replay.cpp:176) limits how far behind the GPU is, and that is many frames when a frame has few recordings.
    • Each entry now also keeps vk_retirement_value() from when it was let go. It is freed once vk_timeline_completed() has passed that value, and never on a value of 0.
  • the overlays' dead list, gpu/vk/vk_imgui.cpp:
    • reap (main :197, :200) frees 4 counts on. With no window the overlays are drawn into the replay's recordings (RX_VK_SHOT_MENU), and idle() (:477) counts frames without waiting for the GPU.
    • With no window an entry now waits for the timeline value too. In a window it is as before (the present ring's fences).
  • Left as they are: TAAU's replaced histories (vk_draw.cpp, 120 frames). With no window every frame records its offscreen copy, so 120 frames are more recordings than the ring holds.

Measurements

Verdict: nothing to measure. A normal run does the same work at the same time; only a GPU that runs late changes when something is freed.

Flags and settings

None

Testing

Tested: game-free, on macOS 26.7, Apple M4 Max, arm64 and x86_64 (Rosetta):

  • tests/test_async_safe_frees.py (new) and tests/test_ui_texture_upload.py (case headless added) fail on main aa95d35f and pass on this PR, on both architectures.
  • Whole-file compiles of the six renderer files touched or referenced (vk_texture.cpp, vk_reactive.cpp, vk_temporal.cpp, vk_fsr3_inputs.cpp, vk_imgui.cpp, vk_replay.cpp) with nx2/build.py's macOS commands, on both architectures: every compile exits 0 with no warnings, as on main.
  • An object check finds the only new references are vk_retirement_value and vk_timeline_completed, both defined in vk_replay.o.
  • python -I -B tests/run_all.py on this Mac, where main's runner takes macOS for Linux: 544 passed, 33 failed, 17 skipped. Main aa95d35f gives 543 passed, 33 failed, 17 skipped, and by name every result is the same apart from the new test (pass). The 33 fail the same way on main on this Mac: test_appimage_package_retry, test_appimage_player_build, test_collect_wide, test_control, test_cpm_discovery, test_crash_dialog, test_crash_record, test_env_on, test_game_layout, test_lift_cleanup_error, test_linux_preflight, test_longshot_ctl, test_low_memory_notice, test_make_game_cache, test_mem_gate, test_mem_gate_children, test_mirror_trim, test_oot_state_tap, test_options_budget_once, test_options_path, test_pcache_header_read, test_pipeline_cache_file, test_pipeline_cache_save, test_pipeline_play_cache, test_pipeline_tour, test_prof_symbols, test_program_cache_cleanup, test_program_cache_one_variant, test_readback_late, test_report_run, test_split_giants, test_vk_pgo, test_walk_key.

Not tested: in the game, on any platform (neither hazard can be produced on demand: it needs a GPU stall over 5 s, or the GPU more than 4 frames behind with no window). Not built on Windows (llvm-mingw) or Linux. The code has no platform branch, so it runs the same on both.

Must be tested:

  • Windows and Linux, any GPU, with the Khronos validation layer installed: build this PR (head 90f6c3e3) and main aa95d35f the same way and run python -I -B tests/run_all.py. Then start each with HOS_HEADLESS=1 RX_VK_FSR=1 RX_VK_REACTIVE=1 RX_VK_VALIDATION=1 (no window, FSR 3.1, the reactive mask) and play to the title and into the field. Expect every check to pass, and no validation error on this PR that main does not also show. A read-back that runs out of its 5 s wait cannot be made on demand (by hand: test request T-NTFW)

Risks

What could break: a read-back after a GPU stall now waits for the stall to end instead of going on. A real hang still ends at the GPU hang watch (RX_VK_GPU_HANG_SEC, 30 s). The upscalers' images and the headless overlays are kept until the GPU has passed them, at most a few frames longer than before.

Turning it off: revert the commit

Compatibility: Nothing changes

Checklist

Everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description) of the status line's type.
  • Why gives the reason plainly and links the issues that fit (the tracker searched).
  • Measurements has a verdict, or one line saying why there is nothing to measure.
  • Testing names what was tested, what wasn't, and what must still be tested, where and how. (Test request T-NTFW.)
  • Rebased on current main (main aa95d35f, head 90f6c3e3).
  • python -I -B tests/run_all.py passes (any failure that also fails on main is named).
  • 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.

When it applies

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • 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.
**Type:** fix · **Risk:** low · **Tested on:** macOS 26.7, Apple M4 Max (game-free checks only, arm64 and x86_64) · **Needs:** Windows and Linux, any GPU ## Why When the GPU runs late, the renderer can free or reset things the GPU is still using. Two places go by a clock or a frame count instead of the GPU's progress: - After a texture write-back's read-back waits 5 s without finishing, the next read-back resets its command pool and fence and records into its command buffer again, while the GPU may still be running it. - With no window (`HOS_HEADLESS`), the upscalers' images are freed 4 frames after they were last used. Nothing in that mode waits for the GPU each frame, so the GPU can still be using them. Either can end in a GPU fault or a lost device, on any GPU. Neither has been seen in a game; they were found by reading the source, and the new test shows each one on main's code. The issue tracker's titles (snapshot of 2026-11-14 20:01) have nothing that fits: #130 and #583 are device losses with other causes, #241 is the read-back's cost. ## How to reproduce **Seen on:** not seen in a game; shown on main's own functions by `tests/test_async_safe_frees.py` and `tests/test_ui_texture_upload.py` (case `headless`), on any OS **Settings and switches:** for the read-back: any settings, a GPU stall of more than 5 s during a texture write-back (`vk: a batch of N texture write-backs did not complete in time`); for the frees: `HOS_HEADLESS=1` with DLSS, FSR 3.1 or FSR 4 as the upscaler or `RX_VK_REACTIVE=1`, or `RX_VK_SHOT_MENU=1` **Steps:** run `python -I -B tests/test_async_safe_frees.py --headers <Vulkan-Headers/include>` on main; in a game run, set `RX_VK_VALIDATION=1` with the settings above and play while the GPU is busy (an area load) **Expected:** nothing reset, begun or destroyed while a GPU command may still use it **Actual:** the test reports a pool and a fence reset, and a command buffer begun, under a read-back that has not finished, and each of the three free lists freeing before the GPU passed the work that used it. The validation layer would name it (for example `VUID-vkResetCommandPool-commandPool-00040`, `VUID-vkDestroyImage-image-01000`) when the GPU is that far behind ## What changed The read-back waits for a submit whose 5 s wait ran out before its pool or fence is reset. The free lists counted in frames also wait for the timeline value that followed the work, as `vk_fsr3.cpp` (`drop_image`, `free_dead`) already does. Nothing is freed earlier than before; it is only freed later where the GPU had not finished. - read-backs, `gpu/vk/vk_texture.cpp`: - `readback_begin` (main `:3077`) reset `rb_pool`, and `readback_submit_wait` (`:3111`) reset `rb_fence`, with no wait after the 5 s wait (`:3117`) had given up. Vulkan forbids both resets while the command buffer or the submit is pending, and drivers reuse a reset pool's command memory. - Now `rb_pending` stays set from the submit until its fence is seen signalled, and `readback_settle()` waits for it without a limit before either reset. - `flush_textures`'s timeout branch (`:3337`) already kept the batch's staging buffers until the next submission (`:3348`). The images the copies read were left free: a texture's death waits only for its `last_value` on the timeline (`:4671`, `:4684`), and the read-back's own submit is not on it. Each copied texture's `last_value` is now raised to that same submission. - frees counted in frames, `gpu/vk/vk_reactive.cpp`, `gpu/vk/vk_temporal.cpp`, `gpu/vk/vk_fsr3_inputs.cpp`: - `later` and `free_dead` (main `vk_reactive.cpp:76`, `:81`; `vk_temporal.cpp:291`, `:296`; `vk_fsr3_inputs.cpp:98`, `:103`) freed an image, view, buffer or released feature 4 frames on. This includes DLSS's and FSR 4's, through `temporal_later` (`vk_temporal.cpp:821`). - With no window, `do_present` returns before `present_image` (`gpu/vk/vk_replay.cpp:4499`), so nothing waits for the present ring's fences. Only the 48-slot recording ring (`vk_replay.cpp:176`) limits how far behind the GPU is, and that is many frames when a frame has few recordings. - Each entry now also keeps `vk_retirement_value()` from when it was let go. It is freed once `vk_timeline_completed()` has passed that value, and never on a value of 0. - the overlays' dead list, `gpu/vk/vk_imgui.cpp`: - `reap` (main `:197`, `:200`) frees 4 counts on. With no window the overlays are drawn into the replay's recordings (`RX_VK_SHOT_MENU`), and `idle()` (`:477`) counts frames without waiting for the GPU. - With no window an entry now waits for the timeline value too. In a window it is as before (the present ring's fences). - Left as they are: TAAU's replaced histories (`vk_draw.cpp`, 120 frames). With no window every frame records its offscreen copy, so 120 frames are more recordings than the ring holds. ## Measurements **Verdict:** nothing to measure. A normal run does the same work at the same time; only a GPU that runs late changes when something is freed. ### Flags and settings None ## Testing **Tested:** game-free, on macOS 26.7, Apple M4 Max, arm64 and x86_64 (Rosetta): - `tests/test_async_safe_frees.py` (new) and `tests/test_ui_texture_upload.py` (case `headless` added) fail on main `aa95d35f` and pass on this PR, on both architectures. - Whole-file compiles of the six renderer files touched or referenced (`vk_texture.cpp`, `vk_reactive.cpp`, `vk_temporal.cpp`, `vk_fsr3_inputs.cpp`, `vk_imgui.cpp`, `vk_replay.cpp`) with `nx2/build.py`'s macOS commands, on both architectures: every compile exits 0 with no warnings, as on main. - An object check finds the only new references are `vk_retirement_value` and `vk_timeline_completed`, both defined in `vk_replay.o`. - `python -I -B tests/run_all.py` on this Mac, where main's runner takes macOS for Linux: 544 passed, 33 failed, 17 skipped. Main `aa95d35f` gives 543 passed, 33 failed, 17 skipped, and by name every result is the same apart from the new test (pass). The 33 fail the same way on main on this Mac: test_appimage_package_retry, test_appimage_player_build, test_collect_wide, test_control, test_cpm_discovery, test_crash_dialog, test_crash_record, test_env_on, test_game_layout, test_lift_cleanup_error, test_linux_preflight, test_longshot_ctl, test_low_memory_notice, test_make_game_cache, test_mem_gate, test_mem_gate_children, test_mirror_trim, test_oot_state_tap, test_options_budget_once, test_options_path, test_pcache_header_read, test_pipeline_cache_file, test_pipeline_cache_save, test_pipeline_play_cache, test_pipeline_tour, test_prof_symbols, test_program_cache_cleanup, test_program_cache_one_variant, test_readback_late, test_report_run, test_split_giants, test_vk_pgo, test_walk_key. **Not tested:** in the game, on any platform (neither hazard can be produced on demand: it needs a GPU stall over 5 s, or the GPU more than 4 frames behind with no window). Not built on Windows (llvm-mingw) or Linux. The code has no platform branch, so it runs the same on both. **Must be tested:** - Windows and Linux, any GPU, with the Khronos validation layer installed: build this PR (head 90f6c3e3) and main aa95d35f the same way and run `python -I -B tests/run_all.py`. Then start each with `HOS_HEADLESS=1 RX_VK_FSR=1 RX_VK_REACTIVE=1 RX_VK_VALIDATION=1` (no window, FSR 3.1, the reactive mask) and play to the title and into the field. Expect every check to pass, and no validation error on this PR that main does not also show. A read-back that runs out of its 5 s wait cannot be made on demand (by hand: test request T-NTFW) ## Risks **What could break:** a read-back after a GPU stall now waits for the stall to end instead of going on. A real hang still ends at the GPU hang watch (`RX_VK_GPU_HANG_SEC`, 30 s). The upscalers' images and the headless overlays are kept until the GPU has passed them, at most a few frames longer than before. **Turning it off:** revert the commit **Compatibility:** Nothing changes ## Checklist ### Everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`) of the status line's type. - [x] Why gives the reason plainly and links the issues that fit (the tracker searched). - [x] Measurements has a verdict, or one line saying why there is nothing to measure. - [x] Testing names what was tested, what wasn't, and what must still be tested, where and how. (Test request T-NTFW.) - [x] Rebased on current `main` (main aa95d35f, head `90f6c3e3`). - [x] `python -I -B tests/run_all.py` passes (any failure that also fails on `main` is named). - [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. ### When it applies - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened.
fix(vk): free on the GPU's progress, not on a time or a frame count: read-back timeouts and the upscalers' frees
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 5s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
90f6c3e35f
Some frees waited for a 5 s limit or a few frames, not for the GPU: a hazard on every GPU, reached when the GPU runs
late. Nothing is freed earlier than before; only later where the GPU had not finished.

- Read-backs: a read-back whose 5 s wait ran out left rb_pool and rb_fence to be reset, and rb_cmd begun again,
  by the next read-back with no wait while the GPU could still run it: a pool with a pending command buffer and a
  fence of a pending submit must not be reset, and drivers reuse a reset pool's command memory. Now they wait for
  it, without a limit, before they are reset (rb_pending, readback_settle). The batch's textures wait for the
  submission after it (last_value), as its staging buffers already did: the copies may still read them.
- Frees counted in frames: what the reactive mask, the temporal upscalers (DLSS and FSR 4 through temporal_later)
  and the FSR 3 inputs let go was freed 4 frames on. With no window (HOS_HEADLESS) presents render offscreen and
  nothing waits for the present ring; only the 48-slot recording ring bounds how far behind the GPU is, many
  frames when a frame has few recordings. They now wait for the timeline value that followed them
  (vk_retirement_value) as well, never for 0, as vk_fsr3.cpp does. Likewise the overlays' dead list with no window
  (RX_VK_SHOT_MENU's overlays are recorded on the timeline, and idle() counts frames with no wait for the GPU).

TAAU's replaced histories (120 frames) are left as they are: 120 frames are more recordings than the ring holds.

tests/test_async_safe_frees.py runs the read-back against a GPU that does not finish in time and the three later()
lists on a timeline of the test's; tests/test_ui_texture_upload.py gains the headless case.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
mooki changed title from WIP: fix(vk): free on the GPU's progress, not on a time or a frame count: read-back timeouts and the upscalers' frees to fix(vk): free on the GPU's progress, not on a time or a frame count: read-back timeouts and the upscalers' frees 2026-11-15 12:00:14 +00:00
HumanBean left a comment

The frees counted in frames (reactive mask, temporal/DLSS/FSR 4, FSR 3 inputs, headless overlays) read right to me: they copy the vk_retirement_value() / vk_timeline_completed() rule vk_fsr3.cpp already uses, never free on 0, and free_dead(all) in vk_temporal.cpp still frees everything on teardown. No vk_api.h change, no generated files. That half I would approve.

The read-back half has to change before merge, because #553 and #554 are open for exactly the same two defects (a read-back reset under a pending submit; a timed-out batch's source images freed under its copies), and they were run on a Steam Deck (non-regression, no timeout hit). This PR does not mention them. As it stands:

  • It conflicts with both in gpu/vk/vk_texture.cpp: the rb_pending declaration (553 declares bool rb_pending = false, rb_lost = false; at the same place), readback_submit_wait's last lines, and the for (Flush& f : fs) timeout loop (554 edits the same lines to add f.t->last_value).
  • The designs are incompatible, not just textually: 553 makes readback_begin return false while the fence is unsignalled (a 0 ns wait) and keeps the batch's textures resident for a later pass, with a device-lost state. This PR instead waits with UINT64_MAX in readback_settle and then goes on, evicting the batch's textures with no copy of their texels (the loss 553 removes). The new tests also assert opposite behaviour (test_readback_timeout_reuse.py expects readback_begin() to fail while pending; test_async_safe_frees.py expects it to begin after an unbounded wait), so both cannot pass on one tree.
  • The last_value raise is the same fix as 554 (if (f.t) here, unguarded there).
  • The unbounded wait runs on whatever thread calls flush_texture/texture_frame: a stall that never reports device lost holds that thread until the 30 s hang watch ends the process. 553's non-blocking path avoids this.

Please drop the read-back parts (the vk_texture.cpp hunks, F5_READBACK and the texture checks in tests/test_async_safe_frees.py, its docstring lines) and keep this PR to the frame-counted frees, rebased after 553 and 554 land. If you think the unbounded wait is better than 553's design, argue that on 553 instead.

Smaller: Dead::value = retired_value() in vk_imgui.cpp is a default member initializer, so it is taken wherever a Dead is constructed. That is right for the current push_back({presents, ...}) sites; a one-line comment saying so would help.

The frees counted in frames (reactive mask, temporal/DLSS/FSR 4, FSR 3 inputs, headless overlays) read right to me: they copy the `vk_retirement_value()` / `vk_timeline_completed()` rule `vk_fsr3.cpp` already uses, never free on 0, and `free_dead(all)` in `vk_temporal.cpp` still frees everything on teardown. No `vk_api.h` change, no generated files. That half I would approve. The read-back half has to change before merge, because https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/553 and https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/554 are open for exactly the same two defects (a read-back reset under a pending submit; a timed-out batch's source images freed under its copies), and they were run on a Steam Deck (non-regression, no timeout hit). This PR does not mention them. As it stands: - It conflicts with both in `gpu/vk/vk_texture.cpp`: the `rb_pending` declaration (553 declares `bool rb_pending = false, rb_lost = false;` at the same place), `readback_submit_wait`'s last lines, and the `for (Flush& f : fs)` timeout loop (554 edits the same lines to add `f.t->last_value`). - The designs are incompatible, not just textually: 553 makes `readback_begin` return false while the fence is unsignalled (a 0 ns wait) and keeps the batch's textures resident for a later pass, with a device-lost state. This PR instead waits with `UINT64_MAX` in `readback_settle` and then goes on, evicting the batch's textures with no copy of their texels (the loss 553 removes). The new tests also assert opposite behaviour (`test_readback_timeout_reuse.py` expects `readback_begin()` to fail while pending; `test_async_safe_frees.py` expects it to begin after an unbounded wait), so both cannot pass on one tree. - The `last_value` raise is the same fix as 554 (`if (f.t)` here, unguarded there). - The unbounded wait runs on whatever thread calls `flush_texture`/`texture_frame`: a stall that never reports device lost holds that thread until the 30 s hang watch ends the process. 553's non-blocking path avoids this. Please drop the read-back parts (the `vk_texture.cpp` hunks, `F5_READBACK` and the `texture` checks in `tests/test_async_safe_frees.py`, its docstring lines) and keep this PR to the frame-counted frees, rebased after 553 and 554 land. If you think the unbounded wait is better than 553's design, argue that on 553 instead. Smaller: `Dead::value = retired_value()` in `vk_imgui.cpp` is a default member initializer, so it is taken wherever a `Dead` is constructed. That is right for the current `push_back({presents, ...})` sites; a one-line comment saying so would help.
@ -3062,0 +3063,4 @@
// GPU may still be running it, and a pool whose command buffer is pending, or a fence of a pending submit, must not be reset (a
// driver reuses a reset pool's command memory). So rb_pool and rb_fence wait for it, without a limit, before they are reset; they
// were reset with no wait, its command buffer begun again under it
bool rb_pending = false;
Owner

Duplicates and conflicts with #553 (same rb_pending, opposite design: 553 returns false from readback_begin while pending and keeps the textures; this waits unbounded then evicts without the copy). Drop the read-back part and rebase after 553/554.

Duplicates and conflicts with https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/553 (same `rb_pending`, opposite design: 553 returns false from readback_begin while pending and keeps the textures; this waits unbounded then evicts without the copy). Drop the read-back part and rebase after 553/554.
Some checks failed
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 3s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Guide screenshots / Guide images and local UI source review (pull_request) Failing after 5s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
This pull request has changes conflicting with the target branch.
  • gpu/vk/vk_texture.cpp
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'fix/async-safe-frees:mooki-fix/async-safe-frees'
git switch 'mooki-fix/async-safe-frees'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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!788
No description provided.