1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
//! Functions for spawning threads and launching compiled `DeviceFnMut`s use crate::device::*; use crate::error::*; use crate::pool::*; use std::sync::Arc; /// Constructs a [`Spawner`](struct.Spawner.html) with the given number of threads spawned /// /// Each `spawn(n)` will spawn a new dimension of threads of size `n`. In other words, for each thread already spawned, `n` threads are spawned. /// If more than 3 dimensions are added, all dimensions are collapsed into the "x" dimension where the size /// of the "x" dimension is now the product of all sizes of dimensions so far. Until 3 dimensions of threads have been spawned, /// threads will be spawned on dimensions "x", "y", and "z" in that order. /// /// This can be used in conjunction with `Spawner` as follows. `spawn` returns a `Spawner` which lets you `.spawn` more dimensions of threads. /// ``` /// # use {emu_core::prelude::*, emu_glsl::*, zerocopy::*}; /// # fn main() -> Result<(), Box<dyn std::error::Error>> { /// // don't forget - this should always be the first thing you call /// // don't assume there is a device available without calling this /// // if there isn't one, you will recieve a runtime panic /// futures::executor::block_on(assert_device_pool_initialized()); /// /// // move data to a device /// let data = vec![1.0; 1 << 20]; /// let mut data_on_gpu: DeviceBox<[f32]> = data.as_device_boxed_mut()?; /// /// // compile a kernel /// let kernel: GlslKernel = GlslKernel::new() /// .param_mut::<[f32], _>("float[] data") /// .param::<f32, _>("float scalar") /// .with_kernel_code(r#" /// uint index = (1 << 10) * gl_GlobalInvocationID.x + gl_GlobalInvocationID.y; /// data[index] = data[index] * scalar; /// "#); /// let c = compile::<GlslKernel, GlslKernelCompile, _, GlobalCache>(kernel)?.finish()?; /// /// // run the compiled kernel /// unsafe { /// spawn(1 << 10) /// .spawn(1 << 10) /// .launch(call!(c, &mut data_on_gpu, &DeviceBox::new(10.0f32)?))?; /// } /// /// // download data from the GPU and check the result /// assert_eq!(futures::executor::block_on(data_on_gpu.get())?, vec![10.0; 1 << 20].into_boxed_slice()); /// # Ok(()) /// # } /// ``` pub fn spawn(num_threads: u32) -> Spawner { Spawner { work_space_dim: vec![num_threads], } } /// A "builder" for a space of threads that are to be spawned /// /// See [`spawn`](fn.spawn.html) for more details. pub struct Spawner { work_space_dim: Vec<u32>, } impl Spawner { /// Adds a new dimension to the space of threads with size determined by the given number of threads pub fn spawn(mut self, num_threads: u32) -> Self { self.work_space_dim.push(num_threads); self } fn get_work_space_dim(&self) -> Result<(u32, u32, u32), LaunchError> { match self.work_space_dim.len() { 0 => Ok((0, 0, 0)), 1 => Ok((self.work_space_dim[0], 1, 1)), 2 => Ok((self.work_space_dim[0], self.work_space_dim[1], 1)), 3 => Ok(( self.work_space_dim[0], self.work_space_dim[1], self.work_space_dim[2], )), _ => Ok((self.work_space_dim.iter().product(), 1, 1)), } } /// Launches given `DeviceFnMut` with given arguments on the space of threads built so far /// /// You can provide the arguments using [`ArgsBuilder`](../device/struct.ArgsBuilder.html) or using the `call` macro. pub unsafe fn launch<'a>( &self, device_fn_mut_with_args: (Arc<DeviceFnMut>, DeviceFnMutArgs<'a>), ) -> Result<(), LaunchError> { take() .map_err(|_| LaunchError::NoDevice)? .lock() .unwrap() .call( &device_fn_mut_with_args.0, self.get_work_space_dim()?, device_fn_mut_with_args.1, ) } } /// A macro which evaluates to something that can be passed into [`launch`](spawn/struct.Spawner.html#method.launch) /// /// For example usage, see [`spawn`](spawn/fn.spawn.html) #[macro_export] macro_rules! call { ($fn_mut:expr $( ,$fn_mut_arg:expr )*) => ( { ( $fn_mut, ArgsBuilder::new()$( .arg($fn_mut_arg) )*.build() ) } ) }