From 0d05ea56d3abbc58d2269369955f3fca18023706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 6 Sep 2025 21:17:38 +0200 Subject: [PATCH] Introduce lightweight `draw` for `wgpu::Primitive` --- wgpu/src/lib.rs | 60 ++++++++++++++++++++++++++--------------- wgpu/src/primitive.rs | 62 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 27 deletions(-) diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 0ff74524..db68748a 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -534,7 +534,6 @@ impl Renderer { if !layer.primitives.is_empty() { let render_span = debug::render(debug::Primitive::Shader); - let _ = ManuallyDrop::into_inner(render_pass); let primitive_storage = self .engine @@ -542,11 +541,30 @@ impl Renderer { .read() .expect("Read primitive storage"); + let mut need_render = Vec::new(); + for instance in &layer.primitives { if let Some(clip_bounds) = (instance.bounds * scale) .intersection(&physical_bounds) .and_then(Rectangle::snap) { + let drawn = instance.primitive.draw( + &primitive_storage, + &mut render_pass, + frame, + &clip_bounds, + ); + + if !drawn { + need_render.push((instance, clip_bounds)); + } + } + } + + if !need_render.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + for (instance, clip_bounds) in need_render { instance.primitive.render( &primitive_storage, encoder, @@ -554,29 +572,29 @@ impl Renderer { &clip_bounds, ); } + + render_pass = ManuallyDrop::new(encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some("iced_wgpu render pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: frame, + depth_slice: None, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + }, + )], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }, + )); } render_span.finish(); - - render_pass = ManuallyDrop::new(encoder.begin_render_pass( - &wgpu::RenderPassDescriptor { - label: Some("iced_wgpu render pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: frame, - depth_slice: None, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Load, - store: wgpu::StoreOp::Store, - }, - }, - )], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }, - )); } #[cfg(any(feature = "svg", feature = "image"))] diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index eef7cfe9..49081b60 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -42,14 +42,39 @@ pub trait Primitive: Debug + MaybeSend + MaybeSync + 'static { viewport: &Viewport, ); - /// Renders the [`Primitive`]. + /// Draws the [`Primitive`] in the given [`wgpu::RenderPass`]. + /// + /// When possible, this should be implement over [`render`](Self::render) + /// since reusing the existing render pass should be considerably more + /// efficient than issuing a completely new one. + /// + /// If you have complex composition needs, then you can leverage + /// [`render`](Self::render) by returning `false` here. + /// + /// By default, it does nothing and returns `false`. + fn draw( + &self, + _renderer: &Self::Renderer, + _render_pass: &mut wgpu::RenderPass<'_>, + _target: &wgpu::TextureView, + _clip_bounds: &Rectangle, + ) -> bool { + false + } + + /// Renders the [`Primitive`], using the given [`wgpu::CommandEncoder`]. + /// + /// This will only be called if [`draw`](Self::draw) returns `false`. + /// + /// By default, it does nothing. fn render( &self, - renderer: &Self::Renderer, - encoder: &mut wgpu::CommandEncoder, - target: &wgpu::TextureView, - clip_bounds: &Rectangle, - ); + _renderer: &Self::Renderer, + _encoder: &mut wgpu::CommandEncoder, + _target: &wgpu::TextureView, + _clip_bounds: &Rectangle, + ) { + } } pub(crate) trait Stored: @@ -65,6 +90,14 @@ pub(crate) trait Stored: viewport: &Viewport, ); + fn draw( + &self, + storage: &Storage, + render_pass: &mut wgpu::RenderPass<'_>, + target: &wgpu::TextureView, + clip_bounds: &Rectangle, + ) -> bool; + fn render( &self, storage: &Storage, @@ -105,6 +138,23 @@ impl Stored for BlackBox

{ .prepare(renderer, device, queue, bounds, viewport); } + fn draw( + &self, + storage: &Storage, + render_pass: &mut wgpu::RenderPass<'_>, + target: &wgpu::TextureView, + clip_bounds: &Rectangle, + ) -> bool { + let renderer = storage + .get::

() + .expect("renderer should be initialized") + .downcast_ref::() + .expect("renderer should have the proper type"); + + self.primitive + .draw(renderer, render_pass, target, clip_bounds) + } + fn render( &self, storage: &Storage,