1use crate::dit::Proj;
21use crate::pool::Pool;
22use cortiq_core::CmfModel;
23use std::sync::Arc;
24
25const IMAGENET_MEAN: [f32; 3] = [0.485, 0.456, 0.406];
26const IMAGENET_STD: [f32; 3] = [0.229, 0.224, 0.225];
27
28const TILE: usize = 256;
30const TILE_OVERLAP_MIN: usize = 64;
31const CLIP_LENGTH: usize = 17;
34const TOKEN_DROP: usize = 3;
35
36struct Block {
37 norm1: Vec<f32>,
38 norm2: Vec<f32>,
39 scale1: Vec<f32>,
40 scale2: Vec<f32>,
41 qkv: Proj, qkv_b: Vec<f32>,
43 out: Proj,
44 out_b: Vec<f32>,
45 w1: Proj, w1_b: Vec<f32>,
47 w2: Proj, w2_b: Vec<f32>,
49}
50
51pub struct VideoVae {
52 post_quant: Proj,
53 post_quant_b: Vec<f32>,
54 x_embed: Proj,
55 x_embed_b: Vec<f32>,
56 registers: Vec<f32>, blocks: Vec<Block>,
58 norm_out_w: Vec<f32>,
59 norm_out_b: Vec<f32>,
60 proj_out: Proj,
61 proj_out_b: Vec<f32>,
62 latents_mean: Vec<f32>,
63 latents_std: Vec<f32>,
64 pool: Option<Arc<Pool>>,
65 dim: usize,
66 heads: usize,
67 head_dim: usize,
68 z_channels: usize,
69 patch: usize,
70 patch_t: usize,
71 n_reg: usize,
72 rope_theta: f32,
73 rope_dim: usize,
74 eps: f64,
75}
76
77fn layer_norm(x: &[f32], w: &[f32], b: &[f32], eps: f64, dst: &mut [f32]) {
78 let n = x.len() as f64;
79 let mean = x.iter().map(|&v| v as f64).sum::<f64>() / n;
80 let var = x.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / n;
81 let inv = 1.0 / (var + eps).sqrt();
82 for (((d, &v), &g), &bb) in dst.iter_mut().zip(x).zip(w).zip(b) {
83 *d = ((v as f64 - mean) * inv) as f32 * g + bb;
84 }
85}
86
87fn rms_norm_into(x: &[f32], w: &[f32], eps: f64, dst: &mut [f32]) {
89 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
90 let inv = 1.0 / (ss + eps).sqrt();
91 for ((d, &v), &g) in dst.iter_mut().zip(x).zip(w) {
92 *d = (v as f64 * inv) as f32 * g;
93 }
94}
95
96fn rms_norm_plain(x: &mut [f32], eps: f64) {
98 let ss = x.iter().map(|&v| (v as f64) * (v as f64)).sum::<f64>() / x.len() as f64;
99 let inv = 1.0 / (ss + eps).sqrt();
100 for v in x.iter_mut() {
101 *v = (*v as f64 * inv) as f32;
102 }
103}
104
105fn silu(v: f32) -> f32 {
106 v / (1.0 + (-v).exp())
107}
108
109struct SendPtr(*mut f32);
110unsafe impl Send for SendPtr {}
111unsafe impl Sync for SendPtr {}
112impl SendPtr {
113 #[allow(clippy::mut_from_ref)]
115 unsafe fn row(&self, off: usize, len: usize) -> &mut [f32] {
116 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
117 }
118}
119
120fn softmax_inplace(row: &mut [f32]) {
121 let mx = row.iter().cloned().fold(f32::MIN, f32::max);
122 let mut den = 0f32;
123 for r in row.iter_mut() {
124 *r = (*r - mx).exp();
125 den += *r;
126 }
127 if den > 0.0 {
128 let inv = 1.0 / den;
129 for r in row.iter_mut() {
130 *r *= inv;
131 }
132 }
133}
134
135fn split_tiles(input_len: usize, ratio: usize) -> (Vec<usize>, Vec<usize>, Vec<usize>) {
139 if TILE >= input_len {
140 return (vec![0], vec![input_len], Vec::new());
141 }
142 let mut n = input_len.div_ceil(TILE);
143 let (mut overlaps, mut remaining);
144 loop {
145 overlaps = vec![TILE_OVERLAP_MIN; n - 1];
146 let total: usize = overlaps.iter().sum();
147 if TILE * n < total + input_len {
148 n += 1;
149 continue;
150 }
151 remaining = TILE * n - total - input_len;
152 break;
153 }
154 for i in 0..remaining / ratio {
155 overlaps[i % (n - 1)] += ratio;
156 }
157 let mut starts = vec![0usize];
158 for i in 0..n - 1 {
159 starts.push(starts[i] + TILE - overlaps[i]);
160 }
161 (starts, vec![TILE; n], overlaps)
162}
163
164impl VideoVae {
165 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
166 let cfg: serde_json::Value = serde_json::from_slice(
167 model.tensor_bytes("vvae.config_json").map_err(|e| e.to_string())?,
168 )
169 .map_err(|e| format!("vvae.config_json: {e}"))?;
170 let u = |k: &str, d: usize| cfg[k].as_u64().map(|v| v as usize).unwrap_or(d);
171 let f32v = |n: &str| crate::dit::cmf_f32(model, n);
172 let n = u("num_layers", 36);
173 let mut blocks = Vec::with_capacity(n);
174 for l in 0..n {
175 let p = format!("vvae.blocks.{l}");
176 blocks.push(Block {
177 norm1: f32v(&format!("{p}.norm1"))?,
178 norm2: f32v(&format!("{p}.norm2"))?,
179 scale1: f32v(&format!("{p}.scale1"))?,
180 scale2: f32v(&format!("{p}.scale2"))?,
181 qkv: Proj::from_model(model, &format!("{p}.attn.to_qkv.weight"))?,
182 qkv_b: f32v(&format!("{p}.attn.to_qkv.bias"))?,
183 out: Proj::from_model(model, &format!("{p}.attn.to_out.weight"))?,
184 out_b: f32v(&format!("{p}.attn.to_out.bias"))?,
185 w1: Proj::from_model(model, &format!("{p}.ff.w1.weight"))?,
186 w1_b: f32v(&format!("{p}.ff.w1.bias"))?,
187 w2: Proj::from_model(model, &format!("{p}.ff.w2.weight"))?,
188 w2_b: f32v(&format!("{p}.ff.w2.bias"))?,
189 });
190 }
191 let dim = u("dim", 2048);
192 let heads = u("heads", 32);
193 Ok(Self {
194 post_quant: Proj::from_model(model, "vvae.post_quant_conv.weight")?,
195 post_quant_b: f32v("vvae.post_quant_conv.bias")?,
196 x_embed: Proj::from_model(model, "vvae.x_embedder.weight")?,
197 x_embed_b: f32v("vvae.x_embedder.bias")?,
198 registers: f32v("vvae.register_tokens")?,
199 blocks,
200 norm_out_w: f32v("vvae.norm_out.weight")?,
201 norm_out_b: f32v("vvae.norm_out.bias")?,
202 proj_out: Proj::from_model(model, "vvae.proj_out.weight")?,
203 proj_out_b: f32v("vvae.proj_out.bias")?,
204 latents_mean: f32v("vvae.latents_mean")?,
205 latents_std: f32v("vvae.latents_std")?,
206 pool: Pool::from_env(),
207 dim,
208 heads,
209 head_dim: dim / heads,
210 z_channels: u("z_channels", 24),
211 patch: u("patch_size", 16),
212 patch_t: u("patch_size_t", 4),
213 n_reg: u("num_register_tokens", 4),
214 rope_theta: cfg["rope_theta"].as_f64().unwrap_or(100.0) as f32,
215 rope_dim: ((dim / heads) as f64 * cfg["rope_dim_ratio"].as_f64().unwrap_or(0.75))
216 as usize,
217 eps: cfg["eps"].as_f64().unwrap_or(1e-5),
218 })
219 }
220
221 fn axis(n: usize) -> Vec<f32> {
225 (0..n)
226 .map(|i| 2.0 * ((i as f32 + 0.5) / n as f32) - 1.0)
227 .collect()
228 }
229
230 fn rope_angles(&self, t: usize, h: usize, w: usize, suffix: usize) -> Vec<f32> {
233 let k = self.rope_dim / 6; let inv: Vec<f32> = (0..k)
235 .map(|i| 1.0 / self.rope_theta.powf(i as f32 * 2.0 * 3.0 / self.rope_dim as f32))
236 .collect();
237 let (ta, ha, wa) = (Self::axis(t), Self::axis(h), Self::axis(w));
238 let tau = 2.0 * std::f32::consts::PI;
239 let mut out = Vec::with_capacity((t * h * w + suffix) * 3 * k);
240 for ti in 0..t {
241 for hi in 0..h {
242 for wi in 0..w {
243 for &c in &[ta[ti], ha[hi], wa[wi]] {
244 for &f in &inv {
245 out.push(tau * c * f);
246 }
247 }
248 }
249 }
250 }
251 out.extend(std::iter::repeat_n(0.0, suffix * 3 * k));
252 out
253 }
254
255 fn attention(&self, qkv: &[f32], n: usize, attn: &mut [f32], angles: &[f32]) {
256 let (nh, hd, dim) = (self.heads, self.head_dim, self.dim);
257 let pairs = angles.len() / n; let scale = 1.0 / (hd as f32).sqrt();
259 let pool = self.pool.as_deref();
260 let mut qh = vec![0f32; n * hd];
261 let mut kh = vec![0f32; n * hd];
262 let mut vt = vec![0f32; hd * n];
263 let mut scores = vec![0f32; n * n];
264 let mut oh = vec![0f32; n * hd];
265 for h in 0..nh {
266 for p in 0..n {
267 let base = p * 3 * dim + h * 3 * hd;
270 qh[p * hd..(p + 1) * hd].copy_from_slice(&qkv[base..base + hd]);
271 kh[p * hd..(p + 1) * hd].copy_from_slice(&qkv[base + hd..base + 2 * hd]);
272 for d in 0..hd {
273 vt[d * n + p] = qkv[base + 2 * hd + d];
274 }
275 }
276 for (buf, _) in [(&mut qh, 0), (&mut kh, 1)] {
277 for p in 0..n {
278 let x = &mut buf[p * hd..(p + 1) * hd];
279 rms_norm_plain(x, self.eps);
280 for j in 0..pairs {
281 let (s, c) = angles[p * pairs + j].sin_cos();
282 let (a, b) = (x[j], x[j + pairs]);
283 x[j] = a * c - b * s;
284 x[j + pairs] = a * s + b * c;
285 }
286 }
287 }
288 for v in qh.iter_mut() {
289 *v *= scale;
290 }
291 crate::fcd_ops::gemm_nt(&qh, &kh, &mut scores, n, hd, n, pool);
292 let sp = SendPtr(scores.as_mut_ptr());
293 let soft = |lo: usize, hi: usize| {
294 for r in lo..hi {
295 softmax_inplace(unsafe { sp.row(r * n, n) });
297 }
298 };
299 match pool {
300 Some(pl) => pl.run_rows(n, &soft),
301 None => soft(0, n),
302 }
303 crate::fcd_ops::gemm_nt(&scores, &vt, &mut oh, n, n, hd, pool);
304 for p in 0..n {
305 attn[p * dim + h * hd..p * dim + (h + 1) * hd]
306 .copy_from_slice(&oh[p * hd..(p + 1) * hd]);
307 }
308 }
309 }
310
311 fn decode_tile(&self, z: &[f32], t: usize, h: usize, w: usize) -> Vec<f32> {
314 let pool = self.pool.as_deref();
315 let dim = self.dim;
316 let np = t * h * w;
317 let suffix = 1 + self.n_reg;
318 let n = np + suffix;
319
320 let zc = self.z_channels;
323 let mut rows = vec![0f32; np * zc];
324 for c in 0..zc {
325 for i in 0..np {
326 rows[i * zc + c] = z[c * np + i];
327 }
328 }
329 let mut pq = vec![0f32; np * zc];
330 self.post_quant.matmat(&rows, np, &mut pq, pool);
331 for r in pq.chunks_exact_mut(zc) {
332 for (v, &b) in r.iter_mut().zip(&self.post_quant_b) {
333 *v += b;
334 }
335 }
336 let mut x = vec![0f32; n * dim];
337 self.x_embed.matmat(&pq, np, &mut x[..np * dim], pool);
338 for r in x[..np * dim].chunks_exact_mut(dim) {
339 for (v, &b) in r.iter_mut().zip(&self.x_embed_b) {
340 *v += b;
341 }
342 }
343 x[np * dim..(np + self.n_reg) * dim].copy_from_slice(&self.registers);
345
346 let angles = self.rope_angles(t, h, w, suffix);
347 let mut xn = vec![0f32; n * dim];
348 let mut qkv = vec![0f32; n * 3 * dim];
349 let mut attn = vec![0f32; n * dim];
350 let mut proj = vec![0f32; n * dim];
351 let inner = 4 * dim;
352 for blk in &self.blocks {
353 for (o, src) in xn.chunks_exact_mut(dim).zip(x.chunks_exact(dim)) {
354 rms_norm_into(src, &blk.norm1, self.eps, o);
355 }
356 blk.qkv.matmat(&xn, n, &mut qkv, pool);
357 for r in qkv.chunks_exact_mut(3 * dim) {
358 for (v, &b) in r.iter_mut().zip(&blk.qkv_b) {
359 *v += b;
360 }
361 }
362 self.attention(&qkv, n, &mut attn, &angles);
363 blk.out.matmat(&attn, n, &mut proj, pool);
364 for (p, r) in proj.chunks_exact_mut(dim).enumerate() {
365 for (i, v) in r.iter_mut().enumerate() {
366 *v += blk.out_b[i];
367 x[p * dim + i] += *v * blk.scale1[i];
368 }
369 }
370 for (o, src) in xn.chunks_exact_mut(dim).zip(x.chunks_exact(dim)) {
371 rms_norm_into(src, &blk.norm2, self.eps, o);
372 }
373 let mut gu = vec![0f32; n * 2 * inner];
374 blk.w1.matmat(&xn, n, &mut gu, pool);
375 let mut act = vec![0f32; n * inner];
376 for p in 0..n {
377 let r = &gu[p * 2 * inner..(p + 1) * 2 * inner];
378 for i in 0..inner {
379 act[p * inner + i] =
380 silu(r[i] + blk.w1_b[i]) * (r[inner + i] + blk.w1_b[inner + i]);
381 }
382 }
383 blk.w2.matmat(&act, n, &mut proj, pool);
384 for (p, r) in proj.chunks_exact_mut(dim).enumerate() {
385 for (i, v) in r.iter_mut().enumerate() {
386 *v += blk.w2_b[i];
387 x[p * dim + i] += *v * blk.scale2[i];
388 }
389 }
390 }
391
392 let (pt, ps) = (self.patch_t, self.patch);
393 let od = 3 * pt * ps * ps;
394 let mut head = vec![0f32; np * dim];
395 for (o, src) in head.chunks_exact_mut(dim).zip(x[..np * dim].chunks_exact(dim)) {
396 layer_norm(src, &self.norm_out_w, &self.norm_out_b, self.eps, o);
397 }
398 let mut out = vec![0f32; np * od];
399 self.proj_out.matmat(&head, np, &mut out, pool);
400 for r in out.chunks_exact_mut(od) {
401 for (v, &b) in r.iter_mut().zip(&self.proj_out_b) {
402 *v += b;
403 }
404 }
405
406 let (ot, oh, ow) = (t * pt, h * ps, w * ps);
408 let mut px = vec![0f32; 3 * ot * oh * ow];
409 for ti in 0..t {
410 for hi in 0..h {
411 for wi in 0..w {
412 let src = &out[((ti * h + hi) * w + wi) * od..((ti * h + hi) * w + wi + 1) * od];
413 for c in 0..3 {
414 for a in 0..pt {
415 for b in 0..ps {
416 for d in 0..ps {
417 px[((c * ot + ti * pt + a) * oh + hi * ps + b) * ow
418 + wi * ps + d] =
419 src[((c * pt + a) * ps + b) * ps + d];
420 }
421 }
422 }
423 }
424 }
425 }
426 }
427 px
428 }
429
430 fn decode_clip(&self, z: &[f32], t: usize, zh: usize, zw: usize) -> Vec<f32> {
433 let r = self.patch;
434 let (height, width) = (zh * r, zw * r);
435 let (ys, yl, yo) = split_tiles(height, r);
436 let (xs, xl, xo) = split_tiles(width, r);
437 let frames = t * self.patch_t;
438 let mut canvas = vec![0f32; 3 * frames * height * width];
439
440 let mut row_tails: Vec<Vec<f32>> = Vec::new();
443 let mut out_y = 0usize;
444 for (i, (&ip, &il)) in ys.iter().zip(&yl).enumerate() {
445 let (zi, zl) = (ip / r, il / r);
446 let mut new_tails: Vec<Vec<f32>> = Vec::new();
447 let mut left_tail: Option<Vec<f32>> = None;
448 let mut out_x = 0usize;
449 let mut row_h = 0usize;
450 for (j, (&jp, &jl)) in xs.iter().zip(&xl).enumerate() {
451 let (zj, zw_t) = (jp / r, jl / r);
452 let mut sub = vec![0f32; self.z_channels * t * zl * zw_t];
453 for c in 0..self.z_channels {
454 for ti in 0..t {
455 for hh in 0..zl {
456 for ww in 0..zw_t {
457 sub[((c * t + ti) * zl + hh) * zw_t + ww] =
458 z[((c * t + ti) * zh + zi + hh) * zw + zj + ww];
459 }
460 }
461 }
462 }
463 let mut tile = self.decode_tile(&sub, t, zl, zw_t);
464 let (mut th, mut tw) = (zl * r, zw_t * r);
465 if i + 1 < ys.len() {
466 new_tails.push(crop(&tile, frames, th, tw, th - yo[i], th, 0, tw));
467 }
468 let next_left = if j + 1 < xs.len() {
469 Some(crop(&tile, frames, th, tw, 0, th, tw - xo[j], tw))
470 } else {
471 None
472 };
473 if i > 0 {
474 tile = blend(&row_tails[j], &tile, frames, th, tw, yo[i - 1], 2);
475 }
476 if j > 0 {
477 let lt = left_tail.as_ref().unwrap();
478 tile = blend(lt, &tile, frames, th, tw, xo[j - 1], 3);
479 }
480 left_tail = next_left;
481 if i + 1 < ys.len() {
482 tile = crop(&tile, frames, th, tw, 0, th - yo[i], 0, tw);
483 th -= yo[i];
484 }
485 if j + 1 < xs.len() {
486 tile = crop(&tile, frames, th, tw, 0, th, 0, tw - xo[j]);
487 tw -= xo[j];
488 }
489 for c in 0..3 {
490 for f in 0..frames {
491 for hh in 0..th {
492 let dst = ((c * frames + f) * height + out_y + hh) * width + out_x;
493 let src = ((c * frames + f) * th + hh) * tw;
494 canvas[dst..dst + tw].copy_from_slice(&tile[src..src + tw]);
495 }
496 }
497 }
498 out_x += tw;
499 row_h = th;
500 }
501 row_tails = new_tails;
502 out_y += row_h;
503 }
504 canvas
505 }
506
507 pub fn decode(&self, z: &[f32], t_lat: usize, zh: usize, zw: usize) -> (Vec<f32>, usize) {
510 let zc = self.z_channels;
511 let np = t_lat * zh * zw;
512 let mut zz = vec![0f32; zc * np];
513 for c in 0..zc {
514 let (m, s) = (self.latents_mean[c], self.latents_std[c]);
515 for i in 0..np {
516 zz[c * np + i] = z[c * np + i] * s + m;
517 }
518 }
519
520 let ratio_t = self.patch_t;
521 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;
531 let mut pad = 0usize;
532 if pseudo % chunk_tokens != 0 {
533 pad = chunk_tokens - pseudo % chunk_tokens;
534 pseudo += pad;
535 }
536 let mut chunks = pseudo / chunk_tokens - usize::from(TOKEN_DROP > 0);
537 if chunks < 1 {
538 pad += chunk_tokens;
539 chunks += 1;
540 }
541 let t_pad = t_lat + pad;
542 if pad > 0 {
543 let mut grown = vec![0f32; zc * t_pad * zh * zw];
544 for c in 0..zc {
545 for ti in 0..t_pad {
546 let src = ti.min(t_lat - 1);
547 let a = (c * t_lat + src) * zh * zw;
548 let b = (c * t_pad + ti) * zh * zw;
549 grown[b..b + zh * zw].copy_from_slice(&zz[a..a + zh * zw]);
550 }
551 }
552 zz = grown;
553 }
554
555 let (h, w) = (zh * self.patch, zw * self.patch);
556 let mut out: Vec<f32> = Vec::new();
557 let mut carry: Option<(Vec<f32>, usize)> = None;
558 for i in 0..chunks {
559 let a = i * chunk_tokens;
560 let b = (a + chunk_tokens + token_overlap).min(t_pad);
561 let n = b.saturating_sub(a.min(t_pad));
562 if n == 0 {
563 continue;
564 }
565 let mut sub = vec![0f32; zc * n * zh * zw];
566 for c in 0..zc {
567 let src = (c * t_pad + a) * zh * zw;
568 let dst = c * n * zh * zw;
569 sub[dst..dst + n * zh * zw].copy_from_slice(&zz[src..src + n * zh * zw]);
570 }
571 let dec = self.decode_clip(&sub, n, zh, zw);
572 let dec_frames = n * ratio_t;
573 for j in 0..2 {
574 let fa = j * chunk_dec;
575 let fb = (fa + chunk_dec).min(dec_frames);
576 if fb <= fa + frame_pre_pad {
577 continue;
578 }
579 let mut part = frames_of(&dec, dec_frames, h, w, fa + frame_pre_pad, fb);
580 let mut pn = fb - fa - frame_pre_pad;
581 if j == 0 {
582 if let Some((tail, tn)) = carry.take() {
583 part = blend_frames(&tail, tn, &part, pn, h, w, frame_overlap);
584 pn = part.len() / (3 * h * w);
585 }
586 append_frames(&mut out, &part, pn, h, w);
587 } else {
588 carry = Some((part, pn));
589 }
590 }
591 if i + 1 == chunks {
592 if let Some((tail, tn)) = carry.take() {
593 append_frames(&mut out, &tail, tn, h, w);
594 }
595 }
596 }
597
598 let frames = out.len() / (3 * h * w);
602 let want = t_lat * ratio_t - pad_frames(t_lat, pad, chunk_tokens, ratio_t);
603 for c in 0..3 {
604 let base = c * frames * h * w;
605 for v in out[base..base + frames * h * w].iter_mut() {
606 *v = (*v * IMAGENET_STD[c] + IMAGENET_MEAN[c]).clamp(0.0, 1.0);
607 }
608 }
609 let keep = want.min(frames);
610 if keep < frames {
611 let mut trimmed = vec![0f32; 3 * keep * h * w];
612 for c in 0..3 {
613 let src = c * frames * h * w;
614 let dst = c * keep * h * w;
615 trimmed[dst..dst + keep * h * w].copy_from_slice(&out[src..src + keep * h * w]);
616 }
617 out = trimmed;
618 }
619 (out, keep)
620 }
621
622 pub fn spatial_ratio(&self) -> usize {
623 self.patch
624 }
625 pub fn temporal_ratio(&self) -> usize {
626 self.patch_t
627 }
628}
629
630fn pad_frames(t_lat: usize, pad: usize, chunk_tokens: usize, ratio_t: usize) -> usize {
632 if pad == 0 {
633 return 0;
634 }
635 let intra = CLIP_LENGTH % ratio_t;
636 if intra == 0 {
637 return pad * ratio_t;
638 }
639 (0..pad)
640 .map(|k| {
641 if (t_lat + k) % chunk_tokens == 0 {
642 intra
643 } else {
644 ratio_t
645 }
646 })
647 .sum()
648}
649
650#[allow(clippy::too_many_arguments)]
652fn crop(x: &[f32], f: usize, h: usize, w: usize, y0: usize, y1: usize, x0: usize, x1: usize) -> Vec<f32> {
653 let (nh, nw) = (y1 - y0, x1 - x0);
654 let mut out = vec![0f32; 3 * f * nh * nw];
655 for c in 0..3 {
656 for fi in 0..f {
657 for yy in 0..nh {
658 let s = ((c * f + fi) * h + y0 + yy) * w + x0;
659 let d = ((c * f + fi) * nh + yy) * nw;
660 out[d..d + nw].copy_from_slice(&x[s..s + nw]);
661 }
662 }
663 }
664 out
665}
666
667#[allow(clippy::too_many_arguments)]
671fn blend(a: &[f32], b: &[f32], f: usize, h: usize, w: usize, extent: usize, dim: usize) -> Vec<f32> {
672 let ah = a.len() / (3 * f * w);
673 let aw = a.len() / (3 * f * h);
674 let mut out = b.to_vec();
675 let e = if dim == 2 {
676 extent.min(ah).min(h)
677 } else {
678 extent.min(aw).min(w)
679 };
680 for c in 0..3 {
681 for fi in 0..f {
682 for k in 0..e {
683 let wb = k as f32 / e as f32;
684 let wa = 1.0 - wb;
685 if dim == 2 {
686 let sa = ((c * f + fi) * ah + ah - e + k) * w;
687 let sb = ((c * f + fi) * h + k) * w;
688 for x in 0..w {
689 out[sb + x] = a[sa + x] * wa + b[sb + x] * wb;
690 }
691 } else {
692 for y in 0..h {
693 let sa = ((c * f + fi) * h + y) * aw + aw - e + k;
694 let sb = ((c * f + fi) * h + y) * w + k;
695 out[sb] = a[sa] * wa + b[sb] * wb;
696 }
697 }
698 }
699 }
700 }
701 out
702}
703
704fn frames_of(x: &[f32], f: usize, h: usize, w: usize, a: usize, b: usize) -> Vec<f32> {
706 let n = b - a;
707 let mut out = vec![0f32; 3 * n * h * w];
708 for c in 0..3 {
709 let s = (c * f + a) * h * w;
710 let d = c * n * h * w;
711 out[d..d + n * h * w].copy_from_slice(&x[s..s + n * h * w]);
712 }
713 out
714}
715
716#[allow(clippy::too_many_arguments)]
718fn blend_frames(
719 tail: &[f32],
720 tn: usize,
721 part: &[f32],
722 pn: usize,
723 h: usize,
724 w: usize,
725 extent: usize,
726) -> Vec<f32> {
727 let e = extent.min(tn).min(pn);
728 let mut out = part.to_vec();
729 for c in 0..3 {
730 for k in 0..e {
731 let wb = k as f32 / e as f32;
732 let wa = 1.0 - wb;
733 let s = ((c * tn) + tn - e + k) * h * w;
734 let d = ((c * pn) + k) * h * w;
735 for i in 0..h * w {
736 out[d + i] = tail[s + i] * wa + part[d + i] * wb;
737 }
738 }
739 }
740 out
741}
742
743fn append_frames(out: &mut Vec<f32>, part: &[f32], n: usize, h: usize, w: usize) {
745 let old = out.len() / (3 * h * w);
746 let total = old + n;
747 let mut grown = vec![0f32; 3 * total * h * w];
748 for c in 0..3 {
749 if old > 0 {
750 let s = c * old * h * w;
751 let d = c * total * h * w;
752 grown[d..d + old * h * w].copy_from_slice(&out[s..s + old * h * w]);
753 }
754 let s = c * n * h * w;
755 let d = (c * total + old) * h * w;
756 grown[d..d + n * h * w].copy_from_slice(&part[s..s + n * h * w]);
757 }
758 *out = grown;
759}
760
761#[cfg(test)]
762mod tests {
763 use super::*;
764
765 #[test]
766 fn tile_schedule_matches_the_reference() {
767 let (s, l, o) = split_tiles(288, 16);
769 assert_eq!(s, vec![0, 32]);
770 assert_eq!(l, vec![256, 256]);
771 assert_eq!(o, vec![224]);
772 let (s, l, o) = split_tiles(512, 16);
774 assert_eq!(s, vec![0, 128, 256]);
775 assert_eq!(l, vec![256; 3]);
776 assert_eq!(o, vec![128, 128]);
777 assert_eq!(split_tiles(256, 16).0, vec![0]);
779 assert_eq!(split_tiles(128, 16).1, vec![128]);
780 }
781
782 #[test]
783 fn temporal_constants_come_out_as_the_reference_computes_them() {
784 let ratio_t = 4usize;
785 let chunk = CLIP_LENGTH.div_ceil(ratio_t);
786 assert_eq!(chunk, 5);
787 assert_eq!((chunk - TOKEN_DROP % chunk) % chunk, 2);
788 assert_eq!((ratio_t - CLIP_LENGTH % ratio_t) % ratio_t, 3);
789 }
790}
791
792struct EncConv {
807 w: Vec<f32>, b: Vec<f32>,
809 out_ch: usize,
810 in_ch: usize,
811 k: usize,
812 stride: usize,
813 pad: usize,
814}
815
816impl EncConv {
817 fn load(model: &Arc<CmfModel>, name: &str, stride: usize, pad: usize) -> Result<Self, String> {
818 let e = model
819 .tensor(&format!("{name}.weight"))
820 .ok_or_else(|| format!("missing {name}.weight"))?;
821 let (out_ch, in_ch, k) = (e.shape[0], e.shape[1], e.shape[2]);
823 Ok(Self {
824 w: crate::dit::cmf_f32(model, &format!("{name}.weight"))?,
825 b: crate::dit::cmf_f32(model, &format!("{name}.bias"))?,
826 out_ch,
827 in_ch,
828 k,
829 stride,
830 pad,
831 })
832 }
833
834 fn apply(&self, x: &[f32], h: usize, w: usize, pool: Option<&Pool>) -> (Vec<f32>, usize, usize) {
836 let (ph, pw) = (h + 2 * self.pad, w + 2 * self.pad);
837 let oh = (ph - self.k) / self.stride + 1;
838 let ow = (pw - self.k) / self.stride + 1;
839 let refl = |i: isize, n: usize| -> usize {
842 let n = n as isize;
843 let mut i = i;
844 while i < 0 || i >= n {
845 if i < 0 {
846 i = -i;
847 }
848 if i >= n {
849 i = 2 * (n - 1) - i;
850 }
851 }
852 i as usize
853 };
854 let mut out = vec![0f32; self.out_ch * oh * ow];
855 let ptr = SendPtr(out.as_mut_ptr());
856 let work = |lo: usize, hi: usize| {
857 for o in lo..hi {
858 let dst = unsafe { ptr.row(o * oh * ow, oh * ow) };
860 dst.fill(self.b[o]);
861 for i in 0..self.in_ch {
862 let ker = &self.w[(o * self.in_ch + i) * self.k * self.k
863 ..(o * self.in_ch + i + 1) * self.k * self.k];
864 let src = &x[i * h * w..(i + 1) * h * w];
865 for oy in 0..oh {
866 for ox in 0..ow {
867 let mut acc = 0f32;
868 for ky in 0..self.k {
869 let sy = (oy * self.stride + ky) as isize - self.pad as isize;
870 let sy = refl(sy, h);
871 for kx in 0..self.k {
872 let sx = (ox * self.stride + kx) as isize - self.pad as isize;
873 acc += ker[ky * self.k + kx] * src[sy * w + refl(sx, w)];
874 }
875 }
876 dst[oy * ow + ox] += acc;
877 }
878 }
879 }
880 }
881 };
882 match pool {
883 Some(p) => p.run_rows(self.out_ch, &work),
884 None => work(0, self.out_ch),
885 }
886 (out, oh, ow)
887 }
888}
889
890struct GroupNorm {
892 w: Vec<f32>,
893 b: Vec<f32>,
894}
895
896impl GroupNorm {
897 fn load(model: &Arc<CmfModel>, name: &str) -> Result<Self, String> {
898 Ok(Self {
899 w: crate::dit::cmf_f32(model, &format!("{name}.weight"))?,
900 b: crate::dit::cmf_f32(model, &format!("{name}.bias"))?,
901 })
902 }
903
904 fn apply(&self, x: &mut [f32], ch: usize, hw: usize) {
905 let groups = 32;
906 let per = ch / groups;
907 for g in 0..groups {
908 let seg = &mut x[g * per * hw..(g + 1) * per * hw];
909 let n = seg.len() as f64;
910 let mean = seg.iter().map(|&v| v as f64).sum::<f64>() / n;
911 let var = seg.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / n;
912 let inv = 1.0 / (var + 1e-6).sqrt();
913 for (i, v) in seg.iter_mut().enumerate() {
914 let c = g * per + i / hw;
915 *v = ((*v as f64 - mean) * inv) as f32 * self.w[c] + self.b[c];
916 }
917 }
918 }
919}
920
921struct ResBlock {
922 norm1: GroupNorm,
923 norm2: GroupNorm,
924 conv1: EncConv,
925 conv2: EncConv,
926 shortcut: Option<EncConv>,
927}
928
929pub struct VideoVaeEncoder {
932 conv_in: EncConv,
933 levels: Vec<(Vec<ResBlock>, Option<EncConv>)>,
934 norm_out: GroupNorm,
935 conv_out: EncConv,
936 quant: Vec<f32>, quant_b: Vec<f32>,
938 latents_mean: Vec<f32>,
939 latents_std: Vec<f32>,
940 pool: Option<Arc<Pool>>,
941 z_channels: usize,
942 ratio: usize,
943}
944
945impl VideoVaeEncoder {
946 pub fn from_cmf(model: &Arc<CmfModel>) -> Result<Self, String> {
947 let cfg: serde_json::Value = serde_json::from_slice(
948 model
949 .tensor_bytes("vvae.config_json")
950 .map_err(|e| e.to_string())?,
951 )
952 .map_err(|e| format!("vvae.config_json: {e}"))?;
953 let space_down: Vec<usize> = cfg["space_down"]
954 .as_array()
955 .map(|a| a.iter().map(|v| v.as_u64().unwrap_or(1) as usize).collect())
956 .unwrap_or_else(|| vec![2, 2, 2, 2, 1, 1]);
957 let n_res = cfg["num_res_blocks"].as_u64().unwrap_or(2) as usize;
958 let mut levels = Vec::new();
959 for (i, &sd) in space_down.iter().enumerate() {
960 let mut blocks = Vec::new();
961 for j in 0..n_res {
962 let p = format!("vvae.enc.down.{i}.block.{j}");
963 let shortcut = model
964 .tensor(&format!("{p}.nin_shortcut.weight"))
965 .map(|_| EncConv::load(model, &format!("{p}.nin_shortcut"), 1, 0))
966 .transpose()?;
967 blocks.push(ResBlock {
968 norm1: GroupNorm::load(model, &format!("{p}.norm1"))?,
969 norm2: GroupNorm::load(model, &format!("{p}.norm2"))?,
970 conv1: EncConv::load(model, &format!("{p}.conv1"), 1, 1)?,
971 conv2: EncConv::load(model, &format!("{p}.conv2"), 1, 1)?,
972 shortcut,
973 });
974 }
975 let down = if sd > 1 {
978 Some(EncConv::load(
979 model,
980 &format!("vvae.enc.down.{i}.downsample.conv"),
981 sd,
982 0,
983 )?)
984 } else {
985 None
986 };
987 levels.push((blocks, down));
988 }
989 Ok(Self {
990 conv_in: EncConv::load(model, "vvae.enc.conv_in", 1, 1)?,
991 levels,
992 norm_out: GroupNorm::load(model, "vvae.enc.norm_out")?,
993 conv_out: EncConv::load(model, "vvae.enc.conv_out", 1, 1)?,
994 quant: crate::dit::cmf_f32(model, "vvae.quant_conv.weight")?,
995 quant_b: crate::dit::cmf_f32(model, "vvae.quant_conv.bias")?,
996 latents_mean: crate::dit::cmf_f32(model, "vvae.latents_mean")?,
997 latents_std: crate::dit::cmf_f32(model, "vvae.latents_std")?,
998 pool: Pool::from_env(),
999 z_channels: cfg["z_channels"].as_u64().unwrap_or(24) as usize,
1000 ratio: cfg["patch_size"].as_u64().unwrap_or(16) as usize,
1001 })
1002 }
1003
1004 fn encode_tile(&self, x: &[f32], h: usize, w: usize) -> (Vec<f32>, usize, usize) {
1006 let pool = self.pool.as_deref();
1007 let (mut cur, mut ch, mut cw) = self.conv_in.apply(x, h, w, pool);
1008 let mut c = self.conv_in.out_ch;
1009 for (blocks, down) in &self.levels {
1010 for b in blocks {
1011 let mut hh = cur.clone();
1012 self.norm_out_like(&b.norm1, &mut hh, c, ch * cw);
1013 for v in hh.iter_mut() {
1014 *v = silu(*v);
1015 }
1016 let (mut t, th, tw) = b.conv1.apply(&hh, ch, cw, pool);
1017 let oc = b.conv1.out_ch;
1018 self.norm_out_like(&b.norm2, &mut t, oc, th * tw);
1019 for v in t.iter_mut() {
1020 *v = silu(*v);
1021 }
1022 let (t2, th2, tw2) = b.conv2.apply(&t, th, tw, pool);
1023 let skip = match &b.shortcut {
1024 Some(s) => s.apply(&cur, ch, cw, pool).0,
1025 None => cur.clone(),
1026 };
1027 cur = t2.iter().zip(&skip).map(|(&a, &b)| a + b).collect();
1028 (ch, cw, c) = (th2, tw2, oc);
1029 }
1030 if let Some(d) = down {
1031 let (padded, ph, pw) = reflect_pad_far(&cur, c, ch, cw);
1034 let (t, th, tw) = d.apply(&padded, ph, pw, pool);
1035 cur = t;
1036 (ch, cw, c) = (th, tw, d.out_ch);
1037 }
1038 }
1039 self.norm_out_like(&self.norm_out, &mut cur, c, ch * cw);
1040 for v in cur.iter_mut() {
1041 *v = silu(*v);
1042 }
1043 let (moments, mh, mw) = self.conv_out.apply(&cur, ch, cw, pool);
1044 let n = 2 * self.z_channels;
1046 let mut out = vec![0f32; n * mh * mw];
1047 for o in 0..n {
1048 for p in 0..mh * mw {
1049 let mut acc = self.quant_b[o];
1050 for i in 0..n {
1051 acc += self.quant[o * n + i] * moments[i * mh * mw + p];
1052 }
1053 out[o * mh * mw + p] = acc;
1054 }
1055 }
1056 (out, mh, mw)
1057 }
1058
1059 fn norm_out_like(&self, g: &GroupNorm, x: &mut [f32], ch: usize, hw: usize) {
1060 g.apply(x, ch, hw);
1061 }
1062
1063 pub fn encode_frame(&self, rgb: &[f32], h: usize, w: usize) -> (Vec<f32>, usize, usize) {
1066 let mut x = vec![0f32; 3 * h * w];
1068 for c in 0..3 {
1069 for p in 0..h * w {
1070 let v = (rgb[c * h * w + p] + 1.0) * 0.5;
1071 x[c * h * w + p] = (v - IMAGENET_MEAN[c]) / IMAGENET_STD[c];
1072 }
1073 }
1074 let r = self.ratio;
1075 let (ys, yl, yo) = split_tiles(h, r);
1076 let (xs, xl, xo) = split_tiles(w, r);
1077 let (zh, zw) = (h / r, w / r);
1078 let zc = 2 * self.z_channels;
1079 let mut rows: Vec<Vec<Vec<f32>>> = Vec::new();
1080 let mut dims: Vec<Vec<(usize, usize)>> = Vec::new();
1081 for (&ip, &il) in ys.iter().zip(&yl) {
1082 let mut row = Vec::new();
1083 let mut rd = Vec::new();
1084 for (&jp, &jl) in xs.iter().zip(&xl) {
1085 let mut sub = vec![0f32; 3 * il * jl];
1086 for c in 0..3 {
1087 for yy in 0..il {
1088 let s = (c * h + ip + yy) * w + jp;
1089 let d = (c * il + yy) * jl;
1090 sub[d..d + jl].copy_from_slice(&x[s..s + jl]);
1091 }
1092 }
1093 let (t, th, tw) = self.encode_tile(&sub, il, jl);
1094 row.push(t);
1095 rd.push((th, tw));
1096 }
1097 rows.push(row);
1098 dims.push(rd);
1099 }
1100 let mut canvas = vec![0f32; zc * zh * zw];
1102 let mut out_y = 0usize;
1103 for i in 0..rows.len() {
1104 let mut out_x = 0usize;
1105 let mut row_h = 0usize;
1106 for j in 0..rows[i].len() {
1107 let (th, tw) = dims[i][j];
1108 let mut tile = rows[i][j].clone();
1109 let (mut ch_, mut cw_) = (th, tw);
1110 if i > 0 {
1111 tile = blend_plane(&rows[i - 1][j], &tile, zc, dims[i - 1][j], (ch_, cw_), yo[i - 1] / r, 0);
1112 }
1113 if j > 0 {
1114 tile = blend_plane(&rows[i][j - 1], &tile, zc, dims[i][j - 1], (ch_, cw_), xo[j - 1] / r, 1);
1115 }
1116 if i + 1 < rows.len() {
1117 tile = crop_plane(&tile, zc, ch_, cw_, 0, ch_ - yo[i] / r, 0, cw_);
1118 ch_ -= yo[i] / r;
1119 }
1120 if j + 1 < rows[i].len() {
1121 tile = crop_plane(&tile, zc, ch_, cw_, 0, ch_, 0, cw_ - xo[j] / r);
1122 cw_ -= xo[j] / r;
1123 }
1124 for c in 0..zc {
1125 for yy in 0..ch_ {
1126 let d = (c * zh + out_y + yy) * zw + out_x;
1127 let s = (c * ch_ + yy) * cw_;
1128 canvas[d..d + cw_].copy_from_slice(&tile[s..s + cw_]);
1129 }
1130 }
1131 out_x += cw_;
1132 row_h = ch_;
1133 }
1134 out_y += row_h;
1135 }
1136 let mut z = vec![0f32; self.z_channels * zh * zw];
1138 for c in 0..self.z_channels {
1139 let (m, s) = (self.latents_mean[c], self.latents_std[c]);
1140 for p in 0..zh * zw {
1141 z[c * zh * zw + p] = (canvas[c * zh * zw + p] - m) / s;
1142 }
1143 }
1144 (z, zh, zw)
1145 }
1146}
1147
1148fn reflect_pad_far(x: &[f32], c: usize, h: usize, w: usize) -> (Vec<f32>, usize, usize) {
1150 let (ph, pw) = (h + 1, w + 1);
1151 let mut out = vec![0f32; c * ph * pw];
1152 for ci in 0..c {
1153 for y in 0..ph {
1154 let sy = if y < h { y } else { h - 2 };
1155 for x2 in 0..pw {
1156 let sx = if x2 < w { x2 } else { w - 2 };
1157 out[(ci * ph + y) * pw + x2] = x[(ci * h + sy) * w + sx];
1158 }
1159 }
1160 }
1161 (out, ph, pw)
1162}
1163
1164fn blend_plane(
1166 a: &[f32],
1167 b: &[f32],
1168 c: usize,
1169 ad: (usize, usize),
1170 bd: (usize, usize),
1171 extent: usize,
1172 dim: usize,
1173) -> Vec<f32> {
1174 let (ah, aw) = ad;
1175 let (bh, bw) = bd;
1176 let mut out = b.to_vec();
1177 let e = if dim == 0 { extent.min(ah).min(bh) } else { extent.min(aw).min(bw) };
1178 if e == 0 {
1179 return out;
1180 }
1181 for ci in 0..c {
1182 for k in 0..e {
1183 let wb = k as f32 / e as f32;
1184 let wa = 1.0 - wb;
1185 if dim == 0 {
1186 for x in 0..bw.min(aw) {
1187 let sa = (ci * ah + ah - e + k) * aw + x;
1188 let sb = (ci * bh + k) * bw + x;
1189 out[sb] = a[sa] * wa + b[sb] * wb;
1190 }
1191 } else {
1192 for y in 0..bh.min(ah) {
1193 let sa = (ci * ah + y) * aw + aw - e + k;
1194 let sb = (ci * bh + y) * bw + k;
1195 out[sb] = a[sa] * wa + b[sb] * wb;
1196 }
1197 }
1198 }
1199 }
1200 out
1201}
1202
1203#[allow(clippy::too_many_arguments)]
1205fn crop_plane(x: &[f32], c: usize, h: usize, w: usize, y0: usize, y1: usize, x0: usize, x1: usize) -> Vec<f32> {
1206 let (nh, nw) = (y1 - y0, x1 - x0);
1207 let mut out = vec![0f32; c * nh * nw];
1208 for ci in 0..c {
1209 for y in 0..nh {
1210 let s = (ci * h + y0 + y) * w + x0;
1211 let d = (ci * nh + y) * nw;
1212 out[d..d + nw].copy_from_slice(&x[s..s + nw]);
1213 }
1214 }
1215 out
1216}