Skip to content

Commit 6ce5f97

Browse files
aegioscycursoragent
andcommitted
fix: bypass spatial VAE tiling for Wan 2.1 I2V video encode/decode
When vae_tiling is enabled, vae_encode() and decode_first_stage() use a 2D spatial tiling path that is incompatible with the 4D video tensors produced by the Wan video VAE. ENCODE: vae_encode() pre-allocates its result as [W/8, H/8, z_dim, batch]. For Wan 2.1 I2V the WanVAE encoder produces [W/8, H/8, t_latent, z_dim]. Passing encode_video=false (the default) hits the tiling branch and produces a transposed tensor instead of the correct shape, causing assertion failures. DECODE: decode_first_stage() checks vae_tiling_params.enabled without a !decode_video guard. The Wan video latent is fed through the 2D tiling path, which produces all-black (zero) output frames. Fix: - Add encode_video=true to the Wan 2.1 I2V concat-latent encode call - Add !decode_video guard to the tiling branch in decode_first_stage() This allows video VAE operations to bypass tiling while keeping it enabled for regular image inference. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 10f8d44 commit 6ce5f97

2 files changed

Lines changed: 14 additions & 9 deletions

File tree

src/stable-diffusion.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2410,17 +2410,17 @@ class StableDiffusionGGML {
24102410
return latent_frames_to_video_frames(video_frames_to_latent_frames(frames));
24112411
}
24122412

2413-
sd::Tensor<float> encode_to_vae_latents(const sd::Tensor<float>& x) {
2414-
auto latents = first_stage_model->encode(n_threads, x, vae_tiling_params, circular_x, circular_y);
2413+
sd::Tensor<float> encode_to_vae_latents(const sd::Tensor<float>& x, bool encode_video = false) {
2414+
auto latents = first_stage_model->encode(n_threads, x, vae_tiling_params, encode_video, circular_x, circular_y);
24152415
if (latents.empty()) {
24162416
return {};
24172417
}
24182418
latents = first_stage_model->vae_output_to_latents(latents, rng);
24192419
return latents;
24202420
}
24212421

2422-
sd::Tensor<float> encode_first_stage(const sd::Tensor<float>& x) {
2423-
auto latents = encode_to_vae_latents(x);
2422+
sd::Tensor<float> encode_first_stage(const sd::Tensor<float>& x, bool encode_video = false) {
2423+
auto latents = encode_to_vae_latents(x, encode_video);
24242424
if (latents.empty()) {
24252425
return {};
24262426
}
@@ -4905,7 +4905,7 @@ static std::optional<ImageGenerationLatents> prepare_video_generation_latents(sd
49054905
sd::ops::slice_assign(&image, 2, request->frames - 1, request->frames, end_image.unsqueeze(2));
49064906
}
49074907

4908-
auto concat_latent = sd_ctx->sd->encode_first_stage(image); // [b, c, t, h/vae_scale_factor, w/vae_scale_factor]
4908+
auto concat_latent = sd_ctx->sd->encode_first_stage(image, /*encode_video=*/true); // [b, c, t, h/vae_scale_factor, w/vae_scale_factor]; encode_video bypasses spatial tiling
49094909
if (concat_latent.empty()) {
49104910
LOG_ERROR("failed to encode video conditioning frames");
49114911
return std::nullopt;

src/vae.hpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,19 @@ struct VAE : public GGMLRunner {
113113
sd::Tensor<float> encode(int n_threads,
114114
const sd::Tensor<float>& x,
115115
sd_tiling_params_t tiling_params,
116-
bool circular_x = false,
117-
bool circular_y = false) {
116+
bool encode_video = false,
117+
bool circular_x = false,
118+
bool circular_y = false) {
118119
int64_t t0 = ggml_time_ms();
119120
sd::Tensor<float> input = x;
120121
sd::Tensor<float> output;
121122
if (scale_input) {
122123
scale_tensor_to_minus1_1(&input);
123124
}
124125

125-
if (tiling_params.enabled) {
126+
// Video VAEs produce 4D/5D tensors that the 2D spatial tiling path cannot
127+
// handle correctly; bypass spatial tiling for video encode.
128+
if (tiling_params.enabled && !encode_video) {
126129
const int scale_factor = get_scale_factor();
127130
int64_t W = input.shape()[0] / scale_factor;
128131
int64_t H = input.shape()[1] / scale_factor;
@@ -169,7 +172,9 @@ struct VAE : public GGMLRunner {
169172
sd::Tensor<float> output;
170173
set_tiling_params(tiling_params);
171174

172-
if (tiling_params.enabled) {
175+
// Video VAEs produce 4D/5D tensors that the 2D spatial tiling path cannot
176+
// handle correctly; bypass spatial tiling for video decode.
177+
if (tiling_params.enabled && !decode_video) {
173178
const int scale_factor = get_scale_factor();
174179
int64_t W = input.shape()[0] * scale_factor;
175180
int64_t H = input.shape()[1] * scale_factor;

0 commit comments

Comments
 (0)