fix(video): keep a slow movie's pictures and survive Intel's compiler #2849

Open
github-sync wants to merge 8 commits from github/pr/2849 into main
Owner

@yeetbot90 opened this pull request on GitHub · 2026-11-12 16:32 UTC · original

��What this is for: a Windows PC with Intel UHD Graphics can play the opening movie instead of skipping almost every picture, and can get into the first scene without the process dying inside Intel's compiler. What is measured: game-free checks on Windows, including the movie check run against this branch and against the source from before the movie change. Not measured: the reporter's game was not run again, on that UHD or on any other GPU. No pictures.

This branch is current main (b157c3e8). #2827 is already merged there, so it is not repeated here.

Problem

From one Windows log, build v2.1.0-rc.3-256-g51da0464, Intel UHD Graphics, Vulkan 1.4, driver 0x195bb0 / Intel Corporation 101.7088, 4 logical processors, shaders not prepared at the build.

  • dec_acquire (host/nn_video.c) waits about 100 ms for the first picture, then hands the buffer out black. The first decode took 426.1 ms. The log says the first buffer went out before the first picture was decoded (shown black) twice.
  • The same function, when the picture in hand is late and it is the only one queued, decodes the next packet so it can skip it. The movie clock is the wall clock, so that decode makes the next acquire later. Mean decode was 19.5 ms and pictures are 16.7 ms apart, and 1933 of 2221 pictures were skipped. A decoder that is already faster than the pictures still needs to decode ahead and drop late ones (issues 146 and 179).
  • The linear movie upscale did run (movie upscaling uses the linear compute path). The coop-matrix movie networks stayed left out. That is unchanged.
  • The first 3D scene compiled shaders on the spot (the shaders' preparation was skipped at the build). One submit was slow submit: 109326.6 ms (draw 108018.5 ms, pipelines 7.1 ms). Later: 134.8 draws left out (not ready), skips at vk_draw.cpp 6756 and 6752, 312 pipelines (203441 ms). Those draws are the mesh fallback waiting on compute pipelines. They are not what killed the process.
  • The process then died: CRASH: exception 0xc0000005 (read 0x170, guest 0xfffff00000000170) at igvk64.dll+0x14d102, backtrace igvk64.dll+0x14d102 igvk64.dll+0x134aaf igvk64.dll+0x7f23a igvk64.dll+0x322ef hookshot_vk.dll+0x21d861 hookshot_vk.dll+0x23467d ucrtbase.dll+0x2cd30 KERNEL32.DLL+0x2dda7 ntdll.dll+0x7ee4c. The log has no driver faulted compiling line, so the fault was not inside guarded_graphics or guarded_compute. vkCreateShaderModule and gpl_link's pipeline create were not guarded, and graphics pipelines were still built side by side.
  • Colour stretch of network 0775215f8da720e6 (512x288 -> 1536x864) squares the decoded sqrt values on purpose. That math is unchanged. An unbound storage buffer reported as reading zeros is unchanged.

What changed

  • host/nn_video.c dec_acquire: the first acquire waits ~500 ms for a picture, by the same clock as #2682's later ~100 ms wait. A Stop or Flush with nothing submitted since still does not wait. HOS_VIDEO_ACQUIRE_TRIES=1 is still 50 tries. If the mean decode time is already longer than the gap between pictures, the late picture is shown instead of decoding another one in order to drop it. Pictures already queued are still dropped. HOS_VIDEO_NO_CATCHUP=1 still skips nothing.
  • gpu/vk/vk_internal.h IntelDriverGate: on Windows, vendor 0x8086 only, one thread enters vkCreateShaderModule and vkCreate*Pipelines. RX_VK_NO_INTEL_SERIAL leaves them side by side. NVIDIA, AMD, and Intel on other systems take no lock.
  • guarded_shader_module (gpu/vk/vk_draw.cpp): a fault comes back as result -77 and that program is left out. gpl_link, the movie and upscale helpers, a masked clear's modules, and the compute warm pass call it in this file. make_module and the temporal pipelines call the same functions through extern_guarded_*, because a header declaration of guarded_* itself was a second function and the Windows build rejected every call as ambiguous. A masked clear does not hold the gate across cached_graphics, which takes the same lock.
  • readme.md and docs/development.md say what the movie does now and name RX_VK_NO_INTEL_SERIAL, HOS_VIDEO_NO_CATCHUP and HOS_VIDEO_ACQUIRE_TRIES.

How it was tested

Windows, clang 22.1.5 (x86_64-pc-windows-msvc), 2026-11-12. No game window. Rebased onto main b157c3e8.

  • python -I -B tests/test_video_catchup.py: printed ok. The same test against host/nn_video.c from before this movie change printed FAIL: the first acquire waits ~500 ms for a picture, FAIL: an acquire after the first waits ~100 ms, and FAIL: a decoder slower than the pictures shows the late one instead of decoding in order to skip it, then exited 1. The fast-decoder case still passed on that old source.
  • python -I -B tests/test_vcore_video_acquire.py: printed ok. The first acquire waits ~500 ms, an acquire after that ~100 ms even when a sleep takes 5 ms, and a stopped decoder does not wait.
  • python -I -B tests/test_intel_compiler_gate.py: printed Intel's compiler on Windows is entered by one thread, and a fault leaves that shader out: ok.
  • python -I -B tests/test_shader_module_guard.py: the extracted guarded_shader_module, with a stand-in that raises an access violation after writing a handle, printed a fault creating a shader module leaves that program out: ok.
  • Also passed here after the rebase: test_opening_skip.py, test_driver_faults.py, test_masked_clear_failure.py, test_program_cache_cleanup.py, test_upscale_fallback.py, test_precompile_compute_guard.py.
  • The first Windows build failed: declaring guarded_compute and guarded_shader_module in the header made a second function, and llvm-mingw reported the calls ambiguous. The wrappers are the fix. tests/run_all.py was not run as a whole.

Not tested

  • The reporter's Windows Intel UHD was not run again. Nothing here shows that the opening plays or that the first scene survives on that PC.
  • NVIDIA, AMD, and Linux Intel were not run.
  • A movie whose decoder is faster than the pictures was not played; only the game-free stand-in covers that path.
  • The first visit will still hitch while shaders compile, because preparation was skipped and this GPU has no mesh shaders.

Risks

  • A movie that decodes slower than it plays stays behind its sound. That is the report the decoder already prints, and it is the alternative to skipping the picture.
  • The first acquire can block for about half a second when no picture is ready yet. A later acquire, and one after Stop or Flush, keeps #2682's shorter wait.
  • On Windows Intel, shader and pipeline creates in one thread can make the first scene's compile take longer on the wall clock. RX_VK_NO_INTEL_SERIAL turns that off.
  • A fault in the compiler leaves that one shader or pipeline out (a missing object) instead of ending the process.
  • #2456 also aims at an igvk64.dll crash, by keeping pipeline-cache saves off the build, and #2790 says four runs of that branch on a UHD 770 did not crash. This branch does not include #2456. The two changes are not the same, and this one was not run on that GPU.

Overlaps

  • #2827 is merged in main and is not in this branch's commits.
  • #2682 is merged. This branch keeps its clock wait and its "no wait after Stop or Flush", and lengthens only the first acquire to ~500 ms.
  • #2456 and #2790, above. #2564 and #2629 are other Intel changes and do not touch this path.
  • No open issue was found for this movie's skip spiral.
> [@yeetbot90](https://github.com/yeetbot90) opened this pull request on GitHub · 2026-11-12 16:32 UTC · [original](https://github.com/NotRexed/Hookshot/pull/2849) ��**What this is for:** a Windows PC with Intel UHD Graphics can play the opening movie instead of skipping almost every picture, and can get into the first scene without the process dying inside Intel's compiler. **What is measured:** game-free checks on Windows, including the movie check run against this branch and against the source from before the movie change. **Not measured:** the reporter's game was not run again, on that UHD or on any other GPU. No pictures. This branch is current `main` (`b157c3e8`). #2827 is already merged there, so it is not repeated here. ## Problem From one Windows log, build `v2.1.0-rc.3-256-g51da0464`, Intel UHD Graphics, Vulkan 1.4, driver `0x195bb0` / Intel Corporation 101.7088, 4 logical processors, shaders not prepared at the build. - `dec_acquire` (`host/nn_video.c`) waits about 100 ms for the first picture, then hands the buffer out black. The first decode took 426.1 ms. The log says `the first buffer went out before the first picture was decoded (shown black)` twice. - The same function, when the picture in hand is late and it is the only one queued, decodes the next packet so it can skip it. The movie clock is the wall clock, so that decode makes the next acquire later. Mean decode was 19.5 ms and pictures are 16.7 ms apart, and 1933 of 2221 pictures were skipped. A decoder that is already faster than the pictures still needs to decode ahead and drop late ones (issues 146 and 179). - The linear movie upscale did run (`movie upscaling uses the linear compute path`). The coop-matrix movie networks stayed left out. That is unchanged. - The first 3D scene compiled shaders on the spot (`the shaders' preparation was skipped at the build`). One submit was `slow submit: 109326.6 ms` (draw 108018.5 ms, pipelines 7.1 ms). Later: `134.8 draws left out (not ready)`, skips at `vk_draw.cpp` 6756 and 6752, `312 pipelines (203441 ms)`. Those draws are the mesh fallback waiting on compute pipelines. They are not what killed the process. - The process then died: `CRASH: exception 0xc0000005 (read 0x170, guest 0xfffff00000000170) at igvk64.dll+0x14d102`, backtrace `igvk64.dll+0x14d102 igvk64.dll+0x134aaf igvk64.dll+0x7f23a igvk64.dll+0x322ef hookshot_vk.dll+0x21d861 hookshot_vk.dll+0x23467d ucrtbase.dll+0x2cd30 KERNEL32.DLL+0x2dda7 ntdll.dll+0x7ee4c`. The log has no `driver faulted compiling` line, so the fault was not inside `guarded_graphics` or `guarded_compute`. `vkCreateShaderModule` and `gpl_link`'s pipeline create were not guarded, and graphics pipelines were still built side by side. - Colour stretch of network `0775215f8da720e6` (`512x288 -> 1536x864`) squares the decoded sqrt values on purpose. That math is unchanged. An unbound storage buffer reported as reading zeros is unchanged. ## What changed - `host/nn_video.c` `dec_acquire`: the first acquire waits ~500 ms for a picture, by the same clock as #2682's later ~100 ms wait. A Stop or Flush with nothing submitted since still does not wait. `HOS_VIDEO_ACQUIRE_TRIES=1` is still 50 tries. If the mean decode time is already longer than the gap between pictures, the late picture is shown instead of decoding another one in order to drop it. Pictures already queued are still dropped. `HOS_VIDEO_NO_CATCHUP=1` still skips nothing. - `gpu/vk/vk_internal.h` `IntelDriverGate`: on Windows, vendor `0x8086` only, one thread enters `vkCreateShaderModule` and `vkCreate*Pipelines`. `RX_VK_NO_INTEL_SERIAL` leaves them side by side. NVIDIA, AMD, and Intel on other systems take no lock. - `guarded_shader_module` (`gpu/vk/vk_draw.cpp`): a fault comes back as result -77 and that program is left out. `gpl_link`, the movie and upscale helpers, a masked clear's modules, and the compute warm pass call it in this file. `make_module` and the temporal pipelines call the same functions through `extern_guarded_*`, because a header declaration of `guarded_*` itself was a second function and the Windows build rejected every call as ambiguous. A masked clear does not hold the gate across `cached_graphics`, which takes the same lock. - `readme.md` and `docs/development.md` say what the movie does now and name `RX_VK_NO_INTEL_SERIAL`, `HOS_VIDEO_NO_CATCHUP` and `HOS_VIDEO_ACQUIRE_TRIES`. ## How it was tested Windows, clang 22.1.5 (`x86_64-pc-windows-msvc`), 2026-11-12. No game window. Rebased onto `main` `b157c3e8`. - `python -I -B tests/test_video_catchup.py`: printed `ok`. The same test against `host/nn_video.c` from before this movie change printed `FAIL: the first acquire waits ~500 ms for a picture`, `FAIL: an acquire after the first waits ~100 ms`, and `FAIL: a decoder slower than the pictures shows the late one instead of decoding in order to skip it`, then exited 1. The fast-decoder case still passed on that old source. - `python -I -B tests/test_vcore_video_acquire.py`: printed `ok`. The first acquire waits ~500 ms, an acquire after that ~100 ms even when a sleep takes 5 ms, and a stopped decoder does not wait. - `python -I -B tests/test_intel_compiler_gate.py`: printed `Intel's compiler on Windows is entered by one thread, and a fault leaves that shader out: ok`. - `python -I -B tests/test_shader_module_guard.py`: the extracted `guarded_shader_module`, with a stand-in that raises an access violation after writing a handle, printed `a fault creating a shader module leaves that program out: ok`. - Also passed here after the rebase: `test_opening_skip.py`, `test_driver_faults.py`, `test_masked_clear_failure.py`, `test_program_cache_cleanup.py`, `test_upscale_fallback.py`, `test_precompile_compute_guard.py`. - The first Windows build failed: declaring `guarded_compute` and `guarded_shader_module` in the header made a second function, and llvm-mingw reported the calls ambiguous. The wrappers are the fix. `tests/run_all.py` was not run as a whole. ## Not tested - The reporter's Windows Intel UHD was not run again. Nothing here shows that the opening plays or that the first scene survives on that PC. - NVIDIA, AMD, and Linux Intel were not run. - A movie whose decoder is faster than the pictures was not played; only the game-free stand-in covers that path. - The first visit will still hitch while shaders compile, because preparation was skipped and this GPU has no mesh shaders. ## Risks - A movie that decodes slower than it plays stays behind its sound. That is the report the decoder already prints, and it is the alternative to skipping the picture. - The first acquire can block for about half a second when no picture is ready yet. A later acquire, and one after Stop or Flush, keeps #2682's shorter wait. - On Windows Intel, shader and pipeline creates in one thread can make the first scene's compile take longer on the wall clock. `RX_VK_NO_INTEL_SERIAL` turns that off. - A fault in the compiler leaves that one shader or pipeline out (a missing object) instead of ending the process. - #2456 also aims at an `igvk64.dll` crash, by keeping pipeline-cache saves off the build, and #2790 says four runs of that branch on a UHD 770 did not crash. This branch does not include #2456. The two changes are not the same, and this one was not run on that GPU. ## Overlaps - #2827 is merged in `main` and is not in this branch's commits. - #2682 is merged. This branch keeps its clock wait and its "no wait after Stop or Flush", and lengthens only the first acquire to ~500 ms. - #2456 and #2790, above. #2564 and #2629 are other Intel changes and do not touch this path. - No open issue was found for this movie's skip spiral. <!-- github-sync:item:2849 -->
A decoder slower than the gap between pictures was asked to decode the next one so it could be dropped. The clock gained every decode, and the opening skipped 1933 of 2221 pictures. The first acquire waits about 500 ms, by the same clock as the later 100 ms wait, and a stopped decoder still does not wait.

Co-authored-by: Cursor <[email protected]>
Intel's compiler on Windows was entered by several threads, and vkCreateShaderModule had no guard. A read of address 0x170 in igvk64 ended the process during the first scene. On that driver one thread enters the compiler, and a fault drops the shader instead of the process. Other vendors, and Intel on other systems, are unchanged.

Co-authored-by: Cursor <[email protected]>
Declaring them in the header made a second function beside the ones in the unnamed namespace, so every call in vk_draw.cpp was ambiguous and the Windows build stopped. Other files call the same functions through a wrapper.

Co-authored-by: Cursor <[email protected]>
The stopped-decoder check was still counting a decoder that had already acquired once.

Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
The move rewrote that link only when realpath could follow it. Directory order can copy the link after the folder has gone, and the old path was then kept.

Co-authored-by: Cursor <[email protected]>
The previous push started only the title check.

Co-authored-by: Cursor <[email protected]>
Commenting is not possible because the repository is archived.
No description provided.