use ndarray::Array4;
use std::os::raw::{c_int, c_longlong, c_uint};
use std::sync::OnceLock;
type ScatterFn = unsafe extern "C" fn(
*const c_longlong, *const c_int, *const c_int, *const c_int, c_longlong, *const c_longlong, c_int, c_longlong, c_int, c_uint, *const c_longlong, *const c_longlong, c_int, c_int, c_int, c_int, *mut u8, ) -> c_int;
fn lib_path() -> String {
std::env::var("EVLIB_CUDA_LIB").unwrap_or_else(|_| "librvt_scatter.so".to_string())
}
static LIB: OnceLock<libloading::Library> = OnceLock::new();
fn cuda_lib() -> Result<&'static libloading::Library, String> {
if let Some(l) = LIB.get() {
return Ok(l);
}
let path = lib_path();
let loaded = unsafe { libloading::Library::new(&path) }
.map_err(|e| format!("failed to load CUDA library {path}: {e}"))?;
let _ = LIB.set(loaded);
Ok(LIB.get().expect("CUDA library set"))
}
#[allow(clippy::too_many_arguments)]
pub fn stacked_histogram_dense_cuda(
t: &[i64],
x: &[i32],
y: &[i32],
p: &[i32],
grid: &[i64],
delta_t_us: i64,
nbins: usize,
count_cutoff: u32,
row_map: &[i64],
col_map: &[i64],
out_h: usize,
out_w: usize,
) -> Result<Array4<u8>, String> {
let n_windows = grid.len();
let channels = 2 * nbins;
let buf_len = n_windows * channels * out_h * out_w;
let mut out = vec![0u8; buf_len];
let libh = cuda_lib()?;
unsafe {
let func: libloading::Symbol<ScatterFn> = libh
.get(b"scatter_windows")
.map_err(|e| format!("missing symbol scatter_windows: {e}"))?;
let rc = func(
t.as_ptr(),
x.as_ptr(),
y.as_ptr(),
p.as_ptr(),
t.len() as c_longlong,
grid.as_ptr(),
n_windows as c_int,
delta_t_us as c_longlong,
nbins as c_int,
count_cutoff as c_uint,
row_map.as_ptr(),
col_map.as_ptr(),
col_map.len() as c_int,
row_map.len() as c_int,
out_h as c_int,
out_w as c_int,
out.as_mut_ptr(),
);
if rc != 0 {
return Err(format!("scatter_windows returned CUDA error code {rc}"));
}
}
Array4::from_shape_vec((n_windows, channels, out_h, out_w), out)
.map_err(|e| format!("failed to shape CUDA output: {e}"))
}