1use cortiq_core::CmfModel;
14use std::cell::Cell;
15use std::sync::atomic::{AtomicBool, AtomicU8, AtomicU32, AtomicU64, Ordering};
16use std::sync::{Arc, OnceLock};
17
18pub struct EmbryoGraphModel {
27 pub id: u64,
28 pub hidden: usize,
29 pub intermediate: usize,
30 pub vocab: usize,
31 pub layers: usize,
32 pub phase_heads: usize,
33 pub nphase: usize,
34 pub phase_dv: usize,
35 pub anchor_q_heads: usize,
36 pub anchor_kv_heads: usize,
37 pub anchor_head_dim: usize,
38 pub rotary_dim: usize,
39 pub rope_freq: Vec<f32>,
42 pub max_seq: usize,
43 pub cluster_count: usize,
44 pub cluster_size: usize,
45 pub phase_state_len: usize,
46 pub state_stride: usize,
47 pub kv_stride: usize,
48 pub norm_gemma: bool,
49 pub phase_mass: f32,
50 pub weights: Vec<f32>,
51 pub meta: Vec<u32>,
52 pub lm_head: Vec<f32>,
53 pub wide: bool,
56 pub wide_q8: bool,
57 pub mats: Vec<u8>,
58 pub lm_head_packed: Vec<u8>,
59 pub clusters: Vec<f32>,
60 pub final_norm: Vec<f32>,
61 pub inv_freq: Vec<f32>,
62 pub bounded: bool,
67 pub kv_layers: usize,
71 pub state_layers: usize,
74 pub anchor_window: usize,
75 pub anchor_sink: usize,
76 pub phase_layers: usize,
78 pub gdn_layers: usize,
82 pub gdn_heads: usize,
83 pub gdn_k_heads: usize,
84 pub gdn_dk: usize,
85 pub gdn_dv: usize,
86 pub gdn_kk: usize,
87}
88
89impl EmbryoGraphModel {
90 pub fn gdn_c_dim(&self) -> usize {
92 2 * self.gdn_k_heads * self.gdn_dk + self.gdn_heads * self.gdn_dv
93 }
94}
95
96pub const EMBRYO_META_HEADER: usize = 32;
99
100pub const EMBRYO_CHUNK_MAX: usize = 64;
104
105pub fn forward_embryo_graph_chunk(
110 model: &Arc<EmbryoGraphModel>,
111 kv_id: u64,
112 rows: &[f32],
113 position: usize,
114 n: usize,
115 logits: &mut Vec<f32>,
116) -> bool {
117 #[cfg(feature = "gpu")]
118 if backend() == Backend::Wgpu {
119 return crate::gpu_wgpu::forward_embryo_graph_chunk(model, kv_id, rows, position, n, logits);
120 }
121 let _ = (model, kv_id, rows, position, n, logits);
122 false
123}
124
125pub fn embryo_device_state_bytes(kv_id: u64) -> Option<(u64, u64)> {
131 #[cfg(feature = "gpu")]
132 if backend() == Backend::Wgpu {
133 return crate::gpu_wgpu::embryo_device_state_bytes(kv_id);
134 }
135 let _ = kv_id;
136 None
137}
138
139pub fn counts_as_placement_weight(model: &CmfModel, name: &str) -> bool {
146 model.header.genome.is_none() || cortiq_core::knowledge::is_trunk_tensor(name)
147}
148
149pub fn placement_weight_bytes(model: &CmfModel) -> u64 {
153 if model.header.genome.is_none() {
154 return model.primary_bytes().len() as u64;
155 }
156 model
157 .tensors
158 .iter()
159 .filter(|t| counts_as_placement_weight(model, &t.name))
160 .map(|t| t.nbytes)
161 .sum()
162}
163
164pub fn embryo_device_next_position(kv_id: u64) -> Option<usize> {
168 #[cfg(feature = "gpu")]
169 if backend() == Backend::Wgpu {
170 return crate::gpu_wgpu::embryo_device_next_position(kv_id);
171 }
172 let _ = kv_id;
173 None
174}
175
176thread_local! {
177 static CUR_LAYER: Cell<i64> = const { Cell::new(-1) };
181 static CPU_ONLY: Cell<bool> = const { Cell::new(false) };
186 static PROBE_COLD: Cell<bool> = const { Cell::new(false) };
190}
191
192pub struct CpuScopeGuard(bool);
198
199impl Drop for CpuScopeGuard {
200 fn drop(&mut self) {
201 CPU_ONLY.with(|c| c.set(self.0));
202 }
203}
204
205pub fn enter_cpu_scope() -> CpuScopeGuard {
206 let previous = CPU_ONLY.with(|c| c.replace(true));
207 CpuScopeGuard(previous)
208}
209
210pub fn gpu_scope<R>(f: impl FnOnce() -> R) -> R {
216 struct Restore(bool);
217 impl Drop for Restore {
218 fn drop(&mut self) {
219 CPU_ONLY.with(|c| c.set(self.0));
220 }
221 }
222 let prev = CPU_ONLY.with(|c| c.replace(false));
223 let restore = Restore(prev);
224 let out = f();
225 drop(restore);
226 out
227}
228
229pub fn cpu_scope<R>(f: impl FnOnce() -> R) -> R {
231 let _restore = enter_cpu_scope();
232 f()
233}
234
235pub(crate) fn inherit_cpu_scope() -> impl Fn() -> Option<CpuScopeGuard> + Copy {
240 let on = CPU_ONLY.get();
241 move || on.then(enter_cpu_scope)
242}
243
244pub fn probe_set_device(label: &str) {
249 let _ = DEVICE_LABEL.set(label.to_string());
250}
251
252fn device_label() -> &'static str {
253 DEVICE_LABEL.get().map(String::as_str).unwrap_or("unknown")
254}
255
256static DEVICE_LABEL: std::sync::OnceLock<String> = std::sync::OnceLock::new();
257
258static CACHE_DIR: std::sync::OnceLock<std::path::PathBuf> = std::sync::OnceLock::new();
267
268pub fn set_cache_dir(dir: std::path::PathBuf) {
270 let _ = CACHE_DIR.set(dir);
271}
272
273pub fn cache_dir_pub() -> std::path::PathBuf {
275 cache_dir()
276}
277
278fn cache_dir() -> std::path::PathBuf {
279 if let Some(d) = CACHE_DIR.get() {
280 return d.clone();
281 }
282 match std::env::var_os("TMPDIR") {
283 Some(t) => std::path::PathBuf::from(t),
284 None => std::env::temp_dir(),
285 }
286}
287
288fn probe_cache_path() -> Option<std::path::PathBuf> {
291 match std::env::var("CMF_PROBE_CACHE") {
292 Ok(v) if v == "0" => None,
293 Ok(v) => Some(std::path::PathBuf::from(v)),
294 Err(_) => Some(cache_dir().join("cortiq-gpu-probe.tsv")),
295 }
296}
297
298fn probe_cache_key_named(class: &str) -> String {
302 format!(
303 "{}\t{}\t{}",
304 env!("CARGO_PKG_VERSION"),
305 device_label(),
306 class
307 )
308}
309
310const CLASS_NAMES: [&str; 8] = [
311 "ffn",
312 "matvec",
313 "matmat",
314 "qkv-batch",
315 "matmat-wide",
316 "lm-head",
317 "gemm-nt",
318 "moe",
323];
324
325fn probe_cache_load() {
334 static ONCE: std::sync::Once = std::sync::Once::new();
335 ONCE.call_once(|| {
336 let Some(path) = probe_cache_path() else {
337 return;
338 };
339 if cfg!(test) && std::env::var("CMF_PROBE_CACHE").is_err() {
344 return;
345 }
346 let Ok(text) = std::fs::read_to_string(&path) else {
347 return;
348 };
349 probe_cache_adopt(&text);
350 });
351}
352
353fn probe_cache_adopt(text: &str) {
357 for line in text.lines() {
358 let Some((key, verdict)) = line.rsplit_once('\t') else {
359 continue;
360 };
361 let winner = match verdict.trim() {
362 "gpu" => 1u8,
363 "cpu" => 2u8,
364 _ => continue,
365 };
366 for (i, name) in CLASS_NAMES.iter().enumerate() {
367 if probe_cache_key_named(name) == key {
368 let _ = PROBES[i].state.compare_exchange(
369 0,
370 winner,
371 Ordering::Relaxed,
372 Ordering::Relaxed,
373 );
374 tracing::debug!("gpu probe [{name}]: remembered → {verdict}");
375 }
376 }
377 }
378}
379
380fn probe_cache_store(c: OpClass, winner: u8) {
383 let Some(path) = probe_cache_path() else {
384 return;
385 };
386 let line = format!(
387 "{}\t{}\n",
388 probe_cache_key_named(CLASS_NAMES[c as usize]),
389 if winner == 1 { "gpu" } else { "cpu" }
390 );
391 use std::io::Write;
392 if let Ok(mut f) = std::fs::OpenOptions::new()
393 .create(true)
394 .append(true)
395 .open(&path)
396 {
397 let _ = f.write_all(line.as_bytes());
398 }
399}
400
401pub fn cold_epoch() -> u64 {
407 COLD_EPOCH.load(std::sync::atomic::Ordering::Relaxed)
408}
409static COLD_EPOCH: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
410
411pub(crate) fn probe_note_cold() {
412 COLD_EPOCH.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
413 PROBE_COLD.with(|c| c.set(true));
414}
415
416pub(crate) fn probe_was_cold() -> bool {
420 PROBE_COLD.with(|c| c.get())
421}
422
423pub fn set_layer(l: i64) {
425 CUR_LAYER.with(|c| c.set(l));
426}
427
428pub fn cur_layer() -> i64 {
430 CUR_LAYER.with(|c| c.get())
431}
432
433pub fn automatic_layer_prefix(
436 model: &Arc<CmfModel>,
437 num_layers: usize,
438 physical_layers: usize,
439) -> Option<usize> {
440 match backend() {
441 #[cfg(feature = "gpu")]
442 Backend::Wgpu => {
443 crate::gpu_wgpu::automatic_layer_prefix(model, num_layers, physical_layers)
444 }
445 _ => None,
446 }
447}
448
449fn layer_ranges() -> &'static Option<Vec<(i64, i64)>> {
452 static R: OnceLock<Option<Vec<(i64, i64)>>> = OnceLock::new();
453 R.get_or_init(|| {
454 let s = std::env::var("CMF_GPU_LAYERS").ok()?;
455 let mut v = Vec::new();
456 for part in s.split(',') {
457 let part = part.trim();
458 match part.split_once('-') {
459 Some((a, b)) => v.push((a.trim().parse().ok()?, b.trim().parse().ok()?)),
460 None => {
461 let x: i64 = part.parse().ok()?;
462 v.push((x, x));
463 }
464 }
465 }
466 Some(v)
467 })
468}
469
470fn layer_allowed() -> bool {
471 match layer_ranges() {
472 None => true,
473 Some(ranges) => {
474 let cur = CUR_LAYER.with(|c| c.get());
475 cur < 0 || ranges.iter().any(|(a, b)| cur >= *a && cur <= *b)
476 }
477 }
478}
479
480pub fn enabled_here() -> bool {
484 !CPU_ONLY.with(|c| c.get()) && enabled() && layer_allowed()
485}
486
487pub fn q2tp_gpu_opt_in() -> bool {
493 std::env::var("CMF_Q2TP_GPU").as_deref() == Ok("1")
494}
495
496#[derive(Clone, Copy)]
508pub enum OpClass {
509 Ffn = 0,
511 Matvec = 1,
513 Matmat = 2,
515 Batch = 3,
517 MatmatWide = 4,
523 MatvecHead = 5,
530 GemmNt = 6,
537 Moe = 7,
540}
541
542pub fn matvec_class(rows: usize, cols: usize) -> OpClass {
546 if rows * cols >= 67_108_864 {
547 OpClass::MatvecHead
548 } else {
549 OpClass::Matvec
550 }
551}
552
553pub enum ProbeArm {
555 Gpu,
557 CpuTimed,
559 Cpu,
561}
562
563const PROBE_SAMPLES: u32 = 6;
565
566const PROBE_DECLINE_LIMIT: u32 = 16;
570
571const PROBE_WARMUP: u32 = 1;
573
574struct Probe {
575 state: AtomicU8,
577 flip: AtomicU32,
578 gpu_ns: AtomicU64,
579 gpu_n: AtomicU32,
580 declines: AtomicU32,
590 gpu_burn: AtomicU32,
603 cpu_ns: AtomicU64,
604 cpu_n: AtomicU32,
605 gpu_min: AtomicU64,
612 cpu_min: AtomicU64,
613}
614
615impl Probe {
616 const fn new() -> Self {
617 Self {
618 state: AtomicU8::new(0),
619 flip: AtomicU32::new(0),
620 gpu_ns: AtomicU64::new(0),
621 gpu_n: AtomicU32::new(0),
622 declines: AtomicU32::new(0),
623 gpu_burn: AtomicU32::new(PROBE_WARMUP),
624 cpu_ns: AtomicU64::new(0),
625 cpu_n: AtomicU32::new(0),
626 gpu_min: AtomicU64::new(u64::MAX),
627 cpu_min: AtomicU64::new(u64::MAX),
628 }
629 }
630}
631
632static PROBES: [Probe; 8] = [
633 Probe::new(),
634 Probe::new(),
635 Probe::new(),
636 Probe::new(),
637 Probe::new(),
638 Probe::new(),
639 Probe::new(),
640 Probe::new(),
641];
642
643static TRUST_GPU: AtomicBool = AtomicBool::new(false);
650
651pub fn trust_gpu() -> GpuTrust {
653 let was = TRUST_GPU.swap(true, Ordering::Relaxed);
654 GpuTrust(was)
655}
656
657pub struct GpuTrust(bool);
658
659impl Drop for GpuTrust {
660 fn drop(&mut self) {
661 TRUST_GPU.store(self.0, Ordering::Relaxed);
662 }
663}
664
665fn probe_on_for(c: OpClass) -> bool {
666 if TRUST_GPU.load(Ordering::Relaxed) && matches!(c, OpClass::MatmatWide | OpClass::Ffn) {
672 return false;
673 }
674 probe_on()
675}
676
677pub fn probe_enabled() -> bool {
681 probe_on()
682}
683
684fn probe_on() -> bool {
685 static ON: OnceLock<bool> = OnceLock::new();
686 *ON.get_or_init(|| {
687 std::env::var("CMF_GPU_PROBE")
688 .map(|v| v != "0" && v != "off")
689 .unwrap_or(true)
690 })
691}
692
693pub fn q1_force() -> bool {
698 #[cfg(target_os = "macos")]
699 {
700 backend() == Backend::Metal
701 }
702 #[cfg(not(target_os = "macos"))]
703 {
704 false
705 }
706}
707
708pub fn fused_block_trusted() -> bool {
727 #[cfg(target_os = "macos")]
728 if backend() == Backend::Metal {
729 return true;
730 }
731 wgpu_graph_default()
732}
733
734pub fn weight_is_resident(model: &Arc<CmfModel>, idx: usize) -> bool {
746 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
747 {
748 return crate::gpu_wgpu::weight_is_resident(model, idx);
749 }
750 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
751 {
752 let _ = (model, idx);
753 true
754 }
755}
756
757pub fn probe_arm_cold_prefers_gpu(c: OpClass, weights_resident: bool) -> ProbeArm {
758 if !weights_resident && probe_deciding(c) {
759 return ProbeArm::Gpu;
760 }
761 probe_arm(c)
762}
763
764pub fn probe_arm(c: OpClass) -> ProbeArm {
765 PROBE_COLD.with(|f| f.set(false));
770 if !probe_on_for(c) {
771 return ProbeArm::Gpu;
772 }
773 probe_cache_load();
774 let p = &PROBES[c as usize];
775 match p.state.load(Ordering::Relaxed) {
776 1 => ProbeArm::Gpu,
777 2 => ProbeArm::Cpu,
778 _ => {
779 if p.flip.fetch_add(1, Ordering::Relaxed) % 2 == 0 {
780 ProbeArm::Gpu
781 } else {
782 ProbeArm::CpuTimed
783 }
784 }
785 }
786}
787
788pub fn probe_note_decline(c: OpClass) {
792 let p = &PROBES[c as usize];
793 if p.state.load(Ordering::Relaxed) != 0 {
794 return;
795 }
796 let n = p.declines.fetch_add(1, Ordering::Relaxed) + 1;
797 if n >= PROBE_DECLINE_LIMIT
798 && p.state
799 .compare_exchange(0, 2, Ordering::Relaxed, Ordering::Relaxed)
800 .is_ok()
801 {
802 tracing::info!(
803 "gpu probe [{}]: device declined {n} times → cpu",
804 CLASS_NAMES[c as usize]
805 );
806 }
807}
808
809pub fn probe_record(c: OpClass, gpu: bool, dur: std::time::Duration) {
812 probe_record_into(
813 &PROBES[c as usize],
814 CLASS_NAMES[c as usize],
815 Some(c),
816 gpu,
817 dur,
818 )
819}
820
821fn probe_record_into(
824 p: &Probe,
825 class_name: &str,
826 cache: Option<OpClass>,
827 gpu: bool,
828 dur: std::time::Duration,
829) {
830 if p.state.load(Ordering::Relaxed) != 0 {
831 return;
832 }
833 if gpu && PROBE_COLD.with(|f| f.replace(false)) {
834 return; }
836 if gpu {
837 let left = p.gpu_burn.load(Ordering::Relaxed);
841 if left > 0 {
842 p.gpu_burn.store(left - 1, Ordering::Relaxed);
843 return; }
845 }
846 let ns = dur.as_nanos().min(u64::MAX as u128) as u64;
847 if gpu {
848 p.gpu_ns.fetch_add(ns, Ordering::Relaxed);
849 p.gpu_n.fetch_add(1, Ordering::Relaxed);
850 p.gpu_min.fetch_min(ns, Ordering::Relaxed);
851 } else {
852 p.cpu_ns.fetch_add(ns, Ordering::Relaxed);
853 p.cpu_n.fetch_add(1, Ordering::Relaxed);
854 p.cpu_min.fetch_min(ns, Ordering::Relaxed);
855 }
856 let (gn, cn) = (
857 p.gpu_n.load(Ordering::Relaxed),
858 p.cpu_n.load(Ordering::Relaxed),
859 );
860 if gn >= 2 && cn >= 2 {
861 let g = p.gpu_min.load(Ordering::Relaxed) as f64;
865 let cp = p.cpu_min.load(Ordering::Relaxed) as f64;
866 if (gn < PROBE_SAMPLES || cn < PROBE_SAMPLES) && g < cp * 2.0 && cp < g * 2.0 {
876 return;
877 }
878 let winner = if g <= cp { 1 } else { 2 };
879 if p.state
880 .compare_exchange(0, winner, Ordering::Relaxed, Ordering::Relaxed)
881 .is_ok()
882 {
883 tracing::info!(
884 "gpu probe [{}]: gpu {:.2} ms vs cpu {:.2} ms per op → {}",
885 class_name,
886 g / 1e6,
887 cp / 1e6,
888 if winner == 1 { "gpu" } else { "cpu" },
889 );
890 if let Some(c) = cache {
891 probe_cache_store(c, winner);
892 }
893 }
894 }
895}
896
897pub fn probe_deciding(c: OpClass) -> bool {
900 probe_on_for(c) && PROBES[c as usize].state.load(Ordering::Relaxed) == 0
901}
902
903#[allow(unused_variables)]
913pub fn q8_resident_or_upload(model: &Arc<CmfModel>, idx: usize) -> bool {
914 static PROBE_UPLOADS: AtomicU32 = AtomicU32::new(0);
915 let may_upload = PROBE_UPLOADS.load(Ordering::Relaxed) < 4;
916 let resident = match backend() {
917 #[cfg(target_os = "macos")]
918 Backend::Metal => crate::gpu_metal::q8_resident_or_upload(model, idx, may_upload),
919 #[cfg(feature = "gpu")]
920 Backend::Wgpu => crate::gpu_wgpu::q8_resident_or_upload(model, idx, may_upload),
921 Backend::None => false,
922 #[cfg(feature = "cuda")]
923 Backend::Cuda => false,
924 };
925 if !resident && may_upload {
926 PROBE_UPLOADS.fetch_add(1, Ordering::Relaxed);
927 }
928 resident
929}
930
931#[cfg(test)]
933pub(crate) fn probe_reset() {
934 for p in &PROBES {
935 p.state.store(0, Ordering::Relaxed);
936 p.flip.store(0, Ordering::Relaxed);
937 p.gpu_ns.store(0, Ordering::Relaxed);
938 p.gpu_n.store(0, Ordering::Relaxed);
939 p.cpu_ns.store(0, Ordering::Relaxed);
940 p.cpu_n.store(0, Ordering::Relaxed);
941 }
942}
943
944#[cfg(test)]
948static PROBE_TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
949
950#[cfg(test)]
951fn probe_test_guard() -> std::sync::MutexGuard<'static, ()> {
952 PROBE_TEST_LOCK
953 .lock()
954 .unwrap_or_else(std::sync::PoisonError::into_inner)
955}
956
957#[cfg(test)]
958mod probe_tests {
959 use super::*;
960 use std::time::Duration;
961
962 #[test]
963 fn cpu_only_whole_operator_dispatch_inherits_and_restores_scope() {
964 let pool = crate::pool::Pool::with_spin(3, 0);
965 cpu_scope(|| {
966 let inherit = inherit_cpu_scope();
967 pool.run_rows(64, &|_, _| {
968 let _guard = inherit();
969 assert!(CPU_ONLY.get());
970 });
971 });
972 pool.run_rows(64, &|_, _| assert!(!CPU_ONLY.get()));
973 }
974
975 #[test]
978 fn probe_alternates_discards_cold_and_decides() {
979 let _probe_guard = probe_test_guard();
980 probe_reset();
981 assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::Gpu));
983 assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::CpuTimed));
984
985 probe_note_cold();
989 probe_record(OpClass::Ffn, true, Duration::from_secs(1000));
990 for _ in 0..PROBE_SAMPLES {
991 probe_record(OpClass::Ffn, true, Duration::from_millis(1));
992 probe_record(OpClass::Ffn, false, Duration::from_millis(4));
993 }
994 assert!(matches!(probe_arm(OpClass::Ffn), ProbeArm::Gpu));
995
996 for _ in 0..PROBE_SAMPLES {
998 probe_record(OpClass::Matmat, true, Duration::from_millis(4));
999 probe_record(OpClass::Matmat, false, Duration::from_millis(1));
1000 }
1001 assert!(matches!(probe_arm(OpClass::Matmat), ProbeArm::Cpu));
1002
1003 cpu_scope(|| CPU_ONLY.with(|c| assert!(c.get())));
1005 CPU_ONLY.with(|c| assert!(!c.get()));
1006 cpu_scope(|| {
1007 cpu_scope(|| CPU_ONLY.with(|c| assert!(c.get())));
1008 CPU_ONLY.with(|c| assert!(c.get()));
1009 });
1010 let _ = std::panic::catch_unwind(|| cpu_scope(|| panic!("scope test")));
1011 CPU_ONLY.with(|c| assert!(!c.get()));
1012 probe_reset();
1013 }
1014
1015 #[test]
1016 fn a_remembered_verdict_is_adopted_and_a_stranger_is_not() {
1017 let _probe_guard = probe_test_guard();
1018 let mine = probe_cache_key_named("gemm-nt");
1030 let state = || {
1031 PROBES[OpClass::GemmNt as usize]
1032 .state
1033 .load(Ordering::Relaxed)
1034 };
1035
1036 probe_cache_adopt("SomeOtherGPU/Vulkan\tgemm-nt\tgpu\n");
1038 assert_eq!(state(), 0);
1039 let older = mine.replacen(env!("CARGO_PKG_VERSION"), "0.0.0-old", 1);
1041 assert_ne!(older, mine);
1042 probe_cache_adopt(&format!("{older}\tgpu\n"));
1043 assert_eq!(state(), 0);
1044 probe_cache_adopt(&format!("{mine}\tcpu\n"));
1046 assert_eq!(state(), 2);
1047
1048 PROBES[OpClass::GemmNt as usize]
1049 .state
1050 .store(0, Ordering::Relaxed);
1051 }
1052}
1053
1054pub const GPU_MIN_ROWS: usize = 65_536;
1057
1058pub fn min_rows() -> usize {
1065 if let Some(v) = std::env::var("CMF_GPU_MIN_ROWS")
1066 .ok()
1067 .and_then(|v| v.parse().ok())
1068 {
1069 return v;
1070 }
1071 if discrete() { 4096 } else { GPU_MIN_ROWS }
1072}
1073
1074pub fn discrete() -> bool {
1076 match backend() {
1077 #[cfg(feature = "gpu")]
1078 Backend::Wgpu => crate::gpu_wgpu::is_discrete(),
1079 #[cfg(target_os = "macos")]
1080 Backend::Metal => false, Backend::None => false,
1082 #[cfg(feature = "cuda")]
1083 Backend::Cuda => true,
1084 }
1085}
1086
1087pub struct MoeJob<'a> {
1091 pub gate: (usize, usize, usize, &'a [f32]),
1092 pub up: (usize, usize, usize, &'a [f32]),
1093 pub down: (usize, usize, usize, &'a [f32]),
1094 pub xs_gate: Vec<f32>,
1095 pub xs_up: Vec<f32>,
1096 pub down_col: &'a [f32],
1097 pub w: f32,
1098 pub q1: bool,
1101 pub q4t: bool,
1104 pub q4tp: bool,
1108 pub gu_q2: bool,
1112 pub swiglu_limit: f32,
1117 pub act: u32,
1123}
1124
1125pub struct BatchJob<'a> {
1127 pub idx: usize,
1128 pub rows: usize,
1129 pub cols: usize,
1130 pub row_scale: &'a [f32],
1131 pub xs: Vec<f32>,
1132 pub layout: BatchLayout,
1136}
1137
1138#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1141pub enum BatchLayout {
1142 Q8,
1143 Q1,
1144 Q1g,
1146 Q4t,
1147 Q4tp,
1148}
1149
1150#[derive(Clone, Copy, PartialEq, Eq)]
1151enum Backend {
1152 None,
1153 #[cfg(target_os = "macos")]
1154 Metal,
1155 #[cfg(feature = "gpu")]
1156 Wgpu,
1157 #[cfg(feature = "cuda")]
1159 Cuda,
1160}
1161
1162pub fn backend_name() -> String {
1164 match backend() {
1165 #[cfg(target_os = "macos")]
1166 Backend::Metal => "metal".into(),
1167 #[cfg(feature = "gpu")]
1168 Backend::Wgpu => "wgpu".into(),
1169 Backend::None => "cpu".into(),
1170 #[cfg(feature = "cuda")]
1171 Backend::Cuda => "cuda".into(),
1172 }
1173}
1174
1175fn backend() -> Backend {
1176 #[cfg(feature = "cuda")]
1179 if crate::gpu_cuda::selected() {
1180 return if crate::gpu_cuda::enabled() {
1181 Backend::Cuda
1182 } else {
1183 Backend::None
1184 };
1185 }
1186 #[cfg(not(feature = "cuda"))]
1189 {
1190 static SAID: std::sync::Once = std::sync::Once::new();
1191 SAID.call_once(|| {
1192 if std::env::var("CMF_GPU").as_deref() == Ok("cuda") {
1193 tracing::warn!(
1194 "CMF_GPU=cuda: this build has no CUDA backend (build with `--features cuda`)"
1195 );
1196 }
1197 });
1198 }
1199 #[cfg(feature = "gpu")]
1200 if crate::gpu_wgpu::selected() {
1201 return if crate::gpu_wgpu::enabled() {
1202 Backend::Wgpu
1203 } else {
1204 Backend::None
1205 };
1206 }
1207 #[cfg(target_os = "macos")]
1208 if crate::gpu_metal::enabled() {
1209 return Backend::Metal;
1210 }
1211 Backend::None
1212}
1213
1214pub fn backend_available() -> bool {
1220 #[cfg(target_os = "macos")]
1221 {
1222 true
1224 }
1225 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1226 {
1227 static AVAIL: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1228 *AVAIL.get_or_init(crate::gpu_wgpu::adapter_probe)
1229 }
1230 #[cfg(all(not(feature = "gpu"), not(target_os = "macos")))]
1231 {
1232 false
1233 }
1234}
1235
1236static GPU_PAUSED: PauseGate = PauseGate::new();
1242
1243struct PauseGate(std::sync::atomic::AtomicUsize);
1248
1249impl PauseGate {
1250 const fn new() -> Self {
1251 Self(std::sync::atomic::AtomicUsize::new(0))
1252 }
1253
1254 fn enter(&self) {
1255 self.0.fetch_add(1, Ordering::Relaxed);
1256 }
1257
1258 fn leave(&self) {
1259 self.0.fetch_sub(1, Ordering::Relaxed);
1260 }
1261
1262 fn paused(&self) -> bool {
1263 self.0.load(Ordering::Relaxed) != 0
1264 }
1265}
1266
1267pub fn pause_gpu() -> GpuPause {
1270 GPU_PAUSED.enter();
1271 GpuPause(())
1272}
1273
1274pub struct GpuPause(());
1275
1276impl Drop for GpuPause {
1277 fn drop(&mut self) {
1278 GPU_PAUSED.leave();
1279 }
1280}
1281
1282pub fn enabled() -> bool {
1283 !GPU_PAUSED.paused() && backend() != Backend::None
1284}
1285
1286#[cfg(test)]
1287mod pause_gate_tests {
1288 use super::PauseGate;
1289
1290 #[test]
1293 fn pause_holds_until_the_last_guard() {
1294 let g = PauseGate::new();
1295 assert!(!g.paused());
1296 g.enter();
1297 g.enter();
1298 g.leave();
1299 assert!(g.paused(), "one holder left, the other's pause was lifted");
1300 g.leave();
1301 assert!(!g.paused());
1302 }
1303}
1304
1305pub fn wgpu_active() -> bool {
1319 #[cfg(feature = "gpu")]
1320 {
1321 matches!(backend(), Backend::Wgpu)
1322 }
1323 #[cfg(not(feature = "gpu"))]
1324 {
1325 false
1326 }
1327}
1328
1329pub fn default_device() -> usize {
1336 static D: std::sync::OnceLock<usize> = std::sync::OnceLock::new();
1337 *D.get_or_init(|| {
1338 std::env::var("CMF_GPU_ADAPTER")
1339 .ok()
1340 .and_then(|v| v.trim().parse::<usize>().ok())
1341 .unwrap_or(0)
1342 })
1343}
1344
1345thread_local! {
1346 static CUR_DEV: std::cell::Cell<Option<usize>> = const { std::cell::Cell::new(None) };
1347}
1348
1349pub fn current_device() -> usize {
1351 CUR_DEV.with(|c| c.get()).unwrap_or_else(default_device)
1352}
1353
1354pub fn set_current_device(i: usize) {
1358 CUR_DEV.with(|c| c.set(Some(i)));
1359}
1360
1361pub fn with_device<R>(dev: usize, f: impl FnOnce() -> R) -> R {
1363 let prev = CUR_DEV.with(|c| c.replace(Some(dev)));
1364 let r = f();
1365 CUR_DEV.with(|c| c.set(prev));
1366 r
1367}
1368
1369pub fn device_count() -> usize {
1372 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1373 {
1374 return crate::gpu_wgpu::adapter_count();
1375 }
1376 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
1377 {
1378 usize::from(backend_available())
1379 }
1380}
1381
1382pub fn vram_budget() -> u64 {
1386 #[cfg(feature = "cuda")]
1387 if backend() == Backend::Cuda {
1388 return crate::gpu_cuda::vram_budget();
1389 }
1390 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
1391 {
1392 return crate::gpu_wgpu::device_vram_budget();
1393 }
1394 #[cfg(not(all(feature = "gpu", not(target_os = "macos"))))]
1395 {
1396 if backend_available() { u64::MAX } else { 0 }
1397 }
1398}
1399
1400pub fn resident_bytes() -> u64 {
1404 #[cfg(feature = "cuda")]
1405 if backend() == Backend::Cuda {
1406 return crate::gpu_cuda::resident_bytes();
1407 }
1408 #[cfg(feature = "gpu")]
1409 {
1410 if backend() == Backend::Wgpu {
1411 return crate::gpu_wgpu::resident_bytes();
1412 }
1413 }
1414 0
1415}
1416
1417pub fn o1_device_stats(kv_id: u64) -> (usize, u64) {
1421 #[cfg(feature = "gpu")]
1422 {
1423 if backend() == Backend::Wgpu {
1424 return crate::gpu_wgpu::o1_device_stats(kv_id);
1425 }
1426 #[cfg(feature = "cuda")]
1427 if backend() == Backend::Cuda {
1428 return crate::gpu_cuda::o1_device_stats(kv_id);
1429 }
1430 }
1431 let _ = kv_id;
1432 (0, 0)
1433}
1434
1435pub fn upload_bytes() -> u64 {
1439 #[cfg(feature = "gpu")]
1440 {
1441 return crate::gpu_wgpu::UPLOAD_BYTES.load(std::sync::atomic::Ordering::Relaxed);
1442 }
1443 #[cfg(not(feature = "gpu"))]
1444 0
1445}
1446
1447pub fn upload_bandwidth_probe(block: usize, rounds: usize) -> Option<f64> {
1451 #[cfg(feature = "gpu")]
1452 {
1453 return crate::gpu_wgpu::upload_bandwidth_probe(block, rounds);
1454 }
1455 let _ = (block, rounds);
1456 None
1457}
1458
1459#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1471pub enum GraphPhase {
1472 Prefill,
1473 Decode,
1474}
1475
1476pub fn wgpu_graph_on(phase: GraphPhase) -> bool {
1484 match std::env::var("CMF_GPU_WGPU_GRAPH").ok().as_deref() {
1485 Some("0") => false,
1486 Some("prefill") => phase == GraphPhase::Prefill,
1487 Some(_) => true,
1488 None => {
1489 if wgpu_graph_default() {
1490 return true;
1491 }
1492 let _ = phase;
1497 false
1498 }
1499 }
1500}
1501
1502pub fn batched_attend_tiled() -> bool {
1505 #[cfg(feature = "cuda")]
1507 if backend() == Backend::Cuda {
1508 return true;
1509 }
1510 #[cfg(feature = "gpu")]
1511 {
1512 crate::gpu_wgpu::batched_attend_tiled()
1513 }
1514 #[cfg(not(feature = "gpu"))]
1515 {
1516 false
1517 }
1518}
1519
1520pub fn cuda_batch_rows() -> Option<usize> {
1523 #[cfg(feature = "cuda")]
1524 if backend() == Backend::Cuda {
1525 return Some(crate::gpu_cuda::batch_rows());
1526 }
1527 None
1528}
1529
1530pub fn cuda_active() -> bool {
1532 #[cfg(feature = "cuda")]
1533 {
1534 backend() == Backend::Cuda
1535 }
1536 #[cfg(not(feature = "cuda"))]
1537 {
1538 false
1539 }
1540}
1541
1542pub fn wgpu_graph_default() -> bool {
1543 #[cfg(feature = "cuda")]
1545 if backend() == Backend::Cuda {
1546 return true;
1547 }
1548 #[cfg(feature = "gpu")]
1549 {
1550 matches!(backend(), Backend::Wgpu)
1556 && (crate::gpu_wgpu::discrete_active()
1557 || (cfg!(target_os = "macos") && crate::gpu_wgpu::adapter_up()))
1558 }
1559 #[cfg(not(feature = "gpu"))]
1560 {
1561 false
1562 }
1563}
1564
1565#[allow(clippy::too_many_arguments, unused_variables)]
1567pub fn q8_matvec_range(
1568 model: &Arc<CmfModel>,
1569 idx: usize,
1570 row0: usize,
1571 row_scale: &[f32],
1572 xs: &[f32],
1573 rows: usize,
1574 cols: usize,
1575 out: &mut [f32],
1576) -> bool {
1577 match backend() {
1578 #[cfg(target_os = "macos")]
1579 Backend::Metal => {
1580 crate::gpu_metal::q8_matvec_range(model, idx, row0, row_scale, xs, rows, cols, out)
1581 }
1582 #[cfg(feature = "gpu")]
1583 Backend::Wgpu => {
1584 crate::gpu_wgpu::q8_matvec_range(model, idx, row0, row_scale, xs, rows, cols, out)
1585 }
1586 Backend::None => false,
1587 #[cfg(feature = "cuda")]
1588 Backend::Cuda => false,
1589 }
1590}
1591
1592pub(crate) fn q82_short_rows(
1594 model: &Arc<CmfModel>,
1595 idx: usize,
1596 xs: &[f32],
1597 b: usize,
1598 rows: usize,
1599 cols: usize,
1600 out: &mut [f32],
1601) -> bool {
1602 #[cfg(feature = "gpu")]
1603 if enabled_here() && backend() == Backend::Wgpu {
1604 return crate::gpu_wgpu::q82_short_rows(model, idx, xs, b, rows, cols, out);
1605 }
1606 let _ = (model, idx, xs, b, rows, cols, out);
1607 false
1608}
1609
1610#[allow(clippy::too_many_arguments, unused_variables)]
1613#[allow(clippy::too_many_arguments)]
1617pub fn q8_matmat_2f(
1618 model: &Arc<CmfModel>,
1619 idx: usize,
1620 row_scale: &[f32],
1621 col_field: &[f32],
1622 xs: &[f32],
1623 b: usize,
1624 rows: usize,
1625 cols: usize,
1626 out: &mut [f32],
1627) -> bool {
1628 #[allow(unreachable_patterns)]
1629 match backend() {
1630 #[cfg(feature = "gpu")]
1631 Backend::Wgpu => {
1632 crate::gpu_wgpu::q8_matmat_2f(model, idx, row_scale, col_field, xs, b, rows, cols, out)
1633 }
1634 _ => false,
1635 }
1636}
1637
1638pub fn q8_matmat(
1639 model: &Arc<CmfModel>,
1640 idx: usize,
1641 row_scale: &[f32],
1642 pre: &[f32],
1643 b: usize,
1644 rows: usize,
1645 cols: usize,
1646 out: &mut [f32],
1647) -> bool {
1648 match backend() {
1649 #[cfg(target_os = "macos")]
1650 Backend::Metal => {
1651 crate::gpu_metal::q8_matmat(model, idx, row_scale, pre, b, rows, cols, out)
1652 }
1653 #[cfg(feature = "gpu")]
1654 Backend::Wgpu => crate::gpu_wgpu::q8_matmat(model, idx, row_scale, pre, b, rows, cols, out),
1655 Backend::None => false,
1656 #[cfg(feature = "cuda")]
1657 Backend::Cuda => false,
1658 }
1659}
1660
1661#[allow(unused_variables)]
1664pub fn q1_matvec(
1665 model: &Arc<CmfModel>,
1666 idx: usize,
1667 xs: &[f32],
1668 rows: usize,
1669 cols: usize,
1670 out: &mut [f32],
1671) -> bool {
1672 match backend() {
1673 #[cfg(target_os = "macos")]
1674 Backend::Metal => crate::gpu_metal::q1_matvec(model, idx, xs, rows, cols, out),
1675 #[cfg(feature = "gpu")]
1676 Backend::Wgpu => crate::gpu_wgpu::q1_matvec(model, idx, xs, rows, cols, out),
1677 Backend::None => false,
1678 #[cfg(feature = "cuda")]
1679 Backend::Cuda => false,
1680 }
1681}
1682
1683#[allow(unused_variables)]
1687pub fn q1g_matvec(
1688 model: &Arc<CmfModel>,
1689 idx: usize,
1690 xs: &[f32],
1691 rows: usize,
1692 cols: usize,
1693 out: &mut [f32],
1694) -> bool {
1695 match backend() {
1696 #[cfg(target_os = "macos")]
1697 Backend::Metal => crate::gpu_metal::q1g_matvec(model, idx, xs, rows, cols, out),
1698 #[cfg(feature = "gpu")]
1699 Backend::Wgpu => crate::gpu_wgpu::q1g_matvec(model, idx, xs, rows, cols, out),
1700 Backend::None => false,
1701 #[cfg(feature = "cuda")]
1702 Backend::Cuda => false,
1703 }
1704}
1705
1706#[allow(clippy::too_many_arguments)]
1710pub fn attn_dropin(
1711 model: &Arc<CmfModel>,
1712 kv_id: u64,
1713 layer: usize,
1714 normed: &[f32],
1715 wq_idx: usize,
1716 wk_idx: usize,
1717 wv_idx: usize,
1718 wo_idx: usize,
1719 q_norm: Option<&[f32]>,
1720 k_norm: Option<&[f32]>,
1721 late_qk_norm: bool,
1722 invf: &[f32],
1723 nh: usize,
1724 nkv: usize,
1725 hd: usize,
1726 rd: usize,
1727 hidden: usize,
1728 pos: usize,
1729 cap: usize,
1730 gemma: bool,
1731 eps: f32,
1732 cpu_k: &[Vec<f32>],
1733 cpu_v: &[Vec<f32>],
1734 out: &mut [f32],
1735) -> bool {
1736 match backend() {
1737 #[cfg(feature = "gpu")]
1738 Backend::Wgpu => crate::gpu_wgpu::attn_dropin_gpu(
1739 model, kv_id, layer, normed, wq_idx, wk_idx, wv_idx, wo_idx, q_norm, k_norm,
1740 late_qk_norm, invf, nh, nkv, hd, rd, hidden, pos, cap, gemma, eps, cpu_k, cpu_v, out,
1741 ),
1742 #[allow(unused_variables)]
1743 _ => false,
1744 }
1745}
1746
1747#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1751pub enum GraphPrismOp {
1752 None,
1753 Forward,
1754 InverseEmbedding,
1755}
1756
1757pub const GRAPH_KIND_BLR: u8 = 11;
1761
1762pub struct GraphW<'a> {
1766 pub idx: usize,
1767 pub kind: u8,
1768 pub row_scale: &'a [f32],
1769 pub data: &'a [f32],
1770 pub prism: GraphPrismOp,
1771 pub affine: bool,
1772}
1773
1774pub enum GraphAttn<'a> {
1777 Full {
1778 wq: GraphW<'a>,
1779 wk: GraphW<'a>,
1780 wv: GraphW<'a>,
1781 wo: GraphW<'a>,
1782 q_norm: Option<&'a [f32]>,
1783 k_norm: Option<&'a [f32]>,
1784 late_qk_norm: bool,
1786 bias: Option<(&'a [f32], &'a [f32], &'a [f32])>,
1788 output_gate: bool,
1791 cpu_k: &'a [Vec<f32>],
1792 cpu_v: &'a [Vec<f32>],
1793 cpu_base: usize,
1797 geom: Option<GraphAttnGeom<'a>>,
1804 head_gate: Option<GraphW<'a>>,
1809 },
1810 Gdn {
1811 qkv: GraphW<'a>,
1812 z: GraphW<'a>,
1813 a: GraphW<'a>,
1814 b: GraphW<'a>,
1815 out: GraphW<'a>,
1816 conv1d: &'a [f32],
1817 a_log: &'a [f32],
1818 dt_bias: &'a [f32],
1819 norm: &'a [f32],
1820 nv: usize,
1821 nk: usize,
1822 dk: usize,
1823 dv: usize,
1824 kk: usize,
1825 cpu_state: &'a [f32],
1830 preview: Option<GdnPreviewW<'a>>,
1833 },
1834 ShortConv {
1841 inp: GraphW<'a>,
1843 out: GraphW<'a>,
1845 taps: &'a [f32],
1848 kernel: usize,
1849 cpu_state: &'a [f32],
1853 },
1854}
1855
1856#[derive(Clone, Copy)]
1861pub struct GraphAttnGeom<'a> {
1862 pub nkv: usize,
1864 pub hd: usize,
1868 pub dv: usize,
1870 pub rd: usize,
1872 pub invf: &'a [f32],
1874 pub rope_scale: f32,
1879 pub window: Option<usize>,
1882 pub sink: Option<&'a [f32]>,
1885 pub v_norm: bool,
1888 pub dash: Option<GraphDash<'a>>,
1891 pub vres: Option<(f32, &'a [f32])>,
1895}
1896
1897pub const DASH_CHUNK_MAX: usize = 64;
1901
1902#[derive(Clone, Copy)]
1904pub struct GraphDash<'a> {
1905 pub qk_proj: &'a [f32],
1907 pub q_norm: &'a [f32],
1908 pub k_norm: &'a [f32],
1909 pub head_cls: &'a [f32],
1910 pub idx_scale: f32,
1911 pub floor: f32,
1912 pub sigma: f32,
1913 pub k: usize,
1914 pub block: usize,
1915 pub local_blocks: usize,
1916 pub nblk: usize,
1917 pub cands: &'a [u32],
1919 pub coff: &'a [u32],
1920 pub angles: &'a [f32],
1922}
1923
1924pub fn vres_graph_on() -> bool {
1926 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
1927 *ON.get_or_init(|| std::env::var("CMF_VRES_GRAPH").as_deref() != Ok("0"))
1928}
1929
1930#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1934pub enum GraphAct {
1935 Silu,
1936 GeluErf,
1939 GeluTanh,
1942 SwiGluOaiM3,
1946}
1947
1948impl GraphAct {
1949 pub fn code(self) -> u32 {
1950 match self {
1951 Self::Silu => 0,
1952 Self::GeluErf => 1,
1953 Self::GeluTanh => 2,
1954 Self::SwiGluOaiM3 => 3,
1955 }
1956 }
1957}
1958
1959#[derive(Clone, Copy)]
1966pub struct GraphMhc<'a> {
1967 pub attn_w: &'a [f32],
1968 pub attn_b: &'a [f32],
1969 pub ffn_w: &'a [f32],
1970 pub ffn_b: &'a [f32],
1971 pub enter: bool,
1972 pub leave: bool,
1973 pub iters: usize,
1974 pub pre_scale: f32,
1975}
1976
1977pub struct GraphLayer<'a> {
1978 pub mhc: Option<GraphMhc<'a>>,
1981 pub input_norm: &'a [f32],
1982 pub attn: GraphAttn<'a>,
1983 pub post_norm: &'a [f32],
1984 pub ffn: GraphFfn<'a>,
1985 pub attn_out_norm: Option<&'a [f32]>,
1988 pub ffn_out_norm: Option<&'a [f32]>,
1989 pub layer_scale: Option<f32>,
1992}
1993
1994pub struct GraphMoeDetachedShared<'a> {
2002 pub gate: GraphW<'a>,
2003 pub up: GraphW<'a>,
2004 pub down: GraphW<'a>,
2005 pub width: usize,
2006}
2007
2008pub enum GraphFfn<'a> {
2013 AttentionOnly,
2016 Dense {
2017 gate: GraphW<'a>,
2018 up: GraphW<'a>,
2019 down: GraphW<'a>,
2020 act: GraphAct,
2023 limit: f32,
2026 hot: Option<GraphMoeDetachedShared<'a>>,
2031 },
2032 Moe {
2033 router: GraphW<'a>,
2035 shared_gate: GraphW<'a>,
2037 experts: Vec<(usize, usize, usize)>,
2041 n_exp: usize,
2043 top_k: usize,
2044 inter: usize,
2045 act: GraphAct,
2050 norm_topk: bool,
2051 q4tp: bool,
2057 gu_q2: bool,
2061 q82: bool,
2066 sigmoid: bool,
2070 sigmoid_denom_eps: f32,
2071 bias: Option<&'a [f32]>,
2074 has_shared: bool,
2078 detached_shared: Option<GraphMoeDetachedShared<'a>>,
2082 shared_gated: bool,
2087 route_scale: f32,
2090 },
2091}
2092
2093#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2096pub enum TokenGraphOutcome {
2097 Declined,
2099 Completed,
2101 Failed,
2103}
2104
2105#[allow(clippy::too_many_arguments)]
2110pub fn forward_token_graph(
2111 model: &Arc<CmfModel>,
2112 kv_id: u64,
2113 layers: &[GraphLayer],
2114 o1: &[Option<Vec<crate::nystrom::O1DeviceView<'_>>>],
2117 o1_epoch: u64,
2118 invf: &[f32],
2119 h: &mut [f32],
2120 nh: usize,
2121 nkv: usize,
2122 hd: usize,
2123 attn_scale: f32,
2124 rd: usize,
2125 hidden: usize,
2126 inter: usize,
2127 position: usize,
2128 cap: usize,
2129 gemma: bool,
2130 eps: f32,
2131 lm_head: Option<(&GraphW, usize)>,
2132 final_norm: &[f32],
2133 logits: &mut Vec<f32>,
2134 loop_norm_at: &[usize],
2135 steps: usize,
2136 embed: Option<(&GraphW, usize, f32)>,
2137 ids_out: Option<&mut Vec<u32>>,
2138 layers_run: Option<&mut usize>,
2141 layer_base: usize,
2145 hidden_too: bool,
2147) -> TokenGraphOutcome {
2148 match backend() {
2149 #[cfg(feature = "gpu")]
2150 Backend::Wgpu => crate::gpu_wgpu::forward_token_graph(
2151 model,
2152 kv_id,
2153 layers,
2154 o1,
2155 o1_epoch,
2156 invf,
2157 h,
2158 nh,
2159 nkv,
2160 hd,
2161 attn_scale,
2162 rd,
2163 hidden,
2164 inter,
2165 position,
2166 cap,
2167 gemma,
2168 eps,
2169 lm_head,
2170 final_norm,
2171 logits,
2172 loop_norm_at,
2173 steps,
2174 embed,
2175 ids_out,
2176 layers_run,
2177 layer_base,
2178 hidden_too,
2179 ),
2180 #[cfg(feature = "cuda")]
2181 Backend::Cuda => crate::gpu_cuda::forward_token_graph(
2182 model,
2183 kv_id,
2184 layers,
2185 o1,
2186 o1_epoch,
2187 invf,
2188 h,
2189 nh,
2190 nkv,
2191 hd,
2192 attn_scale,
2193 rd,
2194 hidden,
2195 position,
2196 cap,
2197 gemma,
2198 eps,
2199 lm_head,
2200 final_norm,
2201 logits,
2202 loop_norm_at,
2203 steps,
2204 layers_run,
2205 layer_base,
2206 hidden_too,
2207 ),
2208 #[allow(unused_variables)]
2209 _ => {
2210 let _ = (
2211 attn_scale,
2212 lm_head,
2213 final_norm,
2214 logits,
2215 loop_norm_at,
2216 layers_run,
2217 layer_base,
2218 hidden_too,
2219 );
2220 TokenGraphOutcome::Declined
2221 }
2222 }
2223}
2224
2225#[allow(clippy::too_many_arguments)]
2227pub fn dory_forward(
2228 id: u64,
2229 model: Option<&std::sync::Arc<cortiq_core::CmfModel>>,
2230 m: &crate::dory::Dory,
2231 ids: &[u32],
2232 pos: usize,
2233 loops: usize,
2234 want: crate::dory::Want,
2235 logits: &mut Vec<f32>,
2236) -> bool {
2237 #[cfg(target_os = "macos")]
2238 if backend() == Backend::Metal {
2239 return model.is_some_and(|md| crate::gpu_metal::dory::forward(id, md, m, ids, pos, loops, want, logits));
2240 }
2241 #[cfg(feature = "cuda")]
2242 if backend() == Backend::Cuda {
2243 return crate::gpu_cuda::dory_forward(id, m, ids, pos, loops, want, logits);
2244 }
2245 #[cfg(feature = "gpu")]
2246 if backend() == Backend::Wgpu {
2247 return crate::gpu_wgpu::dory::forward(id, m, ids, pos, loops, want, logits);
2248 }
2249 let _ = (id, model, m, ids, pos, loops, want, logits);
2250 false
2251}
2252
2253pub fn dory_reset(id: u64) {
2255 #[cfg(target_os = "macos")]
2256 if backend() == Backend::Metal {
2257 crate::gpu_metal::dory::reset(id);
2258 return;
2259 }
2260 #[cfg(feature = "cuda")]
2261 if backend() == Backend::Cuda {
2262 crate::gpu_cuda::dory_reset(id);
2263 return;
2264 }
2265 #[cfg(feature = "gpu")]
2266 if backend() == Backend::Wgpu {
2267 crate::gpu_wgpu::dory::reset(id);
2268 }
2269 let _ = id;
2270}
2271
2272pub fn dory_sealed(id: u64) -> bool {
2275 #[cfg(target_os = "macos")]
2276 if backend() == Backend::Metal {
2277 return crate::gpu_metal::dory::sealed(id);
2278 }
2279 #[cfg(feature = "cuda")]
2280 if backend() == Backend::Cuda {
2281 return crate::gpu_cuda::dory_sealed(id);
2282 }
2283 #[cfg(feature = "gpu")]
2284 if backend() == Backend::Wgpu {
2285 return crate::gpu_wgpu::dory::sealed(id);
2286 }
2287 let _ = id;
2288 false
2289}
2290
2291pub fn dory_kv_bytes(id: u64) -> Option<u64> {
2292 #[cfg(target_os = "macos")]
2293 if backend() == Backend::Metal {
2294 return crate::gpu_metal::dory::kv_bytes(id);
2295 }
2296 #[cfg(feature = "cuda")]
2297 if backend() == Backend::Cuda {
2298 return crate::gpu_cuda::dory_kv_bytes(id);
2299 }
2300 #[cfg(feature = "gpu")]
2301 if backend() == Backend::Wgpu {
2302 return crate::gpu_wgpu::dory::kv_bytes(id);
2303 }
2304 let _ = id;
2305 None
2306}
2307
2308pub fn embryo_wide_ready(q8: bool) -> bool {
2310 #[cfg(feature = "gpu")]
2311 if backend() == Backend::Wgpu {
2312 return crate::gpu_wgpu::embryo_wide_ready(q8);
2313 }
2314 let _ = q8;
2315 false
2316}
2317
2318pub fn forward_embryo_graph(
2323 model: &Arc<EmbryoGraphModel>,
2324 kv_id: u64,
2325 hidden: &[f32],
2326 position: usize,
2327 logits: &mut Vec<f32>,
2328) -> bool {
2329 #[cfg(feature = "gpu")]
2330 if backend() == Backend::Wgpu {
2331 return crate::gpu_wgpu::forward_embryo_graph(model, kv_id, hidden, position, logits);
2332 }
2333 let _ = (model, kv_id, hidden, position, logits);
2334 false
2335}
2336
2337#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2341pub enum BatchGraphOutcome {
2342 Declined,
2345 Completed,
2347 Failed,
2351}
2352
2353pub struct SpecTail<'a> {
2354 pub lm: GraphW<'a>,
2355 pub lm_rows: usize,
2356 pub final_norm: &'a [f32],
2357 pub logits_out: &'a mut Vec<f32>,
2358 pub pick: Option<(PreviewPick<'a>, &'a mut Vec<(u32, f32)>)>,
2362}
2363
2364pub struct PreviewPick<'a> {
2370 pub vocab: usize,
2371 pub counts: &'a [u32],
2372 pub stop: &'a [u32],
2373 pub forbid: &'a [u32],
2374 pub think_end: Option<u32>,
2375 pub close_open: bool,
2376 pub len0: usize,
2377 pub frequency: f32,
2378 pub presence: f32,
2379 pub repetition: f32,
2380 pub length_ramp: (f32, f32, f32),
2382 pub think_ramp: (f32, f32, f32),
2383}
2384
2385#[allow(clippy::too_many_arguments)]
2389pub fn forward_batch_graph(
2390 model: &Arc<CmfModel>,
2391 kv_id: u64,
2392 layers: &[GraphLayer],
2393 invf: &[f32],
2394 h: &mut [f32],
2395 nh: usize,
2396 nkv: usize,
2397 hd: usize,
2398 rd: usize,
2399 hidden: usize,
2400 inter: usize,
2401 positions: &[usize],
2402 cap: usize,
2403 gemma: bool,
2404 eps: f32,
2405 attn_scale: f32,
2406 k: usize,
2407 o1: &[Option<Vec<crate::nystrom::O1DeviceView<'_>>>],
2410 o1_epoch: u64,
2411 spec: Option<SpecTail<'_>>,
2412 layers_run: Option<&mut usize>,
2418) -> BatchGraphOutcome {
2419 forward_batch_graph_at(model, kv_id, 0, layers, invf, h, nh, nkv, hd, rd, hidden, inter, positions, cap, gemma, eps, attn_scale, k, o1, o1_epoch, spec, layers_run)
2420}
2421
2422thread_local! {
2423 static MIMO_ATTN_SCRATCH: std::cell::Cell<bool> = const { std::cell::Cell::new(true) };
2424}
2425
2426pub(crate) fn mimo_attention_scratch_enabled() -> bool {
2427 MIMO_ATTN_SCRATCH.with(std::cell::Cell::get)
2428}
2429
2430#[doc(hidden)]
2433pub fn mimo_attention_scratch_scope<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
2434 struct Restore(bool);
2435 impl Drop for Restore {
2436 fn drop(&mut self) {
2437 MIMO_ATTN_SCRATCH.with(|v| v.set(self.0));
2438 }
2439 }
2440 let _restore = Restore(MIMO_ATTN_SCRATCH.with(|v| v.replace(enabled)));
2441 f()
2442}
2443
2444pub fn g4_batch_on() -> bool {
2447 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2448 *ON.get_or_init(|| std::env::var("CMF_G4_BATCH").as_deref() != Ok("0"))
2449}
2450
2451pub(crate) fn moe_q82_graph_on() -> bool {
2454 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2455 *ON.get_or_init(|| std::env::var("CMF_MOE_Q82").as_deref() != Ok("0"))
2456}
2457
2458thread_local! {
2459 static MIMO_Q8_SHORT: std::cell::Cell<bool> = const { std::cell::Cell::new(true) };
2460}
2461
2462pub(crate) fn mimo_q8_short_enabled() -> bool {
2463 MIMO_Q8_SHORT.with(std::cell::Cell::get)
2464}
2465
2466#[doc(hidden)]
2468pub fn mimo_q8_short_scope<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
2469 struct Restore(bool);
2470 impl Drop for Restore {
2471 fn drop(&mut self) {
2472 MIMO_Q8_SHORT.with(|v| v.set(self.0));
2473 }
2474 }
2475 let _restore = Restore(MIMO_Q8_SHORT.with(|v| v.replace(enabled)));
2476 f()
2477}
2478
2479#[allow(clippy::too_many_arguments)]
2481pub fn forward_batch_graph_at(
2482 model: &Arc<CmfModel>,
2483 kv_id: u64,
2484 layer_base: usize,
2485 layers: &[GraphLayer],
2486 invf: &[f32],
2487 h: &mut [f32],
2488 nh: usize,
2489 nkv: usize,
2490 hd: usize,
2491 rd: usize,
2492 hidden: usize,
2493 inter: usize,
2494 positions: &[usize],
2495 cap: usize,
2496 gemma: bool,
2497 eps: f32,
2498 attn_scale: f32,
2499 k: usize,
2500 o1: &[Option<Vec<crate::nystrom::O1DeviceView<'_>>>],
2503 o1_epoch: u64,
2504 spec: Option<SpecTail<'_>>,
2505 layers_run: Option<&mut usize>,
2511) -> BatchGraphOutcome {
2512 match backend() {
2513 #[cfg(feature = "gpu")]
2514 Backend::Wgpu => crate::gpu_wgpu::forward_batch_graph_at(
2515 model, kv_id, layer_base, layers, invf, h, nh, nkv, hd, rd, hidden, inter, positions,
2516 cap, gemma, eps, attn_scale, k, o1, o1_epoch, spec, layers_run,
2517 ),
2518 #[cfg(feature = "cuda")]
2519 Backend::Cuda => crate::gpu_cuda::forward_batch_graph_at(
2520 model, kv_id, layer_base, layers, invf, h, nh, nkv, hd, rd, hidden, positions, cap,
2521 gemma, eps, attn_scale, k, o1, o1_epoch, spec, layers_run,
2522 ),
2523 #[allow(unreachable_patterns)]
2524 _ => {
2525 let _ = (o1, o1_epoch, spec, layers_run);
2526 BatchGraphOutcome::Declined
2527 }
2528 }
2529}
2530
2531pub fn rows_matvec(
2534 model: &Arc<CmfModel>,
2535 gw: &GraphW,
2536 rows: usize,
2537 cols: usize,
2538 x: &[f32],
2539 n: usize,
2540 y: &mut [f32],
2541) -> bool {
2542 #[cfg(feature = "cuda")]
2543 if backend() == Backend::Cuda {
2544 return crate::gpu_cuda::rows_matvec(model, gw, rows, cols, x, n, y);
2545 }
2546 let _ = (model, gw, rows, cols, x, n, y);
2547 false
2548}
2549
2550pub fn gdn_spec_restore(kv_id: u64, slot: usize, base_pos: usize, expected_layers: usize) -> bool {
2555 #[cfg(feature = "cuda")]
2556 if backend() == Backend::Cuda {
2557 return crate::gpu_cuda::gdn_spec_restore(kv_id, slot, base_pos, expected_layers);
2558 }
2559 #[cfg(feature = "gpu")]
2560 if backend() == Backend::Wgpu {
2561 return crate::gpu_wgpu::gdn_spec_restore(kv_id, slot, base_pos, expected_layers);
2562 }
2563 #[allow(unreachable_code)]
2564 {
2565 let _ = (kv_id, slot, base_pos, expected_layers);
2566 false
2567 }
2568}
2569
2570pub fn gdn_snapshot(kv_id: u64, pos: usize) -> bool {
2574 #[cfg(feature = "cuda")]
2575 if backend() == Backend::Cuda {
2576 return crate::gpu_cuda::gdn_snapshot(kv_id, pos);
2577 }
2578 let _ = (kv_id, pos);
2579 false
2580}
2581
2582pub fn gdn_snapshot_restore(kv_id: u64, pos: usize) -> bool {
2585 #[cfg(feature = "cuda")]
2586 if backend() == Backend::Cuda {
2587 return crate::gpu_cuda::gdn_snapshot_restore(kv_id, pos);
2588 }
2589 let _ = (kv_id, pos);
2590 false
2591}
2592
2593pub fn graph_kv_set_stored(kv_id: u64, layer: usize, stored: usize) -> bool {
2599 #[cfg(feature = "cuda")]
2600 if backend() == Backend::Cuda {
2601 return crate::gpu_cuda::kv_set_stored(kv_id, layer, stored);
2602 }
2603 #[cfg(feature = "gpu")]
2604 if backend() == Backend::Wgpu {
2605 return crate::gpu_wgpu::kv_mirror_set_stored(kv_id, layer, stored);
2606 }
2607 #[cfg(target_os = "macos")]
2608 if backend() == Backend::Metal {
2609 crate::gpu_metal::kv_mirror_set_stored(kv_id, layer, stored);
2610 return true;
2611 }
2612 false
2613}
2614
2615pub fn graph_kv_stored(_kv_id: u64, _layer: usize) -> Option<usize> {
2619 #[cfg(feature = "cuda")]
2620 if backend() == Backend::Cuda {
2621 return crate::gpu_cuda::kv_stored(_kv_id, _layer);
2622 }
2623 #[cfg(feature = "gpu")]
2624 if backend() == Backend::Wgpu {
2625 return crate::gpu_wgpu::kv_mirror_stored(_kv_id, _layer);
2626 }
2627 None
2628}
2629
2630pub fn graph_state_resident(_kv_id: u64, _layer: usize) -> bool {
2633 #[cfg(feature = "cuda")]
2634 if backend() == Backend::Cuda {
2635 return crate::gpu_cuda::state_resident(_kv_id, _layer);
2636 }
2637 #[cfg(feature = "gpu")]
2638 if backend() == Backend::Wgpu {
2639 return crate::gpu_wgpu::graph_state_resident(_kv_id, _layer);
2640 }
2641 false
2642}
2643
2644pub fn o1_qcap_begin(_layers: &[usize]) -> bool {
2648 #[cfg(feature = "gpu")]
2649 if backend() == Backend::Wgpu {
2650 crate::gpu_wgpu::o1_qcap_begin(_layers);
2651 return true;
2652 }
2653 #[cfg(feature = "cuda")]
2654 if backend() == Backend::Cuda {
2655 return crate::gpu_cuda::o1_qcap_begin(_layers);
2656 }
2657 false
2658}
2659
2660pub fn o1_qcap_stride(_stride: usize) -> bool {
2664 #[cfg(feature = "cuda")]
2665 if backend() == Backend::Cuda {
2666 return crate::gpu_cuda::o1_qcap_stride(_stride);
2667 }
2668 false
2669}
2670
2671pub fn o1_qcap_drain() -> Option<Vec<(usize, Vec<f32>)>> {
2673 #[cfg(feature = "gpu")]
2674 if backend() == Backend::Wgpu {
2675 return crate::gpu_wgpu::o1_qcap_drain();
2676 }
2677 #[cfg(feature = "cuda")]
2678 if backend() == Backend::Cuda {
2679 return crate::gpu_cuda::o1_qcap_drain();
2680 }
2681 None
2682}
2683
2684pub fn o1_spec_commit(_kv_id: u64, _pos0: usize, _rows: usize) -> bool {
2686 #[cfg(feature = "cuda")]
2687 if backend() == Backend::Cuda {
2688 return crate::gpu_cuda::o1_spec_commit(_kv_id, _pos0, _rows);
2689 }
2690 #[cfg(feature = "gpu")]
2691 if backend() == Backend::Wgpu {
2692 return crate::gpu_wgpu::o1_spec_commit(_kv_id, _pos0, _rows);
2693 }
2694 #[cfg(target_os = "macos")]
2695 if backend() == Backend::Metal {
2696 return crate::gpu_metal::o1c::spec_commit(_kv_id, _rows);
2697 }
2698 false
2699}
2700
2701pub fn o1_far_sums_on() -> bool {
2704 #[cfg(feature = "cuda")]
2705 if backend() == Backend::Cuda {
2706 return std::env::var("CMF_O1_FAR_GPU").as_deref() != Ok("0") && crate::gpu_cuda::ctx_ready();
2707 }
2708 false
2709}
2710
2711#[allow(clippy::too_many_arguments)]
2718pub fn o1_far_sums(nkv: usize, hpk: usize, hd: usize, m: usize, scale: f32, qt: &[f32], idx: &[u32], ks: &[&[f32]], vs: &[&[f32]]) -> Option<(Vec<f32>, Vec<f32>)> {
2719 #[cfg(feature = "cuda")]
2720 if backend() == Backend::Cuda {
2721 return crate::gpu_cuda::o1_far_sums(nkv, hpk, hd, m, scale, qt, idx, ks, vs);
2722 }
2723 let _ = (nkv, hpk, hd, m, scale, qt, idx, ks, vs);
2724 None
2725}
2726
2727pub fn o1_acap_begin(_stride: usize) {
2731 #[cfg(feature = "cuda")]
2732 if backend() == Backend::Cuda {
2733 crate::gpu_cuda::o1_acap_begin(_stride);
2734 }
2735 #[cfg(feature = "gpu")]
2736 if backend() == Backend::Wgpu {
2737 crate::gpu_wgpu::o1_acap_begin();
2738 }
2739 #[cfg(target_os = "macos")]
2740 if backend() == Backend::Metal {
2741 crate::gpu_metal::o1c::acap_begin();
2742 }
2743}
2744
2745pub fn o1_acap_end() {
2746 #[cfg(feature = "cuda")]
2747 if backend() == Backend::Cuda {
2748 crate::gpu_cuda::o1_acap_end();
2749 }
2750 #[cfg(feature = "gpu")]
2751 if backend() == Backend::Wgpu {
2752 crate::gpu_wgpu::o1_acap_end();
2753 }
2754 #[cfg(target_os = "macos")]
2755 if backend() == Backend::Metal {
2756 crate::gpu_metal::o1c::cap_end();
2757 }
2758}
2759
2760pub fn o1_acap_take() -> Option<Vec<(usize, Vec<f32>, Vec<f32>, Vec<f32>)>> {
2762 #[cfg(feature = "cuda")]
2763 if backend() == Backend::Cuda {
2764 return crate::gpu_cuda::o1_acap_take();
2765 }
2766 #[cfg(feature = "gpu")]
2767 if backend() == Backend::Wgpu {
2768 return crate::gpu_wgpu::o1_acap_take();
2769 }
2770 #[cfg(target_os = "macos")]
2771 if backend() == Backend::Metal {
2772 return Some(crate::gpu_metal::o1c::cap_take());
2773 }
2774 None
2775}
2776
2777pub fn o1_qcap_take() -> Option<Vec<(usize, Vec<f32>)>> {
2780 #[cfg(feature = "gpu")]
2781 if backend() == Backend::Wgpu {
2782 return crate::gpu_wgpu::o1_qcap_take();
2783 }
2784 #[cfg(feature = "cuda")]
2785 if backend() == Backend::Cuda {
2786 return crate::gpu_cuda::o1_qcap_take();
2787 }
2788 None
2789}
2790
2791pub fn graph_gdn_state_read(_kv_id: u64, _layer: usize) -> Option<Vec<f32>> {
2794 #[cfg(feature = "cuda")]
2795 if backend() == Backend::Cuda {
2796 return crate::gpu_cuda::gdn_state_read(_kv_id, _layer);
2797 }
2798 #[cfg(feature = "gpu")]
2799 if backend() == Backend::Wgpu {
2800 return crate::gpu_wgpu::graph_gdn_state_read(_kv_id, _layer);
2801 }
2802 None
2803}
2804
2805pub fn graph_kv_read_rows(
2809 _kv_id: u64,
2810 _reqs: &[(usize, usize, usize)],
2811 _nkv: usize,
2812 _hd: usize,
2813) -> Option<Vec<(Vec<f32>, Vec<f32>)>> {
2814 #[cfg(feature = "cuda")]
2815 if backend() == Backend::Cuda {
2816 return crate::gpu_cuda::kv_read_rows(_kv_id, _reqs, _nkv, _hd);
2817 }
2818 #[cfg(feature = "gpu")]
2819 if backend() == Backend::Wgpu {
2820 return crate::gpu_wgpu::kv_mirror_read_rows(_kv_id, _reqs, _nkv, _hd);
2821 }
2822 None
2823}
2824
2825pub fn graph_kv_pull_host(
2831 _kv_id: u64,
2832 _layer: usize,
2833 _from: usize,
2834 _to: usize,
2835 _nkv: usize,
2836 _hd: usize,
2837) -> Option<(Vec<f32>, Vec<f32>, usize)> {
2838 #[cfg(feature = "cuda")]
2839 if backend() == Backend::Cuda {
2840 return crate::gpu_cuda::kv_pull_host(_kv_id, _layer, _from, _to, _nkv, _hd);
2841 }
2842 #[cfg(feature = "gpu")]
2843 if backend() == Backend::Wgpu {
2844 return crate::gpu_wgpu::kv_mirror_pull_host(_kv_id, _layer, _from, _to, _nkv, _hd);
2845 }
2846 None
2847}
2848
2849pub fn set_batch_defer(_on: bool) {
2852 #[cfg(feature = "cuda")]
2853 if backend() == Backend::Cuda {
2854 crate::gpu_cuda::set_batch_defer(_on);
2855 }
2856 #[cfg(feature = "gpu")]
2857 if backend() == Backend::Wgpu {
2858 crate::gpu_wgpu::set_batch_defer(_on);
2859 }
2860}
2861
2862pub fn set_batch_kv_hint(_rows: usize) {
2865 #[cfg(feature = "cuda")]
2866 if backend() == Backend::Cuda {
2867 crate::gpu_cuda::set_batch_kv_hint(_rows);
2868 }
2869 #[cfg(feature = "gpu")]
2870 if backend() == Backend::Wgpu {
2871 crate::gpu_wgpu::set_batch_kv_hint(_rows);
2872 }
2873}
2874
2875pub fn graph_kv16_note(_kv_id: u64, _allowed: bool) {
2878 #[cfg(feature = "gpu")]
2879 if backend() == Backend::Wgpu {
2880 crate::gpu_wgpu::kv16::note(_kv_id, _allowed);
2881 }
2882}
2883
2884pub fn graph_kv_reset(_kv_id: u64) {
2886 #[cfg(feature = "cuda")]
2887 if backend() == Backend::Cuda {
2888 crate::gpu_cuda::kv_reset(_kv_id);
2889 }
2890 #[cfg(feature = "gpu")]
2891 if backend() == Backend::Wgpu {
2892 crate::gpu_wgpu::kv_mirror_reset(_kv_id);
2893 crate::gpu_wgpu::embryo_graph_reset(_kv_id);
2894 }
2895}
2896
2897pub fn q1t_matvec(
2901 model: &Arc<CmfModel>,
2902 idx: usize,
2903 xs: &[f32],
2904 rows: usize,
2905 cols: usize,
2906 out: &mut [f32],
2907) -> bool {
2908 match backend() {
2909 #[cfg(target_os = "macos")]
2910 Backend::Metal => {
2911 if metal_q1t_enabled() {
2912 crate::gpu_metal::q1t_matvec(model, idx, xs, rows, cols, out)
2913 } else {
2914 false
2915 }
2916 }
2917 #[cfg(feature = "gpu")]
2918 Backend::Wgpu => crate::gpu_wgpu::q1t_matvec(model, idx, xs, rows, cols, out),
2919 Backend::None => false,
2920 #[cfg(feature = "cuda")]
2921 Backend::Cuda => false,
2922 }
2923}
2924
2925#[allow(unused_variables)]
2928pub fn q4b_matvec(
2929 model: &Arc<CmfModel>,
2930 idx: usize,
2931 xs: &[f32],
2932 rows: usize,
2933 cols: usize,
2934 out: &mut [f32],
2935) -> bool {
2936 match backend() {
2937 #[cfg(target_os = "macos")]
2938 Backend::Metal => false,
2939 #[cfg(feature = "gpu")]
2940 Backend::Wgpu => crate::gpu_wgpu::q4b_matvec(model, idx, xs, rows, cols, out),
2941 Backend::None => false,
2942 #[cfg(feature = "cuda")]
2943 Backend::Cuda => false,
2944 }
2945}
2946
2947pub fn q1t_matmat(
2950 model: &Arc<CmfModel>,
2951 idx: usize,
2952 xs: &[f32],
2953 b: usize,
2954 rows: usize,
2955 cols: usize,
2956 out: &mut [f32],
2957) -> bool {
2958 match backend() {
2959 #[cfg(target_os = "macos")]
2960 Backend::Metal => crate::gpu_metal::q1t_matmat(model, idx, xs, b, rows, cols, out),
2964 #[cfg(feature = "gpu")]
2965 Backend::Wgpu => crate::gpu_wgpu::q1t_matmat(model, idx, xs, b, rows, cols, out),
2966 Backend::None => false,
2967 #[cfg(feature = "cuda")]
2968 Backend::Cuda => false,
2969 }
2970}
2971
2972#[cfg(target_os = "macos")]
2976pub(crate) fn metal_q1t_enabled() -> bool {
2977 std::env::var("CMF_METAL_Q1T")
2978 .map(|v| v != "0" && !v.eq_ignore_ascii_case("off"))
2979 .unwrap_or(true)
2980}
2981
2982pub fn q1_matmat(
2984 model: &Arc<CmfModel>,
2985 idx: usize,
2986 xs: &[f32],
2987 b: usize,
2988 rows: usize,
2989 cols: usize,
2990 out: &mut [f32],
2991) -> bool {
2992 match backend() {
2993 #[cfg(feature = "gpu")]
2994 Backend::Wgpu => crate::gpu_wgpu::q1_matmat(model, idx, xs, b, rows, cols, out),
2995 #[allow(unused_variables)]
2996 _ => false,
2997 }
2998}
2999
3000pub fn q1g_matmat(
3002 model: &Arc<CmfModel>,
3003 idx: usize,
3004 xs: &[f32],
3005 b: usize,
3006 rows: usize,
3007 cols: usize,
3008 out: &mut [f32],
3009) -> bool {
3010 match backend() {
3011 #[cfg(feature = "gpu")]
3012 Backend::Wgpu => crate::gpu_wgpu::q1g_matmat(model, idx, xs, b, rows, cols, out),
3013 #[allow(unused_variables)]
3014 _ => false,
3015 }
3016}
3017
3018static MM_KILL: AtomicBool = AtomicBool::new(false);
3023pub(crate) fn mm_killed() -> bool {
3024 MM_KILL.load(Ordering::Relaxed)
3025}
3026pub(crate) fn mm_kill() {
3027 MM_KILL.store(true, Ordering::Relaxed);
3028}
3029
3030static MM_STRIKES: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(0);
3037const MM_STRIKES_TO_KILL: u32 = 3;
3038static MM_ARMED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
3045
3046pub fn mm_kill_arm(on: bool) {
3049 MM_ARMED.store(on, Ordering::Relaxed);
3050 if on {
3051 MM_STRIKES.store(0, Ordering::Relaxed);
3052 }
3053}
3054
3055pub(crate) fn mm_budget_check(
3062 what: &str,
3063 el: std::time::Duration,
3064 budget: std::time::Duration,
3065 exempt: bool,
3066) {
3067 if el <= budget {
3068 MM_STRIKES.store(0, Ordering::Relaxed);
3069 return;
3070 }
3071 if exempt || !MM_ARMED.load(Ordering::Relaxed) {
3072 return;
3073 }
3074 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3075 let on = *ON.get_or_init(|| std::env::var("CMF_MM_KILL").as_deref() != Ok("0"));
3076 let n = MM_STRIKES.fetch_add(1, Ordering::Relaxed) + 1;
3077 if !on {
3078 tracing::info!(
3079 "gpu {what} took {el:?} (budget {budget:?}) — over budget, CMF_MM_KILL=0 keeps the device"
3080 );
3081 return;
3082 }
3083 if n >= MM_STRIKES_TO_KILL {
3084 tracing::warn!(
3085 "gpu {what} took {el:?} (budget {budget:?}), {n} in a row — \
3086 device contended, CPU for the rest of the process (CMF_MM_KILL=0 to override)"
3087 );
3088 mm_kill();
3089 } else {
3090 tracing::info!(
3091 "gpu {what} took {el:?} (budget {budget:?}) — strike {n} of {MM_STRIKES_TO_KILL}"
3092 );
3093 }
3094}
3095
3096#[allow(unused_variables, clippy::too_many_arguments)]
3101pub fn chunk_attend(
3102 q: &[f32],
3103 k: &[&[f32]],
3104 v: &[&[f32]],
3105 b: usize,
3106 s0: usize,
3107 nh: usize,
3108 nkv: usize,
3109 hd: usize,
3110 scale: f32,
3111 out: &mut [f32],
3112) -> bool {
3113 match backend() {
3114 #[cfg(feature = "gpu")]
3115 Backend::Wgpu => crate::gpu_wgpu::chunk_attend(q, k, v, b, s0, nh, nkv, hd, scale, out),
3116 #[allow(unreachable_patterns)]
3117 _ => false,
3118 }
3119}
3120
3121#[allow(unused_variables, clippy::too_many_arguments)]
3124pub fn chunk_attend_win(
3125 q: &[f32],
3126 k: &[&[f32]],
3127 v: &[&[f32]],
3128 b: usize,
3129 s0: usize,
3130 nh: usize,
3131 nkv: usize,
3132 hd: usize,
3133 scale: f32,
3134 window: usize,
3135 out: &mut [f32],
3136) -> bool {
3137 match backend() {
3138 #[cfg(feature = "gpu")]
3139 Backend::Wgpu => {
3140 crate::gpu_wgpu::chunk_attend_win(q, k, v, b, s0, nh, nkv, hd, scale, window, out)
3141 }
3142 #[allow(unreachable_patterns)]
3143 _ => false,
3144 }
3145}
3146
3147#[allow(unused_variables)]
3150pub fn gemm_many_keep(
3151 model: &Arc<CmfModel>,
3152 idxs: &[(usize, usize)],
3153 xs: &[f32],
3154 b: usize,
3155 cols: usize,
3156 out: &mut [f32],
3157) -> bool {
3158 match backend() {
3159 #[cfg(feature = "gpu")]
3160 Backend::Wgpu => crate::gpu_wgpu::gemm_many_keep(model, idxs, xs, b, cols, out),
3161 #[allow(unreachable_patterns)]
3162 _ => false,
3163 }
3164}
3165
3166thread_local! {
3167 static PREFILL_FAST_GEMM: Cell<bool> = const { Cell::new(false) };
3171}
3172
3173pub struct PrefillFastGemmGuard(bool);
3175
3176impl Drop for PrefillFastGemmGuard {
3177 fn drop(&mut self) {
3178 PREFILL_FAST_GEMM.with(|c| c.set(self.0));
3179 }
3180}
3181
3182pub fn enter_prefill_fast_gemm() -> PrefillFastGemmGuard {
3186 PrefillFastGemmGuard(PREFILL_FAST_GEMM.with(|c| c.replace(true)))
3187}
3188
3189pub fn prefill_fast_gemm() -> bool {
3191 PREFILL_FAST_GEMM.with(|c| c.get())
3192}
3193
3194pub const F16_MAX: f32 = 65504.0;
3197
3198pub fn abs_max_or_inf(xs: &[f32]) -> f32 {
3202 let mut acc = [0f32; 16];
3203 let mut bad = [false; 16];
3204 let mut it = xs.chunks_exact(16);
3205 for c in &mut it {
3206 for ((a, f), &v) in acc.iter_mut().zip(bad.iter_mut()).zip(c) {
3207 let v = v.abs();
3208 *f |= !(v < f32::INFINITY);
3210 *a = if v > *a { v } else { *a };
3211 }
3212 }
3213 let mut m = 0f32;
3214 for &v in it.remainder() {
3215 let v = v.abs();
3216 if !(v < f32::INFINITY) {
3217 return f32::INFINITY;
3218 }
3219 m = m.max(v);
3220 }
3221 if bad.iter().any(|&f| f) {
3222 return f32::INFINITY;
3223 }
3224 acc.iter().fold(m, |m, &a| m.max(a))
3225}
3226
3227#[derive(Clone, Copy)]
3229pub struct GdnPreviewW<'a> {
3230 pub conv_right: &'a [f32],
3232 pub logit_scale: &'a [f32],
3233 pub history_bias: &'a [f32],
3234 pub history_c: &'a [f32],
3235}
3236
3237#[derive(Clone, Copy, Debug, PartialEq)]
3243pub struct BlockPreview {
3244 pub coreset_top_p: Option<f32>,
3246}
3247
3248thread_local! {
3249 static BLOCK_PREVIEW: Cell<Option<BlockPreview>> = const { Cell::new(None) };
3250}
3251
3252pub struct BlockPreviewGuard(Option<BlockPreview>);
3254
3255impl Drop for BlockPreviewGuard {
3256 fn drop(&mut self) {
3257 BLOCK_PREVIEW.with(|c| c.set(self.0));
3258 }
3259}
3260
3261pub fn enter_block_preview(p: BlockPreview) -> BlockPreviewGuard {
3263 BlockPreviewGuard(BLOCK_PREVIEW.with(|c| c.replace(Some(p))))
3264}
3265
3266pub fn block_preview() -> Option<BlockPreview> {
3268 BLOCK_PREVIEW.with(|c| c.get())
3269}
3270
3271#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3277pub struct PrefillMirror {
3278 pub kv_id: u64,
3279 pub layer: usize,
3280 pub limit: usize,
3281}
3282
3283thread_local! {
3284 static PREFILL_MIRROR: Cell<Option<PrefillMirror>> = const { Cell::new(None) };
3288}
3289
3290pub struct PrefillMirrorGuard(Option<PrefillMirror>);
3292
3293impl Drop for PrefillMirrorGuard {
3294 fn drop(&mut self) {
3295 PREFILL_MIRROR.with(|c| c.set(self.0));
3296 }
3297}
3298
3299pub fn enter_prefill_mirror(t: PrefillMirror) -> PrefillMirrorGuard {
3302 PrefillMirrorGuard(PREFILL_MIRROR.with(|c| c.replace(Some(t))))
3303}
3304
3305pub fn prefill_mirror() -> Option<PrefillMirror> {
3307 PREFILL_MIRROR.with(|c| c.get())
3308}
3309
3310#[allow(unused_variables, clippy::too_many_arguments)]
3323pub fn chunk_attend_mirror(
3324 t: PrefillMirror,
3325 cpu_k: &[Vec<f32>],
3326 cpu_v: &[Vec<f32>],
3327 base: usize,
3328 q: &[f32],
3329 b: usize,
3330 s0: usize,
3331 nh: usize,
3332 nkv: usize,
3333 hd: usize,
3334 scale: f32,
3335 ring: Option<usize>,
3336 window: usize,
3337 operand_max: f32,
3338 out: &mut [f32],
3339) -> bool {
3340 match backend() {
3341 #[cfg(feature = "gpu")]
3342 Backend::Wgpu => crate::gpu_wgpu::chunk_attend_mirror(
3347 t.kv_id,
3348 t.layer,
3349 t.limit,
3350 cpu_k,
3351 cpu_v,
3352 base,
3353 q,
3354 b,
3355 s0,
3356 nh,
3357 nkv,
3358 hd,
3359 scale,
3360 ring,
3361 window,
3362 std::env::var("CMF_PREFILL_ATTN_COOP").as_deref() != Ok("0"),
3363 operand_max,
3364 out,
3365 ),
3366 #[allow(unreachable_patterns)]
3367 _ => false,
3368 }
3369}
3370
3371#[allow(unused_variables, clippy::too_many_arguments)]
3379pub fn chunk_attend_mirror_wo(
3380 t: PrefillMirror,
3381 cpu_k: &[Vec<f32>],
3382 cpu_v: &[Vec<f32>],
3383 base: usize,
3384 q: &[f32],
3385 b: usize,
3386 s0: usize,
3387 nh: usize,
3388 nkv: usize,
3389 hd: usize,
3390 scale: f32,
3391 ring: Option<usize>,
3392 window: usize,
3393 operand_max: f32,
3394 gains: Option<&[f32]>,
3395 wo: (&Arc<CmfModel>, usize),
3396 hidden: usize,
3397 out: &mut [f32],
3398) -> bool {
3399 match backend() {
3400 #[cfg(feature = "gpu")]
3401 Backend::Wgpu => crate::gpu_wgpu::chunk_attend_mirror_wo(
3402 t.kv_id,
3403 t.layer,
3404 t.limit,
3405 cpu_k,
3406 cpu_v,
3407 base,
3408 q,
3409 b,
3410 s0,
3411 nh,
3412 nkv,
3413 hd,
3414 scale,
3415 ring,
3416 window,
3417 std::env::var("CMF_PREFILL_ATTN_COOP").as_deref() != Ok("0"),
3418 operand_max,
3419 gains,
3420 wo,
3421 hidden,
3422 out,
3423 ),
3424 #[allow(unreachable_patterns)]
3425 _ => false,
3426 }
3427}
3428
3429pub fn chunk_attend_windowed() -> bool {
3431 match backend() {
3432 #[cfg(feature = "gpu")]
3433 Backend::Wgpu => true,
3434 #[allow(unreachable_patterns)]
3435 _ => false,
3436 }
3437}
3438
3439#[allow(unused_variables, clippy::too_many_arguments)]
3443pub fn q4t_qkv(
3444 model: &Arc<CmfModel>,
3445 wq: usize,
3446 wk: usize,
3447 wv: usize,
3448 xs: &[f32],
3449 b: usize,
3450 cols: usize,
3451 rq: usize,
3452 rk: usize,
3453 rv: usize,
3454 out: &mut [f32],
3455) -> bool {
3456 match backend() {
3457 #[cfg(feature = "gpu")]
3458 Backend::Wgpu => crate::gpu_wgpu::q4t_qkv(model, wq, wk, wv, xs, b, cols, rq, rk, rv, out),
3459 #[allow(unreachable_patterns)]
3460 _ => false,
3461 }
3462}
3463
3464#[allow(unused_variables, clippy::too_many_arguments)]
3466#[allow(clippy::too_many_arguments, unused_variables)]
3470pub fn q4tp_ffn_packed(
3471 model: &Arc<CmfModel>,
3472 w1: usize,
3473 w2: usize,
3474 xs: &[f32],
3475 b: usize,
3476 hidden: usize,
3477 inter: usize,
3478 bias: Option<&[f32]>,
3479 out: &mut [f32],
3480) -> bool {
3481 match backend() {
3482 #[cfg(feature = "gpu")]
3483 Backend::Wgpu => {
3484 crate::gpu_wgpu::ffn_packed(model, w1, w2, xs, b, hidden, inter, bias, out)
3485 }
3486 #[allow(unreachable_patterns)]
3487 _ => false,
3488 }
3489}
3490
3491pub fn q4tp_ffn(
3492 model: &Arc<CmfModel>,
3493 w1: usize,
3494 w3: usize,
3495 w2: usize,
3496 xs: &[f32],
3497 b: usize,
3498 hidden: usize,
3499 inter: usize,
3500 out: &mut [f32],
3501) -> bool {
3502 match backend() {
3503 #[cfg(target_os = "macos")]
3504 Backend::Metal => crate::gpu_metal::q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
3505 #[cfg(feature = "gpu")]
3506 Backend::Wgpu => crate::gpu_wgpu::q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
3507 #[allow(unreachable_patterns)]
3508 _ => false,
3509 }
3510}
3511
3512#[allow(clippy::too_many_arguments, unused_variables)]
3516pub fn q4_ffn_act(
3517 model: &Arc<CmfModel>,
3518 w1: usize,
3519 w3: usize,
3520 w2: usize,
3521 xs: &[f32],
3522 b: usize,
3523 hidden: usize,
3524 inter: usize,
3525 q4tp: bool,
3526 act: GraphAct,
3527 out: &mut [f32],
3528) -> bool {
3529 match act {
3530 GraphAct::Silu if q4tp => q4tp_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
3531 GraphAct::Silu => q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
3532 GraphAct::GeluErf | GraphAct::GeluTanh | GraphAct::SwiGluOaiM3 => match backend() {
3536 #[cfg(feature = "gpu")]
3537 Backend::Wgpu => crate::gpu_wgpu::q4_ffn_act(
3538 model,
3539 w1,
3540 w3,
3541 w2,
3542 xs,
3543 b,
3544 hidden,
3545 inter,
3546 q4tp,
3547 act.code(),
3548 out,
3549 ),
3550 #[allow(unreachable_patterns)]
3551 _ => false,
3552 },
3553 }
3554}
3555
3556#[allow(clippy::too_many_arguments, unused_variables)]
3561pub fn ffn_act_keep(
3562 model: &Arc<CmfModel>,
3563 w1: usize,
3564 w3: usize,
3565 w2: usize,
3566 xs: &[f32],
3567 b: usize,
3568 hidden: usize,
3569 inter: usize,
3570 act: GraphAct,
3571 out: &mut [f32],
3572) -> bool {
3573 match backend() {
3574 #[cfg(feature = "gpu")]
3575 Backend::Wgpu => crate::gpu_wgpu::ffn_act_keep(
3576 model,
3577 w1,
3578 w3,
3579 w2,
3580 xs,
3581 b,
3582 hidden,
3583 inter,
3584 act.code(),
3585 out,
3586 ),
3587 #[allow(unreachable_patterns)]
3588 _ => false,
3589 }
3590}
3591
3592#[allow(clippy::too_many_arguments, unused_variables)]
3597pub fn q4tp_gelu_ffn(
3598 model: &Arc<CmfModel>,
3599 w_in: usize,
3600 w_out: usize,
3601 xs: &[f32],
3602 b: usize,
3603 hidden: usize,
3604 inter: usize,
3605 bias_in: &[f32],
3606 bias_out: &[f32],
3607 out: &mut [f32],
3608) -> bool {
3609 match backend() {
3610 #[cfg(feature = "gpu")]
3611 Backend::Wgpu => crate::gpu_wgpu::q4tp_gelu_ffn(
3612 model, w_in, w_out, xs, b, hidden, inter, bias_in, bias_out, out,
3613 ),
3614 #[allow(unreachable_patterns)]
3615 _ => false,
3616 }
3617}
3618
3619pub fn q4t_ffn(
3620 model: &Arc<CmfModel>,
3621 w1: usize,
3622 w3: usize,
3623 w2: usize,
3624 xs: &[f32],
3625 b: usize,
3626 hidden: usize,
3627 inter: usize,
3628 out: &mut [f32],
3629) -> bool {
3630 match backend() {
3631 #[cfg(target_os = "macos")]
3632 Backend::Metal => crate::gpu_metal::q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
3633 #[cfg(feature = "gpu")]
3634 Backend::Wgpu => crate::gpu_wgpu::q4t_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
3635 #[allow(unreachable_patterns)]
3636 _ => false,
3637 }
3638}
3639
3640pub struct DitBlockArgs<'a> {
3645 pub n: usize,
3646 pub hidden: usize,
3647 pub inter: usize,
3648 pub nh: usize,
3649 pub nkv: usize,
3650 pub hd: usize,
3651 pub eps: f32,
3652 pub rope_cos: &'a [f32],
3653 pub rope_sin: &'a [f32],
3654 pub norm1: &'a [f32],
3655 pub norm2: &'a [f32],
3656 pub ffn_norm1: &'a [f32],
3657 pub ffn_norm2: &'a [f32],
3658 pub norm_q: &'a [f32],
3659 pub norm_k: &'a [f32],
3660 pub s_msa: &'a [f32],
3661 pub gate_msa: &'a [f32],
3662 pub s_mlp: &'a [f32],
3663 pub gate_mlp: &'a [f32],
3664 pub wq: usize,
3665 pub wk: usize,
3666 pub wv: usize,
3667 pub wo: usize,
3668 pub w1: usize,
3669 pub w3: usize,
3670 pub w2: usize,
3671 pub q4tp: bool,
3675 pub resident_in: bool,
3678 pub resident_out: bool,
3682}
3683
3684pub fn dit_chain_supported() -> bool {
3688 #[cfg(feature = "gpu")]
3689 {
3690 return matches!(backend(), Backend::Wgpu) && fused_dit_block_available();
3691 }
3692 #[allow(unreachable_code)]
3693 false
3694}
3695
3696pub fn dit_state_fetch(_x: &mut [f32]) -> bool {
3699 #[cfg(feature = "gpu")]
3700 {
3701 if matches!(backend(), Backend::Wgpu) {
3702 return crate::gpu_wgpu::dit_state_fetch(_x);
3703 }
3704 }
3705 false
3706}
3707
3708#[allow(unused_variables)]
3712#[allow(unused_variables, clippy::too_many_arguments)]
3716pub fn dit_qkv(
3717 model: &Arc<CmfModel>,
3718 wq: usize,
3719 wk: usize,
3720 wv: usize,
3721 xs: &[f32],
3722 b: usize,
3723 hidden: usize,
3724 qrows: usize,
3725 kvrows: usize,
3726 q_out: &mut [f32],
3727 k_out: &mut [f32],
3728 v_out: &mut [f32],
3729) -> bool {
3730 match backend() {
3731 #[cfg(feature = "gpu")]
3732 Backend::Wgpu => crate::gpu_wgpu::q4tp_qkv(
3733 model, wq, wk, wv, xs, b, hidden, qrows, kvrows, q_out, k_out, v_out,
3734 ),
3735 #[allow(unreachable_patterns)]
3736 _ => false,
3737 }
3738}
3739
3740pub struct QwenImageAttentionArgs<'a> {
3748 pub image: &'a [f32],
3749 pub text: &'a [f32],
3750 pub image_tokens: usize,
3751 pub text_tokens: usize,
3752 pub heads: usize,
3753 pub head_dim: usize,
3754 pub image_q: usize,
3755 pub image_k: usize,
3756 pub image_v: usize,
3757 pub text_q: usize,
3758 pub text_k: usize,
3759 pub text_v: usize,
3760 pub image_out: usize,
3761 pub text_out: usize,
3762 pub image_q_norm: &'a [f32],
3763 pub image_k_norm: &'a [f32],
3764 pub text_q_norm: &'a [f32],
3765 pub text_k_norm: &'a [f32],
3766 pub image_cos: &'a [f32],
3767 pub image_sin: &'a [f32],
3768 pub text_cos: &'a [f32],
3769 pub text_sin: &'a [f32],
3770 pub image_q_bias: &'a [f32],
3771 pub image_k_bias: &'a [f32],
3772 pub image_v_bias: &'a [f32],
3773 pub text_q_bias: &'a [f32],
3774 pub text_k_bias: &'a [f32],
3775 pub text_v_bias: &'a [f32],
3776 pub image_out_bias: &'a [f32],
3777 pub text_out_bias: &'a [f32],
3778 pub image_proj: &'a mut [f32],
3779 pub text_proj: &'a mut [f32],
3780}
3781
3782#[allow(clippy::too_many_fields)]
3787pub struct QwenImageChainBlock<'a> {
3788 pub image_mod: &'a [f32],
3789 pub text_mod: &'a [f32],
3790 pub image_q: usize,
3791 pub image_k: usize,
3792 pub image_v: usize,
3793 pub text_q: usize,
3794 pub text_k: usize,
3795 pub text_v: usize,
3796 pub image_out: usize,
3797 pub text_out: usize,
3798 pub image_q_norm: &'a [f32],
3799 pub image_k_norm: &'a [f32],
3800 pub text_q_norm: &'a [f32],
3801 pub text_k_norm: &'a [f32],
3802 pub image_q_bias: &'a [f32],
3803 pub image_k_bias: &'a [f32],
3804 pub image_v_bias: &'a [f32],
3805 pub text_q_bias: &'a [f32],
3806 pub text_k_bias: &'a [f32],
3807 pub text_v_bias: &'a [f32],
3808 pub image_out_bias: &'a [f32],
3809 pub text_out_bias: &'a [f32],
3810 pub image_attn_gate: &'a [f32],
3811 pub text_attn_gate: &'a [f32],
3812 pub image_mlp_in: usize,
3813 pub image_mlp_out: usize,
3814 pub text_mlp_in: usize,
3815 pub text_mlp_out: usize,
3816 pub image_mlp_in_bias: &'a [f32],
3817 pub image_mlp_out_bias: &'a [f32],
3818 pub text_mlp_in_bias: &'a [f32],
3819 pub text_mlp_out_bias: &'a [f32],
3820}
3821
3822#[allow(clippy::too_many_fields)]
3828pub struct QwenImageBlockArgs<'a> {
3829 pub image: &'a mut [f32],
3832 pub text: &'a mut [f32],
3833 pub image_norm: &'a [f32],
3834 pub text_norm: &'a [f32],
3835 pub image_tokens: usize,
3836 pub text_tokens: usize,
3837 pub heads: usize,
3838 pub head_dim: usize,
3839 pub image_cos: &'a [f32],
3840 pub image_sin: &'a [f32],
3841 pub text_cos: &'a [f32],
3842 pub text_sin: &'a [f32],
3843 pub image_q: usize,
3844 pub image_k: usize,
3845 pub image_v: usize,
3846 pub text_q: usize,
3847 pub text_k: usize,
3848 pub text_v: usize,
3849 pub image_out: usize,
3850 pub text_out: usize,
3851 pub image_q_norm: &'a [f32],
3852 pub image_k_norm: &'a [f32],
3853 pub text_q_norm: &'a [f32],
3854 pub text_k_norm: &'a [f32],
3855 pub image_q_bias: &'a [f32],
3856 pub image_k_bias: &'a [f32],
3857 pub image_v_bias: &'a [f32],
3858 pub text_q_bias: &'a [f32],
3859 pub text_k_bias: &'a [f32],
3860 pub text_v_bias: &'a [f32],
3861 pub image_out_bias: &'a [f32],
3862 pub text_out_bias: &'a [f32],
3863 pub image_attn_gate: &'a [f32],
3864 pub text_attn_gate: &'a [f32],
3865 pub image_mlp_in: usize,
3866 pub image_mlp_out: usize,
3867 pub text_mlp_in: usize,
3868 pub text_mlp_out: usize,
3869 pub image_mlp_in_bias: &'a [f32],
3870 pub image_mlp_out_bias: &'a [f32],
3871 pub text_mlp_in_bias: &'a [f32],
3872 pub text_mlp_out_bias: &'a [f32],
3873 pub image_mlp_mod: &'a [f32],
3874 pub text_mlp_mod: &'a [f32],
3875 pub image_mlp_gate: &'a [f32],
3876 pub text_mlp_gate: &'a [f32],
3877}
3878
3879pub struct QwenImageChainArgs<'a> {
3884 pub image: &'a mut [f32],
3885 pub text: &'a mut [f32],
3886 pub image_tokens: usize,
3887 pub text_tokens: usize,
3888 pub heads: usize,
3889 pub head_dim: usize,
3890 pub image_cos: &'a [f32],
3891 pub image_sin: &'a [f32],
3892 pub text_cos: &'a [f32],
3893 pub text_sin: &'a [f32],
3894 pub blocks: &'a [QwenImageChainBlock<'a>],
3895}
3896
3897#[allow(unused_variables)]
3898pub fn qwen_image_attention(
3899 model: &Arc<CmfModel>,
3900 args: &mut QwenImageAttentionArgs<'_>,
3901) -> bool {
3902 match backend() {
3903 #[cfg(feature = "gpu")]
3904 Backend::Wgpu => crate::gpu_wgpu::qwen_image_attention(model, args),
3905 #[allow(unreachable_patterns)]
3906 _ => false,
3907 }
3908}
3909
3910#[allow(unused_variables)]
3911pub fn qwen_image_block(model: &Arc<CmfModel>, args: &mut QwenImageBlockArgs<'_>) -> bool {
3912 match backend() {
3913 #[cfg(feature = "gpu")]
3914 Backend::Wgpu => crate::gpu_wgpu::qwen_image_block(model, args),
3915 #[allow(unreachable_patterns)]
3916 _ => false,
3917 }
3918}
3919
3920#[allow(unused_variables)]
3924pub fn qwen_image_chain(model: &Arc<CmfModel>, args: &mut QwenImageChainArgs<'_>) -> bool {
3925 match backend() {
3926 #[cfg(feature = "gpu")]
3927 Backend::Wgpu => crate::gpu_wgpu::qwen_image_chain(model, args),
3928 #[allow(unreachable_patterns)]
3929 _ => false,
3930 }
3931}
3932
3933#[allow(unused_variables, clippy::too_many_arguments)]
3940pub fn qwen_image_mlp_inplace(
3941 model: &Arc<CmfModel>,
3942 w_in: usize,
3943 w_out: usize,
3944 data: &mut [f32],
3945 batch: usize,
3946 hidden: usize,
3947 inter: usize,
3948 bias_in: &[f32],
3949 bias_out: &[f32],
3950 modulation: &[f32],
3951 gate: &[f32],
3952) -> bool {
3953 match backend() {
3954 #[cfg(feature = "gpu")]
3955 Backend::Wgpu => crate::gpu_wgpu::qwen_image_mlp_inplace(
3956 model,
3957 w_in,
3958 w_out,
3959 data,
3960 batch,
3961 hidden,
3962 inter,
3963 bias_in,
3964 bias_out,
3965 modulation,
3966 gate,
3967 ),
3968 #[allow(unreachable_patterns)]
3969 _ => false,
3970 }
3971}
3972
3973pub fn fused_dit_block_available() -> bool {
3977 #[cfg(target_os = "macos")]
3978 {
3979 matches!(backend(), Backend::Metal) && fused_block_trusted()
3980 }
3981 #[cfg(not(target_os = "macos"))]
3982 {
3983 false
3984 }
3985}
3986
3987pub fn dit_block(model: &Arc<CmfModel>, a: &DitBlockArgs, x: &mut [f32]) -> bool {
3988 dit_block_seg(model, a, &[a.n], x)
3989}
3990
3991pub fn dit_block_seg(
3995 model: &Arc<CmfModel>,
3996 a: &DitBlockArgs,
3997 segs: &[usize],
3998 x: &mut [f32],
3999) -> bool {
4000 match backend() {
4001 #[cfg(target_os = "macos")]
4002 Backend::Metal if segs.len() <= 1 => crate::gpu_metal::dit_block(model, a, x),
4003 #[cfg(feature = "gpu")]
4010 Backend::Wgpu
4011 if match std::env::var("CMF_DIT_FUSED").ok().as_deref() {
4012 Some("0") => false,
4013 Some(_) => true,
4014 None => crate::gpu_wgpu::discrete_active(),
4015 } =>
4016 {
4017 crate::gpu_wgpu::dit_block_seg(model, a, segs, x)
4018 }
4019 #[allow(unreachable_patterns)]
4020 _ => false,
4021 }
4022}
4023
4024pub struct VaeResnetArgs<'a> {
4028 pub groups: usize,
4029 pub ic: usize,
4030 pub oc: usize,
4031 pub h: usize,
4032 pub w: usize,
4033 pub n1w: &'a [f32],
4034 pub n1b: &'a [f32],
4035 pub c1w: &'a [f32],
4036 pub c1b: &'a [f32],
4037 pub c1k: usize,
4038 pub n2w: &'a [f32],
4039 pub n2b: &'a [f32],
4040 pub c2w: &'a [f32],
4041 pub c2b: &'a [f32],
4042 pub c2k: usize,
4043 pub shortcut: Option<(&'a [f32], &'a [f32], usize)>,
4044}
4045
4046#[allow(unused_variables)]
4049pub fn vae_resnet(a: &VaeResnetArgs, x: &[f32], out: &mut [f32]) -> bool {
4050 match backend() {
4051 #[cfg(target_os = "macos")]
4052 Backend::Metal => crate::gpu_metal::vae_resnet(a, x, out),
4053 _ => false,
4054 }
4055}
4056
4057#[allow(unused_variables, clippy::too_many_arguments)]
4060pub fn vae_upsample_conv(
4061 w: &[f32],
4062 bias: &[f32],
4063 x: &[f32],
4064 ic: usize,
4065 oc: usize,
4066 h: usize,
4067 w_img: usize,
4068 k: usize,
4069 out: &mut [f32],
4070) -> bool {
4071 match backend() {
4072 #[cfg(target_os = "macos")]
4073 Backend::Metal => crate::gpu_metal::vae_upsample_conv(w, bias, x, ic, oc, h, w_img, k, out),
4074 #[cfg(feature = "gpu")]
4075 Backend::Wgpu => crate::gpu_wgpu::vae_upsample_conv(w, bias, x, ic, oc, h, w_img, k, out),
4076 #[allow(unreachable_patterns)]
4077 _ => false,
4078 }
4079}
4080
4081#[allow(unused_variables, clippy::too_many_arguments)]
4084pub fn vae_conv2d(
4085 w: &[f32],
4086 bias: &[f32],
4087 x: &[f32],
4088 ic: usize,
4089 oc: usize,
4090 h: usize,
4091 w_img: usize,
4092 k: usize,
4093 out: &mut [f32],
4094) -> bool {
4095 match backend() {
4096 #[cfg(target_os = "macos")]
4097 Backend::Metal => crate::gpu_metal::vae_conv2d(w, bias, x, ic, oc, h, w_img, k, out),
4098 #[cfg(feature = "gpu")]
4099 Backend::Wgpu => crate::gpu_wgpu::vae_conv2d(w, bias, x, ic, oc, h, w_img, k, out),
4100 #[allow(unreachable_patterns)]
4101 _ => false,
4102 }
4103}
4104
4105#[allow(unused_variables, clippy::too_many_arguments)]
4109#[allow(unused_variables)]
4113#[allow(clippy::too_many_arguments)]
4114#[allow(clippy::too_many_arguments, unused_variables)]
4117pub fn dit_qkv_attention(
4118 model: &Arc<CmfModel>,
4119 qkv_idx: usize,
4120 xn: &[f32],
4121 n: usize,
4122 hidden: usize,
4123 nh: usize,
4124 hd: usize,
4125 scale: f32,
4126 nr: (&[f32], &[f32], &[f32], f32),
4127 out: &mut [f32],
4128) -> bool {
4129 match backend() {
4130 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4131 Backend::Wgpu => crate::gpu_wgpu::dit_qkv_attention(
4132 model, qkv_idx, xn, n, hidden, nh, hd, scale, nr, out,
4133 ),
4134 #[allow(unreachable_patterns)]
4135 _ => false,
4136 }
4137}
4138
4139pub struct H3ChainBlock<'a> {
4142 pub qkv: usize,
4143 pub out: usize,
4144 pub fc1: usize,
4145 pub fc2: usize,
4146 pub norm1: &'a [f32],
4147 pub norm2: &'a [f32],
4148 pub q_norm: &'a [f32],
4149 pub k_norm: &'a [f32],
4150 pub mods: &'a [f32],
4153 pub qkv_bias: &'a [f32],
4156 pub out_bias: &'a [f32],
4157 pub fc1_bias: &'a [f32],
4158 pub fc2_bias: &'a [f32],
4159 pub scale1: &'a [f32],
4160 pub scale2: &'a [f32],
4161}
4162
4163pub struct H3Chain<'a> {
4165 pub model: &'a Arc<CmfModel>,
4166 pub blocks: &'a [H3ChainBlock<'a>],
4167 pub n: usize,
4168 pub hidden: usize,
4169 pub heads: usize,
4170 pub head_dim: usize,
4171 pub ffn: usize,
4172 pub eps: f32,
4173 pub qk_eps: f32,
4174 pub angles: &'a [f32],
4176 pub rows: &'a [u32],
4178 pub groups: usize,
4182 pub qkv_interleaved: bool,
4185}
4186
4187pub struct TeChainLayer<'a> {
4190 pub q: usize,
4191 pub k: usize,
4192 pub v: usize,
4193 pub o: usize,
4194 pub gate: usize,
4195 pub up: usize,
4196 pub down: usize,
4197 pub input_norm: &'a [f32],
4198 pub post_norm: &'a [f32],
4199 pub q_norm: &'a [f32],
4200 pub k_norm: &'a [f32],
4201}
4202
4203pub struct TeChain<'a> {
4205 pub model: &'a Arc<CmfModel>,
4206 pub layers: &'a [TeChainLayer<'a>],
4207 pub n: usize,
4208 pub hidden: usize,
4209 pub heads: usize,
4210 pub kv_heads: usize,
4211 pub head_dim: usize,
4212 pub inter: usize,
4213 pub eps: f32,
4214 pub cs: &'a [f32],
4216}
4217
4218#[allow(unused_variables)]
4221pub fn te_chain(job: &TeChain<'_>, h: &mut [f32]) -> bool {
4222 match backend() {
4223 #[cfg(feature = "cuda")]
4224 Backend::Cuda => crate::gpu_cuda::h3_te_chain(job, h),
4225 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4226 Backend::Wgpu => crate::gpu_wgpu::h3::te_chain(job, h),
4227 #[cfg(target_os = "macos")]
4228 Backend::Metal => crate::gpu_metal::h3::te_chain(job, h),
4229 #[allow(unreachable_patterns)]
4230 _ => false,
4231 }
4232}
4233
4234pub enum ConvStep<'a> {
4236 GnSilu { w: &'a [f32], b: &'a [f32] },
4238 Conv {
4242 w: &'a [f32],
4243 b: &'a [f32],
4244 out_ch: usize,
4245 in_ch: usize,
4246 k: usize,
4247 stride: usize,
4248 pad: usize,
4249 far: bool,
4250 },
4251 Save,
4253 AddSaved { via: Option<(&'a [f32], &'a [f32])> },
4256}
4257
4258#[allow(unused_variables)]
4261pub fn conv_chain(steps: &[ConvStep<'_>], x: &[f32], c: usize, h: usize, w: usize) -> Option<(Vec<f32>, usize, usize, usize)> {
4262 match backend() {
4263 #[cfg(feature = "cuda")]
4264 Backend::Cuda => crate::gpu_cuda::h3_conv_chain(steps, x, c, h, w),
4265 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4266 Backend::Wgpu => crate::gpu_wgpu::h3_conv::conv_chain(steps, x, c, h, w),
4267 #[allow(unreachable_patterns)]
4268 _ => None,
4269 }
4270}
4271
4272pub fn h3_chain_available() -> bool {
4274 match backend() {
4275 #[cfg(feature = "cuda")]
4276 Backend::Cuda => true,
4277 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4278 Backend::Wgpu => crate::gpu_wgpu::h3::available(),
4279 #[cfg(target_os = "macos")]
4280 Backend::Metal => crate::gpu_metal::h3::available(),
4281 #[allow(unreachable_patterns)]
4282 _ => false,
4283 }
4284}
4285
4286#[allow(unused_variables)]
4290pub fn h3_chain(job: &H3Chain<'_>, h: &mut [f32]) -> bool {
4291 match backend() {
4292 #[cfg(feature = "cuda")]
4293 Backend::Cuda => crate::gpu_cuda::h3_chain(job, h),
4294 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4295 Backend::Wgpu => crate::gpu_wgpu::h3::chain(job, h),
4296 #[cfg(target_os = "macos")]
4297 Backend::Metal => crate::gpu_metal::h3::chain(job, h),
4298 #[allow(unreachable_patterns)]
4299 _ => false,
4300 }
4301}
4302
4303#[allow(clippy::too_many_arguments)]
4306pub fn dit_qkv_attn_out(
4307 model: &Arc<CmfModel>,
4308 qkv_idx: usize,
4309 out_idx: usize,
4310 xn: &[f32],
4311 n: usize,
4312 hidden: usize,
4313 nh: usize,
4314 hd: usize,
4315 scale: f32,
4316 nr: (&[f32], &[f32], &[f32], f32),
4317 proj: &mut [f32],
4318) -> bool {
4319 match backend() {
4320 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4321 Backend::Wgpu => crate::gpu_wgpu::dit_qkv_attn_out(
4322 model, qkv_idx, out_idx, xn, n, hidden, nh, hd, scale, nr, proj,
4323 ),
4324 #[allow(unreachable_patterns)]
4325 _ => false,
4326 }
4327}
4328
4329#[allow(clippy::too_many_arguments)]
4331pub fn vae_qkv_attn_out(
4332 model: &Arc<CmfModel>,
4333 qkv_idx: usize,
4334 out_idx: usize,
4335 xn: &[f32],
4336 n: usize,
4337 dim: usize,
4338 nh: usize,
4339 hd: usize,
4340 scale: f32,
4341 angles: &[f32],
4342 eps: f32,
4343 qkv_bias: &[f32],
4344 proj: &mut [f32],
4345) -> bool {
4346 match backend() {
4347 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4348 Backend::Wgpu => crate::gpu_wgpu::vae_qkv_attn_out(
4349 model, qkv_idx, out_idx, xn, n, dim, nh, hd, scale, angles, eps, qkv_bias, proj,
4350 ),
4351 #[allow(unreachable_patterns)]
4352 _ => false,
4353 }
4354}
4355
4356#[allow(clippy::too_many_arguments)]
4357pub fn vae_attention_packed(
4358 qkv: &[f32],
4359 nh: usize,
4360 n: usize,
4361 hd: usize,
4362 scale: f32,
4363 angles: &[f32],
4364 eps: f32,
4365 out: &mut [f32],
4366) -> bool {
4367 vae_attention_packed_layout(qkv, nh, n, hd, scale, angles, eps, out, 1)
4368}
4369
4370#[allow(clippy::too_many_arguments)]
4371pub fn vae_attention_packed_layout(
4372 qkv: &[f32],
4373 nh: usize,
4374 n: usize,
4375 hd: usize,
4376 scale: f32,
4377 angles: &[f32],
4378 eps: f32,
4379 out: &mut [f32],
4380 layout: u32,
4381) -> bool {
4382 match backend() {
4383 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4384 Backend::Wgpu => crate::gpu_wgpu::vae_attention_packed_layout(
4385 qkv, nh, n, hd, scale, angles, eps, out, layout,
4386 ),
4387 #[allow(unreachable_patterns)]
4388 _ => false,
4389 }
4390}
4391
4392#[allow(clippy::too_many_arguments)]
4393pub fn dit_split_only(
4394 qkv: &[f32],
4395 nh: usize,
4396 n: usize,
4397 hd: usize,
4398 layout: u32,
4399 norm: Option<(&[f32], f32)>,
4400 out_q: &mut [f32],
4401) -> bool {
4402 match backend() {
4403 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4404 Backend::Wgpu => crate::gpu_wgpu::dit_split_only(qkv, nh, n, hd, layout, norm, out_q),
4405 #[allow(unreachable_patterns)]
4406 _ => false,
4407 }
4408}
4409
4410pub fn gemm_nt_f32_transient(
4418 x: &[f32],
4419 w: &[f32],
4420 y: &mut [f32],
4421 n: usize,
4422 k: usize,
4423 m: usize,
4424) -> bool {
4425 match backend() {
4426 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4427 Backend::Wgpu => crate::gpu_wgpu::gemm_nt_f32_transient(x, w, y, n, k, m),
4428 #[allow(unreachable_patterns)]
4429 _ => false,
4430 }
4431}
4432
4433pub struct VitLayer<'a> {
4437 pub ln1_w: &'a [f32],
4438 pub ln1_b: &'a [f32],
4439 pub qkv: &'a [f32],
4440 pub qkv_b: &'a [f32],
4441 pub proj: &'a [f32],
4442 pub proj_b: &'a [f32],
4443 pub ln2_w: &'a [f32],
4444 pub ln2_b: &'a [f32],
4445 pub fc1: &'a [f32],
4446 pub fc1_b: &'a [f32],
4447 pub fc2: &'a [f32],
4448 pub fc2_b: &'a [f32],
4449}
4450
4451pub struct VitArgs<'a> {
4456 pub key: u64,
4457 pub layers: Vec<VitLayer<'a>>,
4458 pub n: usize,
4459 pub d: usize,
4460 pub heads: usize,
4461 pub head_dim: usize,
4462 pub inter: usize,
4463 pub eps: f64,
4464 pub gelu_tanh: bool,
4465 pub quick_gelu: bool,
4467 pub rope_halves: bool,
4468 pub cos: &'a [f32],
4469 pub sin: &'a [f32],
4470 pub prof: bool,
4471}
4472
4473#[allow(unused_variables)]
4477pub fn vit_encode(a: &VitArgs, x: &mut [f32]) -> bool {
4478 if std::env::var("CMF_VIT_RESIDENT").as_deref() == Ok("0") {
4479 return false;
4480 }
4481 match backend() {
4482 #[cfg(target_os = "macos")]
4483 Backend::Metal => crate::gpu_metal::vit::encode(a, x),
4484 #[cfg(feature = "gpu")]
4485 Backend::Wgpu => crate::gpu_wgpu::vit::encode(a, x),
4486 #[allow(unreachable_patterns)]
4487 _ => false,
4488 }
4489}
4490
4491pub struct SamLayer<'a> {
4494 pub ln1_w: &'a [f32],
4495 pub ln1_b: &'a [f32],
4496 pub qkv: &'a [f32],
4497 pub qkv_b: &'a [f32],
4498 pub proj: &'a [f32],
4499 pub proj_b: &'a [f32],
4500 pub rel: &'a [f32],
4501 pub ln2_w: &'a [f32],
4502 pub ln2_b: &'a [f32],
4503 pub fc1: &'a [f32],
4504 pub fc1_b: &'a [f32],
4505 pub fc2: &'a [f32],
4506 pub fc2_b: &'a [f32],
4507 pub window: usize,
4508}
4509
4510pub struct SamConv<'a> {
4512 pub w: &'a [f32],
4513 pub ci: usize,
4514 pub co: usize,
4515 pub k: usize,
4516 pub stride: usize,
4517 pub pad: usize,
4518 pub norm: bool,
4520}
4521
4522pub struct SamArgs<'a> {
4523 pub key: u64,
4524 pub layers: Vec<SamLayer<'a>>,
4525 pub convs: Vec<SamConv<'a>>,
4526 pub norms: Vec<&'a [f32]>,
4527 pub rel_global: Vec<Vec<f32>>,
4529 pub g: usize,
4530 pub d: usize,
4531 pub heads: usize,
4532 pub head_dim: usize,
4533 pub inter: usize,
4534 pub eps: f64,
4535}
4536
4537#[allow(unused_variables)]
4539pub fn sam_encode(a: &SamArgs, x: &[f32], out: &mut Vec<f32>) -> bool {
4540 if std::env::var("CMF_VIT_RESIDENT").as_deref() == Ok("0") {
4541 return false;
4542 }
4543 match backend() {
4544 #[cfg(target_os = "macos")]
4545 Backend::Metal => crate::gpu_metal::vit::sam_encode(a, x, out),
4546 #[cfg(feature = "gpu")]
4547 Backend::Wgpu => crate::gpu_wgpu::vit::sam_encode(a, x, out),
4548 #[allow(unreachable_patterns)]
4549 _ => false,
4550 }
4551}
4552
4553pub fn primal_vae_run(
4557 key: u64,
4558 weights: &crate::primal_vae::PvWeights,
4559 plan: &crate::primal_vae::PvPlan,
4560 input: &[f32],
4561 out: &mut [f32],
4562) -> bool {
4563 match backend() {
4564 #[cfg(target_os = "macos")]
4565 Backend::Metal => crate::gpu_metal::primal_vae::run(key, weights, plan, input, out),
4566 #[cfg(feature = "gpu")]
4567 Backend::Wgpu => crate::gpu_wgpu::primal_vae::run(key, weights, plan, input, out),
4568 #[allow(unreachable_patterns)]
4569 _ => false,
4570 }
4571}
4572
4573pub fn vision_gemm_nt(x: &[f32], w: &[f32], y: &mut [f32], n: usize, k: usize, m: usize) -> bool {
4578 if n * k * m < (1 << 22) {
4579 return false;
4580 }
4581 match backend() {
4582 #[cfg(target_os = "macos")]
4583 Backend::Metal => crate::gpu_metal::egemma2::gemm_nt(x, w, y, n, k, m),
4584 #[cfg(feature = "gpu")]
4585 Backend::Wgpu => crate::gpu_wgpu::gemm_nt_f32(x, w, y, n, k, m),
4586 #[cfg(feature = "cuda")]
4587 Backend::Cuda => crate::gpu_cuda::h3_gemm_nt(x, w, y, n, k, m),
4588 #[allow(unreachable_patterns)]
4589 _ => false,
4590 }
4591}
4592
4593pub fn gemm_nt_f32(x: &[f32], w: &[f32], y: &mut [f32], n: usize, k: usize, m: usize) -> bool {
4596 match backend() {
4597 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4598 Backend::Wgpu => crate::gpu_wgpu::gemm_nt_f32(x, w, y, n, k, m),
4599 #[cfg(feature = "cuda")]
4600 Backend::Cuda => crate::gpu_cuda::h3_gemm_nt(x, w, y, n, k, m),
4601 #[allow(unreachable_patterns)]
4602 _ => false,
4603 }
4604}
4605
4606#[allow(clippy::too_many_arguments)]
4609pub fn music3_ffn(
4610 model: &std::sync::Arc<CmfModel>,
4611 idx_in: usize,
4612 idx_out: usize,
4613 h: &[f32],
4614 bias_in: &[f32],
4615 n: usize,
4616 hs: usize,
4617 inter: usize,
4618 out: &mut [f32],
4619) -> bool {
4620 match backend() {
4621 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4622 Backend::Wgpu => {
4623 crate::gpu_wgpu::music3_ffn(model, idx_in, idx_out, h, bias_in, n, hs, inter, out)
4624 }
4625 #[allow(unreachable_patterns)]
4626 _ => false,
4627 }
4628}
4629
4630#[allow(clippy::too_many_arguments)]
4634pub fn conv1d_gemm(
4635 x: &[f32],
4636 w: &[f32],
4637 ic: usize,
4638 oc: usize,
4639 n: usize,
4640 k: usize,
4641 pad: usize,
4642 dil: usize,
4643 out_n: usize,
4644 yt: &mut [f32],
4645) -> bool {
4646 match backend() {
4647 #[cfg(target_os = "macos")]
4648 Backend::Metal => crate::gpu_metal::conv1d_gemm(x, w, ic, oc, n, k, pad, dil, out_n, yt),
4649 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4650 Backend::Wgpu => crate::gpu_wgpu::conv1d_gemm(x, w, ic, oc, n, k, pad, dil, out_n, yt),
4651 #[cfg(feature = "cuda")]
4652 Backend::Cuda => {
4653 out_n * ic * k * oc >= (1 << 22) && crate::gpu_cuda::h3_conv1d(x, w, ic, oc, n, k, pad, dil, out_n, yt)
4655 }
4656 #[allow(unreachable_patterns)]
4657 _ => false,
4658 }
4659}
4660
4661#[allow(clippy::too_many_arguments)]
4663pub fn vae_conv2d_coop(
4664 w: &[f32],
4665 bias: Option<&[f32]>,
4666 x: &[f32],
4667 ic: usize,
4668 oc: usize,
4669 h: usize,
4670 wi: usize,
4671 k: usize,
4672 out: &mut [f32],
4673) -> bool {
4674 match backend() {
4675 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4676 Backend::Wgpu => crate::gpu_wgpu::vae_conv2d_coop(w, bias, x, ic, oc, h, wi, k, out),
4677 #[allow(unreachable_patterns)]
4678 _ => false,
4679 }
4680}
4681
4682pub fn dit_attention_packed(
4683 qkv: &[f32],
4684 nh: usize,
4685 n: usize,
4686 hd: usize,
4687 scale: f32,
4688 nr: Option<(&[f32], &[f32], &[f32], f32)>,
4691 out: &mut [f32],
4692) -> bool {
4693 match backend() {
4694 #[cfg(feature = "gpu")]
4701 Backend::Wgpu => crate::gpu_wgpu::dit_attention_packed(qkv, nh, n, hd, scale, nr, out),
4702 #[allow(unreachable_patterns)]
4703 _ => false,
4704 }
4705}
4706
4707pub fn dit_attention_packed_available() -> bool {
4715 #[allow(unreachable_patterns)]
4716 match backend() {
4717 #[cfg(feature = "gpu")]
4718 Backend::Wgpu => crate::gpu_wgpu::dit_attention_packed_ready(),
4719 _ => false,
4720 }
4721}
4722
4723pub fn dit_attention(
4724 qh: &[f32],
4725 kh: &[f32],
4726 vh: &[f32],
4727 nh: usize,
4728 nkv: usize,
4729 n: usize,
4730 hd: usize,
4731 scale: f32,
4732 out: &mut [f32],
4733) -> bool {
4734 match backend() {
4735 #[cfg(target_os = "macos")]
4736 Backend::Metal => crate::gpu_metal::dit_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
4737 #[cfg(feature = "gpu")]
4738 Backend::Wgpu => crate::gpu_wgpu::dit_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
4739 #[cfg(feature = "cuda")]
4740 Backend::Cuda => crate::gpu_cuda::h3_attention(qh, kh, vh, nh, nkv, n, hd, scale, out),
4741 #[allow(unreachable_patterns)]
4742 _ => false,
4743 }
4744}
4745
4746pub fn gram_alloc(cols: usize) -> Option<u64> {
4749 #[cfg(feature = "gpu")]
4750 if backend() == Backend::Wgpu {
4751 return crate::gpu_wgpu::gram::alloc(cols);
4752 }
4753 let _ = cols;
4754 None
4755}
4756
4757pub fn gram_add(id: u64, xs: &[f32], b: usize) -> bool {
4759 #[cfg(feature = "gpu")]
4760 if backend() == Backend::Wgpu {
4761 return crate::gpu_wgpu::gram::add(id, xs, b);
4762 }
4763 let _ = (id, xs, b);
4764 false
4765}
4766
4767#[cfg(feature = "gpu")]
4769pub fn gram_add_dev(id: u64, x: &wgpu::Buffer, b: usize) -> bool {
4770 if backend() == Backend::Wgpu {
4771 return crate::gpu_wgpu::gram::add_dev(id, x, b);
4772 }
4773 false
4774}
4775
4776pub fn gram_take(id: u64) -> Option<Vec<f64>> {
4778 #[cfg(feature = "gpu")]
4779 if backend() == Backend::Wgpu {
4780 return crate::gpu_wgpu::gram::take(id);
4781 }
4782 let _ = id;
4783 None
4784}
4785
4786pub fn gptq_fold_dev(h_id: u64, n: usize, lambda: f64) -> Option<u64> {
4791 #[cfg(feature = "gpu")]
4792 if backend() == Backend::Wgpu {
4793 return crate::gpu_wgpu::gptq_dev::fold(h_id, n, lambda);
4794 }
4795 let _ = (h_id, n, lambda);
4796 None
4797}
4798
4799pub fn gptq_fold_info(id: u64) -> Option<(Vec<bool>, Vec<f32>, f64)> {
4801 #[cfg(feature = "gpu")]
4802 if backend() == Backend::Wgpu {
4803 return crate::gpu_wgpu::gptq_dev::info(id);
4804 }
4805 let _ = id;
4806 None
4807}
4808
4809pub fn gptq_fold_read(id: u64) -> Option<Vec<f32>> {
4811 #[cfg(feature = "gpu")]
4812 if backend() == Backend::Wgpu {
4813 return crate::gpu_wgpu::gptq_dev::read_u(id);
4814 }
4815 let _ = id;
4816 None
4817}
4818
4819pub fn gptq_fold_free(id: u64) {
4821 #[cfg(feature = "gpu")]
4822 if backend() == Backend::Wgpu {
4823 crate::gpu_wgpu::gptq_dev::free(id);
4824 }
4825 let _ = id;
4826}
4827
4828#[allow(clippy::too_many_arguments)]
4830pub fn gptq_sweep_q4tp_dev(
4831 id: u64,
4832 w: &[f32],
4833 rows: usize,
4834 tab: &[f32],
4835 lost: &[f32],
4836 nib: &mut [u8],
4837 codes: &mut [u32],
4838) -> bool {
4839 #[cfg(feature = "gpu")]
4840 if backend() == Backend::Wgpu {
4841 return crate::gpu_wgpu::gptq_dev::sweep_q4tp(id, w, rows, tab, lost, nib, codes);
4842 }
4843 let _ = (id, w, rows, tab, lost, nib, codes);
4844 false
4845}
4846
4847pub fn gptq_sweep_q2tp_dev(
4849 id: u64,
4850 w: &[f32],
4851 rows: usize,
4852 tab: &[f32],
4853 hw: &[f32],
4854 chunks: &mut [u8],
4855 codes: &mut [u32],
4856) -> bool {
4857 #[cfg(feature = "gpu")]
4858 if backend() == Backend::Wgpu {
4859 return crate::gpu_wgpu::gptq_dev::sweep_q2tp(id, w, rows, tab, hw, chunks, codes);
4860 }
4861 let _ = (id, w, rows, tab, hw, chunks, codes);
4862 false
4863}
4864
4865pub fn gram_read(id: u64) -> Option<Vec<f64>> {
4868 #[cfg(feature = "gpu")]
4869 if backend() == Backend::Wgpu {
4870 return crate::gpu_wgpu::gram::read(id);
4871 }
4872 let _ = id;
4873 None
4874}
4875
4876pub fn gram_clear() {
4878 #[cfg(feature = "gpu")]
4879 if backend() == Backend::Wgpu {
4880 crate::gpu_wgpu::gram::clear();
4881 }
4882}
4883
4884#[allow(unused_variables)]
4889pub fn q4tp_matmat(
4890 model: &Arc<CmfModel>,
4891 idx: usize,
4892 xs: &[f32],
4893 b: usize,
4894 rows: usize,
4895 cols: usize,
4896 out: &mut [f32],
4897) -> bool {
4898 match backend() {
4899 #[cfg(target_os = "macos")]
4900 Backend::Metal => crate::gpu_metal::q4tp_matmat(model, idx, xs, b, rows, cols, out),
4901 #[cfg(feature = "gpu")]
4902 Backend::Wgpu => crate::gpu_wgpu::q4tp_matmat(model, idx, xs, b, rows, cols, out),
4903 #[cfg(feature = "cuda")]
4904 Backend::Cuda => crate::gpu_cuda::h3_matmat(model, idx, xs, b, rows, cols, out),
4905 #[allow(unreachable_patterns)]
4906 _ => false,
4907 }
4908}
4909
4910#[allow(unused_variables)]
4915pub fn stage_done(model: &CmfModel) {
4916 match backend() {
4917 #[cfg(all(feature = "gpu", not(target_os = "macos")))]
4918 Backend::Wgpu => {
4919 crate::gpu_wgpu::h3::release();
4920 crate::gpu_wgpu::h3_conv::release();
4921 crate::gpu_wgpu::release_model_buffers(model)
4922 }
4923 #[cfg(feature = "cuda")]
4924 Backend::Cuda => crate::gpu_cuda::h3_release(),
4925 #[cfg(target_os = "macos")]
4926 Backend::Metal => crate::gpu_metal::h3::release(),
4927 #[allow(unreachable_patterns)]
4928 _ => {}
4929 }
4930}
4931
4932pub fn h3_prof_report() -> Option<String> {
4934 match backend() {
4935 #[cfg(feature = "cuda")]
4936 Backend::Cuda => crate::gpu_cuda::h3_prof_report(),
4937 #[allow(unreachable_patterns)]
4938 _ => None,
4939 }
4940}
4941
4942pub fn layer_taps_arm(layers: &[usize]) {
4947 match backend() {
4948 #[cfg(feature = "gpu")]
4949 Backend::Wgpu => crate::gpu_wgpu::layer_taps_arm(layers),
4950 #[cfg(feature = "cuda")]
4951 Backend::Cuda => crate::gpu_cuda::layer_taps_arm(layers),
4952 #[allow(unreachable_patterns)]
4953 _ => {
4954 let _ = layers;
4955 }
4956 }
4957}
4958
4959pub fn layer_taps_take(out: &mut Vec<f32>) -> bool {
4962 match backend() {
4963 #[cfg(feature = "gpu")]
4964 Backend::Wgpu => crate::gpu_wgpu::layer_taps_take(out),
4965 #[cfg(feature = "cuda")]
4966 Backend::Cuda => crate::gpu_cuda::layer_taps_take(out),
4967 #[allow(unreachable_patterns)]
4968 _ => {
4969 let _ = out;
4970 false
4971 }
4972 }
4973}
4974
4975pub fn q4tp_rows8_many(
4981 model: &Arc<CmfModel>,
4982 jobs: &[(usize, usize, usize, usize)],
4983 xs: &[f32],
4984 b: usize,
4985 outs: &mut [&mut [f32]],
4986) -> bool {
4987 match backend() {
4988 #[cfg(target_os = "macos")]
4989 Backend::Metal => {
4990 let takes: Vec<usize> = jobs.iter().map(|j| j.3).collect();
4991 let jobs: Vec<crate::gpu_metal::MmJob> = jobs
4992 .iter()
4993 .map(|&(idx, rows, cols, _)| crate::gpu_metal::MmJob { idx, rows, cols })
4994 .collect();
4995 crate::gpu_metal::q4tp_n8_many(model, &jobs, Some(&takes), xs, b, outs)
4996 }
4997 #[allow(unreachable_patterns)]
4998 _ => {
4999 let _ = (model, jobs, xs, b, outs);
5000 false
5001 }
5002 }
5003}
5004
5005#[allow(clippy::too_many_arguments)]
5007pub fn q4tp_rows8_ffn(
5008 model: &Arc<CmfModel>,
5009 w1: usize,
5010 w3: usize,
5011 w2: usize,
5012 xs: &[f32],
5013 b: usize,
5014 hidden: usize,
5015 inter: usize,
5016 out: &mut [f32],
5017) -> bool {
5018 match backend() {
5019 #[cfg(target_os = "macos")]
5020 Backend::Metal => crate::gpu_metal::q4tp_n8_ffn(model, w1, w3, w2, xs, b, hidden, inter, out),
5021 #[allow(unreachable_patterns)]
5022 _ => {
5023 let _ = (model, w1, w3, w2, xs, b, hidden, inter, out);
5024 false
5025 }
5026 }
5027}
5028
5029pub fn q4tp_matmat_many(
5033 model: &Arc<CmfModel>,
5034 jobs: &[(usize, usize, usize)],
5035 xs: &[f32],
5036 b: usize,
5037 outs: &mut [&mut [f32]],
5038) -> bool {
5039 match backend() {
5040 #[cfg(target_os = "macos")]
5041 Backend::Metal => {
5042 let jobs: Vec<crate::gpu_metal::MmJob> = jobs
5043 .iter()
5044 .map(|&(idx, rows, cols)| crate::gpu_metal::MmJob { idx, rows, cols })
5045 .collect();
5046 crate::gpu_metal::q4tp_matmat_many(model, &jobs, xs, b, outs)
5047 }
5048 #[cfg(feature = "gpu")]
5049 Backend::Wgpu => crate::gpu_wgpu::q4tp_matmat_many(model, jobs, xs, b, outs),
5050 #[allow(unreachable_patterns)]
5051 _ => {
5052 let _ = (model, jobs, xs, b, outs);
5053 false
5054 }
5055 }
5056}
5057
5058pub fn q2tp_matmat(
5061 model: &Arc<CmfModel>,
5062 idx: usize,
5063 xs: &[f32],
5064 b: usize,
5065 rows: usize,
5066 cols: usize,
5067 out: &mut [f32],
5068) -> bool {
5069 match backend() {
5070 #[cfg(target_os = "macos")]
5071 Backend::Metal => crate::gpu_metal::q2tp_matmat(model, idx, xs, b, rows, cols, out),
5072 #[cfg(feature = "gpu")]
5073 Backend::Wgpu => crate::gpu_wgpu::q2tp_matmat(model, idx, xs, b, rows, cols, out),
5074 #[allow(unreachable_patterns)]
5075 _ => false,
5076 }
5077}
5078
5079pub fn q2tp_affine_matmat(
5082 model: &Arc<CmfModel>,
5083 idx: usize,
5084 xs: &[f32],
5085 b: usize,
5086 rows: usize,
5087 cols: usize,
5088 out: &mut [f32],
5089) -> bool {
5090 match backend() {
5091 #[cfg(target_os = "macos")]
5092 Backend::Metal => crate::gpu_metal::q2tp_affine_matmat(model, idx, xs, b, rows, cols, out),
5093 #[cfg(feature = "gpu")]
5094 Backend::Wgpu => crate::gpu_wgpu::q2tp_affine_matmat(model, idx, xs, b, rows, cols, out),
5095 #[allow(unreachable_patterns)]
5096 _ => false,
5097 }
5098}
5099
5100pub fn blr_matmat(t: &crate::blr::BlrTensor, xs: &[f32], b: usize, out: &mut [f32]) -> bool {
5105 match backend() {
5106 #[cfg(feature = "gpu")]
5107 Backend::Wgpu => crate::gpu_wgpu::blr_matmat(t, xs, b, out),
5108 #[allow(unreachable_patterns)]
5109 _ => {
5110 let _ = (t, xs, b, out);
5111 false
5112 }
5113 }
5114}
5115
5116pub fn mxfp4_matvec(
5120 model: &Arc<CmfModel>,
5121 idx: usize,
5122 xs: &[f32],
5123 rows: usize,
5124 cols: usize,
5125 out: &mut [f32],
5126) -> bool {
5127 mxfp4_matmat(model, idx, xs, 1, rows, cols, out)
5128}
5129
5130pub fn mxfp4_matmat(
5133 model: &Arc<CmfModel>,
5134 idx: usize,
5135 xs: &[f32],
5136 b: usize,
5137 rows: usize,
5138 cols: usize,
5139 out: &mut [f32],
5140) -> bool {
5141 match backend() {
5142 #[cfg(feature = "gpu")]
5143 Backend::Wgpu => crate::gpu_wgpu::mxfp4::matmat(model, idx, xs, b, rows, cols, out),
5144 #[allow(unreachable_patterns)]
5145 _ => {
5146 let _ = (model, idx, xs, b, rows, cols, out);
5147 false
5148 }
5149 }
5150}
5151
5152pub fn q2tp_matvec(
5154 model: &Arc<CmfModel>,
5155 idx: usize,
5156 xs: &[f32],
5157 rows: usize,
5158 cols: usize,
5159 out: &mut [f32],
5160) -> bool {
5161 match backend() {
5162 #[cfg(target_os = "macos")]
5163 Backend::Metal => crate::gpu_metal::q2tp_matvec(model, idx, xs, rows, cols, out),
5164 #[cfg(feature = "gpu")]
5165 Backend::Wgpu => crate::gpu_wgpu::q2tp_matvec(model, idx, xs, rows, cols, out),
5166 #[allow(unreachable_patterns)]
5167 _ => false,
5168 }
5169}
5170
5171pub fn q2tp_affine_matvec(
5175 model: &Arc<CmfModel>,
5176 idx: usize,
5177 xs: &[f32],
5178 rows: usize,
5179 cols: usize,
5180 out: &mut [f32],
5181) -> bool {
5182 match backend() {
5183 #[cfg(target_os = "macos")]
5184 Backend::Metal => crate::gpu_metal::q2tp_affine_matvec(model, idx, xs, rows, cols, out),
5185 #[cfg(feature = "gpu")]
5186 Backend::Wgpu => crate::gpu_wgpu::q2tp_affine_matvec(model, idx, xs, rows, cols, out),
5187 #[allow(unreachable_patterns)]
5188 _ => false,
5189 }
5190}
5191
5192pub fn q4tp_matvec(
5197 model: &Arc<CmfModel>,
5198 idx: usize,
5199 xs: &[f32],
5200 rows: usize,
5201 cols: usize,
5202 out: &mut [f32],
5203) -> bool {
5204 match backend() {
5205 #[cfg(target_os = "macos")]
5206 Backend::Metal => crate::gpu_metal::q4tp_matvec_for_test(model, idx, xs, rows, cols, out),
5207 #[cfg(feature = "gpu")]
5208 Backend::Wgpu => crate::gpu_wgpu::q4tp_matvec(model, idx, xs, rows, cols, out),
5209 #[allow(unreachable_patterns)]
5210 _ => false,
5211 }
5212}
5213
5214pub fn q4t_matvec(
5220 model: &Arc<CmfModel>,
5221 idx: usize,
5222 xs: &[f32],
5223 rows: usize,
5224 cols: usize,
5225 out: &mut [f32],
5226) -> bool {
5227 match backend() {
5228 #[cfg(target_os = "macos")]
5229 Backend::Metal => crate::gpu_metal::q4t_matvec_for_test(model, idx, xs, rows, cols, out),
5230 #[allow(unreachable_patterns)]
5231 _ => false,
5232 }
5233}
5234
5235pub fn q4t_matmat(
5236 model: &Arc<CmfModel>,
5237 idx: usize,
5238 xs: &[f32],
5239 b: usize,
5240 rows: usize,
5241 cols: usize,
5242 out: &mut [f32],
5243) -> bool {
5244 match backend() {
5245 #[cfg(target_os = "macos")]
5246 Backend::Metal => crate::gpu_metal::q4t_matmat(model, idx, xs, b, rows, cols, out),
5247 #[cfg(feature = "gpu")]
5248 Backend::Wgpu => crate::gpu_wgpu::q4t_matmat(model, idx, xs, b, rows, cols, out),
5249 #[allow(unreachable_patterns)]
5250 _ => false,
5251 }
5252}
5253
5254pub type SetW = (usize, usize, usize);
5261
5262pub enum SetMixer<'a> {
5263 Conv {
5264 in_proj: SetW,
5265 out_proj: SetW,
5266 taps: &'a [f32],
5268 kernel: usize,
5269 },
5270 Attn {
5271 wq: SetW,
5272 wk: SetW,
5273 wv: SetW,
5274 wo: SetW,
5275 q_norm: Option<&'a [f32]>,
5276 k_norm: Option<&'a [f32]>,
5277 head_gate: Option<&'a [f32]>,
5279 inv_freq: &'a [f32],
5280 rd: usize,
5281 nkv: usize,
5282 window: Option<usize>,
5284 },
5285}
5286
5287pub struct SetLayer<'a> {
5288 pub input_norm: &'a [f32],
5289 pub post_norm: &'a [f32],
5290 pub mixer: SetMixer<'a>,
5291 pub attn_out_norm: Option<&'a [f32]>,
5292 pub ffn_out_norm: Option<&'a [f32]>,
5293 pub gate: SetW,
5295 pub up: SetW,
5296 pub down: SetW,
5297}
5298
5299pub struct SetDims {
5300 pub hidden: usize,
5301 pub nh: usize,
5302 pub hd: usize,
5303 pub eps: f32,
5304 pub gemma: bool,
5305 pub scale: f32,
5306}
5307
5308pub struct SetHead<'a> {
5311 pub w: SetW,
5312 pub rows: &'a [usize],
5313}
5314
5315#[allow(clippy::too_many_arguments, unused_variables)]
5320pub fn lfm_set_forward(
5321 model: &Arc<CmfModel>,
5322 layers: &[SetLayer],
5323 dims: &SetDims,
5324 final_norm: &[f32],
5325 rows: &[f32],
5326 t: usize,
5327 state: usize,
5328 group: &[i32],
5329 head: Option<&SetHead>,
5330) -> Option<(Vec<f32>, Vec<f32>)> {
5331 #[allow(unreachable_patterns)]
5332 match backend() {
5333 #[cfg(target_os = "macos")]
5334 Backend::Metal => crate::gpu_metal::lfm_set::forward(
5335 model, layers, dims, final_norm, rows, t, state, group, head,
5336 ),
5337 #[cfg(feature = "gpu")]
5338 Backend::Wgpu => crate::gpu_wgpu::lfm_set::forward(
5339 model, layers, dims, final_norm, rows, t, state, group, head,
5340 ),
5341 _ => None,
5342 }
5343}
5344
5345#[cfg(target_os = "macos")]
5347pub use crate::gpu_metal::{
5348 AttnDeviceParams, AttnGpuLayer, GdnGpuCfg, GdnGpuLayer, GpuFlintMoe, GpuMoe, GraphDims,
5349 MetalFfn, O1AttnParams, SconvGpuLayer, TokenGraph, kv_mirror_drop, kv_mirror_read_last,
5350 kv_mirror_take_imp,
5351};
5352
5353#[cfg(target_os = "macos")]
5355pub fn gdn_block(
5356 model: &Arc<CmfModel>,
5357 layers: &[GdnGpuLayer],
5358 states: &mut [&mut [f32]],
5359 cfg: &GdnGpuCfg,
5360 h: &mut [f32],
5361) -> bool {
5362 match backend() {
5363 Backend::Metal => crate::gpu_metal::gdn_block(model, layers, states, cfg, h),
5364 _ => false,
5365 }
5366}
5367
5368#[allow(unused_variables)]
5370pub fn moe_block(model: &Arc<CmfModel>, jobs: &[MoeJob], out: &mut [f32]) -> bool {
5371 match backend() {
5372 #[cfg(target_os = "macos")]
5377 Backend::Metal => crate::gpu_metal::moe_block(model, jobs, out),
5378 #[cfg(feature = "gpu")]
5379 Backend::Wgpu => crate::gpu_wgpu::moe_block(model, jobs, out),
5380 Backend::None => false,
5381 #[cfg(feature = "cuda")]
5382 Backend::Cuda => false,
5383 }
5384}
5385
5386#[allow(unused_variables)]
5388pub fn matvec_batch(model: &Arc<CmfModel>, jobs: &[BatchJob], out: &mut [&mut [f32]]) -> bool {
5389 match backend() {
5390 #[cfg(target_os = "macos")]
5391 Backend::Metal => crate::gpu_metal::matvec_batch(model, jobs, out),
5392 #[cfg(feature = "gpu")]
5393 Backend::Wgpu => crate::gpu_wgpu::matvec_batch(model, jobs, out),
5394 Backend::None => false,
5395 #[cfg(feature = "cuda")]
5396 Backend::Cuda => false,
5397 }
5398}
5399
5400static GRAPH_RACE_STATE: AtomicU8 = AtomicU8::new(0); static GRAPH_RACE_FLIP: AtomicU32 = AtomicU32::new(0);
5416static GRAPH_RACE_ARM_GRAPH: AtomicU8 = AtomicU8::new(0); static GRAPH_RACE_TOK: AtomicU32 = AtomicU32::new(0); static GRAPH_NS: [AtomicU64; 2] = [AtomicU64::new(0), AtomicU64::new(0)]; static GRAPH_N: [AtomicU32; 2] = [AtomicU32::new(0), AtomicU32::new(0)];
5420
5421const GRAPH_RACE_SAMPLES: u32 = 4;
5423
5424pub fn graph_race_begin_generation() {
5443 #[cfg(feature = "gpu")]
5448 {
5449 static FLUSHED: std::sync::Once = std::sync::Once::new();
5461 static FIRST: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
5462 if FIRST.swap(false, Ordering::Relaxed) {
5463 } else {
5465 FLUSHED.call_once(crate::gpu_wgpu::pipeline_cache_flush);
5466 }
5467 }
5468 GRAPH_RACE_TOK.store(0, Ordering::Relaxed);
5469 if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
5470 return;
5471 }
5472 let (gn, cn) = (
5473 GRAPH_N[1].load(Ordering::Relaxed),
5474 GRAPH_N[0].load(Ordering::Relaxed),
5475 );
5476 if gn >= GRAPH_RACE_SAMPLES && cn >= GRAPH_RACE_SAMPLES {
5477 let g_avg = GRAPH_NS[1].load(Ordering::Relaxed) / gn as u64;
5478 let c_avg = GRAPH_NS[0].load(Ordering::Relaxed) / cn as u64;
5479 let verdict = if g_avg < c_avg { 1 } else { 2 };
5480 GRAPH_RACE_STATE.store(verdict, Ordering::Relaxed);
5481 tracing::info!(
5482 "wgpu graph race: graph {:.2} ms/tok vs normal {:.2} ms/tok -> {}",
5483 g_avg as f64 / 1e6,
5484 c_avg as f64 / 1e6,
5485 if verdict == 1 { "graph" } else { "normal path" }
5486 );
5487 return;
5488 }
5489 let flip = GRAPH_RACE_FLIP.fetch_add(1, Ordering::Relaxed);
5490 GRAPH_RACE_ARM_GRAPH.store((flip % 2 == 1) as u8, Ordering::Relaxed);
5491}
5492
5493pub fn graph_race_use_graph(trusted: bool) -> bool {
5497 if trusted {
5498 return true;
5499 }
5500 match GRAPH_RACE_STATE.load(Ordering::Relaxed) {
5501 1 => true,
5502 2 => false,
5503 _ => GRAPH_RACE_ARM_GRAPH.load(Ordering::Relaxed) == 1,
5504 }
5505}
5506
5507pub fn graph_race_first_token_hopeless(dur: std::time::Duration) -> bool {
5512 if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
5513 return false;
5514 }
5515 let first = GRAPH_RACE_TOK.load(Ordering::Relaxed) == 0;
5516 let cn = GRAPH_N[0].load(Ordering::Relaxed);
5517 if !first || cn == 0 {
5518 return false;
5519 }
5520 let c_avg = GRAPH_NS[0].load(Ordering::Relaxed) / cn as u64;
5521 let ns = dur.as_nanos() as u64;
5522 if ns > 1_000_000_000 && ns > 4 * c_avg {
5523 GRAPH_RACE_STATE.store(2, Ordering::Relaxed);
5524 tracing::info!(
5525 "wgpu graph race: first graph token {:.0} ms vs normal {:.2} ms/tok — hopeless, normal path wins",
5526 ns as f64 / 1e6,
5527 c_avg as f64 / 1e6
5528 );
5529 return true;
5530 }
5531 false
5532}
5533
5534pub fn graph_race_record(used_graph: bool, dur: std::time::Duration) {
5538 if GRAPH_RACE_STATE.load(Ordering::Relaxed) != 0 {
5539 return;
5540 }
5541 let tok = GRAPH_RACE_TOK.fetch_add(1, Ordering::Relaxed);
5542 if tok == 0 {
5543 return;
5544 }
5545 let i = used_graph as usize;
5546 GRAPH_NS[i].fetch_add(dur.as_nanos() as u64, Ordering::Relaxed);
5547 GRAPH_N[i].fetch_add(1, Ordering::Relaxed);
5548}
5549
5550pub(crate) fn fp_bytes(data: &[u8]) -> u64 {
5560 #[inline]
5561 fn fnv(mut h: u64, bytes: &[u8]) -> u64 {
5562 let (chunks, tail) = bytes.split_at(bytes.len() & !7);
5563 for c in chunks.chunks_exact(8) {
5564 h ^= u64::from_le_bytes(c.try_into().unwrap());
5565 h = h.wrapping_mul(0x100_0000_01b3);
5566 }
5567 for &b in tail {
5568 h ^= b as u64;
5569 h = h.wrapping_mul(0x100_0000_01b3);
5570 }
5571 h
5572 }
5573 let mut h = 0xcbf2_9ce4_8422_2325u64 ^ (data.len() as u64);
5574 if data.len() <= 4096 {
5575 return fnv(h, data);
5576 }
5577 let step = (data.len() - 64) / 63;
5578 for i in 0..64 {
5579 h = fnv(h, &data[i * step..i * step + 64]);
5580 }
5581 h
5582}
5583
5584pub(crate) fn fp_f32(data: &[f32]) -> u64 {
5587 let bytes = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
5588 fp_bytes(bytes)
5589}
5590
5591#[cfg(test)]
5592mod fp_tests {
5593 use super::fp_bytes;
5594
5595 #[test]
5600 fn fp_bytes_sees_a_change_anywhere_in_a_sampled_slice() {
5601 let n = 1 << 20; let base: Vec<u8> = (0..n).map(|i| (i * 31 + 7) as u8).collect();
5603 let h0 = fp_bytes(&base);
5604 assert_eq!(h0, fp_bytes(&base), "fingerprint must be deterministic");
5605 let mut dense = base.clone();
5608 for b in dense.iter_mut() {
5609 *b = b.wrapping_add(1);
5610 }
5611 assert_ne!(
5612 h0,
5613 fp_bytes(&dense),
5614 "a fully different tensor slipped through"
5615 );
5616 assert_ne!(h0, fp_bytes(&base[..n - 64]));
5619 let mut small = vec![3u8; 4096];
5622 let hs = fp_bytes(&small);
5623 small[2048] ^= 1;
5624 assert_ne!(hs, fp_bytes(&small), "full hash missed a one-byte change");
5625 for n in [4097usize, 5000, 64 * 64, 1 << 16] {
5627 let v = vec![9u8; n];
5628 let _ = fp_bytes(&v); }
5630 }
5631}
5632
5633pub fn bake_release() {
5637 #[cfg(feature = "gpu")]
5638 crate::gpu_wgpu::bake_release();
5639}
5640
5641pub fn bake_precision_strict(on: bool) {
5645 #[cfg(feature = "gpu")]
5646 crate::gpu_wgpu::bake_precision_strict(on);
5647 #[cfg(not(feature = "gpu"))]
5648 let _ = on;
5649}
5650
5651pub fn hostprof_encode_done(t0: std::time::Instant) {
5657 use std::sync::atomic::{AtomicU64, Ordering};
5658 static ENC: AtomicU64 = AtomicU64::new(0);
5659 static N: AtomicU64 = AtomicU64::new(0);
5660 if std::env::var("CMF_GRAPH_HOSTPROF").as_deref() != Ok("1") {
5661 return;
5662 }
5663 ENC.fetch_add(t0.elapsed().as_nanos() as u64, Ordering::Relaxed);
5664 let n = N.fetch_add(1, Ordering::Relaxed) + 1;
5665 if n % 100 == 0 {
5666 eprintln!(
5667 "hostprof: encode {:.2} ms/token over {n} tokens",
5668 ENC.load(Ordering::Relaxed) as f64 / n as f64 / 1e6
5669 );
5670 }
5671}
5672
5673pub fn hostprof_total(t0: std::time::Instant) {
5674 use std::sync::atomic::{AtomicU64, Ordering};
5675 static TOT: AtomicU64 = AtomicU64::new(0);
5676 static N: AtomicU64 = AtomicU64::new(0);
5677 if std::env::var("CMF_GRAPH_HOSTPROF").as_deref() != Ok("1") {
5678 return;
5679 }
5680 TOT.fetch_add(t0.elapsed().as_nanos() as u64, Ordering::Relaxed);
5681 let n = N.fetch_add(1, Ordering::Relaxed) + 1;
5682 if n % 100 == 0 {
5683 eprintln!(
5684 "hostprof: total {:.2} ms/token over {n} tokens",
5685 TOT.load(Ordering::Relaxed) as f64 / n as f64 / 1e6
5686 );
5687 }
5688}
5689
5690pub fn stageprof(stage: u32, dt: std::time::Duration) {
5694 use std::sync::atomic::{AtomicU64, Ordering};
5695 static NS: [AtomicU64; 4] = [
5696 AtomicU64::new(0),
5697 AtomicU64::new(0),
5698 AtomicU64::new(0),
5699 AtomicU64::new(0),
5700 ];
5701 static N: AtomicU64 = AtomicU64::new(0);
5702 if std::env::var("CMF_GRAPH_HOSTPROF").as_deref() != Ok("1") {
5703 return;
5704 }
5705 NS[stage as usize % 4].fetch_add(dt.as_nanos() as u64, Ordering::Relaxed);
5706 if stage == 1 {
5707 let n = N.fetch_add(1, Ordering::Relaxed) + 1;
5708 if n % 200 == 0 {
5709 eprintln!(
5710 "stageprof: planning {:.2} ms/tok | gdn-item {:.2} ms/tok | attn-item {:.2} ms/tok ({n} tok)",
5711 NS[1].load(Ordering::Relaxed) as f64 / n as f64 / 1e6,
5712 NS[2].load(Ordering::Relaxed) as f64 / n as f64 / 1e6,
5713 NS[3].load(Ordering::Relaxed) as f64 / n as f64 / 1e6
5714 );
5715 }
5716 }
5717}
5718
5719pub fn weight_bytes_dispatched() -> u64 {
5722 let mut total = 0u64;
5723 #[cfg(target_os = "macos")]
5724 {
5725 total += crate::gpu_metal::WEIGHT_BYTES.load(std::sync::atomic::Ordering::Relaxed);
5726 }
5727 #[cfg(feature = "gpu")]
5728 {
5729 total += crate::gpu_wgpu::WEIGHT_BYTES.load(std::sync::atomic::Ordering::Relaxed);
5730 }
5731 total
5732}
5733
5734pub fn weight_bytes_by() -> [u64; 6] {
5737 #[cfg(target_os = "macos")]
5738 {
5739 let mut o = [0u64; 6];
5740 for (i, a) in crate::gpu_metal::WEIGHT_BYTES_BY.iter().enumerate() {
5741 o[i] = a.load(std::sync::atomic::Ordering::Relaxed);
5742 }
5743 return o;
5744 }
5745 #[allow(unreachable_code)]
5746 [0; 6]
5747}
5748
5749#[cfg(test)]
5750mod f16_guard_tests {
5751 use super::*;
5752
5753 #[test]
5756 fn abs_max_or_inf_is_the_magnitude_maximum() {
5757 let mut xs: Vec<f32> = (0..37).map(|i| (i as f32 - 18.0) * 0.5).collect();
5758 assert_eq!(abs_max_or_inf(&xs), 9.0);
5759 xs[36] = -70000.0; assert_eq!(abs_max_or_inf(&xs), 70000.0);
5761 xs[3] = f32::NAN; assert_eq!(abs_max_or_inf(&xs), f32::INFINITY);
5763 xs[3] = 0.0;
5764 xs[35] = f32::NEG_INFINITY;
5765 assert_eq!(abs_max_or_inf(&xs), f32::INFINITY);
5766 assert_eq!(abs_max_or_inf(&[]), 0.0);
5767 assert_eq!(abs_max_or_inf(&[-0.0, -1e-30]), 1e-30);
5768 }
5769}
5770
5771#[cfg(test)]
5772mod probe_warmup_tests {
5773 use super::*;
5774 use std::time::Duration;
5775
5776 fn ms(v: f64) -> Duration {
5777 Duration::from_nanos((v * 1e6) as u64)
5778 }
5779
5780 #[test]
5785 fn one_cold_first_sample_does_not_lose_the_class() {
5786 let p = Probe::new();
5787 probe_record_into(&p, "gemm-nt", None, true, ms(117.01));
5789 probe_record_into(&p, "gemm-nt", None, true, ms(1.1));
5790 probe_record_into(&p, "gemm-nt", None, true, ms(1.0));
5791 probe_record_into(&p, "gemm-nt", None, false, ms(3.19));
5792 probe_record_into(&p, "gemm-nt", None, false, ms(3.20));
5793 assert_eq!(
5794 p.state.load(Ordering::Relaxed),
5795 1,
5796 "the device is 3x faster once warm and must win"
5797 );
5798 }
5799
5800 #[test]
5804 fn the_warmup_is_spent_once_and_never_underflows() {
5805 let p = Probe::new();
5806 for _ in 0..8 {
5807 probe_record_into(&p, "matmat", None, true, ms(10.0));
5808 }
5809 assert_eq!(p.gpu_burn.load(Ordering::Relaxed), 0, "spent, not wrapped");
5810 assert_eq!(
5811 p.gpu_n.load(Ordering::Relaxed),
5812 7,
5813 "one sample burned, the rest counted"
5814 );
5815 }
5816
5817 #[test]
5823 fn a_class_whose_device_always_declines_settles_on_the_host() {
5824 let _probe_guard = probe_test_guard();
5825 let c = OpClass::MatmatWide;
5829 let p = &PROBES[c as usize];
5830 p.state.store(0, Ordering::Relaxed);
5831 p.declines.store(0, Ordering::Relaxed);
5832 for _ in 0..(PROBE_DECLINE_LIMIT - 1) {
5833 probe_note_decline(c);
5834 }
5835 assert_eq!(
5836 p.state.load(Ordering::Relaxed),
5837 0,
5838 "one short of the limit is still a question, not an answer"
5839 );
5840 probe_note_decline(c);
5841 assert_eq!(p.state.load(Ordering::Relaxed), 2, "settled on the host");
5842 assert!(matches!(probe_arm(c), ProbeArm::Cpu));
5843 p.state.store(0, Ordering::Relaxed);
5844 p.declines.store(0, Ordering::Relaxed);
5845 }
5846
5847 #[test]
5850 fn a_slow_device_still_loses_after_the_warmup() {
5851 let p = Probe::new();
5852 for _ in 0..4 {
5853 probe_record_into(&p, "matvec", None, true, ms(40.0));
5854 }
5855 for _ in 0..4 {
5856 probe_record_into(&p, "matvec", None, false, ms(2.0));
5857 }
5858 assert_eq!(p.state.load(Ordering::Relaxed), 2, "host wins on merit");
5859 }
5860}
5861
5862pub(crate) struct ImageStageGuard {
5865 #[cfg(target_os = "macos")]
5866 metal: Option<crate::gpu_metal::ImageStageGuard>,
5867 #[cfg(feature = "gpu")]
5868 wgpu: crate::gpu_wgpu::ImageStageGuard,
5869}
5870
5871pub(crate) fn image_stage_scope() -> ImageStageGuard {
5872 ImageStageGuard {
5873 #[cfg(target_os = "macos")]
5874 metal: if matches!(backend(), Backend::Metal) {
5875 Some(crate::gpu_metal::image_stage_scope())
5876 } else {
5877 None
5878 },
5879 #[cfg(feature = "gpu")]
5880 wgpu: crate::gpu_wgpu::image_stage_scope(),
5881 }
5882}
5883
5884impl ImageStageGuard {
5885 pub(crate) fn track_model(&mut self, uid: u64) {
5886 #[cfg(target_os = "macos")]
5887 if let Some(metal) = &mut self.metal {
5888 metal.track_model(uid);
5889 }
5890 #[cfg(feature = "gpu")]
5891 self.wgpu.track_model(uid);
5892 #[cfg(not(target_os = "macos"))]
5893 let _ = uid;
5894 }
5895}
5896
5897#[derive(Clone, Copy, Debug, PartialEq)]
5917pub struct Qi21Geom {
5918 pub hidden: usize,
5919 pub nh: usize,
5920 pub hd: usize,
5921 pub inter: usize,
5922 pub in_ch: usize,
5923 pub eps: f32,
5924}
5925
5926pub struct Qi21BlockRef<'a> {
5929 pub w: [usize; 7],
5930 pub norm_q: &'a [f32],
5931 pub norm_k: &'a [f32],
5932}
5933
5934pub struct Qi21PrefillArgs<'a> {
5936 pub model: &'a Arc<CmfModel>,
5937 pub geom: Qi21Geom,
5938 pub blocks: &'a [Qi21BlockRef<'a>],
5939 pub img_in: &'a [f32],
5941 pub proj_out: &'a [f32],
5942 pub key: u64,
5943 pub x: &'a [f32],
5945 pub lp: usize,
5946 pub rope_p: (&'a [f32], &'a [f32]),
5948 pub rope_t: (&'a [f32], &'a [f32]),
5949 pub vis: &'a [u32],
5951 pub mods0: &'a [f32],
5953 pub n: usize,
5955}
5956
5957#[allow(unused_variables)]
5959pub fn qi21_prefill(a: &Qi21PrefillArgs) -> bool {
5960 match backend() {
5961 #[cfg(target_os = "macos")]
5962 Backend::Metal => crate::gpu_metal::qi21::prefill(a),
5963 #[cfg(feature = "gpu")]
5964 Backend::Wgpu => crate::gpu_wgpu::qi21::prefill(a),
5965 #[allow(unreachable_patterns)]
5966 _ => false,
5967 }
5968}
5969
5970#[allow(unused_variables)]
5972pub fn qi21_step(key: u64, xtok: &[f32], mods: &[f32], fs: &[f32], out: &mut [f32]) -> bool {
5973 match backend() {
5974 #[cfg(target_os = "macos")]
5975 Backend::Metal => crate::gpu_metal::qi21::step(key, xtok, mods, fs, out),
5976 #[cfg(feature = "gpu")]
5977 Backend::Wgpu => crate::gpu_wgpu::qi21::step(key, xtok, mods, fs, out),
5978 #[allow(unreachable_patterns)]
5979 _ => false,
5980 }
5981}
5982
5983#[allow(unused_variables)]
5986pub fn qi21_release_key(key: u64) {
5987 #[cfg(target_os = "macos")]
5988 if matches!(backend(), Backend::Metal) {
5989 crate::gpu_metal::qi21::release_key(key);
5990 }
5991 #[cfg(feature = "gpu")]
5992 crate::gpu_wgpu::qi21::release_key(key);
5993}
5994
5995pub fn qi21_release() {
5998 #[cfg(target_os = "macos")]
5999 if matches!(backend(), Backend::Metal) {
6000 crate::gpu_metal::qi21::release();
6001 }
6002 #[cfg(feature = "gpu")]
6003 {
6004 crate::gpu_wgpu::qi21::release();
6005 crate::gpu_wgpu::qi21_vae::release();
6006 }
6007}
6008
6009#[derive(Clone, Copy)]
6012pub struct Qi21VaeConvRef<'a> {
6013 pub w: &'a [f32],
6014 pub b: &'a [f32],
6015 pub ci: usize,
6016 pub co: usize,
6017 pub k: usize,
6018}
6019
6020pub struct Qi21VaeResRef<'a> {
6023 pub g1: &'a [f32],
6024 pub c1: Qi21VaeConvRef<'a>,
6025 pub g2: &'a [f32],
6026 pub c2: Qi21VaeConvRef<'a>,
6027 pub shortcut: Option<Qi21VaeConvRef<'a>>,
6028}
6029
6030pub struct Qi21VaeUpRef<'a> {
6034 pub resnets: Vec<Qi21VaeResRef<'a>>,
6035 pub up: Option<(Qi21VaeConvRef<'a>, usize)>,
6036 pub in_dim: usize,
6037 pub out_dim: usize,
6038}
6039
6040pub struct Qi21VaeDecodeArgs<'a> {
6043 pub key: u64,
6045 pub post_quant: Qi21VaeConvRef<'a>,
6046 pub conv_in: Qi21VaeConvRef<'a>,
6047 pub mid_res: [Qi21VaeResRef<'a>; 2],
6048 pub attn_gamma: &'a [f32],
6051 pub attn_qkv: Qi21VaeConvRef<'a>,
6052 pub attn_proj: Qi21VaeConvRef<'a>,
6053 pub ups: Vec<Qi21VaeUpRef<'a>>,
6054 pub norm_out: &'a [f32],
6055 pub conv_out: Qi21VaeConvRef<'a>,
6056}
6057
6058#[allow(unused_variables)]
6062pub fn qi21_vae_decode(a: &Qi21VaeDecodeArgs, z: &[f32], h: usize, w: usize, out: &mut [f32]) -> bool {
6063 match backend() {
6064 #[cfg(feature = "gpu")]
6065 Backend::Wgpu => crate::gpu_wgpu::qi21_vae::decode(a, z, h, w, out),
6066 #[allow(unreachable_patterns)]
6067 _ => false,
6068 }
6069}
6070
6071#[derive(Clone, Copy)]
6079pub struct ZBlockRef<'a> {
6080 pub wq: usize,
6082 pub wk: usize,
6083 pub wv: usize,
6084 pub wo: usize,
6085 pub w1: usize,
6088 pub w3: usize,
6089 pub w2: usize,
6090 pub norm1: &'a [f32],
6092 pub norm2: &'a [f32],
6093 pub ffn_norm1: &'a [f32],
6095 pub ffn_norm2: &'a [f32],
6096 pub norm_q: &'a [f32],
6098 pub norm_k: &'a [f32],
6099}
6100
6101#[derive(Clone, Copy, Debug, PartialEq)]
6105pub struct ZGeom {
6106 pub hidden: usize,
6107 pub nh: usize,
6108 pub hd: usize,
6109 pub inter: usize,
6110 pub eps: f32,
6111 pub final_eps: f32,
6112 pub patch_dim: usize,
6113}
6114
6115pub struct ZPrepareArgs<'a> {
6119 pub model: &'a Arc<CmfModel>,
6120 pub geom: ZGeom,
6121 pub key: u64,
6124 pub n_img: usize,
6127 pub n_img_p: usize,
6128 pub n_cap_p: usize,
6129 pub grid: (usize, usize),
6132 pub cap: &'a [f32],
6134 pub rope_img: (&'a [f32], &'a [f32]),
6137 pub rope_joint: (&'a [f32], &'a [f32]),
6139 pub x_emb_w: &'a [f32],
6142 pub x_emb_b: &'a [f32],
6143 pub x_pad: &'a [f32],
6144 pub final_w: &'a [f32],
6146 pub final_b: &'a [f32],
6147 pub noise_refiner: &'a [ZBlockRef<'a>],
6149 pub layers: &'a [ZBlockRef<'a>],
6150 pub mods_all: Option<&'a [f32]>,
6156 pub final_scale_all: Option<&'a [f32]>,
6157 pub neg: Option<ZNegArgs<'a>>,
6163}
6164
6165pub struct ZNegArgs<'a> {
6169 pub cap: &'a [f32],
6171 pub n_cap_p: usize,
6172 pub rope_img: (&'a [f32], &'a [f32]),
6174 pub rope_joint: (&'a [f32], &'a [f32]),
6176}
6177
6178pub struct ZStepArgs<'a> {
6180 pub key: u64,
6183 pub step: usize,
6186 pub x_tok: &'a [f32],
6190 pub mods: &'a [f32],
6194 pub final_scale: &'a [f32],
6196 pub out: &'a mut [f32],
6199 pub out_neg: Option<&'a mut [f32]>,
6202}
6203
6204#[allow(unused_variables)]
6208pub fn zimage_prepare(a: &ZPrepareArgs) -> bool {
6209 match backend() {
6210 #[cfg(target_os = "macos")]
6211 Backend::Metal => crate::gpu_metal::zimage::prepare(a),
6212 #[cfg(feature = "gpu")]
6213 Backend::Wgpu => crate::gpu_wgpu::zimage::prepare(a),
6214 #[allow(unreachable_patterns)]
6215 _ => false,
6216 }
6217}
6218
6219#[allow(unused_variables)]
6223pub fn zimage_step(a: &mut ZStepArgs) -> bool {
6224 match backend() {
6225 #[cfg(target_os = "macos")]
6226 Backend::Metal => crate::gpu_metal::zimage::step(a),
6227 #[cfg(feature = "gpu")]
6228 Backend::Wgpu => crate::gpu_wgpu::zimage::step(a),
6229 #[allow(unreachable_patterns)]
6230 _ => false,
6231 }
6232}
6233
6234#[allow(unused_variables)]
6239pub fn zimage_preload(
6240 model: &Arc<CmfModel>,
6241 geom: &ZGeom,
6242 noise_refiner: &[ZBlockRef],
6243 layers: &[ZBlockRef],
6244 context_refiner: &[ZBlockRef],
6245) -> bool {
6246 match backend() {
6247 #[cfg(target_os = "macos")]
6248 Backend::Metal => {
6249 crate::gpu_metal::zimage::preload(model, geom, noise_refiner, layers, context_refiner)
6250 }
6251 #[cfg(feature = "gpu")]
6252 Backend::Wgpu => crate::gpu_wgpu::zimage::preload(model, geom, noise_refiner, layers, context_refiner),
6253 #[allow(unreachable_patterns)]
6254 _ => false,
6255 }
6256}
6257
6258pub fn zimage_flush_pipelines() {
6262 #[cfg(feature = "gpu")]
6263 if matches!(backend(), Backend::Wgpu) {
6264 crate::gpu_wgpu::pipeline_cache_flush();
6265 }
6266}
6267
6268pub fn zimage_warmup() -> bool {
6273 match backend() {
6274 #[cfg(target_os = "macos")]
6275 Backend::Metal => crate::gpu_metal::zimage::warmup(),
6276 #[cfg(feature = "gpu")]
6277 Backend::Wgpu => crate::gpu_wgpu::zimage::warmup(),
6278 #[allow(unreachable_patterns)]
6279 _ => false,
6280 }
6281}
6282
6283#[allow(unused_variables)]
6287pub fn vae_prewarm(a: &crate::vae::VaeChainArgs) -> bool {
6288 match backend() {
6289 #[cfg(target_os = "macos")]
6290 Backend::Metal => crate::gpu_metal::zimage::vae_prewarm(a),
6291 #[cfg(feature = "gpu")]
6292 Backend::Wgpu => crate::gpu_wgpu::zimage::vae_prewarm(a),
6293 #[allow(unreachable_patterns)]
6294 _ => false,
6295 }
6296}
6297
6298pub fn zimage_release_dit() {
6301 #[cfg(target_os = "macos")]
6302 crate::gpu_metal::zimage::release_dit();
6303 #[cfg(feature = "gpu")]
6304 crate::gpu_wgpu::zimage::release_dit();
6305}
6306
6307pub fn zimage_release() {
6312 #[cfg(target_os = "macos")]
6313 crate::gpu_metal::zimage::release();
6314 #[cfg(feature = "gpu")]
6315 crate::gpu_wgpu::zimage::release();
6316}
6317
6318#[allow(unused_variables)]
6324pub fn zimage_refine_caption(
6325 model: &Arc<CmfModel>,
6326 geom: &ZGeom,
6327 blocks: &[ZBlockRef],
6328 rope_cap: (&[f32], &[f32]),
6329 cap: &mut [f32],
6330) -> bool {
6331 match backend() {
6332 #[cfg(target_os = "macos")]
6333 Backend::Metal => {
6334 crate::gpu_metal::zimage::refine_caption(model, geom, blocks, rope_cap, cap)
6335 }
6336 #[cfg(feature = "gpu")]
6337 Backend::Wgpu => {
6338 crate::gpu_wgpu::zimage::refine_caption(model, geom, blocks, rope_cap, cap)
6339 }
6340 #[allow(unreachable_patterns)]
6341 _ => false,
6342 }
6343}
6344
6345#[allow(unused_variables)]
6351pub fn vae_decode_chain(
6352 a: &crate::vae::VaeChainArgs,
6353 z: &[f32],
6354 h: usize,
6355 w: usize,
6356 out: &mut [f32],
6357) -> bool {
6358 match backend() {
6359 #[cfg(target_os = "macos")]
6360 Backend::Metal => crate::gpu_metal::zimage::vae_decode_chain(a, z, h, w, out),
6361 #[cfg(feature = "gpu")]
6362 Backend::Wgpu => crate::gpu_wgpu::zimage::vae_decode_chain(a, z, h, w, out),
6363 #[allow(unreachable_patterns)]
6364 _ => false,
6365 }
6366}
6367
6368
6369pub trait S2vStepper {
6373 fn step(&mut self, step: usize, chunk: usize, x: &[f32], memory: &[Vec<f32>; 3]) -> Result<Vec<f32>, String>;
6377}
6378
6379#[allow(unused_variables)]
6381pub fn s2v_prepare(
6382 dit: &crate::s2v::S2vDit,
6383 g: &crate::s2v::S2vGeom,
6384 cond: &crate::s2vgen::S2vCond,
6385 ts: &[f32],
6386 sf: [f32; 3],
6387) -> Option<Box<dyn S2vStepper>> {
6388 match backend() {
6389 #[cfg(feature = "gpu")]
6390 Backend::Wgpu => crate::gpu_wgpu::s2v::prepare(dit, g, cond, ts, sf).map(|d| Box::new(d) as Box<dyn S2vStepper>),
6391 #[cfg(target_os = "macos")]
6392 Backend::Metal => crate::gpu_metal::s2v::prepare(dit, g, cond, ts, sf).map(|d| Box::new(d) as Box<dyn S2vStepper>),
6393 #[allow(unreachable_patterns)]
6394 _ => None,
6395 }
6396}
6397
6398pub fn s2v_release() {
6400 #[cfg(feature = "gpu")]
6401 crate::gpu_wgpu::s2v_vae::release();
6402 #[cfg(target_os = "macos")]
6403 crate::gpu_metal::s2v::release();
6404}
6405
6406#[allow(unused_variables)]
6408pub fn s2v_vae_decode(vae: &crate::s2v_vae::S2vVae, z: &crate::ltxvae::Vol) -> Option<crate::ltxvae::Vol> {
6409 match backend() {
6410 #[cfg(feature = "gpu")]
6411 Backend::Wgpu => crate::gpu_wgpu::s2v_vae::decode(vae, z),
6412 #[cfg(target_os = "macos")]
6413 Backend::Metal => crate::gpu_metal::s2v::decode(vae, z),
6414 #[allow(unreachable_patterns)]
6415 _ => None,
6416 }
6417}
6418
6419#[allow(unused_variables)]
6421pub fn s2v_vae_encode(enc: &crate::s2v_vae::S2vVaeEncoder, img: &crate::ltxvae::Vol) -> Option<(crate::ltxvae::Vol, crate::ltxvae::Vol)> {
6422 match backend() {
6423 #[cfg(feature = "gpu")]
6424 Backend::Wgpu => crate::gpu_wgpu::s2v_vae::encode(enc, img),
6425 #[cfg(target_os = "macos")]
6426 Backend::Metal => crate::gpu_metal::s2v::encode(enc, img),
6427 #[allow(unreachable_patterns)]
6428 _ => None,
6429 }
6430}
6431
6432#[allow(unused_variables)]
6434pub fn s2v_check_forward(dit: &crate::s2v::S2vDit, i: &crate::s2v::S2vInput<'_>) -> Option<Vec<f32>> {
6435 let cond = crate::s2vgen::S2vCond {
6436 text: i.text_feats.to_vec(),
6437 pooled: i.pooled.to_vec(),
6438 audio: vec![i.audio_feats.to_vec()],
6439 reference: i.reference.to_vec(),
6440 height: i.geom.lh * 8,
6441 width: i.geom.lw * 8,
6442 };
6443 let mut dev = s2v_prepare(dit, &i.geom, &cond, &[i.t], i.scale_factor)?;
6444 let memory = [i.mem1.to_vec(), i.mem2.to_vec(), i.mem4.to_vec()];
6445 dev.step(0, 0, i.x, &memory).ok()
6446}