Skip to main content

mtl_gpu/encoder/
resource_state_encoder.rs

1//! Resource state command encoder.
2//!
3//! Corresponds to `Metal/MTLResourceStateCommandEncoder.hpp`.
4
5use std::ffi::c_void;
6use std::ptr::NonNull;
7
8use mtl_foundation::{Referencing, UInteger};
9use mtl_sys::{msg_send_0, msg_send_1, sel};
10
11use crate::Buffer;
12use crate::Texture;
13use crate::enums::SparseTextureMappingMode;
14use crate::sync::Fence;
15use crate::types::{Origin, Region, Size};
16
17/// Arguments for indirect texture mapping operations.
18///
19/// C++ equivalent: `MTL::MapIndirectArguments`
20#[repr(C, packed)]
21#[derive(Copy, Clone, Debug, Default)]
22pub struct MapIndirectArguments {
23    /// X origin of the region.
24    pub region_origin_x: u32,
25    /// Y origin of the region.
26    pub region_origin_y: u32,
27    /// Z origin of the region.
28    pub region_origin_z: u32,
29    /// Width of the region.
30    pub region_size_width: u32,
31    /// Height of the region.
32    pub region_size_height: u32,
33    /// Depth of the region.
34    pub region_size_depth: u32,
35    /// Mipmap level.
36    pub mip_map_level: u32,
37    /// Slice ID.
38    pub slice_id: u32,
39}
40
41/// A command encoder for resource state operations.
42///
43/// C++ equivalent: `MTL::ResourceStateCommandEncoder`
44///
45/// Resource state encoders are used to update sparse texture mappings
46/// and manage resource state for sparse resources.
47#[repr(transparent)]
48pub struct ResourceStateCommandEncoder(pub(crate) NonNull<c_void>);
49
50impl ResourceStateCommandEncoder {
51    /// Create a ResourceStateCommandEncoder from a raw pointer.
52    ///
53    /// # Safety
54    ///
55    /// The pointer must be a valid Metal resource state command encoder object.
56    #[inline]
57    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
58        NonNull::new(ptr).map(Self)
59    }
60
61    /// Get the raw pointer to the encoder.
62    #[inline]
63    pub fn as_raw(&self) -> *mut c_void {
64        self.0.as_ptr()
65    }
66
67    // =========================================================================
68    // CommandEncoder base methods
69    // =========================================================================
70
71    /// Get the device that created this encoder.
72    ///
73    /// C++ equivalent: `Device* device() const`
74    pub fn device(&self) -> crate::Device {
75        unsafe {
76            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
77            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
78            crate::Device::from_raw(ptr).expect("encoder has no device")
79        }
80    }
81
82    /// Get the command buffer that this encoder is encoding into.
83    ///
84    /// C++ equivalent: `CommandBuffer* commandBuffer() const`
85    pub fn command_buffer(&self) -> crate::CommandBuffer {
86        unsafe {
87            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(commandBuffer));
88            let _: *mut c_void = msg_send_0(ptr, sel!(retain));
89            crate::CommandBuffer::from_raw(ptr).expect("encoder has no command buffer")
90        }
91    }
92
93    /// Get the label for this encoder.
94    ///
95    /// C++ equivalent: `NS::String* label() const`
96    pub fn label(&self) -> Option<String> {
97        unsafe {
98            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
99            if ptr.is_null() {
100                return None;
101            }
102            let utf8_ptr: *const std::ffi::c_char =
103                mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
104            if utf8_ptr.is_null() {
105                return None;
106            }
107            let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
108            Some(c_str.to_string_lossy().into_owned())
109        }
110    }
111
112    /// Set the label for this encoder.
113    ///
114    /// C++ equivalent: `void setLabel(const NS::String*)`
115    pub fn set_label(&self, label: &str) {
116        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
117            unsafe {
118                msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
119            }
120        }
121    }
122
123    /// End encoding commands with this encoder.
124    ///
125    /// C++ equivalent: `void endEncoding()`
126    #[inline]
127    pub fn end_encoding(&self) {
128        unsafe {
129            msg_send_0::<()>(self.as_ptr(), sel!(endEncoding));
130        }
131    }
132
133    /// Insert a debug signpost.
134    ///
135    /// C++ equivalent: `void insertDebugSignpost(const NS::String*)`
136    pub fn insert_debug_signpost(&self, string: &str) {
137        if let Some(ns_string) = mtl_foundation::String::from_str(string) {
138            unsafe {
139                msg_send_1::<(), *const c_void>(
140                    self.as_ptr(),
141                    sel!(insertDebugSignpost:),
142                    ns_string.as_ptr(),
143                );
144            }
145        }
146    }
147
148    /// Push a debug group.
149    ///
150    /// C++ equivalent: `void pushDebugGroup(const NS::String*)`
151    pub fn push_debug_group(&self, string: &str) {
152        if let Some(ns_string) = mtl_foundation::String::from_str(string) {
153            unsafe {
154                msg_send_1::<(), *const c_void>(
155                    self.as_ptr(),
156                    sel!(pushDebugGroup:),
157                    ns_string.as_ptr(),
158                );
159            }
160        }
161    }
162
163    /// Pop the current debug group.
164    ///
165    /// C++ equivalent: `void popDebugGroup()`
166    #[inline]
167    pub fn pop_debug_group(&self) {
168        unsafe {
169            msg_send_0::<()>(self.as_ptr(), sel!(popDebugGroup));
170        }
171    }
172
173    /// Insert a barrier to synchronize queue stages.
174    ///
175    /// C++ equivalent: `void barrierAfterQueueStages(Stages, Stages)`
176    #[inline]
177    pub fn barrier_after_queue_stages(
178        &self,
179        after_stages: crate::enums::Stages,
180        before_stages: crate::enums::Stages,
181    ) {
182        unsafe {
183            mtl_sys::msg_send_2::<(), crate::enums::Stages, crate::enums::Stages>(
184                self.as_ptr(),
185                sel!(barrierAfterQueueStages:beforeQueueStages:),
186                after_stages,
187                before_stages,
188            );
189        }
190    }
191
192    // =========================================================================
193    // Texture Mapping Operations
194    // =========================================================================
195
196    /// Update sparse texture mapping for a region.
197    ///
198    /// C++ equivalent: `void updateTextureMapping(const Texture*, SparseTextureMappingMode, Region, NS::UInteger, NS::UInteger)`
199    pub fn update_texture_mapping(
200        &self,
201        texture: &Texture,
202        mode: SparseTextureMappingMode,
203        region: Region,
204        mip_level: UInteger,
205        slice: UInteger,
206    ) {
207        unsafe {
208            mtl_sys::msg_send_5::<
209                (),
210                *const c_void,
211                SparseTextureMappingMode,
212                Region,
213                UInteger,
214                UInteger,
215            >(
216                self.as_ptr(),
217                sel!(updateTextureMapping: mode: region: mipLevel: slice:),
218                texture.as_ptr(),
219                mode,
220                region,
221                mip_level,
222                slice,
223            );
224        }
225    }
226
227    /// Update sparse texture mapping using indirect arguments from a buffer.
228    ///
229    /// C++ equivalent: `void updateTextureMapping(const Texture*, SparseTextureMappingMode, const Buffer*, NS::UInteger)`
230    pub fn update_texture_mapping_indirect(
231        &self,
232        texture: &Texture,
233        mode: SparseTextureMappingMode,
234        indirect_buffer: &Buffer,
235        indirect_buffer_offset: UInteger,
236    ) {
237        unsafe {
238            mtl_sys::msg_send_4::<
239                (),
240                *const c_void,
241                SparseTextureMappingMode,
242                *const c_void,
243                UInteger,
244            >(
245                self.as_ptr(),
246                sel!(updateTextureMapping: mode: indirectBuffer: indirectBufferOffset:),
247                texture.as_ptr(),
248                mode,
249                indirect_buffer.as_ptr(),
250                indirect_buffer_offset,
251            );
252        }
253    }
254
255    /// Update sparse texture mappings for multiple regions.
256    ///
257    /// C++ equivalent: `void updateTextureMappings(const Texture*, SparseTextureMappingMode, const Region*, const NS::UInteger*, const NS::UInteger*, NS::UInteger)`
258    ///
259    /// # Safety
260    ///
261    /// The regions, mip_levels, and slices arrays must have at least `num_regions` elements.
262    pub unsafe fn update_texture_mappings(
263        &self,
264        texture: &Texture,
265        mode: SparseTextureMappingMode,
266        regions: *const Region,
267        mip_levels: *const UInteger,
268        slices: *const UInteger,
269        num_regions: UInteger,
270    ) {
271        unsafe {
272            mtl_sys::msg_send_6::<
273                (),
274                *const c_void,
275                SparseTextureMappingMode,
276                *const Region,
277                *const UInteger,
278                *const UInteger,
279                UInteger,
280            >(
281                self.as_ptr(),
282                sel!(updateTextureMappings: mode: regions: mipLevels: slices: numRegions:),
283                texture.as_ptr(),
284                mode,
285                regions,
286                mip_levels,
287                slices,
288                num_regions,
289            );
290        }
291    }
292
293    /// Move texture mappings from one texture to another.
294    ///
295    /// C++ equivalent: `void moveTextureMappingsFromTexture(...)`
296    #[allow(clippy::too_many_arguments)]
297    pub fn move_texture_mappings_from_texture(
298        &self,
299        source_texture: &Texture,
300        source_slice: UInteger,
301        source_level: UInteger,
302        source_origin: Origin,
303        source_size: Size,
304        destination_texture: &Texture,
305        destination_slice: UInteger,
306        destination_level: UInteger,
307        destination_origin: Origin,
308    ) {
309        unsafe {
310            mtl_sys::msg_send_9::<
311                (),
312                *const c_void,
313                UInteger,
314                UInteger,
315                Origin,
316                Size,
317                *const c_void,
318                UInteger,
319                UInteger,
320                Origin,
321            >(
322                self.as_ptr(),
323                sel!(moveTextureMappingsFromTexture: sourceSlice: sourceLevel: sourceOrigin: sourceSize: toTexture: destinationSlice: destinationLevel: destinationOrigin:),
324                source_texture.as_ptr(),
325                source_slice,
326                source_level,
327                source_origin,
328                source_size,
329                destination_texture.as_ptr(),
330                destination_slice,
331                destination_level,
332                destination_origin,
333            );
334        }
335    }
336
337    // =========================================================================
338    // Fence Operations
339    // =========================================================================
340
341    /// Update a fence.
342    ///
343    /// C++ equivalent: `void updateFence(const Fence*)`
344    #[inline]
345    pub fn update_fence(&self, fence: &Fence) {
346        unsafe {
347            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(updateFence:), fence.as_ptr());
348        }
349    }
350
351    /// Wait for a fence.
352    ///
353    /// C++ equivalent: `void waitForFence(const Fence*)`
354    #[inline]
355    pub fn wait_for_fence(&self, fence: &Fence) {
356        unsafe {
357            msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(waitForFence:), fence.as_ptr());
358        }
359    }
360}
361
362impl Clone for ResourceStateCommandEncoder {
363    fn clone(&self) -> Self {
364        unsafe {
365            msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
366        }
367        Self(self.0)
368    }
369}
370
371impl Drop for ResourceStateCommandEncoder {
372    fn drop(&mut self) {
373        unsafe {
374            msg_send_0::<()>(self.as_ptr(), sel!(release));
375        }
376    }
377}
378
379impl Referencing for ResourceStateCommandEncoder {
380    #[inline]
381    fn as_ptr(&self) -> *const c_void {
382        self.0.as_ptr()
383    }
384}
385
386unsafe impl Send for ResourceStateCommandEncoder {}
387unsafe impl Sync for ResourceStateCommandEncoder {}
388
389impl std::fmt::Debug for ResourceStateCommandEncoder {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        f.debug_struct("ResourceStateCommandEncoder")
392            .field("label", &self.label())
393            .finish()
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use super::*;
400
401    #[test]
402    fn test_resource_state_encoder_size() {
403        assert_eq!(
404            std::mem::size_of::<ResourceStateCommandEncoder>(),
405            std::mem::size_of::<*mut c_void>()
406        );
407    }
408
409    #[test]
410    fn test_map_indirect_arguments_size() {
411        // 8 u32 fields = 32 bytes
412        assert_eq!(std::mem::size_of::<MapIndirectArguments>(), 32);
413    }
414}