mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-09-29 17:38:14 -05:00
feat: add hunyuan video 1.5 support (#1795)
This commit is contained in:
@@ -0,0 +1,834 @@
|
||||
#ifndef __SD_MODEL_VAE_HUNYUAN_VAE_HPP__
|
||||
#define __SD_MODEL_VAE_HUNYUAN_VAE_HPP__
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "model/vae/wan_vae.hpp"
|
||||
#include "model_manager.h"
|
||||
|
||||
namespace Hunyuan {
|
||||
constexpr int HUNYUAN_VIDEO_VAE_GRAPH_SIZE = 65536;
|
||||
constexpr int HUNYUAN_VIDEO_VAE_GRAPH_SIZE_PER_LATENT_FRAME = 8192;
|
||||
constexpr int HUNYUAN_VIDEO_VAE_TEMPORAL_CHUNK_SIZE = 1;
|
||||
|
||||
struct TemporalConvCarry {
|
||||
const std::vector<ggml_tensor*>* input = nullptr;
|
||||
std::vector<ggml_tensor*>* output = nullptr;
|
||||
size_t input_index = 0;
|
||||
|
||||
bool is_continuation() const {
|
||||
return input != nullptr;
|
||||
}
|
||||
|
||||
ggml_tensor* take() {
|
||||
GGML_ASSERT(input != nullptr && input_index < input->size());
|
||||
return (*input)[input_index++];
|
||||
}
|
||||
|
||||
void push(ggml_tensor* tensor) {
|
||||
if (output != nullptr) {
|
||||
output->push_back(tensor);
|
||||
}
|
||||
}
|
||||
|
||||
void finish() const {
|
||||
GGML_ASSERT(input == nullptr || input_index == input->size());
|
||||
}
|
||||
};
|
||||
|
||||
static ggml_tensor* repeat_interleave_channels(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
int64_t repeats,
|
||||
int64_t width,
|
||||
int64_t height,
|
||||
int64_t frames) {
|
||||
GGML_ASSERT(repeats > 0);
|
||||
GGML_ASSERT(width * height * frames == x->ne[0] * x->ne[1] * x->ne[2]);
|
||||
int64_t channels = x->ne[3];
|
||||
if (repeats == 1) {
|
||||
return ggml_reshape_4d(ctx->ggml_ctx, x, width, height, frames, channels);
|
||||
}
|
||||
x = ggml_reshape_3d(ctx->ggml_ctx, x, width * height * frames, 1, channels);
|
||||
auto target = ggml_new_tensor_3d(ctx->ggml_ctx, x->type, width * height * frames, repeats, channels);
|
||||
x = ggml_repeat(ctx->ggml_ctx, x, target);
|
||||
return ggml_reshape_4d(ctx->ggml_ctx, x, width, height, frames, channels * repeats);
|
||||
}
|
||||
|
||||
class CausalConv3d : public GGMLBlock {
|
||||
protected:
|
||||
std::tuple<int, int, int> kernel_size;
|
||||
|
||||
public:
|
||||
CausalConv3d(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
std::tuple<int, int, int> kernel_size,
|
||||
std::tuple<int, int, int> stride = {1, 1, 1},
|
||||
std::tuple<int, int, int> padding = {0, 0, 0},
|
||||
std::tuple<int, int, int> dilation = {1, 1, 1},
|
||||
bool bias = true)
|
||||
: kernel_size(kernel_size) {
|
||||
blocks["conv"] = std::make_shared<Conv3d>(in_channels, out_channels, kernel_size, stride, padding, dilation, bias);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
TemporalConvCarry* carry = nullptr) {
|
||||
// x: [N*IC, ID, IH, IW]
|
||||
// result: x: [N*OC, OD, OH, OW]
|
||||
// assert N == 1
|
||||
|
||||
auto conv = std::dynamic_pointer_cast<Conv3d>(blocks["conv"]);
|
||||
|
||||
int pad_w = std::get<2>(kernel_size) / 2;
|
||||
int pad_h = std::get<1>(kernel_size) / 2;
|
||||
int pad_t = std::get<0>(kernel_size) - 1;
|
||||
std::vector<ggml_tensor*> temporal_frames;
|
||||
temporal_frames.reserve(x->ne[2] + pad_t);
|
||||
if (pad_t > 0) {
|
||||
if (carry != nullptr && carry->is_continuation()) {
|
||||
auto previous = carry->take();
|
||||
GGML_ASSERT(previous->ne[2] <= pad_t);
|
||||
for (int64_t frame = 0; frame < previous->ne[2]; frame++) {
|
||||
temporal_frames.push_back(ggml_ext_slice(ctx->ggml_ctx, previous, 2, frame, frame + 1));
|
||||
}
|
||||
for (int64_t frame = previous->ne[2]; frame < pad_t; frame++) {
|
||||
temporal_frames.push_back(ggml_ext_slice(ctx->ggml_ctx, x, 2, 0, 1));
|
||||
}
|
||||
} else {
|
||||
auto first = ggml_ext_slice(ctx->ggml_ctx, x, 2, 0, 1);
|
||||
for (int frame = 0; frame < pad_t; frame++) {
|
||||
temporal_frames.push_back(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int64_t frame = 0; frame < x->ne[2]; frame++) {
|
||||
temporal_frames.push_back(ggml_ext_slice(ctx->ggml_ctx, x, 2, frame, frame + 1));
|
||||
}
|
||||
|
||||
if (pad_t > 0 && carry != nullptr && carry->output != nullptr) {
|
||||
ggml_tensor* next = nullptr;
|
||||
for (int frame = pad_t; frame > 0; frame--) {
|
||||
auto item = temporal_frames[temporal_frames.size() - frame];
|
||||
next = next == nullptr ? item : ggml_concat(ctx->ggml_ctx, next, item, 2);
|
||||
}
|
||||
carry->push(ggml_cont(ctx->ggml_ctx, next));
|
||||
}
|
||||
|
||||
ggml_tensor* padded = nullptr;
|
||||
for (auto frame : temporal_frames) {
|
||||
padded = padded == nullptr ? frame : ggml_concat(ctx->ggml_ctx, padded, frame, 2);
|
||||
}
|
||||
auto replicate_pad = [&](ggml_tensor* input, int dim, int left, int right) {
|
||||
if (left > 0) {
|
||||
auto first = ggml_ext_slice(ctx->ggml_ctx, input, dim, 0, 1);
|
||||
for (int i = 0; i < left; i++) {
|
||||
input = ggml_concat(ctx->ggml_ctx, first, input, dim);
|
||||
}
|
||||
}
|
||||
if (right > 0) {
|
||||
auto last = ggml_ext_slice(ctx->ggml_ctx, input, dim, input->ne[dim] - 1, input->ne[dim]);
|
||||
for (int i = 0; i < right; i++) {
|
||||
input = ggml_concat(ctx->ggml_ctx, input, last, dim);
|
||||
}
|
||||
}
|
||||
return input;
|
||||
};
|
||||
padded = replicate_pad(padded, 0, pad_w, pad_w);
|
||||
padded = replicate_pad(padded, 1, pad_h, pad_h);
|
||||
return conv->forward(ctx, padded);
|
||||
}
|
||||
};
|
||||
|
||||
class AttnBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
|
||||
public:
|
||||
AttnBlock(int64_t in_channels)
|
||||
: in_channels(in_channels) {
|
||||
blocks["norm"] = std::make_shared<WAN::RMS_norm>(in_channels);
|
||||
blocks["q"] = std::make_shared<Conv3d>(in_channels, in_channels, std::tuple{1, 1, 1});
|
||||
blocks["k"] = std::make_shared<Conv3d>(in_channels, in_channels, std::tuple{1, 1, 1});
|
||||
blocks["v"] = std::make_shared<Conv3d>(in_channels, in_channels, std::tuple{1, 1, 1});
|
||||
blocks["proj_out"] = std::make_shared<Conv3d>(in_channels, in_channels, std::tuple{1, 1, 1});
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x) override {
|
||||
// x: [b*c, t, h, w]
|
||||
auto norm = std::dynamic_pointer_cast<WAN::RMS_norm>(blocks["norm"]);
|
||||
auto q_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["q"]);
|
||||
auto k_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["k"]);
|
||||
auto v_proj = std::dynamic_pointer_cast<UnaryBlock>(blocks["v"]);
|
||||
auto proj_out = std::dynamic_pointer_cast<UnaryBlock>(blocks["proj_out"]);
|
||||
|
||||
const int64_t b = x->ne[3] / in_channels;
|
||||
|
||||
auto identity = x;
|
||||
|
||||
x = norm->forward(ctx, x);
|
||||
|
||||
const int64_t c = x->ne[3] / b;
|
||||
const int64_t t = x->ne[2];
|
||||
const int64_t h = x->ne[1];
|
||||
const int64_t w = x->ne[0];
|
||||
|
||||
auto q = q_proj->forward(ctx, x); // [b*c, t, h, w]
|
||||
auto k = k_proj->forward(ctx, x); // [b*c, t, h, w]
|
||||
auto v = v_proj->forward(ctx, x); // [b*c, t, h, w]
|
||||
|
||||
q = ggml_reshape_3d(ctx->ggml_ctx, q, w * h * t, c, b); // [b, c, t*h*w]
|
||||
q = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, q, 1, 0, 2, 3)); // [b, t*h*w, c]
|
||||
|
||||
k = ggml_reshape_3d(ctx->ggml_ctx, k, w * h * t, c, b); // [b, c, t*h*w]
|
||||
k = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, k, 1, 0, 2, 3)); // [b, t*h*w, c]
|
||||
|
||||
v = ggml_reshape_3d(ctx->ggml_ctx, v, w * h * t, c, b); // [b, c, t*h*w]
|
||||
v = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, v, 1, 0, 2, 3)); // [b, t*h*w, c]
|
||||
|
||||
x = ggml_ext_attention_ext(ctx->ggml_ctx, ctx->backend, q, k, v, 1, nullptr, false, ctx->flash_attn_enabled); // [b, t*h*w, c]
|
||||
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 1, 0, 2, 3)); // [b, c, t*h*w]
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, w, h, t, c * b); // [b*c, t, h, w]
|
||||
|
||||
x = proj_out->forward(ctx, x);
|
||||
|
||||
x = ggml_add(ctx->ggml_ctx, x, identity);
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class ResnetBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
int64_t out_channels;
|
||||
|
||||
public:
|
||||
ResnetBlock(int64_t in_channels,
|
||||
int64_t out_channels)
|
||||
: in_channels(in_channels),
|
||||
out_channels(out_channels) {
|
||||
blocks["norm1"] = std::make_shared<WAN::RMS_norm>(in_channels);
|
||||
blocks["conv1"] = std::make_shared<CausalConv3d>(in_channels, out_channels, std::tuple{3, 3, 3});
|
||||
|
||||
blocks["norm2"] = std::make_shared<WAN::RMS_norm>(out_channels);
|
||||
blocks["conv2"] = std::make_shared<CausalConv3d>(out_channels, out_channels, std::tuple{3, 3, 3});
|
||||
|
||||
if (out_channels != in_channels) {
|
||||
blocks["nin_shortcut"] = std::make_shared<CausalConv3d>(in_channels, out_channels, std::tuple{1, 1, 1});
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
||||
return forward(ctx, x, nullptr);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
TemporalConvCarry* carry) {
|
||||
// x: [B*IC, IT, OH, OW]
|
||||
// return: [B*OC, OT, OH, OW]
|
||||
auto norm1 = std::dynamic_pointer_cast<WAN::RMS_norm>(blocks["norm1"]);
|
||||
auto conv1 = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv1"]);
|
||||
auto norm2 = std::dynamic_pointer_cast<WAN::RMS_norm>(blocks["norm2"]);
|
||||
auto conv2 = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv2"]);
|
||||
|
||||
auto h = x;
|
||||
h = norm1->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // swish
|
||||
h = conv1->forward(ctx, h, carry);
|
||||
|
||||
h = norm2->forward(ctx, h);
|
||||
h = ggml_silu_inplace(ctx->ggml_ctx, h); // swish
|
||||
// dropout, skip for inference
|
||||
h = conv2->forward(ctx, h, carry);
|
||||
|
||||
// skip connection
|
||||
if (out_channels != in_channels) {
|
||||
auto nin_shortcut = std::dynamic_pointer_cast<CausalConv3d>(blocks["nin_shortcut"]);
|
||||
|
||||
x = nin_shortcut->forward(ctx, x); // [B*OC, OT, OH, OW]
|
||||
}
|
||||
|
||||
h = ggml_add(ctx->ggml_ctx, h, x);
|
||||
return h; // [B*OC, OT, OH, OW]
|
||||
}
|
||||
};
|
||||
|
||||
class Upsample : public GGMLBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
int64_t out_channels;
|
||||
int64_t factor_t;
|
||||
int64_t factor_s;
|
||||
int64_t factor;
|
||||
int64_t repeats;
|
||||
|
||||
public:
|
||||
Upsample(int64_t in_channels, int64_t out_channels, bool add_temporal_upsample)
|
||||
: in_channels(in_channels), out_channels(out_channels) {
|
||||
if (add_temporal_upsample) {
|
||||
factor_t = 2;
|
||||
} else {
|
||||
factor_t = 1;
|
||||
}
|
||||
factor_s = 2;
|
||||
factor = factor_t * factor_s * factor_s;
|
||||
GGML_ASSERT(out_channels * factor % in_channels == 0);
|
||||
repeats = out_channels * factor / in_channels;
|
||||
blocks["conv"] = std::make_shared<CausalConv3d>(in_channels, out_channels * factor, std::tuple{3, 3, 3});
|
||||
}
|
||||
|
||||
static ggml_tensor* _pixel_shuffle_3d(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
int64_t factor_t,
|
||||
int64_t factor_s,
|
||||
int64_t B = 1) {
|
||||
// x: [B*factor*C, T, H, W]
|
||||
// return: [B*C, T*factor_t, H*factor_s, W*factor_s]
|
||||
GGML_ASSERT(B == 1);
|
||||
int64_t factor = factor_t * factor_s * factor_s;
|
||||
int64_t C = x->ne[3] / factor;
|
||||
int64_t T = x->ne[2];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t W = x->ne[0];
|
||||
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H * T, C, factor); // [factor, C, T*H, W]
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 1, 3, 2)); // [C, factor, T*H, W]
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H * T, factor_s, factor_s * factor_t * C); // [C*factor_t*factor_s, factor_s, T*H, W]
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 2, 0, 1, 3)); // [C*factor_t*factor_s, T*H, W, factor_s]
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, factor_s * W, H * T, factor_s, factor_t * C); // [C*factor_t, factor_s, T*H, W*factor_s]
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // [C*factor_t, T*H, factor_s, W*factor_s]
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, factor_s * W * factor_s * H, T, factor_t, C); // [C, factor_t, T, H*factor_s*W*factor_s]
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 2, 1, 3)); // [C, T, factor_t, H*factor_s*W*factor_s]
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, factor_s * W, factor_s * H, factor_t * T, C); // [C, T*factor_t, H*factor_s, W*factor_s]
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
TemporalConvCarry* carry = nullptr) {
|
||||
// x: [B*IC, T, H, W]
|
||||
// return: [B*OC, 1 + (T - 1)*factor_t, H*factor_s, W*factor_s]
|
||||
const int64_t B = x->ne[3] / in_channels;
|
||||
GGML_ASSERT(B == 1);
|
||||
|
||||
auto conv = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv"]);
|
||||
|
||||
const bool continuation = carry != nullptr && carry->is_continuation();
|
||||
auto h = conv->forward(ctx, x, carry); // [B*factor*OC, T, H, W]
|
||||
|
||||
ggml_tensor* shortcut = nullptr;
|
||||
if (factor_t == 2 && !continuation) {
|
||||
auto h_first = ggml_ext_slice(ctx->ggml_ctx, h, 2, 0, 1); // [B*factor*OC, 1, H, W]
|
||||
h_first = _pixel_shuffle_3d(ctx, h_first, 1, factor_s, B); // [B*2*OC, 1, H*factor_s, W*factor_s]
|
||||
h_first = ggml_ext_slice(ctx->ggml_ctx, h_first, 3, 0, out_channels); // [B*OC, 1, H*factor_s, W*factor_s]
|
||||
|
||||
auto x_first = ggml_ext_slice(ctx->ggml_ctx, x, 2, 0, 1);
|
||||
x_first = repeat_interleave_channels(ctx, x_first, repeats / 2, x->ne[0], x->ne[1], 1);
|
||||
x_first = _pixel_shuffle_3d(ctx, x_first, 1, factor_s, B);
|
||||
|
||||
if (x->ne[2] == 1) {
|
||||
return ggml_add(ctx->ggml_ctx, h_first, x_first);
|
||||
}
|
||||
|
||||
auto h_next = ggml_ext_slice(ctx->ggml_ctx, h, 2, 1, h->ne[2]); // [B*factor*OC, T - 1, H, W]
|
||||
h_next = _pixel_shuffle_3d(ctx, h_next, factor_t, factor_s, B); // [B*OC, (T - 1)*factor_t, H*factor_s, W*factor_s]
|
||||
|
||||
h = ggml_concat(ctx->ggml_ctx, h_first, h_next, 2); // [B*OC, 1 + (T - 1)*factor_t, H*factor_s, W*factor_s]
|
||||
|
||||
auto x_next = ggml_ext_slice(ctx->ggml_ctx, x, 2, 1, x->ne[2]);
|
||||
x_next = repeat_interleave_channels(ctx, x_next, repeats, x->ne[0], x->ne[1], x->ne[2] - 1);
|
||||
x_next = _pixel_shuffle_3d(ctx, x_next, factor_t, factor_s, B);
|
||||
|
||||
shortcut = ggml_concat(ctx->ggml_ctx, x_first, x_next, 2); // [B*OC, 1 + (T - 1)*factor_t, H*factor_s, W*factor_s]
|
||||
} else {
|
||||
h = _pixel_shuffle_3d(ctx, h, factor_t, factor_s, B);
|
||||
shortcut = repeat_interleave_channels(ctx, x, repeats, x->ne[0], x->ne[1], x->ne[2]);
|
||||
shortcut = _pixel_shuffle_3d(ctx, shortcut, factor_t, factor_s, B); // [B*OC, T*factor_t, H*factor_s, W*factor_s]
|
||||
}
|
||||
|
||||
return ggml_add(ctx->ggml_ctx, h, shortcut);
|
||||
}
|
||||
};
|
||||
|
||||
static ggml_tensor* pixel_unshuffle_3d(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
int64_t factor_t,
|
||||
int64_t factor_s) {
|
||||
GGML_ASSERT(x->ne[0] % factor_s == 0);
|
||||
GGML_ASSERT(x->ne[1] % factor_s == 0);
|
||||
GGML_ASSERT(x->ne[2] % factor_t == 0);
|
||||
int64_t W = x->ne[0] / factor_s;
|
||||
int64_t H = x->ne[1] / factor_s;
|
||||
int64_t T = x->ne[2] / factor_t;
|
||||
int64_t C = x->ne[3];
|
||||
int64_t factor = factor_t * factor_s * factor_s;
|
||||
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, factor_s * W * factor_s * H, factor_t, T, C);
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 2, 1, 3));
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, factor_s * W, factor_s, H * T, factor_t * C);
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 2, 1, 3));
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, factor_s, W, H * T, factor_s * factor_t * C);
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 1, 2, 0, 3));
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H * T, factor, C);
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
||||
return ggml_reshape_4d(ctx->ggml_ctx, x, W, H, T, C * factor);
|
||||
}
|
||||
|
||||
static ggml_tensor* mean_channel_groups(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
int64_t group_size) {
|
||||
GGML_ASSERT(group_size > 0);
|
||||
GGML_ASSERT(x->ne[3] % group_size == 0);
|
||||
if (group_size == 1) {
|
||||
return x;
|
||||
}
|
||||
int64_t W = x->ne[0];
|
||||
int64_t H = x->ne[1];
|
||||
int64_t T = x->ne[2];
|
||||
int64_t spatial = W * H * T;
|
||||
int64_t groups = x->ne[3] / group_size;
|
||||
x = ggml_reshape_3d(ctx->ggml_ctx, x, spatial, group_size, groups);
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 1, 0, 2, 3));
|
||||
x = ggml_sum_rows(ctx->ggml_ctx, x);
|
||||
x = ggml_ext_cont(ctx->ggml_ctx, ggml_ext_torch_permute(ctx->ggml_ctx, x, 1, 0, 2, 3));
|
||||
x = ggml_reshape_4d(ctx->ggml_ctx, x, W, H, T, groups);
|
||||
return ggml_scale(ctx->ggml_ctx, x, 1.f / static_cast<float>(group_size));
|
||||
}
|
||||
|
||||
class Downsample : public GGMLBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
int64_t out_channels;
|
||||
int64_t factor_t;
|
||||
int64_t factor_s = 2;
|
||||
int64_t factor;
|
||||
int64_t group_size;
|
||||
|
||||
public:
|
||||
Downsample(int64_t in_channels, int64_t out_channels, bool add_temporal_downsample)
|
||||
: in_channels(in_channels),
|
||||
out_channels(out_channels),
|
||||
factor_t(add_temporal_downsample ? 2 : 1),
|
||||
factor(factor_t * factor_s * factor_s),
|
||||
group_size(factor * in_channels / out_channels) {
|
||||
GGML_ASSERT(out_channels % factor == 0);
|
||||
GGML_ASSERT(factor * in_channels % out_channels == 0);
|
||||
blocks["conv"] = std::make_shared<CausalConv3d>(in_channels,
|
||||
out_channels / factor,
|
||||
std::tuple{3, 3, 3});
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
auto conv = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv"]);
|
||||
auto h = conv->forward(ctx, x);
|
||||
|
||||
ggml_tensor* h_first = nullptr;
|
||||
ggml_tensor* x_first = nullptr;
|
||||
if (factor_t == 2) {
|
||||
h_first = ggml_ext_slice(ctx->ggml_ctx, h, 2, 0, 1);
|
||||
h_first = pixel_unshuffle_3d(ctx, h_first, 1, factor_s);
|
||||
h_first = ggml_concat(ctx->ggml_ctx, h_first, h_first, 3);
|
||||
|
||||
x_first = ggml_ext_slice(ctx->ggml_ctx, x, 2, 0, 1);
|
||||
x_first = pixel_unshuffle_3d(ctx, x_first, 1, factor_s);
|
||||
x_first = mean_channel_groups(ctx, x_first, group_size / 2);
|
||||
|
||||
if (x->ne[2] == 1) {
|
||||
return ggml_add(ctx->ggml_ctx, h_first, x_first);
|
||||
}
|
||||
h = ggml_ext_slice(ctx->ggml_ctx, h, 2, 1, h->ne[2]);
|
||||
x = ggml_ext_slice(ctx->ggml_ctx, x, 2, 1, x->ne[2]);
|
||||
}
|
||||
|
||||
GGML_ASSERT(h->ne[2] % factor_t == 0);
|
||||
h = pixel_unshuffle_3d(ctx, h, factor_t, factor_s);
|
||||
x = pixel_unshuffle_3d(ctx, x, factor_t, factor_s);
|
||||
x = mean_channel_groups(ctx, x, group_size);
|
||||
|
||||
if (factor_t == 2) {
|
||||
h = ggml_concat(ctx->ggml_ctx, h_first, h, 2);
|
||||
x = ggml_concat(ctx->ggml_ctx, x_first, x, 2);
|
||||
}
|
||||
return ggml_add(ctx->ggml_ctx, h, x);
|
||||
}
|
||||
};
|
||||
|
||||
class MidBlock : public UnaryBlock {
|
||||
protected:
|
||||
int64_t in_channels;
|
||||
int num_layers;
|
||||
bool add_attention;
|
||||
|
||||
public:
|
||||
MidBlock(int64_t in_channels,
|
||||
int num_layers = 1,
|
||||
bool add_attention = true)
|
||||
: in_channels(in_channels),
|
||||
num_layers(num_layers),
|
||||
add_attention(add_attention) {
|
||||
blocks["block_1"] = std::make_shared<ResnetBlock>(in_channels, in_channels);
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
if (add_attention) {
|
||||
blocks["attn_" + std::to_string(i + 1)] = std::make_shared<AttnBlock>(in_channels);
|
||||
}
|
||||
blocks["block_" + std::to_string(i + 2)] = std::make_shared<ResnetBlock>(in_channels, in_channels);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
||||
// x: [B*C, T, H, W]
|
||||
// return: [B*C, T, H, W]
|
||||
auto block_1 = std::dynamic_pointer_cast<ResnetBlock>(blocks["block_1"]);
|
||||
|
||||
x = block_1->forward(ctx, x);
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
if (add_attention) {
|
||||
auto block = std::dynamic_pointer_cast<AttnBlock>(blocks["attn_" + std::to_string(i + 1)]);
|
||||
x = block->forward(ctx, x);
|
||||
}
|
||||
auto block = std::dynamic_pointer_cast<ResnetBlock>(blocks["block_" + std::to_string(i + 2)]);
|
||||
x = block->forward(ctx, x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class UpBlock : public UnaryBlock {
|
||||
protected:
|
||||
int num_layers;
|
||||
int64_t upsample_out_channels;
|
||||
|
||||
public:
|
||||
UpBlock(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
int num_layers = 1,
|
||||
int64_t upsample_out_channels = 0,
|
||||
bool add_temporal_upsample = true)
|
||||
: num_layers(num_layers),
|
||||
upsample_out_channels(upsample_out_channels) {
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
int64_t IC = i == 0 ? in_channels : out_channels;
|
||||
blocks["block." + std::to_string(i)] = std::make_shared<ResnetBlock>(IC, out_channels);
|
||||
}
|
||||
if (upsample_out_channels > 0) {
|
||||
blocks["upsample"] = std::make_shared<Upsample>(out_channels, upsample_out_channels, add_temporal_upsample);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
||||
return forward(ctx, x, nullptr);
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx,
|
||||
ggml_tensor* x,
|
||||
TemporalConvCarry* carry) {
|
||||
// x: [B*IC, T, H, W]
|
||||
// return: [B*OC, T, H, W] or [B*OC, T, H*2, W*2] or [B*OC, T*2, H*2, W*2]
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
auto block = std::dynamic_pointer_cast<ResnetBlock>(blocks["block." + std::to_string(i)]);
|
||||
x = block->forward(ctx, x, carry);
|
||||
}
|
||||
if (upsample_out_channels > 0) {
|
||||
auto upsample = std::dynamic_pointer_cast<Upsample>(blocks["upsample"]);
|
||||
x = upsample->forward(ctx, x, carry);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class DownBlock : public UnaryBlock {
|
||||
protected:
|
||||
int num_layers;
|
||||
int64_t downsample_out_channels;
|
||||
|
||||
public:
|
||||
DownBlock(int64_t in_channels,
|
||||
int64_t out_channels,
|
||||
int num_layers,
|
||||
int64_t downsample_out_channels = 0,
|
||||
bool add_temporal_downsample = false)
|
||||
: num_layers(num_layers),
|
||||
downsample_out_channels(downsample_out_channels) {
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
int64_t IC = i == 0 ? in_channels : out_channels;
|
||||
blocks["block." + std::to_string(i)] = std::make_shared<ResnetBlock>(IC, out_channels);
|
||||
}
|
||||
if (downsample_out_channels > 0) {
|
||||
blocks["downsample"] = std::make_shared<Downsample>(out_channels,
|
||||
downsample_out_channels,
|
||||
add_temporal_downsample);
|
||||
}
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
||||
for (int i = 0; i < num_layers; i++) {
|
||||
auto block = std::dynamic_pointer_cast<ResnetBlock>(blocks["block." + std::to_string(i)]);
|
||||
x = block->forward(ctx, x);
|
||||
}
|
||||
if (downsample_out_channels > 0) {
|
||||
auto downsample = std::dynamic_pointer_cast<Downsample>(blocks["downsample"]);
|
||||
x = downsample->forward(ctx, x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
};
|
||||
|
||||
class Encoder : public GGMLBlock {
|
||||
protected:
|
||||
int64_t z_channels;
|
||||
std::vector<int64_t> block_out_channels;
|
||||
|
||||
public:
|
||||
Encoder(int64_t in_channels = 3,
|
||||
int64_t z_channels = 32,
|
||||
std::vector<int64_t> block_out_channels = {128, 256, 512, 1024, 1024},
|
||||
int layers_per_block = 2,
|
||||
int spatial_compression_ratio = 16,
|
||||
int temporal_compression_ratio = 4,
|
||||
bool downsample_match_channel = true)
|
||||
: z_channels(z_channels),
|
||||
block_out_channels(std::move(block_out_channels)) {
|
||||
blocks["conv_in"] = std::make_shared<CausalConv3d>(in_channels,
|
||||
this->block_out_channels[0],
|
||||
std::tuple{3, 3, 3});
|
||||
|
||||
int spatial_depth = static_cast<int>(std::log2(static_cast<double>(spatial_compression_ratio)));
|
||||
int temporal_start = static_cast<int>(std::log2(static_cast<double>(spatial_compression_ratio / temporal_compression_ratio)));
|
||||
int64_t channels = this->block_out_channels[0];
|
||||
for (int i = 0; i < static_cast<int>(this->block_out_channels.size()); i++) {
|
||||
int64_t out_channels = this->block_out_channels[i];
|
||||
if (i < spatial_depth) {
|
||||
int64_t next_channels = downsample_match_channel ? this->block_out_channels[i + 1] : out_channels;
|
||||
blocks["down." + std::to_string(i)] = std::make_shared<DownBlock>(channels,
|
||||
out_channels,
|
||||
layers_per_block,
|
||||
next_channels,
|
||||
i >= temporal_start);
|
||||
channels = next_channels;
|
||||
} else {
|
||||
blocks["down." + std::to_string(i)] = std::make_shared<DownBlock>(channels,
|
||||
out_channels,
|
||||
layers_per_block);
|
||||
channels = out_channels;
|
||||
}
|
||||
}
|
||||
|
||||
blocks["mid"] = std::make_shared<MidBlock>(channels);
|
||||
blocks["norm_out"] = std::make_shared<WAN::RMS_norm>(channels);
|
||||
blocks["conv_out"] = std::make_shared<CausalConv3d>(channels,
|
||||
z_channels * 2,
|
||||
std::tuple{3, 3, 3});
|
||||
}
|
||||
|
||||
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
auto conv_in = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv_in"]);
|
||||
auto mid = std::dynamic_pointer_cast<MidBlock>(blocks["mid"]);
|
||||
auto norm_out = std::dynamic_pointer_cast<WAN::RMS_norm>(blocks["norm_out"]);
|
||||
auto conv_out = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv_out"]);
|
||||
|
||||
x = conv_in->forward(ctx, x);
|
||||
for (int i = 0; i < static_cast<int>(block_out_channels.size()); i++) {
|
||||
auto down = std::dynamic_pointer_cast<DownBlock>(blocks["down." + std::to_string(i)]);
|
||||
x = down->forward(ctx, x);
|
||||
}
|
||||
x = mid->forward(ctx, x);
|
||||
|
||||
auto shortcut = mean_channel_groups(ctx, x, x->ne[3] / (z_channels * 2));
|
||||
x = norm_out->forward(ctx, x);
|
||||
x = ggml_silu_inplace(ctx->ggml_ctx, x);
|
||||
x = conv_out->forward(ctx, x);
|
||||
x = ggml_add(ctx->ggml_ctx, x, shortcut);
|
||||
return ggml_ext_slice(ctx->ggml_ctx, x, 3, 0, z_channels);
|
||||
}
|
||||
};
|
||||
|
||||
class Decoder : public GGMLBlock {
|
||||
protected:
|
||||
int64_t repeats;
|
||||
std::vector<int64_t> block_out_channels;
|
||||
|
||||
public:
|
||||
Decoder(int64_t in_channels = 32,
|
||||
int64_t out_channels = 3,
|
||||
std::vector<int64_t> block_out_channels = {1024, 1024, 512, 256, 128},
|
||||
int layers_per_block = 2,
|
||||
int spatial_compression_ratio = 16,
|
||||
int temporal_compression_ratio = 4,
|
||||
bool upsample_match_channel = true)
|
||||
: block_out_channels(std::move(block_out_channels)) {
|
||||
repeats = this->block_out_channels[0] / in_channels;
|
||||
blocks["conv_in"] = std::make_shared<CausalConv3d>(in_channels, this->block_out_channels[0], std::tuple{3, 3, 3});
|
||||
blocks["mid"] = std::make_shared<MidBlock>(this->block_out_channels[0]);
|
||||
|
||||
int64_t IC = this->block_out_channels[0];
|
||||
for (int i = 0; i < this->block_out_channels.size(); i++) {
|
||||
int64_t OC = this->block_out_channels[i];
|
||||
bool add_spatial_upsample = i < std::log2(static_cast<double>(spatial_compression_ratio));
|
||||
bool add_temporal_upsample = i < std::log2(static_cast<double>(temporal_compression_ratio));
|
||||
|
||||
if (add_spatial_upsample || add_temporal_upsample) {
|
||||
int64_t upsample_out_channels = upsample_match_channel ? this->block_out_channels[i + 1] : OC;
|
||||
blocks["up." + std::to_string(i)] = std::make_shared<UpBlock>(IC, OC, layers_per_block + 1, upsample_out_channels, add_temporal_upsample);
|
||||
IC = upsample_out_channels;
|
||||
} else {
|
||||
blocks["up." + std::to_string(i)] = std::make_shared<UpBlock>(IC, OC, layers_per_block + 1, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
blocks["norm_out"] = std::make_shared<WAN::RMS_norm>(this->block_out_channels.back());
|
||||
blocks["conv_out"] = std::make_shared<CausalConv3d>(this->block_out_channels.back(), out_channels, std::tuple{3, 3, 3});
|
||||
}
|
||||
|
||||
struct ggml_tensor* forward(GGMLRunnerContext* ctx, struct ggml_tensor* z) {
|
||||
auto conv_in = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv_in"]);
|
||||
auto mid_block = std::dynamic_pointer_cast<MidBlock>(blocks["mid"]);
|
||||
auto norm_out = std::dynamic_pointer_cast<WAN::RMS_norm>(blocks["norm_out"]);
|
||||
auto conv_out = std::dynamic_pointer_cast<CausalConv3d>(blocks["conv_out"]);
|
||||
|
||||
auto h = conv_in->forward(ctx, z);
|
||||
|
||||
auto shortcut = repeat_interleave_channels(ctx, z, repeats, z->ne[0], z->ne[1], z->ne[2]);
|
||||
h = ggml_add(ctx->ggml_ctx, h, shortcut);
|
||||
|
||||
h = mid_block->forward(ctx, h);
|
||||
|
||||
ggml_tensor* output = nullptr;
|
||||
std::vector<ggml_tensor*> carry_input;
|
||||
const int64_t frames = h->ne[2];
|
||||
for (int64_t start = 0; start < frames; start += HUNYUAN_VIDEO_VAE_TEMPORAL_CHUNK_SIZE) {
|
||||
const int64_t end = std::min(start + HUNYUAN_VIDEO_VAE_TEMPORAL_CHUNK_SIZE, frames);
|
||||
auto chunk = ggml_ext_slice(ctx->ggml_ctx, h, 2, start, end);
|
||||
|
||||
std::vector<ggml_tensor*> carry_output;
|
||||
TemporalConvCarry carry{
|
||||
start == 0 ? nullptr : &carry_input,
|
||||
end == frames ? nullptr : &carry_output,
|
||||
};
|
||||
|
||||
for (int i = 0; i < block_out_channels.size(); i++) {
|
||||
auto up_block = std::dynamic_pointer_cast<UpBlock>(blocks["up." + std::to_string(i)]);
|
||||
chunk = up_block->forward(ctx, chunk, &carry);
|
||||
}
|
||||
|
||||
chunk = norm_out->forward(ctx, chunk);
|
||||
chunk = ggml_silu_inplace(ctx->ggml_ctx, chunk); // nonlinearity/swish
|
||||
chunk = conv_out->forward(ctx, chunk, &carry);
|
||||
carry.finish();
|
||||
|
||||
output = output == nullptr ? chunk : ggml_concat(ctx->ggml_ctx, output, chunk, 2);
|
||||
carry_input = std::move(carry_output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
class HunyuanVideoVAERunner : public VAE {
|
||||
protected:
|
||||
bool decode_only;
|
||||
Encoder encoder;
|
||||
Decoder decoder;
|
||||
|
||||
public:
|
||||
HunyuanVideoVAERunner(ggml_backend_t backend,
|
||||
const String2TensorStorage& tensor_storage_map,
|
||||
const std::string& prefix,
|
||||
bool decode_only,
|
||||
SDVersion version,
|
||||
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
||||
: VAE(version, backend, prefix, weight_manager),
|
||||
decode_only(decode_only ||
|
||||
tensor_storage_map.find(prefix + ".encoder.conv_in.conv.weight") == tensor_storage_map.end()) {
|
||||
if (!this->decode_only) {
|
||||
encoder.init(params_ctx, tensor_storage_map, prefix + ".encoder");
|
||||
}
|
||||
decoder.init(params_ctx, tensor_storage_map, prefix + ".decoder");
|
||||
}
|
||||
|
||||
std::string get_desc() override {
|
||||
return "hunyuan_video_vae";
|
||||
}
|
||||
|
||||
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
||||
if (!decode_only) {
|
||||
encoder.get_param_tensors(tensors, weight_prefix + ".encoder");
|
||||
}
|
||||
decoder.get_param_tensors(tensors, weight_prefix + ".decoder");
|
||||
}
|
||||
|
||||
int get_encoder_output_channels(int input_channels) override {
|
||||
SD_UNUSED(input_channels);
|
||||
return 32;
|
||||
}
|
||||
|
||||
sd::Tensor<float> vae_output_to_latents(const sd::Tensor<float>& vae_output,
|
||||
std::shared_ptr<RNG> rng) override {
|
||||
SD_UNUSED(rng);
|
||||
return vae_output;
|
||||
}
|
||||
|
||||
sd::Tensor<float> diffusion_to_vae_latents(const sd::Tensor<float>& latents) override {
|
||||
return latents / 1.03682f;
|
||||
}
|
||||
|
||||
sd::Tensor<float> vae_to_diffusion_latents(const sd::Tensor<float>& latents) override {
|
||||
return latents * 1.03682f;
|
||||
}
|
||||
|
||||
ggml_cgraph* build_graph(const sd::Tensor<float>& input_tensor, bool decode_graph) {
|
||||
size_t graph_size = HUNYUAN_VIDEO_VAE_GRAPH_SIZE;
|
||||
if (decode_graph) {
|
||||
graph_size = std::max(graph_size,
|
||||
HUNYUAN_VIDEO_VAE_GRAPH_SIZE_PER_LATENT_FRAME *
|
||||
static_cast<size_t>(input_tensor.shape()[2]));
|
||||
}
|
||||
ggml_cgraph* gf = new_graph_custom(graph_size);
|
||||
ggml_tensor* input = make_input(input_tensor);
|
||||
auto runner_ctx = get_context();
|
||||
ggml_tensor* output = decode_graph ? decoder.forward(&runner_ctx, input)
|
||||
: encoder.forward(&runner_ctx, input);
|
||||
ggml_build_forward_expand(gf, output);
|
||||
return gf;
|
||||
}
|
||||
|
||||
sd::Tensor<float> _compute(const int n_threads,
|
||||
const sd::Tensor<float>& input,
|
||||
bool decode_graph) override {
|
||||
if (!decode_graph && decode_only) {
|
||||
LOG_ERROR("Hunyuan Video VAE encoder weights are not available");
|
||||
return {};
|
||||
}
|
||||
|
||||
sd::Tensor<float> expanded;
|
||||
if (input.dim() == 4) {
|
||||
expanded = input.unsqueeze(2);
|
||||
}
|
||||
const auto& graph_input = expanded.empty() ? input : expanded;
|
||||
auto get_graph = [&]() -> ggml_cgraph* {
|
||||
return build_graph(graph_input, decode_graph);
|
||||
};
|
||||
auto output = restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph,
|
||||
n_threads,
|
||||
true,
|
||||
true,
|
||||
true),
|
||||
graph_input.dim());
|
||||
if (!output.empty() && input.dim() == 4) {
|
||||
output.squeeze_(2);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Hunyuan
|
||||
|
||||
#endif // __SD_MODEL_VAE_HUNYUAN_VAE_HPP__
|
||||
@@ -528,6 +528,9 @@ public:
|
||||
if (version == VERSION_WAN2_2_TI2V) {
|
||||
z_channels = 48;
|
||||
patch = 2;
|
||||
} else if (sd_version_is_hunyuan_video(version)) {
|
||||
z_channels = 32;
|
||||
patch = 2;
|
||||
} else if (sd_version_is_ltxav(version)) {
|
||||
z_channels = 128;
|
||||
patch = 4;
|
||||
@@ -542,12 +545,12 @@ public:
|
||||
|
||||
ggml_tensor* decode(GGMLRunnerContext* ctx, ggml_tensor* z) {
|
||||
auto decoder = std::dynamic_pointer_cast<TinyVideoDecoder>(blocks["decoder"]);
|
||||
if (sd_version_is_wan(version) || sd_version_is_ltxav(version)) {
|
||||
if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(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_ltxav(version)) {
|
||||
if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(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));
|
||||
}
|
||||
@@ -556,7 +559,7 @@ public:
|
||||
|
||||
ggml_tensor* encode(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
||||
auto encoder = std::dynamic_pointer_cast<TinyVideoEncoder>(blocks["encoder"]);
|
||||
if (sd_version_is_wan(version) || sd_version_is_ltxav(version)) {
|
||||
if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) {
|
||||
// (W, H, T, C) -> (W, H, C, T)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
||||
}
|
||||
@@ -569,7 +572,7 @@ public:
|
||||
}
|
||||
}
|
||||
x = encoder->forward(ctx, x);
|
||||
if (sd_version_is_wan(version) || sd_version_is_ltxav(version)) {
|
||||
if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) {
|
||||
// (W, H, C, T) -> (W, H, T, C)
|
||||
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
int scale_factor = 8;
|
||||
if (version == VERSION_LTXAV) {
|
||||
scale_factor = 32;
|
||||
} else if (version == VERSION_WAN2_2_TI2V) {
|
||||
} else if (version == VERSION_WAN2_2_TI2V || sd_version_is_hunyuan_video(version)) {
|
||||
scale_factor = 16;
|
||||
} else if (sd_version_uses_flux2_vae(version)) {
|
||||
scale_factor = 16;
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
// Image VAE encode is more sensitive to tile boundary context than decode.
|
||||
// Keep the smaller legacy factor for video VAEs, but default image encode
|
||||
// tiles to 64 latent pixels so a 512px SD image is encoded as one tile.
|
||||
const float encode_tile_factor = (sd_version_is_wan(version) || sd_version_is_ltxav(version)) ? 1.30539f : 2.0f;
|
||||
const float encode_tile_factor = (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version)) ? 1.30539f : 2.0f;
|
||||
get_tile_sizes(tile_size_x, tile_size_y, tile_overlap, tiling_params, W, H, encode_tile_factor);
|
||||
LOG_DEBUG("VAE Tile size: %dx%d", tile_size_x, tile_size_y);
|
||||
output = tiled_compute(input,
|
||||
|
||||
Reference in New Issue
Block a user