Skip to main content

mtl_gpu/device/
limits.rs

1//! Device limits and alignment queries.
2//!
3//! Corresponds to limit query methods in `MTL::Device` from `Metal/MTLDevice.hpp`.
4
5use std::ffi::c_void;
6
7use mtl_foundation::{Referencing, UInteger};
8use mtl_sys::{msg_send_0, msg_send_1, msg_send_2, msg_send_3, msg_send_4, sel};
9
10use super::Device;
11use crate::enums::{
12    PixelFormat, ResourceOptions, SparsePageSize, SparseTextureRegionAlignmentMode, TextureType,
13};
14use crate::types::{Region, Size, SizeAndAlign};
15
16impl Device {
17    // =========================================================================
18    // Thread Limits
19    // =========================================================================
20
21    /// Get the maximum threads per threadgroup.
22    ///
23    /// C++ equivalent: `Size maxThreadsPerThreadgroup() const`
24    #[inline]
25    pub fn max_threads_per_threadgroup(&self) -> Size {
26        unsafe { msg_send_0(self.as_ptr(), sel!(maxThreadsPerThreadgroup)) }
27    }
28
29    /// Get the maximum threadgroup memory length in bytes.
30    ///
31    /// C++ equivalent: `NS::UInteger maxThreadgroupMemoryLength() const`
32    #[inline]
33    pub fn max_threadgroup_memory_length(&self) -> UInteger {
34        unsafe { msg_send_0(self.as_ptr(), sel!(maxThreadgroupMemoryLength)) }
35    }
36
37    // =========================================================================
38    // Buffer Limits
39    // =========================================================================
40
41    /// Get the maximum buffer length in bytes.
42    ///
43    /// C++ equivalent: `NS::UInteger maxBufferLength() const`
44    #[inline]
45    pub fn max_buffer_length(&self) -> UInteger {
46        unsafe { msg_send_0(self.as_ptr(), sel!(maxBufferLength)) }
47    }
48
49    // =========================================================================
50    // Argument Buffer Limits
51    // =========================================================================
52
53    /// Get the maximum number of samplers that can be in an argument buffer.
54    ///
55    /// C++ equivalent: `NS::UInteger maxArgumentBufferSamplerCount() const`
56    #[inline]
57    pub fn max_argument_buffer_sampler_count(&self) -> UInteger {
58        unsafe { msg_send_0(self.as_ptr(), sel!(maxArgumentBufferSamplerCount)) }
59    }
60
61    // =========================================================================
62    // Texture Alignment
63    // =========================================================================
64
65    /// Get the minimum alignment for a linear texture with the given pixel format.
66    ///
67    /// C++ equivalent: `NS::UInteger minimumLinearTextureAlignmentForPixelFormat(MTL::PixelFormat format)`
68    #[inline]
69    pub fn minimum_linear_texture_alignment_for_pixel_format(
70        &self,
71        format: PixelFormat,
72    ) -> UInteger {
73        unsafe {
74            msg_send_1(
75                self.as_ptr(),
76                sel!(minimumLinearTextureAlignmentForPixelFormat:),
77                format,
78            )
79        }
80    }
81
82    /// Get the minimum alignment for a texture buffer with the given pixel format.
83    ///
84    /// C++ equivalent: `NS::UInteger minimumTextureBufferAlignmentForPixelFormat(MTL::PixelFormat format)`
85    #[inline]
86    pub fn minimum_texture_buffer_alignment_for_pixel_format(
87        &self,
88        format: PixelFormat,
89    ) -> UInteger {
90        unsafe {
91            msg_send_1(
92                self.as_ptr(),
93                sel!(minimumTextureBufferAlignmentForPixelFormat:),
94                format,
95            )
96        }
97    }
98
99    // =========================================================================
100    // Sparse Texture Support
101    // =========================================================================
102
103    /// Get the sparse tile size in bytes.
104    ///
105    /// C++ equivalent: `NS::UInteger sparseTileSizeInBytes() const`
106    #[inline]
107    pub fn sparse_tile_size_in_bytes(&self) -> UInteger {
108        unsafe { msg_send_0(self.as_ptr(), sel!(sparseTileSizeInBytes)) }
109    }
110
111    /// Get the sparse tile size in bytes for a specific page size.
112    ///
113    /// C++ equivalent: `NS::UInteger sparseTileSizeInBytes(MTL::SparsePageSize sparsePageSize)`
114    #[inline]
115    pub fn sparse_tile_size_in_bytes_for_page_size(&self, page_size: SparsePageSize) -> UInteger {
116        unsafe {
117            msg_send_1(
118                self.as_ptr(),
119                sel!(sparseTileSizeInBytesForSparsePageSize:),
120                page_size,
121            )
122        }
123    }
124
125    /// Get the sparse tile size for a texture configuration.
126    ///
127    /// C++ equivalent: `Size sparseTileSize(MTL::TextureType, MTL::PixelFormat, NS::UInteger sampleCount)`
128    #[inline]
129    pub fn sparse_tile_size(
130        &self,
131        texture_type: TextureType,
132        pixel_format: PixelFormat,
133        sample_count: UInteger,
134    ) -> Size {
135        unsafe {
136            msg_send_3(
137                self.as_ptr(),
138                sel!(sparseTileSizeWithTextureType: pixelFormat: sampleCount:),
139                texture_type,
140                pixel_format,
141                sample_count,
142            )
143        }
144    }
145
146    /// Get the sparse tile size for a texture configuration with specific page size.
147    ///
148    /// C++ equivalent: `Size sparseTileSize(MTL::TextureType, MTL::PixelFormat, NS::UInteger, MTL::SparsePageSize)`
149    #[inline]
150    pub fn sparse_tile_size_with_page_size(
151        &self,
152        texture_type: TextureType,
153        pixel_format: PixelFormat,
154        sample_count: UInteger,
155        page_size: SparsePageSize,
156    ) -> Size {
157        unsafe {
158            msg_send_4(
159                self.as_ptr(),
160                sel!(sparseTileSizeWithTextureType: pixelFormat: sampleCount: sparsePageSize:),
161                texture_type,
162                pixel_format,
163                sample_count,
164                page_size,
165            )
166        }
167    }
168
169    /// Convert pixel regions to tile regions for sparse textures.
170    ///
171    /// C++ equivalent: `void convertSparsePixelRegions(...)`
172    pub fn convert_sparse_pixel_regions(
173        &self,
174        pixel_regions: &[Region],
175        tile_regions: &mut [Region],
176        tile_size: Size,
177        mode: SparseTextureRegionAlignmentMode,
178    ) {
179        assert_eq!(pixel_regions.len(), tile_regions.len());
180        unsafe {
181            mtl_sys::msg_send_5::<
182                (),
183                *const Region,
184                *mut Region,
185                Size,
186                SparseTextureRegionAlignmentMode,
187                usize,
188            >(
189                self.as_ptr(),
190                sel!(convertSparsePixelRegions: toTileRegions: withTileSize: alignmentMode: numRegions:),
191                pixel_regions.as_ptr(),
192                tile_regions.as_mut_ptr(),
193                tile_size,
194                mode,
195                pixel_regions.len(),
196            );
197        }
198    }
199
200    /// Convert tile regions to pixel regions for sparse textures.
201    ///
202    /// C++ equivalent: `void convertSparseTileRegions(...)`
203    pub fn convert_sparse_tile_regions(
204        &self,
205        tile_regions: &[Region],
206        pixel_regions: &mut [Region],
207        tile_size: Size,
208    ) {
209        assert_eq!(tile_regions.len(), pixel_regions.len());
210        unsafe {
211            msg_send_4::<(), *const Region, *mut Region, Size, usize>(
212                self.as_ptr(),
213                sel!(convertSparseTileRegions: toPixelRegions: withTileSize: numRegions:),
214                tile_regions.as_ptr(),
215                pixel_regions.as_mut_ptr(),
216                tile_size,
217                tile_regions.len(),
218            );
219        }
220    }
221
222    // =========================================================================
223    // Heap Size and Alignment
224    // =========================================================================
225
226    /// Get the size and alignment for a buffer in a heap.
227    ///
228    /// C++ equivalent: `SizeAndAlign heapBufferSizeAndAlign(NS::UInteger length, MTL::ResourceOptions options)`
229    #[inline]
230    pub fn heap_buffer_size_and_align(
231        &self,
232        length: UInteger,
233        options: ResourceOptions,
234    ) -> SizeAndAlign {
235        unsafe {
236            msg_send_2(
237                self.as_ptr(),
238                sel!(heapBufferSizeAndAlignWithLength: options:),
239                length,
240                options,
241            )
242        }
243    }
244
245    /// Get the size and alignment for a texture in a heap.
246    ///
247    /// C++ equivalent: `SizeAndAlign heapTextureSizeAndAlign(const MTL::TextureDescriptor* desc)`
248    ///
249    /// # Safety
250    ///
251    /// The descriptor pointer must be a valid MTLTextureDescriptor.
252    #[inline]
253    pub unsafe fn heap_texture_size_and_align(&self, descriptor: *const c_void) -> SizeAndAlign {
254        unsafe {
255            msg_send_1(
256                self.as_ptr(),
257                sel!(heapTextureSizeAndAlignWithDescriptor:),
258                descriptor,
259            )
260        }
261    }
262
263    /// Get the size and alignment for an acceleration structure in a heap (by size).
264    ///
265    /// C++ equivalent: `SizeAndAlign heapAccelerationStructureSizeAndAlign(NS::UInteger size)`
266    #[inline]
267    pub fn heap_acceleration_structure_size_and_align_with_size(
268        &self,
269        size: UInteger,
270    ) -> SizeAndAlign {
271        unsafe {
272            msg_send_1(
273                self.as_ptr(),
274                sel!(heapAccelerationStructureSizeAndAlignWithSize:),
275                size,
276            )
277        }
278    }
279
280    /// Get the size and alignment for an acceleration structure in a heap (by descriptor).
281    ///
282    /// C++ equivalent: `SizeAndAlign heapAccelerationStructureSizeAndAlign(const MTL::AccelerationStructureDescriptor*)`
283    ///
284    /// # Safety
285    ///
286    /// The descriptor pointer must be a valid MTLAccelerationStructureDescriptor.
287    #[inline]
288    pub unsafe fn heap_acceleration_structure_size_and_align_with_descriptor(
289        &self,
290        descriptor: *const c_void,
291    ) -> SizeAndAlign {
292        unsafe {
293            msg_send_1(
294                self.as_ptr(),
295                sel!(heapAccelerationStructureSizeAndAlignWithDescriptor:),
296                descriptor,
297            )
298        }
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305    use crate::device::system_default;
306
307    #[test]
308    fn test_buffer_limits() {
309        let device = system_default().expect("no Metal device");
310
311        let max_buffer = device.max_buffer_length();
312        assert!(max_buffer > 0, "max buffer length should be positive");
313        println!("Max buffer length: {} bytes", max_buffer);
314    }
315
316    #[test]
317    fn test_thread_limits() {
318        let device = system_default().expect("no Metal device");
319
320        let max_threads = device.max_threads_per_threadgroup();
321        let w = { max_threads.width };
322        let h = { max_threads.height };
323        let d = { max_threads.depth };
324        assert!(w > 0 && h > 0 && d > 0);
325        println!("Max threads per threadgroup: {}x{}x{}", w, h, d);
326
327        let max_tg_memory = device.max_threadgroup_memory_length();
328        assert!(max_tg_memory > 0);
329        println!("Max threadgroup memory: {} bytes", max_tg_memory);
330    }
331
332    #[test]
333    fn test_texture_alignment() {
334        let device = system_default().expect("no Metal device");
335
336        let alignment =
337            device.minimum_linear_texture_alignment_for_pixel_format(PixelFormat::RGBA8_UNORM);
338        assert!(alignment > 0);
339        println!("RGBA8 linear texture alignment: {} bytes", alignment);
340    }
341}