use ruda_kernel::dsl as kernel_dsl;
use core::f32::consts::PI;
use ruda_kernel::dsl::prelude::*;
use ruda_kernel::library::tensor::AsView as _;
use ruda_kernel::library::tensor::AsViewExpand;
use ruda_kernel::library::tensor::AsViewMut as _;
use ruda_kernel::library::tensor::AsViewMutExpand;
use ruda_kernel::library::tensor::TensorHandle;
use crate::{
fft::{
FftMode,
fft_parallel::{bit_reverse, fft_butterfly_parallel},
},
layout::BatchSignalLayout,
};
pub(crate) const MAX_SHARED_N_FFT: usize = 4096;
const MAX_UNITS_PER_RUDA: usize = 256;
pub(crate) struct CfftBindings<R: Runtime> {
pub(crate) input_re: TensorBinding<R>,
pub(crate) input_im: TensorBinding<R>,
pub(crate) output_re: TensorBinding<R>,
pub(crate) output_im: TensorBinding<R>,
}
#[derive(Clone, Copy)]
struct CfftPlan {
dim: usize,
count: usize,
n_fft: usize,
fft_mode: FftMode,
}
pub(crate) fn factor_four_step(n_fft: usize) -> (usize, usize) {
assert!(
n_fft.is_power_of_two(),
"four-step needs power-of-two n_fft"
);
let log2_n = n_fft.trailing_zeros() as usize;
let max_log2 = MAX_SHARED_N_FFT.trailing_zeros() as usize;
let log2_n1 = log2_n / 2;
let log2_n2 = log2_n - log2_n1;
let (log2_n1, log2_n2) = if log2_n2 > max_log2 {
(log2_n - max_log2, max_log2)
} else {
(log2_n1, log2_n2)
};
assert!(
log2_n1 <= max_log2 && log2_n2 <= max_log2,
"four-step cannot handle n_fft = {n_fft} with MAX_SHARED_N_FFT = {MAX_SHARED_N_FFT}",
);
(1 << log2_n1, 1 << log2_n2)
}
pub(crate) fn cfft_launch_any_size<R: Runtime>(
client: &ComputeClient<R>,
bindings: CfftBindings<R>,
dim: usize,
dtype: StorageType,
fft_mode: FftMode,
) -> Result<(), LaunchError> {
let n_fft = bindings.input_re.shape[dim];
assert!(n_fft.is_power_of_two(), "cfft needs power-of-two n_fft");
assert!(n_fft >= 2);
let count: usize = bindings
.input_re
.shape
.iter()
.enumerate()
.filter(|(i, _)| *i != dim)
.map(|(_, e)| *e)
.product();
if count == 0 {
return Ok(());
}
let plan = CfftPlan {
dim,
count,
n_fft,
fft_mode,
};
if n_fft <= MAX_SHARED_N_FFT {
cfft_shared_launch::<R>(client, bindings, plan)
} else {
cfft_four_step_launch::<R>(client, bindings, dtype, plan)
}
}
fn cfft_shared_launch<R: Runtime>(
client: &ComputeClient<R>,
bindings: CfftBindings<R>,
plan: CfftPlan,
) -> Result<(), LaunchError> {
let log2_n = plan.n_fft.trailing_zeros() as usize;
let threads_per_ruda = (plan.n_fft / 2).clamp(1, MAX_UNITS_PER_RUDA);
let ruda_dim = RudaDim::new_1d(threads_per_ruda as u32);
let ruda_count =
ruda_kernel::dsl::calculate_ruda_count_elemwise(client, plan.count, RudaDim::new_single());
cfft_shared_kernel::launch::<f32, R>(
client,
ruda_count,
ruda_dim,
bindings.input_re.into_tensor_arg(),
bindings.input_im.into_tensor_arg(),
bindings.output_re.into_tensor_arg(),
bindings.output_im.into_tensor_arg(),
plan.count as u32,
plan.n_fft,
log2_n,
threads_per_ruda,
plan.dim,
plan.fft_mode,
);
Ok(())
}
fn cfft_four_step_launch<R: Runtime>(
client: &ComputeClient<R>,
bindings: CfftBindings<R>,
dtype: StorageType,
plan: CfftPlan,
) -> Result<(), LaunchError> {
let (n1, n2) = factor_four_step(plan.n_fft);
let scratch_shape: Vec<usize> = bindings.input_re.shape.to_vec();
let elems: usize = scratch_shape.iter().product();
let scratch_re = TensorHandle::<R>::new_contiguous(
scratch_shape.clone(),
client.empty(elems * dtype.size()),
dtype,
);
let scratch_im = TensorHandle::<R>::new_contiguous(
scratch_shape.clone(),
client.empty(elems * dtype.size()),
dtype,
);
{
let threads_per_ruda = (n1 / 2).clamp(1, MAX_UNITS_PER_RUDA);
let log2_n1 = n1.trailing_zeros() as usize;
let ruda_dim = RudaDim::new_1d(threads_per_ruda as u32);
let ruda_count =
ruda_kernel::dsl::calculate_ruda_count_elemwise(client, plan.count * n2, RudaDim::new_single());
cfft_four_step_radix1_kernel::launch::<f32, R>(
client,
ruda_count,
ruda_dim,
bindings.input_re.into_tensor_arg(),
bindings.input_im.into_tensor_arg(),
scratch_re.clone().binding().into_tensor_arg(),
scratch_im.clone().binding().into_tensor_arg(),
(plan.count * n2) as u32,
n1,
n2,
log2_n1,
threads_per_ruda,
plan.dim,
plan.fft_mode,
);
}
{
let threads_per_ruda = (n2 / 2).clamp(1, MAX_UNITS_PER_RUDA);
let log2_n2 = n2.trailing_zeros() as usize;
let ruda_dim = RudaDim::new_1d(threads_per_ruda as u32);
let ruda_count =
ruda_kernel::dsl::calculate_ruda_count_elemwise(client, plan.count * n1, RudaDim::new_single());
cfft_four_step_radix2_kernel::launch::<f32, R>(
client,
ruda_count,
ruda_dim,
scratch_re.clone().binding().into_tensor_arg(),
scratch_im.clone().binding().into_tensor_arg(),
(plan.count * n1) as u32,
n1,
n2,
log2_n2,
threads_per_ruda,
plan.dim,
plan.fft_mode,
);
}
{
let total = plan.count * plan.n_fft;
let ruda_dim = RudaDim::new_1d(256);
let ruda_count = ruda_kernel::dsl::calculate_ruda_count_elemwise(client, total, ruda_dim);
cfft_four_step_transpose_kernel::launch::<f32, R>(
client,
ruda_count,
ruda_dim,
scratch_re.binding().into_tensor_arg(),
scratch_im.binding().into_tensor_arg(),
bindings.output_re.into_tensor_arg(),
bindings.output_im.into_tensor_arg(),
total as u32,
n1,
n2,
plan.dim,
);
}
Ok(())
}
mod kernels;
use kernels::*;