From f739158f3eb11e016e87bfadfa4a965ce90c695e Mon Sep 17 00:00:00 2001 From: vmobilis <75476228+vmobilis@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:19:35 +0300 Subject: [PATCH 1/3] use `sd_get_preview_interval()` --- src/stable-diffusion.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/stable-diffusion.cpp b/src/stable-diffusion.cpp index 109a2a483..7370f6a03 100644 --- a/src/stable-diffusion.cpp +++ b/src/stable-diffusion.cpp @@ -2670,7 +2670,9 @@ class StableDiffusionGGML { denoised = denoised * denoise_mask + init_latent * (1.0f - denoise_mask); } if (sd_should_preview_denoised() && preview.callback != nullptr) { - preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); + if (step % sd_get_preview_interval() == 0) { + preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); + } } report_sample_progress(step, steps, &last_progress_us); sd::guidance::GuiderOutput output; @@ -2679,7 +2681,9 @@ class StableDiffusionGGML { } if (sd_should_preview_noisy() && preview.callback != nullptr) { - preview_image(step, noised_input, version, preview.mode, preview.callback, preview.data, true); + if (step % sd_get_preview_interval() == 0) { + preview_image(step, noised_input, version, preview.mode, preview.callback, preview.data, true); + } } sd::Tensor cond_out; @@ -2889,7 +2893,9 @@ class StableDiffusionGGML { denoised = denoised * denoise_mask + init_latent * (1.0f - denoise_mask); } if (sd_should_preview_denoised() && preview.callback != nullptr) { - preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); + if (step % sd_get_preview_interval() == 0) { + preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); + } } report_sample_progress(step, steps, &last_progress_us); output.pred = denoised; From cb2d258440785ac33db27984ffbf38c9eb5d38bc Mon Sep 17 00:00:00 2001 From: vmobilis <75476228+vmobilis@users.noreply.github.com> Date: Thu, 27 Aug 2026 13:31:06 +0300 Subject: [PATCH 2/3] Options for `preview-interval` --- src/stable-diffusion.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/stable-diffusion.cpp b/src/stable-diffusion.cpp index 7370f6a03..ddf4840e2 100644 --- a/src/stable-diffusion.cpp +++ b/src/stable-diffusion.cpp @@ -259,6 +259,8 @@ class StableDiffusionGGML { bool is_using_v_parameterization = false; bool is_using_edm_v_parameterization = false; + bool preview_last_step = false; + size_t control_net_params_mem_size = 0; std::shared_ptr model_manager; @@ -2639,6 +2641,18 @@ class StableDiffusionGGML { float c_out = scaling[1]; float c_in = scaling[2]; + bool preview_needed = preview.callback != nullptr; + if (preview_needed) { + int preview_interval = sd_get_preview_interval(); + if (preview_interval > 0) { // every Nth step + preview_needed = step % preview_interval == 0; + } else if (preview_interval < 0) { // only (-N)th step + preview_needed = step == -preview_interval; + } else { // last step of base resolution / high noise pass + preview_needed = preview_last_step && step == steps; + } + } + std::vector base_timesteps_vec = prepare_sample_timesteps(sigma, shifted_timestep); std::vector timesteps_vec = base_timesteps_vec; sd::Tensor audio_timesteps_tensor; @@ -2669,10 +2683,8 @@ class StableDiffusionGGML { if (!denoise_mask.empty()) { denoised = denoised * denoise_mask + init_latent * (1.0f - denoise_mask); } - if (sd_should_preview_denoised() && preview.callback != nullptr) { - if (step % sd_get_preview_interval() == 0) { - preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); - } + if (preview_needed && sd_should_preview_denoised()) { + preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); } report_sample_progress(step, steps, &last_progress_us); sd::guidance::GuiderOutput output; @@ -2680,10 +2692,8 @@ class StableDiffusionGGML { return output; } - if (sd_should_preview_noisy() && preview.callback != nullptr) { - if (step % sd_get_preview_interval() == 0) { - preview_image(step, noised_input, version, preview.mode, preview.callback, preview.data, true); - } + if (preview_needed && sd_should_preview_noisy()) { + preview_image(step, noised_input, version, preview.mode, preview.callback, preview.data, true); } sd::Tensor cond_out; @@ -2892,10 +2902,8 @@ class StableDiffusionGGML { if (!denoise_mask.empty()) { denoised = denoised * denoise_mask + init_latent * (1.0f - denoise_mask); } - if (sd_should_preview_denoised() && preview.callback != nullptr) { - if (step % sd_get_preview_interval() == 0) { - preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); - } + if (preview_needed && sd_should_preview_denoised()) { + preview_image(step, denoised, version, preview.mode, preview.callback, preview.data, false); } report_sample_progress(step, steps, &last_progress_us); output.pred = denoised; @@ -5666,6 +5674,7 @@ SD_API bool generate_image(sd_ctx_t* sd_ctx, ImageGenerationEmbeds embeds = std::move(*embeds_opt); std::vector> final_latents; + sd_ctx->sd->preview_last_step = true; int64_t denoise_start = ggml_time_ms(); for (int b = 0; b < request.batch_count; b++) { sd_cancel_mode_t cancel = sd_ctx->sd->get_cancel_flag(); @@ -5726,6 +5735,7 @@ SD_API bool generate_image(sd_ctx_t* sd_ctx, return false; } int64_t denoise_end = ggml_time_ms(); + sd_ctx->sd->preview_last_step = false; LOG_INFO("generating %zu latent images completed, taking %.2fs", final_latents.size(), (denoise_end - denoise_start) * 1.0f / 1000); @@ -6938,6 +6948,7 @@ SD_API bool generate_video(sd_ctx_t* sd_ctx, } LOG_DEBUG("sample(high noise) %dx%dx%d", W, H, T); + sd_ctx->sd->preview_last_step = true; int64_t sampling_start = ggml_time_ms(); std::vector high_noise_sigmas(plan.sigmas.begin(), plan.sigmas.begin() + plan.high_noise_sample_steps + 1); plan.sigmas = std::vector(plan.sigmas.begin() + plan.high_noise_sample_steps, plan.sigmas.end()); @@ -6968,6 +6979,7 @@ SD_API bool generate_video(sd_ctx_t* sd_ctx, request.cache_params, latents.video_positions); int64_t sampling_end = ggml_time_ms(); + sd_ctx->sd->preview_last_step = false; if (x_t_sampled.empty()) { LOG_ERROR("sampling(high noise) failed after %.2fs", (sampling_end - sampling_start) * 1.0f / 1000); return false; From 1c6b83e56029898fc3f41b531c0762cd3258bc01 Mon Sep 17 00:00:00 2001 From: vmobilis <75476228+vmobilis@users.noreply.github.com> Date: Thu, 27 Aug 2026 13:36:34 +0300 Subject: [PATCH 3/3] Description of preview options --- examples/cli/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/cli/main.cpp b/examples/cli/main.cpp index 953a44e9d..249a6a551 100644 --- a/examples/cli/main.cpp +++ b/examples/cli/main.cpp @@ -94,7 +94,7 @@ struct SDCliParams { options.int_options = { {"", "--preview-interval", - "interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at every step)", + "interval in denoising steps between consecutive updates of the image preview file (default is 1, meaning updating at every step). Set to -N to preview only the Nth single step. Set to 0 to preview the last step of 1st pass (base resolution / high noise) sampling", &preview_interval}, {"", "--output-begin-idx",