open_cl_low_level/
device.rs

1use std::fmt;
2
3use crate::ffi::*;
4
5use crate::{
6    ClPlatformID, ClPointer, DeviceAffinityDomain, DeviceExecCapabilities, DeviceInfo,
7    DeviceLocalMemType, DeviceMemCacheType, DeviceType, Error, Output, PlatformPtr,
8    StatusCodeError, ObjectWrapper,
9};
10
11use crate::cl_helpers::{cl_get_info5, cl_get_object, cl_get_object_count};
12
13/// NOTE: UNUSABLE_DEVICE_ID might be osx specific? or OpenCL
14/// implementation specific?
15/// UNUSABLE_DEVICE_ID was the cl_device_id encountered on my Macbook
16/// Pro for a Radeon graphics card that becomes unavailable when
17/// powersaving mode enables. Apparently the OpenCL platform can still
18/// see the device, instead of a "legit" cl_device_id the inactive
19/// device's cl_device_id is listed as 0xFFFF_FFFF.
20pub const UNUSABLE_DEVICE_ID: cl_device_id = 0xFFFF_FFFF as *mut usize as cl_device_id;
21
22pub const UNUSABLE_DEVICE_ERROR: Error = Error::DeviceError(DeviceError::UnusableDevice);
23
24pub const NO_PARENT_DEVICE_ERROR: Error = Error::DeviceError(DeviceError::NoParentDevice);
25
26pub fn device_usability_check(device_id: cl_device_id) -> Result<(), Error> {
27    if device_id == UNUSABLE_DEVICE_ID {
28        Err(UNUSABLE_DEVICE_ERROR)
29    } else {
30        Ok(())
31    }
32}
33
34// NOTE: fix cl_device_type
35pub fn cl_get_device_count(platform: cl_platform_id, device_type: cl_device_type) -> Output<u32> {
36    unsafe {
37        cl_get_object_count::<cl_platform_id, cl_device_type, cl_device_id>(
38            platform,
39            device_type,
40            clGetDeviceIDs,
41        )
42    }
43}
44
45pub fn list_devices_by_type(
46    platform: &ClPlatformID,
47    device_type: DeviceType,
48) -> Output<Vec<ClDeviceID>> {
49    unsafe {
50        match cl_get_object(platform.platform_ptr(), device_type.into(), clGetDeviceIDs) {
51            Ok(cl_ptr) => {
52                let devices: Vec<ClDeviceID> = cl_ptr
53                    .into_vec()
54                    .into_iter()
55                    .map(|d| ClDeviceID::new(d))
56                    .filter_map(Result::ok)
57                    .collect();
58                Ok(devices)
59            }
60            Err(Error::StatusCodeError(StatusCodeError { status_code: -1 })) => Ok(vec![]),
61            Err(Error::StatusCodeError(StatusCodeError { status_code: -30 })) => Ok(vec![]),
62            Err(e) => Err(e),
63        }
64    }
65}
66
67pub unsafe fn cl_get_device_info<T>(device: cl_device_id, flag: DeviceInfo) -> Output<ClPointer<T>>
68where
69    T: Copy,
70{
71    device_usability_check(device)?;
72    cl_get_info5(device.device_ptr(), flag.into(), clGetDeviceInfo)
73}
74
75/// An error related to a Device.
76#[derive(Debug, Fail, PartialEq, Eq, Clone)]
77pub enum DeviceError {
78    #[fail(display = "Device is not in a usable state")]
79    UnusableDevice,
80
81    #[fail(display = "The given platform had no default device")]
82    NoDefaultDevice,
83
84    #[fail(display = "The given device had no parent device")]
85    NoParentDevice,
86}
87
88pub type ClDeviceID = ObjectWrapper<cl_device_id>;
89
90impl DevicePtr for ClDeviceID {
91    unsafe fn device_ptr(&self) -> cl_device_id {
92        self.cl_object()
93    }
94}
95
96impl DevicePtr for &ClDeviceID {
97    unsafe fn device_ptr(&self) -> cl_device_id {
98        self.cl_object()
99    }
100}
101
102impl DevicePtr for cl_device_id {
103    unsafe fn device_ptr(&self) -> cl_device_id {
104        *self
105    }
106}
107
108macro_rules! info_fn {
109    ($name:ident, $flag:ident, String) => {
110        fn $name(&self) -> Output<String> {
111            unsafe{
112                cl_get_device_info(self.device_ptr(), DeviceInfo::$flag)
113                    .map(|ret| ret.into_string() )
114            }
115        }
116    };
117
118    ($name:ident, $flag:ident, bool) => {
119        fn $name(&self) -> Output<bool> {
120            use crate::ffi::cl_bool;
121            unsafe {
122                cl_get_device_info::<cl_bool>(self.device_ptr(), DeviceInfo::$flag).map(From::from)
123            }
124        }
125    };
126
127    ($name:ident, $flag:ident, $cl_type:ty, Vec<$output_t:ty>) => {
128        fn $name(&self) -> Output<Vec<$output_t>> {
129            unsafe {
130                cl_get_device_info(self.device_ptr(), DeviceInfo::$flag).map(|ret| ret.into_vec())
131            }
132        }
133    };
134
135    ($name:ident, $flag:ident, $output_t:ty) => {
136        fn $name(&self) -> Output<$output_t> {
137            unsafe {
138                cl_get_device_info(self.device_ptr(), DeviceInfo::$flag).map(|ret| ret.into_one())
139            }
140        }
141    };
142
143    ($name:ident, $flag:ident, $cl_type:ty, $output_t:ty) => {
144        fn $name(&self) -> Output<$output_t> {
145            unsafe {
146                cl_get_device_info(self.device_ptr(), DeviceInfo::$flag)
147                    .map(|ret| ret.into_one())
148            }
149        }
150    };
151}
152
153pub trait DevicePtr
154where
155    Self: fmt::Debug + Sized,
156{
157    unsafe fn device_ptr(&self) -> cl_device_id;
158
159    fn is_usable(&self) -> bool {
160        unsafe { self.device_ptr() != UNUSABLE_DEVICE_ID }
161    }
162
163    fn usability_check(&self) -> Output<()> {
164        if self.is_usable() {
165            Ok(())
166        } else {
167            Err(DeviceError::UnusableDevice.into())
168        }
169    }
170
171    info_fn!(
172        global_mem_cacheline_size,
173        GlobalMemCachelineSize,
174        cl_uint,
175        u32
176    );
177    info_fn!(
178        native_vector_width_double,
179        NativeVectorWidthDouble,
180        cl_uint,
181        u32
182    );
183    info_fn!(
184        native_vector_width_half,
185        NativeVectorWidthHalf,
186        cl_uint,
187        u32
188    );
189    info_fn!(address_bits, AddressBits, cl_uint, u32);
190    info_fn!(max_clock_frequency, MaxClockFrequency, cl_uint, u32);
191    info_fn!(max_compute_units, MaxComputeUnits, cl_uint, u32);
192    info_fn!(max_constant_args, MaxConstantArgs, cl_uint, u32);
193    info_fn!(max_read_image_args, MaxReadImageArgs, cl_uint, u32);
194    info_fn!(max_samplers, MaxSamplers, cl_uint, u32);
195    info_fn!(
196        max_work_item_dimensions,
197        MaxWorkItemDimensions,
198        cl_uint,
199        u32
200    );
201    info_fn!(max_write_image_args, MaxWriteImageArgs, cl_uint, u32);
202    info_fn!(mem_base_addr_align, MemBaseAddrAlign, cl_uint, u32);
203    info_fn!(min_data_type_align_size, MinDataTypeAlignSize, cl_uint, u32);
204    info_fn!(
205        native_vector_width_char,
206        NativeVectorWidthChar,
207        cl_uint,
208        u32
209    );
210    info_fn!(
211        native_vector_width_short,
212        NativeVectorWidthShort,
213        cl_uint,
214        u32
215    );
216    info_fn!(native_vector_width_int, NativeVectorWidthInt, cl_uint, u32);
217    info_fn!(
218        native_vector_width_long,
219        NativeVectorWidthLong,
220        cl_uint,
221        u32
222    );
223    info_fn!(
224        native_vector_width_float,
225        NativeVectorWidthFloat,
226        cl_uint,
227        u32
228    );
229    info_fn!(
230        partition_max_sub_devices,
231        PartitionMaxSubDevices,
232        cl_uint,
233        u32
234    );
235    info_fn!(
236        preferred_vector_width_char,
237        PreferredVectorWidthChar,
238        cl_uint,
239        u32
240    );
241    info_fn!(
242        preferred_vector_width_short,
243        PreferredVectorWidthShort,
244        cl_uint,
245        u32
246    );
247    info_fn!(
248        preferred_vector_width_int,
249        PreferredVectorWidthInt,
250        cl_uint,
251        u32
252    );
253    info_fn!(
254        preferred_vector_width_long,
255        PreferredVectorWidthLong,
256        cl_uint,
257        u32
258    );
259    info_fn!(
260        preferred_vector_width_float,
261        PreferredVectorWidthFloat,
262        cl_uint,
263        u32
264    );
265    info_fn!(
266        preferred_vector_width_double,
267        PreferredVectorWidthDouble,
268        cl_uint,
269        u32
270    );
271    info_fn!(
272        preferred_vector_width_half,
273        PreferredVectorWidthHalf,
274        cl_uint,
275        u32
276    );
277    info_fn!(vendor_id, VendorId, cl_uint, u32);
278
279    // cl_bool
280    info_fn!(available, Available, bool);
281    info_fn!(compiler_available, CompilerAvailable, bool);
282    info_fn!(endian_little, EndianLittle, bool);
283    info_fn!(error_correction_support, ErrorCorrectionSupport, bool);
284    info_fn!(host_unified_memory, HostUnifiedMemory, bool);
285    info_fn!(image_support, ImageSupport, bool);
286    info_fn!(linker_available, LinkerAvailable, bool);
287    info_fn!(preferred_interop_user_sync, PreferredInteropUserSync, bool);
288
289    // char[]
290    info_fn!(name, Name, String);
291    info_fn!(opencl_c_version, OpenclCVersion, String);
292    info_fn!(profile, Profile, String);
293    info_fn!(vendor, Vendor, String);
294    info_fn!(version, Version, String);
295    info_fn!(driver_version, DriverVersion, String);
296
297    // ulong as u64
298    info_fn!(global_mem_cache_size, GlobalMemCacheSize, cl_ulong, u64);
299    info_fn!(global_mem_size, GlobalMemSize, cl_ulong, u64);
300    info_fn!(local_mem_size, LocalMemSize, cl_ulong, u64);
301    info_fn!(
302        max_constant_buffer_size,
303        MaxConstantBufferSize,
304        cl_ulong,
305        u64
306    );
307    info_fn!(max_mem_alloc_size, MaxMemAllocSize, cl_ulong, u64);
308
309    // size_t as usize
310    info_fn!(image2d_max_width, Image2DMaxWidth, size_t, usize);
311    info_fn!(image2d_max_height, Image2DMaxHeight, size_t, usize);
312    info_fn!(image3d_max_width, Image3DMaxWidth, size_t, usize);
313    info_fn!(image3d_max_height, Image3DMaxHeight, size_t, usize);
314    info_fn!(image3d_max_depth, Image3DMaxDepth, size_t, usize);
315    info_fn!(image_max_buffer_size, ImageMaxBufferSize, size_t, usize);
316    info_fn!(image_max_array_size, ImageMaxArraySize, size_t, usize);
317    info_fn!(max_parameter_size, MaxParameterSize, size_t, usize);
318    info_fn!(max_work_group_size, MaxWorkGroupSize, size_t, usize);
319    info_fn!(printf_buffer_size, PrintfBufferSize, size_t, usize);
320    info_fn!(
321        profiling_timer_resolution,
322        ProfilingTimerResolution,
323        size_t,
324        usize
325    );
326
327    // size_t[]
328    info_fn!(max_work_item_sizes, MaxWorkItemSizes, size_t, Vec<usize>);
329
330    // cl_device_local_mem_type
331    info_fn!(
332        local_mem_type,
333        LocalMemType,
334        cl_device_local_mem_type,
335        DeviceLocalMemType
336    );
337
338    // ExecutionCapabilities
339    info_fn!(
340        execution_capabilities,
341        ExecutionCapabilities,
342        cl_device_exec_capabilities,
343        DeviceExecCapabilities
344    );
345
346    //  CL_DEVICE_GLOBAL_MEM_CACHE_TYPE
347    info_fn!(
348        global_mem_cache_type,
349        GlobalMemCacheType,
350        cl_device_mem_cache_type,
351        DeviceMemCacheType
352    );
353
354    // cl_device_affinity_domain
355    info_fn!(
356        partition_affinity_domain,
357        PartitionAffinityDomain,
358        cl_device_affinity_domain,
359        DeviceAffinityDomain
360    );
361
362    // DeviceType
363    info_fn!(device_type, Type, cl_device_type, DeviceType);
364}
365
366unsafe impl Send for ClDeviceID {}
367unsafe impl Sync for ClDeviceID {}
368
369#[cfg(test)]
370mod tests {
371    use crate::ffi::*;
372    use crate::*;
373
374    #[test]
375    fn unusable_device_id_results_in_an_unusable_device_error() {
376        let unusable_device_id = 0xFFFF_FFFF as cl_device_id;
377        let error = unsafe { ClDeviceID::new(unusable_device_id) };
378        assert_eq!(error, Err(UNUSABLE_DEVICE_ERROR));
379    }
380
381    #[test]
382    fn lists_all_devices() {
383        let platform = ClPlatformID::default();
384        let devices =
385            list_devices_by_type(&platform, DeviceType::ALL).expect("Failed to list all devices");
386        assert!(devices.len() > 0);
387    }
388
389    #[test]
390    fn devices_of_many_types_can_be_listed_for_a_platform() {
391        let platform = ClPlatformID::default();
392        let _ = list_devices_by_type(&platform, DeviceType::DEFAULT)
393            .expect("Failed to list DEFAULT devices");
394        let _ =
395            list_devices_by_type(&platform, DeviceType::CPU).expect("Failed to list CPU devices");
396        let _ =
397            list_devices_by_type(&platform, DeviceType::GPU).expect("Failed to list GPU devices");
398        let _ = list_devices_by_type(&platform, DeviceType::ACCELERATOR)
399            .expect("Failed to list ACCELERATOR devices");
400        let _ = list_devices_by_type(&platform, DeviceType::CUSTOM)
401            .expect("Failed to list CUSTOM devices");
402        let _ =
403            list_devices_by_type(&platform, DeviceType::ALL).expect("Failed to list ALL devices");
404    }
405
406    #[test]
407    fn device_fmt_debug_works() {
408        ll_testing::with_each_device(|device| {
409            let formatted = format!("{:?}", device);
410            expect_method!(formatted, contains, device.address());
411            expect_method!(formatted, contains, "cl_device_id");
412        })
413    }
414}
415#[cfg(test)]
416mod device_ptr_tests {
417    use crate::*;
418
419    #[test]
420    fn device_name_works() {
421        ll_testing::with_each_device(|device| {
422            let name: String = device.name().unwrap();
423            assert!(name.len() > 0);
424        })
425    }
426
427    macro_rules! test_method {
428        ($method:ident) => {
429            paste::item! {
430                #[test]
431                fn [<$method _works>]() {
432                    ll_testing::with_each_device(|device| {
433                        let _result = device.$method().unwrap();
434                    })
435                }
436            }
437        };
438    }
439
440    // u32
441    test_method!(global_mem_cacheline_size);
442    test_method!(native_vector_width_double);
443    test_method!(native_vector_width_half);
444    test_method!(address_bits);
445    test_method!(max_clock_frequency);
446    test_method!(max_compute_units);
447    test_method!(max_constant_args);
448    test_method!(max_read_image_args);
449    test_method!(max_samplers);
450    test_method!(max_work_item_dimensions);
451    test_method!(max_write_image_args);
452    test_method!(mem_base_addr_align);
453    test_method!(min_data_type_align_size);
454    test_method!(native_vector_width_char);
455    test_method!(native_vector_width_short);
456    test_method!(native_vector_width_int);
457    test_method!(native_vector_width_long);
458    test_method!(native_vector_width_float);
459    test_method!(partition_max_sub_devices);
460    test_method!(preferred_vector_width_char);
461    test_method!(preferred_vector_width_short);
462    test_method!(preferred_vector_width_int);
463    test_method!(preferred_vector_width_long);
464    test_method!(preferred_vector_width_float);
465    test_method!(preferred_vector_width_double);
466    test_method!(preferred_vector_width_half);
467    test_method!(vendor_id);
468
469    // bool
470    test_method!(available);
471    test_method!(compiler_available);
472    test_method!(endian_little);
473    test_method!(error_correction_support);
474    test_method!(host_unified_memory);
475    test_method!(image_support);
476    test_method!(linker_available);
477    test_method!(preferred_interop_user_sync);
478
479    // String
480    test_method!(name);
481    test_method!(opencl_c_version);
482    test_method!(profile);
483    test_method!(vendor);
484    test_method!(version);
485    test_method!(driver_version);
486
487    // u64
488    test_method!(global_mem_cache_size);
489    test_method!(global_mem_size);
490    test_method!(local_mem_size);
491    test_method!(max_constant_buffer_size);
492    test_method!(max_mem_alloc_size);
493
494    // usize
495    test_method!(image2d_max_width);
496    test_method!(image2d_max_height);
497    test_method!(image3d_max_width);
498    test_method!(image3d_max_height);
499    test_method!(image3d_max_depth);
500    test_method!(image_max_buffer_size);
501    test_method!(image_max_array_size);
502    test_method!(max_parameter_size);
503    test_method!(max_work_group_size);
504    test_method!(printf_buffer_size);
505    test_method!(profiling_timer_resolution);
506
507    // Vec<usize>
508    test_method!(max_work_item_sizes);
509
510    // cl_device_local_mem_type
511    test_method!(local_mem_type);
512
513    // ExecutionCapabilities
514    test_method!(execution_capabilities);
515    //  CL_DEVICE_GLOBAL_MEM_CACHE_TYPE
516    test_method!(global_mem_cache_type);
517
518    // cl_device_affinity_domain
519    test_method!(partition_affinity_domain);
520
521    // DeviceType
522    test_method!(device_type);
523}