Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/storage.rs
9349 views
1
use crate::{
2
render_asset::{AssetExtractionError, PrepareAssetError, RenderAsset, RenderAssetPlugin},
3
render_resource::{Buffer, BufferUsages},
4
renderer::{RenderDevice, RenderQueue},
5
};
6
use bevy_app::{App, Plugin};
7
use bevy_asset::{Asset, AssetApp, AssetId, RenderAssetUsages};
8
use bevy_ecs::system::{lifetimeless::SRes, SystemParamItem};
9
use bevy_reflect::{prelude::ReflectDefault, Reflect};
10
use bevy_utils::default;
11
use encase::{internal::WriteInto, ShaderType};
12
use wgpu::util::BufferInitDescriptor;
13
14
/// Adds [`ShaderBuffer`] as an asset that is extracted and uploaded to the GPU.
15
#[derive(Default)]
16
pub struct StoragePlugin;
17
18
impl Plugin for StoragePlugin {
19
fn build(&self, app: &mut App) {
20
app.add_plugins(RenderAssetPlugin::<GpuShaderBuffer>::default())
21
.init_asset::<ShaderBuffer>()
22
.register_asset_reflect::<ShaderBuffer>();
23
}
24
}
25
26
/// A storage buffer that is prepared as a [`RenderAsset`] and uploaded to the GPU.
27
#[derive(Asset, Reflect, Debug, Clone)]
28
#[reflect(opaque)]
29
#[reflect(Default, Debug, Clone)]
30
pub struct ShaderBuffer {
31
/// Optional data used to initialize the buffer.
32
pub data: Option<Vec<u8>>,
33
/// The buffer description used to create the buffer.
34
pub buffer_description: wgpu::BufferDescriptor<'static>,
35
/// The asset usage of the storage buffer.
36
pub asset_usage: RenderAssetUsages,
37
/// Whether this buffer should be copied on the GPU when resized.
38
pub copy_on_resize: bool,
39
}
40
41
impl Default for ShaderBuffer {
42
fn default() -> Self {
43
Self {
44
data: None,
45
buffer_description: wgpu::BufferDescriptor {
46
label: None,
47
size: 0,
48
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
49
mapped_at_creation: false,
50
},
51
asset_usage: RenderAssetUsages::default(),
52
copy_on_resize: false,
53
}
54
}
55
}
56
57
impl ShaderBuffer {
58
/// Creates a new storage buffer with the given data and asset usage.
59
pub fn new(data: &[u8], asset_usage: RenderAssetUsages) -> Self {
60
let mut storage = ShaderBuffer {
61
data: Some(data.to_vec()),
62
..default()
63
};
64
storage.asset_usage = asset_usage;
65
storage
66
}
67
68
/// Creates a new storage buffer with the given size and asset usage.
69
pub fn with_size(size: usize, asset_usage: RenderAssetUsages) -> Self {
70
let mut storage = ShaderBuffer {
71
data: None,
72
..default()
73
};
74
storage.buffer_description.size = size as u64;
75
storage.buffer_description.mapped_at_creation = false;
76
storage.asset_usage = asset_usage;
77
storage
78
}
79
80
/// Sets the data of the storage buffer to the given [`ShaderType`].
81
pub fn set_data<T>(&mut self, value: T)
82
where
83
T: ShaderType + WriteInto,
84
{
85
let size = value.size().get() as usize;
86
let mut wrapper = encase::StorageBuffer::<Vec<u8>>::new(Vec::with_capacity(size));
87
wrapper.write(&value).unwrap();
88
self.data = Some(wrapper.into_inner());
89
}
90
91
/// Resizes the buffer to the new size.
92
///
93
/// If CPU data is present, it will be truncated or zero-extended.
94
/// Does not preserve GPU data when the descriptor changes.
95
pub fn resize(&mut self, size: u64) {
96
self.buffer_description.size = size;
97
if let Some(ref mut data) = self.data {
98
data.resize(size as usize, 0);
99
}
100
}
101
102
/// Resizes the buffer to the new size, preserving existing data.
103
///
104
/// If CPU data is present, it will be truncated or zero-extended.
105
/// If no CPU data is present, sets `copy_on_resize` to preserve GPU data.
106
pub fn resize_in_place(&mut self, size: u64) {
107
self.buffer_description.size = size;
108
if let Some(ref mut data) = self.data {
109
data.resize(size as usize, 0);
110
} else {
111
self.copy_on_resize = true;
112
}
113
}
114
}
115
116
impl<T> From<T> for ShaderBuffer
117
where
118
T: ShaderType + WriteInto,
119
{
120
fn from(value: T) -> Self {
121
let size = value.size().get() as usize;
122
let mut wrapper = encase::StorageBuffer::<Vec<u8>>::new(Vec::with_capacity(size));
123
wrapper.write(&value).unwrap();
124
Self::new(wrapper.as_ref(), RenderAssetUsages::default())
125
}
126
}
127
128
/// A storage buffer that is prepared as a [`RenderAsset`] and uploaded to the GPU.
129
pub struct GpuShaderBuffer {
130
pub buffer: Buffer,
131
pub buffer_descriptor: wgpu::BufferDescriptor<'static>,
132
pub had_data: bool,
133
}
134
135
impl RenderAsset for GpuShaderBuffer {
136
type SourceAsset = ShaderBuffer;
137
type Param = (SRes<RenderDevice>, SRes<RenderQueue>);
138
139
fn asset_usage(source_asset: &Self::SourceAsset) -> RenderAssetUsages {
140
source_asset.asset_usage
141
}
142
143
fn take_gpu_data(
144
source: &mut Self::SourceAsset,
145
previous_gpu_asset: Option<&Self>,
146
) -> Result<Self::SourceAsset, AssetExtractionError> {
147
let data = source.data.take();
148
149
let valid_upload = data.is_some() || previous_gpu_asset.is_none_or(|prev| !prev.had_data);
150
151
valid_upload
152
.then(|| Self::SourceAsset {
153
data,
154
..source.clone()
155
})
156
.ok_or(AssetExtractionError::AlreadyExtracted)
157
}
158
159
fn prepare_asset(
160
source_asset: Self::SourceAsset,
161
_: AssetId<Self::SourceAsset>,
162
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
163
previous_asset: Option<&Self>,
164
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
165
let had_data = source_asset.data.is_some();
166
167
// when cpu data is provided, the actual buffer size is determined by the vec length,
168
// not the descriptor size
169
let actual_size = source_asset
170
.data
171
.as_ref()
172
.map(|d| d.len() as u64)
173
.unwrap_or(source_asset.buffer_description.size);
174
175
let buffer = if let Some(prev) = previous_asset
176
&& prev.buffer_descriptor.size == actual_size
177
&& prev.buffer_descriptor.usage == source_asset.buffer_description.usage
178
&& prev.buffer_descriptor.label == source_asset.buffer_description.label
179
&& source_asset
180
.buffer_description
181
.usage
182
.contains(BufferUsages::COPY_DST)
183
{
184
if let Some(ref data) = source_asset.data {
185
render_queue.write_buffer(&prev.buffer, 0, data);
186
}
187
prev.buffer.clone()
188
} else if let Some(ref data) = source_asset.data {
189
render_device.create_buffer_with_data(&BufferInitDescriptor {
190
label: source_asset.buffer_description.label,
191
contents: data,
192
usage: source_asset.buffer_description.usage,
193
})
194
} else {
195
let new_buffer = render_device.create_buffer(&source_asset.buffer_description);
196
if source_asset.copy_on_resize
197
&& let Some(previous) = previous_asset
198
&& previous
199
.buffer_descriptor
200
.usage
201
.contains(BufferUsages::COPY_SRC)
202
&& source_asset
203
.buffer_description
204
.usage
205
.contains(BufferUsages::COPY_DST)
206
{
207
let copy_size = source_asset
208
.buffer_description
209
.size
210
.min(previous.buffer_descriptor.size);
211
let mut encoder =
212
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
213
label: Some("copy_buffer_on_resize"),
214
});
215
encoder.copy_buffer_to_buffer(&previous.buffer, 0, &new_buffer, 0, copy_size);
216
render_queue.submit([encoder.finish()]);
217
}
218
new_buffer
219
};
220
221
Ok(GpuShaderBuffer {
222
buffer,
223
buffer_descriptor: wgpu::BufferDescriptor {
224
size: actual_size,
225
..source_asset.buffer_description
226
},
227
had_data,
228
})
229
}
230
}
231
232