Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/meshlet/persistent_buffer.rs
6600 views
1
use bevy_render::{
2
render_resource::{
3
BindingResource, Buffer, BufferAddress, BufferDescriptor, BufferUsages,
4
CommandEncoderDescriptor, COPY_BUFFER_ALIGNMENT,
5
},
6
renderer::{RenderDevice, RenderQueue},
7
};
8
use core::{num::NonZero, ops::Range};
9
use range_alloc::RangeAllocator;
10
11
/// Wrapper for a GPU buffer holding a large amount of data that persists across frames.
12
pub struct PersistentGpuBuffer<T: PersistentGpuBufferable> {
13
/// Debug label for the buffer.
14
label: &'static str,
15
/// Handle to the GPU buffer.
16
buffer: Buffer,
17
/// Tracks free slices of the buffer.
18
allocation_planner: RangeAllocator<BufferAddress>,
19
/// Queue of pending writes, and associated metadata.
20
write_queue: Vec<(T, T::Metadata, Range<BufferAddress>)>,
21
}
22
23
impl<T: PersistentGpuBufferable> PersistentGpuBuffer<T> {
24
/// Create a new persistent buffer.
25
pub fn new(label: &'static str, render_device: &RenderDevice) -> Self {
26
Self {
27
label,
28
buffer: render_device.create_buffer(&BufferDescriptor {
29
label: Some(label),
30
size: 0,
31
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
32
mapped_at_creation: false,
33
}),
34
allocation_planner: RangeAllocator::new(0..0),
35
write_queue: Vec::new(),
36
}
37
}
38
39
/// Queue an item of type T to be added to the buffer, returning the byte range within the buffer that it will be located at.
40
pub fn queue_write(&mut self, data: T, metadata: T::Metadata) -> Range<BufferAddress> {
41
let data_size = data.size_in_bytes() as u64;
42
debug_assert!(data_size.is_multiple_of(COPY_BUFFER_ALIGNMENT));
43
if let Ok(buffer_slice) = self.allocation_planner.allocate_range(data_size) {
44
self.write_queue
45
.push((data, metadata, buffer_slice.clone()));
46
return buffer_slice;
47
}
48
49
let buffer_size = self.allocation_planner.initial_range();
50
let double_buffer_size = (buffer_size.end - buffer_size.start) * 2;
51
let new_size = double_buffer_size.max(data_size);
52
self.allocation_planner.grow_to(buffer_size.end + new_size);
53
54
let buffer_slice = self.allocation_planner.allocate_range(data_size).unwrap();
55
self.write_queue
56
.push((data, metadata, buffer_slice.clone()));
57
buffer_slice
58
}
59
60
/// Upload all pending data to the GPU buffer.
61
pub fn perform_writes(&mut self, render_queue: &RenderQueue, render_device: &RenderDevice) {
62
if self.allocation_planner.initial_range().end > self.buffer.size() {
63
self.expand_buffer(render_device, render_queue);
64
}
65
66
let queue_count = self.write_queue.len();
67
68
for (data, metadata, buffer_slice) in self.write_queue.drain(..) {
69
let buffer_slice_size =
70
NonZero::<u64>::new(buffer_slice.end - buffer_slice.start).unwrap();
71
let mut buffer_view = render_queue
72
.write_buffer_with(&self.buffer, buffer_slice.start, buffer_slice_size)
73
.unwrap();
74
data.write_bytes_le(metadata, &mut buffer_view, buffer_slice.start);
75
}
76
77
let queue_saturation = queue_count as f32 / self.write_queue.capacity() as f32;
78
if queue_saturation < 0.3 {
79
self.write_queue = Vec::new();
80
}
81
}
82
83
/// Mark a section of the GPU buffer as no longer needed.
84
pub fn mark_slice_unused(&mut self, buffer_slice: Range<BufferAddress>) {
85
self.allocation_planner.free_range(buffer_slice);
86
}
87
88
pub fn binding(&self) -> BindingResource<'_> {
89
self.buffer.as_entire_binding()
90
}
91
92
/// Expand the buffer by creating a new buffer and copying old data over.
93
fn expand_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
94
let size = self.allocation_planner.initial_range();
95
let new_buffer = render_device.create_buffer(&BufferDescriptor {
96
label: Some(self.label),
97
size: size.end - size.start,
98
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
99
mapped_at_creation: false,
100
});
101
102
let mut command_encoder = render_device.create_command_encoder(&CommandEncoderDescriptor {
103
label: Some("persistent_gpu_buffer_expand"),
104
});
105
command_encoder.copy_buffer_to_buffer(&self.buffer, 0, &new_buffer, 0, self.buffer.size());
106
render_queue.submit([command_encoder.finish()]);
107
108
self.buffer = new_buffer;
109
}
110
}
111
112
/// A trait representing data that can be written to a [`PersistentGpuBuffer`].
113
pub trait PersistentGpuBufferable {
114
/// Additional metadata associated with each item, made available during `write_bytes_le`.
115
type Metadata;
116
117
/// The size in bytes of `self`. This will determine the size of the buffer passed into
118
/// `write_bytes_le`.
119
///
120
/// All data written must be in a multiple of `wgpu::COPY_BUFFER_ALIGNMENT` bytes. Failure to do so will
121
/// result in a panic when using [`PersistentGpuBuffer`].
122
fn size_in_bytes(&self) -> usize;
123
124
/// Convert `self` + `metadata` into bytes (little-endian), and write to the provided buffer slice.
125
/// Any bytes not written to in the slice will be zeroed out when uploaded to the GPU.
126
fn write_bytes_le(
127
&self,
128
metadata: Self::Metadata,
129
buffer_slice: &mut [u8],
130
buffer_offset: BufferAddress,
131
);
132
}
133
134