use super::{
sys::UnsafeBuffer, BufferAccess, BufferAccessObject, BufferContents, BufferInner, BufferUsage,
};
use crate::{
buffer::{sys::UnsafeBufferCreateInfo, BufferCreationError, TypedBufferAccess},
device::{physical::QueueFamily, Device, DeviceOwned, Queue},
memory::{
pool::{
AllocFromRequirementsFilter, AllocLayout, MappingRequirement, MemoryPoolAlloc,
PotentialDedicatedAllocation, StdMemoryPoolAlloc,
},
DedicatedAllocation, DeviceMemoryAllocationError, MappedDeviceMemory, MemoryPool,
},
sync::{AccessError, Sharing},
DeviceSize,
};
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use smallvec::SmallVec;
use std::{
error, fmt,
hash::{Hash, Hasher},
marker::PhantomData,
mem::size_of,
ops::{Deref, DerefMut, Range},
ptr,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
#[derive(Debug)]
pub struct CpuAccessibleBuffer<T, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>>
where
T: BufferContents + ?Sized,
{
inner: UnsafeBuffer,
memory: A,
access: RwLock<CurrentGpuAccess>,
queue_families: SmallVec<[u32; 4]>,
marker: PhantomData<Box<T>>,
}
#[derive(Debug)]
enum CurrentGpuAccess {
NonExclusive {
num: AtomicUsize,
},
Exclusive {
num: usize,
},
}
impl<T> CpuAccessibleBuffer<T>
where
T: BufferContents,
{
pub fn from_data(
device: Arc<Device>,
usage: BufferUsage,
host_cached: bool,
data: T,
) -> Result<Arc<CpuAccessibleBuffer<T>>, DeviceMemoryAllocationError> {
unsafe {
let uninitialized = CpuAccessibleBuffer::raw(
device,
size_of::<T>() as DeviceSize,
usage,
host_cached,
[],
)?;
{
let mut mapping = uninitialized.write().unwrap();
ptr::write::<T>(&mut *mapping, data)
}
Ok(uninitialized)
}
}
#[inline]
pub unsafe fn uninitialized(
device: Arc<Device>,
usage: BufferUsage,
host_cached: bool,
) -> Result<Arc<CpuAccessibleBuffer<T>>, DeviceMemoryAllocationError> {
CpuAccessibleBuffer::raw(device, size_of::<T>() as DeviceSize, usage, host_cached, [])
}
}
impl<T> CpuAccessibleBuffer<[T]>
where
[T]: BufferContents,
{
pub fn from_iter<I>(
device: Arc<Device>,
usage: BufferUsage,
host_cached: bool,
data: I,
) -> Result<Arc<CpuAccessibleBuffer<[T]>>, DeviceMemoryAllocationError>
where
I: IntoIterator<Item = T>,
I::IntoIter: ExactSizeIterator,
{
let data = data.into_iter();
unsafe {
let uninitialized = CpuAccessibleBuffer::uninitialized_array(
device,
data.len() as DeviceSize,
usage,
host_cached,
)?;
{
let mut mapping = uninitialized.write().unwrap();
for (i, o) in data.zip(mapping.iter_mut()) {
ptr::write(o, i);
}
}
Ok(uninitialized)
}
}
#[inline]
pub unsafe fn uninitialized_array(
device: Arc<Device>,
len: DeviceSize,
usage: BufferUsage,
host_cached: bool,
) -> Result<Arc<CpuAccessibleBuffer<[T]>>, DeviceMemoryAllocationError> {
CpuAccessibleBuffer::raw(
device,
len * size_of::<T>() as DeviceSize,
usage,
host_cached,
[],
)
}
}
impl<T> CpuAccessibleBuffer<T>
where
T: BufferContents + ?Sized,
{
pub unsafe fn raw<'a, I>(
device: Arc<Device>,
size: DeviceSize,
usage: BufferUsage,
host_cached: bool,
queue_families: I,
) -> Result<Arc<CpuAccessibleBuffer<T>>, DeviceMemoryAllocationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
let queue_families = queue_families
.into_iter()
.map(|f| f.id())
.collect::<SmallVec<[u32; 4]>>();
let buffer = {
match UnsafeBuffer::new(
device.clone(),
UnsafeBufferCreateInfo {
sharing: if queue_families.len() >= 2 {
Sharing::Concurrent(queue_families.clone())
} else {
Sharing::Exclusive
},
size,
usage,
..Default::default()
},
) {
Ok(b) => b,
Err(BufferCreationError::AllocError(err)) => return Err(err),
Err(_) => unreachable!(), }
};
let mem_reqs = buffer.memory_requirements();
let mem = MemoryPool::alloc_from_requirements(
&Device::standard_pool(&device),
&mem_reqs,
AllocLayout::Linear,
MappingRequirement::Map,
Some(DedicatedAllocation::Buffer(&buffer)),
|m| {
if m.is_host_cached() {
if host_cached {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
} else {
if host_cached {
AllocFromRequirementsFilter::Allowed
} else {
AllocFromRequirementsFilter::Preferred
}
}
},
)?;
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
debug_assert!(mem.mapped_memory().is_some());
buffer.bind_memory(mem.memory(), mem.offset())?;
Ok(Arc::new(CpuAccessibleBuffer {
inner: buffer,
memory: mem,
access: RwLock::new(CurrentGpuAccess::NonExclusive {
num: AtomicUsize::new(0),
}),
queue_families: queue_families,
marker: PhantomData,
}))
}
}
impl<T, A> CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
{
#[inline]
pub fn queue_families(&self) -> Vec<QueueFamily> {
self.queue_families
.iter()
.map(|&num| {
self.device()
.physical_device()
.queue_family_by_id(num)
.unwrap()
})
.collect()
}
}
impl<T, A> CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
A: MemoryPoolAlloc,
{
#[inline]
pub fn read(&self) -> Result<ReadLock<T>, ReadLockError> {
let lock = match self.access.try_read() {
Some(l) => l,
None => return Err(ReadLockError::CpuWriteLocked),
};
if let CurrentGpuAccess::Exclusive { .. } = *lock {
return Err(ReadLockError::GpuWriteLocked);
}
let mapped_memory = self.memory.mapped_memory().unwrap();
let offset = self.memory.offset();
let range = offset..offset + self.inner.size();
let bytes = unsafe {
mapped_memory.invalidate_range(range.clone()).unwrap();
mapped_memory.read(range.clone()).unwrap()
};
Ok(ReadLock {
data: T::from_bytes(bytes).unwrap(),
range,
lock,
})
}
#[inline]
pub fn write(&self) -> Result<WriteLock<T>, WriteLockError> {
let lock = match self.access.try_write() {
Some(l) => l,
None => return Err(WriteLockError::CpuLocked),
};
match *lock {
CurrentGpuAccess::NonExclusive { ref num } if num.load(Ordering::SeqCst) == 0 => (),
_ => return Err(WriteLockError::GpuLocked),
}
let mapped_memory = self.memory.mapped_memory().unwrap();
let offset = self.memory.offset();
let range = offset..offset + self.inner.size();
let bytes = unsafe {
mapped_memory.invalidate_range(range.clone()).unwrap();
mapped_memory.write(range.clone()).unwrap()
};
Ok(WriteLock {
data: T::from_bytes_mut(bytes).unwrap(),
mapped_memory,
range,
lock,
})
}
}
unsafe impl<T, A> BufferAccess for CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
#[inline]
fn inner(&self) -> BufferInner {
BufferInner {
buffer: &self.inner,
offset: 0,
}
}
#[inline]
fn size(&self) -> DeviceSize {
self.inner.size()
}
#[inline]
fn conflict_key(&self) -> (u64, u64) {
(self.inner.key(), 0)
}
#[inline]
fn try_gpu_lock(&self, exclusive_access: bool, _: &Queue) -> Result<(), AccessError> {
if exclusive_access {
let mut lock = match self.access.try_write() {
Some(lock) => lock,
None => return Err(AccessError::AlreadyInUse),
};
match *lock {
CurrentGpuAccess::NonExclusive { ref num } if num.load(Ordering::SeqCst) == 0 => (),
_ => return Err(AccessError::AlreadyInUse),
};
*lock = CurrentGpuAccess::Exclusive { num: 1 };
Ok(())
} else {
let lock = match self.access.try_read() {
Some(lock) => lock,
None => return Err(AccessError::AlreadyInUse),
};
match *lock {
CurrentGpuAccess::Exclusive { .. } => return Err(AccessError::AlreadyInUse),
CurrentGpuAccess::NonExclusive { ref num } => num.fetch_add(1, Ordering::SeqCst),
};
Ok(())
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
{
let read_lock = self.access.read();
if let CurrentGpuAccess::NonExclusive { ref num } = *read_lock {
let prev = num.fetch_add(1, Ordering::SeqCst);
debug_assert!(prev >= 1);
return;
}
}
{
let mut write_lock = self.access.write();
if let CurrentGpuAccess::Exclusive { ref mut num } = *write_lock {
*num += 1;
} else {
unreachable!()
}
}
}
#[inline]
unsafe fn unlock(&self) {
{
let read_lock = self.access.read();
if let CurrentGpuAccess::NonExclusive { ref num } = *read_lock {
let prev = num.fetch_sub(1, Ordering::SeqCst);
debug_assert!(prev >= 1);
return;
}
}
{
let mut write_lock = self.access.write();
if let CurrentGpuAccess::Exclusive { ref mut num } = *write_lock {
if *num != 1 {
*num -= 1;
return;
}
} else {
panic!()
}
*write_lock = CurrentGpuAccess::NonExclusive {
num: AtomicUsize::new(0),
};
}
}
}
impl<T, A> BufferAccessObject for Arc<CpuAccessibleBuffer<T, A>>
where
T: BufferContents + ?Sized,
A: Send + Sync + 'static,
{
#[inline]
fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
self.clone()
}
}
unsafe impl<T, A> TypedBufferAccess for CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
type Content = T;
}
unsafe impl<T, A> DeviceOwned for CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
{
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
impl<T, A> PartialEq for CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner() && self.size() == other.size()
}
}
impl<T, A> Eq for CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
}
impl<T, A> Hash for CpuAccessibleBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
self.size().hash(state);
}
}
#[derive(Debug)]
pub struct ReadLock<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
data: &'a T,
range: Range<DeviceSize>,
lock: RwLockReadGuard<'a, CurrentGpuAccess>,
}
impl<'a, T> Deref for ReadLock<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.data
}
}
#[derive(Debug)]
pub struct WriteLock<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
data: &'a mut T,
mapped_memory: &'a MappedDeviceMemory,
range: Range<DeviceSize>,
lock: RwLockWriteGuard<'a, CurrentGpuAccess>,
}
impl<'a, T> Drop for WriteLock<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
#[inline]
fn drop(&mut self) {
unsafe {
self.mapped_memory.flush_range(self.range.clone()).unwrap();
}
}
}
impl<'a, T> Deref for WriteLock<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.data
}
}
impl<'a, T> DerefMut for WriteLock<'a, T>
where
T: BufferContents + ?Sized + 'a,
{
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.data
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ReadLockError {
CpuWriteLocked,
GpuWriteLocked,
}
impl error::Error for ReadLockError {}
impl fmt::Display for ReadLockError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
ReadLockError::CpuWriteLocked => {
"the buffer is already locked for write mode by the CPU"
}
ReadLockError::GpuWriteLocked => {
"the buffer is already locked for write mode by the GPU"
}
}
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum WriteLockError {
CpuLocked,
GpuLocked,
}
impl error::Error for WriteLockError {}
impl fmt::Display for WriteLockError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
WriteLockError::CpuLocked => "the buffer is already locked by the CPU",
WriteLockError::GpuLocked => "the buffer is already locked by the GPU",
}
)
}
}
#[cfg(test)]
mod tests {
use crate::buffer::{BufferUsage, CpuAccessibleBuffer};
#[test]
fn create_empty_buffer() {
let (device, queue) = gfx_dev_and_queue!();
const EMPTY: [i32; 0] = [];
assert_should_panic!({
CpuAccessibleBuffer::from_data(device.clone(), BufferUsage::all(), false, EMPTY)
.unwrap();
CpuAccessibleBuffer::from_iter(device, BufferUsage::all(), false, EMPTY.into_iter())
.unwrap();
});
}
}