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