use super::{
sys::{Buffer, BufferCreateInfo, BufferMemory, RawBuffer},
BufferAccess, BufferAccessObject, BufferContents, BufferInner, BufferUsage,
CpuAccessibleBuffer, TypedBufferAccess,
};
use crate::{
buffer::{BufferError, ExternalBufferInfo},
command_buffer::{allocator::CommandBufferAllocator, AutoCommandBufferBuilder, CopyBufferInfo},
device::{Device, DeviceOwned},
memory::{
allocator::{
AllocationCreateInfo, AllocationCreationError, AllocationType,
MemoryAllocatePreference, MemoryAllocator, MemoryUsage,
},
DedicatedAllocation, DeviceMemoryError, ExternalMemoryHandleType,
ExternalMemoryHandleTypes,
},
sync::Sharing,
DeviceSize,
};
use smallvec::SmallVec;
use std::{
fs::File,
hash::{Hash, Hasher},
marker::PhantomData,
mem::size_of,
sync::Arc,
};
#[derive(Debug)]
pub struct DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
inner: Arc<Buffer>,
marker: PhantomData<Box<T>>,
}
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents,
{
pub fn new(
allocator: &(impl MemoryAllocator + ?Sized),
usage: BufferUsage,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<Arc<DeviceLocalBuffer<T>>, AllocationCreationError> {
unsafe {
DeviceLocalBuffer::raw(
allocator,
size_of::<T>() as DeviceSize,
usage,
queue_family_indices,
)
}
}
}
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
pub fn from_buffer<B, L, A>(
allocator: &(impl MemoryAllocator + ?Sized),
source: Arc<B>,
usage: BufferUsage,
command_buffer_builder: &mut AutoCommandBufferBuilder<L, A>,
) -> Result<Arc<DeviceLocalBuffer<T>>, AllocationCreationError>
where
B: TypedBufferAccess<Content = T> + 'static,
A: CommandBufferAllocator,
{
unsafe {
let actual_usage = BufferUsage {
transfer_dst: true,
..usage
};
let buffer = DeviceLocalBuffer::raw(
allocator,
source.size(),
actual_usage,
source
.device()
.active_queue_family_indices()
.iter()
.copied(),
)?;
command_buffer_builder
.copy_buffer(CopyBufferInfo::buffers(source, buffer.clone()))
.unwrap();
Ok(buffer)
}
}
}
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents,
{
pub fn from_data<L, A>(
allocator: &(impl MemoryAllocator + ?Sized),
data: T,
usage: BufferUsage,
command_buffer_builder: &mut AutoCommandBufferBuilder<L, A>,
) -> Result<Arc<DeviceLocalBuffer<T>>, AllocationCreationError>
where
A: CommandBufferAllocator,
{
let source = CpuAccessibleBuffer::from_data(
allocator,
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
false,
data,
)?;
DeviceLocalBuffer::from_buffer(allocator, source, usage, command_buffer_builder)
}
}
impl<T> DeviceLocalBuffer<[T]>
where
[T]: BufferContents,
{
pub fn from_iter<D, L, A>(
allocator: &(impl MemoryAllocator + ?Sized),
data: D,
usage: BufferUsage,
command_buffer_builder: &mut AutoCommandBufferBuilder<L, A>,
) -> Result<Arc<DeviceLocalBuffer<[T]>>, AllocationCreationError>
where
D: IntoIterator<Item = T>,
D::IntoIter: ExactSizeIterator,
A: CommandBufferAllocator,
{
let source = CpuAccessibleBuffer::from_iter(
allocator,
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
false,
data,
)?;
DeviceLocalBuffer::from_buffer(allocator, source, usage, command_buffer_builder)
}
}
impl<T> DeviceLocalBuffer<[T]>
where
[T]: BufferContents,
{
pub fn array(
allocator: &(impl MemoryAllocator + ?Sized),
len: DeviceSize,
usage: BufferUsage,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<Arc<DeviceLocalBuffer<[T]>>, AllocationCreationError> {
unsafe {
DeviceLocalBuffer::raw(
allocator,
len * size_of::<T>() as DeviceSize,
usage,
queue_family_indices,
)
}
}
}
impl<T> DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
pub unsafe fn raw(
allocator: &(impl MemoryAllocator + ?Sized),
size: DeviceSize,
usage: BufferUsage,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<Arc<DeviceLocalBuffer<T>>, AllocationCreationError> {
let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect();
let raw_buffer = RawBuffer::new(
allocator.device().clone(),
BufferCreateInfo {
sharing: if queue_family_indices.len() >= 2 {
Sharing::Concurrent(queue_family_indices)
} else {
Sharing::Exclusive
},
size,
usage,
..Default::default()
},
)
.map_err(|err| match err {
BufferError::AllocError(err) => err,
_ => unreachable!(),
})?;
let requirements = *raw_buffer.memory_requirements();
let create_info = AllocationCreateInfo {
requirements,
allocation_type: AllocationType::Linear,
usage: MemoryUsage::GpuOnly,
allocate_preference: MemoryAllocatePreference::Unknown,
dedicated_allocation: Some(DedicatedAllocation::Buffer(&raw_buffer)),
..Default::default()
};
match allocator.allocate_unchecked(create_info) {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.alignment == 0);
debug_assert!(alloc.size() == requirements.size);
let inner = Arc::new(
raw_buffer
.bind_memory_unchecked(alloc)
.map_err(|(err, _, _)| err)?,
);
Ok(Arc::new(DeviceLocalBuffer {
inner,
marker: PhantomData,
}))
}
Err(err) => Err(err),
}
}
pub unsafe fn raw_with_exportable_fd(
allocator: &(impl MemoryAllocator + ?Sized),
size: DeviceSize,
usage: BufferUsage,
queue_family_indices: impl IntoIterator<Item = u32>,
) -> Result<Arc<DeviceLocalBuffer<T>>, AllocationCreationError> {
let enabled_extensions = allocator.device().enabled_extensions();
assert!(enabled_extensions.khr_external_memory_fd);
assert!(enabled_extensions.khr_external_memory);
let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect();
let external_memory_properties = allocator
.device()
.physical_device()
.external_buffer_properties(ExternalBufferInfo {
usage,
..ExternalBufferInfo::handle_type(ExternalMemoryHandleType::OpaqueFd)
})
.unwrap()
.external_memory_properties;
assert!(external_memory_properties.exportable);
let external_memory_handle_types = ExternalMemoryHandleTypes {
opaque_fd: true,
..ExternalMemoryHandleTypes::empty()
};
let raw_buffer = RawBuffer::new(
allocator.device().clone(),
BufferCreateInfo {
sharing: if queue_family_indices.len() >= 2 {
Sharing::Concurrent(queue_family_indices)
} else {
Sharing::Exclusive
},
size,
usage,
external_memory_handle_types,
..Default::default()
},
)
.map_err(|err| match err {
BufferError::AllocError(err) => err,
_ => unreachable!(),
})?;
let requirements = raw_buffer.memory_requirements();
let memory_type_index = allocator
.find_memory_type_index(requirements.memory_type_bits, MemoryUsage::GpuOnly.into())
.expect("failed to find a suitable memory type");
let memory_properties = allocator.device().physical_device().memory_properties();
let heap_index = memory_properties.memory_types[memory_type_index as usize].heap_index;
assert!(size <= memory_properties.memory_heaps[heap_index as usize].size);
match allocator.allocate_dedicated_unchecked(
memory_type_index,
requirements.size,
Some(DedicatedAllocation::Buffer(&raw_buffer)),
external_memory_handle_types,
) {
Ok(alloc) => {
debug_assert!(alloc.offset() % requirements.alignment == 0);
debug_assert!(alloc.size() == requirements.size);
let inner = Arc::new(
raw_buffer
.bind_memory_unchecked(alloc)
.map_err(|(err, _, _)| err)?,
);
Ok(Arc::new(DeviceLocalBuffer {
inner,
marker: PhantomData,
}))
}
Err(err) => Err(err),
}
}
pub fn export_posix_fd(&self) -> Result<File, DeviceMemoryError> {
let allocation = match self.inner.memory() {
BufferMemory::Normal(a) => a,
BufferMemory::Sparse => unreachable!(),
};
allocation
.device_memory()
.export_fd(ExternalMemoryHandleType::OpaqueFd)
}
}
unsafe impl<T> DeviceOwned for DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
fn device(&self) -> &Arc<Device> {
self.inner.device()
}
}
unsafe impl<T> BufferAccess for DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
fn inner(&self) -> BufferInner<'_> {
BufferInner {
buffer: &self.inner,
offset: 0,
}
}
fn size(&self) -> DeviceSize {
self.inner.size()
}
}
impl<T> BufferAccessObject for Arc<DeviceLocalBuffer<T>>
where
T: BufferContents + ?Sized,
{
fn as_buffer_access_object(&self) -> Arc<dyn BufferAccess> {
self.clone()
}
}
unsafe impl<T> TypedBufferAccess for DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
type Content = T;
}
impl<T> PartialEq for DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
fn eq(&self, other: &Self) -> bool {
self.inner() == other.inner() && self.size() == other.size()
}
}
impl<T> Eq for DeviceLocalBuffer<T> where T: BufferContents + ?Sized {}
impl<T> Hash for DeviceLocalBuffer<T>
where
T: BufferContents + ?Sized,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner().hash(state);
self.size().hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
command_buffer::{
allocator::StandardCommandBufferAllocator, CommandBufferUsage,
PrimaryCommandBufferAbstract,
},
memory::allocator::StandardMemoryAllocator,
sync::GpuFuture,
};
#[test]
fn from_data_working() {
let (device, queue) = gfx_dev_and_queue!();
let command_buffer_allocator =
StandardCommandBufferAllocator::new(device.clone(), Default::default());
let mut command_buffer_builder = AutoCommandBufferBuilder::primary(
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
let memory_allocator = StandardMemoryAllocator::new_default(device);
let buffer = DeviceLocalBuffer::from_data(
&memory_allocator,
12u32,
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
&mut command_buffer_builder,
)
.unwrap();
let destination = CpuAccessibleBuffer::from_data(
&memory_allocator,
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
},
false,
0,
)
.unwrap();
command_buffer_builder
.copy_buffer(CopyBufferInfo::buffers(buffer, destination.clone()))
.unwrap();
let _ = command_buffer_builder
.build()
.unwrap()
.execute(queue)
.unwrap()
.then_signal_fence_and_flush()
.unwrap();
let destination_content = destination.read().unwrap();
assert_eq!(*destination_content, 12);
}
#[test]
fn from_iter_working() {
let (device, queue) = gfx_dev_and_queue!();
let command_buffer_allocator =
StandardCommandBufferAllocator::new(device.clone(), Default::default());
let mut command_buffer_builder = AutoCommandBufferBuilder::primary(
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
let allocator = StandardMemoryAllocator::new_default(device);
let buffer = DeviceLocalBuffer::from_iter(
&allocator,
(0..512u32).map(|n| n * 2),
BufferUsage {
transfer_src: true,
..BufferUsage::empty()
},
&mut command_buffer_builder,
)
.unwrap();
let destination = CpuAccessibleBuffer::from_iter(
&allocator,
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
},
false,
(0..512).map(|_| 0u32),
)
.unwrap();
command_buffer_builder
.copy_buffer(CopyBufferInfo::buffers(buffer, destination.clone()))
.unwrap();
let _ = command_buffer_builder
.build()
.unwrap()
.execute(queue)
.unwrap()
.then_signal_fence_and_flush()
.unwrap();
let destination_content = destination.read().unwrap();
for (n, &v) in destination_content.iter().enumerate() {
assert_eq!(n * 2, v as usize);
}
}
#[test]
#[allow(unused)]
fn create_buffer_zero_size_data() {
let (device, queue) = gfx_dev_and_queue!();
let command_buffer_allocator =
StandardCommandBufferAllocator::new(device.clone(), Default::default());
let mut command_buffer_builder = AutoCommandBufferBuilder::primary(
&command_buffer_allocator,
queue.queue_family_index(),
CommandBufferUsage::OneTimeSubmit,
)
.unwrap();
let allocator = StandardMemoryAllocator::new_default(device);
assert_should_panic!({
DeviceLocalBuffer::from_data(
&allocator,
(),
BufferUsage {
transfer_dst: true,
..BufferUsage::empty()
},
&mut command_buffer_builder,
)
.unwrap();
});
}
}