Skip to content

Runtime: the game's render thread on the reserved core (HOS_PIN_RENDER=0: off) - #1034

Merged
NotRexed merged 1 commit into
NotRexed:mainfrom
jordanella:render-thread-core
Nov 9, 2026
Merged

NotRexed merged 1 commit into
NotRexed:mainfrom
jordanella:render-thread-core

Conversation

@jordanella

Copy link
Copy Markdown
Contributor

In the default synchronous mode (RX_VK_ASYNC unset), the Vulkan back end replays each submission on the game thread that submits it. In OoT that is mostly the "Presentation Thread", which submits 4 of the 6 queues. Meanwhile hos_reserved_cpus() keeps the last physical core free of every game thread for the async replay thread, which only exists with RX_VK_ASYNC=1. So on any machine with 8+ logical processors that core sat empty, and the replay shared cores with the game's threads.

This change gives the Presentation Thread the reserved core, and that thread alone. The mask is the one hos_reserved_cpus() already works out, so nothing is CPU-specific and no core is taken from the game that wasn't already reserved. It is skipped with RX_VK_ASYNC=1, where the core belongs to the replay thread, and HOS_PIN still takes precedence. HOS_PIN_RENDER=0 turns it off. The log says once which processors it got.

Measured on a Ryzen 9 3900X (12 cores / 24 threads, 4 CCXs) with an RTX 3080 on Windows 11. Kokiri Forest, standing still after loading a save, headless, input timed in game frames (HOS_INPUT_FRAME_CLOCK=1), the same route every run, live numbers over presents 3120-5520:

build runs fps replay per frame (submit phase)
main 2 34.7 / 35.1 23.6 / 23.4 ms
this branch 2 36.8 / 36.6 22.1 / 22.0 ms

Every replay phase got faster by about the same fraction, which fits contention rather than any one piece of code: the same frame replayed in isolation (RX_VK_BENCH) was ~25% faster than during play before the change. Giving more threads cores of their own (MainThread, or a whole CCX for the two) measured within noise of this, and so did raising the thread's priority.

Not measured: CPUs with 6 or 8 physical cores, where the reserved core is a bigger share of the machine. The reservation itself is unchanged by this PR, though, so the trade is the same as today's: the core was already kept from the game.

The numbers were measured on v2.0.5 (232eecb). The branch is rebased onto main 3b4347d; the rebase did not change the patch (git range-diff shows it identical), and host/hos_thread.c has no upstream changes since. python -I -B tests/run_all.py: 104 passed, 3 failed, 13 skipped on Windows. The three failures (test_changelog.py, test_fps_overlay_order.py, test_precompile_compute_guard.py) fail the same way on main 3b4347d on this machine.

🤖 Generated with Claude Code

…R=0: off)

hos_reserved_cpus keeps the last physical core free of the game's threads for the Vulkan back end's replay thread, which
exists only with RX_VK_ASYNC=1. In the default synchronous mode the replay runs on the thread that submits, and the core
stayed empty. OoT's "Presentation Thread" submits 4 of its 6 queues, so most of each frame's replay is on it: it now gets
the reserved core (the mask hos_reserved_cpus works out, so any CPU with 8+ logical processors), and it alone. No core
is taken from the game that was not already. Not with RX_VK_ASYNC=1 (the replay thread's then); HOS_PIN still decides
over it.

Ryzen 9 3900X, Kokiri Forest, the same as HOS_PIN=Presentation=C00000 measured: the replay ~24 -> ~22 ms a frame,
34 -> 36 fps, in two runs each.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@NotRexed
NotRexed merged commit 34c0858 into NotRexed:main Nov 9, 2026
jordanella added a commit to jordanella/Hookshot that referenced this pull request Nov 9, 2026
…e thread's own processor group; the replay pin on Windows only

Review of NotRexed#1158:
- A hybrid CPU with few performance cores (i5-1235U: 2 of them and 8 efficiency cores) kept one of its two from the
  game. pick_reserved_core now wants 4+ cores of the fastest kind, and 8+ logical processors as before (a CPU of one kind
  with 8+ has 4+ cores); else none is kept: on an efficiency core the replay was slower than on no reserved core at all
  (NotRexed#1158: an i7-14700KF, an i7-13700K).
- The cores were group 0's; the affinity calls' masks are the calling thread's group's (GetThreadGroupAffinity).
- On Linux a thread made inside a pinned replay inherited the pin (the capture's copy helpers, the shader workers, the
  pool imports, ... are made on first use), and the fastest-kind choice is Windows-only: replay_pin_mask is 0 there, as
  before NotRexed#1034.
- tests/test_reserved_core.py is in run_all.py's table (g++), and checks the thresholds: the hybrid laptop CPUs, Lunar
  Lake, CPUs under 8 logical processors.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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