diff --git a/blade-egui/src/belt.rs b/blade-egui/src/belt.rs index 1b5594be..0f0946f9 100644 --- a/blade-egui/src/belt.rs +++ b/blade-egui/src/belt.rs @@ -85,7 +85,7 @@ impl BufferBelt { bp } - pub fn flush(&mut self, sp: blade_graphics::SyncPoint) { + pub fn flush(&mut self, sp: &blade_graphics::SyncPoint) { self.buffers .extend(self.active.drain(..).map(|(rb, _)| (rb, sp.clone()))); } diff --git a/blade-egui/src/lib.rs b/blade-egui/src/lib.rs index bc821437..fac41a03 100644 --- a/blade-egui/src/lib.rs +++ b/blade-egui/src/lib.rs @@ -349,7 +349,7 @@ impl GuiPainter { /// Call this after submitting work at the given `sync_point`. #[profiling::function] - pub fn after_submit(&mut self, sync_point: blade_graphics::SyncPoint) { + pub fn after_submit(&mut self, sync_point: &blade_graphics::SyncPoint) { self.textures_to_delete.extend( self.textures_dropped .drain(..) diff --git a/blade-render/src/lib.rs b/blade-render/src/lib.rs index db414309..0d664053 100644 --- a/blade-render/src/lib.rs +++ b/blade-render/src/lib.rs @@ -14,6 +14,7 @@ pub mod model; mod render; pub mod shader; pub mod texture; +pub mod util; pub use asset_hub::*; pub use model::Model; diff --git a/blade-render/src/render/mod.rs b/blade-render/src/render/mod.rs index 45cd5793..281f6f49 100644 --- a/blade-render/src/render/mod.rs +++ b/blade-render/src/render/mod.rs @@ -748,6 +748,13 @@ impl ShaderPipelines { } } +/// Temporary resources associated with a GPU frame. +#[derive(Default)] +pub struct FrameResources { + pub buffers: Vec, + pub acceleration_structures: Vec, +} + impl Renderer { /// Create a new renderer with a given configuration. /// @@ -1043,8 +1050,7 @@ impl Renderer { env_map: Option>, asset_hub: &crate::AssetHub, gpu: &blade_graphics::Context, - temp_buffers: &mut Vec, - temp_acceleration_structures: &mut Vec, + temp: &mut FrameResources, ) { let (env_view, env_extent) = match env_map { Some(handle) => { @@ -1057,7 +1063,8 @@ impl Renderer { .assign(env_view, env_extent, command_encoder, gpu); if self.acceleration_structure != blade_graphics::AccelerationStructure::default() { - temp_acceleration_structures.push(self.acceleration_structure); + temp.acceleration_structures + .push(self.acceleration_structure); } let geometry_count = objects @@ -1070,7 +1077,7 @@ impl Renderer { let hit_size = (geometry_count.max(1) * mem::size_of::()) as u64; //TODO: reuse the hit buffer if self.hit_buffer != blade_graphics::Buffer::default() { - temp_buffers.push(self.hit_buffer); + temp.buffers.push(self.hit_buffer); } self.hit_buffer = gpu.create_buffer(blade_graphics::BufferDesc { name: "hit entries", @@ -1082,7 +1089,7 @@ impl Renderer { size: hit_size, memory: blade_graphics::Memory::Upload, }); - temp_buffers.push(hit_staging); + temp.buffers.push(hit_staging); { let mut transfers = command_encoder.transfer(); transfers.copy_buffer_to_buffer(hit_staging.at(0), self.hit_buffer.at(0), hit_size); @@ -1222,8 +1229,8 @@ impl Renderer { scratch_buf.at(0), ); - temp_buffers.push(instance_buf); - temp_buffers.push(scratch_buf); + temp.buffers.push(instance_buf); + temp.buffers.push(scratch_buf); } /// Prepare to render a frame. diff --git a/blade-render/src/util/frame_pacer.rs b/blade-render/src/util/frame_pacer.rs new file mode 100644 index 00000000..41938dee --- /dev/null +++ b/blade-render/src/util/frame_pacer.rs @@ -0,0 +1,68 @@ +use crate::render::FrameResources; +use std::mem; + +/// Utility object that encapsulates the logic +/// of always rendering 1 frame at a time, and +/// cleaning up the temporary resources. +pub struct FramePacer { + frame_index: usize, + prev_resources: FrameResources, + prev_sync_point: Option, + command_encoder: Option, + next_resources: FrameResources, +} + +impl FramePacer { + pub fn new(context: &blade_graphics::Context) -> Self { + let encoder = context.create_command_encoder(blade_graphics::CommandEncoderDesc { + name: "main", + buffer_count: 2, + }); + Self { + frame_index: 0, + prev_resources: FrameResources::default(), + prev_sync_point: None, + command_encoder: Some(encoder), + next_resources: FrameResources::default(), + } + } + + #[profiling::function] + pub fn wait_for_previous_frame(&mut self, context: &blade_graphics::Context) { + if let Some(sp) = self.prev_sync_point.take() { + context.wait_for(&sp, !0); + } + for buffer in self.prev_resources.buffers.drain(..) { + context.destroy_buffer(buffer); + } + for accel_structure in self.prev_resources.acceleration_structures.drain(..) { + context.destroy_acceleration_structure(accel_structure); + } + } + + pub fn last_sync_point(&self) -> Option<&blade_graphics::SyncPoint> { + self.prev_sync_point.as_ref() + } + + pub fn destroy(&mut self, context: &blade_graphics::Context) { + self.wait_for_previous_frame(context); + context.destroy_command_encoder(self.command_encoder.take().unwrap()); + } + + pub fn begin_frame(&mut self) -> (&mut blade_graphics::CommandEncoder, &mut FrameResources) { + let encoder = self.command_encoder.as_mut().unwrap(); + encoder.start(); + (encoder, &mut self.next_resources) + } + + pub fn end_frame(&mut self, context: &blade_graphics::Context) -> &blade_graphics::SyncPoint { + let sync_point = context.submit(self.command_encoder.as_mut().unwrap()); + self.frame_index += 1; + // Wait for the previous frame immediately - this ensures that we are + // only processing one frame at a time, and yet not stalling. + self.wait_for_previous_frame(context); + self.prev_sync_point = Some(sync_point); + mem::swap(&mut self.prev_resources, &mut self.next_resources); + self.prev_sync_point.as_ref().unwrap() + } +} diff --git a/blade-render/src/util/mod.rs b/blade-render/src/util/mod.rs new file mode 100644 index 00000000..3cae34a9 --- /dev/null +++ b/blade-render/src/util/mod.rs @@ -0,0 +1,3 @@ +mod frame_pacer; + +pub use self::frame_pacer::*; diff --git a/examples/particle/main.rs b/examples/particle/main.rs index 2e8b06ae..294a3df1 100644 --- a/examples/particle/main.rs +++ b/examples/particle/main.rs @@ -103,7 +103,7 @@ impl Example { } encoder.present(frame); let sync_point = self.context.submit(encoder); - self.gui_painter.after_submit(sync_point.clone()); + self.gui_painter.after_submit(&sync_point); if let Some(sp) = self.prev_sync_point.take() { self.context.wait_for(&sp, !0); diff --git a/examples/scene/main.rs b/examples/scene/main.rs index 7634a05c..6735038f 100644 --- a/examples/scene/main.rs +++ b/examples/scene/main.rs @@ -119,13 +119,10 @@ struct ConfigScene { struct Example { scene_path: PathBuf, scene_environment_map: String, - prev_temp_buffers: Vec, - prev_acceleration_structures: Vec, - prev_sync_point: Option, + pacer: blade_render::util::FramePacer, renderer: blade_render::Renderer, scene_load_task: Option, gui_painter: blade_egui::GuiPainter, - command_encoder: Option, asset_hub: blade_render::AssetHub, context: Arc, environment_map: Option>, @@ -198,37 +195,30 @@ impl Example { log::info!("Spinning up the renderer"); shader_task.join(); - let mut command_encoder = context.create_command_encoder(gpu::CommandEncoderDesc { - name: "main", - buffer_count: 2, - }); - command_encoder.start(); + let mut pacer = blade_render::util::FramePacer::new(&context); + let (command_encoder, _) = pacer.begin_frame(); let render_config = blade_render::RenderConfig { screen_size, surface_format, max_debug_lines: 1000, }; let renderer = blade_render::Renderer::new( - &mut command_encoder, + command_encoder, &context, shaders, &asset_hub.shaders, &render_config, ); - let sync_point = context.submit(&mut command_encoder); - + pacer.end_frame(&context); let gui_painter = blade_egui::GuiPainter::new(surface_format, &context); Self { scene_path: PathBuf::new(), scene_environment_map: String::new(), - prev_temp_buffers: Vec::new(), - prev_acceleration_structures: Vec::new(), - prev_sync_point: Some(sync_point), + pacer, renderer, scene_load_task: None, gui_painter, - command_encoder: Some(command_encoder), asset_hub, context, environment_map: None, @@ -289,12 +279,10 @@ impl Example { fn destroy(&mut self) { self.workers.clear(); - self.wait_for_previous_frame(); + self.pacer.destroy(&self.context); self.gui_painter.destroy(&self.context); self.renderer.destroy(&self.context); self.asset_hub.destroy(); - self.context - .destroy_command_encoder(self.command_encoder.take().unwrap()); } pub fn load_scene(&mut self, scene_path: &Path) { @@ -391,19 +379,6 @@ impl Example { log::info!("Saving scene to: {}", scene_path.display()); } - #[profiling::function] - fn wait_for_previous_frame(&mut self) { - if let Some(sp) = self.prev_sync_point.take() { - self.context.wait_for(&sp, !0); - } - for buffer in self.prev_temp_buffers.drain(..) { - self.context.destroy_buffer(buffer); - } - for accel_structure in self.prev_acceleration_structures.drain(..) { - self.context.destroy_acceleration_structure(accel_structure); - } - } - #[profiling::function] fn render( &mut self, @@ -416,7 +391,7 @@ impl Example { self.need_accumulation_reset |= self.renderer.hot_reload( &self.asset_hub, &self.context, - self.prev_sync_point.as_ref().unwrap(), + self.pacer.last_sync_point().unwrap(), ); } @@ -426,12 +401,11 @@ impl Example { let new_render_size = surface_config.size; if new_render_size != self.renderer.get_screen_size() { log::info!("Resizing to {}", new_render_size); - self.wait_for_previous_frame(); + self.pacer.wait_for_previous_frame(&self.context); self.context.resize(surface_config); } - let command_encoder = self.command_encoder.as_mut().unwrap(); - command_encoder.start(); + let (command_encoder, temp) = self.pacer.begin_frame(); if new_render_size != self.renderer.get_screen_size() { self.renderer .resize_screen(new_render_size, command_encoder, &self.context); @@ -441,9 +415,7 @@ impl Example { self.gui_painter .update_textures(command_encoder, gui_textures, &self.context); - let mut temp_buffers = Vec::new(); - let mut temp_acceleration_structures = Vec::new(); - self.asset_hub.flush(command_encoder, &mut temp_buffers); + self.asset_hub.flush(command_encoder, &mut temp.buffers); if let Some(ref task) = self.scene_load_task { if task.is_done() { @@ -464,8 +436,7 @@ impl Example { self.environment_map, &self.asset_hub, &self.context, - &mut temp_buffers, - &mut temp_acceleration_structures, + temp, ); self.have_objects_changed = false; } @@ -522,14 +493,8 @@ impl Example { } command_encoder.present(frame); - let sync_point = self.context.submit(command_encoder); - self.gui_painter.after_submit(sync_point.clone()); - - self.wait_for_previous_frame(); - self.prev_sync_point = Some(sync_point); - self.prev_temp_buffers.extend(temp_buffers); - self.prev_acceleration_structures - .extend(temp_acceleration_structures); + let sync_point = self.pacer.end_frame(&self.context); + self.gui_painter.after_submit(sync_point); } fn add_manipulation_gizmo(&mut self, obj_index: usize, ui: &mut egui::Ui) {