Skip to content

Commit 2472949

Browse files
authored
Add buffer reuse and copy on resize. (#22559)
Makes `ShaderStorageBuffer` have parity with `Image` vis-a-vis #22552. We apply the same pattern used there here.
1 parent 27fd2c4 commit 2472949

File tree

1 file changed

+82
-23
lines changed

1 file changed

+82
-23
lines changed

crates/bevy_render/src/storage.rs

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
render_asset::{AssetExtractionError, PrepareAssetError, RenderAsset, RenderAssetPlugin},
33
render_resource::{Buffer, BufferUsages},
4-
renderer::RenderDevice,
4+
renderer::{RenderDevice, RenderQueue},
55
};
66
use bevy_app::{App, Plugin};
77
use bevy_asset::{Asset, AssetApp, AssetId, RenderAssetUsages};
@@ -34,6 +34,8 @@ pub struct ShaderBuffer {
3434
pub buffer_description: wgpu::BufferDescriptor<'static>,
3535
/// The asset usage of the storage buffer.
3636
pub asset_usage: RenderAssetUsages,
37+
/// Whether this buffer should be copied on the GPU when resized.
38+
pub copy_on_resize: bool,
3739
}
3840

3941
impl Default for ShaderBuffer {
@@ -43,10 +45,11 @@ impl Default for ShaderBuffer {
4345
buffer_description: wgpu::BufferDescriptor {
4446
label: None,
4547
size: 0,
46-
usage: BufferUsages::STORAGE,
48+
usage: BufferUsages::STORAGE | BufferUsages::COPY_SRC | BufferUsages::COPY_DST,
4749
mapped_at_creation: false,
4850
},
4951
asset_usage: RenderAssetUsages::default(),
52+
copy_on_resize: false,
5053
}
5154
}
5255
}
@@ -84,6 +87,30 @@ impl ShaderBuffer {
8487
wrapper.write(&value).unwrap();
8588
self.data = Some(wrapper.into_inner());
8689
}
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+
}
87114
}
88115

89116
impl<T> From<T> for ShaderBuffer
@@ -101,12 +128,13 @@ where
101128
/// A storage buffer that is prepared as a [`RenderAsset`] and uploaded to the GPU.
102129
pub struct GpuShaderBuffer {
103130
pub buffer: Buffer,
131+
pub buffer_descriptor: wgpu::BufferDescriptor<'static>,
104132
pub had_data: bool,
105133
}
106134

107135
impl RenderAsset for GpuShaderBuffer {
108136
type SourceAsset = ShaderBuffer;
109-
type Param = SRes<RenderDevice>;
137+
type Param = (SRes<RenderDevice>, SRes<RenderQueue>);
110138

111139
fn asset_usage(source_asset: &Self::SourceAsset) -> RenderAssetUsages {
112140
source_asset.asset_usage
@@ -131,28 +159,59 @@ impl RenderAsset for GpuShaderBuffer {
131159
fn prepare_asset(
132160
source_asset: Self::SourceAsset,
133161
_: AssetId<Self::SourceAsset>,
134-
render_device: &mut SystemParamItem<Self::Param>,
135-
_: Option<&Self>,
162+
(render_device, render_queue): &mut SystemParamItem<Self::Param>,
163+
previous_asset: Option<&Self>,
136164
) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
137-
match source_asset.data {
138-
Some(data) => {
139-
let buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
140-
label: source_asset.buffer_description.label,
141-
contents: &data,
142-
usage: source_asset.buffer_description.usage,
143-
});
144-
Ok(GpuShaderBuffer {
145-
buffer,
146-
had_data: true,
147-
})
165+
let had_data = source_asset.data.is_some();
166+
167+
let buffer = if let Some(prev) = previous_asset
168+
&& prev.buffer_descriptor == source_asset.buffer_description
169+
&& source_asset
170+
.buffer_description
171+
.usage
172+
.contains(BufferUsages::COPY_DST)
173+
{
174+
if let Some(ref data) = source_asset.data {
175+
render_queue.write_buffer(&prev.buffer, 0, data);
148176
}
149-
None => {
150-
let buffer = render_device.create_buffer(&source_asset.buffer_description);
151-
Ok(GpuShaderBuffer {
152-
buffer,
153-
had_data: false,
154-
})
177+
prev.buffer.clone()
178+
} else if let Some(ref data) = source_asset.data {
179+
render_device.create_buffer_with_data(&BufferInitDescriptor {
180+
label: source_asset.buffer_description.label,
181+
contents: data,
182+
usage: source_asset.buffer_description.usage,
183+
})
184+
} else {
185+
let new_buffer = render_device.create_buffer(&source_asset.buffer_description);
186+
if source_asset.copy_on_resize
187+
&& let Some(previous) = previous_asset
188+
&& previous
189+
.buffer_descriptor
190+
.usage
191+
.contains(BufferUsages::COPY_SRC)
192+
&& source_asset
193+
.buffer_description
194+
.usage
195+
.contains(BufferUsages::COPY_DST)
196+
{
197+
let copy_size = source_asset
198+
.buffer_description
199+
.size
200+
.min(previous.buffer_descriptor.size);
201+
let mut encoder =
202+
render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
203+
label: Some("copy_buffer_on_resize"),
204+
});
205+
encoder.copy_buffer_to_buffer(&previous.buffer, 0, &new_buffer, 0, copy_size);
206+
render_queue.submit([encoder.finish()]);
155207
}
156-
}
208+
new_buffer
209+
};
210+
211+
Ok(GpuShaderBuffer {
212+
buffer,
213+
buffer_descriptor: source_asset.buffer_description,
214+
had_data,
215+
})
157216
}
158217
}

0 commit comments

Comments
 (0)