mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-09-24 20:20:37 +00:00
880 lines
36 KiB
C++
880 lines
36 KiB
C++
#ifndef __SD_MODEL_VAE_TAE_HPP__
|
|
#define __SD_MODEL_VAE_TAE_HPP__
|
|
|
|
#include "core/ggml_extend.hpp"
|
|
#include "model.h"
|
|
|
|
/*
|
|
=================================== TinyAutoEncoder ===================================
|
|
References:
|
|
https://github.com/huggingface/diffusers/blob/main/src/diffusers/model/autoencoders/vae.py
|
|
https://github.com/madebyollin/taesd/blob/main/taesd.py
|
|
|
|
*/
|
|
|
|
class TAEBlock : public UnaryBlock {
|
|
protected:
|
|
int n_in;
|
|
int n_out;
|
|
bool use_midblock_gn;
|
|
|
|
public:
|
|
TAEBlock(int n_in, int n_out, bool use_midblock_gn = false)
|
|
: n_in(n_in), n_out(n_out), use_midblock_gn(use_midblock_gn) {
|
|
blocks["conv.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_in, n_out, {3, 3}, {1, 1}, {1, 1}));
|
|
blocks["conv.2"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_out, n_out, {3, 3}, {1, 1}, {1, 1}));
|
|
blocks["conv.4"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_out, n_out, {3, 3}, {1, 1}, {1, 1}));
|
|
if (n_in != n_out) {
|
|
blocks["skip"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_in, n_out, {1, 1}, {1, 1}, {1, 1}, {1, 1}, false));
|
|
}
|
|
if (use_midblock_gn) {
|
|
int n_gn = n_in * 4;
|
|
blocks["pool.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_in, n_gn, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
|
blocks["pool.1"] = std::shared_ptr<GGMLBlock>(new GroupNorm(4, n_gn));
|
|
// pool.2 is ReLU, handled in forward
|
|
blocks["pool.3"] = std::shared_ptr<GGMLBlock>(new Conv2d(n_gn, n_in, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
|
}
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
|
// x: [n, n_in, h, w]
|
|
// return: [n, n_out, h, w]
|
|
|
|
if (use_midblock_gn) {
|
|
auto pool_0 = std::dynamic_pointer_cast<Conv2d>(blocks["pool.0"]);
|
|
auto pool_1 = std::dynamic_pointer_cast<GroupNorm>(blocks["pool.1"]);
|
|
auto pool_3 = std::dynamic_pointer_cast<Conv2d>(blocks["pool.3"]);
|
|
|
|
auto p = pool_0->forward(ctx, x);
|
|
p = pool_1->forward(ctx, p);
|
|
p = ggml_relu_inplace(ctx->ggml_ctx, p);
|
|
p = pool_3->forward(ctx, p);
|
|
|
|
x = ggml_add(ctx->ggml_ctx, x, p);
|
|
}
|
|
|
|
auto conv_0 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.0"]);
|
|
auto conv_2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.2"]);
|
|
auto conv_4 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.4"]);
|
|
|
|
auto h = conv_0->forward(ctx, x);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv_2->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv_4->forward(ctx, h);
|
|
|
|
if (n_in != n_out) {
|
|
auto skip = std::dynamic_pointer_cast<Conv2d>(blocks["skip"]);
|
|
LOG_VERBOSE("skip");
|
|
x = skip->forward(ctx, x);
|
|
}
|
|
|
|
h = ggml_add(ctx->ggml_ctx, h, x);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class TinyEncoder : public UnaryBlock {
|
|
int in_channels = 3;
|
|
int channels = 64;
|
|
int z_channels = 4;
|
|
int num_blocks = 3;
|
|
|
|
public:
|
|
TinyEncoder(int z_channels = 4, bool use_midblock_gn = false)
|
|
: z_channels(z_channels) {
|
|
int index = 0;
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels, channels, {3, 3}, {1, 1}, {1, 1}));
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
|
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {2, 2}, {1, 1}, {1, 1}, false));
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
|
}
|
|
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {2, 2}, {1, 1}, {1, 1}, false));
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
|
}
|
|
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {2, 2}, {1, 1}, {1, 1}, false));
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels, use_midblock_gn));
|
|
}
|
|
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, z_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
|
// x: [n, in_channels, h, w]
|
|
// return: [n, z_channels, h/8, w/8]
|
|
|
|
for (int i = 0; i < num_blocks * 3 + 6; i++) {
|
|
auto block = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(i)]);
|
|
|
|
x = block->forward(ctx, x);
|
|
}
|
|
|
|
return x;
|
|
}
|
|
};
|
|
|
|
class TinyDecoder : public UnaryBlock {
|
|
int z_channels = 4;
|
|
int channels = 64;
|
|
int out_channels = 3;
|
|
int num_blocks = 3;
|
|
|
|
public:
|
|
TinyDecoder(int z_channels = 4, bool use_midblock_gn = false)
|
|
: z_channels(z_channels) {
|
|
int index = 0;
|
|
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(z_channels, channels, {3, 3}, {1, 1}, {1, 1}));
|
|
index++; // nn.ReLU()
|
|
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels, use_midblock_gn));
|
|
}
|
|
index++; // nn.Upsample()
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {1, 1}, {1, 1}, {1, 1}, false));
|
|
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
|
}
|
|
index++; // nn.Upsample()
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {1, 1}, {1, 1}, {1, 1}, false));
|
|
|
|
for (int i = 0; i < num_blocks; i++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
|
}
|
|
index++; // nn.Upsample()
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels, {3, 3}, {1, 1}, {1, 1}, {1, 1}, false));
|
|
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TAEBlock(channels, channels));
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* z) override {
|
|
// z: [n, z_channels, h, w]
|
|
// return: [n, out_channels, h*8, w*8]
|
|
|
|
auto h = ggml_ext_scale(ctx->ggml_ctx, z, 1.0f / 3.0f);
|
|
h = ggml_tanh_inplace(ctx->ggml_ctx, h);
|
|
h = ggml_ext_scale(ctx->ggml_ctx, h, 3.0f);
|
|
|
|
for (int i = 0; i < num_blocks * 3 + 10; i++) {
|
|
if (blocks.find(std::to_string(i)) == blocks.end()) {
|
|
if (i == 1) {
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
} else {
|
|
h = ggml_upscale(ctx->ggml_ctx, h, 2, GGML_SCALE_MODE_NEAREST);
|
|
}
|
|
continue;
|
|
}
|
|
auto block = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(i)]);
|
|
|
|
h = block->forward(ctx, h);
|
|
}
|
|
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class TPool : public UnaryBlock {
|
|
int stride;
|
|
|
|
public:
|
|
TPool(int channels, int stride)
|
|
: stride(stride) {
|
|
blocks["conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels * stride, channels, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
|
auto conv = std::dynamic_pointer_cast<UnaryBlock>(blocks["conv"]);
|
|
auto h = x;
|
|
if (stride != 1) {
|
|
h = ggml_reshape_4d(ctx->ggml_ctx, h, h->ne[0], h->ne[1], h->ne[2] * stride, h->ne[3] / stride);
|
|
}
|
|
h = conv->forward(ctx, h);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class TGrow : public UnaryBlock {
|
|
int stride;
|
|
|
|
public:
|
|
TGrow(int channels, int stride)
|
|
: stride(stride) {
|
|
blocks["conv"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, channels * stride, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x) override {
|
|
auto conv = std::dynamic_pointer_cast<UnaryBlock>(blocks["conv"]);
|
|
auto h = conv->forward(ctx, x);
|
|
if (stride != 1) {
|
|
h = ggml_reshape_4d(ctx->ggml_ctx, h, h->ne[0], h->ne[1], h->ne[2] / stride, h->ne[3] * stride);
|
|
}
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class MemBlock : public GGMLBlock {
|
|
bool has_skip_conv = false;
|
|
|
|
public:
|
|
MemBlock(int channels, int out_channels)
|
|
: has_skip_conv(channels != out_channels) {
|
|
blocks["conv.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels * 2, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
blocks["conv.2"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
blocks["conv.4"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
if (has_skip_conv) {
|
|
blocks["skip"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, out_channels, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
|
}
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* past) {
|
|
// x: [n, channels, h, w]
|
|
auto conv0 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.0"]);
|
|
auto conv1 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.2"]);
|
|
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.4"]);
|
|
|
|
auto h = ggml_concat(ctx->ggml_ctx, x, past, 2);
|
|
h = conv0->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv1->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv2->forward(ctx, h);
|
|
|
|
auto skip = x;
|
|
if (has_skip_conv) {
|
|
auto skip_conv = std::dynamic_pointer_cast<Conv2d>(blocks["skip"]);
|
|
skip = skip_conv->forward(ctx, x);
|
|
}
|
|
h = ggml_add_inplace(ctx->ggml_ctx, h, skip);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class WideMemBlock : public GGMLBlock {
|
|
bool has_skip_conv = false;
|
|
|
|
public:
|
|
WideMemBlock(int channels, int out_channels)
|
|
: has_skip_conv(channels != out_channels) {
|
|
int groups = std::max(1, out_channels / 64);
|
|
blocks["conv.0"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels * 2, out_channels, {1, 1}, {1, 1}));
|
|
blocks["conv.2"] = std::shared_ptr<GGMLBlock>(new Conv2d_grouped(out_channels, out_channels, groups, {3, 3}, {1, 1}, {1, 1}));
|
|
blocks["conv.4"] = std::shared_ptr<GGMLBlock>(new Conv2d(out_channels, out_channels, {1, 1}, {1, 1}));
|
|
blocks["conv.6"] = std::shared_ptr<GGMLBlock>(new Conv2d_grouped(out_channels, out_channels, groups, {3, 3}, {1, 1}, {1, 1}));
|
|
if (has_skip_conv) {
|
|
blocks["skip"] = std::shared_ptr<GGMLBlock>(new Conv2d(channels, out_channels, {1, 1}, {1, 1}, {0, 0}, {1, 1}, false));
|
|
}
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* x, ggml_tensor* past) {
|
|
// x: [n, channels, h, w]
|
|
auto conv0 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.0"]);
|
|
auto conv1 = std::dynamic_pointer_cast<Conv2d_grouped>(blocks["conv.2"]);
|
|
auto conv2 = std::dynamic_pointer_cast<Conv2d>(blocks["conv.4"]);
|
|
auto conv3 = std::dynamic_pointer_cast<Conv2d_grouped>(blocks["conv.6"]);
|
|
|
|
auto h = ggml_concat(ctx->ggml_ctx, x, past, 2);
|
|
h = conv0->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv1->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv2->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
h = conv3->forward(ctx, h);
|
|
|
|
auto skip = x;
|
|
if (has_skip_conv) {
|
|
auto skip_conv = std::dynamic_pointer_cast<Conv2d>(blocks["skip"]);
|
|
skip = skip_conv->forward(ctx, x);
|
|
}
|
|
h = ggml_add_inplace(ctx->ggml_ctx, h, skip);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
ggml_tensor*
|
|
patchify(ggml_context* ctx,
|
|
ggml_tensor* x,
|
|
int64_t patch_size,
|
|
int64_t b = 1) {
|
|
// x: [f, b*c, h*q, w*r]
|
|
// return: [f, b*c*r*q, h, w]
|
|
if (patch_size == 1) {
|
|
return x;
|
|
}
|
|
int64_t r = patch_size;
|
|
int64_t q = patch_size;
|
|
|
|
int64_t W = x->ne[0];
|
|
int64_t H = x->ne[1];
|
|
int64_t C = x->ne[2];
|
|
int64_t f = x->ne[3];
|
|
|
|
int64_t w = W / r;
|
|
int64_t h = H / q;
|
|
|
|
x = ggml_reshape_4d(ctx, x, W, q, h, C * f); // [W, q, h, C*f]
|
|
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [W, h, q, C*f]
|
|
x = ggml_reshape_4d(ctx, x, r, w, h, q * C * f); // [r, w, h, q*C*f]
|
|
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 1, 2, 0, 3)); // [w, h, r, q*C*f]
|
|
x = ggml_reshape_4d(ctx, x, w, h, r * q * C, f); // [f, b*c*r*q, h, w]
|
|
|
|
return x;
|
|
}
|
|
|
|
ggml_tensor* unpatchify(ggml_context* ctx,
|
|
ggml_tensor* x,
|
|
int64_t patch_size,
|
|
int64_t b = 1) {
|
|
// x: [f, b*c*r*q, h, w]
|
|
// return: [f, b*c, h*q, w*r]
|
|
if (patch_size == 1) {
|
|
return x;
|
|
}
|
|
int64_t r = patch_size;
|
|
int64_t q = patch_size;
|
|
int64_t c = x->ne[2] / b / q / r;
|
|
int64_t f = x->ne[3];
|
|
int64_t h = x->ne[1];
|
|
int64_t w = x->ne[0];
|
|
|
|
x = ggml_reshape_4d(ctx, x, w, h, r, q * c * b * f); // [q*c*b*f, r, h, w]
|
|
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 2, 0, 1, 3)); // [r, w, h, q*c*b*f]
|
|
x = ggml_reshape_4d(ctx, x, r * w, h, q, c * b * f); // [c*b*f, q, h, r*w]
|
|
x = ggml_ext_cont(ctx, ggml_ext_torch_permute(ctx, x, 0, 2, 1, 3)); // [r*w, q, h, c*b*f]
|
|
x = ggml_reshape_4d(ctx, x, r * w, q * h, c * b, f);
|
|
|
|
return x;
|
|
}
|
|
|
|
class TinyVideoEncoder : public UnaryBlock {
|
|
int in_channels = 3;
|
|
int hidden = 64;
|
|
int z_channels = 4;
|
|
int num_blocks = 3;
|
|
int num_layers = 3;
|
|
int patch_size = 1;
|
|
|
|
public:
|
|
int t_downscale = 1;
|
|
TinyVideoEncoder(int z_channels = 4, int patch_size = 1, std::vector<bool> time_downscale = {true, true, false})
|
|
: z_channels(z_channels), patch_size(patch_size) {
|
|
t_downscale = 1;
|
|
for (bool downscale : time_downscale) {
|
|
if (downscale) {
|
|
t_downscale *= 2;
|
|
}
|
|
}
|
|
int index = 0;
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(in_channels * patch_size * patch_size, hidden, {3, 3}, {1, 1}, {1, 1}));
|
|
index++; // nn.ReLU()
|
|
for (int i = 0; i < num_layers; i++) {
|
|
int stride = time_downscale[i] ? 2 : 1;
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TPool(hidden, stride));
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(hidden, hidden, {3, 3}, {2, 2}, {1, 1}, {1, 1}, false));
|
|
for (int j = 0; j < num_blocks; j++) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new MemBlock(hidden, hidden));
|
|
}
|
|
}
|
|
blocks[std::to_string(index)] = std::shared_ptr<GGMLBlock>(new Conv2d(hidden, z_channels, {3, 3}, {1, 1}, {1, 1}));
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* z) override {
|
|
auto first_conv = std::dynamic_pointer_cast<Conv2d>(blocks["0"]);
|
|
|
|
if (patch_size > 1) {
|
|
z = patchify(ctx->ggml_ctx, z, patch_size, 1);
|
|
}
|
|
|
|
auto h = first_conv->forward(ctx, z);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
|
|
int index = 2;
|
|
for (int i = 0; i < num_layers; i++) {
|
|
auto pool = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
|
auto conv = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
|
|
|
h = pool->forward(ctx, h);
|
|
h = conv->forward(ctx, h);
|
|
for (int j = 0; j < num_blocks; j++) {
|
|
auto block = std::dynamic_pointer_cast<MemBlock>(blocks[std::to_string(index++)]);
|
|
auto mem = ggml_ext_pad_ext(ctx->ggml_ctx, ctx->backend, h, 0, 0, 0, 0, 0, 0, 1, 0);
|
|
mem = ggml_view_4d(ctx->ggml_ctx, mem, h->ne[0], h->ne[1], h->ne[2], h->ne[3], h->nb[1], h->nb[2], h->nb[3], 0);
|
|
h = block->forward(ctx, h, mem);
|
|
}
|
|
}
|
|
auto last_conv = std::dynamic_pointer_cast<Conv2d>(blocks[std::to_string(index)]);
|
|
h = last_conv->forward(ctx, h);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class TinyVideoDecoder : public UnaryBlock {
|
|
int z_channels = 4;
|
|
int out_channels = 3;
|
|
int num_blocks = 3;
|
|
static const int num_layers = 3;
|
|
int channels[num_layers + 1] = {256, 128, 64, 64};
|
|
int patch_size = 1;
|
|
bool is_wide = false;
|
|
|
|
public:
|
|
int t_upscale = 1;
|
|
TinyVideoDecoder(int z_channels = 4, int patch_size = 1, std::vector<bool> time_upscale = {false, true, true}, bool is_wide = false)
|
|
: z_channels(z_channels), patch_size(patch_size), is_wide(is_wide) {
|
|
t_upscale = 1;
|
|
if (is_wide) {
|
|
channels[0] = 1024;
|
|
channels[1] = 512;
|
|
channels[2] = 256;
|
|
}
|
|
|
|
for (bool upscale : time_upscale) {
|
|
if (upscale) {
|
|
t_upscale *= 2;
|
|
}
|
|
}
|
|
int index = 1; // Clamp()
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(z_channels, channels[0], {3, 3}, {1, 1}, {1, 1}));
|
|
index++; // nn.ReLU()
|
|
for (int i = 0; i < num_layers; i++) {
|
|
int stride = time_upscale[i] ? 2 : 1;
|
|
for (int j = 0; j < num_blocks; j++) {
|
|
if (is_wide) {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new WideMemBlock(channels[i], channels[i]));
|
|
} else {
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new MemBlock(channels[i], channels[i]));
|
|
}
|
|
}
|
|
index++; // nn.Upsample()
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new TGrow(channels[i], stride));
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels[i], channels[i + 1], {3, 3}, {1, 1}, {1, 1}, {1, 1}, false));
|
|
}
|
|
index++; // nn.ReLU()
|
|
blocks[std::to_string(index++)] = std::shared_ptr<GGMLBlock>(new Conv2d(channels[num_layers], out_channels * patch_size * patch_size, {3, 3}, {1, 1}, {1, 1}));
|
|
}
|
|
|
|
ggml_tensor* forward(GGMLRunnerContext* ctx, ggml_tensor* z) override {
|
|
auto first_conv = std::dynamic_pointer_cast<Conv2d>(blocks["1"]);
|
|
|
|
// Clamp()
|
|
auto h = ggml_ext_scale(ctx->ggml_ctx,
|
|
ggml_tanh_inplace(ctx->ggml_ctx,
|
|
ggml_ext_scale(ctx->ggml_ctx, z, 1.0f / 3.0f)),
|
|
3.0f,
|
|
true);
|
|
|
|
h = first_conv->forward(ctx, h);
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
int index = 3;
|
|
for (int i = 0; i < num_layers; i++) {
|
|
for (int j = 0; j < num_blocks; j++) {
|
|
auto mem = ggml_ext_pad_ext(ctx->ggml_ctx, ctx->backend, h, 0, 0, 0, 0, 0, 0, 1, 0);
|
|
mem = ggml_view_4d(ctx->ggml_ctx, mem, h->ne[0], h->ne[1], h->ne[2], h->ne[3], h->nb[1], h->nb[2], h->nb[3], 0);
|
|
if (is_wide) {
|
|
auto block = std::dynamic_pointer_cast<WideMemBlock>(blocks[std::to_string(index++)]);
|
|
h = block->forward(ctx, h, mem);
|
|
} else {
|
|
auto block = std::dynamic_pointer_cast<MemBlock>(blocks[std::to_string(index++)]);
|
|
h = block->forward(ctx, h, mem);
|
|
}
|
|
}
|
|
// upsample
|
|
index++;
|
|
h = ggml_upscale(ctx->ggml_ctx, h, 2, GGML_SCALE_MODE_NEAREST);
|
|
auto block = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
|
h = block->forward(ctx, h);
|
|
block = std::dynamic_pointer_cast<UnaryBlock>(blocks[std::to_string(index++)]);
|
|
h = block->forward(ctx, h);
|
|
}
|
|
h = ggml_relu_inplace(ctx->ggml_ctx, h);
|
|
|
|
auto last_conv = std::dynamic_pointer_cast<Conv2d>(blocks[std::to_string(++index)]);
|
|
h = last_conv->forward(ctx, h);
|
|
if (patch_size > 1) {
|
|
h = unpatchify(ctx->ggml_ctx, h, patch_size, 1);
|
|
}
|
|
// shape(W, H, 3, (t_upscale - 1) + T) => shape(W, H, 3, T)
|
|
h = ggml_view_4d(ctx->ggml_ctx, h, h->ne[0], h->ne[1], h->ne[2], h->ne[3] - (t_upscale - 1), h->nb[1], h->nb[2], h->nb[3], (t_upscale - 1) * h->nb[3]);
|
|
return h;
|
|
}
|
|
};
|
|
|
|
class TAEHV : public GGMLBlock {
|
|
protected:
|
|
bool decode_only;
|
|
SDVersion version;
|
|
bool is_wide;
|
|
|
|
public:
|
|
int z_channels = 16;
|
|
std::vector<bool> time_downscale = {true, true, false};
|
|
std::vector<bool> time_upscale = {false, true, true};
|
|
|
|
public:
|
|
TAEHV(bool decode_only = true, SDVersion version = VERSION_WAN2, bool is_wide = false)
|
|
: decode_only(decode_only), version(version), is_wide(is_wide) {
|
|
int patch = 1;
|
|
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;
|
|
time_downscale = {true, true, true};
|
|
time_upscale = {true, true, true};
|
|
} else if (sd_version_is_minimax_h3(version)) {
|
|
z_channels = 24;
|
|
patch = 2;
|
|
time_downscale = {true, true, false};
|
|
}
|
|
blocks["decoder"] = std::shared_ptr<GGMLBlock>(new TinyVideoDecoder(z_channels, patch, time_upscale, is_wide));
|
|
if (!decode_only) {
|
|
blocks["encoder"] = std::shared_ptr<GGMLBlock>(new TinyVideoEncoder(z_channels, patch, time_downscale));
|
|
}
|
|
}
|
|
|
|
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_hunyuan_video(version) || sd_version_is_ltxav(version) || sd_version_is_minimax_h3(version)) {
|
|
// (W, H, C, T) -> (W, H, T, C)
|
|
z = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, z, 0, 1, 3, 2));
|
|
}
|
|
auto result = decoder->forward(ctx, z);
|
|
|
|
if (sd_version_is_minimax_h3(version)) {
|
|
int64_t num_frames = result->ne[3];
|
|
int64_t chunk_frames = 5 * decoder->t_upscale;
|
|
int64_t pad = (chunk_frames - (num_frames % chunk_frames)) % chunk_frames;
|
|
|
|
result = ggml_ext_pad_ext(ctx->ggml_ctx, ctx->backend, result, 0, 0, 0, 0, 0, 0, 0, pad, false, false);
|
|
|
|
int64_t num_chunks = (num_frames + pad) / chunk_frames;
|
|
auto to_trim = decoder->t_upscale - 1;
|
|
std::vector<ggml_tensor*> to_concat = {};
|
|
for (int i = 0; i < num_chunks; i++) {
|
|
auto chunk = ggml_view_4d(ctx->ggml_ctx, result,
|
|
result->ne[0], result->ne[1], result->ne[2], chunk_frames - to_trim,
|
|
result->nb[1], result->nb[2], result->nb[3],
|
|
i * chunk_frames * result->nb[3]);
|
|
to_concat.push_back(chunk);
|
|
}
|
|
result = ggml_ext_vec_concat(ctx->ggml_ctx, to_concat, 3);
|
|
result = ggml_view_4d(ctx->ggml_ctx, result,
|
|
result->ne[0], result->ne[1], result->ne[2],
|
|
result->ne[3] - decoder->t_upscale * 3,
|
|
result->nb[1], result->nb[2], result->nb[3], 0);
|
|
}
|
|
|
|
if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version) || sd_version_is_minimax_h3(version)) {
|
|
// (W, H, T, C) -> (W, H, C, T)
|
|
result = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, result, 0, 1, 3, 2));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ggml_tensor* encode_h3(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
auto encoder = std::dynamic_pointer_cast<TinyVideoEncoder>(blocks["encoder"]);
|
|
|
|
int64_t num_frames = x->ne[3];
|
|
int64_t pad = (17 - (num_frames % 17)) % 17;
|
|
|
|
if (pad > 0) {
|
|
auto last_frame = ggml_view_4d(ctx->ggml_ctx, x,
|
|
x->ne[0], x->ne[1], x->ne[2], 1,
|
|
x->nb[1], x->nb[2], x->nb[3],
|
|
(num_frames - 1) * x->nb[3]);
|
|
for (int i = 0; i < pad; i++) {
|
|
x = ggml_concat(ctx->ggml_ctx, x, last_frame, 3);
|
|
}
|
|
}
|
|
|
|
int64_t T_padded = x->ne[3];
|
|
int64_t num_chunks = T_padded / 17;
|
|
|
|
auto zero_frame = ggml_view_4d(ctx->ggml_ctx, x,
|
|
x->ne[0], x->ne[1], x->ne[2], 1,
|
|
x->nb[1], x->nb[2], x->nb[3], 0);
|
|
auto zeros_1 = ggml_scale(ctx->ggml_ctx, ggml_cont(ctx->ggml_ctx, zero_frame), 0.0f);
|
|
auto zeros_3 = zeros_1;
|
|
for (int i = 1; i < 3; i++) {
|
|
zeros_3 = ggml_concat(ctx->ggml_ctx, zeros_3, zeros_1, 3);
|
|
}
|
|
ggml_tensor* out = nullptr;
|
|
if (false) {
|
|
std::vector<ggml_tensor*> to_concat = {};
|
|
for (int i = 0; i < num_chunks; i++) {
|
|
auto chunk = ggml_view_4d(ctx->ggml_ctx, x,
|
|
x->ne[0], x->ne[1], x->ne[2], 17,
|
|
x->nb[1], x->nb[2], x->nb[3],
|
|
i * 17 * x->nb[3]);
|
|
|
|
auto chunk_padded = ggml_concat(ctx->ggml_ctx, zeros_3, chunk, 3);
|
|
|
|
to_concat.push_back(chunk_padded);
|
|
}
|
|
ggml_tensor* x_in = ggml_ext_vec_concat(ctx->ggml_ctx, to_concat, 3);
|
|
out = encoder->forward(ctx, x_in);
|
|
} else {
|
|
std::vector<ggml_tensor*> to_concat = {};
|
|
for (int i = 0; i < num_chunks; i++) {
|
|
auto chunk = ggml_view_4d(ctx->ggml_ctx, x,
|
|
x->ne[0], x->ne[1], x->ne[2], 17,
|
|
x->nb[1], x->nb[2], x->nb[3],
|
|
i * 17 * x->nb[3]);
|
|
|
|
auto chunk_padded = ggml_concat(ctx->ggml_ctx, zeros_3, chunk, 3);
|
|
|
|
auto chunk_out = encoder->forward(ctx, chunk_padded);
|
|
// auto chunk_out = encoder->forward_seq(ctx, chunk_padded); // ~same vram usage, and straight-up slower. it's already sequential enough
|
|
|
|
to_concat.push_back(chunk_out);
|
|
}
|
|
out = ggml_ext_vec_concat(ctx->ggml_ctx, to_concat, 3);
|
|
}
|
|
|
|
// Return x[:, :-3] - drop the last 3 elements in the T dimension
|
|
int64_t out_T = out->ne[3];
|
|
out = ggml_view_4d(ctx->ggml_ctx, out,
|
|
out->ne[0], out->ne[1], out->ne[2], out_T - 3,
|
|
out->nb[1], out->nb[2], out->nb[3], 0);
|
|
|
|
return ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, out, 0, 1, 3, 2));
|
|
}
|
|
|
|
ggml_tensor* encode(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
if (sd_version_is_wan(version) || sd_version_is_hunyuan_video(version) || sd_version_is_ltxav(version) || (sd_version_is_minimax_h3(version) && x->ne[3] > 1)) {
|
|
// (W, H, T, C) -> (W, H, C, T)
|
|
x = ggml_cont(ctx->ggml_ctx, ggml_permute(ctx->ggml_ctx, x, 0, 1, 3, 2));
|
|
}
|
|
if (sd_version_is_minimax_h3(version)) {
|
|
return encode_h3(ctx, x);
|
|
}
|
|
|
|
auto encoder = std::dynamic_pointer_cast<TinyVideoEncoder>(blocks["encoder"]);
|
|
|
|
int64_t num_frames = x->ne[3];
|
|
if (num_frames % encoder->t_downscale) {
|
|
// pad to multiple of encoder->t_downscale at the end
|
|
auto last_frame = ggml_view_4d(ctx->ggml_ctx, x, x->ne[0], x->ne[1], x->ne[2], 1, x->nb[1], x->nb[2], x->nb[3], (num_frames - 1) * x->nb[3]);
|
|
for (int i = 0; i < encoder->t_downscale - num_frames % encoder->t_downscale; i++) {
|
|
x = ggml_concat(ctx->ggml_ctx, x, last_frame, 3);
|
|
}
|
|
}
|
|
x = encoder->forward(ctx, x);
|
|
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));
|
|
}
|
|
return x;
|
|
}
|
|
};
|
|
|
|
class TAESD : public GGMLBlock {
|
|
protected:
|
|
bool decode_only;
|
|
bool taef2 = false;
|
|
|
|
public:
|
|
int z_channels = 4;
|
|
|
|
public:
|
|
TAESD(bool decode_only = true, SDVersion version = VERSION_SD1)
|
|
: decode_only(decode_only) {
|
|
bool use_midblock_gn = false;
|
|
taef2 = sd_version_uses_flux2_vae(version);
|
|
|
|
if (sd_version_is_dit(version)) {
|
|
z_channels = 16;
|
|
}
|
|
if (taef2) {
|
|
z_channels = 32;
|
|
use_midblock_gn = true;
|
|
}
|
|
blocks["decoder.layers"] = std::shared_ptr<GGMLBlock>(new TinyDecoder(z_channels, use_midblock_gn));
|
|
|
|
if (!decode_only) {
|
|
blocks["encoder.layers"] = std::shared_ptr<GGMLBlock>(new TinyEncoder(z_channels, use_midblock_gn));
|
|
}
|
|
}
|
|
|
|
ggml_tensor* decode(GGMLRunnerContext* ctx, ggml_tensor* z) {
|
|
auto decoder = std::dynamic_pointer_cast<TinyDecoder>(blocks["decoder.layers"]);
|
|
if (taef2) {
|
|
z = unpatchify(ctx->ggml_ctx, z, 2);
|
|
}
|
|
return decoder->forward(ctx, z);
|
|
}
|
|
|
|
ggml_tensor* encode(GGMLRunnerContext* ctx, ggml_tensor* x) {
|
|
auto encoder = std::dynamic_pointer_cast<TinyEncoder>(blocks["encoder.layers"]);
|
|
auto z = encoder->forward(ctx, x);
|
|
if (taef2) {
|
|
z = patchify(ctx->ggml_ctx, z, 2);
|
|
}
|
|
return z;
|
|
}
|
|
};
|
|
|
|
struct TinyImageAutoEncoder : public VAE {
|
|
TAESD taesd;
|
|
bool decode_only = false;
|
|
|
|
TinyImageAutoEncoder(ggml_backend_t backend,
|
|
const String2TensorStorage& tensor_storage_map,
|
|
const std::string prefix,
|
|
bool decoder_only = true,
|
|
SDVersion version = VERSION_SD1,
|
|
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
|
: VAE(version, backend, "tae", weight_manager),
|
|
decode_only(decoder_only),
|
|
taesd(decoder_only, version) {
|
|
scale_input = false;
|
|
taesd.init(params_ctx, tensor_storage_map, prefix);
|
|
}
|
|
|
|
std::string get_desc() override {
|
|
return "taesd";
|
|
}
|
|
|
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
|
taesd.get_param_tensors(tensors, weight_prefix);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
sd::Tensor<float> vae_to_diffusion_latents(const sd::Tensor<float>& latents) override {
|
|
return latents;
|
|
}
|
|
|
|
int get_encoder_output_channels(int input_channels) {
|
|
return taesd.z_channels;
|
|
}
|
|
|
|
ggml_cgraph* build_graph(const sd::Tensor<float>& z_tensor, bool decode_graph) {
|
|
ggml_cgraph* gf = ggml_new_graph(compute_ctx);
|
|
ggml_tensor* z = make_input(z_tensor);
|
|
auto runner_ctx = get_context();
|
|
ggml_tensor* out = decode_graph ? taesd.decode(&runner_ctx, z) : taesd.encode(&runner_ctx, z);
|
|
ggml_build_forward_expand(gf, out);
|
|
return gf;
|
|
}
|
|
|
|
sd::Tensor<float> _compute(const int n_threads,
|
|
const sd::Tensor<float>& z_tensor,
|
|
bool decode_graph) override {
|
|
auto get_graph = [&]() -> ggml_cgraph* {
|
|
return build_graph(z_tensor, decode_graph);
|
|
};
|
|
|
|
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), z_tensor.dim());
|
|
}
|
|
};
|
|
|
|
struct TinyVideoAutoEncoder : public VAE {
|
|
TAEHV taehv;
|
|
bool decode_only = false;
|
|
bool is_wide = false;
|
|
|
|
TinyVideoAutoEncoder(ggml_backend_t backend,
|
|
const String2TensorStorage& tensor_storage_map,
|
|
const std::string prefix,
|
|
bool decoder_only = true,
|
|
SDVersion version = VERSION_WAN2,
|
|
std::shared_ptr<RunnerWeightManager> weight_manager = nullptr)
|
|
: VAE(version, backend, "tae", weight_manager),
|
|
decode_only(decoder_only) {
|
|
for (auto tensor_storage : tensor_storage_map) {
|
|
if (tensor_storage.first.find(prefix + ".3.conv.6.weight") != std::string::npos) {
|
|
is_wide = true;
|
|
break;
|
|
}
|
|
}
|
|
taehv = TAEHV(decoder_only, version, is_wide);
|
|
scale_input = false;
|
|
taehv.init(params_ctx, tensor_storage_map, prefix);
|
|
}
|
|
|
|
std::string get_desc() override {
|
|
return "taehv";
|
|
}
|
|
|
|
bool supports_temporal_tiling(VAETemporalDirection direction) const override {
|
|
return direction == VAETemporalDirection::DECODE && !sd_version_is_minimax_h3(version);
|
|
}
|
|
|
|
int get_temporal_tile_output_scale(VAETemporalDirection direction) const override {
|
|
SD_UNUSED(direction);
|
|
int scale = 1;
|
|
for (bool upscale : taehv.time_upscale) {
|
|
if (upscale) {
|
|
scale *= 2;
|
|
}
|
|
}
|
|
return scale;
|
|
}
|
|
|
|
void get_param_tensors(std::map<std::string, ggml_tensor*>& tensors) override {
|
|
taehv.get_param_tensors(tensors, weight_prefix);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
sd::Tensor<float> vae_to_diffusion_latents(const sd::Tensor<float>& latents) override {
|
|
return latents;
|
|
}
|
|
|
|
int get_encoder_output_channels(int input_channels) {
|
|
return taehv.z_channels;
|
|
}
|
|
|
|
ggml_cgraph* build_graph(const sd::Tensor<float>& z_tensor, bool decode_graph) {
|
|
ggml_cgraph* gf = decode_graph && is_wide ? ggml_new_graph_custom(compute_ctx, 4096, false)
|
|
: ggml_new_graph(compute_ctx);
|
|
ggml_tensor* z = make_input(z_tensor);
|
|
auto runner_ctx = get_context();
|
|
ggml_tensor* out = decode_graph ? taehv.decode(&runner_ctx, z) : taehv.encode(&runner_ctx, z);
|
|
ggml_build_forward_expand(gf, out);
|
|
return gf;
|
|
}
|
|
|
|
sd::Tensor<float> _compute(const int n_threads,
|
|
const sd::Tensor<float>& z_tensor,
|
|
bool decode_graph) override {
|
|
auto get_graph = [&]() -> ggml_cgraph* {
|
|
return build_graph(z_tensor, decode_graph);
|
|
};
|
|
|
|
return restore_trailing_singleton_dims(GGMLRunner::compute<float>(get_graph, n_threads, false), z_tensor.dim());
|
|
}
|
|
};
|
|
|
|
#endif // __SD_MODEL_VAE_TAE_HPP__
|