#![cfg(feature = "gpu")]
use std::sync::Arc;
use rlx_ir::{DType, Graph, OpExtension, Shape, register_op};
use rlx_runtime::{Device, Session};
use rlx_wgpu::wgpu_gpu_custom::{WgpuGpuKernel, register_wgpu_gpu_kernel};
mod common;
struct TimesThreeIr;
impl OpExtension for TimesThreeIr {
fn name(&self) -> &str {
"test.times_three_wgpu"
}
fn num_inputs(&self) -> usize {
1
}
fn infer_shape(&self, inputs: &[&Shape], _: &[u8]) -> Shape {
inputs[0].clone()
}
}
const WGSL: &str = r#"
@group(0) @binding(0) var<storage, read_write> arena: array<f32>;
@group(0) @binding(1) var<storage, read> params: array<u32>;
@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
let i = gid.x;
let out_off = params[0];
let out_len = params[1];
let in0_off = params[4];
if (i < out_len) {
arena[out_off + i] = arena[in0_off + i] * 3.0;
}
}
"#;
#[derive(Debug)]
struct TimesThreeWgpu;
impl WgpuGpuKernel for TimesThreeWgpu {
fn name(&self) -> &str {
"test.times_three_wgpu"
}
fn wgsl(&self) -> &str {
WGSL
}
}
#[test]
fn times_three_runs_on_wgpu_via_raw_gpu_kernel() {
let _gpu = common::serialize_gpu();
if common::skip_unless_available(rlx_runtime::Device::Gpu, "wgpu") {
return;
}
register_op(Arc::new(TimesThreeIr));
register_wgpu_gpu_kernel(Arc::new(TimesThreeWgpu));
let n = 8usize;
let mut g = Graph::new("wgpu_gpu_custom");
let x = g.input("x", Shape::new(&[n], DType::F32));
let y = g.custom_op("test.times_three_wgpu", vec![], vec![x]);
g.set_outputs(vec![y]);
let mut c = Session::new(Device::Gpu).compile(g);
let x0: Vec<f32> = (0..n).map(|i| i as f32 - 3.0).collect();
let outs = c.run(&[("x", &x0)]);
let out = &outs[0];
let expect: Vec<f32> = x0.iter().map(|v| v * 3.0).collect();
assert_eq!(out.len(), expect.len(), "out={out:?}");
for (a, b) in out.iter().zip(expect.iter()) {
assert!((a - b).abs() < 1e-4, "out={out:?} expect={expect:?}");
}
}