"""SolarRenderer: few-step generative reference rendering on MiniMax-H3. Structure video in, rendered video out: the released SolarRenderer LoRA pair (a four-step sampler LoRA merged into the weights, a rank-384 control LoRA on top of the Ref2VA transformer) renders what the structure video describes, guided by a six-part prompt and anchored on an optional first frame. Sampling takes **four** model evaluations. The 62 GiB Qwen3-VL text conditioner runs in a separate Space (`multimodalart/qwen3vl-conditioner`); the wire format between the halves is a safetensors file of `prompt_embeds` + `text_token_tags`. Everything else — the reference/target VAE encoding and the denoising itself — is this Space's, and follows the authors' `scripts/render.py` and `tools/omni_preencode.py` step for step: the structure video is encoded as an observation laid over every target frame, the first frame as a held still on latent 0, and the target (the structure clip itself, for shape and length only) through the released `ref2va` `latents_only` chain. The member safetensors the renderer reads is written exactly as `omni_preencode.encode()` writes it. """ import os os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True") import spaces # noqa: E402 (before torch: it patches torch.cuda) import json import math import shutil import sys import tempfile import time from functools import cache from pathlib import Path import gradio as gr import numpy as np import torch HERE = Path(__file__).resolve().parent sys.path.insert(0, str(HERE / "src")) sys.path.insert(0, str(HERE / "tools")) VERSION = "solarrenderer-v1" FRAMES = 158 # the encoded length the release was trained at: 47 latents, 6.58 s at 24 fps RANK = ALPHA = 384 FPS = 24.0 STILL_FRAMES = 13 # H3 takes no video shorter than this; a first frame is held still for it CONDITIONER_SPACE = os.environ.get("CONDITIONER_SPACE", "multimodalart/qwen3vl-conditioner") H3_ROOT = os.environ.get("H3_ROOT", "MiniMaxAI/MiniMax-H3") LORA_REPO = os.environ.get("LORA_REPO", "Enderfga/SolarRenderer") GPU_SIZE = os.environ.get("GPU_SIZE", "xlarge") # 61.7 GiB DiT + fp32 VAE does not fit "large" GPU_DURATION = int(os.environ.get("GPU_DURATION", "460")) # measured 393 s (85 placement + 32 encode + 241 sample + 17 decode + moves) + 15% CANVASES = { # the conditioner's labels, one per target canvas (768, 1344): "1344x768 · 16:9 full", (1344, 768): "768x1344 · 9:16 full", } SILENCE = HERE / "assets" / "h3_silence_153_158_170.safetensors" # -------------------------------------------------------------------------------------------- startup load def _download_loras() -> tuple[Path, Path]: """The released non-causal LoRA pair, fetched once into the Space's cache.""" from huggingface_hub import hf_hub_download return ( Path(hf_hub_download(LORA_REPO, "solarrenderer_4step_lora.safetensors")), Path(hf_hub_download(LORA_REPO, "solarrenderer_control_lora.safetensors")), ) def load_model() -> dict: """Everything except the Qwen3-VL conditioner, on the CPU at startup. The transformer is 61.7 GiB and the two VAE copies another ~15 GiB — more than ZeroGPU's startup pack can hold — so placement happens on the card inside the first GPU call instead, exactly as the deployed H3 Spaces do. The 4-step LoRA is merged into the weights and the control LoRA injected as a PEFT adapter before placement, so the card only ever sees the final weights. The `ref2va` `latents_only` encoding chain is built here too, but **without** its text encoder: only the `before_encode` + `vae_encoder` blocks, whose single pretrained weight is the bf16 video VAE. """ from diffusers import ComponentsManager, ModularPipeline from diffusers.modular_pipelines import SequentialPipelineBlocks from solarwm.backends.minimax_h3.artifacts import load_silence_latents from solarwm.backends.minimax_h3.lora import inject_h3_lora, merge_lora_delta from solarwm.backends.minimax_h3.observations import observation_profile_from_train_config from solarwm.backends.minimax_h3.optional import load_conditioners, load_transformer from solarwm.backends.minimax_h3.stage0p5 import H3Stage0p5Core began = time.perf_counter() model_cfg = { "checkpoint_path": H3_ROOT, "transformer_subfolder": "transformer_ref", "torch_dtype": "bfloat16", # flash-attn has no sm_120 (Blackwell) kernel; cuDNN's SDPA is what the deployed H3 Spaces # run on this hardware. Stamped first, and the VAEs are stamped "native" after it — the # registry's active backend is global. "attention_backend": "_native_cudnn", "adapter": {"rank": RANK, "alpha": ALPHA, "dropout": 0.0}, "low_cpu_mem_usage": True, } modules = load_transformer(model_cfg, device=torch.device("cpu")) def lora_pairs(path: str) -> tuple[dict, float]: from safetensors import safe_open from safetensors.torch import load_file with safe_open(path, framework="pt") as handle: meta = handle.metadata() or {} return load_file(path), float(meta.get("alpha", ALPHA)) fewstep, control = _download_loras() pairs, alpha = lora_pairs(str(fewstep)) rank = int(next(v for k, v in pairs.items() if k.endswith("lora_A.weight")).shape[0]) merged = merge_lora_delta(modules.transformer, pairs, alpha=alpha, rank=rank, scale=1.0) print(f"[{VERSION}] four-step LoRA merged into {merged} weights", flush=True) wrapped, lora = inject_h3_lora(modules.transformer, model_cfg["adapter"], base_identity={}) pairs, _alpha = lora_pairs(str(control)) lora.load_state_dict({f"base_model.model.{k}": v for k, v in pairs.items()}, broadcast=False) print(f"[{VERSION}] control LoRA loaded into the adapter", flush=True) conditioners = load_conditioners( model_cfg, device=torch.device("cpu"), qwen=False, video_vae=True, audio_vae=False, schedulers=False ) conditioners.video_vae.set_attention_backend("native") # the fp32 decoder has no cuDNN kernel # The authors' `latents_only` chain, built by hand so the 62 GiB Qwen text encoder that # `omni_preencode.build_pipelines` also loads never enters this Space. blocks = ModularPipeline.from_pretrained(H3_ROOT).blocks sub = blocks.get_workflow("ref2va").sub_blocks latents_only = SequentialPipelineBlocks.from_blocks_dict( {name: sub[name] for name in ("before_encode", "vae_encoder")} ).init_pipeline(H3_ROOT, components_manager=ComponentsManager()) latents_only.load_components(dtype=torch.bfloat16) latents_only.vae.set_attention_backend("native") for name in latents_only.pretrained_component_names: component = getattr(latents_only, name, None) if isinstance(component, torch.nn.Module): component.eval().requires_grad_(False) silence, _ = load_silence_latents(str(SILENCE), pixel_frames=FRAMES) core = H3Stage0p5Core( wrapped, silence, torch.device("cpu"), geometry=None, observations=observation_profile_from_train_config({}), flow_map=False, ) print(f"[{VERSION}] model loaded on CPU in {time.perf_counter() - began:.0f} s", flush=True) return { "core": core, "conditioners": conditioners, "latents_only": latents_only, "placed": False, "vae": None, } MODEL = load_model() # -------------------------------------------------------------------------------------------- media plumbing def canvas_of(path: str) -> tuple[int, int]: """The canvas a clip is encoded on: landscape or portrait by its frame's shape. The same rule as `omni_preencode.canvas` — the part of the frame that is kept decides. """ import omni_preencode as pre stream = pre.open_media(path).streams.video[0] width, height = int(stream.width), int(stream.height) return (768, 1344) if width >= height else (1344, 768) def read_video(path: str, seconds: float = 0.0) -> tuple[np.ndarray, float]: """Decode a clip — only its first `seconds` when given — exactly as `omni_preencode.read_video`.""" import omni_preencode as pre container = pre.open_media(path) stream = container.streams.video[0] stream.thread_type = "AUTO" fps = float(stream.average_rate) limit = int(math.ceil(seconds * fps)) + 2 if seconds else 0 frames = [] for frame in container.decode(stream): frames.append(frame.to_ndarray(format="rgb24")) if limit and len(frames) >= limit: break container.close() if len(frames) < 2: raise ValueError("the structure video is too short: SolarRenderer renders a 6.6 s clip") return np.stack(frames), fps def still_video(first: str, canvas: tuple[int, int], out: str) -> str: """The first frame as the 13-frame 24 fps video H3's reference blocks take. `omni_preencode.reference` lays an aligned still over the canvas and repeats it `STILL_FRAMES` times; pre-encoding it here means the same still reaches both the conditioner (over its API) and the local VAE encoder pixel for pixel. """ import av from PIL import Image import omni_preencode as pre image = Image.open(first).convert("RGB") frame = np.asarray(pre.aligned_image(image, canvas))[None] frames = np.repeat(frame, STILL_FRAMES, axis=0) height, width = canvas with av.open(out, "w") as container: stream = container.add_stream("libx264", rate=int(FPS)) stream.width, stream.height = width, height stream.pix_fmt = "yuv444p" stream.options = {"crf": "0", "preset": "veryfast"} # near-lossless; identical frames either way for pixels in frames: picture = av.VideoFrame.from_ndarray(pixels, format="rgb24") for packet in stream.encode(picture): container.mux(packet) for packet in stream.encode(): container.mux(packet) return out # -------------------------------------------------------------------------------------------- the conditioner half @cache def conditioner(): """The Qwen3-VL half, running in its own Space so this one never loads the 62 GiB encoder.""" from gradio_client import Client return Client(CONDITIONER_SPACE) def encode_remote(prompt: str, structure: str, first_video: str | None, canvas: tuple[int, int]) -> tuple[Path, dict]: """`prompt_embeds` + `text_token_tags` for the request, from the conditioner Space. H3 numbers references per modality: both of this demo's references are videos, so the structure clip is `