fix(vk): RX_VK_JOB_STEAL=true no longer turns off a draw's pipeline build where it waits #233

Merged
HumanBean merged 1 commit from TrashPanda/Longshot:fix/pipeline-claim into main 2026-11-13 16:28:26 +00:00
Contributor

Why

A draw that has to wait for its pipeline should never stand behind the whole build queue: on Mako an RX 6600 froze 118 s on one draw behind 26 queued jobs. Longshot already avoids that by running the queued build on the waiting thread. But its switch was read with atoi, so RX_VK_JOB_STEAL=true (or on, yes) quietly turned the protection off. There was also no log line to show it working.

Ported from Mako, by sharkiix, github.com/Shakoquo/Mako@0d93fb1972 ("a draw that must wait for its pipeline builds it itself when no worker has taken it").

What changed

Mako's commit adds JobClaim, jobs_push_claim and jobs_claim_run. Longshot has had the same behaviour since aafbaf5d: jobs_wait takes the job pushed with the flag it waits on out of the queue and runs it on the waiting thread. graphics_pipeline's build (mesh programs included) and both translation jobs are pushed with their flags. A second, claim-based mechanism for the same wait would duplicate it, so this PR keeps the steal and closes what was left:

  • jobs: jobs_wait in gpu/vk/vk_jobs.cpp reads RX_VK_JOB_STEAL by longshot_env.h's on/off rule (env_value_off): only 0, false, off and no turn it off. It now returns whether it ran the job on this thread. The job is still taken out of the queue under the lock workers take jobs under, so it runs exactly once and no leftover is queued for a worker; this is the equivalent of Mako's taken flag.

  • draw: the wait_for lambda in draw() (gpu/vk/vk_draw.cpp) logs the first 10 times a pipeline build or a translation ran where it waited, with how many other jobs were queued or running and how long it took. This is Mako's a draw's pipeline built where it waited line.

  • docs: RX_VK_JOB_STEAL added to docs/development.md's switch table; it was not documented.

  • tests: tests/test_jobs_wait_runs_queued now checks:

    • the stolen job runs once and nothing is left queued;
    • 2000 rounds of free workers racing two waiters for the same job never run it twice and never return before it ran;
    • RX_VK_JOB_STEAL=true and " 1 " keep the steal on, while 0, off and No turn it off (the job then runs on a worker);
    • the source check covers queue.erase, env_value_off and draw()'s pipeline wait going through jobs_wait.

    test_draw_side_effects and test_jobs_failure follow the new signature and include path.

  • Checked and left as they are: compute pipelines (dispatch()) and the mesh fallback's compute pipelines (fallback_pipelines_ready). These are not waited for behind the queue: they are pushed urgent, and the dispatch or draw waits 50 ms and is then left out. Running them on the recording thread would bring back the 15-23 s AMD stalls those paths were moved off it for.

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
RX_VK_JOB_STEAL 0/false/off/no: a draw waits for a worker to reach its build, behind the queue, as before aafbaf5d. Before this PR any value atoi read as 0 (true, on, yes) also turned it off On (unchanged) On by default, as it was Any GPU where a new area queues many pipelines (Mako's report: RX 6600) Nothing: it only changes who runs a queued job In the game, in this PR (see Testing)

Measurements

None possible: there is no game dump on this PC. The change in behaviour is the switch's reading and a log line; the steal itself is unchanged since aafbaf5d. The motivating report is Mako's (sharkiix): on an RX 6600, one draw waited 118 s with "26 jobs queued".

main: fdc2840b (2026-11-13)
this PR: fix/pipeline-claim @ ab7b4950
Hardware: RTX 4070 Ti, NVIDIA 595.99.02, Linux (game-free checks only)
Scene: none (no game)
Method: tests/test_jobs_wait_runs_queued.py and python -I -B tests/run_all.py --jobs 3, each at its commit
How to reproduce: python -I -B tests/test_jobs_wait_runs_queued.py; on main, its own test binary run with RX_VK_JOB_STEAL=true hangs

main @ fdc2840b this PR @ ab7b4950
RX_VK_JOB_STEAL=true, every worker busy: the waited build never runs on the waiting thread (killed by the 10 s timeout) runs on the waiting thread at once
Free workers racing two waiters, 2000 rounds: a job run twice not checked 0
tests/run_all.py 503 passed, 3 failed, 18 skipped 503 passed, 3 failed, 18 skipped (the same 3)
main @ fdc2840b, main's own test binary: RX_VK_JOB_STEAL=true timeout 10 ./jobs-wait -> exit 124; unset -> exit 0
this PR @ ab7b4950:
PASS (steal): a waited-for job still queued runs once, on the waiting thread unless the steal is off   (unset, "true", " 1 ")
(race: 108 of 2000 jobs ran on a waiting thread, the rest on a worker)
PASS (race): ...
PASS (off): ...   ("0", "off", "No")

Testing

Tested:

  • python -I -B tests/run_all.py --jobs 3 on main @ fdc2840b and this PR @ ab7b4950 (Linux, RTX 4070 Ti, 595.99.02): 503 passed, 3 failed, 18 skipped on both. The three failures also fail on main, in the same way: test_display_pointer (undefined win::rx_option), test_texture_staging_publication (undeclared log_debug) and test_lift_cache.
  • The touched tests (test_jobs_wait_runs_queued, test_jobs_failure, test_draw_side_effects, test_ub_threads) pass. The new test fails on main.
  • vk_jobs.cpp and vk_draw.cpp compile with the back end's flags (clang++ -fsyntax-only, nx2/build.py's include folders).

Not tested: Not run in the game: no game dump on this PC. Not built for Windows (llvm-mingw). The change is portable C++ with no platform calls, but it is not compiled there.

Needs other testers:

  • Any GPU, a cold pipeline cache, a new area: the log shows vk: a draw's pipeline build ran where it waited lines and no multi-second freezes with jobs queued. Expect the same frame pacing as main (request to open).
  • AMD RX 6000 on Windows: the scene from Mako's report (a new area on a cold cache). Expect no draw waiting tens of seconds (request to open).

Screenshots

Nothing on screen.

Risk and rollback

Low. The steal's behaviour is unchanged unless the switch was set to something like true, which now keeps it on as the name says. The log line is capped at 10. RX_VK_JOB_STEAL=0 turns the steal off; reverting the commit is enough.

Reviewer notes

Start with jobs_wait in gpu/vk/vk_jobs.cpp, then wait_for in draw(). Mako's JobClaim was not ported on purpose: Longshot's flag-based steal takes the job out of the queue, so no no-op copy is left behind for a worker and the run-once guarantee comes from the queue's lock. #234 (quick pipelines, Mako 8292d363) is stacked on this one.

Issues

See Mako's report in 0d93fb19 (an RX 6600, one draw 118 s behind 26 queued jobs).

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. (Not run in the game: no dump on this PC; 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 to measure in; the game-free results are above.)
  • 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 noted, and the switch reference updated (docs/development.md, the README).
  • 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.
  • Docs updated.
## Why A draw that has to wait for its pipeline should never stand behind the whole build queue: on Mako an RX 6600 froze 118 s on one draw behind 26 queued jobs. Longshot already avoids that by running the queued build on the waiting thread. But its switch was read with `atoi`, so `RX_VK_JOB_STEAL=true` (or `on`, `yes`) quietly turned the protection off. There was also no log line to show it working. Ported from Mako, by sharkiix, https://github.com/Shakoquo/Mako/commit/0d93fb197275ace3be28f48a3333c44dac6e8a25 ("a draw that must wait for its pipeline builds it itself when no worker has taken it"). ## What changed Mako's commit adds `JobClaim`, `jobs_push_claim` and `jobs_claim_run`. Longshot has had the same behaviour since aafbaf5d: `jobs_wait` takes the job pushed with the flag it waits on out of the queue and runs it on the waiting thread. `graphics_pipeline`'s build (mesh programs included) and both translation jobs are pushed with their flags. A second, claim-based mechanism for the same wait would duplicate it, so this PR keeps the steal and closes what was left: - jobs: `jobs_wait` in `gpu/vk/vk_jobs.cpp` reads `RX_VK_JOB_STEAL` by `longshot_env.h`'s on/off rule (`env_value_off`): only `0`, `false`, `off` and `no` turn it off. It now returns whether it ran the job on this thread. The job is still taken out of the queue under the lock workers take jobs under, so it runs exactly once and no leftover is queued for a worker; this is the equivalent of Mako's `taken` flag. - draw: the `wait_for` lambda in `draw()` (`gpu/vk/vk_draw.cpp`) logs the first 10 times a pipeline build or a translation ran where it waited, with how many other jobs were queued or running and how long it took. This is Mako's `a draw's pipeline built where it waited` line. - docs: `RX_VK_JOB_STEAL` added to `docs/development.md`'s switch table; it was not documented. - tests: `tests/test_jobs_wait_runs_queued` now checks: - the stolen job runs once and nothing is left queued; - 2000 rounds of free workers racing two waiters for the same job never run it twice and never return before it ran; - `RX_VK_JOB_STEAL=true` and `" 1 "` keep the steal on, while `0`, `off` and `No` turn it off (the job then runs on a worker); - the source check covers `queue.erase`, `env_value_off` and `draw()`'s pipeline wait going through `jobs_wait`. `test_draw_side_effects` and `test_jobs_failure` follow the new signature and include path. - Checked and left as they are: compute pipelines (`dispatch()`) and the mesh fallback's compute pipelines (`fallback_pipelines_ready`). These are not waited for behind the queue: they are pushed urgent, and the dispatch or draw waits 50 ms and is then left out. Running them on the recording thread would bring back the 15-23 s AMD stalls those paths were moved off it for. ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | `RX_VK_JOB_STEAL` | `0`/`false`/`off`/`no`: a draw waits for a worker to reach its build, behind the queue, as before aafbaf5d. Before this PR any value `atoi` read as 0 (`true`, `on`, `yes`) also turned it off | On (unchanged) | On by default, as it was | Any GPU where a new area queues many pipelines (Mako's report: RX 6600) | Nothing: it only changes who runs a queued job | In the game, in this PR (see Testing) | ## Measurements None possible: there is no game dump on this PC. The change in behaviour is the switch's reading and a log line; the steal itself is unchanged since aafbaf5d. The motivating report is Mako's (sharkiix): on an RX 6600, one draw waited 118 s with "26 jobs queued". main: fdc2840b (2026-11-13) this PR: fix/pipeline-claim @ ab7b4950 Hardware: RTX 4070 Ti, NVIDIA 595.99.02, Linux (game-free checks only) Scene: none (no game) Method: `tests/test_jobs_wait_runs_queued.py` and `python -I -B tests/run_all.py --jobs 3`, each at its commit How to reproduce: `python -I -B tests/test_jobs_wait_runs_queued.py`; on main, its own test binary run with `RX_VK_JOB_STEAL=true` hangs | | main @ fdc2840b | this PR @ ab7b4950 | |---|---|---| | `RX_VK_JOB_STEAL=true`, every worker busy: the waited build | never runs on the waiting thread (killed by the 10 s timeout) | runs on the waiting thread at once | | Free workers racing two waiters, 2000 rounds: a job run twice | not checked | 0 | | `tests/run_all.py` | 503 passed, 3 failed, 18 skipped | 503 passed, 3 failed, 18 skipped (the same 3) | ``` main @ fdc2840b, main's own test binary: RX_VK_JOB_STEAL=true timeout 10 ./jobs-wait -> exit 124; unset -> exit 0 this PR @ ab7b4950: PASS (steal): a waited-for job still queued runs once, on the waiting thread unless the steal is off (unset, "true", " 1 ") (race: 108 of 2000 jobs ran on a waiting thread, the rest on a worker) PASS (race): ... PASS (off): ... ("0", "off", "No") ``` ## Testing **Tested:** - `python -I -B tests/run_all.py --jobs 3` on main @ fdc2840b and this PR @ ab7b4950 (Linux, RTX 4070 Ti, 595.99.02): 503 passed, 3 failed, 18 skipped on both. The three failures also fail on main, in the same way: `test_display_pointer` (undefined `win::rx_option`), `test_texture_staging_publication` (undeclared `log_debug`) and `test_lift_cache`. - The touched tests (`test_jobs_wait_runs_queued`, `test_jobs_failure`, `test_draw_side_effects`, `test_ub_threads`) pass. The new test fails on main. - `vk_jobs.cpp` and `vk_draw.cpp` compile with the back end's flags (`clang++ -fsyntax-only`, nx2/build.py's include folders). **Not tested:** Not run in the game: no game dump on this PC. Not built for Windows (llvm-mingw). The change is portable C++ with no platform calls, but it is not compiled there. **Needs other testers:** - Any GPU, a cold pipeline cache, a new area: the log shows `vk: a draw's pipeline build ran where it waited` lines and no multi-second freezes with `jobs queued`. Expect the same frame pacing as main (request to open). - AMD RX 6000 on Windows: the scene from Mako's report (a new area on a cold cache). Expect no draw waiting tens of seconds (request to open). ## Screenshots Nothing on screen. ## Risk and rollback Low. The steal's behaviour is unchanged unless the switch was set to something like `true`, which now keeps it on as the name says. The log line is capped at 10. `RX_VK_JOB_STEAL=0` turns the steal off; reverting the commit is enough. ## Reviewer notes Start with `jobs_wait` in `gpu/vk/vk_jobs.cpp`, then `wait_for` in `draw()`. Mako's `JobClaim` was not ported on purpose: Longshot's flag-based steal takes the job out of the queue, so no no-op copy is left behind for a worker and the run-once guarantee comes from the queue's lock. #234 (quick pipelines, Mako 8292d363) is stacked on this one. ## Issues See Mako's report in 0d93fb19 (an RX 6600, one draw 118 s behind 26 queued jobs). ## 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). - [ ] Tested in the game, or what wasn't is named under Testing. (Not run in the game: no dump on this PC; named under Testing.) - [x] 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 to measure in; the game-free results are above.) - [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 noted, and the switch reference updated (docs/development.md, the README). - [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. - [x] Docs updated.
fix(vk): RX_VK_JOB_STEAL=true no longer turns off a draw's pipeline build where it waits
Some checks failed
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Failing after 16s
ab7b495070
Mako's 0d93fb19 (an RX 6600: one draw waited 118 s behind 26 queued jobs) adds a claim so that a draw
that must wait for its pipeline builds it itself when no worker has taken it. Longshot has had the same
since aafbaf5d: jobs_wait takes a job pushed with the flag it waits for out of the queue and runs it on
the waiting thread, and graphics_pipeline (mesh programs included) and both translation jobs are pushed
with their flags. A claim beside it would be a second mechanism for the same wait, so this keeps the
steal and closes what was left:

- RX_VK_JOB_STEAL was read with atoi: =true, =on or =yes turned the steal off. It now follows
  longshot_env.h's rule (off for 0, false, off, no), and is documented in docs/development.md.
- jobs_wait says whether it ran the job here; draw() logs the first 10 times a pipeline build or a
  translation ran where it waited, with the jobs queued or running, so a tester can see it happen.
- tests/test_jobs_wait_runs_queued: the stolen job runs once and leaves nothing queued; 2000 rounds of
  free workers racing two waiters for the same job never run it twice; RX_VK_JOB_STEAL=true and " 1 "
  keep it on (main: =true hangs, the steal off), 0/off/No turn it off.

Compute pipelines (dispatch, the mesh fallback's) are not waited for behind the queue: they are pushed
urgent and the dispatch waits 50 ms then is left out, so they are left as they are.

Ported from Mako, by sharkiix, 0d93fb1972

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Assisted-by: Claude Opus 5.5
Co-authored-by: Cursor <[email protected]>
HumanBean left a comment

Read the diff, jobs_wait/jobs_push/worker in vk_jobs.cpp, every jobs_wait caller (draw()'s wait_for, dispatch(), program_wait), the jobs pushed with a flag (graphics_pipeline, both translation jobs) and the compute and fallback builds. I did not build or run anything.

What's good

  • The parse fix is right. env_value_off turns the steal off for 0, false, off, no and an empty value. The old atoi read also turned it off for an empty value and for 0, so =0 and an empty value mean the same as before. true, on and yes (atoi 0, so off before) now keep the steal on, which is the point of the PR. The only other values that change are odd ones like 00, n or disable, which are on now. Nothing in the repo (docs, scripts, presets) sets the switch, so no shipped config changes meaning.
  • The steal is safe as it stands, and this PR doesn't change it. A worker pops under mu, and jobs_wait erases under mu before it unlocks, so a job a worker has started can't be taken again. Each job runs exactly once and its std::function is moved out once, so nothing is built or freed twice. A second waiter on the same flag finds no entry and sleeps on done_cv, and the steal path's jobs_notify() wakes it. The job runs with mu released, so workers and nested jobs_push calls (the cold GPL libraries) aren't blocked. draw() holds no ParLock or shader_mu at either wait_for.
  • The tests are good: run-once and nothing left queued, a 2000-round race between two waiters and free workers, and each switch value run in its own process.

On "is the freeze fixed at every wait site"
In Longshot's default the 118 s case could not happen before this PR either: the steal has been on when the switch is unset since aafbaf5d. This PR fixes it only for someone who set RX_VK_JOB_STEAL to a word. The wait sites:

  • Graphics translation and pipeline waits go through jobs_wait and steal. With RX_VK_TRANSLATE_WAIT_MS set, jobs_wait_for doesn't steal, but it is bounded and then leaves the draw out.
  • Compute programs are translated synchronously in program_pick, so dispatch()'s jobs_wait(cp->ready) rarely waits.
  • Compute and fallback pipelines wait at most 50 ms and are then left out.
  • The create-time loads (programs_load_wait) have their own CAS claim, separate from this switch.

The longest wait left is one build a worker has already started (one pipeline, not a queue), which is the design. Precompile before the title doesn't record draws, so it can't hold a draw.

Must change

  • Nothing blocking.

Should change

  • The description's framing. Helps on: any GPU where a new area queues many pipelines and the first Needs other testers item really test aafbaf5d's steal, not this PR. Say plainly that with the switch unset, behaviour is unchanged, and that this PR fixes RX_VK_JOB_STEAL=<word> and adds the log line. The in-game check this PR needs is only that the ran where it waited line appears.
  • The log line: see inline. Its (RX_VK_JOB_STEAL=0: waited in turn) suffix reads as if the switch were set, and the count isn't the queue it would have waited behind.
  • dispatch()'s jobs_wait(cp->ready) ignores the new return value and isn't logged. That's fine given compute translation is synchronous; just say so if it's on purpose.
  • "Rebased on current main" is ticked, but the PR is on fdc2840b and main is at 1ef93e79 (two commits, #54 and #78). They touch none of these files, so there's no conflict, but rebase or untick the box.

Not checked: Mako's licence. As far as I can tell no Mako code was copied, only the idea and a log wording, and Mako's commit is credited. Not built for Windows.

Read the diff, `jobs_wait`/`jobs_push`/`worker` in `vk_jobs.cpp`, every `jobs_wait` caller (`draw()`'s `wait_for`, `dispatch()`, `program_wait`), the jobs pushed with a flag (`graphics_pipeline`, both translation jobs) and the compute and fallback builds. I did not build or run anything. **What's good** - The parse fix is right. `env_value_off` turns the steal off for `0`, `false`, `off`, `no` and an empty value. The old `atoi` read also turned it off for an empty value and for `0`, so `=0` and an empty value mean the same as before. `true`, `on` and `yes` (atoi 0, so off before) now keep the steal on, which is the point of the PR. The only other values that change are odd ones like `00`, `n` or `disable`, which are on now. Nothing in the repo (docs, scripts, presets) sets the switch, so no shipped config changes meaning. - The steal is safe as it stands, and this PR doesn't change it. A worker pops under `mu`, and `jobs_wait` erases under `mu` before it unlocks, so a job a worker has started can't be taken again. Each job runs exactly once and its `std::function` is moved out once, so nothing is built or freed twice. A second waiter on the same flag finds no entry and sleeps on `done_cv`, and the steal path's `jobs_notify()` wakes it. The job runs with `mu` released, so workers and nested `jobs_push` calls (the cold GPL libraries) aren't blocked. `draw()` holds no `ParLock` or `shader_mu` at either `wait_for`. - The tests are good: run-once and nothing left queued, a 2000-round race between two waiters and free workers, and each switch value run in its own process. **On "is the freeze fixed at every wait site"** In Longshot's default the 118 s case could not happen before this PR either: the steal has been on when the switch is unset since aafbaf5d. This PR fixes it only for someone who set `RX_VK_JOB_STEAL` to a word. The wait sites: - Graphics translation and pipeline waits go through `jobs_wait` and steal. With `RX_VK_TRANSLATE_WAIT_MS` set, `jobs_wait_for` doesn't steal, but it is bounded and then leaves the draw out. - Compute programs are translated synchronously in `program_pick`, so `dispatch()`'s `jobs_wait(cp->ready)` rarely waits. - Compute and fallback pipelines wait at most 50 ms and are then left out. - The create-time loads (`programs_load_wait`) have their own CAS claim, separate from this switch. The longest wait left is one build a worker has already started (one pipeline, not a queue), which is the design. Precompile before the title doesn't record draws, so it can't hold a draw. **Must change** - Nothing blocking. **Should change** - The description's framing. *Helps on: any GPU where a new area queues many pipelines* and the first *Needs other testers* item really test aafbaf5d's steal, not this PR. Say plainly that with the switch unset, behaviour is unchanged, and that this PR fixes `RX_VK_JOB_STEAL=<word>` and adds the log line. The in-game check this PR needs is only that the `ran where it waited` line appears. - The log line: see inline. Its `(RX_VK_JOB_STEAL=0: waited in turn)` suffix reads as if the switch were set, and the count isn't the queue it would have waited behind. - `dispatch()`'s `jobs_wait(cp->ready)` ignores the new return value and isn't logged. That's fine given compute translation is synchronous; just say so if it's on purpose. - "Rebased on current main" is ticked, but the PR is on fdc2840b and main is at 1ef93e79 (two commits, https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/54 and https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/78). They touch none of these files, so there's no conflict, but rebase or untick the box. Not checked: Mako's licence. As far as I can tell no Mako code was copied, only the idea and a log wording, and Mako's commit is credited. Not built for Windows.
@ -216,6 +216,7 @@ Pipeline-cache file ownership, temporary storage and failure behavior are descri
| `RX_VK_SKIP_PROG=<prefix>,...` | Leaves out the draws of these programs (default `53509044401d78d2`; `-` leaves out none) |
| `RX_VK_PIPE_SKIP=0`, `1` or `2` | Default/`1`: wait for shader translation (with `RX_VK_TRANSLATE_WAIT_MS` set, a draw whose targets hold an earlier frame waits at most that long a frame in all, then is left out). A pending pipeline can be skipped only for attachment-only draws whose targets hold an earlier frame. `0`: wait for every translation and pipeline. `2`: restore the old skip for untranslated draws with persistent targets at once; this can omit writes before the shader is known. Translated memory-writing draws still wait |
| `RX_VK_TRANSLATE_WAIT_MS=<ms>` | Off by default: each shader still translating is waited out, as #1573 does. Set (`8` is the tested value): how long, in all, each recording thread waits in a frame for shaders still translating before leaving their draws on earlier frames' targets out. For first-visit pauses of 0.2-3 s on a cold or changed program bank; a translation slower than the bound can lose a memory write, since its writes are not known before it is translated. A negative value is off; anything but a number is ignored with a warning; the value in use is logged. Compute dispatches still wait their translation out |
| `RX_VK_JOB_STEAL=0` | A draw that must wait for its shader translation or pipeline build waits for a worker to reach it, behind every job queued before it, as before. By default a job it waits for that no worker has started yet is taken out of the queue and run on the waiting thread (it runs once either way; one a worker has started is waited for). The log's `vk: a draw's ... ran where it waited` lines (the first 10) say when it happened. Off follows the on/off rule above (`0`, `false`, `off`, `no`) |
Owner

Nit: an empty value (RX_VK_JOB_STEAL=) also turns it off under env_value_off, as it did under atoi. The general rule at line 108 covers that, but the list in brackets here leaves it out.

Nit: an empty value (`RX_VK_JOB_STEAL=`) also turns it off under `env_value_off`, as it did under `atoi`. The general rule at line 108 covers that, but the list in brackets here leaves it out.
@ -6840,3 +6840,3 @@
PROF_EXACT();
const auto t0 = std::chrono::steady_clock::now();
jobs_wait(ready);
const int queued = jobs_pending();
Owner

jobs_pending() counts every job queued or running, including the ones queued after this one, not the jobs this wait would have stood behind. The comment on 6848 ("the queue the wait would have stood behind") and the log's wording overstate it a little. Either say "jobs pending", or have jobs_wait report the job's position in queue when it erases it, which is the number that explains a freeze.

`jobs_pending()` counts every job queued or running, including the ones queued after this one, not the jobs this wait would have stood behind. The comment on 6848 ("the queue the wait would have stood behind") and the log's wording overstate it a little. Either say "jobs pending", or have `jobs_wait` report the job's position in `queue` when it erases it, which is the number that explains a freeze.
@ -6845,0 +6847,4 @@
static std::atomic<int> told{0};
if (here && told++ < 10) // (longshot: that the steal ran, with the queue the wait would have stood behind)
logf("vk: a draw's %s ran where it waited, ahead of the queue (%d other jobs queued or running), in %.1f ms "
"(RX_VK_JOB_STEAL=0: waited in turn)",
Owner

The suffix (RX_VK_JOB_STEAL=0: waited in turn) is printed on every steal and reads as if the switch were set to 0. Something like (with RX_VK_JOB_STEAL=0 it would have waited in turn) says what is meant.

The suffix `(RX_VK_JOB_STEAL=0: waited in turn)` is printed on every steal and reads as if the switch were set to 0. Something like `(with RX_VK_JOB_STEAL=0 it would have waited in turn)` says what is meant.
HumanBean added this to the v3.0.0 milestone 2026-11-13 16:27:58 +00:00
HumanBean changed title from WIP: fix(vk): RX_VK_JOB_STEAL=true no longer turns off a draw's pipeline build where it waits to fix(vk): RX_VK_JOB_STEAL=true no longer turns off a draw's pipeline build where it waits 2026-11-13 16:28:01 +00:00
HumanBean deleted branch fix/pipeline-claim 2026-11-13 16:28:28 +00:00
Some checks failed
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Failing after 16s
Some workflows are waiting to be reviewed.
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!233
No description provided.