WIP: Intel GPU fixes: device fault diagnostics, dynamic iGPU budget, hybrid CPU affinity #1569

Closed
Maine774 wants to merge 7 commits from intel-gpu-fixes into main
Maine774 commented 2026-11-10 09:00:41 +00:00 (Migrated from github.com)

Summary

Three targeted improvements for Intel GPU issues:

1. Enhanced Device Loss Diagnostics for Intel Arc GPUs

  • Issue: #999, #1291 - Device loss on Intel Arc (A750, B580, B390, Pro B50) and Core Ultra iGPUs
  • Current: PR #1291 added basic device loss handling with checkpoint reporting, but no Intel-specific fault info
  • Change: device_lost() in vk_replay.cpp now calls device_fault_report() for Intel (vendorID 0x8086) using VK_EXT_device_fault
  • Benefit: Captures fault addresses, vendor fault codes, and vendor binary data to identify which shader/dispatch caused the device loss

2. Dynamic iGPU Texture Budget Based on System RAM

  • Issue: #999 - Integrated GPU memory pressure (OOM on 16GB systems during preparation)
  • Current: Fixed 2560 MB cap for all iGPUs
  • Change: Dynamic calculation in vk_texture.cpp:
    • 8GB system → 1.5 GB budget
    • 12GB system → 2 GB
    • 16GB system → 2.5 GB (previous default)
    • 24GB system → 3.5 GB
    • 32GB system → 4 GB
    • 48GB+ system → 6 GB (dGPU max)
  • Reserves 1/3 system RAM (min 8GB, max 16GB) for non-texture usage (game allocations, render targets, OS, driver overhead)
  • Still respects RX_VK_VRAM_MB override

3. Intel Hybrid CPU Core Affinity Enhancements

  • Issue: #1286 - Presentation/replay thread lands on E-core instead of P-core
  • Current: PR #1158/1034 has basic hybrid detection but no debug logging or manual override
  • Changes in hos_thread.c:
    • Debug logging showing core selection reasoning (core masks, classes, popcounts)
    • HOS_RESERVE_CORE_MASK env var for manual override/testing
    • Better error handling/logging in extended/legacy API paths
    • Legacy API fallback heuristic: P-cores = 2+ logical processors (HT), E-cores = 1 logical

Testing

  • All relevant unit tests pass:
    • test_cas_loop_ceiling.py ✅
    • test_device_lost_exit.py ✅ (updated fixture with Intel fault stubs)
    • test_reserved_core.py ✅ (updated fixture with hos_log stub)
  • Tested on Windows with llvm-mingw (clang++ 23.1.5, libc++)

Backwards Compatibility

  • All changes are additive or replace with stricter defaults
  • Environment variable overrides preserved (RX_VK_VRAM_MB, HOS_RESERVE_CORE, HOS_RESERVE_CORE_MASK)
  • No API changes
  • Logging only adds info, doesn't change behavior
  • #999 (partial): Better device loss diagnostics for Intel Arc
  • #999 (partial): Dynamic iGPU memory budget prevents OOM
  • #1286: Replay thread on P-core instead of E-core
  • #1291 (enhancement): Intel-specific fault info on device loss
## Summary Three targeted improvements for Intel GPU issues: ### 1. Enhanced Device Loss Diagnostics for Intel Arc GPUs - **Issue**: #999, #1291 - Device loss on Intel Arc (A750, B580, B390, Pro B50) and Core Ultra iGPUs - **Current**: PR #1291 added basic device loss handling with checkpoint reporting, but no Intel-specific fault info - **Change**: `device_lost()` in `vk_replay.cpp` now calls `device_fault_report()` for Intel (vendorID 0x8086) using `VK_EXT_device_fault` - **Benefit**: Captures fault addresses, vendor fault codes, and vendor binary data to identify which shader/dispatch caused the device loss ### 2. Dynamic iGPU Texture Budget Based on System RAM - **Issue**: #999 - Integrated GPU memory pressure (OOM on 16GB systems during preparation) - **Current**: Fixed 2560 MB cap for all iGPUs - **Change**: Dynamic calculation in `vk_texture.cpp`: - 8GB system → 1.5 GB budget - 12GB system → 2 GB - 16GB system → 2.5 GB (previous default) - 24GB system → 3.5 GB - 32GB system → 4 GB - 48GB+ system → 6 GB (dGPU max) - Reserves 1/3 system RAM (min 8GB, max 16GB) for non-texture usage (game allocations, render targets, OS, driver overhead) - Still respects `RX_VK_VRAM_MB` override ### 3. Intel Hybrid CPU Core Affinity Enhancements - **Issue**: #1286 - Presentation/replay thread lands on E-core instead of P-core - **Current**: PR #1158/1034 has basic hybrid detection but no debug logging or manual override - **Changes** in `hos_thread.c`: - Debug logging showing core selection reasoning (core masks, classes, popcounts) - `HOS_RESERVE_CORE_MASK` env var for manual override/testing - Better error handling/logging in extended/legacy API paths - Legacy API fallback heuristic: P-cores = 2+ logical processors (HT), E-cores = 1 logical ## Testing - All relevant unit tests pass: - `test_cas_loop_ceiling.py` ✅ - `test_device_lost_exit.py` ✅ (updated fixture with Intel fault stubs) - `test_reserved_core.py` ✅ (updated fixture with hos_log stub) - Tested on Windows with llvm-mingw (clang++ 23.1.5, libc++) ## Backwards Compatibility - All changes are additive or replace with stricter defaults - Environment variable overrides preserved (`RX_VK_VRAM_MB`, `HOS_RESERVE_CORE`, `HOS_RESERVE_CORE_MASK`) - No API changes - Logging only adds info, doesn't change behavior ## Related Issues - #999 (partial): Better device loss diagnostics for Intel Arc - #999 (partial): Dynamic iGPU memory budget prevents OOM - #1286: Replay thread on P-core instead of E-core - #1291 (enhancement): Intel-specific fault info on device loss
BrunoSilvestre00 commented 2026-11-10 15:18:41 +00:00 (Migrated from github.com)

Hi @Maine774, heads-up: head 8637526 doesn't compile on Windows (llvm-mingw from HookshotBuilder v2.0.12 tools, make_game.py). There are two independent errors:

  1. gpu/vk/vk_replay.cpp: device_fault_report() is called (around line 2420 on the PR head) but only defined in gpu/vk/vk_draw.cpp, with no declaration that vk_replay.cpp can see:

    gpu\vk\vk_replay.cpp: error: use of undeclared identifier 'device_fault_report'
    

    A declaration (e.g. void device_fault_report();) in a header both files include, or in vk_replay.cpp, fixes it.

  2. host/hos_thread.c: the new code uses bool / true / false (e.g. static bool logged = false;, bool success = false;), but this C file doesn't include <stdbool.h>, and neither does hos.h / a64.h. Before C23, C has no bool keyword:

    host\hos_thread.c: error: unknown type name 'bool'
    host\hos_thread.c: error: use of undeclared identifier 'false'
    

    Adding #include <stdbool.h> to hos_thread.c (or using int) fixes it.

I'm interested in the hybrid-CPU affinity part. On an i5-14600KF (6P+8E) game threads wait ~250 ms for a virtual core (#1420 / #995), so I'm happy to test it once it builds.

Hi @Maine774, heads-up: head 8637526 doesn't compile on Windows (llvm-mingw from HookshotBuilder v2.0.12 tools, `make_game.py`). There are two independent errors: 1. **`gpu/vk/vk_replay.cpp`**: `device_fault_report()` is called (around line 2420 on the PR head) but only defined in `gpu/vk/vk_draw.cpp`, with no declaration that `vk_replay.cpp` can see: ``` gpu\vk\vk_replay.cpp: error: use of undeclared identifier 'device_fault_report' ``` A declaration (e.g. `void device_fault_report();`) in a header both files include, or in `vk_replay.cpp`, fixes it. 2. **`host/hos_thread.c`**: the new code uses `bool` / `true` / `false` (e.g. `static bool logged = false;`, `bool success = false;`), but this C file doesn't include `<stdbool.h>`, and neither does `hos.h` / `a64.h`. Before C23, C has no `bool` keyword: ``` host\hos_thread.c: error: unknown type name 'bool' host\hos_thread.c: error: use of undeclared identifier 'false' ``` Adding `#include <stdbool.h>` to `hos_thread.c` (or using `int`) fixes it. I'm interested in the hybrid-CPU affinity part. On an i5-14600KF (6P+8E) game threads wait ~250 ms for a virtual core (#1420 / #995), so I'm happy to test it once it builds.
hckrman101 (Migrated from github.com) reviewed 2026-11-10 16:38:58 +00:00
hckrman101 (Migrated from github.com) left a comment

The three ideas are reasonable (fault info on Intel device loss, a RAM-scaled iGPU texture budget, hybrid-core logging plus an override), but as it stands the head doesn't compile, as BrunoSilvestre00 already reported. I confirmed both errors in the code:

  1. device_fault_report() is defined in gpu/vk/vk_draw.cpp and isn't declared in vk_internal.h, vk_draw.h or anywhere vk_replay.cpp can see. Add a declaration to a shared header. tests/test_device_lost_exit.py defines its own stub, so it can't catch this.
  2. host/hos_thread.c is C and uses bool/true/false (static bool logged, bool success) with no <stdbool.h>. Include it or use int. The new code in pick_reserved_core is not Windows-only, so Linux hits this too.

Smaller points (non-blocking):

  • nit: budget = std::max(budget, 1024ull << 20) can lift the budget above own, which is the driver's limit. A std::min(own, ...) clamp would be safer.
  • nit: HOS_RESERVE_CORE_MASK with an unparsable value silently gives mask 0 (no reserved core). Fine for a debug override, but worth a log line.
  • Heads-up: #1638 touches the same pick_reserved_core code, so one of the two will need a rebase.

Once it builds on Windows and Linux I'm happy to look again.

The three ideas are reasonable (fault info on Intel device loss, a RAM-scaled iGPU texture budget, hybrid-core logging plus an override), but as it stands the head doesn't compile, as BrunoSilvestre00 already reported. I confirmed both errors in the code: 1. `device_fault_report()` is defined in `gpu/vk/vk_draw.cpp` and isn't declared in `vk_internal.h`, `vk_draw.h` or anywhere `vk_replay.cpp` can see. Add a declaration to a shared header. `tests/test_device_lost_exit.py` defines its own stub, so it can't catch this. 2. `host/hos_thread.c` is C and uses `bool`/`true`/`false` (`static bool logged`, `bool success`) with no `<stdbool.h>`. Include it or use `int`. The new code in `pick_reserved_core` is not Windows-only, so Linux hits this too. Smaller points (non-blocking): - nit: `budget = std::max(budget, 1024ull << 20)` can lift the budget above `own`, which is the driver's limit. A `std::min(own, ...)` clamp would be safer. - nit: `HOS_RESERVE_CORE_MASK` with an unparsable value silently gives mask 0 (no reserved core). Fine for a debug override, but worth a log line. - Heads-up: #1638 touches the same `pick_reserved_core` code, so one of the two will need a rebase. Once it builds on Windows and Linux I'm happy to look again.
hckrman101 (Migrated from github.com) commented 2026-11-10 16:38:59 +00:00

device_fault_report isn't declared in this TU (it lives in vk_draw.cpp), so this is an undeclared identifier. Declare it in vk_internal.h or vk_draw.h.

`device_fault_report` isn't declared in this TU (it lives in vk_draw.cpp), so this is an undeclared identifier. Declare it in vk_internal.h or vk_draw.h.
hckrman101 (Migrated from github.com) commented 2026-11-10 16:38:59 +00:00

This is a C file without <stdbool.h>, so bool/true/false don't compile here (same for bool success further down). Include it or use int.

This is a C file without `<stdbool.h>`, so `bool`/`true`/`false` don't compile here (same for `bool success` further down). Include it or use `int`.
Maine774 commented 2026-11-10 19:55:20 +00:00 (Migrated from github.com)

Thanks, I'll look into this and get back to you.

Thanks, I'll look into this and get back to you.
mooki000 commented 2026-11-11 04:51:37 +00:00 (Migrated from github.com)

I tried this on Linux (Arch, clang 23.1.3, libstdc++ from GCC 16), merged into main 872cb68, to test it on an Intel UHD 770 (Mesa 26.2.5). The head b0bff2b doesn't compile there:

gpu/vk/vk_texture.cpp:3747:26: error: no matching function for call to 'min'
gpu/vk/vk_texture.cpp:3750:22: error: no matching function for call to 'max'
note: candidate template ignored: deduced conflicting types for parameter '_Tp' ('uint64_t' (aka 'unsigned long') vs. 'unsigned long long')
7 errors generated.

(The line numbers are those of the merge.) They are the seven std::min(budget, NNNNull << 20) / std::max(budget, 1024ull << 20) calls in the new iGPU budget block. On Linux uint64_t is unsigned long, so the template cannot deduce one type; on Windows it is unsigned long long, which is why it builds there. Giving the type, std::min<uint64_t>(...) / std::max<uint64_t>(...), compiles with this clang (checked on a two-line file, not yet in a build of the whole branch).

One note for testing on Linux Intel: Mesa's ANV on this UHD 770 does not list VK_EXT_device_fault, so the fault diagnostics have nothing to report on it. I'll run the budget part on the UHD 770 once it builds and post what it shows.

I tried this on Linux (Arch, clang 23.1.3, libstdc++ from GCC 16), merged into main 872cb68, to test it on an Intel UHD 770 (Mesa 26.2.5). The head b0bff2b doesn't compile there: ``` gpu/vk/vk_texture.cpp:3747:26: error: no matching function for call to 'min' gpu/vk/vk_texture.cpp:3750:22: error: no matching function for call to 'max' note: candidate template ignored: deduced conflicting types for parameter '_Tp' ('uint64_t' (aka 'unsigned long') vs. 'unsigned long long') 7 errors generated. ``` (The line numbers are those of the merge.) They are the seven `std::min(budget, NNNNull << 20)` / `std::max(budget, 1024ull << 20)` calls in the new iGPU budget block. On Linux `uint64_t` is `unsigned long`, so the template cannot deduce one type; on Windows it is `unsigned long long`, which is why it builds there. Giving the type, `std::min<uint64_t>(...)` / `std::max<uint64_t>(...)`, compiles with this clang (checked on a two-line file, not yet in a build of the whole branch). One note for testing on Linux Intel: Mesa's ANV on this UHD 770 does not list `VK_EXT_device_fault`, so the fault diagnostics have nothing to report on it. I'll run the budget part on the UHD 770 once it builds and post what it shows.
mooki000 commented 2026-11-11 13:36:37 +00:00 (Migrated from github.com)

Follow-up to my comment above, with the run I said I'd do.

The new head 5b19f5ac still doesn't compile on Linux. The merge of main didn't touch the seven calls, so gpu/vk/vk_texture.cpp (clang 23.1.3, libstdc++, the build's flags) stops at the same place:

gpu/vk/vk_texture.cpp:3811:26: error: no matching function for call to 'min'
...
gpu/vk/vk_texture.cpp:3824:22: error: no matching function for call to 'max'
7 errors generated.

std::min<uint64_t>(...) / std::max<uint64_t>(...) on lines 3811-3824 fixes it.

The budget part on the UHD 770, with that cast fixed locally (b0bff2b merged into main 872cb68). Same save-load scene into Kokiri Forest as main, 8 minutes, headless, Mesa 26.2.5, 93 GB RAM:

  • It sets vk: integrated GPU texture budget: 6144 MB (system RAM 93 GB, driver allows 6144 MB, reserved 16 GB). Main's is 2560 MB.
  • Neither budget holds on this GPU. Both builds count about 17.6 GB of textures (textures 17667 MB (budget 6144 MB) here, 17608 MB (budget 2560 MB) on main), while every allocation of the process together is about 4.9 GB (4884 MB used).
  • The cause is in the counting. Intel samples ASTC natively, and create() counts each ASTC texel as the block's 16 bytes, so these textures count 16-144 times their real size. The fix for that is #2288; with it, main counts 2241 MB (budget 2560 MB) in the same scene.
  • Nothing else changed in this run. The picture after loading is the same as main's, which is wrong on this GPU for an unrelated reason. 9.0 fps against 8.4 at different load, GPU-bound at about 104 ms a frame, so that's no real difference. Peak RSS 15.8 GB against 14.7 GB. No errors, nothing from i915 in the kernel log.

With native ASTC counted right, a bigger budget on a big-RAM machine would mean something on Intel. Until then this part changes the number in the log line, not what's evicted.

As noted before, the fault report has nothing to call on ANV here (no VK_EXT_device_fault), and the core selection is Windows-only, so I couldn't test either.

Follow-up to my comment above, with the run I said I'd do. **The new head 5b19f5ac still doesn't compile on Linux.** The merge of main didn't touch the seven calls, so `gpu/vk/vk_texture.cpp` (clang 23.1.3, libstdc++, the build's flags) stops at the same place: ``` gpu/vk/vk_texture.cpp:3811:26: error: no matching function for call to 'min' ... gpu/vk/vk_texture.cpp:3824:22: error: no matching function for call to 'max' 7 errors generated. ``` `std::min<uint64_t>(...)` / `std::max<uint64_t>(...)` on lines 3811-3824 fixes it. **The budget part on the UHD 770**, with that cast fixed locally (b0bff2b merged into main 872cb68). Same save-load scene into Kokiri Forest as main, 8 minutes, headless, Mesa 26.2.5, 93 GB RAM: - It sets `vk: integrated GPU texture budget: 6144 MB (system RAM 93 GB, driver allows 6144 MB, reserved 16 GB)`. Main's is 2560 MB. - Neither budget holds on this GPU. Both builds count about 17.6 GB of textures (`textures 17667 MB (budget 6144 MB)` here, `17608 MB (budget 2560 MB)` on main), while every allocation of the process together is about 4.9 GB (`4884 MB used`). - The cause is in the counting. Intel samples ASTC natively, and `create()` counts each ASTC texel as the block's 16 bytes, so these textures count 16-144 times their real size. The fix for that is #2288; with it, main counts `2241 MB (budget 2560 MB)` in the same scene. - Nothing else changed in this run. The picture after loading is the same as main's, which is wrong on this GPU for an unrelated reason. 9.0 fps against 8.4 at different load, GPU-bound at about 104 ms a frame, so that's no real difference. Peak RSS 15.8 GB against 14.7 GB. No errors, nothing from i915 in the kernel log. With native ASTC counted right, a bigger budget on a big-RAM machine would mean something on Intel. Until then this part changes the number in the log line, not what's evicted. As noted before, the fault report has nothing to call on ANV here (no `VK_EXT_device_fault`), and the core selection is Windows-only, so I couldn't test either.
Commenting is not possible because the repository is archived.
No description provided.