From b05e61f5c8ae61c9f3c7cc08cded53901ebbccfd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 21:07:54 +0200 Subject: Redesign `iced_wgpu` layering architecture --- wgpu/src/lib.rs | 578 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 570 insertions(+), 8 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index b00e5c3c..0e173e0a 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -22,8 +22,8 @@ )] #![forbid(rust_2018_idioms)] #![deny( - missing_debug_implementations, - missing_docs, + // missing_debug_implementations, + //missing_docs, unsafe_code, unused_results, rustdoc::broken_intra_doc_links @@ -37,13 +37,21 @@ pub mod window; #[cfg(feature = "geometry")] pub mod geometry; -mod backend; mod buffer; mod color; +mod engine; mod quad; mod text; mod triangle; +#[cfg(any(feature = "image", feature = "svg"))] +#[path = "image/mod.rs"] +mod image; + +#[cfg(not(any(feature = "image", feature = "svg")))] +#[path = "image/null.rs"] +mod image; + use buffer::Buffer; pub use iced_graphics as graphics; @@ -51,16 +59,570 @@ pub use iced_graphics::core; pub use wgpu; -pub use backend::Backend; -pub use layer::Layer; +pub use engine::Engine; +pub use layer::{Layer, LayerMut}; pub use primitive::Primitive; pub use settings::Settings; -#[cfg(any(feature = "image", feature = "svg"))] -mod image; +#[cfg(feature = "geometry")] +pub use geometry::Geometry; + +use crate::core::{ + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, +}; +use crate::graphics::text::{Editor, Paragraph}; +use crate::graphics::Viewport; + +use std::borrow::Cow; /// A [`wgpu`] graphics renderer for [`iced`]. /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs /// [`iced`]: https://github.com/iced-rs/iced -pub type Renderer = iced_graphics::Renderer; +#[allow(missing_debug_implementations)] +pub struct Renderer { + default_font: Font, + default_text_size: Pixels, + layers: layer::Stack, + + // TODO: Centralize all the image feature handling + #[cfg(any(feature = "svg", feature = "image"))] + image_cache: image::cache::Shared, +} + +impl Renderer { + pub fn new(settings: Settings, _engine: &Engine) -> Self { + Self { + default_font: settings.default_font, + default_text_size: settings.default_text_size, + layers: layer::Stack::new(), + + #[cfg(any(feature = "svg", feature = "image"))] + image_cache: _engine.image_cache().clone(), + } + } + + pub fn draw_primitive(&mut self, _primitive: Primitive) {} + + pub fn present>( + &mut self, + engine: &mut Engine, + device: &wgpu::Device, + queue: &wgpu::Queue, + encoder: &mut wgpu::CommandEncoder, + clear_color: Option, + format: wgpu::TextureFormat, + frame: &wgpu::TextureView, + viewport: &Viewport, + overlay: &[T], + ) { + let target_size = viewport.physical_size(); + let scale_factor = viewport.scale_factor() as f32; + let transformation = viewport.projection(); + + for line in overlay { + println!("{}", line.as_ref()); + } + + self.prepare( + engine, + device, + queue, + format, + encoder, + scale_factor, + target_size, + transformation, + ); + + self.render( + engine, + device, + encoder, + frame, + clear_color, + scale_factor, + target_size, + ); + } + + fn prepare( + &mut self, + engine: &mut Engine, + device: &wgpu::Device, + queue: &wgpu::Queue, + _format: wgpu::TextureFormat, + encoder: &mut wgpu::CommandEncoder, + scale_factor: f32, + target_size: Size, + transformation: Transformation, + ) { + for layer in self.layers.iter_mut() { + match layer { + LayerMut::Live(live) => { + if !live.quads.is_empty() { + engine.quad_pipeline.prepare_batch( + device, + encoder, + &mut engine.staging_belt, + &live.quads, + transformation, + scale_factor, + ); + } + + if !live.meshes.is_empty() { + engine.triangle_pipeline.prepare_batch( + device, + encoder, + &mut engine.staging_belt, + &live.meshes, + transformation + * Transformation::scale(scale_factor), + ); + } + + if !live.text.is_empty() { + engine.text_pipeline.prepare_batch( + device, + queue, + encoder, + &live.text, + live.bounds.unwrap_or(Rectangle::with_size( + Size::INFINITY, + )), + scale_factor, + target_size, + ); + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !live.images.is_empty() { + engine.image_pipeline.prepare( + device, + encoder, + &mut engine.staging_belt, + &live.images, + transformation, + scale_factor, + ); + } + } + LayerMut::Cached(mut cached) => { + if !cached.quads.is_empty() { + engine.quad_pipeline.prepare_cache( + device, + encoder, + &mut engine.staging_belt, + &mut cached.quads, + transformation, + scale_factor, + ); + } + + if !cached.meshes.is_empty() { + engine.triangle_pipeline.prepare_cache( + device, + encoder, + &mut engine.staging_belt, + &mut cached.meshes, + transformation + * Transformation::scale(scale_factor), + ); + } + + if !cached.text.is_empty() { + let bounds = cached + .bounds + .unwrap_or(Rectangle::with_size(Size::INFINITY)); + + engine.text_pipeline.prepare_cache( + device, + queue, + encoder, + &mut cached.text, + bounds, + scale_factor, + target_size, + ); + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !cached.images.is_empty() { + engine.image_pipeline.prepare( + device, + encoder, + &mut engine.staging_belt, + &cached.images, + transformation, + scale_factor, + ); + } + } + } + } + } + + fn render( + &mut self, + engine: &mut Engine, + device: &wgpu::Device, + encoder: &mut wgpu::CommandEncoder, + frame: &wgpu::TextureView, + clear_color: Option, + scale_factor: f32, + target_size: Size, + ) { + use std::mem::ManuallyDrop; + + let mut render_pass = ManuallyDrop::new(encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some("iced_wgpu render pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: frame, + resolve_target: None, + ops: wgpu::Operations { + load: match clear_color { + Some(background_color) => wgpu::LoadOp::Clear({ + let [r, g, b, a] = + graphics::color::pack(background_color) + .components(); + + wgpu::Color { + r: f64::from(r), + g: f64::from(g), + b: f64::from(b), + a: f64::from(a), + } + }), + None => wgpu::LoadOp::Load, + }, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }, + )); + + let mut quad_layer = 0; + let mut mesh_layer = 0; + let mut text_layer = 0; + + #[cfg(any(feature = "svg", feature = "image"))] + let mut image_layer = 0; + + // TODO: Can we avoid collecting here? + let layers: Vec<_> = self.layers.iter().collect(); + + for layer in &layers { + match layer { + Layer::Live(live) => { + let bounds = live + .bounds + .map(|bounds| bounds * scale_factor) + .map(Rectangle::snap) + .unwrap_or(Rectangle::with_size(target_size)); + + if !live.quads.is_empty() { + engine.quad_pipeline.render_batch( + quad_layer, + bounds, + &live.quads, + &mut render_pass, + ); + + quad_layer += 1; + } + + if !live.meshes.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + engine.triangle_pipeline.render_batch( + device, + encoder, + frame, + mesh_layer, + target_size, + &live.meshes, + bounds, + scale_factor, + ); + + mesh_layer += 1; + + render_pass = + ManuallyDrop::new(encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some("iced_wgpu render pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: frame, + 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, + }, + )); + } + + if !live.text.is_empty() { + engine.text_pipeline.render_batch( + text_layer, + bounds, + &mut render_pass, + ); + + text_layer += 1; + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !live.images.is_empty() { + engine.image_pipeline.render( + image_layer, + bounds, + &mut render_pass, + ); + + image_layer += 1; + } + } + Layer::Cached(cached) => { + let bounds = cached + .bounds + .map(|bounds| bounds * scale_factor) + .map(Rectangle::snap) + .unwrap_or(Rectangle::with_size(target_size)); + + if !cached.quads.is_empty() { + engine.quad_pipeline.render_cache( + &cached.quads, + bounds, + &mut render_pass, + ); + } + + if !cached.meshes.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + engine.triangle_pipeline.render_cache( + device, + encoder, + frame, + target_size, + &cached.meshes, + bounds, + scale_factor, + ); + + render_pass = + ManuallyDrop::new(encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some("iced_wgpu render pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: frame, + 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, + }, + )); + } + + if !cached.text.is_empty() { + engine.text_pipeline.render_cache( + &cached.text, + bounds, + &mut render_pass, + ); + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !cached.images.is_empty() { + engine.image_pipeline.render( + image_layer, + bounds, + &mut render_pass, + ); + + image_layer += 1; + } + } + } + } + + let _ = ManuallyDrop::into_inner(render_pass); + } +} + +impl core::Renderer for Renderer { + fn start_layer(&mut self, bounds: Rectangle) { + self.layers.push_clip(Some(bounds)); + } + + fn end_layer(&mut self, _bounds: Rectangle) { + self.layers.pop_clip(); + } + + fn start_transformation(&mut self, transformation: Transformation) { + self.layers.push_transformation(transformation); + } + + fn end_transformation(&mut self, _transformation: Transformation) { + self.layers.pop_transformation(); + } + + fn fill_quad( + &mut self, + quad: core::renderer::Quad, + background: impl Into, + ) { + self.layers.draw_quad(quad, background.into()); + } + + fn clear(&mut self) { + self.layers.clear(); + } +} + +impl core::text::Renderer for Renderer { + type Font = Font; + type Paragraph = Paragraph; + type Editor = Editor; + + const ICON_FONT: Font = Font::with_name("Iced-Icons"); + const CHECKMARK_ICON: char = '\u{f00c}'; + const ARROW_DOWN_ICON: char = '\u{e800}'; + + fn default_font(&self) -> Self::Font { + self.default_font + } + + fn default_size(&self) -> Pixels { + self.default_text_size + } + + fn load_font(&mut self, font: Cow<'static, [u8]>) { + graphics::text::font_system() + .write() + .expect("Write font system") + .load_font(font); + + // TODO: Invalidate buffer cache + } + + fn fill_paragraph( + &mut self, + text: &Self::Paragraph, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + self.layers + .draw_paragraph(text, position, color, clip_bounds); + } + + fn fill_editor( + &mut self, + editor: &Self::Editor, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + self.layers + .draw_editor(editor, position, color, clip_bounds); + } + + fn fill_text( + &mut self, + text: core::Text, + position: Point, + color: Color, + clip_bounds: Rectangle, + ) { + self.layers.draw_text(text, position, color, clip_bounds); + } +} + +#[cfg(feature = "image")] +impl core::image::Renderer for Renderer { + type Handle = core::image::Handle; + + fn measure_image(&self, handle: &Self::Handle) -> Size { + self.image_cache.lock().measure_image(handle) + } + + fn draw_image( + &mut self, + handle: Self::Handle, + filter_method: core::image::FilterMethod, + bounds: Rectangle, + ) { + self.layers.draw_image(handle, filter_method, bounds); + } +} + +#[cfg(feature = "svg")] +impl core::svg::Renderer for Renderer { + fn measure_svg(&self, handle: &core::svg::Handle) -> Size { + self.image_cache.lock().measure_svg(handle) + } + + fn draw_svg( + &mut self, + handle: core::svg::Handle, + color_filter: Option, + bounds: Rectangle, + ) { + self.layers.draw_svg(handle, color_filter, bounds); + } +} + +impl graphics::mesh::Renderer for Renderer { + fn draw_mesh(&mut self, mesh: graphics::Mesh) { + self.layers.draw_mesh(mesh); + } +} + +#[cfg(feature = "geometry")] +impl graphics::geometry::Renderer for Renderer { + type Geometry = Geometry; + type Frame = geometry::Frame; + + fn new_frame(&self, size: Size) -> Self::Frame { + geometry::Frame::new(size) + } + + fn draw_geometry(&mut self, geometry: Self::Geometry) { + match geometry { + Geometry::Live(layers) => { + for layer in layers { + self.layers.draw_layer(layer); + } + } + Geometry::Cached(layers) => { + for layer in layers.as_ref() { + self.layers.draw_cached_layer(layer); + } + } + } + } +} + +impl graphics::compositor::Default for crate::Renderer { + type Compositor = window::Compositor; +} -- cgit From 88b72de282441367092b07f8075eb931eff495ad Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 22:13:00 +0200 Subject: Implement preliminary cache grouping for mesh primitives Due to AA, it's very expensive to render every cached layer independently. --- wgpu/src/lib.rs | 95 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 34 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 0e173e0a..b9869583 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -315,9 +315,10 @@ impl Renderer { // TODO: Can we avoid collecting here? let layers: Vec<_> = self.layers.iter().collect(); + let mut i = 0; - for layer in &layers { - match layer { + while i < layers.len() { + match layers[i] { Layer::Live(live) => { let bounds = live .bounds @@ -393,32 +394,54 @@ impl Renderer { image_layer += 1; } + + i += 1; } - Layer::Cached(cached) => { - let bounds = cached - .bounds - .map(|bounds| bounds * scale_factor) - .map(Rectangle::snap) - .unwrap_or(Rectangle::with_size(target_size)); + Layer::Cached(_) => { + let group_len = layers[i..] + .iter() + .position(|layer| matches!(layer, Layer::Live(_))) + .unwrap_or(layers.len()); - if !cached.quads.is_empty() { - engine.quad_pipeline.render_cache( - &cached.quads, - bounds, - &mut render_pass, - ); + let group = layers[i..i + group_len].iter().map(|layer| { + let Layer::Cached(cached) = layer else { + unreachable!() + }; + + let bounds = cached + .bounds + .map(|bounds| bounds * scale_factor) + .map(Rectangle::snap) + .unwrap_or(Rectangle::with_size(target_size)); + + (cached, bounds) + }); + + for (cached, bounds) in group.clone() { + if !cached.quads.is_empty() { + engine.quad_pipeline.render_cache( + &cached.quads, + bounds, + &mut render_pass, + ); + } } - if !cached.meshes.is_empty() { + let group_has_meshes = group + .clone() + .any(|(cached, _)| !cached.meshes.is_empty()); + + if group_has_meshes { let _ = ManuallyDrop::into_inner(render_pass); - engine.triangle_pipeline.render_cache( + engine.triangle_pipeline.render_cache_group( device, encoder, frame, target_size, - &cached.meshes, - bounds, + group.clone().map(|(cached, bounds)| { + (&cached.meshes, bounds) + }), scale_factor, ); @@ -443,24 +466,28 @@ impl Renderer { )); } - if !cached.text.is_empty() { - engine.text_pipeline.render_cache( - &cached.text, - bounds, - &mut render_pass, - ); + for (cached, bounds) in group { + if !cached.text.is_empty() { + engine.text_pipeline.render_cache( + &cached.text, + bounds, + &mut render_pass, + ); + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !cached.images.is_empty() { + engine.image_pipeline.render( + image_layer, + bounds, + &mut render_pass, + ); + + image_layer += 1; + } } - #[cfg(any(feature = "svg", feature = "image"))] - if !cached.images.is_empty() { - engine.image_pipeline.render( - image_layer, - bounds, - &mut render_pass, - ); - - image_layer += 1; - } + i += group_len; } } } -- cgit From 1672b0d619a87fcbe8aa6c6699ac6b20f9a6181b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 3 Apr 2024 22:57:28 +0200 Subject: Reintroduce debug overlay in `iced_wgpu` --- wgpu/src/lib.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index b9869583..2d023d8b 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -120,9 +120,7 @@ impl Renderer { let scale_factor = viewport.scale_factor() as f32; let transformation = viewport.projection(); - for line in overlay { - println!("{}", line.as_ref()); - } + self.draw_overlay(overlay, viewport); self.prepare( engine, @@ -494,6 +492,50 @@ impl Renderer { let _ = ManuallyDrop::into_inner(render_pass); } + + fn draw_overlay( + &mut self, + overlay: &[impl AsRef], + viewport: &Viewport, + ) { + use crate::core::alignment; + use crate::core::text::Renderer as _; + use crate::core::Renderer as _; + use crate::core::Vector; + + self.with_layer( + Rectangle::with_size(viewport.logical_size()), + |renderer| { + for (i, line) in overlay.iter().enumerate() { + let text = crate::core::Text { + content: line.as_ref().to_owned(), + bounds: viewport.logical_size(), + size: Pixels(20.0), + line_height: core::text::LineHeight::default(), + font: Font::MONOSPACE, + horizontal_alignment: alignment::Horizontal::Left, + vertical_alignment: alignment::Vertical::Top, + shaping: core::text::Shaping::Basic, + }; + + renderer.fill_text( + text.clone(), + Point::new(11.0, 11.0 + 25.0 * i as f32), + Color::new(0.9, 0.9, 0.9, 1.0), + Rectangle::with_size(Size::INFINITY), + ); + + renderer.fill_text( + text, + Point::new(11.0, 11.0 + 25.0 * i as f32) + + Vector::new(-1.0, -1.0), + Color::BLACK, + Rectangle::with_size(Size::INFINITY), + ); + } + }, + ); + } } impl core::Renderer for Renderer { -- cgit From 394e599c3a096b036aabdd6f850c4a7c518d44fa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Apr 2024 00:40:39 +0200 Subject: Fix layer transformations --- wgpu/src/lib.rs | 158 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 85 insertions(+), 73 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 2d023d8b..4705cfa0 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -116,32 +116,10 @@ impl Renderer { viewport: &Viewport, overlay: &[T], ) { - let target_size = viewport.physical_size(); - let scale_factor = viewport.scale_factor() as f32; - let transformation = viewport.projection(); - self.draw_overlay(overlay, viewport); - self.prepare( - engine, - device, - queue, - format, - encoder, - scale_factor, - target_size, - transformation, - ); - - self.render( - engine, - device, - encoder, - frame, - clear_color, - scale_factor, - target_size, - ); + self.prepare(engine, device, queue, format, encoder, viewport); + self.render(engine, device, encoder, frame, clear_color, viewport); } fn prepare( @@ -151,10 +129,10 @@ impl Renderer { queue: &wgpu::Queue, _format: wgpu::TextureFormat, encoder: &mut wgpu::CommandEncoder, - scale_factor: f32, - target_size: Size, - transformation: Transformation, + viewport: &Viewport, ) { + let scale_factor = viewport.scale_factor() as f32; + for layer in self.layers.iter_mut() { match layer { LayerMut::Live(live) => { @@ -164,7 +142,7 @@ impl Renderer { encoder, &mut engine.staging_belt, &live.quads, - transformation, + viewport.projection(), scale_factor, ); } @@ -175,7 +153,7 @@ impl Renderer { encoder, &mut engine.staging_belt, &live.meshes, - transformation + viewport.projection() * Transformation::scale(scale_factor), ); } @@ -187,10 +165,11 @@ impl Renderer { encoder, &live.text, live.bounds.unwrap_or(Rectangle::with_size( - Size::INFINITY, + viewport.logical_size(), )), - scale_factor, - target_size, + live.transformation + * Transformation::scale(scale_factor), + viewport.physical_size(), ); } @@ -201,38 +180,46 @@ impl Renderer { encoder, &mut engine.staging_belt, &live.images, - transformation, + viewport.projection(), scale_factor, ); } } - LayerMut::Cached(mut cached) => { + LayerMut::Cached(layer_transformation, mut cached) => { if !cached.quads.is_empty() { engine.quad_pipeline.prepare_cache( device, encoder, &mut engine.staging_belt, &mut cached.quads, - transformation, + viewport.projection(), scale_factor, ); } if !cached.meshes.is_empty() { + let transformation = + Transformation::scale(scale_factor) + * layer_transformation; + engine.triangle_pipeline.prepare_cache( device, encoder, &mut engine.staging_belt, &mut cached.meshes, - transformation - * Transformation::scale(scale_factor), + viewport.projection(), + transformation, ); } if !cached.text.is_empty() { - let bounds = cached - .bounds - .unwrap_or(Rectangle::with_size(Size::INFINITY)); + let bounds = cached.bounds.unwrap_or( + Rectangle::with_size(viewport.logical_size()), + ); + + let transformation = + Transformation::scale(scale_factor) + * layer_transformation; engine.text_pipeline.prepare_cache( device, @@ -240,8 +227,8 @@ impl Renderer { encoder, &mut cached.text, bounds, - scale_factor, - target_size, + transformation, + viewport.physical_size(), ); } @@ -252,7 +239,7 @@ impl Renderer { encoder, &mut engine.staging_belt, &cached.images, - transformation, + viewport.projection(), scale_factor, ); } @@ -268,8 +255,7 @@ impl Renderer { encoder: &mut wgpu::CommandEncoder, frame: &wgpu::TextureView, clear_color: Option, - scale_factor: f32, - target_size: Size, + viewport: &Viewport, ) { use std::mem::ManuallyDrop; @@ -312,22 +298,37 @@ impl Renderer { let mut image_layer = 0; // TODO: Can we avoid collecting here? + let scale_factor = viewport.scale_factor() as f32; + let screen_bounds = Rectangle::with_size(viewport.logical_size()); + let physical_bounds = Rectangle::::from(Rectangle::with_size( + viewport.physical_size(), + )); + let layers: Vec<_> = self.layers.iter().collect(); let mut i = 0; + // println!("RENDER"); + while i < layers.len() { match layers[i] { Layer::Live(live) => { - let bounds = live - .bounds - .map(|bounds| bounds * scale_factor) + let layer_transformation = + Transformation::scale(scale_factor) + * live.transformation; + + let layer_bounds = live.bounds.unwrap_or(screen_bounds); + + let Some(physical_bounds) = physical_bounds + .intersection(&(layer_bounds * layer_transformation)) .map(Rectangle::snap) - .unwrap_or(Rectangle::with_size(target_size)); + else { + continue; + }; if !live.quads.is_empty() { engine.quad_pipeline.render_batch( quad_layer, - bounds, + physical_bounds, &live.quads, &mut render_pass, ); @@ -336,6 +337,7 @@ impl Renderer { } if !live.meshes.is_empty() { + // println!("LIVE PASS"); let _ = ManuallyDrop::into_inner(render_pass); engine.triangle_pipeline.render_batch( @@ -343,10 +345,10 @@ impl Renderer { encoder, frame, mesh_layer, - target_size, + viewport.physical_size(), &live.meshes, - bounds, - scale_factor, + physical_bounds, + &layer_transformation, ); mesh_layer += 1; @@ -375,7 +377,7 @@ impl Renderer { if !live.text.is_empty() { engine.text_pipeline.render_batch( text_layer, - bounds, + physical_bounds, &mut render_pass, ); @@ -386,7 +388,7 @@ impl Renderer { if !live.images.is_empty() { engine.image_pipeline.render( image_layer, - bounds, + physical_bounds, &mut render_pass, ); @@ -395,25 +397,35 @@ impl Renderer { i += 1; } - Layer::Cached(_) => { + Layer::Cached(_, _) => { let group_len = layers[i..] .iter() .position(|layer| matches!(layer, Layer::Live(_))) - .unwrap_or(layers.len()); - - let group = layers[i..i + group_len].iter().map(|layer| { - let Layer::Cached(cached) = layer else { - unreachable!() - }; - - let bounds = cached - .bounds - .map(|bounds| bounds * scale_factor) - .map(Rectangle::snap) - .unwrap_or(Rectangle::with_size(target_size)); - - (cached, bounds) - }); + .unwrap_or(layers.len() - i); + + let group = + layers[i..i + group_len].iter().filter_map(|layer| { + let Layer::Cached(transformation, cached) = layer + else { + unreachable!() + }; + + let physical_bounds = cached + .bounds + .and_then(|bounds| { + physical_bounds.intersection( + &(bounds + * *transformation + * Transformation::scale( + scale_factor, + )), + ) + }) + .unwrap_or(physical_bounds) + .snap(); + + Some((cached, physical_bounds)) + }); for (cached, bounds) in group.clone() { if !cached.quads.is_empty() { @@ -430,17 +442,17 @@ impl Renderer { .any(|(cached, _)| !cached.meshes.is_empty()); if group_has_meshes { + // println!("CACHE PASS"); let _ = ManuallyDrop::into_inner(render_pass); engine.triangle_pipeline.render_cache_group( device, encoder, frame, - target_size, + viewport.physical_size(), group.clone().map(|(cached, bounds)| { (&cached.meshes, bounds) }), - scale_factor, ); render_pass = -- cgit From 6d3e1d835e1688fbc58622a03a784ed25ed3f0e1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 5 Apr 2024 23:59:21 +0200 Subject: Decouple caching from layering and simplify everything --- wgpu/src/lib.rs | 444 +++++++++++++++++--------------------------------------- 1 file changed, 132 insertions(+), 312 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 4705cfa0..d632919f 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -60,7 +60,7 @@ pub use iced_graphics::core; pub use wgpu; pub use engine::Engine; -pub use layer::{Layer, LayerMut}; +pub use layer::Layer; pub use primitive::Primitive; pub use settings::Settings; @@ -85,6 +85,9 @@ pub struct Renderer { default_text_size: Pixels, layers: layer::Stack, + triangle_storage: triangle::Storage, + text_storage: text::Storage, + // TODO: Centralize all the image feature handling #[cfg(any(feature = "svg", feature = "image"))] image_cache: image::cache::Shared, @@ -97,6 +100,9 @@ impl Renderer { default_text_size: settings.default_text_size, layers: layer::Stack::new(), + triangle_storage: triangle::Storage::new(), + text_storage: text::Storage::new(), + #[cfg(any(feature = "svg", feature = "image"))] image_cache: _engine.image_cache().clone(), } @@ -117,9 +123,11 @@ impl Renderer { overlay: &[T], ) { self.draw_overlay(overlay, viewport); - self.prepare(engine, device, queue, format, encoder, viewport); self.render(engine, device, encoder, frame, clear_color, viewport); + + self.triangle_storage.trim(); + self.text_storage.trim(); } fn prepare( @@ -134,116 +142,51 @@ impl Renderer { let scale_factor = viewport.scale_factor() as f32; for layer in self.layers.iter_mut() { - match layer { - LayerMut::Live(live) => { - if !live.quads.is_empty() { - engine.quad_pipeline.prepare_batch( - device, - encoder, - &mut engine.staging_belt, - &live.quads, - viewport.projection(), - scale_factor, - ); - } - - if !live.meshes.is_empty() { - engine.triangle_pipeline.prepare_batch( - device, - encoder, - &mut engine.staging_belt, - &live.meshes, - viewport.projection() - * Transformation::scale(scale_factor), - ); - } - - if !live.text.is_empty() { - engine.text_pipeline.prepare_batch( - device, - queue, - encoder, - &live.text, - live.bounds.unwrap_or(Rectangle::with_size( - viewport.logical_size(), - )), - live.transformation - * Transformation::scale(scale_factor), - viewport.physical_size(), - ); - } - - #[cfg(any(feature = "svg", feature = "image"))] - if !live.images.is_empty() { - engine.image_pipeline.prepare( - device, - encoder, - &mut engine.staging_belt, - &live.images, - viewport.projection(), - scale_factor, - ); - } - } - LayerMut::Cached(layer_transformation, mut cached) => { - if !cached.quads.is_empty() { - engine.quad_pipeline.prepare_cache( - device, - encoder, - &mut engine.staging_belt, - &mut cached.quads, - viewport.projection(), - scale_factor, - ); - } - - if !cached.meshes.is_empty() { - let transformation = - Transformation::scale(scale_factor) - * layer_transformation; - - engine.triangle_pipeline.prepare_cache( - device, - encoder, - &mut engine.staging_belt, - &mut cached.meshes, - viewport.projection(), - transformation, - ); - } - - if !cached.text.is_empty() { - let bounds = cached.bounds.unwrap_or( - Rectangle::with_size(viewport.logical_size()), - ); - - let transformation = - Transformation::scale(scale_factor) - * layer_transformation; - - engine.text_pipeline.prepare_cache( - device, - queue, - encoder, - &mut cached.text, - bounds, - transformation, - viewport.physical_size(), - ); - } - - #[cfg(any(feature = "svg", feature = "image"))] - if !cached.images.is_empty() { - engine.image_pipeline.prepare( - device, - encoder, - &mut engine.staging_belt, - &cached.images, - viewport.projection(), - scale_factor, - ); - } - } + if !layer.quads.is_empty() { + engine.quad_pipeline.prepare( + device, + encoder, + &mut engine.staging_belt, + &layer.quads, + viewport.projection(), + scale_factor, + ); + } + + if !layer.triangles.is_empty() { + engine.triangle_pipeline.prepare( + device, + encoder, + &mut engine.staging_belt, + &mut self.triangle_storage, + &layer.triangles, + viewport.projection() * Transformation::scale(scale_factor), + ); + } + + if !layer.text.is_empty() { + engine.text_pipeline.prepare( + device, + queue, + encoder, + &mut self.text_storage, + &layer.text, + layer.bounds, + Transformation::scale(scale_factor), + viewport.physical_size(), + ); + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !layer.images.is_empty() { + engine.image_pipeline.prepare( + device, + encoder, + &mut engine.staging_belt, + &layer.images, + viewport.projection(), + scale_factor, + ); } } } @@ -297,208 +240,87 @@ impl Renderer { #[cfg(any(feature = "svg", feature = "image"))] let mut image_layer = 0; - // TODO: Can we avoid collecting here? let scale_factor = viewport.scale_factor() as f32; - let screen_bounds = Rectangle::with_size(viewport.logical_size()); let physical_bounds = Rectangle::::from(Rectangle::with_size( viewport.physical_size(), )); - let layers: Vec<_> = self.layers.iter().collect(); - let mut i = 0; + let scale = Transformation::scale(scale_factor); - // println!("RENDER"); + for layer in self.layers.iter() { + let Some(physical_bounds) = + physical_bounds.intersection(&(layer.bounds * scale)) + else { + continue; + }; - while i < layers.len() { - match layers[i] { - Layer::Live(live) => { - let layer_transformation = - Transformation::scale(scale_factor) - * live.transformation; + let scissor_rect = physical_bounds.snap(); - let layer_bounds = live.bounds.unwrap_or(screen_bounds); + if !layer.quads.is_empty() { + engine.quad_pipeline.render( + quad_layer, + scissor_rect, + &layer.quads, + &mut render_pass, + ); - let Some(physical_bounds) = physical_bounds - .intersection(&(layer_bounds * layer_transformation)) - .map(Rectangle::snap) - else { - continue; - }; + quad_layer += 1; + } - if !live.quads.is_empty() { - engine.quad_pipeline.render_batch( - quad_layer, - physical_bounds, - &live.quads, - &mut render_pass, - ); - - quad_layer += 1; - } - - if !live.meshes.is_empty() { - // println!("LIVE PASS"); - let _ = ManuallyDrop::into_inner(render_pass); - - engine.triangle_pipeline.render_batch( - device, - encoder, - frame, - mesh_layer, - viewport.physical_size(), - &live.meshes, - physical_bounds, - &layer_transformation, - ); - - mesh_layer += 1; - - render_pass = - ManuallyDrop::new(encoder.begin_render_pass( - &wgpu::RenderPassDescriptor { - label: Some("iced_wgpu render pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: frame, - 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, - }, - )); - } - - if !live.text.is_empty() { - engine.text_pipeline.render_batch( - text_layer, - physical_bounds, - &mut render_pass, - ); - - text_layer += 1; - } - - #[cfg(any(feature = "svg", feature = "image"))] - if !live.images.is_empty() { - engine.image_pipeline.render( - image_layer, - physical_bounds, - &mut render_pass, - ); - - image_layer += 1; - } - - i += 1; - } - Layer::Cached(_, _) => { - let group_len = layers[i..] - .iter() - .position(|layer| matches!(layer, Layer::Live(_))) - .unwrap_or(layers.len() - i); - - let group = - layers[i..i + group_len].iter().filter_map(|layer| { - let Layer::Cached(transformation, cached) = layer - else { - unreachable!() - }; - - let physical_bounds = cached - .bounds - .and_then(|bounds| { - physical_bounds.intersection( - &(bounds - * *transformation - * Transformation::scale( - scale_factor, - )), - ) - }) - .unwrap_or(physical_bounds) - .snap(); - - Some((cached, physical_bounds)) - }); - - for (cached, bounds) in group.clone() { - if !cached.quads.is_empty() { - engine.quad_pipeline.render_cache( - &cached.quads, - bounds, - &mut render_pass, - ); - } - } - - let group_has_meshes = group - .clone() - .any(|(cached, _)| !cached.meshes.is_empty()); - - if group_has_meshes { - // println!("CACHE PASS"); - let _ = ManuallyDrop::into_inner(render_pass); - - engine.triangle_pipeline.render_cache_group( - device, - encoder, - frame, - viewport.physical_size(), - group.clone().map(|(cached, bounds)| { - (&cached.meshes, bounds) - }), - ); - - render_pass = - ManuallyDrop::new(encoder.begin_render_pass( - &wgpu::RenderPassDescriptor { - label: Some("iced_wgpu render pass"), - color_attachments: &[Some( - wgpu::RenderPassColorAttachment { - view: frame, - 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, + if !layer.triangles.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + mesh_layer += engine.triangle_pipeline.render( + device, + encoder, + frame, + &self.triangle_storage, + mesh_layer, + &layer.triangles, + viewport.physical_size(), + physical_bounds, + scale, + ); + + render_pass = ManuallyDrop::new(encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some("iced_wgpu render pass"), + color_attachments: &[Some( + wgpu::RenderPassColorAttachment { + view: frame, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, }, - )); - } - - for (cached, bounds) in group { - if !cached.text.is_empty() { - engine.text_pipeline.render_cache( - &cached.text, - bounds, - &mut render_pass, - ); - } - - #[cfg(any(feature = "svg", feature = "image"))] - if !cached.images.is_empty() { - engine.image_pipeline.render( - image_layer, - bounds, - &mut render_pass, - ); - - image_layer += 1; - } - } - - i += group_len; - } + }, + )], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }, + )); + } + + if !layer.text.is_empty() { + text_layer += engine.text_pipeline.render( + &self.text_storage, + text_layer, + &layer.text, + scissor_rect, + &mut render_pass, + ); + } + + #[cfg(any(feature = "svg", feature = "image"))] + if !layer.images.is_empty() { + engine.image_pipeline.render( + image_layer, + scissor_rect, + &mut render_pass, + ); + + image_layer += 1; } } @@ -552,7 +374,7 @@ impl Renderer { impl core::Renderer for Renderer { fn start_layer(&mut self, bounds: Rectangle) { - self.layers.push_clip(Some(bounds)); + self.layers.push_clip(bounds); } fn end_layer(&mut self, _bounds: Rectangle) { @@ -690,15 +512,13 @@ impl graphics::geometry::Renderer for Renderer { fn draw_geometry(&mut self, geometry: Self::Geometry) { match geometry { - Geometry::Live(layers) => { - for layer in layers { - self.layers.draw_layer(layer); - } + Geometry::Live { meshes, text } => { + self.layers.draw_mesh_group(meshes); + self.layers.draw_text_group(text); } - Geometry::Cached(layers) => { - for layer in layers.as_ref() { - self.layers.draw_cached_layer(layer); - } + Geometry::Cached(cache) => { + self.layers.draw_mesh_cache(cache.meshes); + self.layers.draw_text_cache(cache.text); } } } -- cgit From 441aac25995290a83162a4728f22492ff69a5f4d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 6 Apr 2024 03:06:40 +0200 Subject: Avoid generating empty caches in `iced_wgpu` --- wgpu/src/lib.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index d632919f..dfc1aad4 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -517,8 +517,13 @@ impl graphics::geometry::Renderer for Renderer { self.layers.draw_text_group(text); } Geometry::Cached(cache) => { - self.layers.draw_mesh_cache(cache.meshes); - self.layers.draw_text_cache(cache.text); + if let Some(meshes) = cache.meshes { + self.layers.draw_mesh_cache(meshes); + } + + if let Some(text) = cache.text { + self.layers.draw_text_cache(text); + } } } } -- cgit From 5cd98f069dea8720bca7748d6c12fa410cbe79b5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 12:42:12 +0200 Subject: Use built-in `[lints]` table in `Cargo.toml` --- wgpu/src/lib.rs | 8 -------- 1 file changed, 8 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index b00e5c3c..2cb9cf04 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -20,14 +20,6 @@ #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] -#![forbid(rust_2018_idioms)] -#![deny( - missing_debug_implementations, - missing_docs, - unsafe_code, - unused_results, - rustdoc::broken_intra_doc_links -)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] pub mod layer; pub mod primitive; -- cgit From 288f62bfb691a91e01b9ddbce9dbdc560ee9036a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 7 Apr 2024 18:45:30 +0200 Subject: Share `msaa::Blit` texture between multiple windows --- wgpu/src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index ccad08d5..030bcade 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -117,7 +117,7 @@ impl Renderer { ) { self.draw_overlay(overlay, viewport); self.prepare(engine, device, queue, format, encoder, viewport); - self.render(engine, device, encoder, frame, clear_color, viewport); + self.render(engine, encoder, frame, clear_color, viewport); self.triangle_storage.trim(); self.text_storage.trim(); @@ -153,7 +153,8 @@ impl Renderer { &mut engine.staging_belt, &mut self.triangle_storage, &layer.triangles, - viewport.projection() * Transformation::scale(scale_factor), + Transformation::scale(scale_factor), + viewport.physical_size(), ); } @@ -187,7 +188,6 @@ impl Renderer { fn render( &mut self, engine: &mut Engine, - device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, frame: &wgpu::TextureView, clear_color: Option, @@ -264,13 +264,11 @@ impl Renderer { let _ = ManuallyDrop::into_inner(render_pass); mesh_layer += engine.triangle_pipeline.render( - device, encoder, frame, &self.triangle_storage, mesh_layer, &layer.triangles, - viewport.physical_size(), physical_bounds, scale, ); -- cgit From d922b478156488a7bc03c6e791e05c040d702634 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 Apr 2024 15:04:35 +0200 Subject: Reintroduce support for custom primitives in `iced_wgpu` --- wgpu/src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 030bcade..0580399d 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -101,8 +101,6 @@ impl Renderer { } } - pub fn draw_primitive(&mut self, _primitive: Primitive) {} - pub fn present>( &mut self, engine: &mut Engine, @@ -158,6 +156,19 @@ impl Renderer { ); } + if !layer.primitives.is_empty() { + for instance in &layer.primitives { + instance.primitive.prepare( + device, + queue, + engine.format, + &mut engine.primitive_storage, + &instance.bounds, + viewport, + ); + } + } + if !layer.text.is_empty() { engine.text_pipeline.prepare( device, @@ -247,7 +258,9 @@ impl Renderer { continue; }; - let scissor_rect = physical_bounds.snap(); + let Some(scissor_rect) = physical_bounds.snap() else { + continue; + }; if !layer.quads.is_empty() { engine.quad_pipeline.render( @@ -293,6 +306,43 @@ impl Renderer { )); } + if !layer.primitives.is_empty() { + let _ = ManuallyDrop::into_inner(render_pass); + + for instance in &layer.primitives { + if let Some(clip_bounds) = (instance.bounds * scale) + .intersection(&physical_bounds) + .and_then(Rectangle::snap) + { + instance.primitive.render( + encoder, + &engine.primitive_storage, + frame, + &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, + 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, + }, + )); + } + if !layer.text.is_empty() { text_layer += engine.text_pipeline.render( &self.text_storage, @@ -520,6 +570,12 @@ impl graphics::geometry::Renderer for Renderer { } } +impl primitive::Renderer for Renderer { + fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive) { + self.layers.draw_primitive(bounds, Box::new(primitive)); + } +} + impl graphics::compositor::Default for crate::Renderer { type Compositor = window::Compositor; } -- cgit From 2c6fd9ac14c5d270e05b97b7a7fab811d25834c4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 Apr 2024 15:35:54 +0200 Subject: Make arguments of `Renderer::new` explicit in `iced_wgpu` --- wgpu/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 0580399d..b1ae4e89 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -87,10 +87,14 @@ pub struct Renderer { } impl Renderer { - pub fn new(settings: Settings, _engine: &Engine) -> Self { + pub fn new( + _engine: &Engine, + default_font: Font, + default_text_size: Pixels, + ) -> Self { Self { - default_font: settings.default_font, - default_text_size: settings.default_text_size, + default_font, + default_text_size, layers: layer::Stack::new(), triangle_storage: triangle::Storage::new(), -- cgit From 6ad5bb3597f640ac329801adf735d633bf0a512f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 9 Apr 2024 22:25:16 +0200 Subject: Port `iced_tiny_skia` to new layering architecture --- wgpu/src/lib.rs | 58 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 27 deletions(-) (limited to 'wgpu/src/lib.rs') diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index b1ae4e89..178522de 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -66,8 +66,6 @@ use crate::core::{ use crate::graphics::text::{Editor, Paragraph}; use crate::graphics::Viewport; -use std::borrow::Cow; - /// A [`wgpu`] graphics renderer for [`iced`]. /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs @@ -422,7 +420,7 @@ impl core::Renderer for Renderer { self.layers.push_clip(bounds); } - fn end_layer(&mut self, _bounds: Rectangle) { + fn end_layer(&mut self) { self.layers.pop_clip(); } @@ -430,7 +428,7 @@ impl core::Renderer for Renderer { self.layers.push_transformation(transformation); } - fn end_transformation(&mut self, _transformation: Transformation) { + fn end_transformation(&mut self) { self.layers.pop_transformation(); } @@ -439,7 +437,8 @@ impl core::Renderer for Renderer { quad: core::renderer::Quad, background: impl Into, ) { - self.layers.draw_quad(quad, background.into()); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_quad(quad, background.into(), transformation); } fn clear(&mut self) { @@ -464,15 +463,6 @@ impl core::text::Renderer for Renderer { self.default_text_size } - fn load_font(&mut self, font: Cow<'static, [u8]>) { - graphics::text::font_system() - .write() - .expect("Write font system") - .load_font(font); - - // TODO: Invalidate buffer cache - } - fn fill_paragraph( &mut self, text: &Self::Paragraph, @@ -480,8 +470,15 @@ impl core::text::Renderer for Renderer { color: Color, clip_bounds: Rectangle, ) { - self.layers - .draw_paragraph(text, position, color, clip_bounds); + let (layer, transformation) = self.layers.current_mut(); + + layer.draw_paragraph( + text, + position, + color, + clip_bounds, + transformation, + ); } fn fill_editor( @@ -491,8 +488,8 @@ impl core::text::Renderer for Renderer { color: Color, clip_bounds: Rectangle, ) { - self.layers - .draw_editor(editor, position, color, clip_bounds); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_editor(editor, position, color, clip_bounds, transformation); } fn fill_text( @@ -502,7 +499,8 @@ impl core::text::Renderer for Renderer { color: Color, clip_bounds: Rectangle, ) { - self.layers.draw_text(text, position, color, clip_bounds); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_text(text, position, color, clip_bounds, transformation); } } @@ -520,7 +518,8 @@ impl core::image::Renderer for Renderer { filter_method: core::image::FilterMethod, bounds: Rectangle, ) { - self.layers.draw_image(handle, filter_method, bounds); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_image(handle, filter_method, bounds, transformation); } } @@ -536,13 +535,15 @@ impl core::svg::Renderer for Renderer { color_filter: Option, bounds: Rectangle, ) { - self.layers.draw_svg(handle, color_filter, bounds); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_svg(handle, color_filter, bounds, transformation); } } impl graphics::mesh::Renderer for Renderer { fn draw_mesh(&mut self, mesh: graphics::Mesh) { - self.layers.draw_mesh(mesh); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_mesh(mesh, transformation); } } @@ -556,18 +557,20 @@ impl graphics::geometry::Renderer for Renderer { } fn draw_geometry(&mut self, geometry: Self::Geometry) { + let (layer, transformation) = self.layers.current_mut(); + match geometry { Geometry::Live { meshes, text } => { - self.layers.draw_mesh_group(meshes); - self.layers.draw_text_group(text); + layer.draw_mesh_group(meshes, transformation); + layer.draw_text_group(text, transformation); } Geometry::Cached(cache) => { if let Some(meshes) = cache.meshes { - self.layers.draw_mesh_cache(meshes); + layer.draw_mesh_cache(meshes, transformation); } if let Some(text) = cache.text { - self.layers.draw_text_cache(text); + layer.draw_text_cache(text, transformation); } } } @@ -576,7 +579,8 @@ impl graphics::geometry::Renderer for Renderer { impl primitive::Renderer for Renderer { fn draw_primitive(&mut self, bounds: Rectangle, primitive: impl Primitive) { - self.layers.draw_primitive(bounds, Box::new(primitive)); + let (layer, transformation) = self.layers.current_mut(); + layer.draw_primitive(bounds, Box::new(primitive), transformation); } } -- cgit