use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use objc2::runtime::ProtocolObject;
use objc2_metal::MTLBuffer;
use super::Device;
use super::allocator::{Pool, Raw};
use crate::{GpuError, Result};
pub struct Buffer {
raw: Option<Raw>,
ptr: NonNull<u8>,
len: usize,
size: usize,
pool: Arc<Pool>,
in_flight: Arc<AtomicUsize>,
}
unsafe impl Send for Buffer {}
unsafe impl Sync for Buffer {}
impl Device {
pub fn new_buffer(&self, len: usize) -> Result<Buffer> {
let pool = self.pool().clone();
let (raw, size) = match pool.alloc(self.raw(), len)? {
Some((raw, size)) => (Some(raw), size),
None => (None, 0),
};
let ptr = raw
.as_ref()
.map_or(NonNull::dangling(), |r| r.contents().cast());
Ok(Buffer {
raw,
ptr,
len,
size,
pool,
in_flight: Arc::new(AtomicUsize::new(0)),
})
}
pub fn buffer_from(&self, bytes: &[u8]) -> Result<Buffer> {
let mut buffer = self.new_buffer(bytes.len())?;
buffer.contents_mut()?.copy_from_slice(bytes);
Ok(buffer)
}
}
impl Buffer {
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn in_use(&self) -> bool {
self.in_flight.load(Ordering::Acquire) != 0
}
pub fn contents(&self) -> Result<&[u8]> {
if self.in_use() {
return Err(GpuError::BufferInUse);
}
if self.raw.is_none() {
return Ok(&[]);
}
Ok(unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) })
}
pub fn contents_mut(&mut self) -> Result<&mut [u8]> {
if self.in_use() {
return Err(GpuError::BufferInUse);
}
if self.raw.is_none() {
return Ok(&mut []);
}
Ok(unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) })
}
pub(crate) fn raw(&self) -> Option<&ProtocolObject<dyn MTLBuffer>> {
self.raw.as_deref()
}
pub(crate) fn retained(&self) -> Option<Raw> {
self.raw.clone()
}
pub(crate) fn guard(&self) -> Arc<AtomicUsize> {
self.in_flight.clone()
}
pub(crate) fn id(&self) -> usize {
Arc::as_ptr(&self.in_flight) as usize
}
}
impl Drop for Buffer {
fn drop(&mut self) {
if let Some(raw) = self.raw.take() {
let reusable = !self.in_use();
self.pool.free(raw, self.size, reusable);
}
}
}
impl std::fmt::Debug for Buffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Buffer")
.field("len", &self.len)
.field("in_use", &self.in_use())
.finish()
}
}