ComfyUI/comfy_extras
John Pollock 093d17c587 Add SeedVR2 temporal chunk and merge nodes
## Summary

Sampling a long SeedVR2 video in one pass runs out of VRAM: the DiT working set grows linearly with latent frames times pixel area, so a 100 frame clip at 4x upscale needs more memory than any consumer card has. This PR adds two workflow-level nodes that split the latent into overlapping temporal chunks and recombine the sampled chunks with a Hann crossfade. The executor's list handling runs the stock KSampler once per chunk, so the sampler itself is untouched.

- **Chunk SeedVR2 Latent** splits the latent on the temporal axis. `frames_per_chunk` is in pixel frames on the 4n+1 grid, `temporal_overlap` sets how many latent frames adjacent chunks share, and `chunking_mode=auto` solves the chunk size from measured free VRAM and the latent's own dimensions. The node outputs the effective overlap so the merge is wired, not typed.
- **Merge SeedVR2 Latent Chunks** recombines the sampled chunks in order, crossfading each shared region with a Hann window (flat shoulders on the outer thirds, fade across the middle third). Zero overlap is a plain concatenation, bit-identical to `torch.cat`.

## Changes

- Added 2 nodes and 1 crossfade helper to `comfy_extras/nodes_seedvr.py` (+201 lines).
- Added 4 chunk-law constants to `comfy/ldm/seedvr/constants.py` (+10 lines).
- Added pytest unit tests in `tests-unit/comfy_extras_test/test_seedvr2_temporal_chunk.py` (+66 lines): chunk geometry, 4n+1 and mode validation, overlap clamping, temporal noise mask slicing (5-D and 4-D), the auto law including batch scaling, crossfade weights and blend direction, round trip, and metadata handling.

Everything outside the two new nodes is byte-identical to the base branch. The new constants are read only by the chunk node, and workflows that do not use these nodes take no new code path.

## Auto mode calibration

The auto law is `max_latent_frames = (free_GiB - reserved - margin) / (0.30 * megapixels)`, calibrated on an RTX 5090 (32 GB) with the 3b fp16 model: a 17-cell resolution sweep plus temporal bisection located the activation wall and confirmed it is linear in latent frames times pixel area (the same total-voxel budget holds from 1.5:1 through 24:1 aspect ratios and under transposition). The margin is four standard deviations of the measured run-to-run spread, which costs about one latent frame of chunk size and makes an out-of-memory failure a lottery ticket rather than a coin flip. Manual mode bypasses the law entirely.

On a 32 GB card, a 640x480x100 input at 4x upscale sampled as a single chunk allocates past 31 GiB and dies; auto mode picks 49 frame chunks (three chunks with overlap) and the full pipeline completes in about 240 seconds. The same law stands down on small inputs: a 320x240x100 clip runs as a single chunk because it fits.

## Example workflow

Load a video, 4x upscale, auto chunking, temporal overlap 3. Expected output for a 640x480x100 input: 2560x1920, 100 frames, seams invisible at the default overlap.

<details>
<summary>API workflow JSON</summary>

```json
{
  "14": {"inputs": {"vae_name": "ema_vae_fp16.safetensors"}, "class_type": "VAELoader"},
  "17": {"inputs": {"video": ["24", 0]}, "class_type": "GetVideoComponents"},
  "20": {"inputs": {"fps": ["17", 2], "images": ["30", 0], "audio": ["17", 1]}, "class_type": "CreateVideo"},
  "21": {"inputs": {"tile_size": 192, "overlap": 64, "temporal_size": 64, "temporal_overlap": 8, "pixels": ["27", 0], "vae": ["14", 0]}, "class_type": "VAEEncodeTiled"},
  "22": {"inputs": {"tile_size": 256, "overlap": 32, "temporal_size": 64, "temporal_overlap": 8, "samples": ["33", 0], "vae": ["14", 0]}, "class_type": "VAEDecodeTiled"},
  "23": {"inputs": {"unet_name": "seedvr2_3b_fp16.safetensors", "weight_dtype": "default"}, "class_type": "UNETLoader"},
  "24": {"inputs": {"file": "input.mp4", "video-preview": ""}, "class_type": "LoadVideo"},
  "25": {"inputs": {"filename_prefix": "video/seedvr2_upscale", "format": "auto", "codec": "auto", "video": ["20", 0]}, "class_type": "SaveVideo"},
  "26": {"inputs": {"resize_type": "scale by multiplier", "resize_type.multiplier": 4, "scale_method": "bicubic", "input": ["17", 0]}, "class_type": "ResizeImageMaskNode"},
  "27": {"inputs": {"resized_images": ["26", 0]}, "class_type": "SeedVR2Preprocess"},
  "28": {"inputs": {"model": ["23", 0], "vae_conditioning": ["32", 0]}, "class_type": "SeedVR2Conditioning"},
  "29": {"inputs": {"seed": 5770521, "steps": 1, "cfg": 1, "sampler_name": "euler", "scheduler": "simple", "denoise": 1, "model": ["23", 0], "positive": ["28", 0], "negative": ["28", 1], "latent_image": ["32", 0]}, "class_type": "KSampler"},
  "30": {"inputs": {"color_correction_method": "lab", "images": ["22", 0], "original_resized_images": ["26", 0]}, "class_type": "SeedVR2PostProcessing"},
  "32": {"inputs": {"frames_per_chunk": 21, "temporal_overlap": 3, "chunking_mode": "auto", "latent": ["21", 0]}, "class_type": "SeedVR2TemporalChunk"},
  "33": {"inputs": {"temporal_overlap": ["32", 1], "latent_chunks": ["29", 0]}, "class_type": "SeedVR2TemporalMerge"}
}
```

</details>

## Prior art

- Reference implementation: https://github.com/ByteDance-Seed/SeedVR
- Community precedent for temporal chunking with blended reassembly: https://github.com/numz/ComfyUI-SeedVR2_VideoUpscaler

Chunk boundaries are a mathematical compromise: the model attends within a chunk, so different chunkings produce different outputs. The overlap crossfade hides the seam; power users can widen or zero the overlap from the workflow.
2026-07-04 13:05:19 -05:00
..
chainner_models
frame_interpolation_models fix: Proper memory estimation for frame interpolation when not using dynamic VRAM (#13698) 2026-05-04 20:20:40 +03:00
mediapipe Remove useless annotations imports. (#14105) 2026-05-25 19:23:29 -07:00
color_util.py feat: Bounding boxes canvas and Ideogram JSON prompt (#14537) 2026-06-25 22:34:09 +08:00
nodes_ace.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_advanced_samplers.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_align_your_steps.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_apg.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_ar_video.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_attention_multiply.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_audio_encoder.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_audio.py Add output socket to save nodes (#13866) 2026-06-22 10:15:28 +08:00
nodes_bernini.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_bg_removal.py Move bg_removal_model input socket to first position for nicer display (#14353) 2026-06-09 23:36:56 +08:00
nodes_boogu.py Add negative prompt to boogu edit node and set min images to 0. (#14529) 2026-06-17 15:42:29 -07:00
nodes_bounding_boxes.py feat: Bounding boxes canvas and Ideogram JSON prompt (#14537) 2026-06-25 22:34:09 +08:00
nodes_camera_trajectory.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_canny.py chore: Various QoL updates of nodes display names, descriptions and categories (CORE-190, CORE-191) (#13830) 2026-05-19 00:13:48 -04:00
nodes_cfg.py feat: Microsoft Lens support (CORE-248) (#14077) 2026-05-25 23:01:51 -07:00
nodes_chroma_radiance.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_clip_sdxl.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_color.py feat: Bounding boxes canvas and Ideogram JSON prompt (#14537) 2026-06-25 22:34:09 +08:00
nodes_compositing.py chore: Various QoL updates of nodes display names, descriptions and categories (CORE-190, CORE-191) (#13830) 2026-05-19 00:13:48 -04:00
nodes_cond.py chore: Update nodes categories (#14674) 2026-07-01 05:20:20 +08:00
nodes_context_windows.py feat: Context Windows sampling with LTX2 models and IC-LoRa guides (CORE-3) (#13325) 2026-06-20 07:47:31 +08:00
nodes_controlnet.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_cosmos.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_curve.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_custom_sampler.py chore: Update nodes categories (#14674) 2026-07-01 05:20:20 +08:00
nodes_dataset.py harden: load training-dataset shards with weights_only=True (#14543) 2026-06-18 15:30:57 -04:00
nodes_depth_anything_3.py Depth anything 3 (Core-135) (#13853) 2026-06-10 09:28:24 +08:00
nodes_differential_diffusion.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_easycache.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_edit_model.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_eps.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_flux.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_frame_interpolation.py Rename a bunch of nodes (#14547) 2026-06-20 08:01:28 +08:00
nodes_freelunch.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_fresca.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_gaussian_splat.py feat: add PreviewGaussianSplat + PreviewPointCloud nodes (#14194) 2026-06-05 12:30:58 -07:00
nodes_gits.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_glsl.py Update GLSL node to use ANGLE library (CORE-162) (#13195) 2026-06-27 08:40:31 +08:00
nodes_hidream_o1.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_hidream.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_hooks.py Disable dynamic_vram when weight hooks applied (#12653) 2026-02-28 16:50:18 -05:00
nodes_hunyuan3d.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_hunyuan.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_hypernetwork.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_hypertile.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_ideogram4.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_image_compare.py chore: Update display names and categories (CORE-151) (#13693) 2026-05-04 17:37:25 -07:00
nodes_images.py Add output socket to save nodes (#13866) 2026-06-22 10:15:28 +08:00
nodes_ip2p.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_json_prompt.py feat: Bounding boxes canvas and Ideogram JSON prompt (#14537) 2026-06-25 22:34:09 +08:00
nodes_kandinsky5.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_latent.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_load_3d.py feat: add Load3DAdvanced node (#14316) 2026-06-20 07:06:55 +08:00
nodes_logic.py Rename a bunch of nodes (#14547) 2026-06-20 08:01:28 +08:00
nodes_lora_debug.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_lora_extract.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_lotus.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_lt_audio.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_lt_upsampler.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_lt.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_lumina2.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_mahiro.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_mask.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_math.py Fix uncaught OverflowError in Math Expression node for large int results (#14214) 2026-06-01 18:15:04 -07:00
nodes_mediapipe.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_mochi.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_model_advanced.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_model_downscale.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_model_merging_model_specific.py Add advanced krea 2 model merging node. (#14621) 2026-06-24 20:37:30 -07:00
nodes_model_merging.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_model_patch.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_moge.py Depth anything 3 (Core-135) (#13853) 2026-06-10 09:28:24 +08:00
nodes_morphology.py chore: Various QoL updates of nodes display names, descriptions and categories (CORE-190, CORE-191) (#13830) 2026-05-19 00:13:48 -04:00
nodes_multigpu.py multigpu: use unet_manual_cast for SelectModelDevice compute dtype (#14108) 2026-05-25 20:03:37 -07:00
nodes_nag.py Add category to Normalized Attention Guidance node (#12565) 2026-02-21 19:51:21 -05:00
nodes_nop.py chore: Various QoL updates of nodes display names, descriptions and categories (CORE-190, CORE-191) (#13830) 2026-05-19 00:13:48 -04:00
nodes_number_convert.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_optimalsteps.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_pag.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_painter.py Remove useless annotations imports. (#14105) 2026-05-25 19:23:29 -07:00
nodes_perpneg.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_photomaker.py chore: Update nodes categories (#14674) 2026-07-01 05:20:20 +08:00
nodes_pid.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_pixart.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_post_processing.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_preview_any.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_primitive.py Rename a bunch of nodes (#14547) 2026-06-20 08:01:28 +08:00
nodes_qwen.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_rebatch.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_replacements.py Node Replacement API (#12014) 2026-02-15 02:12:30 -08:00
nodes_resolution.py Improve ResolutionSelector (#14309) 2026-06-09 01:05:10 +08:00
nodes_rope.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_rtdetr.py Update nodes titles (#14417) 2026-06-16 11:42:00 +08:00
nodes_sag.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_sam3.py Update nodes titles (#14417) 2026-06-16 11:42:00 +08:00
nodes_save_3d.py feat: add PreviewGaussianSplat + PreviewPointCloud nodes (#14194) 2026-06-05 12:30:58 -07:00
nodes_scail.py feat: SCAIL-2 multireference (CORE-310) (#14509) 2026-06-17 16:21:23 +03:00
nodes_sd3.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_sdpose.py Add the checkbox to disable head drawing in node SDPoseDrawKeypoints (#14446) 2026-06-16 16:21:04 +08:00
nodes_sdupscale.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_seed.py feat: Add Support For Simple Seed (CORE-295) (#14616) 2026-06-25 09:39:10 +08:00
nodes_seedvr.py Add SeedVR2 temporal chunk and merge nodes 2026-07-04 13:05:19 -05:00
nodes_slg.py feat: mark 429 widgets as advanced for collapsible UI (#12197) 2026-02-19 19:20:02 -08:00
nodes_stable3d.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_stable_cascade.py chore: Update nodes categories (#14674) 2026-07-01 05:20:20 +08:00
nodes_string.py feat: Bounding boxes canvas and Ideogram JSON prompt (#14537) 2026-06-25 22:34:09 +08:00
nodes_tcfg.py convert nodes_tcfg.py to V3 schema (#9942) 2025-09-26 14:13:05 -07:00
nodes_textgen.py feat: Support text generation with Qwen3-VL (CORE-276) (#14298) 2026-06-17 08:12:44 +08:00
nodes_tomesd.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_toolkit.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_torch_compile.py Update nodes categories and display names (CORE-89) (#13786) 2026-05-08 01:02:55 -04:00
nodes_train.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_triposplat.py chore: Update nodes categories (#14674) 2026-07-01 05:20:20 +08:00
nodes_upscale_model.py chore: Update nodes categories (#14145) 2026-05-27 20:43:33 -04:00
nodes_video_model.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_video.py Add output socket to save nodes (#13866) 2026-06-22 10:15:28 +08:00
nodes_void.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_wan.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_wandancer.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_wanmove.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
nodes_webcam.py add search aliases to all nodes (#12035) 2026-01-22 18:36:58 -08:00
nodes_zimage.py chore: Update nodes categories (CORE-263) (#14460) 2026-06-17 08:33:09 +08:00
void_noise_warp.py Void model - pass 1 & 2 (CORE-38) (#13403) 2026-05-05 19:59:04 -07:00