xref: /aosp_15_r20/external/mesa3d/src/gallium/frontends/rusticl/api/util.rs (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 use crate::api::icd::{ArcedCLObject, CLResult};
2 use crate::api::types::*;
3 use crate::core::event::*;
4 use crate::core::queue::*;
5 
6 use mesa_rust_util::properties::Properties;
7 use mesa_rust_util::ptr::CheckedPtr;
8 use rusticl_opencl_gen::*;
9 
10 use std::cmp;
11 use std::convert::TryInto;
12 use std::ffi::CStr;
13 use std::ffi::CString;
14 use std::mem::{size_of, MaybeUninit};
15 use std::ops::BitAnd;
16 use std::slice;
17 use std::sync::Arc;
18 
19 pub trait CLInfo<I> {
query(&self, q: I, vals: &[u8]) -> CLResult<Vec<MaybeUninit<u8>>>20     fn query(&self, q: I, vals: &[u8]) -> CLResult<Vec<MaybeUninit<u8>>>;
21 
get_info( &self, param_name: I, param_value_size: usize, param_value: *mut ::std::os::raw::c_void, param_value_size_ret: *mut usize, ) -> CLResult<()>22     fn get_info(
23         &self,
24         param_name: I,
25         param_value_size: usize,
26         param_value: *mut ::std::os::raw::c_void,
27         param_value_size_ret: *mut usize,
28     ) -> CLResult<()> {
29         let arr = if !param_value.is_null() && param_value_size != 0 {
30             unsafe { slice::from_raw_parts(param_value.cast(), param_value_size) }
31         } else {
32             &[]
33         };
34 
35         let d = self.query(param_name, arr)?;
36         let size: usize = d.len();
37 
38         // CL_INVALID_VALUE [...] if size in bytes specified by param_value_size is < size of return
39         // type as specified in the Context Attributes table and param_value is not a NULL value.
40         if param_value_size < size && !param_value.is_null() {
41             return Err(CL_INVALID_VALUE);
42         }
43 
44         // param_value_size_ret returns the actual size in bytes of data being queried by param_name.
45         // If param_value_size_ret is NULL, it is ignored.
46         param_value_size_ret.write_checked(size);
47 
48         // param_value is a pointer to memory where the appropriate result being queried is returned.
49         // If param_value is NULL, it is ignored.
50         unsafe {
51             param_value.copy_checked(d.as_ptr().cast(), size);
52         }
53 
54         Ok(())
55     }
56 }
57 
58 pub trait CLInfoObj<I, O> {
query(&self, o: O, q: I) -> CLResult<Vec<MaybeUninit<u8>>>59     fn query(&self, o: O, q: I) -> CLResult<Vec<MaybeUninit<u8>>>;
60 
get_info_obj( &self, obj: O, param_name: I, param_value_size: usize, param_value: *mut ::std::os::raw::c_void, param_value_size_ret: *mut usize, ) -> CLResult<()>61     fn get_info_obj(
62         &self,
63         obj: O,
64         param_name: I,
65         param_value_size: usize,
66         param_value: *mut ::std::os::raw::c_void,
67         param_value_size_ret: *mut usize,
68     ) -> CLResult<()> {
69         let d = self.query(obj, param_name)?;
70         let size: usize = d.len();
71 
72         // CL_INVALID_VALUE [...] if size in bytes specified by param_value_size is < size of return
73         // type as specified in the Context Attributes table and param_value is not a NULL value.
74         if param_value_size < size && !param_value.is_null() {
75             return Err(CL_INVALID_VALUE);
76         }
77 
78         // param_value_size_ret returns the actual size in bytes of data being queried by param_name.
79         // If param_value_size_ret is NULL, it is ignored.
80         param_value_size_ret.write_checked(size);
81 
82         // param_value is a pointer to memory where the appropriate result being queried is returned.
83         // If param_value is NULL, it is ignored.
84         unsafe {
85             param_value.copy_checked(d.as_ptr().cast(), size);
86         }
87 
88         Ok(())
89     }
90 }
91 
92 pub trait CLProp {
cl_vec(&self) -> Vec<MaybeUninit<u8>>93     fn cl_vec(&self) -> Vec<MaybeUninit<u8>>;
94 }
95 
96 macro_rules! cl_prop_for_type {
97     ($ty: ty) => {
98         impl CLProp for $ty {
99             fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
100                 unsafe { slice::from_raw_parts(std::ptr::from_ref(self).cast(), size_of::<Self>()) }
101                     .to_vec()
102             }
103         }
104     };
105 }
106 
107 cl_prop_for_type!(cl_char);
108 cl_prop_for_type!(cl_uchar);
109 cl_prop_for_type!(cl_ushort);
110 cl_prop_for_type!(cl_int);
111 cl_prop_for_type!(cl_uint);
112 cl_prop_for_type!(cl_ulong);
113 cl_prop_for_type!(isize);
114 cl_prop_for_type!(usize);
115 
116 cl_prop_for_type!(cl_device_integer_dot_product_acceleration_properties_khr);
117 cl_prop_for_type!(cl_device_pci_bus_info_khr);
118 cl_prop_for_type!(cl_image_format);
119 cl_prop_for_type!(cl_name_version);
120 
121 impl CLProp for bool {
cl_vec(&self) -> Vec<MaybeUninit<u8>>122     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
123         cl_prop::<cl_bool>(if *self { CL_TRUE } else { CL_FALSE })
124     }
125 }
126 
127 impl CLProp for &str {
cl_vec(&self) -> Vec<MaybeUninit<u8>>128     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
129         to_maybeuninit_vec(
130             CString::new(*self)
131                 .unwrap_or_default()
132                 .into_bytes_with_nul(),
133         )
134     }
135 }
136 
137 impl CLProp for &CStr {
cl_vec(&self) -> Vec<MaybeUninit<u8>>138     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
139         to_maybeuninit_vec(self.to_bytes_with_nul().to_vec())
140     }
141 }
142 
143 impl<T> CLProp for Vec<T>
144 where
145     T: CLProp,
146 {
cl_vec(&self) -> Vec<MaybeUninit<u8>>147     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
148         let mut res: Vec<MaybeUninit<u8>> = Vec::new();
149         for i in self {
150             res.append(&mut i.cl_vec())
151         }
152         res
153     }
154 }
155 
156 impl<T> CLProp for &T
157 where
158     T: CLProp,
159 {
cl_vec(&self) -> Vec<MaybeUninit<u8>>160     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
161         T::cl_vec(self)
162     }
163 }
164 
165 impl<T> CLProp for [T]
166 where
167     T: CLProp,
168 {
cl_vec(&self) -> Vec<MaybeUninit<u8>>169     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
170         let mut res: Vec<MaybeUninit<u8>> = Vec::new();
171         for i in self {
172             res.append(&mut i.cl_vec())
173         }
174         res
175     }
176 }
177 
178 impl<T, const I: usize> CLProp for [T; I]
179 where
180     T: CLProp,
181 {
cl_vec(&self) -> Vec<MaybeUninit<u8>>182     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
183         let mut res: Vec<MaybeUninit<u8>> = Vec::new();
184         for i in self {
185             res.append(&mut i.cl_vec())
186         }
187         res
188     }
189 }
190 
191 impl<T> CLProp for *const T {
cl_vec(&self) -> Vec<MaybeUninit<u8>>192     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
193         (*self as usize).cl_vec()
194     }
195 }
196 
197 impl<T> CLProp for *mut T {
cl_vec(&self) -> Vec<MaybeUninit<u8>>198     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
199         (*self as usize).cl_vec()
200     }
201 }
202 
203 impl<T> CLProp for Properties<T>
204 where
205     T: CLProp + Default,
206 {
cl_vec(&self) -> Vec<MaybeUninit<u8>>207     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
208         let mut res: Vec<MaybeUninit<u8>> = Vec::new();
209         for (k, v) in &self.props {
210             res.append(&mut k.cl_vec());
211             res.append(&mut v.cl_vec());
212         }
213         res.append(&mut T::default().cl_vec());
214         res
215     }
216 }
217 
218 impl<T> CLProp for Option<T>
219 where
220     T: CLProp,
221 {
cl_vec(&self) -> Vec<MaybeUninit<u8>>222     fn cl_vec(&self) -> Vec<MaybeUninit<u8>> {
223         self.as_ref().map_or(Vec::new(), |v| v.cl_vec())
224     }
225 }
226 
cl_prop<T>(v: T) -> Vec<MaybeUninit<u8>> where T: CLProp + Sized,227 pub fn cl_prop<T>(v: T) -> Vec<MaybeUninit<u8>>
228 where
229     T: CLProp + Sized,
230 {
231     v.cl_vec()
232 }
233 
234 const CL_DEVICE_TYPES: u32 = CL_DEVICE_TYPE_ACCELERATOR
235     | CL_DEVICE_TYPE_CPU
236     | CL_DEVICE_TYPE_GPU
237     | CL_DEVICE_TYPE_CUSTOM
238     | CL_DEVICE_TYPE_DEFAULT;
239 
check_cl_device_type(val: cl_device_type) -> CLResult<()>240 pub fn check_cl_device_type(val: cl_device_type) -> CLResult<()> {
241     let v: u32 = val.try_into().or(Err(CL_INVALID_DEVICE_TYPE))?;
242     if v == CL_DEVICE_TYPE_ALL || v & CL_DEVICE_TYPES == v {
243         return Ok(());
244     }
245     Err(CL_INVALID_DEVICE_TYPE)
246 }
247 
248 pub const CL_IMAGE_TYPES: [cl_mem_object_type; 6] = [
249     CL_MEM_OBJECT_IMAGE1D,
250     CL_MEM_OBJECT_IMAGE2D,
251     CL_MEM_OBJECT_IMAGE3D,
252     CL_MEM_OBJECT_IMAGE1D_ARRAY,
253     CL_MEM_OBJECT_IMAGE2D_ARRAY,
254     CL_MEM_OBJECT_IMAGE1D_BUFFER,
255 ];
256 
check_cl_bool<T: PartialEq + TryInto<cl_uint>>(val: T) -> Option<bool>257 pub fn check_cl_bool<T: PartialEq + TryInto<cl_uint>>(val: T) -> Option<bool> {
258     let c: u32 = val.try_into().ok()?;
259     if c != CL_TRUE && c != CL_FALSE {
260         return None;
261     }
262     Some(c == CL_TRUE)
263 }
264 
event_list_from_cl( q: &Arc<Queue>, num_events_in_wait_list: cl_uint, event_wait_list: *const cl_event, ) -> CLResult<Vec<Arc<Event>>>265 pub fn event_list_from_cl(
266     q: &Arc<Queue>,
267     num_events_in_wait_list: cl_uint,
268     event_wait_list: *const cl_event,
269 ) -> CLResult<Vec<Arc<Event>>> {
270     // CL_INVALID_EVENT_WAIT_LIST if event_wait_list is NULL and num_events_in_wait_list > 0, or
271     // event_wait_list is not NULL and num_events_in_wait_list is 0, or if event objects in
272     // event_wait_list are not valid events.
273     let res = Event::arcs_from_arr(event_wait_list, num_events_in_wait_list)
274         .map_err(|_| CL_INVALID_EVENT_WAIT_LIST)?;
275 
276     // CL_INVALID_CONTEXT if context associated with command_queue and events in event_list are not
277     // the same.
278     if res.iter().any(|e| e.context != q.context) {
279         return Err(CL_INVALID_CONTEXT);
280     }
281 
282     Ok(res)
283 }
284 
to_maybeuninit_vec<T: Copy>(v: Vec<T>) -> Vec<MaybeUninit<T>>285 pub fn to_maybeuninit_vec<T: Copy>(v: Vec<T>) -> Vec<MaybeUninit<T>> {
286     // In my tests the compiler was smart enough to turn this into a noop
287     v.into_iter().map(MaybeUninit::new).collect()
288 }
289 
checked_compare(a: usize, o: cmp::Ordering, b: u64) -> bool290 pub fn checked_compare(a: usize, o: cmp::Ordering, b: u64) -> bool {
291     if usize::BITS > u64::BITS {
292         a.cmp(&(b as usize)) == o
293     } else {
294         (a as u64).cmp(&b) == o
295     }
296 }
297 
is_alligned<T>(ptr: *const T, alignment: usize) -> bool298 pub fn is_alligned<T>(ptr: *const T, alignment: usize) -> bool {
299     ptr as usize & (alignment - 1) == 0
300 }
301 
bit_check<A: BitAnd<Output = A> + PartialEq + Default, B: Into<A>>(a: A, b: B) -> bool302 pub fn bit_check<A: BitAnd<Output = A> + PartialEq + Default, B: Into<A>>(a: A, b: B) -> bool {
303     a & b.into() != A::default()
304 }
305 
306 // Taken from "Appendix D: Checking for Memory Copy Overlap"
307 // src_offset and dst_offset are additions to support sub-buffers
check_copy_overlap( src_origin: &CLVec<usize>, src_offset: usize, dst_origin: &CLVec<usize>, dst_offset: usize, region: &CLVec<usize>, row_pitch: usize, slice_pitch: usize, ) -> bool308 pub fn check_copy_overlap(
309     src_origin: &CLVec<usize>,
310     src_offset: usize,
311     dst_origin: &CLVec<usize>,
312     dst_offset: usize,
313     region: &CLVec<usize>,
314     row_pitch: usize,
315     slice_pitch: usize,
316 ) -> bool {
317     let slice_size = (region[1] - 1) * row_pitch + region[0];
318     let block_size = (region[2] - 1) * slice_pitch + slice_size;
319     let src_start =
320         src_origin[2] * slice_pitch + src_origin[1] * row_pitch + src_origin[0] + src_offset;
321     let src_end = src_start + block_size;
322     let dst_start =
323         dst_origin[2] * slice_pitch + dst_origin[1] * row_pitch + dst_origin[0] + dst_offset;
324     let dst_end = dst_start + block_size;
325 
326     /* No overlap if dst ends before src starts or if src ends
327      * before dst starts.
328      */
329     if (dst_end <= src_start) || (src_end <= dst_start) {
330         return false;
331     }
332 
333     /* No overlap if region[0] for dst or src fits in the gap
334      * between region[0] and row_pitch.
335      */
336     {
337         let src_dx = (src_origin[0] + src_offset) % row_pitch;
338         let dst_dx = (dst_origin[0] + dst_offset) % row_pitch;
339         if ((dst_dx >= src_dx + region[0]) && (dst_dx + region[0] <= src_dx + row_pitch))
340             || ((src_dx >= dst_dx + region[0]) && (src_dx + region[0] <= dst_dx + row_pitch))
341         {
342             return false;
343         }
344     }
345 
346     /* No overlap if region[1] for dst or src fits in the gap
347      * between region[1] and slice_pitch.
348      */
349     {
350         let src_dy = (src_origin[1] * row_pitch + src_origin[0] + src_offset) % slice_pitch;
351         let dst_dy = (dst_origin[1] * row_pitch + dst_origin[0] + dst_offset) % slice_pitch;
352         if ((dst_dy >= src_dy + slice_size) && (dst_dy + slice_size <= src_dy + slice_pitch))
353             || ((src_dy >= dst_dy + slice_size) && (src_dy + slice_size <= dst_dy + slice_pitch))
354         {
355             return false;
356         }
357     }
358 
359     /* Otherwise src and dst overlap. */
360     true
361 }
362 
363 pub mod cl_slice {
364     use crate::api::util::CLResult;
365     use mesa_rust_util::ptr::addr;
366     use rusticl_opencl_gen::CL_INVALID_VALUE;
367     use std::mem;
368     use std::slice;
369 
370     /// Wrapper around [`std::slice::from_raw_parts`] that returns `Err(CL_INVALID_VALUE)` if any of these conditions is met:
371     /// - `data` is null
372     /// - `data` is not correctly aligned for `T`
373     /// - `len * std::mem::size_of::<T>()` is larger than `isize::MAX`
374     /// - `data` + `len * std::mem::size_of::<T>()` wraps around the address space
375     ///
376     /// # Safety
377     /// The behavior is undefined if any of the other requirements imposed by
378     /// [`std::slice::from_raw_parts`] is violated.
379     #[inline]
from_raw_parts<'a, T>(data: *const T, len: usize) -> CLResult<&'a [T]>380     pub unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> CLResult<&'a [T]> {
381         if allocation_obviously_invalid(data, len) {
382             return Err(CL_INVALID_VALUE);
383         }
384 
385         // SAFETY: We've checked that `data` is not null and properly aligned. We've also checked
386         // that the total size in bytes does not exceed `isize::MAX` and that adding that size to
387         // `data` does not wrap around the address space.
388         //
389         // The caller has to uphold the other safety requirements imposed by [`std::slice::from_raw_parts`].
390         unsafe { Ok(slice::from_raw_parts(data, len)) }
391     }
392 
393     /// Wrapper around [`std::slice::from_raw_parts_mut`] that returns `Err(CL_INVALID_VALUE)` if any of these conditions is met:
394     /// - `data` is null
395     /// - `data` is not correctly aligned for `T`
396     /// - `len * std::mem::size_of::<T>()` is larger than `isize::MAX`
397     /// - `data` + `len * std::mem::size_of::<T>()` wraps around the address space
398     ///
399     /// # Safety
400     /// The behavior is undefined if any of the other requirements imposed by
401     /// [`std::slice::from_raw_parts_mut`] is violated.
402     #[inline]
from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> CLResult<&'a mut [T]>403     pub unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> CLResult<&'a mut [T]> {
404         if allocation_obviously_invalid(data, len) {
405             return Err(CL_INVALID_VALUE);
406         }
407 
408         // SAFETY: We've checked that `data` is not null and properly aligned. We've also checked
409         // that the total size in bytes does not exceed `isize::MAX` and that adding that size to
410         // `data` does not wrap around the address space.
411         //
412         // The caller has to uphold the other safety requirements imposed by [`std::slice::from_raw_parts_mut`].
413         unsafe { Ok(slice::from_raw_parts_mut(data, len)) }
414     }
415 
416     #[must_use]
allocation_obviously_invalid<T>(data: *const T, len: usize) -> bool417     fn allocation_obviously_invalid<T>(data: *const T, len: usize) -> bool {
418         let Some(total_size) = mem::size_of::<T>().checked_mul(len) else {
419             return true;
420         };
421         data.is_null()
422             || !mesa_rust_util::ptr::is_aligned(data)
423             || total_size > isize::MAX as usize
424             || addr(data).checked_add(total_size).is_none()
425     }
426 }
427