1use std::ffi::c_void;
9use std::ptr::NonNull;
10
11use mtl_foundation::{Referencing, UInteger};
12use mtl_sys::{msg_send_0, msg_send_1, sel};
13
14use crate::enums::{BufferSparseTier, ResourceOptions};
15use crate::types::ResourceID;
16
17#[repr(transparent)]
24pub struct Buffer(pub(crate) NonNull<c_void>);
25
26impl Buffer {
27 #[inline]
33 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
34 NonNull::new(ptr).map(Self)
35 }
36
37 #[inline]
39 pub fn as_raw(&self) -> *mut c_void {
40 self.0.as_ptr()
41 }
42
43 #[inline]
51 pub fn length(&self) -> UInteger {
52 unsafe { msg_send_0(self.as_ptr(), sel!(length)) }
53 }
54
55 #[inline]
61 pub fn contents(&self) -> Option<*mut c_void> {
62 unsafe {
63 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(contents));
64 if ptr.is_null() { None } else { Some(ptr) }
65 }
66 }
67
68 #[inline]
75 pub fn did_modify_range(&self, location: UInteger, length: UInteger) {
76 unsafe {
77 let range = mtl_foundation::Range::new(location, length);
78 msg_send_1::<(), mtl_foundation::Range>(self.as_ptr(), sel!(didModifyRange:), range);
79 }
80 }
81
82 #[inline]
89 pub fn gpu_address(&self) -> u64 {
90 unsafe { msg_send_0(self.as_ptr(), sel!(gpuAddress)) }
91 }
92
93 pub unsafe fn new_texture(
101 &self,
102 descriptor: *const c_void,
103 offset: UInteger,
104 bytes_per_row: UInteger,
105 ) -> Option<crate::texture::Texture> {
106 unsafe {
107 let ptr: *mut c_void = mtl_sys::msg_send_3(
108 self.as_ptr(),
109 sel!(newTextureWithDescriptor: offset: bytesPerRow:),
110 descriptor,
111 offset,
112 bytes_per_row,
113 );
114 crate::texture::Texture::from_raw(ptr)
115 }
116 }
117
118 pub fn new_tensor(
122 &self,
123 descriptor: &crate::tensor::TensorDescriptor,
124 offset: UInteger,
125 bytes_per_row: UInteger,
126 ) -> Option<crate::tensor::Tensor> {
127 unsafe {
128 let ptr: *mut c_void = mtl_sys::msg_send_3(
129 self.as_ptr(),
130 sel!(newTensorWithDescriptor: offset: bytesPerRow:),
131 descriptor.as_ptr(),
132 offset,
133 bytes_per_row,
134 );
135 crate::tensor::Tensor::from_raw(ptr)
136 }
137 }
138
139 pub fn add_debug_marker(&self, marker: &str, location: UInteger, length: UInteger) {
143 if let Some(ns_marker) = mtl_foundation::String::from_str(marker) {
144 let range = mtl_foundation::Range::new(location, length);
145 unsafe {
146 mtl_sys::msg_send_2::<(), *const c_void, mtl_foundation::Range>(
147 self.as_ptr(),
148 sel!(addDebugMarker: range:),
149 ns_marker.as_ptr(),
150 range,
151 );
152 }
153 }
154 }
155
156 #[inline]
160 pub fn remove_all_debug_markers(&self) {
161 unsafe {
162 msg_send_0::<()>(self.as_ptr(), sel!(removeAllDebugMarkers));
163 }
164 }
165
166 #[inline]
170 pub fn remote_storage_buffer(&self) -> Option<Buffer> {
171 unsafe {
172 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(remoteStorageBuffer));
173 if ptr.is_null() {
174 return None;
175 }
176 let _: *mut c_void = msg_send_0(ptr, sel!(retain));
178 Buffer::from_raw(ptr)
179 }
180 }
181
182 pub fn new_remote_buffer_view_for_device(&self, device: &crate::Device) -> Option<Buffer> {
186 unsafe {
187 let ptr: *mut c_void = msg_send_1(
188 self.as_ptr(),
189 sel!(newRemoteBufferViewForDevice:),
190 device.as_ptr(),
191 );
192 Buffer::from_raw(ptr)
193 }
194 }
195
196 #[inline]
200 pub fn gpu_resource_id(&self) -> ResourceID {
201 unsafe { msg_send_0(self.as_ptr(), sel!(gpuResourceID)) }
202 }
203
204 #[inline]
208 pub fn sparse_buffer_tier(&self) -> BufferSparseTier {
209 unsafe { msg_send_0(self.as_ptr(), sel!(sparseBufferTier)) }
210 }
211
212 pub fn label(&self) -> Option<String> {
220 unsafe {
221 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
222 if ptr.is_null() {
223 return None;
224 }
225 let utf8_ptr: *const std::ffi::c_char =
227 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
228 if utf8_ptr.is_null() {
229 return None;
230 }
231 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
232 Some(c_str.to_string_lossy().into_owned())
233 }
234 }
235
236 pub fn set_label(&self, label: &str) {
240 if let Some(ns_label) = mtl_foundation::String::from_str(label) {
241 unsafe {
242 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
243 }
244 }
245 }
246
247 pub fn device(&self) -> crate::Device {
251 unsafe {
252 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
253 let _: *mut c_void = msg_send_0(ptr, sel!(retain));
255 crate::Device::from_raw(ptr).expect("buffer has no device")
256 }
257 }
258
259 #[inline]
263 pub fn resource_options(&self) -> ResourceOptions {
264 unsafe { msg_send_0(self.as_ptr(), sel!(resourceOptions)) }
265 }
266
267 #[inline]
271 pub fn allocated_size(&self) -> UInteger {
272 unsafe { msg_send_0(self.as_ptr(), sel!(allocatedSize)) }
273 }
274}
275
276impl Clone for Buffer {
277 fn clone(&self) -> Self {
278 unsafe {
279 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
280 }
281 Self(self.0)
282 }
283}
284
285impl Drop for Buffer {
286 fn drop(&mut self) {
287 unsafe {
288 msg_send_0::<()>(self.as_ptr(), sel!(release));
289 }
290 }
291}
292
293impl Referencing for Buffer {
294 #[inline]
295 fn as_ptr(&self) -> *const c_void {
296 self.0.as_ptr()
297 }
298}
299
300unsafe impl Send for Buffer {}
302unsafe impl Sync for Buffer {}
303
304impl std::fmt::Debug for Buffer {
305 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306 f.debug_struct("Buffer")
307 .field("length", &self.length())
308 .field("label", &self.label())
309 .finish()
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_buffer_size() {
319 assert_eq!(
320 std::mem::size_of::<Buffer>(),
321 std::mem::size_of::<*mut c_void>()
322 );
323 }
324}