[egui-wgpu] Do vertex & index buffer in a single copy each (#2820)
* [egui-wgpu] Do vertex & index buffer in a single copy each Also, copy uniform buffer only if necessary. Previously, we did hundreds of small copies via queue.write_buffer which would create a new buffer for each of these copies. Now, there are only two gpu sided copy operations and the memory goes directly to the staging buffer. In a quick debug test on Rerun this decreased time for the `update_buffer` method from about 0.87ms to 0.37ms! * fix comparing padding on UniformBuffer in wgpu renderer --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
5ccc71415e
commit
b1f837ca2f
|
|
@ -4,6 +4,7 @@ All notable changes to the `egui-wgpu` integration will be noted in this file.
|
|||
|
||||
## Unreleased
|
||||
* Add `read_screan_rgba` to the egui-wgpu `Painter`, to allow for capturing the current frame when using wgpu. Used in conjuction with `Frame::request_screenshot`. ([#2676](https://github.com/emilk/egui/pull/2676))
|
||||
* Improve performance of `update_buffers` ([#2820](https://github.com/emilk/egui/pull/2820))
|
||||
|
||||
|
||||
## 0.21.0 - 2023-02-08
|
||||
|
|
@ -13,6 +14,7 @@ All notable changes to the `egui-wgpu` integration will be noted in this file.
|
|||
* `egui-wgpu` now only depends on `epaint` instead of the entire `egui` ([#2438](https://github.com/emilk/egui/pull/2438)).
|
||||
* `winit::Painter` now supports transparent backbuffer ([#2684](https://github.com/emilk/egui/pull/2684)).
|
||||
|
||||
|
||||
## 0.20.0 - 2022-12-08 - web support
|
||||
* Renamed `RenderPass` to `Renderer`.
|
||||
* Renamed `RenderPass::execute` to `RenderPass::render`.
|
||||
|
|
|
|||
|
|
@ -133,9 +133,15 @@ struct UniformBuffer {
|
|||
_padding: [u32; 2],
|
||||
}
|
||||
|
||||
impl PartialEq for UniformBuffer {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.screen_size_in_points == other.screen_size_in_points
|
||||
}
|
||||
}
|
||||
|
||||
struct SlicedBuffer {
|
||||
buffer: wgpu::Buffer,
|
||||
slices: Vec<Range<wgpu::BufferAddress>>,
|
||||
slices: Vec<Range<usize>>,
|
||||
capacity: wgpu::BufferAddress,
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +153,7 @@ pub struct Renderer {
|
|||
vertex_buffer: SlicedBuffer,
|
||||
|
||||
uniform_buffer: wgpu::Buffer,
|
||||
previous_uniform_buffer_content: UniformBuffer,
|
||||
uniform_bind_group: wgpu::BindGroup,
|
||||
texture_bind_group_layout: wgpu::BindGroupLayout,
|
||||
|
||||
|
|
@ -332,6 +339,11 @@ impl Renderer {
|
|||
capacity: INDEX_BUFFER_START_CAPACITY,
|
||||
},
|
||||
uniform_buffer,
|
||||
// Buffers on wgpu are zero initialized, so this is indeed its current state!
|
||||
previous_uniform_buffer_content: UniformBuffer {
|
||||
screen_size_in_points: [0.0, 0.0],
|
||||
_padding: [0, 0],
|
||||
},
|
||||
uniform_bind_group,
|
||||
texture_bind_group_layout,
|
||||
textures: HashMap::new(),
|
||||
|
|
@ -403,12 +415,16 @@ impl Renderer {
|
|||
if let Some((_texture, bind_group)) = self.textures.get(&mesh.texture_id) {
|
||||
render_pass.set_bind_group(1, bind_group, &[]);
|
||||
render_pass.set_index_buffer(
|
||||
self.index_buffer.buffer.slice(index_buffer_slice.clone()),
|
||||
self.index_buffer.buffer.slice(
|
||||
index_buffer_slice.start as u64..index_buffer_slice.end as u64,
|
||||
),
|
||||
wgpu::IndexFormat::Uint32,
|
||||
);
|
||||
render_pass.set_vertex_buffer(
|
||||
0,
|
||||
self.vertex_buffer.buffer.slice(vertex_buffer_slice.clone()),
|
||||
self.vertex_buffer.buffer.slice(
|
||||
vertex_buffer_slice.start as u64..vertex_buffer_slice.end as u64,
|
||||
),
|
||||
);
|
||||
render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
|
||||
} else {
|
||||
|
|
@ -748,17 +764,18 @@ impl Renderer {
|
|||
|
||||
let screen_size_in_points = screen_descriptor.screen_size_in_points();
|
||||
|
||||
{
|
||||
crate::profile_scope!("uniforms");
|
||||
// Update uniform buffer
|
||||
let uniform_buffer_content = UniformBuffer {
|
||||
screen_size_in_points,
|
||||
_padding: Default::default(),
|
||||
};
|
||||
if uniform_buffer_content != self.previous_uniform_buffer_content {
|
||||
crate::profile_scope!("update uniforms");
|
||||
queue.write_buffer(
|
||||
&self.uniform_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&[UniformBuffer {
|
||||
screen_size_in_points,
|
||||
_padding: Default::default(),
|
||||
}]),
|
||||
bytemuck::cast_slice(&[uniform_buffer_content]),
|
||||
);
|
||||
self.previous_uniform_buffer_content = uniform_buffer_content;
|
||||
}
|
||||
|
||||
// Determine how many vertices & indices need to be rendered.
|
||||
|
|
@ -774,73 +791,104 @@ impl Renderer {
|
|||
})
|
||||
};
|
||||
|
||||
{
|
||||
// Resize index buffer if needed:
|
||||
if index_count > 0 {
|
||||
crate::profile_scope!("indices");
|
||||
|
||||
self.index_buffer.slices.clear();
|
||||
let required_size = (std::mem::size_of::<u32>() * index_count) as u64;
|
||||
if self.index_buffer.capacity < required_size {
|
||||
let required_index_buffer_size = (std::mem::size_of::<u32>() * index_count) as u64;
|
||||
if self.index_buffer.capacity < required_index_buffer_size {
|
||||
// Resize index buffer if needed.
|
||||
self.index_buffer.capacity =
|
||||
(self.index_buffer.capacity * 2).at_least(required_size);
|
||||
(self.index_buffer.capacity * 2).at_least(required_index_buffer_size);
|
||||
self.index_buffer.buffer = create_index_buffer(device, self.index_buffer.capacity);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// Resize vertex buffer if needed:
|
||||
let mut index_buffer_staging = queue
|
||||
.write_buffer_with(
|
||||
&self.index_buffer.buffer,
|
||||
0,
|
||||
NonZeroU64::new(required_index_buffer_size).unwrap(),
|
||||
)
|
||||
.expect("Failed to create staging buffer for index data");
|
||||
let mut index_offset = 0;
|
||||
for epaint::ClippedPrimitive { primitive, .. } in paint_jobs.iter() {
|
||||
match primitive {
|
||||
Primitive::Mesh(mesh) => {
|
||||
let size = mesh.indices.len() * std::mem::size_of::<u32>();
|
||||
let slice = index_offset..(size + index_offset);
|
||||
index_buffer_staging[slice.clone()]
|
||||
.copy_from_slice(bytemuck::cast_slice(&mesh.indices));
|
||||
self.index_buffer.slices.push(slice);
|
||||
index_offset += size;
|
||||
}
|
||||
Primitive::Callback(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if vertex_count > 0 {
|
||||
crate::profile_scope!("vertices");
|
||||
|
||||
self.vertex_buffer.slices.clear();
|
||||
let required_size = (std::mem::size_of::<Vertex>() * vertex_count) as u64;
|
||||
if self.vertex_buffer.capacity < required_size {
|
||||
let required_vertex_buffer_size = (std::mem::size_of::<Vertex>() * vertex_count) as u64;
|
||||
if self.vertex_buffer.capacity < required_vertex_buffer_size {
|
||||
// Resize vertex buffer if needed.
|
||||
self.vertex_buffer.capacity =
|
||||
(self.vertex_buffer.capacity * 2).at_least(required_size);
|
||||
(self.vertex_buffer.capacity * 2).at_least(required_vertex_buffer_size);
|
||||
self.vertex_buffer.buffer =
|
||||
create_vertex_buffer(device, self.vertex_buffer.capacity);
|
||||
}
|
||||
}
|
||||
|
||||
// Upload index & vertex data and call user callbacks
|
||||
let mut user_cmd_bufs = Vec::new(); // collect user command buffers
|
||||
|
||||
crate::profile_scope!("primitives");
|
||||
for epaint::ClippedPrimitive { primitive, .. } in paint_jobs.iter() {
|
||||
match primitive {
|
||||
Primitive::Mesh(mesh) => {
|
||||
{
|
||||
let index_offset = self.index_buffer.slices.last().unwrap_or(&(0..0)).end;
|
||||
let data = bytemuck::cast_slice(&mesh.indices);
|
||||
queue.write_buffer(&self.index_buffer.buffer, index_offset, data);
|
||||
self.index_buffer
|
||||
.slices
|
||||
.push(index_offset..(data.len() as wgpu::BufferAddress + index_offset));
|
||||
let mut vertex_buffer_staging = queue
|
||||
.write_buffer_with(
|
||||
&self.vertex_buffer.buffer,
|
||||
0,
|
||||
NonZeroU64::new(required_vertex_buffer_size).unwrap(),
|
||||
)
|
||||
.expect("Failed to create staging buffer for vertex data");
|
||||
let mut vertex_offset = 0;
|
||||
for epaint::ClippedPrimitive { primitive, .. } in paint_jobs.iter() {
|
||||
match primitive {
|
||||
Primitive::Mesh(mesh) => {
|
||||
let size = mesh.vertices.len() * std::mem::size_of::<Vertex>();
|
||||
let slice = vertex_offset..(size + vertex_offset);
|
||||
vertex_buffer_staging[slice.clone()]
|
||||
.copy_from_slice(bytemuck::cast_slice(&mesh.vertices));
|
||||
self.vertex_buffer.slices.push(slice);
|
||||
vertex_offset += size;
|
||||
}
|
||||
{
|
||||
let vertex_offset = self.vertex_buffer.slices.last().unwrap_or(&(0..0)).end;
|
||||
let data = bytemuck::cast_slice(&mesh.vertices);
|
||||
queue.write_buffer(&self.vertex_buffer.buffer, vertex_offset, data);
|
||||
self.vertex_buffer.slices.push(
|
||||
vertex_offset..(data.len() as wgpu::BufferAddress + vertex_offset),
|
||||
);
|
||||
}
|
||||
}
|
||||
Primitive::Callback(callback) => {
|
||||
let cbfn = if let Some(c) = callback.callback.downcast_ref::<CallbackFn>() {
|
||||
c
|
||||
} else {
|
||||
tracing::warn!("Unknown paint callback: expected `egui_wgpu::CallbackFn`");
|
||||
continue;
|
||||
};
|
||||
|
||||
crate::profile_scope!("callback");
|
||||
user_cmd_bufs.extend((cbfn.prepare)(
|
||||
device,
|
||||
queue,
|
||||
encoder,
|
||||
&mut self.paint_callback_resources,
|
||||
));
|
||||
Primitive::Callback(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
user_cmd_bufs
|
||||
{
|
||||
crate::profile_scope!("user command buffers");
|
||||
let mut user_cmd_bufs = Vec::new(); // collect user command buffers
|
||||
for epaint::ClippedPrimitive { primitive, .. } in paint_jobs.iter() {
|
||||
match primitive {
|
||||
Primitive::Mesh(_) => {}
|
||||
Primitive::Callback(callback) => {
|
||||
let cbfn = if let Some(c) = callback.callback.downcast_ref::<CallbackFn>() {
|
||||
c
|
||||
} else {
|
||||
tracing::warn!(
|
||||
"Unknown paint callback: expected `egui_wgpu::CallbackFn`"
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
crate::profile_scope!("callback");
|
||||
user_cmd_bufs.extend((cbfn.prepare)(
|
||||
device,
|
||||
queue,
|
||||
encoder,
|
||||
&mut self.paint_callback_resources,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
user_cmd_bufs
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue