opencl_api/api/
device.rs

1/*
2 * device.rs - Device API wrappers (Part of OpenCL Platform Layer).
3 *
4 * Copyright 2020-2021 Naman Bishnoi
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18use crate::objects::bitfields::DeviceType;
19use crate::objects::enums::{ParamValue, Size};
20use crate::objects::functions::{bytes_into_string, status_update};
21use crate::objects::structs::DeviceInfo;
22use crate::objects::traits::GetSetGo;
23use crate::objects::types::{APIResult, DeviceList, DevicePtr, PlatformPtr, Properties};
24use crate::{gen_object_list, gen_param_value, get_count, size_getter};
25use libc::c_void;
26use opencl_heads::ffi;
27use opencl_heads::ffi::{clCreateSubDevices, clGetDeviceIDs, clGetDeviceInfo};
28use opencl_heads::types::*;
29use std::ptr;
30
31pub fn get_device_ids(platform: &PlatformPtr, device_type: DeviceType) -> APIResult<DeviceList> {
32    let device_type = device_type.get();
33    let platform = platform.unwrap();
34    let device_count = get_count!(clGetDeviceIDs, platform, device_type);
35
36    if device_count == 0 {
37        Ok(Vec::default())
38    } else {
39        gen_object_list!(
40            clGetDeviceIDs,
41            DeviceList,
42            device_count,
43            platform,
44            device_type
45        )
46    }
47}
48
49pub fn get_device_info(device: &DevicePtr, param_name: cl_device_info) -> APIResult<ParamValue> {
50    type D = DeviceInfo;
51    let fn_name = "clGetDeviceInfo";
52    let device = device.unwrap();
53    size_getter!(get_device_info_size, clGetDeviceInfo);
54    match param_name {
55        D::NAME
56        | D::VENDOR
57        | D::VERSION
58        | D::PROFILE
59        | D::DRIVER_VERSION
60        | D::EXTENSIONS
61        | D::OPENCL_C_VERSION
62        | D::BUILT_IN_KERNELS
63        | D::IL_VERSION
64        | D::LATEST_CONFORMANCE_VERSION_PASSED => {
65            let size = get_device_info_size(device, param_name)?;
66            let param_value = gen_param_value!(clGetDeviceInfo, u8, device, param_name, size);
67            Ok(ParamValue::String(bytes_into_string(param_value)?))
68        }
69        D::VENDOR_ID
70        | D::MAX_COMPUTE_UNITS
71        | D::MAX_WORK_ITEM_DIMENSIONS
72        | D::PREFERRED_VECTOR_WIDTH_CHAR
73        | D::PREFERRED_VECTOR_WIDTH_SHORT
74        | D::PREFERRED_VECTOR_WIDTH_INT
75        | D::PREFERRED_VECTOR_WIDTH_LONG
76        | D::PREFERRED_VECTOR_WIDTH_FLOAT
77        | D::PREFERRED_VECTOR_WIDTH_DOUBLE
78        | D::PREFERRED_VECTOR_WIDTH_HALF
79        | D::NATIVE_VECTOR_WIDTH_CHAR
80        | D::NATIVE_VECTOR_WIDTH_SHORT
81        | D::NATIVE_VECTOR_WIDTH_INT
82        | D::NATIVE_VECTOR_WIDTH_LONG
83        | D::NATIVE_VECTOR_WIDTH_FLOAT
84        | D::NATIVE_VECTOR_WIDTH_DOUBLE
85        | D::NATIVE_VECTOR_WIDTH_HALF
86        | D::MAX_CLOCK_FREQUENCY
87        | D::ADDRESS_BITS
88        | D::MAX_READ_IMAGE_ARGS
89        | D::MAX_WRITE_IMAGE_ARGS
90        | D::MAX_READ_WRITE_IMAGE_ARGS
91        | D::MAX_SAMPLERS
92        | D::IMAGE_PITCH_ALIGNMENT
93        | D::IMAGE_BASE_ADDRESS_ALIGNMENT
94        | D::MAX_PIPE_ARGS
95        | D::PIPE_MAX_ACTIVE_RESERVATIONS
96        | D::PIPE_MAX_PACKET_SIZE
97        | D::MEM_BASE_ADDR_ALIGN
98        | D::MIN_DATA_TYPE_ALIGN_SIZE
99        | D::GLOBAL_MEM_CACHELINE_SIZE
100        | D::MAX_CONSTANT_ARGS
101        | D::QUEUE_ON_DEVICE_PREFERRED_SIZE
102        | D::QUEUE_ON_DEVICE_MAX_SIZE
103        | D::MAX_ON_DEVICE_QUEUES
104        | D::MAX_ON_DEVICE_EVENTS
105        | D::PARTITION_MAX_SUB_DEVICES
106        | D::REFERENCE_COUNT
107        | D::PREFERRED_PLATFORM_ATOMIC_ALIGNMENT
108        | D::PREFERRED_GLOBAL_ATOMIC_ALIGNMENT
109        | D::PREFERRED_LOCAL_ATOMIC_ALIGNMENT
110        | D::MAX_NUM_SUB_GROUPS
111        | D::IMAGE_SUPPORT
112        | D::ERROR_CORRECTION_SUPPORT
113        | D::HOST_UNIFIED_MEMORY
114        | D::ENDIAN_LITTLE
115        | D::AVAILABLE
116        | D::COMPILER_AVAILABLE
117        | D::LINKER_AVAILABLE
118        | D::PREFERRED_INTEROP_USER_SYNC
119        | D::SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS
120        | D::NON_UNIFORM_WORK_GROUP_SUPPORT
121        | D::WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT
122        | D::GENERIC_ADDRESS_SPACE_SUPPORT
123        | D::PIPE_SUPPORT
124        | D::NUMERIC_VERSION
125        | D::GLOBAL_MEM_CACHE_TYPE
126        | D::LOCAL_MEM_TYPE => {
127            let param_value = gen_param_value!(clGetDeviceInfo, u32, device, param_name);
128            Ok(ParamValue::UInt(param_value))
129        }
130        D::MAX_MEM_ALLOC_SIZE
131        | D::GLOBAL_MEM_CACHE_SIZE
132        | D::GLOBAL_MEM_SIZE
133        | D::MAX_CONSTANT_BUFFER_SIZE
134        | D::LOCAL_MEM_SIZE
135        | D::TYPE
136        | D::SINGLE_FP_CONFIG
137        | D::DOUBLE_FP_CONFIG
138        | D::EXECUTION_CAPABILITIES
139        | D::QUEUE_ON_HOST_PROPERTIES
140        | D::QUEUE_ON_DEVICE_PROPERTIES
141        | D::PARTITION_AFFINITY_DOMAIN
142        | D::SVM_CAPABILITIES
143        | D::ATOMIC_MEMORY_CAPABILITIES
144        | D::ATOMIC_FENCE_CAPABILITIES
145        | D::DEVICE_ENQUEUE_CAPABILITIES => {
146            let param_value = gen_param_value!(clGetDeviceInfo, u64, device, param_name);
147            Ok(ParamValue::ULong(param_value))
148        }
149        D::ILS_WITH_VERSION
150        | D::BUILT_IN_KERNELS_WITH_VERSION
151        | D::OPENCL_C_ALL_VERSIONS
152        | D::OPENCL_C_FEATURES
153        | D::EXTENSIONS_WITH_VERSION => {
154            let size = get_device_info_size(device, param_name)?;
155            let param_value =
156                gen_param_value!(clGetDeviceInfo, cl_name_version, device, param_name, size);
157            Ok(ParamValue::NameVersion(param_value))
158        }
159        D::MAX_WORK_GROUP_SIZE
160        | D::IMAGE2D_MAX_WIDTH
161        | D::IMAGE2D_MAX_HEIGHT
162        | D::IMAGE3D_MAX_WIDTH
163        | D::IMAGE3D_MAX_HEIGHT
164        | D::IMAGE3D_MAX_DEPTH
165        | D::IMAGE_MAX_BUFFER_SIZE
166        | D::IMAGE_MAX_ARRAY_SIZE
167        | D::MAX_PARAMETER_SIZE
168        | D::MAX_GLOBAL_VARIABLE_SIZE
169        | D::GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE
170        | D::PROFILING_TIMER_RESOLUTION
171        | D::PRINTF_BUFFER_SIZE
172        | D::PREFERRED_WORK_GROUP_SIZE_MULTIPLE => {
173            let param_value = gen_param_value!(clGetDeviceInfo, usize, device, param_name);
174            Ok(ParamValue::CSize(param_value))
175        }
176        D::PLATFORM | D::PARENT_DEVICE => {
177            let param_value = gen_param_value!(clGetDeviceInfo, isize, device, param_name);
178            Ok(ParamValue::CPtr(param_value))
179        }
180        D::PARTITION_PROPERTIES | D::PARTITION_TYPE => {
181            let size = get_device_info_size(device, param_name)?;
182            let param_value = gen_param_value!(clGetDeviceInfo, isize, device, param_name, size);
183            Ok(ParamValue::ArrCPtr(param_value))
184        }
185        D::MAX_WORK_ITEM_SIZES => {
186            let size = get_device_info_size(device, param_name)?;
187            let param_value = gen_param_value!(clGetDeviceInfo, usize, device, param_name, size);
188            Ok(ParamValue::ArrCSize(param_value))
189        }
190        _ => status_update(40404, fn_name, ParamValue::default()),
191    }
192}
193
194pub fn get_device_and_host_timer(device: &DevicePtr) -> APIResult<(cl_ulong, cl_ulong)> {
195    let fn_name = "clGetDeviceAndHostTimer";
196    let mut device_timestamp = cl_ulong::default();
197    let mut host_timestamp = cl_ulong::default();
198    let status_code = unsafe {
199        ffi::clGetDeviceAndHostTimer(device.unwrap(), &mut device_timestamp, &mut host_timestamp)
200    };
201    status_update(status_code, fn_name, (device_timestamp, host_timestamp))
202}
203/// `clGetHostTimer` returns the current value of the host clock as seen by device.
204/// This value is in the same timebase as the host_timestamp returned from `clGetDeviceAndHostTimer`.
205/// The implementation will return with as low a latency as possible to allow a correlation
206/// with a subsequent application sampled time. The host timestamp and device timestamp returned
207/// by this function and `clGetDeviceAndHostTimer` each have an implementation defined timebase.
208/// The timestamps will always be in their respective timebases regardless of which query function
209/// is used. The timestamp returned from `clGetEventProfilingInfo` for an event on a device and a
210/// device timestamp queried from the same device will always be in the same timebase.
211///
212/// `clGetHostTimer` will return CL_SUCCESS with a time value in host_timestamp if provided. Otherwise, it returns one of the following errors:
213///
214/// - CL_INVALID_DEVICE if device is not a valid device.
215///
216/// - CL_INVALID_OPERATION if the platform associated with device does not support device and host timer synchronization.
217///
218/// - CL_INVALID_VALUE if host_timestamp is NULL.
219///
220/// - CL_OUT_OF_RESOURCES if there is a failure to allocate resources required by the OpenCL implementation on the device.
221///
222/// - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the OpenCL implementation on the host.
223pub fn get_host_timer(device: &DevicePtr) -> APIResult<cl_ulong> {
224    let fn_name = "clGetHostTimer";
225    let mut host_timestamp = cl_ulong::default();
226    let status_code = unsafe { ffi::clGetHostTimer(device.unwrap(), &mut host_timestamp) };
227    status_update(status_code, fn_name, host_timestamp)
228}
229
230/// Partitioning a device
231pub fn create_sub_devices(in_device: &DevicePtr, properties: &Properties) -> APIResult<DeviceList> {
232    let in_device = in_device.unwrap();
233    let properties = match properties {
234        Some(x) => x.as_ptr(),
235        None => ptr::null(),
236    };
237    let device_partition_count = get_count!(clCreateSubDevices, in_device, properties);
238    let device_partition_count = device_partition_count * 8;
239    gen_object_list!(
240        clCreateSubDevices,
241        DeviceList,
242        device_partition_count,
243        in_device,
244        properties
245    )
246}
247
248pub fn retain_device(device: &DevicePtr) -> APIResult<()> {
249    let fn_name = "clRetainDevice";
250    let status_code = unsafe { ffi::clRetainDevice(device.unwrap()) };
251    status_update(status_code, fn_name, ())
252}
253
254pub fn release_device(device: DevicePtr) -> APIResult<()> {
255    let fn_name = "clReleaseDevice";
256    let status_code = unsafe { ffi::clReleaseDevice(device.unwrap()) };
257    status_update(status_code, fn_name, ())
258}
259
260/************************/
261/* /\ /\ /\ /\ /\ /\ /\ */
262/*|__|__|__|__|__|__|__|*/
263/*|  |  |  |  |  |  |  |*/
264/*|  |  Unit Tests  |  |*/
265/*|__|__|__|__|__|__|__|*/
266/*|__|__|__|__|__|__|__|*/
267/************************/
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272    use crate::api::platform;
273    use crate::objects::structs::{DevicePartitionProperty, PlatformInfo};
274    use crate::objects::types::PlatformPtr;
275    #[test]
276    fn test_get_device_info() {
277        let all_platforms = platform::get_platform_ids().unwrap();
278        assert_ne!(all_platforms.len(), 0);
279        // let id = all_platforms[0];
280        let id = PlatformPtr::from_ptr(all_platforms[0], "main_fn").unwrap();
281
282        let name = platform::get_platform_info(&id, PlatformInfo::NAME).unwrap();
283        let platform_name = name.unwrap_string().unwrap();
284        assert_ne!(platform_name, "");
285        println!("CL_PLATFORM_NAME: {}", platform_name);
286
287        let device =
288            DeviceType::new(DeviceType::CPU + DeviceType::GPU + DeviceType::DEFAULT).unwrap();
289
290        // device.set(DeviceType::GPU).unwrap();
291        let device_ids = get_device_ids(&id, device).unwrap();
292        assert!(0 < device_ids.len());
293        // Choose the first GPU device
294        let device_id = DevicePtr::from_ptr(device_ids[0], "test_fn").unwrap();
295        let vendor_name = get_device_info(&device_id, DeviceInfo::VENDOR).unwrap();
296        let vendor_name = vendor_name.unwrap_string().unwrap();
297        println!("CL_DEVICE_VENDOR_NAME: {}", vendor_name);
298        assert_ne!(vendor_name, "");
299        let vendor_id = get_device_info(&device_id, DeviceInfo::VENDOR_ID).unwrap();
300        let vendor_id = vendor_id.unwrap_uint().unwrap();
301        println!("CL_DEVICE_VENDOR_ID: {:X}", vendor_id);
302        assert_ne!(vendor_id, 0);
303    }
304
305    #[test]
306    #[ignore]
307    fn test_create_sub_device() {
308        let all_platforms = platform::get_platform_ids().unwrap();
309        assert_ne!(all_platforms.len(), 0);
310        let id = PlatformPtr::from_ptr(all_platforms[0], "main_fn").unwrap();
311
312        let name = platform::get_platform_info(&id, PlatformInfo::NAME).unwrap();
313        let platform_name = name.unwrap_string().unwrap();
314        assert_ne!(platform_name, "");
315        println!("CL_PLATFORM_NAME: {}", platform_name);
316
317        let device =
318            DeviceType::new(DeviceType::CPU + DeviceType::GPU + DeviceType::DEFAULT).unwrap();
319
320        let device_ids = get_device_ids(&id, device).unwrap();
321        assert!(0 < device_ids.len());
322        // Choose the first GPU device
323        // let device_id = device_ids[0];
324        let device_id = DevicePtr::from_ptr(device_ids[0], "test_fn").unwrap();
325        let vendor_name =
326            get_device_info(&device_id, DeviceInfo::PARTITION_MAX_SUB_DEVICES).unwrap();
327        let max_sub_devices = vendor_name.unwrap_uint().unwrap();
328        println!("CL_DEVICE_COMPUTE_UNITS: {}", max_sub_devices);
329
330        if max_sub_devices > 1 {
331            let sub_cu_count = max_sub_devices / 4;
332            let properties = DevicePartitionProperty.equally(sub_cu_count);
333
334            let sub_device_list = match create_sub_devices(&device_id, &properties) {
335                Ok(x) => x,
336                Err(x) => {
337                    eprintln!("{}", x);
338                    Vec::new()
339                }
340            };
341            println!("CL_DEVICE_LIST: {:?}", sub_device_list);
342            assert!(0 < sub_device_list.len());
343        }
344    }
345}