Skip to main content

mtl_gpu/depth_stencil/
mod.rs

1//! Metal depth/stencil state.
2//!
3//! Corresponds to `Metal/MTLDepthStencil.hpp`.
4//!
5//! Depth and stencil testing configurations.
6
7use std::ffi::c_void;
8use std::ptr::NonNull;
9
10use mtl_foundation::Referencing;
11use mtl_sys::{msg_send_0, msg_send_1, sel};
12
13use crate::enums::{CompareFunction, StencilOperation};
14use crate::types::ResourceID;
15
16/// An object that contains depth and stencil test configurations.
17///
18/// C++ equivalent: `MTL::DepthStencilState`
19#[repr(transparent)]
20pub struct DepthStencilState(pub(crate) NonNull<c_void>);
21
22impl DepthStencilState {
23    /// Create a DepthStencilState from a raw pointer.
24    ///
25    /// # Safety
26    ///
27    /// The pointer must be a valid Metal depth/stencil state object.
28    #[inline]
29    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
30        NonNull::new(ptr).map(Self)
31    }
32
33    /// Get the raw pointer to the depth/stencil state.
34    #[inline]
35    pub fn as_raw(&self) -> *mut c_void {
36        self.0.as_ptr()
37    }
38
39    /// Get the label for this depth/stencil state.
40    ///
41    /// C++ equivalent: `NS::String* label() const`
42    pub fn label(&self) -> Option<String> {
43        unsafe {
44            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
45            if ptr.is_null() {
46                return None;
47            }
48            let utf8_ptr: *const std::ffi::c_char =
49                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
50            if utf8_ptr.is_null() {
51                return None;
52            }
53            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
54            Some(c_str.to_string_lossy().into_owned())
55        }
56    }
57
58    /// Get the device that created this depth/stencil state.
59    ///
60    /// C++ equivalent: `Device* device() const`
61    pub fn device(&self) -> crate::Device {
62        unsafe {
63            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
64            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
65            crate::Device::from_raw(ptr).expect("depth/stencil state has no device")
66        }
67    }
68
69    /// Get the GPU resource ID for bindless access.
70    ///
71    /// C++ equivalent: `ResourceID gpuResourceID() const`
72    #[inline]
73    pub fn gpu_resource_id(&self) -> ResourceID {
74        unsafe { msg_send_0(self.as_ptr(), sel!(gpuResourceID)) }
75    }
76}
77
78impl Clone for DepthStencilState {
79    fn clone(&self) -> Self {
80        unsafe {
81            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
82        }
83        Self(self.0)
84    }
85}
86
87impl Drop for DepthStencilState {
88    fn drop(&mut self) {
89        unsafe {
90            msg_send_0::<()>(self.as_ptr(), sel!(release));
91        }
92    }
93}
94
95impl Referencing for DepthStencilState {
96    #[inline]
97    fn as_ptr(&self) -> *const c_void {
98        self.0.as_ptr()
99    }
100}
101
102unsafe impl Send for DepthStencilState {}
103unsafe impl Sync for DepthStencilState {}
104
105impl std::fmt::Debug for DepthStencilState {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_struct("DepthStencilState")
108            .field("label", &self.label())
109            .finish()
110    }
111}
112
113// ============================================================================
114// Stencil Descriptor
115// ============================================================================
116
117/// A configuration for stencil test operations.
118///
119/// C++ equivalent: `MTL::StencilDescriptor`
120#[repr(transparent)]
121pub struct StencilDescriptor(pub(crate) NonNull<c_void>);
122
123impl StencilDescriptor {
124    /// Create a new stencil descriptor.
125    ///
126    /// C++ equivalent: `static StencilDescriptor* alloc()->init()`
127    pub fn new() -> Option<Self> {
128        unsafe {
129            let class = mtl_sys::Class::get("MTLStencilDescriptor")?;
130            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
131            if ptr.is_null() {
132                return None;
133            }
134            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
135            Self::from_raw(ptr)
136        }
137    }
138
139    /// Create a StencilDescriptor from a raw pointer.
140    ///
141    /// # Safety
142    ///
143    /// The pointer must be a valid Metal stencil descriptor object.
144    #[inline]
145    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
146        NonNull::new(ptr).map(Self)
147    }
148
149    /// Get the raw pointer.
150    #[inline]
151    pub fn as_raw(&self) -> *mut c_void {
152        self.0.as_ptr()
153    }
154
155    /// Get the stencil compare function.
156    ///
157    /// C++ equivalent: `CompareFunction stencilCompareFunction() const`
158    #[inline]
159    pub fn stencil_compare_function(&self) -> CompareFunction {
160        unsafe { msg_send_0(self.as_ptr(), sel!(stencilCompareFunction)) }
161    }
162
163    /// Set the stencil compare function.
164    ///
165    /// C++ equivalent: `void setStencilCompareFunction(CompareFunction)`
166    #[inline]
167    pub fn set_stencil_compare_function(&self, func: CompareFunction) {
168        unsafe {
169            msg_send_1::<(), CompareFunction>(
170                self.as_ptr(),
171                sel!(setStencilCompareFunction:),
172                func,
173            );
174        }
175    }
176
177    /// Get the stencil failure operation.
178    ///
179    /// C++ equivalent: `StencilOperation stencilFailureOperation() const`
180    #[inline]
181    pub fn stencil_failure_operation(&self) -> StencilOperation {
182        unsafe { msg_send_0(self.as_ptr(), sel!(stencilFailureOperation)) }
183    }
184
185    /// Set the stencil failure operation.
186    ///
187    /// C++ equivalent: `void setStencilFailureOperation(StencilOperation)`
188    #[inline]
189    pub fn set_stencil_failure_operation(&self, op: StencilOperation) {
190        unsafe {
191            msg_send_1::<(), StencilOperation>(
192                self.as_ptr(),
193                sel!(setStencilFailureOperation:),
194                op,
195            );
196        }
197    }
198
199    /// Get the depth failure operation.
200    ///
201    /// C++ equivalent: `StencilOperation depthFailureOperation() const`
202    #[inline]
203    pub fn depth_failure_operation(&self) -> StencilOperation {
204        unsafe { msg_send_0(self.as_ptr(), sel!(depthFailureOperation)) }
205    }
206
207    /// Set the depth failure operation.
208    ///
209    /// C++ equivalent: `void setDepthFailureOperation(StencilOperation)`
210    #[inline]
211    pub fn set_depth_failure_operation(&self, op: StencilOperation) {
212        unsafe {
213            msg_send_1::<(), StencilOperation>(self.as_ptr(), sel!(setDepthFailureOperation:), op);
214        }
215    }
216
217    /// Get the depth/stencil pass operation.
218    ///
219    /// C++ equivalent: `StencilOperation depthStencilPassOperation() const`
220    #[inline]
221    pub fn depth_stencil_pass_operation(&self) -> StencilOperation {
222        unsafe { msg_send_0(self.as_ptr(), sel!(depthStencilPassOperation)) }
223    }
224
225    /// Set the depth/stencil pass operation.
226    ///
227    /// C++ equivalent: `void setDepthStencilPassOperation(StencilOperation)`
228    #[inline]
229    pub fn set_depth_stencil_pass_operation(&self, op: StencilOperation) {
230        unsafe {
231            msg_send_1::<(), StencilOperation>(
232                self.as_ptr(),
233                sel!(setDepthStencilPassOperation:),
234                op,
235            );
236        }
237    }
238
239    /// Get the read mask.
240    ///
241    /// C++ equivalent: `uint32_t readMask() const`
242    #[inline]
243    pub fn read_mask(&self) -> u32 {
244        unsafe { msg_send_0(self.as_ptr(), sel!(readMask)) }
245    }
246
247    /// Set the read mask.
248    ///
249    /// C++ equivalent: `void setReadMask(uint32_t)`
250    #[inline]
251    pub fn set_read_mask(&self, mask: u32) {
252        unsafe {
253            msg_send_1::<(), u32>(self.as_ptr(), sel!(setReadMask:), mask);
254        }
255    }
256
257    /// Get the write mask.
258    ///
259    /// C++ equivalent: `uint32_t writeMask() const`
260    #[inline]
261    pub fn write_mask(&self) -> u32 {
262        unsafe { msg_send_0(self.as_ptr(), sel!(writeMask)) }
263    }
264
265    /// Set the write mask.
266    ///
267    /// C++ equivalent: `void setWriteMask(uint32_t)`
268    #[inline]
269    pub fn set_write_mask(&self, mask: u32) {
270        unsafe {
271            msg_send_1::<(), u32>(self.as_ptr(), sel!(setWriteMask:), mask);
272        }
273    }
274}
275
276impl Default for StencilDescriptor {
277    fn default() -> Self {
278        Self::new().expect("failed to create stencil descriptor")
279    }
280}
281
282impl Clone for StencilDescriptor {
283    fn clone(&self) -> Self {
284        unsafe {
285            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(copy));
286            Self::from_raw(ptr).expect("failed to copy stencil descriptor")
287        }
288    }
289}
290
291impl Drop for StencilDescriptor {
292    fn drop(&mut self) {
293        unsafe {
294            msg_send_0::<()>(self.as_ptr(), sel!(release));
295        }
296    }
297}
298
299impl Referencing for StencilDescriptor {
300    #[inline]
301    fn as_ptr(&self) -> *const c_void {
302        self.0.as_ptr()
303    }
304}
305
306unsafe impl Send for StencilDescriptor {}
307unsafe impl Sync for StencilDescriptor {}
308
309// ============================================================================
310// Depth Stencil Descriptor
311// ============================================================================
312
313/// A configuration for depth and stencil test operations.
314///
315/// C++ equivalent: `MTL::DepthStencilDescriptor`
316#[repr(transparent)]
317pub struct DepthStencilDescriptor(pub(crate) NonNull<c_void>);
318
319impl DepthStencilDescriptor {
320    /// Create a new depth/stencil descriptor.
321    ///
322    /// C++ equivalent: `static DepthStencilDescriptor* alloc()->init()`
323    pub fn new() -> Option<Self> {
324        unsafe {
325            let class = mtl_sys::Class::get("MTLDepthStencilDescriptor")?;
326            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
327            if ptr.is_null() {
328                return None;
329            }
330            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
331            Self::from_raw(ptr)
332        }
333    }
334
335    /// Create a DepthStencilDescriptor from a raw pointer.
336    ///
337    /// # Safety
338    ///
339    /// The pointer must be a valid Metal depth/stencil descriptor object.
340    #[inline]
341    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
342        NonNull::new(ptr).map(Self)
343    }
344
345    /// Get the raw pointer.
346    #[inline]
347    pub fn as_raw(&self) -> *mut c_void {
348        self.0.as_ptr()
349    }
350
351    /// Get the depth compare function.
352    ///
353    /// C++ equivalent: `CompareFunction depthCompareFunction() const`
354    #[inline]
355    pub fn depth_compare_function(&self) -> CompareFunction {
356        unsafe { msg_send_0(self.as_ptr(), sel!(depthCompareFunction)) }
357    }
358
359    /// Set the depth compare function.
360    ///
361    /// C++ equivalent: `void setDepthCompareFunction(CompareFunction)`
362    #[inline]
363    pub fn set_depth_compare_function(&self, func: CompareFunction) {
364        unsafe {
365            msg_send_1::<(), CompareFunction>(self.as_ptr(), sel!(setDepthCompareFunction:), func);
366        }
367    }
368
369    /// Check if depth write is enabled.
370    ///
371    /// C++ equivalent: `bool isDepthWriteEnabled() const`
372    #[inline]
373    pub fn is_depth_write_enabled(&self) -> bool {
374        unsafe { msg_send_0(self.as_ptr(), sel!(isDepthWriteEnabled)) }
375    }
376
377    /// Set whether depth write is enabled.
378    ///
379    /// C++ equivalent: `void setDepthWriteEnabled(bool)`
380    #[inline]
381    pub fn set_depth_write_enabled(&self, enabled: bool) {
382        unsafe {
383            msg_send_1::<(), bool>(self.as_ptr(), sel!(setDepthWriteEnabled:), enabled);
384        }
385    }
386
387    /// Get the front face stencil descriptor.
388    ///
389    /// C++ equivalent: `StencilDescriptor* frontFaceStencil() const`
390    pub fn front_face_stencil(&self) -> Option<StencilDescriptor> {
391        unsafe {
392            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(frontFaceStencil));
393            if ptr.is_null() {
394                return None;
395            }
396            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
397            StencilDescriptor::from_raw(ptr)
398        }
399    }
400
401    /// Set the front face stencil descriptor.
402    ///
403    /// C++ equivalent: `void setFrontFaceStencil(StencilDescriptor*)`
404    pub fn set_front_face_stencil(&self, stencil: Option<&StencilDescriptor>) {
405        let ptr = stencil.map_or(std::ptr::null(), |s| s.as_ptr());
406        unsafe {
407            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setFrontFaceStencil:), ptr);
408        }
409    }
410
411    /// Get the back face stencil descriptor.
412    ///
413    /// C++ equivalent: `StencilDescriptor* backFaceStencil() const`
414    pub fn back_face_stencil(&self) -> Option<StencilDescriptor> {
415        unsafe {
416            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(backFaceStencil));
417            if ptr.is_null() {
418                return None;
419            }
420            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
421            StencilDescriptor::from_raw(ptr)
422        }
423    }
424
425    /// Set the back face stencil descriptor.
426    ///
427    /// C++ equivalent: `void setBackFaceStencil(StencilDescriptor*)`
428    pub fn set_back_face_stencil(&self, stencil: Option<&StencilDescriptor>) {
429        let ptr = stencil.map_or(std::ptr::null(), |s| s.as_ptr());
430        unsafe {
431            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setBackFaceStencil:), ptr);
432        }
433    }
434
435    /// Get the label.
436    ///
437    /// C++ equivalent: `NS::String* label() const`
438    pub fn label(&self) -> Option<String> {
439        unsafe {
440            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
441            if ptr.is_null() {
442                return None;
443            }
444            let utf8_ptr: *const std::ffi::c_char =
445                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
446            if utf8_ptr.is_null() {
447                return None;
448            }
449            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
450            Some(c_str.to_string_lossy().into_owned())
451        }
452    }
453
454    /// Set the label.
455    ///
456    /// C++ equivalent: `void setLabel(const NS::String*)`
457    pub fn set_label(&self, label: &str) {
458        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
459            unsafe {
460                msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
461            }
462        }
463    }
464}
465
466impl Default for DepthStencilDescriptor {
467    fn default() -> Self {
468        Self::new().expect("failed to create depth/stencil descriptor")
469    }
470}
471
472impl Clone for DepthStencilDescriptor {
473    fn clone(&self) -> Self {
474        unsafe {
475            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(copy));
476            Self::from_raw(ptr).expect("failed to copy depth/stencil descriptor")
477        }
478    }
479}
480
481impl Drop for DepthStencilDescriptor {
482    fn drop(&mut self) {
483        unsafe {
484            msg_send_0::<()>(self.as_ptr(), sel!(release));
485        }
486    }
487}
488
489impl Referencing for DepthStencilDescriptor {
490    #[inline]
491    fn as_ptr(&self) -> *const c_void {
492        self.0.as_ptr()
493    }
494}
495
496unsafe impl Send for DepthStencilDescriptor {}
497unsafe impl Sync for DepthStencilDescriptor {}
498
499impl std::fmt::Debug for DepthStencilDescriptor {
500    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
501        f.debug_struct("DepthStencilDescriptor")
502            .field("depth_compare_function", &self.depth_compare_function())
503            .field("is_depth_write_enabled", &self.is_depth_write_enabled())
504            .field("label", &self.label())
505            .finish()
506    }
507}
508
509#[cfg(test)]
510mod tests {
511    use super::*;
512
513    #[test]
514    fn test_depth_stencil_state_size() {
515        assert_eq!(
516            std::mem::size_of::<DepthStencilState>(),
517            std::mem::size_of::<*mut c_void>()
518        );
519    }
520
521    #[test]
522    fn test_depth_stencil_descriptor_creation() {
523        let desc = DepthStencilDescriptor::new();
524        assert!(desc.is_some());
525    }
526}