From 1faff9fd4f4f2eb8c8b4c59c6008431c4a72dd97 Mon Sep 17 00:00:00 2001 From: Wagner Bruna Date: Thu, 20 Aug 2026 12:19:33 -0300 Subject: [PATCH] sd: cherry-pick changes up to master-827-97d2990 feat: add taeh3 support fix: prevent gallocr hash overflow in tiny graph-cut segments fix: re-clamp streaming VRAM budget to currently free memory fix: mark graph cuts with both a prefix and a suffix fix: make max_order of lms sampler configurable fix: guard against missing sampler/scheduler names chore: format code --- otherarch/sdcpp/examples/common/common.cpp | 23 ++-- otherarch/sdcpp/include/stable-diffusion.h | 4 + otherarch/sdcpp/src/core/ggml_extend.hpp | 7 +- otherarch/sdcpp/src/core/ggml_graph_cut.cpp | 55 ++++++++-- otherarch/sdcpp/src/core/ggml_graph_cut.h | 1 + otherarch/sdcpp/src/model/vae/tae.hpp | 113 +++++++++++++++++++- otherarch/sdcpp/src/runtime/denoiser.hpp | 68 ++++++++---- otherarch/sdcpp/src/stable-diffusion.cpp | 23 ++-- 8 files changed, 242 insertions(+), 52 deletions(-) diff --git a/otherarch/sdcpp/examples/common/common.cpp b/otherarch/sdcpp/examples/common/common.cpp index 28f18d9e6119..35812157c93a 100644 --- a/otherarch/sdcpp/examples/common/common.cpp +++ b/otherarch/sdcpp/examples/common/common.cpp @@ -1008,7 +1008,7 @@ ArgOptions SDGenerationParams::get_options() { &hires_upscaler}, {"", "--extra-sample-args", - "extra sampler/scheduler/guidance args, key=value list. CFG supports guidance_schedule; APG supports apg_eta, apg_momentum, apg_norm_threshold, apg_norm_threshold_smoothing; SLG supports slg_uncond; lcm supports noise_clip_std, noise_scale_start, noise_scale_end; flux supports base_shift, max_shift; ltx2 supports max_shift, base_shift, stretch, terminal; euler_ge supports gamma; beta scheduler supports alpha, beta; logit_normal supports mu, std, logsnr_min, logsnr_max, resolution_aware; lms supports lms_divisions", + "extra sampler/scheduler/guidance args, key=value list. CFG supports guidance_schedule; APG supports apg_eta, apg_momentum, apg_norm_threshold, apg_norm_threshold_smoothing; SLG supports slg_uncond; lcm supports noise_clip_std, noise_scale_start, noise_scale_end; flux supports base_shift, max_shift; ltx2 supports max_shift, base_shift, stretch, terminal; euler_ge supports gamma; beta scheduler supports alpha, beta; logit_normal supports mu, std, logsnr_min, logsnr_max, resolution_aware; lms supports lms_max_order, lms_shift, lms_divisions", (int)',', &extra_sample_args}, {"", @@ -1555,6 +1555,16 @@ ArgOptions SDGenerationParams::get_options() { return 1; }; + std::string sample_methods = sample_method_to_str[0]; + for (int i = 1; i < SAMPLE_METHOD_COUNT; i++) { + sample_methods += ", " + std::string(sample_method_to_str[i]); + } + + std::string schedulers = scheduler_to_str[0]; + for (int i = 1; i < SCHEDULER_COUNT; i++) { + schedulers += ", " + std::string(scheduler_to_str[i]); + } + options.manual_options = { {"-s", "--seed", @@ -1562,17 +1572,18 @@ ArgOptions SDGenerationParams::get_options() { on_seed_arg}, {"", "--sampling-method", - "sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, dpm++2m_sde, dpm++2m_sde_bt, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp, lms]" - "(default: euler for Flux/SD3/Wan, euler_a otherwise)", + "sampling method, one of [" + sample_methods + "], " + "default: euler for Flux/SD3/Wan, euler_a otherwise", on_sample_method_arg}, {"", "--high-noise-sampling-method", - "(high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, dpm++2m_sde, dpm++2m_sde_bt, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp, lms]" - " default: euler for Flux/SD3/Wan, euler_a otherwise", + "(high noise) sampling method, one of [" + sample_methods + "], " + "default: euler for Flux/SD3/Wan, euler_a otherwise", on_high_noise_sample_method_arg}, {"", "--scheduler", - "denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple, kl_optimal, lcm, bong_tangent, ltx2, logit_normal, flux2, flux, beta], alias: normal=discrete, default: model-specific", + "denoiser sigma scheduler, one of [" + schedulers + "], " + "alias: normal=discrete, default: model-specific", on_scheduler_arg}, {"", "--sigmas", diff --git a/otherarch/sdcpp/include/stable-diffusion.h b/otherarch/sdcpp/include/stable-diffusion.h index 0a1162338fe8..857680ca4f18 100644 --- a/otherarch/sdcpp/include/stable-diffusion.h +++ b/otherarch/sdcpp/include/stable-diffusion.h @@ -60,6 +60,8 @@ enum sample_method_t { SAMPLE_METHOD_COUNT }; +extern SD_API const char* sample_method_to_str[]; + enum scheduler_t { DISCRETE_SCHEDULER, KARRAS_SCHEDULER, @@ -80,6 +82,8 @@ enum scheduler_t { SCHEDULER_COUNT }; +extern SD_API const char* scheduler_to_str[]; + enum prediction_t { EPS_PRED, V_PRED, diff --git a/otherarch/sdcpp/src/core/ggml_extend.hpp b/otherarch/sdcpp/src/core/ggml_extend.hpp index 53de509ad7e8..9fe41d50b3a3 100644 --- a/otherarch/sdcpp/src/core/ggml_extend.hpp +++ b/otherarch/sdcpp/src/core/ggml_extend.hpp @@ -2408,13 +2408,14 @@ struct GGMLRunner { GGML_ASSERT(gf != nullptr); size_t effective_budget = max_graph_vram_bytes; + size_t free_clamp = SIZE_MAX; if (stream_layers_enabled && max_graph_vram_bytes > 0 && runtime_backend != nullptr) { ggml_backend_dev_t dev = ggml_backend_get_device(runtime_backend); if (dev != nullptr && ggml_backend_dev_type(dev) != GGML_BACKEND_DEVICE_TYPE_CPU) { size_t free_vram = 0, total_vram = 0; ggml_backend_dev_memory(dev, &free_vram, &total_vram); constexpr size_t safety_margin = 512ull * 1024 * 1024; - size_t free_clamp = (free_vram > safety_margin) ? (free_vram - safety_margin) : 0; + free_clamp = (free_vram > safety_margin) ? (free_vram - safety_margin) : 0; if (free_clamp < effective_budget) { LOG_DEBUG("%s clamping streaming budget: actual free VRAM %.2f MB < user cap %.2f MB", get_desc().c_str(), @@ -2431,7 +2432,9 @@ struct GGMLRunner { observed_max_effective_budget_ = effective_budget; budget_increased = true; } else { - effective_budget = observed_max_effective_budget_; + // Keep the plan cache stable, but never plan above what is free now: + // another model or process can take VRAM after the first measurement. + effective_budget = std::min(observed_max_effective_budget_, free_clamp); } } diff --git a/otherarch/sdcpp/src/core/ggml_graph_cut.cpp b/otherarch/sdcpp/src/core/ggml_graph_cut.cpp index d4874b054f29..542b7fe1616f 100644 --- a/otherarch/sdcpp/src/core/ggml_graph_cut.cpp +++ b/otherarch/sdcpp/src/core/ggml_graph_cut.cpp @@ -453,11 +453,12 @@ namespace sd::ggml_graph_cut { if (tensor == nullptr || tensor->name[0] == '\0') { return false; } - return std::strncmp(tensor->name, GGML_RUNNER_CUT_PREFIX, std::strlen(GGML_RUNNER_CUT_PREFIX)) == 0; + return starts_with(tensor->name, GGML_RUNNER_CUT_PREFIX) && + ends_with(tensor->name, GGML_RUNNER_CUT_SUFFIX); } std::string make_graph_cut_name(const std::string& group, const std::string& output) { - return std::string(GGML_RUNNER_CUT_PREFIX) + group + "|" + output; + return std::string(GGML_RUNNER_CUT_PREFIX) + group + "|" + output + GGML_RUNNER_CUT_SUFFIX; } void mark_graph_cut(ggml_tensor* tensor, const std::string& group, const std::string& output) { @@ -603,7 +604,43 @@ namespace sd::ggml_graph_cut { GGML_ASSERT(gf != nullptr); GGML_ASSERT(graph_ctx_out != nullptr); - const size_t graph_size = segment.internal_node_indices.size() + segment.input_refs.size() + 8; + // Collect leaf inputs and internal nodes, then any tensor they + // reference that is not already represented, notably the view_src of a + // view-typed input leaf. ggml_gallocr sizes its hash set from + // n_nodes + n_leafs (plus a 25% margin that rounds down to zero for a + // one-node segment), so every distinct tensor it will hash must be + // counted here or a tiny segment overflows the hash set and aborts. + std::vector leaves; + std::unordered_set represented; + for (const auto& input : segment.input_refs) { + ggml_tensor* current_input = input_tensor(gf, input); + if (current_input == nullptr) { + continue; + } + if (represented.insert(current_input).second) { + leaves.push_back(current_input); + } + } + for (int node_idx : segment.internal_node_indices) { + represented.insert(ggml_graph_node(gf, node_idx)); + } + auto add_reference = [&](ggml_tensor* tensor) { + if (tensor != nullptr && represented.insert(tensor).second) { + leaves.push_back(tensor); + } + }; + for (int node_idx : segment.internal_node_indices) { + ggml_tensor* node = ggml_graph_node(gf, node_idx); + for (int src_idx = 0; src_idx < GGML_MAX_SRC; ++src_idx) { + add_reference(node->src[src_idx]); + } + add_reference(node->view_src); + } + for (size_t i = 0; i < leaves.size(); ++i) { + add_reference(leaves[i]->view_src); + } + + const size_t graph_size = segment.internal_node_indices.size() + leaves.size() + 8; ggml_init_params params = { /*.mem_size =*/ggml_graph_overhead_custom(graph_size, false) + 1024, /*.mem_buffer =*/nullptr, @@ -614,13 +651,9 @@ namespace sd::ggml_graph_cut { ggml_cgraph* segment_graph = ggml_new_graph_custom(graph_ctx, graph_size, false); GGML_ASSERT(segment_graph != nullptr); - for (const auto& input : segment.input_refs) { - ggml_tensor* current_input = input_tensor(gf, input); - if (current_input == nullptr) { - continue; - } + for (ggml_tensor* leaf : leaves) { GGML_ASSERT(segment_graph->n_leafs < segment_graph->size); - segment_graph->leafs[segment_graph->n_leafs++] = current_input; + segment_graph->leafs[segment_graph->n_leafs++] = leaf; } for (int output_node_index : segment.output_node_indices) { @@ -751,7 +784,9 @@ namespace sd::ggml_graph_cut { plan.has_cuts = true; std::string full_name(node->name); - std::string payload = full_name.substr(std::strlen(GGML_RUNNER_CUT_PREFIX)); + size_t prefix_len = std::strlen(GGML_RUNNER_CUT_PREFIX); + size_t suffix_len = std::strlen(GGML_RUNNER_CUT_SUFFIX); + std::string payload = full_name.substr(prefix_len, full_name.size() - prefix_len - suffix_len); size_t sep = payload.find('|'); std::string group = sep == std::string::npos ? payload : payload.substr(0, sep); diff --git a/otherarch/sdcpp/src/core/ggml_graph_cut.h b/otherarch/sdcpp/src/core/ggml_graph_cut.h index 17f2f1d7dda4..d955049d826e 100644 --- a/otherarch/sdcpp/src/core/ggml_graph_cut.h +++ b/otherarch/sdcpp/src/core/ggml_graph_cut.h @@ -68,6 +68,7 @@ namespace sd::ggml_graph_cut { }; static constexpr const char* GGML_RUNNER_CUT_PREFIX = "ggml_runner_cut:"; + static constexpr const char* GGML_RUNNER_CUT_SUFFIX = "|"; struct MaxVramAssignment { float default_gib = 0.f; diff --git a/otherarch/sdcpp/src/model/vae/tae.hpp b/otherarch/sdcpp/src/model/vae/tae.hpp index 710729fd7271..a03e5d21a296 100644 --- a/otherarch/sdcpp/src/model/vae/tae.hpp +++ b/otherarch/sdcpp/src/model/vae/tae.hpp @@ -426,10 +426,10 @@ class TinyVideoDecoder : public UnaryBlock { static const int num_layers = 3; int channels[num_layers + 1] = {256, 128, 64, 64}; int patch_size = 1; - int t_upscale = 1; bool is_wide = false; public: + int t_upscale = 1; TinyVideoDecoder(int z_channels = 4, int patch_size = 1, std::vector time_upscale = {false, true, true}, bool is_wide = false) : z_channels(z_channels), patch_size(patch_size), is_wide(is_wide) { t_upscale = 1; @@ -536,6 +536,10 @@ class TAEHV : public GGMLBlock { patch = 4; time_downscale = {true, true, true}; time_upscale = {true, true, true}; + } else if (sd_version_is_minimax_h3(version)) { + z_channels = 24; + patch = 2; + time_downscale = {true, true, false}; } blocks["decoder"] = std::shared_ptr(new TinyVideoDecoder(z_channels, patch, time_upscale, is_wide)); if (!decode_only) { @@ -545,24 +549,123 @@ class TAEHV : public GGMLBlock { ggml_tensor* decode(GGMLRunnerContext* ctx, ggml_tensor* z) { auto decoder = std::dynamic_pointer_cast(blocks["decoder"]); - if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) { + if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version) || sd_version_is_minimax_h3(version)) { // (W, H, C, T) -> (W, H, T, C) z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 0, 1, 3, 2)); } auto result = decoder->forward(ctx, z); - if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) { + + if (sd_version_is_minimax_h3(version)) { + int64_t num_frames = result->ne[3]; + int64_t chunk_frames = 5 * decoder->t_upscale; + int64_t pad = (chunk_frames - (num_frames % chunk_frames)) % chunk_frames; + + result = ggml_ext_pad_ext(ctx->ggml_ctx, ctx->backend, result, 0, 0, 0, 0, 0, 0, 0, pad, false, false); + + int64_t num_chunks = (num_frames + pad) / chunk_frames; + auto to_trim = decoder->t_upscale - 1; + std::vector to_concat = {}; + for (int i = 0; i < num_chunks; i++) { + auto chunk = ggml_view_4d(ctx->ggml_ctx, result, + result->ne[0], result->ne[1], result->ne[2], chunk_frames - to_trim, + result->nb[1], result->nb[2], result->nb[3], + i * chunk_frames * result->nb[3]); + to_concat.push_back(chunk); + } + result = ggml_ext_vec_concat(ctx->ggml_ctx, to_concat, 3); + result = ggml_view_4d(ctx->ggml_ctx, result, + result->ne[0], result->ne[1], result->ne[2], + result->ne[3] - decoder->t_upscale * 3, + result->nb[1], result->nb[2], result->nb[3], 0); + } + + if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version) || sd_version_is_minimax_h3(version)) { // (W, H, T, C) -> (W, H, C, T) result = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, result, 0, 1, 3, 2)); } return result; } - ggml_tensor* encode(GGMLRunnerContext* ctx, ggml_tensor* x) { + ggml_tensor* encode_h3(GGMLRunnerContext* ctx, ggml_tensor* x) { auto encoder = std::dynamic_pointer_cast(blocks["encoder"]); - if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) { + + int64_t num_frames = x->ne[3]; + int64_t pad = (17 - (num_frames % 17)) % 17; + + if (pad > 0) { + auto last_frame = ggml_view_4d(ctx->ggml_ctx, x, + x->ne[0], x->ne[1], x->ne[2], 1, + x->nb[1], x->nb[2], x->nb[3], + (num_frames - 1) * x->nb[3]); + for (int i = 0; i < pad; i++) { + x = ggml_concat(ctx->ggml_ctx, x, last_frame, 3); + } + } + + int64_t T_padded = x->ne[3]; + int64_t num_chunks = T_padded / 17; + + auto zero_frame = ggml_view_4d(ctx->ggml_ctx, x, + x->ne[0], x->ne[1], x->ne[2], 1, + x->nb[1], x->nb[2], x->nb[3], 0); + auto zeros_1 = ggml_scale(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, zero_frame), 0.0f); + auto zeros_3 = zeros_1; + for (int i = 1; i < 3; i++) { + zeros_3 = ggml_concat(ctx->ggml_ctx, zeros_3, zeros_1, 3); + } + ggml_tensor* out = nullptr; + if (false) { + std::vector to_concat = {}; + for (int i = 0; i < num_chunks; i++) { + auto chunk = ggml_view_4d(ctx->ggml_ctx, x, + x->ne[0], x->ne[1], x->ne[2], 17, + x->nb[1], x->nb[2], x->nb[3], + i * 17 * x->nb[3]); + + auto chunk_padded = ggml_concat(ctx->ggml_ctx, zeros_3, chunk, 3); + + to_concat.push_back(chunk_padded); + } + ggml_tensor* x_in = ggml_ext_vec_concat(ctx->ggml_ctx, to_concat, 3); + out = encoder->forward(ctx, x_in); + } else { + std::vector to_concat = {}; + for (int i = 0; i < num_chunks; i++) { + auto chunk = ggml_view_4d(ctx->ggml_ctx, x, + x->ne[0], x->ne[1], x->ne[2], 17, + x->nb[1], x->nb[2], x->nb[3], + i * 17 * x->nb[3]); + + auto chunk_padded = ggml_concat(ctx->ggml_ctx, zeros_3, chunk, 3); + + auto chunk_out = encoder->forward(ctx, chunk_padded); + // auto chunk_out = encoder->forward_seq(ctx, chunk_padded); // ~same vram usage, and straight-up slower. it's already sequential enough + + to_concat.push_back(chunk_out); + } + out = ggml_ext_vec_concat(ctx->ggml_ctx, to_concat, 3); + } + + // Return x[:, :-3] - drop the last 3 elements in the T dimension + int64_t out_T = out->ne[3]; + out = ggml_view_4d(ctx->ggml_ctx, out, + out->ne[0], out->ne[1], out->ne[2], out_T - 3, + out->nb[1], out->nb[2], out->nb[3], 0); + + return ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, out, 0, 1, 3, 2)); + } + + ggml_tensor* encode(GGMLRunnerContext* ctx, ggml_tensor* x) { + if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version) || (sd_version_is_minimax_h3(version) && x->ne[3] > 1)) { // (W, H, T, C) -> (W, H, C, T) x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 1, 3, 2)); } + if (sd_version_is_minimax_h3(version)) { + return encode_h3(ctx, x); + } + + auto encoder = std::dynamic_pointer_cast(blocks["encoder"]); + int64_t num_frames = x->ne[3]; if (num_frames % encoder->t_downscale) { // pad to multiple of encoder->t_downscale at the end diff --git a/otherarch/sdcpp/src/runtime/denoiser.hpp b/otherarch/sdcpp/src/runtime/denoiser.hpp index f58f42b55fcd..e4d9af02065a 100644 --- a/otherarch/sdcpp/src/runtime/denoiser.hpp +++ b/otherarch/sdcpp/src/runtime/denoiser.hpp @@ -2582,27 +2582,49 @@ static sd::Tensor sample_lms(denoise_cb_t model, sd::Tensor x, const std::vector& sigmas, const SamplerExtraArgs& extra_sample_args) { - // Linear Multi-Step from https://github.com/crowsonkb/k-diffusion - + // Linear Multi-Step from https://github.com/crowsonkb/k-diffusion, + // modified with "history shift" value, which seemingly needs less steps int divisions = 1000; + int max_order = 4; + int shift = 1; // 4, 0 - original; 4, 1 - PR #1843; 3, 1 - smoother image for (const auto& [key, value] : extra_sample_args) { int parsed = 0; + if (key == "lms_max_order") { + if (!parse_strict_int(value, parsed)) { + LOG_WARN("ignoring invalid lms extra sample arg '%s=%s'", key.c_str(), value.c_str()); + continue; + } + max_order = std::max(1, parsed); + // smaller values make the result softer, closer to Euler + // higher values need more steps + // values above 12 can produce NaNs, depending on steps and scheduler + } + if (key == "lms_shift") { + if (!parse_strict_int(value, parsed)) { + LOG_WARN("ignoring invalid lms extra sample arg '%s=%s'", key.c_str(), value.c_str()); + continue; + } + shift = std::max(0, parsed); + // for a low number of steps, the value 1 works best + } if (key == "lms_divisions") { if (!parse_strict_int(value, parsed)) { LOG_WARN("ignoring invalid lms extra sample arg '%s=%s'", key.c_str(), value.c_str()); continue; } divisions = parsed; // std::max(1, parsed); - // values above 35M produce noise, can be fixed by double precision // values < 1 always produce noise + // values above 30M require double precision in the integrator + // (they are needless and just slow the integration down, but + // with single precision they softly produce noise + // near the 35M, it can be used for distorted generations) } } - LOG_DEBUG("linear multi-step sampler: integrating using %i division%s", divisions, (divisions == 1) ? "" : "s"); auto linear_multistep_coeff = [=](const int order, const int m, const int j) -> float { if (!divisions) return sigmas[m + 1] - sigmas[m]; // delta / 0 * 0 -#define LMS_PRECISION float // double +#define LMS_PRECISION float // when divisions > 30 millions, the double precision fixes noise const LMS_PRECISION a = sigmas[m], dx = (sigmas[m + 1] - a) / divisions, s = sigmas[m - j]; const LMS_PRECISION b0 = a + 0.5f * dx; // using Riemann middle integral LMS_PRECISION sum = 0.0f; @@ -2622,11 +2644,12 @@ static sd::Tensor sample_lms(denoise_cb_t model, return sum * dx; }; - const int max_order = 4; - float lms_coeff[max_order]; + int steps = static_cast(sigmas.size()) - 1; + max_order = std::min(max_order, steps); // history can not be larger than steps + LOG_DEBUG("linear multi-step sampler: lms_max_order = %i, lms_shift = %i, lms_divisions = %i", max_order, shift, divisions); + std::vector lms_coeff(max_order); std::vector> hist = {}; - int steps = static_cast(sigmas.size()) - 1; for (int i = 0; i < steps; i++) { const float sigma = sigmas[i]; @@ -2637,25 +2660,26 @@ static sd::Tensor sample_lms(denoise_cb_t model, sd::Tensor denoised = std::move(denoised_opt.pred); const int order = std::min(max_order, i + 1); + for (int c = 0; c < order; c++) // computing coefficients lms_coeff[c] = linear_multistep_coeff(order, i, c); sd::Tensor d_cur = (x - denoised) / sigma; - switch (order) { - case 4: // derivative + 3 history points - x += hist[hist.size() - 2] * lms_coeff[3]; - case 3: - x += hist[hist.size() - 1] * lms_coeff[2]; - case 2: - x += hist.back() * lms_coeff[1]; - case 1: - x += d_cur * lms_coeff[0]; - } - - if (hist.size() == static_cast(max_order - 1)) { - hist.erase(hist.begin()); + x += d_cur * lms_coeff[0]; + if (max_order > 1) { // if max_order == 1, the history is not used (order always < 2) + int hist_size_p1 = hist.size() + 1; + if (i) { // history does not exist at 1st step + int hist_max = hist.size() - 1; + for (int c = 2; c <= order; c++) + x += hist[std::min(hist_max, hist_size_p1 - c + shift)] * lms_coeff[c - 1]; + // max_order == 4 => hist[] index = 2, 1, 0 + // shift == 1 => hist[] index = 2, 2, 1 + } + if (hist_size_p1 == max_order) { + hist.erase(hist.begin()); + } + hist.push_back(std::move(d_cur)); } - hist.push_back(std::move(d_cur)); } return x; } diff --git a/otherarch/sdcpp/src/stable-diffusion.cpp b/otherarch/sdcpp/src/stable-diffusion.cpp index eff54afe0b54..a10f89989f3c 100644 --- a/otherarch/sdcpp/src/stable-diffusion.cpp +++ b/otherarch/sdcpp/src/stable-diffusion.cpp @@ -152,6 +152,9 @@ const char* sampling_methods_str[] = { "LMS", }; +static_assert(SAMPLE_METHOD_COUNT == sizeof(sampling_methods_str) / sizeof(sampling_methods_str[0]), + "\nnumber of elements in sampling_methods_str[] != SAMPLE_METHOD_COUNT"); + /*================================================== Helper Functions ================================================*/ static bool sd_version_supports_ref_latent_img_cfg(SDVersion version) { @@ -1262,11 +1265,11 @@ class StableDiffusionGGML { tae_preview_only = false; use_tae = true; } - if (sd_version_is_minimax_h3(version) && use_tae) { - LOG_WARN("MiniMax-H3 does not have a compatible TAE; ignoring --taesd"); - tae_preview_only = false; - use_tae = false; - } + // if (sd_version_is_minimax_h3(version) && use_tae) { + // LOG_WARN("MiniMax-H3 does not have a compatible TAE; ignoring --taesd"); + // tae_preview_only = false; + // use_tae = false; + // } auto& tensor_storage_map = model_loader.get_tensor_storage_map(); @@ -1641,7 +1644,7 @@ class StableDiffusionGGML { } auto create_tae = [&](bool decode_only) -> std::shared_ptr { - if (sd_version_uses_wan_vae(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) { + if (sd_version_uses_wan_vae(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version) || sd_version_is_minimax_h3(version)) { return std::make_shared(backend_for(SDBackendModule::VAE), tensor_storage_map, "decoder", @@ -2573,7 +2576,7 @@ class StableDiffusionGGML { return; } } else if (channels == 24) { - if(sd_version_is_minimax_h3(version)){ + if (sd_version_is_minimax_h3(version)) { latent_rgb_proj = minimax_latent_rgb_proj; latent_rgb_bias = minimax_latent_rgb_bias; } else { @@ -3567,6 +3570,9 @@ const char* sample_method_to_str[] = { "lms", }; +static_assert(SAMPLE_METHOD_COUNT == sizeof(sample_method_to_str) / sizeof(sample_method_to_str[0]), + "\nnumber of elements in sample_method_to_str[] != SAMPLE_METHOD_COUNT"); + const char* sd_sample_method_name(enum sample_method_t sample_method) { if (sample_method < SAMPLE_METHOD_COUNT) { return sample_method_to_str[sample_method]; @@ -3602,6 +3608,9 @@ const char* scheduler_to_str[] = { "beta", }; +static_assert(SCHEDULER_COUNT == sizeof(scheduler_to_str) / sizeof(scheduler_to_str[0]), + "\nnumber of elements in scheduler_to_str[] != SCHEDULER_COUNT"); + const char* sd_scheduler_name(enum scheduler_t scheduler) { if (scheduler < SCHEDULER_COUNT) { return scheduler_to_str[scheduler];