v0.3.0
stable-diffusion-go v0.3.0 — pure-Go binding + prebuilt native libraries.
Built from leejet/stable-diffusion.cpp @ e92e86f (upstream pin: master-802-e92e86f).
go get github.com/Pendra-Cloud/stable-diffusion-go@v0.3.0 for the
binding; the lib archives attached here match this exact tag. Each archive
contains a self-contained libstable-diffusion (ggml statically linked,
hidden visibility — exports only the sd_* symbols pkg/sd/load.go registers).
CUDA archives require a matching host CUDA runtime; Vulkan archives require
a Vulkan loader/ICD on the host. The Windows archive carries the per-CPU/GPU
(avx2/avx512/avx/noavx/vulkan/cuda13) subdir tree the binding selects from.
What & why
Adds whitelisted sd_*-prefixed wrappers around ggml's device-memory registry, compiled into libstable-diffusion, plus a GpuDevices() binding.
This lets a host that only dlopens libstable-diffusion (which statically links ggml with hidden visibility) read per-device free/total VRAM in-process — the same measurement llama.cpp / whisper.cpp already expose through their own libggml.
Without it, a Pendra worker measuring a stable-diffusion model's footprint on a discrete GPU has no per-device VRAM reading, falls back to a process-RSS delta that misses dedicated VRAM, and reports ~0.1 GB — so the model's real weights land un-attributed in the console memory map (Pendra-Cloud/pendra#1489). The worker-side consumer of GpuDevices() is a follow-up PR in the pendra repo.
Changes
csrc/sd_devmem.cpp—SD_APIwrappers:sd_backend_dev_count / get / memory / name / type, thin passthroughs to the hiddenggml_backend_dev_*.scripts/inject-devmem-wrapper.sh— drops the wrapper intoupstream/srcbefore the build; picked up by upstream'ssrc/*.cppCONFIGURE_DEPENDSglob (noCMakeListsedit, survives upstream refactors).build-libs.yml— inject step in all four build jobs (linux, linux-cuda, macos, windows).lib/expected-symbols.txt— the fail-closed gate now requires the 5 new symbols and still forbids any exportedggml_*, so ggml stays static + hidden (no base-name collision with another in-process ggml backend).pkg/sd—registerDevMemFunctions(optional: recovers so it never failsLoadagainst an older lib, e.g. aPENDRA_SD_LIBoverride) +GpuDevices()(panic-safe enumeration).
Verification
Built locally on Apple Silicon (Metal) with the wrapper injected:
nm -gUshows all 5_sd_backend_dev_*exported; noggml_*leak (only the allowedggml_metallib_start/endmarkers).scripts/check-symbols.sh→ OK — all 54 expected symbols exported, no ggml leakage.- Runtime:
sd.Load()+sd.GpuDevices()returns realMTL0free/total (~12.1 GiB), plusBLAS/CPUdevices with correct types. go test ./pkg/sd -run TestExpectedSymbolsMatchBinding(drift gate) passes.
The full CI matrix build is the remaining gate. Recommend a workflow_dispatch gating run (only=linux-amd64-cuda) to validate the injection on the CUDA leg before merge.
🤖 Generated with Claude Code
What's Changed
- feat(devmem): export sd_backend_dev_* wrappers for in-process VRAM reads by @tomcrawf90 in #29
Full Changelog: v0.2.4...v0.3.0