# SPDX-License-Identifier: Apache-2.0 """Viggle Remake render worker: one resident process per GPU, rendering jobs from a file queue. viggle-remake-worker --gpu 0 [--home ~/.viggle-remake] [--base MiniMaxAI/MiniMax-H3] [--lora-repo Viggle/Viggle-Remake] The base transformer, the video and audio VAEs and both LoRAs (swap, edit) load once; only the active mode's LoRA sits on the card. The 64-layer Qwen3-VL conditioner stays in host RAM and its decoder layers are streamed onto the card for each new prompt; embeddings are cached on disk. Queue protocol and memory table: WORKER_NOTES.md. """ import os import sys if "--gpu" in sys.argv[:-1]: # before torch: CUDA reads this once, and some imports below already initialise it os.environ["CUDA_VISIBLE_DEVICES"] = sys.argv[sys.argv.index("--gpu") + 1] os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True") os.environ.setdefault("PYTORCH_ALLOC_CONF", os.environ["PYTORCH_CUDA_ALLOC_CONF"]) import argparse # noqa: E402 import hashlib # noqa: E402 import json # noqa: E402 import socket # noqa: E402 import subprocess # noqa: E402 import threading # noqa: E402 import time # noqa: E402 import traceback # noqa: E402 from pathlib import Path # noqa: E402 import numpy as np # noqa: E402 import torch # noqa: E402 from accelerate import cpu_offload # noqa: E402 from huggingface_hub import snapshot_download # noqa: E402 from PIL import Image # noqa: E402 from transformers import AutoProcessor, AutoTokenizer, Qwen3VLForConditionalGeneration # noqa: E402 from diffusers import MiniMaxH3Transformer3DModel, ModularPipeline, attention_backend # noqa: E402 from diffusers.modular_pipelines.minimax_h3 import MiniMaxH3ImageReference, MiniMaxH3VideoReference # noqa: E402 from diffusers.modular_pipelines.minimax_h3.before_denoise import patchify_video_latents # noqa: E402 from diffusers.modular_pipelines.minimax_h3.encoders import (MiniMaxH3Ref2VATextEncoderStep, # noqa: E402 encode_vae_condition, get_qwen3vl_prompt_embeds) from diffusers.modular_pipelines.minimax_h3.modular_pipeline import (audio_latent_num_frames, # noqa: E402 video_latent_num_frames) from diffusers.utils.export_utils import encode_video # noqa: E402 from . import worker_patches as P # noqa: E402 MODES = ("swap", "edit") FRAMES = range(90, 346, 17) # 17n + 5, 24 fps AREA = {480: 832 * 480, 736: 1280 * 736} # the canvases LAW was measured on; a job's cond may have any aspect within the area # peak reserved GiB <= A + B * num_frames: the upper envelope of a sweep at 90..345 frames, 6 pinned refs, measured # on B200; each ref fewer saves ~0.2 (480) / ~0.3 (736) GiB. MARGIN covers fragmentation when the card is full LAW = {480: (80.8, 0.029), 736: (79.6, 0.088)} MARGIN = 3.0 BASE_FILES = ["modular_model_index.json", "transformer/*", "text_encoder/*", "tokenizer/*", "processor/*", "vae/*", "audio_vae/*", "scheduler/*", "audio_scheduler/*"] def local(repo, patterns): return repo if Path(repo).is_dir() else snapshot_download(repo, allow_patterns=patterns) def max_frames(cap_gib): return {se: max([nf for nf in FRAMES if a + b * nf + MARGIN <= cap_gib], default=0) for se, (a, b) in LAW.items()} def token_ids(processor, tokenizer, prompt, nf, nref): # the presentation is text only: a label per 2 fps block of the source, one per ref, then the prompt verbatim _, stamps = MiniMaxH3Ref2VATextEncoderStep._sample_video_condition_frames( np.zeros((nf, 1, 1, 3), dtype=np.uint8), 24.0, 2.0, processor.video_processor.temporal_patch_size) segments = ["