Skip to content

feat: update stable-diffusion.cpp to master-982-0518b34 - #27

Merged
leehack merged 5 commits into
mainfrom
qualify/sd-upstream-982
Oct 22, 2026
Merged

leehack merged 5 commits into
mainfrom
qualify/sd-upstream-982

Conversation

@leehack

@leehack leehack commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

Moves stable-diffusion.cpp to master-982-0518b34 (ggml dc998771) and keeps Metal builds correct under upstream's new batched_cfg=true default. Refs #20. Not a release; full record in docs/upstream_982_qualification.md.

Consumer ABI delta

v0.2.0-4 This branch
sizeof(sd_ctx_params_t) 320 336
batched_cfg (bool) absent offset 289, former padding; init true
flash_attn_mode, conv_direct_mode (const char*) absent offsets 320, 328; init null (legacy bool route)
Other sd_ctx_params_t members unchanged offsets unchanged
sd_img_gen_params_t / sd_vid_gen_params_t / sd_sample_params_t 552 / 584 / 96 unchanged
convert_with_components 11 args 13 args: appends const sd_lora_t* loras, int lora_count
New exports sd_get_preview_info, sd_ctx_supports_ref_image_conditioning

sd_ctx_params_init writes past an old 320-byte allocation: llamadart must regenerate bindings and map batched_cfg and both mode strings explicitly before loading this runtime.

Attention/convolution modes. A null mode honors a legacy flag only when it is true. With the flags false (the init default and what llamadart writes today) upstream runs its auto probes: on CPU, auto enables flash attention, so the image changes from v0.2.0-4 (4ca7bc91 → bf06aed2). On Metal, auto is not seed-reproducible: flash attention is picked by a timing probe at load (the auditor saw it on in 2 of 10 loads). To keep v0.2.0-4 behavior llamadart must always pass both modes:

v0.2.0-4 flags flash_attn_mode
flash_attn "off,te=on,clip_vision=on,vae=on,diffusion=on"
only diffusion_flash_attn "off,diffusion=on"
neither "off"

conv_direct_mode = "off" + ",diffusion=on,control_net=on" for diffusion_conv_direct + ",vae=on" for vae_conv_direct. Verified: candidate CPU "off" = v0.2.0-4 flags-false image and "off,diffusion=on" = v0.2.0-4 diffusion_flash_attn image. On MTL0, three loads of each explicit mode gave identical images and logged no auto probe.

Metal batched CFG (leejet#2149)

ggml-metal GROUP_NORM still writes only batch item 0, so batched CFG returns a wrong image as success. sd_dart_new_sd_ctx now loads a copy with batched_cfg=false when built with Metal (SD_DART_METAL from SD_METAL); other builds pass the flag through. Raw new_sd_ctx is unchanged.

Control (Apple M4 Max) Result
Model-free GROUP_NORM [8,8,64,2], sentinel 123456 CPU all written; MTL0 batch-2 item 1 4096/4096 unchanged
SD-Turbo F16 + TAESD 128², seed 7, CFG 2, raw MTL0 unbatched mean 129.74; batched range 71..147, 46,710/49,152 bytes off by >16
Same, CPU batched == unbatched == published v0.2.0-4 CPU image, byte-identical
Guarded sd_dart_new_sd_ctx, batched_cfg=true, MTL0 byte-identical to unbatched Metal
Published v0.2.0-4 vs candidate unbatched, MTL0 mean abs diff 0.15, max 5
batched-cfg stand-in, -DSD_DART_METAL=1 passes; fails with the guard removed (mutation check)

Carry inventory

No carry retired. Two carries the pin lacks: Metal unbatched CFG (wrapper, this PR) and patches/pickle_line_span.patch for #23 (merged in #26 and brought in by merging main). The patch applies to 0518b34 (pickle_io.cpp is byte-identical to the baseline) and test_pickle_span passes. Kept, with reasons in the doc: progress ring, log/error ring and spin abandonment, context registry and guarded calls, Apple hidden __cxa_atexit, bounded teardown waits, Linux owned-static retention and GCC 13 floor, static-registry device queries, GPU status mapping, free-memory clamp (ggml 65c60b2 overlaps but is unqualified on real Vulkan), Metal fail-loud (#21), Windows short build path + EP_PREFIX, export allowlist, MSVC generator, Android 16 KiB alignment and CPU-only, CPU desktop payload, build policy, Apple minimum OS and privacy manifest. The submodule itself stays unpatched.

Upstream fixes adopted: 8c442fb (#2144) UTF-8 model paths on Windows, now smoke-tested on every smoke target (converts between files in a non-ASCII directory); 60423ab (#2124) / b7a74e6 (#2119) memory reclamation, unqualified (no Vulkan iGPU).

Evidence (macOS arm64, guarded build with #26's patch, dylib e4e1a2be, pinned Dart 3.47.1 SDK)

Check Result
validate_artifacts.py macos-arm64 pass
build.py wrapper-definitions check (new) SD_DART_METAL=1; mutation (definition removed from CMakeLists) stops the build after configure
smoke_test.py macos-arm64 --expect-device MTL pass, incl. non-ASCII path conversion
unittest discover -s tests (SD_REQUIRE_DART/RUNTIME/METAL_GENERATION, 3 runs) 64 tests, pass, 7 skipped (Linux-only, real model, libllamadart)
test_exit_teardown_runtime.py, ASan, Metal generation pass, 5 skipped
Metal guard control after the merge guarded batched = unbatched (7f798ea8); raw batched still defective
Same with SD-Turbo Q4_0 real model, release + Dart VM harness pass, 3 skipped; untracked-context control aborts in ggml-metal as before
Same, real model, ASan pass, 4 skipped

The real-model rows ran before the merge; the merged build differs only in the patched legacy pickle reader.

Evidence logs and probes: llamadart-evidence/sd-982-pr/ on the qualifying Mac (not committed).

CI: every build job now fails if stable-diffusion was configured without the SD_DART_METAL / SD_DART_GPU_BACKEND value its target needs. The macOS and Linux smoke build jobs run test_runtime_api.py against the built runtime with SD_REQUIRE_RUNTIME=1, so a missing runtime fails instead of skipping.

Coordination: SD PR#26 landed first (aa1f6d6). This branch merges main (merge commit 0a4d0ca), and the doc and README list both carries.

Unqualified

Linux cooperative native/Dart teardown (CI covers CPU/lavapipe only), Windows (CI smoke only), real Vulkan memory/convolution/batched CFG, FP8 CPU, iOS devices and full XCFramework/signing audit, Android devices. CPU parity is not GPU evidence. SD#23 (legacy pickle >2 GiB) is out of scope.

High-risk QA

Native runtime and exit-teardown change: needs an exact-head independent review before ready. Draft until then.

Upstream 982 enables batched CFG by default, and ggml-metal GROUP_NORM
writes only the first batch item (leejet/stable-diffusion.cpp#2149), so a
batched step returns a wrong image as success. sd_dart_new_sd_ctx loads a
copy with batched_cfg off when the library is built with Metal.

Smoke-tests conversion under a non-ASCII UTF-8 path on every target, and
records the Metal controls, local suite and remaining gates.
A null flash_attn_mode or conv_direct_mode with its legacy flag false
selects upstream's auto probes, which change output and are not
seed-reproducible on Metal. Record the explicit consumer mapping that
keeps v0.2.0-4 behavior.

build.py now fails after configure when stable-diffusion lacks the
SD_DART_METAL or SD_DART_GPU_BACKEND value its target needs, and the CI
build jobs run the runtime API test against the built runtime instead of
skipping it.
@leehack
leehack marked this pull request as ready for review October 22, 2026 14:36
@leehack
leehack merged commit 992d3f7 into main Oct 22, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant