use std::ffi::c_void;
use std::fmt;
use cubecl::client::ComputeClient;
use cubecl::prelude::{ArrayArg, CubeCount, CubeDim, CubeElement, TensorBinding};
use cubecl_cuda::CudaRuntime as CubeclCudaRuntime;
use crate::{TensorRank, TypedTensor};
use super::{dispatch, CudaRuntime};
pub struct DeviceByteBuffer {
handle: Option<cubecl_runtime::server::Handle>,
ptr: *mut c_void,
}
impl fmt::Debug for DeviceByteBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DeviceByteBuffer")
.field("is_empty", &self.is_empty())
.field("ptr", &self.ptr)
.finish_non_exhaustive()
}
}
impl DeviceByteBuffer {
pub fn none() -> Self {
Self {
handle: None,
ptr: std::ptr::null_mut(),
}
}
pub fn ptr(&self) -> *mut c_void {
self.ptr
}
pub fn is_empty(&self) -> bool {
self.handle.is_none()
}
}
pub fn with_cubecl_client<R>(
rt: &CudaRuntime,
launch: impl FnOnce(&ComputeClient<CubeclCudaRuntime>) -> R,
) -> R {
launch(rt.client())
}
pub fn flush_cubecl_client(rt: &CudaRuntime, op: &'static str) -> crate::Result<()> {
rt.client()
.flush()
.map_err(|err| crate::Error::backend_failure(op, format!("CubeCL launch failed: {err:?}")))
}
pub fn raw_cuda_stream(rt: &CudaRuntime, op: &'static str) -> crate::Result<u64> {
rt.raw_cuda_stream()
.map_err(|err| crate::Error::backend_failure(op, err.to_string()))
}
pub fn cube_count_for_len(len: usize) -> crate::Result<CubeCount> {
dispatch::cube_count_for_len(len)
}
pub fn cube_dim_1d() -> CubeDim {
dispatch::cube_dim_1d()
}
pub fn alloc_output<T: CubeElement + Clone + Send + Sync + 'static>(
rt: &CudaRuntime,
shape: &[usize],
) -> crate::Result<TypedTensor<T>> {
dispatch::alloc_output(rt, shape)
}
pub fn ensure_typed_tensor_resident<T: 'static>(
tensor: &TypedTensor<T, impl TensorRank>,
op: &'static str,
) -> crate::Result<()> {
dispatch::cubecl_buffer(tensor, op)?;
Ok(())
}
pub fn typed_tensor_binding<T: CubeElement + Clone>(
tensor: &TypedTensor<T, impl TensorRank>,
op: &'static str,
) -> crate::Result<TensorBinding<CubeclCudaRuntime>> {
dispatch::typed_tensor_binding(tensor, op)
}
pub fn typed_tensor_array_arg<T: CubeElement + Clone>(
tensor: &TypedTensor<T, impl TensorRank>,
op: &'static str,
) -> crate::Result<ArrayArg<CubeclCudaRuntime>> {
dispatch::typed_tensor_array_arg(tensor, op)
}
pub fn typed_device_ptr<T: 'static>(
rt: &CudaRuntime,
tensor: &TypedTensor<T, impl TensorRank>,
op: &'static str,
) -> crate::Result<*mut c_void> {
dispatch::ensure_resident_on_runtime(rt, tensor, op)?;
let buffer = dispatch::cubecl_buffer(tensor, op)?;
let resource = rt
.client()
.get_resource(buffer.handle().clone())
.map_err(|err| {
crate::Error::backend_failure(op, format!("failed to obtain CubeCL resource: {err:?}"))
})?;
Ok(resource.resource().ptr as usize as *mut c_void)
}
pub fn upload_typed_tensor<T>(
rt: &CudaRuntime,
shape: Vec<usize>,
data: Vec<T>,
) -> crate::Result<TypedTensor<T>>
where
T: CubeElement + Clone + Send + Sync + 'static,
{
let len = data.len();
let handle = rt.client().create_from_slice(T::as_bytes(&data));
dispatch::typed_from_cubecl(
shape,
crate::CubeclBuffer::new(handle, len),
rt.device_ordinal(),
)
}
pub fn download_typed_tensor<T>(
rt: &CudaRuntime,
tensor: &TypedTensor<T, impl TensorRank>,
op: &'static str,
) -> crate::Result<TypedTensor<T>>
where
T: CubeElement + Clone + 'static,
{
dispatch::ensure_resident_on_runtime(rt, tensor, op)?;
let buffer = dispatch::cubecl_buffer(tensor, op)?;
if tensor.n_elements() == 0 {
return TypedTensor::from_vec_col_major(tensor.shape().to_vec(), Vec::new());
}
rt.synchronize()?;
let bytes = rt
.client()
.read_one(buffer.handle().clone())
.map_err(|err| {
crate::Error::backend_failure(op, format!("failed to download tensor: {err:?}"))
})?;
TypedTensor::from_vec_col_major(tensor.shape().to_vec(), T::from_bytes(&bytes).to_vec())
}
pub fn alloc_device_bytes(
rt: &CudaRuntime,
nbytes: usize,
op: &'static str,
) -> crate::Result<DeviceByteBuffer> {
if nbytes == 0 {
return Ok(DeviceByteBuffer::none());
}
let handle = rt.client().empty(nbytes);
device_bytes_from_handle(rt, handle, op)
}
pub fn upload_device_bytes(
rt: &CudaRuntime,
bytes: &[u8],
op: &'static str,
) -> crate::Result<DeviceByteBuffer> {
if bytes.is_empty() {
return Ok(DeviceByteBuffer::none());
}
let handle = rt.client().create_from_slice(bytes);
device_bytes_from_handle(rt, handle, op)
}
fn device_bytes_from_handle(
rt: &CudaRuntime,
handle: cubecl_runtime::server::Handle,
op: &'static str,
) -> crate::Result<DeviceByteBuffer> {
let resource = rt.client().get_resource(handle.clone()).map_err(|err| {
crate::Error::backend_failure(op, format!("failed to obtain CubeCL resource: {err:?}"))
})?;
Ok(DeviceByteBuffer {
handle: Some(handle),
ptr: resource.resource().ptr as usize as *mut c_void,
})
}