diff options
Diffstat (limited to 'wgpu/src/backend.rs')
| -rw-r--r-- | wgpu/src/backend.rs | 429 | 
1 files changed, 265 insertions, 164 deletions
| diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 6a299425..4a0c54f0 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -1,14 +1,13 @@ +use crate::core; +use crate::core::{Color, Font, Point, Size}; +use crate::graphics::backend; +use crate::graphics::color; +use crate::graphics::{Transformation, Viewport}; +use crate::primitive::{self, Primitive};  use crate::quad;  use crate::text;  use crate::triangle; -use crate::{Settings, Transformation}; - -use iced_graphics::backend; -use iced_graphics::font; -use iced_graphics::layer::Layer; -use iced_graphics::{Primitive, Viewport}; -use iced_native::alignment; -use iced_native::{Font, Size}; +use crate::{Layer, Settings};  #[cfg(feature = "tracing")]  use tracing::info_span; @@ -16,11 +15,13 @@ use tracing::info_span;  #[cfg(any(feature = "image", feature = "svg"))]  use crate::image; +use std::borrow::Cow; +  /// A [`wgpu`] graphics backend for [`iced`].  ///  /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs  /// [`iced`]: https://github.com/iced-rs/iced -#[derive(Debug)] +#[allow(missing_debug_implementations)]  pub struct Backend {      quad_pipeline: quad::Pipeline,      text_pipeline: text::Pipeline, @@ -29,6 +30,7 @@ pub struct Backend {      #[cfg(any(feature = "image", feature = "svg"))]      image_pipeline: image::Pipeline, +    default_font: Font,      default_text_size: f32,  } @@ -36,16 +38,11 @@ impl Backend {      /// Creates a new [`Backend`].      pub fn new(          device: &wgpu::Device, +        queue: &wgpu::Queue,          settings: Settings,          format: wgpu::TextureFormat,      ) -> Self { -        let text_pipeline = text::Pipeline::new( -            device, -            format, -            settings.default_font, -            settings.text_multithreading, -        ); - +        let text_pipeline = text::Pipeline::new(device, queue, format);          let quad_pipeline = quad::Pipeline::new(device, format);          let triangle_pipeline =              triangle::Pipeline::new(device, format, settings.antialiasing); @@ -61,6 +58,7 @@ impl Backend {              #[cfg(any(feature = "image", feature = "svg"))]              image_pipeline, +            default_font: settings.default_font,              default_text_size: settings.default_text_size,          }      } @@ -72,8 +70,9 @@ impl Backend {      pub fn present<T: AsRef<str>>(          &mut self,          device: &wgpu::Device, -        staging_belt: &mut wgpu::util::StagingBelt, +        queue: &wgpu::Queue,          encoder: &mut wgpu::CommandEncoder, +        clear_color: Option<Color>,          frame: &wgpu::TextureView,          primitives: &[Primitive],          viewport: &Viewport, @@ -90,180 +89,268 @@ impl Backend {          let mut layers = Layer::generate(primitives, viewport);          layers.push(Layer::overlay(overlay_text, viewport)); +        self.prepare( +            device, +            queue, +            encoder, +            scale_factor, +            transformation, +            &layers, +        ); + +        while !self.prepare_text( +            device, +            queue, +            scale_factor, +            target_size, +            &layers, +        ) {} + +        self.render( +            device, +            encoder, +            frame, +            clear_color, +            scale_factor, +            target_size, +            &layers, +        ); + +        self.quad_pipeline.end_frame(); +        self.text_pipeline.end_frame(); +        self.triangle_pipeline.end_frame(); + +        #[cfg(any(feature = "image", feature = "svg"))] +        self.image_pipeline.end_frame(); +    } + +    fn prepare_text( +        &mut self, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        scale_factor: f32, +        target_size: Size<u32>, +        layers: &[Layer<'_>], +    ) -> bool {          for layer in layers { -            self.flush( -                device, -                scale_factor, -                transformation, -                &layer, -                staging_belt, -                encoder, -                frame, -                target_size, -            ); +            let bounds = (layer.bounds * scale_factor).snap(); + +            if bounds.width < 1 || bounds.height < 1 { +                continue; +            } + +            if !layer.text.is_empty() +                && !self.text_pipeline.prepare( +                    device, +                    queue, +                    &layer.text, +                    layer.bounds, +                    scale_factor, +                    target_size, +                ) +            { +                return false; +            }          } -        #[cfg(any(feature = "image", feature = "svg"))] -        self.image_pipeline.trim_cache(device, encoder); +        true      } -    fn flush( +    fn prepare(          &mut self,          device: &wgpu::Device, +        queue: &wgpu::Queue, +        _encoder: &mut wgpu::CommandEncoder,          scale_factor: f32,          transformation: Transformation, -        layer: &Layer<'_>, -        staging_belt: &mut wgpu::util::StagingBelt, +        layers: &[Layer<'_>], +    ) { +        for layer in layers { +            let bounds = (layer.bounds * scale_factor).snap(); + +            if bounds.width < 1 || bounds.height < 1 { +                continue; +            } + +            if !layer.quads.is_empty() { +                self.quad_pipeline.prepare( +                    device, +                    queue, +                    &layer.quads, +                    transformation, +                    scale_factor, +                ); +            } + +            if !layer.meshes.is_empty() { +                let scaled = transformation +                    * Transformation::scale(scale_factor, scale_factor); + +                self.triangle_pipeline.prepare( +                    device, +                    queue, +                    &layer.meshes, +                    scaled, +                ); +            } + +            #[cfg(any(feature = "image", feature = "svg"))] +            { +                if !layer.images.is_empty() { +                    let scaled = transformation +                        * Transformation::scale(scale_factor, scale_factor); + +                    self.image_pipeline.prepare( +                        device, +                        queue, +                        _encoder, +                        &layer.images, +                        scaled, +                        scale_factor, +                    ); +                } +            } +        } +    } + +    fn render( +        &mut self, +        device: &wgpu::Device,          encoder: &mut wgpu::CommandEncoder,          target: &wgpu::TextureView, +        clear_color: Option<Color>, +        scale_factor: f32,          target_size: Size<u32>, +        layers: &[Layer<'_>],      ) { -        let bounds = (layer.bounds * scale_factor).snap(); +        use std::mem::ManuallyDrop; -        if bounds.width < 1 || bounds.height < 1 { -            return; -        } +        let mut quad_layer = 0; +        let mut triangle_layer = 0; +        #[cfg(any(feature = "image", feature = "svg"))] +        let mut image_layer = 0; +        let mut text_layer = 0; + +        let mut render_pass = ManuallyDrop::new(encoder.begin_render_pass( +            &wgpu::RenderPassDescriptor { +                label: Some("iced_wgpu::quad render pass"), +                color_attachments: &[Some(wgpu::RenderPassColorAttachment { +                    view: target, +                    resolve_target: None, +                    ops: wgpu::Operations { +                        load: match clear_color { +                            Some(background_color) => wgpu::LoadOp::Clear({ +                                let [r, g, b, a] = +                                    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: true, +                    }, +                })], +                depth_stencil_attachment: None, +            }, +        )); -        if !layer.quads.is_empty() { -            self.quad_pipeline.draw( -                device, -                staging_belt, -                encoder, -                &layer.quads, -                transformation, -                scale_factor, -                bounds, -                target, -            ); -        } +        for layer in layers { +            let bounds = (layer.bounds * scale_factor).snap(); -        if !layer.meshes.is_empty() { -            let scaled = transformation -                * Transformation::scale(scale_factor, scale_factor); - -            self.triangle_pipeline.draw( -                device, -                staging_belt, -                encoder, -                target, -                target_size, -                scaled, -                scale_factor, -                &layer.meshes, -            ); -        } +            if bounds.width < 1 || bounds.height < 1 { +                return; +            } -        #[cfg(any(feature = "image", feature = "svg"))] -        { -            if !layer.images.is_empty() { -                let scaled = transformation -                    * Transformation::scale(scale_factor, scale_factor); +            if !layer.quads.is_empty() { +                self.quad_pipeline.render( +                    quad_layer, +                    bounds, +                    &layer.quads, +                    &mut render_pass, +                ); + +                quad_layer += 1; +            } -                self.image_pipeline.draw( +            if !layer.meshes.is_empty() { +                let _ = ManuallyDrop::into_inner(render_pass); + +                self.triangle_pipeline.render(                      device, -                    staging_belt,                      encoder, -                    &layer.images, -                    scaled, -                    bounds,                      target, +                    triangle_layer, +                    target_size, +                    &layer.meshes,                      scale_factor,                  ); + +                triangle_layer += 1; + +                render_pass = ManuallyDrop::new(encoder.begin_render_pass( +                    &wgpu::RenderPassDescriptor { +                        label: Some("iced_wgpu::quad render pass"), +                        color_attachments: &[Some( +                            wgpu::RenderPassColorAttachment { +                                view: target, +                                resolve_target: None, +                                ops: wgpu::Operations { +                                    load: wgpu::LoadOp::Load, +                                    store: true, +                                }, +                            }, +                        )], +                        depth_stencil_attachment: None, +                    }, +                ));              } -        } -        if !layer.text.is_empty() { -            for text in layer.text.iter() { -                // Target physical coordinates directly to avoid blurry text -                let text = wgpu_glyph::Section { -                    // TODO: We `round` here to avoid rerasterizing text when -                    // its position changes slightly. This can make text feel a -                    // bit "jumpy". We may be able to do better once we improve -                    // our text rendering/caching pipeline. -                    screen_position: ( -                        (text.bounds.x * scale_factor).round(), -                        (text.bounds.y * scale_factor).round(), -                    ), -                    // TODO: Fix precision issues with some scale factors. -                    // -                    // The `ceil` here can cause some words to render on the -                    // same line when they should not. -                    // -                    // Ideally, `wgpu_glyph` should be able to compute layout -                    // using logical positions, and then apply the proper -                    // scaling when rendering. This would ensure that both -                    // measuring and rendering follow the same layout rules. -                    bounds: ( -                        (text.bounds.width * scale_factor).ceil(), -                        (text.bounds.height * scale_factor).ceil(), -                    ), -                    text: vec![wgpu_glyph::Text { -                        text: text.content, -                        scale: wgpu_glyph::ab_glyph::PxScale { -                            x: text.size * scale_factor, -                            y: text.size * scale_factor, -                        }, -                        font_id: self.text_pipeline.find_font(text.font), -                        extra: wgpu_glyph::Extra { -                            color: text.color, -                            z: 0.0, -                        }, -                    }], -                    layout: wgpu_glyph::Layout::default() -                        .h_align(match text.horizontal_alignment { -                            alignment::Horizontal::Left => { -                                wgpu_glyph::HorizontalAlign::Left -                            } -                            alignment::Horizontal::Center => { -                                wgpu_glyph::HorizontalAlign::Center -                            } -                            alignment::Horizontal::Right => { -                                wgpu_glyph::HorizontalAlign::Right -                            } -                        }) -                        .v_align(match text.vertical_alignment { -                            alignment::Vertical::Top => { -                                wgpu_glyph::VerticalAlign::Top -                            } -                            alignment::Vertical::Center => { -                                wgpu_glyph::VerticalAlign::Center -                            } -                            alignment::Vertical::Bottom => { -                                wgpu_glyph::VerticalAlign::Bottom -                            } -                        }), -                }; - -                self.text_pipeline.queue(text); +            #[cfg(any(feature = "image", feature = "svg"))] +            { +                if !layer.images.is_empty() { +                    self.image_pipeline.render( +                        image_layer, +                        bounds, +                        &mut render_pass, +                    ); + +                    image_layer += 1; +                }              } -            self.text_pipeline.draw_queued( -                device, -                staging_belt, -                encoder, -                target, -                transformation, -                wgpu_glyph::Region { -                    x: bounds.x, -                    y: bounds.y, -                    width: bounds.width, -                    height: bounds.height, -                }, -            ); +            if !layer.text.is_empty() { +                self.text_pipeline +                    .render(text_layer, bounds, &mut render_pass); + +                text_layer += 1; +            }          } + +        let _ = ManuallyDrop::into_inner(render_pass);      }  } -impl iced_graphics::Backend for Backend { +impl crate::graphics::Backend for Backend { +    type Primitive = primitive::Custom; +      fn trim_measurements(&mut self) { -        self.text_pipeline.trim_measurement_cache() +        self.text_pipeline.trim_measurements();      }  }  impl backend::Text for Backend { -    const ICON_FONT: Font = font::ICONS; -    const CHECKMARK_ICON: char = font::CHECKMARK_ICON; -    const ARROW_DOWN_ICON: char = font::ARROW_DOWN_ICON; +    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) -> Font { +        self.default_font +    }      fn default_size(&self) -> f32 {          self.default_text_size @@ -273,45 +360,59 @@ impl backend::Text for Backend {          &self,          contents: &str,          size: f32, +        line_height: core::text::LineHeight,          font: Font,          bounds: Size, -    ) -> (f32, f32) { -        self.text_pipeline.measure(contents, size, font, bounds) +        shaping: core::text::Shaping, +    ) -> Size { +        self.text_pipeline.measure( +            contents, +            size, +            line_height, +            font, +            bounds, +            shaping, +        )      }      fn hit_test(          &self,          contents: &str,          size: f32, +        line_height: core::text::LineHeight,          font: Font,          bounds: Size, -        point: iced_native::Point, +        shaping: core::text::Shaping, +        point: Point,          nearest_only: bool, -    ) -> Option<text::Hit> { +    ) -> Option<core::text::Hit> {          self.text_pipeline.hit_test(              contents,              size, +            line_height,              font,              bounds, +            shaping,              point,              nearest_only,          )      } + +    fn load_font(&mut self, font: Cow<'static, [u8]>) { +        self.text_pipeline.load_font(font); +    }  }  #[cfg(feature = "image")]  impl backend::Image for Backend { -    fn dimensions(&self, handle: &iced_native::image::Handle) -> Size<u32> { +    fn dimensions(&self, handle: &core::image::Handle) -> Size<u32> {          self.image_pipeline.dimensions(handle)      }  }  #[cfg(feature = "svg")]  impl backend::Svg for Backend { -    fn viewport_dimensions( -        &self, -        handle: &iced_native::svg::Handle, -    ) -> Size<u32> { +    fn viewport_dimensions(&self, handle: &core::svg::Handle) -> Size<u32> {          self.image_pipeline.viewport_dimensions(handle)      }  } | 
