fix(vk): on weak Intel laptops the shaders are prepared and the scene's passes draw within seconds, the pipeline cache is saved, and a Debug button sets the lightest rows #404

Merged
HumanBean merged 14 commits from Yeetbot90/Longshot:fix/igpu-weak-laptop into main 2026-11-14 21:05:58 +00:00
Contributor

Why

On a weak Intel laptop (i3-1115G4, UHD Graphics, 2 cores / 4 threads, 16 GB, Windows) the game is close to unplayable. The build's shader preparation is stopped by its watchdog, so every start builds every shader in play. The scene's light and exposure passes are left out for minutes (the Deku Tree's dialogue drawn blown out). The GPU device was lost in the Deku Tree's last cutscene, and on nightly-132 the game crashed inside Intel's driver entering Hyrule Castle, both times with video memory over what the driver allows. This PR lets the preparation finish, draws those passes within seconds, keeps integrated GPUs under their memory allowance with room to spare (and gives textures up as soon as the driver refuses memory), and adds a Debug button that sets the lightest rows at once. He also reports a blown-out Castle Town square and Hyrule Castle, and black streaks on Link's hair: what his logs say about those, and the switches that tell their causes apart on his laptop, are under What changed and Testing.

What changed

The causes, from Unity's logs: two sets of five on nightly-130-0caf0618 (main @ 0caf0618) and five on nightly-132-5e072fc8 (main @ 5e072fc8).

  • Shaders. Intel's Windows compiler builds compute pipelines one at a time (serial_builds_on). With the optimizer on it took 0.3 s to 389.5 s for each of 37 dispatches on nightly-130, about 22 minutes on that one thread. On nightly-132 it took 351 s in Kokiri Forest alone, 169 s for the longest. The preparation's 300 s watchdog stops it and leaves longshot_prepare_skipped, so the preparation was skipped in 15 of 15 logs.
  • Memory, Deku Tree. The textures' pool held 9 blocks (1152 MB) made at the start, which VMA keeps for the whole run, while the textures' budget fell to 720 MB. With the pool down to those blocks (kept_only), the rule that frees textures by the driver's count waited even past the allowance. The device was lost at 6.6 of 6.5 GB.
  • Memory, Hyrule Castle. The same 9 blocks were there (in 5 of 5 nightly-132 logs). At 6.7 GB of 7.0 GB allowed, entering Hyrule Castle added 560 MB of imports (1648 to 2048 MB live, plus 160 MB retired but still imported). The count reached 7.5 of 7.0 GB and six graphics pipelines were not made (-2, VK_ERROR_OUT_OF_DEVICE_MEMORY). The next vkCmdDispatchIndirect then faulted inside igvk64.dll, reading 0x107e, a null pointer plus an offset, as Intel's driver does when its own allocation failed. That dispatch is the mesh fallback's: longshot_vk.dll+0x21a1f0 matches the return address of fallback_record's vkCmdDispatchIndirect(cmd, f->records, 20) in a build of main @ 5e072fc8 with the nightly's own toolchain. Its pipelines and buffers are made and published correctly, so the fault is the driver's, out of memory.

The changes:

  • jobs: jobs_push_serial(fn, on_failure, first) in gpu/vk/vk_jobs.cpp / .h adds a front lane. The game's dispatches go there, ahead of the mesh fallback's model builds, in order among themselves. After each build the thread calls pipeline_cache_between_builds().
  • pipeline cache: pipeline_cache_save in gpu/vk/vk_draw.cpp. A save put off on Intel's serial builds (the PcacheFetch lock against igvk64's crash in vkGetPipelineCacheData) is now owed (pcache_save_owed). pipeline_cache_between_builds runs it on the serial thread between two builds, the one moment no compute build is under way. The function's signature is unchanged (checks cut it out by its text).
  • dispatch builds: gpu/vk/vk_draw.cpp, gpu/vk/vk_shader.h. On Intel's serial path a dispatch's first build is without the optimizer (VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT, as the mesh fallback's builds already are). compute_optimize then builds the optimized one on the back lane, and the dispatch binds it once made (Program::compute_optimized). The first pipeline stays for the session, because a recording may hold it.
  • preparation and precompile: precompile_run and precompile_warm in gpu/vk/vk_draw.cpp, through precompile_compute_quick. On Intel's serial builds they take the optimized pipeline when the caches have it, and otherwise build the unoptimized one, so the preparation finishes within its 300 s. A program made this way owes its optimized build (Program::optimize_owed), which its first dispatch queues on the back lane. Programs that are never dispatched are never optimized.
  • memory, blocks: blocks_at_start in gpu/vk/vram_compact.h, called from gpu/vk/vk_vma.cpp. An integrated GPU now makes no texture blocks at the start, because its heap is the PC's RAM. Blocks are made as needed and given back, and the driver's-count rule is not held back by kept_only. Dedicated cards keep the 15% of the heap made at the start (the ~170 ms vkAllocateMemory measured on AMD's Windows driver). RX_VK_IMAGE_PREALLOC still sets a number.
  • memory, line: count_line_pct in gpu/vk/vram_compact.h, used by texture_frame in gpu/vk/vk_texture.cpp. The textures give up the excess once the driver's count passes 90% of the allowance on an integrated GPU, not 97%. A dedicated card's driver pages what is past the allowance out to system memory; an integrated GPU's heap is already system memory, so its driver's own allocations fail instead (both crashes above). On Unity's laptop 97% left 215 MB of room, less than the 560 MB Hyrule Castle added at once; 90% leaves 717 MB. Dedicated cards keep 97%. The VRAM log line's "room in the blocks: not squeezed" note goes by the same line.
  • memory, room in the blocks: room_in_blocks in gpu/vk/vram_compact.h, used by texture_frame. The line above alone would not have squeezed in his Hyrule Castle run: the last count before the load was 6709 of 7172 MB (93.5%), but 1121 MB of it was free inside VMA's blocks, more than an eighth of the allowance, and that room made the squeeze wait. On a dedicated card new images take that space. On an integrated GPU what grows near the line is the driver's own memory (pipelines, command memory) and the 64 MB staging arenas the load made, which no block's free space takes. The room no longer spares the squeeze on an integrated GPU; the compaction (on by default) gives the freed space back, so the ratchet the rule was written against (#2156, #2711) does not come back. Dedicated cards unchanged.
  • memory, refusals: note_out_of_memory in gpu/vk/vk_draw.cpp counts every pipeline the driver refuses with VK_ERROR_OUT_OF_DEVICE_MEMORY or _HOST_MEMORY (both guarded_graphics_held and guarded_compute_held, which every build goes through). At its next look (every 10 frames, about 3 s at his 3.5 fps) texture_frame gives up the count's excess over its line, 256 MB at least (vram_compact::short_excess), on any GPU, and logs vk: the driver refused N pipeline(s) for want of memory. His six refusals came before the fault in igvk64.dll; the textures did not react to them.
  • memory, log: the start line vk: integrated GPU texture budget printed 45% of the allowance (the dedicated cards' share for textures) as "driver allows 3331 MB" on a laptop allowed 7402 MB. It now prints the allowance and the share apart. The comment that called the share 70% now says 45% up to 8.5 GB, else 70%.
  • textures: low_vram_texture_mips in gpu/vk/vk_texture.cpp. It also counts an integrated GPU on a PC with 16 GB of RAM or less (system_ram_bytes()) as small, and it goes by Minimum's budgets (opts.budget == 1), not only the preset, because the button below leaves the preset on Custom with those budgets. On Minimum, decoded ASTC textures then keep at most 1024 texels: Unity's 1365x1365x90 arrays go from 53 MB to 13 MB. Render targets and depth images are untouched.
  • Auto: gpu/vk/auto_settings.h. Auto on an integrated GPU with 4 threads or fewer sets Render resolution to 33% (RES_ULTRA_PERFORMANCE) instead of Minimum's 50%.
  • menu: gpu/vk/vk_menu.cpp, gpu/vk/vk_menu.h, Debug tab:
    • Intel quick compute builds (On by default; shown, and run, only for Intel graphics on Windows, like the Intel shader compiles row: intel_quick_compute asks intel_compiler_row, since Mesa's ANV on Linux ignores the flag). Off: one optimized build, as before.
    • Set up for a weak laptop, a button shown only on integrated graphics. It sets Minimum's rows and memory budgets, Render resolution 33% with the game's own upscaler, plain window scale, no sharpening, no frame generation, Intel's shaders one at a time, and quick compute builds. The preset then reads Custom. The rows that need a restart ask for one at close, as when set by hand.
  • tests: tests/test_blocks_at_start.py and .cpp (the integrated GPU's blocks and line; the pool fixture that #50 added on main compiles the real blocks_at_start, which now asks the device type, so its stub device has one and an integrated case was added), tests/test_texture_squeeze.py (the squeeze goes by count_line_pct and room_in_blocks, and the refusals are counted and acted on), tests/test_vram_compact.cpp (room_in_blocks and short_excess with his numbers), and the checks named under Measurements.
  • docs: README.md (the smaller mips on integrated GPUs; longshot_prepare_skipped under If something goes wrong), docs/development.md (RX_VK_COMPUTE_QUICK_FIRST).
  • Deliberately unchanged: other GPUs. Nothing here runs without serial_builds_on except the memory blocks, the 90% line, the room rule, the mips rule, the Auto rule and the button, which all need an integrated GPU, and the refusals rule, which runs only once a driver has refused memory. Rows a player set on Custom stay as they are; only Auto and the button change rows.

What the newest logs show that this PR does not change, for whoever picks it up next:

  • The frame rate is held by the CPU, not the GPU. In play (over 300 draws a frame) the GPU-bound wait was 0.0 to 0.1 ms in every sample: 97 in Kokiri Forest, 29 in Hyrule Field, 42 in the Market and Hyrule Castle. Those runs had medians of 5.4, 5.4 and 8.5 fps (110 to 185 ms a frame). The replay thread's own time was 4 ms a frame in the Market and 25 to 31 ms in Kokiri Forest and Hyrule Field. The rest goes to the game's threads, which share 4 hardware threads with the replay and the shader builds (vcores: waits of 0.2 to 2 ms on average, up to 1.9 s). A lower resolution or less GPU work will not raise this laptop's frame rate much; fewer builds in play (the preparation finishing) is what frees the CPU.
  • Uploads are slow from contention, not size. There were 145 uploads over 15 ms (the slow upload line), 5.3 s in all across four logs, and a 24 KB texture took 17 ms. No local fix.
  • The 12288x12288 shadow map (D16, 288 MB) is checked for zeros on the CPU once a start, when the game first makes it: 85 to 439 ms in each of 14 of his logs (105 ms in the Hyrule Castle one), a hitch at the first scene. The check is already split across threads and reads 8 bytes at a time; the time is most likely Windows giving 73,728 untouched pages their first touch, which the unswizzle it replaces would pay too.
  • Frame generation costs little here. The player had it on Auto, and Auto skips blending when two game frames are more than 100 ms apart (at 5 to 9 fps, nearly every frame). The button still turns it off, for the latency.
  • The bright Castle Town square and Hyrule Castle. On entering the Market in one log 413 to 770 draws a frame were left out with about 500 build jobs queued, and later spikes had 189 queued; compute dispatches left out were few there (5 in one log, 12 in another, each built within seconds), against the Deku Tree's 22 to 28 s builds. So in Castle Town the missing draws, not a missing exposure pass, are the likelier cause, and they end once the area's pipelines are built (a second visit uses the cache). Not changed here: RX_VK_SYNC_PIPELINES=1 builds each pipeline at its draw instead of leaving it out, which tells the two apart on his laptop (Testing).
  • Black streaks on Link's hair. Nothing in the logs names a cause. What his laptop does that most PCs do not: under 8 GB allowed, every ASTC texture from 2 MB up, the characters' too, is decoded and re-encoded to BC3 on the GPU ("all large ASTC textures kept as BC3"); Intel's Windows compiler also gets the shaders' f16 math and OpIsNan as they are, and both went wrong on other drivers: hair drew white on RX 580s until f16 was lowered to f32 there, and AMD's older Windows compiler failed OpIsNan with black and white blocks (RX_VK_NAN_BITS; the NaN guard on colour outputs is such a test too). One switch each, in Testing; whichever clears the streaks becomes a default for Intel in a follow-up.
  • Timers counted in presents. The compaction's hold lets go of textures unused for 600 presents and its reserve 1800: ten and thirty seconds at 60 fps, three and nine minutes at his 3.5 fps, so the hold barely acts on his laptop (budget 1299 MB against 1776 MB held, 0 to 13 textures retired by it a pass). Counting them in seconds changes every GPU's behaviour, so it is left for a follow-up.
  • Not this PR: tests/test_cpm_discovery.py fails on main @ 7ff5256e (#524) with 'types.SimpleNamespace' object has no attribute 'find', and on this branch since it merged main.

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
Debug row "Intel quick compute builds" (ini intel_quick_compute_builds) Intel's serial compute builds on Windows: an unoptimized pipeline first, the optimized one on the back lane On (row shown, and the code run, on Intel Windows only) On: the alternative is a pass missing for minutes Intel iGPUs on Windows (UHD, Iris Xe) Linux with Mesa's ANV, which ignores the flag (thomas-montano's Iris Xe run): off there since e072164e; every other GPU Intel Arc; any Intel device in play
RX_VK_COMPUTE_QUICK_FIRST Overrides that row (0 off, 1 on) Unset (the row decides) Leave unset; for A/B tests as the row as the row as the row
Debug button "Set up for a weak laptop" Sets the lightest rows at once (listed under What changed) Not pressed (button) Offer it to iGPU players with stutter Integrated GPUs with 4 threads or fewer Dedicated cards: the button is hidden Every integrated GPU in play
Auto on an integrated GPU with 4 threads or fewer Render resolution 33% instead of 50% On with Auto On: 50% measured at 121-197 ms per frame on Unity's laptop i3-class iGPU laptops 8+ thread iGPUs: unchanged (50%) In play
Integrated GPU, 16 GB RAM or less, Minimum's budgets Smaller ASTC mips (RX_VK_LOW_VRAM_MIPS=0 turns it off, as before) On On iGPUs sharing 16 GB or less 32 GB PCs, dedicated cards: unchanged In play
Integrated GPU: no texture blocks at the start (RX_VK_IMAGE_PREALLOC=<n> still sets a number) Blocks made as needed and given back On On iGPUs: 1152 MB less held on Unity's laptop Dedicated cards: unchanged In play
Integrated GPU: the driver's-count line at 90% (no switch; RX_VK_SQUEEZE_ALWAYS=1 still squeezes whatever room the blocks hold) Textures give up the excess once the driver's count passes 90% of the allowance (97% on dedicated cards) On On: past the allowance Intel's driver crashes rather than paging iGPUs: 717 MB of room on Unity's laptop instead of 215 Dedicated cards: unchanged (97%) In play
Integrated GPU: room in VMA's blocks no longer spares that squeeze (no switch) Past the line the textures give up the excess whatever the blocks hold free On On: the driver's own allocations cannot use that room iGPUs: his Hyrule Castle run sat at 93.5% with 1121 MB free in the blocks, unsqueezed Dedicated cards: unchanged In play
Pipelines refused for want of memory (no switch) The textures give up the count's excess over its line, 256 MB at least, at the next look On, any GPU, only after a refusal On: a driver that refuses memory faults soon after Any GPU that runs out A GPU that never runs out: never runs In play
RX_VK_SYNC_PIPELINES=1 (existing, a test) Each pipeline built at its draw, none left out Unset Only to test the bright square — — His laptop
RX_VK_NAN_BITS=1, RX_VK_NO_F16=1, RX_VK_ASTC_BC_ALL=0 (existing, tests) isnan as a test of bits; f16 math lowered to f32; the characters' ASTC kept as RGBA8, not BC3 Unset One at a time, only to test the hair — — His laptop

Measurements

main: 0caf0618 (2026-11-14)
this PR: fix/igpu-weak-laptop @ 688441b1
Since measured: the in-game column for main is Unity's logs; the Hyrule Castle rows are from his nightly-132-5e072fc8 logs (main @ 5e072fc8), which hold every change main made since 0caf0618 that touches these paths. This PR's column is the game-free checks, run at 688441b1 (main @ 7ff5256e merged in).
Hardware: main's in-game column is Unity's laptop (i3-1115G4, Intel UHD Graphics, driver 0x195bb0, 16 GB, Windows; 7402 MB allowed at the start); the checks ran on this PC (Windows 11, llvm-mingw clang 20261104, Python 3)
Scene: his play to the Deku Tree's last cutscene (two sets of five nightly-130 logs), and from Kokiri Forest through Hyrule Field and the Market into Hyrule Castle (five nightly-132 logs); 640x480 output, Render resolution 33%, FSR 3.1, frame generation Auto, Custom preset; the checks need no game
Method: lines counted by script from his 15 logs; the crash address matched against a build of main @ 5e072fc8 with the nightly's own toolchain (llvm-symbolizer, llvm-objdump); the PR's changed checks run against main's source or the branch before each fix, and against this PR's; tests/run_all.py on both sides
How to reproduce: python -I -B tests/run_all.py on each side; for main's source, copy this PR's tests/ changes into a worktree at 0caf0618 and run the checks named below; for the crash, build main @ 5e072fc8 with RX_VK_ONLY=1 and run llvm-symbolizer --obj=longshot_vk.dll 0x18021a1f0

main @ 0caf0618 this PR @ 688441b1
Shader preparation at the build (Unity's logs) skipped in 15 of 15 logs (10 nightly-130, 5 nightly-132) not run on his laptop yet (Testing)
Slowest compute build, optimizer on (Unity's logs) 389.5 s (nightly-130); 169 s, 351 s in all in Kokiri Forest (nightly-132) first build unoptimized (test_pipeline_state_computes)
Texture blocks made at the start, his integrated GPU (8061 MB heap) 9 (1152 MB), kept all run, in 15 of 15 logs 0 (test_blocks_at_start)
Driver's count where the textures give up the excess, integrated GPU (7191 MB allowed) 97%: 6975 MB; crashed at 7.5 GB entering Hyrule Castle (nightly-132) 90%: 6472 MB (test_blocks_at_start)
His last count before Hyrule Castle: 6709 of 7172 MB, 1121 MB free in the blocks not squeezed (room in the blocks) squeezed by 259 MB (test_vram_compact, test_texture_squeeze)
Six pipelines refused with -2 (his nightly-132 log) nothing given up; a fault in igvk64.dll next the excess or 256 MB given up at the next look (test_vram_compact, test_texture_squeeze)
The integrated GPU budget line on his laptop "driver allows 3331 MB" (the 45% share; 7402 MB allowed) the allowance and the share apart (read in the source; nothing checks a log line)
1365x1365x90 ASTC array after the weak laptop button (Custom, Minimum's budgets) 53 MB, full size 13 MB, 682x682 (test_low_vram_texture_mips)
Auto's render resolution, integrated GPU, 4 threads 50% 33% (test_auto_settings)
The PR's 10 changed checks 10 fail (8 on main's source; test_texture_squeeze on the branch before 288e98c5 and again before 688441b1; test_vram_compact before 688441b1) 10 pass
tests/run_all.py 532 passed, 5 failed, 34 skipped (main @ 1be60eae) 532 passed, 6 failed, 34 skipped at 688441b1: the same 5 and test_cpm_discovery, which fails on main @ 7ff5256e too (#524)
[hos host] longshot nightly-130-0caf0618 (source 65722f5b)
[hos host] vk: the shaders' preparation was skipped at the build: left to when each is first drawn
[hos host] vk: compute pipeline of 383a566958350efe: SLOW compile, 389499 ms (on a worker)
[hos Presentation Thread] vk: over the driver's video-memory allowance (6.6 of 6.5 GB)
[hos Presentation Thread] vk: image 1119x1119x1 x1 levels 1 format 126 (NVN 0x34) not made
[hos Presentation Thread] vk: submit 257558 failed (-4)
[hos Presentation Thread] vk: FATAL: the GPU device was lost (VK_ERROR_DEVICE_LOST): ...

[hos host] longshot nightly-132-5e072fc8 (source b03e150f)
[hos Presentation Thread] vk: textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start
[hos Presentation Thread] vk: integrated GPU texture budget: 2560 MB (system RAM 15 GB, driver allows 3331 MB, reserved 8 GB)
[hos Presentation Thread] vk: VRAM: 6717 MB used of 7191 MB allowed (878 MB of it free in the blocks); textures 2004 MB (budget 1312 MB), 1904 textures
[hos Presentation Thread] vk: imports: 49 live (1648 MB), 0 retired still imported (0 MB), 7 retired given back (512 MB)
[hos BaseProcExecutor5] map: HyruleCastle (child)
[hos Presentation Thread] vk: imports: 61 live (2048 MB), 2 retired still imported (160 MB), 10 retired given back (736 MB)
[hos Presentation Thread] vk: over the driver's video-memory allowance (7.5 of 7.0 GB)
[hos host] vk: graphics pipeline for 6f9142def3b6a978 not made (-2)
[hos Presentation Thread] CRASH: exception 0xc0000005 (read 0x107e, guest 0xfffff0000000107e) at igvk64.dll+0x87793
[hos Presentation Thread] CRASH: host backtrace: igvk64.dll+0x87793 igvk64.dll+0x101855 igvk64.dll+0xe37d8 igvk64.dll+0x725ac longshot_vk.dll+0x21a1f0 longshot_vk.dll+0x1ee232 longshot_vk.dll+0x1d2fa5 ...

main @ 5e072fc8 built with the nightly's toolchain, llvm-symbolizer:
0x18021a1f0  fallback_record(...)::$_0::operator()(unsigned long long, bool) const   (return address of vkCmdDispatchIndirect, offset 0x14)
0x1801ee232  fallback_record(VkCommandBuffer_T*, mkwvk::Program&, mkwvk::DrawArgs const&)
0x1801d2fa5  mkwvk::draw(VkCommandBuffer_T*, mkwvk::NvnState&, mkwvk::DrawArgs const&)

main @ 0caf0618, with this PR's checks:
test_intel_quick_compute       exit 1: FAIL: the quick build is not limited to the serial builds and the row
test_pipeline_cache_save       exit 1: Ran 15 tests / FAILED (failures=1, skipped=1)
test_pcache_fetch_lock         exit 1: AssertionError: a save put off is not owed to the serial builds' thread
test_auto_settings             exit 1: test_auto_settings.cpp:23:28: error: use of undeclared identifier 'RES_ULTRA_PERFORMANCE'
test_low_vram_texture_mips     exit 1: PASS custom-preset / FAIL:107 x.skip == (uint32_t)skip   (the next case: custom-with-minimums-budgets)
test_blocks_at_start           exit 1: vk_vma.cpp: blocks_at_start() no longer has: vram_compact::blocks_at_start(heap, kImageBlock, holds_textures, dev.props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
test_pipeline_state_computes   exit 1: ValueError: substring not found (no precompile_compute_quick)
test_precompile_compute_guard  exit 1: ValueError: substring not found (no precompile_compute_quick)

the branch before 288e98c5 (the 90% line), with its checks:
test_blocks_at_start           exit 1: test_blocks_at_start.cpp:55:11: error: use of undeclared identifier 'count_line_pct'
test_texture_squeeze           exit 1: the driver's-limit squeeze holds an integrated GPU at a dedicated card's 97%, where its driver's own allocations fail past the allowance

the branch before 688441b1 (the room rule and the refusals), with their checks:
test_texture_squeeze           exit 1: the 97% squeeze runs whatever room VMA's blocks hold (the check now reads room_in_blocks in vram_compact.h)
test_vram_compact              exit 1: test_vram_compact.cpp:106:11: error: use of undeclared identifier 'room_in_blocks'

this PR @ 688441b1:
test_intel_quick_compute       pass
test_pipeline_cache_save       pass
test_pcache_fetch_lock         pass
test_auto_settings             pass
test_low_vram_texture_mips     pass
test_blocks_at_start           pass (PASS pool constructors: default, compaction-off, override, prealloc-off, fallback, failed-pool, integrated)
test_pipeline_state_computes   pass
test_precompile_compute_guard  pass
test_texture_squeeze           pass
test_vram_compact              pass (PASS: the compaction's rules)
test_keep_rendered             pass (reads the same 97% elsewhere: unchanged)

The pipeline cache: in Unity's first set of logs, five starts each loaded 182 KB and one save was logged in all. In his second set it grew from 0.6 MB to 77 MB over five starts, so saves did happen there. The owed save makes them happen between builds, not only when no build is under way.

tests/run_all.py, both sides on this PC: main @ 1be60eae and this branch at 288e98c5 gave 532 passed, 5 failed, 34 skipped each (test_release, test_oot_state_tap, test_tested_configurations, test_dev_packs, test_walk_key: this PC's setup, none reads these files). test_dlss_sdk_cache failed once on the branch while both suites ran at once and passed twice on each side alone. At 159a12d9 (main @ 7ff5256e merged in) every check was run by name except test_longshot_ctl and test_oot_state_tap, which start a detached process that opens a console window on this PC: the same 4 setup failures as main, test_cpm_discovery (fails on main @ 7ff5256e too, #524), and 12 checks that declare a Windows skip and only ran because they were named.

Testing

Tested: On this PC (Windows 11, llvm-mingw clang 20261104, no Intel GPU), as in Measurements: the changed checks fail without their fix and pass with it; tests/run_all.py fails the same checks on both sides; the renderer (longshot_vk.dll) builds with llvm-mingw at 688441b1. The causes were read from Unity's 15 logs, counted by script, and the Hyrule Castle crash's address matched against a build of main @ 5e072fc8 made with the same toolchain as his nightly.

Not tested: This PR in the game, on any GPU: this PC has no Intel GPU, so nobody has yet seen the preparation finish, the Deku Tree cutscene or Hyrule Castle stay within the allowance, or the menu rows. Not measured: whether Intel's Windows driver honours DISABLE_OPTIMIZATION for these shaders and how much faster that build is; the GPU cost of the unoptimized passes until their optimized builds arrive; how often the 90% line makes textures come back (a hitch each) in big areas. Not built: Linux (host code is portable C++; only the Windows toolchain was run).

Needs other testers:

  • Intel UHD laptop on Windows (Unity's i3-1115G4): delete cache/longshot_prepare_skipped, rebuild, press Set up for a weak laptop, play to the Deku Tree's last cutscene and into Hyrule Castle; expecting the preparation to finish, no SLOW compile in play, no device loss, no crash in igvk64.dll, the start line integrated GPU texture budget naming about 7400 MB allowed, and the VRAM: lines under 90% of the allowance; then Death Mountain Trail and Goron City, the heaviest areas after the castle, for the same lines (T-AUXG)
  • The same laptop, the bright square: enter the Market once with RX_VK_SYNC_PIPELINES=1 set (pauses expected while it builds). Square not blown out: the left-out draws are the cause. Still blown out: something else, and a screenshot and log of that run (T-AUXG)
  • The same laptop, the black streaks on Link's hair: one run each with RX_VK_NAN_BITS=1, RX_VK_NO_F16=1 and RX_VK_ASTC_BC_ALL=0, one at a time, a close-up of Link's hair in each; whichever clears them names the cause (T-AUXG)
  • AMD integrated GPU (a Radeon 780M laptop): Auto and the button; expecting no new stutter from blocks made in play and no textures made again over and over at the 90% line (T-V2U8)

Screenshots

Nothing captured: the two new rows show only on Intel or integrated graphics, and this PC has neither. The memory line and the log line change nothing on screen. Unity's screenshots of main are described under Why.

Risk and rollback

  • Unoptimized scene passes cost more GPU time until their optimized builds are bound; on a GPU at 120-200 ms a frame that may show. The row (or RX_VK_COMPUTE_QUICK_FIRST=0) goes back to one optimized build.
  • Each dispatch built twice adds a pipeline per dispatch to the cache and keeps the unoptimized one for the session.
  • Integrated GPUs make texture blocks in play again: a vkAllocateMemory of 128 MB when textures grow, not measured on Intel's or AMD's integrated drivers. RX_VK_IMAGE_PREALLOC=9 restores the old count.
  • The 90% line on integrated GPUs evicts textures sooner: in a big area some are made again, a hitch when they come back. RX_VK_SQUEEZE_ALWAYS does not restore 97%; reverting 288e98c5 does.
  • Squeezing past the room in the blocks on integrated GPUs evicts textures that would have fit in it: more textures made again near the line, a hitch each. RX_VK_SQUEEZE_ALWAYS=1 already squeezed this way on any GPU; reverting 688441b1 restores the wait.
  • The refusals rule gives up 256 MB at least after any refused pipeline, on any GPU. A driver that refuses for another reason with an out-of-memory code costs some textures, made again when drawn. It logs each time (the first 8).
  • Smaller mips on integrated GPUs with 16 GB or less on Minimum: textures over 1024 texels are blurrier up close. RX_VK_LOW_VRAM_MIPS=0 turns it off.
  • The owed save makes the next build wait for one cache fetch and write (about 100 ms at 100 MB).
  • A folder that already has longshot_prepare_skipped keeps skipping until it is rebuilt or the file deleted (README).
  • The budget line's wording changed; a script that reads "driver allows" from it now gets the allowance, not the share.
  • Reverting the PR restores main's behaviour; nothing it writes is read by main differently.

Reviewer notes

Start with precompile_compute_quick and compute_optimize in gpu/vk/vk_draw.cpp, then blocks_at_start, count_line_pct, room_in_blocks and short_excess in gpu/vk/vram_compact.h and their use in texture_frame (gpu/vk/vk_texture.cpp), and note_out_of_memory in gpu/vk/vk_draw.cpp. Overlaps: #288 changes the line that calls low_vram_texture_mips (not its body); #50 (merged) added the pool fixture in tests/test_blocks_at_start.py, updated here for the device type; #284 reworks how that test finds its source, so whichever lands second merges it; #27 moves test_low_vram_texture_mips.py and test_precompile_compute_guard.py to a shared source cutter. Left for a follow-up on purpose: the CPU contention on 4-thread laptops (What changed, the last list), which is where this laptop's frame rate is held; the compaction's timers counted in presents; the hair and bright-square causes until his switch runs name them; and test_cpm_discovery on main (#524). Open questions: should the quick compute builds go to every GPU on serial builds (RX_VK_COMPUTE_SERIAL=1)? Should Auto keep 50% and leave 33% to the button? Is 90% right for every integrated GPU, or should AMD's APUs, whose driver may page, keep 97%?

Issues

Reported by Unity on Discord, in the issues channel (his report): the shader stutter and the blown-out Deku Tree dialogue, the device loss in the Deku Tree's last cutscene (nightly-130), the crash entering Hyrule Castle (nightly-132), the blown-out Castle Town square and Hyrule Castle, and the black streaks on Link's hair, each with his logs and options file (the lines quoted under Measurements). There is no issue on the tracker for them yet.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. (Game-free checks, deterministic, both sides on this PC; main's in-game numbers are from Unity's logs, this PR's in-game run is owed: Testing.)
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Flags noted, and the switch reference updated (docs/development.md, the README).
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Docs updated.
## Why On a weak Intel laptop (i3-1115G4, UHD Graphics, 2 cores / 4 threads, 16 GB, Windows) the game is close to unplayable. The build's shader preparation is stopped by its watchdog, so every start builds every shader in play. The scene's light and exposure passes are left out for minutes (the Deku Tree's dialogue drawn blown out). The GPU device was lost in the Deku Tree's last cutscene, and on `nightly-132` the game crashed inside Intel's driver entering Hyrule Castle, both times with video memory over what the driver allows. This PR lets the preparation finish, draws those passes within seconds, keeps integrated GPUs under their memory allowance with room to spare (and gives textures up as soon as the driver refuses memory), and adds a Debug button that sets the lightest rows at once. He also reports a blown-out Castle Town square and Hyrule Castle, and black streaks on Link's hair: what his logs say about those, and the switches that tell their causes apart on his laptop, are under What changed and Testing. ## What changed The causes, from Unity's logs: two sets of five on `nightly-130-0caf0618` (main @ 0caf0618) and five on `nightly-132-5e072fc8` (main @ 5e072fc8). - **Shaders.** Intel's Windows compiler builds compute pipelines one at a time (`serial_builds_on`). With the optimizer on it took 0.3 s to **389.5 s** for each of 37 dispatches on `nightly-130`, about 22 minutes on that one thread. On `nightly-132` it took 351 s in Kokiri Forest alone, 169 s for the longest. The preparation's 300 s watchdog stops it and leaves `longshot_prepare_skipped`, so the preparation was skipped in 15 of 15 logs. - **Memory, Deku Tree.** The textures' pool held 9 blocks (1152 MB) made at the start, which VMA keeps for the whole run, while the textures' budget fell to 720 MB. With the pool down to those blocks (`kept_only`), the rule that frees textures by the driver's count waited even past the allowance. The device was lost at 6.6 of 6.5 GB. - **Memory, Hyrule Castle.** The same 9 blocks were there (in 5 of 5 `nightly-132` logs). At 6.7 GB of 7.0 GB allowed, entering Hyrule Castle added 560 MB of imports (1648 to 2048 MB live, plus 160 MB retired but still imported). The count reached 7.5 of 7.0 GB and six graphics pipelines were not made (`-2`, `VK_ERROR_OUT_OF_DEVICE_MEMORY`). The next `vkCmdDispatchIndirect` then faulted inside `igvk64.dll`, reading `0x107e`, a null pointer plus an offset, as Intel's driver does when its own allocation failed. That dispatch is the mesh fallback's: `longshot_vk.dll+0x21a1f0` matches the return address of `fallback_record`'s `vkCmdDispatchIndirect(cmd, f->records, 20)` in a build of main @ 5e072fc8 with the nightly's own toolchain. Its pipelines and buffers are made and published correctly, so the fault is the driver's, out of memory. The changes: - jobs: `jobs_push_serial(fn, on_failure, first)` in `gpu/vk/vk_jobs.cpp` / `.h` adds a front lane. The game's dispatches go there, ahead of the mesh fallback's model builds, in order among themselves. After each build the thread calls `pipeline_cache_between_builds()`. - pipeline cache: `pipeline_cache_save` in `gpu/vk/vk_draw.cpp`. A save put off on Intel's serial builds (the `PcacheFetch` lock against igvk64's crash in `vkGetPipelineCacheData`) is now **owed** (`pcache_save_owed`). `pipeline_cache_between_builds` runs it on the serial thread between two builds, the one moment no compute build is under way. The function's signature is unchanged (checks cut it out by its text). - dispatch builds: `gpu/vk/vk_draw.cpp`, `gpu/vk/vk_shader.h`. On Intel's serial path a dispatch's first build is **without the optimizer** (`VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT`, as the mesh fallback's builds already are). `compute_optimize` then builds the optimized one on the back lane, and the dispatch binds it once made (`Program::compute_optimized`). The first pipeline stays for the session, because a recording may hold it. - preparation and precompile: `precompile_run` and `precompile_warm` in `gpu/vk/vk_draw.cpp`, through `precompile_compute_quick`. On Intel's serial builds they take the optimized pipeline when the caches have it, and otherwise build the unoptimized one, so the preparation finishes within its 300 s. A program made this way owes its optimized build (`Program::optimize_owed`), which its first dispatch queues on the back lane. Programs that are never dispatched are never optimized. - memory, blocks: `blocks_at_start` in `gpu/vk/vram_compact.h`, called from `gpu/vk/vk_vma.cpp`. An integrated GPU now makes **no texture blocks at the start**, because its heap is the PC's RAM. Blocks are made as needed and given back, and the driver's-count rule is not held back by `kept_only`. Dedicated cards keep the 15% of the heap made at the start (the ~170 ms `vkAllocateMemory` measured on AMD's Windows driver). `RX_VK_IMAGE_PREALLOC` still sets a number. - memory, line: `count_line_pct` in `gpu/vk/vram_compact.h`, used by `texture_frame` in `gpu/vk/vk_texture.cpp`. The textures give up the excess once the driver's count passes **90%** of the allowance on an integrated GPU, not 97%. A dedicated card's driver pages what is past the allowance out to system memory; an integrated GPU's heap is already system memory, so its driver's own allocations fail instead (both crashes above). On Unity's laptop 97% left 215 MB of room, less than the 560 MB Hyrule Castle added at once; 90% leaves 717 MB. Dedicated cards keep 97%. The VRAM log line's "room in the blocks: not squeezed" note goes by the same line. - memory, room in the blocks: `room_in_blocks` in `gpu/vk/vram_compact.h`, used by `texture_frame`. The line above alone would not have squeezed in his Hyrule Castle run: the last count before the load was 6709 of 7172 MB (93.5%), but 1121 MB of it was free inside VMA's blocks, more than an eighth of the allowance, and that room made the squeeze wait. On a dedicated card new images take that space. On an integrated GPU what grows near the line is the driver's own memory (pipelines, command memory) and the 64 MB staging arenas the load made, which no block's free space takes. The room no longer spares the squeeze on an integrated GPU; the compaction (on by default) gives the freed space back, so the ratchet the rule was written against (#2156, #2711) does not come back. Dedicated cards unchanged. - memory, refusals: `note_out_of_memory` in `gpu/vk/vk_draw.cpp` counts every pipeline the driver refuses with `VK_ERROR_OUT_OF_DEVICE_MEMORY` or `_HOST_MEMORY` (both `guarded_graphics_held` and `guarded_compute_held`, which every build goes through). At its next look (every 10 frames, about 3 s at his 3.5 fps) `texture_frame` gives up the count's excess over its line, 256 MB at least (`vram_compact::short_excess`), on any GPU, and logs `vk: the driver refused N pipeline(s) for want of memory`. His six refusals came before the fault in `igvk64.dll`; the textures did not react to them. - memory, log: the start line `vk: integrated GPU texture budget` printed 45% of the allowance (the dedicated cards' share for textures) as "driver allows 3331 MB" on a laptop allowed 7402 MB. It now prints the allowance and the share apart. The comment that called the share 70% now says 45% up to 8.5 GB, else 70%. - textures: `low_vram_texture_mips` in `gpu/vk/vk_texture.cpp`. It also counts an integrated GPU on a PC with 16 GB of RAM or less (`system_ram_bytes()`) as small, and it goes by Minimum's budgets (`opts.budget == 1`), not only the preset, because the button below leaves the preset on Custom with those budgets. On Minimum, decoded ASTC textures then keep at most 1024 texels: Unity's 1365x1365x90 arrays go from 53 MB to 13 MB. Render targets and depth images are untouched. - Auto: `gpu/vk/auto_settings.h`. Auto on an integrated GPU with 4 threads or fewer sets Render resolution to 33% (`RES_ULTRA_PERFORMANCE`) instead of Minimum's 50%. - menu: `gpu/vk/vk_menu.cpp`, `gpu/vk/vk_menu.h`, Debug tab: - **Intel quick compute builds** (On by default; shown, and run, only for Intel graphics on Windows, like the Intel shader compiles row: `intel_quick_compute` asks `intel_compiler_row`, since Mesa's ANV on Linux ignores the flag). Off: one optimized build, as before. - **Set up for a weak laptop**, a button shown only on integrated graphics. It sets Minimum's rows and memory budgets, Render resolution 33% with the game's own upscaler, plain window scale, no sharpening, no frame generation, Intel's shaders one at a time, and quick compute builds. The preset then reads Custom. The rows that need a restart ask for one at close, as when set by hand. - tests: `tests/test_blocks_at_start.py` and `.cpp` (the integrated GPU's blocks and line; the pool fixture that #50 added on main compiles the real `blocks_at_start`, which now asks the device type, so its stub device has one and an integrated case was added), `tests/test_texture_squeeze.py` (the squeeze goes by `count_line_pct` and `room_in_blocks`, and the refusals are counted and acted on), `tests/test_vram_compact.cpp` (`room_in_blocks` and `short_excess` with his numbers), and the checks named under Measurements. - docs: `README.md` (the smaller mips on integrated GPUs; `longshot_prepare_skipped` under *If something goes wrong*), `docs/development.md` (`RX_VK_COMPUTE_QUICK_FIRST`). - Deliberately unchanged: other GPUs. Nothing here runs without `serial_builds_on` except the memory blocks, the 90% line, the room rule, the mips rule, the Auto rule and the button, which all need an integrated GPU, and the refusals rule, which runs only once a driver has refused memory. Rows a player set on Custom stay as they are; only Auto and the button change rows. What the newest logs show that this PR does not change, for whoever picks it up next: - **The frame rate is held by the CPU, not the GPU.** In play (over 300 draws a frame) the `GPU-bound wait` was 0.0 to 0.1 ms in every sample: 97 in Kokiri Forest, 29 in Hyrule Field, 42 in the Market and Hyrule Castle. Those runs had medians of 5.4, 5.4 and 8.5 fps (110 to 185 ms a frame). The replay thread's own time was 4 ms a frame in the Market and 25 to 31 ms in Kokiri Forest and Hyrule Field. The rest goes to the game's threads, which share 4 hardware threads with the replay and the shader builds (`vcores`: waits of 0.2 to 2 ms on average, up to 1.9 s). A lower resolution or less GPU work will not raise this laptop's frame rate much; fewer builds in play (the preparation finishing) is what frees the CPU. - **Uploads are slow from contention, not size.** There were 145 uploads over 15 ms (the `slow upload` line), 5.3 s in all across four logs, and a 24 KB texture took 17 ms. No local fix. - **The 12288x12288 shadow map (D16, 288 MB) is checked for zeros on the CPU once a start**, when the game first makes it: 85 to 439 ms in each of 14 of his logs (105 ms in the Hyrule Castle one), a hitch at the first scene. The check is already split across threads and reads 8 bytes at a time; the time is most likely Windows giving 73,728 untouched pages their first touch, which the unswizzle it replaces would pay too. - **Frame generation costs little here.** The player had it on Auto, and Auto skips blending when two game frames are more than 100 ms apart (at 5 to 9 fps, nearly every frame). The button still turns it off, for the latency. - **The bright Castle Town square and Hyrule Castle.** On entering the Market in one log 413 to 770 draws a frame were left out with about 500 build jobs queued, and later spikes had 189 queued; compute dispatches left out were few there (5 in one log, 12 in another, each built within seconds), against the Deku Tree's 22 to 28 s builds. So in Castle Town the missing draws, not a missing exposure pass, are the likelier cause, and they end once the area's pipelines are built (a second visit uses the cache). Not changed here: `RX_VK_SYNC_PIPELINES=1` builds each pipeline at its draw instead of leaving it out, which tells the two apart on his laptop (Testing). - **Black streaks on Link's hair.** Nothing in the logs names a cause. What his laptop does that most PCs do not: under 8 GB allowed, every ASTC texture from 2 MB up, the characters' too, is decoded and re-encoded to BC3 on the GPU ("all large ASTC textures kept as BC3"); Intel's Windows compiler also gets the shaders' f16 math and `OpIsNan` as they are, and both went wrong on other drivers: hair drew white on RX 580s until f16 was lowered to f32 there, and AMD's older Windows compiler failed `OpIsNan` with black and white blocks (`RX_VK_NAN_BITS`; the NaN guard on colour outputs is such a test too). One switch each, in Testing; whichever clears the streaks becomes a default for Intel in a follow-up. - **Timers counted in presents.** The compaction's hold lets go of textures unused for 600 presents and its reserve 1800: ten and thirty seconds at 60 fps, three and nine minutes at his 3.5 fps, so the hold barely acts on his laptop (budget 1299 MB against 1776 MB held, 0 to 13 textures retired by it a pass). Counting them in seconds changes every GPU's behaviour, so it is left for a follow-up. - **Not this PR:** `tests/test_cpm_discovery.py` fails on main @ 7ff5256e (#524) with `'types.SimpleNamespace' object has no attribute 'find'`, and on this branch since it merged main. ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | Debug row "Intel quick compute builds" (ini `intel_quick_compute_builds`) | Intel's serial compute builds on Windows: an unoptimized pipeline first, the optimized one on the back lane | On (row shown, and the code run, on Intel Windows only) | On: the alternative is a pass missing for minutes | Intel iGPUs on Windows (UHD, Iris Xe) | Linux with Mesa's ANV, which ignores the flag (thomas-montano's Iris Xe run): off there since e072164e; every other GPU | Intel Arc; any Intel device in play | | `RX_VK_COMPUTE_QUICK_FIRST` | Overrides that row (0 off, 1 on) | Unset (the row decides) | Leave unset; for A/B tests | as the row | as the row | as the row | | Debug button "Set up for a weak laptop" | Sets the lightest rows at once (listed under What changed) | Not pressed (button) | Offer it to iGPU players with stutter | Integrated GPUs with 4 threads or fewer | Dedicated cards: the button is hidden | Every integrated GPU in play | | Auto on an integrated GPU with 4 threads or fewer | Render resolution 33% instead of 50% | On with Auto | On: 50% measured at 121-197 ms per frame on Unity's laptop | i3-class iGPU laptops | 8+ thread iGPUs: unchanged (50%) | In play | | Integrated GPU, 16 GB RAM or less, Minimum's budgets | Smaller ASTC mips (`RX_VK_LOW_VRAM_MIPS=0` turns it off, as before) | On | On | iGPUs sharing 16 GB or less | 32 GB PCs, dedicated cards: unchanged | In play | | Integrated GPU: no texture blocks at the start (`RX_VK_IMAGE_PREALLOC=<n>` still sets a number) | Blocks made as needed and given back | On | On | iGPUs: 1152 MB less held on Unity's laptop | Dedicated cards: unchanged | In play | | Integrated GPU: the driver's-count line at 90% (no switch; `RX_VK_SQUEEZE_ALWAYS=1` still squeezes whatever room the blocks hold) | Textures give up the excess once the driver's count passes 90% of the allowance (97% on dedicated cards) | On | On: past the allowance Intel's driver crashes rather than paging | iGPUs: 717 MB of room on Unity's laptop instead of 215 | Dedicated cards: unchanged (97%) | In play | | Integrated GPU: room in VMA's blocks no longer spares that squeeze (no switch) | Past the line the textures give up the excess whatever the blocks hold free | On | On: the driver's own allocations cannot use that room | iGPUs: his Hyrule Castle run sat at 93.5% with 1121 MB free in the blocks, unsqueezed | Dedicated cards: unchanged | In play | | Pipelines refused for want of memory (no switch) | The textures give up the count's excess over its line, 256 MB at least, at the next look | On, any GPU, only after a refusal | On: a driver that refuses memory faults soon after | Any GPU that runs out | A GPU that never runs out: never runs | In play | | `RX_VK_SYNC_PIPELINES=1` (existing, a test) | Each pipeline built at its draw, none left out | Unset | Only to test the bright square | — | — | His laptop | | `RX_VK_NAN_BITS=1`, `RX_VK_NO_F16=1`, `RX_VK_ASTC_BC_ALL=0` (existing, tests) | `isnan` as a test of bits; f16 math lowered to f32; the characters' ASTC kept as RGBA8, not BC3 | Unset | One at a time, only to test the hair | — | — | His laptop | ## Measurements main: 0caf0618 (2026-11-14) this PR: fix/igpu-weak-laptop @ 688441b1 Since measured: the in-game column for main is Unity's logs; the Hyrule Castle rows are from his `nightly-132-5e072fc8` logs (main @ 5e072fc8), which hold every change main made since 0caf0618 that touches these paths. This PR's column is the game-free checks, run at 688441b1 (main @ 7ff5256e merged in). Hardware: main's in-game column is Unity's laptop (i3-1115G4, Intel UHD Graphics, driver 0x195bb0, 16 GB, Windows; 7402 MB allowed at the start); the checks ran on this PC (Windows 11, llvm-mingw clang 20261104, Python 3) Scene: his play to the Deku Tree's last cutscene (two sets of five `nightly-130` logs), and from Kokiri Forest through Hyrule Field and the Market into Hyrule Castle (five `nightly-132` logs); 640x480 output, Render resolution 33%, FSR 3.1, frame generation Auto, Custom preset; the checks need no game Method: lines counted by script from his 15 logs; the crash address matched against a build of main @ 5e072fc8 with the nightly's own toolchain (`llvm-symbolizer`, `llvm-objdump`); the PR's changed checks run against main's source or the branch before each fix, and against this PR's; `tests/run_all.py` on both sides How to reproduce: `python -I -B tests/run_all.py` on each side; for main's source, copy this PR's `tests/` changes into a worktree at 0caf0618 and run the checks named below; for the crash, build main @ 5e072fc8 with `RX_VK_ONLY=1` and run `llvm-symbolizer --obj=longshot_vk.dll 0x18021a1f0` | | main @ 0caf0618 | this PR @ 688441b1 | |---|---|---| | Shader preparation at the build (Unity's logs) | skipped in 15 of 15 logs (10 `nightly-130`, 5 `nightly-132`) | not run on his laptop yet (Testing) | | Slowest compute build, optimizer on (Unity's logs) | 389.5 s (`nightly-130`); 169 s, 351 s in all in Kokiri Forest (`nightly-132`) | first build unoptimized (test_pipeline_state_computes) | | Texture blocks made at the start, his integrated GPU (8061 MB heap) | 9 (1152 MB), kept all run, in 15 of 15 logs | 0 (test_blocks_at_start) | | Driver's count where the textures give up the excess, integrated GPU (7191 MB allowed) | 97%: 6975 MB; crashed at 7.5 GB entering Hyrule Castle (`nightly-132`) | 90%: 6472 MB (test_blocks_at_start) | | His last count before Hyrule Castle: 6709 of 7172 MB, 1121 MB free in the blocks | not squeezed (room in the blocks) | squeezed by 259 MB (test_vram_compact, test_texture_squeeze) | | Six pipelines refused with `-2` (his `nightly-132` log) | nothing given up; a fault in `igvk64.dll` next | the excess or 256 MB given up at the next look (test_vram_compact, test_texture_squeeze) | | The integrated GPU budget line on his laptop | "driver allows 3331 MB" (the 45% share; 7402 MB allowed) | the allowance and the share apart (read in the source; nothing checks a log line) | | 1365x1365x90 ASTC array after the weak laptop button (Custom, Minimum's budgets) | 53 MB, full size | 13 MB, 682x682 (test_low_vram_texture_mips) | | Auto's render resolution, integrated GPU, 4 threads | 50% | 33% (test_auto_settings) | | The PR's 10 changed checks | 10 fail (8 on main's source; `test_texture_squeeze` on the branch before 288e98c5 and again before 688441b1; `test_vram_compact` before 688441b1) | 10 pass | | `tests/run_all.py` | 532 passed, 5 failed, 34 skipped (main @ 1be60eae) | 532 passed, 6 failed, 34 skipped at 688441b1: the same 5 and `test_cpm_discovery`, which fails on main @ 7ff5256e too (#524) | ``` [hos host] longshot nightly-130-0caf0618 (source 65722f5b) [hos host] vk: the shaders' preparation was skipped at the build: left to when each is first drawn [hos host] vk: compute pipeline of 383a566958350efe: SLOW compile, 389499 ms (on a worker) [hos Presentation Thread] vk: over the driver's video-memory allowance (6.6 of 6.5 GB) [hos Presentation Thread] vk: image 1119x1119x1 x1 levels 1 format 126 (NVN 0x34) not made [hos Presentation Thread] vk: submit 257558 failed (-4) [hos Presentation Thread] vk: FATAL: the GPU device was lost (VK_ERROR_DEVICE_LOST): ... [hos host] longshot nightly-132-5e072fc8 (source b03e150f) [hos Presentation Thread] vk: textures from a pool of their own, 128 MB blocks (RX_VK_COMPACT), 9 made at the start [hos Presentation Thread] vk: integrated GPU texture budget: 2560 MB (system RAM 15 GB, driver allows 3331 MB, reserved 8 GB) [hos Presentation Thread] vk: VRAM: 6717 MB used of 7191 MB allowed (878 MB of it free in the blocks); textures 2004 MB (budget 1312 MB), 1904 textures [hos Presentation Thread] vk: imports: 49 live (1648 MB), 0 retired still imported (0 MB), 7 retired given back (512 MB) [hos BaseProcExecutor5] map: HyruleCastle (child) [hos Presentation Thread] vk: imports: 61 live (2048 MB), 2 retired still imported (160 MB), 10 retired given back (736 MB) [hos Presentation Thread] vk: over the driver's video-memory allowance (7.5 of 7.0 GB) [hos host] vk: graphics pipeline for 6f9142def3b6a978 not made (-2) [hos Presentation Thread] CRASH: exception 0xc0000005 (read 0x107e, guest 0xfffff0000000107e) at igvk64.dll+0x87793 [hos Presentation Thread] CRASH: host backtrace: igvk64.dll+0x87793 igvk64.dll+0x101855 igvk64.dll+0xe37d8 igvk64.dll+0x725ac longshot_vk.dll+0x21a1f0 longshot_vk.dll+0x1ee232 longshot_vk.dll+0x1d2fa5 ... main @ 5e072fc8 built with the nightly's toolchain, llvm-symbolizer: 0x18021a1f0 fallback_record(...)::$_0::operator()(unsigned long long, bool) const (return address of vkCmdDispatchIndirect, offset 0x14) 0x1801ee232 fallback_record(VkCommandBuffer_T*, mkwvk::Program&, mkwvk::DrawArgs const&) 0x1801d2fa5 mkwvk::draw(VkCommandBuffer_T*, mkwvk::NvnState&, mkwvk::DrawArgs const&) main @ 0caf0618, with this PR's checks: test_intel_quick_compute exit 1: FAIL: the quick build is not limited to the serial builds and the row test_pipeline_cache_save exit 1: Ran 15 tests / FAILED (failures=1, skipped=1) test_pcache_fetch_lock exit 1: AssertionError: a save put off is not owed to the serial builds' thread test_auto_settings exit 1: test_auto_settings.cpp:23:28: error: use of undeclared identifier 'RES_ULTRA_PERFORMANCE' test_low_vram_texture_mips exit 1: PASS custom-preset / FAIL:107 x.skip == (uint32_t)skip (the next case: custom-with-minimums-budgets) test_blocks_at_start exit 1: vk_vma.cpp: blocks_at_start() no longer has: vram_compact::blocks_at_start(heap, kImageBlock, holds_textures, dev.props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) test_pipeline_state_computes exit 1: ValueError: substring not found (no precompile_compute_quick) test_precompile_compute_guard exit 1: ValueError: substring not found (no precompile_compute_quick) the branch before 288e98c5 (the 90% line), with its checks: test_blocks_at_start exit 1: test_blocks_at_start.cpp:55:11: error: use of undeclared identifier 'count_line_pct' test_texture_squeeze exit 1: the driver's-limit squeeze holds an integrated GPU at a dedicated card's 97%, where its driver's own allocations fail past the allowance the branch before 688441b1 (the room rule and the refusals), with their checks: test_texture_squeeze exit 1: the 97% squeeze runs whatever room VMA's blocks hold (the check now reads room_in_blocks in vram_compact.h) test_vram_compact exit 1: test_vram_compact.cpp:106:11: error: use of undeclared identifier 'room_in_blocks' this PR @ 688441b1: test_intel_quick_compute pass test_pipeline_cache_save pass test_pcache_fetch_lock pass test_auto_settings pass test_low_vram_texture_mips pass test_blocks_at_start pass (PASS pool constructors: default, compaction-off, override, prealloc-off, fallback, failed-pool, integrated) test_pipeline_state_computes pass test_precompile_compute_guard pass test_texture_squeeze pass test_vram_compact pass (PASS: the compaction's rules) test_keep_rendered pass (reads the same 97% elsewhere: unchanged) ``` The pipeline cache: in Unity's first set of logs, five starts each loaded 182 KB and one save was logged in all. In his second set it grew from 0.6 MB to 77 MB over five starts, so saves did happen there. The owed save makes them happen between builds, not only when no build is under way. `tests/run_all.py`, both sides on this PC: main @ 1be60eae and this branch at 288e98c5 gave 532 passed, 5 failed, 34 skipped each (`test_release`, `test_oot_state_tap`, `test_tested_configurations`, `test_dev_packs`, `test_walk_key`: this PC's setup, none reads these files). `test_dlss_sdk_cache` failed once on the branch while both suites ran at once and passed twice on each side alone. At 159a12d9 (main @ 7ff5256e merged in) every check was run by name except `test_longshot_ctl` and `test_oot_state_tap`, which start a detached process that opens a console window on this PC: the same 4 setup failures as main, `test_cpm_discovery` (fails on main @ 7ff5256e too, #524), and 12 checks that declare a Windows skip and only ran because they were named. ## Testing **Tested:** On this PC (Windows 11, llvm-mingw clang 20261104, no Intel GPU), as in Measurements: the changed checks fail without their fix and pass with it; `tests/run_all.py` fails the same checks on both sides; the renderer (`longshot_vk.dll`) builds with llvm-mingw at 688441b1. The causes were read from Unity's 15 logs, counted by script, and the Hyrule Castle crash's address matched against a build of main @ 5e072fc8 made with the same toolchain as his nightly. **Not tested:** This PR in the game, on any GPU: this PC has no Intel GPU, so nobody has yet seen the preparation finish, the Deku Tree cutscene or Hyrule Castle stay within the allowance, or the menu rows. Not measured: whether Intel's Windows driver honours `DISABLE_OPTIMIZATION` for these shaders and how much faster that build is; the GPU cost of the unoptimized passes until their optimized builds arrive; how often the 90% line makes textures come back (a hitch each) in big areas. Not built: Linux (host code is portable C++; only the Windows toolchain was run). **Needs other testers:** - Intel UHD laptop on Windows (Unity's i3-1115G4): delete `cache/longshot_prepare_skipped`, rebuild, press Set up for a weak laptop, play to the Deku Tree's last cutscene and into Hyrule Castle; expecting the preparation to finish, no `SLOW compile` in play, no device loss, no crash in `igvk64.dll`, the start line `integrated GPU texture budget` naming about 7400 MB allowed, and the `VRAM:` lines under 90% of the allowance; then Death Mountain Trail and Goron City, the heaviest areas after the castle, for the same lines (T-AUXG) - The same laptop, the bright square: enter the Market once with `RX_VK_SYNC_PIPELINES=1` set (pauses expected while it builds). Square not blown out: the left-out draws are the cause. Still blown out: something else, and a screenshot and log of that run (T-AUXG) - The same laptop, the black streaks on Link's hair: one run each with `RX_VK_NAN_BITS=1`, `RX_VK_NO_F16=1` and `RX_VK_ASTC_BC_ALL=0`, one at a time, a close-up of Link's hair in each; whichever clears them names the cause (T-AUXG) - AMD integrated GPU (a Radeon 780M laptop): Auto and the button; expecting no new stutter from blocks made in play and no textures made again over and over at the 90% line (T-V2U8) ## Screenshots Nothing captured: the two new rows show only on Intel or integrated graphics, and this PC has neither. The memory line and the log line change nothing on screen. Unity's screenshots of main are described under Why. ## Risk and rollback - Unoptimized scene passes cost more GPU time until their optimized builds are bound; on a GPU at 120-200 ms a frame that may show. The row (or `RX_VK_COMPUTE_QUICK_FIRST=0`) goes back to one optimized build. - Each dispatch built twice adds a pipeline per dispatch to the cache and keeps the unoptimized one for the session. - Integrated GPUs make texture blocks in play again: a `vkAllocateMemory` of 128 MB when textures grow, not measured on Intel's or AMD's integrated drivers. `RX_VK_IMAGE_PREALLOC=9` restores the old count. - The 90% line on integrated GPUs evicts textures sooner: in a big area some are made again, a hitch when they come back. `RX_VK_SQUEEZE_ALWAYS` does not restore 97%; reverting 288e98c5 does. - Squeezing past the room in the blocks on integrated GPUs evicts textures that would have fit in it: more textures made again near the line, a hitch each. `RX_VK_SQUEEZE_ALWAYS=1` already squeezed this way on any GPU; reverting 688441b1 restores the wait. - The refusals rule gives up 256 MB at least after any refused pipeline, on any GPU. A driver that refuses for another reason with an out-of-memory code costs some textures, made again when drawn. It logs each time (the first 8). - Smaller mips on integrated GPUs with 16 GB or less on Minimum: textures over 1024 texels are blurrier up close. `RX_VK_LOW_VRAM_MIPS=0` turns it off. - The owed save makes the next build wait for one cache fetch and write (about 100 ms at 100 MB). - A folder that already has `longshot_prepare_skipped` keeps skipping until it is rebuilt or the file deleted (README). - The budget line's wording changed; a script that reads "driver allows" from it now gets the allowance, not the share. - Reverting the PR restores main's behaviour; nothing it writes is read by main differently. ## Reviewer notes Start with `precompile_compute_quick` and `compute_optimize` in `gpu/vk/vk_draw.cpp`, then `blocks_at_start`, `count_line_pct`, `room_in_blocks` and `short_excess` in `gpu/vk/vram_compact.h` and their use in `texture_frame` (`gpu/vk/vk_texture.cpp`), and `note_out_of_memory` in `gpu/vk/vk_draw.cpp`. Overlaps: #288 changes the line that calls `low_vram_texture_mips` (not its body); #50 (merged) added the pool fixture in `tests/test_blocks_at_start.py`, updated here for the device type; #284 reworks how that test finds its source, so whichever lands second merges it; #27 moves `test_low_vram_texture_mips.py` and `test_precompile_compute_guard.py` to a shared source cutter. Left for a follow-up on purpose: the CPU contention on 4-thread laptops (What changed, the last list), which is where this laptop's frame rate is held; the compaction's timers counted in presents; the hair and bright-square causes until his switch runs name them; and `test_cpm_discovery` on main (#524). Open questions: should the quick compute builds go to every GPU on serial builds (`RX_VK_COMPUTE_SERIAL=1`)? Should Auto keep 50% and leave 33% to the button? Is 90% right for every integrated GPU, or should AMD's APUs, whose driver may page, keep 97%? ## Issues Reported by Unity on Discord, in the issues channel ([his report](https://discord.com/channels/1569849200047427645/1571208448681447548)): the shader stutter and the blown-out Deku Tree dialogue, the device loss in the Deku Tree's last cutscene (`nightly-130`), the crash entering Hyrule Castle (`nightly-132`), the blown-out Castle Town square and Hyrule Castle, and the black streaks on Link's hair, each with his logs and options file (the lines quoted under Measurements). There is no issue on the tracker for them yet. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. (Game-free checks, deterministic, both sides on this PC; main's in-game numbers are from Unity's logs, this PR's in-game run is owed: Testing.) - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Flags noted, and the switch reference updated (docs/development.md, the README). - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Docs updated.
fix(intel): weak iGPU laptops: the scene's passes first and unoptimized at once, the pipeline cache saved between builds, 33% on 4 threads, a Debug button
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
9afadc4e3e
On an i3-1115G4's UHD Graphics (2 cores, 4 threads, 16 GB) the game's 37 dispatches took 0.3 s to 6.5 min each with Intel's optimizer, 22 minutes on the one serial thread behind the mesh fallback's builds; the Deku Tree's dialogue was drawn blown out meanwhile, and the pipeline cache was never saved (182 KB at every start).

- dispatches' builds go first in the serial queue; on Intel's serial builds the first is made without the optimizer and the optimized one is bound once built (Debug: Intel quick compute builds, RX_VK_COMPUTE_QUICK_FIRST)
- a pipeline cache save put off for builds under way is owed and runs on the serial thread between two builds
- Auto: an integrated GPU with 4 threads or fewer starts at 33%
- Debug: Set up for a weak laptop (integrated graphics), the lightest rows at once

Co-authored-by: Cursor <[email protected]>
fix(vk): Minimum's smaller texture mips on integrated GPUs with 16 GB of RAM or less
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 4s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
c39e9b70be
An integrated GPU's VRAM budget is a share of the system's RAM: Unity's i3-1115G4 (UHD Graphics, 16 GB) was allowed
7.4 GB, past the 4.5 GiB that turns on Minimum's 1024 limit for decoded ASTC textures, while the game, 2 GB of textures
and the driver held 12-13 GB of private memory and Windows paged the game out. On Minimum, an integrated GPU with 16 GB
of RAM or less now counts as small whatever the driver allows.

Co-authored-by: Cursor <[email protected]>
fix(intel): the shader preparation and a start's precompile build Intel's compute pipelines without the optimizer first
Some checks failed
PR description / Description follows the template (pull_request_target) Failing after 3s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
81a7775d72
With the optimizer, one of the game's compute pipelines took Intel's compiler up to 389.5 s on a UHD Graphics
(i3-1115G4): past the build's shader preparation's 300 s with nothing finished, so the preparation was stopped and left
longshot_prepare_skipped, and every later start built each shader when first drawn (all of Unity's runs: "the shaders'
preparation was skipped at the build").

On Intel's one-at-a-time builds with the Intel quick compute builds row on, precompile_run and precompile_warm now ask
the caches for the optimized pipeline first and otherwise build the one a dispatch's first build asks for, without the
optimizer. A program made so owes its optimized build to its first dispatch, which queues it behind the builds asked for
first: a first start makes every kept compute program's (323 dispatched on that laptop), most never dispatched.

Co-authored-by: Cursor <[email protected]>
Yeetbot90 changed title from WIP: fix(vk): on weak Intel laptops the scene's passes draw within seconds, the pipeline cache is saved, and a Debug button sets the lightest rows to fix(vk): on weak Intel laptops the shaders are prepared and the scene's passes draw within seconds, the pipeline cache is saved, and a Debug button sets the lightest rows 2026-11-14 10:57:23 +00:00
fix(vk): integrated GPUs keep no texture blocks made at the start, and Custom after the weak laptop button keeps the smaller mips
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 12s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
922301bda0
Unity's laptop (UHD Graphics, 16 GB) lost the device in the Deku Tree's last cutscene: at 6.6 GB of the 6.5 the driver
allowed, a 1119 x 1119 depth image was not made, and the next submit failed with VK_ERROR_DEVICE_LOST. The textures' pool
started with 15% of the reported heap, nine 128 MB blocks (1152 MB) of the PC's RAM that VMA keeps for the whole run, while
the textures' budget had fallen to 720 MB; and the pool down to those blocks (kept_only) kept the 97% rule from squeezing.
On an integrated GPU no blocks are made at the start now: they are made as needed and given back, and the 97% rule applies.
RX_VK_IMAGE_PREALLOC still sets a number.

low_vram_texture_mips went by the Quality preset, and the Debug tab's Set up for a weak laptop leaves it on Custom with
Minimum's budgets: it now goes by those budgets (opts.budget 1) too.

Co-authored-by: Cursor <[email protected]>
First-time contributor

I checked this PR against Mesa's Intel driver on Linux (Iris Xe, i9-13900H, Mesa 26.2.4). serial_builds_on() goes by vendorID == 0x8086, and intel_quick defaults to 1 even where the row is hidden, so the quick compute builds also run on Linux Intel, not only on Windows as the description says. On Mesa they don't do anything: ANV ignores VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT. Building compute shader 0a9b6e3892907675 alone (held to 32 lanes, shader cache off) gave the same instruction count, cycles, spills and compile time with and without the flag (18.4–18.9 s, two runs each), and the optimized rebuild through the same pipeline cache took 0.0 s. So on Linux it just adds a second pipeline handle per dispatch, which is harmless but doesn't help. It might be worth limiting it to Intel's Windows driver (dev.driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, or #ifdef _WIN32 as the row does), so the row and the code agree. On Linux the slow part is ANV's register allocation spilling at SIMD32, not its optimizer. I have fixes for that on a branch: compute-pipeline pinning on Mesa Intel, a compile gate weighted by memory, a smaller preparation plan, a lower spilling rate and a lighter mesh fallback epilogue. I'll open a PR for them once I've finished testing on this laptop. Your branch merges cleanly with mine, and the checks we both touch pass on the merge.

I checked this PR against Mesa's Intel driver on Linux (Iris Xe, i9-13900H, Mesa 26.2.4). serial_builds_on() goes by vendorID == 0x8086, and intel_quick defaults to 1 even where the row is hidden, so the quick compute builds also run on Linux Intel, not only on Windows as the description says. On Mesa they don't do anything: ANV ignores VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT. Building compute shader 0a9b6e3892907675 alone (held to 32 lanes, shader cache off) gave the same instruction count, cycles, spills and compile time with and without the flag (18.4–18.9 s, two runs each), and the optimized rebuild through the same pipeline cache took 0.0 s. So on Linux it just adds a second pipeline handle per dispatch, which is harmless but doesn't help. It might be worth limiting it to Intel's Windows driver (dev.driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, or #ifdef _WIN32 as the row does), so the row and the code agree. On Linux the slow part is ANV's register allocation spilling at SIMD32, not its optimizer. I have fixes for that on a branch: compute-pipeline pinning on Mesa Intel, a compile gate weighted by memory, a smaller preparation plan, a lower spilling rate and a lighter mesh fallback epilogue. I'll open a PR for them once I've finished testing on this laptop. Your branch merges cleanly with mine, and the checks we both touch pass on the merge.
Author
Contributor

ok

ok
fix(intel): the quick compute builds only on Intel's Windows driver, where their row shows
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
e072164e3d
serial_builds_on() goes by Intel's vendor ID and the row defaulted to on where it is hidden, so the quick builds also ran
on Linux with Mesa's ANV. ANV ignores VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT: the same instructions, cycles, spills
and compile time with and without it on an Iris Xe (Mesa 26.2.4, reported on the pull request), so there each dispatch
only gained a second pipeline handle. intel_quick_compute() now also asks intel_compiler_row(), the row's own rule
(Intel graphics on Windows), so the row and the code agree.

Co-Authored-By: Cursor <[email protected]>
Author
Contributor

Thanks, that's right: the row was Windows-only but the code wasn't. e072164e makes intel_quick_compute() also ask intel_compiler_row() (Intel graphics on Windows, the row's own rule), so on Linux with ANV the dispatches get the one optimized build, as before. test_intel_quick_compute.py checks it, and docs/development.md now says Windows only. Good to hear the branches merge cleanly; I'll look out for your ANV spilling PR.

Thanks, that's right: the row was Windows-only but the code wasn't. e072164e makes `intel_quick_compute()` also ask `intel_compiler_row()` (Intel graphics on Windows, the row's own rule), so on Linux with ANV the dispatches get the one optimized build, as before. `test_intel_quick_compute.py` checks it, and `docs/development.md` now says Windows only. Good to hear the branches merge cleanly; I'll look out for your ANV spilling PR.
Merge branch 'main' into fix/igpu-weak-laptop
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
af5f76576b
Merge branch 'main' into fix/igpu-weak-laptop
Some checks failed
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 10s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
0c41ff5fd6
Merge branch 'main' into fix/igpu-weak-laptop
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 15s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
fd8698c101
Merge branch 'main' into fix/igpu-weak-laptop
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 5s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
33f63677aa
Merge branch 'main' into fix/igpu-weak-laptop
Some checks failed
PR description / Description follows the template (pull_request_target) Has been cancelled
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
20f99162f8
Merge branch 'main' into fix/igpu-weak-laptop
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 17s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
082c69ceb6
fix(vk): integrated GPUs give up textures at 90% of the driver's allowance, not 97%
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 17s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
288e98c5c7
An Intel UHD Graphics laptop (i3-1115G4, 16 GB, nightly-132-5e072fc8) crashed entering Hyrule Castle: 6.7 GB used of the
7.0 GB the driver allowed, its imports grew by 560 MB as the area loaded, the count reached 7.5 of 7.0 GB, six graphics
pipelines were not made (-2, out of device memory), and the next vkCmdDispatchIndirect (fallback_record's mesh dispatch)
faulted inside igvk64.dll reading 0x107e. The Deku Tree's device loss on the same laptop was the same at 6.6 of 6.5 GB.

The squeeze by the driver's count (texture_frame) acted only past 97% of the allowance: right for a dedicated card, whose
driver pages the excess out to system memory, but an integrated GPU's heap is the PC's RAM and its driver has nowhere to
page; its own allocations fail instead. vram_compact::count_line_pct gives 97 on a dedicated card and 90 on an integrated
GPU (717 MB of room on that laptop, not 215), and texture_frame takes the squeeze and its log line from it.

On main that laptop's textures' pool also kept nine blocks made at the start (kept_only), which kept the squeeze waiting
past the allowance; this branch already makes none on integrated GPUs.

tests/test_blocks_at_start.py: the pool fixture added on main (#50) compiles the real blocks_at_start, which this branch
asks for the device type; its stub device now has one, and an integrated case checks no blocks are made at the start.

Co-Authored-By: Cursor <[email protected]>
fix(vk): the integrated GPU's texture budget line says what the driver allows, not the share taken from it
Some checks failed
beanstalk/approval awaiting maintainer approval
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
159a12d991
"integrated GPU texture budget: 2560 MB (system RAM 15 GB, driver allows 3331 MB, ...)" printed 45% of the allowance
(the dedicated cards' share for textures, own) as what the driver allows. The UHD Graphics laptop at Hyrule Castle was
allowed 7402 MB at the start and 7191 MB later; the line read as a 3.3 GB limit. It now prints the allowance and the
share apart, and the comment that called the share 70% says 45% up to 8.5 GB, else 70%.

Co-Authored-By: Cursor <[email protected]>
fix(vk): integrated GPUs squeeze textures whatever room VMA's blocks hold, and give textures up when the driver refuses a pipeline for want of memory
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
beanstalk/approval awaiting maintainer approval
688441b1fd
On the UHD Graphics laptop (i3-1115G4) the last count before Hyrule Castle was 6709 of 7172 MB, over the 90% line, with
1121 MB free in VMA's blocks: more than an eighth of the allowance, so the squeeze waited. On a dedicated card new images
take that space; on an integrated GPU what grows near the line is the driver's own memory (pipelines, command memory) and
the staging arenas, which no block's free space takes. The load took the count to 7.5 of 7.0 GB, six graphics pipelines
were refused (-2) and the next indirect dispatch faulted inside igvk64.dll.

- vram_compact::room_in_blocks: the room in the blocks no longer spares the count line's squeeze on an integrated GPU
  (dedicated cards unchanged: within the allowance, an eighth free).
- A pipeline refused with VK_ERROR_OUT_OF_DEVICE_MEMORY or _HOST_MEMORY is counted (guarded_graphics_held,
  guarded_compute_held); at its next look texture_frame gives up the count's excess over its line, 256 MB at least
  (vram_compact::short_excess), on any GPU, and logs it.

Co-Authored-By: Cursor <[email protected]>
HumanBean deleted branch fix/igpu-weak-laptop 2026-11-14 21:05:58 +00:00
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
beanstalk/approval awaiting maintainer approval
Some workflows are waiting to be reviewed.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!404
No description provided.