diff --git a/crates/egui-wgpu/CHANGELOG.md b/crates/egui-wgpu/CHANGELOG.md index fd16e337..d99ea1bd 100644 --- a/crates/egui-wgpu/CHANGELOG.md +++ b/crates/egui-wgpu/CHANGELOG.md @@ -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`. diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index 524d9893..d7e1007e 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -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>, + slices: Vec>, 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::() * index_count) as u64; - if self.index_buffer.capacity < required_size { + let required_index_buffer_size = (std::mem::size_of::() * 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::(); + 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_count) as u64; - if self.vertex_buffer.capacity < required_size { + let required_vertex_buffer_size = (std::mem::size_of::() * 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::(); + 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::() { - 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::() { + 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 + } } }