Skip to main content

qualia_core_db/gguf_bridge/
init.rs

1//! Engine init/lifecycle: GPU device/queue accessors, try_new, new, KV-cache + GEMM-buffer
2//! allocation/reset. Split from gguf_bridge/mod.rs (structural; no behaviour change).
3use super::*;
4
5impl QTensorEngine {
6    /// Resolve the MC8 elementwise GPU pipeline for a given opcode.
7    pub(crate) fn elem_gpu_pipeline(&self, op: u32) -> Option<&wgpu::ComputePipeline> {
8        use super::gpu_params::{ELEM_OP_ADD_RESIDUAL, ELEM_OP_RMS_NORM};
9        match op {
10            ELEM_OP_RMS_NORM => Some(&self.elem_rms_norm_pipeline),
11            ELEM_OP_ADD_RESIDUAL => Some(&self.elem_add_residual_pipeline),
12            _ => None,
13        }
14    }
15    pub(crate) fn gpu_device(&self) -> &wgpu::Device {
16        #[cfg(target_arch = "wasm32")]
17        {
18            return &self.device;
19        }
20        #[cfg(not(target_arch = "wasm32"))]
21        {
22            &crate::gpu_context::shared_gpu().device
23        }
24    }
25
26    #[inline]
27    pub(crate) fn gpu_queue(&self) -> &wgpu::Queue {
28        #[cfg(target_arch = "wasm32")]
29        {
30            return &self.queue;
31        }
32        #[cfg(not(target_arch = "wasm32"))]
33        {
34            &crate::gpu_context::shared_gpu().queue
35        }
36    }
37
38    /// Shared process-wide wgpu device (LLM + render coexistence).
39    #[inline]
40    pub fn device(&self) -> &wgpu::Device {
41        self.gpu_device()
42    }
43
44    /// Shared process-wide wgpu queue.
45    #[inline]
46    pub fn queue(&self) -> &wgpu::Queue {
47        self.gpu_queue()
48    }
49
50    pub async fn try_new() -> Result<Self, String> {
51        #[cfg(not(target_arch = "wasm32"))]
52        log::info!(
53            "LLM_LOAD|engine-init|0.10|Initializing native GGUF runtime (shared GpuContext)"
54        );
55        #[cfg(target_arch = "wasm32")]
56        log::info!("LLM_LOAD|engine-init|0.10|Initializing WASM GGUF runtime");
57
58        #[cfg(not(target_arch = "wasm32"))]
59        let shared = crate::gpu_context::shared_gpu();
60        #[cfg(not(target_arch = "wasm32"))]
61        let device = &shared.device;
62        #[cfg(not(target_arch = "wasm32"))]
63        let _queue = &shared.queue;
64        #[cfg(not(target_arch = "wasm32"))]
65        log::info!("LLM_LOAD|gpu-device|0.35|Reusing process-wide wgpu device");
66        // NVIDIA: create CUDA multi-weight context early so the GPU leaves idle clocks
67        // even for portable/FastVerify resident decode (measured 3B ~1.5 → ~7 tok/s).
68        #[cfg(all(not(target_arch = "wasm32"), feature = "cuda"))]
69        {
70            let _ = crate::warm_cuda_context();
71        }
72
73        #[cfg(target_arch = "wasm32")]
74        let (wasm_device, wasm_queue) = {
75            let instance = wgpu::Instance::default();
76            // Prefer the high-performance adapter on phones (Pixel / Android
77            // Chrome often expose a low-power fallback that stalls compute).
78            let adapter = match instance
79                .request_adapter(&wgpu::RequestAdapterOptions {
80                    power_preference: wgpu::PowerPreference::HighPerformance,
81                    ..Default::default()
82                })
83                .await
84            {
85                Ok(a) => a,
86                Err(high_err) => instance
87                    .request_adapter(&wgpu::RequestAdapterOptions::default())
88                    .await
89                    .map_err(|e| {
90                        format!(
91                            "Failed to find wgpu adapter (high-performance: {high_err}; default: {e})"
92                        )
93                    })?,
94            };
95            let info = adapter.get_info();
96            log::info!(
97                "LLM_LOAD|webgpu-adapter|0.20|name={} backend={:?} device={:?} vendor={:?}",
98                info.name,
99                info.backend,
100                info.device,
101                info.vendor
102            );
103            // Browser WebGPU exposes a smaller feature set than native backends.
104            // Intersecting with the adapter keeps device creation portable while
105            // still enabling f16/subgroup/timing acceleration where available.
106            let required_features =
107                crate::gpu_context::requested_native_llm_features(adapter.features());
108            let experimental_features =
109                if required_features.contains(wgpu::Features::EXPERIMENTAL_COOPERATIVE_MATRIX) {
110                    // Safety: the feature is both explicitly opted into and advertised
111                    // by the browser adapter before this token is enabled.
112                    unsafe { wgpu::ExperimentalFeatures::enabled() }
113                } else {
114                    wgpu::ExperimentalFeatures::disabled()
115                };
116            // Raise buffer caps to the adapter's advertised maximum (same pattern as
117            // native shared_gpu). Default wgpu caps are too small for real weight
118            // tensors and can make requestDevice succeed then fail at buffer create.
119            let adapter_limits = adapter.limits();
120            let required_limits = wgpu::Limits {
121                max_buffer_size: adapter_limits.max_buffer_size,
122                max_storage_buffer_binding_size: adapter_limits.max_storage_buffer_binding_size,
123                ..wgpu::Limits::default()
124            };
125
126            crate::gguf_bridge::wasm_yield::phase(&format!(
127                "Adapter '{}' — creating WebGPU device…",
128                info.name
129            ))
130            .await;
131            adapter
132                .request_device(&wgpu::DeviceDescriptor {
133                    label: Some("qualia-wasm-llm"),
134                    required_features,
135                    required_limits,
136                    experimental_features,
137                    ..Default::default()
138                })
139                .await
140                .map_err(|e| {
141                    format!(
142                        "WebGPU requestDevice failed on adapter '{}': {e}. \
143                         On phones use SmolLM2-360M only, close other tabs, and ensure Chrome WebGPU is enabled.",
144                        info.name
145                    )
146                })?
147        };
148        #[cfg(target_arch = "wasm32")]
149        let device = &wasm_device;
150        // One yield after device so the UI paints before pipeline compile freezes the main thread.
151        #[cfg(target_arch = "wasm32")]
152        crate::gguf_bridge::wasm_yield::phase(
153            "Compiling compute pipelines (UI may pause 30–90s on phones — leave tab open)…",
154        )
155        .await;
156        #[cfg(not(target_arch = "wasm32"))]
157        let native_pipeline_cache = create_native_pipeline_cache(device);
158        #[cfg(target_arch = "wasm32")]
159        let native_pipeline_cache: Option<wgpu::PipelineCache> = None;
160
161        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
162            label: Some("Fused Transformer Shader"),
163            source: wgpu::ShaderSource::Wgsl(
164                #[cfg(target_arch = "wasm32")]
165                include_str!("../shaders/wasm/fused_transformer.wgsl").into(),
166                #[cfg(not(target_arch = "wasm32"))]
167                include_str!("../shaders/fused_transformer.wgsl").into(),
168            ),
169        });
170
171        // Shared explicit 5-slot layout (bindings 0-3 + residual 4). Module-scope
172        // `residual` in fused_transformer.wgsl requires binding 4 on every entry point.
173        // Plain GEMV call sites bind a dummy residual (often the input buffer).
174        #[cfg(not(target_arch = "wasm32"))]
175        let coop_gemv_residual_bind_layout =
176            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
177                label: Some("CoopGemvBGL"),
178                entries: &[
179                    wgpu::BindGroupLayoutEntry {
180                        binding: 0,
181                        visibility: wgpu::ShaderStages::COMPUTE,
182                        ty: wgpu::BindingType::Buffer {
183                            ty: wgpu::BufferBindingType::Storage { read_only: true },
184                            has_dynamic_offset: false,
185                            min_binding_size: None,
186                        },
187                        count: None,
188                    },
189                    wgpu::BindGroupLayoutEntry {
190                        binding: 1,
191                        visibility: wgpu::ShaderStages::COMPUTE,
192                        ty: wgpu::BindingType::Buffer {
193                            ty: wgpu::BufferBindingType::Storage { read_only: true },
194                            has_dynamic_offset: false,
195                            min_binding_size: None,
196                        },
197                        count: None,
198                    },
199                    wgpu::BindGroupLayoutEntry {
200                        binding: 2,
201                        visibility: wgpu::ShaderStages::COMPUTE,
202                        ty: wgpu::BindingType::Buffer {
203                            ty: wgpu::BufferBindingType::Uniform,
204                            has_dynamic_offset: false,
205                            min_binding_size: None,
206                        },
207                        count: None,
208                    },
209                    wgpu::BindGroupLayoutEntry {
210                        binding: 3,
211                        visibility: wgpu::ShaderStages::COMPUTE,
212                        ty: wgpu::BindingType::Buffer {
213                            ty: wgpu::BufferBindingType::Storage { read_only: false },
214                            has_dynamic_offset: false,
215                            min_binding_size: None,
216                        },
217                        count: None,
218                    },
219                    wgpu::BindGroupLayoutEntry {
220                        binding: 4,
221                        visibility: wgpu::ShaderStages::COMPUTE,
222                        ty: wgpu::BindingType::Buffer {
223                            ty: wgpu::BufferBindingType::Storage { read_only: true },
224                            has_dynamic_offset: false,
225                            min_binding_size: None,
226                        },
227                        count: None,
228                    },
229                ],
230            });
231        #[cfg(not(target_arch = "wasm32"))]
232        let coop_gemv_bind_layout = coop_gemv_residual_bind_layout.clone();
233        #[cfg(not(target_arch = "wasm32"))]
234        let coop_gemv_pipeline_layout =
235            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
236                label: Some("CoopGemvPL"),
237                bind_group_layouts: &[Some(&coop_gemv_bind_layout)],
238                immediate_size: 0,
239            });
240        #[cfg(not(target_arch = "wasm32"))]
241        let coop_gemv_residual_pipeline_layout =
242            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
243                label: Some("CoopGemvResidualPL"),
244                bind_group_layouts: &[Some(&coop_gemv_residual_bind_layout)],
245                immediate_size: 0,
246            });
247
248        #[cfg(target_arch = "wasm32")]
249        let mc8_gemm_bind_layout =
250            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
251                label: Some("MC8GemmBGL"),
252                entries: &[
253                    wgpu::BindGroupLayoutEntry {
254                        binding: 0,
255                        visibility: wgpu::ShaderStages::COMPUTE,
256                        ty: wgpu::BindingType::Buffer {
257                            ty: wgpu::BufferBindingType::Storage { read_only: true },
258                            has_dynamic_offset: false,
259                            min_binding_size: None,
260                        },
261                        count: None,
262                    },
263                    wgpu::BindGroupLayoutEntry {
264                        binding: 1,
265                        visibility: wgpu::ShaderStages::COMPUTE,
266                        ty: wgpu::BindingType::Buffer {
267                            ty: wgpu::BufferBindingType::Storage { read_only: true },
268                            has_dynamic_offset: false,
269                            min_binding_size: None,
270                        },
271                        count: None,
272                    },
273                    wgpu::BindGroupLayoutEntry {
274                        binding: 2,
275                        visibility: wgpu::ShaderStages::COMPUTE,
276                        ty: wgpu::BindingType::Buffer {
277                            ty: wgpu::BufferBindingType::Uniform,
278                            has_dynamic_offset: true,
279                            min_binding_size: std::num::NonZeroU64::new(MC8_UNIFORM_ALIGN as u64),
280                        },
281                        count: None,
282                    },
283                    wgpu::BindGroupLayoutEntry {
284                        binding: 3,
285                        visibility: wgpu::ShaderStages::COMPUTE,
286                        ty: wgpu::BindingType::Buffer {
287                            ty: wgpu::BufferBindingType::Storage { read_only: false },
288                            has_dynamic_offset: false,
289                            min_binding_size: None,
290                        },
291                        count: None,
292                    },
293                ],
294            });
295        #[cfg(target_arch = "wasm32")]
296        let mc8_gemm_pipeline_layout =
297            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
298                label: Some("MC8GemmPL"),
299                bind_group_layouts: &[Some(&mc8_gemm_bind_layout)],
300                immediate_size: 0,
301            });
302
303        #[cfg(target_arch = "wasm32")]
304        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
305            label: Some("Fused Transformer Pipeline (WASM)"),
306            layout: Some(&mc8_gemm_pipeline_layout),
307            module: &shader,
308            entry_point: Some("main"),
309            compilation_options: Default::default(),
310            cache: native_pipeline_cache.as_ref(),
311        });
312        #[cfg(target_arch = "wasm32")]
313        let mmv_q8_0_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
314            label: Some("MulMatVec Q8_0 Pipeline (WASM)"),
315            layout: Some(&mc8_gemm_pipeline_layout),
316            module: &shader,
317            entry_point: Some("mul_mat_vec_q8_0"),
318            compilation_options: Default::default(),
319            cache: native_pipeline_cache.as_ref(),
320        });
321        #[cfg(not(target_arch = "wasm32"))]
322        let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
323            label: Some("Fused Transformer Pipeline"),
324            layout: None,
325            module: &shader,
326            entry_point: Some("main"),
327            compilation_options: Default::default(),
328            cache: native_pipeline_cache.as_ref(),
329        });
330        // 0.0.21: second pipeline over the SAME shader module — the cooperative one-workgroup-per-row
331        // GEMV. Auto-layout; same group-0 bindings as `main`. Native only.
332        //
333        // When the adapter advertises SUBGROUP, build the wave-reduction variant (`coop_gemv_sg` in
334        // coop_gemv_subgroup.wgsl, concatenated after the base with `enable subgroups;`) which
335        // replaces the 8-step barrier-synced shared-memory tree reduction with one `subgroupAdd` per
336        // subgroup. Identical group-0 bindings + (n_out,1,1) dispatch → a drop-in for this field, so
337        // every call site and the derived `coop_gemv_bind_layout` pick it up transparently. Adapters
338        // without subgroups (and wasm) keep the universal shared-memory `coop_gemv`.
339        // All coop GEMV entry points share the explicit 5-slot CoopGemvBGL so bind
340        // groups are interchangeable across single-row / multi-row / residual / warp
341        // (no exclusive-pipeline auto-layout traps).
342        #[cfg(not(target_arch = "wasm32"))]
343        let (coop_gemv_pipeline, coop_gemv_residual_pipeline) =
344            if device.features().contains(wgpu::Features::SUBGROUP) {
345                // Note: do NOT inject `enable subgroups;` — naga/wgpu 29 rejects it.
346                let sg_src = format!(
347                    "{}\n{}",
348                    include_str!("../shaders/fused_transformer.wgsl"),
349                    include_str!("../shaders/coop_gemv_subgroup.wgsl"),
350                );
351                let sg_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
352                    label: Some("Coop GEMV Subgroup Shader"),
353                    source: wgpu::ShaderSource::Wgsl(sg_src.into()),
354                });
355                let gemv = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
356                    label: Some("Coop GEMV SG Pipeline"),
357                    layout: Some(&coop_gemv_pipeline_layout),
358                    module: &sg_module,
359                    entry_point: Some("coop_gemv_sg"),
360                    compilation_options: Default::default(),
361                    cache: native_pipeline_cache.as_ref(),
362                });
363                let resid = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
364                    label: Some("Coop GEMV Residual SG Pipeline"),
365                    layout: Some(&coop_gemv_residual_pipeline_layout),
366                    module: &sg_module,
367                    entry_point: Some("coop_gemv_residual_sg"),
368                    compilation_options: Default::default(),
369                    cache: native_pipeline_cache.as_ref(),
370                });
371                (gemv, resid)
372            } else {
373                let gemv = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
374                    label: Some("Coop GEMV Pipeline"),
375                    layout: Some(&coop_gemv_pipeline_layout),
376                    module: &shader,
377                    entry_point: Some("coop_gemv"),
378                    compilation_options: Default::default(),
379                    cache: native_pipeline_cache.as_ref(),
380                });
381                let resid = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
382                    label: Some("Coop GEMV Residual Pipeline"),
383                    layout: Some(&coop_gemv_residual_pipeline_layout),
384                    module: &shader,
385                    entry_point: Some("coop_gemv_residual"),
386                    compilation_options: Default::default(),
387                    cache: native_pipeline_cache.as_ref(),
388                });
389                (gemv, resid)
390            };
391        #[cfg(not(target_arch = "wasm32"))]
392        let coop_gemv_mr_pipeline =
393            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
394                label: Some("Coop GEMV Multi-Row Pipeline"),
395                layout: Some(&coop_gemv_pipeline_layout),
396                module: &shader,
397                entry_point: Some("coop_gemv_mr"),
398                compilation_options: Default::default(),
399                cache: native_pipeline_cache.as_ref(),
400            });
401        #[cfg(not(target_arch = "wasm32"))]
402        let coop_gemv_residual_mr_pipeline =
403            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
404                label: Some("Coop GEMV Residual Multi-Row Pipeline"),
405                layout: Some(&coop_gemv_residual_pipeline_layout),
406                module: &shader,
407                entry_point: Some("coop_gemv_residual_mr"),
408                compilation_options: Default::default(),
409                cache: native_pipeline_cache.as_ref(),
410            });
411        #[cfg(not(target_arch = "wasm32"))]
412        let coop_gemv_warp_pipeline =
413            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
414                label: Some("Coop GEMV Warp Pipeline"),
415                layout: Some(&coop_gemv_pipeline_layout),
416                module: &shader,
417                entry_point: Some("coop_gemv_warp"),
418                compilation_options: Default::default(),
419                cache: native_pipeline_cache.as_ref(),
420            });
421        #[cfg(not(target_arch = "wasm32"))]
422        let coop_gemv_residual_warp_pipeline =
423            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
424                label: Some("Coop GEMV Residual Warp Pipeline"),
425                layout: Some(&coop_gemv_residual_pipeline_layout),
426                module: &shader,
427                entry_point: Some("coop_gemv_residual_warp"),
428                compilation_options: Default::default(),
429                cache: native_pipeline_cache.as_ref(),
430            });
431
432        #[cfg(not(target_arch = "wasm32"))]
433        let mock_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
434            label: Some("Mock Fused Contraction Shader"),
435            source: wgpu::ShaderSource::Wgsl(
436                include_str!("../shaders/fused_tensor_contraction.wgsl").into(),
437            ),
438        });
439        #[cfg(not(target_arch = "wasm32"))]
440        let mock_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
441            label: Some("Mock Fused Contraction Pipeline"),
442            layout: None,
443            module: &mock_shader,
444            entry_point: Some("main"),
445            compilation_options: Default::default(),
446            cache: native_pipeline_cache.as_ref(),
447        });
448
449        let emb_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
450            label: Some("Quantized Embedding Shader"),
451            source: wgpu::ShaderSource::Wgsl(
452                include_str!("../shaders/quantized_embedding.wgsl").into(),
453            ),
454        });
455        let embedding_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
456            label: Some("Quantized Embedding Pipeline"),
457            layout: None,
458            module: &emb_shader,
459            entry_point: Some("main"),
460            compilation_options: Default::default(),
461            cache: native_pipeline_cache.as_ref(),
462        });
463
464        let attn_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
465            label: Some("Fused Attention Shader"),
466            source: wgpu::ShaderSource::Wgsl(
467                include_str!("../shaders/fused_attention.wgsl").into(),
468            ),
469        });
470        #[cfg(target_arch = "wasm32")]
471        let mc8_attn_bind_layout =
472            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
473                label: Some("MC8AttnBGL"),
474                entries: &[
475                    wgpu::BindGroupLayoutEntry {
476                        binding: 0,
477                        visibility: wgpu::ShaderStages::COMPUTE,
478                        ty: wgpu::BindingType::Buffer {
479                            ty: wgpu::BufferBindingType::Storage { read_only: true },
480                            has_dynamic_offset: false,
481                            min_binding_size: None,
482                        },
483                        count: None,
484                    },
485                    wgpu::BindGroupLayoutEntry {
486                        binding: 1,
487                        visibility: wgpu::ShaderStages::COMPUTE,
488                        ty: wgpu::BindingType::Buffer {
489                            ty: wgpu::BufferBindingType::Storage { read_only: true },
490                            has_dynamic_offset: false,
491                            min_binding_size: None,
492                        },
493                        count: None,
494                    },
495                    wgpu::BindGroupLayoutEntry {
496                        binding: 2,
497                        visibility: wgpu::ShaderStages::COMPUTE,
498                        ty: wgpu::BindingType::Buffer {
499                            ty: wgpu::BufferBindingType::Uniform,
500                            has_dynamic_offset: true,
501                            min_binding_size: std::num::NonZeroU64::new(MC8_UNIFORM_ALIGN as u64),
502                        },
503                        count: None,
504                    },
505                    wgpu::BindGroupLayoutEntry {
506                        binding: 3,
507                        visibility: wgpu::ShaderStages::COMPUTE,
508                        ty: wgpu::BindingType::Buffer {
509                            ty: wgpu::BufferBindingType::Storage { read_only: false },
510                            has_dynamic_offset: false,
511                            min_binding_size: None,
512                        },
513                        count: None,
514                    },
515                    wgpu::BindGroupLayoutEntry {
516                        binding: 4,
517                        visibility: wgpu::ShaderStages::COMPUTE,
518                        ty: wgpu::BindingType::Buffer {
519                            ty: wgpu::BufferBindingType::Storage { read_only: false },
520                            has_dynamic_offset: false,
521                            min_binding_size: None,
522                        },
523                        count: None,
524                    },
525                    wgpu::BindGroupLayoutEntry {
526                        binding: 5,
527                        visibility: wgpu::ShaderStages::COMPUTE,
528                        ty: wgpu::BindingType::Buffer {
529                            ty: wgpu::BufferBindingType::Storage { read_only: true },
530                            has_dynamic_offset: false,
531                            min_binding_size: None,
532                        },
533                        count: None,
534                    },
535                ],
536            });
537        #[cfg(target_arch = "wasm32")]
538        let mc8_attn_pipeline_layout =
539            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
540                label: Some("MC8AttnPL"),
541                bind_group_layouts: &[Some(&mc8_attn_bind_layout)],
542                immediate_size: 0,
543            });
544        #[cfg(target_arch = "wasm32")]
545        let attention_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
546            label: Some("Fused Attention Pipeline"),
547            layout: Some(&mc8_attn_pipeline_layout),
548            module: &attn_shader,
549            entry_point: Some("main"),
550            compilation_options: Default::default(),
551            cache: native_pipeline_cache.as_ref(),
552        });
553        #[cfg(not(target_arch = "wasm32"))]
554        let attention_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
555            label: Some("Fused Attention Pipeline"),
556            layout: None,
557            module: &attn_shader,
558            entry_point: Some("main"),
559            compilation_options: Default::default(),
560            cache: native_pipeline_cache.as_ref(),
561        });
562
563        let elem_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
564            label: Some("Wasm Elementwise Shader"),
565            source: wgpu::ShaderSource::Wgsl(
566                include_str!("../shaders/wasm_elementwise.wgsl").into(),
567            ),
568        });
569        #[cfg(target_arch = "wasm32")]
570        let mc8_elem_bind_layout =
571            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
572                label: Some("MC8ElemBGL"),
573                entries: &[
574                    wgpu::BindGroupLayoutEntry {
575                        binding: 0,
576                        visibility: wgpu::ShaderStages::COMPUTE,
577                        ty: wgpu::BindingType::Buffer {
578                            ty: wgpu::BufferBindingType::Storage { read_only: true },
579                            has_dynamic_offset: false,
580                            min_binding_size: None,
581                        },
582                        count: None,
583                    },
584                    wgpu::BindGroupLayoutEntry {
585                        binding: 1,
586                        visibility: wgpu::ShaderStages::COMPUTE,
587                        ty: wgpu::BindingType::Buffer {
588                            ty: wgpu::BufferBindingType::Storage { read_only: true },
589                            has_dynamic_offset: false,
590                            min_binding_size: None,
591                        },
592                        count: None,
593                    },
594                    wgpu::BindGroupLayoutEntry {
595                        binding: 2,
596                        visibility: wgpu::ShaderStages::COMPUTE,
597                        ty: wgpu::BindingType::Buffer {
598                            ty: wgpu::BufferBindingType::Storage { read_only: false },
599                            has_dynamic_offset: false,
600                            min_binding_size: None,
601                        },
602                        count: None,
603                    },
604                    wgpu::BindGroupLayoutEntry {
605                        binding: 3,
606                        visibility: wgpu::ShaderStages::COMPUTE,
607                        ty: wgpu::BindingType::Buffer {
608                            ty: wgpu::BufferBindingType::Uniform,
609                            has_dynamic_offset: true,
610                            min_binding_size: std::num::NonZeroU64::new(MC8_UNIFORM_ALIGN as u64),
611                        },
612                        count: None,
613                    },
614                ],
615            });
616        #[cfg(target_arch = "wasm32")]
617        let mc8_elem_pipeline_layout =
618            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
619                label: Some("MC8ElemPL"),
620                bind_group_layouts: &[Some(&mc8_elem_bind_layout)],
621                immediate_size: 0,
622            });
623        #[cfg(target_arch = "wasm32")]
624        let elem_rms_norm_pipeline =
625            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
626                label: Some("ElemRmsNorm"),
627                layout: Some(&mc8_elem_pipeline_layout),
628                module: &elem_shader,
629                entry_point: Some("rms_norm_batch"),
630                compilation_options: Default::default(),
631                cache: native_pipeline_cache.as_ref(),
632            });
633        #[cfg(target_arch = "wasm32")]
634        let elem_silu_mul_pipeline =
635            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
636                label: Some("ElemSiluMul"),
637                layout: Some(&mc8_elem_pipeline_layout),
638                module: &elem_shader,
639                entry_point: Some("silu_mul_main"),
640                compilation_options: Default::default(),
641                cache: native_pipeline_cache.as_ref(),
642            });
643        #[cfg(target_arch = "wasm32")]
644        let elem_add_residual_pipeline =
645            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
646                label: Some("ElemAddResidual"),
647                layout: Some(&mc8_elem_pipeline_layout),
648                module: &elem_shader,
649                entry_point: Some("add_residual_main"),
650                compilation_options: Default::default(),
651                cache: native_pipeline_cache.as_ref(),
652            });
653        #[cfg(not(target_arch = "wasm32"))]
654        let elem_rms_norm_pipeline =
655            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
656                label: Some("ElemRmsNorm"),
657                layout: None,
658                module: &elem_shader,
659                entry_point: Some("rms_norm_batch"),
660                compilation_options: Default::default(),
661                cache: native_pipeline_cache.as_ref(),
662            });
663        #[cfg(not(target_arch = "wasm32"))]
664        let elem_silu_mul_pipeline =
665            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
666                label: Some("ElemSiluMul"),
667                layout: None,
668                module: &elem_shader,
669                entry_point: Some("silu_mul_main"),
670                compilation_options: Default::default(),
671                cache: native_pipeline_cache.as_ref(),
672            });
673        #[cfg(not(target_arch = "wasm32"))]
674        let elem_add_residual_pipeline =
675            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
676                label: Some("ElemAddResidual"),
677                layout: None,
678                module: &elem_shader,
679                entry_point: Some("add_residual_main"),
680                compilation_options: Default::default(),
681                cache: native_pipeline_cache.as_ref(),
682            });
683
684        // Phase 5 — Fused FFN expansion pipeline (gate · SiLU · up in one dispatch).
685        // The dequant math is authored once in `dequant_template.wgsl` and instantiated
686        // per weight role here (Rust-side modular WGSL composition), so the proven GEMM
687        // path in `fused_transformer.wgsl` is untouched.
688        #[cfg(target_arch = "wasm32")]
689        let mc8_ffn_fused_bind_layout =
690            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
691                label: Some("MC8FfnFusedBGL"),
692                entries: &[
693                    // 0: ffn_input (normalized hidden, storage read)
694                    wgpu::BindGroupLayoutEntry {
695                        binding: 0,
696                        visibility: wgpu::ShaderStages::COMPUTE,
697                        ty: wgpu::BindingType::Buffer {
698                            ty: wgpu::BufferBindingType::Storage { read_only: true },
699                            has_dynamic_offset: false,
700                            min_binding_size: None,
701                        },
702                        count: None,
703                    },
704                    // 1: gate_words (quantized gate weight, storage read)
705                    wgpu::BindGroupLayoutEntry {
706                        binding: 1,
707                        visibility: wgpu::ShaderStages::COMPUTE,
708                        ty: wgpu::BindingType::Buffer {
709                            ty: wgpu::BufferBindingType::Storage { read_only: true },
710                            has_dynamic_offset: false,
711                            min_binding_size: None,
712                        },
713                        count: None,
714                    },
715                    // 2: up_words (quantized up weight, storage read)
716                    wgpu::BindGroupLayoutEntry {
717                        binding: 2,
718                        visibility: wgpu::ShaderStages::COMPUTE,
719                        ty: wgpu::BindingType::Buffer {
720                            ty: wgpu::BufferBindingType::Storage { read_only: true },
721                            has_dynamic_offset: false,
722                            min_binding_size: None,
723                        },
724                        count: None,
725                    },
726                    // 3: params (GemmParams, dynamic uniform offset — gate's staged params)
727                    wgpu::BindGroupLayoutEntry {
728                        binding: 3,
729                        visibility: wgpu::ShaderStages::COMPUTE,
730                        ty: wgpu::BindingType::Buffer {
731                            ty: wgpu::BufferBindingType::Uniform,
732                            has_dynamic_offset: true,
733                            min_binding_size: std::num::NonZeroU64::new(MC8_UNIFORM_ALIGN as u64),
734                        },
735                        count: None,
736                    },
737                    // 4: ffn_output (silu(gate)·up intermediate, storage read_write)
738                    wgpu::BindGroupLayoutEntry {
739                        binding: 4,
740                        visibility: wgpu::ShaderStages::COMPUTE,
741                        ty: wgpu::BindingType::Buffer {
742                            ty: wgpu::BufferBindingType::Storage { read_only: false },
743                            has_dynamic_offset: false,
744                            min_binding_size: None,
745                        },
746                        count: None,
747                    },
748                ],
749            });
750        #[cfg(target_arch = "wasm32")]
751        let mc8_ffn_fused_pipeline = {
752            // Modular WGSL: shared scaffold + per-role dequant instances composed at runtime.
753            let tpl = include_str!("../shaders/wasm/dequant_template.wgsl");
754            let gate_fns = tpl.replace("$W", "gate_words").replace("$S", "_gate");
755            let up_fns = tpl.replace("$W", "up_words").replace("$S", "_up");
756            let base = include_str!("../shaders/wasm/fused_ffn.wgsl");
757            // Inject the per-role dequant math at the marker (between shared helpers and
758            // the entry point) so declarations precede their uses.
759            // WASM copy already has subgroup entry points stripped — no runtime filtering needed.
760            let src = base.replace("// @@DEQUANT_FUNCTIONS@@", &format!("{gate_fns}\n{up_fns}"));
761            let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
762                label: Some("FusedFFNExpansion"),
763                source: wgpu::ShaderSource::Wgsl(src.into()),
764            });
765            let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
766                label: Some("MC8FfnFusedPL"),
767                bind_group_layouts: &[Some(&mc8_ffn_fused_bind_layout)],
768                immediate_size: 0,
769            });
770            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
771                label: Some("FusedFFNExpansionPipeline"),
772                layout: Some(&layout),
773                module: &module,
774                entry_point: Some("fused_ffn_expansion"),
775                compilation_options: Default::default(),
776                cache: native_pipeline_cache.as_ref(),
777            })
778        };
779
780        // Native T-A1 — same fused_ffn.wgsl, static uniform (no dynamic offset).
781        // Wired into resident_decode mega-pass when gate/up share a supported quant type.
782        #[cfg(not(target_arch = "wasm32"))]
783        let ffn_fused_bind_layout =
784            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
785                label: Some("NativeFfnFusedBGL"),
786                entries: &[
787                    wgpu::BindGroupLayoutEntry {
788                        binding: 0,
789                        visibility: wgpu::ShaderStages::COMPUTE,
790                        ty: wgpu::BindingType::Buffer {
791                            ty: wgpu::BufferBindingType::Storage { read_only: true },
792                            has_dynamic_offset: false,
793                            min_binding_size: None,
794                        },
795                        count: None,
796                    },
797                    wgpu::BindGroupLayoutEntry {
798                        binding: 1,
799                        visibility: wgpu::ShaderStages::COMPUTE,
800                        ty: wgpu::BindingType::Buffer {
801                            ty: wgpu::BufferBindingType::Storage { read_only: true },
802                            has_dynamic_offset: false,
803                            min_binding_size: None,
804                        },
805                        count: None,
806                    },
807                    wgpu::BindGroupLayoutEntry {
808                        binding: 2,
809                        visibility: wgpu::ShaderStages::COMPUTE,
810                        ty: wgpu::BindingType::Buffer {
811                            ty: wgpu::BufferBindingType::Storage { read_only: true },
812                            has_dynamic_offset: false,
813                            min_binding_size: None,
814                        },
815                        count: None,
816                    },
817                    wgpu::BindGroupLayoutEntry {
818                        binding: 3,
819                        visibility: wgpu::ShaderStages::COMPUTE,
820                        ty: wgpu::BindingType::Buffer {
821                            ty: wgpu::BufferBindingType::Uniform,
822                            has_dynamic_offset: false,
823                            min_binding_size: std::num::NonZeroU64::new(std::mem::size_of::<
824                                GemmGpuParams,
825                            >(
826                            )
827                                as u64),
828                        },
829                        count: None,
830                    },
831                    wgpu::BindGroupLayoutEntry {
832                        binding: 4,
833                        visibility: wgpu::ShaderStages::COMPUTE,
834                        ty: wgpu::BindingType::Buffer {
835                            ty: wgpu::BufferBindingType::Storage { read_only: false },
836                            has_dynamic_offset: false,
837                            min_binding_size: None,
838                        },
839                        count: None,
840                    },
841                ],
842            });
843        #[cfg(not(target_arch = "wasm32"))]
844        let (
845            ffn_fused_pipeline,
846            ffn_fused_coop_pipeline,
847            ffn_fused_mr_pipeline,
848            ffn_fused_warp_pipeline,
849        ) = {
850            let tpl = include_str!("../shaders/dequant_template.wgsl");
851            let gate_fns = tpl.replace("$W", "gate_words").replace("$S", "_gate");
852            let up_fns = tpl.replace("$W", "up_words").replace("$S", "_up");
853            let base = include_str!("../shaders/fused_ffn.wgsl");
854            let src = base.replace("// @@DEQUANT_FUNCTIONS@@", &format!("{gate_fns}\n{up_fns}"));
855            let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
856                label: Some("NativeFusedFFNExpansion"),
857                source: wgpu::ShaderSource::Wgsl(src.into()),
858            });
859            let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
860                label: Some("NativeFfnFusedPL"),
861                bind_group_layouts: &[Some(&ffn_fused_bind_layout)],
862                immediate_size: 0,
863            });
864            let naive = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
865                label: Some("NativeFusedFFNExpansionPipeline"),
866                layout: Some(&layout),
867                module: &module,
868                entry_point: Some("fused_ffn_expansion"),
869                compilation_options: Default::default(),
870                cache: native_pipeline_cache.as_ref(),
871            });
872            // Prefer subgroup reduction when available (same as coop_gemv_sg).
873            let coop_ep = if device.features().contains(wgpu::Features::SUBGROUP) {
874                "coop_fused_ffn_sg"
875            } else {
876                "coop_fused_ffn_expansion"
877            };
878            let coop = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
879                label: Some("NativeCoopFusedFFNExpansionPipeline"),
880                layout: Some(&layout),
881                module: &module,
882                entry_point: Some(coop_ep),
883                compilation_options: Default::default(),
884                cache: native_pipeline_cache.as_ref(),
885            });
886            // Multi-row fused FFN (4 rows/WG, one K-sweep) — Q4_K_SOA 3B lever.
887            let coop_mr = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
888                label: Some("NativeCoopFusedFFNMultiRowPipeline"),
889                layout: Some(&layout),
890                module: &module,
891                entry_point: Some("coop_fused_ffn_mr"),
892                compilation_options: Default::default(),
893                cache: native_pipeline_cache.as_ref(),
894            });
895            // Warp fused FFN (32 thr/row, 8 cols/lane).
896            let coop_warp = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
897                label: Some("NativeCoopFusedFFNWarpPipeline"),
898                layout: Some(&layout),
899                module: &module,
900                entry_point: Some("coop_fused_ffn_warp"),
901                compilation_options: Default::default(),
902                cache: native_pipeline_cache.as_ref(),
903            });
904            log::info!(
905                "LLM_LOAD|fused_ffn|coop_entry={coop_ep}|mr=coop_fused_ffn_mr|warp=coop_fused_ffn_warp"
906            );
907            (naive, coop, coop_mr, coop_warp)
908        };
909
910        // Dual K+V GEMV (shared act) — mega-kernel slice for resident decode.
911        #[cfg(not(target_arch = "wasm32"))]
912        let dual_gemv_bind_layout =
913            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
914                label: Some("DualGemvBGL"),
915                entries: &[
916                    wgpu::BindGroupLayoutEntry {
917                        binding: 0,
918                        visibility: wgpu::ShaderStages::COMPUTE,
919                        ty: wgpu::BindingType::Buffer {
920                            ty: wgpu::BufferBindingType::Storage { read_only: true },
921                            has_dynamic_offset: false,
922                            min_binding_size: None,
923                        },
924                        count: None,
925                    },
926                    wgpu::BindGroupLayoutEntry {
927                        binding: 1,
928                        visibility: wgpu::ShaderStages::COMPUTE,
929                        ty: wgpu::BindingType::Buffer {
930                            ty: wgpu::BufferBindingType::Storage { read_only: true },
931                            has_dynamic_offset: false,
932                            min_binding_size: None,
933                        },
934                        count: None,
935                    },
936                    wgpu::BindGroupLayoutEntry {
937                        binding: 2,
938                        visibility: wgpu::ShaderStages::COMPUTE,
939                        ty: wgpu::BindingType::Buffer {
940                            ty: wgpu::BufferBindingType::Uniform,
941                            has_dynamic_offset: false,
942                            min_binding_size: None,
943                        },
944                        count: None,
945                    },
946                    wgpu::BindGroupLayoutEntry {
947                        binding: 3,
948                        visibility: wgpu::ShaderStages::COMPUTE,
949                        ty: wgpu::BindingType::Buffer {
950                            ty: wgpu::BufferBindingType::Storage { read_only: false },
951                            has_dynamic_offset: false,
952                            min_binding_size: None,
953                        },
954                        count: None,
955                    },
956                    wgpu::BindGroupLayoutEntry {
957                        binding: 4,
958                        visibility: wgpu::ShaderStages::COMPUTE,
959                        ty: wgpu::BindingType::Buffer {
960                            ty: wgpu::BufferBindingType::Storage { read_only: true },
961                            has_dynamic_offset: false,
962                            min_binding_size: None,
963                        },
964                        count: None,
965                    },
966                    wgpu::BindGroupLayoutEntry {
967                        binding: 5,
968                        visibility: wgpu::ShaderStages::COMPUTE,
969                        ty: wgpu::BindingType::Buffer {
970                            ty: wgpu::BufferBindingType::Storage { read_only: false },
971                            has_dynamic_offset: false,
972                            min_binding_size: None,
973                        },
974                        count: None,
975                    },
976                ],
977            });
978        #[cfg(not(target_arch = "wasm32"))]
979        let (dual_gemv_pipeline, dual_gemv_mr_pipeline) = {
980            let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
981                label: Some("Dual GEMV Shader"),
982                source: wgpu::ShaderSource::Wgsl(include_str!("../shaders/dual_gemv.wgsl").into()),
983            });
984            let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
985                label: Some("DualGemvPL"),
986                bind_group_layouts: &[Some(&dual_gemv_bind_layout)],
987                immediate_size: 0,
988            });
989            let dual = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
990                label: Some("Dual GEMV Pipeline"),
991                layout: Some(&layout),
992                module: &module,
993                entry_point: Some("coop_gemv_dual"),
994                compilation_options: Default::default(),
995                cache: native_pipeline_cache.as_ref(),
996            });
997            let dual_mr = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
998                label: Some("Dual GEMV Multi-Row Pipeline"),
999                layout: Some(&layout),
1000                module: &module,
1001                entry_point: Some("coop_gemv_dual_mr"),
1002                compilation_options: Default::default(),
1003                cache: native_pipeline_cache.as_ref(),
1004            });
1005            (dual, dual_mr)
1006        };
1007
1008        // Triple Q+K+V GEMV (shared act, GQA) — resident mega-pass: 3 dispatches → 1.
1009        #[cfg(not(target_arch = "wasm32"))]
1010        let triple_gemv_bind_layout =
1011            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1012                label: Some("TripleGemvBGL"),
1013                entries: &[
1014                    // 0 input, 1 Wq, 2 params, 3 out_q, 4 Wk, 5 out_k, 6 Wv, 7 out_v
1015                    wgpu::BindGroupLayoutEntry {
1016                        binding: 0,
1017                        visibility: wgpu::ShaderStages::COMPUTE,
1018                        ty: wgpu::BindingType::Buffer {
1019                            ty: wgpu::BufferBindingType::Storage { read_only: true },
1020                            has_dynamic_offset: false,
1021                            min_binding_size: None,
1022                        },
1023                        count: None,
1024                    },
1025                    wgpu::BindGroupLayoutEntry {
1026                        binding: 1,
1027                        visibility: wgpu::ShaderStages::COMPUTE,
1028                        ty: wgpu::BindingType::Buffer {
1029                            ty: wgpu::BufferBindingType::Storage { read_only: true },
1030                            has_dynamic_offset: false,
1031                            min_binding_size: None,
1032                        },
1033                        count: None,
1034                    },
1035                    wgpu::BindGroupLayoutEntry {
1036                        binding: 2,
1037                        visibility: wgpu::ShaderStages::COMPUTE,
1038                        ty: wgpu::BindingType::Buffer {
1039                            ty: wgpu::BufferBindingType::Uniform,
1040                            has_dynamic_offset: false,
1041                            min_binding_size: None,
1042                        },
1043                        count: None,
1044                    },
1045                    wgpu::BindGroupLayoutEntry {
1046                        binding: 3,
1047                        visibility: wgpu::ShaderStages::COMPUTE,
1048                        ty: wgpu::BindingType::Buffer {
1049                            ty: wgpu::BufferBindingType::Storage { read_only: false },
1050                            has_dynamic_offset: false,
1051                            min_binding_size: None,
1052                        },
1053                        count: None,
1054                    },
1055                    wgpu::BindGroupLayoutEntry {
1056                        binding: 4,
1057                        visibility: wgpu::ShaderStages::COMPUTE,
1058                        ty: wgpu::BindingType::Buffer {
1059                            ty: wgpu::BufferBindingType::Storage { read_only: true },
1060                            has_dynamic_offset: false,
1061                            min_binding_size: None,
1062                        },
1063                        count: None,
1064                    },
1065                    wgpu::BindGroupLayoutEntry {
1066                        binding: 5,
1067                        visibility: wgpu::ShaderStages::COMPUTE,
1068                        ty: wgpu::BindingType::Buffer {
1069                            ty: wgpu::BufferBindingType::Storage { read_only: false },
1070                            has_dynamic_offset: false,
1071                            min_binding_size: None,
1072                        },
1073                        count: None,
1074                    },
1075                    wgpu::BindGroupLayoutEntry {
1076                        binding: 6,
1077                        visibility: wgpu::ShaderStages::COMPUTE,
1078                        ty: wgpu::BindingType::Buffer {
1079                            ty: wgpu::BufferBindingType::Storage { read_only: true },
1080                            has_dynamic_offset: false,
1081                            min_binding_size: None,
1082                        },
1083                        count: None,
1084                    },
1085                    wgpu::BindGroupLayoutEntry {
1086                        binding: 7,
1087                        visibility: wgpu::ShaderStages::COMPUTE,
1088                        ty: wgpu::BindingType::Buffer {
1089                            ty: wgpu::BufferBindingType::Storage { read_only: false },
1090                            has_dynamic_offset: false,
1091                            min_binding_size: None,
1092                        },
1093                        count: None,
1094                    },
1095                ],
1096            });
1097        #[cfg(not(target_arch = "wasm32"))]
1098        let triple_gemv_pipeline = {
1099            let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
1100                label: Some("Triple GEMV Shader"),
1101                source: wgpu::ShaderSource::Wgsl(
1102                    include_str!("../shaders/triple_gemv.wgsl").into(),
1103                ),
1104            });
1105            let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1106                label: Some("TripleGemvPL"),
1107                bind_group_layouts: &[Some(&triple_gemv_bind_layout)],
1108                immediate_size: 0,
1109            });
1110            device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
1111                label: Some("Triple GEMV Pipeline"),
1112                layout: Some(&layout),
1113                module: &module,
1114                entry_point: Some("coop_gemv_triple"),
1115                compilation_options: Default::default(),
1116                cache: native_pipeline_cache.as_ref(),
1117            })
1118        };
1119
1120        // DirectML is a *second* D3D12 device next to wgpu. Always-on init competed for
1121        // A2000 VRAM and added driver overhead while the resident decode path uses wgpu.
1122        // Opt in with QUALIA_DIRECTML=1 (or QUALIA_LLM_DIRECTML=1). Default: wgpu-only.
1123        #[cfg(target_os = "windows")]
1124        let dml_status = {
1125            let want = matches!(
1126                std::env::var("QUALIA_DIRECTML")
1127                    .or_else(|_| std::env::var("QUALIA_LLM_DIRECTML"))
1128                    .ok()
1129                    .as_deref(),
1130                Some("1") | Some("true") | Some("on")
1131            );
1132            if !want {
1133                log::info!(
1134                    "LLM_LOAD|gpu-backend|0.45|DirectML deferred (set QUALIA_DIRECTML=1 to enable second D3D12 device)"
1135                );
1136                None
1137            } else {
1138                match crate::directml_bridge::DmlDevice::new() {
1139                    Ok(device) => {
1140                        log::info!(
1141                            "DirectML device initialization: Ok({})",
1142                            device.adapter_desc
1143                        );
1144                        log::info!(
1145                            "LLM_LOAD|gpu-backend|0.45|DirectML ready on {}",
1146                            device.adapter_desc
1147                        );
1148                        log::info!(
1149                            "LLM_LOAD|gpu-route|0.48|Streaming weights through DirectML with {:.1} GiB VRAM free",
1150                            bytes_to_gib(
1151                                device
1152                                    .local_budget_bytes
1153                                    .saturating_sub(device.local_usage_bytes)
1154                            )
1155                        );
1156                        Some(device)
1157                    }
1158                    Err(err) => {
1159                        log::warn!("DirectML device initialization failed: {:?}", err);
1160                        log::info!(
1161                            "LLM_LOAD|gpu-backend|0.45|DirectML unavailable; using wgpu fallback"
1162                        );
1163                        None
1164                    }
1165                }
1166            }
1167        };
1168
1169        #[cfg(not(target_os = "windows"))]
1170        {
1171            log::info!("LLM_LOAD|gpu-backend|0.45|Using wgpu fallback backend for native compute");
1172        }
1173
1174        #[cfg(not(target_arch = "wasm32"))]
1175        let pipeline_bind_layout = pipeline.get_bind_group_layout(0);
1176        // Keep the explicit non-exclusive CoopGemvBGL (created above). Do NOT replace with
1177        // pipeline.get_bind_group_layout — that yields exclusive layouts and breaks multi-row.
1178        #[cfg(not(target_arch = "wasm32"))]
1179        let embedding_bind_layout = embedding_pipeline.get_bind_group_layout(0);
1180        #[cfg(not(target_arch = "wasm32"))]
1181        let attention_bind_layout = attention_pipeline.get_bind_group_layout(0);
1182        #[cfg(not(target_arch = "wasm32"))]
1183        let elem_silu_mul_bind_layout = elem_silu_mul_pipeline.get_bind_group_layout(0);
1184        #[cfg(not(target_arch = "wasm32"))]
1185        let attention_kv_gemm_params = device.create_buffer(&wgpu::BufferDescriptor {
1186            label: Some("AttentionKvGemmParams"),
1187            size: 256 * 2,
1188            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1189            mapped_at_creation: false,
1190        });
1191        #[cfg(not(target_arch = "wasm32"))]
1192        let attention_kv_params = device.create_buffer(&wgpu::BufferDescriptor {
1193            label: Some("AttentionKvWriteParams"),
1194            size: 256 * 2,
1195            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1196            mapped_at_creation: false,
1197        });
1198
1199        let engine = Self {
1200            #[cfg(target_arch = "wasm32")]
1201            device: wasm_device,
1202            #[cfg(target_arch = "wasm32")]
1203            queue: wasm_queue,
1204            pipeline,
1205            #[cfg(target_arch = "wasm32")]
1206            mmv_q8_0_pipeline,
1207            #[cfg(not(target_arch = "wasm32"))]
1208            native_pipeline_cache,
1209            #[cfg(not(target_arch = "wasm32"))]
1210            pipeline_bind_layout,
1211            #[cfg(not(target_arch = "wasm32"))]
1212            coop_gemv_pipeline,
1213            #[cfg(not(target_arch = "wasm32"))]
1214            coop_gemv_bind_layout,
1215            #[cfg(not(target_arch = "wasm32"))]
1216            coop_gemv_mr_pipeline,
1217            #[cfg(not(target_arch = "wasm32"))]
1218            coop_gemv_residual_pipeline,
1219            #[cfg(not(target_arch = "wasm32"))]
1220            coop_gemv_residual_bind_layout,
1221            #[cfg(not(target_arch = "wasm32"))]
1222            coop_gemv_residual_mr_pipeline,
1223            #[cfg(not(target_arch = "wasm32"))]
1224            coop_gemv_warp_pipeline,
1225            #[cfg(not(target_arch = "wasm32"))]
1226            coop_gemv_residual_warp_pipeline,
1227            #[cfg(not(target_arch = "wasm32"))]
1228            mock_pipeline,
1229            embedding_pipeline,
1230            #[cfg(not(target_arch = "wasm32"))]
1231            embedding_bind_layout,
1232            attention_pipeline,
1233            #[cfg(not(target_arch = "wasm32"))]
1234            attention_bind_layout,
1235            is_initialized: true,
1236            #[cfg(target_os = "windows")]
1237            dml: dml_status,
1238            gguf_mmap: None,
1239            #[cfg(target_arch = "wasm32")]
1240            cached_tokenizer: None,
1241            #[cfg(target_arch = "wasm32")]
1242            cached_tensor_index: None,
1243            #[cfg(target_arch = "wasm32")]
1244            cached_token_embd: None,
1245            #[cfg(target_arch = "wasm32")]
1246            p64_resident: None,
1247            #[cfg(not(target_arch = "wasm32"))]
1248            p64_index: None,
1249            #[cfg(not(target_arch = "wasm32"))]
1250            tensor_index_cache: None,
1251            tensor_data_offset: 0,
1252            hyperparams: crate::gguf_sharder::GgufHyperparams::default(),
1253            max_tensor_bytes: 0,
1254            gemm_input_buf: None,
1255            gemm_weight_buf: None,
1256            #[cfg(target_arch = "wasm32")]
1257            mc8_weight_arena: None,
1258            #[cfg(target_arch = "wasm32")]
1259            mc8_weights_resident: false,
1260            #[cfg(target_arch = "wasm32")]
1261            mc8_weight_role_stride: [0u64; 7],
1262            #[cfg(target_arch = "wasm32")]
1263            gemm_weight_buf_b: None,
1264            gemm_output_buf: None,
1265            gemm_params_buf: None,
1266            gemm_output_staging: None,
1267            output_topk_pipeline: None,
1268            output_topk_bind_layout: None,
1269            topk_cand_val_buf: None,
1270            topk_cand_idx_buf: None,
1271            topk_cand_staging: None,
1272            topk_params_buf: None,
1273            gemm_aux_buf: None,
1274            gemm_ffn_buf: None,
1275            #[cfg(target_arch = "wasm32")]
1276            prefill_scratch_buf: None,
1277            #[cfg(target_arch = "wasm32")]
1278            prefill_work_buf_a: None,
1279            #[cfg(target_arch = "wasm32")]
1280            prefill_work_buf_b: None,
1281            #[cfg(target_arch = "wasm32")]
1282            mc8_q_proj_buf: None,
1283            #[cfg(target_arch = "wasm32")]
1284            mc8_k_proj_buf: None,
1285            #[cfg(target_arch = "wasm32")]
1286            mc8_v_proj_buf: None,
1287            gemm_max_out_dim: MAX_STACK_GEMM_OUT as u32,
1288            gemm_max_input_floats: 0,
1289            kv_layout: None,
1290            kv_cache_gpu: None,
1291            kv_cache_cpu: None,
1292            attention_params_buf: None,
1293            attention_mask_buf: None,
1294            elem_rms_norm_pipeline,
1295            elem_silu_mul_pipeline,
1296            #[cfg(not(target_arch = "wasm32"))]
1297            elem_silu_mul_bind_layout,
1298            elem_add_residual_pipeline,
1299            elem_params_buf: None,
1300            norm_weight_buf: None,
1301            #[cfg(target_arch = "wasm32")]
1302            mc8_gemm_bind_layout,
1303            #[cfg(target_arch = "wasm32")]
1304            mc8_elem_bind_layout,
1305            #[cfg(target_arch = "wasm32")]
1306            mc8_attn_bind_layout,
1307            #[cfg(target_arch = "wasm32")]
1308            mc8_ffn_fused_bind_layout,
1309            #[cfg(target_arch = "wasm32")]
1310            mc8_ffn_fused_pipeline,
1311            #[cfg(not(target_arch = "wasm32"))]
1312            ffn_fused_bind_layout,
1313            #[cfg(not(target_arch = "wasm32"))]
1314            ffn_fused_pipeline,
1315            #[cfg(not(target_arch = "wasm32"))]
1316            ffn_fused_coop_pipeline,
1317            #[cfg(not(target_arch = "wasm32"))]
1318            ffn_fused_mr_pipeline,
1319            #[cfg(not(target_arch = "wasm32"))]
1320            ffn_fused_warp_pipeline,
1321            #[cfg(not(target_arch = "wasm32"))]
1322            dual_gemv_pipeline,
1323            #[cfg(not(target_arch = "wasm32"))]
1324            dual_gemv_mr_pipeline,
1325            #[cfg(not(target_arch = "wasm32"))]
1326            dual_gemv_bind_layout,
1327            #[cfg(not(target_arch = "wasm32"))]
1328            triple_gemv_pipeline,
1329            #[cfg(not(target_arch = "wasm32"))]
1330            triple_gemv_bind_layout,
1331            mc8_logits_resident_buf: None,
1332            mc8_logits_row_bytes: 0,
1333            #[cfg(not(target_arch = "wasm32"))]
1334            ternary_ffn: None,
1335            #[cfg(not(target_arch = "wasm32"))]
1336            resident_decode: super::resident_decode::ResidentDecodeState::Unbuilt,
1337            #[cfg(all(not(target_arch = "wasm32"), feature = "cuda"))]
1338            cuda_decode_plan: super::cuda_decode_plan::CudaDecodePlanState::Unbuilt,
1339            #[cfg(not(target_arch = "wasm32"))]
1340            prefill_arena: super::prefill_arena::PrefillArenaState::Unbuilt,
1341            #[cfg(not(target_arch = "wasm32"))]
1342            verify_arena: super::verify_arena::VerifyArenaState::Unbuilt,
1343            #[cfg(not(target_arch = "wasm32"))]
1344            gemm_resident_weights: std::sync::Mutex::new(std::collections::HashMap::new()),
1345            #[cfg(not(target_arch = "wasm32"))]
1346            ffn_fused_params: None,
1347            #[cfg(not(target_arch = "wasm32"))]
1348            attention_kv_gemm_params: Some(attention_kv_gemm_params),
1349            #[cfg(not(target_arch = "wasm32"))]
1350            attention_kv_params: Some(attention_kv_params),
1351            #[cfg(target_arch = "wasm32")]
1352            mc8_norm_resident_buf: None,
1353            #[cfg(target_arch = "wasm32")]
1354            mc8_norm_stride: 0,
1355            #[cfg(target_arch = "wasm32")]
1356            mc8_bg_cache: std::sync::Mutex::new(std::collections::HashMap::new()),
1357        };
1358        // Exercise the CPU elementwise oracle (ReLU) once at engine init so the fallback
1359        // path stays linked when GPU elem kernels are unavailable.
1360        let mut relu_probe = [-1.0f32, 2.0];
1361        let _ =
1362            super::cpu_ops::apply_cpu_elem_op(super::gpu_params::ELEM_OP_RELU, &mut relu_probe, 2);
1363        let _ = super::gpu_params::elem_op_label(super::gpu_params::ELEM_OP_RMS_NORM);
1364        let _ = engine.elem_gpu_pipeline(super::gpu_params::ELEM_OP_RMS_NORM);
1365        let _ = engine.elem_gpu_pipeline(super::gpu_params::ELEM_OP_ADD_RESIDUAL);
1366        Ok(engine)
1367    }
1368
1369    #[cfg(not(target_arch = "wasm32"))]
1370    pub fn new() -> Self {
1371        let handle = tokio::runtime::Handle::try_current().unwrap_or_else(|_| {
1372            let rt = Box::leak(Box::new(tokio::runtime::Runtime::new().unwrap()));
1373            rt.handle().clone()
1374        });
1375        tokio::task::block_in_place(|| {
1376            handle
1377                .block_on(Self::try_new())
1378                .expect("Failed to initialize native GGUF engine")
1379        })
1380    }
1381
1382    pub(crate) fn ensure_kv_cache(&mut self, h: &crate::gguf_sharder::GgufHyperparams) {
1383        let layout = match KvCacheLayout::from_hyperparams(h) {
1384            Some(l) => l,
1385            None => {
1386                #[cfg(target_arch = "wasm32")]
1387                wlog(
1388                    "[kv_cache] FAILED from_hyperparams (zero dims or exceeds KV_CACHE_MAX_BYTES)",
1389                );
1390                return;
1391            }
1392        };
1393        let bytes = (layout.total_f32_elems * std::mem::size_of::<f32>()) as wgpu::BufferAddress;
1394        // Native: honour U0 VRAM ledger pins. WASM: always allocate CPU mirror + wgpu storage
1395        // (ledger models host adapter VRAM; browser WebGPU has separate limits).
1396        #[cfg(not(target_arch = "wasm32"))]
1397        {
1398            let ledger = crate::gpu_context::global_vram_ledger();
1399            let orch = crate::gpu_context::universe_orchestrator();
1400            if !ledger.can_allocate_in_universe(
1401                &orch,
1402                crate::gpu_context::ComputeUniverse::LlmInference,
1403                bytes,
1404            ) {
1405                log::warn!(
1406                    "LLM_LOAD|kv-cache|denied|U0 budget {:.1} MiB used, need {:.1} MiB (mode {:?})",
1407                    ledger.universe_used_bytes(crate::gpu_context::ComputeUniverse::LlmInference)
1408                        as f64
1409                        / (1024.0 * 1024.0),
1410                    bytes as f64 / (1024.0 * 1024.0),
1411                    orch.active_mode,
1412                );
1413                return;
1414            }
1415        }
1416        let gpu = self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1417            label: Some("StaticKvCacheArena"),
1418            size: bytes.max(4),
1419            // COPY_SRC: MC8 pt3e L0 probe reads K/V slots via pipeline_read_kv_head.
1420            usage: wgpu::BufferUsages::STORAGE
1421                | wgpu::BufferUsages::COPY_DST
1422                | wgpu::BufferUsages::COPY_SRC,
1423            mapped_at_creation: false,
1424        });
1425        let cpu = vec![0f32; layout.total_f32_elems].into_boxed_slice();
1426        let attn_params_bytes = {
1427            #[cfg(target_arch = "wasm32")]
1428            {
1429                (MC8_MAX_ATTN_UNIFORM_CHUNK_SLOTS * MC8_UNIFORM_ALIGN) as wgpu::BufferAddress
1430            }
1431            #[cfg(not(target_arch = "wasm32"))]
1432            {
1433                std::mem::size_of::<AttentionGpuParams>() as wgpu::BufferAddress
1434            }
1435        };
1436        self.attention_params_buf =
1437            Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1438                label: Some("AttentionParams"),
1439                size: attn_params_bytes.max(4),
1440                usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1441                mapped_at_creation: false,
1442            }));
1443        let mask_bytes = (MAX_ATTN_MASK_UPLOAD_WORDS * std::mem::size_of::<u32>()).max(4);
1444        self.attention_mask_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1445            label: Some("AttentionKvMaskBatch"),
1446            size: mask_bytes as wgpu::BufferAddress,
1447            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1448            mapped_at_creation: false,
1449        }));
1450        self.kv_layout = Some(layout);
1451        self.kv_cache_gpu = Some(gpu);
1452        self.kv_cache_cpu = Some(cpu);
1453        // W5b Phase 4b: seed the dictionary atoms into each layer's arena tail (after the codes).
1454        #[cfg(not(target_arch = "wasm32"))]
1455        self.upload_dict_atoms();
1456        #[cfg(not(target_arch = "wasm32"))]
1457        {
1458            let ledger = crate::gpu_context::global_vram_ledger();
1459            ledger.record_kv_cache(bytes);
1460        }
1461        log::info!(
1462            "LLM_LOAD|kv-cache|0.86|Reserved {:.1} MiB KV cache (GPU + CPU mirror, context {})",
1463            bytes as f64 / (1024.0 * 1024.0),
1464            layout.max_context,
1465        );
1466        #[cfg(not(target_arch = "wasm32"))]
1467        eprintln!(
1468            "[gguf_bridge] KV arena {} slots ({:.1} MiB, {}), context={}",
1469            layout.total_f32_elems,
1470            bytes as f64 / (1024.0 * 1024.0),
1471            if layout.dict_k > 0 {
1472                "dict-coded"
1473            } else if layout.int8 {
1474                "int8+scale"
1475            } else {
1476                "f32"
1477            },
1478            layout.max_context,
1479        );
1480    }
1481
1482    /// Zero the static KV arena at the start of a new decode context (zero heap in decode).
1483    pub fn reset_kv_cache(&mut self) {
1484        let Some(layout) = self.kv_layout.as_ref() else {
1485            return;
1486        };
1487        let n = layout.total_f32_elems;
1488        if let Some(cpu) = self.kv_cache_cpu.as_mut() {
1489            for v in cpu.iter_mut().take(n) {
1490                unsafe { core::ptr::write_volatile(v, 0.0) };
1491            }
1492        }
1493        if let (Some(cpu), Some(gpu)) = (self.kv_cache_cpu.as_ref(), self.kv_cache_gpu.as_ref()) {
1494            self.gpu_queue()
1495                .write_buffer(gpu, 0, bytemuck::cast_slice(&cpu[..n]));
1496        }
1497        // W5b Phase 4b: the zero above wiped the atoms tail — re-seed it (dict mode only).
1498        #[cfg(not(target_arch = "wasm32"))]
1499        self.upload_dict_atoms();
1500    }
1501
1502    /// W5b Phase 4b: write the installed dictionary atoms into the tail of each layer's arena slice
1503    /// (after that layer's code region). No-op unless dict mode is active and atoms are installed.
1504    #[cfg(not(target_arch = "wasm32"))]
1505    pub(crate) fn upload_dict_atoms(&self) {
1506        let Some(layout) = self.kv_layout.as_ref() else {
1507            return;
1508        };
1509        if layout.dict_k == 0 {
1510            return;
1511        }
1512        let (Some(buf), Some((flat, na, hd))) = (
1513            self.kv_cache_gpu.as_ref(),
1514            crate::kv_dict_runtime::atoms_flat(),
1515        ) else {
1516            return;
1517        };
1518        let code_region = (layout.max_context * 2 * layout.n_kv_head * layout.dict_k) as usize;
1519        let per_layer_atoms = 2 * na * hd;
1520        let ls = layout.layer_stride as usize;
1521        for l in 0..layout.n_layer as usize {
1522            let s = l * per_layer_atoms;
1523            let e = ((l + 1) * per_layer_atoms).min(flat.len());
1524            if e <= s {
1525                break;
1526            }
1527            let dst_word = l * ls + code_region;
1528            self.gpu_queue().write_buffer(
1529                buf,
1530                (dst_word * 4) as u64,
1531                bytemuck::cast_slice(&flat[s..e]),
1532            );
1533        }
1534    }
1535
1536    pub fn get_kv_cache_cpu(&self) -> Option<&[f32]> {
1537        self.kv_cache_cpu.as_deref()
1538    }
1539
1540    pub fn set_kv_cache_cpu(&mut self, data: &[f32]) {
1541        let Some(layout) = self.kv_layout.as_ref() else {
1542            return;
1543        };
1544        let n = layout.total_f32_elems;
1545        if data.len() < n {
1546            return;
1547        }
1548        if let Some(cpu) = self.kv_cache_cpu.as_mut() {
1549            cpu[..n].copy_from_slice(&data[..n]);
1550        }
1551        if let (Some(cpu), Some(gpu)) = (self.kv_cache_cpu.as_ref(), self.kv_cache_gpu.as_ref()) {
1552            self.gpu_queue()
1553                .write_buffer(gpu, 0, bytemuck::cast_slice(&cpu[..n]));
1554        }
1555    }
1556
1557    /// Read the entire GPU KV-cache arena back to host as `f32` (native, cold path — forge KV capture,
1558    /// not the decode hot path). The returned flat buffer is interpretable via `KvCacheLayout::
1559    /// k_index`/`v_index` **only when the layout is f32** (int8 KV disabled at load); with an int8
1560    /// layout the bytes are packed i8 lanes + scales and this returns `None`.
1561    #[cfg(not(target_arch = "wasm32"))]
1562    pub fn read_kv_cache_gpu(&self) -> Option<Vec<f32>> {
1563        let gpu = self.kv_cache_gpu.as_ref()?;
1564        let layout = self.kv_layout.as_ref()?;
1565        if layout.int8 {
1566            return None;
1567        }
1568        let n = layout.total_f32_elems;
1569        let size = (n * std::mem::size_of::<f32>()) as wgpu::BufferAddress;
1570        let staging = self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1571            label: Some("KvCacheReadback"),
1572            size: size.max(4),
1573            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
1574            mapped_at_creation: false,
1575        });
1576        let mut encoder =
1577            self.gpu_device()
1578                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1579                    label: Some("KvReadback"),
1580                });
1581        encoder.copy_buffer_to_buffer(gpu, 0, &staging, 0, size);
1582        self.gpu_queue().submit(Some(encoder.finish()));
1583
1584        let slice = staging.slice(..);
1585        let (tx, rx) = futures_channel::oneshot::channel();
1586        slice.map_async(wgpu::MapMode::Read, move |v| {
1587            let _ = tx.send(v);
1588        });
1589        self.poll_wait();
1590        let handle = tokio::runtime::Handle::try_current().unwrap_or_else(|_| {
1591            let rt = Box::leak(Box::new(tokio::runtime::Runtime::new().unwrap()));
1592            rt.handle().clone()
1593        });
1594        if handle.block_on(rx).ok()?.is_err() {
1595            return None;
1596        }
1597        let data = slice
1598            .get_mapped_range()
1599            .expect("wgpu buffer map_range failed");
1600        let out: Vec<f32> = bytemuck::cast_slice(&data).to_vec();
1601        drop(data);
1602        staging.unmap();
1603        Some(out)
1604    }
1605
1606    /// Decode the current f32 KV cache into per-layer K and V vectors for token positions `0..n_tokens`
1607    /// (capped at `max_per_layer` per layer per stream). The **GPU-readback** capture route for the
1608    /// sparse-KV-dictionary go/no-go — reads the real decode-path K/V straight from VRAM, no CPU
1609    /// reference forward. Returns `None` on an int8 layout or if readback fails.
1610    #[cfg(not(target_arch = "wasm32"))]
1611    pub fn capture_kv_f32(
1612        &self,
1613        n_tokens: u32,
1614        max_per_layer: usize,
1615    ) -> Option<crate::kv_capture::KvCapture> {
1616        let layout = *self.kv_layout.as_ref()?;
1617        let flat = self.read_kv_cache_gpu()?;
1618        let n_layer = layout.n_layer as usize;
1619        let n_kv = layout.n_kv_head as usize;
1620        let head_dim = layout.head_dim as usize;
1621        if head_dim == 0 || n_kv == 0 || n_layer == 0 {
1622            return None;
1623        }
1624        let take = (n_tokens as usize).min(layout.max_context as usize);
1625        let mut k = vec![Vec::new(); n_layer];
1626        let mut v = vec![Vec::new(); n_layer];
1627        let read_vec = |base_of: &dyn Fn(u32) -> usize| -> Option<Vec<f32>> {
1628            let mut out = Vec::with_capacity(head_dim);
1629            for d in 0..head_dim {
1630                let idx = base_of(d as u32);
1631                if idx >= flat.len() {
1632                    return None;
1633                }
1634                out.push(flat[idx]);
1635            }
1636            Some(out)
1637        };
1638        for l in 0..n_layer {
1639            for pos in 0..take {
1640                let slot = layout.ring_slot(pos as u32);
1641                for hkv in 0..n_kv {
1642                    if k[l].len() < max_per_layer {
1643                        if let Some(vec_k) =
1644                            read_vec(&|d| layout.k_index(l as u32, slot, hkv as u32, d))
1645                        {
1646                            k[l].push(vec_k);
1647                        }
1648                    }
1649                    if v[l].len() < max_per_layer {
1650                        if let Some(vec_v) =
1651                            read_vec(&|d| layout.v_index(l as u32, slot, hkv as u32, d))
1652                        {
1653                            v[l].push(vec_v);
1654                        }
1655                    }
1656                }
1657            }
1658        }
1659        Some(crate::kv_capture::KvCapture { head_dim, k, v })
1660    }
1661
1662    pub(crate) fn ensure_gemm_buffers(&mut self, max_weight_bytes: usize, max_out_dim: u32) {
1663        // A1a: build the persistent GPU top-k pipeline + candidate buffers once (additive; the
1664        // existing argmax path is unaffected whether or not this succeeds).
1665        if self.output_topk_pipeline.is_none() {
1666            self.init_output_topk();
1667        }
1668        let need_input = MAX_STACK_GEMM_IN.max(MAX_PREFILL_BATCH_FLOATS);
1669        let prefill_bufs_ready = {
1670            #[cfg(target_arch = "wasm32")]
1671            {
1672                self.prefill_scratch_buf.is_some()
1673                    && self.prefill_work_buf_a.is_some()
1674                    && self.prefill_work_buf_b.is_some()
1675            }
1676            #[cfg(not(target_arch = "wasm32"))]
1677            {
1678                true
1679            }
1680        };
1681        #[cfg(target_arch = "wasm32")]
1682        let weight_arena_ready = self.mc8_weight_arena.is_some();
1683        #[cfg(not(target_arch = "wasm32"))]
1684        let weight_arena_ready = true;
1685        if self.gemm_weight_buf.is_some()
1686            && max_weight_bytes <= self.max_tensor_bytes
1687            && self.gemm_max_input_floats >= need_input
1688            && prefill_bufs_ready
1689            && weight_arena_ready
1690        {
1691            return;
1692        }
1693        let w_bytes = max_weight_bytes.max(4) as wgpu::BufferAddress;
1694        let in_bytes = (need_input * 4) as wgpu::BufferAddress;
1695        let out_bytes = (max_out_dim as usize * 4).max(4) as wgpu::BufferAddress;
1696        self.gemm_input_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1697            label: Some("LayerGemmInput"),
1698            size: in_bytes,
1699            usage: wgpu::BufferUsages::STORAGE
1700                | wgpu::BufferUsages::COPY_DST
1701                | wgpu::BufferUsages::COPY_SRC,
1702            mapped_at_creation: false,
1703        }));
1704        self.gemm_weight_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1705            label: Some("LayerGemmWeight"),
1706            size: w_bytes,
1707            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1708            mapped_at_creation: false,
1709        }));
1710        #[cfg(target_arch = "wasm32")]
1711        {
1712            let weight_usage = wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST;
1713            // Initial arena buffers are placeholders — mc8_upload_all_resident_weights replaces
1714            // them with properly-sized per-role buffers. Using minimal size here avoids a
1715            // transient GPU memory peak of 7 × w_bytes (~336 MB for SmolLM2-360M Q8_0) that
1716            // coexists with the resident buffers during the async swap, causing WebGPU OOM.
1717            let arena_min = 4u64;
1718            let mk_arena = |label: &str| {
1719                self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1720                    label: Some(label),
1721                    size: arena_min,
1722                    usage: weight_usage,
1723                    mapped_at_creation: false,
1724                })
1725            };
1726            let qkv_k = mk_arena("MC8WeightAttnK");
1727            let qkv_v = mk_arena("MC8WeightAttnV");
1728            let qkv_q = mk_arena("MC8WeightAttnQ");
1729            let o_proj = mk_arena("MC8WeightOProj");
1730            let gate = mk_arena("MC8WeightGate");
1731            let up = mk_arena("MC8WeightUp");
1732            let down = mk_arena("MC8WeightDown");
1733            self.mc8_weight_arena = Some(Mc8WeightArenaBufs {
1734                qkv_k,
1735                qkv_v,
1736                qkv_q,
1737                o_proj,
1738                gate,
1739                up,
1740                down,
1741            });
1742            // gemm_weight_buf_b is a legacy decode-path ping-pong that is never read;
1743            // allocate at minimal size to avoid wasting GPU memory.
1744            self.gemm_weight_buf_b =
1745                Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1746                    label: Some("LayerGemmWeightB"),
1747                    size: arena_min,
1748                    usage: weight_usage,
1749                    mapped_at_creation: false,
1750                }));
1751        }
1752        self.gemm_output_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1753            label: Some("LayerGemmOutput"),
1754            size: out_bytes,
1755            usage: wgpu::BufferUsages::STORAGE
1756                | wgpu::BufferUsages::COPY_SRC
1757                | wgpu::BufferUsages::COPY_DST,
1758            mapped_at_creation: false,
1759        }));
1760        let gemm_params_bytes = {
1761            #[cfg(target_arch = "wasm32")]
1762            {
1763                (MC8_MAX_GEMM_UNIFORM_CHUNK_SLOTS * MC8_UNIFORM_ALIGN) as wgpu::BufferAddress
1764            }
1765            #[cfg(not(target_arch = "wasm32"))]
1766            {
1767                std::mem::size_of::<GemmGpuParams>() as wgpu::BufferAddress
1768            }
1769        };
1770        self.gemm_params_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1771            label: Some("LayerGemmParams"),
1772            size: gemm_params_bytes.max(4),
1773            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1774            mapped_at_creation: false,
1775        }));
1776        #[cfg(target_arch = "wasm32")]
1777        let staging_bytes = out_bytes.max((65536 * 4) as wgpu::BufferAddress);
1778        #[cfg(not(target_arch = "wasm32"))]
1779        let staging_bytes = out_bytes;
1780        self.gemm_output_staging = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1781            label: Some("LayerGemmStaging"),
1782            size: staging_bytes,
1783            usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
1784            mapped_at_creation: false,
1785        }));
1786        self.gemm_aux_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1787            label: Some("LayerGemmAux"),
1788            size: out_bytes,
1789            usage: wgpu::BufferUsages::STORAGE
1790                | wgpu::BufferUsages::COPY_DST
1791                | wgpu::BufferUsages::COPY_SRC,
1792            mapped_at_creation: false,
1793        }));
1794        self.gemm_ffn_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1795            label: Some("LayerGemmFfnUp"),
1796            size: out_bytes,
1797            usage: wgpu::BufferUsages::STORAGE
1798                | wgpu::BufferUsages::COPY_DST
1799                | wgpu::BufferUsages::COPY_SRC,
1800            mapped_at_creation: false,
1801        }));
1802        #[cfg(target_arch = "wasm32")]
1803        {
1804            self.prefill_scratch_buf =
1805                Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1806                    label: Some("PrefillBatchScratch"),
1807                    size: in_bytes,
1808                    usage: wgpu::BufferUsages::STORAGE
1809                        | wgpu::BufferUsages::COPY_DST
1810                        | wgpu::BufferUsages::COPY_SRC,
1811                    mapped_at_creation: false,
1812                }));
1813            // Per-token row: norm + gate + up + save (see encode_prefill_q_ffn_tail_fused).
1814            let work_row_floats =
1815                (MAX_HIDDEN_DIM + 2 * max_out_dim as usize + MAX_HIDDEN_DIM).max(4);
1816            let work_bytes =
1817                (PREFILL_CHUNK_SIZE * work_row_floats * 4).max(4) as wgpu::BufferAddress;
1818            let work_usage = wgpu::BufferUsages::STORAGE
1819                | wgpu::BufferUsages::COPY_DST
1820                | wgpu::BufferUsages::COPY_SRC;
1821            self.prefill_work_buf_a =
1822                Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1823                    label: Some("PrefillBatchWorkA"),
1824                    size: work_bytes,
1825                    usage: work_usage,
1826                    mapped_at_creation: false,
1827                }));
1828            self.prefill_work_buf_b =
1829                Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1830                    label: Some("PrefillBatchWorkB"),
1831                    size: work_bytes,
1832                    usage: work_usage,
1833                    mapped_at_creation: false,
1834                }));
1835            // Phase 5.5: Q/K/V projection scratch (parallel-GEMM output). work_bytes ≥ q_dim×tokens.
1836            self.mc8_q_proj_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1837                label: Some("MC8QProj"),
1838                size: work_bytes,
1839                usage: work_usage,
1840                mapped_at_creation: false,
1841            }));
1842            self.mc8_k_proj_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1843                label: Some("MC8KProj"),
1844                size: work_bytes,
1845                usage: work_usage,
1846                mapped_at_creation: false,
1847            }));
1848            self.mc8_v_proj_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1849                label: Some("MC8VProj"),
1850                size: work_bytes,
1851                usage: work_usage,
1852                mapped_at_creation: false,
1853            }));
1854        }
1855        let elem_params_bytes = {
1856            #[cfg(target_arch = "wasm32")]
1857            {
1858                (MC8_MAX_ELEM_UNIFORM_CHUNK_SLOTS * MC8_UNIFORM_ALIGN) as wgpu::BufferAddress
1859            }
1860            #[cfg(not(target_arch = "wasm32"))]
1861            {
1862                std::mem::size_of::<ElemGpuParams>() as wgpu::BufferAddress
1863            }
1864        };
1865        self.elem_params_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1866            label: Some("ElemParams"),
1867            size: elem_params_bytes.max(4),
1868            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1869            mapped_at_creation: false,
1870        }));
1871        let norm_bytes = (MAX_HIDDEN_DIM * 4) as wgpu::BufferAddress;
1872        self.norm_weight_buf = Some(self.gpu_device().create_buffer(&wgpu::BufferDescriptor {
1873            label: Some("NormWeights"),
1874            size: norm_bytes.max(4),
1875            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1876            mapped_at_creation: false,
1877        }));
1878        self.gemm_max_out_dim = max_out_dim;
1879        self.gemm_max_input_floats = need_input;
1880        self.max_tensor_bytes = max_weight_bytes;
1881    }
1882}