
核心理念2026/7/7每次仅加载一个模型中间结果暂存于系统内存最终输出才写入磁盘。与 ComfyUI / sd-cli 一次性加载所有模型的策略不同分步管线确保每一步骤可独占全部资源尤其是显存。架构图textStep 1 Step 2 Step 3 ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 加载 Qwen3 │ │ 加载 DiT │ │ 加载 VAE │ │ text encoder│ │ diffusion │ │ decode │ │ │ │ │ │ │ │ prompt ─────→│ cond │ cond ───────→│ latent │ latent ─────→│ img │ (CPU 5GB) │ float[] │ (GPU 3-4GB) │ float[] │ (GPU 0.5GB) │ png │ │ │ │ │ │ │ ↓ 释放 Qwen3 │ │ ↓ 释放 DiT │ │ ↓ 释放 VAE │ │ VRAM: 0 │ │ VRAM: 0 │ │ 写磁盘完成 │ └──────────────┘ └──────────────┘ └──────────────┘ 内存中传递: cond (float array) → latent (float array) → image (sd_image_t)具体改动给 DLL 加三个导出函数1. sd_encode_condition — 只跑文本编码将 prompt 编码为 conditioning tensor并以平铺的 float 数组返回。cSD_API bool sd_encode_condition( sd_ctx_t* sd_ctx, const char* prompt, const char* negative_prompt, const sd_lora_t* loras, int lora_count, float** cond_data, // 输出condition float 数组调用者 free int64_t* cond_size // 输出数组元素个数 );需要暴露StableDiffusionGGML::get_learned_condition()原私有方法。cond_data包含c_crossattnc_vectorc_concat的序列化拼接。返回后调用者需调用free_sd_ctx()释放 Qwen3 模型。2. sd_sample — 只跑采样输入 condition输出 latent。cSD_API bool sd_sample( sd_ctx_t* sd_ctx, const float* cond_data, int64_t cond_size, int width, int height, int steps, float cfg_scale, int64_t seed, float** latent_data, // 输出latent float 数组 int64_t* latent_size );需要暴露StableDiffusionGGML::sample()原私有方法。内部反序列化cond_data还原为SDCondition结构。latent_data为采样结果例如 FLUX.2 的 latent 尺寸为 64×64×16。可通过--backend diffusionvulkan1指定后端。3. sd_decode_latent — 只跑 VAE 解码输入 latent输出最终图像。cSD_API sd_image_t sd_decode_latent( sd_ctx_t* sd_ctx, const float* latent_data, int64_t latent_size );需要暴露StableDiffusionGGML::decode_first_stage()原私有方法。可通过--backend vaevulkan1让 VAE 在 GPU 上运行模型仅 164 MB。调用方主程序逻辑text1. 创建 sd_ctx仅传入 --llmQwen3其余路径置空 sd_ctx_params.llm_path flux2-klein-9b-uncensored-q4_k_m.gguf sd_ctx_params.diffusion_model_path NULL sd_ctx_params.vae_path NULL ctx new_sd_ctx(params) 2. sd_encode_condition(ctx, prompt, loras, cond_data, cond_size) 此时 cond_data 为内存中的 float 数组 3. free_sd_ctx(ctx) // Qwen3 完全释放显存归零 4. 新建 sd_ctx仅传入 --diffusion-modelDiT sd_ctx_params.diffusion_model_path flux-2-klein-9b-Q4_0.gguf sd_ctx_params.llm_path NULL sd_ctx_params.backend diffusionvulkan1 ctx new_sd_ctx(params) 5. sd_sample(ctx, cond_data, cond_size, 512, 512, 4, 1.0, 42, latent_data, latent_size) 此时 latent_data 存于内存中 6. free(cond_data) // 释放 condition free_sd_ctx(ctx) // DiT 完全释放显存归零 7. 新建 sd_ctx仅传入 --vae sd_ctx_params.vae_path flux2-vae.safetensors sd_ctx_params.backend vaevulkan1 ctx new_sd_ctx(params) 8. img sd_decode_latent(ctx, latent_data, latent_size) stbi_write_png(output.png, img) 9. free(latent_data) free_sd_images(img, 1) free_sd_ctx(ctx) // VAE 释放每步内存占用步骤加载模型大小VRAMRAM备注1Qwen3 文本编码器5.0 GB0 GB5.0 GBCPU 推理不占显存释放——0 GB0 GB回归零2DiT (Q4_0)5.3 GB~3 GB0 GB权重 offload 至 GPU释放——0 GB0 GB回归零3VAE164 MB0.5 GB0 GB全量置于 GPU总 VRAM 峰值~3 GB远低于 RX 580 的 8 GB 上限对 img2img / inpainting 的支持在 Step 1 与 Step 2 之间插入一步即可textStep 1.5: VAE 编码原图 加载 VAE可复用 Step 3 逻辑 原图 → VAE encode → init_latent内存 float[] 随后 Step 2 的 sd_sample 接收 init_latent cond 作为输入此额外步骤耗时约 3~5 秒VAE 编码走 GPU。编译方法bashgit clone https://github.com/leejet/stable-diffusion.cpp cd stable-diffusion.cpp mkdir build cd build cmake .. -DGGML_VULKANON -DCMAKE_BUILD_TYPERelease cmake --build . --config Release编译产出build/bin/Release/stable-diffusion.dll→ 替换D:\files\FLUX.2\自行编译调用方 exe链接该 DLL需要修改的源文件文件改动include/stable-diffusion.h增加 3 个SD_API函数声明src/stable-diffusion.cpp实现这 3 个函数调用内部私有方法src/stable-diffusion.h内部将encode_first_stage()、sample()、decode_first_stage()设为 public 或添加 public wrapper与 ComfyUI / sd-cli 对比ComfyUIsd-cli 一次加载本方案模型加载方式全加载全加载分步加载中间结果显存常驻显存常驻内存 float arrayVRAM 峰值高~6-7 GB中~3 GB低~3 GBimg2img 额外开销小均在显存小均在显存略大多一次 VAE 加载跨 prompt 复用 condition节点缓存不支持代码层手动缓存控制粒度节点级CLI 参数级函数调用级