pxl_rust/runtime/map.rs
1//! Abstracts data-parallel computation based on a map operation for a given job.
2//!
3//! `Map` module enables efficient execution of the same device kernel across
4//! multiple chunks of input data in parallel. It is designed to work within a job
5//! context, leveraging hardware-level parallelism in XCENA device.
6//!
7//! It applies the same device kernel across each chunk of the input data,
8//! leveraging hardware-level parallelism for performance.
9//!
10//! This module provides a Rust abstraction around the C++ `pxl::runtime::Map` class.
11use crate::ffi::ArgInfo_t;
12use crate::ffi::{self, PxlResult};
13use crate::runtime::Stream;
14use std::ffi::c_void;
15use std::pin::Pin;
16
17/// Abstracts data-parallel computation based on a map operation for a given job.
18///
19/// This struct wraps a FFI pointer to the C++ `pxl::runtime::Map` class.
20/// It provides methods for executing parallel processing, synchronizing operations,
21/// and setting up asynchronous callbacks and stream.
22pub struct Map {
23 /// Pointer to the underlying FFI `Map` object.
24 inner: *mut ffi::Map,
25 /// Pointer to the underlying FFI `Stream` object.
26 stream_: *mut ffi::Stream,
27}
28
29/// Implementation of the `Map` struct.
30impl Map {
31 /// Creates a new `Map` wrapper from a raw FFI pointer.
32 /// This is called in runtime wrapper APIs requiring an actual `Map` object.
33 /// # Arguments
34 /// * `ptr` - A raw pointer to the underlying FFI `Map` object.
35 /// # Safety
36 /// Caller must ensure that `ptr` is valid and remains valid for the lifetime of `Map`.
37 pub(crate) fn new(ptr: *mut ffi::Map) -> Self {
38 Map {
39 inner: ptr,
40 stream_: unsafe { ffi::createStream() },
41 }
42 }
43
44 pub(crate) fn destroy(&mut self) {
45 if !self.inner.is_null() {
46 unsafe { ffi::destroy_map(self.inner) };
47 unsafe { ffi::destroyStream(self.stream_) };
48 self.inner = std::ptr::null_mut();
49 self.stream_ = std::ptr::null_mut();
50 }
51 }
52
53 /// gets the raw pointer of the underlying FFI `Map` object.
54 pub fn get(&self) -> *mut ffi::Map {
55 self.inner
56 }
57
58 /// Sets success callback function with user data.
59 /// # Arguments
60 /// * `callback` - Callback function as a c_void pointer.
61 /// * `user_data` - User data as a c_void pointer.
62 /// # Safety
63 /// Caller must ensure that the callback, user data remain valid for the lifetime of `Map`.
64 /// # Example
65 /// ```
66 /// use std::ffi::c_void;
67 /// use std::sync::{
68 /// atomic::{AtomicBool, Ordering},
69 /// Arc,
70 /// }; ///
71 /// unsafe extern "C" fn completion_callback(ptr: *mut c_void) {
72 /// let flag = ptr as *const AtomicBool;
73 /// println!("✅ Completion callback called with ptr: {:?}", flag);
74 /// (*flag).store(true, Ordering::SeqCst);
75 /// }
76 /// let done_flag = Arc::new(AtomicBool::new(false));
77 /// let done_ptr = Arc::into_raw(done_flag.clone()) as *mut c_void;
78 /// map.set_completion_callback(completion_callback as *mut c_void, done_ptr);
79 /// ```
80 pub fn set_completion_callback(&mut self, callback: *mut c_void, user_data: *mut c_void) {
81 unsafe {
82 ffi::map_set_completion_callback(
83 self.inner,
84 callback as *mut ffi::c_void,
85 user_data as *mut ffi::c_void,
86 );
87 }
88 }
89
90 /// Sets error callback function with user data.
91 /// # Arguments
92 /// * `callback` - Callback function as a `c_void` pointer.
93 /// * `user_data` - User data as a `c_void` pointer.
94 /// # Safety
95 /// Caller must ensure that the callback and user data remain valid for the lifetime of `Map`.
96 /// # Example
97 /// ```
98 /// use std::ffi::c_void;
99 /// use std::sync::{
100 /// atomic::{AtomicBool, Ordering},
101 /// Arc,
102 /// };
103 /// unsafe extern "C" fn error_callback(ptr: *mut c_void) {
104 /// let flag = ptr as *const AtomicBool;
105 /// println!("❌ Error callback called with ptr: {:?}", flag);
106 /// (*flag).store(true, Ordering::SeqCst);
107 /// }
108 /// let error_flag = Arc::new(AtomicBool::new(false));
109 /// let error_ptr = Arc::into_raw(error_flag.clone()) as *mut c_void;
110 /// map.set_error_callback(error_callback as *mut c_void, error_ptr);
111 /// ```
112 pub fn set_error_callback(&mut self, callback: *mut c_void, user_data: *mut c_void) {
113 unsafe {
114 ffi::map_set_error_callback(
115 self.inner,
116 callback as *mut ffi::c_void,
117 user_data as *mut ffi::c_void,
118 );
119 }
120 }
121
122 /// Sets message callback function with user data.
123 /// # Arguments
124 /// * `callback` - Callback function as a `c_void` pointer.
125 /// * `user_data` - User data as a `c_void` pointer.
126 /// # Safety
127 /// Caller must ensure that the callback and user data remain valid for the lifetime of `Map`.
128 /// # Example
129 /// ```
130 /// use std::ffi::c_void;
131 /// use std::sync::Arc;
132 /// unsafe extern "C" fn message_callback(ptr: *mut c_void) {
133 /// println!("📩 Message callback called with ptr: {:?}", ptr);
134 /// }
135 /// let message_data = Arc::new(42); // Example user data
136 /// let message_ptr = Arc::into_raw(message_data.clone()) as *mut c_void;
137 /// map.set_message_callback(message_callback as *mut c_void, message_ptr);
138 /// ```
139 pub fn set_message_callback(&mut self, callback: *mut c_void, user_data: *mut c_void) {
140 unsafe {
141 ffi::map_set_message_callback(
142 self.inner,
143 callback as *mut ffi::c_void,
144 user_data as *mut ffi::c_void,
145 );
146 }
147 }
148
149 pub fn set_batch_size(&mut self, batch_size: &u32) {
150 unsafe {
151 let inner_pin = Pin::new_unchecked(&mut *self.inner);
152 inner_pin.setBatchSize(batch_size);
153 }
154 }
155
156 /// Sets the cluster bitmap for the map operation.
157 /// # Arguments
158 /// * `cluster_bitmap` - The cluster bitmap to be used for the map operation (default = 0xF).
159 /// # Example
160 /// ```
161 /// map.set_cluster_bitmap(0xF); // Use all clusters
162 /// ```
163 pub fn set_cluster_bitmap(&mut self, cluster_bitmap: u32) {
164 unsafe {
165 let inner_pin = Pin::new_unchecked(&mut *self.inner);
166 inner_pin.setClusterBitmap(&cluster_bitmap);
167 }
168 }
169
170 /// Sets the stream to the default stream.
171 /// # Example
172 /// ```
173 /// map.set_default_stream()?;
174 /// ```
175 pub fn set_default_stream(&mut self) {
176 unsafe {
177 ffi::map_set_default_stream(self.inner);
178 }
179 }
180
181 /// Sets the `Stream` for `Map` object.
182 /// # Arguments
183 /// * `stream` - A reference to the `Stream` object to be set.
184 /// # Example
185 /// ```rust
186 /// let stream = pxl::runtime::create_stream();
187 /// map.set_stream(&stream);
188 /// ```
189 pub fn set_stream(&mut self, stream: &Stream) {
190 self.stream_ = stream.get();
191 }
192
193 /// Returns the stream ID that the map operation is associated with.
194 /// # Returns
195 /// The stream ID as a `u32`.
196 /// # Example
197 /// ```
198 /// let stream_id = map.stream_id();
199 /// println!("Current stream ID: {}", stream_id);
200 /// ```
201 pub fn stream_id(&self) -> u32 {
202 unsafe { (*self.inner).streamId() }
203 }
204
205 /// Executes with the given arguments.
206 /// This is called in execute! macro.
207 /// # Arguments
208 /// * `args` - A vector of `ArgInfo_t` objects representing the arguments to be passed.
209 /// # Returns
210 /// Execution status as a PxlResult.
211 /// # Safety
212 /// Caller must ensure that the `args` vector is valid and contains valid pointers.
213 /// # Example
214 /// Please refer to usage of `execute!` macro.
215 pub fn execute(&mut self, args: &Vec<ArgInfo_t>) -> PxlResult {
216 unsafe {
217 // println!("Executing with arguments:");
218 // for arg in args.iter() {
219 // println!("{:?}", arg);
220 // }
221 let args_ptr = args.as_ptr() as *mut ArgInfo_t;
222 let num_args = args.len();
223 ffi::map_execute(self.inner, args_ptr, num_args)
224 }
225 }
226
227 /// Synchronizes the `Map` operation after execute request.
228 /// # Returns
229 /// Synchronization status as a PxlResult.
230 /// # Safety
231 /// Caller must ensure that execution status returned from `execute` is valid.
232 /// # Example
233 /// ```rust
234 /// let ret = map.synchronize();
235 /// ```
236 pub fn synchronize(&mut self) -> PxlResult {
237 unsafe { Pin::new_unchecked(&mut *self.inner).synchronize() }
238 }
239
240 /// Gets the execution status of the map operation.
241 /// # Returns
242 /// `ExecuteStatus` indicating the current execution status.
243 /// # Example
244 /// ```
245 /// use pxl::ExecuteStatus;
246 /// let status = map.get_execute_status();
247 /// match status {
248 /// ExecuteStatus::ExecuteDone => println!("Execution completed successfully"),
249 /// ExecuteStatus::Fail => println!("Execution failed"),
250 /// _ => println!("Execution in progress or other status"),
251 /// }
252 /// ```
253 pub fn get_execute_status(&self) -> ffi::ExecuteStatus {
254 unsafe { (*self.inner).getExecuteStatus() }
255 }
256
257 /// Gets the error report of the map operation.
258 /// # Returns
259 /// `Vec<KernelError_t>` containing the error report.
260 /// If successful, the beginIndex and endIndex will be 0xFFFFFFFF.
261 /// If unsuccessful, they indicate the range of failed task index.
262 /// # Example
263 /// ```
264 /// let kernel_errors = map.get_kernel_error();
265 /// for error in &kernel_errors {
266 /// if error.beginIndex != 0xFFFFFFFF && error.endIndex != 0xFFFFFFFF {
267 /// println!("Error in range: {} - {}", error.beginIndex, error.endIndex);
268 /// }
269 /// }
270 /// ```
271 pub fn get_kernel_error(&mut self) -> Vec<ffi::KernelError_t> {
272 unsafe {
273 let mut error_count: usize = 0;
274 let error_ptr = ffi::map_get_kernel_error(self.inner, &mut error_count as *mut usize);
275
276 if error_ptr.is_null() || error_count == 0 {
277 return Vec::new();
278 }
279
280 let errors = std::slice::from_raw_parts(error_ptr, error_count);
281 errors.to_vec()
282 }
283 }
284}