1use crate::{ffi, util::take_optional_string, MetalDevice};
2use core::ffi::c_void;
3use std::ffi::CString;
4
5macro_rules! opaque_state {
6 ($(#[$meta:meta])* pub struct $name:ident;) => {
7 $(#[$meta])*
8pub struct $name {
10 ptr: *mut c_void,
11 }
12
13 impl Drop for $name {
14 fn drop(&mut self) {
15 if !self.ptr.is_null() {
16 unsafe { ffi::am_object_release(self.ptr) };
17 self.ptr = core::ptr::null_mut();
18 }
19 }
20 }
21
22 impl $name {
23#[must_use]
25 pub const fn as_ptr(&self) -> *mut c_void {
26 self.ptr
27 }
28
29 fn wrap(ptr: *mut c_void) -> Option<Self> {
30 if ptr.is_null() {
31 None
32 } else {
33 Some(Self { ptr })
34 }
35 }
36
37 }
38 };
39}
40
41pub mod compare_function {
43 pub const NEVER: usize = 0;
45 pub const LESS: usize = 1;
47 pub const EQUAL: usize = 2;
49 pub const LESS_EQUAL: usize = 3;
51 pub const GREATER: usize = 4;
53 pub const NOT_EQUAL: usize = 5;
55 pub const GREATER_EQUAL: usize = 6;
57 pub const ALWAYS: usize = 7;
59}
60
61pub mod stencil_operation {
63 pub const KEEP: usize = 0;
65 pub const ZERO: usize = 1;
67 pub const REPLACE: usize = 2;
69 pub const INCREMENT_CLAMP: usize = 3;
71 pub const DECREMENT_CLAMP: usize = 4;
73 pub const INVERT: usize = 5;
75 pub const INCREMENT_WRAP: usize = 6;
77 pub const DECREMENT_WRAP: usize = 7;
79}
80
81pub mod sampler_min_mag_filter {
83 pub const NEAREST: usize = 0;
85 pub const LINEAR: usize = 1;
87}
88
89pub mod sampler_mip_filter {
91 pub const NOT_MIPMAPPED: usize = 0;
93 pub const NEAREST: usize = 1;
95 pub const LINEAR: usize = 2;
97}
98
99pub mod sampler_address_mode {
101 pub const CLAMP_TO_EDGE: usize = 0;
103 pub const MIRROR_CLAMP_TO_EDGE: usize = 1;
105 pub const REPEAT: usize = 2;
107 pub const MIRROR_REPEAT: usize = 3;
109 pub const CLAMP_TO_ZERO: usize = 4;
111 pub const CLAMP_TO_BORDER_COLOR: usize = 5;
113}
114
115pub mod sampler_border_color {
117 pub const TRANSPARENT_BLACK: usize = 0;
119 pub const OPAQUE_BLACK: usize = 1;
121 pub const OPAQUE_WHITE: usize = 2;
123}
124
125pub mod sampler_reduction_mode {
127 pub const WEIGHTED_AVERAGE: usize = 0;
129 pub const MINIMUM: usize = 1;
131 pub const MAXIMUM: usize = 2;
133}
134
135#[derive(Debug, Clone, Copy)]
137pub struct StencilDescriptor {
138 pub stencil_compare_function: usize,
140 pub stencil_failure_operation: usize,
142 pub depth_failure_operation: usize,
144 pub depth_stencil_pass_operation: usize,
146 pub read_mask: u32,
148 pub write_mask: u32,
150}
151
152impl Default for StencilDescriptor {
153 fn default() -> Self {
154 Self {
155 stencil_compare_function: compare_function::ALWAYS,
156 stencil_failure_operation: stencil_operation::KEEP,
157 depth_failure_operation: stencil_operation::KEEP,
158 depth_stencil_pass_operation: stencil_operation::KEEP,
159 read_mask: u32::MAX,
160 write_mask: u32::MAX,
161 }
162 }
163}
164
165impl StencilDescriptor {
166 #[must_use]
168 pub const fn new() -> Self {
169 Self {
170 stencil_compare_function: compare_function::ALWAYS,
171 stencil_failure_operation: stencil_operation::KEEP,
172 depth_failure_operation: stencil_operation::KEEP,
173 depth_stencil_pass_operation: stencil_operation::KEEP,
174 read_mask: u32::MAX,
175 write_mask: u32::MAX,
176 }
177 }
178}
179
180#[derive(Debug, Clone, Default)]
182pub struct DepthStencilDescriptor {
183 pub depth_compare_function: usize,
185 pub depth_write_enabled: bool,
187 pub front_face_stencil: Option<StencilDescriptor>,
189 pub back_face_stencil: Option<StencilDescriptor>,
191 pub label: Option<String>,
193}
194
195impl DepthStencilDescriptor {
196 #[must_use]
198 pub const fn new() -> Self {
199 Self {
200 depth_compare_function: compare_function::ALWAYS,
201 depth_write_enabled: false,
202 front_face_stencil: None,
203 back_face_stencil: None,
204 label: None,
205 }
206 }
207}
208
209#[derive(Debug, Clone)]
211pub struct SamplerDescriptor {
212 pub min_filter: usize,
214 pub mag_filter: usize,
216 pub mip_filter: usize,
218 pub max_anisotropy: usize,
220 pub s_address_mode: usize,
222 pub t_address_mode: usize,
224 pub r_address_mode: usize,
226 pub border_color: usize,
228 pub reduction_mode: usize,
230 pub normalized_coordinates: bool,
232 pub lod_min_clamp: f32,
234 pub lod_max_clamp: f32,
236 pub lod_average: bool,
238 pub lod_bias: f32,
240 pub compare_function: usize,
242 pub support_argument_buffers: bool,
244 pub label: Option<String>,
246}
247
248impl Default for SamplerDescriptor {
249 fn default() -> Self {
250 Self {
251 min_filter: sampler_min_mag_filter::NEAREST,
252 mag_filter: sampler_min_mag_filter::NEAREST,
253 mip_filter: sampler_mip_filter::NOT_MIPMAPPED,
254 max_anisotropy: 1,
255 s_address_mode: sampler_address_mode::CLAMP_TO_EDGE,
256 t_address_mode: sampler_address_mode::CLAMP_TO_EDGE,
257 r_address_mode: sampler_address_mode::CLAMP_TO_EDGE,
258 border_color: sampler_border_color::TRANSPARENT_BLACK,
259 reduction_mode: sampler_reduction_mode::WEIGHTED_AVERAGE,
260 normalized_coordinates: true,
261 lod_min_clamp: 0.0,
262 lod_max_clamp: f32::MAX,
263 lod_average: false,
264 lod_bias: 0.0,
265 compare_function: compare_function::NEVER,
266 support_argument_buffers: false,
267 label: None,
268 }
269 }
270}
271
272impl SamplerDescriptor {
273 #[must_use]
275 pub fn new() -> Self {
276 Self::default()
277 }
278}
279
280opaque_state!(
281 pub struct DepthStencilState;
283);
284opaque_state!(
285 pub struct SamplerState;
287);
288
289impl DepthStencilState {
290 #[must_use]
292 pub fn label(&self) -> Option<String> {
293 unsafe { take_optional_string(ffi::am_object_copy_label(self.as_ptr())) }
294 }
295}
296
297impl SamplerState {
298 #[must_use]
300 pub fn label(&self) -> Option<String> {
301 unsafe { take_optional_string(ffi::am_object_copy_label(self.as_ptr())) }
302 }
303}
304
305impl MetalDevice {
306 #[must_use]
308 pub fn argument_buffers_support(&self) -> usize {
309 unsafe { ffi::am_device_argument_buffers_support(self.as_ptr()) }
310 }
311
312 #[must_use]
314 pub fn new_depth_stencil_state(
315 &self,
316 descriptor: &DepthStencilDescriptor,
317 ) -> Option<DepthStencilState> {
318 let label = descriptor
319 .label
320 .as_deref()
321 .and_then(|value| CString::new(value).ok());
322 let label_ptr = label
323 .as_deref()
324 .map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
325 let front = descriptor.front_face_stencil.unwrap_or_default();
326 let back = descriptor.back_face_stencil.unwrap_or_default();
327 DepthStencilState::wrap(unsafe {
328 ffi::am_device_new_depth_stencil_state(
329 self.as_ptr(),
330 descriptor.depth_compare_function,
331 descriptor.depth_write_enabled,
332 descriptor.front_face_stencil.is_some(),
333 front.stencil_compare_function,
334 front.stencil_failure_operation,
335 front.depth_failure_operation,
336 front.depth_stencil_pass_operation,
337 front.read_mask,
338 front.write_mask,
339 descriptor.back_face_stencil.is_some(),
340 back.stencil_compare_function,
341 back.stencil_failure_operation,
342 back.depth_failure_operation,
343 back.depth_stencil_pass_operation,
344 back.read_mask,
345 back.write_mask,
346 label_ptr,
347 )
348 })
349 }
350
351 #[must_use]
353 pub fn new_sampler_state(&self, descriptor: &SamplerDescriptor) -> Option<SamplerState> {
354 let label = descriptor
355 .label
356 .as_deref()
357 .and_then(|value| CString::new(value).ok());
358 let label_ptr = label
359 .as_deref()
360 .map_or(core::ptr::null(), core::ffi::CStr::as_ptr);
361 SamplerState::wrap(unsafe {
362 ffi::am_device_new_sampler_state(
363 self.as_ptr(),
364 descriptor.min_filter,
365 descriptor.mag_filter,
366 descriptor.mip_filter,
367 descriptor.max_anisotropy,
368 descriptor.s_address_mode,
369 descriptor.t_address_mode,
370 descriptor.r_address_mode,
371 descriptor.border_color,
372 descriptor.reduction_mode,
373 descriptor.normalized_coordinates,
374 descriptor.lod_min_clamp,
375 descriptor.lod_max_clamp,
376 descriptor.lod_average,
377 descriptor.lod_bias,
378 descriptor.compare_function,
379 descriptor.support_argument_buffers,
380 label_ptr,
381 )
382 })
383 }
384}