ocl_core/
functions.rs

1//! Thin and safe OpenCL API function wrappers.
2//!
3//!
4//!
5//!
6// ### Error Handling Notes
7//
8// In an ideal world, each function would have its own error enum with a
9// variant for each of the possible errors that API can throw with nice long
10// descriptions (presumably ported from the official docs). For now, all
11// functions use the same error type `ApiError` and error messages include a
12// link to the relevant Khronos API reference web page.
13//
14
15use crate::ffi::{c_void, size_t};
16use num_traits::FromPrimitive;
17use std::env;
18use std::ffi::CString;
19use std::fmt;
20use std::iter;
21use std::mem;
22use std::ptr;
23use std::thread;
24use std::time::Duration;
25
26#[cfg(not(feature = "opencl_vendor_mesa"))]
27use crate::ffi::{
28    clCreateFromGLBuffer, clCreateFromGLRenderbuffer, clCreateFromGLTexture,
29    clCreateFromGLTexture2D, clCreateFromGLTexture3D, clEnqueueAcquireGLObjects,
30    clEnqueueReleaseGLObjects,
31};
32#[cfg(not(feature = "opencl_vendor_mesa"))]
33use crate::ffi::{cl_GLenum, cl_GLint, cl_GLuint, cl_gl_context_info};
34
35use crate::ffi::{
36    self, cl_addressing_mode, cl_bool, cl_buffer_create_type, cl_command_queue,
37    cl_command_queue_info, cl_context, cl_context_info, cl_context_properties, cl_device_id,
38    cl_device_info, cl_device_type, cl_event, cl_event_info, cl_filter_mode, cl_image_desc,
39    cl_image_format, cl_image_info, cl_int, cl_kernel, cl_kernel_arg_info, cl_kernel_info,
40    cl_kernel_work_group_info, cl_mem, cl_mem_flags, cl_mem_info, cl_mem_object_type,
41    cl_platform_id, cl_platform_info, cl_profiling_info, cl_program, cl_program_build_info,
42    cl_program_info, cl_sampler, cl_sampler_info, cl_uint,
43};
44
45use crate::error::{Error as OclCoreError, Result as OclCoreResult};
46
47use crate::{
48    AddressingMode, ArgVal, AsMem, BufferCreateType, BufferRegion, BuildProgramCallbackFn,
49    ClContextPtr, ClDeviceIdPtr, ClEventPtrRef, ClNullEventPtr, ClPlatformIdPtr, ClVersions,
50    ClWaitListPtr, CommandExecutionStatus, CommandQueue, CommandQueueInfo, CommandQueueInfoResult,
51    CommandQueueProperties, Context, ContextInfo, ContextInfoResult, ContextProperties,
52    CreateContextCallbackFn, DeviceId, DeviceInfo, DeviceInfoResult, DeviceType, Event,
53    EventCallbackFn, EventInfo, EventInfoResult, FilterMode, ImageDescriptor, ImageFormat,
54    ImageFormatParseResult, ImageInfo, ImageInfoResult, Kernel, KernelArgInfo, KernelArgInfoResult,
55    KernelInfo, KernelInfoResult, KernelWorkGroupInfo, KernelWorkGroupInfoResult, MapFlags, Mem,
56    MemCmdAll, MemCmdRw, MemFlags, MemInfo, MemInfoResult, MemMap, MemMigrationFlags,
57    MemObjectType, OclPrm, OpenclVersion, PlatformId, PlatformInfo, PlatformInfoResult,
58    ProfilingInfo, ProfilingInfoResult, Program, ProgramBuildInfo, ProgramBuildInfoResult,
59    ProgramInfo, ProgramInfoResult, Sampler, SamplerInfo, SamplerInfoResult, Status, UserDataPtr,
60};
61
62#[cfg(not(feature = "opencl_vendor_mesa"))]
63use crate::{GlContextInfo, GlContextInfoResult};
64
65// [TODO]: Do proper auto-detection of available OpenGL context type.
66#[cfg(target_os = "macos")]
67const CL_GL_SHARING_EXT: &str = "cl_APPLE_gl_sharing";
68#[cfg(not(target_os = "macos"))]
69const CL_GL_SHARING_EXT: &str = "cl_khr_gl_sharing";
70
71const KERNEL_DEBUG_SLEEP_DURATION_MS: u64 = 150;
72const PLATFORM_IDS_ATTEMPT_TIMEOUT_MS: u64 = 2000;
73const PLATFORM_IDS_ATTEMPT_COUNT: u64 = 5;
74
75//============================================================================
76//============================================================================
77//=============================== CALLBACKS ==================================
78//============================================================================
79//============================================================================
80
81/// Don't be a dummy. Buckle your `_dummy_callback`.
82pub extern "C" fn _dummy_event_callback(_: ffi::cl_event, _: i32, _: *mut c_void) {}
83
84/// If `event_status` is `CommandExecutionStatus::Complete`, the `cl_event`
85/// pointed to by `user_data` will be set to the same.
86///
87/// `user_data` must be a `cl_event` which has not yet had its destructor run
88/// (`::release_event`).
89///
90/// `src_event_ptr` is not used and does not need anything special done with
91/// its destructor (it will already have been managed by the call to `::set_event_callback`.
92///
93pub extern "C" fn _complete_user_event(
94    src_event_ptr: cl_event,
95    event_status: i32,
96    user_data: *mut c_void,
97) {
98    #[cfg(not(feature = "event_debug_print"))]
99    let _ = src_event_ptr;
100
101    if event_status == CommandExecutionStatus::Complete as i32 && !user_data.is_null() {
102        let tar_event_ptr = user_data as *mut _ as cl_event;
103
104        unsafe {
105            let user_event = Event::from_raw(tar_event_ptr);
106
107            #[cfg(feature = "event_debug_print")]
108            println!(
109                "::_complete_user_event: Setting event complete for: \
110                source: {:?}, target: {:?}...",
111                src_event_ptr, &user_event
112            );
113
114            crate::set_user_event_status(&user_event, CommandExecutionStatus::Complete).unwrap();
115        }
116
117        #[cfg(feature = "event_debug_print")]
118        println!(
119            "  - Event status has been set to 'CommandExecutionStatus::Complete' \
120            for event: {:?}",
121            tar_event_ptr
122        );
123    } else {
124        // NOTE: Though these should be unreachable, panic/unwrap will likely
125        // crash the calling module:
126        match CommandExecutionStatus::from_i32(event_status) {
127            Some(status_enum) => panic!(
128                "ocl_core::_complete_event: User data is null or event \
129                is not complete. Status: '{:?}'",
130                status_enum
131            ),
132            None => eval_errcode(
133                event_status,
134                (),
135                "clSetEventCallback",
136                Some(format!("src_event_ptr: {:?}", src_event_ptr)),
137            )
138            .unwrap(),
139        }
140    }
141}
142
143//============================================================================
144//============================================================================
145//============================ ERROR HANDLING ================================
146//============================================================================
147//============================================================================
148
149static SDK_DOCS_URL_PRE: &'static str =
150    "https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/";
151static SDK_DOCS_URL_SUF: &'static str = ".html#errors";
152
153/// An OpenCL API error.
154#[derive(thiserror::Error)]
155pub struct ApiError {
156    status: Status,
157    fn_name: &'static str,
158    fn_info: Option<String>,
159}
160
161impl ApiError {
162    pub fn new<S: Into<String>>(
163        errcode: i32,
164        fn_name: &'static str,
165        fn_info: Option<S>,
166    ) -> ApiError {
167        let status = match Status::from_i32(errcode) {
168            Some(s) => s,
169            None => panic!(
170                "ocl_core::Error::err_status: Invalid error code: '{}'. \
171                Aborting.",
172                errcode
173            ),
174        };
175
176        let fn_info = fn_info.map(|s| s.into());
177
178        ApiError {
179            status,
180            fn_name,
181            fn_info,
182        }
183    }
184
185    pub fn status(&self) -> Status {
186        self.status
187    }
188}
189
190impl fmt::Display for ApiError {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        let fn_info_string = if let Some(ref fn_info) = self.fn_info {
193            format!("(\"{}\")", fn_info)
194        } else {
195            String::with_capacity(0)
196        };
197
198        let status_int = self.status as i32;
199
200        write!(
201            f,
202            "\n\n\
203            ################################ OPENCL ERROR ############################### \
204            \n\nError executing function: {}{}  \
205            \n\nStatus error code: {:?} ({})  \
206            \n\nPlease visit the following url for more information: \n\n{}{}{}  \n\n\
207            ############################################################################# \n",
208            self.fn_name,
209            fn_info_string,
210            self.status,
211            status_int,
212            SDK_DOCS_URL_PRE,
213            self.fn_name,
214            SDK_DOCS_URL_SUF
215        )
216    }
217}
218
219impl fmt::Debug for ApiError {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        fmt::Display::fmt(&self, f)
222    }
223}
224
225/// Evaluates `errcode` and returns an `Err` with a failure message if it is
226/// not 0 (Status::CL_SUCCESS).
227///
228#[inline(always)]
229fn eval_errcode<T, S>(
230    errcode: cl_int,
231    result: T,
232    fn_name: &'static str,
233    fn_info: Option<S>,
234) -> OclCoreResult<T>
235where
236    S: Into<String>,
237{
238    if (Status::CL_SUCCESS as i32) == errcode {
239        Ok(result)
240    } else {
241        Err(ApiError::new(errcode, fn_name, fn_info).into())
242    }
243}
244
245/// An OpenCL program build error.
246#[derive(Debug, thiserror::Error)]
247pub enum ProgramBuildError {
248    #[error("Device list is empty. Aborting build.")]
249    DeviceListEmpty,
250    #[error(
251        "\n\n\
252        ###################### OPENCL PROGRAM BUILD DEBUG OUTPUT \
253        ######################\
254        \n\n{0}\n\
255        ########################################################\
256        #######################\
257        \n\n"
258    )]
259    BuildLog(String),
260    #[error("{0}")]
261    InfoResult(Box<OclCoreError>),
262}
263
264/// If the program pointed to by `cl_program` for any of the devices listed in
265/// `device_ids` has a build log of any length, it will be returned as an
266/// errcode result.
267///
268pub fn program_build_err<D: ClDeviceIdPtr>(
269    program: &Program,
270    device_ids: &[D],
271) -> Result<(), ProgramBuildError> {
272    if device_ids.is_empty() {
273        return Err(ProgramBuildError::DeviceListEmpty);
274    }
275
276    for device_id in device_ids.iter().cloned() {
277        match get_program_build_info(program, device_id, ProgramBuildInfo::BuildLog) {
278            Ok(ProgramBuildInfoResult::BuildLog(log)) => {
279                if log.len() > 1 {
280                    return Err(ProgramBuildError::BuildLog(log));
281                }
282            }
283            Err(err) => return Err(ProgramBuildError::InfoResult(Box::new(err))),
284            _ => panic!("Unexpected 'ProgramBuildInfoResult' variant."),
285        }
286    }
287
288    Ok(())
289}
290
291/// An API function identifier.
292#[allow(dead_code)]
293#[derive(Debug)]
294pub(crate) enum ApiFunction {
295    None,
296    RetainDevice,
297    ReleaseDevice,
298    CreateProgramWithIl,
299    CreateImage,
300    CreateFromGLTexture,
301    CreateFromD3D11Texture,
302    GetKernelArgInfo,
303    EnqueueFillBuffer,
304    EnqueueFillImage,
305    EnqueueMigrateMemObjects,
306    EnqueueMarkerWithWaitList,
307    EnqueueBarrierWithWaitList,
308    GetExtensionFunctionAddressForPlatform,
309    CompileProgram,
310    LinkProgram,
311}
312
313/// A version kind identifier.
314#[derive(Debug)]
315pub(crate) enum VersionKind {
316    Device,
317    Platform,
318}
319
320/// A version too low error.
321#[derive(Debug, thiserror::Error)]
322#[error("OpenCL ({kind:?}) version too low to use {function:?} (detected: {detected}, required: {required}).")]
323pub struct VersionLowError {
324    detected: OpenclVersion,
325    required: OpenclVersion,
326    function: ApiFunction,
327    kind: VersionKind,
328}
329
330/// An error representing miscellaneous errors from throughout this module.
331#[derive(Debug, thiserror::Error)]
332pub enum ApiWrapperError {
333    #[error("Unable to get platform id list after {} seconds of waiting.", _0)]
334    GetPlatformIdsPlatformListUnavailable(u64),
335    #[error("`devices_max` can not be zero.")]
336    GetDeviceIdsDevicesMaxZero,
337    #[error("No devices specified.")]
338    CreateContextNoDevicesSpecified,
339    #[error("Buffer length and data length and do not match.")]
340    CreateBufferDataLengthMismatch,
341    #[error(
342        "One or more of the devices contained in the list provided to \
343        '::create_context` doesn't support the cl_gl_sharing extension and cannot be \
344        used to create a context associated with OpenGL. [FIXME: determine recommended \
345        resolution - gl_device list fn doesn't work yet]."
346    )]
347    CreateContextClGlSharingUnsupported,
348    #[error("Length of 'devices' must be greater than zero.")]
349    CreateProgramWithBinaryDevicesLenZero,
350    #[error(
351        "Length of 'devices' must equal the length of 'binaries' \
352        (e.g. one binary per device)."
353    )]
354    CreateProgramWithBinaryDevicesLenMismatch,
355    #[error(
356        "The specified function does not exist for the implementation or \
357        'platform' is not a valid platform."
358    )]
359    GetExtensionFunctionAddressForPlatformInvalidFunction,
360    #[error("No OpenCL platforms found. Check your driver.")]
361    DefaultPlatformNoPlatforms,
362    #[error(
363        "The default platform set by the environment variable \
364        'OCL_DEFAULT_PLATFORM_IDX' has an index which is out of range \
365        (index: [{default_platform_idx}], max: [{max_idx}])."
366    )]
367    DefaultPlatformEnvVarBadIdx {
368        default_platform_idx: usize,
369        max_idx: usize,
370    },
371    #[error(
372        "The default device type set by the environment variable \
373        'OCL_DEFAULT_DEVICE_TYPE': ('{0}') is invalid. Valid types are: 'DEFAULT', 'CPU', \
374        'GPU', 'ACCELERATOR', 'CUSTOM', and 'ALL'."
375    )]
376    DefaultDeviceTypeInvalidType(String),
377    #[error("The function pointer to {0} was not resolved.")]
378    ExtensionFunctionPointerNotResolved(String),
379}
380
381//============================================================================
382//============================================================================
383//=========================== SUPPORT FUNCTIONS ==============================
384//============================================================================
385//============================================================================
386
387/// A device pointer list.
388///
389/// Used to create a safe (and platform-homogeneous) list of device pointers
390/// to be passed to a runtime.
391struct DevicePtrList(Vec<cl_device_id>);
392
393impl DevicePtrList {
394    /// Create a new device pointer list.
395    fn new<D: ClDeviceIdPtr>(devices: Option<&[D]>) -> DevicePtrList {
396        let list = match devices {
397            Some(device_ids) => device_ids.iter().map(|d| d.as_ptr()).collect::<Vec<_>>(),
398            None => Vec::new(),
399        };
400
401        DevicePtrList(list)
402    }
403
404    /// Returns a pointer to the list of device pointers.
405    fn as_ptr(&self) -> *const cl_device_id {
406        match self.0.len() {
407            0 => ptr::null(),
408            _ => self.0.as_ptr(),
409        }
410    }
411
412    /// Returns the number of devices in the pointer list.
413    fn num(&self) -> u32 {
414        self.0.len() as u32
415    }
416}
417
418impl<D> From<Option<&[D]>> for DevicePtrList
419where
420    D: ClDeviceIdPtr,
421{
422    fn from(devices: Option<&[D]>) -> DevicePtrList {
423        DevicePtrList::new(devices)
424    }
425}
426
427/// Maps options of slices to pointers and a length.
428fn resolve_event_ptrs<En: ClNullEventPtr, Ewl: ClWaitListPtr>(
429    wait_list: Option<Ewl>,
430    new_event: Option<En>,
431) -> (cl_uint, *const cl_event, *mut cl_event) {
432    // If the wait list is empty or if its containing option is none, map to (0, null),
433    // otherwise map to the length and pointer:
434    let (wait_list_len, wait_list_ptr) = match wait_list {
435        Some(wl) => {
436            if wl.count() > 0 {
437                (wl.count(), unsafe { wl.as_ptr_ptr() } as *const cl_event)
438            } else {
439                (0, ptr::null() as *const cl_event)
440            }
441        }
442        None => (0, ptr::null() as *const cl_event),
443    };
444
445    let new_event_ptr = match new_event {
446        Some(mut ne) => ne.alloc_new(),
447        None => ptr::null_mut() as *mut cl_event,
448    };
449
450    (wait_list_len, wait_list_ptr, new_event_ptr)
451}
452
453/// Converts an array option reference into a pointer to the contained array.
454fn resolve_work_dims(work_dims: Option<&[usize; 3]>) -> *const size_t {
455    match work_dims {
456        Some(w) => w as *const [usize; 3] as *const size_t,
457        None => ptr::null(),
458    }
459}
460
461/// Verifies that OpenCL versions are above a specified threshold.
462pub(crate) fn verify_versions(
463    versions: &[OpenclVersion],
464    required_version: [u16; 2],
465    function: ApiFunction,
466    kind: VersionKind,
467) -> OclCoreResult<()> {
468    let reqd_ver = OpenclVersion::from(required_version);
469
470    for &d_ver in versions {
471        if d_ver < reqd_ver {
472            return Err(VersionLowError {
473                detected: d_ver,
474                required: reqd_ver,
475                function,
476                kind,
477            }
478            .into());
479        }
480    }
481
482    Ok(())
483}
484
485// Verifies that a platform version (`provided_version`) is above a threshold
486// (`required_version`).
487fn verify_platform_version<V: ClVersions>(
488    provided_version: Option<&OpenclVersion>,
489    required_version: [u16; 2],
490    fallback_version_source: &V,
491    function: ApiFunction,
492) -> OclCoreResult<()> {
493    match provided_version {
494        Some(pv) => {
495            let vers = [*pv];
496            verify_versions(&vers, required_version, function, VersionKind::Platform)
497        }
498        None => fallback_version_source.verify_platform_version(required_version),
499    }
500}
501
502// Verifies that a device version (`provided_version`) is above a threshold
503// (`required_version`).
504fn verify_device_version<V: ClVersions>(
505    provided_version: Option<&OpenclVersion>,
506    required_version: [u16; 2],
507    fallback_version_source: &V,
508    function: ApiFunction,
509) -> OclCoreResult<()> {
510    match provided_version {
511        Some(pv) => {
512            let ver = [*pv];
513            verify_versions(&ver, required_version, function, VersionKind::Device)
514        }
515        None => fallback_version_source.verify_device_versions(required_version),
516    }
517}
518
519// Verifies multiple device versions.
520fn verify_device_versions<V: ClVersions>(
521    provided_versions: Option<&[OpenclVersion]>,
522    required_version: [u16; 2],
523    fallback_versions_source: &V,
524    function: ApiFunction,
525) -> OclCoreResult<()> {
526    match provided_versions {
527        Some(pv) => verify_versions(pv, required_version, function, VersionKind::Device),
528        None => fallback_versions_source.verify_device_versions(required_version),
529    }
530}
531
532//============================================================================
533//============================================================================
534//======================= OPENCL FUNCTION WRAPPERS ===========================
535//============================================================================
536//============================================================================
537
538//============================================================================
539//============================= Platform API =================================
540//============================================================================
541
542/// Returns a list of available platforms as 'core' objects.
543pub fn get_platform_ids() -> OclCoreResult<Vec<PlatformId>> {
544    let mut num_platforms = 0 as cl_uint;
545
546    // Get a count of available platforms:
547    let mut errcode: cl_int =
548        unsafe { ffi::clGetPlatformIDs(0, ptr::null_mut(), &mut num_platforms) };
549
550    // Deal with ICD wake up problems when called from multiple threads at the
551    // same time by adding a delay/retry loop:
552    if errcode == Status::CL_PLATFORM_NOT_FOUND_KHR as i32 {
553        // println!("CL_PLATFORM_NOT_FOUND_KHR... looping until platform list is available...");
554        let sleep_ms = PLATFORM_IDS_ATTEMPT_TIMEOUT_MS;
555        let mut iters_rmng = PLATFORM_IDS_ATTEMPT_COUNT;
556
557        while errcode == Status::CL_PLATFORM_NOT_FOUND_KHR as i32 {
558            if iters_rmng == 0 {
559                return Err(ApiWrapperError::GetPlatformIdsPlatformListUnavailable(
560                    (PLATFORM_IDS_ATTEMPT_COUNT * sleep_ms) / 1000,
561                )
562                .into());
563            }
564
565            // Sleep to allow the ICD to refresh or whatever it does:
566            thread::sleep(Duration::from_millis(sleep_ms));
567
568            // Get a count of available platforms:
569            errcode = unsafe { ffi::clGetPlatformIDs(0, ptr::null_mut(), &mut num_platforms) };
570
571            iters_rmng -= 1;
572        }
573    }
574
575    eval_errcode(errcode, (), "clGetPlatformIDs", None::<String>)?;
576
577    // If no platforms are found, return an empty vec directly:
578    if num_platforms == 0 {
579        return Ok(vec![]);
580    }
581
582    // Create a vec with the appropriate size:
583    let mut null_vec: Vec<usize> = iter::repeat(0).take(num_platforms as usize).collect();
584    let (ptr, len, cap) = (null_vec.as_mut_ptr(), null_vec.len(), null_vec.capacity());
585
586    // Steal the vec's soul:
587    let mut platforms: Vec<PlatformId> = unsafe {
588        mem::forget(null_vec);
589        Vec::from_raw_parts(ptr as *mut PlatformId, len, cap)
590    };
591
592    errcode = unsafe {
593        ffi::clGetPlatformIDs(
594            num_platforms,
595            platforms.as_mut_ptr() as *mut cl_platform_id,
596            ptr::null_mut(),
597        )
598    };
599
600    eval_errcode(errcode, platforms, "clGetPlatformIDs", None::<String>)
601}
602
603/// Returns platform information of the requested type.
604pub fn get_platform_info<P: ClPlatformIdPtr>(
605    platform: P,
606    request: PlatformInfo,
607) -> OclCoreResult<PlatformInfoResult> {
608    let mut result_size = 0 as size_t;
609
610    let errcode = unsafe {
611        ffi::clGetPlatformInfo(
612            platform.as_ptr(),
613            request as cl_platform_info,
614            0 as size_t,
615            ptr::null_mut(),
616            &mut result_size as *mut size_t,
617        )
618    };
619
620    // if let Err(err) = eval_errcode(errcode, (), "clGetPlatformInfo", None::<String>) {
621    //     return PlatformInfoResult::Error(Box::new(err));
622    // }
623    eval_errcode(errcode, (), "clGetPlatformInfo", None::<String>)?;
624
625    // If result size is zero, return an empty info result directly:
626    if result_size == 0 {
627        return PlatformInfoResult::from_bytes(request, vec![]);
628    }
629
630    let mut result: Vec<u8> = iter::repeat(32u8).take(result_size as usize).collect();
631
632    let errcode = unsafe {
633        ffi::clGetPlatformInfo(
634            platform.as_ptr(),
635            request as cl_platform_info,
636            result_size as size_t,
637            result.as_mut_ptr() as *mut c_void,
638            ptr::null_mut() as *mut size_t,
639        )
640    };
641
642    let result = eval_errcode(errcode, result, "clGetPlatformInfo", None::<String>)?;
643    PlatformInfoResult::from_bytes(request, result)
644}
645
646//============================================================================
647//============================= Device APIs  =================================
648//============================================================================
649
650/// Returns a list of available devices for a particular platform.
651pub fn get_device_ids<P: ClPlatformIdPtr>(
652    platform: P,
653    device_types: Option<DeviceType>,
654    devices_max: Option<u32>,
655) -> OclCoreResult<Vec<DeviceId>> {
656    let device_types = device_types.unwrap_or(default_device_type()?);
657    let mut devices_available: cl_uint = 0;
658
659    let devices_max = match devices_max {
660        Some(d) => {
661            if d == 0 {
662                return Err(ApiWrapperError::GetDeviceIdsDevicesMaxZero.into());
663            } else {
664                d
665            }
666        }
667        None => crate::DEVICES_MAX,
668    };
669
670    let mut device_ids: Vec<DeviceId> = iter::repeat(unsafe { DeviceId::null() })
671        .take(devices_max as usize)
672        .collect();
673
674    let errcode = unsafe {
675        ffi::clGetDeviceIDs(
676            platform.as_ptr(),
677            device_types.bits() as cl_device_type,
678            devices_max,
679            device_ids.as_mut_ptr() as *mut cl_device_id,
680            &mut devices_available,
681        )
682    };
683    eval_errcode(errcode, (), "clGetDeviceIDs", None::<String>)?;
684
685    // Trim vec len:
686    unsafe {
687        device_ids.set_len(devices_available as usize);
688    }
689    device_ids.shrink_to_fit();
690
691    Ok(device_ids)
692}
693
694/// Returns raw information about a device, as a vector of bytes.
695pub fn get_device_info_raw<D: ClDeviceIdPtr>(device: D, request: u32) -> OclCoreResult<Vec<u8>> {
696    let mut result_size: size_t = 0;
697
698    let errcode = unsafe {
699        ffi::clGetDeviceInfo(
700            device.as_ptr() as cl_device_id,
701            request as cl_device_info,
702            0 as size_t,
703            ptr::null_mut(),
704            &mut result_size as *mut size_t,
705        )
706    };
707
708    // Don't generate a full error report for `CL_INVALID_VALUE` or
709    // `CL_INVALID_OPERATION` it's always just an extension unsupported by the
710    // device (i.e. `CL_DEVICE_HALF_FP_CONFIG` on Intel or Apple). Note:
711    // `CL_INVALID_OPERATION` is actually an invalid error value for this
712    // function and is a bug. Don't hold your breath for a fix.
713    if errcode < 0 {
714        if Status::from_i32(errcode).unwrap() == Status::CL_INVALID_VALUE {
715            return Err(OclCoreError::String(
716                "<unavailable (CL_INVALID_VALUE)>".to_string(),
717            ));
718        } else if Status::from_i32(errcode).unwrap() == Status::CL_INVALID_OPERATION {
719            return Err(OclCoreError::String(
720                "<unavailable (CL_INVALID_OPERATION)>".to_string(),
721            ));
722        }
723    }
724
725    eval_errcode(errcode, (), "clGetDeviceInfo", None::<String>)?;
726
727    // If result size is zero, return an empty vector directly:
728    if result_size == 0 {
729        return Ok(vec![]);
730    }
731
732    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
733
734    let errcode = unsafe {
735        ffi::clGetDeviceInfo(
736            device.as_ptr() as cl_device_id,
737            request as cl_device_info,
738            result_size as size_t,
739            result.as_mut_ptr() as *mut _ as *mut c_void,
740            ptr::null_mut(),
741        )
742    };
743
744    eval_errcode(errcode, result, "clGetDeviceInfo", None::<String>)
745}
746
747/// Returns information about a device.
748pub fn get_device_info<D: ClDeviceIdPtr>(
749    device: D,
750    request: DeviceInfo,
751) -> OclCoreResult<DeviceInfoResult> {
752    let result = get_device_info_raw(device, request as cl_device_info)?;
753
754    match request {
755        DeviceInfo::MaxWorkItemSizes => {
756            let max_wi_dims = match get_device_info(device, DeviceInfo::MaxWorkItemDimensions)? {
757                DeviceInfoResult::MaxWorkItemDimensions(d) => d,
758                // Ok(DeviceInfoResult::Error(err)) => return Err(*err),
759                _ => panic!(
760                    "get_device_info(): Error determining dimensions for \
761                    'DeviceInfo::MaxWorkItemSizes' due to mismatched variants."
762                ),
763            };
764            DeviceInfoResult::from_bytes_max_work_item_sizes(request, result, max_wi_dims)
765        }
766        _ => DeviceInfoResult::from_bytes(request, result),
767    }
768}
769
770/// [UNIMPLEMENTED: Please implement me]
771///
772/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
773pub fn create_sub_devices(device_version: Option<&OpenclVersion>) -> OclCoreResult<()> {
774    // clCreateSubDevices(in_device: cl_device_id,
775    //                    properties: *const cl_device_partition_property,
776    //                    num_devices: cl_uint,
777    //                    out_devices: *mut cl_device_id,
778    //                    num_devices_ret: *mut cl_uint) -> cl_int;
779
780    let _ = device_version;
781    unimplemented!();
782}
783
784/// Increments the reference count of a device.
785///
786/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
787pub unsafe fn retain_device(
788    device: &DeviceId,
789    device_version: Option<&OpenclVersion>,
790) -> OclCoreResult<()> {
791    verify_device_version(device_version, [1, 2], device, ApiFunction::RetainDevice)?;
792    eval_errcode(
793        ffi::clRetainDevice(device.as_ptr()),
794        (),
795        "clRetainDevice",
796        None::<String>,
797    )
798}
799
800/// Decrements the reference count of a device.
801///
802/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
803pub unsafe fn release_device(
804    device: &DeviceId,
805    device_version: Option<&OpenclVersion>,
806) -> OclCoreResult<()> {
807    verify_device_version(device_version, [1, 2], device, ApiFunction::ReleaseDevice)?;
808    eval_errcode(
809        ffi::clReleaseDevice(device.as_ptr()),
810        (),
811        "clReleaseDevice",
812        None::<String>,
813    )
814}
815
816//============================================================================
817//============================= Context APIs  ================================
818//============================================================================
819
820/// Creates a new context pointer valid for all devices in `device_ids`.
821///
822/// Platform is specified in `properties`. If `properties` is `None`, the platform may
823/// default to the first available.
824///
825/// [FIXME]: Incomplete implementation. Callback and userdata untested.
826/// [FIXME]: Verify OpenCL Version on property.
827/// [FIXME]: Most context sources not implemented for `ContextProperties`.
828//
829// [NOTE]: Leave commented "DEBUG" print statements intact until more
830// `ContextProperties` variants are implemented. [PROBABLY DONE]
831pub fn create_context<D: ClDeviceIdPtr>(
832    properties: Option<&ContextProperties>,
833    device_ids: &[D],
834    pfn_notify: Option<CreateContextCallbackFn>,
835    user_data: Option<UserDataPtr>,
836) -> OclCoreResult<Context> {
837    if device_ids.is_empty() {
838        return Err(ApiWrapperError::CreateContextNoDevicesSpecified.into());
839    }
840
841    // // [DEBUG]:
842    // println!("CREATE_CONTEXT: ORIGINAL: properties: {:?}", properties);
843
844    // If properties specify an OpenGL context/sharegroup, ensure all devices
845    // in the provided list support the `cl_gl_sharing` extension:
846    if let Some(properties) = properties {
847        if properties.contains_gl_context_or_sharegroup() {
848            for &device in device_ids {
849                match device_supports_cl_gl_sharing(device) {
850                    Ok(true) => {}
851                    Ok(false) => {
852                        return Err(ApiWrapperError::CreateContextClGlSharingUnsupported.into())
853                    }
854                    Err(err) => return Err(err),
855                }
856            }
857        }
858    }
859
860    let properties_bytes: Vec<isize> = match properties {
861        Some(props) => props.to_raw(),
862        None => Vec::<isize>::with_capacity(0),
863    };
864
865    // // [DEBUG]:
866    // print!("CREATE_CONTEXT: BYTES: ");
867    // util::print_bytes_as_hex(&properties_bytes);
868    // print!("\n");
869
870    // [FIXME]: Disabled:
871    let properties_ptr = if properties_bytes.is_empty() {
872        ptr::null() as *const cl_context_properties
873    } else {
874        properties_bytes.as_ptr()
875    };
876
877    // [FIXME]: Disabled:
878    let user_data_ptr = match user_data {
879        Some(_) => ptr::null_mut(),
880        None => ptr::null_mut(),
881    };
882
883    let device_ids: Vec<_> = device_ids.iter().map(|d| d.as_ptr()).collect();
884
885    let mut errcode: cl_int = 0;
886
887    let context_ptr = unsafe {
888        ffi::clCreateContext(
889            properties_ptr,
890            device_ids.len() as cl_uint,
891            device_ids.as_ptr() as *const cl_device_id,
892            pfn_notify,
893            user_data_ptr,
894            &mut errcode,
895        )
896    };
897    // // [DEBUG]:
898    // println!("CREATE_CONTEXT: CONTEXT PTR: {:?}", context);
899    eval_errcode(errcode, context_ptr, "clCreateContext", None::<String>)
900        .map(|ctx_ptr| unsafe { Context::from_raw_create_ptr(ctx_ptr) })
901}
902
903/// Creates a new context pointer for all devices of a specific type.
904///
905/// Platform is specified in `properties`. If `properties` is `None`, the platform may
906/// default to the first available.
907///
908/// [FIXME]: Incomplete implementation. Callback and userdata untested.
909/// [FIXME]: Verify OpenCL Version on property.
910/// [FIXME]: Most context sources not implemented for `ContextProperties`.
911//
912// [NOTE]: Leave commented "DEBUG" print statements intact until more
913// `ContextProperties` variants are implemented.
914pub fn create_context_from_type<D: ClDeviceIdPtr>(
915    properties: Option<&ContextProperties>,
916    device_type: DeviceType,
917    pfn_notify: Option<CreateContextCallbackFn>,
918    user_data: Option<UserDataPtr>,
919) -> OclCoreResult<Context> {
920    // [DEBUG]:
921    // println!("CREATE_CONTEXT: ORIGINAL: properties: {:?}", properties);
922
923    let properties_bytes: Vec<isize> = match properties {
924        Some(props) => props.to_raw(),
925        None => Vec::<isize>::with_capacity(0),
926    };
927
928    // [DEBUG]:
929    // print!("CREATE_CONTEXT: BYTES: ");
930    // util::print_bytes_as_hex(&properties_bytes);
931    // print!("\n");
932
933    // [FIXME]: Disabled:
934    let properties_ptr = if properties_bytes.is_empty() {
935        ptr::null() as *const cl_context_properties
936    } else {
937        properties_bytes.as_ptr()
938    };
939
940    // [FIXME]: Disabled:
941    let user_data_ptr = match user_data {
942        Some(_) => ptr::null_mut(),
943        None => ptr::null_mut(),
944    };
945
946    let mut errcode: cl_int = 0;
947
948    let context_ptr = unsafe {
949        ffi::clCreateContextFromType(
950            properties_ptr,
951            device_type.bits(),
952            pfn_notify,
953            user_data_ptr,
954            &mut errcode,
955        )
956    };
957    eval_errcode(
958        errcode,
959        context_ptr,
960        "clCreateContextFromType",
961        None::<String>,
962    )
963    .map(|ctx_ptr| unsafe { Context::from_raw_create_ptr(ctx_ptr) })
964}
965
966/// Increments the reference count of a context.
967pub unsafe fn retain_context<C>(context: C) -> OclCoreResult<()>
968where
969    C: ClContextPtr,
970{
971    eval_errcode(
972        ffi::clRetainContext(context.as_ptr()),
973        (),
974        "clRetainContext",
975        None::<String>,
976    )
977}
978
979/// Decrements reference count of a context.
980pub unsafe fn release_context<C>(context: C) -> OclCoreResult<()>
981where
982    C: ClContextPtr,
983{
984    eval_errcode(
985        ffi::clReleaseContext(context.as_ptr()),
986        (),
987        "clReleaseContext",
988        None::<String>,
989    )
990}
991
992fn get_context_info_unparsed<C>(context: C, request: ContextInfo) -> OclCoreResult<Vec<u8>>
993where
994    C: ClContextPtr,
995{
996    let mut result_size: size_t = 0;
997
998    let errcode = unsafe {
999        ffi::clGetContextInfo(
1000            context.as_ptr() as cl_context,
1001            request as cl_context_info,
1002            0 as size_t,
1003            ptr::null_mut(),
1004            &mut result_size as *mut usize,
1005        )
1006    };
1007
1008    eval_errcode(errcode, (), "clGetContextInfo", None::<String>)?;
1009
1010    // Check for invalid context pointer (a potentially hard to track down bug)
1011    // using ridiculous and probably platform-specific logic [if the `Devices`
1012    // variant is passed and we're not in the release config]:
1013    if !cfg!(release) {
1014        let err_if_zero_result_size = request as cl_context_info == ffi::CL_CONTEXT_DEVICES;
1015
1016        if result_size > 10000 || (result_size == 0 && err_if_zero_result_size) {
1017            return Err(OclCoreError::String(
1018                "\n\nocl::core::context_info(): \
1019                Possible invalid context detected. \n\
1020                Context info result size is either '> 10k bytes' or '== 0'. Almost certainly an \n\
1021                invalid context object. If not, please file an issue at: \n\
1022                https://github.com/cogciprocate/ocl/issues.\n\n"
1023                    .to_string(),
1024            ));
1025        }
1026    }
1027
1028    // If result size is zero, return an empty info result directly:
1029    if result_size == 0 {
1030        return Ok(vec![]);
1031    }
1032
1033    let mut result: Vec<u8> = iter::repeat(0).take(result_size).collect();
1034
1035    let errcode = unsafe {
1036        ffi::clGetContextInfo(
1037            context.as_ptr() as cl_context,
1038            request as cl_context_info,
1039            result_size as size_t,
1040            result.as_mut_ptr() as *mut c_void,
1041            ptr::null_mut(),
1042        )
1043    };
1044
1045    eval_errcode(errcode, result, "clGetContextInfo", None::<String>)
1046}
1047
1048/// Returns various kinds of context information.
1049///
1050/// [SDK Reference](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clGetContextInfo.html)
1051///
1052/// ## Errors
1053///
1054/// Returns an error result for all the reasons listed in the SDK in addition
1055/// to an additional error when called with `CL_CONTEXT_DEVICES` as described
1056/// in in the `verify_context()` documentation below.
1057pub fn get_context_info<C>(context: C, request: ContextInfo) -> OclCoreResult<ContextInfoResult>
1058where
1059    C: ClContextPtr,
1060{
1061    ContextInfoResult::from_bytes(request, get_context_info_unparsed(context, request)?)
1062}
1063
1064/// Returns the platform for a context.
1065///
1066/// Errors upon the usual OpenCL errors.
1067///
1068/// Returns `None` if the context properties do not specify a platform.
1069pub fn get_context_platform<C>(context: C) -> OclCoreResult<Option<PlatformId>>
1070where
1071    C: ClContextPtr,
1072{
1073    let props_raw_bytes = get_context_info_unparsed(context, ContextInfo::Properties)?;
1074
1075    let prop = unsafe {
1076        let props_raw = crate::util::bytes_into_vec::<isize>(props_raw_bytes)?;
1077        ContextProperties::extract_property_from_raw(crate::ContextProperty::Platform, &props_raw)
1078    };
1079
1080    if let Some(crate::ContextPropertyValue::Platform(plat)) = prop {
1081        Ok(Some(plat))
1082    } else {
1083        Ok(None)
1084    }
1085}
1086
1087/// Used to query current or available devices associated with an existing
1088/// OpenGL context/sharegroup.
1089///
1090/// `properties` must identify a single valid GL context or GL share group
1091/// object and a valid platform.
1092#[cfg(not(feature = "opencl_vendor_mesa"))]
1093pub fn get_gl_context_info_khr(
1094    properties: &ContextProperties,
1095    request: GlContextInfo,
1096) -> OclCoreResult<GlContextInfoResult> {
1097    let cl_get_gl_context_info_khr_fn: ffi::clGetGLContextInfoKHR_fn = unsafe {
1098        let fn_name = match ::std::ffi::CString::new("clGetGLContextInfoKHR") {
1099            Ok(s) => s,
1100            Err(err) => return Err(err.into()),
1101        };
1102
1103        let plat = match properties.get_platform() {
1104            Some(p) => p,
1105            None => {
1106                return Err("ocl::core::get_gl_context_info_khr: \
1107                    Context properties must specify a platform."
1108                    .into());
1109            }
1110        };
1111
1112        let fn_ptr = ffi::clGetExtensionFunctionAddressForPlatform(
1113            plat.as_ptr(),
1114            fn_name.as_ptr() as *mut _,
1115        );
1116
1117        if fn_ptr.is_null() {
1118            return Err("Unable to get extension function \
1119                address for clGetGLContextInfoKHR. The function is not supported by this \
1120                platform."
1121                .into());
1122        }
1123
1124        std::mem::transmute(fn_ptr)
1125    };
1126
1127    let props_bytes = properties.to_raw();
1128    let mut result_size: size_t = 0;
1129
1130    let errcode = cl_get_gl_context_info_khr_fn(
1131        props_bytes.as_ptr(),
1132        request as cl_gl_context_info,
1133        0 as size_t,
1134        ptr::null_mut(),
1135        &mut result_size as *mut usize,
1136    );
1137
1138    // if let Err(err) = eval_errcode(errcode, (), "clGetGlContextInfoKhr", None::<String>) {
1139    //     return GlContextInfoResult::Error(Box::new(err));
1140    // }
1141    eval_errcode(errcode, (), "clGetGlContextInfoKhr", None::<String>)?;
1142
1143    if result_size == 0 {
1144        return GlContextInfoResult::from_bytes(request, vec![]);
1145    }
1146
1147    // // DEBUG:
1148    // let result_size = match request {
1149    //     GlContextInfo::CurrentDevice => ::std::mem::size_of::<ffi::cl_device_id>(),
1150    //     GlContextInfo::Devices => ::std::mem::size_of::<*mut ffi::cl_device_id>(),
1151    // };
1152
1153    let mut result: Vec<u8> = iter::repeat(0).take(result_size).collect();
1154
1155    let errcode = cl_get_gl_context_info_khr_fn(
1156        props_bytes.as_ptr(),
1157        request as cl_gl_context_info,
1158        result_size as size_t,
1159        result.as_mut_ptr() as *mut c_void,
1160        ptr::null_mut(),
1161    );
1162
1163    let result = eval_errcode(errcode, result, "clGetGlContextInfoKhr", None::<String>)?;
1164    GlContextInfoResult::from_bytes(request, result)
1165}
1166
1167//============================================================================
1168//========================== Command Queue APIs ==============================
1169//============================================================================
1170
1171/// Returns a new command queue pointer.
1172pub fn create_command_queue<C, D>(
1173    context: C,
1174    device: D,
1175    properties: Option<CommandQueueProperties>,
1176) -> OclCoreResult<CommandQueue>
1177where
1178    C: ClContextPtr,
1179    D: ClDeviceIdPtr,
1180{
1181    // Verify that the context is valid:
1182    verify_context(context)?;
1183
1184    let cmd_queue_props = match properties {
1185        Some(p) => p.bits,
1186        None => 0,
1187    };
1188
1189    let mut errcode: cl_int = 0;
1190
1191    let cq_ptr = unsafe {
1192        ffi::clCreateCommandQueue(
1193            context.as_ptr(),
1194            device.as_ptr(),
1195            cmd_queue_props,
1196            &mut errcode,
1197        )
1198    };
1199    eval_errcode(errcode, cq_ptr, "clCreateCommandQueue", None::<String>)
1200        .map(|cq_ptr| unsafe { CommandQueue::from_raw_create_ptr(cq_ptr) })
1201}
1202
1203/// Increments the reference count of a command queue.
1204pub unsafe fn retain_command_queue(queue: &CommandQueue) -> OclCoreResult<()> {
1205    eval_errcode(
1206        ffi::clRetainCommandQueue(queue.as_ptr()),
1207        (),
1208        "clRetainCommandQueue",
1209        None::<String>,
1210    )
1211}
1212
1213/// Decrements the reference count of a command queue.
1214///
1215/// [FIXME]: Return result
1216pub unsafe fn release_command_queue(queue: &CommandQueue) -> OclCoreResult<()> {
1217    eval_errcode(
1218        ffi::clReleaseCommandQueue(queue.as_ptr()),
1219        (),
1220        "clReleaseCommandQueue",
1221        None::<String>,
1222    )
1223}
1224
1225/// Returns information about a command queue
1226pub fn get_command_queue_info(
1227    queue: &CommandQueue,
1228    request: CommandQueueInfo,
1229) -> OclCoreResult<CommandQueueInfoResult> {
1230    let mut result_size: size_t = 0;
1231
1232    let errcode = unsafe {
1233        ffi::clGetCommandQueueInfo(
1234            queue.as_ptr() as cl_command_queue,
1235            request as cl_command_queue_info,
1236            0 as size_t,
1237            ptr::null_mut(),
1238            &mut result_size as *mut size_t,
1239        )
1240    };
1241
1242    eval_errcode(errcode, (), "clGetCommandQueueInfo", None::<String>)?;
1243
1244    // If result size is zero, return an empty info result directly:
1245    if result_size == 0 {
1246        return CommandQueueInfoResult::from_bytes(request, vec![]);
1247    }
1248
1249    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
1250
1251    let errcode = unsafe {
1252        ffi::clGetCommandQueueInfo(
1253            queue.as_ptr() as cl_command_queue,
1254            request as cl_command_queue_info,
1255            result_size,
1256            result.as_mut_ptr() as *mut _ as *mut c_void,
1257            ptr::null_mut(),
1258        )
1259    };
1260
1261    let result = eval_errcode(errcode, result, "clGetCommandQueueInfo", None::<String>)?;
1262    CommandQueueInfoResult::from_bytes(request, result)
1263}
1264
1265//============================================================================
1266//========================== Memory Object APIs ==============================
1267//============================================================================
1268
1269/// Returns a new buffer pointer with size (bytes): `len` * sizeof(T).
1270///
1271/// ## Safety
1272///
1273/// The caller must ensure that correct and appropriate `flags` are being
1274/// used.
1275///
1276pub unsafe fn create_buffer<C, T>(
1277    context: C,
1278    flags: MemFlags,
1279    len: usize,
1280    data: Option<&[T]>,
1281) -> OclCoreResult<Mem>
1282where
1283    C: ClContextPtr,
1284    T: OclPrm,
1285{
1286    // Verify that the context is valid:
1287    verify_context(context)?;
1288
1289    let mut errcode: cl_int = 0;
1290
1291    let host_ptr = match data {
1292        Some(d) => {
1293            if d.len() != len {
1294                return Err(ApiWrapperError::CreateBufferDataLengthMismatch.into());
1295            }
1296            d.as_ptr() as cl_mem
1297        }
1298        None => ptr::null_mut(),
1299    };
1300
1301    let buf_ptr = ffi::clCreateBuffer(
1302        context.as_ptr(),
1303        flags.bits() as cl_mem_flags,
1304        len * mem::size_of::<T>(),
1305        host_ptr,
1306        &mut errcode,
1307    );
1308
1309    // [TODO]: Convert back the return style to this:
1310    eval_errcode(errcode, buf_ptr, "clCreateBuffer", None::<String>)
1311        .map(|ptr| Mem::from_raw_create_ptr(ptr))
1312}
1313
1314/// Return a buffer pointer from a `OpenGL` buffer object.
1315///
1316/// ## Safety
1317///
1318/// The caller must ensure that correct and appropriate `flags` are being
1319/// used.
1320///
1321// [UNTESTED]
1322//
1323#[cfg(not(feature = "opencl_vendor_mesa"))]
1324pub unsafe fn create_from_gl_buffer<C>(
1325    context: C,
1326    gl_object: cl_GLuint,
1327    flags: MemFlags,
1328) -> OclCoreResult<Mem>
1329where
1330    C: ClContextPtr,
1331{
1332    // Verify that the context is valid
1333    verify_context(context)?;
1334
1335    let mut errcode: cl_int = 0;
1336
1337    let buf_ptr = clCreateFromGLBuffer(
1338        context.as_ptr(),
1339        flags.bits() as cl_mem_flags,
1340        gl_object,
1341        &mut errcode,
1342    );
1343
1344    eval_errcode(errcode, buf_ptr, "clCreateFromGLBuffer", None::<String>)
1345        .map(|ptr| Mem::from_raw_create_ptr(ptr))
1346}
1347
1348/// Return a renderbuffer pointer from a `OpenGL` renderbuffer object.
1349///
1350/// ## Safety
1351///
1352/// The caller must ensure that correct and appropriate `flags` are being
1353/// used.
1354///
1355// [UNTESTED]
1356//
1357#[cfg(not(feature = "opencl_vendor_mesa"))]
1358pub unsafe fn create_from_gl_renderbuffer<C>(
1359    context: C,
1360    renderbuffer: cl_GLuint,
1361    flags: MemFlags,
1362) -> OclCoreResult<Mem>
1363where
1364    C: ClContextPtr,
1365{
1366    // Verify that the context is valid
1367    verify_context(context)?;
1368
1369    let mut errcode: cl_int = 0;
1370
1371    let buf_ptr = clCreateFromGLRenderbuffer(
1372        context.as_ptr(),
1373        flags.bits() as cl_mem_flags,
1374        renderbuffer,
1375        &mut errcode,
1376    );
1377
1378    eval_errcode(
1379        errcode,
1380        buf_ptr,
1381        "clCreateFromGLRenderbuffer",
1382        None::<String>,
1383    )
1384    .map(|ptr| Mem::from_raw_create_ptr(ptr))
1385}
1386
1387/// Return a texture2D pointer from a `OpenGL` texture2D object.
1388///
1389/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
1390///
1391/// ## Safety
1392///
1393/// The caller must ensure that correct and appropriate `flags` are being
1394/// used.
1395///
1396// [TODO]: If version is < 1.2, automatically use older versions.
1397//
1398#[cfg(not(feature = "opencl_vendor_mesa"))]
1399pub unsafe fn create_from_gl_texture<C>(
1400    context: C,
1401    texture_target: cl_GLenum,
1402    miplevel: cl_GLint,
1403    texture: cl_GLuint,
1404    flags: MemFlags,
1405    device_versions: Option<&[OpenclVersion]>,
1406) -> OclCoreResult<Mem>
1407where
1408    C: ClContextPtr,
1409{
1410    // Verify that the context is valid
1411    verify_context(context)?;
1412
1413    // Verify device versions:
1414    verify_device_versions(
1415        device_versions,
1416        [1, 2],
1417        &context.as_ptr(),
1418        ApiFunction::CreateFromGLTexture,
1419    )?;
1420
1421    // [TODO]: Forward old OpenCL versions to these instead:
1422    // let obj_core = match image_desc.image_depth {
1423    //     2 => unsafe { try!(core::create_from_gl_texture_2d(
1424    //                         queue.context_core(),
1425    //                         texture_target,
1426    //                         miplevel,
1427    //                         texture,
1428    //                         flags)) },
1429    //     3 => unsafe { try!(core::create_from_gl_texture_3d(
1430    //                         queue.context_core(),
1431    //                         texture_target,
1432    //                         miplevel,
1433    //                         texture,
1434    //                         flags)) },
1435    //     _ => unimplemented!() // FIXME: return an error ? or panic! ?
1436    // };
1437
1438    let mut errcode: cl_int = 0;
1439
1440    let buf_ptr = clCreateFromGLTexture(
1441        context.as_ptr(),
1442        flags.bits() as cl_mem_flags,
1443        texture_target,
1444        miplevel,
1445        texture,
1446        &mut errcode,
1447    );
1448
1449    eval_errcode(errcode, buf_ptr, "clCreateFromGLTexture", None::<String>)
1450        .map(|ptr| Mem::from_raw_create_ptr(ptr))
1451}
1452
1453/// Return a texture2D pointer from a `OpenGL` texture2D object.
1454///
1455// [UNTESTED] [DEPRICATED]
1456#[cfg(not(feature = "opencl_vendor_mesa"))]
1457pub unsafe fn create_from_gl_texture_2d<C>(
1458    context: C,
1459    texture_target: cl_GLenum,
1460    miplevel: cl_GLint,
1461    texture: cl_GLuint,
1462    flags: MemFlags,
1463) -> OclCoreResult<Mem>
1464where
1465    C: ClContextPtr,
1466{
1467    // Verify that the context is valid
1468    verify_context(context)?;
1469
1470    let mut errcode: cl_int = 0;
1471
1472    let buf_ptr = clCreateFromGLTexture2D(
1473        context.as_ptr(),
1474        flags.bits() as cl_mem_flags,
1475        texture_target,
1476        miplevel,
1477        texture,
1478        &mut errcode,
1479    );
1480
1481    eval_errcode(errcode, buf_ptr, "clCreateFromGLTexture2D", None::<String>)
1482        .map(|ptr| Mem::from_raw_create_ptr(ptr))
1483}
1484
1485/// Return a texture3D pointer from a `OpenGL` texture3D object.
1486///
1487// [UNTESTED] [DEPRICATED]
1488#[cfg(not(feature = "opencl_vendor_mesa"))]
1489pub unsafe fn create_from_gl_texture_3d<C>(
1490    context: C,
1491    texture_target: cl_GLenum,
1492    miplevel: cl_GLint,
1493    texture: cl_GLuint,
1494    flags: MemFlags,
1495) -> OclCoreResult<Mem>
1496where
1497    C: ClContextPtr,
1498{
1499    // Verify that the context is valid
1500    verify_context(context)?;
1501
1502    let mut errcode: cl_int = 0;
1503
1504    let buf_ptr = clCreateFromGLTexture3D(
1505        context.as_ptr(),
1506        flags.bits() as cl_mem_flags,
1507        texture_target,
1508        miplevel,
1509        texture,
1510        &mut errcode,
1511    );
1512
1513    eval_errcode(errcode, buf_ptr, "clCreateFromGLTexture3D", None::<String>)
1514        .map(|ptr| Mem::from_raw_create_ptr(ptr))
1515}
1516
1517/// Return a buffer pointer from a `D3D11` buffer object.
1518///
1519/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
1520///
1521/// ## Safety
1522///
1523/// The caller must ensure that correct and appropriate `flags` are being
1524/// used.
1525///
1526pub unsafe fn create_from_d3d11_buffer<C>(
1527    context: C,
1528    buffer: ffi::cl_id3d11_buffer,
1529    flags: MemFlags,
1530    extension_fns: &crate::ExtensionFunctions,
1531) -> OclCoreResult<Mem>
1532where
1533    C: ClContextPtr,
1534{
1535    // Verify that the context is valid
1536    verify_context(context)?;
1537
1538    match extension_fns.clCreateFromD3D11Buffer {
1539        Some(func) => {
1540            let mut errcode: cl_int = 0;
1541
1542            let buf_ptr = func(
1543                context.as_ptr(),
1544                flags.bits() as cl_mem_flags,
1545                buffer,
1546                &mut errcode,
1547            );
1548
1549            eval_errcode(errcode, buf_ptr, "clCreateFromD3D11Buffer", None::<String>)
1550                .map(|ptr| Mem::from_raw_create_ptr(ptr))
1551        }
1552        None => Err(ApiWrapperError::ExtensionFunctionPointerNotResolved(
1553            "clCreateFromD3D11Buffer".into(),
1554        )
1555        .into()),
1556    }
1557}
1558
1559/// Return a texture2D pointer from a `D3D11` texture2D object.
1560///
1561/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
1562///
1563/// ## Safety
1564///
1565/// The caller must ensure that correct and appropriate `flags` are being
1566/// used.
1567///
1568pub unsafe fn create_from_d3d11_texture2d<C>(
1569    context: C,
1570    texture: ffi::cl_id3d11_texture2d,
1571    subresource: cl_uint,
1572    flags: MemFlags,
1573    device_versions: Option<&[OpenclVersion]>,
1574    extension_fns: &crate::ExtensionFunctions,
1575) -> OclCoreResult<Mem>
1576where
1577    C: ClContextPtr,
1578{
1579    // Verify that the context is valid
1580    verify_context(context)?;
1581
1582    // Verify device versions:
1583    verify_device_versions(
1584        device_versions,
1585        [1, 2],
1586        &context.as_ptr(),
1587        ApiFunction::CreateFromD3D11Texture,
1588    )?;
1589
1590    match extension_fns.clCreateFromD3D11Texture2D {
1591        Some(func) => {
1592            let mut errcode: cl_int = 0;
1593
1594            let buf_ptr = func(
1595                context.as_ptr(),
1596                flags.bits() as cl_mem_flags,
1597                texture,
1598                subresource,
1599                &mut errcode,
1600            );
1601
1602            eval_errcode(
1603                errcode,
1604                buf_ptr,
1605                "clCreateFromD3D11Texture2D",
1606                None::<String>,
1607            )
1608            .map(|ptr| Mem::from_raw_create_ptr(ptr))
1609        }
1610        None => Err(ApiWrapperError::ExtensionFunctionPointerNotResolved(
1611            "clCreateFromD3D11Texture2D".into(),
1612        )
1613        .into()),
1614    }
1615}
1616
1617/// Return a texture3D pointer from a `D3D11` texture3D object.
1618///
1619/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
1620///
1621/// ## Safety
1622///
1623/// The caller must ensure that correct and appropriate `flags` are being
1624/// used.
1625///
1626pub unsafe fn create_from_d3d11_texture3d<C>(
1627    context: C,
1628    texture: ffi::cl_id3d11_texture3d,
1629    subresource: cl_uint,
1630    flags: MemFlags,
1631    device_versions: Option<&[OpenclVersion]>,
1632    extension_fns: &crate::ExtensionFunctions,
1633) -> OclCoreResult<Mem>
1634where
1635    C: ClContextPtr,
1636{
1637    // Verify that the context is valid
1638    verify_context(context)?;
1639
1640    // Verify device versions:
1641    verify_device_versions(
1642        device_versions,
1643        [1, 2],
1644        &context.as_ptr(),
1645        ApiFunction::CreateFromD3D11Texture,
1646    )?;
1647
1648    match extension_fns.clCreateFromD3D11Texture3D {
1649        Some(func) => {
1650            let mut errcode: cl_int = 0;
1651
1652            let buf_ptr = func(
1653                context.as_ptr(),
1654                flags.bits() as cl_mem_flags,
1655                texture,
1656                subresource,
1657                &mut errcode,
1658            );
1659
1660            eval_errcode(
1661                errcode,
1662                buf_ptr,
1663                "clCreateFromD3D11Texture3D",
1664                None::<String>,
1665            )
1666            .map(|ptr| Mem::from_raw_create_ptr(ptr))
1667        }
1668        None => Err(ApiWrapperError::ExtensionFunctionPointerNotResolved(
1669            "clCreateFromD3D11Texture3D".into(),
1670        )
1671        .into()),
1672    }
1673}
1674
1675/// Acquire D3D11 memory objects (buffers and images) that have been created
1676/// from D3D11 objects.
1677///
1678/// To create a slice from a single `Mem` reference without any cost, use
1679/// something like: `unsafe { ::std::slice::from_raw_parts(&core_mem, 1) };`.
1680///
1681pub fn enqueue_acquire_d3d11_objects<En, Ewl>(
1682    command_queue: &CommandQueue,
1683    buffers: &[Mem],
1684    wait_list: Option<Ewl>,
1685    new_event: Option<En>,
1686    extension_fns: &crate::ExtensionFunctions,
1687) -> OclCoreResult<()>
1688where
1689    En: ClNullEventPtr,
1690    Ewl: ClWaitListPtr,
1691{
1692    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
1693
1694    match extension_fns.clEnqueueAcquireD3D11Objects {
1695        Some(func) => {
1696            let errcode = func(
1697                command_queue.as_ptr(),
1698                buffers.len() as u32,
1699                buffers.as_ptr() as *const cl_mem,
1700                wait_list_len,
1701                wait_list_ptr,
1702                new_event_ptr,
1703            );
1704
1705            eval_errcode(errcode, (), "clEnqueueAcquireD3D11Objects", None::<String>)
1706        }
1707        None => Err(ApiWrapperError::ExtensionFunctionPointerNotResolved(
1708            "clEnqueueAcquireD3D11Objects".into(),
1709        )
1710        .into()),
1711    }
1712}
1713
1714/// Release D3D11 memory objects (buffers and images) that have been created
1715/// from D3D11 objects.
1716///
1717/// To create a slice from a single `Mem` reference without any cost, use
1718/// something like: `unsafe { ::std::slice::from_raw_parts(&core_mem, 1) };`.
1719///
1720pub fn enqueue_release_d3d11_objects<En, Ewl>(
1721    command_queue: &CommandQueue,
1722    buffers: &[Mem],
1723    wait_list: Option<Ewl>,
1724    new_event: Option<En>,
1725    extension_fns: &crate::ExtensionFunctions,
1726) -> OclCoreResult<()>
1727where
1728    En: ClNullEventPtr,
1729    Ewl: ClWaitListPtr,
1730{
1731    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
1732
1733    match extension_fns.clEnqueueReleaseD3D11Objects {
1734        Some(func) => {
1735            let errcode = func(
1736                command_queue.as_ptr(),
1737                buffers.len() as u32,
1738                buffers.as_ptr() as *const cl_mem,
1739                wait_list_len,
1740                wait_list_ptr,
1741                new_event_ptr,
1742            );
1743
1744            eval_errcode(errcode, (), "clEnqueueReleaseD3D11Objects", None::<String>)
1745        }
1746        None => Err(ApiWrapperError::ExtensionFunctionPointerNotResolved(
1747            "clEnqueueReleaseD3D11Objects".into(),
1748        )
1749        .into()),
1750    }
1751}
1752
1753/// Creates a new buffer object (referred to as a sub-buffer object) from an
1754/// existing buffer object.
1755///
1756/// The returned sub-buffer has a number of caveats which can cause undefined
1757/// behavior.
1758///
1759/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateSubBuffer.html)
1760///
1761pub fn create_sub_buffer<T: OclPrm>(
1762    buffer: &Mem,
1763    flags: MemFlags,
1764    buffer_create_info: &BufferRegion<T>,
1765) -> OclCoreResult<Mem> {
1766    let buffer_create_type = BufferCreateType::Region;
1767    let buffer_create_info_bytes = buffer_create_info.to_bytes();
1768    let mut errcode = 0i32;
1769
1770    let sub_buf_ptr = unsafe {
1771        ffi::clCreateSubBuffer(
1772            buffer.as_ptr(),
1773            flags.bits(),
1774            buffer_create_type as cl_buffer_create_type,
1775            &buffer_create_info_bytes as *const _ as *const c_void,
1776            &mut errcode,
1777        )
1778    };
1779
1780    eval_errcode(errcode, sub_buf_ptr, "clCreateSubBuffer", None::<String>)
1781        .map(|ptr| unsafe { Mem::from_raw_create_ptr(ptr) })
1782}
1783
1784/// Returns a new image (mem) pointer.
1785///
1786/// [TODO]: If version is < 1.2, automatically use older versions.
1787///
1788///
1789/// ## Safety
1790///
1791///
1792///
1793/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
1794pub unsafe fn create_image<C, T>(
1795    context: C,
1796    flags: MemFlags,
1797    format: &ImageFormat,
1798    desc: &ImageDescriptor,
1799    data: Option<&[T]>,
1800    device_versions: Option<&[OpenclVersion]>,
1801) -> OclCoreResult<Mem>
1802where
1803    C: ClContextPtr,
1804    T: OclPrm,
1805{
1806    // Verify that the context is valid:
1807    verify_context(context)?;
1808
1809    // Verify device versions:
1810    verify_device_versions(
1811        device_versions,
1812        [1, 2],
1813        &context.as_ptr(),
1814        ApiFunction::CreateImage,
1815    )?;
1816
1817    let mut errcode: cl_int = 0;
1818
1819    let host_ptr = match data {
1820        Some(d) => {
1821            // [FIXME]: CALCULATE CORRECT IMAGE SIZE AND COMPARE WITH FORMAT/DESC
1822            // assert!(d.len() == len, "ocl::create_image(): Data length mismatch.");
1823            d.as_ptr() as cl_mem
1824        }
1825        None => ptr::null_mut(),
1826    };
1827
1828    let image_ptr = ffi::clCreateImage(
1829        context.as_ptr(),
1830        flags.bits() as cl_mem_flags,
1831        &format.to_raw() as *const cl_image_format,
1832        &desc.to_raw() as *const cl_image_desc,
1833        host_ptr,
1834        &mut errcode as *mut cl_int,
1835    );
1836
1837    eval_errcode(errcode, image_ptr, "clCreateImage", None::<String>)
1838        .map(|ptr| Mem::from_raw_create_ptr(ptr))
1839}
1840
1841/// Increments the reference counter of a mem object.
1842pub unsafe fn retain_mem_object(mem: &Mem) -> OclCoreResult<()> {
1843    eval_errcode(
1844        ffi::clRetainMemObject(mem.as_ptr()),
1845        (),
1846        "clRetainMemObject",
1847        None::<String>,
1848    )
1849}
1850
1851/// Decrements the reference counter of a mem object.
1852pub unsafe fn release_mem_object(mem: &Mem) -> OclCoreResult<()> {
1853    eval_errcode(
1854        ffi::clReleaseMemObject(mem.as_ptr()),
1855        (),
1856        "clReleaseMemObject",
1857        None::<String>,
1858    )
1859}
1860
1861/// Returns a list of supported image formats.
1862///
1863/// ## Example
1864///
1865/// ```rust,ignore
1866/// let context = Context::builder().build().unwrap();
1867///
1868/// let img_fmts = core::get_supported_image_formats(context,
1869///    core::MEM_READ_WRITE, core::MemObjectType::Image2d)
1870/// ```
1871pub fn get_supported_image_formats<C>(
1872    context: C,
1873    flags: MemFlags,
1874    image_type: MemObjectType,
1875) -> OclCoreResult<Vec<ImageFormatParseResult>>
1876where
1877    C: ClContextPtr,
1878{
1879    let mut num_image_formats = 0 as cl_uint;
1880
1881    let errcode = unsafe {
1882        ffi::clGetSupportedImageFormats(
1883            context.as_ptr(),
1884            flags.bits() as cl_mem_flags,
1885            image_type as cl_mem_object_type,
1886            0 as cl_uint,
1887            ptr::null_mut() as *mut cl_image_format,
1888            &mut num_image_formats as *mut cl_uint,
1889        )
1890    };
1891    eval_errcode(errcode, (), "clGetSupportedImageFormats", None::<String>)?;
1892
1893    // If no formats found, return an empty list directly:
1894    if num_image_formats == 0 {
1895        return Ok(vec![]);
1896    }
1897
1898    let mut image_formats: Vec<cl_image_format> = (0..(num_image_formats as usize))
1899        .map(|_| ImageFormat::new_raw())
1900        .collect();
1901
1902    debug_assert!(image_formats.len() == num_image_formats as usize && !image_formats.is_empty());
1903
1904    let errcode = unsafe {
1905        ffi::clGetSupportedImageFormats(
1906            context.as_ptr(),
1907            flags.bits() as cl_mem_flags,
1908            image_type as cl_mem_object_type,
1909            num_image_formats,
1910            image_formats.as_mut_ptr() as *mut _ as *mut cl_image_format,
1911            ptr::null_mut(),
1912        )
1913    };
1914
1915    eval_errcode(errcode, (), "clGetSupportedImageFormats", None::<String>)?;
1916    Ok(ImageFormat::list_from_raw(image_formats))
1917}
1918
1919/// Get mem object info.
1920pub fn get_mem_object_info(obj: &Mem, request: MemInfo) -> OclCoreResult<MemInfoResult> {
1921    let mut result_size: size_t = 0;
1922
1923    let errcode = unsafe {
1924        ffi::clGetMemObjectInfo(
1925            obj.as_ptr() as cl_mem,
1926            request as cl_mem_info,
1927            0 as size_t,
1928            ptr::null_mut(),
1929            &mut result_size as *mut size_t,
1930        )
1931    };
1932
1933    eval_errcode(errcode, (), "clGetMemObjectInfo", None::<String>)?;
1934
1935    // If result size is zero, return an empty info result directly:
1936    if result_size == 0 {
1937        return MemInfoResult::from_bytes(request, vec![]);
1938    }
1939
1940    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
1941
1942    let errcode = unsafe {
1943        ffi::clGetMemObjectInfo(
1944            obj.as_ptr() as cl_mem,
1945            request as cl_mem_info,
1946            result_size,
1947            result.as_mut_ptr() as *mut _ as *mut c_void,
1948            ptr::null_mut(),
1949        )
1950    };
1951    let result = eval_errcode(errcode, result, "clGetMemObjectInfo", None::<String>)?;
1952    MemInfoResult::from_bytes(request, result)
1953}
1954
1955/// Get image info.
1956pub fn get_image_info(obj: &Mem, request: ImageInfo) -> OclCoreResult<ImageInfoResult> {
1957    let mut result_size: size_t = 0;
1958
1959    let errcode = unsafe {
1960        ffi::clGetImageInfo(
1961            obj.as_ptr() as cl_mem,
1962            request as cl_image_info,
1963            0 as size_t,
1964            ptr::null_mut(),
1965            &mut result_size as *mut size_t,
1966        )
1967    };
1968
1969    eval_errcode(errcode, (), "clGetImageInfo", None::<String>)?;
1970
1971    // If result size is zero, return an empty info result directly:
1972    if result_size == 0 {
1973        return ImageInfoResult::from_bytes(request, vec![]);
1974    }
1975
1976    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
1977
1978    let errcode = unsafe {
1979        ffi::clGetImageInfo(
1980            obj.as_ptr() as cl_mem,
1981            request as cl_image_info,
1982            result_size,
1983            result.as_mut_ptr() as *mut _ as *mut c_void,
1984            ptr::null_mut(),
1985        )
1986    };
1987
1988    let result = eval_errcode(errcode, result, "clGetImageInfo", None::<String>)?;
1989    ImageInfoResult::from_bytes(request, result)
1990}
1991
1992/// [UNIMPLEMENTED: Please implement me]
1993pub fn set_mem_object_destructor_callback() -> OclCoreResult<()> {
1994    // ffi::clSetMemObjectDestructorCallback(memobj: cl_mem,
1995    //                                     pfn_notify: extern fn (cl_mem, *mut c_void),
1996    //                                     user_data: *mut c_void) -> cl_int;
1997    unimplemented!();
1998}
1999
2000//============================================================================
2001//============================= Sampler APIs =================================
2002//============================================================================
2003
2004/// Creates and returns a new sampler object.
2005///
2006/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateSampler.html)
2007pub fn create_sampler<C>(
2008    context: C,
2009    normalize_coords: bool,
2010    addressing_mode: AddressingMode,
2011    filter_mode: FilterMode,
2012) -> OclCoreResult<Sampler>
2013where
2014    C: ClContextPtr,
2015{
2016    let mut errcode = 0;
2017
2018    let sampler = unsafe {
2019        Sampler::from_raw_create_ptr(ffi::clCreateSampler(
2020            context.as_ptr(),
2021            normalize_coords as cl_bool,
2022            addressing_mode as cl_addressing_mode,
2023            filter_mode as cl_filter_mode,
2024            &mut errcode,
2025        ))
2026    };
2027
2028    eval_errcode(errcode, sampler, "clCreateSampler", None::<String>)
2029}
2030
2031/// Increments a sampler reference counter.
2032pub unsafe fn retain_sampler(sampler: &Sampler) -> OclCoreResult<()> {
2033    eval_errcode(
2034        ffi::clRetainSampler(sampler.as_ptr()),
2035        (),
2036        "clRetainSampler",
2037        None::<String>,
2038    )
2039}
2040
2041/// Decrements a sampler reference counter.
2042pub unsafe fn release_sampler(sampler: &Sampler) -> OclCoreResult<()> {
2043    eval_errcode(
2044        ffi::clReleaseSampler(sampler.as_ptr()),
2045        (),
2046        "clReleaseSampler",
2047        None::<String>,
2048    )
2049}
2050
2051/// Returns information about the sampler object.
2052///
2053/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clGetSamplerInfo.html)
2054pub fn get_sampler_info(obj: &Sampler, request: SamplerInfo) -> OclCoreResult<SamplerInfoResult> {
2055    let mut result_size: size_t = 0;
2056
2057    let errcode = unsafe {
2058        ffi::clGetSamplerInfo(
2059            obj.as_ptr() as cl_sampler,
2060            request as cl_sampler_info,
2061            0 as size_t,
2062            ptr::null_mut(),
2063            &mut result_size as *mut size_t,
2064        )
2065    };
2066
2067    // try!(eval_errcode(errcode, result, "clGetSamplerInfo", None::<String>));
2068    // if let Err(err) = eval_errcode(errcode, (), "clGetSamplerInfo", None::<String>) {
2069    //     return SamplerInfoResult::Error(Box::new(err));
2070    // }
2071    eval_errcode(errcode, (), "clGetSamplerInfo", None::<String>)?;
2072
2073    // If result size is zero, return an empty info result directly:
2074    if result_size == 0 {
2075        return SamplerInfoResult::from_bytes(request, vec![]);
2076    }
2077
2078    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2079
2080    let errcode = unsafe {
2081        ffi::clGetSamplerInfo(
2082            obj.as_ptr() as cl_sampler,
2083            request as cl_sampler_info,
2084            result_size,
2085            result.as_mut_ptr() as *mut _ as *mut c_void,
2086            ptr::null_mut(),
2087        )
2088    };
2089
2090    let result = eval_errcode(errcode, result, "clGetSamplerInfo", None::<String>)?;
2091    SamplerInfoResult::from_bytes(request, result)
2092}
2093
2094//============================================================================
2095//========================== Program Object APIs =============================
2096//============================================================================
2097
2098/// Creates a new program.
2099pub fn create_program_with_source<C>(context: C, src_strings: &[CString]) -> OclCoreResult<Program>
2100where
2101    C: ClContextPtr,
2102{
2103    // Verify that the context is valid:
2104    verify_context(context)?;
2105
2106    // Lengths (not including \0 terminator) of each string:
2107    let ks_lens: Vec<usize> = src_strings.iter().map(|cs| cs.as_bytes().len()).collect();
2108
2109    // Pointers to each string:
2110    let kern_string_ptrs: Vec<*const _> = src_strings.iter().map(|cs| cs.as_ptr()).collect();
2111
2112    let mut errcode: cl_int = 0;
2113
2114    let program_ptr = unsafe {
2115        ffi::clCreateProgramWithSource(
2116            context.as_ptr(),
2117            kern_string_ptrs.len() as cl_uint,
2118            kern_string_ptrs.as_ptr() as *const *const _,
2119            ks_lens.as_ptr() as *const usize,
2120            &mut errcode,
2121        )
2122    };
2123
2124    eval_errcode(
2125        errcode,
2126        program_ptr,
2127        "clCreateProgramWithSource",
2128        None::<String>,
2129    )
2130    .map(|ptr| unsafe { Program::from_raw_create_ptr(ptr) })
2131}
2132
2133/// Creates a program object for a context, and loads the binary bits
2134/// specified by binary into the program object.
2135///
2136/// [SDK Docs]: https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clCreateProgramWithBinary.html
2137///
2138// [UNTESTED]
2139///
2140pub fn create_program_with_binary<C, D>(
2141    context: C,
2142    devices: &[D],
2143    binaries: &[&[u8]],
2144) -> OclCoreResult<Program>
2145where
2146    C: ClContextPtr,
2147    D: ClDeviceIdPtr,
2148{
2149    if devices.is_empty() {
2150        return Err(ApiWrapperError::CreateProgramWithBinaryDevicesLenZero.into());
2151    }
2152
2153    if devices.len() != binaries.len() {
2154        return Err(ApiWrapperError::CreateProgramWithBinaryDevicesLenMismatch.into());
2155    }
2156
2157    let lengths: Vec<usize> = binaries.iter().map(|bin| bin.len()).collect();
2158    let mut binary_status: Vec<i32> = iter::repeat(0).take(devices.len()).collect();
2159    let mut errcode: cl_int = 0;
2160
2161    let ptrs = binaries.iter().map(|bin| bin.as_ptr()).collect::<Vec<_>>();
2162
2163    let program = unsafe {
2164        ffi::clCreateProgramWithBinary(
2165            context.as_ptr(),
2166            devices.len() as u32,
2167            devices.as_ptr() as *const _ as *const cl_device_id,
2168            lengths.as_ptr(),
2169            ptrs.as_ptr() as *const *const u8,
2170            binary_status.as_mut_ptr(),
2171            &mut errcode,
2172        )
2173    };
2174
2175    eval_errcode(errcode, (), "clCreateProgramWithBinary", None::<String>)?;
2176
2177    for (i, item) in binary_status.iter().enumerate() {
2178        eval_errcode(
2179            *item,
2180            (),
2181            "clCreateProgramWithBinary",
2182            Some(format!("Device [{}]", i)),
2183        )?;
2184    }
2185
2186    unsafe { Ok(Program::from_raw_create_ptr(program)) }
2187}
2188
2189/// [UNIMPLEMENTED: Please implement me]
2190///
2191/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
2192pub fn create_program_with_built_in_kernels(
2193    device_version: Option<&OpenclVersion>,
2194) -> OclCoreResult<()> {
2195    // clCreateProgramWithBuiltInKernels(context: cl_context,
2196    //                                  num_devices: cl_uint,
2197    //                                  device_list: *const cl_device_id,
2198    //                                  kernel_names: *mut char,
2199    //                                  errcode_ret: *mut cl_int) -> cl_program;
2200    let _ = device_version;
2201    unimplemented!();
2202}
2203
2204/// Returns a new `Program` loaded with the provided IL bytes.
2205///
2206/// [Version Controlled: OpenCL 2.1+] See module docs for more info.
2207#[cfg(feature = "opencl_version_2_1")]
2208pub fn create_program_with_il<C>(
2209    context: C,
2210    il: &[u8],
2211    device_versions: Option<&[OpenclVersion]>,
2212) -> OclCoreResult<Program>
2213where
2214    C: ClContextPtr + ClVersions,
2215{
2216    verify_device_versions(
2217        device_versions,
2218        [2, 1],
2219        &context,
2220        ApiFunction::CreateProgramWithIl,
2221    )?;
2222
2223    let mut errcode: cl_int = 0;
2224
2225    let program_ptr = unsafe {
2226        ffi::clCreateProgramWithIL(
2227            context.as_ptr(),
2228            il.as_ptr() as *mut c_void,
2229            il.len(),
2230            &mut errcode,
2231        )
2232    };
2233
2234    eval_errcode(
2235        errcode,
2236        program_ptr,
2237        "clCreateProgramWithIL",
2238        None::<String>,
2239    )
2240    .map(|ptr| unsafe { Program::from_raw_create_ptr(ptr) })
2241}
2242
2243/// Increments a program reference counter.
2244pub unsafe fn retain_program(program: &Program) -> OclCoreResult<()> {
2245    eval_errcode(
2246        ffi::clRetainProgram(program.as_ptr()),
2247        (),
2248        "clRetainProgram",
2249        None::<String>,
2250    )
2251}
2252
2253/// Decrements a program reference counter.
2254pub unsafe fn release_program(program: &Program) -> OclCoreResult<()> {
2255    eval_errcode(
2256        ffi::clReleaseProgram(program.as_ptr()),
2257        (),
2258        "clReleaseKernel",
2259        None::<String>,
2260    )
2261}
2262
2263pub struct UserDataPh(usize);
2264
2265impl UserDataPh {
2266    fn unwrapped(&self) -> *mut c_void {
2267        ptr::null_mut()
2268    }
2269}
2270
2271/// Builds a program.
2272///
2273/// Callback functions are not yet supported. Please file an issue if you have
2274/// need of this functionality.
2275///
2276//
2277// [NOTE]: Despite what the spec says, some platforms segfault when `null` is
2278// passed for `devices_ptr`.
2279pub fn build_program<D: ClDeviceIdPtr>(
2280    program: &Program,
2281    devices: Option<&[D]>,
2282    options: &CString,
2283    pfn_notify: Option<BuildProgramCallbackFn>,
2284    user_data: Option<Box<UserDataPh>>,
2285) -> OclCoreResult<()> {
2286    assert!(
2287        pfn_notify.is_none() && user_data.is_none(),
2288        "ocl::core::build_program(): Callback functions not yet implemented."
2289    );
2290
2291    let device_ptrs = DevicePtrList::from(devices);
2292
2293    let user_data = match user_data {
2294        Some(ud) => ud.unwrapped(),
2295        None => ptr::null_mut(),
2296    };
2297
2298    let errcode = unsafe {
2299        ffi::clBuildProgram(
2300            program.as_ptr() as cl_program,
2301            device_ptrs.num(),
2302            device_ptrs.as_ptr(),
2303            options.as_ptr(),
2304            pfn_notify,
2305            user_data,
2306        )
2307    };
2308
2309    if errcode == Status::CL_BUILD_PROGRAM_FAILURE as i32 {
2310        if let Some(ds) = devices {
2311            program_build_err(program, ds).map_err(|err| err.into())
2312        } else {
2313            let ds = program.devices()?;
2314            program_build_err(program, &ds).map_err(|err| err.into())
2315        }
2316    } else {
2317        eval_errcode(errcode, (), "clBuildProgram", None::<String>)
2318    }
2319}
2320
2321/// Compiles a program’s source for all the devices or a specific device(s) in
2322/// the OpenCL context associated with program.
2323///
2324/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
2325#[cfg(feature = "opencl_version_1_2")]
2326pub fn compile_program<D: ClDeviceIdPtr>(
2327    program: &Program,
2328    devices: Option<&[D]>,
2329    options: &CString,
2330    input_headers: &[&Program],
2331    header_include_names: &[CString],
2332    pfn_notify: Option<BuildProgramCallbackFn>,
2333    user_data: Option<Box<UserDataPh>>,
2334    device_versions: Option<&[OpenclVersion]>,
2335) -> OclCoreResult<()> {
2336    verify_device_versions(
2337        device_versions,
2338        [1, 2],
2339        program,
2340        ApiFunction::CompileProgram,
2341    )?;
2342
2343    assert!(
2344        pfn_notify.is_none() && user_data.is_none(),
2345        "ocl::core::compile_program(): Callback functions not yet implemented."
2346    );
2347
2348    assert!(input_headers.len() == header_include_names.len(),
2349        "ocl::core::compile_program(): Length of input_headers and header_include_names should be equal.");
2350
2351    let device_ptrs = DevicePtrList::new(devices);
2352
2353    let input_hdrs_ptrs: Vec<_> = input_headers.iter().map(|cs| cs.as_ptr()).collect();
2354    let hdrs_names_ptrs: Vec<*const _> =
2355        header_include_names.iter().map(|cs| cs.as_ptr()).collect();
2356
2357    // Will crash if ptrs is not NULL, but len is 0
2358    let (input_ptr, names_ptr) = if input_headers.is_empty() {
2359        (ptr::null(), ptr::null())
2360    } else {
2361        (input_hdrs_ptrs.as_ptr(), hdrs_names_ptrs.as_ptr())
2362    };
2363
2364    let user_data = match user_data {
2365        Some(ud) => ud.unwrapped(),
2366        None => ptr::null_mut(),
2367    };
2368
2369    let errcode = unsafe {
2370        ffi::clCompileProgram(
2371            program.as_ptr() as cl_program,
2372            device_ptrs.num(),
2373            device_ptrs.as_ptr(),
2374            options.as_ptr(),
2375            input_hdrs_ptrs.len() as cl_uint,
2376            input_ptr as *const cl_program,
2377            names_ptr as *const *const _,
2378            pfn_notify,
2379            user_data,
2380        )
2381    };
2382
2383    if errcode == Status::CL_COMPILE_PROGRAM_FAILURE as i32 {
2384        if let Some(ds) = devices {
2385            program_build_err(program, ds).map_err(|err| err.into())
2386        } else {
2387            let ds = program.devices()?;
2388            program_build_err(program, &ds).map_err(|err| err.into())
2389        }
2390    } else {
2391        eval_errcode(errcode, (), "clCompileProgram", None::<String>)
2392    }
2393}
2394
2395/// Links a set of compiled program objects and libraries for all the devices
2396/// or a specific device(s) in the OpenCL context and creates an executable.
2397///
2398/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
2399#[cfg(feature = "opencl_version_1_2")]
2400pub fn link_program<D: ClDeviceIdPtr, C: ClContextPtr>(
2401    context: C,
2402    devices: Option<&[D]>,
2403    options: &CString,
2404    input_programs: &[&Program],
2405    pfn_notify: Option<BuildProgramCallbackFn>,
2406    user_data: Option<Box<UserDataPh>>,
2407    device_versions: Option<&[OpenclVersion]>,
2408) -> OclCoreResult<Program> {
2409    verify_context(context)?;
2410    verify_device_versions(
2411        device_versions,
2412        [1, 2],
2413        &context.as_ptr(),
2414        ApiFunction::LinkProgram,
2415    )?;
2416
2417    assert!(
2418        pfn_notify.is_none() && user_data.is_none(),
2419        "ocl::core::link_program(): Callback functions not yet implemented."
2420    );
2421
2422    let device_ptrs = DevicePtrList::new(devices);
2423
2424    let input_programs_ptrs: Vec<_> = input_programs.iter().map(|cs| cs.as_ptr()).collect();
2425
2426    let user_data = match user_data {
2427        Some(ud) => ud.unwrapped(),
2428        None => ptr::null_mut(),
2429    };
2430
2431    let mut errcode: cl_int = 0;
2432
2433    let program_ptr = unsafe {
2434        ffi::clLinkProgram(
2435            context.as_ptr(),
2436            device_ptrs.num(),
2437            device_ptrs.as_ptr(),
2438            options.as_ptr(),
2439            input_programs_ptrs.len() as cl_uint,
2440            input_programs_ptrs.as_ptr() as *const cl_program,
2441            pfn_notify,
2442            user_data,
2443            &mut errcode,
2444        )
2445    };
2446
2447    eval_errcode(errcode, program_ptr, "clLinkProgram", None::<String>)
2448        .map(|ptr| unsafe { Program::from_raw_create_ptr(ptr) })
2449}
2450
2451// [DISABLED DUE TO PLATFORM INCOMPATABILITY]
2452// /// [UNTESTED]
2453// /// Unloads a platform compiler.
2454// ///
2455// /// [Version Controlled: OpenCL 1.2+] See module docs for more info.
2456// pub fn unload_platform_compiler(platform: &PlatformId,
2457//          device_version: Option<&OpenclVersion>) -> OclCoreResult<()> {
2458//     unsafe { eval_errcode("clUnloadPlatformCompiler", "",
2459//         ffi::clUnloadPlatformCompiler(platform.as_ptr())) }
2460// }
2461
2462fn get_program_info_raw(program: &Program, request: ProgramInfo) -> OclCoreResult<Vec<u8>> {
2463    let mut result_size: size_t = 0;
2464
2465    let errcode = unsafe {
2466        ffi::clGetProgramInfo(
2467            program.as_ptr() as cl_program,
2468            request as cl_program_info,
2469            0 as size_t,
2470            ptr::null_mut(),
2471            &mut result_size as *mut size_t,
2472        )
2473    };
2474
2475    eval_errcode(errcode, (), "clGetProgramInfo", None::<String>)?;
2476
2477    // If result size is zero, return an empty result directly:
2478    if result_size == 0 {
2479        return Ok(vec![]);
2480    }
2481
2482    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2483
2484    let errcode = unsafe {
2485        ffi::clGetProgramInfo(
2486            program.as_ptr() as cl_program,
2487            request as cl_program_info,
2488            result_size,
2489            result.as_mut_ptr() as *mut _ as *mut c_void,
2490            ptr::null_mut(),
2491        )
2492    };
2493
2494    eval_errcode(errcode, result, "clGetProgramInfo", None::<String>)
2495}
2496
2497/// Returns a `Vec` containing one `Vec<u8>` for each device associated with
2498/// `program`.
2499fn get_program_info_binaries(program: &Program) -> OclCoreResult<Vec<Vec<u8>>> {
2500    let binary_sizes_raw = get_program_info_raw(program, ProgramInfo::BinarySizes)?;
2501    let binary_sizes = unsafe { crate::util::bytes_into_vec::<usize>(binary_sizes_raw)? };
2502
2503    let binaries = binary_sizes
2504        .into_iter()
2505        .map(|size| vec![0u8; size])
2506        .collect::<Vec<Vec<u8>>>();
2507
2508    let mut binary_ptrs = binaries.iter().map(|vec| vec.as_ptr()).collect::<Vec<_>>();
2509
2510    let errcode = unsafe {
2511        ffi::clGetProgramInfo(
2512            program.as_ptr() as cl_program,
2513            ProgramInfo::Binaries as cl_program_info,
2514            mem::size_of::<*mut c_void>() * binary_ptrs.len(),
2515            binary_ptrs.as_mut_ptr() as *mut _ as *mut c_void,
2516            ptr::null_mut(),
2517        )
2518    };
2519
2520    eval_errcode(errcode, binaries, "clGetProgramInfo", None::<String>)
2521}
2522
2523/// Get program info.
2524pub fn get_program_info(
2525    program: &Program,
2526    request: ProgramInfo,
2527) -> OclCoreResult<ProgramInfoResult> {
2528    match request {
2529        ProgramInfo::Binaries => {
2530            get_program_info_binaries(program).map(ProgramInfoResult::Binaries)
2531        }
2532        _ => {
2533            let result = get_program_info_raw(program, request)?;
2534            ProgramInfoResult::from_bytes(request, result)
2535        }
2536    }
2537}
2538
2539/// Get program build info.
2540pub fn get_program_build_info<D: ClDeviceIdPtr + fmt::Debug>(
2541    obj: &Program,
2542    device_obj: D,
2543    request: ProgramBuildInfo,
2544) -> OclCoreResult<ProgramBuildInfoResult> {
2545    let mut result_size: size_t = 0;
2546
2547    let errcode = unsafe {
2548        ffi::clGetProgramBuildInfo(
2549            obj.as_ptr() as cl_program,
2550            device_obj.as_ptr() as cl_device_id,
2551            request as cl_program_build_info,
2552            0 as size_t,
2553            ptr::null_mut(),
2554            &mut result_size as *mut size_t,
2555        )
2556    };
2557
2558    eval_errcode(errcode, (), "clGetProgramBuildInfo", None::<String>)?;
2559
2560    // If result size is zero, return an empty info result directly:
2561    if result_size == 0 {
2562        return ProgramBuildInfoResult::from_bytes(request, vec![]);
2563    }
2564
2565    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2566
2567    let errcode = unsafe {
2568        ffi::clGetProgramBuildInfo(
2569            obj.as_ptr() as cl_program,
2570            device_obj.as_ptr() as cl_device_id,
2571            request as cl_program_build_info,
2572            result_size as size_t,
2573            result.as_mut_ptr() as *mut _ as *mut c_void,
2574            ptr::null_mut(),
2575        )
2576    };
2577
2578    let result = eval_errcode(errcode, result, "clGetProgramBuildInfo", None::<String>)?;
2579    ProgramBuildInfoResult::from_bytes(request, result)
2580}
2581
2582//============================================================================
2583//========================== Kernel Object APIs ==============================
2584//============================================================================
2585
2586/// Returns a new kernel.
2587pub fn create_kernel<S: AsRef<str>>(program: &Program, name: S) -> OclCoreResult<Kernel> {
2588    let mut err: cl_int = 0;
2589
2590    unsafe {
2591        let kernel_ptr = ffi::clCreateKernel(
2592            program.as_ptr(),
2593            CString::new(name.as_ref().as_bytes())?.as_ptr(),
2594            &mut err,
2595        );
2596
2597        eval_errcode(err, kernel_ptr, "clCreateKernel", Some(name.as_ref()))
2598            .map(|ptr| Kernel::from_raw_create_ptr(ptr))
2599    }
2600}
2601
2602/// [UNIMPLEMENTED: Please implement me]
2603pub fn create_kernels_in_program() -> OclCoreResult<()> {
2604    // ffi::clCreateKernelsInProgram(program: cl_program,
2605    //                             num_kernels: cl_uint,
2606    //                             kernels: *mut cl_kernel,
2607    //                             num_kernels_ret: *mut cl_uint) -> cl_int;
2608    unimplemented!();
2609}
2610
2611/// Increments a kernel reference counter.
2612pub unsafe fn retain_kernel(kernel: &Kernel) -> OclCoreResult<()> {
2613    eval_errcode(
2614        ffi::clRetainKernel(kernel.as_ptr()),
2615        (),
2616        "clRetainKernel",
2617        None::<String>,
2618    )
2619}
2620
2621/// Decrements a kernel reference counter.
2622pub unsafe fn release_kernel(kernel: &Kernel) -> OclCoreResult<()> {
2623    eval_errcode(
2624        ffi::clReleaseKernel(kernel.as_ptr()),
2625        (),
2626        "clReleaseKernel",
2627        None::<String>,
2628    )
2629}
2630
2631/// Sets the argument value for the kernel argument at `index`.
2632///
2633/// ### Example:
2634///
2635/// ```rust, ignore
2636/// let kernel = core::create_kernel(&program, "multiply")?;
2637/// core::set_kernel_arg(&kernel, 0, ArgVal::scalar(&10.0f32))?;
2638/// core::set_kernel_arg(&kernel, 1, ArgVal::mem(&buffer))?;
2639/// ```
2640///
2641/// [SDK Documentation](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clSetKernelArg.html)
2642pub fn set_kernel_arg(kernel: &Kernel, index: u32, arg_val: ArgVal) -> OclCoreResult<()> {
2643    let (size, value) = arg_val.as_raw();
2644
2645    let err = unsafe { ffi::clSetKernelArg(kernel.as_ptr(), index, size, value) };
2646
2647    if err != Status::CL_SUCCESS as i32 {
2648        let name = get_kernel_name(kernel)?;
2649        eval_errcode(err, (), "clSetKernelArg", Some(name))
2650    } else {
2651        Ok(())
2652    }
2653}
2654
2655/// Get kernel info.
2656pub fn get_kernel_info(obj: &Kernel, request: KernelInfo) -> OclCoreResult<KernelInfoResult> {
2657    let mut result_size: size_t = 0;
2658
2659    let errcode = unsafe {
2660        ffi::clGetKernelInfo(
2661            obj.as_ptr() as cl_kernel,
2662            request as cl_kernel_info,
2663            0 as size_t,
2664            ptr::null_mut(),
2665            &mut result_size as *mut size_t,
2666        )
2667    };
2668
2669    eval_errcode(errcode, (), "clGetKernelInfo", None::<String>)?;
2670
2671    // If result size is zero, return an empty info result directly:
2672    if result_size == 0 {
2673        return KernelInfoResult::from_bytes(request, vec![]);
2674    }
2675
2676    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2677
2678    let errcode = unsafe {
2679        ffi::clGetKernelInfo(
2680            obj.as_ptr() as cl_kernel,
2681            request as cl_kernel_info,
2682            result_size,
2683            result.as_mut_ptr() as *mut _ as *mut c_void,
2684            ptr::null_mut(),
2685        )
2686    };
2687
2688    let result = eval_errcode(errcode, result, "clGetKernelInfo", None::<String>)?;
2689    KernelInfoResult::from_bytes(request, result)
2690}
2691
2692/// Get kernel arg info.
2693///
2694/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
2695pub fn get_kernel_arg_info(
2696    obj: &Kernel,
2697    arg_index: u32,
2698    request: KernelArgInfo,
2699    device_versions: Option<&[OpenclVersion]>,
2700) -> OclCoreResult<KernelArgInfoResult> {
2701    // Verify device version:
2702    // if let Err(err) = verify_device_versions(device_versions, [1, 2], obj,
2703    //         ApiFunction::GetKernelArgInfo) {
2704    //     return Err(OclCoreError::from(err));
2705    // }
2706
2707    // Verify device version:
2708    verify_device_versions(device_versions, [1, 2], obj, ApiFunction::GetKernelArgInfo)?;
2709
2710    let mut result_size: size_t = 0;
2711
2712    let errcode = unsafe {
2713        ffi::clGetKernelArgInfo(
2714            obj.as_ptr() as cl_kernel,
2715            arg_index as cl_uint,
2716            request as cl_kernel_arg_info,
2717            0 as size_t,
2718            ptr::null_mut(),
2719            &mut result_size as *mut size_t,
2720        )
2721    };
2722
2723    // try!(eval_errcode(errcode, result, "clGetKernelArgInfo", None::<String>));
2724    // if let Err(err) = eval_errcode(errcode, (), "clGetKernelArgInfo", None::<String>) {
2725    //     return KernelArgInfoResult::from(err);
2726    // }
2727    eval_errcode(errcode, (), "clGetKernelArgInfo", None::<String>)?;
2728
2729    // If result size is zero, return an empty info result directly:
2730    if result_size == 0 {
2731        return KernelArgInfoResult::from_bytes(request, vec![]);
2732    }
2733
2734    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2735
2736    let errcode = unsafe {
2737        ffi::clGetKernelArgInfo(
2738            obj.as_ptr() as cl_kernel,
2739            arg_index as cl_uint,
2740            request as cl_kernel_arg_info,
2741            result_size,
2742            result.as_mut_ptr() as *mut _ as *mut c_void,
2743            ptr::null_mut(),
2744        )
2745    };
2746
2747    let result = eval_errcode(errcode, result, "clGetKernelArgInfo", None::<String>)?;
2748    KernelArgInfoResult::from_bytes(request, result)
2749}
2750
2751/// Get kernel work group info.
2752pub fn get_kernel_work_group_info<D: ClDeviceIdPtr>(
2753    obj: &Kernel,
2754    device_obj: D,
2755    request: KernelWorkGroupInfo,
2756) -> OclCoreResult<KernelWorkGroupInfoResult> {
2757    let mut result_size: size_t = 0;
2758
2759    let errcode = unsafe {
2760        ffi::clGetKernelWorkGroupInfo(
2761            obj.as_ptr() as cl_kernel,
2762            device_obj.as_ptr() as cl_device_id,
2763            request as cl_kernel_work_group_info,
2764            0 as size_t,
2765            ptr::null_mut(),
2766            &mut result_size as *mut size_t,
2767        )
2768    };
2769
2770    // Make printing certain platform-specific errors less scary looking:
2771    if let Err(err) = eval_errcode(errcode, (), "clGetKernelWorkGroupInfo", None::<String>) {
2772        if let Some(status) = err.api_status() {
2773            // NVIDIA / APPLE (i think):
2774            if request == KernelWorkGroupInfo::GlobalWorkSize && status == Status::CL_INVALID_VALUE
2775            {
2776                return Ok(KernelWorkGroupInfoResult::CustomBuiltinOnly);
2777            }
2778
2779            // APPLE (bleh):
2780            if status == Status::CL_INVALID_DEVICE {
2781                return Ok(KernelWorkGroupInfoResult::Unavailable(status));
2782            }
2783        }
2784
2785        return Err(err);
2786    }
2787
2788    // If result size is zero, return an empty info result directly:
2789    if result_size == 0 {
2790        return KernelWorkGroupInfoResult::from_bytes(request, vec![]);
2791    }
2792
2793    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2794
2795    let errcode = unsafe {
2796        ffi::clGetKernelWorkGroupInfo(
2797            obj.as_ptr() as cl_kernel,
2798            device_obj.as_ptr() as cl_device_id,
2799            request as cl_kernel_work_group_info,
2800            result_size,
2801            result.as_mut_ptr() as *mut _ as *mut c_void,
2802            ptr::null_mut(),
2803        )
2804    };
2805
2806    let result = eval_errcode(errcode, result, "clGetKernelWorkGroupInfo", None::<String>)?;
2807    KernelWorkGroupInfoResult::from_bytes(request, result)
2808}
2809
2810//============================================================================
2811//========================== Event Object APIs ===============================
2812//============================================================================
2813
2814/// Blocks until the first `num_events` events in `event_list` are complete.
2815pub fn wait_for_events(num_events: u32, event_list: &dyn ClWaitListPtr) -> OclCoreResult<()> {
2816    assert!(event_list.count() >= num_events);
2817
2818    let errcode = unsafe { ffi::clWaitForEvents(num_events, event_list.as_ptr_ptr()) };
2819
2820    eval_errcode(errcode, (), "clWaitForEvents", None::<String>)
2821}
2822
2823/// Get event info.
2824pub fn get_event_info<'e, E: ClEventPtrRef<'e>>(
2825    event: &'e E,
2826    request: EventInfo,
2827) -> OclCoreResult<EventInfoResult> {
2828    let mut result_size: size_t = 0;
2829
2830    let errcode = unsafe {
2831        ffi::clGetEventInfo(
2832            *event.as_ptr_ref(),
2833            request as cl_event_info,
2834            0 as size_t,
2835            ptr::null_mut(),
2836            &mut result_size as *mut size_t,
2837        )
2838    };
2839
2840    eval_errcode(errcode, (), "clGetEventInfo", None::<String>)?;
2841
2842    // If result size is zero, return an empty info result directly:
2843    if result_size == 0 {
2844        return EventInfoResult::from_bytes(request, vec![]);
2845    }
2846
2847    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
2848
2849    let errcode = unsafe {
2850        ffi::clGetEventInfo(
2851            *event.as_ptr_ref(),
2852            request as cl_event_info,
2853            result_size,
2854            result.as_mut_ptr() as *mut _ as *mut c_void,
2855            ptr::null_mut(),
2856        )
2857    };
2858
2859    let result = eval_errcode(errcode, result, "clGetEventInfo", None::<String>)?;
2860    EventInfoResult::from_bytes(request, result)
2861}
2862
2863/// Creates an event not already associated with any command.
2864pub fn create_user_event<C>(context: C) -> OclCoreResult<Event>
2865where
2866    C: ClContextPtr,
2867{
2868    let mut errcode = 0;
2869    let event = unsafe {
2870        Event::from_raw_create_ptr(ffi::clCreateUserEvent(context.as_ptr(), &mut errcode))
2871    };
2872    eval_errcode(errcode, event, "clCreateUserEvent", None::<String>)
2873}
2874
2875/// Increments an event's reference counter.
2876pub unsafe fn retain_event<'e, E: ClEventPtrRef<'e>>(event: &'e E) -> OclCoreResult<()> {
2877    eval_errcode(
2878        ffi::clRetainEvent(*event.as_ptr_ref()),
2879        (),
2880        "clRetainEvent",
2881        None::<String>,
2882    )
2883}
2884
2885/// Decrements an event's reference counter.
2886pub unsafe fn release_event<'e, E: ClEventPtrRef<'e>>(event: &'e E) -> OclCoreResult<()> {
2887    eval_errcode(
2888        ffi::clReleaseEvent(*event.as_ptr_ref()),
2889        (),
2890        "clReleaseEvent",
2891        None::<String>,
2892    )
2893}
2894
2895/// Updates a user events status.
2896///
2897/// Setting status to completion will cause commands waiting upon this event
2898/// to execute.
2899///
2900/// Will return an error if the event is not a 'user' event (created with
2901/// `::create_user_event`).
2902///
2903/// Valid options are (for OpenCL versions 1.1 - 2.1):
2904///
2905/// `CommandExecutionStatus::Complete`
2906/// `CommandExecutionStatus::Running`
2907/// `CommandExecutionStatus::Submitted`
2908/// `CommandExecutionStatus::Queued`
2909///
2910/// To the best of the author's knowledge, the only variant that matters is
2911/// `::Complete`. Everything else is functionally equivalent and is useful
2912/// only for debugging or profiling purposes (this may change in the future).
2913///
2914pub fn set_user_event_status<'e, E: ClEventPtrRef<'e>>(
2915    event: &'e E,
2916    execution_status: CommandExecutionStatus,
2917) -> OclCoreResult<()> {
2918    unsafe {
2919        #[cfg(feature = "event_debug_print")]
2920        println!(
2921            "::set_user_event_status: Setting user event status for event: {:?}",
2922            *event.as_ptr_ref()
2923        );
2924
2925        eval_errcode(
2926            ffi::clSetUserEventStatus(*event.as_ptr_ref(), execution_status as cl_int),
2927            (),
2928            "clSetUserEventStatus",
2929            None::<String>,
2930        )
2931    }
2932}
2933
2934/// Sets a callback function which is called as soon as the `callback_trigger`
2935/// status is reached.
2936///
2937/// ## Safety
2938///
2939///
2940///
2941pub unsafe fn set_event_callback<'e, E: ClEventPtrRef<'e>>(
2942    event: &'e E,
2943    callback_trigger: CommandExecutionStatus,
2944    callback_receiver: Option<EventCallbackFn>,
2945    user_data: *mut c_void,
2946) -> OclCoreResult<()> {
2947    eval_errcode(
2948        ffi::clSetEventCallback(
2949            *event.as_ptr_ref(),
2950            callback_trigger as cl_int,
2951            callback_receiver,
2952            user_data,
2953        ),
2954        (),
2955        "clSetEventCallback",
2956        None::<String>,
2957    )
2958}
2959
2960//============================================================================
2961//============================ Profiling APIs ================================
2962//============================================================================
2963
2964/// Get event profiling info (for debugging / benchmarking).
2965pub fn get_event_profiling_info<'e, E: ClEventPtrRef<'e>>(
2966    event: &'e E,
2967    request: ProfilingInfo,
2968) -> OclCoreResult<ProfilingInfoResult> {
2969    // Apple compatibile value:
2970    let max_result_size_bytes = 8;
2971    let mut result_size: size_t = 0;
2972    let event: cl_event = unsafe { *event.as_ptr_ref() };
2973
2974    let errcode = unsafe {
2975        ffi::clGetEventProfilingInfo(
2976            event,
2977            request as cl_profiling_info,
2978            max_result_size_bytes,
2979            ptr::null_mut(),
2980            &mut result_size as *mut size_t,
2981        )
2982    };
2983
2984    // Make sure our assumption about the maximum value was correct:
2985    assert!(result_size <= max_result_size_bytes);
2986
2987    // Don't generate a full error report for `CL_INVALID_VALUE` it just means
2988    // that event profiling info is not available on this platform.
2989    if errcode < 0 && Status::from_i32(errcode).unwrap() == Status::CL_INVALID_VALUE {
2990        return Err(OclCoreError::String(
2991            "<unavailable (CL_INVALID_VALUE)>".to_string(),
2992        ));
2993    }
2994
2995    eval_errcode(errcode, (), "clGetEventProfilingInfo", None::<String>)?;
2996
2997    // If result size is zero, return an empty info result directly:
2998    if result_size == 0 {
2999        return ProfilingInfoResult::from_bytes(request, vec![]);
3000    }
3001
3002    let mut result: Vec<u8> = iter::repeat(0u8).take(result_size).collect();
3003
3004    let errcode = unsafe {
3005        ffi::clGetEventProfilingInfo(
3006            event,
3007            request as cl_profiling_info,
3008            result_size,
3009            result.as_mut_ptr() as *mut _ as *mut c_void,
3010            ptr::null_mut(),
3011        )
3012    };
3013
3014    let result = eval_errcode(errcode, result, "clGetEventProfilingInfo", None::<String>)?;
3015    ProfilingInfoResult::from_bytes(request, result)
3016}
3017
3018//============================================================================
3019//========================= Flush and Finish APIs ============================
3020//============================================================================
3021
3022/// Flushes a command queue.
3023///
3024/// Issues all previously queued OpenCL commands in a command-queue to the
3025/// device associated with the command-queue.
3026pub fn flush(command_queue: &CommandQueue) -> OclCoreResult<()> {
3027    unsafe {
3028        eval_errcode(
3029            ffi::clFlush(command_queue.as_ptr()),
3030            (),
3031            "clFlush",
3032            None::<String>,
3033        )
3034    }
3035}
3036
3037/// Waits for a queue to finish.
3038///
3039/// Blocks until all previously queued OpenCL commands in a command-queue are
3040/// issued to the associated device and have completed.
3041pub fn finish(command_queue: &CommandQueue) -> OclCoreResult<()> {
3042    unsafe {
3043        let errcode = ffi::clFinish(command_queue.as_ptr());
3044        eval_errcode(errcode, (), "clFinish", None::<String>)
3045    }
3046}
3047
3048//============================================================================
3049//======================= Enqueued Commands APIs =============================
3050//============================================================================
3051
3052/// Enqueues a read from device memory referred to by `buffer` to device memory,
3053/// `data`.
3054///
3055/// ## Safety
3056///
3057/// Caller must ensure that `data` lives until the read is complete. Use
3058/// `new_event` to monitor it (use [`core::EventList::last_clone`] if passing
3059/// an event list as `new_event`).
3060///
3061///
3062/// [`core::EventList::get_clone`]: struct.EventList.html#method.last_clone
3063///
3064pub unsafe fn enqueue_read_buffer<T, M, En, Ewl>(
3065    command_queue: &CommandQueue,
3066    buffer: M,
3067    block: bool,
3068    offset: usize,
3069    data: &mut [T],
3070    wait_list: Option<Ewl>,
3071    new_event: Option<En>,
3072) -> OclCoreResult<()>
3073where
3074    T: OclPrm,
3075    En: ClNullEventPtr,
3076    Ewl: ClWaitListPtr,
3077    M: AsMem<T> + MemCmdRw,
3078{
3079    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3080
3081    let offset_bytes = offset * mem::size_of::<T>();
3082
3083    let errcode = ffi::clEnqueueReadBuffer(
3084        command_queue.as_ptr(),
3085        buffer.as_mem().as_ptr(),
3086        block as cl_uint,
3087        offset_bytes,
3088        (data.len() * mem::size_of::<T>()) as size_t,
3089        data.as_ptr() as cl_mem,
3090        wait_list_len,
3091        wait_list_ptr,
3092        new_event_ptr,
3093    );
3094
3095    eval_errcode(errcode, (), "clEnqueueReadBuffer", None::<String>)
3096}
3097
3098/// Enqueues a command to read from a rectangular region from a buffer object to host memory.
3099///
3100/// ## Safety
3101///
3102/// Caller must ensure that `data` lives until the read is complete. Use
3103/// `new_event` to monitor it (use [`core::EventList::last_clone`] if passing
3104/// an event list as `new_event`).
3105///
3106/// ## Official Documentation
3107///
3108/// [SDK - clEnqueueReadBufferRect](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueReadBufferRect.html)
3109///
3110///
3111/// [`core::EventList::get_clone`]: struct.EventList.html#method.last_clone
3112///
3113pub unsafe fn enqueue_read_buffer_rect<T, M, En, Ewl>(
3114    command_queue: &CommandQueue,
3115    buffer: M,
3116    block: bool,
3117    buffer_origin: [usize; 3],
3118    host_origin: [usize; 3],
3119    region: [usize; 3],
3120    buffer_row_pitch_bytes: usize,
3121    buffer_slc_pitch_bytes: usize,
3122    host_row_pitch_bytes: usize,
3123    host_slc_pitch_bytes: usize,
3124    data: &mut [T],
3125    wait_list: Option<Ewl>,
3126    new_event: Option<En>,
3127) -> OclCoreResult<()>
3128where
3129    T: OclPrm,
3130    En: ClNullEventPtr,
3131    Ewl: ClWaitListPtr,
3132    M: AsMem<T> + MemCmdRw,
3133{
3134    let buffer_origin_bytes = [
3135        buffer_origin[0] * mem::size_of::<T>(),
3136        buffer_origin[1],
3137        buffer_origin[2],
3138    ];
3139    let host_origin_bytes = [
3140        host_origin[0] * mem::size_of::<T>(),
3141        host_origin[1],
3142        host_origin[2],
3143    ];
3144    let region_bytes = [region[0] * mem::size_of::<T>(), region[1], region[2]];
3145
3146    // DEBUG:
3147    if false {
3148        println!(
3149            "buffer_origin_bytes: {:?}, host_origin_bytes: {:?}, region_bytes: {:?}",
3150            buffer_origin_bytes, host_origin_bytes, region_bytes
3151        );
3152        println!(
3153            "buffer_row_pitch_bytes: {}, buffer_slc_pitch_bytes: {}, \
3154            host_row_pitch_bytes: {}, host_slc_pitch_bytes: {}",
3155            buffer_row_pitch_bytes,
3156            buffer_slc_pitch_bytes,
3157            host_row_pitch_bytes,
3158            host_slc_pitch_bytes
3159        );
3160    }
3161
3162    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3163
3164    let errcode = ffi::clEnqueueReadBufferRect(
3165        command_queue.as_ptr(),
3166        buffer.as_mem().as_ptr(),
3167        block as cl_uint,
3168        &buffer_origin_bytes as *const _ as *const usize,
3169        &host_origin_bytes as *const _ as *const usize,
3170        &region_bytes as *const _ as *const usize,
3171        buffer_row_pitch_bytes,
3172        buffer_slc_pitch_bytes,
3173        host_row_pitch_bytes,
3174        host_slc_pitch_bytes,
3175        data.as_ptr() as cl_mem,
3176        wait_list_len,
3177        wait_list_ptr,
3178        new_event_ptr,
3179    );
3180
3181    eval_errcode(errcode, (), "clEnqueueReadBufferRect", None::<String>)
3182}
3183
3184/// Enqueues a write from host memory, `data`, to device memory referred to by
3185/// `buffer`.
3186///
3187/// ## Safety
3188///
3189/// Caller must ensure that `data` lives until the read is complete. Use
3190/// `new_event` to monitor it (use [`core::EventList::last_clone`] if passing
3191/// an event list as `new_event`).
3192///
3193pub unsafe fn enqueue_write_buffer<T, M, En, Ewl>(
3194    command_queue: &CommandQueue,
3195    buffer: M,
3196    block: bool,
3197    offset: usize,
3198    data: &[T],
3199    wait_list: Option<Ewl>,
3200    new_event: Option<En>,
3201) -> OclCoreResult<()>
3202where
3203    T: OclPrm,
3204    En: ClNullEventPtr,
3205    Ewl: ClWaitListPtr,
3206    M: AsMem<T> + MemCmdRw,
3207{
3208    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3209
3210    let offset_bytes = offset * mem::size_of::<T>();
3211
3212    let errcode = ffi::clEnqueueWriteBuffer(
3213        command_queue.as_ptr(),
3214        buffer.as_mem().as_ptr(),
3215        block as cl_uint,
3216        offset_bytes,
3217        (data.len() * mem::size_of::<T>()) as size_t,
3218        data.as_ptr() as cl_mem,
3219        wait_list_len,
3220        wait_list_ptr,
3221        new_event_ptr,
3222    );
3223    eval_errcode(errcode, (), "clEnqueueWriteBuffer", None::<String>)
3224}
3225
3226/// Enqueues a command to write from a rectangular region from host memory to a buffer object.
3227///
3228/// ## Safety
3229///
3230/// Caller must ensure that `data` lives until the read is complete. Use
3231/// `new_event` to monitor it (use [`core::EventList::last_clone`] if passing
3232/// an event list as `new_event`).
3233///
3234/// ## Official Documentation
3235///
3236/// [SDK - clEnqueueWriteBufferRect]
3237///
3238/// [SDK - clEnqueueWriteBufferRect]: https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueWriteBufferRect.html
3239///
3240pub unsafe fn enqueue_write_buffer_rect<T, M, En, Ewl>(
3241    command_queue: &CommandQueue,
3242    buffer: M,
3243    block: bool,
3244    buffer_origin: [usize; 3],
3245    host_origin: [usize; 3],
3246    region: [usize; 3],
3247    buffer_row_pitch_bytes: usize,
3248    buffer_slc_pitch_bytes: usize,
3249    host_row_pitch_bytes: usize,
3250    host_slc_pitch_bytes: usize,
3251    data: &[T],
3252    wait_list: Option<Ewl>,
3253    new_event: Option<En>,
3254) -> OclCoreResult<()>
3255where
3256    T: OclPrm,
3257    En: ClNullEventPtr,
3258    Ewl: ClWaitListPtr,
3259    M: AsMem<T> + MemCmdRw,
3260{
3261    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3262
3263    let buffer_origin_bytes = [
3264        buffer_origin[0] * mem::size_of::<T>(),
3265        buffer_origin[1],
3266        buffer_origin[2],
3267    ];
3268    let host_origin_bytes = [
3269        host_origin[0] * mem::size_of::<T>(),
3270        host_origin[1],
3271        host_origin[2],
3272    ];
3273    let region_bytes = [region[0] * mem::size_of::<T>(), region[1], region[2]];
3274
3275    let errcode = ffi::clEnqueueWriteBufferRect(
3276        command_queue.as_ptr(),
3277        buffer.as_mem().as_ptr(),
3278        block as cl_uint,
3279        &buffer_origin_bytes as *const _ as *const usize,
3280        &host_origin_bytes as *const _ as *const usize,
3281        &region_bytes as *const _ as *const usize,
3282        buffer_row_pitch_bytes,
3283        buffer_slc_pitch_bytes,
3284        host_row_pitch_bytes,
3285        host_slc_pitch_bytes,
3286        data.as_ptr() as cl_mem,
3287        wait_list_len,
3288        wait_list_ptr,
3289        new_event_ptr,
3290    );
3291    eval_errcode(errcode, (), "clEnqueueWriteBufferRect", None::<String>)
3292}
3293
3294/// Enqueues a command to fill a buffer object with a pattern of a given pattern size.
3295///
3296/// ## Pattern (from [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueFillBuffer.html))
3297///
3298/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
3299#[cfg(not(feature = "opencl_vendor_mesa"))]
3300pub fn enqueue_fill_buffer<T, M, En, Ewl>(
3301    command_queue: &CommandQueue,
3302    buffer: M,
3303    pattern: T,
3304    offset: usize,
3305    len: usize,
3306    wait_list: Option<Ewl>,
3307    new_event: Option<En>,
3308    device_version: Option<&OpenclVersion>,
3309) -> OclCoreResult<()>
3310where
3311    T: OclPrm,
3312    En: ClNullEventPtr,
3313    Ewl: ClWaitListPtr,
3314    M: AsMem<T> + MemCmdRw,
3315{
3316    verify_device_version(
3317        device_version,
3318        [1, 2],
3319        command_queue,
3320        ApiFunction::EnqueueFillBuffer,
3321    )?;
3322
3323    let pattern_size = mem::size_of::<T>();
3324    let offset_bytes = offset * mem::size_of::<T>();
3325    let size_bytes = len * mem::size_of::<T>();
3326
3327    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3328
3329    let errcode = unsafe {
3330        ffi::clEnqueueFillBuffer(
3331            command_queue.as_ptr(),
3332            buffer.as_mem().as_ptr(),
3333            &pattern as *const _ as *const c_void,
3334            pattern_size,
3335            offset_bytes,
3336            size_bytes,
3337            wait_list_len,
3338            wait_list_ptr,
3339            new_event_ptr,
3340        )
3341    };
3342    eval_errcode(errcode, (), "clEnqueueFillBuffer", None::<String>)
3343}
3344
3345/// Copies the contents of one buffer to another.
3346pub fn enqueue_copy_buffer<T, M, En, Ewl>(
3347    command_queue: &CommandQueue,
3348    src_buffer: M,
3349    dst_buffer: M,
3350    src_offset: usize,
3351    dst_offset: usize,
3352    len: usize,
3353    wait_list: Option<Ewl>,
3354    new_event: Option<En>,
3355) -> OclCoreResult<()>
3356where
3357    T: OclPrm,
3358    En: ClNullEventPtr,
3359    Ewl: ClWaitListPtr,
3360    M: AsMem<T> + MemCmdAll,
3361{
3362    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3363
3364    let src_offset_bytes = src_offset * mem::size_of::<T>();
3365    let dst_offset_bytes = dst_offset * mem::size_of::<T>();
3366    let len_bytes = len * mem::size_of::<T>();
3367
3368    let errcode = unsafe {
3369        ffi::clEnqueueCopyBuffer(
3370            command_queue.as_ptr(),
3371            src_buffer.as_mem().as_ptr(),
3372            dst_buffer.as_mem().as_ptr(),
3373            src_offset_bytes,
3374            dst_offset_bytes,
3375            len_bytes,
3376            wait_list_len,
3377            wait_list_ptr,
3378            new_event_ptr,
3379        )
3380    };
3381    eval_errcode(errcode, (), "clEnqueueCopyBuffer", None::<String>)
3382}
3383
3384/// Enqueues a command to copy a rectangular region from a buffer object to
3385/// another buffer object.
3386///
3387/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueCopyBufferRect.html)
3388///
3389pub fn enqueue_copy_buffer_rect<T, M, En, Ewl>(
3390    command_queue: &CommandQueue,
3391    src_buffer: M,
3392    dst_buffer: M,
3393    src_origin: [usize; 3],
3394    dst_origin: [usize; 3],
3395    region: [usize; 3],
3396    src_row_pitch_bytes: usize,
3397    src_slc_pitch_bytes: usize,
3398    dst_row_pitch_bytes: usize,
3399    dst_slc_pitch_bytes: usize,
3400    wait_list: Option<Ewl>,
3401    new_event: Option<En>,
3402) -> OclCoreResult<()>
3403where
3404    T: OclPrm,
3405    En: ClNullEventPtr,
3406    Ewl: ClWaitListPtr,
3407    M: AsMem<T> + MemCmdAll,
3408{
3409    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3410
3411    let src_origin_bytes = [
3412        src_origin[0] * mem::size_of::<T>(),
3413        src_origin[1],
3414        src_origin[2],
3415    ];
3416    let dst_origin_bytes = [
3417        dst_origin[0] * mem::size_of::<T>(),
3418        dst_origin[1],
3419        dst_origin[2],
3420    ];
3421    let region_bytes = [region[0] * mem::size_of::<T>(), region[1], region[2]];
3422
3423    let errcode = unsafe {
3424        ffi::clEnqueueCopyBufferRect(
3425            command_queue.as_ptr(),
3426            src_buffer.as_mem().as_ptr(),
3427            dst_buffer.as_mem().as_ptr(),
3428            &src_origin_bytes as *const _ as *const usize,
3429            &dst_origin_bytes as *const _ as *const usize,
3430            &region_bytes as *const _ as *const usize,
3431            src_row_pitch_bytes,
3432            src_slc_pitch_bytes,
3433            dst_row_pitch_bytes,
3434            dst_slc_pitch_bytes,
3435            wait_list_len,
3436            wait_list_ptr,
3437            new_event_ptr,
3438        )
3439    };
3440    eval_errcode(errcode, (), "clEnqueueCopyBufferRect", None::<String>)
3441}
3442
3443/// Acquire OpenCL memory objects (buffers and images) that have been created
3444/// from OpenGL objects.
3445///
3446/// To create a slice from a single `Mem` reference without any cost, use
3447/// something like: `unsafe { ::std::slice::from_raw_parts(&core_mem, 1) };`.
3448///
3449#[cfg(not(feature = "opencl_vendor_mesa"))]
3450pub fn enqueue_acquire_gl_objects<En, Ewl>(
3451    command_queue: &CommandQueue,
3452    buffers: &[Mem],
3453    wait_list: Option<Ewl>,
3454    new_event: Option<En>,
3455) -> OclCoreResult<()>
3456where
3457    En: ClNullEventPtr,
3458    Ewl: ClWaitListPtr,
3459{
3460    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3461
3462    let errcode = unsafe {
3463        clEnqueueAcquireGLObjects(
3464            command_queue.as_ptr(),
3465            buffers.len() as u32,
3466            buffers.as_ptr() as *const cl_mem,
3467            wait_list_len,
3468            wait_list_ptr,
3469            new_event_ptr,
3470        )
3471    };
3472    eval_errcode(errcode, (), "clEnqueueAcquireGLObjects", None::<String>)
3473}
3474
3475/// Release OpenCL memory objects (buffers and images) that have been created
3476/// from OpenGL objects.
3477///
3478/// To create a slice from a single `Mem` reference without any cost, use
3479/// something like: `unsafe { ::std::slice::from_raw_parts(&core_mem, 1) };`.
3480///
3481#[cfg(not(feature = "opencl_vendor_mesa"))]
3482pub fn enqueue_release_gl_objects<En, Ewl>(
3483    command_queue: &CommandQueue,
3484    buffers: &[Mem],
3485    wait_list: Option<Ewl>,
3486    new_event: Option<En>,
3487) -> OclCoreResult<()>
3488where
3489    En: ClNullEventPtr,
3490    Ewl: ClWaitListPtr,
3491{
3492    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3493
3494    let errcode = unsafe {
3495        clEnqueueReleaseGLObjects(
3496            command_queue.as_ptr(),
3497            buffers.len() as u32,
3498            buffers.as_ptr() as *const cl_mem,
3499            wait_list_len,
3500            wait_list_ptr,
3501            new_event_ptr,
3502        )
3503    };
3504    eval_errcode(errcode, (), "clEnqueueReleaseGLObjects", None::<String>)
3505}
3506
3507/// Reads an image from device to host memory.
3508///
3509/// ## Safety
3510///
3511/// Caller must ensure that `data` lives until the read is complete. Use
3512/// `new_event` to monitor it (use [`core::EventList::last_clone`] if passing
3513/// an event list as `new_event`).
3514///
3515/// [`core::EventList::get_clone`]: struct.EventList.html#method.last_clone
3516///
3517// pub unsafe fn enqueue_read_image<T>(
3518pub unsafe fn enqueue_read_image<T, M, En, Ewl>(
3519    command_queue: &CommandQueue,
3520    image: M,
3521    block: bool,
3522    origin: [usize; 3],
3523    region: [usize; 3],
3524    row_pitch_bytes: usize,
3525    slc_pitch_bytes: usize,
3526    data: &mut [T],
3527    wait_list: Option<Ewl>,
3528    new_event: Option<En>,
3529) -> OclCoreResult<()>
3530where
3531    T: OclPrm,
3532    En: ClNullEventPtr,
3533    Ewl: ClWaitListPtr,
3534    M: AsMem<T> + MemCmdRw,
3535{
3536    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3537
3538    let errcode = ffi::clEnqueueReadImage(
3539        command_queue.as_ptr(),
3540        image.as_mem().as_ptr(),
3541        block as cl_uint,
3542        &origin as *const _ as *const usize,
3543        &region as *const _ as *const usize,
3544        row_pitch_bytes,
3545        slc_pitch_bytes,
3546        data.as_ptr() as cl_mem,
3547        wait_list_len,
3548        wait_list_ptr,
3549        new_event_ptr,
3550    );
3551    eval_errcode(errcode, (), "clEnqueueReadImage", None::<String>)
3552}
3553
3554/// Enqueues a command to write to an image or image array object from host memory.
3555///
3556/// ## Safety
3557///
3558/// Caller must ensure that `data` lives until the read is complete. Use
3559/// `new_event` to monitor it (use [`core::EventList::last_clone`] if passing
3560/// an event list as `new_event`).
3561///
3562// TODO:
3563// * Size check (rather than leaving it to API).
3564// * Consider safetyness: local host data could change during write.
3565//
3566pub unsafe fn enqueue_write_image<T, M, En, Ewl>(
3567    command_queue: &CommandQueue,
3568    image: M,
3569    block: bool,
3570    origin: [usize; 3],
3571    region: [usize; 3],
3572    input_row_pitch_bytes: usize,
3573    input_slc_pitch_bytes: usize,
3574    data: &[T],
3575    wait_list: Option<Ewl>,
3576    new_event: Option<En>,
3577) -> OclCoreResult<()>
3578where
3579    T: OclPrm,
3580    En: ClNullEventPtr,
3581    Ewl: ClWaitListPtr,
3582    M: AsMem<T> + MemCmdRw,
3583{
3584    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3585
3586    let errcode = ffi::clEnqueueWriteImage(
3587        command_queue.as_ptr(),
3588        image.as_mem().as_ptr(),
3589        block as cl_uint,
3590        &origin as *const _ as *const usize,
3591        &region as *const _ as *const usize,
3592        input_row_pitch_bytes,
3593        input_slc_pitch_bytes,
3594        data.as_ptr() as cl_mem,
3595        wait_list_len,
3596        wait_list_ptr,
3597        new_event_ptr,
3598    );
3599    eval_errcode(errcode, (), "clEnqueueWriteImage", None::<String>)
3600}
3601
3602/// Enqueues a command to fill an image object with a specified color.
3603///
3604/// ## Fill Color (from [SDK docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueFillImage.html)
3605///
3606/// The fill color. The fill color is a four component RGBA floating-point color
3607/// value if the image channel data type is not an unnormalized signed and
3608/// unsigned integer type, is a four component signed integer value if the image
3609/// channel data type is an unnormalized signed integer type and is a four
3610/// component unsigned integer value if the image channel data type is an
3611/// unormalized unsigned integer type. The fill color will be converted to the
3612/// appropriate image channel format and order associated with image.
3613///
3614/// TODO: Trait constraints for `T`. Presumably it should be 32bits? Testing needed.
3615///
3616/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
3617///
3618// [UNTESTED]
3619//
3620pub fn enqueue_fill_image<T, M, En, Ewl>(
3621    command_queue: &CommandQueue,
3622    image: M,
3623    color: &[T],
3624    origin: [usize; 3],
3625    region: [usize; 3],
3626    wait_list: Option<Ewl>,
3627    new_event: Option<En>,
3628    device_version: Option<&OpenclVersion>,
3629) -> OclCoreResult<()>
3630where
3631    T: OclPrm,
3632    En: ClNullEventPtr,
3633    Ewl: ClWaitListPtr,
3634    M: AsMem<T> + MemCmdAll,
3635{
3636    // Verify device version:
3637    verify_device_version(
3638        device_version,
3639        [1, 2],
3640        command_queue,
3641        ApiFunction::EnqueueFillImage,
3642    )?;
3643
3644    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3645
3646    let errcode = unsafe {
3647        ffi::clEnqueueFillImage(
3648            command_queue.as_ptr(),
3649            image.as_mem().as_ptr(),
3650            color as *const _ as *const c_void,
3651            &origin as *const _ as *const usize,
3652            &region as *const _ as *const usize,
3653            wait_list_len,
3654            wait_list_ptr,
3655            new_event_ptr,
3656        )
3657    };
3658    eval_errcode(errcode, (), "clEnqueueFillImage", None::<String>)
3659}
3660
3661/// Enqueues a command to copy image objects.
3662///
3663/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueCopyImage.html)
3664pub fn enqueue_copy_image<En, Ewl>(
3665    command_queue: &CommandQueue,
3666    src_image: &Mem,
3667    dst_image: &Mem,
3668    src_origin: [usize; 3],
3669    dst_origin: [usize; 3],
3670    region: [usize; 3],
3671    wait_list: Option<Ewl>,
3672    new_event: Option<En>,
3673) -> OclCoreResult<()>
3674where
3675    En: ClNullEventPtr,
3676    Ewl: ClWaitListPtr,
3677{
3678    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3679
3680    let errcode = unsafe {
3681        ffi::clEnqueueCopyImage(
3682            command_queue.as_ptr(),
3683            src_image.as_ptr(),
3684            dst_image.as_ptr(),
3685            &src_origin as *const _ as *const usize,
3686            &dst_origin as *const _ as *const usize,
3687            &region as *const _ as *const usize,
3688            wait_list_len,
3689            wait_list_ptr,
3690            new_event_ptr,
3691        )
3692    };
3693    eval_errcode(errcode, (), "clEnqueueCopyImage", None::<String>)
3694}
3695
3696/// Enqueues a command to copy an image object to a buffer object.
3697///
3698/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueCopyImageToBuffer.html)
3699pub fn enqueue_copy_image_to_buffer<T, M, En, Ewl>(
3700    command_queue: &CommandQueue,
3701    src_image: M,
3702    dst_buffer: M,
3703    src_origin: [usize; 3],
3704    region: [usize; 3],
3705    dst_offset: usize,
3706    wait_list: Option<Ewl>,
3707    new_event: Option<En>,
3708) -> OclCoreResult<()>
3709where
3710    T: OclPrm,
3711    En: ClNullEventPtr,
3712    Ewl: ClWaitListPtr,
3713    M: AsMem<T> + MemCmdAll,
3714{
3715    let dst_offset_bytes = dst_offset * mem::size_of::<T>();
3716
3717    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3718
3719    let errcode = unsafe {
3720        ffi::clEnqueueCopyImageToBuffer(
3721            command_queue.as_ptr(),
3722            src_image.as_mem().as_ptr(),
3723            dst_buffer.as_mem().as_ptr(),
3724            &src_origin as *const _ as *const usize,
3725            &region as *const _ as *const usize,
3726            dst_offset_bytes,
3727            wait_list_len,
3728            wait_list_ptr,
3729            new_event_ptr,
3730        )
3731    };
3732    eval_errcode(errcode, (), "clEnqueueCopyImageToBuffer", None::<String>)
3733}
3734
3735/// Enqueues a command to copy a buffer object to an image object.
3736///
3737/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueCopyBufferToImage.html)
3738pub fn enqueue_copy_buffer_to_image<T, M, En, Ewl>(
3739    command_queue: &CommandQueue,
3740    src_buffer: M,
3741    dst_image: M,
3742    src_offset: usize,
3743    dst_origin: [usize; 3],
3744    region: [usize; 3],
3745    wait_list: Option<Ewl>,
3746    new_event: Option<En>,
3747) -> OclCoreResult<()>
3748where
3749    T: OclPrm,
3750    En: ClNullEventPtr,
3751    Ewl: ClWaitListPtr,
3752    M: AsMem<T> + MemCmdAll,
3753{
3754    let src_offset_bytes = src_offset * mem::size_of::<T>();
3755
3756    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3757
3758    let errcode = unsafe {
3759        ffi::clEnqueueCopyBufferToImage(
3760            command_queue.as_ptr(),
3761            src_buffer.as_mem().as_ptr(),
3762            dst_image.as_mem().as_ptr(),
3763            src_offset_bytes,
3764            &dst_origin as *const _ as *const usize,
3765            &region as *const _ as *const usize,
3766            wait_list_len,
3767            wait_list_ptr,
3768            new_event_ptr,
3769        )
3770    };
3771    eval_errcode(errcode, (), "clEnqueueCopyBufferToImage", None::<String>)
3772}
3773
3774#[inline]
3775unsafe fn _enqueue_map_buffer<T, M>(
3776    command_queue: &CommandQueue,
3777    buffer: M,
3778    block: bool,
3779    map_flags: MapFlags,
3780    offset: usize,
3781    len: usize,
3782    wait_list_len: cl_uint,
3783    wait_list_ptr: *const cl_event,
3784    new_event_ptr: *mut cl_event,
3785) -> OclCoreResult<*mut T>
3786where
3787    T: OclPrm,
3788    M: AsMem<T> + MemCmdAll,
3789{
3790    let offset_bytes = offset * mem::size_of::<T>();
3791    let size_bytes = len * mem::size_of::<T>();
3792
3793    let mut errcode = 0i32;
3794
3795    let mapped_ptr = ffi::clEnqueueMapBuffer(
3796        command_queue.as_ptr(),
3797        buffer.as_mem().as_ptr(),
3798        block as cl_uint,
3799        map_flags.bits(),
3800        offset_bytes,
3801        size_bytes,
3802        wait_list_len,
3803        wait_list_ptr,
3804        new_event_ptr,
3805        &mut errcode,
3806    );
3807
3808    eval_errcode(
3809        errcode,
3810        mapped_ptr as *mut T,
3811        "clEnqueueMapBuffer",
3812        None::<String>,
3813    )
3814}
3815
3816/// Enqueues a command to map a region of the buffer object given
3817/// by `buffer` into the host address space and returns a pointer to this
3818/// mapped region.
3819///
3820/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueMapBuffer.html)
3821///
3822/// ## Safety
3823///
3824/// Caller must ensure that the returned pointer is not used until the map is
3825/// complete. Use `new_event` to monitor it. It also must be ensured that
3826/// memory referred to by the returned pointer is not dropped, reused, or
3827/// otherwise interfered with until `enqueue_unmap_mem_object` is called.
3828///
3829///
3830/// [`EventList::get_clone`]: struct.EventList.html#method.last_clone
3831///
3832pub unsafe fn enqueue_map_buffer<T, M, En, Ewl>(
3833    command_queue: &CommandQueue,
3834    buffer: M,
3835    block: bool,
3836    map_flags: MapFlags,
3837    offset: usize,
3838    len: usize,
3839    wait_list: Option<Ewl>,
3840    new_event: Option<En>,
3841) -> OclCoreResult<MemMap<T>>
3842where
3843    T: OclPrm,
3844    En: ClNullEventPtr,
3845    Ewl: ClWaitListPtr,
3846    M: AsMem<T> + MemCmdAll,
3847{
3848    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3849
3850    let mapped_ptr_res = _enqueue_map_buffer(
3851        command_queue,
3852        buffer.as_mem(),
3853        block,
3854        map_flags,
3855        offset,
3856        len,
3857        wait_list_len,
3858        wait_list_ptr,
3859        new_event_ptr,
3860    );
3861
3862    mapped_ptr_res.map(|ptr| MemMap::from_raw(ptr))
3863}
3864
3865/// Enqueues a command to map a region of the image object given by `image` into
3866/// the host address space and returns a pointer to this mapped region.
3867///
3868/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueMapBuffer.html)
3869///
3870/// ## Safety
3871///
3872/// Caller must ensure that the returned pointer is not used until the map is
3873/// complete. Use `new_event` to monitor it. It also must be ensured that
3874/// memory referred to by the returned pointer is not dropped, reused, or
3875/// otherwise interfered with until `enqueue_unmap_mem_object` is called.
3876///
3877///
3878/// [`EventList::get_clone`]: struct.EventList.html#method.last_clone
3879///
3880///
3881///
3882// [UNTESTED]
3883//
3884pub unsafe fn enqueue_map_image<T, M, En, Ewl>(
3885    command_queue: &CommandQueue,
3886    image: M,
3887    block: bool,
3888    map_flags: MapFlags,
3889    origin: [usize; 3],
3890    region: [usize; 3],
3891    row_pitch_bytes: &mut usize,
3892    slc_pitch_bytes: &mut usize,
3893    wait_list: Option<Ewl>,
3894    new_event: Option<En>,
3895) -> OclCoreResult<MemMap<T>>
3896where
3897    T: OclPrm,
3898    En: ClNullEventPtr,
3899    Ewl: ClWaitListPtr,
3900    M: AsMem<T> + MemCmdAll,
3901{
3902    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3903
3904    let mut errcode = 0i32;
3905
3906    let mapped_ptr = ffi::clEnqueueMapImage(
3907        command_queue.as_ptr(),
3908        image.as_mem().as_ptr(),
3909        block as cl_uint,
3910        map_flags.bits(),
3911        &origin as *const _ as *const usize,
3912        &region as *const _ as *const usize,
3913        row_pitch_bytes,
3914        slc_pitch_bytes,
3915        wait_list_len,
3916        wait_list_ptr,
3917        new_event_ptr,
3918        &mut errcode,
3919    );
3920
3921    // let map_event_core = if !new_event_ptr.is_null() {
3922    //     *new_event_ptr = map_event;
3923    //     Event::from_raw_copied_ptr(map_event)?
3924    // } else {
3925    //     Event::from_raw_create_ptr(map_event)
3926    // };
3927
3928    // eval_errcode(errcode, MemMap::new(mapped_ptr as *mut T, slc_pitch * region[2],
3929    //     None, image.as_mem().clone(), command_queue.clone()), "clEnqueueMapImage", None::<String>)
3930
3931    eval_errcode(errcode, mapped_ptr, "clEnqueueMapImage", None::<String>)
3932        .map(|ptr| MemMap::from_raw(ptr as *mut _ as *mut T))
3933}
3934
3935/// Enqueues a command to unmap a previously mapped region of a memory object.
3936///
3937/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueUnmapMemObject.html)
3938///
3939pub fn enqueue_unmap_mem_object<T, M, En, Ewl>(
3940    command_queue: &CommandQueue,
3941    memobj: M,
3942    mapped_mem: &MemMap<T>,
3943    wait_list: Option<Ewl>,
3944    new_event: Option<En>,
3945) -> OclCoreResult<()>
3946where
3947    T: OclPrm,
3948    En: ClNullEventPtr,
3949    Ewl: ClWaitListPtr,
3950    M: AsMem<T> + MemCmdAll,
3951{
3952    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3953
3954    let errcode = unsafe {
3955        ffi::clEnqueueUnmapMemObject(
3956            command_queue.as_ptr(),
3957            memobj.as_mem().as_ptr(),
3958            mapped_mem.as_void_ptr(),
3959            wait_list_len,
3960            wait_list_ptr,
3961            new_event_ptr,
3962        )
3963    };
3964
3965    eval_errcode(errcode, (), "clEnqueueUnmapMemObject", None::<String>)
3966}
3967
3968/// Enqueues a command to indicate which device a set of memory objects should
3969/// be associated with.
3970///
3971/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueMigrateMemObjects.html)
3972///
3973/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
3974///
3975// [UNTESTED]
3976//
3977pub fn enqueue_migrate_mem_objects<En: ClNullEventPtr, Ewl: ClWaitListPtr>(
3978    command_queue: &CommandQueue,
3979    mem_objects: &[Mem],
3980    flags: MemMigrationFlags,
3981    wait_list: Option<Ewl>,
3982    new_event: Option<En>,
3983    device_version: Option<&OpenclVersion>,
3984) -> OclCoreResult<()> {
3985    // Verify device version:
3986    verify_device_version(
3987        device_version,
3988        [1, 2],
3989        command_queue,
3990        ApiFunction::EnqueueMigrateMemObjects,
3991    )?;
3992
3993    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
3994
3995    let errcode = unsafe {
3996        ffi::clEnqueueMigrateMemObjects(
3997            command_queue.as_ptr(),
3998            mem_objects.len() as u32,
3999            mem_objects.as_ptr() as *const _ as *const cl_mem,
4000            flags.bits(),
4001            wait_list_len,
4002            wait_list_ptr,
4003            new_event_ptr,
4004        )
4005    };
4006    eval_errcode(errcode, (), "clEnqueueMigrateMemObjects", None::<String>)
4007}
4008
4009/// Enqueues a command to execute a kernel on a device.
4010///
4011/// ## Safety
4012///
4013/// Running any kernel is an inherently unsafe process. The API call itself is
4014/// safe but the kernel execution is not.
4015///
4016/// ## Stability
4017///
4018/// * Work dimension/offset sizes *may* eventually be wrapped up in
4019///   specialized types.
4020///
4021/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueNDRangeKernel.html)
4022pub unsafe fn enqueue_kernel<En: ClNullEventPtr, Ewl: ClWaitListPtr>(
4023    command_queue: &CommandQueue,
4024    kernel: &Kernel,
4025    work_dims: u32,
4026    global_work_offset: Option<[usize; 3]>,
4027    global_work_dims: &[usize; 3],
4028    local_work_dims: Option<[usize; 3]>,
4029    wait_list: Option<Ewl>,
4030    new_event: Option<En>,
4031) -> OclCoreResult<()> {
4032    #[cfg(feature = "kernel_debug_sleep")]
4033    #[allow(unused_imports)]
4034    use std::thread;
4035    #[cfg(feature = "kernel_debug_sleep")]
4036    #[allow(unused_imports)]
4037    use std::time::Duration;
4038
4039    #[cfg(feature = "kernel_debug_print")]
4040    println!(
4041        "Resolving events: wait_list: {:?}, new_event: {:?}",
4042        wait_list, new_event
4043    );
4044
4045    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
4046
4047    #[cfg(feature = "kernel_debug_print")]
4048    println!("Resolving global work offset: {:?}...", global_work_offset);
4049
4050    let gwo = resolve_work_dims(global_work_offset.as_ref());
4051
4052    #[cfg(feature = "kernel_debug_print")]
4053    println!("Assigning global work size: {:?}...", global_work_dims);
4054
4055    let gws = global_work_dims as *const size_t;
4056
4057    #[cfg(feature = "kernel_debug_print")]
4058    println!("Resolving local work size: {:?}...", local_work_dims);
4059
4060    let lws = resolve_work_dims(local_work_dims.as_ref());
4061
4062    #[cfg(feature = "kernel_debug_print")]
4063    println!("Preparing to print all details...");
4064
4065    #[cfg(feature = "kernel_debug_print")]
4066    print!(
4067        "core::enqueue_kernel('{}': \
4068        work_dims: {}, \
4069        gwo: {:?}, \
4070        gws: {:?}, \
4071        lws: {:?}, \
4072        wait_list_len: {}, \
4073        wait_list_ptr: {:?}, \
4074        new_event_ptr: {:?}) \
4075        ",
4076        get_kernel_name(&kernel),
4077        work_dims,
4078        global_work_offset,
4079        global_work_dims,
4080        local_work_dims,
4081        wait_list_len,
4082        wait_list_ptr,
4083        new_event_ptr,
4084    );
4085
4086    let errcode = ffi::clEnqueueNDRangeKernel(
4087        command_queue.as_ptr(),
4088        kernel.as_ptr() as cl_kernel,
4089        work_dims,
4090        gwo,
4091        gws,
4092        lws,
4093        wait_list_len,
4094        wait_list_ptr,
4095        new_event_ptr,
4096    );
4097
4098    if cfg!(feature = "kernel_debug_print") {
4099        println!("-> Status: {}.", errcode);
4100    }
4101    if cfg!(feature = "kernel_debug_sleep") {
4102        thread::sleep(Duration::from_millis(KERNEL_DEBUG_SLEEP_DURATION_MS));
4103    }
4104
4105    if errcode != 0 {
4106        let name = get_kernel_name(kernel)?;
4107        eval_errcode(errcode, (), "clEnqueueNDRangeKernel", Some(name))
4108    } else {
4109        Ok(())
4110    }
4111}
4112
4113/// Enqueues a command to execute a kernel on a device.
4114///
4115/// The kernel is executed using a single work-item.
4116///
4117/// From [SDK]: clEnqueueTask is equivalent to calling clEnqueueNDRangeKernel
4118/// with work_dim = 1, global_work_offset = NULL, global_work_size[0] set to 1,
4119/// and local_work_size[0] set to 1.
4120///
4121/// ## Safety
4122///
4123/// Running any kernel is an inherently unsafe process. The API call itself is
4124/// safe but the kernel execution is not.
4125///
4126///
4127/// [SDK]: https://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/clEnqueueTask.html
4128///
4129// [UNTESTED]
4130//
4131pub unsafe fn enqueue_task<En: ClNullEventPtr, Ewl: ClWaitListPtr>(
4132    command_queue: &CommandQueue,
4133    kernel: &Kernel,
4134    wait_list: Option<Ewl>,
4135    new_event: Option<En>,
4136    kernel_name: Option<&str>,
4137) -> OclCoreResult<()> {
4138    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
4139
4140    let errcode = ffi::clEnqueueTask(
4141        command_queue.as_ptr(),
4142        kernel.as_ptr() as cl_kernel,
4143        wait_list_len,
4144        wait_list_ptr,
4145        new_event_ptr,
4146    );
4147    eval_errcode(errcode, (), "clEnqueueTask", kernel_name)
4148}
4149
4150/// [UNIMPLEMENTED: Please implement me]
4151pub fn enqueue_native_kernel() -> OclCoreResult<()> {
4152    // ffi::clEnqueueNativeKernel(command_queue: cl_command_queue,
4153    //                          user_func: extern fn (*mut c_void),
4154    //                          args: *mut c_void,
4155    //                          cb_args: size_t,
4156    //                          num_mem_objects: cl_uint,
4157    //                          mem_list: *const cl_mem,
4158    //                          args_mem_loc: *const *const c_void,
4159    //                          num_events_in_wait_list: cl_uint,
4160    //                          event_wait_list: *const cl_event,
4161    //                          event: *mut cl_event) -> cl_int;
4162    unimplemented!();
4163}
4164
4165/// Enqueues a marker command which waits for either a list of events to
4166/// complete, or all previously enqueued commands to complete.
4167///
4168/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueMarkerWithWaitList.html)
4169///
4170/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
4171pub fn enqueue_marker_with_wait_list<En, Ewl>(
4172    command_queue: &CommandQueue,
4173    wait_list: Option<Ewl>,
4174    new_event: Option<En>,
4175    device_version: Option<&OpenclVersion>,
4176) -> OclCoreResult<()>
4177where
4178    En: ClNullEventPtr,
4179    Ewl: ClWaitListPtr,
4180{
4181    // Verify device version:
4182    verify_device_version(
4183        device_version,
4184        [1, 2],
4185        command_queue,
4186        ApiFunction::EnqueueMarkerWithWaitList,
4187    )?;
4188
4189    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
4190
4191    let errcode = unsafe {
4192        ffi::clEnqueueMarkerWithWaitList(
4193            command_queue.as_ptr(),
4194            wait_list_len,
4195            wait_list_ptr,
4196            new_event_ptr,
4197        )
4198    };
4199    eval_errcode(errcode, (), "clEnqueueMarkerWithWaitList", None::<String>)
4200}
4201
4202/// A synchronization point that enqueues a barrier operation.
4203///
4204/// [SDK Docs](https://www.khronos.org/registry/cl/sdk/1.2/docs/man/xhtml/clEnqueueBarrierWithWaitList.html)
4205///
4206/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
4207pub fn enqueue_barrier_with_wait_list<En, Ewl>(
4208    command_queue: &CommandQueue,
4209    wait_list: Option<Ewl>,
4210    new_event: Option<En>,
4211    device_version: Option<&OpenclVersion>,
4212) -> OclCoreResult<()>
4213where
4214    En: ClNullEventPtr,
4215    Ewl: ClWaitListPtr,
4216{
4217    // Verify device version:
4218    verify_device_version(
4219        device_version,
4220        [1, 2],
4221        command_queue,
4222        ApiFunction::EnqueueBarrierWithWaitList,
4223    )?;
4224
4225    let (wait_list_len, wait_list_ptr, new_event_ptr) = resolve_event_ptrs(wait_list, new_event);
4226
4227    let errcode = unsafe {
4228        ffi::clEnqueueBarrierWithWaitList(
4229            command_queue.as_ptr(),
4230            wait_list_len,
4231            wait_list_ptr,
4232            new_event_ptr,
4233        )
4234    };
4235    eval_errcode(errcode, (), "clEnqueueBarrierWithWaitList", None::<String>)
4236}
4237
4238// Extension function access
4239//
4240// Returns the extension function address for the given function name,
4241// or NULL if a valid function can not be found. The client must
4242// check to make sure the address is not NULL, before using or
4243// or calling the returned function address.
4244//
4245// A non-NULL return value for clGetExtensionFunctionAddressForPlatform does
4246// not guarantee that an extension function is actually supported by the
4247// platform. The application must also make a corresponding query using
4248// clGetPlatformInfo (platform, CL_PLATFORM_EXTENSIONS, ... ) or
4249// clGetDeviceInfo (device,CL_DEVICE_EXTENSIONS, ... ) to determine if an
4250// extension is supported by the OpenCL implementation.
4251//
4252// [FIXME]: Return a generic that implements `Fn` (or `FnMut/Once`?).
4253// TODO: Create another function which will handle the second check described
4254// above in addition to calling this.
4255//
4256// ////////////////////////////////////////////////////////////////////////////
4257/// Returns the address of the extension function named by
4258/// `func_name` for a given platform.
4259///
4260/// The pointer returned should be cast to a function pointer type matching
4261/// the extension function's definition defined in the appropriate extension
4262/// specification and header file.
4263///
4264///
4265/// A non-NULL return value does
4266/// not guarantee that an extension function is actually supported by the
4267/// platform. The application must also make a corresponding query using
4268/// `ocl::core::get_platform_info(platform_core, CL_PLATFORM_EXTENSIONS, ... )` or
4269/// `ocl::core::get_device_info(device_core, CL_DEVICE_EXTENSIONS, ... )`
4270/// to determine if an extension is supported by the OpenCL implementation.
4271///
4272/// [FIXME]: Update enum names above to the wrapped types.
4273///
4274/// ## Errors
4275///
4276/// Returns an error if:
4277///
4278/// - `func_name` cannot be converted to a `CString`.
4279/// - The specified function does not exist for the implementation.
4280/// - 'platform' is not a valid platform.
4281///
4282/// [Version Controlled: OpenCL 1.2+] See module docs for more info.
4283///
4284// [UNTESTED]
4285//
4286pub unsafe fn get_extension_function_address_for_platform(
4287    platform: &PlatformId,
4288    func_name: &str,
4289    platform_version: Option<&OpenclVersion>,
4290) -> OclCoreResult<*mut c_void> {
4291    // Verify platform version:
4292    verify_platform_version(
4293        platform_version,
4294        [1, 2],
4295        platform,
4296        ApiFunction::GetExtensionFunctionAddressForPlatform,
4297    )?;
4298
4299    let func_name_c = CString::new(func_name)?;
4300
4301    let ext_fn =
4302        ffi::clGetExtensionFunctionAddressForPlatform(platform.as_ptr(), func_name_c.as_ptr());
4303
4304    if ext_fn.is_null() {
4305        Err(ApiWrapperError::GetExtensionFunctionAddressForPlatformInvalidFunction.into())
4306    } else {
4307        Ok(ext_fn)
4308    }
4309}
4310
4311//============================================================================
4312//============================================================================
4313//=========================== DERIVED FUNCTIONS ==============================
4314//============================================================================
4315//============================================================================
4316
4317/// Returns a list of versions for devices.
4318pub fn device_versions(device_ids: &[DeviceId]) -> OclCoreResult<Vec<OpenclVersion>> {
4319    let mut d_versions = Vec::with_capacity(device_ids.len());
4320
4321    for device_id in device_ids {
4322        d_versions.push(device_id.version()?);
4323    }
4324
4325    Ok(d_versions)
4326}
4327
4328/// Returns the default platform if set by an environment variable or config
4329/// file.
4330pub fn default_platform_idx() -> usize {
4331    match env::var("OCL_DEFAULT_PLATFORM_IDX") {
4332        Ok(s) => s.parse::<usize>().unwrap_or(0),
4333        Err(_) => 0,
4334    }
4335}
4336
4337/// Returns the default or first platform.
4338pub fn default_platform() -> OclCoreResult<PlatformId> {
4339    let platform_list = get_platform_ids()?;
4340
4341    if platform_list.is_empty() {
4342        Err(ApiWrapperError::DefaultPlatformNoPlatforms.into())
4343    } else {
4344        let default_platform_idx = default_platform_idx();
4345        if default_platform_idx > platform_list.len() - 1 {
4346            Err(ApiWrapperError::DefaultPlatformEnvVarBadIdx {
4347                default_platform_idx,
4348                max_idx: platform_list.len() - 1,
4349            }
4350            .into())
4351        } else {
4352            Ok(platform_list[default_platform_idx])
4353        }
4354    }
4355}
4356
4357/// Returns the default device type bitflags as specified by environment
4358/// variable or else `DEVICE_TYPE_ALL`.
4359pub fn default_device_type() -> OclCoreResult<DeviceType> {
4360    match env::var("OCL_DEFAULT_DEVICE_TYPE") {
4361        Ok(ref s) => match s.trim() {
4362            "DEFAULT" => Ok(DeviceType::DEFAULT),
4363            "CPU" => Ok(DeviceType::CPU),
4364            "GPU" => Ok(DeviceType::GPU),
4365            "ACCELERATOR" => Ok(DeviceType::ACCELERATOR),
4366            "CUSTOM" => Ok(DeviceType::CUSTOM),
4367            "ALL" => Ok(DeviceType::ALL),
4368            _ => Err(ApiWrapperError::DefaultDeviceTypeInvalidType(s.to_owned()).into()),
4369        },
4370        // `DeviceType::ALL` is used to avoid the potentially confusing
4371        // platform-dependent behavior of `DeviceType::DEFAULT`.
4372        Err(_) => Ok(DeviceType::ALL),
4373    }
4374}
4375
4376/// Returns the name of a kernel.
4377pub fn get_kernel_name(kernel: &Kernel) -> OclCoreResult<String> {
4378    let result = get_kernel_info(kernel, KernelInfo::FunctionName)?;
4379    Ok(result.into())
4380}
4381
4382/// Creates, builds, and returns a new program pointer from `src_strings`.
4383///
4384/// TODO: Break out create and build parts into requisite functions then call
4385/// from here.
4386pub fn create_build_program<C, D>(
4387    context: C,
4388    src_strings: &[CString],
4389    device_ids: Option<&[D]>,
4390    cmplr_opts: &CString,
4391) -> OclCoreResult<Program>
4392where
4393    C: ClContextPtr,
4394    D: ClDeviceIdPtr + fmt::Debug,
4395{
4396    let program = create_program_with_source(context, src_strings)?;
4397    build_program(&program, device_ids, cmplr_opts, None, None)?;
4398    Ok(program)
4399}
4400
4401#[allow(dead_code)]
4402/// Blocks until an event is complete.
4403pub fn wait_for_event<'e, E: ClEventPtrRef<'e>>(event: &'e E) -> OclCoreResult<()> {
4404    let errcode = unsafe { ffi::clWaitForEvents(1, event.as_ptr_ref()) };
4405    eval_errcode(errcode, (), "clWaitForEvents", None::<String>)
4406}
4407
4408/// Returns the status of `event`.
4409pub fn event_status<'e, E: ClEventPtrRef<'e>>(
4410    event: &'e E,
4411) -> OclCoreResult<CommandExecutionStatus> {
4412    let mut status_int: cl_int = 0;
4413
4414    let errcode = unsafe {
4415        ffi::clGetEventInfo(
4416            *event.as_ptr_ref(),
4417            ffi::CL_EVENT_COMMAND_EXECUTION_STATUS,
4418            mem::size_of::<cl_int>(),
4419            &mut status_int as *mut _ as *mut c_void,
4420            ptr::null_mut(),
4421        )
4422    };
4423    eval_errcode(errcode, (), "clGetEventInfo", None::<String>)?;
4424
4425    CommandExecutionStatus::from_i32(status_int).ok_or_else(|| {
4426        OclCoreError::String(
4427            "Error converting \
4428        'clGetEventInfo' status output."
4429                .to_string(),
4430        )
4431    })
4432}
4433
4434/// Returns true if an event is complete, false if not complete.
4435pub fn event_is_complete<'e, E: ClEventPtrRef<'e>>(event: &'e E) -> OclCoreResult<bool> {
4436    let mut status_int: cl_int = 0;
4437
4438    let errcode = unsafe {
4439        ffi::clGetEventInfo(
4440            *event.as_ptr_ref(),
4441            ffi::CL_EVENT_COMMAND_EXECUTION_STATUS,
4442            mem::size_of::<cl_int>(),
4443            &mut status_int as *mut _ as *mut c_void,
4444            ptr::null_mut(),
4445        )
4446    };
4447
4448    #[cfg(feature = "event_debug_print")]
4449    unsafe {
4450        println!(
4451            "Event Status: {:?} (ptr: {:?})",
4452            CommandExecutionStatus::from_i32(status_int).unwrap(),
4453            *event.as_ptr_ref()
4454        );
4455    }
4456
4457    eval_errcode(
4458        errcode,
4459        status_int == CommandExecutionStatus::Complete as i32,
4460        "clEventGetInfo",
4461        Some("CL_EVENT_COMMAND_EXECUTION_STATUS"),
4462    )
4463}
4464
4465/// Verifies that the `context` is in fact a context object pointer.
4466///
4467/// ## Assumptions
4468///
4469/// Some (most?/all?) OpenCL implementations do not correctly error if non-
4470/// context pointers are passed. This function relies on the fact that passing
4471/// the `CL_CONTEXT_DEVICES` as the `param_name` to `clGetContextInfo` will
4472/// (at least on my AMD implementation) often return a huge result size if
4473/// `context` is not actually a `cl_context` pointer due to the fact that it's
4474/// reading from some random memory location on non-context structs. Also
4475/// checks for zero because a context must have at least one device (true?).
4476/// Should probably choose a value lower than 10kB because it seems unlikely
4477/// any result would be that big but w/e.
4478///
4479/// [UPDATE]: This function may no longer be necessary now that the core
4480/// pointers have wrappers but it still prevents a hard to track down bug so
4481/// it will stay intact for now.
4482///
4483#[inline]
4484pub fn verify_context<C>(context: C) -> OclCoreResult<()>
4485where
4486    C: ClContextPtr,
4487{
4488    // context_info(context, ffi::CL_CONTEXT_REFERENCE_COUNT)
4489    if cfg!(release) {
4490        Ok(())
4491    } else {
4492        match get_context_info(context, ContextInfo::Devices) {
4493            Err(err) => Err(err),
4494            _ => Ok(()),
4495        }
4496    }
4497}
4498
4499/// Checks to see if a device supports the `CL_GL_SHARING_EXT` extension.
4500fn device_supports_cl_gl_sharing<D: ClDeviceIdPtr>(device: D) -> OclCoreResult<bool> {
4501    match get_device_info(device, DeviceInfo::Extensions) {
4502        Ok(DeviceInfoResult::Extensions(extensions)) => Ok(extensions.contains(CL_GL_SHARING_EXT)),
4503        // Ok(DeviceInfoResult::Error(err)) => Err(*err),
4504        Err(err) => Err(err),
4505        _ => unreachable!(),
4506    }
4507}
4508
4509/// Returns the context for a command queue, bypassing extra processing.
4510pub fn get_command_queue_context_ptr(queue: &CommandQueue) -> OclCoreResult<cl_context> {
4511    let mut result = 0 as cl_context;
4512    let result_size = mem::size_of::<cl_context>();
4513
4514    let errcode = unsafe {
4515        ffi::clGetCommandQueueInfo(
4516            queue.as_ptr(),
4517            CommandQueueInfo::Context as cl_command_queue_info,
4518            result_size,
4519            &mut result as *mut _ as *mut c_void,
4520            ptr::null_mut(),
4521        )
4522    };
4523
4524    eval_errcode(
4525        errcode,
4526        result,
4527        "clGetCommandQueueInfo",
4528        Some("functions::get_command_queue_context_ptr"),
4529    )
4530}
4531
4532//============================================================================
4533//============================================================================
4534//====================== Wow, you made it this far? ==========================
4535//============================================================================
4536//============================================================================