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}