#![cfg(all(feature = "par", feature = "gpu-wgpu"))]
mod block_on;
mod buffer;
mod device;
mod error;
mod execute;
mod kernel;
pub use device::GpuWgpu;
pub use error::{GpuError, GpuResult};
use alloc::string::ToString;
use alloc::vec::Vec;
use core::any::TypeId;
use super::Backend;
use crate::par::{GpuMapChain, Nodus};
pub trait GpuScalar: bytemuck::Pod + Send + Sync + 'static {
fn wgsl_type() -> &'static str;
}
impl GpuScalar for f32 {
fn wgsl_type() -> &'static str {
"f32"
}
}
impl GpuScalar for i32 {
fn wgsl_type() -> &'static str {
"i32"
}
}
impl GpuScalar for u32 {
fn wgsl_type() -> &'static str {
"u32"
}
}
impl GpuWgpu {
fn execute_gpu_chain_bytes(
&self,
input_bytes: &[u8],
chain: &GpuMapChain,
type_name: &str,
element_size: usize,
) -> GpuResult<Vec<u8>> {
if input_bytes.is_empty() {
return Ok(Vec::new());
}
let wgsl_expr = chain.compose_wgsl();
let mut cache = self.kernel_cache().lock().map_err(|_| {
GpuError::DeviceCreationFailed("Failed to lock kernel cache".to_string())
})?;
execute::execute_map_gpu_bytes(
self.device(),
self.queue(),
&mut cache,
input_bytes,
&wgsl_expr,
type_name,
element_size,
)
}
}
impl Backend for GpuWgpu {
fn collect<T>(&self, node: &dyn Nodus<Item = T>) -> Vec<T>
where
T: Clone + Send + Sync + 'static,
{
if node.len() < self.min_len() {
return node.collect_scalar();
}
if let Some(chain) = node.try_gpu_map_chain() {
if let Some((source_bytes, type_name)) = node.try_as_gpu_source() {
if TypeId::of::<T>() != TypeId::of::<f32>()
&& TypeId::of::<T>() != TypeId::of::<i32>()
&& TypeId::of::<T>() != TypeId::of::<u32>()
{
return node.collect_scalar();
}
match self.execute_gpu_chain_bytes(
source_bytes,
&chain,
type_name,
core::mem::size_of::<T>(),
) {
Ok(output_bytes) => {
let element_size = core::mem::size_of::<T>();
let element_align = core::mem::align_of::<T>();
if output_bytes.len() % element_size != 0 {
return node.collect_scalar();
}
let len = output_bytes.len() / element_size;
let ptr = output_bytes.as_ptr();
if !(ptr as usize).is_multiple_of(element_align) {
return node.collect_scalar();
}
unsafe {
let slice = core::slice::from_raw_parts(ptr.cast::<T>(), len);
return slice.to_vec();
}
}
Err(_e) => {
}
}
}
}
use super::CpuScalar;
CpuScalar.collect(node)
}
fn reduce<T, F>(&self, node: &dyn Nodus<Item = T>, f: F) -> Option<T>
where
T: Clone + Send + Sync + 'static,
F: Fn(T, T) -> T + Send + Sync,
{
use super::CpuScalar;
CpuScalar.reduce(node, f)
}
fn reduce_gpu<T, F>(&self, node: &dyn Nodus<Item = T>, wgsl_op: &str, fallback: F) -> Option<T>
where
T: Clone + Send + Sync + 'static,
F: Fn(T, T) -> T + Send + Sync,
{
if node.len() < self.min_len() {
return super::reduce_scalar(node, &fallback);
}
if let Some(chain) = node.try_gpu_map_chain() {
if let Some((source_bytes, type_name)) = node.try_as_gpu_source() {
let element_size = core::mem::size_of::<T>();
let Ok(mut cache) = self.kernel_cache().lock() else {
return super::reduce_scalar(node, &fallback);
};
let map_result = execute::execute_map_to_buffer(
self.device(),
self.queue(),
&mut cache,
source_bytes,
&chain.compose_wgsl(),
type_name,
element_size,
);
if let Ok((buffer, _size_bytes)) = map_result {
if TypeId::of::<T>() != TypeId::of::<f32>()
&& TypeId::of::<T>() != TypeId::of::<i32>()
&& TypeId::of::<T>() != TypeId::of::<u32>()
{
return super::reduce_scalar(node, &fallback);
}
let result: GpuResult<Option<T>> = unsafe {
execute::execute_reduce_from_buffer::<T>(
self.device(),
self.queue(),
&mut cache,
buffer,
node.len(), wgsl_op,
type_name,
)
};
if let Ok(res) = result {
return res;
}
} else {
}
}
}
super::reduce_scalar(node, &fallback)
}
fn fold<T, B, F>(&self, node: &dyn Nodus<Item = T>, init: B, f: F) -> B
where
T: Clone + Send + Sync + 'static,
B: Clone + Send + Sync + 'static,
F: Fn(B, T) -> B + Send + Sync,
{
use super::CpuScalar;
CpuScalar.fold(node, init, f)
}
fn for_each<T, F>(&self, node: &dyn Nodus<Item = T>, f: F)
where
T: Clone + Send + Sync + 'static,
F: Fn(T) + Send + Sync,
{
use super::CpuScalar;
CpuScalar.for_each(node, f);
}
fn any<T, F>(&self, node: &dyn Nodus<Item = T>, predicate: F) -> bool
where
T: Clone + Send + Sync + 'static,
F: Fn(&T) -> bool + Send + Sync,
{
use super::CpuScalar;
CpuScalar.any(node, predicate)
}
fn all<T, F>(&self, node: &dyn Nodus<Item = T>, predicate: F) -> bool
where
T: Clone + Send + Sync + 'static,
F: Fn(&T) -> bool + Send + Sync,
{
use super::CpuScalar;
CpuScalar.all(node, predicate)
}
fn find<T, F>(&self, node: &dyn Nodus<Item = T>, predicate: F) -> Option<T>
where
T: Clone + Send + Sync + 'static,
F: Fn(&T) -> bool + Send + Sync,
{
use super::CpuScalar;
CpuScalar.find(node, predicate)
}
fn count<T>(&self, node: &dyn Nodus<Item = T>) -> usize
where
T: Clone + Send + Sync + 'static,
{
if node.is_indexed() {
return node.len();
}
use super::CpuScalar;
CpuScalar.count(node)
}
}