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, conv1d_tiled_kernel,
38 conv2d_kernel, conv3d_kernel, copy_kernel, cumsum_backward_kernel, cumsum_kernel,
39 dequant_matmul_kernel, elementwise_region_kernel, elementwise_region_spatial_kernel,
40 expand_kernel, fma_kernel, fused_residual_ln_kernel, fused_residual_ln_tee_kernel,
41 fused_residual_rms_norm_kernel, gather_axis_kernel, gather_backward_acc_kernel,
42 gather_backward_zero_kernel, gather_kernel, gather_split_kernel, grouped_matmul_kernel,
43 gru_kernel, im2col2d_kernel, layer_norm_backward_gamma_partial_kernel,
44 layer_norm_backward_gamma_reduce_kernel, layer_norm_backward_input_kernel, layernorm_kernel,
45 mamba2_kernel, 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 } | Step::Conv2dTiled { 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::Im2ColGpu { .. }
666 | Step::GatherSplit { .. }
667 | Step::DequantMatmulGguf { .. }
668 | Step::DequantGroupedMatmulGguf { .. }
669 | Step::GatedDeltaNet { .. }
670 | Step::Lstm { .. }
671 | Step::ConvTranspose2d { .. }
672 | Step::GroupNormHost { .. }
673 | Step::LayerNorm2dHost { .. }
674 | Step::ResizeNearest2xHost { .. }
675 | Step::ReverseHost { .. }
676 | Step::ArgReduceHost { .. }
677 | Step::GruHost { .. }
678 | Step::RnnHost { .. }
679 | Step::Llada2GroupLimitedGate { .. }
680 | Step::UmapKnnHost { .. }
681 | Step::MsDeformAttnHost { .. }
682 | Step::FftHost { .. }
683 | Step::ScanHost { .. }
684 | Step::Im2ColHost { .. }
685 | Step::RngNormalHost { .. }
686 | Step::RngUniformHost { .. }
687 | Step::WelchPeaksHost { .. }
688 | Step::LogMelHost { .. }
689 | Step::LogMelBackwardHost { .. } => {}
690 Step::FusedResidualLn { params } => {
691 let mut p = *params;
692 p.outer = scale(p.outer);
693 dev.queue
694 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
695 }
696 Step::FusedResidualLnTee { params } => {
697 let mut p = *params;
698 p.outer = scale(p.outer);
699 dev.queue
700 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
701 }
702 Step::FusedResidualRmsNorm { params } => {
703 let mut p = *params;
704 p.outer = scale(p.outer);
705 dev.queue
706 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
707 }
708 Step::MatmulQkv { params, kind: _ } => {
709 let mut p = *params;
710 p.m = scale(p.m);
711 dev.queue
712 .write_buffer(&self.uniforms[gpu_ui], 0, bytemuck::bytes_of(&p));
713 }
714 #[cfg(feature = "splat")]
715 Step::GaussianSplatRender { .. }
716 | Step::GaussianSplatRenderBackward { .. }
717 | Step::GaussianSplatPrepare { .. }
718 | Step::GaussianSplatRasterize { .. } => {}
719 }
720 if !matches!(step, Step::FftGpu { .. }) {
721 gpu_ui += 1;
722 }
723 }
724 self.uniforms_active_extent = Some(active);
725 }
726
727 let mm_k = matmul_kernel(&dev.device);
729 let mm_w_active = matmul_wide_active_kernel(&dev.device);
730 let mm_f16w = matmul_f16w_kernel(&dev.device);
731 let mm_f16c = matmul_f16_compute_kernel(&dev.device);
732 let mm_coop = matmul_coop16_kernel(&dev.device);
733 let mm_coop_f16_vk = matmul_coop_f16_vulkan_kernel(&dev.device);
734 let mm_coop_f32 = matmul_coop_f32_active_kernel(&dev.device);
735 let mm_cast = cast_f32_to_f16_kernel(&dev.device);
736 let bk = binary_kernel(&dev.device);
737 let uk = unary_kernel(&dev.device);
738 let ck = compare_kernel(&dev.device);
739 let wk = where_kernel(&dev.device);
740 let fk = fma_kernel(&dev.device);
741 let mut step_i = 0;
742 let mut gpu_bi = 0usize;
743 let mut fft_i = 0usize;
744 while step_i < self.schedule.len() {
745 let mut enc = dev
746 .device
747 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
748 label: Some("rlx-wgpu run"),
749 });
750 {
751 let mut pass = enc.begin_compute_pass(&wgpu::ComputePassDescriptor {
752 label: Some("rlx-wgpu compute pass"),
753 timestamp_writes: None,
754 });
755 let mut pass_dispatched = false;
756 while step_i < self.schedule.len() {
757 if step_is_tail_host(&self.schedule[step_i]) {
758 step_i += 1;
759 continue;
760 }
761 if step_runs_on_host(&self.schedule[step_i]) {
762 break;
763 }
764 if pass_dispatched
769 && step_i > 0
770 && step_needs_pass_flush(&self.schedule[step_i], &self.schedule[step_i - 1])
771 {
772 break;
773 }
774 let step = &self.schedule[step_i];
775 let _perf = rlx_ir::perfetto::TraceSpan::new(step_name(step), "wgpu");
778 match step {
779 Step::CastF32ToF16 { params } => {
780 if let Some(cast_k) = mm_cast {
785 pass.set_pipeline(&cast_k.pipeline);
786 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
787 let (gx, gy, gz) = dispatch_dims(params.len, 64);
788 pass.dispatch_workgroups(gx, gy, gz);
789 }
790 }
791 Step::Matmul {
792 m,
793 n,
794 batch,
795 b_off_f32,
796 b_is_param,
797 compute_precision,
798 ..
799 } =>
800 {
807 #[allow(clippy::unnecessary_unwrap)]
808 let m_s = scale(*m);
812 if m_s == 0 {
813 continue;
814 }
815 let coop_f16_wide = mm_coop_f16_vk.is_some()
816 && *compute_precision == MatmulCompute::CoopF16Vk
817 && crate::coop_f16_vk::use_wide_matmul(
818 *b_off_f32,
819 *n,
820 &self.coop_f16_b_param,
821 &self.coop_f16_vk_wide_b,
822 );
823 pass.set_bind_group(
824 0,
825 coop_f16_vk_bind_group(self, gpu_bi, coop_f16_wide),
826 &[],
827 );
828 let f16w_opt_in = rlx_ir::env::flag("RLX_WGPU_F16_WEIGHTS");
838 if let Some(coop) = mm_coop.as_ref()
839 && *b_is_param
840 && *compute_precision == MatmulCompute::Coop16
841 {
842 pass.set_pipeline(&coop.pipeline);
848 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
849 } else if mm_coop_f16_vk.is_some()
850 && *compute_precision == MatmulCompute::CoopF16Vk
851 {
852 if coop_f16_wide {
853 dispatch_wide_f32_matmul(
854 &mut pass,
855 mm_w_active,
856 mm_k,
857 m_s,
858 *n,
859 *batch,
860 );
861 } else {
862 let n_eff = scale(*n);
863 let coop_vk =
864 matmul_coop_f16_vulkan_active_kernel(&dev.device, n_eff)
865 .expect("coop f16 vk kernel missing");
866 pass.set_pipeline(&coop_vk.pipeline);
867 pass.dispatch_workgroups(
868 m_s.div_ceil(16),
869 n.div_ceil(16),
870 *batch,
871 );
872 }
873 } else if let Some(coop_f32) = mm_coop_f32.as_ref()
874 && *b_is_param
875 && *compute_precision == MatmulCompute::CoopF32
876 {
877 pass.set_pipeline(&coop_f32.pipeline);
880 let backend = wgpu_device()
881 .map(|d| d.backend)
882 .unwrap_or(wgpu::Backend::Noop);
883 let (gx, gy) = if backend == wgpu::Backend::Metal {
884 (n.div_ceil(32), m_s.div_ceil(32))
885 } else {
886 (m_s.div_ceil(8), n.div_ceil(8))
887 };
888 pass.dispatch_workgroups(gx, gy, *batch);
889 } else if let Some(f16c) = mm_f16c.as_ref()
890 && *b_is_param
891 && *compute_precision == MatmulCompute::F16
892 {
893 pass.set_pipeline(&f16c.pipeline);
894 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
895 } else if let Some(f16w) = mm_f16w.as_ref()
896 && *b_is_param
897 && f16w_opt_in
898 {
899 pass.set_pipeline(&f16w.pipeline);
900 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
901 } else if m_s >= 32 && *n >= 64 {
902 pass.set_pipeline(&mm_w_active.pipeline);
903 let backend = wgpu_device()
904 .map(|d| d.backend)
905 .unwrap_or(wgpu::Backend::Noop);
906 let (gx, gy) = if matches!(
907 backend,
908 wgpu::Backend::Vulkan | wgpu::Backend::Dx12
909 ) {
910 (n.div_ceil(64), m_s.div_ceil(64))
911 } else {
912 (n.div_ceil(64), m_s.div_ceil(32))
913 };
914 pass.dispatch_workgroups(gx, gy, *batch);
915 } else {
916 pass.set_pipeline(&mm_k.pipeline);
917 pass.dispatch_workgroups(n.div_ceil(32), m_s.div_ceil(32), *batch);
918 }
919 }
920 Step::Binary { params } => {
921 let n_s = scale(params.n);
922 if n_s == 0 {
923 continue;
924 }
925 pass.set_pipeline(&bk.pipeline);
926 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
927 let (gx, gy, gz) = dispatch_dims(n_s, 64);
928 pass.dispatch_workgroups(gx, gy, gz);
929 }
930 Step::Compare { params } => {
931 let n_s = scale(params.n);
932 if n_s == 0 {
933 continue;
934 }
935 pass.set_pipeline(&ck.pipeline);
936 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
937 let (gx, gy, gz) = dispatch_dims(n_s, 64);
938 pass.dispatch_workgroups(gx, gy, gz);
939 }
940 Step::Unary { params, f16_mirror } => {
941 let n_s = scale(params.n);
942 if n_s == 0 {
943 continue;
944 }
945 if *f16_mirror {
946 if let Some(uk_f16) = unary_f16_mirror_kernel(&dev.device) {
947 pass.set_pipeline(&uk_f16.pipeline);
948 } else {
949 pass.set_pipeline(&uk.pipeline);
950 }
951 } else {
952 pass.set_pipeline(&uk.pipeline);
953 }
954 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
955 let (gx, gy, gz) = dispatch_dims(n_s, 64);
956 pass.dispatch_workgroups(gx, gy, gz);
957 }
958 Step::Where { params } => {
959 let n_s = scale(params.n);
960 if n_s == 0 {
961 continue;
962 }
963 pass.set_pipeline(&wk.pipeline);
964 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
965 let (gx, gy, gz) = dispatch_dims(n_s, 64);
966 pass.dispatch_workgroups(gx, gy, gz);
967 }
968 Step::Fma { params } => {
969 let n_s = scale(params.n);
970 if n_s == 0 {
971 continue;
972 }
973 pass.set_pipeline(&fk.pipeline);
974 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
975 let (gx, gy, gz) = dispatch_dims(n_s, 64);
976 pass.dispatch_workgroups(gx, gy, gz);
977 }
978 Step::Reduce { params } => {
979 let outer_s = scale(params.outer);
980 if outer_s == 0 {
981 continue;
982 }
983 let rk = reduce_kernel(&dev.device);
984 pass.set_pipeline(&rk.pipeline);
985 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
986 let total_out = outer_s.saturating_mul(params.inner);
987 if params.reduce_dim <= 64 {
988 let (gx, gy, gz) = dispatch_dims(total_out, 64);
990 pass.dispatch_workgroups(gx, gy, gz);
991 } else {
992 let (gx, gy, gz) = dispatch_dims(total_out, 1);
996 pass.dispatch_workgroups(gx, gy, gz);
997 }
998 }
999 Step::Softmax { params } => {
1000 let outer_s = scale(params.outer);
1001 if outer_s == 0 {
1002 continue;
1003 }
1004 let sk = softmax_kernel(&dev.device);
1005 pass.set_pipeline(&sk.pipeline);
1006 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1007 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1008 pass.dispatch_workgroups(gx, gy, gz);
1009 }
1010 Step::SoftmaxCrossEntropy { params } => {
1011 let outer_s = scale(params.outer);
1012 if outer_s == 0 {
1013 continue;
1014 }
1015 let sk = softmax_cross_entropy_kernel(&dev.device);
1016 pass.set_pipeline(&sk.pipeline);
1017 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1018 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1019 pass.dispatch_workgroups(gx, gy, gz);
1020 }
1021 Step::LayerNorm { params } => {
1022 let outer_s = scale(params.outer);
1023 if outer_s == 0 {
1024 continue;
1025 }
1026 let lk = layernorm_kernel(&dev.device);
1027 pass.set_pipeline(&lk.pipeline);
1028 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1029 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1030 pass.dispatch_workgroups(gx, gy, gz);
1031 }
1032 Step::RmsNormBackwardInput { params } => {
1033 let outer_s = scale(params.outer);
1034 if outer_s == 0 {
1035 continue;
1036 }
1037 let rk = rms_norm_backward_kernel(&dev.device);
1038 pass.set_pipeline(&rk.pipeline);
1039 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1040 pass.dispatch_workgroups(outer_s, 1, 1);
1041 }
1042 Step::RmsNormBackwardGamma { params }
1043 | Step::RmsNormBackwardBeta { params } => {
1044 if params.inner == 0 {
1045 continue;
1046 }
1047 let rk = rms_norm_backward_param_kernel(&dev.device);
1048 pass.set_pipeline(&rk.pipeline);
1049 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1050 pass.dispatch_workgroups(1, 1, 1);
1051 }
1052 Step::LayerNormBackwardInput { params } => {
1053 let outer_s = scale(params.outer);
1054 if outer_s == 0 {
1055 continue;
1056 }
1057 let lk = layer_norm_backward_input_kernel(&dev.device);
1058 pass.set_pipeline(&lk.pipeline);
1059 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1060 pass.dispatch_workgroups(outer_s, 1, 1);
1061 }
1062 Step::LayerNormBackwardGammaPartial {
1063 params,
1064 num_workgroups,
1065 } => {
1066 if params.inner == 0 || *num_workgroups == 0 {
1067 continue;
1068 }
1069 let lk = layer_norm_backward_gamma_partial_kernel(&dev.device);
1070 pass.set_pipeline(&lk.pipeline);
1071 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1072 pass.dispatch_workgroups(*num_workgroups, 1, 1);
1073 }
1074 Step::LayerNormBackwardGammaReduce { params } => {
1075 if params.inner == 0 {
1076 continue;
1077 }
1078 let lk = layer_norm_backward_gamma_reduce_kernel(&dev.device);
1079 pass.set_pipeline(&lk.pipeline);
1080 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1081 pass.dispatch_workgroups(1, 1, 1);
1082 }
1083 Step::CumsumBackward { params } => {
1084 let outer_s = scale(params.outer);
1085 if outer_s == 0 {
1086 continue;
1087 }
1088 let ck = cumsum_backward_kernel(&dev.device);
1089 pass.set_pipeline(&ck.pipeline);
1090 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1091 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1092 pass.dispatch_workgroups(gx, gy, gz);
1093 }
1094 Step::RopeBackward { params } => {
1095 let seq_s = scale(params.seq);
1096 if seq_s == 0 {
1097 continue;
1098 }
1099 let rk = rope_backward_kernel(&dev.device);
1100 pass.set_pipeline(&rk.pipeline);
1101 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1102 let total = params.batch * seq_s * params.hidden;
1103 let (gx, gy, gz) = dispatch_dims(total, 64);
1104 pass.dispatch_workgroups(gx, gy, gz);
1105 }
1106 Step::GatherBackward { params } => {
1107 let outer_s = scale(params.outer);
1108 if outer_s == 0 {
1109 continue;
1110 }
1111 let total = outer_s * params.axis_dim * params.trailing;
1112 if total > 0 {
1113 let zk = gather_backward_zero_kernel(&dev.device);
1114 pass.set_pipeline(&zk.pipeline);
1115 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1116 let (gx, _, _) = dispatch_dims(total, 256);
1117 pass.dispatch_workgroups(gx, 1, 1);
1118 }
1119 let ak = gather_backward_acc_kernel(&dev.device);
1120 pass.set_pipeline(&ak.pipeline);
1121 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1122 pass.dispatch_workgroups(outer_s, 1, 1);
1123 }
1124 Step::Cumsum { params } => {
1125 let outer_s = scale(params.outer);
1126 if outer_s == 0 {
1127 continue;
1128 }
1129 let ck2 = cumsum_kernel(&dev.device);
1130 pass.set_pipeline(&ck2.pipeline);
1131 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1132 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1133 pass.dispatch_workgroups(gx, gy, gz);
1134 }
1135 Step::FftGpu {
1136 src_off,
1137 dst_off,
1138 outer,
1139 n,
1140 inverse,
1141 norm_scale,
1142 } => {
1143 let res = &self.fft_gpu_steps[fft_i];
1144 fft_i += 1;
1145 crate::fft_dispatch::dispatch_fft_gpu_in_pass(
1146 &dev.device,
1147 &dev.queue,
1148 &mut pass,
1149 res,
1150 *src_off,
1151 *dst_off,
1152 *outer,
1153 *n,
1154 *inverse != 0,
1155 *norm_scale,
1156 );
1157 }
1158 Step::Copy { params } => {
1159 let n_s = scale(params.n);
1160 if n_s == 0 {
1161 continue;
1162 }
1163 let ck2 = copy_kernel(&dev.device);
1164 pass.set_pipeline(&ck2.pipeline);
1165 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1166 let (gx, gy, gz) = dispatch_dims(n_s, 64);
1167 pass.dispatch_workgroups(gx, gy, gz);
1168 }
1169 Step::BufferCopy { .. } => {
1170 }
1172 Step::ElementwiseRegion { params } => {
1173 let len_s = scale(params.len);
1174 if len_s == 0 {
1175 continue;
1176 }
1177 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1178 if params.prologue == rlx_ir::REGION_PROLOGUE_RESIZE_NEAREST_2X_NCHW {
1179 let ek = elementwise_region_spatial_kernel(&dev.device);
1180 pass.set_pipeline(&ek.pipeline);
1181 let (gx, gy, gz) = dispatch_prologue_nchw(
1182 params.out_w,
1183 params.out_h,
1184 params.out_n * params.out_c,
1185 );
1186 pass.dispatch_workgroups(gx, gy, gz);
1187 } else {
1188 let ek = elementwise_region_kernel(&dev.device);
1189 pass.set_pipeline(&ek.pipeline);
1190 let (gx, gy, gz) = dispatch_dims(len_s, 64);
1191 pass.dispatch_workgroups(gx, gy, gz);
1192 }
1193 }
1194 Step::BatchElementwiseRegion { params } => {
1195 let slice_len_s = scale(params.slice_len);
1196 let num_batch_s = scale(params.num_batch);
1197 if slice_len_s == 0 || num_batch_s == 0 {
1198 continue;
1199 }
1200 let ek = batch_elementwise_region_kernel(&dev.device);
1201 pass.set_pipeline(&ek.pipeline);
1202 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1203 let (gx, gy, _) = dispatch_dims(slice_len_s, 64);
1204 pass.dispatch_workgroups(gx, gy, num_batch_s);
1205 }
1206 Step::Transpose { params, .. } => {
1207 let total_s = if params.bucket_outermost == 1 && params.out_dim_0 > 0 {
1211 let scaled_d0 = scale(params.out_dim_0);
1212 let inner = params.out_total / params.out_dim_0;
1213 scaled_d0 * inner
1214 } else {
1215 params.out_total
1216 };
1217 if total_s == 0 {
1218 continue;
1219 }
1220 let tk = transpose_kernel(&dev.device);
1221 pass.set_pipeline(&tk.pipeline);
1222 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1223 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1224 pass.dispatch_workgroups(gx, gy, gz);
1225 }
1226 Step::Narrow { params } => {
1227 let total_s = scale(params.total);
1228 if total_s == 0 {
1229 continue;
1230 }
1231 let nk = narrow_kernel(&dev.device);
1232 pass.set_pipeline(&nk.pipeline);
1233 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1234 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1235 pass.dispatch_workgroups(gx, gy, gz);
1236 }
1237 Step::Concat { params } => {
1238 let total_s = scale(params.total);
1239 if total_s == 0 {
1240 continue;
1241 }
1242 let cck = concat_kernel(&dev.device);
1243 pass.set_pipeline(&cck.pipeline);
1244 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1245 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1246 pass.dispatch_workgroups(gx, gy, gz);
1247 }
1248 Step::Gather { params } => {
1249 let n_out_s = scale(params.n_out);
1250 if n_out_s == 0 {
1251 continue;
1252 }
1253 let gk = gather_kernel(&dev.device);
1254 pass.set_pipeline(&gk.pipeline);
1255 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1256 let (gx, gy, gz) = dispatch_dims(n_out_s, 64);
1257 pass.dispatch_workgroups(gx, gy, gz);
1258 }
1259 Step::GatherAxis { params } => {
1260 let total_s = scale(params.total);
1261 if total_s == 0 {
1262 continue;
1263 }
1264 let gk = gather_axis_kernel(&dev.device);
1265 pass.set_pipeline(&gk.pipeline);
1266 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1267 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1268 pass.dispatch_workgroups(gx, gy, gz);
1269 }
1270 Step::Attention { params, .. } => {
1271 let seq_q_s = scale(params.seq_q);
1275 if seq_q_s == 0 {
1276 continue;
1277 }
1278 let ak = attention_kernel(&dev.device);
1279 pass.set_pipeline(&ak.pipeline);
1280 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1281 let total = params.batch * params.heads * seq_q_s;
1282 let (gx, gy, gz) = dispatch_dims(total, 64);
1283 pass.dispatch_workgroups(gx, gy, gz);
1284 }
1285 Step::AttentionBackward { params, .. } => {
1286 let axis = if params.wrt == 0 {
1287 params.seq_q
1288 } else {
1289 params.seq_k
1290 };
1291 let axis_s = scale(axis);
1292 if axis_s == 0 {
1293 continue;
1294 }
1295 let ak = attention_bwd_kernel(&dev.device);
1296 pass.set_pipeline(&ak.pipeline);
1297 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1298 let total = params.batch * params.heads * axis_s;
1299 let (gx, gy, gz) = dispatch_dims(total, 64);
1300 pass.dispatch_workgroups(gx, gy, gz);
1301 }
1302 Step::Rope { params } => {
1303 let s_active = scale(params.seq);
1306 let total_s = params.batch * s_active * params.last_dim;
1307 if total_s == 0 {
1308 continue;
1309 }
1310 let rk = rope_kernel(&dev.device);
1311 pass.set_pipeline(&rk.pipeline);
1312 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1313 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1314 pass.dispatch_workgroups(gx, gy, gz);
1315 }
1316 Step::Expand { params, .. } => {
1317 let total_s = if params.bucket_outermost == 1 && params.out_dim_0 > 0 {
1318 let scaled_d0 = scale(params.out_dim_0);
1319 let inner = params.out_total / params.out_dim_0;
1320 scaled_d0 * inner
1321 } else {
1322 params.out_total
1323 };
1324 if total_s == 0 {
1325 continue;
1326 }
1327 let ek = expand_kernel(&dev.device);
1328 pass.set_pipeline(&ek.pipeline);
1329 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1330 let (gx, gy, gz) = dispatch_dims(total_s, 64);
1331 pass.dispatch_workgroups(gx, gy, gz);
1332 }
1333 Step::Argmax { params } => {
1334 let outer_s = scale(params.outer);
1335 if outer_s == 0 {
1336 continue;
1337 }
1338 let amk = argmax_kernel(&dev.device);
1339 pass.set_pipeline(&amk.pipeline);
1340 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1341 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1342 pass.dispatch_workgroups(gx, gy, gz);
1343 }
1344 Step::Pool2d { params } => {
1345 let n_s = scale(params.n);
1346 if n_s == 0 {
1347 continue;
1348 }
1349 let pk = pool2d_kernel(&dev.device);
1350 pass.set_pipeline(&pk.pipeline);
1351 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1352 let total = n_s * params.c * params.h_out * params.w_out;
1353 let (gx, gy, gz) = dispatch_dims(total, 64);
1354 pass.dispatch_workgroups(gx, gy, gz);
1355 }
1356 Step::Conv2d { params } => {
1357 let n_s = scale(params.n);
1358 if n_s == 0 {
1359 continue;
1360 }
1361 let ck2 = conv2d_kernel(&dev.device);
1362 pass.set_pipeline(&ck2.pipeline);
1363 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1364 let spatial = params.h_out * params.w_out;
1367 let sp_tiles = spatial.div_ceil(CONV2D_TILE);
1368 let total = n_s * params.c_out * sp_tiles;
1369 let (gx, gy, gz) = dispatch_dims(total, 64);
1370 pass.dispatch_workgroups(gx, gy, gz);
1371 }
1372 Step::Conv2dTiled { params } => {
1373 let n_s = scale(params.n);
1374 if n_s == 0 {
1375 continue;
1376 }
1377 let ck = conv1d_tiled_kernel(&dev.device);
1378 pass.set_pipeline(&ck.pipeline);
1379 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1380 let total = n_s * params.c_out * params.h_out;
1383 let (gx, gy, gz) = dispatch_dims(total, 64);
1384 pass.dispatch_workgroups(gx, gy, gz);
1385 }
1386 Step::Im2ColGpu { params } => {
1387 let imk = im2col2d_kernel(&dev.device);
1390 pass.set_pipeline(&imk.pipeline);
1391 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1392 let total = params.k_total.saturating_mul(params.spatial);
1393 let (gx, gy, gz) = dispatch_dims(total, 64);
1394 pass.dispatch_workgroups(gx, gy, gz);
1395 }
1396 Step::Pool1d { params } => {
1397 let n_s = scale(params.n);
1398 if n_s == 0 {
1399 continue;
1400 }
1401 let pk = pool1d_kernel(&dev.device);
1402 pass.set_pipeline(&pk.pipeline);
1403 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1404 let total = n_s * params.c * params.l_out;
1405 let (gx, gy, gz) = dispatch_dims(total, 64);
1406 pass.dispatch_workgroups(gx, gy, gz);
1407 }
1408 Step::Pool3d { params } => {
1409 let n_s = scale(params.n);
1410 if n_s == 0 {
1411 continue;
1412 }
1413 let pk = pool3d_kernel(&dev.device);
1414 pass.set_pipeline(&pk.pipeline);
1415 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1416 let total = n_s * params.c * params.d_out * params.h_out * params.w_out;
1417 let (gx, gy, gz) = dispatch_dims(total, 64);
1418 pass.dispatch_workgroups(gx, gy, gz);
1419 }
1420 Step::Conv1d { params } => {
1421 let n_s = scale(params.n);
1422 if n_s == 0 {
1423 continue;
1424 }
1425 let ck = conv1d_kernel(&dev.device);
1426 pass.set_pipeline(&ck.pipeline);
1427 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1428 let total = n_s * params.c_out * params.l_out;
1429 let (gx, gy, gz) = dispatch_dims(total, 64);
1430 pass.dispatch_workgroups(gx, gy, gz);
1431 }
1432 Step::Conv3d { params } => {
1433 let n_s = scale(params.n);
1434 if n_s == 0 {
1435 continue;
1436 }
1437 let ck = conv3d_kernel(&dev.device);
1438 pass.set_pipeline(&ck.pipeline);
1439 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1440 let total =
1441 n_s * params.c_out * params.d_out * params.h_out * params.w_out;
1442 let (gx, gy, gz) = dispatch_dims(total, 64);
1443 pass.dispatch_workgroups(gx, gy, gz);
1444 }
1445 Step::ScatterAdd { params } => {
1446 let sk = scatter_add_kernel(&dev.device);
1447 pass.set_pipeline(&sk.pipeline);
1448 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1449 if params.op == 0 {
1455 let (gx, gy, gz) = dispatch_dims(params.out_total, 64);
1456 pass.dispatch_workgroups(gx, gy, gz);
1457 } else {
1458 pass.dispatch_workgroups(1, 1, 1);
1459 }
1460 }
1461 Step::TopK { params } => {
1462 let outer_s = scale(params.outer);
1463 if outer_s == 0 {
1464 continue;
1465 }
1466 let tk = topk_kernel(&dev.device);
1467 pass.set_pipeline(&tk.pipeline);
1468 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1469 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1470 pass.dispatch_workgroups(gx, gy, gz);
1471 }
1472 Step::WelchPeaksGpu { params } => {
1473 let batch_s = scale(params.welch_batch);
1474 if batch_s == 0 {
1475 continue;
1476 }
1477 let wk = welch_peaks_gpu_kernel(&dev.device);
1478 pass.set_pipeline(&wk.pipeline);
1479 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1480 let (gx, gy, gz) = dispatch_dims(batch_s, 64);
1481 pass.dispatch_workgroups(gx, gy, gz);
1482 }
1483 Step::UmapKnn { params } => {
1484 let n_s = scale(params.n);
1485 if n_s == 0 {
1486 continue;
1487 }
1488 let uk = umap_knn_kernel(&dev.device);
1489 pass.set_pipeline(&uk.pipeline);
1490 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1491 let (gx, gy, gz) = dispatch_dims(n_s, 64);
1492 pass.dispatch_workgroups(gx, gy, gz);
1493 }
1494 Step::GroupedMatmul { params } => {
1495 let m_s = scale(params.m);
1496 if m_s == 0 {
1497 continue;
1498 }
1499 let gk = grouped_matmul_kernel(&dev.device);
1500 pass.set_pipeline(&gk.pipeline);
1501 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1502 pass.dispatch_workgroups(params.n.div_ceil(8), m_s.div_ceil(8), 1);
1503 }
1504 Step::Sample { params } => {
1505 let outer_s = scale(params.outer);
1506 if outer_s == 0 {
1507 continue;
1508 }
1509 let sk = sample_kernel(&dev.device);
1510 pass.set_pipeline(&sk.pipeline);
1511 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1512 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1513 pass.dispatch_workgroups(gx, gy, gz);
1514 }
1515 Step::SelectiveScan { params } => {
1516 let ssk = selective_scan_kernel(&dev.device);
1521 pass.set_pipeline(&ssk.pipeline);
1522 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1523 let total = params.batch * params.hidden;
1524 let (gx, gy, gz) = dispatch_dims(total, 64);
1525 pass.dispatch_workgroups(gx, gy, gz);
1526 }
1527 Step::Mamba2 { params } => {
1528 let mk = mamba2_kernel(&dev.device);
1529 pass.set_pipeline(&mk.pipeline);
1530 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1531 let total = params.batch * params.heads * params.head_dim;
1532 let (gx, gy, gz) = dispatch_dims(total, 64);
1533 pass.dispatch_workgroups(gx, gy, gz);
1534 }
1535 Step::Gru { params } => {
1536 let gk = gru_kernel(&dev.device);
1538 pass.set_pipeline(&gk.pipeline);
1539 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1540 pass.dispatch_workgroups(params.batch, 1, 1);
1541 }
1542 Step::Rnn { params } => {
1543 let rk = rnn_kernel(&dev.device);
1544 pass.set_pipeline(&rk.pipeline);
1545 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1546 pass.dispatch_workgroups(params.batch, 1, 1);
1547 }
1548 Step::DequantMatmul { params } => {
1549 let m_s = scale(params.m);
1550 if m_s == 0 {
1551 continue;
1552 }
1553 let dk = dequant_matmul_kernel(&dev.device);
1554 pass.set_pipeline(&dk.pipeline);
1555 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1556 pass.dispatch_workgroups(params.n.div_ceil(8), m_s.div_ceil(8), 1);
1557 }
1558 Step::FusedResidualLn { params } => {
1559 let outer_s = scale(params.outer);
1560 if outer_s == 0 {
1561 continue;
1562 }
1563 let frk = fused_residual_ln_kernel(&dev.device);
1564 pass.set_pipeline(&frk.pipeline);
1565 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1566 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1567 pass.dispatch_workgroups(gx, gy, gz);
1568 }
1569 Step::FusedResidualLnTee { params } => {
1570 let outer_s = scale(params.outer);
1571 if outer_s == 0 {
1572 continue;
1573 }
1574 let frtk = fused_residual_ln_tee_kernel(&dev.device);
1575 pass.set_pipeline(&frtk.pipeline);
1576 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1577 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1578 pass.dispatch_workgroups(gx, gy, gz);
1579 }
1580 Step::FusedResidualRmsNorm { params } => {
1581 let outer_s = scale(params.outer);
1582 if outer_s == 0 {
1583 continue;
1584 }
1585 let frk = fused_residual_rms_norm_kernel(&dev.device);
1586 pass.set_pipeline(&frk.pipeline);
1587 pass.set_bind_group(0, &self.bind_groups[gpu_bi], &[]);
1588 let (gx, gy, gz) = dispatch_dims(outer_s, 64);
1589 pass.dispatch_workgroups(gx, gy, gz);
1590 }
1591 Step::MatmulQkv { params, kind } => {
1592 let m_s = scale(params.m);
1593 if m_s == 0 {
1594 continue;
1595 }
1596 let qkv_coop_wide = matches!(kind, MatmulQkvKind::CoopF16Vk)
1597 && crate::coop_f16_vk::use_wide_matmul(
1598 params.b_off,
1599 params.n,
1600 &self.coop_f16_b_param,
1601 &self.coop_f16_vk_wide_b,
1602 );
1603 pass.set_bind_group(
1604 0,
1605 coop_f16_vk_bind_group(self, gpu_bi, qkv_coop_wide),
1606 &[],
1607 );
1608 match kind {
1609 MatmulQkvKind::CoopF16Vk => {
1610 if qkv_coop_wide {
1611 pass.set_pipeline(&matmul_qkv_kernel(&dev.device).pipeline);
1612 pass.dispatch_workgroups(
1613 params.n.div_ceil(32),
1614 m_s.div_ceil(32),
1615 1,
1616 );
1617 } else {
1618 let n_eff = scale(params.n);
1619 let mqk = matmul_qkv_coop_f16_vk_active_kernel(
1620 &dev.device,
1621 n_eff,
1622 )
1623 .expect("coop f16 matmul_qkv kernel missing");
1624 pass.set_pipeline(&mqk.pipeline);
1625 pass.dispatch_workgroups(
1626 m_s.div_ceil(16),
1627 params.n.div_ceil(16),
1628 1,
1629 );
1630 }
1631 }
1632 MatmulQkvKind::CoopF32 => {
1633 pass.set_pipeline(
1634 &matmul_qkv_coop_f32_kernel(&dev.device)
1635 .expect("coop matmul_qkv kernel missing")
1636 .pipeline,
1637 );
1638 pass.dispatch_workgroups(
1639 params.n.div_ceil(32),
1640 m_s.div_ceil(32),
1641 1,
1642 );
1643 }
1644 MatmulQkvKind::F32 => {
1645 pass.set_pipeline(&matmul_qkv_kernel(&dev.device).pipeline);
1646 pass.dispatch_workgroups(
1647 params.n.div_ceil(32),
1648 m_s.div_ceil(32),
1649 1,
1650 );
1651 }
1652 }
1653 }
1654 Step::GatherSplit { .. }
1655 | Step::DequantMatmulGguf { .. }
1656 | Step::DequantGroupedMatmulGguf { .. }
1657 | Step::GatedDeltaNet { .. }
1658 | Step::Lstm { .. }
1659 | Step::ConvTranspose2d { .. }
1660 | Step::GroupNormHost { .. }
1661 | Step::LayerNorm2dHost { .. }
1662 | Step::ResizeNearest2xHost { .. }
1663 | Step::ReverseHost { .. }
1664 | Step::ArgReduceHost { .. }
1665 | Step::GruHost { .. }
1666 | Step::RnnHost { .. }
1667 | Step::Llada2GroupLimitedGate { .. }
1668 | Step::UmapKnnHost { .. }
1669 | Step::MsDeformAttnHost { .. }
1670 | Step::FftHost { .. }
1671 | Step::ScanHost { .. }
1672 | Step::Im2ColHost { .. }
1673 | Step::RngNormalHost { .. }
1674 | Step::RngUniformHost { .. }
1675 | Step::WelchPeaksHost { .. }
1676 | Step::LogMelHost { .. }
1677 | Step::LogMelBackwardHost { .. } => {}
1678 #[cfg(feature = "splat")]
1679 Step::GaussianSplatRender { .. }
1680 | Step::GaussianSplatRenderBackward { .. }
1681 | Step::GaussianSplatPrepare { .. }
1682 | Step::GaussianSplatRasterize { .. } => {}
1683 }
1684 if !matches!(step, Step::FftGpu { .. }) {
1685 gpu_bi += 1;
1686 }
1687 step_i += 1;
1688 pass_dispatched = true;
1689 }
1690 }
1691 let needs_f16_drain = step_i < self.schedule.len()
1692 && !step_runs_on_host(&self.schedule[step_i])
1693 && step_i > 0
1694 && step_needs_pass_flush(&self.schedule[step_i], &self.schedule[step_i - 1]);
1695 let gpu_schedule_done = step_i >= self.schedule.len();
1696 let skip_readback = rlx_ir::env::flag("RLX_BENCH_DISPATCH_ONLY") || self.dispatch_only;
1697 let defer_tail = gpu_schedule_done && self.schedule.iter().any(step_is_tail_host);
1698 let mut fused_readback: Option<(
1699 ReadbackLayout,
1700 std::sync::mpsc::Receiver<Result<(), wgpu::BufferAsyncError>>,
1701 Vec<usize>,
1702 )> = None;
1703 if gpu_schedule_done && !skip_readback && !defer_tail {
1704 if !self.gpu_handle_feeds.is_empty() {
1705 self.propagate_gpu_handle_feeds_on_gpu(dev, &mut enc);
1706 }
1707 let plan = self.readback_plan();
1708 let out_ids_all: Vec<_> = self.graph.outputs.clone();
1709 let out_ids: Vec<_> = plan.iter().map(|&i| out_ids_all[i]).collect();
1710 let layout = ReadbackLayout::for_nodes(&self.arena, &out_ids);
1711 if use_tiny_readback(&layout, out_ids.len()) && plan == vec![0] {
1712 if self.tiny_readback.is_none() {
1713 self.tiny_readback = Some(TinyReadbackStaging::new(&dev.device));
1714 }
1715 let tiny = self.tiny_readback.as_ref().expect("tiny readback");
1716 encode_readback_copies(&mut enc, &self.arena, tiny.buffer(), &out_ids, &layout);
1717 let map_rx = schedule_readback_map(&mut enc, tiny.buffer(), &layout);
1718 let sub = dev.queue.submit(std::iter::once(enc.finish()));
1719 wait_readback_map(&dev.device, sub, &map_rx, layout.total_bytes);
1720 map_rx.recv().unwrap().unwrap();
1721 return self.pack_readback_outputs(
1722 &plan,
1723 vec![decode_tiny_mapped_f32(tiny.buffer(), layout.total_bytes)],
1724 );
1725 }
1726 ReadbackStaging::prepare(
1727 &dev.device,
1728 &mut self.readback_staging,
1729 layout.total_bytes,
1730 );
1731 if let Some(staging) = self.readback_staging.as_ref() {
1732 encode_readback_copies(
1733 &mut enc,
1734 &self.arena,
1735 staging.buffer(),
1736 &out_ids,
1737 &layout,
1738 );
1739 let map_rx = schedule_readback_map(&mut enc, staging.buffer(), &layout);
1740 fused_readback = Some((layout, map_rx, plan));
1741 }
1742 }
1743 let main_submission = dev.queue.submit(std::iter::once(enc.finish()));
1744 if defer_tail {
1745 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1746 self.run_tail_host_audio_ops(dev);
1747 if !skip_readback {
1748 let mut rb_enc =
1749 dev.device
1750 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1751 label: Some("rlx-wgpu readback after tail-host"),
1752 });
1753 if !self.gpu_handle_feeds.is_empty() {
1754 self.propagate_gpu_handle_feeds_on_gpu(dev, &mut rb_enc);
1755 }
1756 let plan = self.readback_plan();
1757 let out_ids_all: Vec<_> = self.graph.outputs.clone();
1758 let out_ids: Vec<_> = plan.iter().map(|&i| out_ids_all[i]).collect();
1759 let layout = ReadbackLayout::for_nodes(&self.arena, &out_ids);
1760 if use_tiny_readback(&layout, out_ids.len()) && plan == vec![0] {
1761 if self.tiny_readback.is_none() {
1762 self.tiny_readback = Some(TinyReadbackStaging::new(&dev.device));
1763 }
1764 let tiny = self.tiny_readback.as_ref().expect("tiny readback");
1765 encode_readback_copies(
1766 &mut rb_enc,
1767 &self.arena,
1768 tiny.buffer(),
1769 &out_ids,
1770 &layout,
1771 );
1772 let map_rx = schedule_readback_map(&mut rb_enc, tiny.buffer(), &layout);
1773 let sub = dev.queue.submit(std::iter::once(rb_enc.finish()));
1774 wait_readback_map(&dev.device, sub, &map_rx, layout.total_bytes);
1775 map_rx.recv().unwrap().unwrap();
1776 return self.pack_readback_outputs(
1777 &plan,
1778 vec![decode_tiny_mapped_f32(tiny.buffer(), layout.total_bytes)],
1779 );
1780 }
1781 ReadbackStaging::prepare(
1782 &dev.device,
1783 &mut self.readback_staging,
1784 layout.total_bytes,
1785 );
1786 if let Some(staging) = self.readback_staging.as_ref() {
1787 encode_readback_copies(
1788 &mut rb_enc,
1789 &self.arena,
1790 staging.buffer(),
1791 &out_ids,
1792 &layout,
1793 );
1794 let map_rx = schedule_readback_map(&mut rb_enc, staging.buffer(), &layout);
1795 let sub = dev.queue.submit(std::iter::once(rb_enc.finish()));
1796 wait_readback_map(&dev.device, sub, &map_rx, layout.total_bytes);
1797 map_rx.recv().unwrap().unwrap();
1798 self.dump_node_stats_if_requested(dev);
1799 let partial = decode_mapped_readback_f32(staging.buffer(), &layout);
1800 return self.pack_readback_outputs(&plan, partial);
1801 }
1802 }
1803 }
1804 if needs_f16_drain {
1805 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1806 }
1807 let need_host_sync =
1808 step_i < self.schedule.len() && step_runs_on_host(&self.schedule[step_i]);
1809 if need_host_sync {
1810 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1811 }
1812 if gpu_schedule_done {
1813 if skip_readback || defer_tail {
1814 return self
1815 .graph
1816 .outputs
1817 .iter()
1818 .map(|&id| {
1819 let n = self.graph.node(id).shape.num_elements().unwrap_or(0);
1820 vec![0.0; n]
1821 })
1822 .collect();
1823 }
1824 if let (Some((layout, map_rx, plan)), Some(staging)) =
1825 (fused_readback, self.readback_staging.as_ref())
1826 {
1827 wait_readback_map(&dev.device, main_submission, &map_rx, layout.total_bytes);
1828 map_rx.recv().unwrap().unwrap();
1829 self.dump_node_stats_if_requested(dev);
1830 let partial = decode_mapped_readback_f32(staging.buffer(), &layout);
1831 return self.pack_readback_outputs(&plan, partial);
1832 }
1833 break;
1834 }
1835 match &self.schedule[step_i] {
1836 Step::BufferCopy {
1837 src_byte_off,
1838 dst_byte_off,
1839 bytes,
1840 } => {
1841 let src = *src_byte_off;
1844 let dst = *dst_byte_off;
1845 let nbytes = *bytes as u64;
1846 let elems = (nbytes / 4).max(1) as u32;
1847 let lo = src.min(dst);
1848 let hi = src.saturating_add(nbytes).max(dst.saturating_add(nbytes));
1849 let max_binding = dev.device.limits().max_storage_buffer_binding_size;
1850 if hi.saturating_sub(lo) > max_binding {
1856 let bytes_host = self.arena.read_bytes_range(
1857 &dev.device,
1858 &dev.queue,
1859 src as usize,
1860 nbytes as usize,
1861 );
1862 self.arena
1863 .write_bytes_range(&dev.queue, dst as usize, &bytes_host);
1864 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1865 step_i += 1;
1866 continue;
1867 }
1868 let mut base = (lo / 256) * 256;
1869 let span = hi.saturating_sub(base).max(1);
1875 let mut size = span.div_ceil(256) * 256;
1876 size = size.max(256).min(max_binding);
1877 if base.saturating_add(size) > self.arena.size as u64 {
1878 base = (self.arena.size as u64).saturating_sub(size);
1879 base = (base / 256) * 256;
1880 }
1881 let p = CopyParams {
1882 n: elems,
1883 in_off: (src.saturating_sub(base) / 4) as u32,
1884 out_off: (dst.saturating_sub(base) / 4) as u32,
1885 _p0: 0,
1886 _p1: 0,
1887 _p2: 0,
1888 _p3: 0,
1889 _p4: 0,
1890 };
1891 let ck = copy_kernel(&dev.device);
1892 let u = dev.device.create_buffer(&wgpu::BufferDescriptor {
1893 label: Some("rlx-wgpu arena_copy uniform"),
1894 size: std::mem::size_of::<CopyParams>() as u64,
1895 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
1896 mapped_at_creation: false,
1897 });
1898 dev.queue.write_buffer(&u, 0, bytemuck::bytes_of(&p));
1899 let bg =
1900 bind_two_buf0_window(&dev.device, ck, &self.arena.buffer, base, size, &u);
1901 let mut enc =
1902 dev.device
1903 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1904 label: Some("rlx-wgpu arena_copy"),
1905 });
1906 {
1907 let mut pass = enc.begin_compute_pass(&wgpu::ComputePassDescriptor {
1908 label: Some("rlx-wgpu arena_copy pass"),
1909 ..Default::default()
1910 });
1911 pass.set_pipeline(&ck.pipeline);
1912 pass.set_bind_group(0, &bg, &[]);
1913 let (gx, gy, gz) = dispatch_dims(elems, 64);
1914 pass.dispatch_workgroups(gx, gy, gz);
1915 }
1916 dev.queue.submit(std::iter::once(enc.finish()));
1917 let _ = dev.device.poll(wgpu::PollType::wait_indefinitely());
1918 }
1919 Step::GatherSplit {
1920 n_out,
1921 n_idx,
1922 dim,
1923 vocab,
1924 table_byte_off,
1925 idx_byte_off,
1926 out_byte_off,
1927 } => {
1928 run_gather_split(
1929 &self.arena,
1930 &dev.device,
1931 &dev.queue,
1932 *n_out,
1933 *n_idx,
1934 *dim,
1935 *vocab,
1936 *table_byte_off as usize,
1937 *idx_byte_off as usize,
1938 *out_byte_off as usize,
1939 );
1940 }
1941 Step::DequantMatmulGguf {
1942 m,
1943 k,
1944 n,
1945 scheme_id,
1946 x_byte_off,
1947 w_byte_off,
1948 out_byte_off,
1949 } => {
1950 if *m == 1 && crate::gguf_gpu::gemv_supports_scheme(*scheme_id) {
1951 crate::gguf_gpu::run_dequant_matmul_gguf_gemv(
1955 &self.arena,
1956 &dev.device,
1957 &dev.queue,
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 } else if self.dequant_scratch_off > 0 {
1966 crate::gguf_gpu::run_dequant_matmul_gguf_gpu(
1967 &self.arena,
1968 &dev.device,
1969 &dev.queue,
1970 *m as usize,
1971 *k as usize,
1972 *n as usize,
1973 *scheme_id,
1974 *x_byte_off as usize,
1975 *w_byte_off as usize,
1976 self.dequant_scratch_off,
1977 *out_byte_off as usize,
1978 );
1979 } else {
1980 crate::gguf_host::run_dequant_matmul_gguf(
1981 &self.arena,
1982 &dev.device,
1983 &dev.queue,
1984 *m as usize,
1985 *k as usize,
1986 *n as usize,
1987 *scheme_id,
1988 *x_byte_off as usize,
1989 *w_byte_off as usize,
1990 *out_byte_off as usize,
1991 );
1992 }
1993 }
1994 Step::DequantGroupedMatmulGguf {
1995 m,
1996 k,
1997 n,
1998 num_experts,
1999 scheme_id,
2000 x_byte_off,
2001 w_byte_off,
2002 idx_byte_off,
2003 out_byte_off,
2004 } => {
2005 if self.dequant_scratch_off > 0 {
2006 crate::gguf_gpu::run_dequant_grouped_matmul_gguf_gpu(
2007 &self.arena,
2008 &dev.device,
2009 &dev.queue,
2010 *m as usize,
2011 *k as usize,
2012 *n as usize,
2013 *num_experts as usize,
2014 *scheme_id,
2015 *x_byte_off as usize,
2016 *w_byte_off as usize,
2017 *idx_byte_off as usize,
2018 self.dequant_scratch_off,
2019 *out_byte_off as usize,
2020 );
2021 } else {
2022 crate::gguf_host::run_dequant_grouped_matmul_gguf(
2023 &self.arena,
2024 &dev.device,
2025 &dev.queue,
2026 *m as usize,
2027 *k as usize,
2028 *n as usize,
2029 *num_experts as usize,
2030 *scheme_id,
2031 *x_byte_off as usize,
2032 *w_byte_off as usize,
2033 *idx_byte_off as usize,
2034 *out_byte_off as usize,
2035 );
2036 }
2037 }
2038 Step::GatedDeltaNet {
2039 q_byte_off,
2040 k_byte_off,
2041 v_byte_off,
2042 g_byte_off,
2043 beta_byte_off,
2044 state_byte_off,
2045 dst_byte_off,
2046 batch,
2047 seq,
2048 heads,
2049 state_size,
2050 use_carry,
2051 } => {
2052 crate::gdn_host::run_gated_delta_net(
2053 &self.arena,
2054 &dev.device,
2055 &dev.queue,
2056 *q_byte_off as usize,
2057 *k_byte_off as usize,
2058 *v_byte_off as usize,
2059 *g_byte_off as usize,
2060 *beta_byte_off as usize,
2061 *state_byte_off as usize,
2062 *dst_byte_off as usize,
2063 *batch as usize,
2064 *seq as usize,
2065 *heads as usize,
2066 *state_size as usize,
2067 *use_carry,
2068 );
2069 }
2070 Step::Lstm {
2071 x_byte_off,
2072 w_ih_byte_off,
2073 w_hh_byte_off,
2074 bias_byte_off,
2075 h0_byte_off,
2076 c0_byte_off,
2077 dst_byte_off,
2078 batch,
2079 seq,
2080 input_size,
2081 hidden,
2082 num_layers,
2083 bidirectional,
2084 carry,
2085 } => {
2086 crate::lstm_host::run_lstm(
2087 &self.arena,
2088 &dev.device,
2089 &dev.queue,
2090 *x_byte_off as usize,
2091 *w_ih_byte_off as usize,
2092 *w_hh_byte_off as usize,
2093 *bias_byte_off as usize,
2094 *h0_byte_off as usize,
2095 *c0_byte_off as usize,
2096 *dst_byte_off as usize,
2097 *batch as usize,
2098 *seq as usize,
2099 *input_size as usize,
2100 *hidden as usize,
2101 *num_layers as usize,
2102 *bidirectional,
2103 *carry,
2104 );
2105 }
2106 Step::ConvTranspose2d {
2107 src_byte_off,
2108 weight_byte_off,
2109 dst_byte_off,
2110 n,
2111 c_in,
2112 h,
2113 w_in,
2114 c_out,
2115 h_out,
2116 w_out,
2117 kh,
2118 kw,
2119 sh,
2120 sw,
2121 ph,
2122 pw,
2123 dh,
2124 dw,
2125 groups,
2126 } => {
2127 crate::conv_transpose2d_host::run_conv_transpose2d(
2128 &self.arena,
2129 &dev.device,
2130 &dev.queue,
2131 *src_byte_off as usize,
2132 *weight_byte_off as usize,
2133 *dst_byte_off as usize,
2134 *n as usize,
2135 *c_in as usize,
2136 *h as usize,
2137 *w_in as usize,
2138 *c_out as usize,
2139 *h_out as usize,
2140 *w_out as usize,
2141 *kh as usize,
2142 *kw as usize,
2143 *sh as usize,
2144 *sw as usize,
2145 *ph as usize,
2146 *pw as usize,
2147 *dh as usize,
2148 *dw as usize,
2149 *groups as usize,
2150 );
2151 }
2152 Step::GroupNormHost {
2153 src_byte_off,
2154 gamma_byte_off,
2155 beta_byte_off,
2156 dst_byte_off,
2157 n,
2158 c,
2159 h,
2160 w,
2161 num_groups,
2162 eps,
2163 } => {
2164 crate::vision_host::run_group_norm(
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 *num_groups as usize,
2177 *eps,
2178 );
2179 }
2180 Step::LayerNorm2dHost {
2181 src_byte_off,
2182 gamma_byte_off,
2183 beta_byte_off,
2184 dst_byte_off,
2185 n,
2186 c,
2187 h,
2188 w,
2189 eps,
2190 } => {
2191 crate::vision_host::run_layer_norm2d(
2192 &self.arena,
2193 &dev.device,
2194 &dev.queue,
2195 *src_byte_off as usize,
2196 *gamma_byte_off as usize,
2197 *beta_byte_off as usize,
2198 *dst_byte_off as usize,
2199 *n as usize,
2200 *c as usize,
2201 *h as usize,
2202 *w as usize,
2203 *eps,
2204 );
2205 }
2206 Step::ResizeNearest2xHost {
2207 src_byte_off,
2208 dst_byte_off,
2209 n,
2210 c,
2211 h,
2212 w,
2213 } => {
2214 crate::vision_host::run_resize_nearest_2x(
2215 &self.arena,
2216 &dev.device,
2217 &dev.queue,
2218 *src_byte_off as usize,
2219 *dst_byte_off as usize,
2220 *n as usize,
2221 *c as usize,
2222 *h as usize,
2223 *w as usize,
2224 );
2225 }
2226 Step::ReverseHost {
2227 src_byte_off,
2228 dst_byte_off,
2229 dims,
2230 rev_mask,
2231 elem_bytes,
2232 } => {
2233 crate::vision_host::run_reverse(
2234 &self.arena,
2235 &dev.device,
2236 &dev.queue,
2237 *src_byte_off as usize,
2238 *dst_byte_off as usize,
2239 dims,
2240 rev_mask,
2241 *elem_bytes as usize,
2242 );
2243 }
2244 Step::ArgReduceHost {
2245 src_byte_off,
2246 dst_byte_off,
2247 outer,
2248 reduced,
2249 inner,
2250 is_max,
2251 } => {
2252 crate::vision_host::run_argreduce(
2253 &self.arena,
2254 &dev.device,
2255 &dev.queue,
2256 *src_byte_off as usize,
2257 *dst_byte_off as usize,
2258 *outer as usize,
2259 *reduced as usize,
2260 *inner as usize,
2261 *is_max,
2262 );
2263 }
2264 Step::GruHost {
2265 x,
2266 w_ih,
2267 w_hh,
2268 b_ih,
2269 b_hh,
2270 h0,
2271 dst,
2272 batch,
2273 seq,
2274 input_size,
2275 hidden,
2276 num_layers,
2277 bidirectional,
2278 carry,
2279 } => {
2280 crate::vision_host::run_gru(
2281 &self.arena,
2282 &dev.device,
2283 &dev.queue,
2284 *x as usize,
2285 *w_ih as usize,
2286 *w_hh as usize,
2287 *b_ih as usize,
2288 *b_hh as usize,
2289 *h0 as usize,
2290 *dst as usize,
2291 *batch as usize,
2292 *seq as usize,
2293 *input_size as usize,
2294 *hidden as usize,
2295 *num_layers as usize,
2296 *bidirectional,
2297 *carry,
2298 );
2299 }
2300 Step::RnnHost {
2301 x,
2302 w_ih,
2303 w_hh,
2304 bias,
2305 h0,
2306 dst,
2307 batch,
2308 seq,
2309 input_size,
2310 hidden,
2311 num_layers,
2312 bidirectional,
2313 carry,
2314 relu,
2315 } => {
2316 crate::vision_host::run_rnn(
2317 &self.arena,
2318 &dev.device,
2319 &dev.queue,
2320 *x as usize,
2321 *w_ih as usize,
2322 *w_hh as usize,
2323 *bias as usize,
2324 *h0 as usize,
2325 *dst as usize,
2326 *batch as usize,
2327 *seq as usize,
2328 *input_size as usize,
2329 *hidden as usize,
2330 *num_layers as usize,
2331 *bidirectional,
2332 *carry,
2333 *relu,
2334 );
2335 }
2336 Step::Llada2GroupLimitedGate {
2337 sig_byte_off,
2338 route_byte_off,
2339 out_byte_off,
2340 n_elems,
2341 attrs,
2342 } => {
2343 crate::llada2_gate_host::run_llada2_group_limited_gate(
2344 &self.arena,
2345 &dev.device,
2346 &dev.queue,
2347 *sig_byte_off as usize,
2348 *route_byte_off as usize,
2349 *out_byte_off as usize,
2350 *n_elems as usize,
2351 attrs,
2352 );
2353 }
2354 Step::UmapKnnHost {
2355 pairwise_byte_off,
2356 out_byte_off,
2357 n,
2358 k,
2359 } => {
2360 crate::umap_knn_host::run_umap_knn(
2361 &self.arena,
2362 &dev.device,
2363 &dev.queue,
2364 *pairwise_byte_off as usize,
2365 *out_byte_off as usize,
2366 *n as usize,
2367 *k as usize,
2368 );
2369 }
2370 Step::MsDeformAttnHost {
2371 in_offs,
2372 out_byte_off,
2373 out_bytes,
2374 attrs,
2375 } => {
2376 crate::ms_deform_attn::run_ms_deform_attn(
2377 &self.arena,
2378 &dev.device,
2379 &dev.queue,
2380 in_offs,
2381 *out_byte_off as usize,
2382 *out_bytes as usize,
2383 attrs,
2384 );
2385 }
2386 Step::FftHost {
2387 src_byte_off,
2388 dst_byte_off,
2389 outer,
2390 n_complex,
2391 inverse,
2392 norm_tag,
2393 dtype_tag,
2394 } => {
2395 crate::fft_host::run_fft1d(
2396 &self.arena,
2397 &dev.device,
2398 &dev.queue,
2399 *src_byte_off as usize,
2400 *dst_byte_off as usize,
2401 *outer as usize,
2402 *n_complex as usize,
2403 *inverse,
2404 *norm_tag,
2405 fft_dtype_from_tag(*dtype_tag),
2406 );
2407 }
2408 Step::ScanHost {
2409 plan,
2410 outer_init_off,
2411 outer_final_off,
2412 length,
2413 save_trajectory,
2414 xs_outer,
2415 bcast_outer,
2416 } => {
2417 crate::scan_host::run_scan(
2418 &self.arena,
2419 &dev.device,
2420 &dev.queue,
2421 plan,
2422 *outer_init_off,
2423 *outer_final_off,
2424 *length,
2425 *save_trajectory,
2426 xs_outer,
2427 bcast_outer,
2428 );
2429 }
2430 Step::WelchPeaksHost { .. }
2431 | Step::LogMelHost { .. }
2432 | Step::LogMelBackwardHost { .. } => {
2433 unreachable!("tail-host audio ops run after GPU wait")
2434 }
2435 Step::Im2ColHost {
2436 x_byte_off,
2437 col_byte_off,
2438 n,
2439 c_in,
2440 h,
2441 w,
2442 h_out,
2443 w_out,
2444 kh,
2445 kw,
2446 sh,
2447 sw,
2448 ph,
2449 pw,
2450 dh,
2451 dw_dil,
2452 } => {
2453 crate::im2col_host::run_im2col(
2454 &self.arena,
2455 &dev.device,
2456 &dev.queue,
2457 *x_byte_off as usize,
2458 *col_byte_off as usize,
2459 *n,
2460 *c_in,
2461 *h,
2462 *w,
2463 *h_out,
2464 *w_out,
2465 *kh,
2466 *kw,
2467 *sh,
2468 *sw,
2469 *ph,
2470 *pw,
2471 *dh,
2472 *dw_dil,
2473 );
2474 }
2475 Step::RngNormalHost {
2476 dst_byte_off,
2477 len,
2478 mean,
2479 scale,
2480 key,
2481 op_seed,
2482 } => {
2483 let opts = *self.rng.read().expect("rng lock");
2484 crate::rng_host::run_rng_normal(
2485 &self.arena,
2486 &dev.queue,
2487 *dst_byte_off as usize,
2488 *len as usize,
2489 *mean,
2490 *scale,
2491 *key,
2492 *op_seed,
2493 opts,
2494 );
2495 }
2496 Step::RngUniformHost {
2497 dst_byte_off,
2498 len,
2499 low,
2500 high,
2501 key,
2502 op_seed,
2503 } => {
2504 let opts = *self.rng.read().expect("rng lock");
2505 crate::rng_host::run_rng_uniform(
2506 &self.arena,
2507 &dev.queue,
2508 *dst_byte_off as usize,
2509 *len as usize,
2510 *low,
2511 *high,
2512 *key,
2513 *op_seed,
2514 opts,
2515 );
2516 }
2517 #[cfg(feature = "splat")]
2518 Step::GaussianSplatRender {
2519 positions_byte_off,
2520 positions_len,
2521 scales_byte_off,
2522 scales_len,
2523 rotations_byte_off,
2524 rotations_len,
2525 opacities_byte_off,
2526 opacities_len,
2527 colors_byte_off,
2528 colors_len,
2529 sh_coeffs_byte_off,
2530 sh_coeffs_len,
2531 meta_byte_off,
2532 dst_byte_off,
2533 dst_len,
2534 width,
2535 height,
2536 tile_size,
2537 radius_scale,
2538 alpha_cutoff,
2539 max_splat_steps,
2540 transmittance_threshold,
2541 max_list_entries,
2542 } => {
2543 crate::splat::run_gaussian_splat_render(
2544 &self.arena,
2545 &dev.device,
2546 &dev.queue,
2547 *positions_byte_off as usize,
2548 *positions_len as usize,
2549 *scales_byte_off as usize,
2550 *scales_len as usize,
2551 *rotations_byte_off as usize,
2552 *rotations_len as usize,
2553 *opacities_byte_off as usize,
2554 *opacities_len as usize,
2555 *colors_byte_off as usize,
2556 *colors_len as usize,
2557 *sh_coeffs_byte_off as usize,
2558 *sh_coeffs_len as usize,
2559 *meta_byte_off as usize,
2560 *dst_byte_off as usize,
2561 *dst_len as usize,
2562 *width,
2563 *height,
2564 *tile_size,
2565 *radius_scale,
2566 *alpha_cutoff,
2567 *max_splat_steps,
2568 *transmittance_threshold,
2569 *max_list_entries,
2570 );
2571 }
2572 #[cfg(feature = "splat")]
2573 Step::GaussianSplatPrepare {
2574 positions_byte_off,
2575 positions_len,
2576 scales_byte_off,
2577 scales_len,
2578 rotations_byte_off,
2579 rotations_len,
2580 opacities_byte_off,
2581 opacities_len,
2582 colors_byte_off,
2583 colors_len,
2584 sh_coeffs_byte_off,
2585 sh_coeffs_len,
2586 meta_byte_off,
2587 meta_len,
2588 prep_byte_off,
2589 prep_len,
2590 width,
2591 height,
2592 tile_size,
2593 radius_scale,
2594 alpha_cutoff,
2595 max_splat_steps,
2596 transmittance_threshold,
2597 max_list_entries,
2598 } => {
2599 crate::splat::run_gaussian_splat_prepare(
2600 &self.arena,
2601 &dev.device,
2602 &dev.queue,
2603 *positions_byte_off as usize,
2604 *positions_len as usize,
2605 *scales_byte_off as usize,
2606 *scales_len as usize,
2607 *rotations_byte_off as usize,
2608 *rotations_len as usize,
2609 *opacities_byte_off as usize,
2610 *opacities_len as usize,
2611 *colors_byte_off as usize,
2612 *colors_len as usize,
2613 *sh_coeffs_byte_off as usize,
2614 *sh_coeffs_len as usize,
2615 *meta_byte_off as usize,
2616 *meta_len as usize,
2617 *prep_byte_off as usize,
2618 *prep_len as usize,
2619 *width,
2620 *height,
2621 *tile_size,
2622 *radius_scale,
2623 *alpha_cutoff,
2624 *max_splat_steps,
2625 *transmittance_threshold,
2626 *max_list_entries,
2627 );
2628 }
2629 #[cfg(feature = "splat")]
2630 Step::GaussianSplatRasterize {
2631 prep_byte_off,
2632 prep_len,
2633 meta_byte_off,
2634 meta_len,
2635 dst_byte_off,
2636 dst_len,
2637 count,
2638 width,
2639 height,
2640 tile_size,
2641 alpha_cutoff,
2642 max_splat_steps,
2643 transmittance_threshold,
2644 max_list_entries,
2645 } => {
2646 crate::splat::run_gaussian_splat_rasterize(
2647 &self.arena,
2648 &dev.device,
2649 &dev.queue,
2650 *prep_byte_off as usize,
2651 *prep_len as usize,
2652 *meta_byte_off as usize,
2653 *meta_len as usize,
2654 *dst_byte_off as usize,
2655 *dst_len as usize,
2656 *count as usize,
2657 *width,
2658 *height,
2659 *tile_size,
2660 *alpha_cutoff,
2661 *max_splat_steps,
2662 *transmittance_threshold,
2663 *max_list_entries,
2664 );
2665 }
2666 #[cfg(feature = "splat")]
2667 Step::GaussianSplatRenderBackward {
2668 positions_byte_off,
2669 positions_len,
2670 scales_byte_off,
2671 scales_len,
2672 rotations_byte_off,
2673 rotations_len,
2674 opacities_byte_off,
2675 opacities_len,
2676 colors_byte_off,
2677 colors_len,
2678 sh_coeffs_byte_off,
2679 sh_coeffs_len,
2680 meta_byte_off,
2681 d_loss_byte_off,
2682 d_loss_len,
2683 packed_byte_off,
2684 packed_len,
2685 width,
2686 height,
2687 tile_size,
2688 radius_scale,
2689 alpha_cutoff,
2690 max_splat_steps,
2691 transmittance_threshold,
2692 max_list_entries,
2693 loss_grad_clip,
2694 sh_band,
2695 max_anisotropy,
2696 } => {
2697 crate::splat::run_gaussian_splat_render_backward(
2698 &self.arena,
2699 &dev.device,
2700 &dev.queue,
2701 *positions_byte_off as usize,
2702 *positions_len as usize,
2703 *scales_byte_off as usize,
2704 *scales_len as usize,
2705 *rotations_byte_off as usize,
2706 *rotations_len as usize,
2707 *opacities_byte_off as usize,
2708 *opacities_len as usize,
2709 *colors_byte_off as usize,
2710 *colors_len as usize,
2711 *sh_coeffs_byte_off as usize,
2712 *sh_coeffs_len as usize,
2713 *meta_byte_off as usize,
2714 *d_loss_byte_off as usize,
2715 *d_loss_len as usize,
2716 *packed_byte_off as usize,
2717 *packed_len as usize,
2718 *width,
2719 *height,
2720 *tile_size,
2721 *radius_scale,
2722 *alpha_cutoff,
2723 *max_splat_steps,
2724 *transmittance_threshold,
2725 *max_list_entries,
2726 *loss_grad_clip,
2727 *sh_band,
2728 *max_anisotropy,
2729 );
2730 }
2731 _ => break,
2732 }
2733 step_i += 1;
2734 }
2735
2736 self.dump_node_stats_if_requested(dev);
2737
2738 if rlx_ir::env::flag("RLX_WGPU_NAN_TRACE") {
2739 let mut bad_nodes = Vec::new();
2740 for node in self.graph.nodes() {
2741 if !self.arena.has(node.id) {
2742 continue;
2743 }
2744 if matches!(
2746 node.op,
2747 rlx_ir::Op::Input { .. }
2748 | rlx_ir::Op::Param { .. }
2749 | rlx_ir::Op::Constant { .. }
2750 ) {
2751 continue;
2752 }
2753 let data = self.arena.read_f32(&dev.device, &dev.queue, node.id);
2754 let nan_count = data.iter().filter(|v| v.is_nan()).count();
2755 let inf_count = data.iter().filter(|v| v.is_infinite()).count();
2756 if nan_count > 0 || inf_count > 0 {
2757 let first_nan = data.iter().position(|v| v.is_nan());
2759 if let Some(idx) = first_nan {
2760 let lo = idx.saturating_sub(2);
2761 let hi = (idx + 3).min(data.len());
2762 eprintln!(
2763 " node {:?} op={:?} len={} nan={} inf={} \
2764 first_nan_idx={} ctx={:?}",
2765 node.id,
2766 node.op,
2767 data.len(),
2768 nan_count,
2769 inf_count,
2770 idx,
2771 &data[lo..hi]
2772 );
2773 }
2774 bad_nodes.push((node.id, data.len(), nan_count, inf_count));
2775 if bad_nodes.len() >= 3 {
2776 break;
2777 }
2778 }
2779 }
2780 if bad_nodes.is_empty() {
2781 eprintln!("[wgpu-nan-trace] no NaN/Inf in any node — clean run");
2782 } else {
2783 eprintln!(
2784 "[wgpu-nan-trace] first {} bad nodes (above)",
2785 bad_nodes.len()
2786 );
2787 }
2788 }
2789
2790 if rlx_ir::env::flag("RLX_BENCH_DISPATCH_ONLY") {
2791 return self
2792 .graph
2793 .outputs
2794 .iter()
2795 .map(|&id| {
2796 let n = self.graph.node(id).shape.num_elements().unwrap_or(0);
2797 vec![0.0; n]
2798 })
2799 .collect();
2800 }
2801 let out_ids: Vec<_> = self.graph.outputs.clone();
2802 read_f32_many_pooled(
2803 &self.arena,
2804 &dev.device,
2805 &dev.queue,
2806 &out_ids,
2807 &mut self.readback_staging,
2808 )
2809 }
2810}