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}