@@ -1158,6 +1158,27 @@ namespace LTXVAE {
11581158 return WAN::WanVAE::unpatchify (ctx->ggml_ctx , out, patch_size, 1 );
11591159 }
11601160
1161+ ggml_tensor* decode_tiled_chunk (GGMLRunnerContext* ctx,
1162+ ggml_tensor* z,
1163+ ggml_tensor* timestep,
1164+ std::vector<ggml_tensor*>& feat_map,
1165+ int chunk_idx,
1166+ int temporal_tile_overlap,
1167+ int & feat_idx) {
1168+ auto decoder = std::dynamic_pointer_cast<Decoder>(blocks[" decoder" ]);
1169+ auto processor = std::dynamic_pointer_cast<PerChannelStatistics>(blocks[" per_channel_statistics" ]);
1170+ auto latents = processor->un_normalize (ctx, z);
1171+
1172+ feat_idx = 0 ;
1173+ int chunk_overlap = temporal_tile_overlap; // modified by forward_tiled_frame temporal inflation
1174+ auto out_chunk = decoder->forward_tiled_frame (ctx, latents, timestep,
1175+ feat_map, feat_idx, chunk_idx, chunk_overlap);
1176+ if (chunk_overlap > 0 ) {
1177+ out_chunk = ggml_ext_slice (ctx->ggml_ctx , out_chunk, 2 , 0 , out_chunk->ne [2 ] - chunk_overlap);
1178+ }
1179+ return WAN::WanVAE::unpatchify (ctx->ggml_ctx , out_chunk, patch_size, 1 );
1180+ }
1181+
11611182 ggml_tensor* encode (GGMLRunnerContext* ctx,
11621183 ggml_tensor* x) {
11631184 GGML_ASSERT (!decode_only);
@@ -1296,6 +1317,41 @@ struct LTXVideoVAE : public VAE {
12961317 vae.get_param_tensors (tensors, prefix);
12971318 }
12981319
1320+ struct TemporalTilePlan {
1321+ int frames = 1 ;
1322+ int overlap = 0 ;
1323+ int stride = 1 ;
1324+ int num_tiles = 1 ;
1325+ };
1326+
1327+ TemporalTilePlan resolve_temporal_tile_plan (int64_t total_frames) const {
1328+ TemporalTilePlan plan;
1329+ plan.frames = std::max (1 , temporal_tile_frames);
1330+ plan.overlap = std::max (0 , temporal_tile_overlap);
1331+
1332+ if (plan.overlap >= plan.frames ) {
1333+ LOG_WARN (" temporal_tile_overlap (%d) is greater than or equal to temporal_tile_frames (%d), adjusting values to avoid empty decode windows" ,
1334+ plan.overlap ,
1335+ plan.frames );
1336+ plan.overlap = plan.frames - 1 ;
1337+ }
1338+ if (total_frames > 1 && plan.overlap >= total_frames) {
1339+ LOG_WARN (" temporal_tile_overlap (%d) is greater than or equal to total latent frames (%lld), adjusting values to decode at least one tile" ,
1340+ plan.overlap ,
1341+ (long long )total_frames);
1342+ plan.overlap = static_cast <int >(total_frames - 1 );
1343+ }
1344+
1345+ plan.stride = std::max (1 , plan.frames - plan.overlap );
1346+ int64_t tiled_frames = std::max<int64_t >(1 , total_frames - plan.overlap );
1347+ plan.num_tiles = total_frames > 0 ? static_cast <int >((tiled_frames + plan.stride - 1 ) / plan.stride ) : 0 ;
1348+ return plan;
1349+ }
1350+
1351+ std::string temporal_feat_cache_name (size_t feat_idx) const {
1352+ return " ltx_vae_temporal_feat:" + std::to_string (feat_idx);
1353+ }
1354+
12991355 ggml_cgraph* build_graph (const sd::Tensor<float >& z_tensor, bool decode_graph) {
13001356 ggml_cgraph* gf = new_graph_custom (20480 );
13011357 ggml_tensor* z = make_input (z_tensor);
@@ -1306,21 +1362,97 @@ struct LTXVideoVAE : public VAE {
13061362
13071363 auto runner_ctx = get_context ();
13081364 ggml_tensor* out;
1309- bool use_tiled = decode_graph && temporal_tiling_enabled &&
1310- z_tensor.dim () == 5 && z_tensor.shape ()[2 ] > 1 ;
1311- if (use_tiled) {
1312- LOG_DEBUG (" Using LTX VAE temporal tiling params: temporal_tile_frames=%d, temporal_tile_overlap=%d" ,
1313- temporal_tile_frames,
1314- temporal_tile_overlap);
1315- out = vae.decode_tiled (&runner_ctx, z, timestep, temporal_tile_frames, temporal_tile_overlap);
1316- } else {
1317- out = decode_graph ? vae.decode (&runner_ctx, z, timestep) : vae.encode (&runner_ctx, z);
1318- }
1365+ out = decode_graph ? vae.decode (&runner_ctx, z, timestep) : vae.encode (&runner_ctx, z);
13191366 ggml_build_forward_expand (gf, out);
13201367
13211368 return gf;
13221369 }
13231370
1371+ ggml_cgraph* build_temporal_tile_graph (const sd::Tensor<float >& z_chunk_tensor,
1372+ int chunk_idx,
1373+ int chunk_overlap) {
1374+ ggml_cgraph* gf = new_graph_custom (20480 );
1375+ ggml_tensor* z = make_input (z_chunk_tensor);
1376+ ggml_tensor* timestep = nullptr ;
1377+ if (timestep_conditioning) {
1378+ timestep = make_input (decode_timestep_tensor);
1379+ }
1380+
1381+ std::vector<ggml_tensor*> feat_map (128 , nullptr );
1382+ for (size_t feat_idx = 0 ; feat_idx < feat_map.size (); ++feat_idx) {
1383+ feat_map[feat_idx] = get_cache_tensor_by_name (temporal_feat_cache_name (feat_idx));
1384+ }
1385+
1386+ auto runner_ctx = get_context ();
1387+ int feat_count = 0 ;
1388+ ggml_tensor* out = vae.decode_tiled_chunk (&runner_ctx,
1389+ z,
1390+ timestep,
1391+ feat_map,
1392+ chunk_idx,
1393+ chunk_overlap,
1394+ feat_count);
1395+
1396+ for (int feat_idx = 0 ; feat_idx < feat_count && feat_idx < static_cast <int >(feat_map.size ()); ++feat_idx) {
1397+ ggml_tensor* feat_cache = feat_map[static_cast <size_t >(feat_idx)];
1398+ if (feat_cache != nullptr ) {
1399+ cache (temporal_feat_cache_name (static_cast <size_t >(feat_idx)), feat_cache);
1400+ ggml_build_forward_expand (gf, feat_cache);
1401+ }
1402+ }
1403+
1404+ ggml_build_forward_expand (gf, out);
1405+ return gf;
1406+ }
1407+
1408+ sd::Tensor<float > decode_temporal_tiled_streaming (const int n_threads,
1409+ const sd::Tensor<float >& input,
1410+ size_t expected_dim) {
1411+ const int64_t total_frames = input.shape ()[2 ];
1412+ TemporalTilePlan plan = resolve_temporal_tile_plan (total_frames);
1413+
1414+ LOG_DEBUG (" Using streaming temporal tiling: temporal_tile_frames=%d, temporal_tile_overlap=%d, total latent frames=%lld, resulting in %d tiles" ,
1415+ plan.frames ,
1416+ plan.overlap ,
1417+ (long long )total_frames,
1418+ plan.num_tiles );
1419+
1420+ free_cache_ctx_and_buffer ();
1421+ cache_tensor_map.clear ();
1422+
1423+ sd::Tensor<float > output;
1424+ for (int64_t start = 0 ; start < total_frames - plan.overlap ; start += plan.stride ) {
1425+ const int64_t end = std::min<int64_t >(total_frames, start + plan.frames );
1426+ const int chunk_overlap = end < total_frames ? plan.overlap : 0 ;
1427+ auto z_chunk = sd::ops::slice (input, 2 , start, end);
1428+
1429+ LOG_DEBUG (" LTX VAE temporal tile %lld/%d: latent frames [%lld, %lld), overlap=%d" ,
1430+ (long long )(start / plan.stride + 1 ),
1431+ plan.num_tiles ,
1432+ (long long )start,
1433+ (long long )end,
1434+ chunk_overlap);
1435+
1436+ auto get_graph = [&]() -> ggml_cgraph* {
1437+ return build_temporal_tile_graph (z_chunk,
1438+ static_cast <int >(start),
1439+ chunk_overlap);
1440+ };
1441+ auto chunk = restore_trailing_singleton_dims (GGMLRunner::compute<float >(get_graph, n_threads, true ),
1442+ expected_dim);
1443+ if (chunk.empty ()) {
1444+ free_cache_ctx_and_buffer ();
1445+ cache_tensor_map.clear ();
1446+ return {};
1447+ }
1448+ output = output.empty () ? std::move (chunk) : sd::ops::concat (output, chunk, 2 );
1449+ }
1450+
1451+ free_cache_ctx_and_buffer ();
1452+ cache_tensor_map.clear ();
1453+ return output;
1454+ }
1455+
13241456 ggml_cgraph* build_latent_statistics_graph (const sd::Tensor<float >& z_tensor, bool normalize) {
13251457 ggml_cgraph* gf = new_graph_custom (1024 );
13261458 ggml_tensor* z = make_input (z_tensor);
@@ -1356,6 +1488,9 @@ struct LTXVideoVAE : public VAE {
13561488 input = sd::ops::slice (input, 2 , 0 , cropped_t );
13571489 }
13581490 }
1491+ if (decode_graph && temporal_tiling_enabled && input.dim () == 5 && input.shape ()[2 ] > 1 ) {
1492+ return decode_temporal_tiled_streaming (n_threads, input, expected_dim);
1493+ }
13591494 auto get_graph = [&]() -> ggml_cgraph* {
13601495 return build_graph (input, decode_graph);
13611496 };
0 commit comments