use super::{
sys::{UnsafeBuffer, UnsafeBufferCreateInfo},
BufferAccess, BufferAccessObject, BufferContents, BufferCreationError, BufferInner,
BufferUsage, TypedBufferAccess,
};
use crate::{
device::{physical::QueueFamily, Device, DeviceOwned, Queue},
memory::{
pool::{
alloc_dedicated_with_exportable_fd, AllocFromRequirementsFilter, AllocLayout,
MappingRequirement, MemoryPoolAlloc, PotentialDedicatedAllocation, StdMemoryPoolAlloc,
},
DedicatedAllocation, DeviceMemoryAllocationError, DeviceMemoryExportError,
ExternalMemoryHandleType, MemoryPool, MemoryRequirements,
},
sync::{AccessError, Sharing},
DeviceSize,
};
use smallvec::SmallVec;
use std::{
fs::File,
hash::{Hash, Hasher},
marker::PhantomData,
mem::size_of,
sync::{Arc, Mutex},
};
#[derive(Debug)]
pub struct DeviceLocalBuffer<T, A = PotentialDedicatedAllocation<StdMemoryPoolAlloc>>
where
T: BufferContents + ?Sized,
{
inner: UnsafeBuffer,
memory: A,
queue_families: SmallVec<[u32; 4]>,
gpu_lock: Mutex<GpuAccess>,
marker: PhantomData<Box<T>>,
}
#[derive(Debug, Copy, Clone)]
enum GpuAccess {
None,
NonExclusive { num: u32 },
Exclusive { num: u32 },
}
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents,
{
#[inline]
pub fn new<'a, I>(
device: Arc<Device>,
usage: BufferUsage,
queue_families: I,
) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
unsafe {
DeviceLocalBuffer::raw(device, size_of::<T>() as DeviceSize, usage, queue_families)
}
}
}
impl<T> DeviceLocalBuffer<[T]>
where
[T]: BufferContents,
{
#[inline]
pub fn array<'a, I>(
device: Arc<Device>,
len: DeviceSize,
usage: BufferUsage,
queue_families: I,
) -> Result<Arc<DeviceLocalBuffer<[T]>>, DeviceMemoryAllocationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
unsafe {
DeviceLocalBuffer::raw(
device,
len * size_of::<T>() as DeviceSize,
usage,
queue_families,
)
}
}
}
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
pub unsafe fn raw<'a, I>(
device: Arc<Device>,
size: DeviceSize,
usage: BufferUsage,
queue_families: I,
) -> Result<Arc<DeviceLocalBuffer<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, mem_reqs) = Self::build_buffer(&device, size, usage, &queue_families)?;
let mem = MemoryPool::alloc_from_requirements(
&Device::standard_pool(&device),
&mem_reqs,
AllocLayout::Linear,
MappingRequirement::DoNotMap,
Some(DedicatedAllocation::Buffer(&buffer)),
|t| {
if t.is_device_local() {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
},
)?;
debug_assert!((mem.offset() % mem_reqs.alignment) == 0);
buffer.bind_memory(mem.memory(), mem.offset())?;
Ok(Arc::new(DeviceLocalBuffer {
inner: buffer,
memory: mem,
queue_families: queue_families,
gpu_lock: Mutex::new(GpuAccess::None),
marker: PhantomData,
}))
}
pub unsafe fn raw_with_exportable_fd<'a, I>(
device: Arc<Device>,
size: DeviceSize,
usage: BufferUsage,
queue_families: I,
) -> Result<Arc<DeviceLocalBuffer<T>>, DeviceMemoryAllocationError>
where
I: IntoIterator<Item = QueueFamily<'a>>,
{
assert!(device.enabled_extensions().khr_external_memory_fd);
assert!(device.enabled_extensions().khr_external_memory);
let queue_families = queue_families
.into_iter()
.map(|f| f.id())
.collect::<SmallVec<[u32; 4]>>();
let (buffer, mem_reqs) = Self::build_buffer(&device, size, usage, &queue_families)?;
let mem = alloc_dedicated_with_exportable_fd(
device.clone(),
&mem_reqs,
AllocLayout::Linear,
MappingRequirement::DoNotMap,
DedicatedAllocation::Buffer(&buffer),
|t| {
if t.is_device_local() {
AllocFromRequirementsFilter::Preferred
} else {
AllocFromRequirementsFilter::Allowed
}
},
)?;
let mem_offset = mem.offset();
debug_assert!((mem_offset % mem_reqs.alignment) == 0);
buffer.bind_memory(mem.memory(), mem_offset)?;
Ok(Arc::new(DeviceLocalBuffer {
inner: buffer,
memory: mem,
queue_families: queue_families,
gpu_lock: Mutex::new(GpuAccess::None),
marker: PhantomData,
}))
}
unsafe fn build_buffer(
device: &Arc<Device>,
size: DeviceSize,
usage: BufferUsage,
queue_families: &SmallVec<[u32; 4]>,
) -> Result<(UnsafeBuffer, MemoryRequirements), DeviceMemoryAllocationError> {
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();
Ok((buffer, mem_reqs))
}
pub fn export_posix_fd(&self) -> Result<File, DeviceMemoryExportError> {
self.memory
.memory()
.export_fd(ExternalMemoryHandleType::OpaqueFd)
}
}
impl<T, A> DeviceLocalBuffer<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()
}
}
unsafe impl<T, A> DeviceOwned for DeviceLocalBuffer<T, A>
where
T: BufferContents + ?Sized,
{
#[inline]
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<T, A> BufferAccess for DeviceLocalBuffer<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: bool, _: &Queue) -> Result<(), AccessError> {
let mut lock = self.gpu_lock.lock().unwrap();
match &mut *lock {
a @ &mut GpuAccess::None => {
if exclusive {
*a = GpuAccess::Exclusive { num: 1 };
} else {
*a = GpuAccess::NonExclusive { num: 1 };
}
Ok(())
}
&mut GpuAccess::NonExclusive { ref mut num } => {
if exclusive {
Err(AccessError::AlreadyInUse)
} else {
*num += 1;
Ok(())
}
}
&mut GpuAccess::Exclusive { .. } => Err(AccessError::AlreadyInUse),
}
}
#[inline]
unsafe fn increase_gpu_lock(&self) {
let mut lock = self.gpu_lock.lock().unwrap();
match *lock {
GpuAccess::None => panic!(),
GpuAccess::NonExclusive { ref mut num } => {
debug_assert!(*num >= 1);
*num += 1;
}
GpuAccess::Exclusive { ref mut num } => {
debug_assert!(*num >= 1);
*num += 1;
}
}
}
#[inline]
unsafe fn unlock(&self) {
let mut lock = self.gpu_lock.lock().unwrap();
match *lock {
GpuAccess::None => panic!("Tried to unlock a buffer that isn't locked"),
GpuAccess::NonExclusive { ref mut num } => {
assert!(*num >= 1);
*num -= 1;
if *num >= 1 {
return;
}
}
GpuAccess::Exclusive { ref mut num } => {
assert!(*num >= 1);
*num -= 1;
if *num >= 1 {
return;
}
}
};
*lock = GpuAccess::None;
}
}
impl<T, A> BufferAccessObject for Arc<DeviceLocalBuffer<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 DeviceLocalBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
type Content = T;
}
impl<T, A> PartialEq for DeviceLocalBuffer<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 DeviceLocalBuffer<T, A>
where
T: BufferContents + ?Sized,
A: Send + Sync,
{
}
impl<T, A> Hash for DeviceLocalBuffer<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);
}
}