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