#ifndef __SD_MODEL_DIFFUSION_FIBO2_HPP__ #define __SD_MODEL_DIFFUSION_FIBO2_HPP__ #include "core/pe_cache.h" #include "model/diffusion/z_image.hpp" // https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/transformers/transformer_bria_fibo2.py namespace Fibo2 { struct Fibo2Config { int64_t dim = 3584; int64_t channels = 128; int64_t text_dim = 7680; int64_t head_dim = 128; int64_t depth = 44; int64_t refiner_depth = 2; int64_t perceiver_depth = 2; int64_t max_gist = 512; int64_t ffn_dim = 9536; bool fused_qkv = false; bool fused_kv = false; bool fused_ffn = false; std::vector injection_layers = {6, 14, 22, 30, 38}; static Fibo2Config detect_from_weights(const String2TensorStorage& weights, const std::string& prefix) { Fibo2Config config; int64_t depth = 0, refiner_depth = 0, perceiver_depth = 0; std::set injection_layers; for (const auto& [name, tensor] : weights) { if (!starts_with(name, prefix + ".")) { continue; } auto key = name.substr(prefix.size() + 1); if (key == "x_embedder.weight") { config.channels = tensor.logical_ne0(); config.dim = tensor.ne[1]; } else if (key == "perceiver.latent_queries") { config.max_gist = tensor.ne[1]; } else if (key == "perceiver.layers.0.cross_attention.to_k.weight" || key == "perceiver.layers.0.cross_attention.to_kv.weight") { config.text_dim = tensor.logical_ne0(); config.fused_kv = key == "perceiver.layers.0.cross_attention.to_kv.weight"; } else if (key == "layers.0.attention.norm_q.weight") { config.head_dim = tensor.ne[0]; } else if (key == "layers.0.attention.qkv.weight") { config.fused_qkv = true; } else if (key == "layers.0.feed_forward.w1.weight") { config.ffn_dim = tensor.ne[1]; } else if (key == "layers.0.feed_forward.w13.weight") { config.ffn_dim = tensor.ne[1] / 2; config.fused_ffn = true; } if (starts_with(key, "layers.")) { int index = std::stoi(key.substr(7)); depth = std::max(depth, index + 1); if (ends_with(key, ".cross_attn_gate")) { injection_layers.insert(index); } } else if (starts_with(key, "noise_refiner.")) { refiner_depth = std::max(refiner_depth, std::stoi(key.substr(14)) + 1); } else if (starts_with(key, "perceiver.layers.")) { perceiver_depth = std::max(perceiver_depth, std::stoi(key.substr(17)) + 1); } } if (depth) { config.depth = depth; config.refiner_depth = refiner_depth; config.perceiver_depth = perceiver_depth; config.injection_layers.assign(injection_layers.begin(), injection_layers.end()); LOG_VERBOSE("fibo2: dim = %" PRId64 ", depth = %" PRId64 ", head_dim = %" PRId64 ", text_dim = %" PRId64, config.dim, config.depth, config.head_dim, config.text_dim); } return config; } int gist_count(int text_length) const { double ratio = std::clamp((text_length - 256.0) / (2048.0 - 256.0), 0.0, 1.0); return std::min(static_cast(max_gist), static_cast(std::ceil((256 + ratio * (max_gist - 256)) / 64)) * 64); } }; struct Attention : public GGMLBlock { int64_t head_dim; int64_t heads; bool fused_qkv; bool fused_kv; Attention(const Fibo2Config& config, int64_t kv_dim, bool joint = false) : head_dim(config.head_dim), heads(config.dim / config.head_dim), fused_qkv(joint && config.fused_qkv), fused_kv(!joint && config.fused_kv) { if (fused_qkv) { blocks["qkv"] = std::make_shared(config.dim, 3 * config.dim, false); } else { blocks["to_q"] = std::make_shared(config.dim, config.dim, false); if (fused_kv) { blocks["to_kv"] = std::make_shared(kv_dim, 2 * config.dim, false); } else { blocks["to_k"] = std::make_shared(kv_dim, config.dim, false); blocks["to_v"] = std::make_shared(kv_dim, config.dim, false); } } blocks["to_out.0"] = std::make_shared(config.dim, config.dim, false); blocks["norm_q"] = std::make_shared(head_dim, 1e-5f); blocks["norm_k"] = std::make_shared(head_dim, 1e-5f); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* kv, ggml_tensor* pe = nullptr) { auto c = ctx->ggml_ctx; auto project = [&](const char* name, ggml_tensor* h) { return std::dynamic_pointer_cast(blocks[name])->forward(ctx, h); }; auto split_views = [&](ggml_tensor* qkv, int n, int i) { int64_t slice = qkv->ne[0] / n; return ggml_view_4d(c, qkv, head_dim, slice / head_dim, qkv->ne[1], qkv->ne[2] * qkv->ne[3], head_dim * qkv->nb[0], qkv->nb[1], qkv->nb[2], i * slice * qkv->nb[0]); }; auto reshape = [&](ggml_tensor* h) { return ggml_reshape_4d(c, h, head_dim, heads, h->ne[1], h->ne[2]); }; ggml_tensor *q, *k, *v; if (fused_qkv) { auto qkv = project("qkv", x); q = split_views(qkv, 3, 0); k = split_views(qkv, 3, 1); v = split_views(qkv, 3, 2); } else { q = reshape(project("to_q", x)); if (fused_kv) { auto kv_out = project("to_kv", kv); k = split_views(kv_out, 2, 0); v = split_views(kv_out, 2, 1); } else { k = reshape(project("to_k", kv)); v = reshape(project("to_v", kv)); } } q = std::dynamic_pointer_cast(blocks["norm_q"])->forward(ctx, q); k = std::dynamic_pointer_cast(blocks["norm_k"])->forward(ctx, k); auto prepare = [&](ggml_tensor* h) { if (pe) { return Rope::apply_rope(ctx, h, pe); } h = ggml_cont(c, ggml_permute(c, h, 0, 2, 1, 3)); return ggml_reshape_3d(c, h, head_dim, h->ne[1], heads * h->ne[3]); }; auto y = ggml_ext_attention_ext(ctx, prepare(q), prepare(k), v, heads, nullptr, true, ctx->flash_attn_enabled); return std::dynamic_pointer_cast(blocks["to_out.0"])->forward(ctx, y); } }; struct FeedForward : public ZImage::FeedForward { bool fused; FeedForward(const Fibo2Config& config) : ZImage::FeedForward(config.dim, config.ffn_dim, 1), fused(config.fused_ffn) { if (fused) { blocks.erase("w1"); blocks.erase("w3"); blocks["w13"] = std::make_shared(config.dim, 2 * config.ffn_dim, false); } } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) { if (!fused) { return ZImage::FeedForward::forward(ctx, x); } auto w2 = std::dynamic_pointer_cast(blocks["w2"]); w2->set_precision_flags(GGML_PRECISION_F32_VULKAN | GGML_PRECISION_F32_ROCM); x = std::dynamic_pointer_cast(blocks["w13"])->forward(ctx, x); return w2->forward(ctx, ggml_swiglu(ctx->ggml_ctx, x)); } }; struct PerceiverLayer : public GGMLBlock { PerceiverLayer(const Fibo2Config& config) { for (const char* name : {"cross_attn_norm", "self_attn_norm", "ffn_norm"}) { blocks[name] = std::make_shared(config.dim, 1e-5f); } blocks["cross_attention"] = std::make_shared(config, config.text_dim); blocks["self_attention"] = std::make_shared(config, config.dim); blocks["ffn"] = std::make_shared(config); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* text) { auto norm = [&](const char* name, ggml_tensor* h) { return std::dynamic_pointer_cast(blocks[name])->forward(ctx, h); }; auto h = std::dynamic_pointer_cast(blocks["cross_attention"])->forward(ctx, norm("cross_attn_norm", x), text); x = ggml_add(ctx->ggml_ctx, x, h); h = norm("self_attn_norm", x); h = std::dynamic_pointer_cast(blocks["self_attention"])->forward(ctx, h, h); x = ggml_add(ctx->ggml_ctx, x, h); h = std::dynamic_pointer_cast(blocks["ffn"])->forward(ctx, norm("ffn_norm", x)); return ggml_add(ctx->ggml_ctx, x, h); } }; struct Perceiver : public GGMLBlock { Fibo2Config config; Perceiver(const Fibo2Config& config) : config(config) { for (int i = 0; i < config.perceiver_depth; ++i) { blocks["layers." + std::to_string(i)] = std::make_shared(config); } blocks["norm_out"] = std::make_shared(config.dim, 1e-5f); } void init_params(ggml_context* ctx, const String2TensorStorage& weights = {}, const std::string prefix = "") override { params["latent_queries"] = ggml_new_tensor_2d(ctx, GGML_TYPE_F32, config.dim, config.max_gist); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* text, int gist_count) { auto x = ggml_ext_slice(ctx, params["latent_queries"], 1, 0, gist_count); for (int i = 0; i < config.perceiver_depth; ++i) { x = std::dynamic_pointer_cast(blocks["layers." + std::to_string(i)])->forward(ctx, x, text); } return std::dynamic_pointer_cast(blocks["norm_out"])->forward(ctx, x); } }; struct TransformerBlock : public GGMLBlock { bool injection; TransformerBlock(const Fibo2Config& config, bool injection) : injection(injection) { blocks["attention"] = std::make_shared(config, config.dim, true); blocks["feed_forward"] = std::make_shared(config); for (const char* name : {"attention_norm1", "attention_norm2", "ffn_norm1", "ffn_norm2"}) { blocks[name] = std::make_shared(config.dim, 1e-5f); } blocks["adaLN_modulation.0"] = std::make_shared(std::min(config.dim, 256), 4 * config.dim); if (injection) { blocks["cross_attention"] = std::make_shared(config, config.text_dim); blocks["cross_attn_norm1"] = std::make_shared(config.dim, 1e-5f); blocks["cross_attn_norm2"] = std::make_shared(config.dim, 1e-5f); } } void init_params(ggml_context* ctx, const String2TensorStorage& weights = {}, const std::string prefix = "") override { if (injection) { params["cross_attn_gate"] = ggml_new_tensor_1d(ctx, GGML_TYPE_F32, 1); } } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* pe, ggml_tensor* noisy, ggml_tensor* clean, int image_start, int image_count, ggml_tensor* text = nullptr) { auto c = ctx->ggml_ctx; auto norm = [&](const char* name, ggml_tensor* h) { return std::dynamic_pointer_cast(blocks[name])->forward(ctx, h); }; auto modulation = std::dynamic_pointer_cast(blocks["adaLN_modulation.0"]); auto mn = ggml_ext_chunk(ctx, modulation->forward(ctx, noisy), 4, 0); auto mc = ggml_ext_chunk(ctx, modulation->forward(ctx, clean), 4, 0); auto select = [&](int index, bool gate) { auto n = gate ? ggml_tanh(c, mn[index]) : mn[index]; auto k = gate ? ggml_tanh(c, mc[index]) : mc[index]; auto repeat = [&](ggml_tensor* h, int64_t count) { return ggml_repeat(c, h, ggml_new_tensor_2d(c, GGML_TYPE_F32, h->ne[0], count)); }; auto m = repeat(n, image_count); if (image_start) { m = ggml_concat(c, repeat(k, image_start), m, 1); } int64_t tail = x->ne[1] - image_start - image_count; if (tail) { m = ggml_concat(c, m, repeat(k, tail), 1); } return m; }; auto h = ZImage::modulate(ctx, norm("attention_norm1", x), select(0, false)); h = std::dynamic_pointer_cast(blocks["attention"])->forward(ctx, h, h, pe); x = ggml_add(c, x, ggml_mul(c, norm("attention_norm2", h), select(1, true))); if (injection) { h = std::dynamic_pointer_cast(blocks["cross_attention"])->forward(ctx, norm("cross_attn_norm1", x), text); h = ggml_mul(c, norm("cross_attn_norm2", h), ggml_tanh(c, params["cross_attn_gate"])); x = ggml_add(c, x, h); } h = ZImage::modulate(ctx, norm("ffn_norm1", x), select(2, false)); h = std::dynamic_pointer_cast(blocks["feed_forward"])->forward(ctx, h); return ggml_add(c, x, ggml_mul(c, norm("ffn_norm2", h), select(3, true))); } }; struct Fibo2Model : public GGMLBlock { Fibo2Config config; Fibo2Model(const Fibo2Config& config) : config(config) { blocks["x_embedder"] = std::make_shared(config.channels, config.dim); blocks["t_embedder"] = std::make_shared(1024, 256, std::min(config.dim, 256)); blocks["perceiver"] = std::make_shared(config); for (int i = 0; i < config.refiner_depth; ++i) { blocks["noise_refiner." + std::to_string(i)] = std::make_shared(config, false); } for (int i = 0; i < config.depth; ++i) { bool injection = std::find(config.injection_layers.begin(), config.injection_layers.end(), i) != config.injection_layers.end(); blocks["layers." + std::to_string(i)] = std::make_shared(config, injection); } blocks["final_layer"] = std::make_shared(config.dim, 1, config.channels); } ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* t, ggml_tensor* text, ggml_tensor* pe, int gist_count, const std::vector& refs) { auto c = ctx->ggml_ctx; int64_t width = x->ne[0], height = x->ne[1]; int image_count = static_cast(width * height); // The shared FLUX.2 VAE already packs (channel, dy, dx) into 128 channels. auto tokens = [&](ggml_tensor* h) { h = ggml_reshape_3d(c, h, h->ne[0] * h->ne[1], config.channels, 1); return ggml_cont(c, ggml_permute(c, h, 1, 0, 2, 3)); }; x = tokens(x); for (auto ref : refs) { x = ggml_concat(c, x, tokens(ref), 1); } x = std::dynamic_pointer_cast(blocks["x_embedder"])->forward(ctx, x); auto embedder = std::dynamic_pointer_cast(blocks["t_embedder"]); auto noisy = embedder->forward(ctx, t); auto clean = embedder->forward(ctx, ggml_scale(c, t, 0.f)); auto image_pe = ggml_ext_slice(ctx, pe, 3, gist_count, pe->ne[3]); for (int i = 0; i < config.refiner_depth; ++i) { x = std::dynamic_pointer_cast(blocks["noise_refiner." + std::to_string(i)])->forward(ctx, x, image_pe, noisy, clean, 0, image_count); sd::ggml_graph_cut::mark_graph_cut(x, "fibo2.noise_refiner." + std::to_string(i), "x"); } auto bundle = [&](int i) { return ggml_ext_slice(ctx, text, 0, i * config.text_dim, (i + 1) * config.text_dim); }; auto gist = std::dynamic_pointer_cast(blocks["perceiver"])->forward(ctx, bundle(0), gist_count); x = ggml_concat(c, gist, x, 1); int bundle_index = 1; for (int i = 0; i < config.depth; ++i) { auto block = std::dynamic_pointer_cast(blocks["layers." + std::to_string(i)]); auto context = block->injection ? bundle(bundle_index++) : nullptr; x = block->forward(ctx, x, pe, noisy, clean, gist_count, image_count, context); sd::ggml_graph_cut::mark_graph_cut(x, "fibo2.layers." + std::to_string(i), "x"); } x = ggml_ext_slice(ctx, x, 1, gist_count, gist_count + image_count); x = std::dynamic_pointer_cast(blocks["final_layer"])->forward(ctx, x, noisy); x = ggml_cont(c, ggml_permute(c, x, 1, 0, 2, 3)); return ggml_reshape_4d(c, x, width, height, config.channels, 1); } }; struct Fibo2Runner : public DiffusionModelRunner { sd::PECache pe_cache_; Fibo2Config config; Fibo2Model model; Fibo2Runner(ggml_backend_t backend, const String2TensorStorage& weights, const std::string& prefix, std::shared_ptr weight_manager = nullptr) : DiffusionModelRunner(backend, prefix, weight_manager), config(Fibo2Config::detect_from_weights(weights, prefix)), model(config) { model.init(params_ctx, weights, prefix); } std::string get_desc() override { return "fibo2"; } void get_param_tensors(std::map& tensors, const std::string& prefix) override { model.get_param_tensors(tensors, prefix); } sd::Tensor compute(int n_threads, const DiffusionParams& p) override { if (!p.x || !p.timesteps || !p.context || p.context->shape()[0] != config.text_dim * 6) { LOG_ERROR("Fibo 2.0 requires six text feature bundles"); return {}; } auto build = [&]() { auto graph = new_graph_custom(65536); auto x = make_input(*p.x); GGML_ASSERT(x->ne[3] == 1 && x->ne[2] == config.channels); int count = config.gist_count(static_cast(p.context->shape()[1])); std::vector refs; if (p.ref_latents && p.ref_image_params.pass_to_dit) { for (const auto& ref : *p.ref_latents) { refs.push_back(make_input(ref)); } } sd::PECacheKey pe_key("fibo2.pe"); pe_key.add_shape(x).add(count).add(config.max_gist).add_shapes(refs).add(circular_x_enabled); pe_key.add(circular_y_enabled); auto pe = make_input(pe_cache_.get_or_create(pe_key, [&]() { Rope::Embedding embedding; for (int i = 0; i < count; ++i) { embedding.ids.push_back({static_cast(i + 1), 0.f, 0.f}); } embedding.positions.append_tokens(count); auto append_image = [&](ggml_tensor* image, int plane) { auto ids = Rope::gen_flux_img_ids(static_cast(image->ne[1]), static_cast(image->ne[0]), 1, 1, 3, plane, 0, 0, false, &embedding.positions); embedding.ids.insert(embedding.ids.end(), ids.begin(), ids.end()); }; append_image(x, static_cast(config.max_gist + 1)); for (size_t i = 0; i < refs.size(); ++i) { append_image(refs[i], static_cast(config.max_gist + 2 + i)); } embedding.values = Rope::embed_nd(embedding.ids, 1, 256.f, {32, 48, 48}, embedding.layout, &embedding.frequencies); auto values = finish_rope_pe(std::move(embedding)); const int64_t tokens = static_cast(values.size() / 256); return sd::Tensor({2, 2, 64, tokens}, std::move(values)); })); auto ctx = get_context(); auto out = model.forward(&ctx, x, make_input(*p.timesteps), make_input(*p.context), pe, count, refs); ggml_build_forward_expand(graph, out); return graph; }; return restore_trailing_singleton_dims(GGMLRunner::compute(build, n_threads, false), p.x->dim()); } }; } #endif // __SD_MODEL_DIFFUSION_FIBO2_HPP__