WIP: perf(vk): images the renderer lets go of are destroyed on a thread of their own #792

Draft
dyst wants to merge 2 commits from dyst/Longshot:perf/image-reaper into main
Member

Type: perf · Risk: medium · Tested on: Windows 11 (game-free checks) · Needs: Windows, NVIDIA (any RTX), turning the camera in the Market and Hyrule Field; any GPU for the compaction

Why

The game stutters while the camera turns. Part of that is the renderer freeing GPU images on its own thread: when it lets go of textures and render targets, texture_frame destroys each image, its views, buffer and memory right there, and the render-target pool's trim destroys up to 16 more a present. On NVIDIA's Windows driver a vmaDestroyImage is a kernel call of a millisecond and more, and a turn of the camera lets go of many in one present.

Project ReLink, a fork of Longshot, measured 2.4 ms of each frame over 25 ms spent there, against 0.1 ms of a normal frame. Moving the frees to a thread of their own cut its frames over 25 ms and its 99th percentile (31689010, 5224e821).

What changed

The images the renderer lets go of are destroyed on a reaper thread. The textures' compaction rule is kept: nothing is freed while a pass is open.

  • vk: destroy_image_later (gpu/vk/vk_vma.cpp) queues an image with its views, buffer and memory objects for a reaper thread, started on first use.

    • Thread safety: VMA is thread-safe as we create it (no externally-synchronized flag, vma_init). The images handed over are ones nothing can use again: their last recorded use has finished.
  • vk: the compaction rule.

    • The reaper frees only while no defragmentation pass is open: it waits on compact_in_pass under reap_mu.
    • image_compact_pass takes that lock before vmaBeginDefragmentationPass, and image_compact_pass_end holds it through vmaEndDefragmentationPass.
    • Each free runs outside the lock, marked in flight (reaping) under it, so the render thread's destroy_image_later never waits for the queue; image_compact_pass waits for that one free (reap_done_cv) before vmaBeginDefragmentationPass (5333dd63: the first version freed under the lock and kept it while the queue had more, so a hand-over waited for the whole queue).
    • So a pass waits for the one free in flight (a millisecond or so, only when a pass begins), and no free comes inside a pass. texture_frame still hands nothing over while compact.in_pass, as before.
  • vk: texture_frame's dead images and pool_destroy (the render-target pool's trim) use it.

  • switch: RX_VK_REAPER=0 destroys them at once on the render thread, as before. The log says vk: images are destroyed on a thread of their own when it's on.

  • docs: the switch's row in docs/development.md.

  • tests: tests/test_image_reaper.py (new) checks:

    • the hand-over;
    • the lock's order around both VMA pass calls;
    • the reaper's wait;
    • the switch.

    tests/test_target_image_pool.py follows the new calls and still checks pooling before freeing.

Measurements

Not measured here. What to measure, on Windows with an NVIDIA card: frames over 25 ms and the 99th percentile while turning the camera in the Market and on arriving in Hyrule Field, on main and on this PR, at least two interleaved runs each, with tex_frame_ms from the frame trace. ReLink's numbers, for reference:

  • frames over 25 ms: 71/81 down to 67/50/66;
  • 99th percentile: 36-39 ms down to 34-35 ms.

Flags and settings

One new switch, on by default.

Flag What it is Before this PR After this PR Effect Helps / doesn't help / untested Recommendation
RX_VK_REAPER Images destroyed on a thread of their own not there (always on the render thread) on; 0: on the render thread Takes the driver's image frees off the render thread Untested here; helped ReLink on NVIDIA Windows On for everyone; 0 only to compare or if a driver misbehaves

Testing

Tested:

  • Rebased onto main @ 7bacf846 (this PR @ 5333dd63): the 63 checks that read this diff (listed below), not the whole suite, through tests/run_all.py --jobs 6 on Windows 11 with the Builder's llvm-mingw (v2.0.14 tools): 42 passed, 9 failed, 12 skipped for want of the Vulkan headers. Of the 9, test_check_pr_description, test_dev_packs, test_drawn_page_map and test_keep_rendered failed with no output under load and pass when run again; test_crash_dialog, test_oot_state_tap, test_vk_pgo and test_walk_key also fail on main @ 1310ecc6 (the full suite there: 500 passed, 17 failed, 79 skipped), and test_vk_occlusion_queries on main @ 7bacf846. The 12 skipped, again with RX_CPM set to the Builder's CPM cache: 11 pass, and nx2/test_bc_storage_views fails as on main @ 7bacf846. After the fix below (5333dd63), vk_vma.cpp and vk_texture.cpp compile (-fsyntax-only) with the renderer's flags from nx2/build.py and the Builder's CPM headers.

    The 63 checks
    • nx2/test_bc_storage_views.py
    • nx2/test_texture_init_barrier.py
    • tests/test_3d_view_create_flags.py
    • tests/test_amd_subgroup_profiles.py
    • tests/test_astc_bc_policy.py
    • tests/test_bc_raw_targets.py
    • tests/test_bc_upload_retry.py
    • tests/test_blocks_at_start.py
    • tests/test_check_pr_description.py
    • tests/test_commit_limit.py
    • tests/test_commit_room.py
    • tests/test_compressed_over_rt.py
    • tests/test_crash_dialog.py
    • tests/test_dev_packs.py
    • tests/test_direct_buffer_writes.py
    • tests/test_drawn_page_map.py
    • tests/test_drawn_writeback.py
    • tests/test_dump_readback.py
    • tests/test_evict_reason.py
    • tests/test_frame_trace_waits.py
    • tests/test_fsr_uniform_ownership.py
    • tests/test_hold_gpu.py
    • tests/test_image_reaper.py
    • tests/test_image_view_retry.py
    • tests/test_inline_write_order.py
    • tests/test_intel_compiler_gate.py
    • tests/test_intel_compiles_auto.py
    • tests/test_jobs_failure.py
    • tests/test_jobs_wait_runs_queued.py
    • tests/test_keep_rendered.py
    • tests/test_linux_pressure.py
    • tests/test_log_levels.py
    • tests/test_low_vram_texture_mips.py
    • tests/test_mem_trim.py
    • tests/test_mesh_fallback.py
    • tests/test_met_wait_capture.py
    • tests/test_native_astc_texture_count.py
    • tests/test_oot_state_tap.py
    • tests/test_parallel_init_order.py
    • tests/test_parallel_rb_refresh.py
    • tests/test_pipeline_state_portable.py
    • tests/test_prepare_stall.py
    • tests/test_prof_clock.py
    • tests/test_prof_sampling.py
    • tests/test_quick_pipelines.py
    • tests/test_sparse_mirror.py
    • tests/test_split_before_draw.py
    • tests/test_staging_arena_fallback.py
    • tests/test_target_image_pool.py
    • tests/test_target_overlap.py
    • tests/test_texel_buffer_ownership.py
    • tests/test_texture_budget.py
    • tests/test_texture_layout_sizes.py
    • tests/test_texture_squeeze.py
    • tests/test_texture_staging_publication.py
    • tests/test_third_party_objects.py
    • tests/test_threaded_rendering.py
    • tests/test_vk_occlusion_queries.py
    • tests/test_vk_pgo.py
    • tests/test_vram_compact.py
    • tests/test_vulkan_clear_mip.py
    • tests/test_walk_key.py
    • tests/test_wb_shadow_remake.py
  • tests/run_all.py image_reaper target_image_pool import_buffer compact texture_budget vram sparse on Windows: 6 passed, 2 skipped (no Vulkan headers for those two).

  • vk_vma.cpp and vk_texture.cpp compile with the build's flags (llvm-mingw).

Not tested:

  • In the game: frame times, a long session with the compaction running passes, and Linux (Mesa and NVIDIA).

Must be tested:

  • Windows, NVIDIA RTX:
    1. Turn the camera in the Market, and arrive in Hyrule Field, on main and on this PR.
    2. Expect fewer frames over 25 ms on this PR.
    3. Play 30 minutes with the compaction on (the default) and expect vk: VRAM compacted lines and no device loss (by hand).
  • Linux, any GPU: 15 minutes of play. Expect no change in behaviour and no device loss (by hand).

Risks

What could break:

  • A missed rule: a free that VMA doesn't allow while a defragmentation pass is open. The lock is meant to rule that out, and the compaction would misbehave if it didn't.
  • VRAM given back later: a backlog of dead images on the reaper gives VRAM back a little later than before.

Turning it off: RX_VK_REAPER=0, or revert the commit.

Compatibility: nothing changes for a player beyond where the frees happen; caches and saves are untouched.

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.
  • Rebased on current main.
  • 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.
  • Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Docs updated.

🤖 Generated with Claude Code

**Type:** perf · **Risk:** medium · **Tested on:** Windows 11 (game-free checks) · **Needs:** Windows, NVIDIA (any RTX), turning the camera in the Market and Hyrule Field; any GPU for the compaction ## Why The game stutters while the camera turns. Part of that is the renderer freeing GPU images on its own thread: when it lets go of textures and render targets, `texture_frame` destroys each image, its views, buffer and memory right there, and the render-target pool's trim destroys up to 16 more a present. On NVIDIA's Windows driver a `vmaDestroyImage` is a kernel call of a millisecond and more, and a turn of the camera lets go of many in one present. Project ReLink, a fork of Longshot, measured 2.4 ms of each frame over 25 ms spent there, against 0.1 ms of a normal frame. Moving the frees to a thread of their own cut its frames over 25 ms and its 99th percentile ([31689010](https://github.com/pixl27/ProjectReLink/commit/31689010), [5224e821](https://github.com/pixl27/ProjectReLink/commit/5224e821)). ## What changed The images the renderer lets go of are destroyed on a reaper thread. The textures' compaction rule is kept: nothing is freed while a pass is open. - vk: `destroy_image_later` (`gpu/vk/vk_vma.cpp`) queues an image with its views, buffer and memory objects for a reaper thread, started on first use. - **Thread safety:** VMA is thread-safe as we create it (no externally-synchronized flag, `vma_init`). The images handed over are ones nothing can use again: their last recorded use has finished. - vk: **the compaction rule.** - The reaper frees only while no defragmentation pass is open: it waits on `compact_in_pass` under `reap_mu`. - `image_compact_pass` takes that lock before `vmaBeginDefragmentationPass`, and `image_compact_pass_end` holds it through `vmaEndDefragmentationPass`. - Each free runs outside the lock, marked in flight (`reaping`) under it, so the render thread's `destroy_image_later` never waits for the queue; `image_compact_pass` waits for that one free (`reap_done_cv`) before `vmaBeginDefragmentationPass` (5333dd63: the first version freed under the lock and kept it while the queue had more, so a hand-over waited for the whole queue). - So a pass waits for the one free in flight (a millisecond or so, only when a pass begins), and no free comes inside a pass. `texture_frame` still hands nothing over while `compact.in_pass`, as before. - vk: `texture_frame`'s dead images and `pool_destroy` (the render-target pool's trim) use it. - switch: `RX_VK_REAPER=0` destroys them at once on the render thread, as before. The log says `vk: images are destroyed on a thread of their own` when it's on. - docs: the switch's row in `docs/development.md`. - tests: `tests/test_image_reaper.py` (new) checks: - the hand-over; - the lock's order around both VMA pass calls; - the reaper's wait; - the switch. `tests/test_target_image_pool.py` follows the new calls and still checks pooling before freeing. ## Measurements Not measured here. What to measure, on Windows with an NVIDIA card: frames over 25 ms and the 99th percentile while turning the camera in the Market and on arriving in Hyrule Field, on main and on this PR, at least two interleaved runs each, with `tex_frame_ms` from the frame trace. ReLink's numbers, for reference: - frames over 25 ms: 71/81 down to 67/50/66; - 99th percentile: 36-39 ms down to 34-35 ms. ### Flags and settings One new switch, on by default. | Flag | What it is | Before this PR | After this PR | Effect | Helps / doesn't help / untested | Recommendation | |---|---|---|---|---|---|---| | `RX_VK_REAPER` | Images destroyed on a thread of their own | not there (always on the render thread) | on; `0`: on the render thread | Takes the driver's image frees off the render thread | Untested here; helped ReLink on NVIDIA Windows | On for everyone; `0` only to compare or if a driver misbehaves | ## Testing **Tested:** - Rebased onto `main` @ 7bacf846 (this PR @ 5333dd63): the 63 checks that read this diff (listed below), not the whole suite, through `tests/run_all.py --jobs 6` on Windows 11 with the Builder's llvm-mingw (v2.0.14 tools): 42 passed, 9 failed, 12 skipped for want of the Vulkan headers. Of the 9, `test_check_pr_description`, `test_dev_packs`, `test_drawn_page_map` and `test_keep_rendered` failed with no output under load and pass when run again; `test_crash_dialog`, `test_oot_state_tap`, `test_vk_pgo` and `test_walk_key` also fail on main @ 1310ecc6 (the full suite there: 500 passed, 17 failed, 79 skipped), and `test_vk_occlusion_queries` on main @ 7bacf846. The 12 skipped, again with `RX_CPM` set to the Builder's CPM cache: 11 pass, and `nx2/test_bc_storage_views` fails as on main @ 7bacf846. After the fix below (5333dd63), `vk_vma.cpp` and `vk_texture.cpp` compile (`-fsyntax-only`) with the renderer's flags from `nx2/build.py` and the Builder's CPM headers. <details><summary>The 63 checks</summary> - `nx2/test_bc_storage_views.py` - `nx2/test_texture_init_barrier.py` - `tests/test_3d_view_create_flags.py` - `tests/test_amd_subgroup_profiles.py` - `tests/test_astc_bc_policy.py` - `tests/test_bc_raw_targets.py` - `tests/test_bc_upload_retry.py` - `tests/test_blocks_at_start.py` - `tests/test_check_pr_description.py` - `tests/test_commit_limit.py` - `tests/test_commit_room.py` - `tests/test_compressed_over_rt.py` - `tests/test_crash_dialog.py` - `tests/test_dev_packs.py` - `tests/test_direct_buffer_writes.py` - `tests/test_drawn_page_map.py` - `tests/test_drawn_writeback.py` - `tests/test_dump_readback.py` - `tests/test_evict_reason.py` - `tests/test_frame_trace_waits.py` - `tests/test_fsr_uniform_ownership.py` - `tests/test_hold_gpu.py` - `tests/test_image_reaper.py` - `tests/test_image_view_retry.py` - `tests/test_inline_write_order.py` - `tests/test_intel_compiler_gate.py` - `tests/test_intel_compiles_auto.py` - `tests/test_jobs_failure.py` - `tests/test_jobs_wait_runs_queued.py` - `tests/test_keep_rendered.py` - `tests/test_linux_pressure.py` - `tests/test_log_levels.py` - `tests/test_low_vram_texture_mips.py` - `tests/test_mem_trim.py` - `tests/test_mesh_fallback.py` - `tests/test_met_wait_capture.py` - `tests/test_native_astc_texture_count.py` - `tests/test_oot_state_tap.py` - `tests/test_parallel_init_order.py` - `tests/test_parallel_rb_refresh.py` - `tests/test_pipeline_state_portable.py` - `tests/test_prepare_stall.py` - `tests/test_prof_clock.py` - `tests/test_prof_sampling.py` - `tests/test_quick_pipelines.py` - `tests/test_sparse_mirror.py` - `tests/test_split_before_draw.py` - `tests/test_staging_arena_fallback.py` - `tests/test_target_image_pool.py` - `tests/test_target_overlap.py` - `tests/test_texel_buffer_ownership.py` - `tests/test_texture_budget.py` - `tests/test_texture_layout_sizes.py` - `tests/test_texture_squeeze.py` - `tests/test_texture_staging_publication.py` - `tests/test_third_party_objects.py` - `tests/test_threaded_rendering.py` - `tests/test_vk_occlusion_queries.py` - `tests/test_vk_pgo.py` - `tests/test_vram_compact.py` - `tests/test_vulkan_clear_mip.py` - `tests/test_walk_key.py` - `tests/test_wb_shadow_remake.py` </details> - `tests/run_all.py image_reaper target_image_pool import_buffer compact texture_budget vram sparse` on Windows: 6 passed, 2 skipped (no Vulkan headers for those two). - `vk_vma.cpp` and `vk_texture.cpp` compile with the build's flags (llvm-mingw). **Not tested:** - **In the game:** frame times, a long session with the compaction running passes, and Linux (Mesa and NVIDIA). **Must be tested:** - **Windows, NVIDIA RTX:** 1. Turn the camera in the Market, and arrive in Hyrule Field, on main and on this PR. 2. Expect fewer frames over 25 ms on this PR. 3. Play 30 minutes with the compaction on (the default) and expect `vk: VRAM compacted` lines and no device loss (by hand). - **Linux, any GPU:** 15 minutes of play. Expect no change in behaviour and no device loss (by hand). ## Risks **What could break:** - **A missed rule:** a free that VMA doesn't allow while a defragmentation pass is open. The lock is meant to rule that out, and the compaction would misbehave if it didn't. - **VRAM given back later:** a backlog of dead images on the reaper gives VRAM back a little later than before. **Turning it off:** `RX_VK_REAPER=0`, or revert the commit. **Compatibility:** nothing changes for a player beyond where the frees happen; caches and saves are untouched. ## 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. - [x] Rebased on current `main`. - [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] Switches documented (docs/development.md; the README for a menu row) and shipped at their recommended value. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Docs updated. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
perf(vk): images the renderer lets go of are destroyed on a thread of their own
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 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
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 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
beanstalk/approval cancelled: comment @Beanstalk to start it again
beanstalk/windows+nvidia+gpu-rtx-4070 cancelled
8d361dc63f
texture_frame destroyed the dead images, their views, buffers and memory on the render thread, and the
render-target pool's trim its images too, 16 a present. On NVIDIA's Windows driver a vmaDestroyImage is a
kernel call of a millisecond and more, and a turn of the camera lets go of many in one present: Project
ReLink measured 2.4 ms of each frame over 25 ms there against 0.1 ms of the others, and moving them to a
thread cut its frames over 25 ms and its 99th percentile (31689010, 5224e821).

destroy_image_later (vk_vma.cpp) hands them to a reaper thread. The textures' compaction frees nothing
while a pass is open: the reaper waits on the pass flag under a lock that image_compact_pass takes before
vmaBeginDefragmentationPass and image_compact_pass_end holds through vmaEndDefragmentationPass, so a pass
waits for the one free in flight and no free comes inside it. RX_VK_REAPER=0 destroys them at once.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Member

@Beanstalk test Windows with an NVIDIA RTX GPU at head 8d361dc63f. This PC is Windows with an RTX 4070 and the patched Beanstalk agent dev-9585e4a (isolated_gameplay support).

Compare main and this PR with identical settings, frame generation off and no frame-rate cap, using locally created disposable new-game saves. In the Market, measure 20 seconds standing and 20 seconds while turning the camera at full right-stick speed; also measure Hyrule Field arrivals. Use at least two interleaved runs per side, report median frame time, p99, counts of frames over 25 ms if the farm supports them, and VRAM. Record crashes, device loss and invalid-renderer errors. Include comparison screenshots where useful.

Also exercise the default compaction for 30 minutes on the PR, checking for vk: VRAM compacted lines and no device loss. Explicitly state if the farm cannot represent concurrent camera motion and measurement, the long-run duration, the tex_frame_ms trace, or any requested metric; measurements taken after the camera stops must not be described as camera-turn measurements. A short smoke test or skipped scenario is not full performance/lifetime coverage.

Please keep all game files, generated builds, caches and saves local; only normal farm reports, scrubbed logs and requested game screenshots may be uploaded.

@Beanstalk test Windows with an NVIDIA RTX GPU at head 8d361dc63f2a591fa28bfe43579d3f0f36d9234d. This PC is Windows with an RTX 4070 and the patched Beanstalk agent dev-9585e4a (isolated_gameplay support). Compare main and this PR with identical settings, frame generation off and no frame-rate cap, using locally created disposable new-game saves. In the Market, measure 20 seconds standing and 20 seconds while turning the camera at full right-stick speed; also measure Hyrule Field arrivals. Use at least two interleaved runs per side, report median frame time, p99, counts of frames over 25 ms if the farm supports them, and VRAM. Record crashes, device loss and invalid-renderer errors. Include comparison screenshots where useful. Also exercise the default compaction for 30 minutes on the PR, checking for vk: VRAM compacted lines and no device loss. Explicitly state if the farm cannot represent concurrent camera motion and measurement, the long-run duration, the tex_frame_ms trace, or any requested metric; measurements taken after the camera stops must not be described as camera-turn measurements. A short smoke test or skipped scenario is not full performance/lifetime coverage. Please keep all game files, generated builds, caches and saves local; only normal farm reports, scrubbed logs and requested game screenshots may be uploaded.
Member

Beanstalk test farm

Commit 5333dd6 · tier untrusted · approved by nobody yet · dashboard

A member of BigBeans can review this commit and comment @Beanstalk to test it.

<!-- beanstalk:sticky --> ### Beanstalk test farm Commit `5333dd6` · tier **untrusted** · approved by nobody yet · [dashboard](https://beanstalk.magicbean.dev/prs/792) A member of `BigBeans` can review this commit and comment `@Beanstalk` to test it.
Member

@Beanstalk cancel

@Beanstalk cancel
Member

@futondestroyer cancelled 1 queued job.

@futondestroyer cancelled 1 queued job.
dyst force-pushed perf/image-reaper from 8d361dc63f
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 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 5s
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 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
beanstalk/approval cancelled: comment @Beanstalk to start it again
beanstalk/windows+nvidia+gpu-rtx-4070 cancelled
to 5333dd636f
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
Website / Website on Cloudflare (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 11s
PR description / Description follows the template (pull_request_target) Successful in 5s
Website / Website on Cloudflare (pull_request_target) Has been skipped
beanstalk/approval not approved yet
2026-11-15 19:04:21 +00:00
Compare
Author
Member

Rebased onto main @ 7bacf846. Fixed in 5333dd63: the reaper freed each image under reap_mu and, with more queued, never let go of it between frees, so a destroy_image_later on the render thread waited for the whole queue (and a beginning pass for all of it, not the one in flight). Each free now runs outside the lock, marked in flight; image_compact_pass waits for that one before vmaBeginDefragmentationPass, and compact_in_pass, set under the same lock, still keeps every free out of a pass. tests/test_image_reaper.py checks the order. The 63 checks that read this diff: no failure main doesn't have (4 under load passed when run again). Still WIP: Measurements needs its main/this PR table (the Test Farm can't run the camera turn on Windows).

Rebased onto main @ 7bacf846. Fixed in 5333dd63: the reaper freed each image under reap_mu and, with more queued, never let go of it between frees, so a destroy_image_later on the render thread waited for the whole queue (and a beginning pass for all of it, not the one in flight). Each free now runs outside the lock, marked in flight; image_compact_pass waits for that one before vmaBeginDefragmentationPass, and compact_in_pass, set under the same lock, still keeps every free out of a pass. tests/test_image_reaper.py checks the order. The 63 checks that read this diff: no failure main doesn't have (4 under load passed when run again). Still WIP: Measurements needs its main/this PR table (the Test Farm can't run the camera turn on Windows).
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
Website / Website on Cloudflare (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 11s
PR description / Description follows the template (pull_request_target) Successful in 5s
Website / Website on Cloudflare (pull_request_target) Has been skipped
beanstalk/approval not approved yet
This pull request is marked as a work in progress.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'perf/image-reaper:dyst-perf/image-reaper'
git switch 'dyst-perf/image-reaper'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 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!792
No description provided.