Skip to main content

mtl_gpu/device/
texture.rs

1//! Device texture creation methods.
2//!
3//! Corresponds to texture creation methods in `Metal/MTLDevice.hpp`.
4
5use std::ffi::c_void;
6
7use mtl_foundation::{Referencing, UInteger};
8use mtl_sys::{msg_send_1, msg_send_3, sel};
9
10use super::Device;
11use crate::enums::TextureType;
12use crate::error::ValidationError;
13use crate::texture::{Texture, TextureDescriptor};
14
15impl Device {
16    // =========================================================================
17    // Texture Creation
18    // =========================================================================
19
20    /// Create a new texture with the specified descriptor.
21    ///
22    /// C++ equivalent: `Texture* newTexture(const TextureDescriptor*)`
23    ///
24    /// # Safety
25    ///
26    /// The descriptor pointer must be valid.
27    pub unsafe fn new_texture(&self, descriptor: *const c_void) -> Option<Texture> {
28        unsafe {
29            let ptr: *mut c_void =
30                msg_send_1(self.as_ptr(), sel!(newTextureWithDescriptor:), descriptor);
31            Texture::from_raw(ptr)
32        }
33    }
34
35    /// Create a texture with validation.
36    ///
37    /// This safe method validates the descriptor before calling Metal APIs:
38    /// - Ensures width and height are > 0
39    /// - Ensures depth is > 0 for 3D textures
40    /// - Ensures array length is > 0 for array textures
41    /// - Validates mipmap count is within allowed range
42    /// - Validates sample count is supported by the device
43    ///
44    /// Use this method instead of `new_texture` to avoid process aborts
45    /// from Metal's validation layer.
46    ///
47    /// # Example
48    ///
49    /// ```ignore
50    /// let desc = TextureDescriptor::texture_2d_descriptor(
51    ///     PixelFormat::BGRA8_UNORM, 256, 256, false
52    /// ).unwrap();
53    ///
54    /// match device.new_texture_with_descriptor(&desc) {
55    ///     Ok(texture) => { /* use texture */ }
56    ///     Err(ValidationError::InvalidTextureDimensions { .. }) => { /* handle error */ }
57    ///     Err(e) => { /* handle other errors */ }
58    /// }
59    /// ```
60    pub fn new_texture_with_descriptor(
61        &self,
62        descriptor: &TextureDescriptor,
63    ) -> Result<Texture, ValidationError> {
64        let width = descriptor.width();
65        let height = descriptor.height();
66        let depth = descriptor.depth();
67
68        // Validate dimensions are non-zero
69        if width == 0 || height == 0 {
70            return Err(ValidationError::InvalidTextureDimensions {
71                width,
72                height,
73                depth,
74            });
75        }
76
77        // Validate depth for 3D textures
78        let texture_type = descriptor.texture_type();
79        if texture_type == TextureType::TYPE_3D && depth == 0 {
80            return Err(ValidationError::InvalidTextureDimensions {
81                width,
82                height,
83                depth,
84            });
85        }
86
87        // Validate array length for array textures
88        let array_length = descriptor.array_length();
89        let is_array_type = matches!(
90            texture_type,
91            TextureType::TYPE_1D_ARRAY
92                | TextureType::TYPE_2D_ARRAY
93                | TextureType::TYPE_CUBE_ARRAY
94                | TextureType::TYPE_2D_MULTISAMPLE_ARRAY
95        );
96        if is_array_type && array_length == 0 {
97            return Err(ValidationError::InvalidArrayLength);
98        }
99
100        // Validate mipmap count
101        let max_dim = width.max(height).max(depth);
102        let max_mipmaps = 1 + ((max_dim as f64).log2().floor() as UInteger);
103        let requested_mipmaps = descriptor.mipmap_level_count();
104        if requested_mipmaps > max_mipmaps {
105            return Err(ValidationError::InvalidMipmapCount {
106                requested: requested_mipmaps,
107                max_allowed: max_mipmaps,
108            });
109        }
110
111        // Validate sample count
112        let sample_count = descriptor.sample_count();
113        if sample_count > 1 && !self.supports_texture_sample_count(sample_count) {
114            return Err(ValidationError::UnsupportedTextureSampleCount(sample_count));
115        }
116
117        // Call unsafe implementation
118        unsafe {
119            self.new_texture(descriptor.as_ptr())
120                .ok_or(ValidationError::CreationFailed(None))
121        }
122    }
123
124    /// Create a new texture backed by an IOSurface.
125    ///
126    /// C++ equivalent: `Texture* newTexture(const TextureDescriptor*, IOSurfaceRef, NS::UInteger plane)`
127    ///
128    /// # Safety
129    ///
130    /// The descriptor and IOSurface pointers must be valid.
131    pub unsafe fn new_texture_with_iosurface(
132        &self,
133        descriptor: *const c_void,
134        iosurface: *const c_void,
135        plane: UInteger,
136    ) -> Option<Texture> {
137        unsafe {
138            let ptr: *mut c_void = msg_send_3(
139                self.as_ptr(),
140                sel!(newTextureWithDescriptor: iosurface: plane:),
141                descriptor,
142                iosurface,
143                plane,
144            );
145            Texture::from_raw(ptr)
146        }
147    }
148
149    /// Create a shared texture with another device.
150    ///
151    /// C++ equivalent: `Texture* newSharedTexture(const SharedTextureHandle*)`
152    ///
153    /// # Safety
154    ///
155    /// The handle pointer must be valid.
156    pub unsafe fn new_shared_texture_with_handle(&self, handle: *const c_void) -> Option<Texture> {
157        unsafe {
158            let ptr: *mut c_void =
159                msg_send_1(self.as_ptr(), sel!(newSharedTextureWithHandle:), handle);
160            Texture::from_raw(ptr)
161        }
162    }
163
164    /// Create a shared texture with the specified descriptor.
165    ///
166    /// C++ equivalent: `Texture* newSharedTexture(const TextureDescriptor*)`
167    ///
168    /// # Safety
169    ///
170    /// The descriptor pointer must be valid.
171    pub unsafe fn new_shared_texture_with_descriptor(
172        &self,
173        descriptor: *const c_void,
174    ) -> Option<Texture> {
175        unsafe {
176            let ptr: *mut c_void = msg_send_1(
177                self.as_ptr(),
178                sel!(newSharedTextureWithDescriptor:),
179                descriptor,
180            );
181            Texture::from_raw(ptr)
182        }
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    // Texture creation tests require TextureDescriptor which will be implemented
189    // in Phase 4 (Resources). Basic texture tests are placeholder for now.
190}