From 1491a8a7340a21aebc4c7e00f6f98d1a34c943ee Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 23 Sep 2024 10:27:08 +0200 Subject: [PATCH 1/2] Render pass on paint callback has now static lifetime --- crates/eframe/src/web/web_painter_wgpu.rs | 11 +++++++++-- crates/egui-wgpu/src/renderer.rs | 16 +++++++++++----- crates/egui-wgpu/src/winit.rs | 11 +++++++++-- crates/egui_demo_app/src/apps/custom3d_wgpu.rs | 8 ++++---- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/crates/eframe/src/web/web_painter_wgpu.rs b/crates/eframe/src/web/web_painter_wgpu.rs index 7d845a969e5..bbf38e06a15 100644 --- a/crates/eframe/src/web/web_painter_wgpu.rs +++ b/crates/eframe/src/web/web_painter_wgpu.rs @@ -302,7 +302,7 @@ impl WebPainter for WebPainterWgpu { let frame_view = frame .texture .create_view(&wgpu::TextureViewDescriptor::default()); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[Some(wgpu::RenderPassColorAttachment { view: &frame_view, resolve_target: None, @@ -333,7 +333,14 @@ impl WebPainter for WebPainterWgpu { timestamp_writes: None, }); - renderer.render(&mut render_pass, clipped_primitives, &screen_descriptor); + // Forgetting the pass' lifetime means that we are no longer compile-time protected from + // runtime errors caused by accessing the parent encoder before the render pass is dropped. + // Since we don't pass it on to the renderer, we should be perfectly safe against this mistake here! + renderer.render( + &mut render_pass.forget_lifetime(), + clipped_primitives, + &screen_descriptor, + ); } Some(frame) diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index eda934944ea..4d4abf5caa8 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -115,7 +115,7 @@ pub trait CallbackTrait: Send + Sync { fn paint<'a>( &'a self, info: PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, + render_pass: &mut wgpu::RenderPass<'static>, callback_resources: &'a CallbackResources, ); } @@ -408,10 +408,16 @@ impl Renderer { } /// Executes the egui renderer onto an existing wgpu renderpass. - pub fn render<'rp>( - &'rp self, - render_pass: &mut wgpu::RenderPass<'rp>, - paint_jobs: &'rp [epaint::ClippedPrimitive], + /// + /// Note that the lifetime of `render_pass` is `'static` which requires a call to `wgpu::RenderPass::forget_lifetime`. + /// This allows users to pass resources that live outside of the callback resources to the render pass. + /// The render pass internally keeps all referenced resources alive as long as necessary. + /// The only consequence of `forget_lifetime` is that any operation on the parent encoder will cause a runtime error + /// instead of a compile time error. + pub fn render( + &self, + render_pass: &mut wgpu::RenderPass<'static>, + paint_jobs: &[epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) { crate::profile_function!(); diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 46db8821e02..26b3c86ae2c 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -627,7 +627,7 @@ impl Painter { (texture_view, Some(&frame_view)) }); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + let render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("egui_render"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { view, @@ -658,7 +658,14 @@ impl Painter { occlusion_query_set: None, }); - renderer.render(&mut render_pass, clipped_primitives, &screen_descriptor); + // Forgetting the pass' lifetime means that we are no longer compile-time protected from + // runtime errors caused by accessing the parent encoder before the render pass is dropped. + // Since we don't pass it on to the renderer, we should be perfectly safe against this mistake here! + renderer.render( + &mut render_pass.forget_lifetime(), + clipped_primitives, + &screen_descriptor, + ); } { diff --git a/crates/egui_demo_app/src/apps/custom3d_wgpu.rs b/crates/egui_demo_app/src/apps/custom3d_wgpu.rs index 27d5c3cac8d..11030a6727c 100644 --- a/crates/egui_demo_app/src/apps/custom3d_wgpu.rs +++ b/crates/egui_demo_app/src/apps/custom3d_wgpu.rs @@ -160,11 +160,11 @@ impl egui_wgpu::CallbackTrait for CustomTriangleCallback { Vec::new() } - fn paint<'a>( + fn paint( &self, _info: egui::PaintCallbackInfo, - render_pass: &mut wgpu::RenderPass<'a>, - resources: &'a egui_wgpu::CallbackResources, + render_pass: &mut wgpu::RenderPass<'static>, + resources: &egui_wgpu::CallbackResources, ) { let resources: &TriangleRenderResources = resources.get().unwrap(); resources.paint(render_pass); @@ -200,7 +200,7 @@ impl TriangleRenderResources { ); } - fn paint<'rp>(&'rp self, render_pass: &mut wgpu::RenderPass<'rp>) { + fn paint(&self, render_pass: &mut wgpu::RenderPass<'_>) { // Draw our triangle! render_pass.set_pipeline(&self.pipeline); render_pass.set_bind_group(0, &self.bind_group, &[]); From 5231e3cf682db11cec1d1b7fa41a46d274e84556 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Mon, 23 Sep 2024 10:38:55 +0200 Subject: [PATCH 2/2] doc comment improvement, remove unnecessary lifetime constraints --- crates/egui-wgpu/src/renderer.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index 4d4abf5caa8..a3fcd667f5f 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -112,11 +112,11 @@ pub trait CallbackTrait: Send + Sync { /// /// It is given access to the [`wgpu::RenderPass`] so that it can issue draw commands /// into the same [`wgpu::RenderPass`] that is used for all other egui elements. - fn paint<'a>( - &'a self, + fn paint( + &self, info: PaintCallbackInfo, render_pass: &mut wgpu::RenderPass<'static>, - callback_resources: &'a CallbackResources, + callback_resources: &CallbackResources, ); } @@ -409,7 +409,7 @@ impl Renderer { /// Executes the egui renderer onto an existing wgpu renderpass. /// - /// Note that the lifetime of `render_pass` is `'static` which requires a call to `wgpu::RenderPass::forget_lifetime`. + /// Note that the lifetime of `render_pass` is `'static` which requires a call to [`wgpu::RenderPass::forget_lifetime`]. /// This allows users to pass resources that live outside of the callback resources to the render pass. /// The render pass internally keeps all referenced resources alive as long as necessary. /// The only consequence of `forget_lifetime` is that any operation on the parent encoder will cause a runtime error