feat(mlx): add FastH3-8-Step-V2 DMD schedule and thermal-safe MLX inference

- Support official 8-step DMD schedule from fastvideo_inference.json in MLX pipeline
- Implement MiniMaxH3SchedulerState.from_dmd_steps for exact sigma ladder calculation
- Add auto-detection of fastvideo_inference.json in checkpoint converter AdaLN precomputation
- Add inter_step_cooldown_s and per-step MLX graph/cache cleanup to prevent thermal throttling
- Support configurable VAE tile sizes to guarantee 256px tiled decode without grid artifacts
- Skip non-existent shard files gracefully in Qwen3-VL conditioner
This commit is contained in:
vanch
2026-09-17 16:12:02 +08:00
parent 9b0e57fe4b
commit 384eee8aef
4 changed files with 73 additions and 12 deletions
@@ -54,7 +54,29 @@ SUPPORTED_FORMATS = ("int8", "int6", "int4")
DEFAULT_FORMATS = " ".join(SUPPORTED_FORMATS)
def _adaln_cache_timesteps() -> np.ndarray:
def _adaln_cache_timesteps(model_root: str | Path | None = None) -> np.ndarray:
import json
if model_root is not None:
p = Path(model_root)
contract_path = p / "fastvideo_inference.json"
if not contract_path.exists() and p.parent:
contract_path = p.parent / "fastvideo_inference.json"
if contract_path.exists():
try:
contract = json.loads(contract_path.read_text(encoding="utf-8"))
dmd_steps = contract.get("dmd_denoising_steps")
v_shift = float(contract.get("video_scheduler_shift", 10.0))
a_shift = float(contract.get("audio_scheduler_shift", 3.0))
if dmd_steps:
base = np.array([float(r) / 1000.0 for r in dmd_steps] + [0.0], dtype=np.float64)
v_sigmas = v_shift * base / (1.0 + (v_shift - 1.0) * base)
a_sigmas = a_shift * base / (1.0 + (a_shift - 1.0) * base)
v_t = (1.0 - v_sigmas[:-1]).astype(np.float32)
a_t = (1.0 - a_sigmas[:-1]).astype(np.float32)
logger.info("FastH3 checkpoint schedule contract found at %s (%d DMD forwards, video/audio shift=%g/%g)", contract_path, len(dmd_steps), v_shift, a_shift)
return np.unique(np.concatenate([v_t, a_t, [1.0]])).astype(np.float32)
except Exception as exc:
logger.warning("Could not parse %s: %s", contract_path, exc)
video = 1.0 - minimax_h3_sigmas(MINIMAX_H3_VIDEO_SHIFT, 4)[:-1]
audio = 1.0 - minimax_h3_sigmas(MINIMAX_H3_AUDIO_SHIFT, 4)[:-1]
return np.unique(np.concatenate([video, audio, [1.0]])).astype(np.float32)
@@ -84,7 +106,7 @@ def main() -> None:
raise ValueError(f"Unsupported H3 MLX formats: {unsupported}. Choose from {SUPPORTED_FORMATS}.")
out_base = Path(args.out)
out_base.mkdir(parents=True, exist_ok=True)
cache_timesteps = _adaln_cache_timesteps()
cache_timesteps = _adaln_cache_timesteps(args.model_root)
for fmt in formats:
spec = MLXQuantizationSpec.from_name(fmt)