[Tracking] Intel Arc and Core Ultra iGPUs: device lost, black screen after the intro, Linux ANV crashes #999

Open
opened 2026-11-09 05:38:59 +00:00 by NotRexed · 49 comments
NotRexed commented 2026-11-09 05:38:59 +00:00 (Migrated from github.com)

This issue collects every open report of Intel Arc / Core Ultra device lost, black screens and Linux ANV crashes, so there is one place to follow it. The individual reports were closed pointing here; their logs, screenshots and discussion stay in each linked issue.

Summary

On Intel Arc (A750, B580, Pro B50, B390) and Core Ultra iGPUs (Arc 130V / 140V, Meteor Lake):

  • black screen after the intro or title;
  • device lost when Navi appears or a save loads;
  • very slow first-start preparation;
  • on Linux, crashes inside the ANV driver.

Iris Xe and UHD 620 have no mesh shaders; see that tracker.

Affected GPUs and versions

  • GPUs: Arc A750, B580, Pro B50 (Linux), B390 (Linux), Arc 130V / 140V (MSI Claw), Core Ultra 7 155H (Windows and Linux), OneXPlayer G3.
  • Versions: v2.0.2-v2.0.5 and main.

Evidence from the logs

  • Windows device lost: in compute 4b03b3040d7cf076 right after ab7699ee7f536755.
    • #498: B580, 4 of 5 runs, v2.0.2 and v2.0.4, driver 101.9175.
    • #17: Arc 140V and 130V, the same on v2.0.5.
    • RX_VK_FORCE_UPSCALE_STUB=1 and RX_VK_NETWORK_KEEP=none don't change it.
  • #17 (A750): preparation runs at about 1 program a second (116,427 programs); crash at igvk64.dll+0x79ddf3.
  • #741 (Core Ultra 155H): driver 101.9175 plus v2.0.3 got past the Navi scene; one device lost after 45 minutes in compute 435a0138b9013040.
  • #374 (140V): walls missing in Link's house; storage buffer NOT bound at c5+0x60 in 68642bb8 / 6b4dea60 / 739acd67 / d905f52b.
  • Linux ANV:
    • #748 (B390): SIGSEGV at libvulkan_intel.so+0x9d8763 while building pipeline b0b3b40244c71ec7 (the grass-shadow points mesh); the SPIR-V validates, so likely a driver bug.
    • #366 (Meteor Lake): presents stop while a worker spins inside libvulkan_intel.so.
    • #775: preparation OOM-killed at about 28 GiB RSS.
    • #970 (Arc Pro B50): preparation never finishes.

Workarounds

  • Use the latest Intel driver: 101.9175 got the 155H past the Navi scene (#741).
  • Arc 140V: if it goes black after the title, waiting about 5 minutes was reported to help (#637).

Reports merged here (14 issues, about 32 people)

Issue Title People GPU Version Key detail
#17 Intel GPU Issues 9 Arc B580, Arc 140V, Arc 130V, Arc A750, Arc B390, Iris Xe v2.0.5 Intel tracker; v2.0.5: Arc 140V and 130V device lost in 4b03b3040d7cf076 after ab7699ee7f536755 (same as B580); A750 preparation ~1 program/s (116,427 programs, ~30 h) a…
#460 Blackscreen after intro on Intel Arc iGPU 3 Arc 140V (MSI Claw), Arc Pro B50 (Linux) device-lost-fix Black after the logo: Intel compiling big compute shaders on the present thread (58 s each); background compute in ultrawide:
#496 Intel Arc A750 Black Screen 3 Arc A750, Arc B580 low-vram Black after logo -> PC freeze on ultrawide; low-vram builds Intel compute one at a time; B580 on low-vram black, GPU 0% (log)
#498 Black screen after intro Intel gpu 3 Arc B580 v2.0.4 Intel Arc B580: black after first cutscene / loading save into Kokiri: device lost in compute 4b03b3040d7cf076 right after ab7699ee7f536755 (4 of 5 runs, v2.0.2 and v2.0…
#366 Black screen after initial cutscenes on Intel Core Ultra 7 155H Intel… 2 Core Ultra 7 155H Arc iGPU (Linux ANV) v2.0.3 Black after the opening on Meteor Lake Linux: v2.0.3/v2.0.2 presents stop; Presentation Thread waits in draw jobs_wait while one mkw_vk worker (build_graphics) and the s…
#367 Freeze on Intel Arc B580 + Linux 2 Arc B580 (Fedora), RX 580 (Win) foliage-linux-updater Arc B580 Linux: green flashes, black/crash after the first cutscene (log); RX 580 black with sound (no mesh shaders); collaborator said Arc unsupported at the time
#374 Intel Arc 140V (Lunar Lake): walls missing inside Link's house; earli… 2 Arc 140V v2.0.2 Lunar Lake: walls/floor missing in Link's house (unbound SSBO c5+0x60 in 68642bb8/6b4dea60/739acd67/d905f52b); v2.0.2 heavy stutter + crash log
#637 Intel Arc Blackscreen after the Ocarina of Time title screen appears 2 Arc 140V (MSI Claw) honest-eta Black with audio after the title on Arc 140V (4 logs); "wait ~5 min" (govt-boot-licker); same as #460
#631 Intel ARC - Lighting, assets and massive stutters 1 Intel Arc (OneXPlayer G3) ? Minute-long hitches when assets load; lighting/assets broken on Arc
#655 Intel GPUs crash 1 Intel UHD 620, Arc no-visual-studio Freeze after the intro logo on Intel; UHD 620 has no mesh shaders (#454); Arc freeze = compute-on-present-thread fixed in ultrawide (#460)
#741 Intel Arc iGPU (Core Ultra 7 155H): device lost every time Navi leave… 1 Core Ultra 7 155H Arc iGPU v2.0.3 v2.0.2: device lost when Navi leaves (4b03b304 after ab7699ee), VRAM to 48 GB, igvk64.dll heap corruption. v2.0.3 + driver 9175: Navi scene plays, VRAM 6-9 GB, NOT-bound…
#748 Linux, Intel Arc B390 (Mesa ANV 26.2.5): segfault in libvulkan_intel.… 1 Arc B390 (Mesa ANV) main b82bd36 Original first-mesh-draw crash gone on main b82bd36; new SIGSEGV libvulkan_intel.so+0x9d8763 building pipeline b0b3b40244c71ec7 (grass-shadow points mesh, OutputPoints/P…
#775 Linux, Intel Arc iGPU (Meteor Lake), 32 GB: the first start's shader… 1 Core Ultra 7 155H Arc iGPU (Linux, Mesa 25.3.6) v2.0.3 First start's shader preparation OOM-killed at ~28 GiB RSS every start (v2.0.2 and v2.0.3), even with 8 CPUs; precompile builds every kept compute program; asks for per-…
#970 Linux 2.0.5 black screen 1 Arc Pro B50 (Mesa 26.2.5) v2.0.5 Linux: preparation never finishes, different each run (3 logs)

Adding a report

Please update to v2.0.6 first (https://github.com/NotRexed/Hookshot/releases/tag/v2.0.6; Update next to Play in the launcher). Please add: GPU, driver (or Mesa) version, OS, and hookshot_log.txt.

All tracking issues

  • #993 Garbled / rainbow textures that get worse over play time (VRAM over budget, mostly 4-8 GB cards)
  • #994 Floating trees, missing mountains and vanishing near vegetation
  • #995 Low FPS with low GPU usage (CPU-bound), FPS drops in Castle Town / Hyrule Field, shader stutter
  • #996 AMD on Windows: driver crash, device lost or freeze (RX 5000-9000, Deku Tree / after cutscenes)
  • #997 AMD handhelds and APUs (ROG Ally / Ally X, Legion Go, Radeon 780M/860M/890M): black screen, device lost, reboot
  • #998 Black screen with audio after the intro / at the Deku Tree cutscene
  • #999 Intel Arc and Core Ultra iGPUs: device lost, black screen after the intro, Linux ANV crashes
  • #1000 Linux and Steam Deck: GPU hangs, crashes, low FPS and build failures
  • #1001 GPUs without mesh shaders (GTX 9xx/10xx, RX 400/500/5000, Iris Xe/UHD): v2.0.6 fallback reports
  • #1002 Invisible or missing objects, walls and effects
  • #1003 Antivirus false positives on HookshotBuilder.exe (Defender Trojan:Script/Wacatac.H!ml)
  • #1004 Lighting: overexposure, over-bright bloom, crushed or black shadows, black faces
  • #1005 Flicker and flashing: brightness pulses, one-frame flashes, doubled image with frame pacing
  • #1006 AMD rendering: white silhouettes, block squares / grid lines, no anti-aliasing (networks in software)
  • #1007 Feature requests (collected)
This issue collects every open report of Intel Arc / Core Ultra device lost, black screens and Linux ANV crashes, so there is one place to follow it. The individual reports were closed pointing here; their logs, screenshots and discussion stay in each linked issue. ## Summary On Intel Arc (A750, B580, Pro B50, B390) and Core Ultra iGPUs (Arc 130V / 140V, Meteor Lake): - black screen after the intro or title; - device lost when Navi appears or a save loads; - very slow first-start preparation; - on Linux, crashes inside the ANV driver. Iris Xe and UHD 620 have no mesh shaders; see that tracker. ## Affected GPUs and versions - **GPUs:** Arc A750, B580, Pro B50 (Linux), B390 (Linux), Arc 130V / 140V (MSI Claw), Core Ultra 7 155H (Windows and Linux), OneXPlayer G3. - **Versions:** v2.0.2-v2.0.5 and main. ## Evidence from the logs - **Windows device lost:** in compute 4b03b3040d7cf076 right after ab7699ee7f536755. - #498: B580, 4 of 5 runs, v2.0.2 and v2.0.4, driver 101.9175. - #17: Arc 140V and 130V, the same on v2.0.5. - `RX_VK_FORCE_UPSCALE_STUB=1` and `RX_VK_NETWORK_KEEP=none` don't change it. - **#17** (A750): preparation runs at about 1 program a second (116,427 programs); crash at igvk64.dll+0x79ddf3. - **#741** (Core Ultra 155H): driver 101.9175 plus v2.0.3 got past the Navi scene; one device lost after 45 minutes in compute 435a0138b9013040. - **#374** (140V): walls missing in Link's house; `storage buffer NOT bound` at c5+0x60 in 68642bb8 / 6b4dea60 / 739acd67 / d905f52b. - **Linux ANV:** - #748 (B390): SIGSEGV at libvulkan_intel.so+0x9d8763 while building pipeline b0b3b40244c71ec7 (the grass-shadow points mesh); the SPIR-V validates, so likely a driver bug. - #366 (Meteor Lake): presents stop while a worker spins inside libvulkan_intel.so. - #775: preparation OOM-killed at about 28 GiB RSS. - #970 (Arc Pro B50): preparation never finishes. ## Workarounds - Use the latest Intel driver: 101.9175 got the 155H past the Navi scene (#741). - Arc 140V: if it goes black after the title, waiting about 5 minutes was reported to help (#637). ## Reports merged here (14 issues, about 32 people) | Issue | Title | People | GPU | Version | Key detail | |---|---|---|---|---|---| | #17 | Intel GPU Issues | 9 | Arc B580, Arc 140V, Arc 130V, Arc A750, Arc B390, Iris Xe | v2.0.5 | Intel tracker; v2.0.5: Arc 140V and 130V device lost in 4b03b3040d7cf076 after ab7699ee7f536755 (same as B580); A750 preparation ~1 program/s (116,427 programs, ~30 h) a… | | #460 | Blackscreen after intro on Intel Arc iGPU | 3 | Arc 140V (MSI Claw), Arc Pro B50 (Linux) | device-lost-fix | Black after the logo: Intel compiling big compute shaders on the present thread (58 s each); background compute in ultrawide: | | #496 | Intel Arc A750 Black Screen | 3 | Arc A750, Arc B580 | low-vram | Black after logo -> PC freeze on ultrawide; low-vram builds Intel compute one at a time; B580 on low-vram black, GPU 0% (log) | | #498 | Black screen after intro Intel gpu | 3 | Arc B580 | v2.0.4 | Intel Arc B580: black after first cutscene / loading save into Kokiri: device lost in compute 4b03b3040d7cf076 right after ab7699ee7f536755 (4 of 5 runs, v2.0.2 and v2.0… | | #366 | Black screen after initial cutscenes on Intel Core Ultra 7 155H Intel… | 2 | Core Ultra 7 155H Arc iGPU (Linux ANV) | v2.0.3 | Black after the opening on Meteor Lake Linux: v2.0.3/v2.0.2 presents stop; Presentation Thread waits in draw jobs_wait while one mkw_vk worker (build_graphics) and the s… | | #367 | Freeze on Intel Arc B580 + Linux | 2 | Arc B580 (Fedora), RX 580 (Win) | foliage-linux-updater | Arc B580 Linux: green flashes, black/crash after the first cutscene (log); RX 580 black with sound (no mesh shaders); collaborator said Arc unsupported at the time | | #374 | Intel Arc 140V (Lunar Lake): walls missing inside Link's house; earli… | 2 | Arc 140V | v2.0.2 | Lunar Lake: walls/floor missing in Link's house (unbound SSBO c5+0x60 in 68642bb8/6b4dea60/739acd67/d905f52b); v2.0.2 heavy stutter + crash log | | #637 | Intel Arc Blackscreen after the Ocarina of Time title screen appears | 2 | Arc 140V (MSI Claw) | honest-eta | Black with audio after the title on Arc 140V (4 logs); "wait ~5 min" (govt-boot-licker); same as #460 | | #631 | Intel ARC - Lighting, assets and massive stutters | 1 | Intel Arc (OneXPlayer G3) | ? | Minute-long hitches when assets load; lighting/assets broken on Arc | | #655 | Intel GPUs crash | 1 | Intel UHD 620, Arc | no-visual-studio | Freeze after the intro logo on Intel; UHD 620 has no mesh shaders (#454); Arc freeze = compute-on-present-thread fixed in ultrawide (#460) | | #741 | Intel Arc iGPU (Core Ultra 7 155H): device lost every time Navi leave… | 1 | Core Ultra 7 155H Arc iGPU | v2.0.3 | v2.0.2: device lost when Navi leaves (4b03b304 after ab7699ee), VRAM to 48 GB, igvk64.dll heap corruption. v2.0.3 + driver 9175: Navi scene plays, VRAM 6-9 GB, NOT-bound… | | #748 | Linux, Intel Arc B390 (Mesa ANV 26.2.5): segfault in libvulkan_intel.… | 1 | Arc B390 (Mesa ANV) | main b82bd36 | Original first-mesh-draw crash gone on main b82bd36; new SIGSEGV libvulkan_intel.so+0x9d8763 building pipeline b0b3b40244c71ec7 (grass-shadow points mesh, OutputPoints/P… | | #775 | Linux, Intel Arc iGPU (Meteor Lake), 32 GB: the first start's shader… | 1 | Core Ultra 7 155H Arc iGPU (Linux, Mesa 25.3.6) | v2.0.3 | First start's shader preparation OOM-killed at ~28 GiB RSS every start (v2.0.2 and v2.0.3), even with 8 CPUs; precompile builds every kept compute program; asks for per-… | | #970 | Linux 2.0.5 black screen | 1 | Arc Pro B50 (Mesa 26.2.5) | v2.0.5 | Linux: preparation never finishes, different each run (3 logs) | ## Adding a report Please update to v2.0.6 first (https://github.com/NotRexed/Hookshot/releases/tag/v2.0.6; Update next to Play in the launcher). Please add: GPU, driver (or Mesa) version, OS, and `hookshot_log.txt`. ## All tracking issues - #993 Garbled / rainbow textures that get worse over play time (VRAM over budget, mostly 4-8 GB cards) - #994 Floating trees, missing mountains and vanishing near vegetation - #995 Low FPS with low GPU usage (CPU-bound), FPS drops in Castle Town / Hyrule Field, shader stutter - #996 AMD on Windows: driver crash, device lost or freeze (RX 5000-9000, Deku Tree / after cutscenes) - #997 AMD handhelds and APUs (ROG Ally / Ally X, Legion Go, Radeon 780M/860M/890M): black screen, device lost, reboot - #998 Black screen with audio after the intro / at the Deku Tree cutscene - #999 Intel Arc and Core Ultra iGPUs: device lost, black screen after the intro, Linux ANV crashes - #1000 Linux and Steam Deck: GPU hangs, crashes, low FPS and build failures - #1001 GPUs without mesh shaders (GTX 9xx/10xx, RX 400/500/5000, Iris Xe/UHD): v2.0.6 fallback reports - #1002 Invisible or missing objects, walls and effects - #1003 Antivirus false positives on HookshotBuilder.exe (Defender Trojan:Script/Wacatac.H!ml) - #1004 Lighting: overexposure, over-bright bloom, crushed or black shadows, black faces - #1005 Flicker and flashing: brightness pulses, one-frame flashes, doubled image with frame pacing - #1006 AMD rendering: white silhouettes, block squares / grid lines, no anti-aliasing (networks in software) - #1007 Feature requests (collected)
bmiculka commented 2026-11-09 13:57:21 +00:00 (Migrated from github.com)

Man, do you sleep?! Thank for the updates and tracking all this.

v2.0.7 update

Took 2 tries to get the game loaded. First try is hookshot_log.1.txt. 2nd try (hookshot_log.txt) loaded the startup graphics and then the intro - all going at 60 fps. After the intro finished I thought the game had crashed, but left it running. I opened github to update this thread but after a LONG wait (5+ minutes), I heard the Deku tree talking! Popped the game back up - I hear audio, and see the subtitles, but no graphics on screen. I let it run through Deku and Navi talking but n0 graphics update - FPS reported at this point 20-30.

I think the graphics pipe gets lost based on this line appearing after the intro finishes
~3409: [hos host] vk: graphics pipeline for 0e0016204515849a not made (-13)

Seem the GPU is trying to draw again during the Deku tree sequence but nada on screen.

hookshot_log.1.txt
hookshot_log.txt

Man, do you sleep?! Thank for the updates and tracking all this. v2.0.7 update Took 2 tries to get the game loaded. First try is hookshot_log.1.txt. 2nd try (hookshot_log.txt) loaded the startup graphics and then the intro - all going at 60 fps. After the intro finished I thought the game had crashed, but left it running. I opened github to update this thread but after a LONG wait (5+ minutes), I heard the Deku tree talking! Popped the game back up - I hear audio, and see the subtitles, but no graphics on screen. I let it run through Deku and Navi talking but n0 graphics update - FPS reported at this point 20-30. I think the graphics pipe gets lost based on this line appearing after the intro finishes ~3409: [hos host] vk: graphics pipeline for 0e0016204515849a not made (-13) Seem the GPU is trying to draw again during the Deku tree sequence but nada on screen. [hookshot_log.1.txt](https://github.com/user-attachments/files/34582171/hookshot_log.1.txt) [hookshot_log.txt](https://github.com/user-attachments/files/34582172/hookshot_log.txt)
Number9pixel commented 2026-11-09 15:18:17 +00:00 (Migrated from github.com)

The game did not open the first time. The second attempt took a few minutes.
Stuttering during the titling screen.
The cutscene the fps fluctuated below 60 fps but near the end it was constant at 60 fps. The cutscene ended and stuck on the black screen.

Hookshot: v2.0.7

[System]
CPU: Ryzen 5600X
GPU: ARC B580
RAM: 32 Gb
OS: Fedora 45
hookshot_log.txt

The game did not open the first time. The second attempt took a few minutes. Stuttering during the titling screen. The cutscene the fps fluctuated below 60 fps but near the end it was constant at 60 fps. The cutscene ended and stuck on the black screen. Hookshot: v2.0.7 [System] CPU: Ryzen 5600X GPU: ARC B580 RAM: 32 Gb OS: Fedora 45 [hookshot_log.txt](https://github.com/user-attachments/files/34585168/hookshot_log.txt)
VictorNagibator commented 2026-11-09 15:55:51 +00:00 (Migrated from github.com)

The same problem. The first cutscene ends and it stucks on the black screen with sound.

Hookshot: v2.0.8

[System]
CPU: Ryzen 5600
GPU: Intel Arc B580
RAM: 16 GB
OS: Windows 11

hookshot_log.txt

The same problem. The first cutscene ends and it stucks on the black screen with sound. Hookshot: v2.0.8 [System] CPU: Ryzen 5600 GPU: Intel Arc B580 RAM: 16 GB OS: Windows 11 [hookshot_log.txt](https://github.com/user-attachments/files/34587222/hookshot_log.txt)
NotRexed commented 2026-11-09 16:16:42 +00:00 (Migrated from github.com)

@VictorNagibator: thanks for the v2.0.8 log. v2.0.8's new write-back pass was on for every GPU, and it lost the device on newer NVIDIA drivers (#1161). v2.0.9 turns it off by default, so please update to v2.0.9 and say whether the black screen after the first cutscene is gone.

@VictorNagibator: thanks for the v2.0.8 log. v2.0.8's new write-back pass was on for every GPU, and it lost the device on newer NVIDIA drivers (#1161). v2.0.9 turns it off by default, so please update to v2.0.9 and say whether the black screen after the first cutscene is gone.
bmiculka commented 2026-11-09 16:33:25 +00:00 (Migrated from github.com)

v2.0.9 Update.

i5-13600
32GB system Ram
16 GB Arc Pro B50 - Mesa 26.2.5 - Mint 22.3

Thank for the shader and pipeline loading screen. That really helps.

Ultimately, I am getting the same results as v2.0.5 - Startup and Intro graphic display well @ 60 fps. After the main intro plays, screen black for 5 minutes and the Deku tree scene loads with black screen though audio playing and subtitles showing.

This took 4 attempts to start. First (no log) crashed the computer. Second (hookshot_log.2.txt) got through the shaders and started the pipelines before crashing. Third (hookshot_log.1.txt) tried more pipelines but never finished. 4th run (hookshot_log.txt), the game started but seemingly the pipelines never finished but as I said, the game started and then you see my update from above.

hookshot_log.1.txt
hookshot_log.2.txt
hookshot_log.txt

Update: I updated Mesa to 26.2.6, but no change. Pipelines don't finish, get to about 1380ish. Game will load but won't load the Deku tree scene after first intro.

v2.0.9 Update. i5-13600 32GB system Ram 16 GB Arc Pro B50 - Mesa 26.2.5 - Mint 22.3 Thank for the shader and pipeline loading screen. That really helps. Ultimately, I am getting the same results as v2.0.5 - Startup and Intro graphic display well @ 60 fps. After the main intro plays, screen black for 5 minutes and the Deku tree scene loads with black screen though audio playing and subtitles showing. This took 4 attempts to start. First (no log) crashed the computer. Second (hookshot_log.2.txt) got through the shaders and started the pipelines before crashing. Third (hookshot_log.1.txt) tried more pipelines but never finished. 4th run (hookshot_log.txt), the game started but seemingly the pipelines never finished but as I said, the game started and then you see my update from above. [hookshot_log.1.txt](https://github.com/user-attachments/files/34588656/hookshot_log.1.txt) [hookshot_log.2.txt](https://github.com/user-attachments/files/34588655/hookshot_log.2.txt) [hookshot_log.txt](https://github.com/user-attachments/files/34588657/hookshot_log.txt) Update: I updated Mesa to 26.2.6, but no change. Pipelines don't finish, get to about 1380ish. Game will load but won't load the Deku tree scene after first intro.
VictorNagibator commented 2026-11-09 16:39:39 +00:00 (Migrated from github.com)

@VictorNagibator: thanks for the v2.0.8 log. v2.0.8's new write-back pass was on for every GPU, and it lost the device on newer NVIDIA drivers (#1161). v2.0.9 turns it off by default, so please update to v2.0.9 and say whether the black screen after the first cutscene is gone.

Update v2.0.9
I have the same result as v2.0.8: black screen with sound after first cutscene :(

Image

hookshot_log.txt

> [@VictorNagibator](https://github.com/VictorNagibator): thanks for the v2.0.8 log. v2.0.8's new write-back pass was on for every GPU, and it lost the device on newer NVIDIA drivers ([#1161](https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/1161)). v2.0.9 turns it off by default, so please update to v2.0.9 and say whether the black screen after the first cutscene is gone. Update v2.0.9 I have the same result as v2.0.8: black screen with sound after first cutscene :( <img width="1919" height="1030" alt="Image" src="https://github.com/user-attachments/assets/c0cc1c85-21aa-4e24-b496-2143574e4724" /> [hookshot_log.txt](https://github.com/user-attachments/files/34588875/hookshot_log.txt)
Number9pixel commented 2026-11-09 16:57:19 +00:00 (Migrated from github.com)

[Problem]
Pipelines is not able to finish by ending the game brutally.
I was able to open once without completing the pipelines.
First cutscene ends and the game freezes after waiting about 10 minutes it comes back.
Second cutscene is untextured at 4~23 fps until it crashes again.

[Version]
Hookshot: v2.0.8
Hookshot: v2.0.9

[System]
CPU: Ryzen 5600X
GPU: ARC B580
RAM: 32 Gb
OS: Fedora 45

[Logs]
hookshot_log_v2.0.8.txt
hookshot_log_v2.0.9.txt

[Problem] Pipelines is not able to finish by ending the game brutally. I was able to open once without completing the pipelines. First cutscene ends and the game freezes after waiting about 10 minutes it comes back. Second cutscene is untextured at 4~23 fps until it crashes again. [Version] Hookshot: v2.0.8 Hookshot: v2.0.9 [System] CPU: Ryzen 5600X GPU: ARC B580 RAM: 32 Gb OS: Fedora 45 [Logs] [hookshot_log_v2.0.8.txt](https://github.com/user-attachments/files/34589635/hookshot_log_v2.0.8.txt) [hookshot_log_v2.0.9.txt](https://github.com/user-attachments/files/34589466/hookshot_log_v2.0.9.txt)
bmiculka commented 2026-11-09 18:53:43 +00:00 (Migrated from github.com)

v2.0.10 Update.

i5-13600
32GB system Ram
16 GB Arc Pro B50 - Mesa 26.2.6 - Mint 22.3

This did change some things.

Game went through startup and intro (60 fps). Then same black screen (with audio and subtitles, 15-25 fps reported) on Deku tree cinematic, but it actually loaded into it very quickly. No long delay before playing. And even proceeded to I guess Navi going to Link's house. But all black.

First run (hookshot_log.1.txt) it hangs at loading pipeline 1383 and crash. Seems there is something up at that point loading the pipelines. It keeps getting stuck there these past versions. Then the second run (hookshot_log.txt) proceeded as above.

hookshot_log.1.txt
hookshot_log.txt

v2.0.10 Update. i5-13600 32GB system Ram 16 GB Arc Pro B50 - Mesa 26.2.6 - Mint 22.3 This did change some things. Game went through startup and intro (60 fps). Then same black screen (with audio and subtitles, 15-25 fps reported) on Deku tree cinematic, but it actually loaded into it very quickly. No long delay before playing. And even proceeded to I guess Navi going to Link's house. But all black. First run (hookshot_log.1.txt) it hangs at loading pipeline 1383 and crash. Seems there is something up at that point loading the pipelines. It keeps getting stuck there these past versions. Then the second run (hookshot_log.txt) proceeded as above. [hookshot_log.1.txt](https://github.com/user-attachments/files/34593832/hookshot_log.1.txt) [hookshot_log.txt](https://github.com/user-attachments/files/34593833/hookshot_log.txt)
VictorNagibator commented 2026-11-09 21:04:29 +00:00 (Migrated from github.com)

Update v2.0.11

The same result as v2.0.8, as v2.0.9 and as v2.0.10: black screen with sound after first cutscene :(

[System]
CPU: Ryzen 5600
GPU: Intel Arc B580
RAM: 16 GB
OS: Windows 11

hookshot_log.txt

Update v2.0.11 The same result as v2.0.8, as v2.0.9 and as v2.0.10: black screen with sound after first cutscene :( [System] CPU: Ryzen 5600 GPU: Intel Arc B580 RAM: 16 GB OS: Windows 11 [hookshot_log.txt](https://github.com/user-attachments/files/34599853/hookshot_log.txt)
b800047 commented 2026-11-09 22:36:30 +00:00 (Migrated from github.com)

Hookshot: v2.0.11
CPU: Ryzen 7 5700X
GPU: 16gb Intel Arc A770
RAM: 48gb
OS: Windows 11 Home

Might as well put my 2 cents here and see if this helps with progress if any.
Loading of the pipelines seem to take longer and longer (don't know if thats normal)
Menu renders decent at least, flickers but decent on the ui (still funny to me how it seems to "explode" as it convulses)
Intro plays good with small stutters
And now the eternal black abyss before the deku tree ever makes an appearance
and crash

what the problem may be, i hope this log might help

hookshot_log.txt

Hookshot: v2.0.11 CPU: Ryzen 7 5700X GPU: 16gb Intel Arc A770 RAM: 48gb OS: Windows 11 Home Might as well put my 2 cents here and see if this helps with progress if any. Loading of the pipelines seem to take longer and longer (don't know if thats normal) Menu renders decent at least, flickers but decent on the ui (still funny to me how it seems to "explode" as it convulses) Intro plays good with small stutters And now the eternal black abyss before the deku tree ever makes an appearance and crash what the problem may be, i hope this log might help [hookshot_log.txt](https://github.com/user-attachments/files/34603010/hookshot_log.txt)
Ketchup1919 commented 2026-11-09 23:03:37 +00:00 (Migrated from github.com)

Hi! I'm still experiencing crashes and freezes on Hookshot v2.0.11 using an Intel Arc 140V GPU.

System:

Laptop: Samsung Galaxy Book5 Pro

GPU: Intel Arc 140V

CPU: Intel Core Ultra 7

RAM: 32 GB

OS: Windows 11

Intel graphics driver: 32.0.101.9175

Hookshot: v2.0.11

Problem:

The game freezes or loses its picture during the Great Deku Tree introduction, sometimes after the Deku Tree tells Navi to find Link.

Without experimental settings, I have observed:

GPU 3D usage remaining at approximately 100%.

Hookshot CPU usage dropping from around 20–22% to 0.4–2%.

Memory usage reaching approximately 19 GB.

Background audio continuing temporarily before stopping.

GPU usage returning to normal immediately after closing Hookshot.

I also tested RX_VK_MESH_ARGS=1. With this setting, dialogue continued even after the picture disappeared, but GPU usage eventually dropped to 0%, Hookshot's memory usage increased to approximately 22 GB, and total system RAM usage reached 99%.

Relevant findings from the latest log:

vk: submit 72564 failed (-4)
vk: device lost: reading the checkpoints

The checkpoints include:

dispatch of program 499dbfd0721d2858, frame 10365

The log also shows an increasing number of retained capture buffers following the device-loss error.

I noticed the discussion in issue #17 about a possible infinite CasLoop on Intel Arc GPUs. Could this be related to the same underlying problem? Has the bounded CasLoop fix been incorporated into a branch or test build that I could try on my Arc 140V?

I'm happy to provide further logs or test a development build.

Thanks for your work!

hookshot_log.txt

Hi! I'm still experiencing crashes and freezes on Hookshot v2.0.11 using an Intel Arc 140V GPU. System: Laptop: Samsung Galaxy Book5 Pro GPU: Intel Arc 140V CPU: Intel Core Ultra 7 RAM: 32 GB OS: Windows 11 Intel graphics driver: 32.0.101.9175 Hookshot: v2.0.11 Problem: The game freezes or loses its picture during the Great Deku Tree introduction, sometimes after the Deku Tree tells Navi to find Link. Without experimental settings, I have observed: GPU 3D usage remaining at approximately 100%. Hookshot CPU usage dropping from around 20–22% to 0.4–2%. Memory usage reaching approximately 19 GB. Background audio continuing temporarily before stopping. GPU usage returning to normal immediately after closing Hookshot. I also tested RX_VK_MESH_ARGS=1. With this setting, dialogue continued even after the picture disappeared, but GPU usage eventually dropped to 0%, Hookshot's memory usage increased to approximately 22 GB, and total system RAM usage reached 99%. Relevant findings from the latest log: vk: submit 72564 failed (-4) vk: device lost: reading the checkpoints The checkpoints include: dispatch of program 499dbfd0721d2858, frame 10365 The log also shows an increasing number of retained capture buffers following the device-loss error. I noticed the discussion in issue #17 about a possible infinite CasLoop on Intel Arc GPUs. Could this be related to the same underlying problem? Has the bounded CasLoop fix been incorporated into a branch or test build that I could try on my Arc 140V? I'm happy to provide further logs or test a development build. Thanks for your work! [hookshot_log.txt](https://github.com/user-attachments/files/34603735/hookshot_log.txt)
epickatif commented 2026-11-09 23:17:29 +00:00 (Migrated from github.com)

I dream of the day this is playable on the new Intel G3 devices. Keep up the incredible work.

I dream of the day this is playable on the new Intel G3 devices. Keep up the incredible work.
hontalasbranden-prog commented 2026-11-09 23:23:24 +00:00 (Migrated from github.com)

intel arc 140v
version 32.0.101.950
60fps cut scene 30fps innscene with deku tree and navi crashes before entering links house. heavy stutter and broken images constant flashing
v2.0.11

hookshot_log.1.txt

intel arc 140v version 32.0.101.950 60fps cut scene 30fps innscene with deku tree and navi crashes before entering links house. heavy stutter and broken images constant flashing v2.0.11 [hookshot_log.1.txt](https://github.com/user-attachments/files/34604183/hookshot_log.1.txt)
TheArabicParty commented 2026-11-10 00:21:08 +00:00 (Migrated from github.com)

Adding an Intel Arc B390 report on Windows.

CPU: Intel Core Ultra X9 388H

GPU: Intel Arc B390 integrated graphics

RAM: 32 GB

OS: Windows 11 25H2, build 26200.9711

Intel graphics driver: 32.0.101.9175

Version covered by these logs: Hookshot v2.0.8, source 24534a6a

The opening movie plays normally. When the first real-time Deku Tree/Navi scene starts, the picture appears washed out and then stops updating while audio continues. Presentation counters stopped advancing, but background logging continued. This v2.0.8 log documents a hang without a recorded crash or device-lost error.

An earlier v2.0.6 test with the same driver recorded an access violation at igvk64.dll+0x79ddf3, followed by Vulkan submission failure -4. That separate log is included for comparison.

The v2.0.8 test used Vulkan, 50% internal resolution, handheld mode, and frame generation off. Diagnostic memory limits were RX_VK_VRAM_MB=3072 and RX_VK_MIRROR_MB=512; shader preparation and checkpoints were enabled.

hookshot-v2.0.8-hang.txt
previous-v2.0.6-driver-crash.txt
REPORT.txt

Update, same issue on 2.0.11
2.0.11 got a bit further in. Newest log Uploaded below

Hookshot-v2.0.11-B390-test-log.txt

Adding an Intel Arc B390 report on Windows. CPU: Intel Core Ultra X9 388H GPU: Intel Arc B390 integrated graphics RAM: 32 GB OS: Windows 11 25H2, build 26200.9711 Intel graphics driver: 32.0.101.9175 Version covered by these logs: Hookshot v2.0.8, source 24534a6a The opening movie plays normally. When the first real-time Deku Tree/Navi scene starts, the picture appears washed out and then stops updating while audio continues. Presentation counters stopped advancing, but background logging continued. This v2.0.8 log documents a hang without a recorded crash or device-lost error. An earlier v2.0.6 test with the same driver recorded an access violation at igvk64.dll+0x79ddf3, followed by Vulkan submission failure -4. That separate log is included for comparison. The v2.0.8 test used Vulkan, 50% internal resolution, handheld mode, and frame generation off. Diagnostic memory limits were RX_VK_VRAM_MB=3072 and RX_VK_MIRROR_MB=512; shader preparation and checkpoints were enabled. [hookshot-v2.0.8-hang.txt](https://github.com/user-attachments/files/34605632/hookshot-v2.0.8-hang.txt) [previous-v2.0.6-driver-crash.txt](https://github.com/user-attachments/files/34605630/previous-v2.0.6-driver-crash.txt) [REPORT.txt](https://github.com/user-attachments/files/34605631/REPORT.txt) *Update, same issue on 2.0.11* *2.0.11 got a bit further in. Newest log Uploaded below* [Hookshot-v2.0.11-B390-test-log.txt](https://github.com/user-attachments/files/34613086/Hookshot-v2.0.11-B390-test-log.txt)
nogrannydont commented 2026-11-10 01:34:18 +00:00 (Migrated from github.com)

intel arc b580 - driver ver. 32.0.101.9150 - Same issue black screen during Deku Tree .
Hookshot 2.0.11

Device Lost: reading checkpoints

hookshot_log.txt

intel arc b580 - driver ver. 32.0.101.9150 - Same issue black screen during Deku Tree . Hookshot 2.0.11 Device Lost: reading checkpoints [hookshot_log.txt](https://github.com/user-attachments/files/34607315/hookshot_log.txt)
Henrique143 commented 2026-11-10 02:43:17 +00:00 (Migrated from github.com)

v2.0.11: still black screen with sound. Arc B580, driver 101.9175, Windows 11 25H2, i5-9600KF (6C/6T). Log attached.

Same device lost as v2.0.2 and v2.0.4 (5 of my 6 runs):

vk: submit 74513 failed (-4)
vk:   stage 0x1: marker 223150 (dispatch of program 4b03b3040d7cf076, frame 10644)
vk:   stage 0x2000: marker 223149 (dispatch of program ab7699ee7f536755, frame 10644)

New in this log: a 98.7 s submit right after the first fd029a317a9a4b94 network dispatch, just before the device lost:

vk: slow submit: 98734.1 ms: draw 98676.4 ...

First-start preparation took ~2 h (38809 programs, 151 failed).

Happy to test a build or switch for 4b03b3040d7cf076.

hookshot_log_v2.0.11.txt

**v2.0.11: still black screen with sound.** Arc B580, driver 101.9175, Windows 11 25H2, i5-9600KF (6C/6T). Log attached. Same device lost as v2.0.2 and v2.0.4 (5 of my 6 runs): ``` vk: submit 74513 failed (-4) vk: stage 0x1: marker 223150 (dispatch of program 4b03b3040d7cf076, frame 10644) vk: stage 0x2000: marker 223149 (dispatch of program ab7699ee7f536755, frame 10644) ``` New in this log: a 98.7 s submit right after the first `fd029a317a9a4b94` network dispatch, just before the device lost: ``` vk: slow submit: 98734.1 ms: draw 98676.4 ... ``` First-start preparation took ~2 h (38809 programs, 151 failed). Happy to test a build or switch for `4b03b3040d7cf076`. [hookshot_log_v2.0.11.txt](https://github.com/user-attachments/files/34609011/hookshot_log_v2.0.11.txt)
Maine774 commented 2026-11-10 02:51:23 +00:00 (Migrated from github.com)

I was able to get into gameplay(?) finally for the first time, but obviously performance is horrible. I was able to get to the part where Navi leaves to get Link, but the game crashed.

hookshot_log.txt

Image
I was able to get into gameplay(?) finally for the first time, but obviously performance is horrible. I was able to get to the part where Navi leaves to get Link, but the game crashed. [hookshot_log.txt](https://github.com/user-attachments/files/34609216/hookshot_log.txt) <img width="1919" height="1199" alt="Image" src="https://github.com/user-attachments/assets/921b6438-3d67-4870-a3cb-8014b3e0df76" />
hontalasbranden-prog commented 2026-11-10 04:33:53 +00:00 (Migrated from github.com)

able to load save but crash leaving links house heavy texture problems arc 140v 2.0.11

hookshot_log.txt

able to load save but crash leaving links house heavy texture problems arc 140v 2.0.11 [hookshot_log.txt](https://github.com/user-attachments/files/34611898/hookshot_log.txt)
MrDoeki commented 2026-11-10 11:48:51 +00:00 (Migrated from github.com)

Tested on version 2.0.12

GPU: Arc B580 Cpu: Ryzen 5600: Driver gpu: 32.0.101.9150

Game now crashes after the intro, in earlier versions the cutscene after the intro had a blackscreen, but the audio was still running.

hookshot_log.txt

Tested on version 2.0.12 GPU: Arc B580 Cpu: Ryzen 5600: Driver gpu: 32.0.101.9150 Game now crashes after the intro, in earlier versions the cutscene after the intro had a blackscreen, but the audio was still running. [hookshot_log.txt](https://github.com/user-attachments/files/34627280/hookshot_log.txt)
Flyingboots11 commented 2026-11-10 11:53:33 +00:00 (Migrated from github.com)

Intel B580 here also, after latest update it doesn't just freeze it completely shuts down after intro cutscene. :(

Intel B580 here also, after latest update it doesn't just freeze it completely shuts down after intro cutscene. :(
Ketchup1919 commented 2026-11-10 12:27:50 +00:00 (Migrated from github.com)

Quick update: I tested Hookshot v2.0.12 on my Intel Arc 140V, but unfortunately the problem persists.

This time, Hookshot closed completely after the introductory sequence instead of remaining frozen.

I checked Windows Reliability Monitor and found two LiveKernelEvent 141 hardware errors, coinciding with the crashes.

The latest Hookshot log does not appear to contain the previous VK_ERROR_DEVICE_LOST message, but Windows is reporting GPU-related timeouts.

This suggests that the issue may still involve the Vulkan renderer or Intel graphics driver.

I've attached the latest log from v2.0.12. Hopefully, this helps narrow down the cause.

hookshot_log.txt

Quick update: I tested Hookshot v2.0.12 on my Intel Arc 140V, but unfortunately the problem persists. This time, Hookshot closed completely after the introductory sequence instead of remaining frozen. I checked Windows Reliability Monitor and found two LiveKernelEvent 141 hardware errors, coinciding with the crashes. The latest Hookshot log does not appear to contain the previous VK_ERROR_DEVICE_LOST message, but Windows is reporting GPU-related timeouts. This suggests that the issue may still involve the Vulkan renderer or Intel graphics driver. I've attached the latest log from v2.0.12. Hopefully, this helps narrow down the cause. [hookshot_log.txt](https://github.com/user-attachments/files/34629028/hookshot_log.txt)
AntoFas677 commented 2026-11-10 14:09:23 +00:00 (Migrated from github.com)

Intel Arc B580 – VK_ERROR_DEVICE_LOST on Hookshot v2.0.13

System:

  • GPU: Intel Arc B580 32.0.101.9175
  • CPU: AMD Ryzen 7 5700X3D
  • RAM: 32 GB DDR4
  • Renderer: Vulkan 1.4
  • Driver ID reported by Hookshot: 0x1963d7
  • Hookshot: v2.0.13 (source 54e42459)

I tested two runs. The first one ended with a manual restart from the in-game menu, after 4,370 frames.

The second run reached 9,849 frames at 60 FPS, but eventually froze and crashed around frame 9,890.

The final log reports:

vk: slow submit: 82558.4 ms: draw 82512.1
vi: 9890 frames presented (0.4 fps over the last 94.1 s)
vk: submit 69234 failed (-4)
vk: FATAL: the GPU device was lost (VK_ERROR_DEVICE_LOST)

The device-loss checkpoint also references:

dispatch of program 4b03b3040d7cf076
frame 9890

I noticed this is the same compute program mentioned in another Intel Arc report as a possible intermittent device-loss point.

I've attached the complete hookshot_log.txt for investigation.

hookshot_log.txt

**Intel Arc B580 – VK_ERROR_DEVICE_LOST on Hookshot v2.0.13** **System:** - GPU: Intel Arc B580 32.0.101.9175 - CPU: AMD Ryzen 7 5700X3D - RAM: 32 GB DDR4 - Renderer: Vulkan 1.4 - Driver ID reported by Hookshot: `0x1963d7` - Hookshot: v2.0.13 (source `54e42459`) I tested two runs. The first one ended with a manual restart from the in-game menu, after 4,370 frames. The second run reached 9,849 frames at 60 FPS, but eventually froze and crashed around frame 9,890. The final log reports: ```text vk: slow submit: 82558.4 ms: draw 82512.1 vi: 9890 frames presented (0.4 fps over the last 94.1 s) vk: submit 69234 failed (-4) vk: FATAL: the GPU device was lost (VK_ERROR_DEVICE_LOST) ``` The device-loss checkpoint also references: ```text dispatch of program 4b03b3040d7cf076 frame 9890 ``` I noticed this is the same compute program mentioned in another Intel Arc report as a possible intermittent device-loss point. I've attached the complete `hookshot_log.txt` for investigation. [hookshot_log.txt](https://github.com/user-attachments/files/34633400/hookshot_log.txt)
juandante14 commented 2026-11-10 17:57:05 +00:00 (Migrated from github.com)
Image

v2.0.14 still has texture issues, but shader loading speed has improved; it no longer takes a long time to enter the game, and it doesn't get stuck on black screens.

The texture issue, however, persists.

GPU: Intel Arc A750 8GB VRAM
CPU: AMD Ryzen 7 5700X
RAM: 32 GB DDR4

Very good work so far.

<img width="1919" height="1079" alt="Image" src="https://github.com/user-attachments/assets/23f3ecae-d580-4079-95fc-0b9ab5e42255" /> v2.0.14 still has texture issues, but shader loading speed has improved; it no longer takes a long time to enter the game, and it doesn't get stuck on black screens. The texture issue, however, persists. GPU: Intel Arc A750 8GB VRAM CPU: AMD Ryzen 7 5700X RAM: 32 GB DDR4 Very good work so far.
VictorNagibator commented 2026-11-10 19:08:24 +00:00 (Migrated from github.com)

Update v2.0.14
Now after first cutscene it crashes, no black screen - just crash

[System]
CPU: Ryzen 5600
GPU: Intel Arc B580
RAM: 16 GB
OS: Windows 11

hookshot_log.txt

Update v2.0.14 Now after first cutscene it crashes, no black screen - just crash [System] CPU: Ryzen 5600 GPU: Intel Arc B580 RAM: 16 GB OS: Windows 11 [hookshot_log.txt](https://github.com/user-attachments/files/34646454/hookshot_log.txt)
TheArabicParty commented 2026-11-10 19:38:02 +00:00 (Migrated from github.com)

pdate: tested v2.0.14 (source 3a9998c3) on my Intel Arc B390 / Core Ultra X9 388H, 32 GB RAM, Windows 11, Intel driver 32.0.101.9175.

The opening movie ran at approximately 60 FPS. The real-time Deku Tree scene then appeared with severe white haze/overexposure, briefly reporting 51–55 FPS before the picture froze.

The last logged presentation counter was 12000 (51.2 FPS). It stayed unchanged for over 30 seconds while audio-thread and controller logging continued. The FPS overlay also froze. No CRASH, FATAL, or device lost message appeared in the collected log.

This reproduces the render hang seen on v2.0.11. Full v2.0.14 test log attached.

Hookshot-v2.0.14-B390-test-log.txt

pdate: tested v2.0.14 (source 3a9998c3) on my Intel Arc B390 / Core Ultra X9 388H, 32 GB RAM, Windows 11, Intel driver 32.0.101.9175. The opening movie ran at approximately 60 FPS. The real-time Deku Tree scene then appeared with severe white haze/overexposure, briefly reporting 51–55 FPS before the picture froze. The last logged presentation counter was 12000 (51.2 FPS). It stayed unchanged for over 30 seconds while audio-thread and controller logging continued. The FPS overlay also froze. No CRASH, FATAL, or device lost message appeared in the collected log. This reproduces the render hang seen on v2.0.11. Full v2.0.14 test log attached. [Hookshot-v2.0.14-B390-test-log.txt](https://github.com/user-attachments/files/34648120/Hookshot-v2.0.14-B390-test-log.txt)
PierreAlex commented 2026-11-10 20:09:04 +00:00 (Migrated from github.com)

Tested on version 2.0.12

GPU: Arc B580 Cpu: Ryzen 5600: Driver gpu: 32.0.101.9150

Game now crashes after the intro, in earlier versions the cutscene after the intro had a blackscreen, but the audio was still running.

hookshot_log.txt

Exact same problem. No more black screen, just crash.

hookshot_log.txt

> Tested on version 2.0.12 > > GPU: Arc B580 Cpu: Ryzen 5600: Driver gpu: 32.0.101.9150 > > Game now crashes after the intro, in earlier versions the cutscene after the intro had a blackscreen, but the audio was still running. > > [hookshot_log.txt](https://github.com/user-attachments/files/34627280/hookshot_log.txt) Exact same problem. No more black screen, just crash. [hookshot_log.txt](https://github.com/user-attachments/files/34649275/hookshot_log.txt)
antonyHsueh commented 2026-11-10 20:17:14 +00:00 (Migrated from github.com)

Tested v2.0.14 (source 3a9998c3) on an Intel Arc A770 16 GB with 32 GB of RAM, Windows 11 build 26200.9711, and Intel graphics driver 32.0.101.8801.

The opening sequence initially ran at approximately 60 FPS in 2560×1440 fullscreen mode. The screen then went black, and Windows reset the GPU.

hookshot_log.txt

Tested v2.0.14 (source 3a9998c3) on an Intel Arc A770 16 GB with 32 GB of RAM, Windows 11 build 26200.9711, and Intel graphics driver 32.0.101.8801. The opening sequence initially ran at approximately 60 FPS in 2560×1440 fullscreen mode. The screen then went black, and Windows reset the GPU. [hookshot_log.txt](https://github.com/user-attachments/files/34649646/hookshot_log.txt)
jcberry13 commented 2026-11-10 21:04:25 +00:00 (Migrated from github.com)

pc:
*Arc B570 10gb Latest driver
*core i5 10400f
*24gb ddr4 3200
*installed on SSD
Just tried and it crashes on both loading a save, black screen and crash, and starting a new game the first cutscene looks perfect then it crashes and log shows this error (VK_ERROR_DEVICE_LOST), also on main menu there are some graphic issues like some flicker and some lines appearing for a secon across the screen...
hookshot_log.txt

pc: *Arc B570 10gb Latest driver *core i5 10400f *24gb ddr4 3200 *installed on SSD Just tried and it crashes on both loading a save, black screen and crash, and starting a new game the first cutscene looks perfect then it crashes and log shows this error (VK_ERROR_DEVICE_LOST), also on main menu there are some graphic issues like some flicker and some lines appearing for a secon across the screen... [hookshot_log.txt](https://github.com/user-attachments/files/34651107/hookshot_log.txt)
bmiculka commented 2026-11-11 05:16:49 +00:00 (Migrated from github.com)

v2.1.0 Update.

i5-13600
32GB system Ram
16 GB Arc Pro B50 - Mesa 26.2.5 - Mint 22.3

Game compiled. Shader build during compile did not complete. Started on first run. Shaders completed - though looks like some had errors. Pipelines started loading and the game crashed out of memory multiple times. Eventually it's the same story. Intro scene loads and then black. Though I was able to make it to where I see the HUD and can control Link. I could hear him responding to controls.

There has got to be something about these pipelines not finishing methinks.

hookshot_log.1.txt
hookshot_log.2.txt
hookshot_log.3.txt
hookshot_log.4.txt
hookshot_log.txt

v2.1.0 Update. i5-13600 32GB system Ram 16 GB Arc Pro B50 - Mesa 26.2.5 - Mint 22.3 Game compiled. Shader build during compile did not complete. Started on first run. Shaders completed - though looks like some had errors. Pipelines started loading and the game crashed out of memory multiple times. Eventually it's the same story. Intro scene loads and then black. Though I was able to make it to where I see the HUD and can control Link. I could hear him responding to controls. There has got to be something about these pipelines not finishing methinks. [hookshot_log.1.txt](https://github.com/user-attachments/files/34671499/hookshot_log.1.txt) [hookshot_log.2.txt](https://github.com/user-attachments/files/34671503/hookshot_log.2.txt) [hookshot_log.3.txt](https://github.com/user-attachments/files/34671502/hookshot_log.3.txt) [hookshot_log.4.txt](https://github.com/user-attachments/files/34671501/hookshot_log.4.txt) [hookshot_log.txt](https://github.com/user-attachments/files/34671500/hookshot_log.txt)
limaksansama commented 2026-11-11 06:48:24 +00:00 (Migrated from github.com)

Issue for #17

Title: Arc A770 (DG2) / Mesa 26.2.6: v2.1.0 freezes at the save load inside Mesa's brw_reg_alloc, and 30 networks are still left out on this card


Environment

Hookshot v2.1.0 — host source e561602e, renderer hookshot_vk.so source 633eb435
Game Ocarina of Time (Switch 2), built from its own dumps, romfs complete (186 583 files), language fr
GPU Intel Arc A770 Graphics (DG2) — Vulkan 1.4, driver 0x6802006, type 2
Driver Mesa 26.2.6, apiVersion 1.4.354, backend Xe KMD (MESA: warning: Support for this platform is experimental with Xe KMD)
OS Bazzite 45.20261105 (Kinoite), kernel 7.2.15-ogc1.1.fc45.x86_64
CPU / RAM Intel Core RPL-S, 64 GB RAM / 16 GB swap
Also present iGPU Intel(R) Graphics (RPL-S) (device 0) and llvmpipe (device 2); the game picks device 1, the A770

The GPU reports mesh shaders, host memory import and pipeline libraries, but no 16x8x16 f16 cooperative matrices:

vk: matrix multiplies in software (the cooperative matrices lack 16x8x16 f16)

Symptom

Two things, on a fresh v2.1.0 update (the builder replaced bin/hookshot and bin/hookshot_vk.so, then ran --prepare-shaders by itself):

  1. The game freezes at the save load: it renders ~18 s (1080 frames at 60 fps), then no frame is presented again — not for 5 minutes, in 5 different configurations. The process stays at 3–18 threads busy, 15–16 GB RSS, no swap, no kernel message.
  2. Thirty compute networks are still refused with a network left out on this card — 30 distinct keys in this run (v2.0.14 refused ten of the same family). That is the "black picture under the HUD" the source comments describe, and it is why the picture stays empty in v2.0.14 too.

What the frozen process is doing (gdb)

gdb -p <pid> -batch -ex "thread apply all bt" while it is frozen — the render thread and two worker threads are 100 % CPU inside the Intel Vulkan driver, not inside Hookshot:

Thread 114 "Presentation Th":
#0  brw_reg_alloc::setup_live_interference(unsigned int, brw_range)
#1  brw_reg_alloc::build_interference_graph(bool)
#2  brw_reg_alloc::assign_regs(bool, bool)
#3  brw_assign_regs(brw_shader&, bool, bool)
#4  brw_allocate_registers(brw_shader&, bool)
    from /usr/lib64/libvulkan_intel.so

Other runs, same place:
#2  brw_reg_alloc::assign_regs(bool, bool) [clone .part.0]
#2  brw_reg_alloc::spill_reg
#2  brw_reg_alloc::setup_inst_interference
    brw_optimize / brw_opt_register_coalesce

So the freeze is Mesa's Intel register allocator (spilling) on one very large shader. Hookshot itself logs 64 programs "lowered with extended registers" of about 1132–1136 REGMAP words; on a card without f16 cooperative matrices, the translated networks are exactly the kind of shader that puts the allocator into this state.

No GPU reset at all — journalctl -k has 0 xe reset/hang/timeout lines over the session, and there is no device-lost.

Log excerpts

vk: program cache .../hookshot_programs.bin: 0 program keys kept, 0 KB
vk: precompile: compute programs: those play dispatched (12 keys listed as dispatched in play)
vk: precompile: 0 programs, 0 graphics and 0 compute pipelines, 0 mesh fallback programs in 34 ms
vk: prepared 38809 shader programs in play (46510 new, 80 failed, 0 compute pipelines;
    9681 compute variants, 773 programs with texture variants, 59 at the limit) in 76.1 s

The in-play preparation does finish (76 s). Then the last frame line, repeated for as long as the process lives:

vk: 1080 presents, 60.0 fps; a frame: 5 draws (0 skipped), 0 dispatches, 2 passes, 1 barriers,
    6 submits, 0.0 uploads (0.0 MB), 1 ub updates (1 overlays), GPU-bound wait 0.0 ms;
    38 pipelines (697 ms), 841 programs (10 failed), 138 textures (138 MB)

Two of the refused keys, with their dispatch shapes:

vk: rejected compute e409733c45530d75 (control 0x1075e24768) grid 900x1x1:
    img 0x10755b4000 1920x1080x1 fmt 0x25; tex 0x100c922a00 4x4x1 fmt 0x25; tex 0x10755b4000 1920x1080x1 ...
vk: rejected compute 8306877b7dc8784b (control 0x1075e260d8) grid 720x1x1: ...

All 30 distinct keys of this run (grep 'not translated' hookshot_log.txt):

095fca6f01e3030a  0b60fb059b760c01  12252b25d6d178ca  14140d4c1dbe754f  2068f7225228453f
32d694adc29848e8  3f6f2b1d671badcd  47d312044af15f1e  4c2c129fe950b041  5a4d7378db437fed
5ce47f186255e905  69118299d3895eaf  6b6fa97850dd093f  8306877b7dc8784b  8b73eaee02efc5e4
8eea7f897f16e16b  9508ea17e67c2294  a41c78faa56ccf7d  a584b3ac9aafc26c  a7533cc90250f994
c317eca567330b98  cedd917cded8ac67  cf6414e238ee5fca  da26e67666cfb573  da3eda913c2ee00c
da5c29ada3ade41b  de3c10dba4ff39d3  e409733c45530d75  e959b9b1451491c0  ec6e8682fb5fdeae

The ten v2.0.14 refused, all of which are in the list above: e409733c45530d75, 8306877b7dc8784b,
de3c10dba4ff39d3, a41c78faa56ccf7d, e959b9b1451491c0, 5a4d7378db437fed, 8b73eaee02efc5e4,
14140d4c1dbe754f, 4c2c129fe950b041, 3f6f2b1d671badcd.

Note that the dispatch samples its own output image (img and the third tex are the same address 0x10755b4000), so the linear fallback (upscale_stub, "out_area > in_area" and not also an input) cannot take it — the same situation as in v2.0.14, where the result was 60 fps, 2 draws a frame, and a black picture under the HUD.

Configurations tested (all freeze at 1080 presents)

hookshot_env.txt / options Result
RX_VK_MESH_ARGS=1 + RX_VK_PREPARE_BACKGROUND=1 freeze at 1080, 3 threads at 100 % in brw_reg_alloc
+ RX_VK_NETWORK_KEEP=all in hookshot_env.txt freeze at 1080, and the same 30 not translated lines
+ RX_VK_NETWORK_KEEP=all exported by the shell instead freeze at 1080, same 30 lines
+ INTEL_DEBUG=noopt (Mesa without its optimisation passes) freeze at 1080, now in brw_reg_alloc::spill_reg
frame_generation=0, hdr=0, dlss=0, resolution=0 freeze at 1080

Duration tested each time: 2 to 5 minutes with no additional present, 15–16 GB RSS, no swap growth, no kernel event.

Question about RX_VK_NETWORK_KEEP=all

According to vk_shader.cpp (v2.1.0, around line 1599), keep == "all" sets kept_one = true, so the throw at line 1610 should not happen. But setting it, either in hookshot_env.txt or in the process environment, still produces all 30 a network left out on this card lines, key for key. hookshot_env.txt logs no left out (only the game's switches…) line for it, so the file is read. Is there another path that refuses these compute networks, or is RX_VK_NETWORK_KEEP read before the file is applied?

Unrelated, in case it matters

Audio: vk/host logs audio: output on the default device (0 devices), stereo even though PipeWire has several sinks; the game's stream does reach the default sink (a Bluetooth headset), so this is only reported in case the "0 devices" enumeration is wrong.

Attachments

  • hookshot_log.txt (full log of the run above)
  • gdb backtrace (thread apply all bt) taken while frozen

hookshot_log.txt

# Issue for https://git.magicbean.dev/MagicBeanDev/Hookshot/issues/17 **Title:** Arc A770 (DG2) / Mesa 26.2.6: v2.1.0 freezes at the save load inside Mesa's `brw_reg_alloc`, and 30 networks are still left out on this card --- ## Environment | | | |---|---| | Hookshot | **v2.1.0** — host `source e561602e`, renderer `hookshot_vk.so` `source 633eb435` | | Game | Ocarina of Time (Switch 2), built from its own dumps, `romfs` complete (186 583 files), language `fr` | | GPU | **Intel Arc A770 Graphics (DG2)** — `Vulkan 1.4, driver 0x6802006, type 2` | | Driver | **Mesa 26.2.6**, `apiVersion 1.4.354`, backend **Xe KMD** (`MESA: warning: Support for this platform is experimental with Xe KMD`) | | OS | Bazzite 45.20261105 (Kinoite), kernel `7.2.15-ogc1.1.fc45.x86_64` | | CPU / RAM | Intel Core RPL-S, 64 GB RAM / 16 GB swap | | Also present | iGPU `Intel(R) Graphics (RPL-S)` (device 0) and `llvmpipe` (device 2); the game picks device 1, the A770 | The GPU reports mesh shaders, host memory import and pipeline libraries, but **no 16x8x16 f16 cooperative matrices**: ``` vk: matrix multiplies in software (the cooperative matrices lack 16x8x16 f16) ``` ## Symptom Two things, on a fresh v2.1.0 update (the builder replaced `bin/hookshot` and `bin/hookshot_vk.so`, then ran `--prepare-shaders` by itself): 1. **The game freezes** at the save load: it renders ~18 s (1080 frames at 60 fps), then no frame is presented again — not for 5 minutes, in 5 different configurations. The process stays at 3–18 threads busy, 15–16 GB RSS, no swap, no kernel message. 2. **Thirty compute networks are still refused** with `a network left out on this card` — 30 distinct keys in this run (v2.0.14 refused ten of the same family). That is the "black picture under the HUD" the source comments describe, and it is why the picture stays empty in v2.0.14 too. ## What the frozen process is doing (gdb) `gdb -p <pid> -batch -ex "thread apply all bt"` while it is frozen — the render thread and two worker threads are 100 % CPU inside the Intel Vulkan driver, not inside Hookshot: ``` Thread 114 "Presentation Th": #0 brw_reg_alloc::setup_live_interference(unsigned int, brw_range) #1 brw_reg_alloc::build_interference_graph(bool) #2 brw_reg_alloc::assign_regs(bool, bool) #3 brw_assign_regs(brw_shader&, bool, bool) #4 brw_allocate_registers(brw_shader&, bool) from /usr/lib64/libvulkan_intel.so Other runs, same place: #2 brw_reg_alloc::assign_regs(bool, bool) [clone .part.0] #2 brw_reg_alloc::spill_reg #2 brw_reg_alloc::setup_inst_interference brw_optimize / brw_opt_register_coalesce ``` So the freeze is Mesa's Intel register allocator (spilling) on one very large shader. Hookshot itself logs 64 programs "lowered with extended registers" of about 1132–1136 REGMAP words; on a card without f16 cooperative matrices, the translated networks are exactly the kind of shader that puts the allocator into this state. No GPU reset at all — `journalctl -k` has 0 `xe` reset/hang/timeout lines over the session, and there is no device-lost. ## Log excerpts ``` vk: program cache .../hookshot_programs.bin: 0 program keys kept, 0 KB vk: precompile: compute programs: those play dispatched (12 keys listed as dispatched in play) vk: precompile: 0 programs, 0 graphics and 0 compute pipelines, 0 mesh fallback programs in 34 ms vk: prepared 38809 shader programs in play (46510 new, 80 failed, 0 compute pipelines; 9681 compute variants, 773 programs with texture variants, 59 at the limit) in 76.1 s ``` The in-play preparation does finish (76 s). Then the last frame line, repeated for as long as the process lives: ``` vk: 1080 presents, 60.0 fps; a frame: 5 draws (0 skipped), 0 dispatches, 2 passes, 1 barriers, 6 submits, 0.0 uploads (0.0 MB), 1 ub updates (1 overlays), GPU-bound wait 0.0 ms; 38 pipelines (697 ms), 841 programs (10 failed), 138 textures (138 MB) ``` Two of the refused keys, with their dispatch shapes: ``` vk: rejected compute e409733c45530d75 (control 0x1075e24768) grid 900x1x1: img 0x10755b4000 1920x1080x1 fmt 0x25; tex 0x100c922a00 4x4x1 fmt 0x25; tex 0x10755b4000 1920x1080x1 ... vk: rejected compute 8306877b7dc8784b (control 0x1075e260d8) grid 720x1x1: ... ``` All 30 distinct keys of this run (`grep 'not translated' hookshot_log.txt`): ``` 095fca6f01e3030a 0b60fb059b760c01 12252b25d6d178ca 14140d4c1dbe754f 2068f7225228453f 32d694adc29848e8 3f6f2b1d671badcd 47d312044af15f1e 4c2c129fe950b041 5a4d7378db437fed 5ce47f186255e905 69118299d3895eaf 6b6fa97850dd093f 8306877b7dc8784b 8b73eaee02efc5e4 8eea7f897f16e16b 9508ea17e67c2294 a41c78faa56ccf7d a584b3ac9aafc26c a7533cc90250f994 c317eca567330b98 cedd917cded8ac67 cf6414e238ee5fca da26e67666cfb573 da3eda913c2ee00c da5c29ada3ade41b de3c10dba4ff39d3 e409733c45530d75 e959b9b1451491c0 ec6e8682fb5fdeae ``` The ten v2.0.14 refused, all of which are in the list above: `e409733c45530d75`, `8306877b7dc8784b`, `de3c10dba4ff39d3`, `a41c78faa56ccf7d`, `e959b9b1451491c0`, `5a4d7378db437fed`, `8b73eaee02efc5e4`, `14140d4c1dbe754f`, `4c2c129fe950b041`, `3f6f2b1d671badcd`. Note that the dispatch samples its own output image (`img` and the third `tex` are the same address `0x10755b4000`), so the linear fallback (`upscale_stub`, "out_area > in_area" and not also an input) cannot take it — the same situation as in v2.0.14, where the result was 60 fps, 2 draws a frame, and a black picture under the HUD. ## Configurations tested (all freeze at 1080 presents) | `hookshot_env.txt` / options | Result | |---|---| | `RX_VK_MESH_ARGS=1` + `RX_VK_PREPARE_BACKGROUND=1` | freeze at 1080, 3 threads at 100 % in `brw_reg_alloc` | | + `RX_VK_NETWORK_KEEP=all` in `hookshot_env.txt` | freeze at 1080, and the same 30 `not translated` lines | | + `RX_VK_NETWORK_KEEP=all` exported by the shell instead | freeze at 1080, same 30 lines | | + `INTEL_DEBUG=noopt` (Mesa without its optimisation passes) | freeze at 1080, now in `brw_reg_alloc::spill_reg` | | `frame_generation=0`, `hdr=0`, `dlss=0`, `resolution=0` | freeze at 1080 | Duration tested each time: 2 to 5 minutes with no additional present, 15–16 GB RSS, no swap growth, no kernel event. ## Question about `RX_VK_NETWORK_KEEP=all` According to `vk_shader.cpp` (v2.1.0, around line 1599), `keep == "all"` sets `kept_one = true`, so the throw at line 1610 should not happen. But setting it, either in `hookshot_env.txt` or in the process environment, still produces all 30 `a network left out on this card` lines, key for key. `hookshot_env.txt` logs no `left out (only the game's switches…)` line for it, so the file is read. Is there another path that refuses these compute networks, or is `RX_VK_NETWORK_KEEP` read before the file is applied? ## Unrelated, in case it matters Audio: `vk`/host logs `audio: output on the default device (0 devices), stereo` even though PipeWire has several sinks; the game's stream does reach the default sink (a Bluetooth headset), so this is only reported in case the "0 devices" enumeration is wrong. ## Attachments - `hookshot_log.txt` (full log of the run above) - gdb backtrace (`thread apply all bt`) taken while frozen [hookshot_log.txt](https://github.com/user-attachments/files/34674316/hookshot_log.txt)
oscarloria commented 2026-11-11 07:50:50 +00:00 (Migrated from github.com)

Intel Arc B580 on Windows: the device is lost within seconds of the first real-time 3D frame, every time (8 of 8 runs with the default settings or one switch changed; a ninth run, with robust buffer access off, was lost earlier, on the title screen). The checkpoints say where the GPU stopped: usually at a compute dispatch, not always the same one, and at a draw once those were skipped. Skipping programs, leaving out mesh draws, leaving out the upscale networks, turning off the VRAM mirror and giving retired imports back did not help.

Setup

  • Built from source with llvm-mingw (nx2/make_game.py, no DLSS), not with HookshotBuilder, from main at commit f8efe05 (2026-11-11 00:56 UTC). That is 6 commits before the v2.1.0 tag; none of the 6 mentions Intel or the device loss (they touch the pipeline state list and AMD subgroup profiles). The log's first line is hookshot unknown (source 18be5ca9): git was not on PATH during that build, so the version string is unknown; 18be5ca9 is the source hash, not a commit.
  • A baseline run on v2.1.0 is still to do. If it changes anything I will say so here.
  • make_game.py --check: build e0f3ccfa.
  • GPU: Intel Arc B580 12 GB, driver 32.0.101.9150 (igvk64.dll / igc64.dll 32.0.101.9150; the log says driver 0x1963be, mesh shaders yes, host memory import yes, pipeline libraries yes).
  • CPU: AMD Ryzen 7 5800XT (8 cores, 16 threads). RAM: 32 GB. Windows 11 Pro build 26300.
  • No overlays or injectors. Process Lasso was running, with hookshot.exe excluded from ProBalance, at Normal priority and affinity 0-15.
  • In-game options left as they came; display an LG C4 (4K TV). The output resolution and HDR state were not checked.

Shader preparation

  • At build time, --prepare-shaders translated 38,809 programs in 1774 s (70 failed). Then the predicted-pipelines step printed nothing for 15 minutes and the builder stopped it ("the shaders were not prepared now"). There was no TDR at that point.
  • First start: the precompile built 20,487 pipelines (10,796 graphics, 9,691 compute) in 1250 s, without the 30 s stall fallback.
    • That is about 5-15 pipelines/s, in bursts; some stretches dropped below 1/s for a minute or two. The comment in PrepareBar gives about 142/s on an RTX 3090.
    • hookshot.exe kept 9-12.5 of 16 threads busy, with the GPU at about 10%. It is the driver's compiler on the CPU.
  • Later starts took 3.3-9 s, from the driver's cache.

The device loss
Each run: title screen and menus at a steady 60 fps for minutes, then start the game from the menu. Within seconds of the first real-time 3D frame:

  • vk: submit N failed (-4), then VK_ERROR_DEVICE_LOST, exit code 3.
  • Windows logged LiveKernelEvent 141 (a GPU hang recovered by a TDR) within about 4 seconds of the game closing, in all 9 runs, so the GPU stopped making progress; it does not look like a page fault.
  • VK_EXT_device_fault not available on this driver: RX_VK_FAULT_INFO (and the device-fault part of #1569) gives nothing here.
  • VK_NV_device_diagnostic_checkpoints is on by default on this driver, so every run has the checkpoints. A marker is set before its draw or dispatch. "Top" (stage 0x1) is the last marker the GPU reached and "bottom" (stage 0x2000) the last one with all earlier work finished, so the work in flight runs from the dispatch the bottom marker names to the one the top marker names.
hookshot_env.txt (one change at a time) Result Where the GPU stopped (checkpoints)
none (baseline) lost, frame 10749 top = bottom = dispatch of 499dbfd0721d2858; the next draw (a5da9f84b72d1471) never started
RX_VK_SKIP_PROG=499dbfd0721d2858 lost, frame 12668 top: dispatch of 4b03b3040d7cf076; bottom: ab7699ee7f536755 (the same pair as the earlier Windows reports here)
RX_VK_SKIP_PROG=4b03b3040d7cf076 lost, frame 13790 top: dispatch of 592079442d7129da; bottom: 435a0138b9013040
RX_VK_MIRROR=0 (log: VRAM mirror of buffer pools off) lost, frame 10395 top = bottom = dispatch of 499dbfd0721d2858 (as the baseline)
RX_VK_KEEP_RETIRED=0 + RX_VK_IMPORT_HIGH=85 (log: imports given back past 85% ... on) lost, frame 12750 top: dispatch of 592079442d7129da; bottom: 435a0138b9013040
RX_VK_ROBUST=0 (no robust buffer access on line in the log) worse: lost on the title screen, frame 1199, about 20 s after the start-up screen, before any 3D top: draw of b7914ae65792d0c8; bottom: draw of dc310f71db89f779
RX_VK_NO_MESH=1 (task and mesh draws left out) lost, frame 11724 top: dispatch of 4b03b3040d7cf076; bottom: ab7699ee7f536755
RX_VK_SKIP_PROG=499dbfd0721d2858,ab7699ee7f536755,4b03b3040d7cf076,435a0138b9013040,592079442d7129da lost, frame 11029 top = bottom = a draw of b8fec6ceae7175a9
RX_VK_FORCE_UPSCALE_STUB=2 (log: gameplay RGB9E5 upscale uses the typed fallback; no network fd029a317a9a4b94 dispatch) lost, frame 10251 top = bottom = dispatch of 499dbfd0721d2858 (as the baseline)

With RX_VK_ROBUST=0 the driver built the pipelines again (11,501 in 1354 s), and the loss came right after a frame's uploads took 803 ms: ASTC on the GPU 1 (0.8 MB, 802 ms). Windows logged LiveKernelEvent 141 for it too.

The baseline was run once; the skip runs also had RX_VK_FAULT_INFO=1, which does nothing on this driver.

For RX_VK_NO_MESH=1 and for the five skipped programs the log prints nothing that confirms the switch was applied (only the draw side of RX_VK_SKIP_PROG and RX_VK_SKIP_CODE logs a line). The other switches in the table are confirmed by a line in the log, quoted in the first column.

What it suggests (not proven)

  • The place moves between runs without skipping anything: three places, all compute dispatches (499dbfd0721d2858; ab7699ee7f536755 / 4b03b3040d7cf076; 435a0138b9013040 / 592079442d7129da). With all five skipped at once the device was still lost, at a draw. So it does not look like one broken shader, or one family of them.
  • In four runs (4b03b3040d7cf076 skipped, memory, no mesh, five skipped) the log has a capture's wait for write-back N timed out (done N-4) just before the loss: the GPU was already several submissions behind.
  • TdrDelay is not set on this PC, so Windows resets the GPU after its default 2 s. Whether these dispatches would finish with more time was not tested.
  • An observation, not a cause: this driver reports shaderBufferFloat32AtomicAdd = true (VK_EXT_shader_atomic_float), and the back end makes the float add of 499dbfd0721d2858 with a compare-exchange loop.
  • Three runs had slow submit: ... memory sync of 220-250 ms just before the loss (124 ms in the no-mirror run).
  • Not tested: a second baseline run, a longer TdrDelay, RX_VK_COMPUTE_SERIAL=0, RX_VK_NO_INVARIANT=1, RX_VK_EQ_BIAS=0, PR #1569 (git was not available), the Vulkan validation layer, and other drivers.

Logs attached, with paths replaced by <game>: log_base_1.txt, log_skip_499_1.txt, log_skip_4b03_1.txt, log_sin_mirror_1.txt, log_memoria_1.txt, log_sin_robust_1.txt, log_sin_mesh_1.txt, log_skip_cinco_1.txt, log_sin_redes_1.txt.

log_base_1.txt
log_memoria_1.txt
log_sin_mesh_1.txt
log_sin_mirror_1.txt
log_sin_redes_1.txt
log_sin_robust_1.txt
log_skip_4b03_1.txt
log_skip_499_1.txt
log_skip_cinco_1.txt

Intel Arc B580 on Windows: the device is lost within seconds of the first real-time 3D frame, every time (8 of 8 runs with the default settings or one switch changed; a ninth run, with robust buffer access off, was lost earlier, on the title screen). The checkpoints say where the GPU stopped: usually at a compute dispatch, not always the same one, and at a draw once those were skipped. Skipping programs, leaving out mesh draws, leaving out the upscale networks, turning off the VRAM mirror and giving retired imports back did not help. **Setup** - Built from source with llvm-mingw (`nx2/make_game.py`, no DLSS), not with HookshotBuilder, from `main` at commit `f8efe05` (2026-11-11 00:56 UTC). That is 6 commits before the v2.1.0 tag; none of the 6 mentions Intel or the device loss (they touch the pipeline state list and AMD subgroup profiles). The log's first line is `hookshot unknown (source 18be5ca9)`: git was not on PATH during that build, so the version string is `unknown`; `18be5ca9` is the source hash, not a commit. - A baseline run on v2.1.0 is still to do. If it changes anything I will say so here. - `make_game.py --check`: build e0f3ccfa. - GPU: Intel Arc B580 12 GB, driver **32.0.101.9150** (`igvk64.dll` / `igc64.dll` 32.0.101.9150; the log says `driver 0x1963be`, `mesh shaders yes, host memory import yes, pipeline libraries yes`). - CPU: AMD Ryzen 7 5800XT (8 cores, 16 threads). RAM: 32 GB. Windows 11 Pro build 26300. - No overlays or injectors. Process Lasso was running, with `hookshot.exe` excluded from ProBalance, at Normal priority and affinity 0-15. - In-game options left as they came; display an LG C4 (4K TV). The output resolution and HDR state were not checked. **Shader preparation** - At build time, `--prepare-shaders` translated 38,809 programs in 1774 s (70 failed). Then the predicted-pipelines step printed nothing for 15 minutes and the builder stopped it ("the shaders were not prepared now"). There was no TDR at that point. - First start: the precompile built 20,487 pipelines (10,796 graphics, 9,691 compute) in 1250 s, without the 30 s stall fallback. - That is about 5-15 pipelines/s, in bursts; some stretches dropped below 1/s for a minute or two. The comment in `PrepareBar` gives about 142/s on an RTX 3090. - `hookshot.exe` kept 9-12.5 of 16 threads busy, with the GPU at about 10%. It is the driver's compiler on the CPU. - Later starts took 3.3-9 s, from the driver's cache. **The device loss** Each run: title screen and menus at a steady 60 fps for minutes, then start the game from the menu. Within seconds of the first real-time 3D frame: - `vk: submit N failed (-4)`, then `VK_ERROR_DEVICE_LOST`, exit code 3. - Windows logged LiveKernelEvent **141** (a GPU hang recovered by a TDR) within about 4 seconds of the game closing, in all 9 runs, so the GPU stopped making progress; it does not look like a page fault. - `VK_EXT_device_fault not available` on this driver: `RX_VK_FAULT_INFO` (and the device-fault part of #1569) gives nothing here. - `VK_NV_device_diagnostic_checkpoints` is on by default on this driver, so every run has the checkpoints. A marker is set before its draw or dispatch. "Top" (stage 0x1) is the last marker the GPU reached and "bottom" (stage 0x2000) the last one with all earlier work finished, so the work in flight runs from the dispatch the bottom marker names to the one the top marker names. | hookshot_env.txt (one change at a time) | Result | Where the GPU stopped (checkpoints) | |---|---|---| | none (baseline) | lost, frame 10749 | top = bottom = dispatch of `499dbfd0721d2858`; the next draw (`a5da9f84b72d1471`) never started | | `RX_VK_SKIP_PROG=499dbfd0721d2858` | lost, frame 12668 | top: dispatch of **`4b03b3040d7cf076`**; bottom: `ab7699ee7f536755` (the same pair as the earlier Windows reports here) | | `RX_VK_SKIP_PROG=4b03b3040d7cf076` | lost, frame 13790 | top: dispatch of `592079442d7129da`; bottom: `435a0138b9013040` | | `RX_VK_MIRROR=0` (log: `VRAM mirror of buffer pools off`) | lost, frame 10395 | top = bottom = dispatch of `499dbfd0721d2858` (as the baseline) | | `RX_VK_KEEP_RETIRED=0` + `RX_VK_IMPORT_HIGH=85` (log: `imports given back past 85% ... on`) | lost, frame 12750 | top: dispatch of `592079442d7129da`; bottom: `435a0138b9013040` | | `RX_VK_ROBUST=0` (no `robust buffer access on` line in the log) | **worse**: lost on the title screen, frame 1199, about 20 s after the start-up screen, before any 3D | top: draw of `b7914ae65792d0c8`; bottom: draw of `dc310f71db89f779` | | `RX_VK_NO_MESH=1` (task and mesh draws left out) | lost, frame 11724 | top: dispatch of `4b03b3040d7cf076`; bottom: `ab7699ee7f536755` | | `RX_VK_SKIP_PROG=499dbfd0721d2858,ab7699ee7f536755,4b03b3040d7cf076,435a0138b9013040,592079442d7129da` | lost, frame 11029 | top = bottom = a **draw** of `b8fec6ceae7175a9` | | `RX_VK_FORCE_UPSCALE_STUB=2` (log: `gameplay RGB9E5 upscale uses the typed fallback`; no `network fd029a317a9a4b94` dispatch) | lost, frame 10251 | top = bottom = dispatch of `499dbfd0721d2858` (as the baseline) | With `RX_VK_ROBUST=0` the driver built the pipelines again (11,501 in 1354 s), and the loss came right after `a frame's uploads took 803 ms: ASTC on the GPU 1 (0.8 MB, 802 ms)`. Windows logged LiveKernelEvent 141 for it too. The baseline was run once; the skip runs also had `RX_VK_FAULT_INFO=1`, which does nothing on this driver. For `RX_VK_NO_MESH=1` and for the five skipped programs the log prints nothing that confirms the switch was applied (only the draw side of `RX_VK_SKIP_PROG` and `RX_VK_SKIP_CODE` logs a line). The other switches in the table are confirmed by a line in the log, quoted in the first column. **What it suggests (not proven)** - The place moves between runs without skipping anything: three places, all compute dispatches (`499dbfd0721d2858`; `ab7699ee7f536755` / `4b03b3040d7cf076`; `435a0138b9013040` / `592079442d7129da`). With all five skipped at once the device was still lost, at a draw. So it does not look like one broken shader, or one family of them. - In four runs (`4b03b3040d7cf076` skipped, memory, no mesh, five skipped) the log has `a capture's wait for write-back N timed out (done N-4)` just before the loss: the GPU was already several submissions behind. - `TdrDelay` is not set on this PC, so Windows resets the GPU after its default 2 s. Whether these dispatches would finish with more time was not tested. - An observation, not a cause: this driver reports `shaderBufferFloat32AtomicAdd = true` (`VK_EXT_shader_atomic_float`), and the back end makes the float add of `499dbfd0721d2858` with a compare-exchange loop. - Three runs had `slow submit: ... memory sync` of 220-250 ms just before the loss (124 ms in the no-mirror run). - Not tested: a second baseline run, a longer `TdrDelay`, `RX_VK_COMPUTE_SERIAL=0`, `RX_VK_NO_INVARIANT=1`, `RX_VK_EQ_BIAS=0`, PR #1569 (git was not available), the Vulkan validation layer, and other drivers. Logs attached, with paths replaced by `<game>`: `log_base_1.txt`, `log_skip_499_1.txt`, `log_skip_4b03_1.txt`, `log_sin_mirror_1.txt`, `log_memoria_1.txt`, `log_sin_robust_1.txt`, `log_sin_mesh_1.txt`, `log_skip_cinco_1.txt`, `log_sin_redes_1.txt`. [log_base_1.txt](https://github.com/user-attachments/files/34676631/log_base_1.txt) [log_memoria_1.txt](https://github.com/user-attachments/files/34676632/log_memoria_1.txt) [log_sin_mesh_1.txt](https://github.com/user-attachments/files/34676636/log_sin_mesh_1.txt) [log_sin_mirror_1.txt](https://github.com/user-attachments/files/34676638/log_sin_mirror_1.txt) [log_sin_redes_1.txt](https://github.com/user-attachments/files/34676635/log_sin_redes_1.txt) [log_sin_robust_1.txt](https://github.com/user-attachments/files/34676633/log_sin_robust_1.txt) [log_skip_4b03_1.txt](https://github.com/user-attachments/files/34676639/log_skip_4b03_1.txt) [log_skip_499_1.txt](https://github.com/user-attachments/files/34676634/log_skip_499_1.txt) [log_skip_cinco_1.txt](https://github.com/user-attachments/files/34676637/log_skip_cinco_1.txt)
ploob-boop commented 2026-11-11 09:43:14 +00:00 (Migrated from github.com)

Fix for Intel Arc (Lunar Lake, Arc 140V): igvk64.dll crash + device lost at Kokiri

Tested on MSI Claw 8 AI+ (Core Ultra 7 258V, Arc 140V, driver 32.0.101.9175), v2.0.14. With the two changes below, 10 and 15
min runs in Kokiri gameplay, ~31 fps, no crash, no device lost.

1. Crash in igvk64.dll+0x79ddf3 (this issue)

Cause: pipeline_cache_save calls vkGetPipelineCacheData on its background thread while other threads are still creating
pipelines into the same cache. Intel's driver races on this and reads a null pointer. That's legal in Vulkan, so it's a driver
bug, but it's easy to avoid.

Workaround in gpu/vk/vk_draw.cpp, pipeline_cache_save, right after the pcache_built_since_save < 32 early return:

// Intel: vkGetPipelineCacheData racing pipeline creation crashes igvk64.dll (#999); save only on forced saves (exit, prepare
end, menu)
if (!force && dev.props.vendorID == 0x8086 && !getenv("RX_VK_PCACHE_BG_SAVE")) return;

2. VK_ERROR_DEVICE_LOST (TDR) when Kokiri gameplay starts

Cause: the Eden SPIR-V backend always emits StorageAtomicAddF32 as a compare-exchange loop (f32_add_cas). In one compute
shader, thousands of invocations hit the same word, and on Arc the loop runs past the Windows GPU timeout. Arc supports
VK_EXT_shader_atomic_float with shaderBufferFloat32AtomicAdd, so emit a native OpAtomicFAddEXT instead:

  • vk_internal.h: add float_atomic_add = false, to the device feature flags.

  • vk_main.cpp:

    • optional(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME, dev.float_atomic_add);
    • chain VkPhysicalDeviceShaderAtomicFloatFeaturesEXT into the feature query;
    • then dev.float_atomic_add &= fadd.shaderBufferFloat32AtomicAdd;. I also added an env switch, RX_VK_NO_FLOAT_ATOMIC, to
      turn this back off.
      Cause: the Eden SPIR-V backend always emits StorageAtomicAddF32 as a compare-exchange loop (f32_add_cas). In one compute shader, thousands of invocations hit the same word, and on Arc the loop runs past the Windows GPU
      timeout. Arc supports VK_EXT_shader_atomic_float with shaderBufferFloat32AtomicAdd, so emit a native OpAtomicFAddEXT instead:
  • vk_internal.h: add float_atomic_add = false, to the device feature flags.

  • vk_main.cpp:

    • optional(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME, dev.float_atomic_add);
    • chain VkPhysicalDeviceShaderAtomicFloatFeaturesEXT into the feature query;
    • then dev.float_atomic_add &= fadd.shaderBufferFloat32AtomicAdd;. I also added an env switch, RX_VK_NO_FLOAT_ATOMIC, to turn this back off.
  • profile.h: add bool support_float32_atomic_add{};. In vk_shader.cpp, set it from dev.float_atomic_add.

  • collect_shader_info_pass.cpp: StorageAtomicAddF32 sets used_storage_buffer_types |= IR::Type::U32 | IR::Type::F32;, so the F32 SSBO view exists.

  • emit_spirv_atomic.cpp, EmitStorageAtomicAddF32, before the CAS fallback:

    if (ctx.profile.support_float32_atomic_add && binding.IsImmediate() && ctx.ssbos[binding.U32()].F32.value != 0) {
        const Id pointer{StoragePointer(ctx, ctx.storage_types.F32, &StorageDefinitions::F32, binding, offset, sizeof(f32))};
        const auto [scope, semantics]{AtomicArgs(ctx)};
        return ctx.OpAtomicFAdd(ctx.F32[1], pointer, scope, semantics, value);
    }
    
  • emit_spirv.cpp: when info.uses_atomic_f32_add && profile.support_float32_atomic_add, add extension SPV_EXT_shader_atomic_float_add and capability AtomicFloat32AddEXT.

  • sirit: add Id OpAtomicFAdd(Id result_type, Id pointer, Id memory, Id semantics, Id value), which emits spv::Op::OpAtomicFAddEXT.

This changes the translator sources, so the shader stamp changes and the shaders get prepared again once (~30 min on this machine).

Still open on Arc: outdoor scenes look washed out / white haze. Indoors looks fine.

### Fix for Intel Arc (Lunar Lake, Arc 140V): igvk64.dll crash + device lost at Kokiri Tested on MSI Claw 8 AI+ (Core Ultra 7 258V, Arc 140V, driver 32.0.101.9175), v2.0.14. With the two changes below, 10 and 15 min runs in Kokiri gameplay, ~31 fps, no crash, no device lost. **1. Crash in `igvk64.dll+0x79ddf3` (this issue)** Cause: `pipeline_cache_save` calls `vkGetPipelineCacheData` on its background thread while other threads are still creating pipelines into the same cache. Intel's driver races on this and reads a null pointer. That's legal in Vulkan, so it's a driver bug, but it's easy to avoid. Workaround in `gpu/vk/vk_draw.cpp`, `pipeline_cache_save`, right after the `pcache_built_since_save < 32` early return: ```cpp // Intel: vkGetPipelineCacheData racing pipeline creation crashes igvk64.dll (#999); save only on forced saves (exit, prepare end, menu) if (!force && dev.props.vendorID == 0x8086 && !getenv("RX_VK_PCACHE_BG_SAVE")) return; ``` **2. VK_ERROR_DEVICE_LOST (TDR) when Kokiri gameplay starts** Cause: the Eden SPIR-V backend always emits `StorageAtomicAddF32` as a compare-exchange loop (`f32_add_cas`). In one compute shader, thousands of invocations hit the same word, and on Arc the loop runs past the Windows GPU timeout. Arc supports `VK_EXT_shader_atomic_float` with `shaderBufferFloat32AtomicAdd`, so emit a native `OpAtomicFAddEXT` instead: - `vk_internal.h`: add `float_atomic_add = false,` to the device feature flags. - `vk_main.cpp`: - `optional(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME, dev.float_atomic_add);` - chain `VkPhysicalDeviceShaderAtomicFloatFeaturesEXT` into the feature query; - then `dev.float_atomic_add &= fadd.shaderBufferFloat32AtomicAdd;`. I also added an env switch, `RX_VK_NO_FLOAT_ATOMIC`, to turn this back off. Cause: the Eden SPIR-V backend always emits `StorageAtomicAddF32` as a compare-exchange loop (`f32_add_cas`). In one compute shader, thousands of invocations hit the same word, and on Arc the loop runs past the Windows GPU timeout. Arc supports `VK_EXT_shader_atomic_float` with `shaderBufferFloat32AtomicAdd`, so emit a native `OpAtomicFAddEXT` instead: - `vk_internal.h`: add `float_atomic_add = false,` to the device feature flags. - `vk_main.cpp`: - `optional(VK_EXT_SHADER_ATOMIC_FLOAT_EXTENSION_NAME, dev.float_atomic_add);` - chain `VkPhysicalDeviceShaderAtomicFloatFeaturesEXT` into the feature query; - then `dev.float_atomic_add &= fadd.shaderBufferFloat32AtomicAdd;`. I also added an env switch, `RX_VK_NO_FLOAT_ATOMIC`, to turn this back off. - `profile.h`: add `bool support_float32_atomic_add{};`. In `vk_shader.cpp`, set it from `dev.float_atomic_add`. - `collect_shader_info_pass.cpp`: `StorageAtomicAddF32` sets `used_storage_buffer_types |= IR::Type::U32 | IR::Type::F32;`, so the F32 SSBO view exists. - `emit_spirv_atomic.cpp`, `EmitStorageAtomicAddF32`, before the CAS fallback: ```cpp if (ctx.profile.support_float32_atomic_add && binding.IsImmediate() && ctx.ssbos[binding.U32()].F32.value != 0) { const Id pointer{StoragePointer(ctx, ctx.storage_types.F32, &StorageDefinitions::F32, binding, offset, sizeof(f32))}; const auto [scope, semantics]{AtomicArgs(ctx)}; return ctx.OpAtomicFAdd(ctx.F32[1], pointer, scope, semantics, value); } ``` - `emit_spirv.cpp`: when `info.uses_atomic_f32_add && profile.support_float32_atomic_add`, add extension `SPV_EXT_shader_atomic_float_add` and capability `AtomicFloat32AddEXT`. - sirit: add `Id OpAtomicFAdd(Id result_type, Id pointer, Id memory, Id semantics, Id value)`, which emits `spv::Op::OpAtomicFAddEXT`. This changes the translator sources, so the shader stamp changes and the shaders get prepared again once (~30 min on this machine). Still open on Arc: outdoor scenes look washed out / white haze. Indoors looks fine.
peigongdsd commented 2026-11-11 13:59:28 +00:00 (Migrated from github.com)

For Intel DG2/Xe1 user under mesa/ANV: The compilation of pipelines were extrodinarily slow and finally lead to memory overflow due to frequent spilling of GRFs. I am working on https://github.com/HHHHanasak1/xess-xmx-linux/pull/64 which hopefully could address this problem by forcing BRW to use 256 GRFs. If the compilation did overflow your memory, please have a try at this mesa build.

For Intel DG2/Xe1 user under mesa/ANV: The compilation of pipelines were extrodinarily slow and finally lead to memory overflow due to frequent spilling of GRFs. I am working on https://github.com/HHHHanasak1/xess-xmx-linux/pull/64 which hopefully could address this problem by forcing BRW to use 256 GRFs. If the compilation did overflow your memory, please have a try at this mesa build.
bmiculka commented 2026-11-11 14:26:15 +00:00 (Migrated from github.com)

For Intel DG2/Xe1 user under mesa/ANV: The compilation of pipelines were extrodinarily slow and finally lead to memory overflow due to frequent spilling of GRFs. I am working on HHHHanasak1/xess-xmx-linux#64 which hopefully could address this problem by forcing BRW to use 256 GRFs. If the compilation did overflow your memory, please have a try at this mesa build.

@peigongdsd Is this live to try? Looks like the workflow still needs approval, and the last release was yesterday. Seems once merged, you just run install.sh? If it creates instability, can then run uninstall.sh to fallback to my current Mesa 26.2.6?

> For Intel DG2/Xe1 user under mesa/ANV: The compilation of pipelines were extrodinarily slow and finally lead to memory overflow due to frequent spilling of GRFs. I am working on [HHHHanasak1/xess-xmx-linux#64](https://github.com/HHHHanasak1/xess-xmx-linux/pull/64) which hopefully could address this problem by forcing BRW to use 256 GRFs. If the compilation did overflow your memory, please have a try at this mesa build. @peigongdsd Is this live to try? Looks like the workflow still needs approval, and the last release was yesterday. Seems once merged, you just run install.sh? If it creates instability, can then run uninstall.sh to fallback to my current Mesa 26.2.6?
peigongdsd commented 2026-11-11 15:38:35 +00:00 (Migrated from github.com)

For Intel DG2/Xe1 user under mesa/ANV: The compilation of pipelines were extrodinarily slow and finally lead to memory overflow due to frequent spilling of GRFs. I am working on HHHHanasak1/xess-xmx-linux#64 which hopefully could address this problem by forcing BRW to use 256 GRFs. If the compilation did overflow your memory, please have a try at this mesa build.

@peigongdsd Is this live to try? Looks like the workflow still needs approval, and the last release was yesterday. Seems once merged, you just run install.sh? If it creates instability, can then run uninstall.sh to fallback to my current Mesa 26.2.6?

No rush, I am still testing and improving. With this 256 GRFs I am able to compile the compute pipelines but it is still very slow now

> > For Intel DG2/Xe1 user under mesa/ANV: The compilation of pipelines were extrodinarily slow and finally lead to memory overflow due to frequent spilling of GRFs. I am working on [HHHHanasak1/xess-xmx-linux#64](https://github.com/HHHHanasak1/xess-xmx-linux/pull/64) which hopefully could address this problem by forcing BRW to use 256 GRFs. If the compilation did overflow your memory, please have a try at this mesa build. > > [@peigongdsd](https://github.com/peigongdsd) Is this live to try? Looks like the workflow still needs approval, and the last release was yesterday. Seems once merged, you just run install.sh? If it creates instability, can then run uninstall.sh to fallback to my current Mesa 26.2.6? No rush, I am still testing and improving. With this 256 GRFs I am able to compile the compute pipelines but it is still very slow now
Henrique143 commented 2026-11-11 17:42:43 +00:00 (Migrated from github.com)

v2.1.0 (633eb435): now crashes instead of showing a black screen. Arc B580, driver 101.9175, Windows 11 25H2, i5-9600KF. 3 runs, 3 logs attached.

Every run ends with VK_ERROR_DEVICE_LOST (submit returns -4). Windows logs a LiveKernelEvent 141 (GPU reset) at the same moment.

Run Where Top of pipe (stage 0x1) Bottom of pipe (stage 0x2000)
1 loading a save 4b03b3040d7cf076 (frame 2381) ab7699ee7f536755
2 loading a save 592079442d7129da (frame 1347) 435a0138b9013040
3 new game, after the first cutscene 499dbfd0721d2858 (frame 12061) 499dbfd0721d2858

The f8efe05 test above found the same two programs: first 499dbfd0721d2858, then 4b03b3040d7cf076 after ab7699ee7f536755. These runs show both on the v2.1.0 release. 592079442d7129da also stopped one of my v2.0.2 runs.

In all three runs, the first dispatch of the upscale network fd029a317a9a4b94 (typed upscale helper) appears in the log shortly before the failed submit. My v2.0.11 log showed the same thing.

hookshot_v2.1.0_run1_save.txt
hookshot_v2.1.0_run2_save.txt
hookshot_v2.1.0_run3_newgame.txt

**v2.1.0 (633eb435): now crashes instead of showing a black screen.** Arc B580, driver 101.9175, Windows 11 25H2, i5-9600KF. 3 runs, 3 logs attached. Every run ends with `VK_ERROR_DEVICE_LOST` (submit returns -4). Windows logs a `LiveKernelEvent 141` (GPU reset) at the same moment. | Run | Where | Top of pipe (stage 0x1) | Bottom of pipe (stage 0x2000) | |---|---|---|---| | 1 | loading a save | `4b03b3040d7cf076` (frame 2381) | `ab7699ee7f536755` | | 2 | loading a save | `592079442d7129da` (frame 1347) | `435a0138b9013040` | | 3 | new game, after the first cutscene | `499dbfd0721d2858` (frame 12061) | `499dbfd0721d2858` | The f8efe05 test above found the same two programs: first `499dbfd0721d2858`, then `4b03b3040d7cf076` after `ab7699ee7f536755`. These runs show both on the v2.1.0 release. `592079442d7129da` also stopped one of my v2.0.2 runs. In all three runs, the first dispatch of the upscale network `fd029a317a9a4b94` (`typed upscale helper`) appears in the log shortly before the failed submit. My v2.0.11 log showed the same thing. [hookshot_v2.1.0_run1_save.txt](https://github.com/user-attachments/files/34699356/hookshot_v2.1.0_run1_save.txt) [hookshot_v2.1.0_run2_save.txt](https://github.com/user-attachments/files/34699354/hookshot_v2.1.0_run2_save.txt) [hookshot_v2.1.0_run3_newgame.txt](https://github.com/user-attachments/files/34699355/hookshot_v2.1.0_run3_newgame.txt)
Enzusann commented 2026-11-11 18:45:13 +00:00 (Migrated from github.com)

Runs great besides the textures going missing all the time.

On the newest update, one of the hottest issues still isnt fixed.

ARC A580

image This time, in hyrule castle outskirts
Runs great besides the textures going missing all the time. On the newest update, one of the hottest issues still isnt fixed. ARC A580 <img width="1892" height="1040" alt="image" src="https://github.com/user-attachments/assets/d37b16f5-a5bf-46e1-a382-5567dc3f094b" /> This time, in hyrule castle outskirts
Sellierm commented 2026-11-11 20:19:08 +00:00 (Migrated from github.com)

Hi! This is my first contribution here: a cause and a fix for the white veil over Kokiri on Intel iGPUs. The fix is in PR #2464. I worked on it with the help of an AI assistant (Claude Code) and checked everything on my own machine.

My PC

  • Machine: Lenovo laptop (type 83DJ).
  • CPU: Intel Core Ultra 7 155H (Meteor Lake).
  • GPU: integrated Intel Arc Graphics (8 Xe-cores), no dedicated GPU; video memory is shared with system RAM.
  • RAM: 32 GB LPDDR5x-7467.
  • OS: Windows 11 Pro, build 26300 (Insider).
  • Driver: Intel 32.0.101.9175, installed by hand (the version from #741).
  • Hookshot: v2.1.0, with HOS_OUTPUT_RES=3, RX_VK_FIFO=1, RX_VK_PREPARE_BACKGROUND=1.

What I saw: Kokiri Forest covered in white. The ground was missing and fog/sky was painted over it, worst in wide views. Characters, houses and plants were drawn normally.

Cause: on an iGPU the buffer pools have no VRAM mirror (mirror_on() is off for non-discrete GPUs). The GPU therefore reads the uniform buffers in the game's memory when it runs the frame. By then the game has already written the next frame's per-object matrices there.

  • The depth prepass (ffb53ed102dd9908) drew the terrain with those stale matrices, so it landed off screen and the depth stayed at far.
  • The fog/atmosphere pass (1a7482c0ec9991ea) treats far depth as sky and paints over the ground.

So this is not a driver bug. Any GPU without mirrors should be affected; RX_VK_MIRROR=0 on a discrete card would probably reproduce it, but I have not tested that.

Workaround for v2.1.0 now: add this line to hookshot_env.txt:

RX_VK_UB_SNAPSHOT=1

It copies the uniforms as submitted. Kokiri renders correctly and fps are unchanged on my machine (~8.5-11).

Fix: the PR turns this on by default when the pools are not mirrored.

The investigation details (per-draw depth readback vs a CPU projection of the same draws) are in the PR.

Feedback welcome, especially from other Intel iGPU owners: does the line above fix Kokiri for you too?

Before :
avant_kokiri
After :
apres_kokiri

Hi! This is my first contribution here: a cause and a fix for the white veil over Kokiri on Intel iGPUs. The fix is in PR #2464. I worked on it with the help of an AI assistant (Claude Code) and checked everything on my own machine. **My PC** - **Machine:** Lenovo laptop (type 83DJ). - **CPU:** Intel Core Ultra 7 155H (Meteor Lake). - **GPU:** integrated Intel Arc Graphics (8 Xe-cores), no dedicated GPU; video memory is shared with system RAM. - **RAM:** 32 GB LPDDR5x-7467. - **OS:** Windows 11 Pro, build 26300 (Insider). - **Driver:** Intel 32.0.101.9175, installed by hand (the version from #741). - **Hookshot:** v2.1.0, with `HOS_OUTPUT_RES=3`, `RX_VK_FIFO=1`, `RX_VK_PREPARE_BACKGROUND=1`. **What I saw:** Kokiri Forest covered in white. The ground was missing and fog/sky was painted over it, worst in wide views. Characters, houses and plants were drawn normally. **Cause:** on an iGPU the buffer pools have no VRAM mirror (`mirror_on()` is off for non-discrete GPUs). The GPU therefore reads the uniform buffers in the game's memory when it runs the frame. By then the game has already written the next frame's per-object matrices there. - The depth prepass (`ffb53ed102dd9908`) drew the terrain with those stale matrices, so it landed off screen and the depth stayed at far. - The fog/atmosphere pass (`1a7482c0ec9991ea`) treats far depth as sky and paints over the ground. So this is not a driver bug. Any GPU without mirrors should be affected; `RX_VK_MIRROR=0` on a discrete card would probably reproduce it, but I have not tested that. **Workaround for v2.1.0 now:** add this line to `hookshot_env.txt`: ``` RX_VK_UB_SNAPSHOT=1 ``` It copies the uniforms as submitted. Kokiri renders correctly and fps are unchanged on my machine (~8.5-11). **Fix:** the PR turns this on by default when the pools are not mirrored. The investigation details (per-draw depth readback vs a CPU projection of the same draws) are in the PR. Feedback welcome, especially from other Intel iGPU owners: does the line above fix Kokiri for you too? Before : <img width="1280" height="720" alt="avant_kokiri" src="https://github.com/user-attachments/assets/e28a6407-19c5-411c-9c56-bf927b5e03a6" /> After : <img width="1280" height="720" alt="apres_kokiri" src="https://github.com/user-attachments/assets/9eced64f-68a7-4b85-bbd1-8526e40e203e" />
NotRexed commented 2026-11-11 21:21:53 +00:00 (Migrated from github.com)

More reports of this, collected here:

  • #1633: Can not run into the game (crash) (v2.0.12; ARC B580, driver 9150; i9-13900HK, 32GB DDR5, win11 26H2)
  • #2224: Intel G3 extreme (B390) (2.1.0; Intel Arc B390 , Latest drivers - 32.0.101.9150; windows 11, 24gb Ram)
  • #2236: Crash after intro cutscene (v2.1.0; Intel B580; Ryzen 5 7600X, 32 GB, Windows 11)
  • #2264: Game crash at start, fail to start the game (v2.1.0; ARC B580, 9150; i9-13900HK, 32GB, WIN11 26H2)
  • #2498: Game showing graphical glitch in opening screen and in-game on Intel GPU (v2.1.0; 32.0.101.9175 WHQL Certified (Latest); Ryzen 7 7700, 16gb ddr5,Intel arc A750, Windows)
  • #2501: Crash when you load 3D model (v2.1.0; Intel Arc A770 (Resizable BAR: Enabled); E5-2687W v3 32Gb Windows 11)
More reports of this, collected here: - #1633: Can not run into the game (crash) (v2.0.12; ARC B580, driver 9150; i9-13900HK, 32GB DDR5, win11 26H2) - #2224: Intel G3 extreme (B390) (2.1.0; Intel Arc B390 , Latest drivers - 32.0.101.9150; windows 11, 24gb Ram) - #2236: Crash after intro cutscene (v2.1.0; Intel B580; Ryzen 5 7600X, 32 GB, Windows 11) - #2264: Game crash at start, fail to start the game (v2.1.0; ARC B580, 9150; i9-13900HK, 32GB, WIN11 26H2) - #2498: Game showing graphical glitch in opening screen and in-game on Intel GPU (v2.1.0; 32.0.101.9175 WHQL Certified (Latest); Ryzen 7 7700, 16gb ddr5,Intel arc A750, Windows) - #2501: Crash when you load 3D model (v2.1.0; Intel Arc A770 (Resizable BAR: Enabled); E5-2687W v3 32Gb Windows 11)
hontalasbranden-prog commented 2026-11-11 21:57:21 +00:00 (Migrated from github.com)

https://github.com/user-attachments/assets/3f6ae18f-cc10-4cf3-81aa-ff50cc01aa1a

Intel

ARC140V

hookshot version 2.1.0. Same graphic glitches but able to maintain close to 30fps and move around and leave links house for the first time.

hookshot_log.txt

https://github.com/user-attachments/assets/3f6ae18f-cc10-4cf3-81aa-ff50cc01aa1a Intel > ARC140V hookshot version 2.1.0. Same graphic glitches but able to maintain close to 30fps and move around and leave links house for the first time. [hookshot_log.txt](https://github.com/user-attachments/files/34713988/hookshot_log.txt)
Lost-Herondale commented 2026-11-12 02:12:30 +00:00 (Migrated from github.com)

Crashes immediately after the initial video sequence plays right before the Deku tree would start talking. While running on Intel GPU. I was told to also upload here on the Discord. Hookshot Version 2.1.0

hookshot_log.txt

System specs:
GPU Intel Arc A770 Le 16Gb
CPU Ryzen 5 9600x
32 Gb DDR5 ram
Driver
32.0.101.9150

Crashes immediately after the initial video sequence plays right before the Deku tree would start talking. While running on Intel GPU. I was told to also upload here on the Discord. Hookshot Version 2.1.0 [hookshot_log.txt](https://github.com/user-attachments/files/34723306/hookshot_log.txt) System specs: GPU Intel Arc A770 Le 16Gb CPU Ryzen 5 9600x 32 Gb DDR5 ram Driver 32.0.101.9150
NotRexed commented 2026-11-12 03:07:13 +00:00 (Migrated from github.com)

More reports of this, collected here:

  • #2551: Intel ARC B570 (v2.1.0; ARC B570 10gb; Core i5 10400f, 24gb DDR4 3200mhz)
  • #2612: Game crash after first cutscene (v2.1.0; Intel B580 32.0.101.9175; Ryzen 5 7600x, Windows 11)
More reports of this, collected here: - #2551: Intel ARC B570 (v2.1.0; ARC B570 10gb; Core i5 10400f, 24gb DDR4 3200mhz) - #2612: Game crash after first cutscene (v2.1.0; Intel B580 32.0.101.9175; Ryzen 5 7600x, Windows 11)
Webo11x commented 2026-11-12 03:58:34 +00:00 (Migrated from github.com)

Same situation of game crashing after the first cutscene. Tried several config and none is working. And every time I need to watch the cutscene, lol.

Thanks for all the work. Is there any way I could fix this?

Same situation of game crashing after the first cutscene. Tried several config and none is working. And every time I need to watch the cutscene, lol. Thanks for all the work. Is there any way I could fix this?
Owner

@Maine774 commented on GitHub · 2026-11-12 04:55 UTC · original

Able to get into gameplay, but during the cutscene where Saria appears for the first time after link opens the door crashes the game. Many textures arent loaded correctly or at all and plenty of flickering.

> [@Maine774](https://github.com/Maine774) commented on GitHub · 2026-11-12 04:55 UTC · [original](https://github.com/NotRexed/Hookshot/issues/999#issuecomment-6814998172) Able to get into gameplay, but during the cutscene where Saria appears for the first time after link opens the door crashes the game. Many textures arent loaded correctly or at all and plenty of flickering. <!-- github-sync:c:6814998172 -->
Owner

@ploob-boop commented on GitHub · 2026-11-12 10:58 UTC · original

Intel Arc 140V (Lunar Lake iGPU): white-flash flicker fixed, crash fixed, Intel XeSS added

Base: Hookshot v2.0.14, Vulkan back end.
Device: MSI Claw, Intel Arc 140V (integrated), Windows, 1920x1200.
Game: Ocarina of Time (Switch 2).

TL;DR

# Problem Status Patch
1 Whole-screen white flash, once every 8 frames Fixed (root cause found, measured) 01
2 Crash in igvk64.dll inside vkGetPipelineCacheData (this issue) Fixed 01
3 Smaller Intel flicker (menu/logo haze, leaves, stale tiles) Fixed on this device 01
4 No upscaler on Intel (DLSS is NVIDIA-only) Added: Intel XeSS in the DLSS slot 02

Both patches apply cleanly to v2.0.14 with git apply. They were checked in order on a clean tree: 01, then 02.


1. White flash every 8 frames: root cause

Symptom. One frame in 8 turns white over the whole screen: the fog/haze loses its sky light. It is always the frame where the reflection probe's cubemap is swapped in.

Trigger. It happens only when maybe_split (gpu/vk/vk_replay.cpp) cuts one guest submission into several Vulkan command buffers (RX_VK_CHUNK, default 120).

  • RX_VK_CHUNK=0 (no split): 0 flashes, but 18 fps drops to 10.
  • Even one split per submission is enough to cause flashes.

Cause. At every part boundary, maybe_split called flush_ub_updates(). That wrote the inline uniform updates (NOP_UPDATE_UB) recorded so far into guest memory in the middle of the submission. Then it moved ub_first_of_recording forward.

An unsplit submission writes all of its updates once, at its end, in finish_recording. Draws in later parts that read those ranges directly from guest memory (not through an overlay copy) therefore saw a different state than an unsplit run. On the probe-swap frame this gives the white haze.

Fix (3 lines). At a part boundary, do not flush and do not advance ub_first_of_recording. The last part flushes the whole submission's updates, exactly like an unsplit submission. RX_VK_SPLIT_UBFLUSH=1 restores the old behaviour.

Measured. Same scene, 25 s each, 4 back-to-back runs, RX_VK_CHUNK=120:

Build Frames White flashes FPS
with fix 458 0 18.4
without fix 455 56 17.7
with fix 421 0 —
without fix 492 56 —

No fps cost. The split keeps its benefit, while RX_VK_CHUNK=0 costs about 45% fps.

Ruled out first (each tested on its own, no change):

  • CPU/GPU races at record time:
    • waiting for every part on the GPU before recording the next;
    • holding all parts until the submission ends.
  • Page table written immediately (RX_VK_TABLE_NOW=1).
  • Event ADD signals; render conditions.
  • Small CLEAR_BUF on the CPU (RX_VK_CLEAR_CPU_MAX=0).
  • UB snapshot on/off; texture handles read live.
  • Barriers and layouts; XeSS on/off.

Lichii (Eden-based) splits just as often (every 128 draws) and does not flicker. That confirms the boundary itself is fine.

2. Crash in vkGetPipelineCacheData (this issue)

Intel's driver dereferenced a null pointer (igvk64.dll+0x79ddf3) in vkGetPipelineCacheData. This happened while other threads were building pipelines with the same cache. Vulkan allows that usage, so this is a driver race.

Fix: on Intel (vendorID 0x8086), the pipeline cache is saved at exit only, not in the background. RX_VK_PCACHE_BG_SAVE=1 restores the old behaviour.

3. Other Intel / iGPU fixes in patch 01

Change File Why Revert switch
Sampling a target the pass already wrote ends the pass, even when the draw writes that target too vk_draw.cpp draw_hazards Intel read the earlier draw's pixels unsynchronized: flicker none (one condition)
A draw after a draw that wrote storage in the same pass gets its own pass (full barrier) vk_draw.cpp draw Inside a pass nothing orders storage writes with later reads: Intel showed tiles of stale results RX_VK_NO_STORAGE_HAZARD=1
Uniform buffers copied at record time by default on an iGPU vk_draw.cpp uniform_range No VRAM mirror on an iGPU: the GPU reads guest memory the game is already rewriting RX_VK_UB_SNAPSHOT=0
GPU results written in place are noted for the readback wait on an iGPU vk_memory.cpp gpu_buffer_bind OoT's exposure meter was read before the GPU wrote it: brightness pumped none

4. Intel XeSS (patch 02)

XeSS Super Resolution takes DLSS's place in gpu/vk/vk_dlss.cpp. It uses the same inputs DLSS already gets: camera, LDR colour, depth, low-res motion vectors in pixels, and the host's jitter. Only the NGX calls are replaced by XeSS calls.

  • Build-time switch: HOOKSHOT_XESS_SDK=<path to Intel's XeSS SDK> adds -DHOOKSHOT_XESS (nx2/build.py). Without it, or when the NGX SDK is set, nothing changes.
  • Run time: libxess.dll is loaded from the folder of the game's executable, and its functions are resolved at run time. No import library is needed, so this works with llvm-mingw.
  • Device setup: XeSS's required instance/device extensions and features are added at device creation (vk_main.cpp: dlss_device_features).
  • Menu: the Upscaler row shows Intel XeSS and Intel XeSS AA instead of NVIDIA DLSS and NVIDIA DLAA (vk_menu.cpp).
  • Tested: libxess.dll 2.0.2. Log: Intel XeSS draws OoT's upscale: 1536x864 -> 2048x1152.

Best: Handheld profile on, Render resolution Auto, Upscaler Intel XeSS.

handheld_profile=1
resolution=0
output_res=0
ultrawide=0
dlss=1

Same scene, 25 s per run:

Profile Render resolution Output 3D (XeSS input) XeSS FPS White flashes
Handheld Auto 1536x864 1152x648 on ~24-25 0
Docked Auto 2048x1152 1536x864 on ~23 0
Handheld 75% 1536x864 1152x648 on ~19 0
Docked, before these patches Auto 2048x1152 1536x864 on ~18 56 per ~450 frames

Notes

  1. Keep Render resolution on Auto. With a fixed value (67% or 50%), XeSS did not run at all: the log has no XeSS lines and the game's own upscale is used. The higher fps in that case comes from not running XeSS.
  2. Handheld + Auto is the fastest with XeSS. It is faster than Docked + Auto and faster than Handheld + 75%.
  3. Aspect ratio. On a 16:10 screen (1920x1200) the 1536x864 output is 16:9, so there are thin black bars top and bottom. ultrawide=1 (fill 16:10) works in Docked only, not with the handheld profile.
  4. Leave the split on. Keep RX_VK_CHUNK at its default (120). RX_VK_CHUNK=0 also removes the flashes, but costs about 45% fps.

How to apply and build

  1. Start from a clean v2.0.14 source tree (the folder that contains gpu/ and nx2/).
  2. Apply the patches, in this order:
    git apply 01-intel-flicker-and-crash-fixes.patch
    git apply 02-intel-xess.patch
    
  3. Download Intel's XeSS SDK from https://github.com/intel/xess/releases.
  4. Build with the SDK path set:
    set HOOKSHOT_XESS_SDK=C:\path\to\xess_sdk
    python nx2\build.py --standalone
    
  5. Copy libxess.dll from the SDK into the game's folder, next to the executable.
  6. In the game, open the Insert menu and set Graphics → Upscaler → Intel XeSS.

Only patch 01 is needed for the flicker and crash fixes. Patch 02 is optional.

Still open (not caused by these patches)

  • A single object (the player, a tree stump) flashed bright once or twice in several minutes of play. It is rare, local, and not periodic, so it looks like a separate issue.
  • A separate finding, not included in either patch: in submit_recording, value_rec.emplace(r.value, seq) keeps the first part's recording for a split submission, because emplace never overwrites. As a result, wait_recorded() can return once part 1 is done. Replacing it with value_rec[v] = std::max(value_rec[v], seq) did not change the flashes and cost fps here, but the mapping looks wrong.
01-intel-flicker-and-crash-fixes.patch (4 files, 90 lines)
diff --git a/gpu/vk/vk_draw.cpp b/gpu/vk/vk_draw.cpp
index 6e1c49d..830144c 100644
--- a/gpu/vk/vk_draw.cpp
+++ b/gpu/vk/vk_draw.cpp
@@ -2148,7 +2148,8 @@ VkDescriptorBufferInfo uniform_range(const NvnState& s, const Stage& st, uint32_
                 return {cur->stream, at, n};
             }
         // (RX_VK_UB_SNAPSHOT=1: the contents as they are now, in the stream, not the pool the GPU reads later)
-        static const bool snapshot = getenv("RX_VK_UB_SNAPSHOT") != nullptr;
+        static const bool snapshot = getenv("RX_VK_UB_SNAPSHOT") ? atoi(getenv("RX_VK_UB_SNAPSHOT")) != 0
+                                                                : dev.props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;   // (Intel iGPU: no mirrors on an iGPU)
         BufferRef b = snapshot ? BufferRef{} : gpu_buffer_bind(u.addr, n, false);
         if (b && b.offset % align == 0) return {b.buffer, b.offset, n};
         const uint64_t at = stream_alloc(n, align);   // (unaligned or outside the pools: copied into the stream)
@@ -3772,7 +3773,7 @@ uint16_t draw_hazards(VkCommandBuffer cmd, const Program& p, const NvnState& s,
     const char* kind = nullptr;
     for (const Texture* t : gfx_sampled()) {
         const int k = slot_of(t);
-        if (k >= 0 && (pass().written >> k & 1) && !(writes >> k & 1)) { kind = "samples a target the pass wrote"; break; }
+        if (k >= 0 && (pass().written >> k & 1)) { kind = "samples a target the pass wrote"; break; }   // (Intel iGPU: also when it writes it: Intel flicker)
     }
     if (!kind && writes_sampled_target(writes, pass().sampled_targets))
         kind = "writes a target an earlier draw sampled";
@@ -5593,6 +5594,24 @@ void draw(VkCommandBuffer cmd, NvnState& s, const DrawArgs& d) {
     if (!same_desc) gfx_sampled() = draw_sampled();
     static const bool barrier_every = getenv("RX_VK_BARRIER_EVERY_DRAW") != nullptr;   // (an experiment: a pass each)
     if (barrier_every) end_pass(cmd);
+    {   // a draw after one of this pass whose graphics stages wrote storage: in a pass of its own (begin_pass's full
+        // barrier). Inside a pass nothing orders a shader's storage writes with a later draw's reads; Intel showed tiles of
+        // stale results. RX_VK_NO_STORAGE_HAZARD=1: as before
+        static const bool off = getenv("RX_VK_NO_STORAGE_HAZARD") != nullptr;
+        static thread_local bool stored_before;
+        bool stores = false;
+        for (const auto& st : p.stages) {
+            for (const auto& d : st.info.storage_buffers_descriptors) stores |= d.is_written;
+            for (const auto& d : st.info.image_descriptors) stores |= d.is_written;
+            for (const auto& d : st.info.image_buffer_descriptors) stores |= d.is_written;
+        }
+        if (!off && stored_before && pass().open) {
+            static int logged;
+            if (logged++ < 6) logf("vk: a draw (program %016llx) after a storage write in its pass: a pass of its own", (unsigned long long)p.key);
+            end_pass(cmd);
+        }
+        stored_before = stores;
+    }
     uint16_t writes;
     {
         FINE_SCOPE(FT_HAZARD);
@@ -7454,6 +7473,9 @@ int64_t draw_slotgen_saves() {   // (RX_VK_DESC_SLOTGEN: of those, what the glob
 
 void pipeline_cache_save(bool force) {
     if (!pcache || (!force && pcache_built_since_save < 32)) return;
+    // Intel's driver read a null pointer in vkGetPipelineCacheData while other threads built pipelines with the cache
+    // (igvk64.dll+0x79ddf3, Hookshot issue 999): a driver race, legal in Vulkan. There the cache is written at exit only
+    if (!force && dev.props.vendorID == 0x8086 && !getenv("RX_VK_PCACHE_BG_SAVE")) return;
     pcache_built_since_save = 0;
     // (on a thread of its own unless forced, at exit: ~100 MB fetched and written took the replay thread 100+ ms at
     // each scene change; the cache is internally synchronized, pipelines go on being built meanwhile)
diff --git a/gpu/vk/vk_memory.cpp b/gpu/vk/vk_memory.cpp
index 6a36e31..0b953ab 100644
--- a/gpu/vk/vk_memory.cpp
+++ b/gpu/vk/vk_memory.cpp
@@ -2585,6 +2585,8 @@ BufferRef gpu_buffer_bind(uint64_t gpu, uint64_t size, bool written) {
     const uint64_t off = guest - p->lo;
     if (!mirror_on() || !mirror_upload || p->mstate == Pool::MIRROR_REFUSED) {
         if (mirror_on()) { auto& e = direct_by_pool[p->lo]; e.first++, e.second = mirror_upload ? (int)p->mstate : 9; direct_bytes_by_pool[p->lo] += size; }
+        if (written && size <= (1u << 20))   // (Intel iGPU: GPU results in place on an iGPU too, so invalidate() waits for them)
+            wb_pages_note(p->lo + off, p->lo + std::min(off + size, p->hi - p->lo), mem_rec_value);
         return remember({p->buffer, off, size, 0});
     }
     // (a pool's mirror is made at its first binding, in this part's uploads: bound in guest memory until the next
diff --git a/gpu/vk/vk_replay.cpp b/gpu/vk/vk_replay.cpp
index cf5db3c..64c2bca 100644
--- a/gpu/vk/vk_replay.cpp
+++ b/gpu/vk/vk_replay.cpp
@@ -1237,11 +1237,12 @@ void maybe_split(VkCommandBuffer& cmd) {
     static const int64_t every = getenv("RX_VK_CHUNK") ? atoll(getenv("RX_VK_CHUNK")) : 120;
     if (!rec_cur || every <= 0 || ++part_work < every || rec_parts + 1 >= kMaxParts || dump_on()) return;
     end_pass(cmd);
-    flush_ub_updates(cmd);
+    static const bool no_ubflush = getenv("RX_VK_SPLIT_UBFLUSH") == nullptr;   // the updates stay for the last part; =1: flushed at each part as before
+    if (!no_ubflush) flush_ub_updates(cmd);
     memory_end_submission(cmd);
     full_barrier(cmd, "barrier (recording end)");
     submit_recording(*rec_cur, false);
-    ub_first_of_recording = ub_updates.size();
+    if (!no_ubflush) ub_first_of_recording = ub_updates.size();
     Recording& r = begin_recording(rec_cur_value);
     memory_begin_submission();
     full_barrier(r.cmd, "barrier (recording start)");
02-intel-xess.patch (6 files, 548 lines)
diff --git a/gpu/vk/vk_dlss.cpp b/gpu/vk/vk_dlss.cpp
index 1f94e33..d5a095b 100644
--- a/gpu/vk/vk_dlss.cpp
+++ b/gpu/vk/vk_dlss.cpp
@@ -68,7 +68,12 @@
 #include "nvsdk_ngx_vk.h"
 #include "nvsdk_ngx_helpers.h"
 #include "nvsdk_ngx_helpers_vk.h"
-
+#endif
+#ifdef HOOKSHOT_XESS   // (Intel iGPU: Intel XeSS in DLSS's place, patch_xess.py)
+#include <type_traits>
+#include "xess/xess_vk.h"
+#endif
+#if defined(HOOKSHOT_NGX) || defined(HOOKSHOT_XESS)
 #include "shaders/dlss_actor_frag_spv.h"
 #include "shaders/dlss_actor_vert_spv.h"
 #include "shaders/dlss_out_comp_spv.h"
@@ -107,10 +112,16 @@ int dlss_mode() {
 bool dlss_wanted() { return dlss_mode() != 0; }
 bool dlss_full_scale() { return dlss_mode() == 2; }
 
-#ifdef HOOKSHOT_NGX
+#if defined(HOOKSHOT_NGX) || defined(HOOKSHOT_XESS)
 
 namespace {
 
+#ifdef HOOKSHOT_NGX
+constexpr const char *kName = "NVIDIA DLSS", *kShort = "DLSS", *kAa = "DLAA";
+#else
+constexpr const char *kName = "Intel XeSS", *kShort = "XeSS", *kAa = "XeSS AA";
+#endif
+
 struct Image {
     VkImage image = VK_NULL_HANDLE;
     VmaAllocation memory = nullptr;
@@ -133,9 +144,13 @@ struct State {
     std::mutex mu;
     bool ngx_tried = false, ngx_ok = false;
     std::atomic<bool> failed{false};   // (atomic: dlss_jitter_size reads it on the game's thread, without the lock)
+#ifdef HOOKSHOT_NGX
     NVSDK_NGX_Parameter* caps = nullptr;
     NVSDK_NGX_Parameter* params = nullptr;
     NVSDK_NGX_Handle* feature = nullptr;
+#else
+    xess_context_handle_t feature = nullptr;
+#endif
     uint32_t feature_rw = 0, feature_rh = 0, feature_ow = 0, feature_oh = 0;   // (its largest render size, its output)
     uint32_t min_rw = 0, min_rh = 0;
     unsigned feature_preset = ~0u;   // (the preset it was made with: another one from the menu makes it again)
@@ -215,7 +230,7 @@ void note_output(const Texture* out, uint32_t level) {
 
 bool fail(const char* why) {   // (the network runs: why, when it changes and every 600th time)
     State& s = st();
-    if (why != s.reason || ++s.reason_repeats % 600 == 0) logf("vk: NVIDIA DLSS: the game's upscale runs (%s)", why);
+    if (why != s.reason || ++s.reason_repeats % 600 == 0) logf("vk: %s: the game's upscale runs (%s)", kName, why);
     if (why != s.reason) s.reason = why, s.reason_repeats = 0;
     return false;
 }
@@ -273,6 +288,7 @@ bool make_image(VkCommandBuffer cmd, Image& im, VkFormat format, uint32_t w, uin
     return true;
 }
 
+#ifdef HOOKSHOT_NGX
 NVSDK_NGX_Resource_VK resource(const Image& im, bool write) {
     return NVSDK_NGX_Create_ImageView_Resource_VK(im.view, im.image, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, im.format,
                                                   im.width, im.height, write);
@@ -450,6 +466,84 @@ bool ngx_init() {
     return true;
 }
 
+#else  // (HOOKSHOT_XESS)
+
+// Intel XeSS: libxess.dll beside the game's executable, its functions taken at run time (no import library to link;
+// without the DLL the Upscaler row keeps the game's own). RX_VK_DLSS=0: not loaded at all
+struct XessApi {
+    bool ok = false;
+    decltype(&xessVKGetRequiredInstanceExtensions) instance_exts = nullptr;
+    decltype(&xessVKGetRequiredDeviceExtensions) device_exts = nullptr;
+    decltype(&xessVKGetRequiredDeviceFeatures) device_features = nullptr;
+    decltype(&xessVKCreateContext) create = nullptr;
+    decltype(&xessVKInit) init = nullptr;
+    decltype(&xessVKExecute) execute = nullptr;
+    decltype(&xessGetOptimalInputResolution) optimal = nullptr;
+    decltype(&xessDestroyContext) destroy = nullptr;
+    decltype(&xessGetVersion) version = nullptr;
+};
+const XessApi& xess() {
+    static const XessApi api = [] {
+        XessApi a;
+        if (env_switch("RX_VK_DLSS") == 0) return a;
+        std::wstring exe(32768, L'\0');
+        const DWORD n = GetModuleFileNameW(nullptr, exe.data(), (DWORD)exe.size());
+        if (!n || n >= exe.size()) return a;
+        exe.resize(n);
+        const std::filesystem::path dll = std::filesystem::path(exe).parent_path() / L"libxess.dll";
+        const HMODULE lib = LoadLibraryW(dll.c_str());
+        if (!lib) {
+            logf("vk: Intel XeSS: no libxess.dll beside the game (%lu): the game's upscale", GetLastError());
+            return a;
+        }
+        bool all = true;
+        const auto get = [&](auto& f, const char* name) {
+            f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(reinterpret_cast<void*>(GetProcAddress(lib, name)));
+            if (!f) logf("vk: Intel XeSS: libxess.dll has no %s", name), all = false;
+        };
+        get(a.instance_exts, "xessVKGetRequiredInstanceExtensions");
+        get(a.device_exts, "xessVKGetRequiredDeviceExtensions");
+        get(a.device_features, "xessVKGetRequiredDeviceFeatures");
+        get(a.create, "xessVKCreateContext");
+        get(a.init, "xessVKInit");
+        get(a.execute, "xessVKExecute");
+        get(a.optimal, "xessGetOptimalInputResolution");
+        get(a.destroy, "xessDestroyContext");
+        get(a.version, "xessGetVersion");
+        a.ok = all;
+        return a;
+    }();
+    return api;
+}
+
+/// XeSS on this device, once (the NGX path's name, so the chain calls it the same): a context made and let go again
+bool ngx_init() {
+    State& s = st();
+    if (s.ngx_tried) return s.ngx_ok;
+    s.ngx_tried = true;
+    const XessApi& x = xess();
+    if (!x.ok || (g_why.load() > DLSS_OK)) {
+        if (g_why.load() <= DLSS_OK) g_why = DLSS_NO_RUNTIME;
+        return false;
+    }
+    xess_version_t v{};
+    x.version(&v);
+    xess_context_handle_t c = nullptr;
+    const xess_result_t r = x.create(dev.instance, dev.physical, dev.device, &c);
+    if (r != XESS_RESULT_SUCCESS || !c) {
+        logf("vk: Intel XeSS %u.%u.%u: no context on this device (%d)", v.major, v.minor, v.patch, (int)r);
+        g_why = DLSS_NEEDS_RTX;
+        return false;
+    }
+    x.destroy(c);
+    logf("vk: Intel XeSS %u.%u.%u started", v.major, v.minor, v.patch);
+    s.ngx_ok = true;
+    g_why = DLSS_OK;
+    return true;
+}
+
+#endif  // (HOOKSHOT_NGX)
+
 /// One compute pipeline with push descriptors; what it made is destroyed again when a step fails
 bool make_pipeline(const uint32_t* code, size_t size, const VkDescriptorType* types, uint32_t n, uint32_t push,
                    VkDescriptorSetLayout& set, VkPipelineLayout& layout, VkPipeline& pipe) {
@@ -721,6 +815,7 @@ int actor_motion(VkCommandBuffer cmd, const Camera& cam, const float* before, ui
     return (int)draws.size();
 }
 
+#ifdef HOOKSHOT_NGX
 /// DLSS's preset: the DLSS model row's, RX_VK_DLSS_PRESET over it (upscaler_rows.h dlss_preset)
 unsigned wanted_preset() {
     static const char* const env = getenv("RX_VK_DLSS_PRESET");
@@ -809,10 +904,164 @@ bool make_feature(VkCommandBuffer cmd, uint32_t rw, uint32_t rh, uint32_t ow, ui
     return true;
 }
 
+
+/// NGX's evaluation of the chain's inputs into s.output (false: it failed, said)
+bool evaluate(VkCommandBuffer cmd, const float jitter_px[2], bool reset, uint32_t rw, uint32_t rh) {
+    State& s = st();
+    NVSDK_NGX_Resource_VK color = resource(s.color, false), depth = resource(s.depth, false), mv = resource(s.motion, false),
+                          output = resource(s.output, true);
+    NVSDK_NGX_VK_DLSS_Eval_Params ep{};
+    ep.Feature.pInColor = &color, ep.Feature.pInOutput = &output;
+    ep.pInDepth = &depth, ep.pInMotionVectors = &mv;
+    ep.InJitterOffsetX = jitter_px[0], ep.InJitterOffsetY = jitter_px[1];
+    ep.InRenderSubrectDimensions = {rw, rh};
+    ep.InReset = reset ? 1 : 0;
+    ep.InMVScaleX = ep.InMVScaleY = 1.0f;
+    const NVSDK_NGX_Result r = NGX_VULKAN_EVALUATE_DLSS_EXT(cmd, s.feature, s.params, &ep);
+    note_work();   // (as at its creation: the barrier after it, and the game's next one, are recorded)
+    if (NVSDK_NGX_FAILED(r)) {
+        logf("vk: NVIDIA DLSS: its evaluation failed (%#x): the game's upscale from now on", (unsigned)r);
+        return false;
+    }
+    return true;
+}
+void release_feature(NVSDK_NGX_Handle* f) {
+    later([f] { NVSDK_NGX_VULKAN_ReleaseFeature(f); });
+}
+
+#else  // (HOOKSHOT_XESS)
+
+void release_feature(xess_context_handle_t f) {
+    later([f] { xess().destroy(f); });
+}
+
+/// XeSS's context for this output size, made again when it or the render size's mode changes. Its init uploads its network
+/// on the queue (its own submits): taken while nothing else submits (dev.present_mu, then the present queue's lock)
+bool make_feature(VkCommandBuffer, uint32_t rw, uint32_t rh, uint32_t ow, uint32_t oh) {
+    State& s = st();
+    if (s.feature && s.feature_ow == ow && s.feature_oh == oh && rw <= s.feature_rw && rh <= s.feature_rh &&
+        rw >= s.min_rw && rh >= s.min_rh)
+        return true;
+    char why[160];
+    if (!s.feature && !s.features_made) snprintf(why, sizeof why, "the first");
+    else if (!s.feature) snprintf(why, sizeof why, "again after it was let go");
+    else if (s.feature_ow != ow || s.feature_oh != oh)
+        snprintf(why, sizeof why, "the output went from %ux%u to %ux%u", s.feature_ow, s.feature_oh, ow, oh);
+    else
+        snprintf(why, sizeof why, "the render size %ux%u left its range, %ux%u to %ux%u", rw, rh, s.min_rw, s.min_rh,
+                 s.feature_rw, s.feature_rh);
+    if (s.feature) release_feature(s.feature), s.feature = nullptr;
+    const XessApi& x = xess();
+    xess_context_handle_t c = nullptr;
+    if (x.create(dev.instance, dev.physical, dev.device, &c) != XESS_RESULT_SUCCESS || !c) {
+        logf("vk: Intel XeSS: no context");
+        s.failed = true;
+        return false;
+    }
+    // (the quality mode whose range of render sizes holds this one; AA when the render size is the output's)
+    static const xess_quality_settings_t scaled[] = {XESS_QUALITY_SETTING_ULTRA_QUALITY_PLUS, XESS_QUALITY_SETTING_ULTRA_QUALITY,
+                                                     XESS_QUALITY_SETTING_QUALITY, XESS_QUALITY_SETTING_BALANCED,
+                                                     XESS_QUALITY_SETTING_PERFORMANCE, XESS_QUALITY_SETTING_ULTRA_PERFORMANCE};
+    const xess_2d_t out{ow, oh};
+    xess_quality_settings_t mode = XESS_QUALITY_SETTING_AA;
+    uint32_t max_w = ow, max_h = oh, min_w = ow, min_h = oh;
+    bool found = rw == ow && rh == oh;
+    for (const xess_quality_settings_t q : scaled) {
+        if (found) break;
+        xess_2d_t opt{}, lo{}, hi{};
+        if (x.optimal(c, &out, q, &opt, &lo, &hi) != XESS_RESULT_SUCCESS) continue;
+        if (rw >= lo.x && rh >= lo.y && rw <= hi.x && rh <= hi.y)
+            mode = q, max_w = hi.x, max_h = hi.y, min_w = lo.x, min_h = lo.y, found = true;
+    }
+    if (!found) {
+        x.destroy(c);
+        return fail("no XeSS mode takes this render size");
+    }
+    xess_vk_init_params_t ip{};
+    ip.outputResolution = out;
+    ip.qualitySetting = mode;
+    ip.initFlags = XESS_INIT_FLAG_LDR_INPUT_COLOR;   // (LDR colour, already tonemapped; motion at the render size in pixels)
+    const auto t0 = std::chrono::steady_clock::now();
+    xess_result_t r;
+    {
+        std::lock_guard qk(dev.present_mu);
+        std::lock_guard pq(dev.present_queue_mu);
+        r = x.init(c, &ip);
+    }
+    const double made_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - t0).count();
+    if (r != XESS_RESULT_SUCCESS) {
+        logf("vk: Intel XeSS: not initialised (%d) for %ux%u -> %ux%u", (int)r, max_w, max_h, ow, oh);
+        x.destroy(c);
+        s.failed = true;
+        return false;
+    }
+    s.feature = c;
+    s.feature_rw = max_w, s.feature_rh = max_h, s.feature_ow = ow, s.feature_oh = oh, s.min_rw = min_w, s.min_rh = min_h;
+    s.features_made++;
+    logf("vk: Intel XeSS: context %llu made (%s; frame %llu, %.1f ms of CPU): %ux%u (%ux%u to %ux%u) -> %ux%u, mode %d",
+         (unsigned long long)s.features_made, why, (unsigned long long)texture_frame_no(), made_ms, rw, rh, min_w, min_h, max_w,
+         max_h, ow, oh, (int)mode);
+    s.last_frame = 0;   // (a new history)
+    return true;
+}
+
+/// The inputs from GENERAL to what XeSS reads them in (SHADER_READ_ONLY_OPTIMAL), or back (to_read false)
+void input_layouts(VkCommandBuffer cmd, bool to_read) {
+    State& s = st();
+    VkImageMemoryBarrier2 b[3]{};
+    const Image* im[3] = {&s.color, &s.depth, &s.motion};
+    for (int i = 0; i < 3; i++) {
+        b[i] = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2};
+        b[i].srcStageMask = b[i].dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
+        b[i].srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT;
+        b[i].dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT;
+        b[i].oldLayout = to_read ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+        b[i].newLayout = to_read ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL;
+        b[i].srcQueueFamilyIndex = b[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+        b[i].image = im[i]->image, b[i].subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
+    }
+    VkDependencyInfo di{VK_STRUCTURE_TYPE_DEPENDENCY_INFO};
+    di.imageMemoryBarrierCount = 3, di.pImageMemoryBarriers = b;
+    vkCmdPipelineBarrier2(cmd, &di);
+}
+
+xess_vk_image_view_info view_info(const Image& im) {
+    xess_vk_image_view_info v{};
+    v.imageView = im.view, v.image = im.image, v.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
+    v.format = im.format, v.width = im.width, v.height = im.height;
+    return v;
+}
+
+/// XeSS's execution of the chain's inputs into s.output (false: it failed, said). RX_VK_XESS_JITTER=-1: the jitter given
+/// negated (a check of its sign: the picture shimmers when it is the wrong one)
+bool evaluate(VkCommandBuffer cmd, const float jitter_px[2], bool reset, uint32_t rw, uint32_t rh) {
+    State& s = st();
+    static const float sign = env_switch("RX_VK_XESS_JITTER") == -1 && getenv("RX_VK_XESS_JITTER") ? -1.0f : 1.0f;
+    input_layouts(cmd, true);
+    xess_vk_execute_params_t ep{};
+    ep.colorTexture = view_info(s.color), ep.velocityTexture = view_info(s.motion), ep.depthTexture = view_info(s.depth);
+    ep.outputTexture = view_info(s.output);
+    ep.jitterOffsetX = sign * jitter_px[0], ep.jitterOffsetY = sign * jitter_px[1];
+    ep.exposureScale = 1.0f;
+    ep.resetHistory = reset ? 1 : 0;
+    ep.inputWidth = rw, ep.inputHeight = rh;
+    const xess_result_t r = xess().execute(s.feature, cmd, &ep);
+    note_work();   // (XeSS's own commands, which vk_work.h does not see)
+    input_layouts(cmd, false);
+    if (r != XESS_RESULT_SUCCESS) {
+        logf("vk: Intel XeSS: its execution failed (%d): the game's upscale from now on", (int)r);
+        return false;
+    }
+    return true;
+}
+
+#endif  // (HOOKSHOT_NGX)
+
 }  // namespace
 
 bool dlss_built() { return true; }
 
+#ifdef HOOKSHOT_NGX
 void dlss_instance_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all) {
     if (env_switch("RX_VK_DLSS") == 0) return;
     for (const auto& e : all)
@@ -839,6 +1088,60 @@ void dlss_device_extensions(std::vector<const char*>& want, const std::vector<Vk
     // dlss_probe or at the first upscale: a GTX has them too)
     if (!both) g_why = DLSS_NEEDS_RTX;
 }
+void* dlss_device_features(void* features2) { return features2; }
+#else  // (HOOKSHOT_XESS)
+
+namespace {
+void add_once(std::vector<const char*>& want, const char* name) {
+    for (const char* w : want)
+        if (!strcmp(w, name)) return;
+    want.push_back(name);
+}
+}  // namespace
+
+void dlss_instance_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all) {
+    const XessApi& x = xess();
+    if (!x.ok) return void(g_why = env_switch("RX_VK_DLSS") == 0 ? DLSS_ENV_OFF : DLSS_NO_RUNTIME);
+    uint32_t n = 0, api = 0;
+    const char* const* names = nullptr;
+    if (x.instance_exts(&n, &names, &api) != XESS_RESULT_SUCCESS) return void(g_why = DLSS_NEEDS_RTX);
+    for (uint32_t i = 0; i < n; i++) {
+        bool have = false;
+        for (const auto& e : all) have |= !strcmp(e.extensionName, names[i]);
+        if (!have) return logf("vk: Intel XeSS: needs the instance extension %s", names[i]), void(g_why = DLSS_NEEDS_RTX);
+    }
+    for (uint32_t i = 0; i < n; i++) add_once(want, names[i]);
+}
+
+void dlss_device_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all) {
+    const XessApi& x = xess();
+    if (!x.ok || g_why.load() > DLSS_OK) return;
+    uint32_t n = 0;
+    const char* const* names = nullptr;
+    if (x.device_exts(dev.instance, dev.physical, &n, &names) != XESS_RESULT_SUCCESS) {
+        logf("vk: Intel XeSS: does not run on this device");
+        return void(g_why = DLSS_NEEDS_RTX);
+    }
+    for (uint32_t i = 0; i < n; i++) {
+        bool have = false;
+        for (const auto& e : all) have |= !strcmp(e.extensionName, names[i]);
+        if (!have) return logf("vk: Intel XeSS: needs the device extension %s", names[i]), void(g_why = DLSS_NEEDS_RTX);
+    }
+    for (uint32_t i = 0; i < n; i++) add_once(want, names[i]);
+}
+
+void* dlss_device_features(void* features2) {
+    const XessApi& x = xess();
+    if (!x.ok || g_why.load() > DLSS_OK) return features2;
+    void* chain = features2;   // (XeSS sets what it needs in it, and may put structures of its own in front)
+    if (x.device_features(dev.instance, dev.physical, &chain) != XESS_RESULT_SUCCESS || !chain) {
+        logf("vk: Intel XeSS: its device features were not given");
+        g_why = DLSS_NEEDS_RTX;
+        return features2;
+    }
+    return chain;
+}
+#endif  // (HOOKSHOT_NGX)
 
 void dlss_probe() {
     if (dlss_why() != DLSS_OK || g_why.load(std::memory_order_relaxed) >= 0 || !dev.device) return;
@@ -904,7 +1207,11 @@ int dlss_status(char* b, size_t n) {
     for (const auto& u : s.uploads)
         if (u.buffer) own += allocation_size(u.memory);
     unsigned long long ngx = 0;
+#ifdef HOOKSHOT_NGX
     const bool ngx_known = s.caps && NVSDK_NGX_SUCCEED(NGX_DLSS_GET_STATS(s.caps, &ngx));
+#else
+    const bool ngx_known = false;
+#endif
     const uint64_t draws = s.actor_draws - s.status_draws, frames = s.actor_frames - s.status_frames;
     char feature[48] = "";
     if (ngx_known) snprintf(feature, sizeof feature, ", its feature %llu MB", ngx >> 20);
@@ -912,7 +1219,7 @@ int dlss_status(char* b, size_t n) {
     if (s.status_passed) snprintf(passed, sizeof passed, ", %llu left to the game's network", (unsigned long long)s.status_passed);
     snprintf(b, n, "; %s: %llu frames drawn%s, %llu features made, jitter %+.3f %+.3f px%s, actor motion %llu draws in %llu "
              "frames (%.3f ms CPU a frame), its images %llu MB%s",
-             mode == 2 ? "DLAA" : "DLSS", (unsigned long long)s.status_drawn, passed, (unsigned long long)s.features_made,
+             mode == 2 ? kAa : kShort, (unsigned long long)s.status_drawn, passed, (unsigned long long)s.features_made,
              s.status_jitter[0], s.status_jitter[1], s.status_motion ? "" : " (no motion)",
              (unsigned long long)draws, (unsigned long long)frames, frames ? s.actor_ns / 1e6 / (double)frames : 0.0,
              (unsigned long long)(own >> 20), feature);
@@ -927,12 +1234,11 @@ void dlss_idle() {
     const uint64_t now = texture_frame_no();
     if (now - s.asked <= 120) return;   // (DLSS in use, or off for under 120 frames (2 s): kept, a row stepped through comes back at once)
     if (s.feature) {
-        NVSDK_NGX_Handle* old = s.feature;
-        later([old] { NVSDK_NGX_VULKAN_ReleaseFeature(old); });
+        release_feature(s.feature);
         s.feature = nullptr;
         s.feature_rw = s.feature_rh = s.feature_ow = s.feature_oh = s.min_rw = s.min_rh = 0;
-        logf("vk: NVIDIA DLSS: not used for 120 frames (the last at frame %llu, now %llu): its feature and images let go; "
-             "the next upscale with it makes them again", (unsigned long long)s.asked, (unsigned long long)now);
+        logf("vk: %s: not used for 120 frames (the last at frame %llu, now %llu): its feature and images let go; "
+             "the next upscale with it makes them again", kName, (unsigned long long)s.asked, (unsigned long long)now);
     }
     for (Image* im : {&s.color, &s.depth, &s.motion, &s.output}) drop_image(*im);
     for (auto& pb : s.pose_buffers)   // (the actor motion's poses and cameras)
@@ -1151,22 +1457,10 @@ bool dlss_upscale(VkCommandBuffer cmd, uint64_t key, bool known, Texture* in, Vk
         vkCmdDispatch(cmd, (rw + 7) / 8, (rh + 7) / 8, 1);
         full_barrier(cmd);
     }
-    NVSDK_NGX_Resource_VK color = resource(s.color, false), depth = resource(s.depth, false), mv = resource(s.motion, false),
-                          output = resource(s.output, true);
-    NVSDK_NGX_VK_DLSS_Eval_Params ep{};
-    ep.Feature.pInColor = &color, ep.Feature.pInOutput = &output;
-    ep.pInDepth = &depth, ep.pInMotionVectors = &mv;
     // (the jitter in render pixels, x right and y down)
     float jitter_px[2];
     dlss_math::jitter_to_pixels(cam.jitter, rw, rh, jitter_px);
-    ep.InJitterOffsetX = jitter_px[0], ep.InJitterOffsetY = jitter_px[1];
-    ep.InRenderSubrectDimensions = {rw, rh};
-    ep.InReset = reset ? 1 : 0;
-    ep.InMVScaleX = ep.InMVScaleY = 1.0f;
-    const NVSDK_NGX_Result r = NGX_VULKAN_EVALUATE_DLSS_EXT(cmd, s.feature, s.params, &ep);
-    note_work();   // (as at its creation: the barrier after it, and the game's next one, are recorded)
-    if (NVSDK_NGX_FAILED(r)) {
-        logf("vk: NVIDIA DLSS: its evaluation failed (%#x): the game's upscale from now on", (unsigned)r);
+    if (!evaluate(cmd, jitter_px, reset, rw, rh)) {
         s.failed = true;
         return false;   // (the network writes its output over whatever the evaluation did)
     }
@@ -1188,12 +1482,12 @@ bool dlss_upscale(VkCommandBuffer cmd, uint64_t key, bool known, Texture* in, Vk
     s.last_frame = now;
     s.last_jitter[0] = cam.jitter[0], s.last_jitter[1] = cam.jitter[1];
     // (for the status line, dlss_status: the jitter DLSS was given, in render pixels)
-    s.status_jitter[0] = ep.InJitterOffsetX, s.status_jitter[1] = ep.InJitterOffsetY, s.status_motion = motion;
+    s.status_jitter[0] = jitter_px[0], s.status_jitter[1] = jitter_px[1], s.status_motion = motion;
     s.status_drawn++;
 
     std::memcpy(s.last_vp, cam.current, sizeof s.last_vp);
     s.jitter_w = rw, s.jitter_h = rh, s.jitter_phases = (uint32_t)dlss_jitter_phases(rw, rh, dw, dh);
-    if (s.reason) logf("vk: NVIDIA DLSS draws the upscale again (frame %llu)", (unsigned long long)now);
+    if (s.reason) logf("vk: %s draws the upscale again (frame %llu)", kName, (unsigned long long)now);
     s.reason = nullptr;
     // (RX_VK_DLSS_DEBUG=alternate, a test's reference: every other frame the game's own upscale over DLSS's, which still
     // draws and keeps its history. A frame of DLSS then has the game's picture of the frames either side to be measured against)
@@ -1201,7 +1495,7 @@ bool dlss_upscale(VkCommandBuffer cmd, uint64_t key, bool known, Texture* in, Vk
     if (alternate && (now & 1)) return false;
     if (!s.told_first) {
         s.told_first = true;
-        logf("vk: NVIDIA DLSS draws OoT's upscale: %ux%u -> %ux%u%s", rw, rh, ow, oh, show_motion ? " (its motion shown)" : "");
+        logf("vk: %s draws OoT's upscale: %ux%u -> %ux%u%s", kName, rw, rh, ow, oh, show_motion ? " (its motion shown)" : "");
     }
     return true;
 }
@@ -1212,6 +1506,7 @@ bool dlss_built() { return false; }
 void dlss_probe() {}
 void dlss_instance_extensions(std::vector<const char*>&, const std::vector<VkExtensionProperties>&) {}
 void dlss_device_extensions(std::vector<const char*>&, const std::vector<VkExtensionProperties>&) {}
+void* dlss_device_features(void* features2) { return features2; }
 void dlss_note_scene_draw(const Texture*, uint32_t, uint32_t, Texture*, uint64_t, uint64_t) {}
 bool dlss_scene_size(uint32_t, uint32_t) { return false; }
 bool dlss_upscale(VkCommandBuffer, uint64_t, bool, Texture*, VkImageView, Texture*, VkImageView, uint32_t) { return false; }
diff --git a/gpu/vk/vk_dlss.h b/gpu/vk/vk_dlss.h
index 6d6eee9..9b54d97 100644
--- a/gpu/vk/vk_dlss.h
+++ b/gpu/vk/vk_dlss.h
@@ -29,6 +29,9 @@ bool dlss_full_scale();
 void dlss_instance_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all);
 /// The device's extensions DLSS needs, added to `want` when the device has them
 void dlss_device_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all);
+/// The features the upscaler needs (Intel XeSS), set in the VkPhysicalDeviceFeatures2 chain: the head for
+/// vkCreateDevice (XeSS may put structures of its own in front), features2 itself when there is nothing to add
+void* dlss_device_features(void* features2);
 /// A draw into a scene-sized target (colour target 0 and the depth target), with the vertex stage's uniform buffer 3 at
 /// `camera` (its size): the camera of the frame, when that buffer holds one
 void dlss_note_scene_draw(const Texture* color0, uint32_t width, uint32_t height, Texture* depth, uint64_t camera,
diff --git a/gpu/vk/vk_main.cpp b/gpu/vk/vk_main.cpp
index 99304ec..083f1c0 100644
--- a/gpu/vk/vk_main.cpp
+++ b/gpu/vk/vk_main.cpp
@@ -418,7 +418,7 @@ bool create_device() {
                         f.features.sparseResidencyImage2D && !(getenv("RX_VK_SPARSE") && !atoi(getenv("RX_VK_SPARSE")));
     dev.sparse_buffers = (families[dev.queue_family].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) && f.features.sparseBinding;
     VkDeviceCreateInfo ci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO};
-    ci.pNext = &f;
+    ci.pNext = dlss_device_features(&f);   // (Intel iGPU: Intel XeSS's, vk_dlss.cpp)
     ci.queueCreateInfoCount = compute_family != UINT32_MAX ? 2 : 1;
     ci.pQueueCreateInfos = qs;
     ci.enabledExtensionCount = (uint32_t)want.size();
diff --git a/gpu/vk/vk_menu.cpp b/gpu/vk/vk_menu.cpp
index e9474bb..bb831d0 100644
--- a/gpu/vk/vk_menu.cpp
+++ b/gpu/vk/vk_menu.cpp
@@ -148,7 +148,11 @@ unsigned upscalers_available() {
 }
 int upscaler() { return fsr3_wanted() ? (int)ur::FSR3 : dlss_mode(); }
 const char* tx_upscaler(int) {
+#ifdef HOOKSHOT_XESS
+    static const char* t[] = {"Game's own", "Intel XeSS", "Intel XeSS AA", "AMD FSR 3.1"};
+#else
     static const char* t[] = {"Game's own", "NVIDIA DLSS", "NVIDIA DLAA", "AMD FSR 3.1"};
+#endif
     return t[std::clamp(upscaler(), 0, ur::kUpscalers - 1)];
 }
 /// The Upscaler: the one shown (what runs, upscaler()), and with the game's own, why DLSS and DLAA are not offered
diff --git a/nx2/build.py b/nx2/build.py
index 37406c8..195fc14 100644
--- a/nx2/build.py
+++ b/nx2/build.py
@@ -844,6 +844,11 @@ def main():
         ngx_flags = ["-DHOOKSHOT_NGX", "-I" + os.path.join(NGX_SDK, "include")] if NGX_SDK else []
         if NGX_SDK:
             print("the Vulkan back end: with NVIDIA DLSS (NVIDIA's NGX SDK in %s)" % NGX_SDK, flush=True)
+        # HOOKSHOT_XESS_SDK=<Intel's XeSS SDK>: Intel XeSS in DLSS's place (vk_dlss.cpp; the menu's names in vk_menu.cpp)
+        xess_sdk = "" if NGX_SDK else os.environ.get("HOOKSHOT_XESS_SDK", "")
+        xess_flags = ["-DHOOKSHOT_XESS", "-I" + os.path.join(xess_sdk, "inc")] if xess_sdk else []
+        if xess_sdk:
+            print("the Vulkan back end: with Intel XeSS (its SDK in %s)" % xess_sdk, flush=True)
         # (Eden's recompiler and sirit see only their own headers: a change to the back end's vk_*.h does not
         # rebuild their ~500 files; the back end itself depends on everything, suyu's texture decoders included)
         vk_lib_deps = tree_headers(os.path.join(vk, "eden"), os.path.join(vk, "sirit"), os.path.join(vk, "compat"),
@@ -856,7 +861,8 @@ def main():
             vk_jobs.append((cxx, src, os.path.join(obj_dir, "vk_" + name + ".o"),
                             vflags + ([shader_stamp] if os.path.basename(src) == "vk_shader.cpp" else []) +
                             ([stamp_def] if os.path.basename(src) == "vk_menu.cpp" else []) +
-                            (ngx_flags if os.path.basename(src) == "vk_dlss.cpp" else []),
+                            (ngx_flags if os.path.basename(src) == "vk_dlss.cpp" else []) +
+                            (xess_flags if os.path.basename(src) in ("vk_dlss.cpp", "vk_menu.cpp") else []),
                             vk_lib_deps if lib else vdeps + (logo_text if os.path.basename(src) == "vk_menu.cpp" else b"")))
         if NGX_SDK and not LINUX and toolchain.mingw():
             # (llvm-mingw: NVIDIA's static library was built with Microsoft's compiler for its static runtime (/MT). What it asks
> [@ploob-boop](https://github.com/ploob-boop) commented on GitHub · 2026-11-12 10:58 UTC · [original](https://github.com/NotRexed/Hookshot/issues/999#issuecomment-6821731179) ## Intel Arc 140V (Lunar Lake iGPU): white-flash flicker fixed, crash fixed, Intel XeSS added **Base:** Hookshot v2.0.14, Vulkan back end. **Device:** MSI Claw, Intel Arc 140V (integrated), Windows, 1920x1200. **Game:** Ocarina of Time (Switch 2). ### TL;DR | # | Problem | Status | Patch | |---|---|---|---| | 1 | Whole-screen white flash, once every 8 frames | **Fixed** (root cause found, measured) | `01` | | 2 | Crash in `igvk64.dll` inside `vkGetPipelineCacheData` (this issue) | **Fixed** | `01` | | 3 | Smaller Intel flicker (menu/logo haze, leaves, stale tiles) | **Fixed** on this device | `01` | | 4 | No upscaler on Intel (DLSS is NVIDIA-only) | **Added:** Intel XeSS in the DLSS slot | `02` | Both patches apply cleanly to v2.0.14 with `git apply`. They were checked in order on a clean tree: `01`, then `02`. --- ### 1. White flash every 8 frames: root cause **Symptom.** One frame in 8 turns white over the whole screen: the fog/haze loses its sky light. It is always the frame where the reflection probe's cubemap is swapped in. **Trigger.** It happens only when `maybe_split` (`gpu/vk/vk_replay.cpp`) cuts one guest submission into several Vulkan command buffers (`RX_VK_CHUNK`, default 120). - `RX_VK_CHUNK=0` (no split): 0 flashes, but 18 fps drops to 10. - Even one split per submission is enough to cause flashes. **Cause.** At every part boundary, `maybe_split` called `flush_ub_updates()`. That wrote the inline uniform updates (`NOP_UPDATE_UB`) recorded so far into guest memory in the middle of the submission. Then it moved `ub_first_of_recording` forward. An unsplit submission writes all of its updates once, at its end, in `finish_recording`. Draws in later parts that read those ranges directly from guest memory (not through an overlay copy) therefore saw a different state than an unsplit run. On the probe-swap frame this gives the white haze. **Fix (3 lines).** At a part boundary, do not flush and do not advance `ub_first_of_recording`. The last part flushes the whole submission's updates, exactly like an unsplit submission. `RX_VK_SPLIT_UBFLUSH=1` restores the old behaviour. **Measured.** Same scene, 25 s each, 4 back-to-back runs, `RX_VK_CHUNK=120`: | Build | Frames | White flashes | FPS | |---|---|---|---| | with fix | 458 | **0** | 18.4 | | without fix | 455 | 56 | 17.7 | | with fix | 421 | **0** | — | | without fix | 492 | 56 | — | No fps cost. The split keeps its benefit, while `RX_VK_CHUNK=0` costs about 45% fps. **Ruled out first** (each tested on its own, no change): - CPU/GPU races at record time: - waiting for every part on the GPU before recording the next; - holding all parts until the submission ends. - Page table written immediately (`RX_VK_TABLE_NOW=1`). - Event ADD signals; render conditions. - Small `CLEAR_BUF` on the CPU (`RX_VK_CLEAR_CPU_MAX=0`). - UB snapshot on/off; texture handles read live. - Barriers and layouts; XeSS on/off. Lichii (Eden-based) splits just as often (every 128 draws) and does not flicker. That confirms the boundary itself is fine. ### 2. Crash in `vkGetPipelineCacheData` (this issue) Intel's driver dereferenced a null pointer (`igvk64.dll+0x79ddf3`) in `vkGetPipelineCacheData`. This happened while other threads were building pipelines with the same cache. Vulkan allows that usage, so this is a driver race. **Fix:** on Intel (`vendorID 0x8086`), the pipeline cache is saved at exit only, not in the background. `RX_VK_PCACHE_BG_SAVE=1` restores the old behaviour. ### 3. Other Intel / iGPU fixes in patch `01` | Change | File | Why | Revert switch | |---|---|---|---| | Sampling a target the pass already wrote ends the pass, even when the draw writes that target too | `vk_draw.cpp` `draw_hazards` | Intel read the earlier draw's pixels unsynchronized: flicker | none (one condition) | | A draw after a draw that wrote storage in the same pass gets its own pass (full barrier) | `vk_draw.cpp` `draw` | Inside a pass nothing orders storage writes with later reads: Intel showed tiles of stale results | `RX_VK_NO_STORAGE_HAZARD=1` | | Uniform buffers copied at record time by default on an iGPU | `vk_draw.cpp` `uniform_range` | No VRAM mirror on an iGPU: the GPU reads guest memory the game is already rewriting | `RX_VK_UB_SNAPSHOT=0` | | GPU results written in place are noted for the readback wait on an iGPU | `vk_memory.cpp` `gpu_buffer_bind` | OoT's exposure meter was read before the GPU wrote it: brightness pumped | none | ### 4. Intel XeSS (patch `02`) XeSS Super Resolution takes DLSS's place in `gpu/vk/vk_dlss.cpp`. It uses the same inputs DLSS already gets: camera, LDR colour, depth, low-res motion vectors in pixels, and the host's jitter. Only the NGX calls are replaced by XeSS calls. - **Build-time switch:** `HOOKSHOT_XESS_SDK=<path to Intel's XeSS SDK>` adds `-DHOOKSHOT_XESS` (`nx2/build.py`). Without it, or when the NGX SDK is set, nothing changes. - **Run time:** `libxess.dll` is loaded from the folder of the game's executable, and its functions are resolved at run time. No import library is needed, so this works with llvm-mingw. - **Device setup:** XeSS's required instance/device extensions and features are added at device creation (`vk_main.cpp`: `dlss_device_features`). - **Menu:** the Upscaler row shows `Intel XeSS` and `Intel XeSS AA` instead of `NVIDIA DLSS` and `NVIDIA DLAA` (`vk_menu.cpp`). - **Tested:** `libxess.dll` 2.0.2. Log: `Intel XeSS draws OoT's upscale: 1536x864 -> 2048x1152`. ### Recommended settings on Intel Arc 140V (measured with both patches) **Best:** Handheld profile on, Render resolution `Auto`, Upscaler `Intel XeSS`. ```ini handheld_profile=1 resolution=0 output_res=0 ultrawide=0 dlss=1 ``` Same scene, 25 s per run: | Profile | Render resolution | Output | 3D (XeSS input) | XeSS | FPS | White flashes | |---|---|---|---|---|---|---| | **Handheld** | **Auto** | **1536x864** | **1152x648** | **on** | **~24-25** | **0** | | Docked | Auto | 2048x1152 | 1536x864 | on | ~23 | 0 | | Handheld | 75% | 1536x864 | 1152x648 | on | ~19 | 0 | | Docked, before these patches | Auto | 2048x1152 | 1536x864 | on | ~18 | 56 per ~450 frames | **Notes** 1. **Keep Render resolution on `Auto`.** With a fixed value (67% or 50%), XeSS did not run at all: the log has no XeSS lines and the game's own upscale is used. The higher fps in that case comes from not running XeSS. 2. **Handheld + Auto is the fastest with XeSS.** It is faster than Docked + Auto and faster than Handheld + 75%. 3. **Aspect ratio.** On a 16:10 screen (1920x1200) the 1536x864 output is 16:9, so there are thin black bars top and bottom. `ultrawide=1` (fill 16:10) works in Docked only, not with the handheld profile. 4. **Leave the split on.** Keep `RX_VK_CHUNK` at its default (120). `RX_VK_CHUNK=0` also removes the flashes, but costs about 45% fps. ### How to apply and build 1. Start from a clean v2.0.14 source tree (the folder that contains `gpu/` and `nx2/`). 2. Apply the patches, in this order: ``` git apply 01-intel-flicker-and-crash-fixes.patch git apply 02-intel-xess.patch ``` 3. Download Intel's XeSS SDK from https://github.com/intel/xess/releases. 4. Build with the SDK path set: ``` set HOOKSHOT_XESS_SDK=C:\path\to\xess_sdk python nx2\build.py --standalone ``` 5. Copy `libxess.dll` from the SDK into the game's folder, next to the executable. 6. In the game, open the Insert menu and set Graphics → Upscaler → `Intel XeSS`. Only patch `01` is needed for the flicker and crash fixes. Patch `02` is optional. ### Still open (not caused by these patches) - A single object (the player, a tree stump) flashed bright once or twice in several minutes of play. It is rare, local, and not periodic, so it looks like a separate issue. - A separate finding, not included in either patch: in `submit_recording`, `value_rec.emplace(r.value, seq)` keeps the **first** part's recording for a split submission, because `emplace` never overwrites. As a result, `wait_recorded()` can return once part 1 is done. Replacing it with `value_rec[v] = std::max(value_rec[v], seq)` did not change the flashes and cost fps here, but the mapping looks wrong. <details> <summary><b>01-intel-flicker-and-crash-fixes.patch</b> (4 files, 90 lines)</summary> ```diff diff --git a/gpu/vk/vk_draw.cpp b/gpu/vk/vk_draw.cpp index 6e1c49d..830144c 100644 --- a/gpu/vk/vk_draw.cpp +++ b/gpu/vk/vk_draw.cpp @@ -2148,7 +2148,8 @@ VkDescriptorBufferInfo uniform_range(const NvnState& s, const Stage& st, uint32_ return {cur->stream, at, n}; } // (RX_VK_UB_SNAPSHOT=1: the contents as they are now, in the stream, not the pool the GPU reads later) - static const bool snapshot = getenv("RX_VK_UB_SNAPSHOT") != nullptr; + static const bool snapshot = getenv("RX_VK_UB_SNAPSHOT") ? atoi(getenv("RX_VK_UB_SNAPSHOT")) != 0 + : dev.props.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU; // (Intel iGPU: no mirrors on an iGPU) BufferRef b = snapshot ? BufferRef{} : gpu_buffer_bind(u.addr, n, false); if (b && b.offset % align == 0) return {b.buffer, b.offset, n}; const uint64_t at = stream_alloc(n, align); // (unaligned or outside the pools: copied into the stream) @@ -3772,7 +3773,7 @@ uint16_t draw_hazards(VkCommandBuffer cmd, const Program& p, const NvnState& s, const char* kind = nullptr; for (const Texture* t : gfx_sampled()) { const int k = slot_of(t); - if (k >= 0 && (pass().written >> k & 1) && !(writes >> k & 1)) { kind = "samples a target the pass wrote"; break; } + if (k >= 0 && (pass().written >> k & 1)) { kind = "samples a target the pass wrote"; break; } // (Intel iGPU: also when it writes it: Intel flicker) } if (!kind && writes_sampled_target(writes, pass().sampled_targets)) kind = "writes a target an earlier draw sampled"; @@ -5593,6 +5594,24 @@ void draw(VkCommandBuffer cmd, NvnState& s, const DrawArgs& d) { if (!same_desc) gfx_sampled() = draw_sampled(); static const bool barrier_every = getenv("RX_VK_BARRIER_EVERY_DRAW") != nullptr; // (an experiment: a pass each) if (barrier_every) end_pass(cmd); + { // a draw after one of this pass whose graphics stages wrote storage: in a pass of its own (begin_pass's full + // barrier). Inside a pass nothing orders a shader's storage writes with a later draw's reads; Intel showed tiles of + // stale results. RX_VK_NO_STORAGE_HAZARD=1: as before + static const bool off = getenv("RX_VK_NO_STORAGE_HAZARD") != nullptr; + static thread_local bool stored_before; + bool stores = false; + for (const auto& st : p.stages) { + for (const auto& d : st.info.storage_buffers_descriptors) stores |= d.is_written; + for (const auto& d : st.info.image_descriptors) stores |= d.is_written; + for (const auto& d : st.info.image_buffer_descriptors) stores |= d.is_written; + } + if (!off && stored_before && pass().open) { + static int logged; + if (logged++ < 6) logf("vk: a draw (program %016llx) after a storage write in its pass: a pass of its own", (unsigned long long)p.key); + end_pass(cmd); + } + stored_before = stores; + } uint16_t writes; { FINE_SCOPE(FT_HAZARD); @@ -7454,6 +7473,9 @@ int64_t draw_slotgen_saves() { // (RX_VK_DESC_SLOTGEN: of those, what the glob void pipeline_cache_save(bool force) { if (!pcache || (!force && pcache_built_since_save < 32)) return; + // Intel's driver read a null pointer in vkGetPipelineCacheData while other threads built pipelines with the cache + // (igvk64.dll+0x79ddf3, Hookshot issue 999): a driver race, legal in Vulkan. There the cache is written at exit only + if (!force && dev.props.vendorID == 0x8086 && !getenv("RX_VK_PCACHE_BG_SAVE")) return; pcache_built_since_save = 0; // (on a thread of its own unless forced, at exit: ~100 MB fetched and written took the replay thread 100+ ms at // each scene change; the cache is internally synchronized, pipelines go on being built meanwhile) diff --git a/gpu/vk/vk_memory.cpp b/gpu/vk/vk_memory.cpp index 6a36e31..0b953ab 100644 --- a/gpu/vk/vk_memory.cpp +++ b/gpu/vk/vk_memory.cpp @@ -2585,6 +2585,8 @@ BufferRef gpu_buffer_bind(uint64_t gpu, uint64_t size, bool written) { const uint64_t off = guest - p->lo; if (!mirror_on() || !mirror_upload || p->mstate == Pool::MIRROR_REFUSED) { if (mirror_on()) { auto& e = direct_by_pool[p->lo]; e.first++, e.second = mirror_upload ? (int)p->mstate : 9; direct_bytes_by_pool[p->lo] += size; } + if (written && size <= (1u << 20)) // (Intel iGPU: GPU results in place on an iGPU too, so invalidate() waits for them) + wb_pages_note(p->lo + off, p->lo + std::min(off + size, p->hi - p->lo), mem_rec_value); return remember({p->buffer, off, size, 0}); } // (a pool's mirror is made at its first binding, in this part's uploads: bound in guest memory until the next diff --git a/gpu/vk/vk_replay.cpp b/gpu/vk/vk_replay.cpp index cf5db3c..64c2bca 100644 --- a/gpu/vk/vk_replay.cpp +++ b/gpu/vk/vk_replay.cpp @@ -1237,11 +1237,12 @@ void maybe_split(VkCommandBuffer& cmd) { static const int64_t every = getenv("RX_VK_CHUNK") ? atoll(getenv("RX_VK_CHUNK")) : 120; if (!rec_cur || every <= 0 || ++part_work < every || rec_parts + 1 >= kMaxParts || dump_on()) return; end_pass(cmd); - flush_ub_updates(cmd); + static const bool no_ubflush = getenv("RX_VK_SPLIT_UBFLUSH") == nullptr; // the updates stay for the last part; =1: flushed at each part as before + if (!no_ubflush) flush_ub_updates(cmd); memory_end_submission(cmd); full_barrier(cmd, "barrier (recording end)"); submit_recording(*rec_cur, false); - ub_first_of_recording = ub_updates.size(); + if (!no_ubflush) ub_first_of_recording = ub_updates.size(); Recording& r = begin_recording(rec_cur_value); memory_begin_submission(); full_barrier(r.cmd, "barrier (recording start)"); ``` </details> <details> <summary><b>02-intel-xess.patch</b> (6 files, 548 lines)</summary> ```diff diff --git a/gpu/vk/vk_dlss.cpp b/gpu/vk/vk_dlss.cpp index 1f94e33..d5a095b 100644 --- a/gpu/vk/vk_dlss.cpp +++ b/gpu/vk/vk_dlss.cpp @@ -68,7 +68,12 @@ #include "nvsdk_ngx_vk.h" #include "nvsdk_ngx_helpers.h" #include "nvsdk_ngx_helpers_vk.h" - +#endif +#ifdef HOOKSHOT_XESS // (Intel iGPU: Intel XeSS in DLSS's place, patch_xess.py) +#include <type_traits> +#include "xess/xess_vk.h" +#endif +#if defined(HOOKSHOT_NGX) || defined(HOOKSHOT_XESS) #include "shaders/dlss_actor_frag_spv.h" #include "shaders/dlss_actor_vert_spv.h" #include "shaders/dlss_out_comp_spv.h" @@ -107,10 +112,16 @@ int dlss_mode() { bool dlss_wanted() { return dlss_mode() != 0; } bool dlss_full_scale() { return dlss_mode() == 2; } -#ifdef HOOKSHOT_NGX +#if defined(HOOKSHOT_NGX) || defined(HOOKSHOT_XESS) namespace { +#ifdef HOOKSHOT_NGX +constexpr const char *kName = "NVIDIA DLSS", *kShort = "DLSS", *kAa = "DLAA"; +#else +constexpr const char *kName = "Intel XeSS", *kShort = "XeSS", *kAa = "XeSS AA"; +#endif + struct Image { VkImage image = VK_NULL_HANDLE; VmaAllocation memory = nullptr; @@ -133,9 +144,13 @@ struct State { std::mutex mu; bool ngx_tried = false, ngx_ok = false; std::atomic<bool> failed{false}; // (atomic: dlss_jitter_size reads it on the game's thread, without the lock) +#ifdef HOOKSHOT_NGX NVSDK_NGX_Parameter* caps = nullptr; NVSDK_NGX_Parameter* params = nullptr; NVSDK_NGX_Handle* feature = nullptr; +#else + xess_context_handle_t feature = nullptr; +#endif uint32_t feature_rw = 0, feature_rh = 0, feature_ow = 0, feature_oh = 0; // (its largest render size, its output) uint32_t min_rw = 0, min_rh = 0; unsigned feature_preset = ~0u; // (the preset it was made with: another one from the menu makes it again) @@ -215,7 +230,7 @@ void note_output(const Texture* out, uint32_t level) { bool fail(const char* why) { // (the network runs: why, when it changes and every 600th time) State& s = st(); - if (why != s.reason || ++s.reason_repeats % 600 == 0) logf("vk: NVIDIA DLSS: the game's upscale runs (%s)", why); + if (why != s.reason || ++s.reason_repeats % 600 == 0) logf("vk: %s: the game's upscale runs (%s)", kName, why); if (why != s.reason) s.reason = why, s.reason_repeats = 0; return false; } @@ -273,6 +288,7 @@ bool make_image(VkCommandBuffer cmd, Image& im, VkFormat format, uint32_t w, uin return true; } +#ifdef HOOKSHOT_NGX NVSDK_NGX_Resource_VK resource(const Image& im, bool write) { return NVSDK_NGX_Create_ImageView_Resource_VK(im.view, im.image, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, im.format, im.width, im.height, write); @@ -450,6 +466,84 @@ bool ngx_init() { return true; } +#else // (HOOKSHOT_XESS) + +// Intel XeSS: libxess.dll beside the game's executable, its functions taken at run time (no import library to link; +// without the DLL the Upscaler row keeps the game's own). RX_VK_DLSS=0: not loaded at all +struct XessApi { + bool ok = false; + decltype(&xessVKGetRequiredInstanceExtensions) instance_exts = nullptr; + decltype(&xessVKGetRequiredDeviceExtensions) device_exts = nullptr; + decltype(&xessVKGetRequiredDeviceFeatures) device_features = nullptr; + decltype(&xessVKCreateContext) create = nullptr; + decltype(&xessVKInit) init = nullptr; + decltype(&xessVKExecute) execute = nullptr; + decltype(&xessGetOptimalInputResolution) optimal = nullptr; + decltype(&xessDestroyContext) destroy = nullptr; + decltype(&xessGetVersion) version = nullptr; +}; +const XessApi& xess() { + static const XessApi api = [] { + XessApi a; + if (env_switch("RX_VK_DLSS") == 0) return a; + std::wstring exe(32768, L'\0'); + const DWORD n = GetModuleFileNameW(nullptr, exe.data(), (DWORD)exe.size()); + if (!n || n >= exe.size()) return a; + exe.resize(n); + const std::filesystem::path dll = std::filesystem::path(exe).parent_path() / L"libxess.dll"; + const HMODULE lib = LoadLibraryW(dll.c_str()); + if (!lib) { + logf("vk: Intel XeSS: no libxess.dll beside the game (%lu): the game's upscale", GetLastError()); + return a; + } + bool all = true; + const auto get = [&](auto& f, const char* name) { + f = reinterpret_cast<std::remove_reference_t<decltype(f)>>(reinterpret_cast<void*>(GetProcAddress(lib, name))); + if (!f) logf("vk: Intel XeSS: libxess.dll has no %s", name), all = false; + }; + get(a.instance_exts, "xessVKGetRequiredInstanceExtensions"); + get(a.device_exts, "xessVKGetRequiredDeviceExtensions"); + get(a.device_features, "xessVKGetRequiredDeviceFeatures"); + get(a.create, "xessVKCreateContext"); + get(a.init, "xessVKInit"); + get(a.execute, "xessVKExecute"); + get(a.optimal, "xessGetOptimalInputResolution"); + get(a.destroy, "xessDestroyContext"); + get(a.version, "xessGetVersion"); + a.ok = all; + return a; + }(); + return api; +} + +/// XeSS on this device, once (the NGX path's name, so the chain calls it the same): a context made and let go again +bool ngx_init() { + State& s = st(); + if (s.ngx_tried) return s.ngx_ok; + s.ngx_tried = true; + const XessApi& x = xess(); + if (!x.ok || (g_why.load() > DLSS_OK)) { + if (g_why.load() <= DLSS_OK) g_why = DLSS_NO_RUNTIME; + return false; + } + xess_version_t v{}; + x.version(&v); + xess_context_handle_t c = nullptr; + const xess_result_t r = x.create(dev.instance, dev.physical, dev.device, &c); + if (r != XESS_RESULT_SUCCESS || !c) { + logf("vk: Intel XeSS %u.%u.%u: no context on this device (%d)", v.major, v.minor, v.patch, (int)r); + g_why = DLSS_NEEDS_RTX; + return false; + } + x.destroy(c); + logf("vk: Intel XeSS %u.%u.%u started", v.major, v.minor, v.patch); + s.ngx_ok = true; + g_why = DLSS_OK; + return true; +} + +#endif // (HOOKSHOT_NGX) + /// One compute pipeline with push descriptors; what it made is destroyed again when a step fails bool make_pipeline(const uint32_t* code, size_t size, const VkDescriptorType* types, uint32_t n, uint32_t push, VkDescriptorSetLayout& set, VkPipelineLayout& layout, VkPipeline& pipe) { @@ -721,6 +815,7 @@ int actor_motion(VkCommandBuffer cmd, const Camera& cam, const float* before, ui return (int)draws.size(); } +#ifdef HOOKSHOT_NGX /// DLSS's preset: the DLSS model row's, RX_VK_DLSS_PRESET over it (upscaler_rows.h dlss_preset) unsigned wanted_preset() { static const char* const env = getenv("RX_VK_DLSS_PRESET"); @@ -809,10 +904,164 @@ bool make_feature(VkCommandBuffer cmd, uint32_t rw, uint32_t rh, uint32_t ow, ui return true; } + +/// NGX's evaluation of the chain's inputs into s.output (false: it failed, said) +bool evaluate(VkCommandBuffer cmd, const float jitter_px[2], bool reset, uint32_t rw, uint32_t rh) { + State& s = st(); + NVSDK_NGX_Resource_VK color = resource(s.color, false), depth = resource(s.depth, false), mv = resource(s.motion, false), + output = resource(s.output, true); + NVSDK_NGX_VK_DLSS_Eval_Params ep{}; + ep.Feature.pInColor = &color, ep.Feature.pInOutput = &output; + ep.pInDepth = &depth, ep.pInMotionVectors = &mv; + ep.InJitterOffsetX = jitter_px[0], ep.InJitterOffsetY = jitter_px[1]; + ep.InRenderSubrectDimensions = {rw, rh}; + ep.InReset = reset ? 1 : 0; + ep.InMVScaleX = ep.InMVScaleY = 1.0f; + const NVSDK_NGX_Result r = NGX_VULKAN_EVALUATE_DLSS_EXT(cmd, s.feature, s.params, &ep); + note_work(); // (as at its creation: the barrier after it, and the game's next one, are recorded) + if (NVSDK_NGX_FAILED(r)) { + logf("vk: NVIDIA DLSS: its evaluation failed (%#x): the game's upscale from now on", (unsigned)r); + return false; + } + return true; +} +void release_feature(NVSDK_NGX_Handle* f) { + later([f] { NVSDK_NGX_VULKAN_ReleaseFeature(f); }); +} + +#else // (HOOKSHOT_XESS) + +void release_feature(xess_context_handle_t f) { + later([f] { xess().destroy(f); }); +} + +/// XeSS's context for this output size, made again when it or the render size's mode changes. Its init uploads its network +/// on the queue (its own submits): taken while nothing else submits (dev.present_mu, then the present queue's lock) +bool make_feature(VkCommandBuffer, uint32_t rw, uint32_t rh, uint32_t ow, uint32_t oh) { + State& s = st(); + if (s.feature && s.feature_ow == ow && s.feature_oh == oh && rw <= s.feature_rw && rh <= s.feature_rh && + rw >= s.min_rw && rh >= s.min_rh) + return true; + char why[160]; + if (!s.feature && !s.features_made) snprintf(why, sizeof why, "the first"); + else if (!s.feature) snprintf(why, sizeof why, "again after it was let go"); + else if (s.feature_ow != ow || s.feature_oh != oh) + snprintf(why, sizeof why, "the output went from %ux%u to %ux%u", s.feature_ow, s.feature_oh, ow, oh); + else + snprintf(why, sizeof why, "the render size %ux%u left its range, %ux%u to %ux%u", rw, rh, s.min_rw, s.min_rh, + s.feature_rw, s.feature_rh); + if (s.feature) release_feature(s.feature), s.feature = nullptr; + const XessApi& x = xess(); + xess_context_handle_t c = nullptr; + if (x.create(dev.instance, dev.physical, dev.device, &c) != XESS_RESULT_SUCCESS || !c) { + logf("vk: Intel XeSS: no context"); + s.failed = true; + return false; + } + // (the quality mode whose range of render sizes holds this one; AA when the render size is the output's) + static const xess_quality_settings_t scaled[] = {XESS_QUALITY_SETTING_ULTRA_QUALITY_PLUS, XESS_QUALITY_SETTING_ULTRA_QUALITY, + XESS_QUALITY_SETTING_QUALITY, XESS_QUALITY_SETTING_BALANCED, + XESS_QUALITY_SETTING_PERFORMANCE, XESS_QUALITY_SETTING_ULTRA_PERFORMANCE}; + const xess_2d_t out{ow, oh}; + xess_quality_settings_t mode = XESS_QUALITY_SETTING_AA; + uint32_t max_w = ow, max_h = oh, min_w = ow, min_h = oh; + bool found = rw == ow && rh == oh; + for (const xess_quality_settings_t q : scaled) { + if (found) break; + xess_2d_t opt{}, lo{}, hi{}; + if (x.optimal(c, &out, q, &opt, &lo, &hi) != XESS_RESULT_SUCCESS) continue; + if (rw >= lo.x && rh >= lo.y && rw <= hi.x && rh <= hi.y) + mode = q, max_w = hi.x, max_h = hi.y, min_w = lo.x, min_h = lo.y, found = true; + } + if (!found) { + x.destroy(c); + return fail("no XeSS mode takes this render size"); + } + xess_vk_init_params_t ip{}; + ip.outputResolution = out; + ip.qualitySetting = mode; + ip.initFlags = XESS_INIT_FLAG_LDR_INPUT_COLOR; // (LDR colour, already tonemapped; motion at the render size in pixels) + const auto t0 = std::chrono::steady_clock::now(); + xess_result_t r; + { + std::lock_guard qk(dev.present_mu); + std::lock_guard pq(dev.present_queue_mu); + r = x.init(c, &ip); + } + const double made_ms = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - t0).count(); + if (r != XESS_RESULT_SUCCESS) { + logf("vk: Intel XeSS: not initialised (%d) for %ux%u -> %ux%u", (int)r, max_w, max_h, ow, oh); + x.destroy(c); + s.failed = true; + return false; + } + s.feature = c; + s.feature_rw = max_w, s.feature_rh = max_h, s.feature_ow = ow, s.feature_oh = oh, s.min_rw = min_w, s.min_rh = min_h; + s.features_made++; + logf("vk: Intel XeSS: context %llu made (%s; frame %llu, %.1f ms of CPU): %ux%u (%ux%u to %ux%u) -> %ux%u, mode %d", + (unsigned long long)s.features_made, why, (unsigned long long)texture_frame_no(), made_ms, rw, rh, min_w, min_h, max_w, + max_h, ow, oh, (int)mode); + s.last_frame = 0; // (a new history) + return true; +} + +/// The inputs from GENERAL to what XeSS reads them in (SHADER_READ_ONLY_OPTIMAL), or back (to_read false) +void input_layouts(VkCommandBuffer cmd, bool to_read) { + State& s = st(); + VkImageMemoryBarrier2 b[3]{}; + const Image* im[3] = {&s.color, &s.depth, &s.motion}; + for (int i = 0; i < 3; i++) { + b[i] = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2}; + b[i].srcStageMask = b[i].dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; + b[i].srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; + b[i].dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT | VK_ACCESS_2_MEMORY_WRITE_BIT; + b[i].oldLayout = to_read ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + b[i].newLayout = to_read ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; + b[i].srcQueueFamilyIndex = b[i].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + b[i].image = im[i]->image, b[i].subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + } + VkDependencyInfo di{VK_STRUCTURE_TYPE_DEPENDENCY_INFO}; + di.imageMemoryBarrierCount = 3, di.pImageMemoryBarriers = b; + vkCmdPipelineBarrier2(cmd, &di); +} + +xess_vk_image_view_info view_info(const Image& im) { + xess_vk_image_view_info v{}; + v.imageView = im.view, v.image = im.image, v.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + v.format = im.format, v.width = im.width, v.height = im.height; + return v; +} + +/// XeSS's execution of the chain's inputs into s.output (false: it failed, said). RX_VK_XESS_JITTER=-1: the jitter given +/// negated (a check of its sign: the picture shimmers when it is the wrong one) +bool evaluate(VkCommandBuffer cmd, const float jitter_px[2], bool reset, uint32_t rw, uint32_t rh) { + State& s = st(); + static const float sign = env_switch("RX_VK_XESS_JITTER") == -1 && getenv("RX_VK_XESS_JITTER") ? -1.0f : 1.0f; + input_layouts(cmd, true); + xess_vk_execute_params_t ep{}; + ep.colorTexture = view_info(s.color), ep.velocityTexture = view_info(s.motion), ep.depthTexture = view_info(s.depth); + ep.outputTexture = view_info(s.output); + ep.jitterOffsetX = sign * jitter_px[0], ep.jitterOffsetY = sign * jitter_px[1]; + ep.exposureScale = 1.0f; + ep.resetHistory = reset ? 1 : 0; + ep.inputWidth = rw, ep.inputHeight = rh; + const xess_result_t r = xess().execute(s.feature, cmd, &ep); + note_work(); // (XeSS's own commands, which vk_work.h does not see) + input_layouts(cmd, false); + if (r != XESS_RESULT_SUCCESS) { + logf("vk: Intel XeSS: its execution failed (%d): the game's upscale from now on", (int)r); + return false; + } + return true; +} + +#endif // (HOOKSHOT_NGX) + } // namespace bool dlss_built() { return true; } +#ifdef HOOKSHOT_NGX void dlss_instance_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all) { if (env_switch("RX_VK_DLSS") == 0) return; for (const auto& e : all) @@ -839,6 +1088,60 @@ void dlss_device_extensions(std::vector<const char*>& want, const std::vector<Vk // dlss_probe or at the first upscale: a GTX has them too) if (!both) g_why = DLSS_NEEDS_RTX; } +void* dlss_device_features(void* features2) { return features2; } +#else // (HOOKSHOT_XESS) + +namespace { +void add_once(std::vector<const char*>& want, const char* name) { + for (const char* w : want) + if (!strcmp(w, name)) return; + want.push_back(name); +} +} // namespace + +void dlss_instance_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all) { + const XessApi& x = xess(); + if (!x.ok) return void(g_why = env_switch("RX_VK_DLSS") == 0 ? DLSS_ENV_OFF : DLSS_NO_RUNTIME); + uint32_t n = 0, api = 0; + const char* const* names = nullptr; + if (x.instance_exts(&n, &names, &api) != XESS_RESULT_SUCCESS) return void(g_why = DLSS_NEEDS_RTX); + for (uint32_t i = 0; i < n; i++) { + bool have = false; + for (const auto& e : all) have |= !strcmp(e.extensionName, names[i]); + if (!have) return logf("vk: Intel XeSS: needs the instance extension %s", names[i]), void(g_why = DLSS_NEEDS_RTX); + } + for (uint32_t i = 0; i < n; i++) add_once(want, names[i]); +} + +void dlss_device_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all) { + const XessApi& x = xess(); + if (!x.ok || g_why.load() > DLSS_OK) return; + uint32_t n = 0; + const char* const* names = nullptr; + if (x.device_exts(dev.instance, dev.physical, &n, &names) != XESS_RESULT_SUCCESS) { + logf("vk: Intel XeSS: does not run on this device"); + return void(g_why = DLSS_NEEDS_RTX); + } + for (uint32_t i = 0; i < n; i++) { + bool have = false; + for (const auto& e : all) have |= !strcmp(e.extensionName, names[i]); + if (!have) return logf("vk: Intel XeSS: needs the device extension %s", names[i]), void(g_why = DLSS_NEEDS_RTX); + } + for (uint32_t i = 0; i < n; i++) add_once(want, names[i]); +} + +void* dlss_device_features(void* features2) { + const XessApi& x = xess(); + if (!x.ok || g_why.load() > DLSS_OK) return features2; + void* chain = features2; // (XeSS sets what it needs in it, and may put structures of its own in front) + if (x.device_features(dev.instance, dev.physical, &chain) != XESS_RESULT_SUCCESS || !chain) { + logf("vk: Intel XeSS: its device features were not given"); + g_why = DLSS_NEEDS_RTX; + return features2; + } + return chain; +} +#endif // (HOOKSHOT_NGX) void dlss_probe() { if (dlss_why() != DLSS_OK || g_why.load(std::memory_order_relaxed) >= 0 || !dev.device) return; @@ -904,7 +1207,11 @@ int dlss_status(char* b, size_t n) { for (const auto& u : s.uploads) if (u.buffer) own += allocation_size(u.memory); unsigned long long ngx = 0; +#ifdef HOOKSHOT_NGX const bool ngx_known = s.caps && NVSDK_NGX_SUCCEED(NGX_DLSS_GET_STATS(s.caps, &ngx)); +#else + const bool ngx_known = false; +#endif const uint64_t draws = s.actor_draws - s.status_draws, frames = s.actor_frames - s.status_frames; char feature[48] = ""; if (ngx_known) snprintf(feature, sizeof feature, ", its feature %llu MB", ngx >> 20); @@ -912,7 +1219,7 @@ int dlss_status(char* b, size_t n) { if (s.status_passed) snprintf(passed, sizeof passed, ", %llu left to the game's network", (unsigned long long)s.status_passed); snprintf(b, n, "; %s: %llu frames drawn%s, %llu features made, jitter %+.3f %+.3f px%s, actor motion %llu draws in %llu " "frames (%.3f ms CPU a frame), its images %llu MB%s", - mode == 2 ? "DLAA" : "DLSS", (unsigned long long)s.status_drawn, passed, (unsigned long long)s.features_made, + mode == 2 ? kAa : kShort, (unsigned long long)s.status_drawn, passed, (unsigned long long)s.features_made, s.status_jitter[0], s.status_jitter[1], s.status_motion ? "" : " (no motion)", (unsigned long long)draws, (unsigned long long)frames, frames ? s.actor_ns / 1e6 / (double)frames : 0.0, (unsigned long long)(own >> 20), feature); @@ -927,12 +1234,11 @@ void dlss_idle() { const uint64_t now = texture_frame_no(); if (now - s.asked <= 120) return; // (DLSS in use, or off for under 120 frames (2 s): kept, a row stepped through comes back at once) if (s.feature) { - NVSDK_NGX_Handle* old = s.feature; - later([old] { NVSDK_NGX_VULKAN_ReleaseFeature(old); }); + release_feature(s.feature); s.feature = nullptr; s.feature_rw = s.feature_rh = s.feature_ow = s.feature_oh = s.min_rw = s.min_rh = 0; - logf("vk: NVIDIA DLSS: not used for 120 frames (the last at frame %llu, now %llu): its feature and images let go; " - "the next upscale with it makes them again", (unsigned long long)s.asked, (unsigned long long)now); + logf("vk: %s: not used for 120 frames (the last at frame %llu, now %llu): its feature and images let go; " + "the next upscale with it makes them again", kName, (unsigned long long)s.asked, (unsigned long long)now); } for (Image* im : {&s.color, &s.depth, &s.motion, &s.output}) drop_image(*im); for (auto& pb : s.pose_buffers) // (the actor motion's poses and cameras) @@ -1151,22 +1457,10 @@ bool dlss_upscale(VkCommandBuffer cmd, uint64_t key, bool known, Texture* in, Vk vkCmdDispatch(cmd, (rw + 7) / 8, (rh + 7) / 8, 1); full_barrier(cmd); } - NVSDK_NGX_Resource_VK color = resource(s.color, false), depth = resource(s.depth, false), mv = resource(s.motion, false), - output = resource(s.output, true); - NVSDK_NGX_VK_DLSS_Eval_Params ep{}; - ep.Feature.pInColor = &color, ep.Feature.pInOutput = &output; - ep.pInDepth = &depth, ep.pInMotionVectors = &mv; // (the jitter in render pixels, x right and y down) float jitter_px[2]; dlss_math::jitter_to_pixels(cam.jitter, rw, rh, jitter_px); - ep.InJitterOffsetX = jitter_px[0], ep.InJitterOffsetY = jitter_px[1]; - ep.InRenderSubrectDimensions = {rw, rh}; - ep.InReset = reset ? 1 : 0; - ep.InMVScaleX = ep.InMVScaleY = 1.0f; - const NVSDK_NGX_Result r = NGX_VULKAN_EVALUATE_DLSS_EXT(cmd, s.feature, s.params, &ep); - note_work(); // (as at its creation: the barrier after it, and the game's next one, are recorded) - if (NVSDK_NGX_FAILED(r)) { - logf("vk: NVIDIA DLSS: its evaluation failed (%#x): the game's upscale from now on", (unsigned)r); + if (!evaluate(cmd, jitter_px, reset, rw, rh)) { s.failed = true; return false; // (the network writes its output over whatever the evaluation did) } @@ -1188,12 +1482,12 @@ bool dlss_upscale(VkCommandBuffer cmd, uint64_t key, bool known, Texture* in, Vk s.last_frame = now; s.last_jitter[0] = cam.jitter[0], s.last_jitter[1] = cam.jitter[1]; // (for the status line, dlss_status: the jitter DLSS was given, in render pixels) - s.status_jitter[0] = ep.InJitterOffsetX, s.status_jitter[1] = ep.InJitterOffsetY, s.status_motion = motion; + s.status_jitter[0] = jitter_px[0], s.status_jitter[1] = jitter_px[1], s.status_motion = motion; s.status_drawn++; std::memcpy(s.last_vp, cam.current, sizeof s.last_vp); s.jitter_w = rw, s.jitter_h = rh, s.jitter_phases = (uint32_t)dlss_jitter_phases(rw, rh, dw, dh); - if (s.reason) logf("vk: NVIDIA DLSS draws the upscale again (frame %llu)", (unsigned long long)now); + if (s.reason) logf("vk: %s draws the upscale again (frame %llu)", kName, (unsigned long long)now); s.reason = nullptr; // (RX_VK_DLSS_DEBUG=alternate, a test's reference: every other frame the game's own upscale over DLSS's, which still // draws and keeps its history. A frame of DLSS then has the game's picture of the frames either side to be measured against) @@ -1201,7 +1495,7 @@ bool dlss_upscale(VkCommandBuffer cmd, uint64_t key, bool known, Texture* in, Vk if (alternate && (now & 1)) return false; if (!s.told_first) { s.told_first = true; - logf("vk: NVIDIA DLSS draws OoT's upscale: %ux%u -> %ux%u%s", rw, rh, ow, oh, show_motion ? " (its motion shown)" : ""); + logf("vk: %s draws OoT's upscale: %ux%u -> %ux%u%s", kName, rw, rh, ow, oh, show_motion ? " (its motion shown)" : ""); } return true; } @@ -1212,6 +1506,7 @@ bool dlss_built() { return false; } void dlss_probe() {} void dlss_instance_extensions(std::vector<const char*>&, const std::vector<VkExtensionProperties>&) {} void dlss_device_extensions(std::vector<const char*>&, const std::vector<VkExtensionProperties>&) {} +void* dlss_device_features(void* features2) { return features2; } void dlss_note_scene_draw(const Texture*, uint32_t, uint32_t, Texture*, uint64_t, uint64_t) {} bool dlss_scene_size(uint32_t, uint32_t) { return false; } bool dlss_upscale(VkCommandBuffer, uint64_t, bool, Texture*, VkImageView, Texture*, VkImageView, uint32_t) { return false; } diff --git a/gpu/vk/vk_dlss.h b/gpu/vk/vk_dlss.h index 6d6eee9..9b54d97 100644 --- a/gpu/vk/vk_dlss.h +++ b/gpu/vk/vk_dlss.h @@ -29,6 +29,9 @@ bool dlss_full_scale(); void dlss_instance_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all); /// The device's extensions DLSS needs, added to `want` when the device has them void dlss_device_extensions(std::vector<const char*>& want, const std::vector<VkExtensionProperties>& all); +/// The features the upscaler needs (Intel XeSS), set in the VkPhysicalDeviceFeatures2 chain: the head for +/// vkCreateDevice (XeSS may put structures of its own in front), features2 itself when there is nothing to add +void* dlss_device_features(void* features2); /// A draw into a scene-sized target (colour target 0 and the depth target), with the vertex stage's uniform buffer 3 at /// `camera` (its size): the camera of the frame, when that buffer holds one void dlss_note_scene_draw(const Texture* color0, uint32_t width, uint32_t height, Texture* depth, uint64_t camera, diff --git a/gpu/vk/vk_main.cpp b/gpu/vk/vk_main.cpp index 99304ec..083f1c0 100644 --- a/gpu/vk/vk_main.cpp +++ b/gpu/vk/vk_main.cpp @@ -418,7 +418,7 @@ bool create_device() { f.features.sparseResidencyImage2D && !(getenv("RX_VK_SPARSE") && !atoi(getenv("RX_VK_SPARSE"))); dev.sparse_buffers = (families[dev.queue_family].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT) && f.features.sparseBinding; VkDeviceCreateInfo ci{VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO}; - ci.pNext = &f; + ci.pNext = dlss_device_features(&f); // (Intel iGPU: Intel XeSS's, vk_dlss.cpp) ci.queueCreateInfoCount = compute_family != UINT32_MAX ? 2 : 1; ci.pQueueCreateInfos = qs; ci.enabledExtensionCount = (uint32_t)want.size(); diff --git a/gpu/vk/vk_menu.cpp b/gpu/vk/vk_menu.cpp index e9474bb..bb831d0 100644 --- a/gpu/vk/vk_menu.cpp +++ b/gpu/vk/vk_menu.cpp @@ -148,7 +148,11 @@ unsigned upscalers_available() { } int upscaler() { return fsr3_wanted() ? (int)ur::FSR3 : dlss_mode(); } const char* tx_upscaler(int) { +#ifdef HOOKSHOT_XESS + static const char* t[] = {"Game's own", "Intel XeSS", "Intel XeSS AA", "AMD FSR 3.1"}; +#else static const char* t[] = {"Game's own", "NVIDIA DLSS", "NVIDIA DLAA", "AMD FSR 3.1"}; +#endif return t[std::clamp(upscaler(), 0, ur::kUpscalers - 1)]; } /// The Upscaler: the one shown (what runs, upscaler()), and with the game's own, why DLSS and DLAA are not offered diff --git a/nx2/build.py b/nx2/build.py index 37406c8..195fc14 100644 --- a/nx2/build.py +++ b/nx2/build.py @@ -844,6 +844,11 @@ def main(): ngx_flags = ["-DHOOKSHOT_NGX", "-I" + os.path.join(NGX_SDK, "include")] if NGX_SDK else [] if NGX_SDK: print("the Vulkan back end: with NVIDIA DLSS (NVIDIA's NGX SDK in %s)" % NGX_SDK, flush=True) + # HOOKSHOT_XESS_SDK=<Intel's XeSS SDK>: Intel XeSS in DLSS's place (vk_dlss.cpp; the menu's names in vk_menu.cpp) + xess_sdk = "" if NGX_SDK else os.environ.get("HOOKSHOT_XESS_SDK", "") + xess_flags = ["-DHOOKSHOT_XESS", "-I" + os.path.join(xess_sdk, "inc")] if xess_sdk else [] + if xess_sdk: + print("the Vulkan back end: with Intel XeSS (its SDK in %s)" % xess_sdk, flush=True) # (Eden's recompiler and sirit see only their own headers: a change to the back end's vk_*.h does not # rebuild their ~500 files; the back end itself depends on everything, suyu's texture decoders included) vk_lib_deps = tree_headers(os.path.join(vk, "eden"), os.path.join(vk, "sirit"), os.path.join(vk, "compat"), @@ -856,7 +861,8 @@ def main(): vk_jobs.append((cxx, src, os.path.join(obj_dir, "vk_" + name + ".o"), vflags + ([shader_stamp] if os.path.basename(src) == "vk_shader.cpp" else []) + ([stamp_def] if os.path.basename(src) == "vk_menu.cpp" else []) + - (ngx_flags if os.path.basename(src) == "vk_dlss.cpp" else []), + (ngx_flags if os.path.basename(src) == "vk_dlss.cpp" else []) + + (xess_flags if os.path.basename(src) in ("vk_dlss.cpp", "vk_menu.cpp") else []), vk_lib_deps if lib else vdeps + (logo_text if os.path.basename(src) == "vk_menu.cpp" else b""))) if NGX_SDK and not LINUX and toolchain.mingw(): # (llvm-mingw: NVIDIA's static library was built with Microsoft's compiler for its static runtime (/MT). What it asks ``` </details> <!-- github-sync:c:6821731179 -->
Owner

@nathanmourat6-spec commented on GitHub · 2026-11-12 13:59 UTC · original

GPU: Intel Arc 140V (Lunar Lake iGPU, 18 GB shared) – MSI Claw 7 AI+, Core Ultra 7 258V
Driver: 32.0.101.9150 (8 Oct 2026)
OS: Windows 11
Version: v2.1.0 (source 633eb435)

Game runs (~57-60 fps on title screen) but with graphical glitches:

Title screen: background behind the logo intermittently flickers yellow/black, without any input.
“Paramètres” menu (from title screen): a large black diagonal band across the screen (screenshot attached).

Settings: Frame gen Off, HDR Off, Upscaler: game’s own, Render res: Auto, Profile: Docked, Robust buffers: Auto. “ASTC as RGBA8” is not shown in the Debug tab.

Capture d'écran 2026-11-12 174757 Capture d'écran 2026-11-12 175239

screenshot attached.

> [@nathanmourat6-spec](https://github.com/nathanmourat6-spec) commented on GitHub · 2026-11-12 13:59 UTC · [original](https://github.com/NotRexed/Hookshot/issues/999#issuecomment-6825651021) GPU: Intel Arc 140V (Lunar Lake iGPU, 18 GB shared) – MSI Claw 7 AI+, Core Ultra 7 258V Driver: 32.0.101.9150 (8 Oct 2026) OS: Windows 11 Version: v2.1.0 (source 633eb435) Game runs (~57-60 fps on title screen) but with graphical glitches: Title screen: background behind the logo intermittently flickers yellow/black, without any input. “Paramètres” menu (from title screen): a large black diagonal band across the screen (screenshot attached). Settings: Frame gen Off, HDR Off, Upscaler: game’s own, Render res: Auto, Profile: Docked, Robust buffers: Auto. “ASTC as RGBA8” is not shown in the Debug tab. <img width="1290" height="768" alt="Capture d&#39;écran 2026-11-12 174757" src="https://github.com/user-attachments/assets/bb692c5f-95e0-48a8-ad81-281dd9c033b7" /> <img width="701" height="401" alt="Capture d&#39;écran 2026-11-12 175239" src="https://github.com/user-attachments/assets/1cdeec62-1a5f-4a3b-a587-7e551daef966" /> screenshot attached. <!-- github-sync:c:6825651021 -->
Owner

@bmiculka commented on GitHub · 2026-11-12 15:10 UTC · original

I have yet to test, but I have been getting OOM crashes here, nx2navi, and Tuxchii when compiling/building shaders. Dev at Tuxchii (xTopher1049) has said "On Linux with Mesa drivers (Intel Arc, AMD, Steam Deck), Prepare Shaders was keeping every finished shader in a cache held in memory instead of on disk. " This could be a source of issues during shader and pipeline building though not sure if this is just a Tuxchii issue.

He has prepared a test for me to try. I will revert.

Update: Tuxchii still crashed at the same point. I know its another project but still seeing these OOM crashes on shader builds everywhere.

For info:
Arc Pro B50, Mesa 26.2.6, Mint 22.3
i5-13600, 32 GB system RAM

> [@bmiculka](https://github.com/bmiculka) commented on GitHub · 2026-11-12 15:10 UTC · [original](https://github.com/NotRexed/Hookshot/issues/999#issuecomment-6827406742) I have yet to test, but I have been getting OOM crashes here, nx2navi, and Tuxchii when compiling/building shaders. Dev at Tuxchii (xTopher1049) has said "On Linux with Mesa drivers (Intel Arc, AMD, Steam Deck), Prepare Shaders was keeping every finished shader in a cache held in memory instead of on disk. " This could be a source of issues during shader and pipeline building though not sure if this is just a Tuxchii issue. He has prepared a test for me to try. I will revert. Update: Tuxchii still crashed at the same point. I know its another project but still seeing these OOM crashes on shader builds everywhere. For info: Arc Pro B50, Mesa 26.2.6, Mint 22.3 i5-13600, 32 GB system RAM <!-- github-sync:c:6827406742 -->
Owner

@nahianabir commented on GitHub · 2026-11-12 15:13 UTC · original

I guess its the mesh shader that's causing problem for intel gpus, even i set it into compute mode, still dosent fix the graphical glitch. low fps even with the frame gen.
GPU: Intel Arc A750
Driver: 32.0.101.9175 WHQL Certified (Latest)
OS: Windows 11
Version: v2.1.0 (source 633eb435)

Screenshot 2026-11-12 210250 Screenshot 2026-11-12 210349
> [@nahianabir](https://github.com/nahianabir) commented on GitHub · 2026-11-12 15:13 UTC · [original](https://github.com/NotRexed/Hookshot/issues/999#issuecomment-6827478943) I guess its the mesh shader that's causing problem for intel gpus, even i set it into compute mode, still dosent fix the graphical glitch. low fps even with the frame gen. GPU: Intel Arc A750 Driver: 32.0.101.9175 WHQL Certified (Latest) OS: Windows 11 Version: v2.1.0 (source 633eb435) <img width="1920" height="1032" alt="Screenshot 2026-11-12 210250" src="https://github.com/user-attachments/assets/82437679-132a-4aad-b408-da20208ed66e" /> <img width="1920" height="1032" alt="Screenshot 2026-11-12 210349" src="https://github.com/user-attachments/assets/0c3a174e-4bb0-4082-a8bb-39ea210c8f32" /> <!-- github-sync:c:6827478943 -->
Owner

@mooki000 commented on GitHub · 2026-11-12 15:18 UTC · original

Tracker upkeep note (merged pull requests and code checked against main 51da0464; no new test run was made for it).

Main 51da0464 includes #2464's uniform-buffer snapshots on non-mirrored Intel devices, #2513's loop-based storage-table searches and #2288's corrected native-ASTC accounting. These are concrete completed subcases, but preparation memory exhaustion and in-play compute-queue blocking are not thereby fixed: normal and fallback compute jobs still share the Intel serial queue, and a pending pipeline can leave its dispatch out. The already reported UHD770 result remains a limitation rather than new evidence, and #1610's author owns the oversized-compile follow-up. Please keep the tracker open; the latest combined patches on an older base need separate current-main isolation before their remaining changes can be judged.

> [@mooki000](https://github.com/mooki000) commented on GitHub · 2026-11-12 15:18 UTC · [original](https://github.com/NotRexed/Hookshot/issues/999#issuecomment-6827618705) **Tracker upkeep note** (merged pull requests and code checked against main `51da0464`; no new test run was made for it). Main `51da0464` includes #2464's uniform-buffer snapshots on non-mirrored Intel devices, #2513's loop-based storage-table searches and #2288's corrected native-ASTC accounting. These are concrete completed subcases, but preparation memory exhaustion and in-play compute-queue blocking are not thereby fixed: normal and fallback compute jobs still share the Intel serial queue, and a pending pipeline can leave its dispatch out. The already reported UHD770 result remains a limitation rather than new evidence, and #1610's author owns the oversized-compile follow-up. Please keep the tracker open; the latest combined patches on an older base need separate current-main isolation before their remaining changes can be judged. <!-- github-sync:c:6827618705 -->
Commenting is not possible because the repository is archived.
No milestone
No assignees
2 participants
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/Hookshot#999
No description provided.