#include "generation.h" #include #include #include #include #include "core/rng.hpp" #include "diffusion_engine.h" #include "model/vae/vae.hpp" #include "request.h" #include "runtime/denoiser.hpp" #include "upscaler.h" namespace sd::pipeline { struct CircularAxesState { bool circular_x = false; bool circular_y = false; }; static CircularAxesState configure_image_vae_axes(StableDiffusionGGML* sd, const sd_img_gen_params_t* sd_img_gen_params, const GenerationRequest& request) { CircularAxesState original_axes = {sd->circular_x, sd->circular_y}; if (!sd_img_gen_params->vae_tiling_params.enabled) { if (sd->first_stage_model) { sd->first_stage_model->set_circular_axes(sd->circular_x, sd->circular_y); } if (sd->preview_vae) { sd->preview_vae->set_circular_axes(sd->circular_x, sd->circular_y); } return original_axes; } int tile_size_x, tile_size_y; float overlap; int latent_size_x = request.width / request.vae_scale_factor; int latent_size_y = request.height / request.vae_scale_factor; sd->first_stage_model->get_tile_sizes(tile_size_x, tile_size_y, overlap, sd_img_gen_params->vae_tiling_params, latent_size_x, latent_size_y); sd->circular_x = sd->circular_x && (tile_size_x >= latent_size_x); sd->circular_y = sd->circular_y && (tile_size_y >= latent_size_y); if (sd->first_stage_model) { sd->first_stage_model->set_circular_axes(sd->circular_x, sd->circular_y); } if (sd->preview_vae) { sd->preview_vae->set_circular_axes(sd->circular_x, sd->circular_y); } sd->circular_x = original_axes.circular_x && (tile_size_x < latent_size_x); sd->circular_y = original_axes.circular_y && (tile_size_y < latent_size_y); return original_axes; } static void restore_image_vae_axes(StableDiffusionGGML* sd, const CircularAxesState& original_axes) { sd->circular_x = original_axes.circular_x; sd->circular_y = original_axes.circular_y; } class ImageVaeAxesGuard { private: StableDiffusionGGML* sd = nullptr; CircularAxesState original_axes; public: ImageVaeAxesGuard(StableDiffusionGGML* sd, const sd_img_gen_params_t* sd_img_gen_params, const GenerationRequest& request) : sd(sd), original_axes(configure_image_vae_axes(sd, sd_img_gen_params, request)) {} ~ImageVaeAxesGuard() { restore_image_vae_axes(sd, original_axes); } ImageVaeAxesGuard(const ImageVaeAxesGuard&) = delete; ImageVaeAxesGuard& operator=(const ImageVaeAxesGuard&) = delete; }; sd::Tensor ensure_image_tensor_channels(sd::Tensor image, int channels) { if (image.empty()) { return image; } GGML_ASSERT(image.dim() == 4); int64_t current_channels = image.shape()[2]; if (current_channels == channels) { return image; } if (channels == 4) { sd::Tensor alpha = sd::full({image.shape()[0], image.shape()[1], 1, image.shape()[3]}, 1.f); if (current_channels == 3) { return sd::ops::concat(image, alpha, 2); } if (current_channels == 1) { sd::Tensor rgb = sd::ops::concat(image, image, 2); rgb = sd::ops::concat(rgb, image, 2); return sd::ops::concat(rgb, alpha, 2); } } if (channels == 3 && current_channels >= 3) { return sd::ops::slice(image, 2, 0, 3); } GGML_ABORT("cannot convert image tensor from %lld to %d channels", (long long)current_channels, channels); } static std::optional prepare_image_generation_latents(StableDiffusionGGML* sd, const sd_img_gen_params_t* sd_img_gen_params, GenerationRequest* request, SamplePlan* plan, const RefImageParams& ref_image_params) { int64_t prepare_start_ms = ggml_time_ms(); sd::Tensor init_image_tensor; sd::Tensor control_image_tensor; sd::Tensor mask_image_tensor; int image_channels = sd->get_image_channels(); if (sd_img_gen_params->init_image.data != nullptr) { LOG_INFO("IMG2IMG"); if (request->strength < 1.f) { bool strength_as_noise_level = false; bool force_first_sigma = false; for (const auto& [key, value] : parse_key_value_args(sd_img_gen_params->sample_params.extra_sample_args, "img2img arg")) { if (key == "strength_as_noise_level") { if (!parse_strict_bool(value, strength_as_noise_level)) { LOG_WARN("ignoring invalid img2img sample arg '%s=%s'", key.c_str(), value.c_str()); } } else if (key == "force_first_sigma") { if (!parse_strict_bool(value, force_first_sigma)) { LOG_WARN("ignoring invalid img2img sample arg '%s=%s'", key.c_str(), value.c_str()); } } } size_t t_enc; float target_sigma = -1; if (!strength_as_noise_level) { t_enc = static_cast(plan->sample_steps * request->strength); if (t_enc == static_cast(plan->sample_steps)) { t_enc--; } } else { LOG_VERBOSE("Interpreting denoise strength as relative noise level"); // assume x_noised = K * (x * (1-noise_level) + noise * noise_level) = K * lerp(x, noise, noise_level) // K = 1, noise_level = sigma for flow models // K = 1+sigma, noise_level=sigma/(1+sigma) for diffusion models float target_noise_level = request->strength; target_sigma = sd->denoiser->noise_level_to_sigma(target_noise_level); size_t start_index = 0; for (size_t i = 0; i < plan->sigmas.size(); ++i) { if (plan->sigmas[i] <= target_sigma) { start_index = i; break; } } if (start_index >= plan->sigmas.size() - 1) { start_index = plan->sigmas.size() - 2; // Leave at least 1 step } t_enc = plan->sample_steps - start_index - 1; } LOG_INFO("target t_enc is %zu steps", t_enc); std::vector sigma_sched; sigma_sched.assign(plan->sigmas.begin() + plan->sample_steps - t_enc - 1, plan->sigmas.end()); if (target_sigma > 0 && force_first_sigma && strength_as_noise_level) { LOG_VERBOSE("force_first_sigma to %.4f (from %.4f)", target_sigma, sigma_sched[0]); sigma_sched[0] = target_sigma; } plan->sigmas = std::move(sigma_sched); plan->sample_steps = static_cast(plan->sigmas.size() - 1); } init_image_tensor = ensure_image_tensor_channels(sd_image_to_tensor(sd_img_gen_params->init_image, request->width, request->height), image_channels); } if (sd_img_gen_params->mask_image.data != nullptr) { mask_image_tensor = sd_image_to_tensor(sd_img_gen_params->mask_image, request->width, request->height); mask_image_tensor = sd::ops::round(mask_image_tensor); } if (sd_img_gen_params->control_image.data != nullptr) { control_image_tensor = sd_image_to_tensor(sd_img_gen_params->control_image, request->width, request->height); } if (init_image_tensor.empty() || mask_image_tensor.empty()) { if (sd_version_is_inpaint(sd->version)) { LOG_WARN("inpainting model requires both an init image and a mask image."); } } if (mask_image_tensor.empty()) { mask_image_tensor = sd::full({request->width, request->height, 1, 1}, 1.f); } sd::Tensor latent_mask = sd::ops::interpolate(mask_image_tensor, {request->width / request->vae_scale_factor, request->height / request->vae_scale_factor, 1, 1}, sd::ops::InterpolateMode::NearestMax); sd::Tensor init_latent; sd::Tensor control_latent; if (init_image_tensor.empty()) { if (sd->version == VERSION_QWEN_IMAGE_LAYERED) { init_latent = sd->generate_init_latent(request->width, request->height, request->qwen_image_layers + 1, true); } else { init_latent = sd->generate_init_latent(request->width, request->height); } } else { init_latent = sd->encode_first_stage(init_image_tensor); if (init_latent.empty()) { LOG_ERROR("failed to encode init image"); return std::nullopt; } } if (sd->animatediff_num_frames > 1 && init_latent.dim() >= 4 && init_latent.shape()[3] == 1) { int n_frames = sd->animatediff_num_frames; std::vector shape(init_latent.shape().begin(), init_latent.shape().end()); shape[3] = n_frames; if (!init_image_tensor.empty()) { sd::Tensor replicated(shape); for (int f = 0; f < n_frames; ++f) { sd::ops::slice_assign(&replicated, 3, f, f + 1, init_latent); } init_latent = std::move(replicated); } else { init_latent = sd::Tensor(std::move(shape)); } } if (!control_image_tensor.empty()) { control_latent = sd->encode_first_stage(control_image_tensor); if (control_latent.empty()) { LOG_ERROR("failed to encode control image"); return std::nullopt; } } std::vector> ref_images; for (int i = 0; i < sd_img_gen_params->ref_images_count; i++) { ref_images.push_back(ensure_image_tensor_channels(sd_image_to_tensor(sd_img_gen_params->ref_images[i]), image_channels)); } if (ref_images.empty() && sd_version_is_unet_edit(sd->version)) { LOG_WARN("This model needs at least one reference image; using an empty reference"); ref_images.push_back(sd::zeros({request->width, request->height, image_channels, 1})); request->guidance.img_cfg = request->guidance.txt_cfg; request->use_img_uncond = false; } if (!ref_images.empty()) { LOG_INFO("EDIT mode"); } std::vector> ref_latents; for (size_t i = 0; i < ref_images.size(); i++) { if (sd->version == VERSION_HIDREAM_O1) { continue; } sd::Tensor ref_latent; if (ref_image_params.resize_before_vae && !sd_version_is_pid(sd->version)) { LOG_VERBOSE("auto resize ref images"); double vae_width; double vae_height; if (ref_image_params.resize_vae_to_target) { vae_width = request->width; vae_height = request->height; } else { int default_pixels = sd->version == VERSION_QWEN_IMAGE_2_1 ? request->width * request->height : 1024 * 1024; int target_pixels = ref_image_params.vae_input_max_pixels > 0 ? ref_image_params.vae_input_max_pixels : default_pixels; int vae_image_size = std::min(target_pixels, request->width * request->height); vae_width = sqrt(vae_image_size * ref_images[i].shape()[0] / ref_images[i].shape()[1]); vae_height = vae_width * ref_images[i].shape()[1] / ref_images[i].shape()[0]; } int factor = sd_version_is_qwen_image(sd->version) ? 32 : 16; vae_height = round(vae_height / factor) * factor; vae_width = round(vae_width / factor) * factor; auto resized_ref_img = sd::ops::interpolate(ref_images[i], {static_cast(vae_width), static_cast(vae_height), ref_images[i].shape()[2], ref_images[i].shape()[3]}); LOG_VERBOSE("resize vae ref image %d from %" PRId64 "x%" PRId64 " to %" PRId64 "x%" PRId64, static_cast(i), ref_images[i].shape()[1], ref_images[i].shape()[0], resized_ref_img.shape()[1], resized_ref_img.shape()[0]); ref_latent = sd->encode_first_stage(resized_ref_img); if (sd->version == VERSION_QWEN_IMAGE_2_1) { ref_images[i] = std::move(resized_ref_img); } } else { ref_latent = sd->encode_first_stage(ref_images[i]); } if (ref_latent.empty()) { LOG_ERROR("failed to encode reference image %d", static_cast(i)); return std::nullopt; } ref_latents.push_back(std::move(ref_latent)); } if (sd_version_is_pid(sd->version)) { if (ref_latents.empty()) { LOG_ERROR("PiD requires a reference image"); return std::nullopt; } } sd::Tensor concat_latent; sd::Tensor img_uncond_concat_latent; if (sd_version_is_inpaint(sd->version)) { sd::Tensor masked_init_latent; if (sd->version != VERSION_FLEX_2) { if (!init_image_tensor.empty()) { auto masked_image = ((1.0f - mask_image_tensor) * (init_image_tensor - 0.5f)) + 0.5f; masked_init_latent = sd->encode_first_stage(masked_image); if (masked_init_latent.empty()) { LOG_ERROR("failed to encode masked init image"); return std::nullopt; } } else { masked_init_latent = sd::Tensor::zeros_like(init_latent); } } else { masked_init_latent = ((1.0f - latent_mask) * init_latent); } auto uncond_masked_init_latent = sd::Tensor::zeros_like(masked_init_latent); if (sd->version == VERSION_FLUX_FILL) { auto mask = mask_image_tensor.reshape({request->vae_scale_factor, request->width / request->vae_scale_factor, request->vae_scale_factor, request->height / request->vae_scale_factor}); mask = mask.permute({1, 3, 0, 2}).reshape({request->width / request->vae_scale_factor, request->height / request->vae_scale_factor, request->vae_scale_factor * request->vae_scale_factor, 1}); concat_latent = sd::ops::concat(masked_init_latent, mask, 2); img_uncond_concat_latent = sd::ops::concat(uncond_masked_init_latent, mask, 2); } else if (sd->version == VERSION_FLEX_2) { concat_latent = sd::ops::concat(masked_init_latent, latent_mask, 2); if (!control_latent.empty()) { concat_latent = sd::ops::concat(concat_latent, control_latent, 2); } else { concat_latent = sd::ops::concat(concat_latent, sd::Tensor::zeros_like(masked_init_latent), 2); } img_uncond_concat_latent = sd::ops::concat(uncond_masked_init_latent, latent_mask, 2); img_uncond_concat_latent = sd::ops::concat(img_uncond_concat_latent, sd::Tensor::zeros_like(masked_init_latent), 2); } else { // SD1.x SD2.x SDXL inpaint concat_latent = sd::ops::concat(latent_mask, masked_init_latent, 2); img_uncond_concat_latent = sd::ops::concat(latent_mask, uncond_masked_init_latent, 2); } } if (sd_version_is_unet_edit(sd->version)) { concat_latent = sd::ops::interpolate(ref_latents[0], init_latent.shape()); img_uncond_concat_latent = sd::Tensor::zeros_like(concat_latent); } if (sd->version == VERSION_FLUX_CONTROLS) { if (!control_latent.empty()) { concat_latent = control_latent; } else { concat_latent = sd::Tensor::zeros_like(init_latent); } img_uncond_concat_latent = sd::Tensor::zeros_like(concat_latent); } if (sd_img_gen_params->init_image.data != nullptr || sd_img_gen_params->ref_images_count > 0) { int64_t t1 = ggml_time_ms(); LOG_INFO("encode_first_stage completed, taking %.2fs", (t1 - prepare_start_ms) * 1.0f / 1000); } ImageGenerationLatents latents; latents.init_latent = std::move(init_latent); latents.concat_latent = std::move(concat_latent); latents.img_uncond_concat_latent = std::move(img_uncond_concat_latent); latents.control_image = std::move(control_image_tensor); latents.ref_images = std::move(ref_images); latents.ref_latents = std::move(ref_latents); if (sd_version_is_inpaint(sd->version)) { latent_mask = sd::ops::max_pool_2d(latent_mask, {3, 3}, {1, 1}, {1, 1}); } latents.denoise_mask = std::move(latent_mask); return latents; } static std::optional prepare_image_generation_embeds(StableDiffusionGGML* sd, const sd_img_gen_params_t* sd_img_gen_params, GenerationRequest* request, SamplePlan* plan, ImageGenerationLatents* latents, const RefImageParams& ref_image_params) { ConditionerRunnerEndOnExit conditioner_runner_end{sd->cond_stage_model.get()}; ConditionerParams condition_params; condition_params.text = request->prompt; condition_params.clip_skip = request->clip_skip; condition_params.width = request->width; condition_params.height = request->height; if (ref_image_params.pass_to_vlm) { condition_params.ref_images = &latents->ref_images; } condition_params.ref_image_params = ref_image_params; sd->prepare_generation_extensions(request->pm_params, request->pulid_params, condition_params, plan->total_steps); sd->compute_ip_adapter_tokens(sd_img_gen_params->ip_adapter_image, sd_img_gen_params->ip_adapter_strength); int64_t prepare_start_ms = ggml_time_ms(); condition_params.zero_out_masked = false; auto cond = sd->cond_stage_model->get_learned_condition(sd->n_threads, condition_params); if (cond.empty()) { LOG_ERROR("failed to encode prompt"); return std::nullopt; } if (cond.c_concat.empty() && ref_image_params.pass_to_dit) { cond.c_concat = latents->concat_latent; // TODO: optimize } bool use_ref_latent_img_cfg = request->use_img_uncond && !latents->ref_images.empty() && sd_version_supports_ref_latent_img_cfg(sd->version); SDCondition uncond; if (request->use_uncond || request->use_high_noise_uncond) { if (sd_version_is_ideogram4(sd->version)) { uncond.c_vector = sd::Tensor::from_vector({1.0f}); } else if (sd_version_is_minit2i(sd->version)) { // MiniT2I derives the unconditional signal from the same T5 hidden // states with a zeroed prompt mask, so no extra text encode is needed. uncond.c_crossattn = cond.c_crossattn; uncond.c_vector = sd::Tensor::zeros_like(cond.c_vector); } else if (sd_version_is_sensenova_u1(sd->version)) { auto* sensenova_conditioner = static_cast(sd->cond_stage_model.get()); uncond = sensenova_conditioner->get_unconditional_condition(request->negative_prompt); } else { bool zero_out_masked = false; if (sd_version_is_sdxl(sd->version) && request->negative_prompt.empty() && !sd->is_using_edm_v_parameterization) { zero_out_masked = true; } condition_params.text = request->negative_prompt; condition_params.zero_out_masked = zero_out_masked; if (sd_version_is_llada_image(sd->version)) { // LLaDA-Image CFG keeps the source latent but drops its SigVQ features. condition_params.ref_images = nullptr; } uncond = sd->cond_stage_model->get_learned_condition(sd->n_threads, condition_params); if (uncond.empty()) { LOG_ERROR("failed to encode negative prompt"); return std::nullopt; } } if (uncond.c_concat.empty() && ref_image_params.pass_to_dit) { uncond.c_concat = latents->concat_latent; // TODO: optimize } } SDCondition img_uncond; if (request->use_img_uncond) { if ((request->use_uncond || request->use_high_noise_uncond) && (latents->ref_images.empty() || !use_ref_latent_img_cfg)) { img_uncond = SDCondition(uncond.c_crossattn, uncond.c_vector, latents->img_uncond_concat_latent); } else { bool zero_out_masked = false; if (sd_version_is_sdxl(sd->version) && request->negative_prompt.empty() && !sd->is_using_edm_v_parameterization) { zero_out_masked = true; } condition_params.text = request->negative_prompt; condition_params.zero_out_masked = zero_out_masked; std::vector> empty_ref_images; if (use_ref_latent_img_cfg) { condition_params.ref_images = &empty_ref_images; } img_uncond = sd->cond_stage_model->get_learned_condition(sd->n_threads, condition_params); if (img_uncond.empty()) { LOG_ERROR("failed to encode image guidance prompt"); return std::nullopt; } if (img_uncond.c_concat.empty() && ref_image_params.pass_to_dit) { img_uncond.c_concat = latents->img_uncond_concat_latent; // TODO: optimize } } } int64_t t1 = ggml_time_ms(); LOG_INFO("get_learned_condition completed, taking %.2fs", (t1 - prepare_start_ms) * 1.0f / 1000); ImageGenerationEmbeds embeds; embeds.img_uncond = std::move(img_uncond); embeds.cond = std::move(cond); embeds.uncond = std::move(uncond); return embeds; } static sd_image_t* decode_image_outputs(StableDiffusionGGML* sd, const GenerationRequest& request, const std::vector>& final_latents, int* num_images_out) { if (final_latents.empty()) { LOG_ERROR("no latent images to decode"); return nullptr; } if (final_latents.size() > static_cast(request.batch_count)) { LOG_ERROR("expected at most %d latents, got %zu", request.batch_count, final_latents.size()); return nullptr; } if (final_latents.size() < static_cast(request.batch_count)) { LOG_INFO("decoding %zu/%d latents", final_latents.size(), request.batch_count); } else { LOG_INFO("decoding %zu latents", final_latents.size()); } std::vector> decoded_images; int64_t t0 = ggml_time_ms(); bool cancelled = false; for (size_t i = 0; i < final_latents.size(); i++) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling latent decodings"); cancelled = true; break; } int64_t t1 = ggml_time_ms(); if (sd->version == VERSION_QWEN_IMAGE_LAYERED) { int qwen_image_latent_layers = request.qwen_image_layers + 1; if (final_latents[i].dim() < 5 || final_latents[i].shape()[2] < qwen_image_latent_layers) { LOG_ERROR("qwen image layered expected at least %d latent layers, got shape dim=%d", qwen_image_latent_layers, final_latents[i].dim()); return nullptr; } for (int layer_index = 0; layer_index < qwen_image_latent_layers; layer_index++) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling latent decodings"); cancelled = true; break; } sd::Tensor layer_latent = sd::ops::slice(final_latents[i], 2, layer_index, layer_index + 1); layer_latent.squeeze_(2); sd::Tensor image = sd->decode_first_stage(layer_latent); if (image.empty()) { LOG_ERROR("decode_first_stage failed for latent %zu layer %d", i + 1, layer_index + 1); return nullptr; } decoded_images.push_back(std::move(image)); } if (cancelled) { break; } } else if (sd->animatediff_num_frames > 1 && final_latents[i].dim() >= 4 && final_latents[i].shape()[3] == sd->animatediff_num_frames) { int n_frames = sd->animatediff_num_frames; for (int f = 0; f < n_frames; ++f) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling latent decodings"); cancelled = true; break; } sd::Tensor frame_latent = sd::ops::slice(final_latents[i], 3, f, f + 1); sd::Tensor image = sd->decode_first_stage(frame_latent); if (image.empty()) { LOG_ERROR("decode_first_stage failed for AnimateDiff frame %d/%d", f + 1, n_frames); return nullptr; } decoded_images.push_back(std::move(image)); } } else { sd::Tensor image = sd->decode_first_stage(final_latents[i]); if (image.empty()) { LOG_ERROR("decode_first_stage failed for latent %" PRId64, i + 1); return nullptr; } decoded_images.push_back(std::move(image)); } int64_t t2 = ggml_time_ms(); LOG_INFO("latent %zu decoded, taking %.2fs", i + 1, (t2 - t1) * 1.0f / 1000); } int64_t t4 = ggml_time_ms(); LOG_INFO("decode_first_stage completed, taking %.2fs", (t4 - t0) * 1.0f / 1000); if (decoded_images.empty()) { LOG_ERROR(cancelled ? "cancelled before any latent images were decoded" : "no decoded images"); return nullptr; } int image_count = static_cast(decoded_images.size()); sd_image_t* result_images = (sd_image_t*)calloc(image_count, sizeof(sd_image_t)); if (result_images == nullptr) { return nullptr; } if (num_images_out != nullptr) { *num_images_out = image_count; } for (size_t i = 0; i < decoded_images.size(); i++) { result_images[i] = tensor_to_sd_image(decoded_images[i]); } return result_images; } static sd::Tensor upscale_hires_latent(StableDiffusionGGML* sd, const sd::Tensor& latent, const GenerationRequest& request, UpscalerGGML* upscaler) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling hires latent upscale"); return {}; } auto get_hires_latent_target_shape = [&]() { std::vector target_shape = latent.shape(); if (target_shape.size() < 2) { target_shape.clear(); return target_shape; } target_shape[0] = request.hires.target_width / request.vae_scale_factor; target_shape[1] = request.hires.target_height / request.vae_scale_factor; return target_shape; }; if (request.hires.upscaler == SD_HIRES_UPSCALER_LATENT || request.hires.upscaler == SD_HIRES_UPSCALER_LATENT_NEAREST || request.hires.upscaler == SD_HIRES_UPSCALER_LATENT_NEAREST_EXACT || request.hires.upscaler == SD_HIRES_UPSCALER_LATENT_ANTIALIASED || request.hires.upscaler == SD_HIRES_UPSCALER_LATENT_BICUBIC || request.hires.upscaler == SD_HIRES_UPSCALER_LATENT_BICUBIC_ANTIALIASED) { std::vector target_shape = get_hires_latent_target_shape(); if (target_shape.empty()) { LOG_ERROR("latent has invalid shape for hires upscale"); return {}; } sd::ops::InterpolateMode mode = sd::ops::InterpolateMode::Nearest; bool antialias = false; switch (request.hires.upscaler) { case SD_HIRES_UPSCALER_LATENT: mode = sd::ops::InterpolateMode::Bilinear; break; case SD_HIRES_UPSCALER_LATENT_NEAREST: mode = sd::ops::InterpolateMode::Nearest; break; case SD_HIRES_UPSCALER_LATENT_NEAREST_EXACT: mode = sd::ops::InterpolateMode::NearestExact; break; case SD_HIRES_UPSCALER_LATENT_ANTIALIASED: mode = sd::ops::InterpolateMode::Bilinear; antialias = true; break; case SD_HIRES_UPSCALER_LATENT_BICUBIC: mode = sd::ops::InterpolateMode::Bicubic; break; case SD_HIRES_UPSCALER_LATENT_BICUBIC_ANTIALIASED: mode = sd::ops::InterpolateMode::Bicubic; antialias = true; break; default: break; } LOG_INFO("hires %s upscale %" PRId64 "x%" PRId64 " -> %" PRId64 "x%" PRId64, sd_hires_upscaler_name(request.hires.upscaler), latent.shape()[0], latent.shape()[1], target_shape[0], target_shape[1]); return sd::ops::interpolate(latent, target_shape, mode, false, antialias); } else if (request.hires.upscaler == SD_HIRES_UPSCALER_MODEL || request.hires.upscaler == SD_HIRES_UPSCALER_LANCZOS || request.hires.upscaler == SD_HIRES_UPSCALER_NEAREST) { if (request.hires.upscaler == SD_HIRES_UPSCALER_MODEL && upscaler == nullptr) { LOG_ERROR("hires model upscaler context is null"); return {}; } sd::Tensor decoded = sd->decode_first_stage(latent); if (decoded.empty()) { LOG_ERROR("decode_first_stage failed before hires %s upscale", sd_hires_upscaler_name(request.hires.upscaler)); return {}; } if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling hires image upscale"); return {}; } sd::Tensor upscaled_tensor; if (request.hires.upscaler == SD_HIRES_UPSCALER_MODEL) { upscaled_tensor = upscaler->upscale_tensor(decoded); if (upscaled_tensor.empty()) { LOG_ERROR("hires model upscale failed"); return {}; } if (upscaled_tensor.shape()[0] != request.hires.target_width || upscaled_tensor.shape()[1] != request.hires.target_height) { upscaled_tensor = sd::ops::interpolate(upscaled_tensor, {request.hires.target_width, request.hires.target_height, upscaled_tensor.shape()[2], upscaled_tensor.shape()[3]}); } } else { sd::ops::InterpolateMode mode = request.hires.upscaler == SD_HIRES_UPSCALER_LANCZOS ? sd::ops::InterpolateMode::Lanczos : sd::ops::InterpolateMode::Nearest; LOG_INFO("hires %s image upscale %" PRId64 "x%" PRId64 " -> %dx%d", sd_hires_upscaler_name(request.hires.upscaler), decoded.shape()[0], decoded.shape()[1], request.hires.target_width, request.hires.target_height); upscaled_tensor = sd::ops::interpolate(decoded, {request.hires.target_width, request.hires.target_height, decoded.shape()[2], decoded.shape()[3]}, mode); upscaled_tensor = sd::ops::clamp(upscaled_tensor, 0.0f, 1.0f); } if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling hires latent encode"); return {}; } sd::Tensor upscaled_latent = sd->encode_first_stage(upscaled_tensor); if (upscaled_latent.empty()) { LOG_ERROR("encode_first_stage failed after hires %s upscale", sd_hires_upscaler_name(request.hires.upscaler)); } return upscaled_latent; } LOG_ERROR("unsupported hires upscaler '%s'", sd_hires_upscaler_name(request.hires.upscaler)); return {}; } bool generate_image(StableDiffusionGGML* sd, const sd_img_gen_params_t* sd_img_gen_params, sd_image_t** images_out, int* num_images_out) { if (images_out != nullptr) { *images_out = nullptr; } if (num_images_out != nullptr) { *num_images_out = 0; } if (sd == nullptr || sd_img_gen_params == nullptr) { return false; } if (!sd_version_supports_image_generation(sd->version)) { LOG_ERROR("%s cannot be run with generate_image(); use generate_video() or --mode vid_gen in the CLI", model_version_to_str[sd->version]); return false; } sd->reset_cancel_flag(); int64_t t0 = ggml_time_ms(); sd->vae_tiling_params = sd_img_gen_params->vae_tiling_params; GenerationRequest request(sd, sd_img_gen_params); LOG_INFO("generate_image %dx%d", request.width, request.height); sd->rng->manual_seed(request.seed); sd->sampler_rng->manual_seed(request.seed); sd->set_flow_shift(sd_img_gen_params->sample_params.flow_shift); if (!sd->apply_loras(sd_img_gen_params->loras, sd_img_gen_params->lora_count)) return false; sd->apply_circular_axes(sd_img_gen_params->circular_x, sd_img_gen_params->circular_y); const RefImageParams ref_image_params = sd->resolve_ref_image_params(sd_img_gen_params->ref_image_args); ImageVaeAxesGuard axes_guard(sd, sd_img_gen_params, request); SamplePlan plan(sd, sd_img_gen_params, request); auto latents_opt = prepare_image_generation_latents(sd, sd_img_gen_params, &request, &plan, ref_image_params); if (!latents_opt.has_value()) { return false; } ImageGenerationLatents latents = std::move(*latents_opt); auto embeds_opt = prepare_image_generation_embeds(sd, sd_img_gen_params, &request, &plan, &latents, ref_image_params); if (!embeds_opt.has_value()) { return false; } ImageGenerationEmbeds embeds = std::move(*embeds_opt); std::vector> final_latents; int64_t denoise_start = ggml_time_ms(); for (int b = 0; b < request.batch_count; b++) { sd_cancel_mode_t cancel = sd->get_cancel_flag(); if (cancel == SD_CANCEL_ALL) { LOG_ERROR("cancelling generation"); return false; } if (cancel == SD_CANCEL_NEW_LATENTS) { LOG_INFO("cancelling new latent generation, returning %zu/%d completed latents", final_latents.size(), request.batch_count); break; } int64_t sampling_start = ggml_time_ms(); int64_t cur_seed = request.seed + b; LOG_INFO("generating image: %i/%i - seed %" PRId64, b + 1, request.batch_count, cur_seed); sd->rng->manual_seed(cur_seed); sd->sampler_rng->manual_seed(cur_seed); sd::Tensor noise = sd::randn_like(latents.init_latent, sd->rng); sd::Tensor x_0 = sd->sample(sd->diffusion_model, true, latents.init_latent, std::move(noise), embeds.cond, embeds.uncond, embeds.img_uncond, latents.control_image, request.control_strength, request.guidance, plan.eta, request.shifted_timestep, plan.sample_method, sd->is_flow_denoiser(), plan.extra_sample_args, plan.sigmas, latents.ref_latents, ref_image_params, latents.denoise_mask, sd::Tensor(), 1.f, 0, static_cast(request.fps), request.cache_params, true); int64_t sampling_end = ggml_time_ms(); if (!x_0.empty()) { LOG_INFO("sampling completed, taking %.2fs", (sampling_end - sampling_start) * 1.0f / 1000); final_latents.push_back(std::move(x_0)); continue; } LOG_ERROR("sampling for image %d/%d failed after %.2fs", b + 1, request.batch_count, (sampling_end - sampling_start) * 1.0f / 1000); return false; } int64_t denoise_end = ggml_time_ms(); LOG_INFO("generating %zu latent images completed, taking %.2fs", final_latents.size(), (denoise_end - denoise_start) * 1.0f / 1000); if (final_latents.empty()) { LOG_ERROR("no latent images generated"); return false; } if (request.hires.enabled && request.hires.target_width > 0) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling generation before hires fix"); return false; } LOG_INFO("hires fix: upscaling to %dx%d", request.hires.target_width, request.hires.target_height); std::unique_ptr hires_upscaler; if (request.hires.upscaler == SD_HIRES_UPSCALER_MODEL) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling generation before hires model load"); return false; } LOG_INFO("hires fix: loading model upscaler from '%s'", request.hires.model_path); hires_upscaler = std::make_unique(sd->n_threads, false, request.hires.upscale_tile_size, sd->backend_spec, sd->params_backend_spec); const size_t max_graph_vram_bytes = sd->max_graph_vram_bytes_for_module(SDBackendModule::UPSCALER); hires_upscaler->set_max_graph_vram_bytes(max_graph_vram_bytes); if (!hires_upscaler->load_from_file(request.hires.model_path, sd->n_threads)) { LOG_ERROR("load hires model upscaler failed"); return false; } } int hires_scheduler_steps = 0; std::vector hires_sigma_sched = make_hires_sigma_schedule(sd, request.hires, sd_img_gen_params->sample_params, plan.sample_method, plan.sample_steps, sd->get_image_seq_len(request.hires.target_height, request.hires.target_width), &hires_scheduler_steps); LOG_INFO("hires fix: scheduler_steps=%d, denoising_strength=%.2f, sigma_sched_size=%zu%s", hires_scheduler_steps, request.hires.denoising_strength, hires_sigma_sched.size(), request.hires.custom_sigmas_count > 0 ? ", custom_sigmas=true" : ""); std::vector> hires_final_latents; int64_t hires_denoise_start = ggml_time_ms(); for (int b = 0; b < (int)final_latents.size(); b++) { if (sd->get_cancel_flag() == SD_CANCEL_ALL) { LOG_ERROR("cancelling generation during hires fix"); return false; } int64_t cur_seed = request.seed + b; sd->rng->manual_seed(cur_seed); sd->sampler_rng->manual_seed(cur_seed); sd::Tensor upscaled = upscale_hires_latent(sd, final_latents[b], request, hires_upscaler.get()); if (upscaled.empty()) { return false; } sd::Tensor noise = sd::randn_like(upscaled, sd->rng); sd::Tensor hires_denoise_mask; if (!latents.denoise_mask.empty()) { std::vector mask_shape = latents.denoise_mask.shape(); mask_shape[0] = upscaled.shape()[0]; mask_shape[1] = upscaled.shape()[1]; hires_denoise_mask = sd::ops::interpolate(latents.denoise_mask, mask_shape, sd::ops::InterpolateMode::NearestMax); } int64_t hires_sample_start = ggml_time_ms(); sd::Tensor x_0 = sd->sample(sd->diffusion_model, true, upscaled, std::move(noise), embeds.cond, embeds.uncond, embeds.img_uncond, latents.control_image, request.control_strength, request.guidance, plan.eta, request.shifted_timestep, plan.sample_method, sd->is_flow_denoiser(), plan.extra_sample_args, hires_sigma_sched, latents.ref_latents, ref_image_params, hires_denoise_mask, sd::Tensor(), 1.f, 0, static_cast(request.fps), request.cache_params, false); int64_t hires_sample_end = ggml_time_ms(); if (!x_0.empty()) { LOG_INFO("hires sampling %d/%d completed, taking %.2fs", b + 1, (int)final_latents.size(), (hires_sample_end - hires_sample_start) * 1.0f / 1000); hires_final_latents.push_back(std::move(x_0)); continue; } LOG_ERROR("hires sampling for image %d/%d failed after %.2fs", b + 1, (int)final_latents.size(), (hires_sample_end - hires_sample_start) * 1.0f / 1000); return false; } int64_t hires_denoise_end = ggml_time_ms(); LOG_INFO("hires fix completed, taking %.2fs", (hires_denoise_end - hires_denoise_start) * 1.0f / 1000); final_latents = std::move(hires_final_latents); } int num_images = 0; auto result = decode_image_outputs(sd, request, final_latents, &num_images); if (result == nullptr) { return false; } sd->lora_stat(); int64_t t1 = ggml_time_ms(); LOG_INFO("generate_image completed in %.2fs", (t1 - t0) * 1.0f / 1000); if (num_images_out != nullptr) { *num_images_out = num_images; } if (images_out != nullptr) { *images_out = result; } else { free_sd_images(result, num_images); } return true; } } // namespace sd::pipeline