Studio diffusion (Phase 4): native stable-diffusion.cpp engine for CPU/Mac
Adds the CPU / Apple-Silicon tier of the two-engine strategy, mirroring the chat backend's llama.cpp shell-out. Diffusers stays the default on CUDA / ROCm / XPU; this covers the hardware diffusers serves poorly, consuming the same split GGUF assets Studio already curates. - sd_cpp_args.py: pure sd-cli command builder. Maps the family to its text-encoder flag (Z-Image Qwen3 to --llm, Qwen-Image to --qwen2vl, FLUX.1 CLIP-L + T5), and the diffusers memory policy (none/group/model/sequential) to sd.cpp's offload flags (--offload-to-cpu / --clip-on-cpu / --vae-on-cpu / --vae-tiling / --diffusion-fa), so one user knob drives both engines. - sd_cpp_engine.py: SdCppEngine over a located sd-cli. find_sd_cpp_binary() with the same precedence as the llama finder (env override, then the Studio install root, then in-tree, then PATH), an is_available/version probe, and a one-shot subprocess generate that streams progress and returns the PNG. runtime_env() prepends the binary's directory to the platform library path so a prebuilt's bundled libstable-diffusion.so resolves. select_diffusion_engine() is the pure routing decision (GPU backends to diffusers, CPU/MPS to native when present). - install_sd_cpp_prebuilt.py: resolve + download the per-host prebuilt (macOS-arm64/Metal, Linux x86_64 CPU, Vulkan/ROCm/Windows variants) into the Studio install root. resolve_release_asset() is a pure, unit-tested host-to-asset matrix. - scripts/sd_cpp_smoke.py: end-to-end native generation harness. Tests (CPU-only, subprocess/filesystem stubbed): 49 new across args, engine, routing, runtime env, and the installer resolver. Full diffusion suite 166 passing. Verified on a B200 box: built sd-cli (CUDA) and the prebuilt (CPU) both generate Z-Image-Turbo Q4_K end to end through SdCppEngine: balanced (group offload, 5.0s gen), low_vram (full CPU offload + VAE tiling, 13.4s), and the dynamically-linked CPU prebuilt (50.4s on CPU), all producing coherent images.
This commit is contained in:
parent
7ca5573498
commit
a7b8f825da
7 changed files with 1298 additions and 0 deletions
107
studio/backend/tests/test_sd_cpp_install.py
Normal file
107
studio/backend/tests/test_sd_cpp_install.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
||||
|
||||
"""Unit tests for the prebuilt sd-cli asset resolver (``install_sd_cpp_prebuilt``).
|
||||
|
||||
Pure: the host -> release-asset matrix is exercised against a fixed asset list
|
||||
(a real stable-diffusion.cpp release), no network. The installer lives under
|
||||
``studio/`` (not ``studio/backend``), so the test puts that dir on the path.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_STUDIO = Path(__file__).resolve().parents[2]
|
||||
if str(_STUDIO) not in sys.path:
|
||||
sys.path.insert(0, str(_STUDIO))
|
||||
|
||||
from install_sd_cpp_prebuilt import default_install_dir, resolve_release_asset # noqa: E402
|
||||
|
||||
# A real stable-diffusion.cpp latest-release asset list.
|
||||
_ASSETS = [
|
||||
"cudart-sd-bin-win-cu12-x64.zip",
|
||||
"sd-master-8caa3f9-bin-Darwin-macOS-15.7.7-arm64.zip",
|
||||
"sd-master-8caa3f9-bin-Linux-Ubuntu-24.04-x86_64-rocm-7.13.0.zip",
|
||||
"sd-master-8caa3f9-bin-Linux-Ubuntu-24.04-x86_64-rocm-7.2.1.zip",
|
||||
"sd-master-8caa3f9-bin-Linux-Ubuntu-24.04-x86_64-vulkan.zip",
|
||||
"sd-master-8caa3f9-bin-Linux-Ubuntu-24.04-x86_64.zip",
|
||||
"sd-master-8caa3f9-bin-win-avx-x64.zip",
|
||||
"sd-master-8caa3f9-bin-win-avx2-x64.zip",
|
||||
"sd-master-8caa3f9-bin-win-avx512-x64.zip",
|
||||
"sd-master-8caa3f9-bin-win-cuda12-x64.zip",
|
||||
"sd-master-8caa3f9-bin-win-noavx-x64.zip",
|
||||
"sd-master-8caa3f9-bin-win-rocm-7.13.0-x64.zip",
|
||||
"sd-master-8caa3f9-bin-win-vulkan-x64.zip",
|
||||
]
|
||||
|
||||
|
||||
def _resolve(system, machine, accelerator = "auto"):
|
||||
return resolve_release_asset(_ASSETS, system = system, machine = machine, accelerator = accelerator)
|
||||
|
||||
|
||||
# ── macOS (the key Apple-Silicon target) ────────────────────────────────────
|
||||
|
||||
|
||||
def test_macos_arm64_picks_darwin_arm64():
|
||||
assert _resolve("Darwin", "arm64") == "sd-master-8caa3f9-bin-Darwin-macOS-15.7.7-arm64.zip"
|
||||
# aarch64 spelling resolves the same
|
||||
assert _resolve("Darwin", "aarch64").startswith("sd-master") and "arm64" in _resolve("Darwin", "aarch64")
|
||||
|
||||
|
||||
def test_macos_intel_has_no_prebuilt():
|
||||
# only an arm64 Darwin asset exists -> Intel Macs must build from source
|
||||
assert _resolve("Darwin", "x86_64") is None
|
||||
|
||||
|
||||
# ── Linux (CPU is the default tier) ─────────────────────────────────────────
|
||||
|
||||
|
||||
def test_linux_x86_64_auto_picks_plain_cpu_build():
|
||||
# the plain x86_64 zip, NOT a rocm/vulkan one
|
||||
assert _resolve("Linux", "x86_64") == "sd-master-8caa3f9-bin-Linux-Ubuntu-24.04-x86_64.zip"
|
||||
|
||||
|
||||
def test_linux_vulkan_and_rocm_select_accelerator_builds():
|
||||
assert _resolve("Linux", "x86_64", "vulkan") == "sd-master-8caa3f9-bin-Linux-Ubuntu-24.04-x86_64-vulkan.zip"
|
||||
assert "rocm" in _resolve("Linux", "x86_64", "rocm")
|
||||
|
||||
|
||||
def test_linux_arm64_has_no_prebuilt():
|
||||
assert _resolve("Linux", "aarch64") is None
|
||||
|
||||
|
||||
# ── Windows ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_windows_auto_picks_avx2():
|
||||
assert _resolve("Windows", "AMD64") == "sd-master-8caa3f9-bin-win-avx2-x64.zip"
|
||||
|
||||
|
||||
def test_windows_cuda_picks_cuda12():
|
||||
assert _resolve("Windows", "AMD64", "cuda") == "sd-master-8caa3f9-bin-win-cuda12-x64.zip"
|
||||
|
||||
|
||||
def test_windows_vulkan_picks_vulkan():
|
||||
assert _resolve("Windows", "AMD64", "vulkan") == "sd-master-8caa3f9-bin-win-vulkan-x64.zip"
|
||||
|
||||
|
||||
# ── cudart helper archive is never chosen as the engine ─────────────────────
|
||||
|
||||
|
||||
def test_cudart_runtime_archive_never_selected():
|
||||
for accel in ("auto", "cuda", "vulkan", "rocm"):
|
||||
chosen = _resolve("Windows", "AMD64", accel)
|
||||
assert chosen is None or not chosen.startswith("cudart")
|
||||
|
||||
|
||||
# ── install dir ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_default_install_dir_is_sibling_of_llama(monkeypatch):
|
||||
monkeypatch.delenv("UNSLOTH_STUDIO_HOME", raising = False)
|
||||
monkeypatch.delenv("STUDIO_HOME", raising = False)
|
||||
d = default_install_dir()
|
||||
assert d.name == "stable-diffusion.cpp"
|
||||
assert d.parent.name == ".unsloth"
|
||||
Loading…
Add table
Add a link
Reference in a new issue