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