Skip to main content

mtl_gpu/device/
buffer.rs

1//! Device buffer creation methods.
2//!
3//! Corresponds to buffer creation methods in `Metal/MTLDevice.hpp`.
4
5use std::ffi::c_void;
6
7use mtl_foundation::{Referencing, UInteger};
8use mtl_sys::{msg_send_2, msg_send_3, msg_send_4, sel};
9
10use super::Device;
11use crate::buffer::Buffer;
12use crate::enums::ResourceOptions;
13
14impl Device {
15    // =========================================================================
16    // Buffer Creation
17    // =========================================================================
18
19    /// Create a new buffer with the specified length and options.
20    ///
21    /// C++ equivalent: `Buffer* newBuffer(NS::UInteger length, MTL::ResourceOptions options)`
22    pub fn new_buffer(&self, length: UInteger, options: ResourceOptions) -> Option<Buffer> {
23        unsafe {
24            let ptr: *mut c_void = msg_send_2(
25                self.as_ptr(),
26                sel!(newBufferWithLength: options:),
27                length,
28                options,
29            );
30            Buffer::from_raw(ptr)
31        }
32    }
33
34    /// Create a new buffer initialized with the specified data.
35    ///
36    /// C++ equivalent: `Buffer* newBuffer(const void* pointer, NS::UInteger length, MTL::ResourceOptions options)`
37    pub fn new_buffer_with_bytes(&self, bytes: &[u8], options: ResourceOptions) -> Option<Buffer> {
38        unsafe {
39            let ptr: *mut c_void = msg_send_3(
40                self.as_ptr(),
41                sel!(newBufferWithBytes: length: options:),
42                bytes.as_ptr() as *const c_void,
43                bytes.len() as UInteger,
44                options,
45            );
46            Buffer::from_raw(ptr)
47        }
48    }
49
50    /// Create a new buffer initialized with the specified data (raw pointer version).
51    ///
52    /// C++ equivalent: `Buffer* newBuffer(const void* pointer, NS::UInteger length, MTL::ResourceOptions options)`
53    ///
54    /// # Safety
55    ///
56    /// The pointer must be valid for `length` bytes.
57    pub unsafe fn new_buffer_with_bytes_ptr(
58        &self,
59        pointer: *const c_void,
60        length: UInteger,
61        options: ResourceOptions,
62    ) -> Option<Buffer> {
63        unsafe {
64            let ptr: *mut c_void = msg_send_3(
65                self.as_ptr(),
66                sel!(newBufferWithBytes: length: options:),
67                pointer,
68                length,
69                options,
70            );
71            Buffer::from_raw(ptr)
72        }
73    }
74
75    /// Create a new buffer that wraps existing memory without copying.
76    ///
77    /// The deallocator block will be called when the buffer is deallocated.
78    ///
79    /// C++ equivalent: `Buffer* newBuffer(void* pointer, NS::UInteger length, MTL::ResourceOptions options, void (^)(void*, NS::UInteger))`
80    ///
81    /// # Safety
82    ///
83    /// - The pointer must remain valid until the deallocator is called.
84    /// - The pointer must be page-aligned.
85    pub unsafe fn new_buffer_with_bytes_no_copy<F>(
86        &self,
87        pointer: *mut c_void,
88        length: UInteger,
89        options: ResourceOptions,
90        deallocator: F,
91    ) -> Option<Buffer>
92    where
93        F: Fn(*mut c_void, usize) + Send + 'static,
94    {
95        let block = mtl_sys::DeallocatorBlock::from_fn(deallocator);
96
97        unsafe {
98            let ptr: *mut c_void = msg_send_4(
99                self.as_ptr(),
100                sel!(newBufferWithBytesNoCopy: length: options: deallocator:),
101                pointer,
102                length,
103                options,
104                block.as_ptr(),
105            );
106            // Metal retains the block
107            std::mem::forget(block);
108            Buffer::from_raw(ptr)
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::device::system_default;
117
118    #[test]
119    fn test_new_buffer() {
120        let device = system_default().expect("no Metal device");
121        let buffer = device.new_buffer(1024, ResourceOptions::STORAGE_MODE_SHARED);
122        assert!(buffer.is_some());
123
124        let buffer = buffer.unwrap();
125        assert_eq!(buffer.length(), 1024);
126    }
127
128    #[test]
129    fn test_new_buffer_with_bytes() {
130        let device = system_default().expect("no Metal device");
131        let data = [0u8; 256];
132        let buffer = device.new_buffer_with_bytes(&data, ResourceOptions::STORAGE_MODE_SHARED);
133        assert!(buffer.is_some());
134
135        let buffer = buffer.unwrap();
136        assert_eq!(buffer.length(), 256);
137    }
138
139    #[test]
140    fn test_buffer_contents() {
141        let device = system_default().expect("no Metal device");
142        let data = [1u8, 2, 3, 4, 5, 6, 7, 8];
143        let buffer = device
144            .new_buffer_with_bytes(&data, ResourceOptions::STORAGE_MODE_SHARED)
145            .expect("failed to create buffer");
146
147        let contents = buffer.contents();
148        assert!(contents.is_some());
149
150        // Verify the data was copied
151        let ptr = contents.unwrap() as *const u8;
152        unsafe {
153            assert_eq!(*ptr, 1);
154            assert_eq!(*ptr.add(7), 8);
155        }
156    }
157}