• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::api::icd::CLResult;
2 use crate::api::icd::ReferenceCountedAPIPointer;
3 use crate::core::context::Context;
4 use crate::core::event::Event;
5 use crate::core::memory::MemBase;
6 use crate::core::program::Program;
7 use crate::core::queue::Queue;
8 
9 use mesa_rust_util::conversion::*;
10 use rusticl_opencl_gen::*;
11 
12 use std::borrow::Borrow;
13 use std::ffi::c_void;
14 use std::ffi::CStr;
15 use std::iter::Product;
16 
17 macro_rules! cl_callback {
18     ($cb:ident($fn_alias:ident) {
19         $($p:ident : $ty:ty,)*
20     }) => {
21         pub type $fn_alias = unsafe extern "C" fn(
22             $($p: $ty,)*
23         );
24 
25         // INVARIANT:
26         // All safety requirements on `func` and `data` documented on `$cb::new` are invariants.
27         #[allow(dead_code)]
28         pub struct $cb {
29             func: $fn_alias,
30             data: *mut c_void,
31         }
32 
33         #[allow(dead_code)]
34         impl $cb {
35             /// Creates a new `$cb`. Returns `Err(CL_INVALID_VALUE)` if `func` is `None`.
36             ///
37             /// # SAFETY:
38             ///
39             /// If `func` is `None`, there are no safety requirements. Otherwise:
40             ///
41             /// - `func` must be a thread-safe fn.
42             /// - Passing `data` as the last parameter to `func` must not cause unsoundness.
43             /// - CreateContextCB: `func` must be soundly callable as documented on
44             ///   [`clCreateContext`] in the OpenCL specification.
45             /// - DeleteContextCB: `func` must be soundly callable as documented on
46             ///   [`clSetContextDestructorCallback`] in the OpenCL specification.
47             /// - EventCB: `func` must be soundly callable as documented on
48             ///   [`clSetEventCallback`] in the OpenCL specification.
49             /// - MemCB: `func` must be soundly callable as documented on
50             ///   [`clSetMemObjectDestructorCallback`] in the OpenCL specification.
51             /// - ProgramCB: `func` must be soundly callable as documented on
52             ///   [`clBuildProgram`] in the OpenCL specification.
53             /// - SVMFreeCb: `func` must be soundly callable as documented on
54             ///   [`clEnqueueSVMFree`] in the OpenCL specification.
55             ///
56             /// [`clCreateContext`]: https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clCreateContext
57             /// [`clSetContextDestructorCallback`]: https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clSetContextDestructorCallback
58             /// [`clSetEventCallback`]: https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clSetEventCallback
59             /// [`clSetMemObjectDestructorCallback`]: https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clSetMemObjectDestructorCallback
60             /// [`clBuildProgram`]: https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clBuildProgram
61             /// [`clEnqueueSVMFree`]: https://registry.khronos.org/OpenCL/specs/3.0-unified/html/OpenCL_API.html#clEnqueueSVMFree
62             pub unsafe fn new(func: Option<$fn_alias>, data: *mut c_void) -> CLResult<Self> {
63                 let Some(func) = func else {
64                     return Err(CL_INVALID_VALUE);
65                 };
66                 Ok(Self { func, data })
67             }
68 
69             /// Creates a new Option(`$cb`). Returns:
70             /// - `Ok(Some($cb)) if `func` is `Some(_)`.
71             /// - `Ok(None)` if `func` is `None` and `data` is `null`.
72             /// - `Err(CL_INVALID_VALUE)` if `func` is `None` and `data` is not `null`.
73             ///
74             /// # SAFETY:
75             ///
76             /// The safety requirements are identical to those of [`new`].
77             pub unsafe fn try_new(func: Option<$fn_alias>, data: *mut c_void) -> CLResult<Option<Self>> {
78                 let Some(func) = func else {
79                     return if data.is_null() {
80                         Ok(None)
81                     } else {
82                         Err(CL_INVALID_VALUE)
83                     };
84                 };
85                 Ok(Some(Self { func, data }))
86             }
87         }
88 
89         unsafe impl Send for $cb {}
90         unsafe impl Sync for $cb {}
91     }
92 }
93 
94 cl_callback!(
95     CreateContextCB(FuncCreateContextCB) {
96         errinfo: *const ::std::os::raw::c_char,
97         private_info: *const c_void,
98         cb: usize,
99         user_data: *mut c_void,
100     }
101 );
102 
103 impl CreateContextCB {
_call(self, err_msg: &CStr, private_info: &[u8])104     pub fn _call(self, err_msg: &CStr, private_info: &[u8]) {
105         let err_msg_ptr = err_msg.as_ptr();
106         let private_info_ptr = private_info.as_ptr().cast::<c_void>();
107         // SAFETY: The first parameter must be a valid pointer to a NUL-terminated C string. We
108         // know this is satisfied since that is `CStr`'s type invariant.
109         // The second parameter must be a valid pointer to binary data with the length given in the
110         // thrid parameter. We know both of these are correct since we just got them from a byte slice.
111         // All other requirements are covered by this callback's type invariants.
112         unsafe { (self.func)(err_msg_ptr, private_info_ptr, private_info.len(), self.data) };
113     }
114 }
115 
116 cl_callback!(
117     DeleteContextCB(FuncDeleteContextCB) {
118         context: cl_context,
119         user_data: *mut c_void,
120     }
121 );
122 
123 impl DeleteContextCB {
call(self, ctx: &Context)124     pub fn call(self, ctx: &Context) {
125         let cl = cl_context::from_ptr(ctx);
126         // SAFETY: `cl` must have pointed to an OpenCL context, which is where we just got it from.
127         // All other requirements are covered by this callback's type invariants.
128         unsafe { (self.func)(cl, self.data) };
129     }
130 }
131 
132 cl_callback!(
133     EventCB(FuncEventCB) {
134         event: cl_event,
135         event_command_status: cl_int,
136         user_data: *mut c_void,
137     }
138 );
139 
140 impl EventCB {
call(self, event: &Event, status: cl_int)141     pub fn call(self, event: &Event, status: cl_int) {
142         let cl = cl_event::from_ptr(event);
143         // SAFETY: `cl` must be a valid pointer to an OpenCL event, which is where we just got it from.
144         // All other requirements are covered by this callback's type invariants.
145         unsafe { (self.func)(cl, status, self.data) };
146     }
147 }
148 
149 cl_callback!(
150     MemCB(FuncMemCB) {
151         memobj: cl_mem,
152         user_data: *mut c_void,
153     }
154 );
155 
156 impl MemCB {
call(self, mem: &MemBase)157     pub fn call(self, mem: &MemBase) {
158         let cl = cl_mem::from_ptr(mem);
159         // SAFETY: `cl` must have pointed to an OpenCL context, which is where we just got it from.
160         // All other requirements are covered by this callback's type invariants.
161         unsafe { (self.func)(cl, self.data) };
162     }
163 }
164 
165 cl_callback!(
166     ProgramCB(FuncProgramCB) {
167         program: cl_program,
168         user_data: *mut c_void,
169     }
170 );
171 
172 impl ProgramCB {
call(self, program: &Program)173     pub fn call(self, program: &Program) {
174         let cl = cl_program::from_ptr(program);
175         // SAFETY: `cl` must have pointed to an OpenCL program, which is where we just got it from.
176         // All other requirements are covered by this callback's type invariants.
177         unsafe { (self.func)(cl, self.data) };
178     }
179 }
180 
181 cl_callback!(
182     SVMFreeCb(FuncSVMFreeCb) {
183         queue: cl_command_queue,
184         num_svm_pointers: cl_uint,
185         svm_pointers: *mut *mut c_void,
186         user_data: *mut c_void,
187     }
188 );
189 
190 impl SVMFreeCb {
call(self, queue: &Queue, svm_pointers: &mut [usize])191     pub fn call(self, queue: &Queue, svm_pointers: &mut [usize]) {
192         let cl = cl_command_queue::from_ptr(queue);
193         // SAFETY: `cl` must be a valid pointer to an OpenCL queue, which is where we just got it from.
194         // All other requirements are covered by this callback's type invariants.
195         unsafe {
196             (self.func)(
197                 cl,
198                 svm_pointers.len() as u32,
199                 svm_pointers.as_mut_ptr().cast(),
200                 self.data,
201             )
202         };
203     }
204 }
205 
206 // a lot of APIs use 3 component vectors passed as C arrays
207 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
208 pub struct CLVec<T> {
209     vals: [T; 3],
210 }
211 
212 impl<T: Copy> CLVec<T> {
new(vals: [T; 3]) -> Self213     pub fn new(vals: [T; 3]) -> Self {
214         Self { vals: vals }
215     }
216 
217     /// # Safety
218     ///
219     /// This function is intended for use around OpenCL vectors of size 3.
220     /// Most commonly for `origin` and `region` API arguments.
221     ///
222     /// Using it for anything else is undefined.
from_raw(v: *const T) -> Self223     pub unsafe fn from_raw(v: *const T) -> Self {
224         Self {
225             vals: unsafe { *v.cast() },
226         }
227     }
228 
pixels<'a>(&'a self) -> T where T: Product<&'a T>,229     pub fn pixels<'a>(&'a self) -> T
230     where
231         T: Product<&'a T>,
232     {
233         self.vals.iter().product()
234     }
235 }
236 
237 impl CLVec<usize> {
238     /// returns the offset of point in linear memory.
calc_offset<T: Borrow<Self>>(point: T, pitch: [usize; 3]) -> usize239     pub fn calc_offset<T: Borrow<Self>>(point: T, pitch: [usize; 3]) -> usize {
240         *point.borrow() * pitch
241     }
242 
243     /// returns the scalar size of the described region in linear memory.
calc_size<T: Borrow<Self>>(region: T, pitch: [usize; 3]) -> usize244     pub fn calc_size<T: Borrow<Self>>(region: T, pitch: [usize; 3]) -> usize {
245         (*region.borrow() - [0, 1, 1]) * pitch
246     }
247 
calc_offset_size<T1: Borrow<Self>, T2: Borrow<Self>>( base: T1, region: T2, pitch: [usize; 3], ) -> (usize, usize)248     pub fn calc_offset_size<T1: Borrow<Self>, T2: Borrow<Self>>(
249         base: T1,
250         region: T2,
251         pitch: [usize; 3],
252     ) -> (usize, usize) {
253         (
254             Self::calc_offset(base, pitch),
255             Self::calc_size(region, pitch),
256         )
257     }
258 }
259 
260 impl<T: Default + Copy> Default for CLVec<T> {
default() -> Self261     fn default() -> Self {
262         Self {
263             vals: [T::default(); 3],
264         }
265     }
266 }
267 
268 // provides a ton of functions
269 impl<T> std::ops::Deref for CLVec<T> {
270     type Target = [T; 3];
271 
deref(&self) -> &Self::Target272     fn deref(&self) -> &Self::Target {
273         &self.vals
274     }
275 }
276 
277 impl<T> std::ops::DerefMut for CLVec<T> {
deref_mut(&mut self) -> &mut Self::Target278     fn deref_mut(&mut self) -> &mut Self::Target {
279         &mut self.vals
280     }
281 }
282 
283 impl<T: Copy + std::ops::Add<Output = T>> std::ops::Add for CLVec<T> {
284     type Output = Self;
285 
add(self, other: Self) -> Self286     fn add(self, other: Self) -> Self {
287         self + other.vals
288     }
289 }
290 
291 impl<T: Copy + std::ops::Add<Output = T>> std::ops::Add<[T; 3]> for CLVec<T> {
292     type Output = Self;
293 
add(self, other: [T; 3]) -> Self294     fn add(self, other: [T; 3]) -> Self {
295         Self {
296             vals: [self[0] + other[0], self[1] + other[1], self[2] + other[2]],
297         }
298     }
299 }
300 
301 impl<T: Copy + std::ops::Sub<Output = T>> std::ops::Sub<[T; 3]> for CLVec<T> {
302     type Output = Self;
303 
sub(self, other: [T; 3]) -> Self304     fn sub(self, other: [T; 3]) -> Self {
305         Self {
306             vals: [self[0] - other[0], self[1] - other[1], self[2] - other[2]],
307         }
308     }
309 }
310 
311 impl<T> std::ops::Mul for CLVec<T>
312 where
313     T: Copy + std::ops::Mul<Output = T> + std::ops::Add<Output = T>,
314 {
315     type Output = T;
316 
mul(self, other: Self) -> T317     fn mul(self, other: Self) -> T {
318         self * other.vals
319     }
320 }
321 
322 impl<T> std::ops::Mul<[T; 3]> for CLVec<T>
323 where
324     T: Copy + std::ops::Mul<Output = T> + std::ops::Add<Output = T>,
325 {
326     type Output = T;
327 
mul(self, other: [T; 3]) -> T328     fn mul(self, other: [T; 3]) -> T {
329         self[0] * other[0] + self[1] * other[1] + self[2] * other[2]
330     }
331 }
332 
333 impl<S, T> TryInto<[T; 3]> for CLVec<S>
334 where
335     S: Copy,
336     T: TryFrom<S>,
337     [T; 3]: TryFrom<Vec<T>>,
338 {
339     type Error = cl_int;
340 
try_into(self) -> Result<[T; 3], cl_int>341     fn try_into(self) -> Result<[T; 3], cl_int> {
342         let vec: Result<Vec<T>, _> = self
343             .vals
344             .iter()
345             .map(|v| T::try_from_with_err(*v, CL_OUT_OF_HOST_MEMORY))
346             .collect();
347         vec?.try_into_with_err(CL_OUT_OF_HOST_MEMORY)
348     }
349 }
350 
351 impl<T> From<[T; 3]> for CLVec<T>
352 where
353     T: Copy,
354 {
from(arr: [T; 3]) -> Self355     fn from(arr: [T; 3]) -> Self {
356         Self::new(arr)
357     }
358 }
359 
360 #[allow(non_snake_case)]
361 pub mod IdpAccelProps {
362     use rusticl_opencl_gen::cl_bool;
363     use rusticl_opencl_gen::cl_device_integer_dot_product_acceleration_properties_khr;
new( signed_accelerated: cl_bool, unsigned_accelerated: cl_bool, mixed_signedness_accelerated: cl_bool, accumulating_saturating_signed_accelerated: cl_bool, accumulating_saturating_unsigned_accelerated: cl_bool, accumulating_saturating_mixed_signedness_accelerated: cl_bool, ) -> cl_device_integer_dot_product_acceleration_properties_khr364     pub fn new(
365         signed_accelerated: cl_bool,
366         unsigned_accelerated: cl_bool,
367         mixed_signedness_accelerated: cl_bool,
368         accumulating_saturating_signed_accelerated: cl_bool,
369         accumulating_saturating_unsigned_accelerated: cl_bool,
370         accumulating_saturating_mixed_signedness_accelerated: cl_bool,
371     ) -> cl_device_integer_dot_product_acceleration_properties_khr {
372         cl_device_integer_dot_product_acceleration_properties_khr {
373             signed_accelerated,
374             unsigned_accelerated,
375             mixed_signedness_accelerated,
376             accumulating_saturating_signed_accelerated,
377             accumulating_saturating_unsigned_accelerated,
378             accumulating_saturating_mixed_signedness_accelerated,
379         }
380     }
381 }
382