diff options
Diffstat (limited to '')
| -rw-r--r-- | wgpu/Cargo.toml | 43 | ||||
| -rw-r--r-- | wgpu/src/backend.rs | 23 | ||||
| -rw-r--r-- | wgpu/src/geometry.rs (renamed from graphics/src/widget/canvas/frame.rs) | 158 | ||||
| -rw-r--r-- | wgpu/src/image.rs | 42 | ||||
| -rw-r--r-- | wgpu/src/image/atlas.rs | 192 | ||||
| -rw-r--r-- | wgpu/src/image/atlas/allocation.rs | 3 | ||||
| -rw-r--r-- | wgpu/src/image/atlas/allocator.rs | 4 | ||||
| -rw-r--r-- | wgpu/src/image/atlas/entry.rs | 9 | ||||
| -rw-r--r-- | wgpu/src/image/raster.rs | 121 | ||||
| -rw-r--r-- | wgpu/src/image/vector.rs (renamed from graphics/src/image/vector.rs) | 61 | ||||
| -rw-r--r-- | wgpu/src/layer.rs (renamed from graphics/src/layer.rs) | 78 | ||||
| -rw-r--r-- | wgpu/src/layer/image.rs (renamed from graphics/src/layer/image.rs) | 6 | ||||
| -rw-r--r-- | wgpu/src/layer/mesh.rs (renamed from graphics/src/layer/mesh.rs) | 8 | ||||
| -rw-r--r-- | wgpu/src/layer/quad.rs (renamed from graphics/src/layer/quad.rs) | 0 | ||||
| -rw-r--r-- | wgpu/src/layer/text.rs (renamed from graphics/src/layer/text.rs) | 3 | ||||
| -rw-r--r-- | wgpu/src/lib.rs | 21 | ||||
| -rw-r--r-- | wgpu/src/quad.rs | 7 | ||||
| -rw-r--r-- | wgpu/src/settings.rs | 5 | ||||
| -rw-r--r-- | wgpu/src/text.rs | 15 | ||||
| -rw-r--r-- | wgpu/src/triangle.rs | 91 | ||||
| -rw-r--r-- | wgpu/src/triangle/msaa.rs | 4 | ||||
| -rw-r--r-- | wgpu/src/window.rs | 3 | ||||
| -rw-r--r-- | wgpu/src/window/compositor.rs | 155 | 
23 files changed, 629 insertions, 423 deletions
| diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 9e80a76d..6a313d4f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -8,23 +8,9 @@ license = "MIT AND OFL-1.1"  repository = "https://github.com/iced-rs/iced"  [features] -svg = ["iced_graphics/svg"] +geometry = ["iced_graphics/geometry", "lyon"]  image = ["iced_graphics/image"] -png = ["iced_graphics/png"] -jpeg = ["iced_graphics/jpeg"] -jpeg_rayon = ["iced_graphics/jpeg_rayon"] -gif = ["iced_graphics/gif"] -webp = ["iced_graphics/webp"] -pnm = ["iced_graphics/pnm"] -ico = ["iced_graphics/ico"] -bmp = ["iced_graphics/bmp"] -hdr = ["iced_graphics/hdr"] -dds = ["iced_graphics/dds"] -farbfeld = ["iced_graphics/farbfeld"] -canvas = ["iced_graphics/canvas"] -qr_code = ["iced_graphics/qr_code"] -spirv = ["wgpu/spirv"] -webgl = ["wgpu/webgl"] +svg = ["resvg"]  [dependencies]  wgpu = "0.14" @@ -37,6 +23,9 @@ once_cell = "1.0"  rustc-hash = "1.1"  ouroboros = "0.15" +[target.'cfg(target_arch = "wasm32")'.dependencies] +wgpu = { version = "0.14", features = ["webgl"] } +  [dependencies.twox-hash]  version = "1.6"  default-features = false @@ -49,10 +38,6 @@ features = ["std"]  version = "1.9"  features = ["derive"] -[dependencies.iced_native] -version = "0.9" -path = "../native" -  [dependencies.iced_graphics]  version = "0.7"  path = "../graphics" @@ -60,11 +45,7 @@ path = "../graphics"  [dependencies.glyphon]  version = "0.2"  git = "https://github.com/hecrj/glyphon.git" -rev = "810bc979f9005e2bd343b72b980e57e46174283f" - -[dependencies.tracing] -version = "0.1.6" -optional = true +rev = "edd23695ad53db5f89d455c3c130172fd107d6a2"  [dependencies.encase]  version = "0.3.0" @@ -73,6 +54,18 @@ features = ["glam"]  [dependencies.glam]  version = "0.21.3" +[dependencies.lyon] +version = "1.0" +optional = true + +[dependencies.resvg] +version = "0.29" +optional = true + +[dependencies.tracing] +version = "0.1.6" +optional = true +  [package.metadata.docs.rs]  rustdoc-args = ["--cfg", "docsrs"]  all-features = true diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index e650d9a5..88c58554 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -1,11 +1,11 @@ +use crate::core; +use crate::core::{Color, Font, Point, Size}; +use crate::graphics::backend; +use crate::graphics::{Primitive, Transformation, Viewport};  use crate::quad;  use crate::text;  use crate::triangle; -use crate::{Settings, Transformation}; - -use iced_graphics::backend; -use iced_graphics::layer::Layer; -use iced_graphics::{Color, Font, Primitive, Size, Viewport}; +use crate::{Layer, Settings};  #[cfg(feature = "tracing")]  use tracing::info_span; @@ -119,7 +119,7 @@ impl Backend {          self.triangle_pipeline.end_frame();          #[cfg(any(feature = "image", feature = "svg"))] -        self.image_pipeline.end_frame(device, queue, encoder); +        self.image_pipeline.end_frame();      }      fn prepare_text( @@ -364,9 +364,9 @@ impl backend::Text for Backend {          size: f32,          font: Font,          bounds: Size, -        point: iced_native::Point, +        point: Point,          nearest_only: bool, -    ) -> Option<text::Hit> { +    ) -> Option<core::text::Hit> {          self.text_pipeline.hit_test(              contents,              size, @@ -384,17 +384,14 @@ impl backend::Text for Backend {  #[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)      }  } diff --git a/graphics/src/widget/canvas/frame.rs b/wgpu/src/geometry.rs index d68548ae..59ec31fe 100644 --- a/graphics/src/widget/canvas/frame.rs +++ b/wgpu/src/geometry.rs @@ -1,9 +1,9 @@ -use crate::gradient::Gradient; -use crate::triangle; -use crate::widget::canvas::{path, Fill, Geometry, Path, Stroke, Style, Text}; -use crate::Primitive; - -use iced_native::{Point, Rectangle, Size, Vector}; +use crate::core::{Gradient, Point, Rectangle, Size, Vector}; +use crate::graphics::geometry::fill::{self, Fill}; +use crate::graphics::geometry::{ +    LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, +}; +use crate::graphics::primitive::{self, Primitive};  use lyon::geom::euclid;  use lyon::tessellation; @@ -23,9 +23,9 @@ pub struct Frame {  }  enum Buffer { -    Solid(tessellation::VertexBuffers<triangle::ColoredVertex2D, u32>), +    Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),      Gradient( -        tessellation::VertexBuffers<triangle::Vertex2D, u32>, +        tessellation::VertexBuffers<primitive::Vertex2D, u32>,          Gradient,      ),  } @@ -196,8 +196,8 @@ impl Frame {              .buffers              .get_fill(&self.transforms.current.transform_style(style)); -        let options = -            tessellation::FillOptions::default().with_fill_rule(rule.into()); +        let options = tessellation::FillOptions::default() +            .with_fill_rule(into_fill_rule(rule));          if self.transforms.current.is_identity {              self.fill_tessellator.tessellate_path( @@ -206,7 +206,7 @@ impl Frame {                  buffer.as_mut(),              )          } else { -            let path = path.transformed(&self.transforms.current.raw); +            let path = path.transform(&self.transforms.current.raw);              self.fill_tessellator.tessellate_path(                  path.raw(), @@ -241,8 +241,8 @@ impl Frame {                  lyon::math::Vector::new(size.width, size.height),              ); -        let options = -            tessellation::FillOptions::default().with_fill_rule(rule.into()); +        let options = tessellation::FillOptions::default() +            .with_fill_rule(into_fill_rule(rule));          self.fill_tessellator              .tessellate_rectangle( @@ -264,14 +264,14 @@ impl Frame {          let mut options = tessellation::StrokeOptions::default();          options.line_width = stroke.width; -        options.start_cap = stroke.line_cap.into(); -        options.end_cap = stroke.line_cap.into(); -        options.line_join = stroke.line_join.into(); +        options.start_cap = into_line_cap(stroke.line_cap); +        options.end_cap = into_line_cap(stroke.line_cap); +        options.line_join = into_line_join(stroke.line_join);          let path = if stroke.line_dash.segments.is_empty() {              Cow::Borrowed(path)          } else { -            Cow::Owned(path::dashed(path, stroke.line_dash)) +            Cow::Owned(dashed(path, stroke.line_dash))          };          if self.transforms.current.is_identity { @@ -281,7 +281,7 @@ impl Frame {                  buffer.as_mut(),              )          } else { -            let path = path.transformed(&self.transforms.current.raw); +            let path = path.transform(&self.transforms.current.raw);              self.stroke_tessellator.tessellate_path(                  path.raw(), @@ -344,10 +344,18 @@ impl Frame {      /// operations in different coordinate systems.      #[inline]      pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) { -        self.transforms.previous.push(self.transforms.current); +        self.push_transform();          f(self); +        self.pop_transform(); +    } + +    pub fn push_transform(&mut self) { +        self.transforms.previous.push(self.transforms.current); +    } + +    pub fn pop_transform(&mut self) {          self.transforms.current = self.transforms.previous.pop().unwrap();      } @@ -363,14 +371,19 @@ impl Frame {          f(&mut frame); +        let translation = Vector::new(region.x, region.y); + +        self.clip(frame, translation); +    } + +    pub fn clip(&mut self, frame: Frame, translation: Vector) { +        let size = frame.size();          let primitives = frame.into_primitives();          let (text, meshes) = primitives              .into_iter()              .partition(|primitive| matches!(primitive, Primitive::Text { .. })); -        let translation = Vector::new(region.x, region.y); -          self.primitives.push(Primitive::Group {              primitives: vec![                  Primitive::Translate { @@ -380,7 +393,7 @@ impl Frame {                  Primitive::Translate {                      translation,                      content: Box::new(Primitive::Clip { -                        bounds: Rectangle::with_size(region.size()), +                        bounds: Rectangle::with_size(size),                          content: Box::new(Primitive::Group {                              primitives: text,                          }), @@ -423,11 +436,11 @@ impl Frame {          self.transforms.current.is_identity = false;      } -    /// Produces the [`Geometry`] representing everything drawn on the [`Frame`]. -    pub fn into_geometry(self) -> Geometry { -        Geometry::from_primitive(Primitive::Group { +    /// Produces the [`Primitive`] representing everything drawn on the [`Frame`]. +    pub fn into_primitive(self) -> Primitive { +        Primitive::Group {              primitives: self.into_primitives(), -        }) +        }      }      fn into_primitives(mut self) -> Vec<Primitive> { @@ -436,7 +449,7 @@ impl Frame {                  Buffer::Solid(buffer) => {                      if !buffer.indices.is_empty() {                          self.primitives.push(Primitive::SolidMesh { -                            buffers: triangle::Mesh2D { +                            buffers: primitive::Mesh2D {                                  vertices: buffer.vertices,                                  indices: buffer.indices,                              }, @@ -447,7 +460,7 @@ impl Frame {                  Buffer::Gradient(buffer, gradient) => {                      if !buffer.indices.is_empty() {                          self.primitives.push(Primitive::GradientMesh { -                            buffers: triangle::Mesh2D { +                            buffers: primitive::Mesh2D {                                  vertices: buffer.vertices,                                  indices: buffer.indices,                              }, @@ -465,31 +478,31 @@ impl Frame {  struct Vertex2DBuilder; -impl tessellation::FillVertexConstructor<triangle::Vertex2D> +impl tessellation::FillVertexConstructor<primitive::Vertex2D>      for Vertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::FillVertex<'_>, -    ) -> triangle::Vertex2D { +    ) -> primitive::Vertex2D {          let position = vertex.position(); -        triangle::Vertex2D { +        primitive::Vertex2D {              position: [position.x, position.y],          }      }  } -impl tessellation::StrokeVertexConstructor<triangle::Vertex2D> +impl tessellation::StrokeVertexConstructor<primitive::Vertex2D>      for Vertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::StrokeVertex<'_, '_>, -    ) -> triangle::Vertex2D { +    ) -> primitive::Vertex2D {          let position = vertex.position(); -        triangle::Vertex2D { +        primitive::Vertex2D {              position: [position.x, position.y],          }      } @@ -497,34 +510,99 @@ impl tessellation::StrokeVertexConstructor<triangle::Vertex2D>  struct TriangleVertex2DBuilder([f32; 4]); -impl tessellation::FillVertexConstructor<triangle::ColoredVertex2D> +impl tessellation::FillVertexConstructor<primitive::ColoredVertex2D>      for TriangleVertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::FillVertex<'_>, -    ) -> triangle::ColoredVertex2D { +    ) -> primitive::ColoredVertex2D {          let position = vertex.position(); -        triangle::ColoredVertex2D { +        primitive::ColoredVertex2D {              position: [position.x, position.y],              color: self.0,          }      }  } -impl tessellation::StrokeVertexConstructor<triangle::ColoredVertex2D> +impl tessellation::StrokeVertexConstructor<primitive::ColoredVertex2D>      for TriangleVertex2DBuilder  {      fn new_vertex(          &mut self,          vertex: tessellation::StrokeVertex<'_, '_>, -    ) -> triangle::ColoredVertex2D { +    ) -> primitive::ColoredVertex2D {          let position = vertex.position(); -        triangle::ColoredVertex2D { +        primitive::ColoredVertex2D {              position: [position.x, position.y],              color: self.0,          }      }  } + +fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin { +    match line_join { +        LineJoin::Miter => lyon::tessellation::LineJoin::Miter, +        LineJoin::Round => lyon::tessellation::LineJoin::Round, +        LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel, +    } +} + +fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap { +    match line_cap { +        LineCap::Butt => lyon::tessellation::LineCap::Butt, +        LineCap::Square => lyon::tessellation::LineCap::Square, +        LineCap::Round => lyon::tessellation::LineCap::Round, +    } +} + +fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule { +    match rule { +        fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero, +        fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd, +    } +} + +pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { +    use lyon::algorithms::walk::{ +        walk_along_path, RepeatedPattern, WalkerEvent, +    }; +    use lyon::path::iterator::PathIterator; + +    Path::new(|builder| { +        let segments_odd = (line_dash.segments.len() % 2 == 1) +            .then(|| [line_dash.segments, line_dash.segments].concat()); + +        let mut draw_line = false; + +        walk_along_path( +            path.raw().iter().flattened(0.01), +            0.0, +            lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE, +            &mut RepeatedPattern { +                callback: |event: WalkerEvent<'_>| { +                    let point = Point { +                        x: event.position.x, +                        y: event.position.y, +                    }; + +                    if draw_line { +                        builder.line_to(point); +                    } else { +                        builder.move_to(point); +                    } + +                    draw_line = !draw_line; + +                    true +                }, +                index: line_dash.offset, +                intervals: segments_odd +                    .as_deref() +                    .unwrap_or(line_dash.segments), +            }, +        ); +    }) +} diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index db05d2ff..4163e338 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -1,16 +1,17 @@  mod atlas;  #[cfg(feature = "image")] -use iced_graphics::image::raster; +mod raster;  #[cfg(feature = "svg")] -use iced_graphics::image::vector; +mod vector; -use crate::{Buffer, Transformation};  use atlas::Atlas; -use iced_graphics::layer; -use iced_native::{Rectangle, Size}; +use crate::core::{Rectangle, Size}; +use crate::graphics::Transformation; +use crate::layer; +use crate::Buffer;  use std::cell::RefCell;  use std::mem; @@ -18,10 +19,10 @@ use std::mem;  use bytemuck::{Pod, Zeroable};  #[cfg(feature = "image")] -use iced_native::image; +use crate::core::image;  #[cfg(feature = "svg")] -use iced_native::svg; +use crate::core::svg;  #[cfg(feature = "tracing")]  use tracing::info_span; @@ -29,9 +30,9 @@ use tracing::info_span;  #[derive(Debug)]  pub struct Pipeline {      #[cfg(feature = "image")] -    raster_cache: RefCell<raster::Cache<Atlas>>, +    raster_cache: RefCell<raster::Cache>,      #[cfg(feature = "svg")] -    vector_cache: RefCell<vector::Cache<Atlas>>, +    vector_cache: RefCell<vector::Cache>,      pipeline: wgpu::RenderPipeline,      vertices: wgpu::Buffer, @@ -367,8 +368,10 @@ impl Pipeline {                  #[cfg(feature = "image")]                  layer::Image::Raster { handle, bounds } => {                      if let Some(atlas_entry) = raster_cache.upload( +                        device, +                        queue, +                        encoder,                          handle, -                        &mut (device, queue, encoder),                          &mut self.texture_atlas,                      ) {                          add_instances( @@ -391,11 +394,13 @@ impl Pipeline {                      let size = [bounds.width, bounds.height];                      if let Some(atlas_entry) = vector_cache.upload( +                        device, +                        queue, +                        encoder,                          handle,                          *color,                          size,                          _scale, -                        &mut (device, queue, encoder),                          &mut self.texture_atlas,                      ) {                          add_instances( @@ -476,21 +481,12 @@ impl Pipeline {          }      } -    pub fn end_frame( -        &mut self, -        device: &wgpu::Device, -        queue: &wgpu::Queue, -        encoder: &mut wgpu::CommandEncoder, -    ) { +    pub fn end_frame(&mut self) {          #[cfg(feature = "image")] -        self.raster_cache -            .borrow_mut() -            .trim(&mut self.texture_atlas, &mut (device, queue, encoder)); +        self.raster_cache.borrow_mut().trim(&mut self.texture_atlas);          #[cfg(feature = "svg")] -        self.vector_cache -            .borrow_mut() -            .trim(&mut self.texture_atlas, &mut (device, queue, encoder)); +        self.vector_cache.borrow_mut().trim(&mut self.texture_atlas);          self.prepare_layer = 0;      } diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index 7df67abd..c00b8cef 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -12,8 +12,7 @@ use allocator::Allocator;  pub const SIZE: u32 = 2048; -use iced_graphics::image; -use iced_graphics::Size; +use crate::core::Size;  use std::num::NonZeroU32; @@ -64,6 +63,97 @@ impl Atlas {          self.layers.len()      } +    pub fn upload( +        &mut self, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        encoder: &mut wgpu::CommandEncoder, +        width: u32, +        height: u32, +        data: &[u8], +    ) -> Option<Entry> { +        let entry = { +            let current_size = self.layers.len(); +            let entry = self.allocate(width, height)?; + +            // We grow the internal texture after allocating if necessary +            let new_layers = self.layers.len() - current_size; +            self.grow(new_layers, device, encoder); + +            entry +        }; + +        log::info!("Allocated atlas entry: {:?}", entry); + +        // It is a webgpu requirement that: +        //   BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0 +        // So we calculate padded_width by rounding width up to the next +        // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. +        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; +        let padding = (align - (4 * width) % align) % align; +        let padded_width = (4 * width + padding) as usize; +        let padded_data_size = padded_width * height as usize; + +        let mut padded_data = vec![0; padded_data_size]; + +        for row in 0..height as usize { +            let offset = row * padded_width; + +            padded_data[offset..offset + 4 * width as usize].copy_from_slice( +                &data[row * 4 * width as usize..(row + 1) * 4 * width as usize], +            ) +        } + +        match &entry { +            Entry::Contiguous(allocation) => { +                self.upload_allocation( +                    &padded_data, +                    width, +                    height, +                    padding, +                    0, +                    allocation, +                    queue, +                ); +            } +            Entry::Fragmented { fragments, .. } => { +                for fragment in fragments { +                    let (x, y) = fragment.position; +                    let offset = (y * padded_width as u32 + 4 * x) as usize; + +                    self.upload_allocation( +                        &padded_data, +                        width, +                        height, +                        padding, +                        offset, +                        &fragment.allocation, +                        queue, +                    ); +                } +            } +        } + +        log::info!("Current atlas: {:?}", self); + +        Some(entry) +    } + +    pub fn remove(&mut self, entry: &Entry) { +        log::info!("Removing atlas entry: {:?}", entry); + +        match entry { +            Entry::Contiguous(allocation) => { +                self.deallocate(allocation); +            } +            Entry::Fragmented { fragments, .. } => { +                for fragment in fragments { +                    self.deallocate(&fragment.allocation); +                } +            } +        } +    } +      fn allocate(&mut self, width: u32, height: u32) -> Option<Entry> {          // Allocate one layer if texture fits perfectly          if width == SIZE && height == SIZE { @@ -296,101 +386,3 @@ impl Atlas {              });      }  } - -impl image::Storage for Atlas { -    type Entry = Entry; -    type State<'a> = ( -        &'a wgpu::Device, -        &'a wgpu::Queue, -        &'a mut wgpu::CommandEncoder, -    ); - -    fn upload( -        &mut self, -        width: u32, -        height: u32, -        data: &[u8], -        (device, queue, encoder): &mut Self::State<'_>, -    ) -> Option<Self::Entry> { -        let entry = { -            let current_size = self.layers.len(); -            let entry = self.allocate(width, height)?; - -            // We grow the internal texture after allocating if necessary -            let new_layers = self.layers.len() - current_size; -            self.grow(new_layers, device, encoder); - -            entry -        }; - -        log::info!("Allocated atlas entry: {:?}", entry); - -        // It is a webgpu requirement that: -        //   BufferCopyView.layout.bytes_per_row % wgpu::COPY_BYTES_PER_ROW_ALIGNMENT == 0 -        // So we calculate padded_width by rounding width up to the next -        // multiple of wgpu::COPY_BYTES_PER_ROW_ALIGNMENT. -        let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT; -        let padding = (align - (4 * width) % align) % align; -        let padded_width = (4 * width + padding) as usize; -        let padded_data_size = padded_width * height as usize; - -        let mut padded_data = vec![0; padded_data_size]; - -        for row in 0..height as usize { -            let offset = row * padded_width; - -            padded_data[offset..offset + 4 * width as usize].copy_from_slice( -                &data[row * 4 * width as usize..(row + 1) * 4 * width as usize], -            ) -        } - -        match &entry { -            Entry::Contiguous(allocation) => { -                self.upload_allocation( -                    &padded_data, -                    width, -                    height, -                    padding, -                    0, -                    allocation, -                    queue, -                ); -            } -            Entry::Fragmented { fragments, .. } => { -                for fragment in fragments { -                    let (x, y) = fragment.position; -                    let offset = (y * padded_width as u32 + 4 * x) as usize; - -                    self.upload_allocation( -                        &padded_data, -                        width, -                        height, -                        padding, -                        offset, -                        &fragment.allocation, -                        queue, -                    ); -                } -            } -        } - -        log::info!("Current atlas: {:?}", self); - -        Some(entry) -    } - -    fn remove(&mut self, entry: &Entry, _: &mut Self::State<'_>) { -        log::info!("Removing atlas entry: {:?}", entry); - -        match entry { -            Entry::Contiguous(allocation) => { -                self.deallocate(allocation); -            } -            Entry::Fragmented { fragments, .. } => { -                for fragment in fragments { -                    self.deallocate(&fragment.allocation); -                } -            } -        } -    } -} diff --git a/wgpu/src/image/atlas/allocation.rs b/wgpu/src/image/atlas/allocation.rs index 43aba875..11289771 100644 --- a/wgpu/src/image/atlas/allocation.rs +++ b/wgpu/src/image/atlas/allocation.rs @@ -1,7 +1,6 @@ +use crate::core::Size;  use crate::image::atlas::{self, allocator}; -use iced_graphics::Size; -  #[derive(Debug)]  pub enum Allocation {      Partial { diff --git a/wgpu/src/image/atlas/allocator.rs b/wgpu/src/image/atlas/allocator.rs index 03effdcb..204a5c26 100644 --- a/wgpu/src/image/atlas/allocator.rs +++ b/wgpu/src/image/atlas/allocator.rs @@ -46,10 +46,10 @@ impl Region {          (rectangle.min.x as u32, rectangle.min.y as u32)      } -    pub fn size(&self) -> iced_graphics::Size<u32> { +    pub fn size(&self) -> crate::core::Size<u32> {          let size = self.allocation.rectangle.size(); -        iced_graphics::Size::new(size.width as u32, size.height as u32) +        crate::core::Size::new(size.width as u32, size.height as u32)      }  } diff --git a/wgpu/src/image/atlas/entry.rs b/wgpu/src/image/atlas/entry.rs index 69c05a50..7e4c92a2 100644 --- a/wgpu/src/image/atlas/entry.rs +++ b/wgpu/src/image/atlas/entry.rs @@ -1,8 +1,6 @@ +use crate::core::Size;  use crate::image::atlas; -use iced_graphics::image; -use iced_graphics::Size; -  #[derive(Debug)]  pub enum Entry {      Contiguous(atlas::Allocation), @@ -12,8 +10,9 @@ pub enum Entry {      },  } -impl image::storage::Entry for Entry { -    fn size(&self) -> Size<u32> { +impl Entry { +    #[cfg(feature = "image")] +    pub fn size(&self) -> Size<u32> {          match self {              Entry::Contiguous(allocation) => allocation.size(),              Entry::Fragmented { size, .. } => *size, diff --git a/wgpu/src/image/raster.rs b/wgpu/src/image/raster.rs new file mode 100644 index 00000000..9b38dce4 --- /dev/null +++ b/wgpu/src/image/raster.rs @@ -0,0 +1,121 @@ +use crate::core::image; +use crate::core::Size; +use crate::graphics; +use crate::graphics::image::image_rs; +use crate::image::atlas::{self, Atlas}; + +use std::collections::{HashMap, HashSet}; + +/// Entry in cache corresponding to an image handle +#[derive(Debug)] +pub enum Memory { +    /// Image data on host +    Host(image_rs::ImageBuffer<image_rs::Rgba<u8>, Vec<u8>>), +    /// Storage entry +    Device(atlas::Entry), +    /// Image not found +    NotFound, +    /// Invalid image data +    Invalid, +} + +impl Memory { +    /// Width and height of image +    pub fn dimensions(&self) -> Size<u32> { +        match self { +            Memory::Host(image) => { +                let (width, height) = image.dimensions(); + +                Size::new(width, height) +            } +            Memory::Device(entry) => entry.size(), +            Memory::NotFound => Size::new(1, 1), +            Memory::Invalid => Size::new(1, 1), +        } +    } +} + +/// Caches image raster data +#[derive(Debug, Default)] +pub struct Cache { +    map: HashMap<u64, Memory>, +    hits: HashSet<u64>, +} + +impl Cache { +    /// Load image +    pub fn load(&mut self, handle: &image::Handle) -> &mut Memory { +        if self.contains(handle) { +            return self.get(handle).unwrap(); +        } + +        let memory = match graphics::image::load(handle) { +            Ok(image) => Memory::Host(image.to_rgba8()), +            Err(image_rs::error::ImageError::IoError(_)) => Memory::NotFound, +            Err(_) => Memory::Invalid, +        }; + +        self.insert(handle, memory); +        self.get(handle).unwrap() +    } + +    /// Load image and upload raster data +    pub fn upload( +        &mut self, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        encoder: &mut wgpu::CommandEncoder, +        handle: &image::Handle, +        atlas: &mut Atlas, +    ) -> Option<&atlas::Entry> { +        let memory = self.load(handle); + +        if let Memory::Host(image) = memory { +            let (width, height) = image.dimensions(); + +            let entry = +                atlas.upload(device, queue, encoder, width, height, image)?; + +            *memory = Memory::Device(entry); +        } + +        if let Memory::Device(allocation) = memory { +            Some(allocation) +        } else { +            None +        } +    } + +    /// Trim cache misses from cache +    pub fn trim(&mut self, atlas: &mut Atlas) { +        let hits = &self.hits; + +        self.map.retain(|k, memory| { +            let retain = hits.contains(k); + +            if !retain { +                if let Memory::Device(entry) = memory { +                    atlas.remove(entry); +                } +            } + +            retain +        }); + +        self.hits.clear(); +    } + +    fn get(&mut self, handle: &image::Handle) -> Option<&mut Memory> { +        let _ = self.hits.insert(handle.id()); + +        self.map.get_mut(&handle.id()) +    } + +    fn insert(&mut self, handle: &image::Handle, memory: Memory) { +        let _ = self.map.insert(handle.id(), memory); +    } + +    fn contains(&self, handle: &image::Handle) -> bool { +        self.map.contains_key(&handle.id()) +    } +} diff --git a/graphics/src/image/vector.rs b/wgpu/src/image/vector.rs index 82d77aff..3624e46b 100644 --- a/graphics/src/image/vector.rs +++ b/wgpu/src/image/vector.rs @@ -1,10 +1,9 @@ -//! Vector image loading and caching -use crate::image::Storage; -use crate::Color; - -use iced_native::svg; -use iced_native::Size; +use crate::core::svg; +use crate::core::{Color, Size}; +use crate::image::atlas::{self, Atlas}; +use resvg::tiny_skia; +use resvg::usvg;  use std::collections::{HashMap, HashSet};  use std::fs; @@ -21,7 +20,7 @@ impl Svg {      pub fn viewport_dimensions(&self) -> Size<u32> {          match self {              Svg::Loaded(tree) => { -                let size = tree.svg_node().size; +                let size = tree.size;                  Size::new(size.width() as u32, size.height() as u32)              } @@ -31,17 +30,17 @@ impl Svg {  }  /// Caches svg vector and raster data -#[derive(Debug)] -pub struct Cache<T: Storage> { +#[derive(Debug, Default)] +pub struct Cache {      svgs: HashMap<u64, Svg>, -    rasterized: HashMap<(u64, u32, u32, ColorFilter), T::Entry>, +    rasterized: HashMap<(u64, u32, u32, ColorFilter), atlas::Entry>,      svg_hits: HashSet<u64>,      rasterized_hits: HashSet<(u64, u32, u32, ColorFilter)>,  }  type ColorFilter = Option<[u8; 4]>; -impl<T: Storage> Cache<T> { +impl Cache {      /// Load svg      pub fn load(&mut self, handle: &svg::Handle) -> &Svg {          if self.svgs.contains_key(&handle.id()) { @@ -51,20 +50,14 @@ impl<T: Storage> Cache<T> {          let svg = match handle.data() {              svg::Data::Path(path) => {                  let tree = fs::read_to_string(path).ok().and_then(|contents| { -                    usvg::Tree::from_str( -                        &contents, -                        &usvg::Options::default().to_ref(), -                    ) -                    .ok() +                    usvg::Tree::from_str(&contents, &usvg::Options::default()) +                        .ok()                  });                  tree.map(Svg::Loaded).unwrap_or(Svg::NotFound)              }              svg::Data::Bytes(bytes) => { -                match usvg::Tree::from_data( -                    bytes, -                    &usvg::Options::default().to_ref(), -                ) { +                match usvg::Tree::from_data(bytes, &usvg::Options::default()) {                      Ok(tree) => Svg::Loaded(tree),                      Err(_) => Svg::NotFound,                  } @@ -78,13 +71,15 @@ impl<T: Storage> Cache<T> {      /// Load svg and upload raster data      pub fn upload(          &mut self, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        encoder: &mut wgpu::CommandEncoder,          handle: &svg::Handle,          color: Option<Color>,          [width, height]: [f32; 2],          scale: f32, -        state: &mut T::State<'_>, -        storage: &mut T, -    ) -> Option<&T::Entry> { +        atlas: &mut Atlas, +    ) -> Option<&atlas::Entry> {          let id = handle.id();          let (width, height) = ( @@ -125,6 +120,7 @@ impl<T: Storage> Cache<T> {                      } else {                          usvg::FitTo::Height(height)                      }, +                    tiny_skia::Transform::default(),                      img.as_mut(),                  )?; @@ -140,7 +136,9 @@ impl<T: Storage> Cache<T> {                      });                  } -                let allocation = storage.upload(width, height, &rgba, state)?; +                let allocation = atlas +                    .upload(device, queue, encoder, width, height, &rgba)?; +                  log::debug!("allocating {} {}x{}", id, width, height);                  let _ = self.svg_hits.insert(id); @@ -154,7 +152,7 @@ impl<T: Storage> Cache<T> {      }      /// Load svg and upload raster data -    pub fn trim(&mut self, storage: &mut T, state: &mut T::State<'_>) { +    pub fn trim(&mut self, atlas: &mut Atlas) {          let svg_hits = &self.svg_hits;          let rasterized_hits = &self.rasterized_hits; @@ -163,7 +161,7 @@ impl<T: Storage> Cache<T> {              let retain = rasterized_hits.contains(k);              if !retain { -                storage.remove(entry, state); +                atlas.remove(entry);              }              retain @@ -173,17 +171,6 @@ impl<T: Storage> Cache<T> {      }  } -impl<T: Storage> Default for Cache<T> { -    fn default() -> Self { -        Self { -            svgs: HashMap::new(), -            rasterized: HashMap::new(), -            svg_hits: HashSet::new(), -            rasterized_hits: HashSet::new(), -        } -    } -} -  impl std::fmt::Debug for Svg {      fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {          match self { diff --git a/graphics/src/layer.rs b/wgpu/src/layer.rs index f6eb2fdd..cb9d5e2f 100644 --- a/graphics/src/layer.rs +++ b/wgpu/src/layer.rs @@ -10,11 +10,9 @@ pub use mesh::Mesh;  pub use quad::Quad;  pub use text::Text; -use crate::alignment; -use crate::{ -    Background, Color, Font, Point, Primitive, Rectangle, Size, Vector, -    Viewport, -}; +use crate::core::alignment; +use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; +use crate::graphics::{Primitive, Viewport};  /// A group of primitives that should be clipped together.  #[derive(Debug)] @@ -110,18 +108,6 @@ impl<'a> Layer<'a> {          current_layer: usize,      ) {          match primitive { -            Primitive::None => {} -            Primitive::Group { primitives } => { -                // TODO: Inspect a bit and regroup (?) -                for primitive in primitives { -                    Self::process_primitive( -                        layers, -                        translation, -                        primitive, -                        current_layer, -                    ) -                } -            }              Primitive::Text {                  content,                  bounds, @@ -167,6 +153,27 @@ impl<'a> Layer<'a> {                      border_color: border_color.into_linear(),                  });              } +            Primitive::Image { handle, bounds } => { +                let layer = &mut layers[current_layer]; + +                layer.images.push(Image::Raster { +                    handle: handle.clone(), +                    bounds: *bounds + translation, +                }); +            } +            Primitive::Svg { +                handle, +                color, +                bounds, +            } => { +                let layer = &mut layers[current_layer]; + +                layer.images.push(Image::Vector { +                    handle: handle.clone(), +                    color: *color, +                    bounds: *bounds + translation, +                }); +            }              Primitive::SolidMesh { buffers, size } => {                  let layer = &mut layers[current_layer]; @@ -206,6 +213,17 @@ impl<'a> Layer<'a> {                      });                  }              } +            Primitive::Group { primitives } => { +                // TODO: Inspect a bit and regroup (?) +                for primitive in primitives { +                    Self::process_primitive( +                        layers, +                        translation, +                        primitive, +                        current_layer, +                    ) +                } +            }              Primitive::Clip { bounds, content } => {                  let layer = &mut layers[current_layer];                  let translated_bounds = *bounds + translation; @@ -236,34 +254,16 @@ impl<'a> Layer<'a> {                      current_layer,                  );              } -            Primitive::Cached { cache } => { +            Primitive::Cache { content } => {                  Self::process_primitive(                      layers,                      translation, -                    cache, +                    content,                      current_layer,                  );              } -            Primitive::Image { handle, bounds } => { -                let layer = &mut layers[current_layer]; - -                layer.images.push(Image::Raster { -                    handle: handle.clone(), -                    bounds: *bounds + translation, -                }); -            } -            Primitive::Svg { -                handle, -                color, -                bounds, -            } => { -                let layer = &mut layers[current_layer]; - -                layer.images.push(Image::Vector { -                    handle: handle.clone(), -                    color: *color, -                    bounds: *bounds + translation, -                }); +            _ => { +                // Unsupported!              }          }      } diff --git a/graphics/src/layer/image.rs b/wgpu/src/layer/image.rs index 3eff2397..0de589f8 100644 --- a/graphics/src/layer/image.rs +++ b/wgpu/src/layer/image.rs @@ -1,6 +1,6 @@ -use crate::{Color, Rectangle}; - -use iced_native::{image, svg}; +use crate::core::image; +use crate::core::svg; +use crate::core::{Color, Rectangle};  /// A raster or vector image.  #[derive(Debug, Clone)] diff --git a/graphics/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 7661c5c9..9dd14391 100644 --- a/graphics/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,6 +1,6 @@  //! A collection of triangle primitives. -use crate::triangle; -use crate::{Gradient, Point, Rectangle}; +use crate::core::{Gradient, Point, Rectangle}; +use crate::graphics::primitive;  /// A mesh of triangles.  #[derive(Debug, Clone, Copy)] @@ -11,7 +11,7 @@ pub enum Mesh<'a> {          origin: Point,          /// The vertex and index buffers of the [`Mesh`]. -        buffers: &'a triangle::Mesh2D<triangle::ColoredVertex2D>, +        buffers: &'a primitive::Mesh2D<primitive::ColoredVertex2D>,          /// The clipping bounds of the [`Mesh`].          clip_bounds: Rectangle<f32>, @@ -22,7 +22,7 @@ pub enum Mesh<'a> {          origin: Point,          /// The vertex and index buffers of the [`Mesh`]. -        buffers: &'a triangle::Mesh2D<triangle::Vertex2D>, +        buffers: &'a primitive::Mesh2D<primitive::Vertex2D>,          /// The clipping bounds of the [`Mesh`].          clip_bounds: Rectangle<f32>, diff --git a/graphics/src/layer/quad.rs b/wgpu/src/layer/quad.rs index 0d8bde9d..0d8bde9d 100644 --- a/graphics/src/layer/quad.rs +++ b/wgpu/src/layer/quad.rs diff --git a/graphics/src/layer/text.rs b/wgpu/src/layer/text.rs index 38d62616..fdbdaafb 100644 --- a/graphics/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -1,4 +1,5 @@ -use crate::{alignment, Color, Font, Rectangle}; +use crate::core::alignment; +use crate::core::{Color, Font, Rectangle};  /// A paragraph of text.  #[derive(Debug, Clone, Copy)] diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 9da40572..473f3621 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -25,7 +25,7 @@  )]  #![deny(      missing_debug_implementations, -    missing_docs, +    //missing_docs,      unsafe_code,      unused_results,      clippy::extra_unused_lifetimes, @@ -37,27 +37,29 @@  #![forbid(rust_2018_idioms)]  #![allow(clippy::inherent_to_string, clippy::type_complexity)]  #![cfg_attr(docsrs, feature(doc_cfg))] - +pub mod layer;  pub mod settings;  pub mod window; +#[cfg(feature = "geometry")] +pub mod geometry; +  mod backend;  mod buffer;  mod quad;  mod text;  mod triangle; -pub use iced_graphics::{ -    Antialiasing, Color, Error, Font, Primitive, Viewport, -}; -pub use iced_native::Theme; +pub use iced_graphics as graphics; +pub use iced_graphics::core; +  pub use wgpu;  pub use backend::Backend; +pub use layer::Layer;  pub use settings::Settings; -use crate::buffer::Buffer; -use iced_graphics::Transformation; +use buffer::Buffer;  #[cfg(any(feature = "image", feature = "svg"))]  mod image; @@ -66,5 +68,4 @@ mod image;  ///  /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs  /// [`iced`]: https://github.com/iced-rs/iced -pub type Renderer<Theme = iced_native::Theme> = -    iced_graphics::Renderer<Backend, Theme>; +pub type Renderer<Theme> = iced_graphics::Renderer<Backend, Theme>; diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 246cc5e1..b55216d7 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -1,6 +1,7 @@ -use crate::{Buffer, Transformation}; -use iced_graphics::layer; -use iced_native::Rectangle; +use crate::core::Rectangle; +use crate::graphics::Transformation; +use crate::layer; +use crate::Buffer;  use bytemuck::{Pod, Zeroable};  use std::mem; diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index bd9cf473..7c0750ef 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -1,7 +1,6 @@  //! Configure a renderer. -pub use crate::Antialiasing; - -use crate::Font; +use crate::core::Font; +use crate::graphics::Antialiasing;  /// The settings of a [`Backend`].  /// diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 3839b31f..e99844e6 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,8 +1,7 @@ -pub use iced_native::text::Hit; - -use iced_graphics::layer::Text; -use iced_native::alignment; -use iced_native::{Font, Rectangle, Size}; +use crate::core::alignment; +use crate::core::text::Hit; +use crate::core::{Font, Point, Rectangle, Size}; +use crate::layer::Text;  use rustc_hash::{FxHashMap, FxHashSet};  use std::borrow::Cow; @@ -275,9 +274,9 @@ impl Pipeline {          &self,          content: &str,          size: f32, -        font: iced_native::Font, -        bounds: iced_native::Size, -        point: iced_native::Point, +        font: Font, +        bounds: Size, +        point: Point,          _nearest_only: bool,      ) -> Option<Hit> {          self.system.as_ref().unwrap().with(|fields| { diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 4b4fa16d..9fa521d7 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -2,12 +2,10 @@  mod msaa;  use crate::buffer::r#static::Buffer; -use crate::settings; -use crate::Transformation; +use crate::core::{Gradient, Size}; +use crate::graphics::{Antialiasing, Transformation}; +use crate::layer::mesh::{self, Mesh}; -use iced_graphics::layer::mesh::{self, Mesh}; -use iced_graphics::triangle::ColoredVertex2D; -use iced_graphics::Size;  #[cfg(feature = "tracing")]  use tracing::info_span; @@ -137,7 +135,7 @@ impl Layer {                      gradient_vertex_offset += written_bytes;                      match gradient { -                        iced_graphics::Gradient::Linear(linear) => { +                        Gradient::Linear(linear) => {                              use glam::{IVec4, Vec4};                              let start_offset = self.gradient.color_stop_offset; @@ -319,7 +317,7 @@ impl Pipeline {      pub fn new(          device: &wgpu::Device,          format: wgpu::TextureFormat, -        antialiasing: Option<settings::Antialiasing>, +        antialiasing: Option<Antialiasing>,      ) -> Pipeline {          Pipeline {              blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), @@ -453,7 +451,7 @@ fn primitive_state() -> wgpu::PrimitiveState {  }  fn multisample_state( -    antialiasing: Option<settings::Antialiasing>, +    antialiasing: Option<Antialiasing>,  ) -> wgpu::MultisampleState {      wgpu::MultisampleState {          count: antialiasing.map(|a| a.sample_count()).unwrap_or(1), @@ -465,10 +463,11 @@ fn multisample_state(  mod solid {      use crate::buffer::dynamic;      use crate::buffer::r#static::Buffer; -    use crate::settings; +    use crate::graphics::primitive; +    use crate::graphics::{Antialiasing, Transformation};      use crate::triangle; +      use encase::ShaderType; -    use iced_graphics::Transformation;      #[derive(Debug)]      pub struct Pipeline { @@ -478,7 +477,7 @@ mod solid {      #[derive(Debug)]      pub struct Layer { -        pub vertices: Buffer<triangle::ColoredVertex2D>, +        pub vertices: Buffer<primitive::ColoredVertex2D>,          pub uniforms: dynamic::Buffer<Uniforms>,          pub constants: wgpu::BindGroup,      } @@ -549,7 +548,7 @@ mod solid {          pub fn new(              device: &wgpu::Device,              format: wgpu::TextureFormat, -            antialiasing: Option<settings::Antialiasing>, +            antialiasing: Option<Antialiasing>,          ) -> Self {              let constants_layout = device.create_bind_group_layout(                  &wgpu::BindGroupLayoutDescriptor { @@ -596,7 +595,7 @@ mod solid {                          entry_point: "vs_main",                          buffers: &[wgpu::VertexBufferLayout {                              array_stride: std::mem::size_of::< -                                triangle::ColoredVertex2D, +                                primitive::ColoredVertex2D,                              >()                                  as u64,                              step_mode: wgpu::VertexStepMode::Vertex, @@ -632,12 +631,12 @@ mod solid {  mod gradient {      use crate::buffer::dynamic;      use crate::buffer::r#static::Buffer; -    use crate::settings; +    use crate::graphics::Antialiasing;      use crate::triangle;      use encase::ShaderType;      use glam::{IVec4, Vec4}; -    use iced_graphics::triangle::Vertex2D; +    use iced_graphics::primitive;      #[derive(Debug)]      pub struct Pipeline { @@ -647,7 +646,7 @@ mod gradient {      #[derive(Debug)]      pub struct Layer { -        pub vertices: Buffer<Vertex2D>, +        pub vertices: Buffer<primitive::Vertex2D>,          pub uniforms: dynamic::Buffer<Uniforms>,          pub storage: dynamic::Buffer<Storage>,          pub constants: wgpu::BindGroup, @@ -754,7 +753,7 @@ mod gradient {          pub(super) fn new(              device: &wgpu::Device,              format: wgpu::TextureFormat, -            antialiasing: Option<settings::Antialiasing>, +            antialiasing: Option<Antialiasing>,          ) -> Self {              let constants_layout = device.create_bind_group_layout(                  &wgpu::BindGroupLayoutDescriptor { @@ -810,34 +809,38 @@ mod gradient {                      ),                  }); -            let pipeline = device.create_render_pipeline( -                &wgpu::RenderPipelineDescriptor { -                    label: Some("iced_wgpu::triangle::gradient pipeline"), -                    layout: Some(&layout), -                    vertex: wgpu::VertexState { -                        module: &shader, -                        entry_point: "vs_main", -                        buffers: &[wgpu::VertexBufferLayout { -                            array_stride: std::mem::size_of::<Vertex2D>() -                                as u64, -                            step_mode: wgpu::VertexStepMode::Vertex, -                            attributes: &wgpu::vertex_attr_array!( -                                // Position -                                0 => Float32x2, -                            ), -                        }], +            let pipeline = +                device.create_render_pipeline( +                    &wgpu::RenderPipelineDescriptor { +                        label: Some("iced_wgpu::triangle::gradient pipeline"), +                        layout: Some(&layout), +                        vertex: wgpu::VertexState { +                            module: &shader, +                            entry_point: "vs_main", +                            buffers: &[wgpu::VertexBufferLayout { +                                array_stride: std::mem::size_of::< +                                    primitive::Vertex2D, +                                >( +                                ) +                                    as u64, +                                step_mode: wgpu::VertexStepMode::Vertex, +                                attributes: &wgpu::vertex_attr_array!( +                                    // Position +                                    0 => Float32x2, +                                ), +                            }], +                        }, +                        fragment: Some(wgpu::FragmentState { +                            module: &shader, +                            entry_point: "fs_main", +                            targets: &[triangle::fragment_target(format)], +                        }), +                        primitive: triangle::primitive_state(), +                        depth_stencil: None, +                        multisample: triangle::multisample_state(antialiasing), +                        multiview: None,                      }, -                    fragment: Some(wgpu::FragmentState { -                        module: &shader, -                        entry_point: "fs_main", -                        targets: &[triangle::fragment_target(format)], -                    }), -                    primitive: triangle::primitive_state(), -                    depth_stencil: None, -                    multisample: triangle::multisample_state(antialiasing), -                    multiview: None, -                }, -            ); +                );              Self {                  pipeline, diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index a3016ff8..7144125c 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -1,4 +1,4 @@ -use crate::settings; +use crate::graphics;  #[derive(Debug)]  pub struct Blit { @@ -14,7 +14,7 @@ impl Blit {      pub fn new(          device: &wgpu::Device,          format: wgpu::TextureFormat, -        antialiasing: settings::Antialiasing, +        antialiasing: graphics::Antialiasing,      ) -> Blit {          let sampler = device.create_sampler(&wgpu::SamplerDescriptor {              address_mode_u: wgpu::AddressMode::ClampToEdge, diff --git a/wgpu/src/window.rs b/wgpu/src/window.rs index aac5fb9e..9545a14e 100644 --- a/wgpu/src/window.rs +++ b/wgpu/src/window.rs @@ -1,4 +1,5 @@  //! Display rendering results on windows. -mod compositor; +pub mod compositor;  pub use compositor::Compositor; +pub use wgpu::Surface; diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 365cb603..a67ac3c0 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -1,9 +1,12 @@ -use crate::{Backend, Color, Error, Renderer, Settings, Viewport}; +//! Connect a window with a renderer. +use crate::core::Color; +use crate::graphics; +use crate::graphics::compositor; +use crate::graphics::{Error, Primitive, Viewport}; +use crate::{Backend, Renderer, Settings};  use futures::stream::{self, StreamExt}; -use iced_graphics::compositor; -use iced_native::futures;  use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};  use std::marker::PhantomData; @@ -112,7 +115,78 @@ impl<Theme> Compositor<Theme> {      }  } -impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> { +/// Creates a [`Compositor`] and its [`Backend`] for the given [`Settings`] and +/// window. +pub fn new<Theme, W: HasRawWindowHandle + HasRawDisplayHandle>( +    settings: Settings, +    compatible_window: Option<&W>, +) -> Result<(Compositor<Theme>, Backend), Error> { +    let compositor = futures::executor::block_on(Compositor::request( +        settings, +        compatible_window, +    )) +    .ok_or(Error::GraphicsAdapterNotFound)?; + +    let backend = compositor.create_backend(); + +    Ok((compositor, backend)) +} + +/// Presents the given primitives with the given [`Compositor`] and [`Backend`]. +pub fn present<Theme, T: AsRef<str>>( +    compositor: &mut Compositor<Theme>, +    backend: &mut Backend, +    surface: &mut wgpu::Surface, +    primitives: &[Primitive], +    viewport: &Viewport, +    background_color: Color, +    overlay: &[T], +) -> Result<(), compositor::SurfaceError> { +    match surface.get_current_texture() { +        Ok(frame) => { +            let mut encoder = compositor.device.create_command_encoder( +                &wgpu::CommandEncoderDescriptor { +                    label: Some("iced_wgpu encoder"), +                }, +            ); + +            let view = &frame +                .texture +                .create_view(&wgpu::TextureViewDescriptor::default()); + +            backend.present( +                &compositor.device, +                &compositor.queue, +                &mut encoder, +                Some(background_color), +                view, +                primitives, +                viewport, +                overlay, +            ); + +            // Submit work +            let _submission = compositor.queue.submit(Some(encoder.finish())); +            frame.present(); + +            Ok(()) +        } +        Err(error) => match error { +            wgpu::SurfaceError::Timeout => { +                Err(compositor::SurfaceError::Timeout) +            } +            wgpu::SurfaceError::Outdated => { +                Err(compositor::SurfaceError::Outdated) +            } +            wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost), +            wgpu::SurfaceError::OutOfMemory => { +                Err(compositor::SurfaceError::OutOfMemory) +            } +        }, +    } +} + +impl<Theme> graphics::Compositor for Compositor<Theme> {      type Settings = Settings;      type Renderer = Renderer<Theme>;      type Surface = wgpu::Surface; @@ -121,13 +195,7 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {          settings: Self::Settings,          compatible_window: Option<&W>,      ) -> Result<(Self, Self::Renderer), Error> { -        let compositor = futures::executor::block_on(Self::request( -            settings, -            compatible_window, -        )) -        .ok_or(Error::GraphicsAdapterNotFound)?; - -        let backend = compositor.create_backend(); +        let (compositor, backend) = new(settings, compatible_window)?;          Ok((compositor, Renderer::new(backend)))      } @@ -135,11 +203,15 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {      fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(          &mut self,          window: &W, +        width: u32, +        height: u32,      ) -> wgpu::Surface {          #[allow(unsafe_code)] -        unsafe { -            self.instance.create_surface(window) -        } +        let mut surface = unsafe { self.instance.create_surface(window) }; + +        self.configure_surface(&mut surface, width, height); + +        surface      }      fn configure_surface( @@ -178,49 +250,16 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {          background_color: Color,          overlay: &[T],      ) -> Result<(), compositor::SurfaceError> { -        match surface.get_current_texture() { -            Ok(frame) => { -                let mut encoder = self.device.create_command_encoder( -                    &wgpu::CommandEncoderDescriptor { -                        label: Some("iced_wgpu encoder"), -                    }, -                ); - -                let view = &frame -                    .texture -                    .create_view(&wgpu::TextureViewDescriptor::default()); - -                renderer.with_primitives(|backend, primitives| { -                    backend.present( -                        &self.device, -                        &self.queue, -                        &mut encoder, -                        Some(background_color), -                        view, -                        primitives, -                        viewport, -                        overlay, -                    ); -                }); - -                // Submit work -                let _submission = self.queue.submit(Some(encoder.finish())); -                frame.present(); - -                Ok(()) -            } -            Err(error) => match error { -                wgpu::SurfaceError::Timeout => { -                    Err(compositor::SurfaceError::Timeout) -                } -                wgpu::SurfaceError::Outdated => { -                    Err(compositor::SurfaceError::Outdated) -                } -                wgpu::SurfaceError::Lost => Err(compositor::SurfaceError::Lost), -                wgpu::SurfaceError::OutOfMemory => { -                    Err(compositor::SurfaceError::OutOfMemory) -                } -            }, -        } +        renderer.with_primitives(|backend, primitives| { +            present( +                self, +                backend, +                surface, +                primitives, +                viewport, +                background_color, +                overlay, +            ) +        })      }  } | 
