#ifndef __SD_CONDITIONING_CONDITIONER_HPP__ #define __SD_CONDITIONING_CONDITIONER_HPP__ #include #include #include #include #include #include "core/tensor_ggml.hpp" #include "core/util.h" #include "model/diffusion/model.hpp" #include "model/te/clip.hpp" #include "model/te/llm.hpp" #include "model/te/t5.hpp" #include "model_loader.h" struct SDCondition { sd::Tensor c_crossattn; sd::Tensor c_vector; sd::Tensor c_concat; sd::Tensor c_t5_ids; sd::Tensor c_t5_weights; sd::Tensor c_input_ids; sd::Tensor c_position_ids; sd::Tensor c_token_types; sd::Tensor c_vinput_mask; std::vector>> c_image_embeds; std::vector> c_ref_images; std::vector> c_ref_audios; std::vector c_reference_blocks; std::vector> extra_c_crossattns; SDCondition() = default; SDCondition(sd::Tensor c_crossattn, sd::Tensor c_vector, sd::Tensor c_concat) : c_crossattn(std::move(c_crossattn)), c_vector(std::move(c_vector)), c_concat(std::move(c_concat)) {} bool empty() const { if (!c_crossattn.empty() || !c_vector.empty() || !c_concat.empty() || !c_t5_ids.empty() || !c_t5_weights.empty() || !c_input_ids.empty() || !c_position_ids.empty() || !c_token_types.empty() || !c_vinput_mask.empty()) { return false; } for (const auto& image_embed : c_image_embeds) { if (!image_embed.second.empty()) { return false; } } for (const auto& tensor : c_ref_images) { if (!tensor.empty()) { return false; } } for (const auto& tensor : c_ref_audios) { if (!tensor.empty()) { return false; } } for (const auto& tensor : extra_c_crossattns) { if (!tensor.empty()) { return false; } } return true; } }; enum class MiniMaxH3PresentationKind { IMAGE, VIDEO, AUDIO, }; struct MiniMaxH3PresentationItem { MiniMaxH3PresentationKind kind = MiniMaxH3PresentationKind::IMAGE; std::vector> frames; std::vector timestamps; }; static inline sd::Tensor apply_token_weights(sd::Tensor hidden_states, const std::vector& weights) { if (hidden_states.empty()) { return hidden_states; } bool all_one = true; for (float weight : weights) { if (weight != 1.0f) { all_one = false; break; } } if (all_one) { return hidden_states; } if (hidden_states.dim() == 1) { hidden_states.unsqueeze_(1); } GGML_ASSERT(static_cast(hidden_states.shape()[1]) == weights.size()); float original_mean = hidden_states.mean(); auto chunk_weights = sd::Tensor::from_vector(weights); chunk_weights.reshape_({1, static_cast(weights.size())}); hidden_states *= chunk_weights; float new_mean = hidden_states.mean(); if (std::isfinite(original_mean) && std::isfinite(new_mean) && new_mean != 0.0f) { hidden_states *= (original_mean / new_mean); } return hidden_states; } struct ConditionerParams { std::string text; int clip_skip = -1; int width = -1; int height = -1; bool zero_out_masked = false; const std::vector>* ref_images = nullptr; // for qwen image edit const std::vector* minimax_h3_references = nullptr; RefImageParams ref_image_params; }; struct Conditioner { virtual ~Conditioner() = default; public: virtual SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) = 0; virtual void get_param_tensors(std::map& tensors) = 0; virtual void get_param_tensor_ops(std::map& tensor_ops) {} virtual void set_max_graph_vram_bytes(size_t max_vram_bytes) {} virtual void set_stream_layers_enabled(bool enabled) {} virtual void set_runtime_backends(const std::vector& backends) {} virtual void set_graph_cut_layer_split_enabled(bool enabled) {} virtual void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) {} virtual void get_layer_split_param_tensors(std::map& tensors) {} virtual void set_flash_attention_enabled(bool enabled) = 0; virtual void set_weight_adapter(const std::shared_ptr& adapter) {} virtual void runner_done() {} }; // ldm.modules.encoders.modules.FrozenCLIPEmbedder // Ref: https://github.com/AUTOMATIC1111/stable-diffusion-webui/blob/cad87bf4e3e0b0a759afa94e933527c3123d59bc/modules/sd_hijack_clip.py#L283 struct FrozenCLIPEmbedderWithCustomWords : public Conditioner { SDVersion version = VERSION_SD1; CLIPTokenizer tokenizer; std::shared_ptr text_model; std::shared_ptr text_model2; std::map embedding_map; int32_t num_custom_embeddings = 0; int32_t num_custom_embeddings_2 = 0; std::vector token_embed_custom; std::map> embedding_pos_map; FrozenCLIPEmbedderWithCustomWords(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map, const std::map& orig_embedding_map, SDVersion version = VERSION_SD1, std::shared_ptr weight_manager = nullptr) : version(version), tokenizer(sd_version_is_sd2(version) ? 0 : 49407) { for (const auto& kv : orig_embedding_map) { std::string name = normalize_embedding_name(kv.first); embedding_map[name] = kv.second; tokenizer.add_special_token(name); } bool force_clip_f32 = !embedding_map.empty(); if (sd_version_is_sd1(version)) { text_model = std::make_shared(backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, force_clip_f32, weight_manager); } else if (sd_version_is_sd2(version)) { text_model = std::make_shared(backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPEN_CLIP_VIT_H_14, true, force_clip_f32, weight_manager); } else if (sd_version_is_sdxl(version)) { text_model = std::make_shared(backend, tensor_storage_map, "cond_stage_model.transformer.text_model", OPENAI_CLIP_VIT_L_14, false, force_clip_f32, weight_manager); text_model2 = std::make_shared(backend, tensor_storage_map, "cond_stage_model.1.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false, force_clip_f32, weight_manager); } } void get_param_tensors(std::map& tensors) override { text_model->get_param_tensors(tensors, "cond_stage_model.transformer.text_model"); if (sd_version_is_sdxl(version)) { text_model2->get_param_tensors(tensors, "cond_stage_model.1.transformer.text_model"); } } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { text_model->set_max_graph_vram_bytes(max_vram_bytes); if (sd_version_is_sdxl(version)) { text_model2->set_max_graph_vram_bytes(max_vram_bytes); } } void set_stream_layers_enabled(bool enabled) override { text_model->set_stream_layers_enabled(enabled); if (sd_version_is_sdxl(version)) { text_model2->set_stream_layers_enabled(enabled); } } void set_runtime_backends(const std::vector& backends) override { text_model->set_runtime_backends(backends); if (sd_version_is_sdxl(version)) { text_model2->set_runtime_backends(backends); } } void set_graph_cut_layer_split_enabled(bool enabled) override { text_model->set_graph_cut_layer_split_enabled(enabled); if (sd_version_is_sdxl(version)) { text_model2->set_graph_cut_layer_split_enabled(enabled); } } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { text_model->set_graph_cut_layer_split_backend_vram_limits(limits); if (sd_version_is_sdxl(version)) { text_model2->set_graph_cut_layer_split_backend_vram_limits(limits); } } void set_flash_attention_enabled(bool enabled) override { text_model->set_flash_attention_enabled(enabled); if (sd_version_is_sdxl(version)) { text_model2->set_flash_attention_enabled(enabled); } } void set_weight_adapter(const std::shared_ptr& adapter) override { text_model->set_weight_adapter(adapter); if (sd_version_is_sdxl(version)) { text_model2->set_weight_adapter(adapter); } } void runner_done() override { text_model->runner_done(); if (sd_version_is_sdxl(version)) { text_model2->runner_done(); } } bool load_embedding(std::string embd_name, std::string embd_path, std::vector& bpe_tokens) { ModelLoader model_loader; if (!model_loader.init_from_file_and_convert_name(embd_path)) { LOG_ERROR("embedding '%s' failed", embd_name.c_str()); return false; } auto iter = embedding_pos_map.find(embd_name); if (iter != embedding_pos_map.end()) { LOG_DEBUG("embedding already read in: %s", embd_name.c_str()); for (int i = iter->second.first; i < iter->second.second; i++) { bpe_tokens.push_back(text_model->model.vocab_size + i); } return true; } ggml_init_params params; params.mem_size = 100 * 1024 * 1024; // max for custom embeddings 100 MB params.mem_buffer = nullptr; params.no_alloc = false; ggml_context* embd_ctx = ggml_init(params); ggml_tensor* embd = nullptr; ggml_tensor* embd2 = nullptr; auto on_load = [&](const TensorStorage& tensor_storage, ggml_tensor** dst_tensor) { if (tensor_storage.ne[0] != text_model->model.hidden_size) { if (text_model2) { if (tensor_storage.ne[0] == text_model2->model.hidden_size) { embd2 = ggml_new_tensor_2d(embd_ctx, tensor_storage.type, text_model2->model.hidden_size, tensor_storage.n_dims > 1 ? tensor_storage.ne[1] : 1); *dst_tensor = embd2; } else { LOG_DEBUG("embedding wrong hidden size, got %i, expected %i or %i", tensor_storage.ne[0], text_model->model.hidden_size, text_model2->model.hidden_size); return false; } } else { LOG_DEBUG("embedding wrong hidden size, got %i, expected %i", tensor_storage.ne[0], text_model->model.hidden_size); return false; } } else { embd = ggml_new_tensor_2d(embd_ctx, tensor_storage.type, text_model->model.hidden_size, tensor_storage.n_dims > 1 ? tensor_storage.ne[1] : 1); *dst_tensor = embd; } return true; }; model_loader.set_n_threads(1); model_loader.load_tensors(on_load); int pos_start = num_custom_embeddings; if (embd) { int64_t hidden_size = text_model->model.hidden_size; token_embed_custom.resize(token_embed_custom.size() + ggml_nbytes(embd)); memcpy((void*)(token_embed_custom.data() + num_custom_embeddings * hidden_size * ggml_type_size(embd->type)), embd->data, ggml_nbytes(embd)); for (int i = 0; i < embd->ne[1]; i++) { bpe_tokens.push_back(text_model->model.vocab_size + num_custom_embeddings); // LOG_DEBUG("new custom token: %i", text_model.vocab_size + num_custom_embeddings); num_custom_embeddings++; } LOG_DEBUG("embedding '%s' applied, custom embeddings: %i", embd_name.c_str(), num_custom_embeddings); } if (embd2) { int64_t hidden_size = text_model2->model.hidden_size; token_embed_custom.resize(token_embed_custom.size() + ggml_nbytes(embd2)); memcpy((void*)(token_embed_custom.data() + num_custom_embeddings_2 * hidden_size * ggml_type_size(embd2->type)), embd2->data, ggml_nbytes(embd2)); for (int i = 0; i < embd2->ne[1]; i++) { bpe_tokens.push_back(text_model2->model.vocab_size + num_custom_embeddings_2); // LOG_DEBUG("new custom token: %i", text_model.vocab_size + num_custom_embeddings); num_custom_embeddings_2++; } LOG_DEBUG("embedding '%s' applied, custom embeddings: %i (text model 2)", embd_name.c_str(), num_custom_embeddings_2); } int pos_end = num_custom_embeddings; if (pos_end == pos_start) { return false; } embedding_pos_map[embd_name] = std::pair{pos_start, pos_end}; return true; } static std::string normalize_embedding_name(std::string name) { std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); return name; } bool append_embedding_tokens(std::string str, std::vector& bpe_tokens) { std::string name = normalize_embedding_name(std::move(str)); auto iter = embedding_map.find(name); if (iter == embedding_map.end()) { return false; } return load_embedding(name, iter->second, bpe_tokens); } std::vector convert_token_to_id(std::string text) { auto on_new_token_cb = [&](std::string& str, std::vector& bpe_tokens) -> bool { return append_embedding_tokens(str, bpe_tokens); }; std::vector curr_tokens = tokenizer.encode(text, on_new_token_cb); return curr_tokens; } std::string decode(const std::vector& tokens) { return tokenizer.decode(tokens); } std::pair, std::vector> tokenize(std::string text, size_t min_length = 0, size_t max_length = 0, bool allow_overflow_expand = true) { auto parsed_attention = parse_prompt_attention(text); { std::stringstream ss; ss << "["; for (const auto& item : parsed_attention) { ss << "['" << item.first << "', " << item.second << "], "; } ss << "]"; LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str()); } auto on_new_token_cb = [&](std::string& str, std::vector& bpe_tokens) -> bool { return append_embedding_tokens(str, bpe_tokens); }; std::vector tokens; std::vector weights; for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; float curr_weight = item.second; if (curr_text == "BREAK" && curr_weight == -1.0f) { // Pad token array up to chunk size at this point. // TODO: This is a hardcoded chunk_len, like in stable-diffusion.cpp, make it a parameter for the future? // Also, this is 75 instead of 77 to leave room for BOS and EOS tokens. size_t current_size = tokens.size(); size_t padding_size = (75 - (current_size % 75)) % 75; // Ensure no negative padding if (padding_size > 0) { LOG_DEBUG("BREAK token encountered, padding current chunk by %zu tokens.", padding_size); tokens.insert(tokens.end(), padding_size, tokenizer.EOS_TOKEN_ID); weights.insert(weights.end(), padding_size, 1.0f); } continue; // Skip to the next item after handling BREAK } std::vector curr_tokens = tokenizer.encode(curr_text, on_new_token_cb); tokens.insert(tokens.end(), curr_tokens.begin(), curr_tokens.end()); weights.insert(weights.end(), curr_tokens.size(), curr_weight); } tokenizer.pad_tokens(tokens, &weights, nullptr, min_length, max_length, allow_overflow_expand); // for (int i = 0; i < tokens.size(); i++) { // std::cout << tokens[i] << ":" << weights[i] << ", "; // } // std::cout << std::endl; return {tokens, weights}; } SDCondition get_learned_condition_common(int n_threads, std::vector& tokens, std::vector& weights, int clip_skip, int width, int height, bool zero_out_masked = false) { int64_t t0 = ggml_time_ms(); sd::Tensor hidden_states; // [n_token, hidden_size] or [n_token, hidden_size + hidden_size2] sd::Tensor pooled; if (clip_skip <= 0) { clip_skip = (sd_version_is_sd2(version) || sd_version_is_sdxl(version)) ? 2 : 1; } size_t chunk_len = 77; size_t chunk_count = tokens.size() / chunk_len; for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) { std::vector chunk_tokens(tokens.begin() + chunk_idx * chunk_len, tokens.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_weights(weights.begin() + chunk_idx * chunk_len, weights.begin() + (chunk_idx + 1) * chunk_len); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); sd::Tensor input_ids2; size_t max_token_idx = 0; if (sd_version_is_sdxl(version)) { auto it = std::find(chunk_tokens.begin(), chunk_tokens.end(), tokenizer.EOS_TOKEN_ID); if (it != chunk_tokens.end()) { std::fill(std::next(it), chunk_tokens.end(), 0); } max_token_idx = std::min(std::distance(chunk_tokens.begin(), it), chunk_tokens.size() - 1); input_ids2 = sd::Tensor({static_cast(chunk_tokens.size())}, chunk_tokens); // for (int i = 0; i < chunk_tokens.size(); i++) { // printf("%d ", chunk_tokens[i]); // } // printf("\n"); } { auto chunk_hidden_states = text_model->compute(n_threads, input_ids, num_custom_embeddings, token_embed_custom.data(), max_token_idx, false, clip_skip, false, true, true); GGML_ASSERT(!chunk_hidden_states.empty()); if (sd_version_is_sdxl(version)) { auto chunk_hidden_states2 = text_model2->compute(n_threads, input_ids2, num_custom_embeddings, token_embed_custom.data(), max_token_idx, false, clip_skip, false, true, true); GGML_ASSERT(!chunk_hidden_states2.empty()); chunk_hidden_states = sd::ops::concat(chunk_hidden_states, chunk_hidden_states2, 0); if (chunk_idx == 0) { pooled = text_model2->compute(n_threads, input_ids2, num_custom_embeddings, token_embed_custom.data(), max_token_idx, true, clip_skip, false, true, true); GGML_ASSERT(!pooled.empty()); } } int64_t t1 = ggml_time_ms(); LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0); chunk_hidden_states = apply_token_weights(std::move(chunk_hidden_states), chunk_weights); if (zero_out_masked) { chunk_hidden_states.fill_(0.0f); } if (!hidden_states.empty()) { hidden_states = sd::ops::concat(hidden_states, chunk_hidden_states, 1); } else { hidden_states = std::move(chunk_hidden_states); } } } sd::Tensor vec; if (sd_version_is_sdxl(version)) { int out_dim = 256; int adm_in_channels = 2816; GGML_ASSERT(!pooled.empty()); vec = sd::Tensor({adm_in_channels}); vec.fill_(0.0f); size_t offset = 0; std::copy(pooled.values().begin(), pooled.values().end(), vec.values().begin()); offset += pooled.values().size(); auto append_embedding = [&](const std::vector& timesteps) { sd::Tensor embedding; set_timestep_embedding(timesteps, &embedding, out_dim); std::copy(embedding.values().begin(), embedding.values().end(), vec.values().begin() + static_cast(offset)); offset += embedding.values().size(); }; append_embedding({static_cast(height), static_cast(width)}); append_embedding({0.0f, 0.0f}); append_embedding({static_cast(height), static_cast(width)}); GGML_ASSERT(offset == vec.values().size()); } SDCondition result; if (!hidden_states.empty()) { result.c_crossattn = std::move(hidden_states); } if (!vec.empty()) { result.c_vector = std::move(vec); } return result; } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { auto tokens_and_weights = tokenize(conditioner_params.text, text_model->model.n_token, text_model->model.n_token, true); std::vector& tokens = tokens_and_weights.first; std::vector& weights = tokens_and_weights.second; return get_learned_condition_common(n_threads, tokens, weights, conditioner_params.clip_skip, conditioner_params.width, conditioner_params.height, conditioner_params.zero_out_masked); } }; struct FrozenCLIPVisionEmbedder : public GGMLRunner { CLIPVisionModelProjection vision_model; std::string weight_prefix = "cond_stage_model.transformer"; FrozenCLIPVisionEmbedder(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, std::shared_ptr weight_manager = nullptr) : GGMLRunner(backend, weight_manager) { bool proj_in = false; for (const auto& [name, tensor_storage] : tensor_storage_map) { if (!starts_with(name, weight_prefix)) { continue; } if (contains(name, "self_attn.in_proj")) { proj_in = true; break; } } vision_model = CLIPVisionModelProjection(OPEN_CLIP_VIT_H_14, false, proj_in); vision_model.init(params_ctx, tensor_storage_map, weight_prefix); } std::string get_desc() override { return "clip_vision"; } void get_param_tensors(std::map& tensors) { vision_model.get_param_tensors(tensors, weight_prefix); } ggml_cgraph* build_graph(const sd::Tensor& pixel_values_tensor, bool return_pooled, int clip_skip) { ggml_cgraph* gf = ggml_new_graph(compute_ctx); ggml_tensor* pixel_values = make_input(pixel_values_tensor); auto runner_ctx = get_context(); ggml_tensor* hidden_states = vision_model.forward(&runner_ctx, pixel_values, return_pooled, clip_skip); ggml_build_forward_expand(gf, hidden_states); return gf; } sd::Tensor compute(const int n_threads, const sd::Tensor& pixel_values, bool return_pooled, int clip_skip) { auto get_graph = [&]() -> ggml_cgraph* { return build_graph(pixel_values, return_pooled, clip_skip); }; return take_or_empty(GGMLRunner::compute(get_graph, n_threads, true, true, true)); } }; struct SD3CLIPEmbedder : public Conditioner { CLIPTokenizer clip_l_tokenizer; CLIPTokenizer clip_g_tokenizer; T5UniGramTokenizer t5_tokenizer; std::shared_ptr clip_l; std::shared_ptr clip_g; std::shared_ptr t5; SD3CLIPEmbedder(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, std::shared_ptr weight_manager = nullptr) : clip_g_tokenizer(0) { bool use_clip_l = false; bool use_clip_g = false; bool use_t5 = false; for (auto pair : tensor_storage_map) { if (pair.first.find("text_encoders.clip_l") != std::string::npos) { use_clip_l = true; } else if (pair.first.find("text_encoders.clip_g") != std::string::npos) { use_clip_g = true; } else if (pair.first.find("text_encoders.t5xxl") != std::string::npos) { use_t5 = true; } } if (!use_clip_l && !use_clip_g && !use_t5) { LOG_WARN("IMPORTANT NOTICE: No text encoders provided, cannot process prompts!"); return; } if (use_clip_l) { clip_l = std::make_shared(backend, tensor_storage_map, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, false, false, weight_manager); } if (use_clip_g) { clip_g = std::make_shared(backend, tensor_storage_map, "text_encoders.clip_g.transformer.text_model", OPEN_CLIP_VIT_BIGG_14, false, false, weight_manager); } if (use_t5) { t5 = std::make_shared(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", false, weight_manager); } } void get_param_tensors(std::map& tensors) override { if (clip_l) { clip_l->get_param_tensors(tensors, "text_encoders.clip_l.transformer.text_model"); } if (clip_g) { clip_g->get_param_tensors(tensors, "text_encoders.clip_g.transformer.text_model"); } if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { if (clip_l) { clip_l->set_max_graph_vram_bytes(max_vram_bytes); } if (clip_g) { clip_g->set_max_graph_vram_bytes(max_vram_bytes); } if (t5) { t5->set_max_graph_vram_bytes(max_vram_bytes); } } void set_stream_layers_enabled(bool enabled) override { if (clip_l) { clip_l->set_stream_layers_enabled(enabled); } if (clip_g) { clip_g->set_stream_layers_enabled(enabled); } if (t5) { t5->set_stream_layers_enabled(enabled); } } void set_runtime_backends(const std::vector& backends) override { if (clip_l) { clip_l->set_runtime_backends(backends); } if (clip_g) { clip_g->set_runtime_backends(backends); } if (t5) { t5->set_runtime_backends(backends); } } void set_graph_cut_layer_split_enabled(bool enabled) override { if (clip_l) { clip_l->set_graph_cut_layer_split_enabled(enabled); } if (clip_g) { clip_g->set_graph_cut_layer_split_enabled(enabled); } if (t5) { t5->set_graph_cut_layer_split_enabled(enabled); } } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { if (clip_l) { clip_l->set_graph_cut_layer_split_backend_vram_limits(limits); } if (clip_g) { clip_g->set_graph_cut_layer_split_backend_vram_limits(limits); } if (t5) { t5->set_graph_cut_layer_split_backend_vram_limits(limits); } } void get_layer_split_param_tensors(std::map& tensors) override { if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_flash_attention_enabled(bool enabled) override { if (clip_l) { clip_l->set_flash_attention_enabled(enabled); } if (clip_g) { clip_g->set_flash_attention_enabled(enabled); } if (t5) { t5->set_flash_attention_enabled(enabled); } } void set_weight_adapter(const std::shared_ptr& adapter) override { if (clip_l) { clip_l->set_weight_adapter(adapter); } if (clip_g) { clip_g->set_weight_adapter(adapter); } if (t5) { t5->set_weight_adapter(adapter); } } void runner_done() override { if (clip_l) { clip_l->runner_done(); } if (clip_g) { clip_g->runner_done(); } if (t5) { t5->runner_done(); } } std::vector, std::vector>> tokenize(std::string text, size_t min_length = 0, size_t max_length = 0, bool allow_overflow_expand = true) { auto parsed_attention = parse_prompt_attention(text); { std::stringstream ss; ss << "["; for (const auto& item : parsed_attention) { ss << "['" << item.first << "', " << item.second << "], "; } ss << "]"; LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str()); } auto on_new_token_cb = [&](std::string& str, std::vector& bpe_tokens) -> bool { return false; }; std::vector clip_l_tokens; std::vector clip_l_weights; std::vector clip_g_tokens; std::vector clip_g_weights; std::vector t5_tokens; std::vector t5_weights; for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; float curr_weight = item.second; if (clip_l) { std::vector curr_tokens = clip_l_tokenizer.encode(curr_text, on_new_token_cb); clip_l_tokens.insert(clip_l_tokens.end(), curr_tokens.begin(), curr_tokens.end()); clip_l_weights.insert(clip_l_weights.end(), curr_tokens.size(), curr_weight); } if (clip_g) { std::vector curr_tokens = clip_g_tokenizer.encode(curr_text, on_new_token_cb); clip_g_tokens.insert(clip_g_tokens.end(), curr_tokens.begin(), curr_tokens.end()); clip_g_weights.insert(clip_g_weights.end(), curr_tokens.size(), curr_weight); } if (t5) { std::vector curr_tokens = t5_tokenizer.encode(curr_text); t5_tokens.insert(t5_tokens.end(), curr_tokens.begin(), curr_tokens.end()); t5_weights.insert(t5_weights.end(), curr_tokens.size(), curr_weight); } } if (clip_l) { clip_l_tokenizer.pad_tokens(clip_l_tokens, &clip_l_weights, nullptr, min_length, max_length, allow_overflow_expand); } if (clip_g) { clip_g_tokenizer.pad_tokens(clip_g_tokens, &clip_g_weights, nullptr, min_length, max_length, allow_overflow_expand); } if (t5) { t5_tokenizer.pad_tokens(t5_tokens, &t5_weights, nullptr, min_length, max_length, true); } // for (int i = 0; i < clip_l_tokens.size(); i++) { // std::cout << clip_l_tokens[i] << ":" << clip_l_weights[i] << ", "; // } // std::cout << std::endl; // for (int i = 0; i < clip_g_tokens.size(); i++) { // std::cout << clip_g_tokens[i] << ":" << clip_g_weights[i] << ", "; // } // std::cout << std::endl; // for (int i = 0; i < t5_tokens.size(); i++) { // std::cout << t5_tokens[i] << ":" << t5_weights[i] << ", "; // } // std::cout << std::endl; return {{clip_l_tokens, clip_l_weights}, {clip_g_tokens, clip_g_weights}, {t5_tokens, t5_weights}}; } SDCondition get_learned_condition_common(int n_threads, std::vector, std::vector>> token_and_weights, int clip_skip, bool zero_out_masked = false) { auto& clip_l_tokens = token_and_weights[0].first; auto& clip_l_weights = token_and_weights[0].second; auto& clip_g_tokens = token_and_weights[1].first; auto& clip_g_weights = token_and_weights[1].second; auto& t5_tokens = token_and_weights[2].first; auto& t5_weights = token_and_weights[2].second; if (clip_skip <= 0) { clip_skip = 2; } size_t chunk_len = 77; int64_t t0 = ggml_time_ms(); sd::Tensor hidden_states; sd::Tensor pooled; size_t chunk_count = std::max(std::max(clip_l_tokens.size(), clip_g_tokens.size()), t5_tokens.size()) / chunk_len; for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) { // clip_l sd::Tensor chunk_hidden_states_l; sd::Tensor pooled_l; if (clip_l) { std::vector chunk_tokens(clip_l_tokens.begin() + chunk_idx * chunk_len, clip_l_tokens.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_weights(clip_l_weights.begin() + chunk_idx * chunk_len, clip_l_weights.begin() + (chunk_idx + 1) * chunk_len); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); size_t max_token_idx = 0; chunk_hidden_states_l = clip_l->compute(n_threads, input_ids, 0, nullptr, max_token_idx, false, clip_skip, false, true, true); GGML_ASSERT(!chunk_hidden_states_l.empty()); chunk_hidden_states_l = ::apply_token_weights(std::move(chunk_hidden_states_l), chunk_weights); if (chunk_idx == 0) { auto it = std::find(chunk_tokens.begin(), chunk_tokens.end(), clip_l_tokenizer.EOS_TOKEN_ID); max_token_idx = std::min(std::distance(chunk_tokens.begin(), it), chunk_tokens.size() - 1); pooled_l = clip_l->compute(n_threads, input_ids, 0, nullptr, max_token_idx, true, clip_skip, false, true, true); GGML_ASSERT(!pooled_l.empty()); } } else { chunk_hidden_states_l = sd::Tensor::zeros({768, static_cast(chunk_len), 1}); if (chunk_idx == 0) { pooled_l = sd::Tensor::zeros({768, 1}); } } // clip_g sd::Tensor chunk_hidden_states_g; sd::Tensor pooled_g; if (clip_g) { std::vector chunk_tokens(clip_g_tokens.begin() + chunk_idx * chunk_len, clip_g_tokens.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_weights(clip_g_weights.begin() + chunk_idx * chunk_len, clip_g_weights.begin() + (chunk_idx + 1) * chunk_len); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); size_t max_token_idx = 0; chunk_hidden_states_g = clip_g->compute(n_threads, input_ids, 0, nullptr, max_token_idx, false, clip_skip, false, true, true); GGML_ASSERT(!chunk_hidden_states_g.empty()); chunk_hidden_states_g = ::apply_token_weights(std::move(chunk_hidden_states_g), chunk_weights); if (chunk_idx == 0) { auto it = std::find(chunk_tokens.begin(), chunk_tokens.end(), clip_g_tokenizer.EOS_TOKEN_ID); max_token_idx = std::min(std::distance(chunk_tokens.begin(), it), chunk_tokens.size() - 1); pooled_g = clip_g->compute(n_threads, input_ids, 0, nullptr, max_token_idx, true, clip_skip, false, true, true); GGML_ASSERT(!pooled_g.empty()); } } else { chunk_hidden_states_g = sd::Tensor::zeros({1280, static_cast(chunk_len), 1}); if (chunk_idx == 0) { pooled_g = sd::Tensor::zeros({1280, 1}); } } // t5 sd::Tensor chunk_hidden_states_t5; if (t5) { std::vector chunk_tokens(t5_tokens.begin() + chunk_idx * chunk_len, t5_tokens.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_weights(t5_weights.begin() + chunk_idx * chunk_len, t5_weights.begin() + (chunk_idx + 1) * chunk_len); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); chunk_hidden_states_t5 = t5->compute(n_threads, input_ids, sd::Tensor(), false, true, true); GGML_ASSERT(!chunk_hidden_states_t5.empty()); chunk_hidden_states_t5 = ::apply_token_weights(std::move(chunk_hidden_states_t5), chunk_weights); } else { chunk_hidden_states_t5 = sd::Tensor::zeros({4096, static_cast(chunk_len), 1}); } sd::Tensor chunk_hidden_states_lg = sd::ops::concat(chunk_hidden_states_l, chunk_hidden_states_g, 0); if (chunk_hidden_states_lg.shape()[0] < 4096) { auto pad_shape = chunk_hidden_states_lg.shape(); pad_shape[0] = 4096 - chunk_hidden_states_lg.shape()[0]; chunk_hidden_states_lg = sd::ops::concat(chunk_hidden_states_lg, sd::Tensor::zeros(pad_shape), 0); } sd::Tensor chunk_hidden_states = sd::ops::concat(chunk_hidden_states_lg, chunk_hidden_states_t5, 1); // [n_token*2, 4096] if (chunk_idx == 0) { pooled = sd::ops::concat(pooled_l, pooled_g, 0); // [768 + 1280] } int64_t t1 = ggml_time_ms(); LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0); if (zero_out_masked) { chunk_hidden_states.fill_(0.0f); } if (!hidden_states.empty()) { hidden_states = sd::ops::concat(hidden_states, chunk_hidden_states, 1); } else { hidden_states = std::move(chunk_hidden_states); } } SDCondition result; result.c_crossattn = std::move(hidden_states); result.c_vector = std::move(pooled); return result; } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { auto tokens_and_weights = tokenize(conditioner_params.text, 77, 77, true); return get_learned_condition_common(n_threads, tokens_and_weights, conditioner_params.clip_skip, conditioner_params.zero_out_masked); } }; struct FluxCLIPEmbedder : public Conditioner { CLIPTokenizer clip_l_tokenizer; T5UniGramTokenizer t5_tokenizer; std::shared_ptr clip_l; std::shared_ptr t5; size_t chunk_len = 256; FluxCLIPEmbedder(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, std::shared_ptr weight_manager = nullptr) { bool use_clip_l = false; bool use_t5 = false; for (auto pair : tensor_storage_map) { if (pair.first.find("text_encoders.clip_l") != std::string::npos) { use_clip_l = true; } else if (pair.first.find("text_encoders.t5xxl") != std::string::npos) { use_t5 = true; } } if (!use_clip_l && !use_t5) { LOG_WARN("IMPORTANT NOTICE: No text encoders provided, cannot process prompts!"); return; } if (use_clip_l) { clip_l = std::make_shared(backend, tensor_storage_map, "text_encoders.clip_l.transformer.text_model", OPENAI_CLIP_VIT_L_14, true, false, weight_manager); } else { LOG_WARN("clip_l text encoder not found! Prompt adherence might be degraded."); } if (use_t5) { t5 = std::make_shared(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", false, weight_manager); } else { LOG_WARN("t5xxl text encoder not found! Prompt adherence might be degraded."); } } void get_param_tensors(std::map& tensors) override { if (clip_l) { clip_l->get_param_tensors(tensors, "text_encoders.clip_l.transformer.text_model"); } if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { if (clip_l) { clip_l->set_max_graph_vram_bytes(max_vram_bytes); } if (t5) { t5->set_max_graph_vram_bytes(max_vram_bytes); } } void set_stream_layers_enabled(bool enabled) override { if (clip_l) { clip_l->set_stream_layers_enabled(enabled); } if (t5) { t5->set_stream_layers_enabled(enabled); } } void set_runtime_backends(const std::vector& backends) override { if (clip_l) { clip_l->set_runtime_backends(backends); } if (t5) { t5->set_runtime_backends(backends); } } void set_graph_cut_layer_split_enabled(bool enabled) override { if (clip_l) { clip_l->set_graph_cut_layer_split_enabled(enabled); } if (t5) { t5->set_graph_cut_layer_split_enabled(enabled); } } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { if (clip_l) { clip_l->set_graph_cut_layer_split_backend_vram_limits(limits); } if (t5) { t5->set_graph_cut_layer_split_backend_vram_limits(limits); } } void get_layer_split_param_tensors(std::map& tensors) override { if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_flash_attention_enabled(bool enabled) override { if (clip_l) { clip_l->set_flash_attention_enabled(enabled); } if (t5) { t5->set_flash_attention_enabled(enabled); } } void set_weight_adapter(const std::shared_ptr& adapter) override { if (clip_l) { clip_l->set_weight_adapter(adapter); } if (t5) { t5->set_weight_adapter(adapter); } } void runner_done() override { if (clip_l) { clip_l->runner_done(); } if (t5) { t5->runner_done(); } } std::vector, std::vector>> tokenize(std::string text, size_t min_length = 0, size_t max_length = 0) { auto parsed_attention = parse_prompt_attention(text); { std::stringstream ss; ss << "["; for (const auto& item : parsed_attention) { ss << "['" << item.first << "', " << item.second << "], "; } ss << "]"; LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str()); } auto on_new_token_cb = [&](std::string& str, std::vector& bpe_tokens) -> bool { return false; }; std::vector clip_l_tokens; std::vector clip_l_weights; std::vector t5_tokens; std::vector t5_weights; for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; float curr_weight = item.second; if (clip_l) { std::vector curr_tokens = clip_l_tokenizer.encode(curr_text, on_new_token_cb); clip_l_tokens.insert(clip_l_tokens.end(), curr_tokens.begin(), curr_tokens.end()); clip_l_weights.insert(clip_l_weights.end(), curr_tokens.size(), curr_weight); } if (t5) { std::vector curr_tokens = t5_tokenizer.encode(curr_text); t5_tokens.insert(t5_tokens.end(), curr_tokens.begin(), curr_tokens.end()); t5_weights.insert(t5_weights.end(), curr_tokens.size(), curr_weight); } } if (clip_l) { clip_l_tokenizer.pad_tokens(clip_l_tokens, &clip_l_weights, nullptr, 77, 77, true); } if (t5) { t5_tokenizer.pad_tokens(t5_tokens, &t5_weights, nullptr, min_length, max_length, true); } // for (int i = 0; i < clip_l_tokens.size(); i++) { // std::cout << clip_l_tokens[i] << ":" << clip_l_weights[i] << ", "; // } // std::cout << std::endl; // for (int i = 0; i < t5_tokens.size(); i++) { // std::cout << t5_tokens[i] << ":" << t5_weights[i] << ", "; // } // std::cout << std::endl; return {{clip_l_tokens, clip_l_weights}, {t5_tokens, t5_weights}}; } SDCondition get_learned_condition_common(int n_threads, std::vector, std::vector>> token_and_weights, int clip_skip, bool zero_out_masked = false) { auto& clip_l_tokens = token_and_weights[0].first; auto& clip_l_weights = token_and_weights[0].second; auto& t5_tokens = token_and_weights[1].first; auto& t5_weights = token_and_weights[1].second; if (clip_skip <= 0) { clip_skip = 2; } int64_t t0 = ggml_time_ms(); sd::Tensor hidden_states; // [N, n_token, 4096] sd::Tensor pooled; // [768,] size_t chunk_count = std::max(clip_l_tokens.size() > 0 ? chunk_len : 0, t5_tokens.size()) / chunk_len; for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) { // clip_l if (chunk_idx == 0) { if (clip_l) { size_t chunk_len_l = 77; std::vector chunk_tokens(clip_l_tokens.begin(), clip_l_tokens.begin() + chunk_len_l); std::vector chunk_weights(clip_l_weights.begin(), clip_l_weights.begin() + chunk_len_l); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); size_t max_token_idx = 0; auto it = std::find(chunk_tokens.begin(), chunk_tokens.end(), clip_l_tokenizer.EOS_TOKEN_ID); max_token_idx = std::min(std::distance(chunk_tokens.begin(), it), chunk_tokens.size() - 1); pooled = clip_l->compute(n_threads, input_ids, 0, nullptr, max_token_idx, true, clip_skip, false, true, true); GGML_ASSERT(!pooled.empty()); } else { pooled = sd::Tensor::zeros({768}); } } // t5 sd::Tensor chunk_hidden_states; if (t5) { std::vector chunk_tokens(t5_tokens.begin() + chunk_idx * chunk_len, t5_tokens.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_weights(t5_weights.begin() + chunk_idx * chunk_len, t5_weights.begin() + (chunk_idx + 1) * chunk_len); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); chunk_hidden_states = t5->compute(n_threads, input_ids, sd::Tensor(), false, true, true); GGML_ASSERT(!chunk_hidden_states.empty()); chunk_hidden_states = ::apply_token_weights(std::move(chunk_hidden_states), chunk_weights); if (zero_out_masked) { chunk_hidden_states.fill_(0.0f); } } else { chunk_hidden_states = sd::Tensor::zeros({4096, static_cast(chunk_len)}); } int64_t t1 = ggml_time_ms(); LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0); if (!hidden_states.empty()) { hidden_states = sd::ops::concat(hidden_states, chunk_hidden_states, 1); } else { hidden_states = std::move(chunk_hidden_states); } } SDCondition result; result.c_crossattn = std::move(hidden_states); result.c_vector = std::move(pooled); return result; } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { auto tokens_and_weights = tokenize(conditioner_params.text, chunk_len, chunk_len); return get_learned_condition_common(n_threads, tokens_and_weights, conditioner_params.clip_skip, conditioner_params.zero_out_masked); } }; struct T5CLIPEmbedder : public Conditioner { T5UniGramTokenizer t5_tokenizer; std::shared_ptr t5; size_t chunk_len = 512; bool use_mask = false; int mask_pad = 0; bool is_umt5 = false; T5CLIPEmbedder(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, bool use_mask = false, int mask_pad = 0, bool is_umt5 = false, std::shared_ptr weight_manager = nullptr, const char* model_args = nullptr) : use_mask(use_mask), mask_pad(mask_pad), t5_tokenizer(is_umt5) { for (const auto& [key, value] : parse_key_value_args(model_args, "model arg")) { if (key == "chroma_use_t5_mask") { bool parsed = false; if (parse_strict_bool(value, parsed)) { this->use_mask = parsed; } else { LOG_WARN("ignoring invalid Chroma T5 model arg '%s=%s'", key.c_str(), value.c_str()); } } else if (key == "chroma_t5_mask_pad") { int parsed = 0; if (parse_strict_int(value, parsed)) { this->mask_pad = parsed; } else { LOG_WARN("ignoring invalid Chroma T5 model arg '%s=%s'", key.c_str(), value.c_str()); } } } bool use_t5 = false; for (auto pair : tensor_storage_map) { if (pair.first.find("text_encoders.t5xxl") != std::string::npos) { use_t5 = true; } } if (!use_t5) { LOG_WARN("IMPORTANT NOTICE: No text encoders provided, cannot process prompts!"); return; } else { t5 = std::make_shared(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", is_umt5, weight_manager); } } void get_param_tensors(std::map& tensors) override { if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { if (t5) { t5->set_max_graph_vram_bytes(max_vram_bytes); } } void set_stream_layers_enabled(bool enabled) override { if (t5) { t5->set_stream_layers_enabled(enabled); } } void set_runtime_backends(const std::vector& backends) override { if (t5) { t5->set_runtime_backends(backends); } } void set_graph_cut_layer_split_enabled(bool enabled) override { if (t5) { t5->set_graph_cut_layer_split_enabled(enabled); } } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { if (t5) { t5->set_graph_cut_layer_split_backend_vram_limits(limits); } } void get_layer_split_param_tensors(std::map& tensors) override { if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_flash_attention_enabled(bool enabled) override { if (t5) { t5->set_flash_attention_enabled(enabled); } } void set_weight_adapter(const std::shared_ptr& adapter) override { if (t5) { t5->set_weight_adapter(adapter); } } void runner_done() override { if (t5) { t5->runner_done(); } } std::tuple, std::vector, std::vector> tokenize(std::string text, size_t min_length = 0, size_t max_length = 0) { auto parsed_attention = parse_prompt_attention(text); { std::stringstream ss; ss << "["; for (const auto& item : parsed_attention) { ss << "['" << item.first << "', " << item.second << "], "; } ss << "]"; LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str()); } auto on_new_token_cb = [&](std::string& str, std::vector& bpe_tokens) -> bool { return false; }; std::vector t5_tokens; std::vector t5_weights; std::vector t5_mask; if (t5) { for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; float curr_weight = item.second; std::vector curr_tokens = t5_tokenizer.encode(curr_text); t5_tokens.insert(t5_tokens.end(), curr_tokens.begin(), curr_tokens.end()); t5_weights.insert(t5_weights.end(), curr_tokens.size(), curr_weight); } t5_tokenizer.pad_tokens(t5_tokens, &t5_weights, &t5_mask, min_length, max_length, true); for (auto& mask_value : t5_mask) { mask_value = mask_value > 0.0f ? 0.0f : -HUGE_VALF; } } return {t5_tokens, t5_weights, t5_mask}; } void modify_mask_to_attend_padding(sd::Tensor* mask, int max_seq_length, int num_extra_padding = 8) { GGML_ASSERT(mask != nullptr); float* mask_data = mask->data(); int num_pad = 0; for (int64_t i = 0; i < max_seq_length; i++) { if (num_pad >= num_extra_padding) { break; } if (std::isinf(mask_data[i])) { mask_data[i] = 0; ++num_pad; } } // LOG_DEBUG("PAD: %d", num_pad); } SDCondition get_learned_condition_common(int n_threads, std::tuple, std::vector, std::vector> token_and_weights, int clip_skip, bool zero_out_masked = false) { if (!t5) { SDCondition result; result.c_crossattn = sd::Tensor::zeros({4096, 256}); result.c_vector = sd::Tensor::full({256}, -HUGE_VALF); return result; } auto& t5_tokens = std::get<0>(token_and_weights); auto& t5_weights = std::get<1>(token_and_weights); auto& t5_attn_mask_vec = std::get<2>(token_and_weights); int64_t t0 = ggml_time_ms(); sd::Tensor t5_attn_mask = sd::Tensor::from_vector(t5_attn_mask_vec); sd::Tensor hidden_states; size_t chunk_count = t5_tokens.size() / chunk_len; for (int chunk_idx = 0; chunk_idx < chunk_count; chunk_idx++) { // t5 std::vector chunk_tokens(t5_tokens.begin() + chunk_idx * chunk_len, t5_tokens.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_weights(t5_weights.begin() + chunk_idx * chunk_len, t5_weights.begin() + (chunk_idx + 1) * chunk_len); std::vector chunk_mask(t5_attn_mask_vec.begin() + chunk_idx * chunk_len, t5_attn_mask_vec.begin() + (chunk_idx + 1) * chunk_len); sd::Tensor input_ids({static_cast(chunk_tokens.size())}, chunk_tokens); sd::Tensor t5_attn_mask_chunk; if (use_mask) { t5_attn_mask_chunk = sd::Tensor({static_cast(chunk_mask.size())}, chunk_mask); } auto chunk_hidden_states = t5->compute(n_threads, input_ids, t5_attn_mask_chunk, false, true, true); GGML_ASSERT(!chunk_hidden_states.empty()); chunk_hidden_states = apply_token_weights(std::move(chunk_hidden_states), chunk_weights); if (zero_out_masked) { auto chunk_mask_tensor = sd::Tensor::from_vector(chunk_mask) .reshape_({1, static_cast(chunk_mask.size())}); chunk_hidden_states.masked_fill_(chunk_mask_tensor < 0.0f, 0.0f); } int64_t t1 = ggml_time_ms(); LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0); if (!hidden_states.empty()) { hidden_states = sd::ops::concat(hidden_states, chunk_hidden_states, 1); } else { hidden_states = std::move(chunk_hidden_states); } } modify_mask_to_attend_padding(&t5_attn_mask, static_cast(t5_attn_mask.numel()), mask_pad); SDCondition result; result.c_crossattn = std::move(hidden_states); result.c_vector = std::move(t5_attn_mask); return result; } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { auto tokens_and_weights = tokenize(conditioner_params.text, chunk_len, chunk_len); return get_learned_condition_common(n_threads, tokens_and_weights, conditioner_params.clip_skip, conditioner_params.zero_out_masked); } }; struct MiniT2IConditioner : public Conditioner { T5UniGramTokenizer tokenizer; std::shared_ptr t5; size_t prompt_length = 256; MiniT2IConditioner(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, std::shared_ptr weight_manager = nullptr) { bool use_t5 = false; for (const auto& pair : tensor_storage_map) { if (pair.first.find("text_encoders.t5xxl") != std::string::npos) { use_t5 = true; break; } } if (!use_t5) { LOG_WARN("IMPORTANT NOTICE: No MiniT2I T5 text encoder provided, cannot process prompts!"); return; } t5 = std::make_shared(backend, tensor_storage_map, "text_encoders.t5xxl.transformer", false, weight_manager); } void get_param_tensors(std::map& tensors) override { if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { if (t5) { t5->set_max_graph_vram_bytes(max_vram_bytes); } } void set_stream_layers_enabled(bool enabled) override { if (t5) { t5->set_stream_layers_enabled(enabled); } } void set_runtime_backends(const std::vector& backends) override { if (t5) { t5->set_runtime_backends(backends); } } void set_graph_cut_layer_split_enabled(bool enabled) override { if (t5) { t5->set_graph_cut_layer_split_enabled(enabled); } } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { if (t5) { t5->set_graph_cut_layer_split_backend_vram_limits(limits); } } void get_layer_split_param_tensors(std::map& tensors) override { if (t5) { t5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_flash_attention_enabled(bool enabled) override { if (t5) { t5->set_flash_attention_enabled(enabled); } } void set_weight_adapter(const std::shared_ptr& adapter) override { if (t5) { t5->set_weight_adapter(adapter); } } void runner_done() override { if (t5) { t5->runner_done(); } } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { SDCondition result; if (!t5) { result.c_crossattn = sd::Tensor::zeros({1024, static_cast(prompt_length)}); result.c_vector = sd::Tensor::zeros({static_cast(prompt_length)}); return result; } std::vector tokens = tokenizer.encode(conditioner_params.text); if (tokens.size() > prompt_length) { tokens.resize(prompt_length); } std::vector mask(tokens.size(), 1.0f); while (tokens.size() < prompt_length) { tokens.push_back(tokenizer.PAD_TOKEN_ID); mask.push_back(0.0f); } sd::Tensor input_ids({static_cast(tokens.size())}, tokens); std::vector t5_mask(mask.size(), 0.0f); for (size_t i = 0; i < mask.size(); ++i) { t5_mask[i] = mask[i] > 0.0f ? 0.0f : -HUGE_VALF; } sd::Tensor hidden_states = t5->compute(n_threads, input_ids, sd::Tensor::from_vector(t5_mask), false, true, true); GGML_ASSERT(!hidden_states.empty()); result.c_crossattn = std::move(hidden_states); result.c_vector = sd::Tensor::from_vector(mask); return result; } }; struct AnimaConditioner : public Conditioner { std::shared_ptr qwen_tokenizer; T5UniGramTokenizer t5_tokenizer; std::shared_ptr llm; AnimaConditioner(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, std::shared_ptr weight_manager = nullptr) { qwen_tokenizer = std::make_shared(); llm = std::make_shared(LLM::LLMArch::QWEN3, backend, tensor_storage_map, "text_encoders.llm", false, weight_manager); } void get_param_tensors(std::map& tensors) override { llm->get_param_tensors(tensors, "text_encoders.llm"); } void get_param_tensor_ops(std::map& tensor_ops) override { llm->get_param_tensor_ops(tensor_ops); } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { llm->set_max_graph_vram_bytes(max_vram_bytes); } void set_stream_layers_enabled(bool enabled) override { llm->set_stream_layers_enabled(enabled); } void set_runtime_backends(const std::vector& backends) override { llm->set_runtime_backends(backends); } void set_graph_cut_layer_split_enabled(bool enabled) override { llm->set_graph_cut_layer_split_enabled(enabled); } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { llm->set_graph_cut_layer_split_backend_vram_limits(limits); } void get_layer_split_param_tensors(std::map& tensors) override { llm->get_param_tensors(tensors, "text_encoders.llm"); } void set_flash_attention_enabled(bool enabled) override { llm->set_flash_attention_enabled(enabled); } void set_weight_adapter(const std::shared_ptr& adapter) override { llm->set_weight_adapter(adapter); } void runner_done() override { llm->runner_done(); } std::tuple, std::vector, std::vector, std::vector> tokenize(std::string text) { auto parsed_attention = parse_prompt_attention(text); { std::stringstream ss; ss << "["; for (const auto& item : parsed_attention) { ss << "['" << item.first << "', " << item.second << "], "; } ss << "]"; LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str()); } std::vector qwen_tokens; std::vector qwen_weights; std::vector t5_tokens; std::vector t5_weights; for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; std::vector curr_tokens = qwen_tokenizer->tokenize(curr_text, nullptr); qwen_tokens.insert(qwen_tokens.end(), curr_tokens.begin(), curr_tokens.end()); // Anima uses uniform Qwen token weights. qwen_weights.insert(qwen_weights.end(), curr_tokens.size(), 1.f); } if (qwen_tokens.empty()) { qwen_tokens.push_back(151643); // qwen3 pad token qwen_weights.push_back(1.f); } for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; float curr_weight = item.second; std::vector curr_tokens = t5_tokenizer.encode(curr_text); t5_tokens.insert(t5_tokens.end(), curr_tokens.begin(), curr_tokens.end()); t5_weights.insert(t5_weights.end(), curr_tokens.size(), curr_weight); } t5_tokenizer.pad_tokens(t5_tokens, &t5_weights, nullptr); return {qwen_tokens, qwen_weights, t5_tokens, t5_weights}; } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { int64_t t0 = ggml_time_ms(); auto tokenized = tokenize(conditioner_params.text); auto& qwen_tokens = std::get<0>(tokenized); auto& qwen_weights = std::get<1>(tokenized); auto& t5_tokens = std::get<2>(tokenized); auto& t5_weights = std::get<3>(tokenized); sd::Tensor input_ids({static_cast(qwen_tokens.size()), 1}, qwen_tokens); auto hidden_states = llm->compute(n_threads, input_ids, sd::Tensor(), {}, {}, false, false, true, true); GGML_ASSERT(!hidden_states.empty()); hidden_states = apply_token_weights(std::move(hidden_states), qwen_weights); auto t5_ids_tensor = sd::Tensor::from_vector(t5_tokens); auto t5_weight_tensor = sd::Tensor::from_vector(t5_weights); int64_t t1 = ggml_time_ms(); LOG_DEBUG("computing condition graph completed, taking %" PRId64 " ms", t1 - t0); SDCondition result; result.c_crossattn = std::move(hidden_states); result.c_t5_ids = std::move(t5_ids_tensor); result.c_t5_weights = std::move(t5_weight_tensor); return result; } }; struct LLMEmbedder : public Conditioner { SDVersion version; std::shared_ptr tokenizer; std::shared_ptr llm; std::shared_ptr byt5; LLMEmbedder(ggml_backend_t backend, const String2TensorStorage& tensor_storage_map = {}, SDVersion version = VERSION_QWEN_IMAGE, const std::string prefix = "", bool enable_vision = false, std::shared_ptr weight_manager = nullptr) : version(version) { LLM::LLMArch arch = LLM::LLMArch::QWEN2_5_VL; if (version == VERSION_FLUX2) { arch = LLM::LLMArch::MISTRAL_SMALL_3_2; } else if (sd_version_is_ernie_image(version)) { arch = LLM::LLMArch::MINISTRAL_3_3B; } else if (sd_version_is_lens(version)) { arch = LLM::LLMArch::GPT_OSS_20B; } else if (sd_version_is_pid(version)) { arch = LLM::LLMArch::GEMMA2_2B; } else if (sd_version_is_lingbot_video(version) || sd_version_is_ideogram4(version) || sd_version_is_boogu_image(version) || sd_version_is_sefi_image(version) || sd_version_is_krea2(version) || sd_version_is_minimax_h3(version) || sd_version_is_mage_flow(version)) { arch = LLM::LLMArch::QWEN3_VL; } else if (sd_version_is_z_image(version) || version == VERSION_OVIS_IMAGE || version == VERSION_FLUX2_KLEIN) { arch = LLM::LLMArch::QWEN3; } if (arch == LLM::LLMArch::MISTRAL_SMALL_3_2 || arch == LLM::LLMArch::MINISTRAL_3_3B) { tokenizer = std::make_shared(); } else if (arch == LLM::LLMArch::GPT_OSS_20B) { tokenizer = std::make_shared(); } else if (arch == LLM::LLMArch::GEMMA2_2B) { tokenizer = std::make_shared(); } else { tokenizer = std::make_shared(); } llm = std::make_shared(arch, backend, tensor_storage_map, "text_encoders.llm", enable_vision, weight_manager); if (sd_version_is_hunyuan_video(version)) { const std::string byt5_prefix = "text_encoders.t5xxl.transformer"; for (const auto& [name, _] : tensor_storage_map) { if (starts_with(name, byt5_prefix + ".")) { byt5 = std::make_shared(backend, tensor_storage_map, byt5_prefix, false, weight_manager); break; } } } } void get_param_tensors(std::map& tensors) override { llm->get_param_tensors(tensors, "text_encoders.llm"); if (byt5) { byt5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void get_param_tensor_ops(std::map& tensor_ops) override { llm->get_param_tensor_ops(tensor_ops); } void set_max_graph_vram_bytes(size_t max_vram_bytes) override { llm->set_max_graph_vram_bytes(max_vram_bytes); if (byt5) { byt5->set_max_graph_vram_bytes(max_vram_bytes); } } void set_stream_layers_enabled(bool enabled) override { llm->set_stream_layers_enabled(enabled); if (byt5) { byt5->set_stream_layers_enabled(enabled); } } void set_runtime_backends(const std::vector& backends) override { llm->set_runtime_backends(backends); if (byt5) { byt5->set_runtime_backends(backends); } } void set_graph_cut_layer_split_enabled(bool enabled) override { if (llm) { llm->set_graph_cut_layer_split_enabled(enabled); } if (byt5) { byt5->set_graph_cut_layer_split_enabled(enabled); } } void set_graph_cut_layer_split_backend_vram_limits(const std::vector& limits) override { if (llm) { llm->set_graph_cut_layer_split_backend_vram_limits(limits); } if (byt5) { byt5->set_graph_cut_layer_split_backend_vram_limits(limits); } } void get_layer_split_param_tensors(std::map& tensors) override { llm->get_param_tensors(tensors, "text_encoders.llm"); if (byt5) { byt5->get_param_tensors(tensors, "text_encoders.t5xxl.transformer"); } } void set_flash_attention_enabled(bool enabled) override { llm->set_flash_attention_enabled(enabled); if (byt5) { byt5->set_flash_attention_enabled(enabled); } } void set_weight_adapter(const std::shared_ptr& adapter) override { if (llm) { llm->set_weight_adapter(adapter); } if (byt5) { byt5->set_weight_adapter(adapter); } } void runner_done() override { if (llm) { llm->runner_done(); } if (byt5) { byt5->runner_done(); } } std::tuple, std::vector, std::vector> tokenize(std::string text, const std::pair& attn_range, size_t min_length = 0, size_t max_length = 100000000, bool spell_quotes = false) { std::vector> parsed_attention; if (attn_range.first >= 0 && attn_range.second > 0) { if (attn_range.first > 0) { parsed_attention.emplace_back(text.substr(0, attn_range.first), 1.f); } if (attn_range.second - attn_range.first > 0) { auto new_parsed_attention = parse_prompt_attention(text.substr(attn_range.first, attn_range.second - attn_range.first)); if (spell_quotes) { new_parsed_attention = split_quotation_attention(new_parsed_attention); } parsed_attention.insert(parsed_attention.end(), new_parsed_attention.begin(), new_parsed_attention.end()); } if (attn_range.second < text.size()) { parsed_attention.emplace_back(text.substr(attn_range.second), 1.f); } } else { parsed_attention.emplace_back(text, 1.f); } { std::stringstream ss; ss << "["; for (const auto& item : parsed_attention) { ss << "['" << item.first << "', " << item.second << "], "; } ss << "]"; LOG_DEBUG("parse '%s' to %s", text.c_str(), ss.str().c_str()); } std::vector tokens; std::vector weights; for (const auto& item : parsed_attention) { const std::string& curr_text = item.first; float curr_weight = item.second; std::vector curr_tokens = tokenizer->encode(curr_text, nullptr); tokens.insert(tokens.end(), curr_tokens.begin(), curr_tokens.end()); weights.insert(weights.end(), curr_tokens.size(), curr_weight); } std::vector mask; tokenizer->pad_tokens(tokens, &weights, &mask, min_length, max_length); // for (int i = 0; i < tokens.size(); i++) { // std::cout << tokens[i] << ":" << weights[i] << ", " << i << std::endl; // } // std::cout << std::endl; return {tokens, weights, mask}; } sd::Tensor encode_prompt(int n_threads, const std::string prompt, const std::pair& prompt_attn_range, int min_length, int hidden_states_min_length, const std::vector>>& image_embeds, const std::set& out_layers, int prompt_template_encode_start_idx, bool spell_quotes = false, int max_length = 100000000, const LLM::DeepStackImageEmbeds& deepstack_image_embeds = {}, const std::vector& image_grids = {}) { auto tokens_weights_mask = tokenize(prompt, prompt_attn_range, min_length, max_length, spell_quotes); auto& tokens = std::get<0>(tokens_weights_mask); auto& weights = std::get<1>(tokens_weights_mask); auto& mask = std::get<2>(tokens_weights_mask); sd::Tensor input_ids({static_cast(tokens.size())}, tokens); sd::Tensor attention_mask; if (!mask.empty()) { attention_mask = sd::Tensor({static_cast(mask.size()), static_cast(mask.size())}); const float masked_attention_value = -std::numeric_limits::max() / 4.0f; for (size_t i1 = 0; i1 < mask.size(); ++i1) { for (size_t i0 = 0; i0 < mask.size(); ++i0) { float value = 0.0f; if (mask[i0] == 0.0f) { value += masked_attention_value; } if (i0 > i1) { value += masked_attention_value; } attention_mask[static_cast(i0 + mask.size() * i1)] = value; } } } auto hidden_states = llm->compute(n_threads, input_ids, attention_mask, image_embeds, out_layers, false, false, true, true, deepstack_image_embeds, image_grids); GGML_ASSERT(!hidden_states.empty()); hidden_states = apply_token_weights(std::move(hidden_states), weights); GGML_ASSERT(hidden_states.shape()[1] > prompt_template_encode_start_idx); int64_t zero_pad_len = 0; if (hidden_states_min_length > 0) { if (hidden_states.shape()[1] - prompt_template_encode_start_idx < hidden_states_min_length) { zero_pad_len = hidden_states_min_length - hidden_states.shape()[1] + prompt_template_encode_start_idx; } } sd::Tensor new_hidden_states = sd::ops::slice(hidden_states, 1, prompt_template_encode_start_idx, hidden_states.shape()[1]); if (zero_pad_len > 0) { auto pad_shape = new_hidden_states.shape(); pad_shape[1] = zero_pad_len; new_hidden_states = sd::ops::concat(new_hidden_states, sd::Tensor::zeros(std::move(pad_shape)), 1); } return new_hidden_states; } void resize_image_dims(int height, int width, int& h_bar, int& w_bar, int factor, int min_size, int max_size, RefImageResizeMode mode) { if (min_size > 0 && min_size == max_size) { if (mode == RefImageResizeMode::AREA) { double beta = std::sqrt(static_cast(min_size) / (static_cast(height) * width)); h_bar = std::max(static_cast(factor), static_cast(std::round(height * beta / factor)) * static_cast(factor)); w_bar = std::max(static_cast(factor), static_cast(std::round(width * beta / factor)) * static_cast(factor)); } else if (mode == RefImageResizeMode::LONGEST_SIDE) { int current_max_side = std::max(height, width); double beta = static_cast(min_size) / current_max_side; h_bar = std::max(static_cast(factor), static_cast(std::round(height * beta / factor)) * static_cast(factor)); w_bar = std::max(static_cast(factor), static_cast(std::round(width * beta / factor)) * static_cast(factor)); } return; } if (mode == RefImageResizeMode::AREA) { double current_area = static_cast(h_bar) * w_bar; if (max_size > 0 && current_area > max_size) { double beta = std::sqrt((static_cast(height) * width) / static_cast(max_size)); h_bar = std::max(static_cast(factor), static_cast(std::floor(height / beta / factor)) * static_cast(factor)); w_bar = std::max(static_cast(factor), static_cast(std::floor(width / beta / factor)) * static_cast(factor)); } else if (min_size > 0 && current_area < min_size) { double beta = std::sqrt(static_cast(min_size) / (static_cast(height) * width)); h_bar = static_cast(std::ceil(height * beta / factor)) * static_cast(factor); w_bar = static_cast(std::ceil(width * beta / factor)) * static_cast(factor); } } else if (mode == RefImageResizeMode::LONGEST_SIDE) { int current_max_side = std::max(height, width); if (max_size > 0 && current_max_side > max_size) { double beta = static_cast(max_size) / current_max_side; h_bar = std::max(static_cast(factor), static_cast(std::floor(height * beta / factor)) * static_cast(factor)); w_bar = std::max(static_cast(factor), static_cast(std::floor(width * beta / factor)) * static_cast(factor)); } else if (min_size > 0 && current_max_side < min_size) { double beta = static_cast(min_size) / current_max_side; h_bar = static_cast(std::ceil(height * beta / factor)) * static_cast(factor); w_bar = static_cast(std::ceil(width * beta / factor)) * static_cast(factor); } } } SDCondition get_learned_condition(int n_threads, const ConditionerParams& conditioner_params) override { std::string prompt; std::pair prompt_attn_range; std::vector extra_prompts; std::vector> extra_prompts_attn_range; std::vector>> image_embeds; LLM::DeepStackImageEmbeds deepstack_image_embeds; std::vector image_grids; int prompt_template_encode_start_idx = 34; int min_length = 0; // pad tokens int max_length = 100000000; int hidden_states_min_length = 0; // zero pad hidden_states bool spell_quotes = false; std::set out_layers; int64_t t0 = ggml_time_ms(); RefImageResizeMode resize_mode = conditioner_params.ref_image_params.vlm_resize_mode; if (sd_version_is_minimax_h3(version)) { prompt_template_encode_start_idx = 0; out_layers = {50}; prompt_attn_range = {0, 0}; if (llm->enable_vision) { const std::string placeholder = "<|image_pad|>"; const int patch_size = llm->config.vision.patch_size; const int factor = patch_size * llm->config.vision.spatial_merge_size; auto resize_for_vision = [&](const sd::Tensor& image) { int height = static_cast(image.shape()[1]); int width = static_cast(image.shape()[0]); int h_bar = std::max(factor, static_cast(std::round(static_cast(height) / factor)) * factor); int w_bar = std::max(factor, static_cast(std::round(static_cast(width) / factor)) * factor); resize_image_dims(height, width, h_bar, w_bar, factor, 3136, 12845056, RefImageResizeMode::AREA); auto resized = sd::ops::interpolate( image, std::vector{w_bar, h_bar, image.shape()[2], image.shape()[3]}); for (int64_t i = 0; i < resized.numel(); ++i) { resized[i] = std::clamp(resized[i], 0.f, 1.f) * 2.f - 1.f; } return resized; }; auto add_vision_outputs = [&](std::vector> image_outputs, int grid_h, int grid_w) { GGML_ASSERT(image_outputs.size() == 4); auto image_embed = std::move(image_outputs[0]); prompt += "<|vision_start|>"; int image_embed_idx = static_cast(tokenizer->encode(prompt, nullptr).size()); image_embeds.emplace_back(image_embed_idx, image_embed); if (deepstack_image_embeds.empty()) { deepstack_image_embeds.resize(image_outputs.size() - 1); } for (size_t layer = 0; layer < deepstack_image_embeds.size(); ++layer) { deepstack_image_embeds[layer].emplace_back(image_embed_idx, std::move(image_outputs[layer + 1])); } image_grids.push_back({image_embed_idx, static_cast(image_embed.shape()[1]), grid_h, grid_w}); for (int64_t i = 0; i < image_embed.shape()[1]; ++i) { prompt += placeholder; } prompt += "<|vision_end|>"; }; const auto* references = conditioner_params.minimax_h3_references; if (references != nullptr && !references->empty()) { int picture_index = 0; int video_index = 0; int audio_index = 0; for (const auto& item : *references) { if (item.kind == MiniMaxH3PresentationKind::AUDIO) { prompt += "