1#![allow(unused_imports)]
19
20use crate::buffer::{
21 Arena, ReadbackLayout, ReadbackStaging, TinyReadbackStaging, decode_mapped_readback_f32,
22 decode_tiny_mapped_f32, encode_readback_copies, plan_f32_uniform, read_f32_many_pooled,
23 schedule_readback_map, use_tiny_readback, wait_readback_map,
24};
25use crate::device::wgpu_device;
26use crate::kernels::{
27 ArgmaxParams, AttentionBwdParams, AttentionParams, BatchElementwiseRegionParams, BinaryParams,
28 Conv1dParams, Conv2dParams, Conv3dParams, CopyParams, CumsumBwdParams, CumsumParams,
29 DequantMatmulParams, ElementwiseRegionParams, ExpandParams, FmaParams, FusedResidualLnParams,
30 FusedResidualLnTeeParams, FusedResidualRmsNormParams, GatherAxisParams, GatherBwdParams,
31 GatherParams, GroupedMatmulParams, GruParams, Kernel, LayerNormBwdParams, LayerNormParams,
32 Mamba2Params, MatmulParams, MatmulQkvParams, NarrowConcatParams, Pool1dParams, Pool2dParams,
33 Pool3dParams, ReduceParams, RmsNormBwdParams, RnnParams, RopeBwdParams, RopeParams,
34 SampleParams, ScatterAddParams, SceParams, SelectiveScanParams, SoftmaxParams, TopKParams,
35 TransposeParams, UmapKnnParams, UnaryParams, WelchPeaksGpuParams, WhereParams, argmax_kernel,
36 attention_bwd_kernel, attention_kernel, batch_elementwise_region_kernel, binary_kernel,
37 cast_f32_to_f16_kernel, compare_kernel, concat_kernel, conv1d_kernel, conv2d_kernel,
38 conv3d_kernel, copy_kernel, cumsum_backward_kernel, cumsum_kernel, dequant_matmul_kernel,
39 elementwise_region_kernel, elementwise_region_spatial_kernel, expand_kernel, fma_kernel,
40 fused_residual_ln_kernel, fused_residual_ln_tee_kernel, fused_residual_rms_norm_kernel,
41 gather_axis_kernel, gather_backward_acc_kernel, gather_backward_zero_kernel, gather_kernel,
42 gather_split_kernel, grouped_matmul_kernel, gru_kernel,
43 layer_norm_backward_gamma_partial_kernel, layer_norm_backward_gamma_reduce_kernel,
44 layer_norm_backward_input_kernel, layernorm_kernel, mamba2_kernel,
45 matmul_coop_f16_vulkan_active_kernel, matmul_coop_f16_vulkan_kernel,
46 matmul_coop_f32_active_kernel, matmul_coop16_kernel, matmul_f16_compute_kernel,
47 matmul_f16w_kernel, matmul_kernel, matmul_qkv_coop_f16_vk_active_kernel,
48 matmul_qkv_coop_f16_vk_kernel, matmul_qkv_coop_f32_kernel, matmul_qkv_kernel,
49 matmul_wide_active_kernel, matmul_wide_kernel, narrow_kernel, pool1d_kernel, pool2d_kernel,
50 pool3d_kernel, reduce_kernel, rms_norm_backward_kernel, rms_norm_backward_param_kernel,
51 rnn_kernel, rope_backward_kernel, rope_kernel, sample_kernel, scatter_add_kernel,
52 selective_scan_kernel, softmax_cross_entropy_kernel, softmax_kernel, topk_kernel,
53 transpose_kernel, umap_knn_kernel, unary_f16_mirror_kernel, unary_kernel,
54 welch_peaks_gpu_kernel, where_kernel,
55};
56use rlx_ir::dynamic::{bind_graph, has_dynamic_dims, infer_bindings_from_f32_inputs, same_binding};
57use rlx_ir::op::{Activation, BinaryOp, CmpOp, MaskKind, ReduceOp};
58use rlx_ir::shape::DimBinding;
59use rlx_ir::{Graph, NodeId, Op};
60use std::collections::{HashMap, HashSet};
61use std::num::NonZeroU64;
62
63use super::*;
64
65impl WgpuExecutable {
66 pub fn run(&mut self, inputs: &[(&str, &[f32])]) -> Vec<Vec<f32>> {
67 self.run_read_outputs(inputs, None)
68 }
69
70 pub fn run_read_outputs(
71 &mut self,
72 inputs: &[(&str, &[f32])],
73 read_indices: Option<&[usize]>,
74 ) -> Vec<Vec<f32>> {
75 self.pending_read_indices = read_indices.map(|s| s.to_vec());
76 let outs = self.run_inner(inputs);
77 self.pending_read_indices = None;
78 outs
79 }
80
81 #[cfg(target_arch = "wasm32")]
91 pub async fn run_async(&mut self, inputs: &[(&str, &[f32])]) -> Vec<Vec<f32>> {
92 assert!(
93 !self
94 .schedule
95 .iter()
96 .any(|s| step_runs_on_host(s) || step_is_tail_host(s)),
97 "rlx-wgpu run_async: graph contains host-executed ops unsupported on wasm"
98 );
99
100 self.dispatch_only = true;
102 let _ = self.run_inner(inputs);
103 self.dispatch_only = false;
104
105 let dev =
106 wgpu_device().expect("rlx-wgpu: device not initialized (call init_wgpu_device first)");
107
108 let plan = self.readback_plan();
110 let out_ids_all: Vec<_> = self.graph.outputs.clone();
111 let out_ids: Vec<_> = plan.iter().map(|&i| out_ids_all[i]).collect();
112 let layout = ReadbackLayout::for_nodes(&self.arena, &out_ids);
113 if layout.regions.is_empty() {
114 return Vec::new();
115 }
116 ReadbackStaging::prepare(&dev.device, &mut self.readback_staging, layout.total_bytes);
117 let staging_buf = self
118 .readback_staging
119 .as_ref()
120 .expect("readback staging")
121 .buffer()
122 .clone();
123
124 let mut enc = dev
125 .device
126 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
127 label: Some("rlx-wgpu async readback"),
128 });
129 encode_readback_copies(&mut enc, &self.arena, &staging_buf, &out_ids, &layout);
130 dev.queue.submit(std::iter::once(enc.finish()));
131
132 crate::buffer::wasm_async::map_read_async(&staging_buf, layout.total_bytes)
133 .await
134 .expect("rlx-wgpu async buffer map failed");
135
136 let partial = decode_mapped_readback_f32(&staging_buf, &layout);
137 self.pack_readback_outputs(&plan, partial)
138 }
139
140 pub(crate) fn run_tail_host_audio_ops(&self, dev: &crate::device::WgpuDevice) {
141 if !self.schedule.iter().any(step_is_tail_host) {
142 return;
143 }
144 for step in &self.schedule {
145 if !step_is_tail_host(step) {
146 continue;
147 }
148 match step {
149 Step::WelchPeaksHost {
150 spec_byte_off,
151 dst_byte_off,
152 welch_batch,
153 n_fft,
154 n_segments,
155 k,
156 } => {
157 crate::welch_peaks_host::run_welch_peaks(
158 &self.arena,
159 &dev.device,
160 &dev.queue,
161 *spec_byte_off as usize,
162 *dst_byte_off as usize,
163 *welch_batch as usize,
164 *n_fft as usize,
165 *n_segments as usize,
166 *k as usize,
167 );
168 }
169 Step::LogMelHost {
170 spec_byte_off,
171 filt_byte_off,
172 dst_byte_off,
173 outer,
174 n_fft,
175 n_bins,
176 n_mels,
177 } => {
178 crate::log_mel_host::run_log_mel(
179 &self.arena,
180 &dev.device,
181 &dev.queue,
182 *spec_byte_off as usize,
183 *filt_byte_off as usize,
184 *dst_byte_off as usize,
185 *outer as usize,
186 *n_fft as usize,
187 *n_bins as usize,
188 *n_mels as usize,
189 );
190 }
191 Step::LogMelBackwardHost {
192 spec_byte_off,
193 filt_byte_off,
194 dy_byte_off,
195 dst_byte_off,
196 outer,
197 n_fft,
198 n_bins,
199 n_mels,
200 } => {
201 crate::log_mel_host::run_log_mel_backward(
202 &self.arena,
203 &dev.device,
204 &dev.queue,
205 *spec_byte_off as usize,
206 *filt_byte_off as usize,
207 *dy_byte_off as usize,
208 *dst_byte_off as usize,
209 *outer as usize,
210 *n_fft as usize,
211 *n_bins as usize,
212 *n_mels as usize,
213 );
214 }
215 _ => {}
216 }
217 }
218 }
219
220 pub(crate) fn run_inner(&mut self, inputs: &[(&str, &[f32])]) -> Vec<Vec<f32>> {
221 if self.unresolved.is_some() {
224 self.lazy_compile_for_inputs(inputs);
225 }
226 let dev = wgpu_device().expect("rlx-wgpu: device gone");
227 self.stage_gpu_handle_inputs(dev, inputs);
228 let skip_input_upload =
229 !rlx_ir::env::flag("RLX_WGPU_FORCE_INPUT_UPLOAD") && !self.coop_f16_vk;
230 for &(name, data) in inputs {
231 if let Some(&id) = self.input_offsets.get(name)
232 && self.arena.has(id)
233 {
234 if skip_input_upload {
235 let h = hash_f32_input(data);
236 if self.input_staging_hashes.get(name) == Some(&h) {
237 if self.arena.f16_buffer.is_some() {
238 self.arena.write_f16_shadow(&dev.queue, id, data);
239 }
240 continue;
241 }
242 self.arena.write_f32(&dev.queue, id, data);
243 self.input_staging_hashes.insert(name.to_string(), h);
244 } else {
245 self.arena.write_f32(&dev.queue, id, data);
246 }
247 }
248 }
249 for &(act_id, act, ref src_name) in &self.coop_f16_host_activations {
250 let src =
251 host_tensor_f32(src_name, inputs, &self.stashed_params).unwrap_or_else(|| {
252 panic!("rlx-wgpu CoopF16Vk host activation: missing tensor {src_name:?}")
253 });
254 let mirrored = apply_activation_host(act, src);
255 self.arena.write_f32(&dev.queue, act_id, &mirrored);
256 }
257 if !self.coop_f16_host_activations.is_empty() {
258 let flush = dev
260 .device
261 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
262 label: Some("rlx-wgpu host mirror flush"),
263 });
264 dev.queue.submit(std::iter::once(flush.finish()));
265 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
266 }
267
268 let active = self.active_extent.filter(|_| self.all_safe_for_active());
273 let scale = |full: u32| -> u32 {
274 match active {
275 Some((a, u)) if u > 0 => {
276 let f = full as usize;
277 (f * a).div_ceil(u).min(f) as u32
278 }
279 _ => full,
280 }
281 };
282
283 let need_uniform_writes = self.uniforms_active_extent != Some(active);
289 if need_uniform_writes {
290 let mut gpu_ui = 0usize;
291 for step in self.schedule.iter() {
292 if step_runs_on_host(step) {
293 continue;
294 }
295 match step {
296 Step::CastF32ToF16 { .. } => {
297 }
301 Step::Matmul {
302 m,
303 k,
304 n,
305 a_off_f32,
306 b_off_f32,
307 c_off_f32,
308 batch,
309 a_batch_stride,
310 b_batch_stride,
311 c_batch_stride,
312 has_bias,
313 bias_off_f32,
314 act_id,
315 b_is_param: _,
316 compute_precision: _,
317 } => {
318 let m_scaled = scale(*m);
322 let p = MatmulParams {
323 m: m_scaled,
324 k: *k,
325 n: *n,
326 a_off: *a_off_f32,
327 b_off: *b_off_f32,
328 c_off: *c_off_f32,
329 batch: *batch,
330 a_batch_stride: *a_batch_stride,
331 b_batch_stride: *b_batch_stride,
332 c_batch_stride: *c_batch_stride,
333 has_bias: *has_bias,
334 bias_off: *bias_off_f32,
335 act_id: *act_id,
336 _pad0: 0,
337 _pad1: 0,
338 _pad2: 0,
339 };
340 dev.queue
341 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
342 }
343 Step::Binary { params } | Step::Compare { params } => {
344 let mut p = *params;
345 p.n = scale(p.n);
346 dev.queue
347 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
348 }
349 Step::Unary { params, .. } => {
350 let mut p = *params;
351 p.n = scale(p.n);
352 dev.queue
353 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
354 }
355 Step::Where { params } => {
356 let mut p = *params;
357 p.n = scale(p.n);
358 dev.queue
359 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
360 }
361 Step::Fma { params } => {
362 let mut p = *params;
363 p.n = scale(p.n);
364 dev.queue
365 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
366 }
367 Step::Reduce { params } => {
368 let mut p = *params;
369 p.outer = scale(p.outer);
370 dev.queue
371 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
372 }
373 Step::Softmax { params } => {
374 let mut p = *params;
375 p.outer = scale(p.outer);
376 dev.queue
377 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
378 }
379 Step::SoftmaxCrossEntropy { params } => {
380 let mut p = *params;
381 p.outer = scale(p.outer);
382 dev.queue
383 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
384 }
385 Step::LayerNorm { params } => {
386 let mut p = *params;
387 p.outer = scale(p.outer);
388 dev.queue
389 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
390 }
391 Step::RmsNormBackwardInput { params }
392 | Step::RmsNormBackwardGamma { params }
393 | Step::RmsNormBackwardBeta { params } => {
394 let mut p = *params;
395 p.outer = scale(p.outer);
396 dev.queue
397 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
398 }
399 Step::LayerNormBackwardInput { params } => {
400 let mut p = *params;
401 p.outer = scale(p.outer);
402 dev.queue
403 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
404 }
405 Step::LayerNormBackwardGammaPartial { params, .. } => {
406 let mut p = *params;
407 p.outer = scale(p.outer);
408 dev.queue
409 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
410 }
411 Step::LayerNormBackwardGammaReduce { params } => {
412 dev.queue.write_buffer(
416 &self.uniforms[gpu_ui],
417 0,
418 bytemuck::bytes_of(params),
419 );
420 }
421 Step::CumsumBackward { params } => {
422 let mut p = *params;
423 p.outer = scale(p.outer);
424 dev.queue
425 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
426 }
427 Step::RopeBackward { params } => {
428 let mut p = *params;
429 p.seq = scale(p.seq);
430 dev.queue
431 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
432 }
433 Step::GatherBackward { params } => {
434 let mut p = *params;
435 p.outer = scale(p.outer);
436 dev.queue
437 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
438 }
439 Step::Cumsum { params } => {
440 let mut p = *params;
441 p.outer = scale(p.outer);
442 dev.queue
443 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
444 }
445 Step::FftGpu { .. } => {}
446 Step::Copy { params } => {
447 let mut p = *params;
448 p.n = scale(p.n);
449 dev.queue
450 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
451 }
452 Step::BufferCopy { .. } => {}
453 Step::ElementwiseRegion { params } => {
454 let mut p = *params;
456 p.len = scale(p.len);
457 dev.queue
458 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
459 }
460 Step::BatchElementwiseRegion { params } => {
461 let mut p = *params;
462 p.slice_len = scale(p.slice_len);
463 p.num_batch = scale(p.num_batch);
464 dev.queue
465 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
466 }
467 Step::Transpose { params, .. } => {
468 let mut p = *params;
473 if p.bucket_outermost == 1 && p.out_dim_0 > 0 {
474 let scaled_d0 = scale(p.out_dim_0);
475 let inner = p.out_total / p.out_dim_0;
476 p.out_total = scaled_d0 * inner;
477 }
478 dev.queue
479 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
480 }
481 Step::Narrow { params } => {
482 let mut p = *params;
483 p.total = scale(p.total);
484 dev.queue
485 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
486 }
487 Step::Concat { params } => {
488 let mut p = *params;
489 p.total = scale(p.total);
490 dev.queue
491 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
492 }
493 Step::Gather { params } => {
494 let mut p = *params;
495 p.n_out = scale(p.n_out);
496 dev.queue
497 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
498 }
499 Step::GatherAxis { params } => {
500 let mut p = *params;
501 p.total = scale(p.total);
502 dev.queue
503 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
504 }
505 Step::Attention { params, .. } => {
506 let mut p = *params;
511 p.seq_q = scale(p.seq_q);
512 p.seq_k = scale(p.seq_k);
513 dev.queue
514 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
515 }
516 Step::AttentionBackward { params, .. } => {
517 let mut p = *params;
518 if p.wrt == 0 {
519 p.seq_q = scale(p.seq_q);
520 } else {
521 p.seq_k = scale(p.seq_k);
522 }
523 dev.queue
524 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
525 }
526 Step::Rope { params } => {
527 let mut p = *params;
532 let s_active = scale(p.seq);
533 p.seq = s_active;
534 p.n_total = p.batch * s_active * p.last_dim;
535 dev.queue
536 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
537 }
538 Step::Expand { params, .. } => {
539 let mut p = *params;
541 if p.bucket_outermost == 1 && p.out_dim_0 > 0 {
542 let scaled_d0 = scale(p.out_dim_0);
543 let inner = p.out_total / p.out_dim_0;
544 p.out_total = scaled_d0 * inner;
545 }
546 dev.queue
547 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
548 }
549 Step::Argmax { params } => {
550 let mut p = *params;
551 p.outer = scale(p.outer);
552 dev.queue
553 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
554 }
555 Step::Pool2d { params } => {
556 let mut p = *params;
557 p.n = scale(p.n);
558 dev.queue
559 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
560 }
561 Step::Conv2d { params } => {
562 let mut p = *params;
563 p.n = scale(p.n);
564 dev.queue
565 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
566 }
567 Step::Pool1d { params } => {
568 let mut p = *params;
569 p.n = scale(p.n);
570 dev.queue
571 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
572 }
573 Step::Pool3d { params } => {
574 let mut p = *params;
575 p.n = scale(p.n);
576 dev.queue
577 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
578 }
579 Step::Conv1d { params } => {
580 let mut p = *params;
581 p.n = scale(p.n);
582 dev.queue
583 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
584 }
585 Step::Conv3d { params } => {
586 let mut p = *params;
587 p.n = scale(p.n);
588 dev.queue
589 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
590 }
591 Step::ScatterAdd { params } => {
592 let mut p = *params;
596 if p.op == 1 {
597 p.num_updates = scale(p.num_updates);
598 }
599 dev.queue
600 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
601 }
602 Step::TopK { params } => {
603 let mut p = *params;
604 p.outer = scale(p.outer);
605 dev.queue
606 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
607 }
608 Step::WelchPeaksGpu { params } => {
609 let mut p = *params;
610 p.welch_batch = scale(p.welch_batch);
611 dev.queue
612 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
613 }
614 Step::UmapKnn { params } => {
615 let mut p = *params;
616 p.n = scale(p.n);
617 dev.queue
618 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
619 }
620 Step::GroupedMatmul { params } => {
621 let mut p = *params;
622 p.m = scale(p.m);
623 dev.queue
624 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
625 }
626 Step::Sample { params } => {
627 let mut p = *params;
628 p.outer = scale(p.outer);
629 dev.queue
630 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
631 }
632 Step::SelectiveScan { params } => {
633 let mut p = *params;
635 p.seq = scale(p.seq);
636 dev.queue
637 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
638 }
639 Step::Mamba2 { params } => {
640 let mut p = *params;
641 p.seq = scale(p.seq);
642 dev.queue
643 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
644 }
645 Step::Gru { params } => {
646 let mut p = *params;
647 p.seq = scale(p.seq);
648 dev.queue
649 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
650 }
651 Step::Rnn { params } => {
652 let mut p = *params;
653 p.seq = scale(p.seq);
654 dev.queue
655 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
656 }
657 Step::DequantMatmul { params } => {
658 let mut p = *params;
659 p.m = scale(p.m);
660 dev.queue
661 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
662 }
663 Step::GatherSplit { .. }
664 | Step::DequantMatmulGguf { .. }
665 | Step::DequantGroupedMatmulGguf { .. }
666 | Step::GatedDeltaNet { .. }
667 | Step::Lstm { .. }
668 | Step::ConvTranspose2d { .. }
669 | Step::GroupNormHost { .. }
670 | Step::LayerNorm2dHost { .. }
671 | Step::ResizeNearest2xHost { .. }
672 | Step::ReverseHost { .. }
673 | Step::ArgReduceHost { .. }
674 | Step::GruHost { .. }
675 | Step::RnnHost { .. }
676 | Step::Llada2GroupLimitedGate { .. }
677 | Step::UmapKnnHost { .. }
678 | Step::MsDeformAttnHost { .. }
679 | Step::FftHost { .. }
680 | Step::ScanHost { .. }
681 | Step::Im2ColHost { .. }
682 | Step::RngNormalHost { .. }
683 | Step::RngUniformHost { .. }
684 | Step::WelchPeaksHost { .. }
685 | Step::LogMelHost { .. }
686 | Step::LogMelBackwardHost { .. } => {}
687 Step::FusedResidualLn { params } => {
688 let mut p = *params;
689 p.outer = scale(p.outer);
690 dev.queue
691 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
692 }
693 Step::FusedResidualLnTee { params } => {
694 let mut p = *params;
695 p.outer = scale(p.outer);
696 dev.queue
697 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
698 }
699 Step::FusedResidualRmsNorm { params } => {
700 let mut p = *params;
701 p.outer = scale(p.outer);
702 dev.queue
703 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
704 }
705 Step::MatmulQkv { params, kind: _ } => {
706 let mut p = *params;
707 p.m = scale(p.m);
708 dev.queue
709 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
710 }
711 #[cfg(feature = "splat")]
712 Step::GaussianSplatRender { .. }
713 | Step::GaussianSplatRenderBackward { .. }
714 | Step::GaussianSplatPrepare { .. }
715 | Step::GaussianSplatRasterize { .. } => {}
716 }
717 if !matches!(step, Step::FftGpu { .. }) {
718 gpu_ui += 1;
719 }
720 }
721 self.uniforms_active_extent = Some(active);
722 }
723
724 let mm_k = matmul_kernel(&dev.device);
726 let mm_w_active = matmul_wide_active_kernel(&dev.device);
727 let mm_f16w = matmul_f16w_kernel(&dev.device);
728 let mm_f16c = matmul_f16_compute_kernel(&dev.device);
729 let mm_coop = matmul_coop16_kernel(&dev.device);
730 let mm_coop_f16_vk = matmul_coop_f16_vulkan_kernel(&dev.device);
731 let mm_coop_f32 = matmul_coop_f32_active_kernel(&dev.device);
732 let mm_cast = cast_f32_to_f16_kernel(&dev.device);
733 let bk = binary_kernel(&dev.device);
734 let uk = unary_kernel(&dev.device);
735 let ck = compare_kernel(&dev.device);
736 let wk = where_kernel(&dev.device);
737 let fk = fma_kernel(&dev.device);
738 let mut step_i = 0;
739 let mut gpu_bi = 0usize;
740 let mut fft_i = 0usize;
741 while step_i < self.schedule.len() {
742 let mut enc = dev
743 .device
744 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
745 label: Some("rlx-wgpu run"),
746 });
747 {
748 let mut pass = enc.begin_compute_pass(&wgpu::ComputePassDescriptor {
749 label: Some("rlx-wgpu compute pass"),
750 timestamp_writes: None,
751 });
752 let mut pass_dispatched = false;
753 while step_i < self.schedule.len() {
754 if step_is_tail_host(&self.schedule[step_i]) {
755 step_i += 1;
756 continue;
757 }
758 if step_runs_on_host(&self.schedule[step_i]) {
759 break;
760 }
761 if pass_dispatched
766 && step_i > 0
767 && step_needs_pass_flush(&self.schedule[step_i], &self.schedule[step_i - 1])
768 {
769 break;
770 }
771 let step = &self.schedule[step_i];
772 let _perf = rlx_ir::perfetto::TraceSpan::new(step_name(step), "wgpu");
775 match step {
776 Step::CastF32ToF16 { params } => {
777 if let Some(cast_k) = mm_cast {
782 pass.set_pipeline(&cast_k.pipeline);
783 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
784 let (gx, gy, gz) = dispatch_dims(params.len, 64);
785 pass.dispatch_workgroups(gx, gy, gz);
786 }
787 }
788 Step::Matmul {
789 m,
790 n,
791 batch,
792 b_off_f32,
793 b_is_param,
794 compute_precision,
795 ..
796 } =>
797 {
804 #[allow(clippy::unnecessary_unwrap)]
805 let m_s = scale(*m);
809 if m_s == 0 {
810 continue;
811 }
812 let coop_f16_wide = mm_coop_f16_vk.is_some()
813 && *compute_precision == MatmulCompute::CoopF16Vk
814 && crate::coop_f16_vk::use_wide_matmul(
815 *b_off_f32,
816 *n,
817 &self.coop_f16_b_param,
818 &self.coop_f16_vk_wide_b,
819 );
820 pass.set_bind_group(
821 0,
822 coop_f16_vk_bind_group(self, gpu_bi, coop_f16_wide),
823 &[],
824 );
825 let f16w_opt_in = rlx_ir::env::flag("RLX_WGPU_F16_WEIGHTS");
835 if let Some(coop) = mm_coop.as_ref()
836 && *b_is_param
837 && *compute_precision == MatmulCompute::Coop16
838 {
839 pass.set_pipeline(&coop.pipeline);
845 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
846 } else if mm_coop_f16_vk.is_some()
847 && *compute_precision == MatmulCompute::CoopF16Vk
848 {
849 if coop_f16_wide {
850 dispatch_wide_f32_matmul(
851 &mut pass,
852 mm_w_active,
853 mm_k,
854 m_s,
855 *n,
856 *batch,
857 );
858 } else {
859 let n_eff = scale(*n);
860 let coop_vk =
861 matmul_coop_f16_vulkan_active_kernel(&dev.device, n_eff)
862 .expect("coop f16 vk kernel missing");
863 pass.set_pipeline(&coop_vk.pipeline);
864 pass.dispatch_workgroups(
865 m_s.div_ceil(16),
866 n.div_ceil(16),
867 *batch,
868 );
869 }
870 } else if let Some(coop_f32) = mm_coop_f32.as_ref()
871 && *b_is_param
872 && *compute_precision == MatmulCompute::CoopF32
873 {
874 pass.set_pipeline(&coop_f32.pipeline);
877 let backend = wgpu_device()
878 .map(|d| d.backend)
879 .unwrap_or(wgpu::Backend::Noop);
880 let (gx, gy) = if backend == wgpu::Backend::Metal {
881 (n.div_ceil(32), m_s.div_ceil(32))
882 } else {
883 (m_s.div_ceil(8), n.div_ceil(8))
884 };
885 pass.dispatch_workgroups(gx, gy, *batch);
886 } else if let Some(f16c) = mm_f16c.as_ref()
887 && *b_is_param
888 && *compute_precision == MatmulCompute::F16
889 {
890 pass.set_pipeline(&f16c.pipeline);
891 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
892 } else if let Some(f16w) = mm_f16w.as_ref()
893 && *b_is_param
894 && f16w_opt_in
895 {
896 pass.set_pipeline(&f16w.pipeline);
897 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
898 } else if m_s >= 32 && *n >= 64 {
899 pass.set_pipeline(&mm_w_active.pipeline);
900 let backend = wgpu_device()
901 .map(|d| d.backend)
902 .unwrap_or(wgpu::Backend::Noop);
903 let (gx, gy) = if matches!(
904 backend,
905 wgpu::Backend::Vulkan | wgpu::Backend::Dx12
906 ) {
907 (n.div_ceil(64), m_s.div_ceil(64))
908 } else {
909 (n.div_ceil(64), m_s.div_ceil(32))
910 };
911 pass.dispatch_workgroups(gx, gy, *batch);
912 } else {
913 pass.set_pipeline(&mm_k.pipeline);
914 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
915 }
916 }
917 Step::Binary { params } => {
918 let n_s = scale(params.n);
919 if n_s == 0 {
920 continue;
921 }
922 pass.set_pipeline(&bk.pipeline);
923 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
924 let (gx, gy, gz) = dispatch_dims(n_s, 64);
925 pass.dispatch_workgroups(gx, gy, gz);
926 }
927 Step::Compare { params } => {
928 let n_s = scale(params.n);
929 if n_s == 0 {
930 continue;
931 }
932 pass.set_pipeline(&ck.pipeline);
933 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
934 let (gx, gy, gz) = dispatch_dims(n_s, 64);
935 pass.dispatch_workgroups(gx, gy, gz);
936 }
937 Step::Unary { params, f16_mirror } => {
938 let n_s = scale(params.n);
939 if n_s == 0 {
940 continue;
941 }
942 if *f16_mirror {
943 if let Some(uk_f16) = unary_f16_mirror_kernel(&dev.device) {
944 pass.set_pipeline(&uk_f16.pipeline);
945 } else {
946 pass.set_pipeline(&uk.pipeline);
947 }
948 } else {
949 pass.set_pipeline(&uk.pipeline);
950 }
951 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
952 let (gx, gy, gz) = dispatch_dims(n_s, 64);
953 pass.dispatch_workgroups(gx, gy, gz);
954 }
955 Step::Where { params } => {
956 let n_s = scale(params.n);
957 if n_s == 0 {
958 continue;
959 }
960 pass.set_pipeline(&wk.pipeline);
961 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
962 let (gx, gy, gz) = dispatch_dims(n_s, 64);
963 pass.dispatch_workgroups(gx, gy, gz);
964 }
965 Step::Fma { params } => {
966 let n_s = scale(params.n);
967 if n_s == 0 {
968 continue;
969 }
970 pass.set_pipeline(&fk.pipeline);
971 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
972 let (gx, gy, gz) = dispatch_dims(n_s, 64);
973 pass.dispatch_workgroups(gx, gy, gz);
974 }
975 Step::Reduce { params } => {
976 let outer_s = scale(params.outer);
977 if outer_s == 0 {
978 continue;
979 }
980 let rk = reduce_kernel(&dev.device);
981 pass.set_pipeline(&rk.pipeline);
982 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
983 let total_out = outer_s.saturating_mul(params.inner);
984 if params.reduce_dim <= 64 {
985 let (gx, gy, gz) = dispatch_dims(total_out, 64);
987 pass.dispatch_workgroups(gx, gy, gz);
988 } else {
989 let (gx, gy, gz) = dispatch_dims(total_out, 1);
993 pass.dispatch_workgroups(gx, gy, gz);
994 }
995 }
996 Step::Softmax { params } => {
997 let outer_s = scale(params.outer);
998 if outer_s == 0 {
999 continue;
1000 }
1001 let sk = softmax_kernel(&dev.device);
1002 pass.set_pipeline(&sk.pipeline);
1003 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1004 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1005 pass.dispatch_workgroups(gx, gy, gz);
1006 }
1007 Step::SoftmaxCrossEntropy { params } => {
1008 let outer_s = scale(params.outer);
1009 if outer_s == 0 {
1010 continue;
1011 }
1012 let sk = softmax_cross_entropy_kernel(&dev.device);
1013 pass.set_pipeline(&sk.pipeline);
1014 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1015 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1016 pass.dispatch_workgroups(gx, gy, gz);
1017 }
1018 Step::LayerNorm { params } => {
1019 let outer_s = scale(params.outer);
1020 if outer_s == 0 {
1021 continue;
1022 }
1023 let lk = layernorm_kernel(&dev.device);
1024 pass.set_pipeline(&lk.pipeline);
1025 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1026 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1027 pass.dispatch_workgroups(gx, gy, gz);
1028 }
1029 Step::RmsNormBackwardInput { params } => {
1030 let outer_s = scale(params.outer);
1031 if outer_s == 0 {
1032 continue;
1033 }
1034 let rk = rms_norm_backward_kernel(&dev.device);
1035 pass.set_pipeline(&rk.pipeline);
1036 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1037 pass.dispatch_workgroups(outer_s, 1, 1);
1038 }
1039 Step::RmsNormBackwardGamma { params }
1040 | Step::RmsNormBackwardBeta { params } => {
1041 if params.inner == 0 {
1042 continue;
1043 }
1044 let rk = rms_norm_backward_param_kernel(&dev.device);
1045 pass.set_pipeline(&rk.pipeline);
1046 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1047 pass.dispatch_workgroups(1, 1, 1);
1048 }
1049 Step::LayerNormBackwardInput { params } => {
1050 let outer_s = scale(params.outer);
1051 if outer_s == 0 {
1052 continue;
1053 }
1054 let lk = layer_norm_backward_input_kernel(&dev.device);
1055 pass.set_pipeline(&lk.pipeline);
1056 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1057 pass.dispatch_workgroups(outer_s, 1, 1);
1058 }
1059 Step::LayerNormBackwardGammaPartial {
1060 params,
1061 num_workgroups,
1062 } => {
1063 if params.inner == 0 || *num_workgroups == 0 {
1064 continue;
1065 }
1066 let lk = layer_norm_backward_gamma_partial_kernel(&dev.device);
1067 pass.set_pipeline(&lk.pipeline);
1068 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1069 pass.dispatch_workgroups(*num_workgroups, 1, 1);
1070 }
1071 Step::LayerNormBackwardGammaReduce { params } => {
1072 if params.inner == 0 {
1073 continue;
1074 }
1075 let lk = layer_norm_backward_gamma_reduce_kernel(&dev.device);
1076 pass.set_pipeline(&lk.pipeline);
1077 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1078 pass.dispatch_workgroups(1, 1, 1);
1079 }
1080 Step::CumsumBackward { params } => {
1081 let outer_s = scale(params.outer);
1082 if outer_s == 0 {
1083 continue;
1084 }
1085 let ck = cumsum_backward_kernel(&dev.device);
1086 pass.set_pipeline(&ck.pipeline);
1087 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1088 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1089 pass.dispatch_workgroups(gx, gy, gz);
1090 }
1091 Step::RopeBackward { params } => {
1092 let seq_s = scale(params.seq);
1093 if seq_s == 0 {
1094 continue;
1095 }
1096 let rk = rope_backward_kernel(&dev.device);
1097 pass.set_pipeline(&rk.pipeline);
1098 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1099 let total = params.batch * seq_s * params.hidden;
1100 let (gx, gy, gz) = dispatch_dims(total, 64);
1101 pass.dispatch_workgroups(gx, gy, gz);
1102 }
1103 Step::GatherBackward { params } => {
1104 let outer_s = scale(params.outer);
1105 if outer_s == 0 {
1106 continue;
1107 }
1108 let total = outer_s * params.axis_dim * params.trailing;
1109 if total > 0 {
1110 let zk = gather_backward_zero_kernel(&dev.device);
1111 pass.set_pipeline(&zk.pipeline);
1112 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1113 let (gx, _, _) = dispatch_dims(total, 256);
1114 pass.dispatch_workgroups(gx, 1, 1);
1115 }
1116 let ak = gather_backward_acc_kernel(&dev.device);
1117 pass.set_pipeline(&ak.pipeline);
1118 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1119 pass.dispatch_workgroups(outer_s, 1, 1);
1120 }
1121 Step::Cumsum { params } => {
1122 let outer_s = scale(params.outer);
1123 if outer_s == 0 {
1124 continue;
1125 }
1126 let ck2 = cumsum_kernel(&dev.device);
1127 pass.set_pipeline(&ck2.pipeline);
1128 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1129 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1130 pass.dispatch_workgroups(gx, gy, gz);
1131 }
1132 Step::FftGpu {
1133 src_off,
1134 dst_off,
1135 outer,
1136 n,
1137 inverse,
1138 norm_scale,
1139 } => {
1140 let res = &self.fft_gpu_steps[fft_i];
1141 fft_i += 1;
1142 crate::fft_dispatch::dispatch_fft_gpu_in_pass(
1143 &dev.device,
1144 &dev.queue,
1145 &mut pass,
1146 res,
1147 *src_off,
1148 *dst_off,
1149 *outer,
1150 *n,
1151 *inverse != 0,
1152 *norm_scale,
1153 );
1154 }
1155 Step::Copy { params } => {
1156 let n_s = scale(params.n);
1157 if n_s == 0 {
1158 continue;
1159 }
1160 let ck2 = copy_kernel(&dev.device);
1161 pass.set_pipeline(&ck2.pipeline);
1162 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1163 let (gx, gy, gz) = dispatch_dims(n_s, 64);
1164 pass.dispatch_workgroups(gx, gy, gz);
1165 }
1166 Step::BufferCopy { .. } => {
1167 }
1169 Step::ElementwiseRegion { params } => {
1170 let len_s = scale(params.len);
1171 if len_s == 0 {
1172 continue;
1173 }
1174 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1175 if params.prologue == rlx_ir::REGION_PROLOGUE_RESIZE_NEAREST_2X_NCHW {
1176 let ek = elementwise_region_spatial_kernel(&dev.device);
1177 pass.set_pipeline(&ek.pipeline);
1178 let (gx, gy, gz) = dispatch_prologue_nchw(
1179 params.out_w,
1180 params.out_h,
1181 params.out_n * params.out_c,
1182 );
1183 pass.dispatch_workgroups(gx, gy, gz);
1184 } else {
1185 let ek = elementwise_region_kernel(&dev.device);
1186 pass.set_pipeline(&ek.pipeline);
1187 let (gx, gy, gz) = dispatch_dims(len_s, 64);
1188 pass.dispatch_workgroups(gx, gy, gz);
1189 }
1190 }
1191 Step::BatchElementwiseRegion { params } => {
1192 let slice_len_s = scale(params.slice_len);
1193 let num_batch_s = scale(params.num_batch);
1194 if slice_len_s == 0 || num_batch_s == 0 {
1195 continue;
1196 }
1197 let ek = batch_elementwise_region_kernel(&dev.device);
1198 pass.set_pipeline(&ek.pipeline);
1199 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1200 let (gx, gy, _) = dispatch_dims(slice_len_s, 64);
1201 pass.dispatch_workgroups(gx, gy, num_batch_s);
1202 }
1203 Step::Transpose { params, .. } => {
1204 let total_s = if params.bucket_outermost == 1 && params.out_dim_0 > 0 {
1208 let scaled_d0 = scale(params.out_dim_0);
1209 let inner = params.out_total / params.out_dim_0;
1210 scaled_d0 * inner
1211 } else {
1212 params.out_total
1213 };
1214 if total_s == 0 {
1215 continue;
1216 }
1217 let tk = transpose_kernel(&dev.device);
1218 pass.set_pipeline(&tk.pipeline);
1219 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1220 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1221 pass.dispatch_workgroups(gx, gy, gz);
1222 }
1223 Step::Narrow { params } => {
1224 let total_s = scale(params.total);
1225 if total_s == 0 {
1226 continue;
1227 }
1228 let nk = narrow_kernel(&dev.device);
1229 pass.set_pipeline(&nk.pipeline);
1230 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1231 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1232 pass.dispatch_workgroups(gx, gy, gz);
1233 }
1234 Step::Concat { params } => {
1235 let total_s = scale(params.total);
1236 if total_s == 0 {
1237 continue;
1238 }
1239 let cck = concat_kernel(&dev.device);
1240 pass.set_pipeline(&cck.pipeline);
1241 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1242 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1243 pass.dispatch_workgroups(gx, gy, gz);
1244 }
1245 Step::Gather { params } => {
1246 let n_out_s = scale(params.n_out);
1247 if n_out_s == 0 {
1248 continue;
1249 }
1250 let gk = gather_kernel(&dev.device);
1251 pass.set_pipeline(&gk.pipeline);
1252 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1253 let (gx, gy, gz) = dispatch_dims(n_out_s, 64);
1254 pass.dispatch_workgroups(gx, gy, gz);
1255 }
1256 Step::GatherAxis { params } => {
1257 let total_s = scale(params.total);
1258 if total_s == 0 {
1259 continue;
1260 }
1261 let gk = gather_axis_kernel(&dev.device);
1262 pass.set_pipeline(&gk.pipeline);
1263 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1264 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1265 pass.dispatch_workgroups(gx, gy, gz);
1266 }
1267 Step::Attention { params, .. } => {
1268 let seq_q_s = scale(params.seq_q);
1272 if seq_q_s == 0 {
1273 continue;
1274 }
1275 let ak = attention_kernel(&dev.device);
1276 pass.set_pipeline(&ak.pipeline);
1277 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1278 let total = params.batch * params.heads * seq_q_s;
1279 let (gx, gy, gz) = dispatch_dims(total, 64);
1280 pass.dispatch_workgroups(gx, gy, gz);
1281 }
1282 Step::AttentionBackward { params, .. } => {
1283 let axis = if params.wrt == 0 {
1284 params.seq_q
1285 } else {
1286 params.seq_k
1287 };
1288 let axis_s = scale(axis);
1289 if axis_s == 0 {
1290 continue;
1291 }
1292 let ak = attention_bwd_kernel(&dev.device);
1293 pass.set_pipeline(&ak.pipeline);
1294 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1295 let total = params.batch * params.heads * axis_s;
1296 let (gx, gy, gz) = dispatch_dims(total, 64);
1297 pass.dispatch_workgroups(gx, gy, gz);
1298 }
1299 Step::Rope { params } => {
1300 let s_active = scale(params.seq);
1303 let total_s = params.batch * s_active * params.last_dim;
1304 if total_s == 0 {
1305 continue;
1306 }
1307 let rk = rope_kernel(&dev.device);
1308 pass.set_pipeline(&rk.pipeline);
1309 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1310 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1311 pass.dispatch_workgroups(gx, gy, gz);
1312 }
1313 Step::Expand { params, .. } => {
1314 let total_s = if params.bucket_outermost == 1 && params.out_dim_0 > 0 {
1315 let scaled_d0 = scale(params.out_dim_0);
1316 let inner = params.out_total / params.out_dim_0;
1317 scaled_d0 * inner
1318 } else {
1319 params.out_total
1320 };
1321 if total_s == 0 {
1322 continue;
1323 }
1324 let ek = expand_kernel(&dev.device);
1325 pass.set_pipeline(&ek.pipeline);
1326 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1327 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1328 pass.dispatch_workgroups(gx, gy, gz);
1329 }
1330 Step::Argmax { params } => {
1331 let outer_s = scale(params.outer);
1332 if outer_s == 0 {
1333 continue;
1334 }
1335 let amk = argmax_kernel(&dev.device);
1336 pass.set_pipeline(&amk.pipeline);
1337 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1338 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1339 pass.dispatch_workgroups(gx, gy, gz);
1340 }
1341 Step::Pool2d { params } => {
1342 let n_s = scale(params.n);
1343 if n_s == 0 {
1344 continue;
1345 }
1346 let pk = pool2d_kernel(&dev.device);
1347 pass.set_pipeline(&pk.pipeline);
1348 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1349 let total = n_s * params.c * params.h_out * params.w_out;
1350 let (gx, gy, gz) = dispatch_dims(total, 64);
1351 pass.dispatch_workgroups(gx, gy, gz);
1352 }
1353 Step::Conv2d { params } => {
1354 let n_s = scale(params.n);
1355 if n_s == 0 {
1356 continue;
1357 }
1358 let ck2 = conv2d_kernel(&dev.device);
1359 pass.set_pipeline(&ck2.pipeline);
1360 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1361 let spatial = params.h_out * params.w_out;
1364 let sp_tiles = spatial.div_ceil(CONV2D_TILE);
1365 let total = n_s * params.c_out * sp_tiles;
1366 let (gx, gy, gz) = dispatch_dims(total, 64);
1367 pass.dispatch_workgroups(gx, gy, gz);
1368 }
1369 Step::Pool1d { params } => {
1370 let n_s = scale(params.n);
1371 if n_s == 0 {
1372 continue;
1373 }
1374 let pk = pool1d_kernel(&dev.device);
1375 pass.set_pipeline(&pk.pipeline);
1376 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1377 let total = n_s * params.c * params.l_out;
1378 let (gx, gy, gz) = dispatch_dims(total, 64);
1379 pass.dispatch_workgroups(gx, gy, gz);
1380 }
1381 Step::Pool3d { params } => {
1382 let n_s = scale(params.n);
1383 if n_s == 0 {
1384 continue;
1385 }
1386 let pk = pool3d_kernel(&dev.device);
1387 pass.set_pipeline(&pk.pipeline);
1388 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1389 let total = n_s * params.c * params.d_out * params.h_out * params.w_out;
1390 let (gx, gy, gz) = dispatch_dims(total, 64);
1391 pass.dispatch_workgroups(gx, gy, gz);
1392 }
1393 Step::Conv1d { params } => {
1394 let n_s = scale(params.n);
1395 if n_s == 0 {
1396 continue;
1397 }
1398 let ck = conv1d_kernel(&dev.device);
1399 pass.set_pipeline(&ck.pipeline);
1400 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1401 let total = n_s * params.c_out * params.l_out;
1402 let (gx, gy, gz) = dispatch_dims(total, 64);
1403 pass.dispatch_workgroups(gx, gy, gz);
1404 }
1405 Step::Conv3d { params } => {
1406 let n_s = scale(params.n);
1407 if n_s == 0 {
1408 continue;
1409 }
1410 let ck = conv3d_kernel(&dev.device);
1411 pass.set_pipeline(&ck.pipeline);
1412 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1413 let total =
1414 n_s * params.c_out * params.d_out * params.h_out * params.w_out;
1415 let (gx, gy, gz) = dispatch_dims(total, 64);
1416 pass.dispatch_workgroups(gx, gy, gz);
1417 }
1418 Step::ScatterAdd { params } => {
1419 let sk = scatter_add_kernel(&dev.device);
1420 pass.set_pipeline(&sk.pipeline);
1421 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1422 if params.op == 0 {
1428 let (gx, gy, gz) = dispatch_dims(params.out_total, 64);
1429 pass.dispatch_workgroups(gx, gy, gz);
1430 } else {
1431 pass.dispatch_workgroups(1, 1, 1);
1432 }
1433 }
1434 Step::TopK { params } => {
1435 let outer_s = scale(params.outer);
1436 if outer_s == 0 {
1437 continue;
1438 }
1439 let tk = topk_kernel(&dev.device);
1440 pass.set_pipeline(&tk.pipeline);
1441 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1442 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1443 pass.dispatch_workgroups(gx, gy, gz);
1444 }
1445 Step::WelchPeaksGpu { params } => {
1446 let batch_s = scale(params.welch_batch);
1447 if batch_s == 0 {
1448 continue;
1449 }
1450 let wk = welch_peaks_gpu_kernel(&dev.device);
1451 pass.set_pipeline(&wk.pipeline);
1452 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1453 let (gx, gy, gz) = dispatch_dims(batch_s, 64);
1454 pass.dispatch_workgroups(gx, gy, gz);
1455 }
1456 Step::UmapKnn { params } => {
1457 let n_s = scale(params.n);
1458 if n_s == 0 {
1459 continue;
1460 }
1461 let uk = umap_knn_kernel(&dev.device);
1462 pass.set_pipeline(&uk.pipeline);
1463 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1464 let (gx, gy, gz) = dispatch_dims(n_s, 64);
1465 pass.dispatch_workgroups(gx, gy, gz);
1466 }
1467 Step::GroupedMatmul { params } => {
1468 let m_s = scale(params.m);
1469 if m_s == 0 {
1470 continue;
1471 }
1472 let gk = grouped_matmul_kernel(&dev.device);
1473 pass.set_pipeline(&gk.pipeline);
1474 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1475 pass.dispatch_workgroups(params.n.div_ceil(8), m_s.div_ceil(8), 1);
1476 }
1477 Step::Sample { params } => {
1478 let outer_s = scale(params.outer);
1479 if outer_s == 0 {
1480 continue;
1481 }
1482 let sk = sample_kernel(&dev.device);
1483 pass.set_pipeline(&sk.pipeline);
1484 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1485 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1486 pass.dispatch_workgroups(gx, gy, gz);
1487 }
1488 Step::SelectiveScan { params } => {
1489 let ssk = selective_scan_kernel(&dev.device);
1494 pass.set_pipeline(&ssk.pipeline);
1495 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1496 let total = params.batch * params.hidden;
1497 let (gx, gy, gz) = dispatch_dims(total, 64);
1498 pass.dispatch_workgroups(gx, gy, gz);
1499 }
1500 Step::Mamba2 { params } => {
1501 let mk = mamba2_kernel(&dev.device);
1502 pass.set_pipeline(&mk.pipeline);
1503 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1504 let total = params.batch * params.heads * params.head_dim;
1505 let (gx, gy, gz) = dispatch_dims(total, 64);
1506 pass.dispatch_workgroups(gx, gy, gz);
1507 }
1508 Step::Gru { params } => {
1509 let gk = gru_kernel(&dev.device);
1511 pass.set_pipeline(&gk.pipeline);
1512 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1513 pass.dispatch_workgroups(params.batch, 1, 1);
1514 }
1515 Step::Rnn { params } => {
1516 let rk = rnn_kernel(&dev.device);
1517 pass.set_pipeline(&rk.pipeline);
1518 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1519 pass.dispatch_workgroups(params.batch, 1, 1);
1520 }
1521 Step::DequantMatmul { params } => {
1522 let m_s = scale(params.m);
1523 if m_s == 0 {
1524 continue;
1525 }
1526 let dk = dequant_matmul_kernel(&dev.device);
1527 pass.set_pipeline(&dk.pipeline);
1528 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1529 pass.dispatch_workgroups(params.n.div_ceil(8), m_s.div_ceil(8), 1);
1530 }
1531 Step::FusedResidualLn { params } => {
1532 let outer_s = scale(params.outer);
1533 if outer_s == 0 {
1534 continue;
1535 }
1536 let frk = fused_residual_ln_kernel(&dev.device);
1537 pass.set_pipeline(&frk.pipeline);
1538 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1539 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1540 pass.dispatch_workgroups(gx, gy, gz);
1541 }
1542 Step::FusedResidualLnTee { params } => {
1543 let outer_s = scale(params.outer);
1544 if outer_s == 0 {
1545 continue;
1546 }
1547 let frtk = fused_residual_ln_tee_kernel(&dev.device);
1548 pass.set_pipeline(&frtk.pipeline);
1549 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1550 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1551 pass.dispatch_workgroups(gx, gy, gz);
1552 }
1553 Step::FusedResidualRmsNorm { params } => {
1554 let outer_s = scale(params.outer);
1555 if outer_s == 0 {
1556 continue;
1557 }
1558 let frk = fused_residual_rms_norm_kernel(&dev.device);
1559 pass.set_pipeline(&frk.pipeline);
1560 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1561 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1562 pass.dispatch_workgroups(gx, gy, gz);
1563 }
1564 Step::MatmulQkv { params, kind } => {
1565 let m_s = scale(params.m);
1566 if m_s == 0 {
1567 continue;
1568 }
1569 let qkv_coop_wide = matches!(kind, MatmulQkvKind::CoopF16Vk)
1570 && crate::coop_f16_vk::use_wide_matmul(
1571 params.b_off,
1572 params.n,
1573 &self.coop_f16_b_param,
1574 &self.coop_f16_vk_wide_b,
1575 );
1576 pass.set_bind_group(
1577 0,
1578 coop_f16_vk_bind_group(self, gpu_bi, qkv_coop_wide),
1579 &[],
1580 );
1581 match kind {
1582 MatmulQkvKind::CoopF16Vk => {
1583 if qkv_coop_wide {
1584 pass.set_pipeline(&matmul_qkv_kernel(&dev.device).pipeline);
1585 pass.dispatch_workgroups(
1586 params.n.div_ceil(32),
1587 m_s.div_ceil(32),
1588 1,
1589 );
1590 } else {
1591 let n_eff = scale(params.n);
1592 let mqk = matmul_qkv_coop_f16_vk_active_kernel(
1593 &dev.device,
1594 n_eff,
1595 )
1596 .expect("coop f16 matmul_qkv kernel missing");
1597 pass.set_pipeline(&mqk.pipeline);
1598 pass.dispatch_workgroups(
1599 m_s.div_ceil(16),
1600 params.n.div_ceil(16),
1601 1,
1602 );
1603 }
1604 }
1605 MatmulQkvKind::CoopF32 => {
1606 pass.set_pipeline(
1607 &matmul_qkv_coop_f32_kernel(&dev.device)
1608 .expect("coop matmul_qkv kernel missing")
1609 .pipeline,
1610 );
1611 pass.dispatch_workgroups(
1612 params.n.div_ceil(32),
1613 m_s.div_ceil(32),
1614 1,
1615 );
1616 }
1617 MatmulQkvKind::F32 => {
1618 pass.set_pipeline(&matmul_qkv_kernel(&dev.device).pipeline);
1619 pass.dispatch_workgroups(
1620 params.n.div_ceil(32),
1621 m_s.div_ceil(32),
1622 1,
1623 );
1624 }
1625 }
1626 }
1627 Step::GatherSplit { .. }
1628 | Step::DequantMatmulGguf { .. }
1629 | Step::DequantGroupedMatmulGguf { .. }
1630 | Step::GatedDeltaNet { .. }
1631 | Step::Lstm { .. }
1632 | Step::ConvTranspose2d { .. }
1633 | Step::GroupNormHost { .. }
1634 | Step::LayerNorm2dHost { .. }
1635 | Step::ResizeNearest2xHost { .. }
1636 | Step::ReverseHost { .. }
1637 | Step::ArgReduceHost { .. }
1638 | Step::GruHost { .. }
1639 | Step::RnnHost { .. }
1640 | Step::Llada2GroupLimitedGate { .. }
1641 | Step::UmapKnnHost { .. }
1642 | Step::MsDeformAttnHost { .. }
1643 | Step::FftHost { .. }
1644 | Step::ScanHost { .. }
1645 | Step::Im2ColHost { .. }
1646 | Step::RngNormalHost { .. }
1647 | Step::RngUniformHost { .. }
1648 | Step::WelchPeaksHost { .. }
1649 | Step::LogMelHost { .. }
1650 | Step::LogMelBackwardHost { .. } => {}
1651 #[cfg(feature = "splat")]
1652 Step::GaussianSplatRender { .. }
1653 | Step::GaussianSplatRenderBackward { .. }
1654 | Step::GaussianSplatPrepare { .. }
1655 | Step::GaussianSplatRasterize { .. } => {}
1656 }
1657 if !matches!(step, Step::FftGpu { .. }) {
1658 gpu_bi += 1;
1659 }
1660 step_i += 1;
1661 pass_dispatched = true;
1662 }
1663 }
1664 let needs_f16_drain = step_i < self.schedule.len()
1665 && !step_runs_on_host(&self.schedule[step_i])
1666 && step_i > 0
1667 && step_needs_pass_flush(&self.schedule[step_i], &self.schedule[step_i - 1]);
1668 let gpu_schedule_done = step_i >= self.schedule.len();
1669 let skip_readback = rlx_ir::env::flag("RLX_BENCH_DISPATCH_ONLY") || self.dispatch_only;
1670 let defer_tail = gpu_schedule_done && self.schedule.iter().any(step_is_tail_host);
1671 let mut fused_readback: Option<(
1672 ReadbackLayout,
1673 std::sync::mpsc::Receiver<Result<(), wgpu::BufferAsyncError>>,
1674 Vec<usize>,
1675 )> = None;
1676 if gpu_schedule_done && !skip_readback && !defer_tail {
1677 if !self.gpu_handle_feeds.is_empty() {
1678 self.propagate_gpu_handle_feeds_on_gpu(dev, &mut enc);
1679 }
1680 let plan = self.readback_plan();
1681 let out_ids_all: Vec<_> = self.graph.outputs.clone();
1682 let out_ids: Vec<_> = plan.iter().map(|&i| out_ids_all[i]).collect();
1683 let layout = ReadbackLayout::for_nodes(&self.arena, &out_ids);
1684 if use_tiny_readback(&layout, out_ids.len()) && plan == vec![0] {
1685 if self.tiny_readback.is_none() {
1686 self.tiny_readback = Some(TinyReadbackStaging::new(&dev.device));
1687 }
1688 let tiny = self.tiny_readback.as_ref().expect("tiny readback");
1689 encode_readback_copies(&mut enc, &self.arena, tiny.buffer(), &out_ids, &layout);
1690 let map_rx = schedule_readback_map(&mut enc, tiny.buffer(), &layout);
1691 let sub = dev.queue.submit(std::iter::once(enc.finish()));
1692 wait_readback_map(&dev.device, sub, &map_rx, layout.total_bytes);
1693 map_rx.recv().unwrap().unwrap();
1694 return self.pack_readback_outputs(
1695 &plan,
1696 vec![decode_tiny_mapped_f32(tiny.buffer(), layout.total_bytes)],
1697 );
1698 }
1699 ReadbackStaging::prepare(
1700 &dev.device,
1701 &mut self.readback_staging,
1702 layout.total_bytes,
1703 );
1704 if let Some(staging) = self.readback_staging.as_ref() {
1705 encode_readback_copies(
1706 &mut enc,
1707 &self.arena,
1708 staging.buffer(),
1709 &out_ids,
1710 &layout,
1711 );
1712 let map_rx = schedule_readback_map(&mut enc, staging.buffer(), &layout);
1713 fused_readback = Some((layout, map_rx, plan));
1714 }
1715 }
1716 let main_submission = dev.queue.submit(std::iter::once(enc.finish()));
1717 if defer_tail {
1718 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1719 self.run_tail_host_audio_ops(dev);
1720 if !skip_readback {
1721 let mut rb_enc =
1722 dev.device
1723 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1724 label: Some("rlx-wgpu readback after tail-host"),
1725 });
1726 if !self.gpu_handle_feeds.is_empty() {
1727 self.propagate_gpu_handle_feeds_on_gpu(dev, &mut rb_enc);
1728 }
1729 let plan = self.readback_plan();
1730 let out_ids_all: Vec<_> = self.graph.outputs.clone();
1731 let out_ids: Vec<_> = plan.iter().map(|&i| out_ids_all[i]).collect();
1732 let layout = ReadbackLayout::for_nodes(&self.arena, &out_ids);
1733 if use_tiny_readback(&layout, out_ids.len()) && plan == vec![0] {
1734 if self.tiny_readback.is_none() {
1735 self.tiny_readback = Some(TinyReadbackStaging::new(&dev.device));
1736 }
1737 let tiny = self.tiny_readback.as_ref().expect("tiny readback");
1738 encode_readback_copies(
1739 &mut rb_enc,
1740 &self.arena,
1741 tiny.buffer(),
1742 &out_ids,
1743 &layout,
1744 );
1745 let map_rx = schedule_readback_map(&mut rb_enc, tiny.buffer(), &layout);
1746 let sub = dev.queue.submit(std::iter::once(rb_enc.finish()));
1747 wait_readback_map(&dev.device, sub, &map_rx, layout.total_bytes);
1748 map_rx.recv().unwrap().unwrap();
1749 return self.pack_readback_outputs(
1750 &plan,
1751 vec![decode_tiny_mapped_f32(tiny.buffer(), layout.total_bytes)],
1752 );
1753 }
1754 ReadbackStaging::prepare(
1755 &dev.device,
1756 &mut self.readback_staging,
1757 layout.total_bytes,
1758 );
1759 if let Some(staging) = self.readback_staging.as_ref() {
1760 encode_readback_copies(
1761 &mut rb_enc,
1762 &self.arena,
1763 staging.buffer(),
1764 &out_ids,
1765 &layout,
1766 );
1767 let map_rx = schedule_readback_map(&mut rb_enc, staging.buffer(), &layout);
1768 let sub = dev.queue.submit(std::iter::once(rb_enc.finish()));
1769 wait_readback_map(&dev.device, sub, &map_rx, layout.total_bytes);
1770 map_rx.recv().unwrap().unwrap();
1771 self.dump_node_stats_if_requested(dev);
1772 let partial = decode_mapped_readback_f32(staging.buffer(), &layout);
1773 return self.pack_readback_outputs(&plan, partial);
1774 }
1775 }
1776 }
1777 if needs_f16_drain {
1778 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1779 }
1780 let need_host_sync =
1781 step_i < self.schedule.len() && step_runs_on_host(&self.schedule[step_i]);
1782 if need_host_sync {
1783 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1784 }
1785 if gpu_schedule_done {
1786 if skip_readback || defer_tail {
1787 return self
1788 .graph
1789 .outputs
1790 .iter()
1791 .map(|&id| {
1792 let n = self.graph.node(id).shape.num_elements().unwrap_or(0);
1793 vec![0.0; n]
1794 })
1795 .collect();
1796 }
1797 if let (Some((layout, map_rx, plan)), Some(staging)) =
1798 (fused_readback, self.readback_staging.as_ref())
1799 {
1800 wait_readback_map(&dev.device, main_submission, &map_rx, layout.total_bytes);
1801 map_rx.recv().unwrap().unwrap();
1802 self.dump_node_stats_if_requested(dev);
1803 let partial = decode_mapped_readback_f32(staging.buffer(), &layout);
1804 return self.pack_readback_outputs(&plan, partial);
1805 }
1806 break;
1807 }
1808 match &self.schedule[step_i] {
1809 Step::BufferCopy {
1810 src_byte_off,
1811 dst_byte_off,
1812 bytes,
1813 } => {
1814 let src = *src_byte_off;
1817 let dst = *dst_byte_off;
1818 let nbytes = *bytes as u64;
1819 let elems = (nbytes / 4).max(1) as u32;
1820 let lo = src.min(dst);
1821 let hi = src.saturating_add(nbytes).max(dst.saturating_add(nbytes));
1822 let max_binding = dev.device.limits().max_storage_buffer_binding_size;
1823 if hi.saturating_sub(lo) > max_binding {
1829 let bytes_host = self.arena.read_bytes_range(
1830 &dev.device,
1831 &dev.queue,
1832 src as usize,
1833 nbytes as usize,
1834 );
1835 self.arena
1836 .write_bytes_range(&dev.queue, dst as usize, &bytes_host);
1837 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1838 step_i += 1;
1839 continue;
1840 }
1841 let mut base = (lo / 256) * 256;
1842 let span = hi.saturating_sub(base).max(1);
1848 let mut size = span.div_ceil(256) * 256;
1849 size = size.max(256).min(max_binding);
1850 if base.saturating_add(size) > self.arena.size as u64 {
1851 base = (self.arena.size as u64).saturating_sub(size);
1852 base = (base / 256) * 256;
1853 }
1854 let p = CopyParams {
1855 n: elems,
1856 in_off: (src.saturating_sub(base) / 4) as u32,
1857 out_off: (dst.saturating_sub(base) / 4) as u32,
1858 _p0: 0,
1859 _p1: 0,
1860 _p2: 0,
1861 _p3: 0,
1862 _p4: 0,
1863 };
1864 let ck = copy_kernel(&dev.device);
1865 let u = dev.device.create_buffer(&wgpu::BufferDescriptor {
1866 label: Some("rlx-wgpu arena_copy uniform"),
1867 size: std::mem::size_of::<CopyParams>() as u64,
1868 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1869 mapped_at_creation: false,
1870 });
1871 dev.queue.write_buffer(&u, 0, bytemuck::bytes_of(&p));
1872 let bg =
1873 bind_two_buf0_window(&dev.device, ck, &self.arena.buffer, base, size, &u);
1874 let mut enc =
1875 dev.device
1876 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1877 label: Some("rlx-wgpu arena_copy"),
1878 });
1879 {
1880 let mut pass = enc.begin_compute_pass(&wgpu::ComputePassDescriptor {
1881 label: Some("rlx-wgpu arena_copy pass"),
1882 ..Default::default()
1883 });
1884 pass.set_pipeline(&ck.pipeline);
1885 pass.set_bind_group(0, &bg, &[]);
1886 let (gx, gy, gz) = dispatch_dims(elems, 64);
1887 pass.dispatch_workgroups(gx, gy, gz);
1888 }
1889 dev.queue.submit(std::iter::once(enc.finish()));
1890 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1891 }
1892 Step::GatherSplit {
1893 n_out,
1894 n_idx,
1895 dim,
1896 vocab,
1897 table_byte_off,
1898 idx_byte_off,
1899 out_byte_off,
1900 } => {
1901 run_gather_split(
1902 &self.arena,
1903 &dev.device,
1904 &dev.queue,
1905 *n_out,
1906 *n_idx,
1907 *dim,
1908 *vocab,
1909 *table_byte_off as usize,
1910 *idx_byte_off as usize,
1911 *out_byte_off as usize,
1912 );
1913 }
1914 Step::DequantMatmulGguf {
1915 m,
1916 k,
1917 n,
1918 scheme_id,
1919 x_byte_off,
1920 w_byte_off,
1921 out_byte_off,
1922 } => {
1923 if *m == 1 && crate::gguf_gpu::gemv_supports_scheme(*scheme_id) {
1924 crate::gguf_gpu::run_dequant_matmul_gguf_gemv(
1928 &self.arena,
1929 &dev.device,
1930 &dev.queue,
1931 *k as usize,
1932 *n as usize,
1933 *scheme_id,
1934 *x_byte_off as usize,
1935 *w_byte_off as usize,
1936 *out_byte_off as usize,
1937 );
1938 } else if self.dequant_scratch_off > 0 {
1939 crate::gguf_gpu::run_dequant_matmul_gguf_gpu(
1940 &self.arena,
1941 &dev.device,
1942 &dev.queue,
1943 *m as usize,
1944 *k as usize,
1945 *n as usize,
1946 *scheme_id,
1947 *x_byte_off as usize,
1948 *w_byte_off as usize,
1949 self.dequant_scratch_off,
1950 *out_byte_off as usize,
1951 );
1952 } else {
1953 crate::gguf_host::run_dequant_matmul_gguf(
1954 &self.arena,
1955 &dev.device,
1956 &dev.queue,
1957 *m as usize,
1958 *k as usize,
1959 *n as usize,
1960 *scheme_id,
1961 *x_byte_off as usize,
1962 *w_byte_off as usize,
1963 *out_byte_off as usize,
1964 );
1965 }
1966 }
1967 Step::DequantGroupedMatmulGguf {
1968 m,
1969 k,
1970 n,
1971 num_experts,
1972 scheme_id,
1973 x_byte_off,
1974 w_byte_off,
1975 idx_byte_off,
1976 out_byte_off,
1977 } => {
1978 if self.dequant_scratch_off > 0 {
1979 crate::gguf_gpu::run_dequant_grouped_matmul_gguf_gpu(
1980 &self.arena,
1981 &dev.device,
1982 &dev.queue,
1983 *m as usize,
1984 *k as usize,
1985 *n as usize,
1986 *num_experts as usize,
1987 *scheme_id,
1988 *x_byte_off as usize,
1989 *w_byte_off as usize,
1990 *idx_byte_off as usize,
1991 self.dequant_scratch_off,
1992 *out_byte_off as usize,
1993 );
1994 } else {
1995 crate::gguf_host::run_dequant_grouped_matmul_gguf(
1996 &self.arena,
1997 &dev.device,
1998 &dev.queue,
1999 *m as usize,
2000 *k as usize,
2001 *n as usize,
2002 *num_experts as usize,
2003 *scheme_id,
2004 *x_byte_off as usize,
2005 *w_byte_off as usize,
2006 *idx_byte_off as usize,
2007 *out_byte_off as usize,
2008 );
2009 }
2010 }
2011 Step::GatedDeltaNet {
2012 q_byte_off,
2013 k_byte_off,
2014 v_byte_off,
2015 g_byte_off,
2016 beta_byte_off,
2017 state_byte_off,
2018 dst_byte_off,
2019 batch,
2020 seq,
2021 heads,
2022 state_size,
2023 use_carry,
2024 } => {
2025 crate::gdn_host::run_gated_delta_net(
2026 &self.arena,
2027 &dev.device,
2028 &dev.queue,
2029 *q_byte_off as usize,
2030 *k_byte_off as usize,
2031 *v_byte_off as usize,
2032 *g_byte_off as usize,
2033 *beta_byte_off as usize,
2034 *state_byte_off as usize,
2035 *dst_byte_off as usize,
2036 *batch as usize,
2037 *seq as usize,
2038 *heads as usize,
2039 *state_size as usize,
2040 *use_carry,
2041 );
2042 }
2043 Step::Lstm {
2044 x_byte_off,
2045 w_ih_byte_off,
2046 w_hh_byte_off,
2047 bias_byte_off,
2048 h0_byte_off,
2049 c0_byte_off,
2050 dst_byte_off,
2051 batch,
2052 seq,
2053 input_size,
2054 hidden,
2055 num_layers,
2056 bidirectional,
2057 carry,
2058 } => {
2059 crate::lstm_host::run_lstm(
2060 &self.arena,
2061 &dev.device,
2062 &dev.queue,
2063 *x_byte_off as usize,
2064 *w_ih_byte_off as usize,
2065 *w_hh_byte_off as usize,
2066 *bias_byte_off as usize,
2067 *h0_byte_off as usize,
2068 *c0_byte_off as usize,
2069 *dst_byte_off as usize,
2070 *batch as usize,
2071 *seq as usize,
2072 *input_size as usize,
2073 *hidden as usize,
2074 *num_layers as usize,
2075 *bidirectional,
2076 *carry,
2077 );
2078 }
2079 Step::ConvTranspose2d {
2080 src_byte_off,
2081 weight_byte_off,
2082 dst_byte_off,
2083 n,
2084 c_in,
2085 h,
2086 w_in,
2087 c_out,
2088 h_out,
2089 w_out,
2090 kh,
2091 kw,
2092 sh,
2093 sw,
2094 ph,
2095 pw,
2096 dh,
2097 dw,
2098 groups,
2099 } => {
2100 crate::conv_transpose2d_host::run_conv_transpose2d(
2101 &self.arena,
2102 &dev.device,
2103 &dev.queue,
2104 *src_byte_off as usize,
2105 *weight_byte_off as usize,
2106 *dst_byte_off as usize,
2107 *n as usize,
2108 *c_in as usize,
2109 *h as usize,
2110 *w_in as usize,
2111 *c_out as usize,
2112 *h_out as usize,
2113 *w_out as usize,
2114 *kh as usize,
2115 *kw as usize,
2116 *sh as usize,
2117 *sw as usize,
2118 *ph as usize,
2119 *pw as usize,
2120 *dh as usize,
2121 *dw as usize,
2122 *groups as usize,
2123 );
2124 }
2125 Step::GroupNormHost {
2126 src_byte_off,
2127 gamma_byte_off,
2128 beta_byte_off,
2129 dst_byte_off,
2130 n,
2131 c,
2132 h,
2133 w,
2134 num_groups,
2135 eps,
2136 } => {
2137 crate::vision_host::run_group_norm(
2138 &self.arena,
2139 &dev.device,
2140 &dev.queue,
2141 *src_byte_off as usize,
2142 *gamma_byte_off as usize,
2143 *beta_byte_off as usize,
2144 *dst_byte_off as usize,
2145 *n as usize,
2146 *c as usize,
2147 *h as usize,
2148 *w as usize,
2149 *num_groups as usize,
2150 *eps,
2151 );
2152 }
2153 Step::LayerNorm2dHost {
2154 src_byte_off,
2155 gamma_byte_off,
2156 beta_byte_off,
2157 dst_byte_off,
2158 n,
2159 c,
2160 h,
2161 w,
2162 eps,
2163 } => {
2164 crate::vision_host::run_layer_norm2d(
2165 &self.arena,
2166 &dev.device,
2167 &dev.queue,
2168 *src_byte_off as usize,
2169 *gamma_byte_off as usize,
2170 *beta_byte_off as usize,
2171 *dst_byte_off as usize,
2172 *n as usize,
2173 *c as usize,
2174 *h as usize,
2175 *w as usize,
2176 *eps,
2177 );
2178 }
2179 Step::ResizeNearest2xHost {
2180 src_byte_off,
2181 dst_byte_off,
2182 n,
2183 c,
2184 h,
2185 w,
2186 } => {
2187 crate::vision_host::run_resize_nearest_2x(
2188 &self.arena,
2189 &dev.device,
2190 &dev.queue,
2191 *src_byte_off as usize,
2192 *dst_byte_off as usize,
2193 *n as usize,
2194 *c as usize,
2195 *h as usize,
2196 *w as usize,
2197 );
2198 }
2199 Step::ReverseHost {
2200 src_byte_off,
2201 dst_byte_off,
2202 dims,
2203 rev_mask,
2204 elem_bytes,
2205 } => {
2206 crate::vision_host::run_reverse(
2207 &self.arena,
2208 &dev.device,
2209 &dev.queue,
2210 *src_byte_off as usize,
2211 *dst_byte_off as usize,
2212 dims,
2213 rev_mask,
2214 *elem_bytes as usize,
2215 );
2216 }
2217 Step::ArgReduceHost {
2218 src_byte_off,
2219 dst_byte_off,
2220 outer,
2221 reduced,
2222 inner,
2223 is_max,
2224 } => {
2225 crate::vision_host::run_argreduce(
2226 &self.arena,
2227 &dev.device,
2228 &dev.queue,
2229 *src_byte_off as usize,
2230 *dst_byte_off as usize,
2231 *outer as usize,
2232 *reduced as usize,
2233 *inner as usize,
2234 *is_max,
2235 );
2236 }
2237 Step::GruHost {
2238 x,
2239 w_ih,
2240 w_hh,
2241 b_ih,
2242 b_hh,
2243 h0,
2244 dst,
2245 batch,
2246 seq,
2247 input_size,
2248 hidden,
2249 num_layers,
2250 bidirectional,
2251 carry,
2252 } => {
2253 crate::vision_host::run_gru(
2254 &self.arena,
2255 &dev.device,
2256 &dev.queue,
2257 *x as usize,
2258 *w_ih as usize,
2259 *w_hh as usize,
2260 *b_ih as usize,
2261 *b_hh as usize,
2262 *h0 as usize,
2263 *dst as usize,
2264 *batch as usize,
2265 *seq as usize,
2266 *input_size as usize,
2267 *hidden as usize,
2268 *num_layers as usize,
2269 *bidirectional,
2270 *carry,
2271 );
2272 }
2273 Step::RnnHost {
2274 x,
2275 w_ih,
2276 w_hh,
2277 bias,
2278 h0,
2279 dst,
2280 batch,
2281 seq,
2282 input_size,
2283 hidden,
2284 num_layers,
2285 bidirectional,
2286 carry,
2287 relu,
2288 } => {
2289 crate::vision_host::run_rnn(
2290 &self.arena,
2291 &dev.device,
2292 &dev.queue,
2293 *x as usize,
2294 *w_ih as usize,
2295 *w_hh as usize,
2296 *bias as usize,
2297 *h0 as usize,
2298 *dst as usize,
2299 *batch as usize,
2300 *seq as usize,
2301 *input_size as usize,
2302 *hidden as usize,
2303 *num_layers as usize,
2304 *bidirectional,
2305 *carry,
2306 *relu,
2307 );
2308 }
2309 Step::Llada2GroupLimitedGate {
2310 sig_byte_off,
2311 route_byte_off,
2312 out_byte_off,
2313 n_elems,
2314 attrs,
2315 } => {
2316 crate::llada2_gate_host::run_llada2_group_limited_gate(
2317 &self.arena,
2318 &dev.device,
2319 &dev.queue,
2320 *sig_byte_off as usize,
2321 *route_byte_off as usize,
2322 *out_byte_off as usize,
2323 *n_elems as usize,
2324 attrs,
2325 );
2326 }
2327 Step::UmapKnnHost {
2328 pairwise_byte_off,
2329 out_byte_off,
2330 n,
2331 k,
2332 } => {
2333 crate::umap_knn_host::run_umap_knn(
2334 &self.arena,
2335 &dev.device,
2336 &dev.queue,
2337 *pairwise_byte_off as usize,
2338 *out_byte_off as usize,
2339 *n as usize,
2340 *k as usize,
2341 );
2342 }
2343 Step::MsDeformAttnHost {
2344 in_offs,
2345 out_byte_off,
2346 out_bytes,
2347 attrs,
2348 } => {
2349 crate::ms_deform_attn::run_ms_deform_attn(
2350 &self.arena,
2351 &dev.device,
2352 &dev.queue,
2353 in_offs,
2354 *out_byte_off as usize,
2355 *out_bytes as usize,
2356 attrs,
2357 );
2358 }
2359 Step::FftHost {
2360 src_byte_off,
2361 dst_byte_off,
2362 outer,
2363 n_complex,
2364 inverse,
2365 norm_tag,
2366 dtype_tag,
2367 } => {
2368 crate::fft_host::run_fft1d(
2369 &self.arena,
2370 &dev.device,
2371 &dev.queue,
2372 *src_byte_off as usize,
2373 *dst_byte_off as usize,
2374 *outer as usize,
2375 *n_complex as usize,
2376 *inverse,
2377 *norm_tag,
2378 fft_dtype_from_tag(*dtype_tag),
2379 );
2380 }
2381 Step::ScanHost {
2382 plan,
2383 outer_init_off,
2384 outer_final_off,
2385 length,
2386 save_trajectory,
2387 xs_outer,
2388 bcast_outer,
2389 } => {
2390 crate::scan_host::run_scan(
2391 &self.arena,
2392 &dev.device,
2393 &dev.queue,
2394 plan,
2395 *outer_init_off,
2396 *outer_final_off,
2397 *length,
2398 *save_trajectory,
2399 xs_outer,
2400 bcast_outer,
2401 );
2402 }
2403 Step::WelchPeaksHost { .. }
2404 | Step::LogMelHost { .. }
2405 | Step::LogMelBackwardHost { .. } => {
2406 unreachable!("tail-host audio ops run after GPU wait")
2407 }
2408 Step::Im2ColHost {
2409 x_byte_off,
2410 col_byte_off,
2411 n,
2412 c_in,
2413 h,
2414 w,
2415 h_out,
2416 w_out,
2417 kh,
2418 kw,
2419 sh,
2420 sw,
2421 ph,
2422 pw,
2423 dh,
2424 dw_dil,
2425 } => {
2426 crate::im2col_host::run_im2col(
2427 &self.arena,
2428 &dev.device,
2429 &dev.queue,
2430 *x_byte_off as usize,
2431 *col_byte_off as usize,
2432 *n,
2433 *c_in,
2434 *h,
2435 *w,
2436 *h_out,
2437 *w_out,
2438 *kh,
2439 *kw,
2440 *sh,
2441 *sw,
2442 *ph,
2443 *pw,
2444 *dh,
2445 *dw_dil,
2446 );
2447 }
2448 Step::RngNormalHost {
2449 dst_byte_off,
2450 len,
2451 mean,
2452 scale,
2453 key,
2454 op_seed,
2455 } => {
2456 let opts = *self.rng.read().expect("rng lock");
2457 crate::rng_host::run_rng_normal(
2458 &self.arena,
2459 &dev.queue,
2460 *dst_byte_off as usize,
2461 *len as usize,
2462 *mean,
2463 *scale,
2464 *key,
2465 *op_seed,
2466 opts,
2467 );
2468 }
2469 Step::RngUniformHost {
2470 dst_byte_off,
2471 len,
2472 low,
2473 high,
2474 key,
2475 op_seed,
2476 } => {
2477 let opts = *self.rng.read().expect("rng lock");
2478 crate::rng_host::run_rng_uniform(
2479 &self.arena,
2480 &dev.queue,
2481 *dst_byte_off as usize,
2482 *len as usize,
2483 *low,
2484 *high,
2485 *key,
2486 *op_seed,
2487 opts,
2488 );
2489 }
2490 #[cfg(feature = "splat")]
2491 Step::GaussianSplatRender {
2492 positions_byte_off,
2493 positions_len,
2494 scales_byte_off,
2495 scales_len,
2496 rotations_byte_off,
2497 rotations_len,
2498 opacities_byte_off,
2499 opacities_len,
2500 colors_byte_off,
2501 colors_len,
2502 sh_coeffs_byte_off,
2503 sh_coeffs_len,
2504 meta_byte_off,
2505 dst_byte_off,
2506 dst_len,
2507 width,
2508 height,
2509 tile_size,
2510 radius_scale,
2511 alpha_cutoff,
2512 max_splat_steps,
2513 transmittance_threshold,
2514 max_list_entries,
2515 } => {
2516 crate::splat::run_gaussian_splat_render(
2517 &self.arena,
2518 &dev.device,
2519 &dev.queue,
2520 *positions_byte_off as usize,
2521 *positions_len as usize,
2522 *scales_byte_off as usize,
2523 *scales_len as usize,
2524 *rotations_byte_off as usize,
2525 *rotations_len as usize,
2526 *opacities_byte_off as usize,
2527 *opacities_len as usize,
2528 *colors_byte_off as usize,
2529 *colors_len as usize,
2530 *sh_coeffs_byte_off as usize,
2531 *sh_coeffs_len as usize,
2532 *meta_byte_off as usize,
2533 *dst_byte_off as usize,
2534 *dst_len as usize,
2535 *width,
2536 *height,
2537 *tile_size,
2538 *radius_scale,
2539 *alpha_cutoff,
2540 *max_splat_steps,
2541 *transmittance_threshold,
2542 *max_list_entries,
2543 );
2544 }
2545 #[cfg(feature = "splat")]
2546 Step::GaussianSplatPrepare {
2547 positions_byte_off,
2548 positions_len,
2549 scales_byte_off,
2550 scales_len,
2551 rotations_byte_off,
2552 rotations_len,
2553 opacities_byte_off,
2554 opacities_len,
2555 colors_byte_off,
2556 colors_len,
2557 sh_coeffs_byte_off,
2558 sh_coeffs_len,
2559 meta_byte_off,
2560 meta_len,
2561 prep_byte_off,
2562 prep_len,
2563 width,
2564 height,
2565 tile_size,
2566 radius_scale,
2567 alpha_cutoff,
2568 max_splat_steps,
2569 transmittance_threshold,
2570 max_list_entries,
2571 } => {
2572 crate::splat::run_gaussian_splat_prepare(
2573 &self.arena,
2574 &dev.device,
2575 &dev.queue,
2576 *positions_byte_off as usize,
2577 *positions_len as usize,
2578 *scales_byte_off as usize,
2579 *scales_len as usize,
2580 *rotations_byte_off as usize,
2581 *rotations_len as usize,
2582 *opacities_byte_off as usize,
2583 *opacities_len as usize,
2584 *colors_byte_off as usize,
2585 *colors_len as usize,
2586 *sh_coeffs_byte_off as usize,
2587 *sh_coeffs_len as usize,
2588 *meta_byte_off as usize,
2589 *meta_len as usize,
2590 *prep_byte_off as usize,
2591 *prep_len as usize,
2592 *width,
2593 *height,
2594 *tile_size,
2595 *radius_scale,
2596 *alpha_cutoff,
2597 *max_splat_steps,
2598 *transmittance_threshold,
2599 *max_list_entries,
2600 );
2601 }
2602 #[cfg(feature = "splat")]
2603 Step::GaussianSplatRasterize {
2604 prep_byte_off,
2605 prep_len,
2606 meta_byte_off,
2607 meta_len,
2608 dst_byte_off,
2609 dst_len,
2610 count,
2611 width,
2612 height,
2613 tile_size,
2614 alpha_cutoff,
2615 max_splat_steps,
2616 transmittance_threshold,
2617 max_list_entries,
2618 } => {
2619 crate::splat::run_gaussian_splat_rasterize(
2620 &self.arena,
2621 &dev.device,
2622 &dev.queue,
2623 *prep_byte_off as usize,
2624 *prep_len as usize,
2625 *meta_byte_off as usize,
2626 *meta_len as usize,
2627 *dst_byte_off as usize,
2628 *dst_len as usize,
2629 *count as usize,
2630 *width,
2631 *height,
2632 *tile_size,
2633 *alpha_cutoff,
2634 *max_splat_steps,
2635 *transmittance_threshold,
2636 *max_list_entries,
2637 );
2638 }
2639 #[cfg(feature = "splat")]
2640 Step::GaussianSplatRenderBackward {
2641 positions_byte_off,
2642 positions_len,
2643 scales_byte_off,
2644 scales_len,
2645 rotations_byte_off,
2646 rotations_len,
2647 opacities_byte_off,
2648 opacities_len,
2649 colors_byte_off,
2650 colors_len,
2651 sh_coeffs_byte_off,
2652 sh_coeffs_len,
2653 meta_byte_off,
2654 d_loss_byte_off,
2655 d_loss_len,
2656 packed_byte_off,
2657 packed_len,
2658 width,
2659 height,
2660 tile_size,
2661 radius_scale,
2662 alpha_cutoff,
2663 max_splat_steps,
2664 transmittance_threshold,
2665 max_list_entries,
2666 loss_grad_clip,
2667 sh_band,
2668 max_anisotropy,
2669 } => {
2670 crate::splat::run_gaussian_splat_render_backward(
2671 &self.arena,
2672 &dev.device,
2673 &dev.queue,
2674 *positions_byte_off as usize,
2675 *positions_len as usize,
2676 *scales_byte_off as usize,
2677 *scales_len as usize,
2678 *rotations_byte_off as usize,
2679 *rotations_len as usize,
2680 *opacities_byte_off as usize,
2681 *opacities_len as usize,
2682 *colors_byte_off as usize,
2683 *colors_len as usize,
2684 *sh_coeffs_byte_off as usize,
2685 *sh_coeffs_len as usize,
2686 *meta_byte_off as usize,
2687 *d_loss_byte_off as usize,
2688 *d_loss_len as usize,
2689 *packed_byte_off as usize,
2690 *packed_len as usize,
2691 *width,
2692 *height,
2693 *tile_size,
2694 *radius_scale,
2695 *alpha_cutoff,
2696 *max_splat_steps,
2697 *transmittance_threshold,
2698 *max_list_entries,
2699 *loss_grad_clip,
2700 *sh_band,
2701 *max_anisotropy,
2702 );
2703 }
2704 _ => break,
2705 }
2706 step_i += 1;
2707 }
2708
2709 self.dump_node_stats_if_requested(dev);
2710
2711 if rlx_ir::env::flag("RLX_WGPU_NAN_TRACE") {
2712 let mut bad_nodes = Vec::new();
2713 for node in self.graph.nodes() {
2714 if !self.arena.has(node.id) {
2715 continue;
2716 }
2717 if matches!(
2719 node.op,
2720 rlx_ir::Op::Input { .. }
2721 | rlx_ir::Op::Param { .. }
2722 | rlx_ir::Op::Constant { .. }
2723 ) {
2724 continue;
2725 }
2726 let data = self.arena.read_f32(&dev.device, &dev.queue, node.id);
2727 let nan_count = data.iter().filter(|v| v.is_nan()).count();
2728 let inf_count = data.iter().filter(|v| v.is_infinite()).count();
2729 if nan_count > 0 || inf_count > 0 {
2730 let first_nan = data.iter().position(|v| v.is_nan());
2732 if let Some(idx) = first_nan {
2733 let lo = idx.saturating_sub(2);
2734 let hi = (idx + 3).min(data.len());
2735 eprintln!(
2736 " node {:?} op={:?} len={} nan={} inf={} \
2737 first_nan_idx={} ctx={:?}",
2738 node.id,
2739 node.op,
2740 data.len(),
2741 nan_count,
2742 inf_count,
2743 idx,
2744 &data[lo..hi]
2745 );
2746 }
2747 bad_nodes.push((node.id, data.len(), nan_count, inf_count));
2748 if bad_nodes.len() >= 3 {
2749 break;
2750 }
2751 }
2752 }
2753 if bad_nodes.is_empty() {
2754 eprintln!("[wgpu-nan-trace] no NaN/Inf in any node — clean run");
2755 } else {
2756 eprintln!(
2757 "[wgpu-nan-trace] first {} bad nodes (above)",
2758 bad_nodes.len()
2759 );
2760 }
2761 }
2762
2763 if rlx_ir::env::flag("RLX_BENCH_DISPATCH_ONLY") {
2764 return self
2765 .graph
2766 .outputs
2767 .iter()
2768 .map(|&id| {
2769 let n = self.graph.node(id).shape.num_elements().unwrap_or(0);
2770 vec![0.0; n]
2771 })
2772 .collect();
2773 }
2774 let out_ids: Vec<_> = self.graph.outputs.clone();
2775 read_f32_many_pooled(
2776 &self.arena,
2777 &dev.device,
2778 &dev.queue,
2779 &out_ids,
2780 &mut self.readback_staging,
2781 )
2782 }
2783}