1use crate::error::{Error, Result};
2use crate::ffi;
3use apple_metal::{storage_mode, texture_usage, MetalDevice, MetalTexture};
4use core::ffi::c_void;
5use core::ptr;
6
7pub mod feature_channel_format {
9 pub const NONE: usize = 0;
11 pub const UNORM8: usize = 1;
13 pub const UNORM16: usize = 2;
15 pub const FLOAT16: usize = 3;
17 pub const FLOAT32: usize = 4;
19}
20
21#[allow(non_upper_case_globals)]
23pub mod image_layout {
24 pub const HEIGHTxWIDTHxFEATURE_CHANNELS: usize = 0;
26 pub const FEATURE_CHANNELSxHEIGHTxWIDTH: usize = 1;
28}
29
30pub mod image_edge_mode {
32 pub const ZERO: usize = 0;
34 pub const CLAMP: usize = 1;
36}
37
38pub mod kernel_options {
40 pub const NONE: u32 = 0;
42 pub const SKIP_API_VALIDATION: u32 = 1 << 0;
44 pub const ALLOW_REDUCED_PRECISION: u32 = 1 << 1;
46 pub const DISABLE_INTERNAL_TILING: u32 = 1 << 2;
48 pub const INSERT_DEBUG_GROUPS: u32 = 1 << 3;
50 pub const VERBOSE: u32 = 1 << 4;
52}
53
54#[derive(Debug, Clone, Copy)]
56pub struct ImageDescriptor {
57 pub channel_format: usize,
59 pub width: usize,
61 pub height: usize,
63 pub feature_channels: usize,
65 pub number_of_images: usize,
67 pub usage: usize,
69 pub storage_mode: usize,
71}
72
73impl ImageDescriptor {
74 #[must_use]
76 pub const fn new(
77 width: usize,
78 height: usize,
79 feature_channels: usize,
80 channel_format: usize,
81 ) -> Self {
82 Self {
83 channel_format,
84 width,
85 height,
86 feature_channels,
87 number_of_images: 1,
88 usage: texture_usage::SHADER_READ | texture_usage::SHADER_WRITE,
89 storage_mode: storage_mode::MANAGED,
90 }
91 }
92}
93
94#[derive(Debug, Clone, Copy)]
96pub struct ImageRegion {
97 pub x: usize,
99 pub y: usize,
101 pub z: usize,
103 pub width: usize,
105 pub height: usize,
107 pub depth: usize,
109}
110
111impl ImageRegion {
112 #[must_use]
114 pub const fn new(
115 x: usize,
116 y: usize,
117 z: usize,
118 width: usize,
119 height: usize,
120 depth: usize,
121 ) -> Self {
122 Self {
123 x,
124 y,
125 z,
126 width,
127 height,
128 depth,
129 }
130 }
131
132 #[must_use]
134 pub const fn whole(width: usize, height: usize) -> Self {
135 Self::new(0, 0, 0, width, height, 1)
136 }
137}
138
139#[derive(Debug, Clone, Copy)]
141pub struct ImageReadWriteParams {
142 pub feature_channel_offset: usize,
144 pub feature_channel_count: usize,
146}
147
148impl ImageReadWriteParams {
149 #[must_use]
151 pub const fn new(feature_channel_offset: usize, feature_channel_count: usize) -> Self {
152 Self {
153 feature_channel_offset,
154 feature_channel_count,
155 }
156 }
157
158 #[must_use]
160 pub const fn all(feature_channels: usize) -> Self {
161 Self::new(0, feature_channels)
162 }
163}
164
165pub struct Image {
167 ptr: *mut c_void,
168}
169
170unsafe impl Send for Image {}
172unsafe impl Sync for Image {}
174
175impl Drop for Image {
176 fn drop(&mut self) {
177 if !self.ptr.is_null() {
178 unsafe { ffi::mps_object_release(self.ptr) };
180 self.ptr = ptr::null_mut();
181 }
182 }
183}
184
185impl Image {
186 #[must_use]
188 pub fn new(device: &MetalDevice, descriptor: ImageDescriptor) -> Option<Self> {
189 let ptr = unsafe {
191 ffi::mps_image_new_with_descriptor(
192 device.as_ptr(),
193 descriptor.channel_format,
194 descriptor.width,
195 descriptor.height,
196 descriptor.feature_channels,
197 descriptor.number_of_images,
198 descriptor.usage,
199 descriptor.storage_mode,
200 )
201 };
202 if ptr.is_null() {
203 None
204 } else {
205 Some(Self { ptr })
206 }
207 }
208
209 #[must_use]
211 pub fn from_texture(texture: &MetalTexture, feature_channels: usize) -> Option<Self> {
212 let ptr = unsafe { ffi::mps_image_new_with_texture(texture.as_ptr(), feature_channels) };
214 if ptr.is_null() {
215 None
216 } else {
217 Some(Self { ptr })
218 }
219 }
220
221 #[must_use]
223 pub const fn as_ptr(&self) -> *mut c_void {
224 self.ptr
225 }
226
227 #[must_use]
228 pub(crate) const unsafe fn from_raw(ptr: *mut c_void) -> Self {
229 Self { ptr }
232 }
233
234 #[must_use]
236 pub fn width(&self) -> usize {
237 unsafe { ffi::mps_image_width(self.ptr) }
239 }
240
241 #[must_use]
243 pub fn height(&self) -> usize {
244 unsafe { ffi::mps_image_height(self.ptr) }
246 }
247
248 #[must_use]
250 pub fn feature_channels(&self) -> usize {
251 unsafe { ffi::mps_image_feature_channels(self.ptr) }
253 }
254
255 #[must_use]
257 pub fn number_of_images(&self) -> usize {
258 unsafe { ffi::mps_image_number_of_images(self.ptr) }
260 }
261
262 #[must_use]
264 pub fn pixel_size(&self) -> usize {
265 unsafe { ffi::mps_image_pixel_size(self.ptr) }
267 }
268
269 #[must_use]
271 pub fn pixel_format(&self) -> usize {
272 unsafe { ffi::mps_image_pixel_format(self.ptr) }
274 }
275
276 #[must_use]
278 pub fn whole_region(&self) -> ImageRegion {
279 ImageRegion::whole(self.width(), self.height())
280 }
281
282 pub fn read_bytes(
284 &self,
285 dst: &mut [u8],
286 data_layout: usize,
287 bytes_per_row: usize,
288 region: ImageRegion,
289 params: ImageReadWriteParams,
290 image_index: usize,
291 ) -> Result<()> {
292 let expected = required_bytes(data_layout, bytes_per_row, region, params);
293 if dst.len() < expected {
294 return Err(Error::InvalidLength {
295 expected,
296 actual: dst.len(),
297 });
298 }
299
300 let _ = unsafe {
302 ffi::mps_image_read_bytes(
303 self.ptr,
304 dst.as_mut_ptr().cast(),
305 data_layout,
306 bytes_per_row,
307 region.x,
308 region.y,
309 region.z,
310 region.width,
311 region.height,
312 region.depth,
313 params.feature_channel_offset,
314 params.feature_channel_count,
315 image_index,
316 )
317 };
318 Ok(())
319 }
320
321 pub fn write_bytes(
323 &self,
324 src: &[u8],
325 data_layout: usize,
326 bytes_per_row: usize,
327 region: ImageRegion,
328 params: ImageReadWriteParams,
329 image_index: usize,
330 ) -> Result<()> {
331 let expected = required_bytes(data_layout, bytes_per_row, region, params);
332 if src.len() < expected {
333 return Err(Error::InvalidLength {
334 expected,
335 actual: src.len(),
336 });
337 }
338
339 let _ = unsafe {
341 ffi::mps_image_write_bytes(
342 self.ptr,
343 src.as_ptr().cast(),
344 data_layout,
345 bytes_per_row,
346 region.x,
347 region.y,
348 region.z,
349 region.width,
350 region.height,
351 region.depth,
352 params.feature_channel_offset,
353 params.feature_channel_count,
354 image_index,
355 )
356 };
357 Ok(())
358 }
359
360 pub fn read_f32(&self) -> Result<Vec<f32>> {
362 let len = self.width() * self.height() * self.feature_channels();
363 let mut data = vec![0.0_f32; len];
364 let bytes_per_row = self.width() * self.feature_channels() * core::mem::size_of::<f32>();
365 let bytes = unsafe {
367 core::slice::from_raw_parts_mut(
368 data.as_mut_ptr().cast::<u8>(),
369 core::mem::size_of_val(data.as_slice()),
370 )
371 };
372 self.read_bytes(
373 bytes,
374 image_layout::HEIGHTxWIDTHxFEATURE_CHANNELS,
375 bytes_per_row,
376 self.whole_region(),
377 ImageReadWriteParams::all(self.feature_channels()),
378 0,
379 )?;
380 Ok(data)
381 }
382
383 pub fn write_f32(&self, data: &[f32]) -> Result<()> {
385 let expected = self.width() * self.height() * self.feature_channels();
386 if data.len() != expected {
387 return Err(Error::InvalidLength {
388 expected: expected * core::mem::size_of::<f32>(),
389 actual: core::mem::size_of_val(data),
390 });
391 }
392
393 let bytes_per_row = self.width() * self.feature_channels() * core::mem::size_of::<f32>();
394 let bytes = unsafe {
396 core::slice::from_raw_parts(data.as_ptr().cast::<u8>(), core::mem::size_of_val(data))
397 };
398 self.write_bytes(
399 bytes,
400 image_layout::HEIGHTxWIDTHxFEATURE_CHANNELS,
401 bytes_per_row,
402 self.whole_region(),
403 ImageReadWriteParams::all(self.feature_channels()),
404 0,
405 )
406 }
407}
408
409#[doc(hidden)]
410pub use crate::generated::image::*;
411
412fn required_bytes(
413 data_layout: usize,
414 bytes_per_row: usize,
415 region: ImageRegion,
416 params: ImageReadWriteParams,
417) -> usize {
418 let rows = region.height.saturating_mul(region.depth);
419 let base = bytes_per_row.saturating_mul(rows);
420 if data_layout == image_layout::FEATURE_CHANNELSxHEIGHTxWIDTH {
421 base.saturating_mul(params.feature_channel_count.max(1))
422 } else {
423 base
424 }
425}