aboutsummaryrefslogtreecommitdiff
path: root/stockton-skeleton/src/buffers/staging.rs
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-skeleton/src/buffers/staging.rs')
-rw-r--r--stockton-skeleton/src/buffers/staging.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/stockton-skeleton/src/buffers/staging.rs b/stockton-skeleton/src/buffers/staging.rs
new file mode 100644
index 0000000..5c80f51
--- /dev/null
+++ b/stockton-skeleton/src/buffers/staging.rs
@@ -0,0 +1,103 @@
+//! A buffer that can be written to by the CPU
+
+use crate::{
+ context::RenderingContext,
+ error::LockPoisoned,
+ mem::{Block, MappableBlock, MemoryPool},
+ types::*,
+};
+
+use std::{mem::ManuallyDrop, ops::Range};
+
+use anyhow::{Context, Result};
+use hal::{buffer::Usage, memory::SparseFlags};
+
+/// A buffer that can be written to by the CPU. Usage will be `Usage::TRANSFER_SRC`.
+pub struct StagingBuffer<P: MemoryPool> {
+ buf: ManuallyDrop<BufferT>,
+ mem: ManuallyDrop<P::Block>,
+}
+
+impl<P> StagingBuffer<P>
+where
+ P: MemoryPool,
+ P::Block: MappableBlock,
+{
+ /// Create a new staging buffer from the given RenderingContext. `size` is in bytes.
+ pub fn from_context(context: &mut RenderingContext, size: u64) -> Result<Self> {
+ context.ensure_memory_pool::<P>()?;
+
+ let mut device = context.device().write().map_err(|_| LockPoisoned::Device)?;
+ let mut mempool = context
+ .existing_memory_pool()
+ .unwrap()
+ .write()
+ .map_err(|_| LockPoisoned::MemoryPool)?;
+
+ Self::from_device_pool(&mut device, &mut mempool, size)
+ }
+
+ /// Create a new staging buffer from the given device and memory pool. `size` is in bytes.
+ pub fn from_device_pool(device: &mut DeviceT, mempool: &mut P, size: u64) -> Result<Self> {
+ let mut buffer =
+ unsafe { device.create_buffer(size, Usage::TRANSFER_SRC, SparseFlags::empty()) }
+ .context("Error creating buffer")?;
+
+ let requirements = unsafe { device.get_buffer_requirements(&buffer) };
+
+ let (memory, _) = mempool
+ .alloc(device, requirements.size, requirements.alignment)
+ .context("Error allocating staging memory")?;
+
+ unsafe { device.bind_buffer_memory(memory.memory(), memory.range().start, &mut buffer) }
+ .context("Error binding staging memory to buffer")?;
+
+ Ok(StagingBuffer {
+ buf: ManuallyDrop::new(buffer),
+ mem: ManuallyDrop::new(memory),
+ })
+ }
+
+ /// Map the given range to CPU-visible memory, returning a pointer to the start of that range.
+ /// inner_range is local to this block of memory, not to the container as a whole.
+ pub fn map(&mut self, device: &mut DeviceT, inner_range: Range<u64>) -> Result<*mut u8> {
+ Ok(<<P as MemoryPool>::Block>::map(
+ &mut *self.mem,
+ device,
+ inner_range,
+ )?)
+ }
+
+ /// Remove any mappings present for this staging buffer.
+ pub unsafe fn unmap(&mut self, device: &mut DeviceT) -> Result<()> {
+ self.mem.unmap(device)
+ }
+
+ pub fn deactivate_context(self, context: &mut RenderingContext) {
+ let mut device = context.device().write().unwrap();
+ let mut mempool = context.existing_memory_pool().unwrap().write().unwrap();
+
+ self.deactivate_device_pool(&mut device, &mut mempool)
+ }
+
+ /// Destroy all vulkan objects. This should be called before dropping
+ pub fn deactivate_device_pool(self, device: &mut DeviceT, mempool: &mut P) {
+ unsafe {
+ use std::ptr::read;
+ // Destroy buffer
+ device.destroy_buffer(read(&*self.buf));
+ // Free memory
+ mempool.free(device, read(&*self.mem));
+ }
+ }
+
+ /// Get a reference to the staging buffer's memory.
+ pub fn mem(&self) -> &P::Block {
+ &self.mem
+ }
+
+ /// Get a reference to the staging buffer.
+ pub fn buf(&self) -> &ManuallyDrop<BufferT> {
+ &self.buf
+ }
+}