Skip to main content

agpu/graphics/buffer/
builder.rs

1use bytemuck::Pod;
2use wgpu::util::DeviceExt;
3
4use crate::{Buffer, Gpu};
5
6pub enum BufferInitContent<'a> {
7    /// The buffer will be initialized with the given data
8    Data(&'a [u8]),
9    /// The buffer will be initialized with the given size
10    Size(u64),
11}
12
13pub struct BufferBuilder<'a> {
14    pub gpu: Gpu,
15    pub label: Option<&'a str>,
16    pub usage: wgpu::BufferUsages,
17}
18impl<'a> BufferBuilder<'a> {
19    #[must_use]
20    pub const fn new(gpu: Gpu, label: &'a str) -> Self {
21        BufferBuilder {
22            gpu,
23            label: Some(label),
24            usage: wgpu::BufferUsages::empty(),
25        }
26    }
27
28    /// Set a label that GPU debuggers can display
29    pub fn with_label(mut self, label: &'a str) -> Self {
30        self.label = Some(label);
31        self
32    }
33
34    /// Allow a buffer to be the index buffer in a draw operation.
35    pub fn as_index_buffer(mut self) -> Self {
36        self.usage |= wgpu::BufferUsages::INDEX;
37        self
38    }
39
40    /// Allow a buffer to be the vertex buffer in a draw operation.
41    pub fn as_vertex_buffer(mut self) -> Self {
42        self.usage |= wgpu::BufferUsages::VERTEX;
43        self
44    }
45
46    /// Allow a buffer to be a `BufferBindingType::Uniform` inside a bind group.
47    pub fn as_uniform_buffer(mut self) -> Self {
48        self.usage |= wgpu::BufferUsages::UNIFORM;
49        self
50    }
51
52    /// Allow a buffer to be a `BufferBindingType::Storage` inside a bind group.
53    pub fn as_storage_buffer(mut self) -> Self {
54        self.usage |= wgpu::BufferUsages::STORAGE;
55        self
56    }
57
58    /// Allow a buffer to be the indirect buffer in an indirect draw call.
59    pub fn as_indirect_buffer(mut self) -> Self {
60        self.usage |= wgpu::BufferUsages::INDIRECT;
61        self
62    }
63
64    /// See [`wgpu::BufferUsages::MAP_READ`]
65    pub fn allow_map_read(mut self) -> Self {
66        self.usage |= wgpu::BufferUsages::MAP_READ;
67        self
68    }
69
70    /// See [`wgpu::BufferUsages::MAP_WRITE`]
71    pub fn allow_map_write(mut self) -> Self {
72        self.usage |= wgpu::BufferUsages::MAP_WRITE;
73        self
74    }
75
76    /// See [`wgpu::BufferUsages::COPY_DST`]
77    pub fn allow_copy_to(mut self) -> Self {
78        self.usage |= wgpu::BufferUsages::COPY_DST;
79        self
80    }
81
82    /// See [`wgpu::BufferUsages::COPY_SRC`]
83    pub fn allow_copy_from(mut self) -> Self {
84        self.usage |= wgpu::BufferUsages::COPY_SRC;
85        self
86    }
87
88    pub fn allow_copy(self) -> Self {
89        self.allow_copy_to().allow_copy_from()
90    }
91
92    /// Sets the usage of the buffer
93    /// See also `add_usage` and `rm_usage`
94    pub fn with_usage(mut self, usage: wgpu::BufferUsages) -> Self {
95        self.usage = usage;
96        self
97    }
98    /// Adds the usage flag to the buffer
99    /// See also `with_usage` and `add_usage`
100    pub fn add_usage(mut self, usage: wgpu::BufferUsages) -> Self {
101        self.usage |= usage;
102        self
103    }
104    /// Removes the usage flag from the buffer
105    /// See also `with_usage` and `add_usage`
106    pub fn rm_usage(mut self, usage: wgpu::BufferUsages) -> Self {
107        self.usage &= !usage;
108        self
109    }
110
111    // This is used by build() and build_and_map() for our convenience
112    fn create_impl(&self, init: BufferInitContent) -> (wgpu::Buffer, u64) {
113        match init {
114            BufferInitContent::Data(data) => (
115                self.gpu
116                    .device
117                    .create_buffer_init(&wgpu::util::BufferInitDescriptor {
118                        label: self.label,
119                        usage: self.usage,
120                        contents: data,
121                    }),
122                data.len() as u64,
123            ),
124            BufferInitContent::Size(size) => (
125                self.gpu.device.create_buffer(&wgpu::BufferDescriptor {
126                    label: self.label,
127                    size,
128                    usage: self.usage,
129                    mapped_at_creation: false,
130                }),
131                size,
132            ),
133        }
134    }
135
136    /// Creates the buffer
137    #[must_use]
138    pub fn create<T>(&self, contents: &[T]) -> Buffer
139    where
140        T: Pod,
141    {
142        let (inner, size) =
143            self.create_impl(BufferInitContent::Data(bytemuck::cast_slice(contents)));
144
145        Buffer {
146            inner,
147            gpu: self.gpu.clone(),
148            size,
149            label: self.label.map(|a| a.to_string()).unwrap_or_default(),
150            usages: self.usage,
151        }
152    }
153
154    /// Builds a buffer with 0s, with size in bytes
155    #[must_use]
156    pub fn create_uninit(&self, size: u64) -> Buffer {
157        let (inner, size) = self.create_impl(BufferInitContent::Size(size));
158
159        Buffer {
160            inner,
161            gpu: self.gpu.clone(),
162            size,
163            label: self.label.map(|a| a.to_string()).unwrap_or_default(),
164            usages: self.usage,
165        }
166    }
167
168    pub fn create_empty<T>(&self, count: usize) -> Buffer
169    where
170        T: Sized,
171    {
172        self.create_uninit((count * std::mem::size_of::<T>()) as u64)
173    }
174}