cl3/
event.rs

1// Copyright (c) 2020-2025 Via Technology Ltd. All Rights Reserved.
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` Event Object 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_COMMAND_ACQUIRE_GL_OBJECTS, CL_COMMAND_BARRIER, CL_COMMAND_COMMAND_BUFFER_KHR,
23    CL_COMMAND_COPY_BUFFER, CL_COMMAND_COPY_BUFFER_RECT, CL_COMMAND_COPY_BUFFER_TO_IMAGE,
24    CL_COMMAND_COPY_IMAGE, CL_COMMAND_COPY_IMAGE_TO_BUFFER, CL_COMMAND_FILL_BUFFER,
25    CL_COMMAND_FILL_IMAGE, CL_COMMAND_MAP_BUFFER, CL_COMMAND_MAP_IMAGE, CL_COMMAND_MARKER,
26    CL_COMMAND_MEMADVISE_INTEL, CL_COMMAND_MEMCPY_INTEL, CL_COMMAND_MEMFILL_INTEL,
27    CL_COMMAND_MIGRATE_MEM_OBJECTS, CL_COMMAND_MIGRATEMEM_INTEL, CL_COMMAND_NATIVE_KERNEL,
28    CL_COMMAND_NDRANGE_KERNEL, CL_COMMAND_READ_BUFFER, CL_COMMAND_READ_BUFFER_RECT,
29    CL_COMMAND_READ_IMAGE, CL_COMMAND_RELEASE_GL_OBJECTS, CL_COMMAND_SVM_FREE, CL_COMMAND_SVM_MAP,
30    CL_COMMAND_SVM_MEMCPY, CL_COMMAND_SVM_MEMFILL, CL_COMMAND_SVM_MIGRATE_MEM,
31    CL_COMMAND_SVM_UNMAP, CL_COMMAND_TASK, CL_COMMAND_UNMAP_MEM_OBJECT, CL_COMMAND_USER,
32    CL_COMMAND_WRITE_BUFFER, CL_COMMAND_WRITE_BUFFER_RECT, CL_COMMAND_WRITE_IMAGE, CL_COMPLETE,
33    CL_EVENT_COMMAND_EXECUTION_STATUS, CL_EVENT_COMMAND_QUEUE, CL_EVENT_COMMAND_TYPE,
34    CL_EVENT_CONTEXT, CL_EVENT_REFERENCE_COUNT, CL_INVALID_VALUE, CL_PROFILING_COMMAND_COMPLETE,
35    CL_PROFILING_COMMAND_END, CL_PROFILING_COMMAND_QUEUED, CL_PROFILING_COMMAND_START,
36    CL_PROFILING_COMMAND_SUBMIT, CL_QUEUED, CL_RUNNING, CL_SUBMITTED, CL_SUCCESS, cl_command_queue,
37    cl_command_type, cl_context, cl_event, cl_event_info, cl_int, cl_profiling_info, cl_uint,
38    cl_ulong,
39};
40
41pub use opencl_sys::cl_egl::{
42    CL_COMMAND_ACQUIRE_EGL_OBJECTS_KHR, CL_COMMAND_EGL_FENCE_SYNC_OBJECT_KHR,
43    CL_COMMAND_RELEASE_EGL_OBJECTS_KHR,
44};
45
46use super::info_type::InfoType;
47use super::{api_info_size, api_info_value, api_info_vector};
48use libc::{c_void, intptr_t, size_t};
49use std::fmt;
50use std::mem;
51use std::ptr;
52
53/// Wait for `OpenCL` events to complete.
54/// Calls `clWaitForEvents`.
55///
56/// * `events` - a slice of `OpenCL` events.
57///
58/// returns an empty Result or the error code from the `OpenCL` C API function.
59#[inline]
60#[allow(clippy::cast_possible_truncation)]
61pub fn wait_for_events(events: &[cl_event]) -> Result<(), cl_int> {
62    let status: cl_int =
63        unsafe { cl_call!(clWaitForEvents(events.len() as cl_uint, events.as_ptr())) };
64    if CL_SUCCESS == status {
65        Ok(())
66    } else {
67        Err(status)
68    }
69}
70
71/// Get data about an `OpenCL` event.
72/// Calls `clGetEventInfo` to get the desired data about the event.
73pub fn get_event_data(event: cl_event, param_name: cl_event_info) -> Result<Vec<u8>, cl_int> {
74    api_info_size!(get_size, clGetEventInfo);
75    let size = get_size(event, param_name)?;
76    api_info_vector!(get_vector, u8, clGetEventInfo);
77    get_vector(event, param_name, size)
78}
79
80/// Get specific information about an `OpenCL` event.
81/// Calls `clGetEventInfo` to get the desired information about the event.
82///
83/// * `event` - the `OpenCL` event.
84/// * `param_name` - the type of program information being queried, see:
85///   [Event Object Queries](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#event-info-table).
86///
87/// returns a Result containing the desired information in an `InfoType` enum
88/// or the error code from the `OpenCL` C API function.
89pub fn get_event_info(event: cl_event, param_name: cl_event_info) -> Result<InfoType, cl_int> {
90    match param_name {
91        CL_EVENT_COMMAND_EXECUTION_STATUS => {
92            api_info_value!(get_value, cl_int, clGetEventInfo);
93            Ok(InfoType::Int(get_value(event, param_name)?))
94        }
95
96        CL_EVENT_COMMAND_TYPE | CL_EVENT_REFERENCE_COUNT => {
97            api_info_value!(get_value, cl_uint, clGetEventInfo);
98            Ok(InfoType::Uint(get_value(event, param_name)?))
99        }
100
101        CL_EVENT_COMMAND_QUEUE | CL_EVENT_CONTEXT => {
102            api_info_value!(get_value, intptr_t, clGetEventInfo);
103            Ok(InfoType::Ptr(get_value(event, param_name)?))
104        }
105
106        _ => Ok(InfoType::VecUchar(get_event_data(event, param_name)?)),
107    }
108}
109
110/// Create an `OpenCL` user event object.
111/// Calls `clCreateUserEvent` to create an `OpenCL` event.
112///
113/// * `context` - a valid `OpenCL` context.
114///
115/// returns a Result containing the new `OpenCL` event object
116/// or the error code from the `OpenCL` C API function.
117#[inline]
118pub fn create_user_event(context: cl_context) -> Result<cl_event, cl_int> {
119    let mut status: cl_int = CL_INVALID_VALUE;
120    let event: cl_event = unsafe { cl_call!(clCreateUserEvent(context, &raw mut status)) };
121    if CL_SUCCESS == status {
122        Ok(event)
123    } else {
124        Err(status)
125    }
126}
127
128/// Retain an `OpenCL` event.
129/// Calls clRetainEvent to increment the event reference count.
130///
131/// * `event` - the `OpenCL` event.
132///
133/// returns an empty Result or the error code from the `OpenCL` C API function.
134///
135/// # Safety
136///
137/// This function is unsafe because it changes the `OpenCL` object reference count.
138#[inline]
139pub unsafe fn retain_event(event: cl_event) -> Result<(), cl_int> {
140    let status: cl_int = cl_call!(clRetainEvent(event));
141    if CL_SUCCESS == status {
142        Ok(())
143    } else {
144        Err(status)
145    }
146}
147
148/// Release an `OpenCL` event.
149/// Calls `clReleaseEvent` to decrement the event reference count.
150///
151/// * `event` - the `OpenCL` event.
152///
153/// returns an empty Result or the error code from the `OpenCL` C API function.
154///
155/// # Safety
156///
157/// This function is unsafe because it changes the `OpenCL` object reference count.
158#[inline]
159pub unsafe fn release_event(event: cl_event) -> Result<(), cl_int> {
160    let status: cl_int = cl_call!(clReleaseEvent(event));
161    if CL_SUCCESS == status {
162        Ok(())
163    } else {
164        Err(status)
165    }
166}
167
168/// Set the execution status of a user event object.
169/// Calls `clSetUserEventStatus` to set the execution status.
170///
171/// * `event` - the `OpenCL` event.
172/// * `execution_status` - the `OpenCL` `execution_status`.
173///
174/// returns an empty Result or the error code from the `OpenCL` C API function.
175#[inline]
176pub fn set_user_event_status(event: cl_event, execution_status: cl_int) -> Result<(), cl_int> {
177    let status: cl_int = unsafe { cl_call!(clSetUserEventStatus(event, execution_status)) };
178    if CL_SUCCESS == status {
179        Ok(())
180    } else {
181        Err(status)
182    }
183}
184
185/// Register a user callback function for a specific command execution status,
186/// Calls `clSetEventCallback` to register a callback function.
187///
188/// * `event` - the `OpenCL` event.
189/// * `pfn_notify` - function pointer to the callback function.
190/// * `user_data` - passed as an argument when `pfn_notify` is called, or `ptr::null_mut()`.
191///
192/// returns an empty Result or the error code from the `OpenCL` C API function.
193#[inline]
194pub fn set_event_callback(
195    event: cl_event,
196    command_exec_callback_type: cl_int,
197    pfn_notify: extern "C" fn(cl_event, cl_int, *mut c_void),
198    user_data: *mut c_void,
199) -> Result<(), cl_int> {
200    let status: cl_int = unsafe {
201        cl_call!(clSetEventCallback(
202            event,
203            command_exec_callback_type,
204            Some(pfn_notify),
205            user_data,
206        ))
207    };
208    if CL_SUCCESS == status {
209        Ok(())
210    } else {
211        Err(status)
212    }
213}
214
215/// Get profiling data about an `OpenCL` event.
216/// Calls `clGetEventProfilingInfo` to get the desired profiling data about the event.
217pub fn get_event_profiling_data(
218    event: cl_event,
219    param_name: cl_profiling_info,
220) -> Result<Vec<u8>, cl_int> {
221    api_info_size!(get_size, clGetEventProfilingInfo);
222    let size = get_size(event, param_name)?;
223    api_info_vector!(get_vector, u8, clGetEventProfilingInfo);
224    get_vector(event, param_name, size)
225}
226
227/// Get profiling information for a command associated with an event when
228/// profiling is enabled.
229/// Calls clGetEventProfilingInfo to get the desired information.
230///
231/// * `event` - the `OpenCL` event.
232/// * `param_name` - the type of event profiling information being queried, see:
233///   [Event Profiling Queries](https://www.khronos.org/registry/OpenCL/specs/3.0-unified/html/OpenCL_API.html#event-profiling-info-table).
234///
235/// returns a Result containing the desired information in an `InfoType` enum
236/// or the error code from the `OpenCL` C API function.
237pub fn get_event_profiling_info(
238    event: cl_event,
239    param_name: cl_profiling_info,
240) -> Result<InfoType, cl_int> {
241    match param_name {
242        CL_PROFILING_COMMAND_QUEUED
243        | CL_PROFILING_COMMAND_SUBMIT
244        | CL_PROFILING_COMMAND_START
245        | CL_PROFILING_COMMAND_END
246        | CL_PROFILING_COMMAND_COMPLETE // CL_VERSION_2_0
247         => {
248            api_info_value!(get_value, cl_ulong, clGetEventProfilingInfo);
249            Ok(InfoType::Ulong(get_value(event, param_name)?))
250        }
251
252        _ => Ok(InfoType::VecUchar(get_event_profiling_data(event, param_name)?))
253    }
254}
255
256#[must_use]
257pub const fn status_text(status: cl_int) -> &'static str {
258    match status {
259        CL_COMPLETE => "CL_COMPLETE",
260        CL_RUNNING => "CL_RUNNING",
261        CL_SUBMITTED => "CL_SUBMITTED",
262        CL_QUEUED => "CL_QUEUED",
263        _ => "UNKNOWN_STATUS",
264    }
265}
266
267#[derive(Debug)]
268/// `CommandExecutionStatus` is a newtype around the `OpenCL` command execution status
269pub struct CommandExecutionStatus(pub cl_int);
270
271/// Implement the From trait
272impl From<cl_int> for CommandExecutionStatus {
273    fn from(status: cl_int) -> Self {
274        Self(status)
275    }
276}
277
278/// Implement the Display trait
279impl fmt::Display for CommandExecutionStatus {
280    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281        write!(f, "{}", status_text(self.0))
282    }
283}
284
285#[must_use]
286pub const fn command_type_text(command_type: cl_command_type) -> &'static str {
287    match command_type {
288        CL_COMMAND_NDRANGE_KERNEL => "CL_COMMAND_NDRANGE_KERNEL",
289        CL_COMMAND_TASK => "CL_COMMAND_TASK",
290        CL_COMMAND_NATIVE_KERNEL => "CL_COMMAND_NATIVE_KERNEL",
291        CL_COMMAND_READ_BUFFER => "CL_COMMAND_READ_BUFFER",
292        CL_COMMAND_WRITE_BUFFER => "CL_COMMAND_WRITE_BUFFER",
293        CL_COMMAND_COPY_BUFFER => "CL_COMMAND_COPY_BUFFER",
294        CL_COMMAND_READ_IMAGE => "CL_COMMAND_READ_IMAGE",
295        CL_COMMAND_WRITE_IMAGE => "CL_COMMAND_WRITE_IMAGE",
296        CL_COMMAND_COPY_IMAGE => "CL_COMMAND_COPY_IMAGE",
297        CL_COMMAND_COPY_IMAGE_TO_BUFFER => "CL_COMMAND_COPY_IMAGE_TO_BUFFER",
298        CL_COMMAND_COPY_BUFFER_TO_IMAGE => "CL_COMMAND_COPY_BUFFER_TO_IMAGE",
299        CL_COMMAND_MAP_BUFFER => "CL_COMMAND_MAP_BUFFER",
300        CL_COMMAND_MAP_IMAGE => "CL_COMMAND_MAP_IMAGE",
301        CL_COMMAND_UNMAP_MEM_OBJECT => "CL_COMMAND_UNMAP_MEM_OBJECT",
302        CL_COMMAND_MARKER => "CL_COMMAND_MARKER",
303        CL_COMMAND_ACQUIRE_GL_OBJECTS => "CL_COMMAND_ACQUIRE_GL_OBJECTS",
304        CL_COMMAND_RELEASE_GL_OBJECTS => "CL_COMMAND_RELEASE_GL_OBJECTS",
305        CL_COMMAND_READ_BUFFER_RECT => "CL_COMMAND_READ_BUFFER_RECT",
306        CL_COMMAND_WRITE_BUFFER_RECT => "CL_COMMAND_WRITE_BUFFER_RECT",
307        CL_COMMAND_COPY_BUFFER_RECT => "CL_COMMAND_COPY_BUFFER_RECT",
308        CL_COMMAND_USER => "CL_COMMAND_USER",
309        CL_COMMAND_BARRIER => "CL_COMMAND_BARRIER",
310        CL_COMMAND_MIGRATE_MEM_OBJECTS => "CL_COMMAND_MIGRATE_MEM_OBJECTS",
311        CL_COMMAND_FILL_BUFFER => "CL_COMMAND_FILL_BUFFER",
312        CL_COMMAND_FILL_IMAGE => "CL_COMMAND_FILL_IMAGE",
313        CL_COMMAND_SVM_FREE => "CL_COMMAND_SVM_FREE",
314        CL_COMMAND_SVM_MEMCPY => "CL_COMMAND_SVM_MEMCPY",
315        CL_COMMAND_SVM_MEMFILL => "CL_COMMAND_SVM_MEMFILL",
316        CL_COMMAND_SVM_MAP => "CL_COMMAND_SVM_MAP",
317        CL_COMMAND_SVM_UNMAP => "CL_COMMAND_SVM_UNMAP",
318        CL_COMMAND_SVM_MIGRATE_MEM => "CL_COMMAND_SVM_MIGRATE_MEM",
319
320        // cl_egl values
321        CL_COMMAND_ACQUIRE_EGL_OBJECTS_KHR => "CL_COMMAND_ACQUIRE_EGL_OBJECTS_KHR",
322        CL_COMMAND_RELEASE_EGL_OBJECTS_KHR => "CL_COMMAND_RELEASE_EGL_OBJECTS_KHR",
323        CL_COMMAND_EGL_FENCE_SYNC_OBJECT_KHR => "CL_COMMAND_EGL_FENCE_SYNC_OBJECT_KHR",
324
325        // cl_ext values
326        CL_COMMAND_MEMFILL_INTEL => "CL_COMMAND_MEMFILL_INTEL",
327        CL_COMMAND_MEMCPY_INTEL => "CL_COMMAND_MEMCPY_INTEL",
328        CL_COMMAND_MIGRATEMEM_INTEL => "CL_COMMAND_MIGRATEMEM_INTEL",
329        CL_COMMAND_MEMADVISE_INTEL => "CL_COMMAND_MEMADVISE_INTEL",
330
331        // cl_khr_command_buffer
332        CL_COMMAND_COMMAND_BUFFER_KHR => "CL_COMMAND_COMMAND_BUFFER_KHR",
333
334        _ => "UNKNOWN_COMMAND_TYPE",
335    }
336}
337
338#[derive(Debug)]
339/// `EventCommandType` is a newtype around the `OpenCL` `cl_command_type`
340pub struct EventCommandType(pub cl_command_type);
341
342/// Implement the From trait for `EventCommandType`
343impl From<cl_command_type> for EventCommandType {
344    fn from(command_type: cl_command_type) -> Self {
345        Self(command_type)
346    }
347}
348
349/// Implement the Display trait for `EventCommandType`
350impl fmt::Display for EventCommandType {
351    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
352        write!(f, "{}", command_type_text(self.0))
353    }
354}
355
356#[cfg(test)]
357mod tests {
358    use super::*;
359
360    #[test]
361    fn test_status_text() {
362        let text = status_text(CL_COMPLETE);
363        assert_eq!("CL_COMPLETE", text);
364
365        let text = status_text(CL_RUNNING);
366        assert_eq!("CL_RUNNING", text);
367
368        let text = status_text(CL_SUBMITTED);
369        assert_eq!("CL_SUBMITTED", text);
370
371        let text = status_text(CL_QUEUED);
372        assert_eq!("CL_QUEUED", text);
373
374        let text = status_text(CL_QUEUED + 1);
375        assert_eq!("UNKNOWN_STATUS", text);
376    }
377
378    #[test]
379    fn test_command_type_text() {
380        let text = command_type_text(CL_COMMAND_NDRANGE_KERNEL);
381        assert_eq!("CL_COMMAND_NDRANGE_KERNEL", text);
382
383        let text = command_type_text(CL_COMMAND_COPY_IMAGE);
384        assert_eq!("CL_COMMAND_COPY_IMAGE", text);
385
386        let text = command_type_text(CL_COMMAND_READ_BUFFER_RECT);
387        assert_eq!("CL_COMMAND_READ_BUFFER_RECT", text);
388
389        let text = command_type_text(CL_COMMAND_BARRIER);
390        assert_eq!("CL_COMMAND_BARRIER", text);
391
392        let text = command_type_text(CL_COMMAND_SVM_FREE);
393        assert_eq!("CL_COMMAND_SVM_FREE", text);
394
395        let text = command_type_text(CL_COMMAND_SVM_MIGRATE_MEM + 1);
396        assert_eq!("UNKNOWN_COMMAND_TYPE", text);
397    }
398}