diff options
Diffstat (limited to 'wgpu/src/buffer')
| -rw-r--r-- | wgpu/src/buffer/dynamic.rs | 199 | ||||
| -rw-r--r-- | wgpu/src/buffer/static.rs | 117 | 
2 files changed, 316 insertions, 0 deletions
| diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs new file mode 100644 index 00000000..c0c48c74 --- /dev/null +++ b/wgpu/src/buffer/dynamic.rs @@ -0,0 +1,199 @@ +//! Utilities for uniform buffer operations. +use encase::private::WriteInto; +use encase::ShaderType; +use std::marker::PhantomData; + +/// A dynamic buffer is any type of buffer which does not have a static offset. +pub(crate) struct Buffer<T: ShaderType> { +    offsets: Vec<wgpu::DynamicOffset>, +    cpu: Internal, +    gpu: wgpu::Buffer, +    label: &'static str, +    size: u64, +    _data: PhantomData<T>, +} + +impl<T: ShaderType + WriteInto> Buffer<T> { +    /// Creates a new dynamic uniform buffer. +    pub fn uniform(device: &wgpu::Device, label: &'static str) -> Self { +        Buffer::new( +            device, +            Internal::Uniform(encase::DynamicUniformBuffer::new(Vec::new())), +            label, +            wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, +        ) +    } + +    /// Creates a new dynamic storage buffer. +    pub fn storage(device: &wgpu::Device, label: &'static str) -> Self { +        Buffer::new( +            device, +            Internal::Storage(encase::DynamicStorageBuffer::new(Vec::new())), +            label, +            wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, +        ) +    } + +    fn new( +        device: &wgpu::Device, +        dynamic_buffer_type: Internal, +        label: &'static str, +        usage: wgpu::BufferUsages, +    ) -> Self { +        let initial_size = u64::from(T::min_size()); + +        Self { +            offsets: Vec::new(), +            cpu: dynamic_buffer_type, +            gpu: Buffer::<T>::create_gpu_buffer( +                device, +                label, +                usage, +                initial_size, +            ), +            label, +            size: initial_size, +            _data: Default::default(), +        } +    } + +    fn create_gpu_buffer( +        device: &wgpu::Device, +        label: &'static str, +        usage: wgpu::BufferUsages, +        size: u64, +    ) -> wgpu::Buffer { +        device.create_buffer(&wgpu::BufferDescriptor { +            label: Some(label), +            size, +            usage, +            mapped_at_creation: false, +        }) +    } + +    /// Write a new value to the CPU buffer with proper alignment. Stores the returned offset value +    /// in the buffer for future use. +    pub fn push(&mut self, value: &T) { +        //this write operation on the cpu buffer will adjust for uniform alignment requirements +        let offset = self.cpu.write(value); +        self.offsets.push(offset as u32); +    } + +    /// Resize buffer contents if necessary. This will re-create the GPU buffer if current size is +    /// less than the newly computed size from the CPU buffer. +    /// +    /// If the gpu buffer is resized, its bind group will need to be recreated! +    pub fn resize(&mut self, device: &wgpu::Device) -> bool { +        let new_size = self.cpu.get_ref().len() as u64; + +        if self.size < new_size { +            let usages = match self.cpu { +                Internal::Uniform(_) => { +                    wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST +                } +                Internal::Storage(_) => { +                    wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST +                } +            }; + +            self.gpu = Buffer::<T>::create_gpu_buffer( +                device, self.label, usages, new_size, +            ); +            self.size = new_size; +            true +        } else { +            false +        } +    } + +    /// Write the contents of this dynamic buffer to the GPU via staging belt command. +    pub fn write( +        &mut self, +        device: &wgpu::Device, +        staging_belt: &mut wgpu::util::StagingBelt, +        encoder: &mut wgpu::CommandEncoder, +    ) { +        let size = self.cpu.get_ref().len(); + +        if let Some(buffer_size) = wgpu::BufferSize::new(size as u64) { +            let mut buffer = staging_belt.write_buffer( +                encoder, +                &self.gpu, +                0, +                buffer_size, +                device, +            ); + +            buffer.copy_from_slice(self.cpu.get_ref()); +        } +    } + +    // Gets the aligned offset at the given index from the CPU buffer. +    pub fn offset_at_index(&self, index: usize) -> wgpu::DynamicOffset { +        let offset = self +            .offsets +            .get(index) +            .copied() +            .expect("Index not found in offsets."); + +        offset +    } + +    /// Returns a reference to the GPU buffer. +    pub fn raw(&self) -> &wgpu::Buffer { +        &self.gpu +    } + +    /// Reset the buffer. +    pub fn clear(&mut self) { +        self.offsets.clear(); +        self.cpu.clear(); +    } +} + +// Currently supported dynamic buffers. +enum Internal { +    Uniform(encase::DynamicUniformBuffer<Vec<u8>>), +    Storage(encase::DynamicStorageBuffer<Vec<u8>>), +} + +impl Internal { +    /// Writes the current value to its CPU buffer with proper alignment. +    pub(super) fn write<T: ShaderType + WriteInto>( +        &mut self, +        value: &T, +    ) -> wgpu::DynamicOffset { +        match self { +            Internal::Uniform(buf) => buf +                .write(value) +                .expect("Error when writing to dynamic uniform buffer.") +                as u32, +            Internal::Storage(buf) => buf +                .write(value) +                .expect("Error when writing to dynamic storage buffer.") +                as u32, +        } +    } + +    /// Returns bytearray of aligned CPU buffer. +    pub(super) fn get_ref(&self) -> &Vec<u8> { +        match self { +            Internal::Uniform(buf) => buf.as_ref(), +            Internal::Storage(buf) => buf.as_ref(), +        } +    } + +    /// Resets the CPU buffer. +    pub(super) fn clear(&mut self) { +        match self { +            Internal::Uniform(buf) => { +                buf.as_mut().clear(); +                buf.set_offset(0); +            } +            Internal::Storage(buf) => { +                buf.as_mut().clear(); +                buf.set_offset(0); +            } +        } +    } +} diff --git a/wgpu/src/buffer/static.rs b/wgpu/src/buffer/static.rs new file mode 100644 index 00000000..cf06790c --- /dev/null +++ b/wgpu/src/buffer/static.rs @@ -0,0 +1,117 @@ +use bytemuck::{Pod, Zeroable}; +use std::marker::PhantomData; +use std::mem; + +//128 triangles/indices +const DEFAULT_STATIC_BUFFER_COUNT: wgpu::BufferAddress = 128; + +/// A generic buffer struct useful for items which have no alignment requirements +/// (e.g. Vertex, Index buffers) & no dynamic offsets. +#[derive(Debug)] +pub(crate) struct Buffer<T> { +    //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer +    offsets: Vec<wgpu::BufferAddress>, +    label: &'static str, +    usages: wgpu::BufferUsages, +    gpu: wgpu::Buffer, +    size: wgpu::BufferAddress, +    _data: PhantomData<T>, +} + +impl<T: Pod + Zeroable> Buffer<T> { +    /// Initialize a new static buffer. +    pub fn new( +        device: &wgpu::Device, +        label: &'static str, +        usages: wgpu::BufferUsages, +    ) -> Self { +        let size = (mem::size_of::<T>() as u64) * DEFAULT_STATIC_BUFFER_COUNT; + +        Self { +            offsets: Vec::new(), +            label, +            usages, +            gpu: Self::gpu_buffer(device, label, size, usages), +            size, +            _data: PhantomData, +        } +    } + +    fn gpu_buffer( +        device: &wgpu::Device, +        label: &'static str, +        size: wgpu::BufferAddress, +        usage: wgpu::BufferUsages, +    ) -> wgpu::Buffer { +        device.create_buffer(&wgpu::BufferDescriptor { +            label: Some(label), +            size, +            usage, +            mapped_at_creation: false, +        }) +    } + +    /// Returns whether or not the buffer needs to be recreated. This can happen whenever mesh data +    /// changes & a redraw is requested. +    pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool { +        let size = (mem::size_of::<T>() * new_count) as u64; + +        if self.size < size { +            self.offsets.clear(); +            self.size = size; +            self.gpu = Self::gpu_buffer(device, self.label, size, self.usages); +            true +        } else { +            false +        } +    } + +    /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. +    /// +    /// Returns the size of the written bytes. +    pub fn write( +        &mut self, +        device: &wgpu::Device, +        staging_belt: &mut wgpu::util::StagingBelt, +        encoder: &mut wgpu::CommandEncoder, +        offset: u64, +        content: &[T], +    ) -> u64 { +        let bytes = bytemuck::cast_slice(content); +        let bytes_size = bytes.len() as u64; + +        if let Some(buffer_size) = wgpu::BufferSize::new(bytes_size) { +            let mut buffer = staging_belt.write_buffer( +                encoder, +                &self.gpu, +                offset, +                buffer_size, +                device, +            ); + +            buffer.copy_from_slice(bytes); + +            self.offsets.push(offset); +        } + +        bytes_size +    } + +    fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { +        self.offsets +            .get(index) +            .expect("Offset at index does not exist.") +    } + +    /// Returns the slice calculated from the offset stored at the given index. +    /// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index +    /// 1 that we stored earlier when writing. +    pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { +        self.gpu.slice(self.offset_at(index)..) +    } + +    /// Clears any temporary data from the buffer. +    pub fn clear(&mut self) { +        self.offsets.clear() +    } +} | 
