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