mtl_gpu/device/sampler.rs
1//! Device sampler state creation methods.
2//!
3//! Corresponds to sampler state creation methods in `Metal/MTLDevice.hpp`.
4
5use std::ffi::c_void;
6
7use mtl_foundation::Referencing;
8use mtl_sys::{msg_send_1, sel};
9
10use super::Device;
11use crate::error::ValidationError;
12use crate::sampler::{SamplerDescriptor, SamplerState};
13
14impl Device {
15 // =========================================================================
16 // Sampler State Creation
17 // =========================================================================
18
19 /// Create a new sampler state with the specified descriptor.
20 ///
21 /// C++ equivalent: `SamplerState* newSamplerState(const SamplerDescriptor*)`
22 pub fn new_sampler_state(&self, descriptor: &SamplerDescriptor) -> Option<SamplerState> {
23 unsafe {
24 let ptr: *mut c_void = msg_send_1(
25 self.as_ptr(),
26 sel!(newSamplerStateWithDescriptor:),
27 descriptor.as_ptr(),
28 );
29 SamplerState::from_raw(ptr)
30 }
31 }
32
33 /// Create a sampler state with validation.
34 ///
35 /// This safe method validates the descriptor before calling Metal APIs:
36 /// - Ensures LOD min clamp is <= LOD max clamp
37 /// - Ensures max anisotropy is >= 1 and a power of 2
38 ///
39 /// # Example
40 ///
41 /// ```ignore
42 /// let desc = SamplerDescriptor::new().unwrap();
43 /// desc.set_min_filter(SamplerMinMagFilter::LINEAR);
44 /// desc.set_mag_filter(SamplerMinMagFilter::LINEAR);
45 ///
46 /// match device.new_sampler_state_validated(&desc) {
47 /// Ok(sampler) => { /* use sampler */ }
48 /// Err(ValidationError::InvalidLodRange { .. }) => { /* handle error */ }
49 /// Err(e) => { /* handle other errors */ }
50 /// }
51 /// ```
52 pub fn new_sampler_state_validated(
53 &self,
54 descriptor: &SamplerDescriptor,
55 ) -> Result<SamplerState, ValidationError> {
56 // Validate LOD range
57 let min_lod = descriptor.lod_min_clamp();
58 let max_lod = descriptor.lod_max_clamp();
59 if min_lod > max_lod {
60 return Err(ValidationError::InvalidLodRange {
61 min: min_lod,
62 max: max_lod,
63 });
64 }
65
66 // Validate anisotropy
67 let anisotropy = descriptor.max_anisotropy();
68 if anisotropy < 1 || !anisotropy.is_power_of_two() {
69 return Err(ValidationError::InvalidAnisotropy(anisotropy));
70 }
71
72 // Call existing safe implementation
73 self.new_sampler_state(descriptor)
74 .ok_or(ValidationError::CreationFailed(None))
75 }
76
77 /// Create a new sampler state with a raw descriptor pointer.
78 ///
79 /// C++ equivalent: `SamplerState* newSamplerState(const SamplerDescriptor*)`
80 ///
81 /// # Safety
82 ///
83 /// The descriptor pointer must be valid.
84 pub unsafe fn new_sampler_state_with_ptr(
85 &self,
86 descriptor: *const c_void,
87 ) -> Option<SamplerState> {
88 unsafe {
89 let ptr: *mut c_void = msg_send_1(
90 self.as_ptr(),
91 sel!(newSamplerStateWithDescriptor:),
92 descriptor,
93 );
94 SamplerState::from_raw(ptr)
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::device::system_default;
103 use crate::enums::{SamplerMinMagFilter, SamplerMipFilter};
104
105 #[test]
106 fn test_new_sampler_state() {
107 let device = system_default().expect("no Metal device");
108 let descriptor = SamplerDescriptor::new().expect("failed to create descriptor");
109
110 descriptor.set_min_filter(SamplerMinMagFilter::LINEAR);
111 descriptor.set_mag_filter(SamplerMinMagFilter::LINEAR);
112 descriptor.set_mip_filter(SamplerMipFilter::LINEAR);
113
114 let sampler = device.new_sampler_state(&descriptor);
115 assert!(sampler.is_some());
116 }
117}