diff --git a/docs/contributing/env_vars.md b/docs/contributing/env_vars.md index e2cd6646a..f8c1aea42 100644 --- a/docs/contributing/env_vars.md +++ b/docs/contributing/env_vars.md @@ -278,9 +278,9 @@ longer exists also fails the test, so the fixing pull request deletes its entry. | `FASTVIDEO_H3_SPLICE_FROM_STEP` | int | `4` | eval | First denoising step run by FASTVIDEO_H3_SPLICE_TRANSFORMER. | | `FASTVIDEO_H3_ENCODER_LAYERWISE` | bool | `0` | performance | Stream MiniMax-H3 text-encoder language layers through exact-size pinned host memory (text-only prompts). | | `FASTVIDEO_H3_ENCODER_FUSED_DEQUANT` | bool | `0` | performance | Expand the serialized NVFP4 MiniMax-H3 text encoder with one fused Triton pass on GPUs without FP4 GEMM. | -| `FASTVIDEO_H3_VAE_TILE_BATCH` | int | `1` | performance | Spatial tiles per MiniMax-H3 video VAE decoder call; 1 decodes per tile. | +| `FASTVIDEO_H3_VAE_TILE_BATCH` | int | `8` | performance | Spatial tiles per MiniMax-H3 video VAE decoder call. The ViT decoder treats batch entries independently, so batching tiles matches per-tile decoding, except for NVFP4 decoders with dynamic activation scales, which always decode per tile. On GB200 at 768x1344, 8 tiles per call decodes 107 frames in 3.7 s instead of 5.4 s and 362 frames in 12.8 s instead of 18.3 s (+0.7 GiB peak). 1 decodes per tile. | | `FASTVIDEO_H3_REF2VA_MEMO_ENTRIES` | int | `0` | performance | Entries in each content-keyed MiniMax-H3 memo of reference encodes (Qwen3-VL Ref2VA presentation, VAE keyframe latents); repeat references are reused exactly. 0 disables. | -| `FASTVIDEO_H3_VAE_TILE_PARALLEL` | bool | `0` | performance | Split MiniMax-H3 VAE spatial tiles (not only temporal chunks) across the sequence-parallel ranks in the parallel decode and keyframe encode; bitwise equal to the serial path. | +| `FASTVIDEO_H3_VAE_TILE_PARALLEL` | bool | `0` | performance | Split MiniMax-H3 VAE spatial tiles (not only temporal chunks) across the sequence-parallel ranks in the parallel decode and keyframe encode; bitwise equal to the serial path. Requires FASTVIDEO_H3_VAE_TILE_BATCH=1 (the split replays the per-tile path). | | `FASTVIDEO_H3_VAE_NVFP4_FUSED` | bool | `1` | performance | Run NVFP4 MiniMax-H3 VAE decoder blocks through the bit-exact fused Triton kernels at inference. | | `FASTVIDEO_H3_VAE_NVFP4_CUDA_GRAPH` | bool | `1` | performance | Replay small fused NVFP4 MiniMax-H3 VAE decoder calls (at most four 1797-token tiles) as per-shape CUDA graphs. | | `FASTVIDEO_H3_VAE_NVFP4_DECODER` | path | unset | performance | Exported NVFP4 MiniMax-H3 video decoder (.pt, or .safetensors with bf16 masters or pre-packed NVFP4 weights) that replaces the VAE decoder at load; skips the INT8 ConvRot overlay. | diff --git a/fastvideo/tests/vaes/test_minimax_h3_vae_compile.py b/fastvideo/tests/vaes/test_minimax_h3_vae_compile.py index 1789822f4..4517efe5c 100644 --- a/fastvideo/tests/vaes/test_minimax_h3_vae_compile.py +++ b/fastvideo/tests/vaes/test_minimax_h3_vae_compile.py @@ -15,6 +15,7 @@ import torch import torch.nn as nn import torch.nn.functional as F +import fastvideo.envs as envs from fastvideo.models.vaes.minimax_h3_audio import ( MiniMaxH3AudioBigVGANDecoder, MiniMaxH3AudioVAE, @@ -350,9 +351,9 @@ def test_decode_clip_no_spatial_tiling_stage_ranges() -> None: ] -def test_decode_clip_emits_tiled_stage_ranges(monkeypatch) -> None: +def test_decode_clip_emits_tiled_stage_ranges(env_overrides) -> None: """Nest indexed decoder tiles between tile-splitting and stitching ranges.""" - monkeypatch.setenv("FASTVIDEO_H3_VAE_TILE_BATCH", "1") + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(1)) vae = _empty_typed_module(AutoencoderKLMiniMaxH3) vae.use_tiling = True vae.spatial_compression_ratio = 1 diff --git a/fastvideo/tests/vaes/test_minimax_h3_video_vae_streaming.py b/fastvideo/tests/vaes/test_minimax_h3_video_vae_streaming.py index 2101b9814..d70f1ec70 100644 --- a/fastvideo/tests/vaes/test_minimax_h3_video_vae_streaming.py +++ b/fastvideo/tests/vaes/test_minimax_h3_video_vae_streaming.py @@ -3,6 +3,7 @@ import pytest import torch from torch.testing import assert_close +import fastvideo.envs as envs from fastvideo.configs.models.vaes.minimax_h3_video import ( MiniMaxH3VideoVAEArchConfig, MiniMaxH3VideoVAEConfig, @@ -184,24 +185,24 @@ def test_decode_to_pixels_rejects_incomplete_output(monkeypatch) -> None: vae.decode_to_pixels(latents, output) -def test_tile_batch_defaults_to_eight(monkeypatch) -> None: +def test_tile_batch_defaults_to_eight(env_overrides) -> None: from fastvideo.models.vaes.minimax_h3_video import _tile_batch_size - monkeypatch.delenv("FASTVIDEO_H3_VAE_TILE_BATCH", raising=False) + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(None)) assert _tile_batch_size() == 8 - monkeypatch.setenv("FASTVIDEO_H3_VAE_TILE_BATCH", "3") + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(3)) assert _tile_batch_size() == 3 - monkeypatch.setenv("FASTVIDEO_H3_VAE_TILE_BATCH", "0") + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(0)) assert _tile_batch_size() == 1 -def test_dynamic_nvfp4_act_scale_decodes_per_tile(monkeypatch) -> None: +def test_dynamic_nvfp4_act_scale_decodes_per_tile(env_overrides) -> None: from fastvideo.models.vaes.minimax_h3_nvfp4_decoder import NVFP4DecoderLinear from fastvideo.models.vaes.minimax_h3_video import _tile_batch_size decoder = torch.nn.Module() decoder.proj = NVFP4DecoderLinear(torch.zeros(16, 16), None, None, torch.float32, act_scale="dynamic") - monkeypatch.setenv("FASTVIDEO_H3_VAE_TILE_BATCH", "8") + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(8)) assert _tile_batch_size(decoder) == 1 assert _tile_batch_size() == 8 @@ -213,7 +214,7 @@ def test_dynamic_nvfp4_act_scale_decodes_per_tile(monkeypatch) -> None: @torch.inference_mode() -def test_dynamic_nvfp4_act_scale_decodes_one_tile_per_call(monkeypatch) -> None: +def test_dynamic_nvfp4_act_scale_decodes_one_tile_per_call(env_overrides) -> None: from fastvideo.models.vaes.minimax_h3_nvfp4_decoder import NVFP4DecoderLinear torch.manual_seed(20260814) @@ -223,7 +224,7 @@ def test_dynamic_nvfp4_act_scale_decodes_one_tile_per_call(monkeypatch) -> None: tile_sample_min_overlap_height=2, tile_sample_min_overlap_width=2) latents = torch.randn(1, 4, 12, 4, 4) - monkeypatch.setenv("FASTVIDEO_H3_VAE_TILE_BATCH", "8") + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(8)) calls = [] decoder_forward = vae.decoder.forward @@ -247,7 +248,7 @@ def test_dynamic_nvfp4_act_scale_decodes_one_tile_per_call(monkeypatch) -> None: @torch.inference_mode() -def test_batched_tile_decode_matches_per_tile(monkeypatch) -> None: +def test_batched_tile_decode_matches_per_tile(env_overrides) -> None: torch.manual_seed(20260814) vae = _tiny_vae() vae.enable_tiling(tile_sample_min_height=8, @@ -257,7 +258,7 @@ def test_batched_tile_decode_matches_per_tile(monkeypatch) -> None: latents = torch.randn(1, 4, 12, 4, 4) outputs = {} for tile_batch in (1, 8): - monkeypatch.setenv("FASTVIDEO_H3_VAE_TILE_BATCH", str(tile_batch)) + env_overrides.enter_context(envs.FASTVIDEO_H3_VAE_TILE_BATCH.override(tile_batch)) output = torch.empty(vae.decoded_pixel_shape(latents.shape), dtype=torch.float32) vae.decode_to_pixels(latents, output) outputs[tile_batch] = output