@@ -183,9 +183,8 @@ class TinyDecoder : public UnaryBlock {
183183};
184184
185185class TPool : public UnaryBlock {
186- int stride;
187-
188186public:
187+ int stride;
189188 TPool (int channels, int stride)
190189 : stride(stride) {
191190 blocks[" conv" ] = std::shared_ptr<GGMLBlock>(new Conv2d (channels * stride, channels, {1 , 1 }, {1 , 1 }, {0 , 0 }, {1 , 1 }, false ));
@@ -203,7 +202,7 @@ class TPool : public UnaryBlock {
203202};
204203
205204class TGrow : public UnaryBlock {
206- public:
205+ public:
207206 int stride;
208207 TGrow (int channels, int stride)
209208 : stride(stride) {
@@ -356,6 +355,20 @@ ggml_tensor* unpatchify(ggml_context* ctx,
356355 return x;
357356}
358357
358+ struct WorkItem {
359+ ggml_tensor* xt;
360+ int block_idx;
361+ };
362+
363+ struct SequentialDecoderState {
364+ std::map<int , ggml_tensor*> mem_single;
365+ };
366+
367+ struct SequentialEncoderState {
368+ std::map<int , ggml_tensor*> mem_single;
369+ std::map<int , std::vector<ggml_tensor*>> mem_pool;
370+ };
371+
359372class TinyVideoEncoder : public UnaryBlock {
360373 int in_channels = 3 ;
361374 int hidden = 64 ;
@@ -364,6 +377,9 @@ class TinyVideoEncoder : public UnaryBlock {
364377 int num_layers = 3 ;
365378 int patch_size = 1 ;
366379
380+ int total_blocks = 0 ;
381+ int relu_idx = 0 ;
382+
367383public:
368384 int t_downscale = 1 ;
369385 TinyVideoEncoder (int z_channels = 4 , int patch_size = 1 , std::vector<bool > time_downscale = {true , true , false })
@@ -376,7 +392,7 @@ class TinyVideoEncoder : public UnaryBlock {
376392 }
377393 int index = 0 ;
378394 blocks[std::to_string (index++)] = std::shared_ptr<GGMLBlock>(new Conv2d (in_channels * patch_size * patch_size, hidden, {3 , 3 }, {1 , 1 }, {1 , 1 }));
379- index++; // nn.ReLU()
395+ relu_idx = index++; // nn.ReLU()
380396 for (int i = 0 ; i < num_layers; i++) {
381397 int stride = time_downscale[i] ? 2 : 1 ;
382398 blocks[std::to_string (index++)] = std::shared_ptr<GGMLBlock>(new TPool (hidden, stride));
@@ -385,7 +401,8 @@ class TinyVideoEncoder : public UnaryBlock {
385401 blocks[std::to_string (index++)] = std::shared_ptr<GGMLBlock>(new MemBlock (hidden, hidden));
386402 }
387403 }
388- blocks[std::to_string (index)] = std::shared_ptr<GGMLBlock>(new Conv2d (hidden, z_channels, {3 , 3 }, {1 , 1 }, {1 , 1 }));
404+ blocks[std::to_string (index++)] = std::shared_ptr<GGMLBlock>(new Conv2d (hidden, z_channels, {3 , 3 }, {1 , 1 }, {1 , 1 }));
405+ total_blocks = index;
389406 }
390407
391408 ggml_tensor* forward (GGMLRunnerContext* ctx, ggml_tensor* z) override {
@@ -414,21 +431,96 @@ class TinyVideoEncoder : public UnaryBlock {
414431 }
415432 auto last_conv = std::dynamic_pointer_cast<Conv2d>(blocks[std::to_string (index)]);
416433 h = last_conv->forward (ctx, h);
434+
417435 return h;
418436 }
419- };
420437
438+ ggml_tensor* forward_seq_single_step (GGMLRunnerContext* ctx,
439+ SequentialEncoderState& state,
440+ std::vector<WorkItem>& work_stack) {
441+ while (!work_stack.empty ()) {
442+ WorkItem item = work_stack.back ();
443+ work_stack.pop_back ();
444+
445+ ggml_tensor* xt = item.xt ;
446+ int i = item.block_idx ;
421447
422- struct WorkItem {
423- ggml_tensor* xt;
424- int block_idx;
425- };
448+ if (i >= total_blocks) {
449+ if (patch_size > 1 ) {
450+ xt = unpatchify (ctx->ggml_ctx , xt, patch_size, 1 );
451+ }
452+ return xt;
453+ }
426454
427- // Stateful memory cache tracking MemBlock/WideMemBlock frames across steps
428- struct TinyVideoDecoderState {
429- std::map<int , ggml_tensor*> mem_single;
455+ if (i == relu_idx) {
456+ xt = ggml_relu_inplace (ctx->ggml_ctx , xt);
457+ work_stack.push_back ({xt, i + 1 });
458+ continue ;
459+ }
460+
461+ std::string key = std::to_string (i);
462+ auto block = blocks[key];
463+
464+ if (auto mem_block = std::dynamic_pointer_cast<MemBlock>(block)) {
465+ ggml_tensor* prev_mem = state.mem_single [i];
466+ if (prev_mem == nullptr ) {
467+ prev_mem = ggml_dup_tensor (ctx->ggml_ctx , xt);
468+ prev_mem = ggml_scale (ctx->ggml_ctx , prev_mem, 0 .);
469+ }
470+ ggml_tensor* xt_next = mem_block->forward (ctx, xt, prev_mem);
471+ state.mem_single [i] = xt;
472+
473+ work_stack.push_back ({xt_next, i + 1 });
474+ } else if (auto pool = std::dynamic_pointer_cast<TPool>(block)) {
475+ state.mem_pool [i].push_back (xt);
476+
477+ if ((int )state.mem_pool [i].size () == pool->stride ) {
478+ ggml_tensor* cat_input = ggml_ext_vec_concat (ctx->ggml_ctx , state.mem_pool [i], 3 );
479+ ggml_tensor* xt_next = pool->forward (ctx, cat_input);
480+
481+ state.mem_pool [i].clear ();
482+ work_stack.push_back ({xt_next, i + 1 });
483+ }
484+ } else if (auto unary_block = std::dynamic_pointer_cast<UnaryBlock>(block)) {
485+ ggml_tensor* xt_next = unary_block->forward (ctx, xt);
486+ work_stack.push_back ({xt_next, i + 1 });
487+ }
488+ }
489+
490+ return nullptr ; // Work stack exhausted
491+ }
492+
493+ ggml_tensor* forward_seq (GGMLRunnerContext* ctx,
494+ ggml_tensor* z) {
495+ SequentialEncoderState state;
496+ std::vector<WorkItem> work_stack;
497+
498+ if (patch_size > 1 ) {
499+ z = patchify (ctx->ggml_ctx , z, patch_size, 1 );
500+ }
501+
502+ const std::vector<ggml_tensor*>& latent_frames = ggml_ext_chunk (ctx->ggml_ctx , z, z->ne [3 ], 3 );
503+
504+ for (auto it = latent_frames.rbegin (); it != latent_frames.rend (); ++it) {
505+ work_stack.push_back ({*it, 0 });
506+ }
507+
508+ std::vector<ggml_tensor*> output_frames;
509+
510+ while (!work_stack.empty ()) {
511+ ggml_tensor* out_frame = forward_seq_single_step (ctx, state, work_stack);
512+ if (out_frame != nullptr ) {
513+ output_frames.push_back (out_frame);
514+ }
515+ }
516+
517+ auto h = ggml_ext_vec_concat (ctx->ggml_ctx , output_frames, 3 );
518+ return h;
519+ }
430520};
431521
522+
523+
432524class TinyVideoDecoder : public UnaryBlock {
433525 int z_channels = 4 ;
434526 int out_channels = 3 ;
@@ -439,10 +531,9 @@ class TinyVideoDecoder : public UnaryBlock {
439531 int t_upscale = 1 ;
440532 bool is_wide = false ;
441533
442- // Track total block index count and special unmapped activation indices
443- int total_blocks = 0 ;
444- int clamp_idx = 0 ;
445- int relu1_idx = 0 ;
534+ int total_blocks = 0 ;
535+ int clamp_idx = 0 ;
536+ int relu1_idx = 0 ;
446537 int relu_final_idx = 0 ;
447538 std::set<int > upsample_indices;
448539
@@ -474,11 +565,11 @@ class TinyVideoDecoder : public UnaryBlock {
474565 blocks[std::to_string (index++)] = std::shared_ptr<GGMLBlock>(new MemBlock (channels[i], channels[i]));
475566 }
476567 }
477- upsample_indices.insert (index++); // Nearest-neighbor spatial upsample slot
568+ upsample_indices.insert (index++); // Nearest-neighbor spatial upsample slot
478569 blocks[std::to_string (index++)] = std::shared_ptr<GGMLBlock>(new TGrow (channels[i], stride));
479570 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 ));
480571 }
481- relu_final_idx = index++; // nn.ReLU()
572+ relu_final_idx = index++; // nn.ReLU()
482573 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 }));
483574
484575 total_blocks = index;
@@ -530,7 +621,7 @@ class TinyVideoDecoder : public UnaryBlock {
530621 }
531622
532623 ggml_tensor* forward_seq_single_step (GGMLRunnerContext* ctx,
533- TinyVideoDecoderState & state,
624+ SequentialDecoderState & state,
534625 std::vector<WorkItem>& work_stack) {
535626 while (!work_stack.empty ()) {
536627 WorkItem item = work_stack.back ();
@@ -615,12 +706,10 @@ class TinyVideoDecoder : public UnaryBlock {
615706
616707 return nullptr ; // Work stack exhausted
617708 }
618-
619- ggml_tensor* forward_seq (
620- GGMLRunnerContext* ctx,
621- ggml_tensor* x
622- ) {
623- TinyVideoDecoderState state;
709+
710+ ggml_tensor* forward_seq (GGMLRunnerContext* ctx,
711+ ggml_tensor* x) {
712+ SequentialDecoderState state;
624713 std::vector<WorkItem> work_stack;
625714
626715 const std::vector<ggml_tensor*>& latent_frames = ggml_ext_chunk (ctx->ggml_ctx , x, x->ne [3 ], 3 );
@@ -653,9 +742,9 @@ class TAEHV : public GGMLBlock {
653742 bool decode_only;
654743 SDVersion version;
655744 bool is_wide;
656-
657- public:
658- bool parallel = false ;
745+
746+ public:
747+ bool parallel = false ;
659748 int z_channels = 16 ;
660749 std::vector<bool > time_downscale = {true , true , false };
661750 std::vector<bool > time_upscale = {false , true , true };
@@ -710,7 +799,7 @@ class TAEHV : public GGMLBlock {
710799 x = ggml_concat (ctx->ggml_ctx , x, last_frame, 3 );
711800 }
712801 }
713- x = encoder->forward (ctx, x);
802+ x = parallel ? encoder->forward (ctx, x) : encoder-> forward_seq (ctx, x);
714803 if (sd_version_is_wan (version) || sd_version_is_hunyuan_video (version) || sd_version_is_ltxav (version)) {
715804 // (W, H, C, T) -> (W, H, T, C)
716805 x = ggml_cont (ctx->ggml_ctx , ggml_permute (ctx->ggml_ctx , x, 0 , 1 , 3 , 2 ));
@@ -878,12 +967,16 @@ struct TinyVideoAutoEncoder : public VAE {
878967
879968 ggml_cgraph* build_graph (const sd::Tensor<float >& z_tensor, bool decode_graph) {
880969 ggml_cgraph* gf = nullptr ;
881- ggml_tensor* z = make_input (z_tensor);
970+ ggml_tensor* z = make_input (z_tensor);
882971 if (decode_graph) {
883972 int64_t passes = taehv.parallel ? 1 : z->ne [3 ];
884973 gf = ggml_new_graph_custom (compute_ctx, (is_wide ? 4096 : 2048 ) * passes, false );
885974 } else {
886- gf = ggml_new_graph (compute_ctx);
975+ int64_t frames = z->ne [2 ];
976+ int64_t factor = sd_version_is_minimax_h3 (version) ? 20 : sd_version_is_ltxav (version) ? 8
977+ : 4 ;
978+ int64_t passes = taehv.parallel ? 1 : (frames + factor - 1 ) / factor;
979+ gf = ggml_new_graph_custom (compute_ctx, 2048 * passes, false );
887980 }
888981 auto runner_ctx = get_context ();
889982 ggml_tensor* out = decode_graph ? taehv.decode (&runner_ctx, z) : taehv.encode (&runner_ctx, z);
0 commit comments