Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/render_resource/storage_buffer.rs
9331 views
1
use core::marker::PhantomData;
2
3
use super::Buffer;
4
use crate::{
5
render_resource::make_buffer_label,
6
renderer::{RenderDevice, RenderQueue},
7
};
8
use encase::{
9
internal::WriteInto, DynamicStorageBuffer as DynamicStorageBufferWrapper, ShaderType,
10
StorageBuffer as StorageBufferWrapper,
11
};
12
use wgpu::{util::BufferInitDescriptor, BindingResource, BufferBinding, BufferSize, BufferUsages};
13
14
use super::IntoBinding;
15
16
/// Stores data to be transferred to the GPU and made accessible to shaders as a storage buffer.
17
///
18
/// Storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts of data.
19
/// Note however that WebGL2 does not support storage buffers, so consider alternative options in this case.
20
///
21
/// Storage buffers can store runtime-sized arrays, but only if they are the last field in a structure.
22
///
23
/// The contained data is stored in system RAM. [`write_buffer`](StorageBuffer::write_buffer) queues
24
/// copying of the data from system RAM to VRAM. Storage buffers must conform to [std430 alignment/padding requirements], which
25
/// is automatically enforced by this structure.
26
///
27
/// Other options for storing GPU-accessible data are:
28
/// * [`BufferVec`](crate::render_resource::BufferVec)
29
/// * [`DynamicStorageBuffer`]
30
/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)
31
/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)
32
/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)
33
/// * [`Texture`](crate::render_resource::Texture)
34
/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)
35
///
36
/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage
37
pub struct StorageBuffer<T: ShaderType> {
38
value: T,
39
scratch: StorageBufferWrapper<Vec<u8>>,
40
buffer: Option<Buffer>,
41
label: Option<String>,
42
changed: bool,
43
buffer_usage: BufferUsages,
44
last_written_size: Option<BufferSize>,
45
}
46
47
impl<T: ShaderType> From<T> for StorageBuffer<T> {
48
fn from(value: T) -> Self {
49
Self {
50
value,
51
scratch: StorageBufferWrapper::new(Vec::new()),
52
buffer: None,
53
label: None,
54
changed: false,
55
buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
56
last_written_size: None,
57
}
58
}
59
}
60
61
impl<T: ShaderType + Default> Default for StorageBuffer<T> {
62
fn default() -> Self {
63
Self {
64
value: T::default(),
65
scratch: StorageBufferWrapper::new(Vec::new()),
66
buffer: None,
67
label: None,
68
changed: false,
69
buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
70
last_written_size: None,
71
}
72
}
73
}
74
75
impl<T: ShaderType + WriteInto> StorageBuffer<T> {
76
#[inline]
77
pub fn buffer(&self) -> Option<&Buffer> {
78
self.buffer.as_ref()
79
}
80
81
#[inline]
82
pub fn binding(&self) -> Option<BindingResource<'_>> {
83
Some(BindingResource::Buffer(BufferBinding {
84
buffer: self.buffer()?,
85
offset: 0,
86
size: self.last_written_size,
87
}))
88
}
89
90
pub fn set(&mut self, value: T) {
91
self.value = value;
92
}
93
94
pub fn get(&self) -> &T {
95
&self.value
96
}
97
98
pub fn get_mut(&mut self) -> &mut T {
99
&mut self.value
100
}
101
102
pub fn set_label(&mut self, label: Option<&str>) {
103
let label = label.map(str::to_string);
104
105
if label != self.label {
106
self.changed = true;
107
}
108
109
self.label = label;
110
}
111
112
pub fn get_label(&self) -> Option<&str> {
113
self.label.as_deref()
114
}
115
116
/// Add more [`BufferUsages`] to the buffer.
117
///
118
/// This method only allows addition of flags to the default usage flags.
119
///
120
/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::STORAGE`.
121
pub fn add_usages(&mut self, usage: BufferUsages) {
122
self.buffer_usage |= usage;
123
self.changed = true;
124
}
125
126
/// Queues writing of data from system RAM to VRAM using the [`RenderDevice`]
127
/// and the provided [`RenderQueue`].
128
///
129
/// If there is no GPU-side buffer allocated to hold the data currently stored, or if a GPU-side buffer previously
130
/// allocated does not have enough capacity, a new GPU-side buffer is created.
131
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
132
self.scratch.write(&self.value).unwrap();
133
134
let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);
135
let size = self.scratch.as_ref().len() as u64;
136
137
if capacity < size || self.changed {
138
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
139
label: make_buffer_label::<Self>(&self.label),
140
usage: self.buffer_usage,
141
contents: self.scratch.as_ref(),
142
}));
143
self.changed = false;
144
} else if let Some(buffer) = &self.buffer {
145
queue.write_buffer(buffer, 0, self.scratch.as_ref());
146
}
147
148
self.last_written_size = BufferSize::new(size);
149
}
150
}
151
152
impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a StorageBuffer<T> {
153
#[inline]
154
fn into_binding(self) -> BindingResource<'a> {
155
self.binding().expect("Failed to get buffer")
156
}
157
}
158
159
/// Stores data to be transferred to the GPU and made accessible to shaders as a dynamic storage buffer.
160
///
161
/// This is just a [`StorageBuffer`], but also allows you to set dynamic offsets.
162
///
163
/// Dynamic storage buffers can be made available to shaders in some combination of read/write mode, and can store large amounts
164
/// of data. Note however that WebGL2 does not support storage buffers, so consider alternative options in this case. Dynamic
165
/// storage buffers support multiple separate bindings at dynamic byte offsets and so have a
166
/// [`push`](DynamicStorageBuffer::push) method.
167
///
168
/// The contained data is stored in system RAM. [`write_buffer`](DynamicStorageBuffer::write_buffer)
169
/// queues copying of the data from system RAM to VRAM. The data within a storage buffer binding must conform to
170
/// [std430 alignment/padding requirements]. `DynamicStorageBuffer` takes care of serializing the inner type to conform to
171
/// these requirements. Each item [`push`](DynamicStorageBuffer::push)ed into this structure
172
/// will additionally be aligned to meet dynamic offset alignment requirements.
173
///
174
/// Other options for storing GPU-accessible data are:
175
/// * [`BufferVec`](crate::render_resource::BufferVec)
176
/// * [`DynamicUniformBuffer`](crate::render_resource::DynamicUniformBuffer)
177
/// * [`GpuArrayBuffer`](crate::render_resource::GpuArrayBuffer)
178
/// * [`RawBufferVec`](crate::render_resource::RawBufferVec)
179
/// * [`StorageBuffer`]
180
/// * [`Texture`](crate::render_resource::Texture)
181
/// * [`UniformBuffer`](crate::render_resource::UniformBuffer)
182
///
183
/// [std430 alignment/padding requirements]: https://www.w3.org/TR/WGSL/#address-spaces-storage
184
pub struct DynamicStorageBuffer<T: ShaderType> {
185
scratch: DynamicStorageBufferWrapper<Vec<u8>>,
186
buffer: Option<Buffer>,
187
label: Option<String>,
188
changed: bool,
189
buffer_usage: BufferUsages,
190
last_written_size: Option<BufferSize>,
191
_marker: PhantomData<fn() -> T>,
192
}
193
194
impl<T: ShaderType> Default for DynamicStorageBuffer<T> {
195
fn default() -> Self {
196
Self {
197
scratch: DynamicStorageBufferWrapper::new(Vec::new()),
198
buffer: None,
199
label: None,
200
changed: false,
201
buffer_usage: BufferUsages::COPY_DST | BufferUsages::STORAGE,
202
last_written_size: None,
203
_marker: PhantomData,
204
}
205
}
206
}
207
208
impl<T: ShaderType + WriteInto> DynamicStorageBuffer<T> {
209
#[inline]
210
pub fn buffer(&self) -> Option<&Buffer> {
211
self.buffer.as_ref()
212
}
213
214
#[inline]
215
pub fn binding(&self) -> Option<BindingResource<'_>> {
216
Some(BindingResource::Buffer(BufferBinding {
217
buffer: self.buffer()?,
218
offset: 0,
219
size: self.last_written_size,
220
}))
221
}
222
223
#[inline]
224
pub fn is_empty(&self) -> bool {
225
self.scratch.as_ref().is_empty()
226
}
227
228
#[inline]
229
pub fn push(&mut self, value: T) -> u32 {
230
self.scratch.write(&value).unwrap() as u32
231
}
232
233
pub fn set_label(&mut self, label: Option<&str>) {
234
let label = label.map(str::to_string);
235
236
if label != self.label {
237
self.changed = true;
238
}
239
240
self.label = label;
241
}
242
243
pub fn get_label(&self) -> Option<&str> {
244
self.label.as_deref()
245
}
246
247
/// Add more [`BufferUsages`] to the buffer.
248
///
249
/// This method only allows addition of flags to the default usage flags.
250
///
251
/// The default values for buffer usage are `BufferUsages::COPY_DST` and `BufferUsages::STORAGE`.
252
pub fn add_usages(&mut self, usage: BufferUsages) {
253
self.buffer_usage |= usage;
254
self.changed = true;
255
}
256
257
#[inline]
258
pub fn write_buffer(&mut self, device: &RenderDevice, queue: &RenderQueue) {
259
let capacity = self.buffer.as_deref().map(wgpu::Buffer::size).unwrap_or(0);
260
let size = self.scratch.as_ref().len() as u64;
261
262
if capacity < size || (self.changed && size > 0) {
263
self.buffer = Some(device.create_buffer_with_data(&BufferInitDescriptor {
264
label: make_buffer_label::<Self>(&self.label),
265
usage: self.buffer_usage,
266
contents: self.scratch.as_ref(),
267
}));
268
self.changed = false;
269
} else if let Some(buffer) = &self.buffer {
270
queue.write_buffer(buffer, 0, self.scratch.as_ref());
271
}
272
273
self.last_written_size = BufferSize::new(size);
274
}
275
276
#[inline]
277
pub fn clear(&mut self) {
278
self.scratch.as_mut().clear();
279
self.scratch.set_offset(0);
280
}
281
}
282
283
impl<'a, T: ShaderType + WriteInto> IntoBinding<'a> for &'a DynamicStorageBuffer<T> {
284
#[inline]
285
fn into_binding(self) -> BindingResource<'a> {
286
self.binding().expect("Failed to get buffer")
287
}
288
}
289
290