cl3/
context.rs

1// Copyright (c) 2020-2025 Via Technology Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! `OpenCL` Context API.
16
17#![allow(unused_unsafe)]
18#![allow(non_camel_case_types)]
19#![allow(clippy::not_unsafe_ptr_arg_deref)]
20
21pub use opencl_sys::{
22    CL_CONTEXT_DEVICES, CL_CONTEXT_INTEROP_USER_SYNC, CL_CONTEXT_NUM_DEVICES, CL_CONTEXT_PLATFORM,
23    CL_CONTEXT_PROPERTIES, CL_CONTEXT_REFERENCE_COUNT, CL_INVALID_VALUE, CL_SUCCESS, cl_context,
24    cl_context_info, cl_context_properties, cl_device_id, cl_device_type, cl_int, cl_uint,
25};
26
27use super::info_type::InfoType;
28use super::{api_info_size, api_info_value, api_info_vector};
29use libc::{c_char, c_void, intptr_t, size_t};
30use std::mem;
31use std::ptr;
32
33/// Create an `OpenCL` context.
34/// Calls clCreateContext to create an `OpenCL` context.
35///
36/// * `devices` - a slice of unique devices for an `OpenCL` platform.
37/// * `properties` - a null terminated list of `cl_context_properties`, see
38///   [Context Properties](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#context-properties-table).
39/// * `pfn_notify` - an optional callback function that can be registered by the application.
40/// * `user_data` - passed as the `user_data` argument when `pfn_notify` is called.
41///
42/// returns a Result containing the new `OpenCL` context
43/// or the error code from the `OpenCL` C API function.
44#[allow(unused_unsafe)]
45#[allow(clippy::cast_possible_truncation)]
46#[inline]
47pub fn create_context(
48    devices: &[cl_device_id],
49    properties: *const cl_context_properties,
50    pfn_notify: Option<unsafe extern "C" fn(*const c_char, *const c_void, size_t, *mut c_void)>,
51    user_data: *mut c_void,
52) -> Result<cl_context, cl_int> {
53    let mut status: cl_int = CL_INVALID_VALUE;
54    let context = unsafe {
55        cl_call!(clCreateContext(
56            properties,
57            devices.len() as cl_uint,
58            devices.as_ptr(),
59            pfn_notify,
60            user_data,
61            &raw mut status,
62        ))
63    };
64    if CL_SUCCESS == status {
65        Ok(context)
66    } else {
67        Err(status)
68    }
69}
70
71/// Create an `OpenCL` context from a specific device type.
72/// Calls `clCreateContextFromType` to create an `OpenCL` context.
73///
74/// * `device_type` - the type of `OpenCL` device, see:
75///   [Device Types](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#device-types-table).
76/// * `properties` - a null terminated list of `cl_context_properties`, see:
77///   [Context Properties](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#context-properties-table).
78/// * `pfn_notify` - an optional callback function that can be registered by the application.
79/// * `user_data` - passed as the `user_data` argument when `pfn_notify` is called.
80///
81/// returns a Result containing the new `OpenCL` context
82/// or the error code from the `OpenCL` C API function.
83#[inline]
84pub fn create_context_from_type(
85    device_type: cl_device_type,
86    properties: *const cl_context_properties,
87    pfn_notify: Option<unsafe extern "C" fn(*const c_char, *const c_void, size_t, *mut c_void)>,
88    user_data: *mut c_void,
89) -> Result<cl_context, cl_int> {
90    let mut status: cl_int = CL_INVALID_VALUE;
91    let context = unsafe {
92        cl_call!(clCreateContextFromType(
93            properties,
94            device_type,
95            pfn_notify,
96            user_data,
97            &raw mut status
98        ))
99    };
100    if CL_SUCCESS == status {
101        Ok(context)
102    } else {
103        Err(status)
104    }
105}
106
107/// Retain an `OpenCL` context.
108/// Calls clRetainContext to increment the context reference count.
109///
110/// * `context` - the `cl_context` of the `OpenCL` context.
111///
112/// returns an empty Result or the error code from the `OpenCL` C API function.
113///
114/// # Safety
115///
116/// This function is unsafe because it changes the `OpenCL` object reference count.
117#[inline]
118pub unsafe fn retain_context(context: cl_context) -> Result<(), cl_int> {
119    let status: cl_int = cl_call!(clRetainContext(context));
120    if CL_SUCCESS == status {
121        Ok(())
122    } else {
123        Err(status)
124    }
125}
126
127/// Release an `OpenCL` context.
128/// Calls clReleaseContext to decrement the context reference count.
129///
130/// * `context` - the `cl_context` of the `OpenCL` context.
131///
132/// returns an empty Result or the error code from the `OpenCL` C API function.
133///
134/// # Safety
135///
136/// This function is unsafe because it changes the `OpenCL` object reference count.
137#[inline]
138pub unsafe fn release_context(context: cl_context) -> Result<(), cl_int> {
139    let status: cl_int = cl_call!(clReleaseContext(context));
140    if CL_SUCCESS == status {
141        Ok(())
142    } else {
143        Err(status)
144    }
145}
146
147/// Get data about an `OpenCL` context.
148/// Calls `clGetContextInfo` to get the desired data about the context.
149pub fn get_context_data(
150    context: cl_context,
151    param_name: cl_context_info,
152) -> Result<Vec<u8>, cl_int> {
153    api_info_size!(get_size, clGetContextInfo);
154    let size = get_size(context, param_name)?;
155    api_info_vector!(get_vector, u8, clGetContextInfo);
156    get_vector(context, param_name, size)
157}
158
159/// Get specific information about an `OpenCL` context.
160/// Calls `clGetContextInfo` to get the desired information about the context.
161///
162/// * `context` - the `cl_context` of the `OpenCL` context.
163/// * `param_name` - the type of platform information being queried, see:
164///   [Context Attributes](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#context-info-table).
165///
166/// returns a Result containing the desired information in an `InfoType` enum
167/// or the error code from the `OpenCL` C API function.
168pub fn get_context_info(
169    context: cl_context,
170    param_name: cl_context_info,
171) -> Result<InfoType, cl_int> {
172    api_info_size!(get_size, clGetContextInfo);
173
174    match param_name {
175        CL_CONTEXT_REFERENCE_COUNT | CL_CONTEXT_NUM_DEVICES => {
176            api_info_value!(get_value, cl_uint, clGetContextInfo);
177            Ok(InfoType::Uint(get_value(context, param_name)?))
178        }
179
180        CL_CONTEXT_DEVICES | CL_CONTEXT_PROPERTIES => {
181            api_info_vector!(get_vec, intptr_t, clGetContextInfo);
182            let size = get_size(context, param_name)?;
183            Ok(InfoType::VecIntPtr(get_vec(context, param_name, size)?))
184        }
185
186        _ => Ok(InfoType::VecUchar(get_context_data(context, param_name)?)),
187    }
188}
189
190/// Register a callback function with a context that is called when the `context` is destroyed.
191/// Calls `clSetContextDestructorCallback`.
192/// `CL_VERSION_3_0`
193///
194/// * `context` - the `cl_context` of the `OpenCL` context.
195/// * `pfn_notify` - callback function to be registered by the application.
196/// * `user_data` - passed as the `user_data` argument when `pfn_notify` is called.
197///
198/// returns an empty Result or the error code from the `OpenCL` C API function.
199#[cfg(any(feature = "CL_VERSION_3_0", feature = "dynamic"))]
200#[inline]
201pub fn set_context_destructor_callback(
202    context: cl_context,
203    pfn_notify: Option<unsafe extern "C" fn(cl_context, *mut c_void)>,
204    user_data: *mut c_void,
205) -> Result<(), cl_int> {
206    let status: cl_int = unsafe {
207        cl_call!(clSetContextDestructorCallback(
208            context, pfn_notify, user_data
209        ))
210    };
211    if CL_SUCCESS == status {
212        Ok(())
213    } else {
214        Err(status)
215    }
216}
217// #endif
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222    use crate::device::{CL_DEVICE_TYPE_GPU, get_device_ids};
223    use crate::platform::get_platform_ids;
224
225    #[test]
226    fn test_context() {
227        let platform_ids = get_platform_ids().unwrap();
228
229        // Choose the first platform
230        let platform_id = platform_ids[0];
231
232        let device_ids = get_device_ids(platform_id, CL_DEVICE_TYPE_GPU).unwrap();
233        assert!(0 < device_ids.len());
234
235        let context = create_context(&device_ids, ptr::null(), None, ptr::null_mut());
236        let context = context.unwrap();
237
238        let value = get_context_info(context, CL_CONTEXT_REFERENCE_COUNT).unwrap();
239        let value = cl_uint::from(value);
240        println!("CL_CONTEXT_REFERENCE_COUNT: {}", value);
241        assert!(0 < value);
242
243        let value = get_context_info(context, CL_CONTEXT_DEVICES).unwrap();
244        let value = Vec::<intptr_t>::from(value);
245        println!("CL_CONTEXT_DEVICES: {}", value.len());
246        println!("CL_CONTEXT_DEVICES: {:?}", value);
247        assert!(0 < value.len());
248
249        let value = get_context_info(context, CL_CONTEXT_PROPERTIES).unwrap();
250        let value = Vec::<intptr_t>::from(value);
251        println!("CL_CONTEXT_PROPERTIES: {}", value.len());
252        println!("CL_CONTEXT_PROPERTIES: {:?}", value);
253        // assert!(0 < value.len());
254
255        let value = get_context_info(context, CL_CONTEXT_NUM_DEVICES).unwrap();
256        let value = cl_uint::from(value);
257        println!("CL_CONTEXT_NUM_DEVICES: {}", value);
258        assert!(0 < value);
259
260        unsafe { release_context(context).unwrap() };
261    }
262}