Repository navigation
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On integrated GPUs, treat an impossible Vulkan free-memory report (
free > total, produced when the unsignedheapBudget - heapUsagesubtraction wraps past the heap total) as unusable input rather than "no memory": fall back to the tracked-residency estimatetotal - residentthat the same lambda already computes. Discrete GPUs keep the existing behavior — a wrapped report still maps to 0 available. Note that every non-Vulkan backend already treats this exact signature this way: afree > totalreport does not early-return for them — it falls through to the samemin(free, total - resident)clamp. This change gives Vulkan iGPUs the same treatment, so it is parity with existing semantics rather than new policy. The fallback path also emits a one-lineLOG_VERBOSEwith the raw free/total values, so affected users can surface the wrapped report under-vfor diagnosis.Fixes #2022. May also unblock the same
available 0.00 MBsignature in #2073 if its failure was on the device-free side ofcheck_capacity; if it was on theavailable_budget_bytes(--max-vram/auto-fit) side, this change does not apply — the two sides produce an identical log line.Why
ggml_backend_vk_device_get_memorysumsheapBudget - heapUsageper heap in unsigned arithmetic, and the Vulkan memory-budget spec allows usage to exceed the advisory budget — so a single over-budget heap wrapsfreeto ~2^64. The Vulkan-only guard added in #2020 then reportsavailable 0.00 MBand weight preparation aborts (cannot make enough memory available). On an iGPU this conclusion is particularly misleading: all heaps are backed by the same system RAM, the budget is a soft driver hint, and ggml's UMA allocator falls back to host-visible memory anyway — a wrapped figure is a broken report, not evidence of exhaustion.When the report is impossible, the only trustworthy bound available here is the manager's own accounting, so the wrapped value is replaced by
totaland the existingmin(free, total - resident)clamp applies — i.e., "what we believe is still free by our own books". This is a best-effort bound, not a guarantee of allocatable memory; genuine pressure still surfaces as an allocation failure instead of a wrong early abort. Nothing changes when the report is possible (wrapped or not, the discrete path is untouched).Verification
Hardware: AMD Radeon 610M iGPU (
uma: 1, two heaps ≈ 15.8 GiB shared pool) + NVIDIA RTX 5070 Ti Laptop (discrete Vulkan), Windows 11, master2988060+ this change,SD_VULKAN=ONbuild.heapBudget >= heapUsage, so the wrap is not naturally reachable here — measured with a standalone probe againstggml_backend_dev_memory: reported free decreases linearly across 0–15 GiB of device allocations (15.4 → 0.06 GiB) and allocations fail cleanly at the pool limit. Documented as a hardware/driver difference vs. the Intel report.free = total + 1 KiBintoavailable_device_bytes(local-only, reverted):model manager cannot make enough memory available on Vulkan0: need 589.20 MB device / 77.20 MB budget, available 0.00 MB device / 3072.00 MB budget, failing atqwen_image_2_1 segment 1/34 (qwen_image_2_1.prelude);--backend diffusion=vulkan1,vae=vulkan1,te=cpu --max-vram vulkan1=3 --diffusion-fa --vae-tiling, 256x256).free > totalnever occurs on these drivers, so the existing path is untouched there.Not covered (intentionally out of scope)
available_ram_bytes()(currently inbackend_fit.cpp) to account for untracked system-memory consumers — happy to add that as a follow-up or on request.eDiscreteGpu(e.g., some Strix Halo configurations) keep the old 0-return; covering them would need a different discriminator than the device type.freefigure can also ingest the wrapped value — most notablyggml_graph_cut.cppauto--max-vramdetection, where a wrapped report effectively disables the auto limit, pluslayer_split_partition.cppcapacities,diffusion_engine.cpprow split, andbackend_fit.cppplanning. None of them abort on it, so this PR changes only the single aborting call site; normalizing all consumers behind a shared sanitizer is a reasonable follow-up.total - residentfallback is deliberately optimistic under genuine external pressure: on iGPUstotalsums all heaps (which share the same physical RAM) andresidentonly counts manager-tracked bytes, so the estimate can exceed what is really allocatable when the OS or other apps hold memory. That failure stays on the normal allocation-error path — the Vulkan buffer-type allocator catchesvk::SystemErrorand returns nullptr — rather than the early "cannot make enough memory available" abort. A host-RAM clamp (available_ram_bytes()) would tighten this; left as a follow-up to keep the diff minimal.leejet/ggml) if preferred upstream; this change only makes sd.cpp robust to the impossible report.Checklist
Summary by CodeRabbit