Skip to content

fix(vk): multi-second freezes and white foliage without mesh shaders: fallback pipelines on a worker and in the precompile - #1512

Merged
NotRexed merged 1 commit into
NotRexed:mainfrom
rakefire-impala:fix/mesh-fallback-freezes-pipelines-on-worker
Nov 10, 2026
Merged

NotRexed merged 1 commit into
NotRexed:mainfrom
rakefire-impala:fix/mesh-fallback-freezes-pipelines-on-worker

Conversation

@rakefire-impala

@rakefire-impala rakefire-impala commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

On a GPU without mesh shaders, the fallback compiled its compute pipelines inside the first draw, under one lock. AMD's compiler takes up to 12 s on one, so the game froze, and foliage and the door drew white until each was done. This builds them on a worker, and before play in the precompile.

Independent of the floor-grass and allocator PRs; they merge cleanly in any order. Part of splitting up #1023.

Problem

fallback_compute_pipelines made a program's task and mesh compute pipelines in its first draw, holding one global lock. Every recording thread stopped behind it. On an RX 5500M the game froze for seconds in the first room with foliage, and the door at spawn drew white until its pipelines were done.

Change

The first start after a translator change compiles them all: on this laptop, about 15 minutes for 1,300 programs. The precompile's existing stall rule lets the game start while the last ones finish in the background. Later starts take them from the driver's cache in seconds.

Who it affects

  • Only GPUs without mesh shaders. Only they have fallback (kind-4) programs; on a GPU with mesh shaders nothing here runs.
  • The cost is moved, not added: the compiles that froze the game mid-play now happen before it, once per translator change. Measured on the RX 5500M on 2026-11-10: 885 s for the first start after a translator change (1,300 programs, cold), then 7 s on the next starts (1,381 programs from the driver's cache).

Tests

  • tests/test_mesh_fallback.py checks the worker, the draw, the lock's scope, the precompile and the program cache's load. The check fails on main without this change.
  • test_precompile_compute_guard.py extracts the precompile's compute branch, which is byte-for-byte unchanged.

Tested on

Tested on RX 5500M (RDNA1, 4 GB VRAM, 16 GB RAM), Windows 11, AMD driver; Hookshot main 0c412ad (v2.0.11-383) with these PRs, 2026-11-10
Result No freezes when foliage first appears, and the door isn't white at spawn. Log: 0 fallback draws left out while compiling; precompile: ... 1300 mesh fallback programs in 2934 ms (driver cache warm)

…ompile

fallback_compute_pipelines made a fallback program's compute pipelines in
its first draw, under one lock. AMD's compiler took up to 12 s on one: every
recording thread stopped behind it and the game froze in the first room
with foliage, then the foliage and the door drew white (RX 5500M, RDNA1,
no VK_EXT_mesh_shader).

- fallback_pipelines_ready: the draw builds them on a worker, as dispatch()
  builds a compute program's pipeline (compute_state 0/1/2, which only
  compute programs used; RX_VK_COMPUTE_SERIAL as there). The draw that
  starts the build waits up to 50 ms for it, which covers NVIDIA's
  compiles; a draw while it still compiles is left out, as a dispatch is.
- fallback_compute_pipelines is the build. Its lock now covers NotRexed#1317's
  failure set and the program's pipelines, not the compiles, so several
  programs compile at once on the workers and in the precompile. A failure
  is still remembered by key and variant before the return, and not tried
  again (tests/test_mesh_fallback_failed_once.py, unchanged, passes).
- precompile_run builds every fallback program's pipelines with the
  compute programs', before play. program_cache_load_some takes a new
  fallbacks flag, which only the precompile sets, and then loads every
  program translated for the fallback (kind 4), not only those the notes
  name, whatever the compute list (NotRexed#1417) says: right after an update is
  when they are cold. Only a GPU without mesh shaders has such programs. The first start
  after a translator change compiles them (minutes on AMD; the precompile's
  stall rule lets the game start and they finish behind it); later starts
  take them from the driver's cache.

tests/test_mesh_fallback.py checks the worker, the draw,
the lock's scope, the precompile and the program cache's load.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants