Studio: pin sd.cpp to the mirror build with Windows CUDA, sharing llama.cpp's CUDA runtime by danielhanchen · Pull Request #13175 · unslothai/unsloth · GitHub
Skip to content

Studio: pin sd.cpp to the mirror build with Windows CUDA, sharing llama.cpp's CUDA runtime - #13175

Merged
oobabooga merged 7 commits into
mainfrom
studio-sd-cpp-upstream-fallback-qwen-image-2-1
Oct 12, 2026
Merged

oobabooga merged 7 commits into
mainfrom
studio-sd-cpp-upstream-fallback-qwen-image-2-1

Conversation

@danielhanchen

@danielhanchen danielhanchen commented Oct 10, 2026 •

Copy link
Copy Markdown
Member

Fixes the native engine bug in #12470 (the engine / text encoder picker that issue also asks for is a separate feature and is not part of this PR).

The bug

The pinned sd.cpp release (master-813-bfbef5b-u1d02858) on the Unsloth mirror has no Windows CUDA, Windows ROCm or Linux ROCm asset. On those hosts the installer falls back to upstream at the pin's base, master-813-bfbef5b (2026-08-05). Qwen-Image-2.1 landed upstream in master-883 (137f740, 2026-09-20), so that build cannot run it.

What the reporter saw on Windows + CUDA with the native engine:

  1. The download plan predicts native (nothing installed yet, install allowed) and stages ~11 GB of sd.cpp companions.
  2. The load installs leejet/stable-diffusion.cpp master-813-bfbef5b, the router's architecture gate finds no qwen_image_2_1 in it, logs predates qwen-image-2.1 support; using diffusers, and the load pulls the ~17 GB dense text encoder instead.
  3. Nothing ever upgrades that build, since it was installed for the current pin.

The fix

  • Pin: DEFAULT_TAG moves to master-951-f89d9b1-uafac7a7, the mirror release from Publish Windows CUDA bundles and leave the CUDA runtime to the host stable-diffusion.cpp#25. It is built on upstream master-951, which carries Qwen-Image-2.1 and its reference-alpha fix (master-896), and it now ships Windows CUDA (win-cuda12-x64, win-cuda13-x64) and Linux CUDA 12 / 13 bundles. Hosts the mirror still does not build (ROCm) fall back to upstream master-951-f89d9b1, whose Linux and Windows ROCm builds carry every family marker (qwen_image_2_1, qwen_image_layers, the 2.1 edit marker); Studio's own family check accepts the Linux ROCm install for Qwen-Image-2.1.
  • Upgrade of existing installs: a host that installed the old pin (mirror u1d02858, or leejet master-813-bfbef5b on the fallback hosts) is replaced once on the next load by the existing pin-upgrade path (kill switch UNSLOTH_SD_CPP_AUTO_UPGRADE=0, once-per-process failure memo).
  • Plan prediction (predict_engine): when the resident managed build is about to be replaced for a moved pin, the plan predicts what the load will do (native) instead of planning diffusers off the build that is about to be removed. Without this the upgraded host would stage the dense text encoder once and then load natively.

CUDA bundles share llama.cpp's CUDA runtime

The mirror's CUDA bundles no longer carry cudart / cublas / cublasLt, and ggml is a shared library, so sd-cli and sd-server share one copy of the CUDA kernels. Studio always installs unslothai/llama.cpp, whose CUDA build already needs and resolves the same runtime.

  • install_sd_cpp_prebuilt.py: a CUDA install prefers the cuda<major> asset matching the host runtime (--cuda-major on the CLI). A release without that major still offers its CUDA build, so older self-contained releases and upstream are unchanged; Windows with an unknown runtime keeps cuda12 as before.
  • sd_cpp_backend._install_kwargs: the major is the runtime_line of the installed llama.cpp build, else torch's CUDA version. Non-CUDA installs get exactly the same arguments as before.
  • sd_cpp_engine.runtime_env: a CUDA bundle that ships ggml-cuda but no cudart gets the library directories llama-server gets (LlamaCppBackend._llama_server_env_for_binary), minus llama.cpp's own build dir off Windows since its libggml*.so share sonames with the bundle's (the bundle's DT_RPATH $ORIGIN wins regardless). Every other bundle gets the same environment as before.

Reusing llama.cpp's ggml-cuda itself is not possible: the mirror's ggml carries patches (fused H3 ops, cuDNN attention, ...) that llama.cpp's build does not have, so only the CUDA runtime is shared.

bundle zip CUDA runtime inside ggml-cuda links from the host
old pin Linux-Ubuntu-22.04-x86_64-cuda12 1168 MiB cudart + cublas + cublasLt (828 MiB) none (static in both executables)
new Linux-Ubuntu-22.04-x86_64-cuda12 328 MiB none libcudart / libcublas / libcublasLt.so.12
new Linux-Ubuntu-22.04-x86_64-cuda13 292 MiB none libcudart / libcublas / libcublasLt.so.13
new win-cuda12-x64 324 MiB none cudart64_12, cublas64_12, cublasLt64_12 (old pin: upstream's 322 MiB build plus a 537 MiB cudart archive)
new win-cuda13-x64 287 MiB none cublas64_13, cublasLt64_13 (cudart static, as in unslothai/llama.cpp's cuda13 build)

Every Windows DLL / EXE in the new bundles carries an Authenticode signature.

Verification

Reproduced on main (Linux x86_64, --accelerator vulkan and rocm, the same upstream fallback Windows CUDA took):

sd-cli: source leejet/stable-diffusion.cpp release master-813-bfbef5b
sd_cpp_binary_runs_family(sd-cli, qwen-image-2.1) -> False

Reporter state (a managed tree holding leejet master-813-bfbef5b under the old pin), UNSLOTH_DIFFUSION_ENGINE=sd_cpp, unsloth/Qwen-Image-2.1-GGUF Q4_K_M:

main this PR
plan prediction diffusers (dense text encoder, 14.9 GB required) sd_cpp (11.2 GB required)
load diffusers, the installed sd.cpp build predates 'qwen-image-2.1' support old build replaced, sd_cpp, rendered 512x512

Qwen-Image-2.1 Q4_K_M through Studio's native engine with the new release's Linux bundles (built from the same tree), 512x512, 12 steps, seed 3407, on a B200: selected: sd_cpp, no fallback. cuda13 loaded in 95 s and generated in 36 s, with libcudart.so.13 / libcublas.so.13 mapped from the venv's nvidia/cu13/lib; cuda12 loaded in 70 s and generated in 26 s against the host's CUDA 12.8 runtime. The two images are pixel-identical, peak 11.4 GB.

Live resolution against GitHub, previous head of this PR vs this head (30 host x accelerator cells, 22 move): every mirror host moves u1d02858 -> uafac7a7; Windows CUDA, Windows Vulkan and Linux Vulkan move from the upstream fallback to the mirror (win-cuda12-x64, win-vulkan-x64, Linux-Ubuntu-22.04-x86_64-vulkan); ROCm moves to upstream master-951-f89d9b1.

  • Upstream master-951-f89d9b1 ROCm (the fallback for hosts the mirror skips), installed through Studio's installer: sd_cpp_binary_runs_family(..., qwen-image-2.1) is True for both sd-cli and sd-server.
  • sd-cli --help, old pin vs new: 168 flags each; --canny and --disable-auto-resize-ref-image are gone (Studio passes neither), --conditioning-cache-size and --image-preprocess are new.
  • Runtime-less bundles without Studio's environment and without the ld.so cache fail with libcublasLt.so.13: cannot open shared object file; with Studio's runtime_env they load nvidia/cu13/lib/{libcudart,libcublas,libcublasLt}.so.13 (the files llama-server uses) and find the GPU. With llama.cpp's build dir first on the path they still load their own libggml*.so.
  • Studio's real ensure_sd_server_binary(accelerator="cuda") against the new assets picks the bundle matching llama.cpp's runtime_line, extracts sd-cli, sd-server and the libggml*.so.0, and the result runs --list-devices (CUDA0 NVIDIA B200).

Old pin (u1d02858) vs new pin (uafac7a7), Linux cuda12 bundles through Studio's native engine, same tree, seed 3407, 512x512, B200 (selected: sd_cpp on every run, peak 13 GB):

family old pin new pin mean abs pixel diff (0-255)
FLUX.2-klein-4B Q4_K_M, 4 steps renders, 5 s renders, 4 s 3.4
Qwen-Image-2.1 Q4_K_M, 12 steps renders, 13 s renders, 11 s 8.1
Z-Image-Turbo Q4_K_M, 8 steps renders, 9 s renders, 9 s 21.4

All six images are coherent renders of the prompt. Z-Image moves the most (same subject and scene, slightly different pose and framing); the only change to its default path in this range is upstream's f16 overflow fix for Z-Image quantized matmuls on CUDA (leejet/stable-diffusion.cpp#2074), which rescales the quantized output projection.

Upgrade in place, live: the old pin's self-contained cuda12 bundle installed through Studio, then this head's ensure_sd_server_binary(accelerator="cuda") with a cuda13 host runtime: the new bundle lands in its own sd-<tag>-bin-<label>/ directory (the old libcudart.so.12 stays in the old one), runtime_env adds nvidia/cu13/lib, and sd-cli --list-devices finds the B200.

Switchboard (base 4527f33 vs head bce5bd0, 11 targets: auth, settings, upstream_ci, inference_settings, diffusion_gen, diffusion_video, crawl, diffusion bench tiny http / inproc / matrix, diffusion imports): NO_REGRESSION, 67 SAME, 1 VISUAL_DIFF on the About tab, which is only the llama.cpp version string (b11505 vs b11541, the head install pulled a newer llama.cpp prebuilt).

Not run: no Windows GPU was available, so the Windows CUDA bundles are verified by contents, imports and signatures only.

@danielhanchen danielhanchen self-assigned this Oct 10, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T13:34:32.568164Z 67358f2 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Oct 10, 2026
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

1 similar comment
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3eb91808e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

resident = ensure_sd_server_binary(
allow_install = False, accelerator = accelerator
) or ensure_sd_cpp_binary(allow_install = False, accelerator = accelerator)
return bool(resident) and _pin_moved(resident, accelerator)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Upgrade stale builds after unloading an active native model

When the old managed build is currently running (for example, the user switches from another native family to Qwen-Image-2.1), _pin_moved() returns false because _managed_tree_in_use() is true. The router therefore predicts and selects diffusers, and only afterward _activate() unloads the native model; unlike accelerator changes, no pin-upgrade retry occurs after teardown. Thus the first load still stages the dense diffusers assets and does not install the new fallback, defeating this fix until a subsequent load. The stale-pin check needs a deferred-after-teardown path rather than treating an in-use tree as a build that will not upgrade.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not reachable in practice: the stale build is replaced by the first native load in a Studio process, whatever its family, because the managed tree is free at that point (_pin_moved only stands down while something runs out of the tree). So any native model resident afterwards is already on the new build. The in-use plus stale combination only exists after that upgrade failed, where the once-per-process memo deliberately stops retrying.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 0a556f594c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: e985f8f80e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danielhanchen danielhanchen changed the title Studio: fall back to an upstream sd.cpp build that runs Qwen-Image-2.1 Studio: pin sd.cpp to the mirror build with Windows CUDA, sharing llama.cpp's CUDA runtime Oct 11, 2026
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bce5bd00e3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +339 to +340
runtime = any(n.startswith(("cudart64_", "libcudart.so")) for n in names)
return cuda and not runtime

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Ignore stale CUDA runtimes from the previous bundle

When an existing CUDA 12 managed install is upgraded to the new CUDA 13 bundle, extraction merges into the old tree and _discard_superseded_binaries() removes only sd-cli/sd-server, leaving the old libcudart.so.12 or cudart64_12.dll beside the replacement. This check treats any cudart major as a complete bundled runtime, so runtime_env() does not add llama.cpp's CUDA 13 directories and the new ggml library cannot resolve its CUDA 13 cublas/runtime dependencies. This particularly breaks the moved-pin upgrade this commit introduces; determine whether the matching runtime is present or remove superseded runtime libraries during replacement.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not reproducible: every release zip extracts under its own sd--bin-/ directory, so an upgraded install's bindir holds only the new bundle. Installed the real old pin (u1d02858 cuda12, with libcudart.so.12) through Studio, then upgraded with this head to the cuda13 bundle: the old libcudart.so.12 stays in sd-master-813-...-cuda12/, the new bindir has no cudart, _needs_host_cuda_runtime is True, runtime_env puts nvidia/cu13/lib on the path and sd-cli --list-devices finds the GPU.

Comment on lines +354 to +356
binary = _llama_server_binary()
if not binary:
return []

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provide CUDA libraries without a CUDA llama-server

On the supported off-torch route (for example, UNSLOTH_DIFFUSION_SD_CPP_DEVICE=nvidia:0 beside a ROCm PyTorch/llama.cpp install), there may be no CUDA llama-server runtime to borrow and merely having an NVIDIA driver does not provide cudart/cublas. The new mirror CUDA archives deliberately omit those libraries, but this implementation derives search paths only from the installed llama-server environment, so the freshly installed sd.cpp binary fails its loader probe on such hosts whereas the previous self-contained CUDA bundle worked. The CUDA install needs an independent runtime source or must select a self-contained fallback when llama.cpp is absent or non-CUDA.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not addressed here by design: Studio always installs unslothai/llama.cpp, and on an NVIDIA host its CUDA build is what supplies cudart / cublas; the CUDA bundles intentionally reuse that runtime instead of shipping another copy. A host forcing sd.cpp onto an NVIDIA card while torch and llama.cpp are ROCm builds is outside that setup.

@oobabooga

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 67358f2ffc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +258 to 260
if accel == "cuda":
sel = _prefer_cuda_major(sel, cuda_major or "12")
if sel:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match the Windows runtime helper to the selected CUDA major

When a Windows release contains both CUDA 12/13 engine archives plus the standard cudart-sd-...-cu12...zip, selecting the CUDA 13 archive here is followed by _maybe_fetch_windows_cudart() unconditionally extracting the first Windows cudart helper, regardless of chosen. The resulting CUDA 13 install gains CUDA 12 DLLs; _needs_host_cuda_runtime() then sees cudart64_12.dll and declines to add llama.cpp's CUDA 13 runtime paths, so the CUDA 13 binary cannot resolve its cublas 13 dependencies. Filter the helper by the CUDA major in the selected archive, or skip it for the new runtime-less mirror bundles.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No published release has that shape. The only release with a cuda13 Windows archive is the pinned mirror release, and it ships no cudart helper. Every leejet release (the fallback) publishes only win-cuda12 next to the cu12 cudart archive. The mirror archives also extract into their own sd--bin-/ directory while the cudart helper extracts into the install root, so _needs_host_cuda_runtime(bindir) would not see a cudart64_12.dll even if one were fetched.

@oobabooga

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 67358f2ffc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +2021 to +2023
major = host_cuda_runtime_major()
if major:
kwargs["cuda_major"] = major

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reinstall when the CUDA runtime major changes

When the managed llama.cpp installation switches CUDA lines (for example, CUDA 12 to CUDA 13) without this sd.cpp pin changing, the existing runtime-less sd.cpp bundle is reused because _needs_reinstall() records and compares only the cuda accelerator class and pin. runtime_env() then supplies the new llama.cpp CUDA 13 directories to an sd.cpp binary still linked against CUDA 12 sonames, so native loading fails until the user deletes the managed sd.cpp tree. Persist the selected CUDA major/asset in the install record and include it in the reinstall decision.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already handled by the existing unrunnable-binary repair. sd-cli links libggml.so.0, which links libggml-cuda.so.0, so a bundle whose CUDA major is no longer on the search path fails at exec: the real master-951 cuda13 sd-cli exits 127 with "libcublasLt.so.13: cannot open shared object file" (Windows reports 0xC0000135, also in _WINDOWS_IMAGE_LOAD_FAILURE_STATUSES). _server_binary_runnable probes with runtime_env, returns False, and _usable_or_discard_managed removes the managed copy, so ensure_sd_cpp_binary reinstalls with _install_kwargs. Reproduced with the real cuda13 bundle in a managed tree whose record names the current pin, with no CUDA 13 dirs and llama.cpp on cuda12: the next ensure_sd_cpp_binary(accelerator="cuda") called install(accelerator="cuda", cuda_major="12"). If the old major's libraries are still present, the bundle keeps running on them.

@oobabooga

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

@oobabooga
oobabooga merged commit 2a81fc4 into main Oct 12, 2026
@oobabooga
oobabooga deleted the studio-sd-cpp-upstream-fallback-qwen-image-2-1 branch October 12, 2026 13:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants