use crate::error::{AccelError, AccelResult};
use std::sync::Arc;
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::command_buffer::allocator::{
StandardCommandBufferAllocator, StandardCommandBufferAllocatorCreateInfo,
};
use vulkano::command_buffer::{
AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, PrimaryAutoCommandBuffer,
};
use vulkano::device::DeviceOwned;
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator};
use vulkano::sync::GpuFuture;
use vulkano::DeviceSize;
use vulkano::device::Queue;
pub struct BufferManager {
allocator: Arc<StandardMemoryAllocator>,
command_allocator: Arc<StandardCommandBufferAllocator>,
queue: Arc<Queue>,
}
impl BufferManager {
#[must_use]
pub fn new(allocator: Arc<StandardMemoryAllocator>, queue: Arc<Queue>) -> Self {
let command_allocator = Arc::new(StandardCommandBufferAllocator::new(
allocator.device().clone(),
StandardCommandBufferAllocatorCreateInfo::default(),
));
Self {
allocator,
command_allocator,
queue,
}
}
pub fn create_staging_buffer(&self, size: DeviceSize) -> AccelResult<Subbuffer<[u8]>> {
Buffer::new_slice::<u8>(
self.allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::TRANSFER_SRC,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
size,
)
.map_err(|e| {
AccelError::BufferAllocation(format!("Staging buffer allocation failed: {e:?}"))
})
}
pub fn create_device_buffer(
&self,
size: DeviceSize,
usage: BufferUsage,
) -> AccelResult<Subbuffer<[u8]>> {
Buffer::new_slice::<u8>(
self.allocator.clone(),
BufferCreateInfo {
usage: usage | BufferUsage::TRANSFER_DST | BufferUsage::TRANSFER_SRC,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
..Default::default()
},
size,
)
.map_err(|e| {
AccelError::BufferAllocation(format!("Device buffer allocation failed: {e:?}"))
})
}
pub fn create_readback_buffer(&self, size: DeviceSize) -> AccelResult<Subbuffer<[u8]>> {
Buffer::new_slice::<u8>(
self.allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::TRANSFER_DST,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST
| MemoryTypeFilter::HOST_RANDOM_ACCESS,
..Default::default()
},
size,
)
.map_err(|e| {
AccelError::BufferAllocation(format!("Readback buffer allocation failed: {e:?}"))
})
}
pub fn upload_data(&self, data: &[u8], dst_buffer: &Subbuffer<[u8]>) -> AccelResult<()> {
if data.len() as DeviceSize != dst_buffer.size() {
return Err(AccelError::BufferSizeMismatch {
#[allow(clippy::cast_possible_truncation)]
expected: { dst_buffer.size() as usize },
actual: data.len(),
});
}
let staging = self.create_staging_buffer(data.len() as DeviceSize)?;
{
let mut write = staging.write().map_err(|e| {
AccelError::MemoryMap(format!("Failed to map staging buffer: {e:?}"))
})?;
write.copy_from_slice(data);
}
let mut builder = AutoCommandBufferBuilder::primary(
self.command_allocator.clone(),
self.allocator.device().active_queue_family_indices()[0],
CommandBufferUsage::OneTimeSubmit,
)
.map_err(|e| {
AccelError::CommandBuffer(format!("Failed to create command buffer: {e:?}"))
})?;
builder
.copy_buffer(CopyBufferInfo::buffers(staging.clone(), dst_buffer.clone()))
.map_err(|e| {
AccelError::BufferUpload(format!("Failed to record copy command: {e:?}"))
})?;
let command_buffer = builder.build().map_err(|e| {
AccelError::CommandBuffer(format!("Failed to build command buffer: {e:?}"))
})?;
vulkano::sync::now(self.allocator.device().clone())
.then_execute(self.queue.clone(), command_buffer)
.map_err(|e| AccelError::BufferUpload(format!("Failed to execute upload: {e:?}")))?
.then_signal_fence_and_flush()
.map_err(|e| AccelError::BufferUpload(format!("Failed to flush upload: {e:?}")))?
.wait(None)
.map_err(|e| AccelError::Synchronization(format!("Upload sync failed: {e:?}")))?;
Ok(())
}
pub fn download_data(&self, src_buffer: &Subbuffer<[u8]>) -> AccelResult<Vec<u8>> {
let size = src_buffer.size();
let readback = self.create_readback_buffer(size)?;
let mut builder = AutoCommandBufferBuilder::primary(
self.command_allocator.clone(),
self.allocator.device().active_queue_family_indices()[0],
CommandBufferUsage::OneTimeSubmit,
)
.map_err(|e| {
AccelError::CommandBuffer(format!("Failed to create command buffer: {e:?}"))
})?;
builder
.copy_buffer(CopyBufferInfo::buffers(
src_buffer.clone(),
readback.clone(),
))
.map_err(|e| {
AccelError::BufferDownload(format!("Failed to record copy command: {e:?}"))
})?;
let command_buffer = builder.build().map_err(|e| {
AccelError::CommandBuffer(format!("Failed to build command buffer: {e:?}"))
})?;
vulkano::sync::now(self.allocator.device().clone())
.then_execute(self.queue.clone(), command_buffer)
.map_err(|e| AccelError::BufferDownload(format!("Failed to execute download: {e:?}")))?
.then_signal_fence_and_flush()
.map_err(|e| AccelError::BufferDownload(format!("Failed to flush download: {e:?}")))?
.wait(None)
.map_err(|e| AccelError::Synchronization(format!("Download sync failed: {e:?}")))?;
let read = readback
.read()
.map_err(|e| AccelError::MemoryMap(format!("Failed to map readback buffer: {e:?}")))?;
Ok(read.to_vec())
}
pub fn create_command_buffer(
&self,
) -> AccelResult<AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>> {
AutoCommandBufferBuilder::primary(
self.command_allocator.clone(),
self.allocator.device().active_queue_family_indices()[0],
CommandBufferUsage::OneTimeSubmit,
)
.map_err(|e| AccelError::CommandBuffer(format!("Failed to create command buffer: {e:?}")))
}
}