rust_gpu_tools/
program.rs

1#[cfg(feature = "cuda")]
2use crate::cuda;
3use crate::error::GPUError;
4#[cfg(feature = "opencl")]
5use crate::opencl;
6
7/// Abstraction for running programs on CUDA or OpenCL.
8pub enum Program {
9    /// CUDA program.
10    #[cfg(feature = "cuda")]
11    Cuda(cuda::Program),
12    /// OpenCL program.
13    #[cfg(feature = "opencl")]
14    Opencl(opencl::Program),
15}
16
17impl Program {
18    /// Run some code in the context of the program.
19    ///
20    /// There is an implementation for OpenCL and for CUDA. Both use different Rust types, but
21    /// [`opencl::Program`] and [`cuda::Program`] implement the same API. This means that same
22    /// code code can be used to run on either of them. The only difference is the type of the
23    /// `Program`.
24    ///
25    /// You need to pass in two closures, one for OpenCL, one for CUDA, both get their
26    /// corresponding program type as parameter. For convenience there is the [`program_closures`]
27    /// macro defined, which can help reducing code duplication by creating two closures out of
28    /// a single one.
29    ///
30    /// CUDA and OpenCL support can be enabled/disabled by the `opencl` and `cuda` features. If
31    /// one of them is disabled, you still need to pass in two closures. This way the API stays,
32    /// the same, but you can disable it things at compile-time.
33    ///
34    /// The second parameter is a single arbitrary argument, which will be passed on into the
35    /// closure. This is useful when you e.g. need to pass in a mutable reference. Such a reference
36    /// cannot be shared between closures, hence we pass it on, so that the compiler knows that it
37    /// is used at most once.
38    #[cfg(all(feature = "cuda", feature = "opencl"))]
39    pub fn run<F1, F2, R, E, A>(&self, fun: (F1, F2), arg: A) -> Result<R, E>
40    where
41        E: From<GPUError>,
42        F1: FnOnce(&cuda::Program, A) -> Result<R, E>,
43        F2: FnOnce(&opencl::Program, A) -> Result<R, E>,
44    {
45        match self {
46            Self::Cuda(program) => program.run(fun.0, arg),
47            Self::Opencl(program) => program.run(fun.1, arg),
48        }
49    }
50
51    /// Run some code in the context of the program.
52    ///
53    /// There is an implementation for OpenCL and for CUDA. Both use different Rust types, but
54    /// [`opencl::Program`] and [`cuda::Program`] implement the same API. This means that same
55    /// code code can be used to run on either of them. The only difference is the type of the
56    /// `Program`.
57    ///
58    /// You need to pass in two closures, one for OpenCL, one for CUDA, both get their
59    /// corresponding program type as parameter. For convenience there is the [`program_closures`]
60    /// macro defined, which can help reducing code duplication by creating two closures out of
61    /// a single one.
62    ///
63    /// CUDA and OpenCL support can be enabled/disabled by the `opencl` and `cuda` features. If
64    /// one of them is disabled, you still need to pass in two closures. This way the API stays,
65    /// the same, but you can disable it things at compile-time.
66    ///
67    /// The second parameter is a single arbitrary argument, which will be passed on into the
68    /// closure. This is useful when you e.g. need to pass in a mutable reference. Such a reference
69    /// cannot be shared between closures, hence we pass it on, so that the compiler knows that it
70    /// is used at most once.
71    #[cfg(all(feature = "cuda", not(feature = "opencl")))]
72    pub fn run<F1, F2, R, E, A>(&self, fun: (F1, F2), arg: A) -> Result<R, E>
73    where
74        E: From<GPUError>,
75        F1: FnOnce(&cuda::Program, A) -> Result<R, E>,
76    {
77        match self {
78            Self::Cuda(program) => program.run(fun.0, arg),
79        }
80    }
81
82    /// Run some code in the context of the program.
83    ///
84    /// There is an implementation for OpenCL and for CUDA. Both use different Rust types, but
85    /// [`opencl::Program`] and [`cuda::Program`] implement the same API. This means that same
86    /// code code can be used to run on either of them. The only difference is the type of the
87    /// `Program`.
88    ///
89    /// You need to pass in two closures, one for OpenCL, one for CUDA, both get their
90    /// corresponding program type as parameter. For convenience there is the [`define_closures`]
91    /// macro defined, which can help reducing code duplication by creating two closures out of
92    /// a single one.
93    ///
94    /// CUDA and OpenCL support can be enabled/disabled by the `opencl` and `cuda` features. If
95    /// one of them is disabled, you still need to pass in two closures. This way the API stays,
96    /// the same, but you can disable it things at compile-time.
97    ///
98    /// The second parameter is a single arbitrary argument, which will be passed on into the
99    /// closure. This is useful when you e.g. need to pass in a mutable reference. Such a reference
100    /// cannot be shared between closures, hence we pass it on, so that the compiler knows that it
101    /// is used at most once.
102    #[cfg(all(not(feature = "cuda"), feature = "opencl"))]
103    pub fn run<F1, F2, R, E, A>(&self, fun: (F1, F2), arg: A) -> Result<R, E>
104    where
105        E: From<GPUError>,
106        F2: FnOnce(&opencl::Program, A) -> Result<R, E>,
107    {
108        match self {
109            Self::Opencl(program) => program.run(fun.1, arg),
110        }
111    }
112
113    /// Returns the name of the GPU, e.g. "GeForce RTX 3090".
114    pub fn device_name(&self) -> &str {
115        match self {
116            #[cfg(feature = "cuda")]
117            Self::Cuda(program) => program.device_name(),
118            #[cfg(feature = "opencl")]
119            Self::Opencl(program) => program.device_name(),
120        }
121    }
122}
123
124/// Creates two closures, one for CUDA, one for OpenCL for the given one.
125///
126/// This macro is used to be able to interact with rust-gpu-tools with unified code for both,
127/// CUDA and OpenCL, without the need to repeat the code. The input parameter is a `program` and
128/// it will be mapped to &[`cuda::Program`] and &[`opencl::Program`].
129///
130/// The second parameter is a single arbitrary argument, which will be passed on into the closure.
131/// This is useful when you e.g. need to pass in a mutable reference. Such a reference cannot be
132/// shared between closures, hence we pass it on, so that the compiler knows that it is used at
133/// most once.
134///
135/// Depending on whether the `cuda` and/or `opencl` feature is enabled, it will do the correct
136/// thing and not specify one of them if it is appropriate.
137///
138/// ### Example
139///
140/// ```
141/// use rust_gpu_tools::{cuda, opencl, program_closures};
142///
143/// let closures = program_closures!(|program, arg: u8| -> bool {
144///     true
145/// });
146///
147/// // Generates
148/// let closures = (
149///     |program: &cuda::Program, arg: u8| { true },
150///     |program: &opencl::Program, arg: u8| { true },
151/// );
152///
153/// // If e.g. the `cuda` feature is disabled, it would generate
154/// let closures_without_cuda = (
155///     (),
156///     |program: &opencl::Program, arg: u8| { true },
157/// );
158/// ```
159#[cfg(all(feature = "cuda", feature = "opencl"))]
160#[macro_export]
161macro_rules! program_closures {
162    // Additional argument without a type
163    (|$program:ident, $arg:ident| -> $ret:ty $body:block) => {
164        (
165            |$program: &$crate::cuda::Program, $arg| -> $ret { $body },
166            |$program: &$crate::opencl::Program, $arg| -> $ret { $body },
167        )
168    };
169    // Additional argument with a type
170    (|$program:ident, $arg:ident: $arg_type:ty| -> $ret:ty $body:block) => {
171        (
172            |$program: &$crate::cuda::Program, $arg: $arg_type| -> $ret { $body },
173            |$program: &$crate::opencl::Program, $arg: $arg_type| -> $ret { $body },
174        )
175    };
176}
177
178/// Creates two closures, one for CUDA, one for OpenCL for the given one.
179///
180/// This macro is used to be able to interact with rust-gpu-tools with unified code for both,
181/// CUDA and OpenCL, without the need to repeat the code. The input parameter is a `program` and
182/// it will be mapped to [`&cuda::Program`] and [`&opencl::Program`].
183///
184/// The second parameter is a single arbitrary argument, which will be passed on into the closure.
185/// This is useful when you e.g. need to pass in a mutable reference. Such a reference cannot be
186/// shared between closures, hence we pass it on, so that the compiler knows that it is used at
187/// most once.
188///
189/// Depending on whether the `cuda` and/or `opencl` feature is enabled, it will do the correct
190/// thing and not specify one of them if it is appropriate.
191///
192/// ### Example
193///
194/// ```
195/// use rust_gpu_tools::{cuda, opencl, program_closures};
196///
197/// let closures = program_closures!(|program, arg: u8| -> bool {
198///     true
199/// });
200///
201/// // Generates
202/// let closures = (
203///     |program: &cuda::Program, arg: u8| { true },
204///     |program: &opencl::Program, arg: u8| { true },
205/// );
206///
207/// // If e.g. the `cuda` feature is disabled, it would generate
208/// let closures_without_cuda = (
209///     (),
210///     |program: &opencl::Program, arg: u8| { true },
211/// );
212/// ```
213#[macro_export]
214#[cfg(all(feature = "cuda", not(feature = "opencl")))]
215macro_rules! program_closures {
216    // Additional argument without a type
217    (|$program:ident, $arg:ident| -> $ret:ty $body:block) => {
218        (
219            |$program: &$crate::cuda::Program, $arg| -> $ret { $body },
220            (),
221        )
222    };
223    // Additional argument with a type
224    (|$program:ident, $arg:ident: $arg_type:ty| -> $ret:ty $body:block) => {
225        (
226            |$program: &$crate::cuda::Program, $arg: $arg_type| -> $ret { $body },
227            (),
228        )
229    };
230}
231
232/// Creates two closures, one for CUDA, one for OpenCL for the given one.
233///
234/// This macro is used to be able to interact with rust-gpu-tools with unified code for both,
235/// CUDA and OpenCL, without the need to repeat the code. The input parameter is a `program` and
236/// it will be mapped to [`&cuda::Program`] and [`&opencl::Program`].
237///
238/// The second parameter is a single arbitrary argument, which will be passed on into the closure.
239/// This is useful when you e.g. need to pass in a mutable reference. Such a reference cannot be
240/// shared between closures, hence we pass it on, so that the compiler knows that it is used at
241/// most once.
242///
243/// Depending on whether the `cuda` and/or `opencl` feature is enabled, it will do the correct
244/// thing and not specify one of them if it is appropriate.
245///
246/// ### Example
247///
248/// ```
249/// use rust_gpu_tools::{cuda, opencl, program_closures};
250///
251/// let closures = program_closures!(|program, arg: u8| -> bool {
252///     true
253/// });
254///
255/// // Generates
256/// let closures = (
257///     |program: &cuda::Program, arg: u8| { true },
258///     |program: &opencl::Program, arg: u8| { true },
259/// );
260///
261/// // If e.g. the `cuda` feature is disabled, it would generate
262/// let closures_without_cuda = (
263///     (),
264///     |program: &opencl::Program, arg: u8| { true },
265/// );
266/// ```
267#[macro_export]
268#[cfg(all(not(feature = "cuda"), feature = "opencl"))]
269macro_rules! program_closures {
270    // Additional argument without a type
271    (|$program:ident, $arg:ident| -> $ret:ty $body:block) => {
272        ((), |$program: &$crate::opencl::Program, $arg| -> $ret {
273            $body
274        })
275    };
276    // Additional argument with a type
277    (|$program:ident, $arg:ident: $arg_type:ty| -> $ret:ty $body:block) => {
278        (
279            (),
280            |$program: &$crate::opencl::Program, $arg: $arg_type| -> $ret { $body },
281        )
282    };
283}