# 官方 Ref2VA 原生 diffusers 管线 — 立项验证(2026-08-17,Minimax_5090 GPU6)

## 结论

**可行,已跑通。** 官方 `transformer_ref` + lightx2v ref2v turbo 4step v0.1(alpha 烙入)
+ 剪枝 r8 + fp8 + sage + compile + group-offload,768×1344×124f/5 steps(=4 NFE),
**热跑 227s/条、峰值 24-25.5 GiB**,两条样片换人成立。

## 权重线(全部新产物)

| 物 | 路径 | 说明 |
|---|---|---|
| 官方 transformer_ref | HF 缓存 snapshot `42ed227e.../transformer_ref/`(62G) | 本次 snapshot_download 补齐(HF ~700MB/s,73s) |
| ref2v turbo LoRA | `~/h3_native/lora_converted/minimax_h3_ref2v_turbo_4step_v0.1_bf16.safetensors` | 文件 metadata **alpha=8 / rank=128**(与 sglang 文档一致)→ lora_B × 1/16 烙入 + `.default.` 段清洗 |
| 剪枝产物 | `~/h3_native/weights/ref2va_turbo4_pruned_r8/`(38G) | `prune_ref2va_diffusers.py`(= prune_fl2va 改 `subfolder="transformer_ref"`),LoRA 先融合再剪;curve residual **4.259e-05**(官方基线 4.3e-05 同量级),51 个 adaln linear 折叠 |

## 推理驱动

`~/h3_native/ref2va_render.py`(batch_render.py 的 ref2va modular 用法 × fl2va_render.py 的
量化/compile/计时骨架):

- `ModularPipeline(workflow="ref2va")`,**真文本编码**(Qwen3-VL 32B 走 accelerate cpu_offload
  流式,官方 `MiniMaxH3Ref2VATextEncoderStep` 自己生成 reference 标签+视觉块,prompt 是正文);
- 参考语义按官方释放值:视频参考 768 短边 canvas、**图参 2048 短边,均未动**;
- 源视频截到渲染窗口(124f/24fps=5.17s),音轨丢弃;
- embed cache 按 (prompt, cond, ref, 尺寸) 键控(图参像素会进 Qwen 视觉塔,只按 prompt 会串 case);
- 跑法:`~/h3_native/run_ref2va.sh <gpu> <tag> --manifest ... --transformer-dir
  ~/h3_native/weights/ref2va_turbo4_pruned_r8 --quantize fp8 --compile --offload
  --steps 5 --num-frames 124 --width 768 --height 1344`

## 速度分解(768×1344×124f,5 steps → 4 NFE,fp8+sage+compile+offload,GPU6)

| 阶段 | 冷(首条) | 热(同进程第二条) |
|---|---|---|
| 组件加载+量化+compile 声明 | 48-53s(一次性) | — |
| Setup | 3s | 3s |
| 文本编码(Qwen3-VL 流式) | 27-32s | **0s(embed-cache 命中)** |
| 参考编码(VAE:视频参考+2048 图参) | 32s | 32s |
| denoise 4 NFE | 194-271s(首 1-2 步 66-135s 是 compile 预热) | **步稳态 31.3-31.8s**;本次首步 81s(图参尺寸变化触发一次重编译) |
| VAE decode | 15s | 15s |
| encode_video | 2s | 2s |
| **端到端** | **246-350s** | **227s**(形状全热理论 ≈178s) |

峰值显存:24.1-25.5 GiB(offload 模式)。

- **fp8 常驻(--resident)在 denoise 第 0 步 OOM**(31.16/31.36 GiB,还差 654MB)——
  ref2va 打包序列 ≈ **80k 行**(目标 36.3k + 视频参考 36.3k + 2048 图参 ~6.8k + 文本),
  是 fl2va 768p/124f(~37k)的 2.2 倍,fp8 权重 ~19 GiB + 激活 >12 GiB 装不下 32G 卡。
  按预案退 `--offload`(nvfp4 未试,已知有画质伪影)。
- `cache_size_limit` 8→64 后稳态不变(31.5s/步),说明是算力瓶颈不是重编译;
  但换 case(图参尺寸不同 → 序列长度变)会触发一次 ~50s 的重编译,`dynamic=True`
  与 offload 互斥(weakref 坑,见 torch-compile-playbook),服务化时可按固定图参尺寸预热。

## 与当年 ComfyUI ref2va 生产数字对照

| | ComfyUI 生产(int8_convrot,4步) | 本原生管线(fp8 r8 剪枝,4 NFE) |
|---|---|---|
| 480p/3s 热跑 | 9.5-9.7s | 未测(ref2va 工作流强制 ≥120 帧=5s,无 3s 档) |
| 768p/124f | ~125s/条 | 227s 热(全热 ≈178s) |

差距主要不是内核而是**任务口径**:本次每条都带完整视频参考块(+36k 行,序列翻倍,attention
开销超线性),且 offload 模式下参考编码/decode 各付 ~15-32s 搬运;ComfyUI 的 125s 数字
对应的生产任务未必带同规模视频参考。denoise 稳态 31.5s/步 × 4 = 126s,与 ComfyUI 全程 125s
同量级——大头都在 denoise,原生线没有数量级劣势,进一步压要么 nvfp4(画质代价)要么砍参考 token。

## 样片(~/h3_native/out/)

- `ref2va_9202c3df.mp4`(+`_warm`):摄影棚跳舞西装男 → 牛角动漫巨汉。**背景/机位/动作全保留,
  身份替换干净**,仅右前臂纹身处轻微涂抹。
- `ref2va_9267a48f.mp4`:冰湖滑冰女 → 蓝 T 恤男。替换成立、场景/动作保留;快速旋转段有源
  角色残留(黑手套、裙摆状轮廓),属难 case(剧烈动作+剪影差异大),需更多 case 评估画质线。

计时明细:`~/h3_native/out/stats_ref2va.jsonl`;日志 `/tmp/ref2va_{c1_off,m2case}.log`。

## 已知约束

- ref2va 工作流强制 5.0-15.0s(帧数 120-360 且 n%17==5),最短 124 帧;宽高 32 对齐。
- steps=5 → 4 次 NFE(调度器取区间),正好对上 4step LoRA。
- 参考 canvas 语义(768/2048)不可改——batch_render 实测改小后模型无视图参。

---

# 2026-08-18 最终优化栈重验(viggle 定稿栈移植)

## 同步与改造

- 盘点结论:**两机 `~/h3_native/diffusers`+`pydeps` 已逐文件 bit 一致**(3733 个 .py 聚合 md5 相同,
  H3_MEMOPT 三件套补丁已在 Minimax 树内:`transformer_minimax_h3.py`+`activations.py`),
  fl2va_render.py / native_worker.py / save_quantized.py 三份 md5 亦与 viggle 生产一致——13:14 的
  rsync 已带齐,本次零同步。
- `ref2va_render.py` 升级(原版备份 `ref2va_render.py.bak-preupgrade`):
  1. 文件头 THP 防护(`NUMPY_MADVISE_HUGEPAGE=0` + `prctl(PR_SET_THP_DISABLE)`)+
     `faulthandler.register(SIGUSR1)`,照抄 native_worker.py;
  2. `--denoise-evict-vae`(resident/offload 模式:denoise 第 0 步把双 VAE 挪 CPU,decode 前挪回),
     照抄 fl2va_render.py;
  3. `--refencode-evict-transformer`(resident 专用:参考编码期把 fp8 transformer 挪出、编码完挪回,
     本次新增,见下文为何必须);
  4. [JOB] 记录新增 evict_vae/memopt/ffchunk 字段。
  run_ref2va.sh 本来就指自包含树(pydeps+diffusers/src),未动。

## 重验(768×1344×124f,steps 5=4 NFE,fp8 r8 剪枝,sage+compile,同款 9202c3df case ×2/进程)

### 配置 A:resident + H3_MEMOPT=1 FFCHUNK=4 + --denoise-evict-vae(GPU6)——OOM,但败点变了

fp8 常驻静息 = **29.1 GiB**(fp8 transformer 实测 18.86 GiB + VAE/其余),只剩 ~2.2 GiB;
**参考编码**第一笔 1.43 GiB fp32 像素张量(2048px 图参归一化)即 OOM——比 8/17 的败点
(denoise 第 0 步差 654MB)更早。evict 钩子只救 denoise,救不了 ref-encode。

### 配置 A2:A + --refencode-evict-transformer(GPU7)——denoise 解锁,decode 差 58MB

- ref-encode:transformer 出 11.0s/回 8.8s,编码本体 32.5s(共 52.5s);
- **denoise 常驻成立**:稳态 **30.1-30.5s/步**(冷首步 70.8s compile 预热),denoise 后 alloc 19.1 GiB,
  无 OOM——8/17 的 654MB 缺口确实被三件套+evict 补掉了;
- **VideoDecode OOM,差 58MB**(VAE 回卡后 30.57 GiB alloc,decode conv 激活装不下),冷热两条同败。
- 判决:即便再压 58MB(如 --vae-tile 128),resident 每条要付 transformer 2×PCIe 往返 ~20s,
  只换回 denoise ~1.1s/步 × 4 ≈ 4.4s——**净亏**。ref2va 此规格在 32G 卡上"真常驻"不成立且不划算,
  不再追。

### 配置 B(定稿):offload + H3_MEMOPT=1 FFCHUNK=4 + --denoise-evict-vae(GPU6)

| 指标 | 8/17 裸 offload 基线 | 本次 B |
|---|---|---|
| 热跑端到端 | 227s(含一次跨 case 重编译;全热理论 ≈178s) | **188.0s**(同 case 全热,首 NFE 37.4s) |
| 冷跑端到端 | 246-350s | 273.9s |
| denoise 稳态 | 31.3-31.8s/步 | 31.2-31.3s/步(持平——三件套是省显存不是提速) |
| **峰值显存** | **24.1-25.5 GiB** | **17.3-17.5 GiB**(−7~8G:denoise 期 VAE 已 evict) |

热跑分解:setup 3 + 文本 0(embed-cache)+ ref-encode 32.5 + denoise 131.2(evict out 5.5 含内)
+ vae back 3.7 + decode 19.5 + encode_video 1.7 ≈ 188s。
两条样片正常出片(`out/ref2va_9202c3df_v2{,warm}.mp4`),计时在 `out/stats_ref2va.jsonl`。

**生产跑法**:`H3_MEMOPT=1 H3_MEMOPT_FFCHUNK=4 run_ref2va.sh <gpu> <tag> --manifest ...
--transformer-dir ~/h3_native/weights/ref2va_turbo4_pruned_r8 --quantize fp8 --compile --offload
--denoise-evict-vae --steps 5 --num-frames 124 --width 768 --height 1344`

## fp8 .pt 序列化

`~/h3_native/weights/ref2va_turbo4_pruned_fp8.pt`(**18.86 GiB**,.building→原子 rename)已生成:
quantize 29.2s + save 21.9s,`--verify` 全过(312 quantized + 328 plain tensors bitwise 一致,
GPU7 真 forward 逐位相等)。加载走 `save_quantized.load_quantized_transformer`(meta shell +
assign=True,~19s)。**注意:ref2va_render.py 尚无 --quantized-ckpt 接线**(按指示未强行加),
接线可照 native_worker.py 的用法,省每次启动的 CPU quantize ~29s+38G 读盘。

## 遗留

1. --quantized-ckpt 未接线(见上);
2. 峰值降到 17.5G 后,更长帧数(226/243f)或更高分辨率的 ref2va 有了显存空间,未测;
3. resident 相关两个新 flag 保留在驱动里(带 assert 防误用),文档即本节;
4. 速度想再压只剩 nvfp4(已知伪影)或砍参考 token,三件套/evict 对速度无贡献(预期内)。

---

# 768p15s 21:9 容量测试(2026-08-18,GPU4-7 并行,定稿流式栈)

## 结论

**768 短边 / 21:9(1792×768)/ 15.083s(362f)/ 4 NFE 在 32G 卡上全矩阵跑通,零 OOM。**
视频参考按产品上限拉满 15s(单槽或双槽 7.5s+7.5s)+ 768p 源图参:768 参考 canvas 峰值
29.8-30.2 GiB(余量仅 ~1.2G,**这就是参考上限**);480 参考 canvas 峰值 24.0-24.2 GiB
且全程快 ~1.8×,换人肉眼与 768 档持平、并未被模型无视。

固定配置:fp8 r8 剪枝 + --compile + sage + --offload + H3_MEMOPT=1(FFCHUNK 不开)
+ --denoise-evict-vae + --steps 5(=4 NFE)。输出 362f=15.083s 通过新 `--max-duration 15.1`
覆盖(fl2va 同款 property 覆盖,15.0s 上限是 `MiniMaxH3ModularPipeline.max_duration` property)。

## 为跑通付出的两个新驱动改动(纯显存搬运,数值不变)

1. **lean text encode**:官方 `get_qwen3vl_prompt_embeds` 用 `output_hidden_states=True`
   持有全部 ~65 层 hidden state,15s 参考的 presentation 是 ~20k token → 仅此一项 ~13 GiB,
   四配置全在文本编码期 OOM(第一轮实测)。改为 pre-hook 只捕获 layer 50 的输入
   (hidden_states[50] 即 layers[50] 的输入),`output_hidden_states=False`——单独等价性
   测试**逐位一致**(torch.equal True)。
2. **--textencode-evict-vae**:文本编码期把双 VAE 挪 CPU(闲置),编码完挪回给参考编码。

另新增:`--ref-canvas <px>`(只动视频参考 canvas;**必须在 load_components 之后设置**,
load_components 会把 ConfigSpec 默认值重置回 768——第一轮 480 档白跑就是这个坑,已在驱动内
修正并加 render 前 assert)、manifest `cond2` 第二视频槽(官方支持每请求至多 3 视频参考,
每个都截到输出帧数、向下贴齐 17n+5)。备份:`ref2va_render.py.bak-pre15s`。

## 矩阵与实测(冷=进程内首条含 21:9 新形状编译预热;热=同进程第二条)

| 测试 | 参考构成 | 参考canvas | 总行数(算) | 峰值GiB | 稳态s/步 | 冷e2e | 热e2e | 换人 |
|---|---|---|---|---|---|---|---|---|
| T1(GPU4) | 15.08s 单槽视频 + 图参 | 768(实为1568×672) | ~281.6k | 29.8/30.1 | 240-247 | 1410.5s | **1149.7s** | 成立 |
| T2(GPU5) | 同 T1 | 480(实为960×416) | ~207.4k | 24.0/24.2 | 134-137 | 803.7s | **651.9s** | 成立,肉眼持平 |
| T3(GPU6) | 7.5s+7.5s 双槽 + 图参 | 768 | ~282.6k | 30.0/30.2 | 236-244 | 1435.6s | **1131.0s** | 成立(后半段内容正确驱动) |
| T4(GPU7) | 同 T3 | 480 | ~206.3k | 24.0/24.2 | 120-122 | 768.0s | **633.3s** | 成立 |

行数构成(潜帧=(帧-5)/17×5+2;行=潜帧×W/32×H/32):目标 107×56×24=143,808;
视频参考 768canvas 107×49×21=110,103(双槽 2×52×1029=107,016)、480canvas 107×30×13=41,730
(双槽 40,560);2048 图参 64×107=6,848;presentation 文本行实测 T1 19,663 / T2 13,807 /
T3 23,728 / T4 13,840(480 canvas 连 Qwen 视觉 token 也省);音频 ~1.2k。

热跑分解(T1):ref-encode 96.3s + denoise 4×~243s + vae back 4.5 + decode 67.0 + encode_video 7.4;
T2 同项:32.1 / 4×~135 / 3.7 / 65.0 / 6.0。文本编码(embed-cache 未命中时)768档 102-120s、
480档 38-41s。图参源 690×1152 本就 ≤768p 短边,canvas 2048 未动。

## "参考最多能带多少"

- 768 参考 canvas:**15s 视频参考(单/双槽均可)+1 张图参已是天花板**——峰值 30.1-30.2G,
  距 31.36G 仅 ~1.2G。按实测斜率(T1−T2:75k 行 ≈ 5.9G)只够再加 ~1 张 2048 图参(6.8k 行),
  再多视频参考必炸。
- 480 参考 canvas:余量 ~7.2G ≈ 还能再吃 ~90k 行(约再 +2×15s@480 视频参考),且速度近乎减半。
  样片肉眼换人/背景/动作保持与 768 档看不出差距,**是 15s 长参考的推荐档**,建议后续做正式
  画质对比确认。
- 362f(15.083s)输出无需退 345f;`--max-duration 15.1` 即可,工作流其余校验(帧数 17n+5、
  120-360 区间的 max 端)均由该 property 控制。

样片:`out/ref2va_15s_t{1,2,3,4}_{cold,warm}.mp4`;素材 `testdata_15s/`(21:9 由 9202c3df
竖版源 blur-pad 而成);计时 `out/stats_ref2va.jsonl`;日志 `/tmp/ref2va_t{1,2,3,4}_15s.log`。
