1use std::sync::atomic::{AtomicU64, Ordering};
21
22pub static VAE3D_PROF: [AtomicU64; 8] = [
27 AtomicU64::new(0),
28 AtomicU64::new(0),
29 AtomicU64::new(0),
30 AtomicU64::new(0),
31 AtomicU64::new(0),
32 AtomicU64::new(0),
33 AtomicU64::new(0),
34 AtomicU64::new(0),
35];
36
37pub(crate) fn vae3d_prof_on() -> bool {
38 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
39 *ON.get_or_init(|| std::env::var("CMF_VAE3D_PROF").is_ok())
40}
41
42fn vprof(slot: usize, t: std::time::Instant) {
43 if vae3d_prof_on() {
44 VAE3D_PROF[slot].fetch_add(t.elapsed().as_micros() as u64, Ordering::Relaxed);
45 }
46}
47
48pub fn vae3d_prof_report() -> Option<String> {
50 if !vae3d_prof_on() {
51 return None;
52 }
53 const NAMES: [&str; 8] = [
54 "norm rows",
55 "qkv gemm",
56 "repack+rope",
57 "attention",
58 "out gemm",
59 "ffn",
60 "head+proj",
61 "pixel shuffle",
62 ];
63 let mut v: Vec<(u64, &str)> = VAE3D_PROF
64 .iter()
65 .map(|a| a.load(Ordering::Relaxed))
66 .zip(NAMES)
67 .collect();
68 let total: u64 = v.iter().map(|(u, _)| u).sum();
69 if total == 0 {
70 return None;
71 }
72 v.sort_by(|a, b| b.0.cmp(&a.0));
73 let mut out = format!("vae3d phases (total {:.1} s):\n", total as f64 / 1e6);
74 for (us, name) in v {
75 out.push_str(&format!(
76 " {name:<16} {:>6.1} s {:>5.1}%\n",
77 us as f64 / 1e6,
78 100.0 * us as f64 / total as f64
79 ));
80 }
81 Some(out)
82}
83
84use crate::dit::Proj;
85use crate::pool::Pool;
86use cortiq_core::CmfModel;
87use std::sync::Arc;
88
89const IMAGENET_MEAN: [f32; 3] = [0.485, 0.456, 0.406];
90const IMAGENET_STD: [f32; 3] = [0.229, 0.224, 0.225];
91
92const TILE: usize = 256;
94const TILE_OVERLAP_MIN: usize = 64;
95const CLIP_LENGTH: usize = 17;
98const TOKEN_DROP: usize = 3;
99
100struct Block {
101 norm1: Vec<f32>,
102 norm2: Vec<f32>,
103 scale1: Vec<f32>,
104 scale2: Vec<f32>,
105 qkv: Proj, qkv_b: Vec<f32>,
107 out: Proj,
108 out_b: Vec<f32>,
109 w1: Proj, w1_b: Vec<f32>,
111 w2: Proj, w2_b: Vec<f32>,
113}
114
115pub struct VideoVae {
116 post_quant: Proj,
117 post_quant_b: Vec<f32>,
118 x_embed: Proj,
119 x_embed_b: Vec<f32>,
120 registers: Vec<f32>, blocks: Vec<Block>,
122 norm_out_w: Vec<f32>,
123 norm_out_b: Vec<f32>,
124 proj_out: Proj,
125 proj_out_b: Vec<f32>,
126 latents_mean: Vec<f32>,
127 latents_std: Vec<f32>,
128 pool: Option<Arc<Pool>>,
129 dim: usize,
130 heads: usize,
131 head_dim: usize,
132 z_channels: usize,
133 patch: usize,
134 patch_t: usize,
135 n_reg: usize,
136 rope_theta: f32,
137 rope_dim: usize,
138 eps: f64,
139}
140
141fn layer_norm(x: &[f32], w: &[f32], b: &[f32], eps: f64, dst: &mut [f32]) {
142 let n = x.len() as f64;
143 let mean = x.iter().map(|&v| v as f64).sum::<f64>() / n;
144 let var = x.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / n;
145 let inv = 1.0 / (var + eps).sqrt();
146 for (((d, &v), &g), &bb) in dst.iter_mut().zip(x).zip(w).zip(b) {
147 *d = ((v as f64 - mean) * inv) as f32 * g + bb;
148 }
149}
150
151fn rms_norm_into(x: &[f32], w: &[f32], eps: f64, dst: &mut [f32]) {
153 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
154 let inv = 1.0 / (ss + eps).sqrt();
155 for ((d, &v), &g) in dst.iter_mut().zip(x).zip(w) {
156 *d = (v as f64 * inv) as f32 * g;
157 }
158}
159
160thread_local! {
162 static CHECKING: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
165}
166
167fn rms_norm_plain(x: &mut [f32], eps: f64) {
168 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
169 let inv = 1.0 / (ss + eps).sqrt();
170 for v in x.iter_mut() {
171 *v = (*v as f64 * inv) as f32;
172 }
173}
174
175fn silu(v: f32) -> f32 {
176 v / (1.0 + (-v).exp())
177}
178
179fn rows_par(pool: Option<&crate::pool::Pool>, n: usize, f: &(dyn Fn(usize, usize) + Sync)) {
183 match pool {
184 Some(pl) => pl.run_rows(n, f),
185 None => f(0, n),
186 }
187}
188
189struct SendPtr(*mut f32);
190unsafe impl Send for SendPtr {}
191unsafe impl Sync for SendPtr {}
192impl SendPtr {
193 #[allow(clippy::mut_from_ref)]
195 unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
196 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
197 }
198}
199
200fn softmax_inplace(row: &mut [f32]) {
201 let mx = row.iter().cloned().fold(f32::MIN, f32::max);
202 let mut den = 0f32;
203 for r in row.iter_mut() {
204 *r = (*r - mx).exp();
205 den += *r;
206 }
207 if den > 0.0 {
208 let inv = 1.0 / den;
209 for r in row.iter_mut() {
210 *r *= inv;
211 }
212 }
213}
214
215fn split_tiles(input_len: usize, ratio: usize) -> (Vec<usize>, Vec<usize>, Vec<usize>) {
219 if TILE >= input_len {
220 return (vec![0], vec![input_len], Vec::new());
221 }
222 let mut n = input_len.div_ceil(TILE);
223 let (mut overlaps, remaining);
224 loop {
225 overlaps = vec![TILE_OVERLAP_MIN; n - 1];
226 let total: usize = overlaps.iter().sum();
227 if TILE * n < total + input_len {
228 n += 1;
229 continue;
230 }
231 remaining = TILE * n - total - input_len;
232 break;
233 }
234 for i in 0..remaining / ratio {
235 overlaps[i % (n - 1)] += ratio;
236 }
237 let mut starts = vec![0usize];
238 for i in 0..n - 1 {
239 starts.push(starts[i] + TILE - overlaps[i]);
240 }
241 (starts, vec![TILE; n], overlaps)
242}
243
244impl VideoVae {
245 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
246 let cfg: serde_json::Value = serde_json::from_slice(
247 model
248 .tensor_bytes("vvae.config_json")
249 .map_err(|e| e.to_string())?,
250 )
251 .map_err(|e| format!("vvae.config_json: {e}"))?;
252 let u = |k: &str, d: usize| cfg[k].as_u64().map(|v| v as usize).unwrap_or(d);
253 let f32v = |n: &str| crate::dit::cmf_f32(model, n);
254 let n = u("num_layers", 36);
255 let mut blocks = Vec::with_capacity(n);
256 for l in 0..n {
257 let p = format!("vvae.blocks.{l}");
258 blocks.push(Block {
259 norm1: f32v(&format!("{p}.norm1"))?,
260 norm2: f32v(&format!("{p}.norm2"))?,
261 scale1: f32v(&format!("{p}.scale1"))?,
262 scale2: f32v(&format!("{p}.scale2"))?,
263 qkv: Proj::from_model(model, &format!("{p}.attn.to_qkv.weight"))?,
264 qkv_b: f32v(&format!("{p}.attn.to_qkv.bias"))?,
265 out: Proj::from_model(model, &format!("{p}.attn.to_out.weight"))?,
266 out_b: f32v(&format!("{p}.attn.to_out.bias"))?,
267 w1: Proj::from_model(model, &format!("{p}.ff.w1.weight"))?,
268 w1_b: f32v(&format!("{p}.ff.w1.bias"))?,
269 w2: Proj::from_model(model, &format!("{p}.ff.w2.weight"))?,
270 w2_b: f32v(&format!("{p}.ff.w2.bias"))?,
271 });
272 }
273 let dim = u("dim", 2048);
274 let heads = u("heads", 32);
275 Ok(Self {
276 post_quant: Proj::from_model(model, "vvae.post_quant_conv.weight")?,
277 post_quant_b: f32v("vvae.post_quant_conv.bias")?,
278 x_embed: Proj::from_model(model, "vvae.x_embedder.weight")?,
279 x_embed_b: f32v("vvae.x_embedder.bias")?,
280 registers: f32v("vvae.register_tokens")?,
281 blocks,
282 norm_out_w: f32v("vvae.norm_out.weight")?,
283 norm_out_b: f32v("vvae.norm_out.bias")?,
284 proj_out: Proj::from_model(model, "vvae.proj_out.weight")?,
285 proj_out_b: f32v("vvae.proj_out.bias")?,
286 latents_mean: f32v("vvae.latents_mean")?,
287 latents_std: f32v("vvae.latents_std")?,
288 pool: Pool::from_env(),
289 dim,
290 heads,
291 head_dim: dim / heads,
292 z_channels: u("z_channels", 24),
293 patch: u("patch_size", 16),
294 patch_t: u("patch_size_t", 4),
295 n_reg: u("num_register_tokens", 4),
296 rope_theta: cfg["rope_theta"].as_f64().unwrap_or(100.0) as f32,
297 rope_dim: ((dim / heads) as f64 * cfg["rope_dim_ratio"].as_f64().unwrap_or(0.75))
298 as usize,
299 eps: cfg["eps"].as_f64().unwrap_or(1e-5),
300 })
301 }
302
303 fn axis(n: usize) -> Vec<f32> {
307 (0..n)
308 .map(|i| 2.0 * ((i as f32 + 0.5) / n as f32) - 1.0)
309 .collect()
310 }
311
312 fn rope_angles(&self, t: usize, h: usize, w: usize, suffix: usize) -> Vec<f32> {
315 let k = self.rope_dim / 6; let inv: Vec<f32> = (0..k)
317 .map(|i| {
318 1.0 / self
319 .rope_theta
320 .powf(i as f32 * 2.0 * 3.0 / self.rope_dim as f32)
321 })
322 .collect();
323 let (ta, ha, wa) = (Self::axis(t), Self::axis(h), Self::axis(w));
324 let tau = 2.0 * std::f32::consts::PI;
325 let mut out = Vec::with_capacity((t * h * w + suffix) * 3 * k);
326 for ti in 0..t {
327 for hi in 0..h {
328 for wi in 0..w {
329 for &c in &[ta[ti], ha[hi], wa[wi]] {
330 for &f in &inv {
331 out.push(tau * c * f);
332 }
333 }
334 }
335 }
336 }
337 out.extend(std::iter::repeat_n(0.0, suffix * 3 * k));
338 out
339 }
340
341 fn attention(&self, qkv: &[f32], n: usize, attn: &mut [f32], angles: &[f32]) {
342 let (nh, hd, dim) = (self.heads, self.head_dim, self.dim);
343 let pairs = angles.len() / n; let scale = 1.0 / (hd as f32).sqrt();
345 let pool = self.pool.as_deref();
346 if std::env::var("CMF_VAE3D_ATTN").as_deref() != Ok("cpu")
352 && crate::gpu::enabled_here()
353 && n >= 256
354 {
355 if std::env::var("CMF_VAE3D_CHECK").as_deref() == Ok("1") && !CHECKING.with(|c| c.get())
399 {
400 CHECKING.with(|c| c.set(true));
401 const SENT: f32 = -12345.0;
402 let mut d1 = vec![SENT; attn.len()];
403 let mut d0 = vec![SENT; attn.len()];
404 let ok1 = crate::gpu::vae_attention_packed_layout(
405 qkv,
406 nh,
407 n,
408 hd,
409 scale,
410 angles,
411 self.eps as f32,
412 &mut d1,
413 1,
414 );
415 let ok0 = crate::gpu::vae_attention_packed_layout(
416 qkv,
417 nh,
418 n,
419 hd,
420 scale,
421 angles,
422 self.eps as f32,
423 &mut d0,
424 0,
425 );
426 let wrote = |v: &[f32]| v.iter().filter(|&&x| x != SENT).count();
427 let mut host = vec![0f32; attn.len()];
428 let saved = std::env::var("CMF_VAE3D_ATTN").ok();
429 unsafe { std::env::set_var("CMF_VAE3D_ATTN", "cpu") };
431 self.attention(qkv, n, &mut host, angles);
432 match &saved {
433 Some(v) => unsafe { std::env::set_var("CMF_VAE3D_ATTN", v) },
434 None => unsafe { std::env::remove_var("CMF_VAE3D_ATTN") },
435 }
436 let mut sq = vec![SENT; nh * n * hd];
439 let oks = crate::gpu::dit_split_only(qkv, nh, n, hd, 1, None, &mut sq);
440 let mut sqn = vec![SENT; nh * n * hd];
441 let okn = crate::gpu::dit_split_only(
442 qkv,
443 nh,
444 n,
445 hd,
446 1,
447 Some((angles, self.eps as f32)),
448 &mut sqn,
449 );
450 let mut hq = vec![0f32; nh * n * hd];
451 for p in 0..n {
452 for h in 0..nh {
453 let b = p * 3 * dim + h * 3 * hd;
454 hq[(h * n + p) * hd..(h * n + p) * hd + hd]
455 .copy_from_slice(&qkv[b..b + hd]);
456 }
457 }
458 let sq_wrote = sq.iter().filter(|&&x| x != SENT).count();
459 let sq_diff = sq
460 .iter()
461 .zip(&hq)
462 .fold(0f32, |m, (x, y)| m.max((x - y).abs()));
463 let mut hqn = hq.clone();
466 let pairs = angles.len() / n;
467 for p in 0..n {
468 for h in 0..nh {
469 let r = &mut hqn[(h * n + p) * hd..(h * n + p) * hd + hd];
470 rms_norm_plain(r, self.eps);
471 for j in 0..pairs {
472 let (sn, cs) = angles[p * pairs + j].sin_cos();
473 let (a, b) = (r[j], r[j + pairs]);
474 r[j] = a * cs - b * sn;
475 r[j + pairs] = a * sn + b * cs;
476 }
477 }
478 }
479 let n_wrote = sqn.iter().filter(|&&x| x != SENT).count();
480 let n_diff = sqn
481 .iter()
482 .zip(&hqn)
483 .fold(0f32, |m, (x, y)| m.max((x - y).abs()));
484 eprintln!(
485 "vae split-only: ok={oks} wrote={sq_wrote}/{} maxdiff={sq_diff:.4e} host|max|={:.4e} || +norm: ok={okn} wrote={n_wrote} maxdiff={n_diff:.4e} host|max|={:.4e}",
486 sq.len(),
487 hq.iter().fold(0f32, |m, v| m.max(v.abs())),
488 hqn.iter().fold(0f32, |m, v| m.max(v.abs())),
489 );
490 let md = |a: &[f32], b: &[f32]| {
491 a.iter().zip(b).fold(0f32, |m, (x, y)| m.max((x - y).abs()))
492 };
493 static ONCE: std::sync::Once = std::sync::Once::new();
494 ONCE.call_once(|| {
495 eprintln!(
496 "vae attn check: ok={ok1}/{ok0} wrote={}/{} of {} | host|max| {:.4e} | l1-host {:.4e} | l0-host {:.4e} | l1-l0 {:.4e}",
497 wrote(&d1), wrote(&d0), attn.len(),
498 host.iter().fold(0f32, |m, v| m.max(v.abs())),
499 md(&d1, &host), md(&d0, &host), md(&d1, &d0),
500 )
501 });
502 CHECKING.with(|c| c.set(false));
503 }
504 if std::env::var("CMF_VAE3D_SPLIT").as_deref() == Ok("1")
505 && crate::gpu::vae_attention_packed(
506 qkv,
507 nh,
508 n,
509 hd,
510 scale,
511 angles,
512 self.eps as f32,
513 attn,
514 )
515 {
516 return;
517 }
518 let t_rp = std::time::Instant::now();
519 let mut qa = vec![0f32; nh * n * hd];
520 let mut ka = vec![0f32; nh * n * hd];
521 let mut va = vec![0f32; nh * n * hd];
522 {
523 let (pq, pk, pv) = (
524 SendPtr(qa.as_mut_ptr()),
525 SendPtr(ka.as_mut_ptr()),
526 SendPtr(va.as_mut_ptr()),
527 );
528 let fill = |lo: usize, hi: usize| {
529 for p in lo..hi {
530 for h in 0..nh {
531 let base = p * 3 * dim + h * 3 * hd;
532 let dst = (h * n + p) * hd;
533 let (q, k, v) =
536 unsafe { (pq.row(dst, hd), pk.row(dst, hd), pv.row(dst, hd)) };
537 q.copy_from_slice(&qkv[base..base + hd]);
538 k.copy_from_slice(&qkv[base + hd..base + 2 * hd]);
539 v.copy_from_slice(&qkv[base + 2 * hd..base + 3 * hd]);
540 for x in [&mut *q, &mut *k] {
543 rms_norm_plain(x, self.eps);
544 for j in 0..pairs {
545 let (sn, cs) = angles[p * pairs + j].sin_cos();
546 let (a, b) = (x[j], x[j + pairs]);
547 x[j] = a * cs - b * sn;
548 x[j + pairs] = a * sn + b * cs;
549 }
550 }
551 }
552 }
553 };
554 match pool {
555 Some(pl) => pl.run_rows(n, &fill),
556 None => fill(0, n),
557 }
558 }
559 vprof(2, t_rp);
560 let t_at = std::time::Instant::now();
561 if crate::gpu::dit_attention(&qa, &ka, &va, nh, nh, n, hd, scale, attn) {
562 vprof(3, t_at);
563 return;
564 }
565 }
566 let mut qh = vec![0f32; n * hd];
567 let mut kh = vec![0f32; n * hd];
568 let mut vt = vec![0f32; hd * n];
569 let mut scores = vec![0f32; n * n];
570 let mut oh = vec![0f32; n * hd];
571 for h in 0..nh {
572 for p in 0..n {
573 let base = p * 3 * dim + h * 3 * hd;
576 qh[p * hd..(p + 1) * hd].copy_from_slice(&qkv[base..base + hd]);
577 kh[p * hd..(p + 1) * hd].copy_from_slice(&qkv[base + hd..base + 2 * hd]);
578 for d in 0..hd {
579 vt[d * n + p] = qkv[base + 2 * hd + d];
580 }
581 }
582 for (buf, _) in [(&mut qh, 0), (&mut kh, 1)] {
583 for p in 0..n {
584 let x = &mut buf[p * hd..(p + 1) * hd];
585 rms_norm_plain(x, self.eps);
586 for j in 0..pairs {
587 let (s, c) = angles[p * pairs + j].sin_cos();
588 let (a, b) = (x[j], x[j + pairs]);
589 x[j] = a * c - b * s;
590 x[j + pairs] = a * s + b * c;
591 }
592 }
593 }
594 for v in qh.iter_mut() {
595 *v *= scale;
596 }
597 crate::fcd_ops::gemm_nt(&qh, &kh, &mut scores, n, hd, n, pool);
598 let sp = SendPtr(scores.as_mut_ptr());
599 let soft = |lo: usize, hi: usize| {
600 for r in lo..hi {
601 softmax_inplace(unsafe { sp.row(r * n, n) });
603 }
604 };
605 match pool {
606 Some(pl) => pl.run_rows(n, &soft),
607 None => soft(0, n),
608 }
609 crate::fcd_ops::gemm_nt(&scores, &vt, &mut oh, n, n, hd, pool);
610 for p in 0..n {
611 attn[p * dim + h * hd..p * dim + (h + 1) * hd]
612 .copy_from_slice(&oh[p * hd..(p + 1) * hd]);
613 }
614 }
615 }
616
617 fn decode_tile(&self, z: &[f32], t: usize, h: usize, w: usize) -> Vec<f32> {
620 let pool = self.pool.as_deref();
621 let dim = self.dim;
622 let np = t * h * w;
623 let suffix = 1 + self.n_reg;
624 let n = np + suffix;
625
626 let zc = self.z_channels;
629 let mut rows = vec![0f32; np * zc];
630 for c in 0..zc {
631 for i in 0..np {
632 rows[i * zc + c] = z[c * np + i];
633 }
634 }
635 let mut pq = vec![0f32; np * zc];
636 self.post_quant.matmat(&rows, np, &mut pq, pool);
637 for r in pq.chunks_exact_mut(zc) {
638 for (v, &b) in r.iter_mut().zip(&self.post_quant_b) {
639 *v += b;
640 }
641 }
642 let mut x = vec![0f32; n * dim];
643 self.x_embed.matmat(&pq, np, &mut x[..np * dim], pool);
644 for r in x[..np * dim].chunks_exact_mut(dim) {
645 for (v, &b) in r.iter_mut().zip(&self.x_embed_b) {
646 *v += b;
647 }
648 }
649 x[np * dim..(np + self.n_reg) * dim].copy_from_slice(&self.registers);
651
652 let angles = self.rope_angles(t, h, w, suffix);
653 let mut xn = vec![0f32; n * dim];
654 let mut qkv = vec![0f32; n * 3 * dim];
655 let mut attn = vec![0f32; n * dim];
656 let mut proj = vec![0f32; n * dim];
657 let inner = 4 * dim;
658 for blk in &self.blocks {
659 let t = std::time::Instant::now();
660 {
661 let px = SendPtr(xn.as_mut_ptr());
662 rows_par(pool, n, &|lo, hi| {
663 for p in lo..hi {
664 rms_norm_into(&x[p * dim..(p + 1) * dim], &blk.norm1, self.eps, unsafe {
666 px.row(p * dim, dim)
667 });
668 }
669 });
670 }
671 vprof(0, t);
672 let t = std::time::Instant::now();
673 if std::env::var("CMF_GPU_DEBUG").is_ok() {
679 static ONCE: std::sync::Once = std::sync::Once::new();
680 ONCE.call_once(|| {
681 eprintln!(
682 "vae3d shape: n={n} dim={dim} heads={} head_dim={} pairs={}",
683 self.heads,
684 self.head_dim,
685 angles.len() / n.max(1)
686 )
687 });
688 }
689 let mut fused = false;
690 if std::env::var("CMF_VAE3D_FUSE").as_deref() != Ok("0")
734 && crate::gpu::enabled_here()
735 && n >= 256
736 {
737 if let (Proj::Q(q), Proj::Q(o)) = (&blk.qkv, &blk.out) {
738 if let (Some((m, i)), Some((_, oi))) = (q.mapped_q4tp(), o.mapped_q4tp()) {
739 fused = crate::gpu::vae_qkv_attn_out(
740 m,
741 i,
742 oi,
743 &xn,
744 n,
745 dim,
746 self.heads,
747 self.head_dim,
748 1.0 / (self.head_dim as f32).sqrt(),
749 &angles,
750 self.eps as f32,
751 &blk.qkv_b,
752 &mut proj,
753 );
754 }
755 }
756 }
757 if fused {
758 vprof(1, t);
759 }
760 if !fused {
761 blk.qkv.matmat(&xn, n, &mut qkv, pool);
762 {
763 let pq = SendPtr(qkv.as_mut_ptr());
764 rows_par(pool, n, &|lo, hi| {
765 for p in lo..hi {
766 for (v, &b) in unsafe { pq.row(p * 3 * dim, 3 * dim) }
768 .iter_mut()
769 .zip(&blk.qkv_b)
770 {
771 *v += b;
772 }
773 }
774 });
775 }
776 vprof(1, t);
777 self.attention(&qkv, n, &mut attn, &angles);
778 let t = std::time::Instant::now();
779 blk.out.matmat(&attn, n, &mut proj, pool);
780 vprof(4, t);
781 }
782 {
783 let pxx = SendPtr(x.as_mut_ptr());
784 rows_par(pool, n, &|lo, hi| {
785 for p in lo..hi {
786 let r = &proj[p * dim..(p + 1) * dim];
787 let dst = unsafe { pxx.row(p * dim, dim) };
789 for (i, v) in dst.iter_mut().enumerate() {
790 *v += (r[i] + blk.out_b[i]) * blk.scale1[i];
791 }
792 }
793 });
794 }
795 let t = std::time::Instant::now();
796 {
797 let px = SendPtr(xn.as_mut_ptr());
798 rows_par(pool, n, &|lo, hi| {
799 for p in lo..hi {
800 rms_norm_into(&x[p * dim..(p + 1) * dim], &blk.norm2, self.eps, unsafe {
802 px.row(p * dim, dim)
803 });
804 }
805 });
806 }
807 vprof(0, t);
808 let t_ffn = std::time::Instant::now();
809 if std::env::var("CMF_VAE3D_FFN").as_deref() != Ok("cpu")
814 && crate::gpu::enabled_here()
815 && n >= 64
816 {
817 if let (Proj::Q(q1), Proj::Q(q2)) = (&blk.w1, &blk.w2) {
818 if let (Some((m, i1)), Some((_, i2))) = (q1.mapped_q4tp(), q2.mapped_q4tp()) {
819 let mut fout = vec![0f32; n * dim];
820 if crate::gpu::q4tp_ffn_packed(
821 m,
822 i1,
823 i2,
824 &xn,
825 n,
826 dim,
827 inner,
828 Some(&blk.w1_b),
829 &mut fout,
830 ) {
831 {
832 let pxx = SendPtr(x.as_mut_ptr());
833 rows_par(pool, n, &|lo, hi| {
834 for p in lo..hi {
835 let r = &fout[p * dim..(p + 1) * dim];
836 let dst = unsafe { pxx.row(p * dim, dim) };
838 for (i, v) in dst.iter_mut().enumerate() {
839 *v += (r[i] + blk.w2_b[i]) * blk.scale2[i];
840 }
841 }
842 });
843 }
844 vprof(5, t_ffn);
845 continue;
846 }
847 }
848 }
849 }
850 let mut gu = vec![0f32; n * 2 * inner];
851 blk.w1.matmat(&xn, n, &mut gu, pool);
852 let mut act = vec![0f32; n * inner];
853 for p in 0..n {
854 let r = &gu[p * 2 * inner..(p + 1) * 2 * inner];
855 for i in 0..inner {
856 act[p * inner + i] =
857 silu(r[i] + blk.w1_b[i]) * (r[inner + i] + blk.w1_b[inner + i]);
858 }
859 }
860 blk.w2.matmat(&act, n, &mut proj, pool);
861 {
862 let pxx = SendPtr(x.as_mut_ptr());
863 rows_par(pool, n, &|lo, hi| {
864 for p in lo..hi {
865 let r = &proj[p * dim..(p + 1) * dim];
866 let dst = unsafe { pxx.row(p * dim, dim) };
868 for (i, v) in dst.iter_mut().enumerate() {
869 *v += (r[i] + blk.w2_b[i]) * blk.scale2[i];
870 }
871 }
872 });
873 }
874 vprof(5, t_ffn);
875 }
876
877 let (pt, ps) = (self.patch_t, self.patch);
878 let od = 3 * pt * ps * ps;
879 let t_head = std::time::Instant::now();
880 let mut head = vec![0f32; np * dim];
881 {
882 let ph = SendPtr(head.as_mut_ptr());
883 rows_par(pool, np, &|lo, hi| {
884 for p in lo..hi {
885 layer_norm(
887 &x[p * dim..(p + 1) * dim],
888 &self.norm_out_w,
889 &self.norm_out_b,
890 self.eps,
891 unsafe { ph.row(p * dim, dim) },
892 );
893 }
894 });
895 }
896 let mut out = vec![0f32; np * od];
897 self.proj_out.matmat(&head, np, &mut out, pool);
898 for r in out.chunks_exact_mut(od) {
899 for (v, &b) in r.iter_mut().zip(&self.proj_out_b) {
900 *v += b;
901 }
902 }
903
904 vprof(6, t_head);
905 let t_px = std::time::Instant::now();
906 let (ot, oh, ow) = (t * pt, h * ps, w * ps);
908 let mut px = vec![0f32; 3 * ot * oh * ow];
909 for ti in 0..t {
910 for hi in 0..h {
911 for wi in 0..w {
912 let src =
913 &out[((ti * h + hi) * w + wi) * od..((ti * h + hi) * w + wi + 1) * od];
914 for c in 0..3 {
915 for a in 0..pt {
916 for b in 0..ps {
917 for d in 0..ps {
918 px[((c * ot + ti * pt + a) * oh + hi * ps + b) * ow
919 + wi * ps
920 + d] = src[((c * pt + a) * ps + b) * ps + d];
921 }
922 }
923 }
924 }
925 }
926 }
927 }
928 vprof(7, t_px);
929 px
930 }
931
932 fn decode_clip(&self, z: &[f32], t: usize, zh: usize, zw: usize) -> Vec<f32> {
935 let r = self.patch;
936 let (height, width) = (zh * r, zw * r);
937 let (ys, yl, yo) = split_tiles(height, r);
938 let (xs, xl, xo) = split_tiles(width, r);
939 let frames = t * self.patch_t;
940 let mut canvas = vec![0f32; 3 * frames * height * width];
941
942 let mut row_tails: Vec<Vec<f32>> = Vec::new();
945 let mut out_y = 0usize;
946 for (i, (&ip, &il)) in ys.iter().zip(&yl).enumerate() {
947 let (zi, zl) = (ip / r, il / r);
948 let mut new_tails: Vec<Vec<f32>> = Vec::new();
949 let mut left_tail: Option<Vec<f32>> = None;
950 let mut out_x = 0usize;
951 let mut row_h = 0usize;
952 for (j, (&jp, &jl)) in xs.iter().zip(&xl).enumerate() {
953 let (zj, zw_t) = (jp / r, jl / r);
954 let mut sub = vec![0f32; self.z_channels * t * zl * zw_t];
955 for c in 0..self.z_channels {
956 for ti in 0..t {
957 for hh in 0..zl {
958 for ww in 0..zw_t {
959 sub[((c * t + ti) * zl + hh) * zw_t + ww] =
960 z[((c * t + ti) * zh + zi + hh) * zw + zj + ww];
961 }
962 }
963 }
964 }
965 let mut tile = self.decode_tile(&sub, t, zl, zw_t);
966 let (mut th, mut tw) = (zl * r, zw_t * r);
967 if i + 1 < ys.len() {
968 new_tails.push(crop(&tile, frames, th, tw, th - yo[i], th, 0, tw));
969 }
970 let next_left = if j + 1 < xs.len() {
971 Some(crop(&tile, frames, th, tw, 0, th, tw - xo[j], tw))
972 } else {
973 None
974 };
975 if i > 0 {
976 tile = blend(&row_tails[j], &tile, frames, th, tw, yo[i - 1], 2);
977 }
978 if j > 0 {
979 let lt = left_tail.as_ref().unwrap();
980 tile = blend(lt, &tile, frames, th, tw, xo[j - 1], 3);
981 }
982 left_tail = next_left;
983 if i + 1 < ys.len() {
984 tile = crop(&tile, frames, th, tw, 0, th - yo[i], 0, tw);
985 th -= yo[i];
986 }
987 if j + 1 < xs.len() {
988 tile = crop(&tile, frames, th, tw, 0, th, 0, tw - xo[j]);
989 tw -= xo[j];
990 }
991 for c in 0..3 {
992 for f in 0..frames {
993 for hh in 0..th {
994 let dst = ((c * frames + f) * height + out_y + hh) * width + out_x;
995 let src = ((c * frames + f) * th + hh) * tw;
996 canvas[dst..dst + tw].copy_from_slice(&tile[src..src + tw]);
997 }
998 }
999 }
1000 out_x += tw;
1001 row_h = th;
1002 }
1003 row_tails = new_tails;
1004 out_y += row_h;
1005 }
1006 canvas
1007 }
1008
1009 pub fn decode(&self, z: &[f32], t_lat: usize, zh: usize, zw: usize) -> (Vec<f32>, usize) {
1012 let zc = self.z_channels;
1013 let np = t_lat * zh * zw;
1014 let mut zz = vec![0f32; zc * np];
1015 for c in 0..zc {
1016 let (m, s) = (self.latents_mean[c], self.latents_std[c]);
1017 for i in 0..np {
1018 zz[c * np + i] = z[c * np + i] * s + m;
1019 }
1020 }
1021
1022 let ratio_t = self.patch_t;
1023 let chunk_tokens = CLIP_LENGTH.div_ceil(ratio_t); let token_overlap = (chunk_tokens - TOKEN_DROP % chunk_tokens) % chunk_tokens; let frame_pre_pad = (ratio_t - CLIP_LENGTH % ratio_t) % ratio_t; let frame_overlap = (token_overlap * ratio_t).saturating_sub(frame_pre_pad); let chunk_dec = chunk_tokens * ratio_t; let mut pseudo = t_lat + TOKEN_DROP;
1033 let mut pad = 0usize;
1034 if pseudo % chunk_tokens != 0 {
1035 pad = chunk_tokens - pseudo % chunk_tokens;
1036 pseudo += pad;
1037 }
1038 let mut chunks = pseudo / chunk_tokens - usize::from(TOKEN_DROP > 0);
1039 if chunks < 1 {
1040 pad += chunk_tokens;
1041 chunks += 1;
1042 }
1043 let t_pad = t_lat + pad;
1044 if pad > 0 {
1045 let mut grown = vec![0f32; zc * t_pad * zh * zw];
1046 for c in 0..zc {
1047 for ti in 0..t_pad {
1048 let src = ti.min(t_lat - 1);
1049 let a = (c * t_lat + src) * zh * zw;
1050 let b = (c * t_pad + ti) * zh * zw;
1051 grown[b..b + zh * zw].copy_from_slice(&zz[a..a + zh * zw]);
1052 }
1053 }
1054 zz = grown;
1055 }
1056
1057 let (h, w) = (zh * self.patch, zw * self.patch);
1058 let mut out: Vec<f32> = Vec::new();
1059 let mut carry: Option<(Vec<f32>, usize)> = None;
1060 for i in 0..chunks {
1061 let a = i * chunk_tokens;
1062 let b = (a + chunk_tokens + token_overlap).min(t_pad);
1063 let n = b.saturating_sub(a.min(t_pad));
1064 if n == 0 {
1065 continue;
1066 }
1067 let mut sub = vec![0f32; zc * n * zh * zw];
1068 for c in 0..zc {
1069 let src = (c * t_pad + a) * zh * zw;
1070 let dst = c * n * zh * zw;
1071 sub[dst..dst + n * zh * zw].copy_from_slice(&zz[src..src + n * zh * zw]);
1072 }
1073 let dec = self.decode_clip(&sub, n, zh, zw);
1074 let dec_frames = n * ratio_t;
1075 for j in 0..2 {
1076 let fa = j * chunk_dec;
1077 let fb = (fa + chunk_dec).min(dec_frames);
1078 if fb <= fa + frame_pre_pad {
1079 continue;
1080 }
1081 let mut part = frames_of(&dec, dec_frames, h, w, fa + frame_pre_pad, fb);
1082 let mut pn = fb - fa - frame_pre_pad;
1083 if j == 0 {
1084 if let Some((tail, tn)) = carry.take() {
1085 part = blend_frames(&tail, tn, &part, pn, h, w, frame_overlap);
1086 pn = part.len() / (3 * h * w);
1087 }
1088 append_frames(&mut out, &part, pn, h, w);
1089 } else {
1090 carry = Some((part, pn));
1091 }
1092 }
1093 if i + 1 == chunks {
1094 if let Some((tail, tn)) = carry.take() {
1095 append_frames(&mut out, &tail, tn, h, w);
1096 }
1097 }
1098 }
1099
1100 let frames = out.len() / (3 * h * w);
1104 let want = t_lat * ratio_t - pad_frames(t_lat, pad, chunk_tokens, ratio_t);
1105 for c in 0..3 {
1106 let base = c * frames * h * w;
1107 for v in out[base..base + frames * h * w].iter_mut() {
1108 *v = (*v * IMAGENET_STD[c] + IMAGENET_MEAN[c]).clamp(0.0, 1.0);
1109 }
1110 }
1111 let keep = want.min(frames);
1112 if keep < frames {
1113 let mut trimmed = vec![0f32; 3 * keep * h * w];
1114 for c in 0..3 {
1115 let src = c * frames * h * w;
1116 let dst = c * keep * h * w;
1117 trimmed[dst..dst + keep * h * w].copy_from_slice(&out[src..src + keep * h * w]);
1118 }
1119 out = trimmed;
1120 }
1121 (out, keep)
1122 }
1123
1124 pub fn spatial_ratio(&self) -> usize {
1125 self.patch
1126 }
1127 pub fn temporal_ratio(&self) -> usize {
1128 self.patch_t
1129 }
1130}
1131
1132fn pad_frames(t_lat: usize, pad: usize, chunk_tokens: usize, ratio_t: usize) -> usize {
1134 if pad == 0 {
1135 return 0;
1136 }
1137 let intra = CLIP_LENGTH % ratio_t;
1138 if intra == 0 {
1139 return pad * ratio_t;
1140 }
1141 (0..pad)
1142 .map(|k| {
1143 if (t_lat + k) % chunk_tokens == 0 {
1144 intra
1145 } else {
1146 ratio_t
1147 }
1148 })
1149 .sum()
1150}
1151
1152#[allow(clippy::too_many_arguments)]
1154fn crop(
1155 x: &[f32],
1156 f: usize,
1157 h: usize,
1158 w: usize,
1159 y0: usize,
1160 y1: usize,
1161 x0: usize,
1162 x1: usize,
1163) -> Vec<f32> {
1164 let (nh, nw) = (y1 - y0, x1 - x0);
1165 let mut out = vec![0f32; 3 * f * nh * nw];
1166 for c in 0..3 {
1167 for fi in 0..f {
1168 for yy in 0..nh {
1169 let s = ((c * f + fi) * h + y0 + yy) * w + x0;
1170 let d = ((c * f + fi) * nh + yy) * nw;
1171 out[d..d + nw].copy_from_slice(&x[s..s + nw]);
1172 }
1173 }
1174 }
1175 out
1176}
1177
1178#[allow(clippy::too_many_arguments)]
1182fn blend(
1183 a: &[f32],
1184 b: &[f32],
1185 f: usize,
1186 h: usize,
1187 w: usize,
1188 extent: usize,
1189 dim: usize,
1190) -> Vec<f32> {
1191 let ah = a.len() / (3 * f * w);
1192 let aw = a.len() / (3 * f * h);
1193 let mut out = b.to_vec();
1194 let e = if dim == 2 {
1195 extent.min(ah).min(h)
1196 } else {
1197 extent.min(aw).min(w)
1198 };
1199 for c in 0..3 {
1200 for fi in 0..f {
1201 for k in 0..e {
1202 let wb = k as f32 / e as f32;
1203 let wa = 1.0 - wb;
1204 if dim == 2 {
1205 let sa = ((c * f + fi) * ah + ah - e + k) * w;
1206 let sb = ((c * f + fi) * h + k) * w;
1207 for x in 0..w {
1208 out[sb + x] = a[sa + x] * wa + b[sb + x] * wb;
1209 }
1210 } else {
1211 for y in 0..h {
1212 let sa = ((c * f + fi) * h + y) * aw + aw - e + k;
1213 let sb = ((c * f + fi) * h + y) * w + k;
1214 out[sb] = a[sa] * wa + b[sb] * wb;
1215 }
1216 }
1217 }
1218 }
1219 }
1220 out
1221}
1222
1223fn frames_of(x: &[f32], f: usize, h: usize, w: usize, a: usize, b: usize) -> Vec<f32> {
1225 let n = b - a;
1226 let mut out = vec![0f32; 3 * n * h * w];
1227 for c in 0..3 {
1228 let s = (c * f + a) * h * w;
1229 let d = c * n * h * w;
1230 out[d..d + n * h * w].copy_from_slice(&x[s..s + n * h * w]);
1231 }
1232 out
1233}
1234
1235#[allow(clippy::too_many_arguments)]
1237fn blend_frames(
1238 tail: &[f32],
1239 tn: usize,
1240 part: &[f32],
1241 pn: usize,
1242 h: usize,
1243 w: usize,
1244 extent: usize,
1245) -> Vec<f32> {
1246 let e = extent.min(tn).min(pn);
1247 let mut out = part.to_vec();
1248 for c in 0..3 {
1249 for k in 0..e {
1250 let wb = k as f32 / e as f32;
1251 let wa = 1.0 - wb;
1252 let s = ((c * tn) + tn - e + k) * h * w;
1253 let d = ((c * pn) + k) * h * w;
1254 for i in 0..h * w {
1255 out[d + i] = tail[s + i] * wa + part[d + i] * wb;
1256 }
1257 }
1258 }
1259 out
1260}
1261
1262fn append_frames(out: &mut Vec<f32>, part: &[f32], n: usize, h: usize, w: usize) {
1264 let old = out.len() / (3 * h * w);
1265 let total = old + n;
1266 let mut grown = vec![0f32; 3 * total * h * w];
1267 for c in 0..3 {
1268 if old > 0 {
1269 let s = c * old * h * w;
1270 let d = c * total * h * w;
1271 grown[d..d + old * h * w].copy_from_slice(&out[s..s + old * h * w]);
1272 }
1273 let s = c * n * h * w;
1274 let d = (c * total + old) * h * w;
1275 grown[d..d + n * h * w].copy_from_slice(&part[s..s + n * h * w]);
1276 }
1277 *out = grown;
1278}
1279
1280struct EncConv {
1295 w: Vec<f32>, b: Vec<f32>,
1297 out_ch: usize,
1298 in_ch: usize,
1299 k: usize,
1300 stride: usize,
1301 pad: usize,
1302}
1303
1304impl EncConv {
1305 fn load(model: &Arc<CmfModel>, name: &str, stride: usize, pad: usize) -> Result<Self, String> {
1306 let e = model
1307 .tensor(&format!("{name}.weight"))
1308 .ok_or_else(|| format!("missing {name}.weight"))?;
1309 let (out_ch, in_ch, k) = (e.shape[0], e.shape[1], e.shape[2]);
1311 Ok(Self {
1312 w: crate::dit::cmf_f32(model, &format!("{name}.weight"))?,
1313 b: crate::dit::cmf_f32(model, &format!("{name}.bias"))?,
1314 out_ch,
1315 in_ch,
1316 k,
1317 stride,
1318 pad,
1319 })
1320 }
1321
1322 fn apply(
1324 &self,
1325 x: &[f32],
1326 h: usize,
1327 w: usize,
1328 pool: Option<&Pool>,
1329 ) -> (Vec<f32>, usize, usize) {
1330 let (ph, pw) = (h + 2 * self.pad, w + 2 * self.pad);
1331 let oh = (ph - self.k) / self.stride + 1;
1332 let ow = (pw - self.k) / self.stride + 1;
1333 let refl = |i: isize, n: usize| -> usize {
1336 let n = n as isize;
1337 let mut i = i;
1338 while i < 0 || i >= n {
1339 if i < 0 {
1340 i = -i;
1341 }
1342 if i >= n {
1343 i = 2 * (n - 1) - i;
1344 }
1345 }
1346 i as usize
1347 };
1348 let mut out = vec![0f32; self.out_ch * oh * ow];
1349 let ptr = SendPtr(out.as_mut_ptr());
1350 let work = |lo: usize, hi: usize| {
1351 for o in lo..hi {
1352 let dst = unsafe { ptr.row(o * oh * ow, oh * ow) };
1354 dst.fill(self.b[o]);
1355 for i in 0..self.in_ch {
1356 let ker = &self.w[(o * self.in_ch + i) * self.k * self.k
1357 ..(o * self.in_ch + i + 1) * self.k * self.k];
1358 let src = &x[i * h * w..(i + 1) * h * w];
1359 for oy in 0..oh {
1360 for ox in 0..ow {
1361 let mut acc = 0f32;
1362 for ky in 0..self.k {
1363 let sy = (oy * self.stride + ky) as isize - self.pad as isize;
1364 let sy = refl(sy, h);
1365 for kx in 0..self.k {
1366 let sx = (ox * self.stride + kx) as isize - self.pad as isize;
1367 acc += ker[ky * self.k + kx] * src[sy * w + refl(sx, w)];
1368 }
1369 }
1370 dst[oy * ow + ox] += acc;
1371 }
1372 }
1373 }
1374 }
1375 };
1376 match pool {
1377 Some(p) => p.run_rows(self.out_ch, &work),
1378 None => work(0, self.out_ch),
1379 }
1380 (out, oh, ow)
1381 }
1382}
1383
1384struct GroupNorm {
1386 w: Vec<f32>,
1387 b: Vec<f32>,
1388}
1389
1390impl GroupNorm {
1391 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
1392 Ok(Self {
1393 w: crate::dit::cmf_f32(model, &format!("{name}.weight"))?,
1394 b: crate::dit::cmf_f32(model, &format!("{name}.bias"))?,
1395 })
1396 }
1397
1398 fn apply(&self, x: &mut [f32], ch: usize, hw: usize) {
1399 let groups = 32;
1400 let per = ch / groups;
1401 for g in 0..groups {
1402 let seg = &mut x[g * per * hw..(g + 1) * per * hw];
1403 let n = seg.len() as f64;
1404 let mean = seg.iter().map(|&v| v as f64).sum::<f64>() / n;
1405 let var = seg.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / n;
1406 let inv = 1.0 / (var + 1e-6).sqrt();
1407 for (i, v) in seg.iter_mut().enumerate() {
1408 let c = g * per + i / hw;
1409 *v = ((*v as f64 - mean) * inv) as f32 * self.w[c] + self.b[c];
1410 }
1411 }
1412 }
1413}
1414
1415struct ResBlock {
1416 norm1: GroupNorm,
1417 norm2: GroupNorm,
1418 conv1: EncConv,
1419 conv2: EncConv,
1420 shortcut: Option<EncConv>,
1421}
1422
1423pub struct VideoVaeEncoder {
1426 conv_in: EncConv,
1427 levels: Vec<(Vec<ResBlock>, Option<EncConv>)>,
1428 norm_out: GroupNorm,
1429 conv_out: EncConv,
1430 quant: Vec<f32>, quant_b: Vec<f32>,
1432 latents_mean: Vec<f32>,
1433 latents_std: Vec<f32>,
1434 pool: Option<Arc<Pool>>,
1435 z_channels: usize,
1436 ratio: usize,
1437}
1438
1439impl VideoVaeEncoder {
1440 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
1441 let cfg: serde_json::Value = serde_json::from_slice(
1442 model
1443 .tensor_bytes("vvae.config_json")
1444 .map_err(|e| e.to_string())?,
1445 )
1446 .map_err(|e| format!("vvae.config_json: {e}"))?;
1447 let space_down: Vec<usize> = cfg["space_down"]
1448 .as_array()
1449 .map(|a| a.iter().map(|v| v.as_u64().unwrap_or(1) as usize).collect())
1450 .unwrap_or_else(|| vec![2, 2, 2, 2, 1, 1]);
1451 let n_res = cfg["num_res_blocks"].as_u64().unwrap_or(2) as usize;
1452 let mut levels = Vec::new();
1453 for (i, &sd) in space_down.iter().enumerate() {
1454 let mut blocks = Vec::new();
1455 for j in 0..n_res {
1456 let p = format!("vvae.enc.down.{i}.block.{j}");
1457 let shortcut = model
1458 .tensor(&format!("{p}.nin_shortcut.weight"))
1459 .map(|_| EncConv::load(model, &format!("{p}.nin_shortcut"), 1, 0))
1460 .transpose()?;
1461 blocks.push(ResBlock {
1462 norm1: GroupNorm::load(model, &format!("{p}.norm1"))?,
1463 norm2: GroupNorm::load(model, &format!("{p}.norm2"))?,
1464 conv1: EncConv::load(model, &format!("{p}.conv1"), 1, 1)?,
1465 conv2: EncConv::load(model, &format!("{p}.conv2"), 1, 1)?,
1466 shortcut,
1467 });
1468 }
1469 let down = if sd > 1 {
1472 Some(EncConv::load(
1473 model,
1474 &format!("vvae.enc.down.{i}.downsample.conv"),
1475 sd,
1476 0,
1477 )?)
1478 } else {
1479 None
1480 };
1481 levels.push((blocks, down));
1482 }
1483 Ok(Self {
1484 conv_in: EncConv::load(model, "vvae.enc.conv_in", 1, 1)?,
1485 levels,
1486 norm_out: GroupNorm::load(model, "vvae.enc.norm_out")?,
1487 conv_out: EncConv::load(model, "vvae.enc.conv_out", 1, 1)?,
1488 quant: crate::dit::cmf_f32(model, "vvae.quant_conv.weight")?,
1489 quant_b: crate::dit::cmf_f32(model, "vvae.quant_conv.bias")?,
1490 latents_mean: crate::dit::cmf_f32(model, "vvae.latents_mean")?,
1491 latents_std: crate::dit::cmf_f32(model, "vvae.latents_std")?,
1492 pool: Pool::from_env(),
1493 z_channels: cfg["z_channels"].as_u64().unwrap_or(24) as usize,
1494 ratio: cfg["patch_size"].as_u64().unwrap_or(16) as usize,
1495 })
1496 }
1497
1498 fn encode_tile(&self, x: &[f32], h: usize, w: usize) -> (Vec<f32>, usize, usize) {
1500 let pool = self.pool.as_deref();
1501 let (mut cur, mut ch, mut cw) = self.conv_in.apply(x, h, w, pool);
1502 let mut c = self.conv_in.out_ch;
1503 for (blocks, down) in &self.levels {
1504 for b in blocks {
1505 let mut hh = cur.clone();
1506 self.norm_out_like(&b.norm1, &mut hh, c, ch * cw);
1507 for v in hh.iter_mut() {
1508 *v = silu(*v);
1509 }
1510 let (mut t, th, tw) = b.conv1.apply(&hh, ch, cw, pool);
1511 let oc = b.conv1.out_ch;
1512 self.norm_out_like(&b.norm2, &mut t, oc, th * tw);
1513 for v in t.iter_mut() {
1514 *v = silu(*v);
1515 }
1516 let (t2, th2, tw2) = b.conv2.apply(&t, th, tw, pool);
1517 let skip = match &b.shortcut {
1518 Some(s) => s.apply(&cur, ch, cw, pool).0,
1519 None => cur.clone(),
1520 };
1521 cur = t2.iter().zip(&skip).map(|(&a, &b)| a + b).collect();
1522 (ch, cw, c) = (th2, tw2, oc);
1523 }
1524 if let Some(d) = down {
1525 let (padded, ph, pw) = reflect_pad_far(&cur, c, ch, cw);
1528 let (t, th, tw) = d.apply(&padded, ph, pw, pool);
1529 cur = t;
1530 (ch, cw, c) = (th, tw, d.out_ch);
1531 }
1532 }
1533 self.norm_out_like(&self.norm_out, &mut cur, c, ch * cw);
1534 for v in cur.iter_mut() {
1535 *v = silu(*v);
1536 }
1537 let (moments, mh, mw) = self.conv_out.apply(&cur, ch, cw, pool);
1538 let n = 2 * self.z_channels;
1540 let mut out = vec![0f32; n * mh * mw];
1541 for o in 0..n {
1542 for p in 0..mh * mw {
1543 let mut acc = self.quant_b[o];
1544 for i in 0..n {
1545 acc += self.quant[o * n + i] * moments[i * mh * mw + p];
1546 }
1547 out[o * mh * mw + p] = acc;
1548 }
1549 }
1550 (out, mh, mw)
1551 }
1552
1553 fn norm_out_like(&self, g: &GroupNorm, x: &mut [f32], ch: usize, hw: usize) {
1554 g.apply(x, ch, hw);
1555 }
1556
1557 pub fn encode_frame(&self, rgb: &[f32], h: usize, w: usize) -> (Vec<f32>, usize, usize) {
1560 let mut x = vec![0f32; 3 * h * w];
1562 for c in 0..3 {
1563 for p in 0..h * w {
1564 let v = (rgb[c * h * w + p] + 1.0) * 0.5;
1565 x[c * h * w + p] = (v - IMAGENET_MEAN[c]) / IMAGENET_STD[c];
1566 }
1567 }
1568 let r = self.ratio;
1569 let (ys, yl, yo) = split_tiles(h, r);
1570 let (xs, xl, xo) = split_tiles(w, r);
1571 let (zh, zw) = (h / r, w / r);
1572 let zc = 2 * self.z_channels;
1573 let mut rows: Vec<Vec<Vec<f32>>> = Vec::new();
1574 let mut dims: Vec<Vec<(usize, usize)>> = Vec::new();
1575 for (&ip, &il) in ys.iter().zip(&yl) {
1576 let mut row = Vec::new();
1577 let mut rd = Vec::new();
1578 for (&jp, &jl) in xs.iter().zip(&xl) {
1579 let mut sub = vec![0f32; 3 * il * jl];
1580 for c in 0..3 {
1581 for yy in 0..il {
1582 let s = (c * h + ip + yy) * w + jp;
1583 let d = (c * il + yy) * jl;
1584 sub[d..d + jl].copy_from_slice(&x[s..s + jl]);
1585 }
1586 }
1587 let (t, th, tw) = self.encode_tile(&sub, il, jl);
1588 row.push(t);
1589 rd.push((th, tw));
1590 }
1591 rows.push(row);
1592 dims.push(rd);
1593 }
1594 let mut canvas = vec![0f32; zc * zh * zw];
1596 let mut out_y = 0usize;
1597 for i in 0..rows.len() {
1598 let mut out_x = 0usize;
1599 let mut row_h = 0usize;
1600 for j in 0..rows[i].len() {
1601 let (th, tw) = dims[i][j];
1602 let mut tile = rows[i][j].clone();
1603 let (mut ch_, mut cw_) = (th, tw);
1604 if i > 0 {
1605 tile = blend_plane(
1606 &rows[i - 1][j],
1607 &tile,
1608 zc,
1609 dims[i - 1][j],
1610 (ch_, cw_),
1611 yo[i - 1] / r,
1612 0,
1613 );
1614 }
1615 if j > 0 {
1616 tile = blend_plane(
1617 &rows[i][j - 1],
1618 &tile,
1619 zc,
1620 dims[i][j - 1],
1621 (ch_, cw_),
1622 xo[j - 1] / r,
1623 1,
1624 );
1625 }
1626 if i + 1 < rows.len() {
1627 tile = crop_plane(&tile, zc, ch_, cw_, 0, ch_ - yo[i] / r, 0, cw_);
1628 ch_ -= yo[i] / r;
1629 }
1630 if j + 1 < rows[i].len() {
1631 tile = crop_plane(&tile, zc, ch_, cw_, 0, ch_, 0, cw_ - xo[j] / r);
1632 cw_ -= xo[j] / r;
1633 }
1634 for c in 0..zc {
1635 for yy in 0..ch_ {
1636 let d = (c * zh + out_y + yy) * zw + out_x;
1637 let s = (c * ch_ + yy) * cw_;
1638 canvas[d..d + cw_].copy_from_slice(&tile[s..s + cw_]);
1639 }
1640 }
1641 out_x += cw_;
1642 row_h = ch_;
1643 }
1644 out_y += row_h;
1645 }
1646 let mut z = vec![0f32; self.z_channels * zh * zw];
1648 for c in 0..self.z_channels {
1649 let (m, s) = (self.latents_mean[c], self.latents_std[c]);
1650 for p in 0..zh * zw {
1651 z[c * zh * zw + p] = (canvas[c * zh * zw + p] - m) / s;
1652 }
1653 }
1654 (z, zh, zw)
1655 }
1656}
1657
1658fn reflect_pad_far(x: &[f32], c: usize, h: usize, w: usize) -> (Vec<f32>, usize, usize) {
1660 let (ph, pw) = (h + 1, w + 1);
1661 let mut out = vec![0f32; c * ph * pw];
1662 for ci in 0..c {
1663 for y in 0..ph {
1664 let sy = if y < h { y } else { h - 2 };
1665 for x2 in 0..pw {
1666 let sx = if x2 < w { x2 } else { w - 2 };
1667 out[(ci * ph + y) * pw + x2] = x[(ci * h + sy) * w + sx];
1668 }
1669 }
1670 }
1671 (out, ph, pw)
1672}
1673
1674fn blend_plane(
1676 a: &[f32],
1677 b: &[f32],
1678 c: usize,
1679 ad: (usize, usize),
1680 bd: (usize, usize),
1681 extent: usize,
1682 dim: usize,
1683) -> Vec<f32> {
1684 let (ah, aw) = ad;
1685 let (bh, bw) = bd;
1686 let mut out = b.to_vec();
1687 let e = if dim == 0 {
1688 extent.min(ah).min(bh)
1689 } else {
1690 extent.min(aw).min(bw)
1691 };
1692 if e == 0 {
1693 return out;
1694 }
1695 for ci in 0..c {
1696 for k in 0..e {
1697 let wb = k as f32 / e as f32;
1698 let wa = 1.0 - wb;
1699 if dim == 0 {
1700 for x in 0..bw.min(aw) {
1701 let sa = (ci * ah + ah - e + k) * aw + x;
1702 let sb = (ci * bh + k) * bw + x;
1703 out[sb] = a[sa] * wa + b[sb] * wb;
1704 }
1705 } else {
1706 for y in 0..bh.min(ah) {
1707 let sa = (ci * ah + y) * aw + aw - e + k;
1708 let sb = (ci * bh + y) * bw + k;
1709 out[sb] = a[sa] * wa + b[sb] * wb;
1710 }
1711 }
1712 }
1713 }
1714 out
1715}
1716
1717#[allow(clippy::too_many_arguments)]
1719fn crop_plane(
1720 x: &[f32],
1721 c: usize,
1722 h: usize,
1723 w: usize,
1724 y0: usize,
1725 y1: usize,
1726 x0: usize,
1727 x1: usize,
1728) -> Vec<f32> {
1729 let (nh, nw) = (y1 - y0, x1 - x0);
1730 let mut out = vec![0f32; c * nh * nw];
1731 for ci in 0..c {
1732 for y in 0..nh {
1733 let s = (ci * h + y0 + y) * w + x0;
1734 let d = (ci * nh + y) * nw;
1735 out[d..d + nw].copy_from_slice(&x[s..s + nw]);
1736 }
1737 }
1738 out
1739}
1740
1741#[cfg(test)]
1742mod tests {
1743 use super::*;
1744
1745 #[test]
1746 fn tile_schedule_matches_the_reference() {
1747 let (s, l, o) = split_tiles(288, 16);
1749 assert_eq!(s, vec![0, 32]);
1750 assert_eq!(l, vec![256, 256]);
1751 assert_eq!(o, vec![224]);
1752 let (s, l, o) = split_tiles(512, 16);
1754 assert_eq!(s, vec![0, 128, 256]);
1755 assert_eq!(l, vec![256; 3]);
1756 assert_eq!(o, vec![128, 128]);
1757 assert_eq!(split_tiles(256, 16).0, vec![0]);
1759 assert_eq!(split_tiles(128, 16).1, vec![128]);
1760 }
1761
1762 #[test]
1763 fn temporal_constants_come_out_as_the_reference_computes_them() {
1764 let ratio_t = 4usize;
1765 let chunk = CLIP_LENGTH.div_ceil(ratio_t);
1766 assert_eq!(chunk, 5);
1767 assert_eq!((chunk - TOKEN_DROP % chunk) % chunk, 2);
1768 assert_eq!((ratio_t - CLIP_LENGTH % ratio_t) % ratio_t, 3);
1769 }
1770}