From 95ff96f71f8f8069608ad08985e2b1214dd42284 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Fri, 7 Jul 2023 07:12:37 +0200
Subject: Update `cosmic-text` and `glyphon`

---
 wgpu/src/backend.rs | 53 +++++++++++++----------------------------------------
 wgpu/src/text.rs    | 22 ++++++----------------
 2 files changed, 19 insertions(+), 56 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 4a0c54f0..9966a38c 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -94,18 +94,11 @@ impl Backend {
             queue,
             encoder,
             scale_factor,
+            target_size,
             transformation,
             &layers,
         );
 
-        while !self.prepare_text(
-            device,
-            queue,
-            scale_factor,
-            target_size,
-            &layers,
-        ) {}
-
         self.render(
             device,
             encoder,
@@ -124,44 +117,13 @@ impl Backend {
         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 {
-            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;
-            }
-        }
-
-        true
-    }
-
     fn prepare(
         &mut self,
         device: &wgpu::Device,
         queue: &wgpu::Queue,
         _encoder: &mut wgpu::CommandEncoder,
         scale_factor: f32,
+        target_size: Size<u32>,
         transformation: Transformation,
         layers: &[Layer<'_>],
     ) {
@@ -210,6 +172,17 @@ impl Backend {
                     );
                 }
             }
+
+            if !layer.text.is_empty() {
+                self.text_pipeline.prepare(
+                    device,
+                    queue,
+                    &layer.text,
+                    layer.bounds,
+                    scale_factor,
+                    target_size,
+                );
+            }
         }
     }
 
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 65d3b818..ef910c39 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -35,7 +35,7 @@ impl Pipeline {
                 .into_iter(),
             )),
             renderers: Vec::new(),
-            atlas: glyphon::TextAtlas::new(
+            atlas: glyphon::TextAtlas::with_color_mode(
                 device,
                 queue,
                 format,
@@ -66,7 +66,7 @@ impl Pipeline {
         bounds: Rectangle,
         scale_factor: f32,
         target_size: Size<u32>,
-    ) -> bool {
+    ) {
         if self.renderers.len() <= self.prepare_layer {
             self.renderers.push(glyphon::TextRenderer::new(
                 &mut self.atlas,
@@ -188,21 +188,11 @@ impl Pipeline {
         match result {
             Ok(()) => {
                 self.prepare_layer += 1;
-
-                true
             }
-            Err(glyphon::PrepareError::AtlasFull(content_type)) => {
-                self.prepare_layer = 0;
-
-                #[allow(clippy::needless_bool)]
-                if self.atlas.grow(device, content_type) {
-                    false
-                } else {
-                    // If the atlas cannot grow, then all bets are off.
-                    // Instead of panicking, we will just pray that the result
-                    // will be somewhat readable...
-                    true
-                }
+            Err(glyphon::PrepareError::AtlasFull) => {
+                // If the atlas cannot grow, then all bets are off.
+                // Instead of panicking, we will just pray that the result
+                // will be somewhat readable...
             }
         }
     }
-- 
cgit 


From 126aef88e7647c4690055b4c96aee46ecadcf60e Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Fri, 28 Jul 2023 19:48:39 +0200
Subject: Bump versions :tada:

---
 wgpu/src/lib.rs | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index deb223ef..d1e4b7af 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -1,25 +1,22 @@
-//! A [`wgpu`] renderer for [`iced_native`].
+//! A [`wgpu`] renderer for [Iced].
 //!
 //! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true)
 //!
-//! For now, it is the default renderer of [Iced] in native platforms.
-//!
 //! [`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and
 //! DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the
 //! incoming [WebGPU API].
 //!
 //! Currently, `iced_wgpu` supports the following primitives:
-//! - Text, which is rendered using [`wgpu_glyph`]. No shaping at all.
+//! - Text, which is rendered using [`glyphon`].
 //! - Quads or rectangles, with rounded borders and a solid background color.
 //! - Clip areas, useful to implement scrollables or hide overflowing content.
 //! - Images and SVG, loaded from memory or the file system.
 //! - Meshes of triangles, useful to draw geometry freely.
 //!
 //! [Iced]: https://github.com/iced-rs/iced
-//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native
 //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
 //! [WebGPU API]: https://gpuweb.github.io/gpuweb/
-//! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
+//! [`glyphon`]: https://github.com/grovesNL/glyphon
 #![doc(
     html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"
 )]
-- 
cgit 


From c7e17391c53c9aa076d76982189a5184681aac0c Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 2 Aug 2023 22:05:11 +0200
Subject: Fix `iced_wgpu` freezing on empty layers

The `render` method would return when an empty layer is encountered without explicitly dropping the `RenderPass` (necessary because we use `ManuallyDrop`), which would then leak memory and freeze `wgpu` until the surface was recreated.
---
 wgpu/src/backend.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 9966a38c..c6a17f2c 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -236,7 +236,7 @@ impl Backend {
             let bounds = (layer.bounds * scale_factor).snap();
 
             if bounds.width < 1 || bounds.height < 1 {
-                return;
+                continue;
             }
 
             if !layer.quads.is_empty() {
-- 
cgit 


From a7609a723ff0102ef8f606ac2c3bbabc30974db9 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 2 Aug 2023 22:08:14 +0200
Subject: Avoid empty overlay layer in `iced_wgpu`

---
 wgpu/src/backend.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index c6a17f2c..68d1f805 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -87,7 +87,10 @@ impl Backend {
         let transformation = viewport.projection();
 
         let mut layers = Layer::generate(primitives, viewport);
-        layers.push(Layer::overlay(overlay_text, viewport));
+
+        if !overlay_text.is_empty() {
+            layers.push(Layer::overlay(overlay_text, viewport));
+        }
 
         self.prepare(
             device,
-- 
cgit 


From e86363837d8e3a6241a90cb5b895034f07106059 Mon Sep 17 00:00:00 2001
From: lufte <javier@lufte.net>
Date: Fri, 18 Aug 2023 18:46:22 -0300
Subject: Make the style attribute available on Font

---
 wgpu/src/text.rs | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index ef910c39..fb13545d 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -339,6 +339,14 @@ fn to_stretch(stretch: font::Stretch) -> glyphon::Stretch {
     }
 }
 
+fn to_style(style: font::Style) -> glyphon::Style {
+    match style {
+        font::Style::Normal => glyphon::Style::Normal,
+        font::Style::Italic => glyphon::Style::Italic,
+        font::Style::Oblique => glyphon::Style::Oblique,
+    }
+}
+
 fn to_shaping(shaping: Shaping) -> glyphon::Shaping {
     match shaping {
         Shaping::Basic => glyphon::Shaping::Basic,
@@ -420,7 +428,8 @@ impl Cache {
                 glyphon::Attrs::new()
                     .family(to_family(key.font.family))
                     .weight(to_weight(key.font.weight))
-                    .stretch(to_stretch(key.font.stretch)),
+                    .stretch(to_stretch(key.font.stretch))
+                    .style(to_style(key.font.style)),
                 to_shaping(key.shaping),
             );
 
-- 
cgit 


From ed3454301e663a7cb7d73cd56b57b188f4d14a2f Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 30 Aug 2023 04:31:21 +0200
Subject: Implement explicit text caching in the widget state tree

---
 wgpu/src/backend.rs           |  75 +------
 wgpu/src/layer.rs             |  29 ++-
 wgpu/src/layer/text.rs        |  19 +-
 wgpu/src/lib.rs               |   2 +-
 wgpu/src/settings.rs          |   6 +-
 wgpu/src/text.rs              | 509 +++++++++++-------------------------------
 wgpu/src/window/compositor.rs |   9 +-
 7 files changed, 190 insertions(+), 459 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 68d1f805..65c63f19 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -1,5 +1,5 @@
-use crate::core;
-use crate::core::{Color, Font, Point, Size};
+use crate::core::{Color, Size};
+use crate::graphics;
 use crate::graphics::backend;
 use crate::graphics::color;
 use crate::graphics::{Transformation, Viewport};
@@ -29,9 +29,6 @@ pub struct Backend {
 
     #[cfg(any(feature = "image", feature = "svg"))]
     image_pipeline: image::Pipeline,
-
-    default_font: Font,
-    default_text_size: f32,
 }
 
 impl Backend {
@@ -57,9 +54,6 @@ impl Backend {
 
             #[cfg(any(feature = "image", feature = "svg"))]
             image_pipeline,
-
-            default_font: settings.default_font,
-            default_text_size: settings.default_text_size,
         }
     }
 
@@ -313,65 +307,11 @@ impl Backend {
 
 impl crate::graphics::Backend for Backend {
     type Primitive = primitive::Custom;
-
-    fn trim_measurements(&mut self) {
-        self.text_pipeline.trim_measurements();
-    }
 }
 
 impl backend::Text for Backend {
-    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
-    }
-
-    fn measure(
-        &self,
-        contents: &str,
-        size: f32,
-        line_height: core::text::LineHeight,
-        font: Font,
-        bounds: Size,
-        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,
-        shaping: core::text::Shaping,
-        point: Point,
-        nearest_only: bool,
-    ) -> Option<core::text::Hit> {
-        self.text_pipeline.hit_test(
-            contents,
-            size,
-            line_height,
-            font,
-            bounds,
-            shaping,
-            point,
-            nearest_only,
-        )
+    fn font_system(&self) -> &graphics::text::FontSystem {
+        self.text_pipeline.font_system()
     }
 
     fn load_font(&mut self, font: Cow<'static, [u8]>) {
@@ -381,14 +321,17 @@ impl backend::Text for Backend {
 
 #[cfg(feature = "image")]
 impl backend::Image for Backend {
-    fn dimensions(&self, handle: &core::image::Handle) -> Size<u32> {
+    fn dimensions(&self, handle: &crate::core::image::Handle) -> Size<u32> {
         self.image_pipeline.dimensions(handle)
     }
 }
 
 #[cfg(feature = "svg")]
 impl backend::Svg for Backend {
-    fn viewport_dimensions(&self, handle: &core::svg::Handle) -> Size<u32> {
+    fn viewport_dimensions(
+        &self,
+        handle: &crate::core::svg::Handle,
+    ) -> Size<u32> {
         self.image_pipeline.viewport_dimensions(handle)
     }
 }
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index b8f32db1..7a5a0f7c 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -10,7 +10,7 @@ pub use text::Text;
 
 use crate::core;
 use crate::core::alignment;
-use crate::core::{Color, Font, Point, Rectangle, Size, Vector};
+use crate::core::{Color, Font, Pixels, Point, Rectangle, Size, Vector};
 use crate::graphics;
 use crate::graphics::color;
 use crate::graphics::Viewport;
@@ -56,14 +56,14 @@ impl<'a> Layer<'a> {
             Layer::new(Rectangle::with_size(viewport.logical_size()));
 
         for (i, line) in lines.iter().enumerate() {
-            let text = Text {
+            let text = text::Cached {
                 content: line.as_ref(),
                 bounds: Rectangle::new(
                     Point::new(11.0, 11.0 + 25.0 * i as f32),
                     Size::INFINITY,
                 ),
                 color: Color::new(0.9, 0.9, 0.9, 1.0),
-                size: 20.0,
+                size: Pixels(20.0),
                 line_height: core::text::LineHeight::default(),
                 font: Font::MONOSPACE,
                 horizontal_alignment: alignment::Horizontal::Left,
@@ -71,13 +71,13 @@ impl<'a> Layer<'a> {
                 shaping: core::text::Shaping::Basic,
             };
 
-            overlay.text.push(text);
+            overlay.text.push(Text::Cached(text.clone()));
 
-            overlay.text.push(Text {
+            overlay.text.push(Text::Cached(text::Cached {
                 bounds: text.bounds + Vector::new(-1.0, -1.0),
                 color: Color::BLACK,
                 ..text
-            });
+            }));
         }
 
         overlay
@@ -113,6 +113,19 @@ impl<'a> Layer<'a> {
         current_layer: usize,
     ) {
         match primitive {
+            Primitive::Paragraph {
+                paragraph,
+                position,
+                color,
+            } => {
+                let layer = &mut layers[current_layer];
+
+                layer.text.push(Text::Managed {
+                    paragraph: paragraph.clone(),
+                    position: *position + translation,
+                    color: *color,
+                });
+            }
             Primitive::Text {
                 content,
                 bounds,
@@ -126,7 +139,7 @@ impl<'a> Layer<'a> {
             } => {
                 let layer = &mut layers[current_layer];
 
-                layer.text.push(Text {
+                layer.text.push(Text::Cached(text::Cached {
                     content,
                     bounds: *bounds + translation,
                     size: *size,
@@ -136,7 +149,7 @@ impl<'a> Layer<'a> {
                     horizontal_alignment: *horizontal_alignment,
                     vertical_alignment: *vertical_alignment,
                     shaping: *shaping,
-                });
+                }));
             }
             Primitive::Quad {
                 bounds,
diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs
index ba1bdca8..b61615d6 100644
--- a/wgpu/src/layer/text.rs
+++ b/wgpu/src/layer/text.rs
@@ -1,10 +1,21 @@
 use crate::core::alignment;
 use crate::core::text;
-use crate::core::{Color, Font, Rectangle};
+use crate::core::{Color, Font, Pixels, Point, Rectangle};
+use crate::graphics::text::paragraph;
 
 /// A paragraph of text.
-#[derive(Debug, Clone, Copy)]
-pub struct Text<'a> {
+#[derive(Debug, Clone)]
+pub enum Text<'a> {
+    Managed {
+        paragraph: paragraph::Weak,
+        position: Point,
+        color: Color,
+    },
+    Cached(Cached<'a>),
+}
+
+#[derive(Debug, Clone)]
+pub struct Cached<'a> {
     /// The content of the [`Text`].
     pub content: &'a str,
 
@@ -15,7 +26,7 @@ pub struct Text<'a> {
     pub color: Color,
 
     /// The size of the [`Text`] in logical pixels.
-    pub size: f32,
+    pub size: Pixels,
 
     /// The line height of the [`Text`].
     pub line_height: text::LineHeight,
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index d1e4b7af..cd457072 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -22,7 +22,7 @@
 )]
 #![deny(
     missing_debug_implementations,
-    missing_docs,
+    //missing_docs,
     unsafe_code,
     unused_results,
     clippy::extra_unused_lifetimes,
diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs
index 266a2c87..c9338fec 100644
--- a/wgpu/src/settings.rs
+++ b/wgpu/src/settings.rs
@@ -1,5 +1,5 @@
 //! Configure a renderer.
-use crate::core::Font;
+use crate::core::{Font, Pixels};
 use crate::graphics::Antialiasing;
 
 /// The settings of a [`Backend`].
@@ -21,7 +21,7 @@ pub struct Settings {
     /// The default size of text.
     ///
     /// By default, it will be set to `16.0`.
-    pub default_text_size: f32,
+    pub default_text_size: Pixels,
 
     /// The antialiasing strategy that will be used for triangle primitives.
     ///
@@ -59,7 +59,7 @@ impl Default for Settings {
             present_mode: wgpu::PresentMode::AutoVsync,
             internal_backend: wgpu::Backends::all(),
             default_font: Font::default(),
-            default_text_size: 16.0,
+            default_text_size: Pixels(16.0),
             antialiasing: None,
         }
     }
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index fb13545d..da2062fe 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -1,20 +1,17 @@
 use crate::core::alignment;
-use crate::core::font::{self, Font};
-use crate::core::text::{Hit, LineHeight, Shaping};
-use crate::core::{Pixels, Point, Rectangle, Size};
+use crate::core::{Rectangle, Size};
 use crate::graphics::color;
+use crate::graphics::text::cache::{self, Cache};
+use crate::graphics::text::{FontSystem, Paragraph};
 use crate::layer::Text;
 
-use rustc_hash::{FxHashMap, FxHashSet};
 use std::borrow::Cow;
 use std::cell::RefCell;
-use std::collections::hash_map;
-use std::hash::{BuildHasher, Hash, Hasher};
 use std::sync::Arc;
 
 #[allow(missing_debug_implementations)]
 pub struct Pipeline {
-    font_system: RefCell<glyphon::FontSystem>,
+    font_system: FontSystem,
     renderers: Vec<glyphon::TextRenderer>,
     atlas: glyphon::TextAtlas,
     prepare_layer: usize,
@@ -28,12 +25,7 @@ impl Pipeline {
         format: wgpu::TextureFormat,
     ) -> Self {
         Pipeline {
-            font_system: RefCell::new(glyphon::FontSystem::new_with_fonts(
-                [glyphon::fontdb::Source::Binary(Arc::new(
-                    include_bytes!("../fonts/Iced-Icons.ttf").as_slice(),
-                ))]
-                .into_iter(),
-            )),
+            font_system: FontSystem::new(),
             renderers: Vec::new(),
             atlas: glyphon::TextAtlas::with_color_mode(
                 device,
@@ -50,6 +42,10 @@ impl Pipeline {
         }
     }
 
+    pub fn font_system(&self) -> &FontSystem {
+        &self.font_system
+    }
+
     pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
         let _ = self.font_system.get_mut().db_mut().load_font_source(
             glyphon::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
@@ -80,97 +76,137 @@ impl Pipeline {
         let renderer = &mut self.renderers[self.prepare_layer];
         let cache = self.cache.get_mut();
 
-        if self.prepare_layer == 0 {
-            cache.trim(Purpose::Drawing);
+        enum Allocation {
+            Paragraph(Paragraph),
+            Cache(cache::KeyHash),
         }
 
-        let keys: Vec<_> = sections
+        let allocations: Vec<_> = sections
             .iter()
-            .map(|section| {
-                let (key, _) = cache.allocate(
-                    font_system,
-                    Key {
-                        content: section.content,
-                        size: section.size,
-                        line_height: f32::from(
-                            section
-                                .line_height
-                                .to_absolute(Pixels(section.size)),
-                        ),
-                        font: section.font,
-                        bounds: Size {
-                            width: section.bounds.width,
-                            height: section.bounds.height,
+            .map(|section| match section {
+                Text::Managed { paragraph, .. } => {
+                    paragraph.upgrade().map(Allocation::Paragraph)
+                }
+                Text::Cached(text) => {
+                    let (key, _) = cache.allocate(
+                        font_system,
+                        cache::Key {
+                            content: text.content,
+                            size: text.size.into(),
+                            line_height: f32::from(
+                                text.line_height.to_absolute(text.size),
+                            ),
+                            font: text.font,
+                            bounds: Size {
+                                width: bounds.width,
+                                height: bounds.height,
+                            },
+                            shaping: text.shaping,
                         },
-                        shaping: section.shaping,
-                    },
-                    Purpose::Drawing,
-                );
+                    );
 
-                key
+                    Some(Allocation::Cache(key))
+                }
             })
             .collect();
 
-        let bounds = bounds * scale_factor;
-
-        let text_areas =
-            sections
-                .iter()
-                .zip(keys.iter())
-                .filter_map(|(section, key)| {
-                    let entry = cache.get(key).expect("Get cached buffer");
-
-                    let x = section.bounds.x * scale_factor;
-                    let y = section.bounds.y * scale_factor;
-
-                    let max_width = entry.bounds.width * scale_factor;
-                    let total_height = entry.bounds.height * scale_factor;
-
-                    let left = match section.horizontal_alignment {
-                        alignment::Horizontal::Left => x,
-                        alignment::Horizontal::Center => x - max_width / 2.0,
-                        alignment::Horizontal::Right => x - max_width,
-                    };
-
-                    let top = match section.vertical_alignment {
-                        alignment::Vertical::Top => y,
-                        alignment::Vertical::Center => y - total_height / 2.0,
-                        alignment::Vertical::Bottom => y - total_height,
-                    };
-
-                    let section_bounds = Rectangle {
-                        x: left,
-                        y: top,
-                        width: section.bounds.width * scale_factor,
-                        height: section.bounds.height * scale_factor,
-                    };
-
-                    let clip_bounds = bounds.intersection(&section_bounds)?;
-
-                    Some(glyphon::TextArea {
-                        buffer: &entry.buffer,
-                        left,
-                        top,
-                        scale: scale_factor,
-                        bounds: glyphon::TextBounds {
-                            left: clip_bounds.x as i32,
-                            top: clip_bounds.y as i32,
-                            right: (clip_bounds.x + clip_bounds.width) as i32,
-                            bottom: (clip_bounds.y + clip_bounds.height) as i32,
-                        },
-                        default_color: {
-                            let [r, g, b, a] =
-                                color::pack(section.color).components();
-
-                            glyphon::Color::rgba(
-                                (r * 255.0) as u8,
-                                (g * 255.0) as u8,
-                                (b * 255.0) as u8,
-                                (a * 255.0) as u8,
-                            )
-                        },
-                    })
-                });
+        let layer_bounds = bounds * scale_factor;
+
+        let text_areas = sections.iter().zip(allocations.iter()).filter_map(
+            |(section, allocation)| {
+                let (
+                    buffer,
+                    bounds,
+                    horizontal_alignment,
+                    vertical_alignment,
+                    color,
+                ) = match section {
+                    Text::Managed {
+                        position, color, ..
+                    } => {
+                        use crate::core::text::Paragraph as _;
+
+                        let Some(Allocation::Paragraph(paragraph)) = allocation
+                        else {
+                            return None;
+                        };
+
+                        (
+                            paragraph.buffer(),
+                            Rectangle::new(*position, paragraph.min_bounds()),
+                            paragraph.horizontal_alignment(),
+                            paragraph.vertical_alignment(),
+                            *color,
+                        )
+                    }
+                    Text::Cached(text) => {
+                        let Some(Allocation::Cache(key)) = allocation else {
+                            return None;
+                        };
+
+                        let buffer = cache.get(key).expect("Get cached buffer");
+
+                        (
+                            buffer,
+                            text.bounds,
+                            text.horizontal_alignment,
+                            text.vertical_alignment,
+                            text.color,
+                        )
+                    }
+                };
+
+                let x = bounds.x * scale_factor;
+                let y = bounds.y * scale_factor;
+
+                let max_width = bounds.width * scale_factor;
+                let total_height = bounds.height * scale_factor;
+
+                let left = match horizontal_alignment {
+                    alignment::Horizontal::Left => x,
+                    alignment::Horizontal::Center => x - max_width / 2.0,
+                    alignment::Horizontal::Right => x - max_width,
+                };
+
+                let top = match vertical_alignment {
+                    alignment::Vertical::Top => y,
+                    alignment::Vertical::Center => y - total_height / 2.0,
+                    alignment::Vertical::Bottom => y - total_height,
+                };
+
+                let section_bounds = Rectangle {
+                    x: left,
+                    y: top,
+                    width: max_width,
+                    height: total_height,
+                };
+
+                let clip_bounds = layer_bounds.intersection(&section_bounds)?;
+
+                Some(glyphon::TextArea {
+                    buffer,
+                    left,
+                    top,
+                    scale: scale_factor,
+                    bounds: glyphon::TextBounds {
+                        left: clip_bounds.x as i32,
+                        top: clip_bounds.y as i32,
+                        right: (clip_bounds.x + clip_bounds.width) as i32,
+                        bottom: (clip_bounds.y + clip_bounds.height) as i32,
+                    },
+                    default_color: {
+                        let [r, g, b, a] = color::pack(color).components();
+
+                        glyphon::Color::rgba(
+                            (r * 255.0) as u8,
+                            (g * 255.0) as u8,
+                            (b * 255.0) as u8,
+                            (a * 255.0) as u8,
+                        )
+                    },
+                })
+            },
+        );
 
         let result = renderer.prepare(
             device,
@@ -219,287 +255,8 @@ impl Pipeline {
 
     pub fn end_frame(&mut self) {
         self.atlas.trim();
+        self.cache.get_mut().trim();
 
         self.prepare_layer = 0;
     }
-
-    pub fn trim_measurements(&mut self) {
-        self.cache.get_mut().trim(Purpose::Measuring);
-    }
-
-    pub fn measure(
-        &self,
-        content: &str,
-        size: f32,
-        line_height: LineHeight,
-        font: Font,
-        bounds: Size,
-        shaping: Shaping,
-    ) -> Size {
-        let mut cache = self.cache.borrow_mut();
-
-        let line_height = f32::from(line_height.to_absolute(Pixels(size)));
-
-        let (_, entry) = cache.allocate(
-            &mut self.font_system.borrow_mut(),
-            Key {
-                content,
-                size,
-                line_height,
-                font,
-                bounds,
-                shaping,
-            },
-            Purpose::Measuring,
-        );
-
-        entry.bounds
-    }
-
-    pub fn hit_test(
-        &self,
-        content: &str,
-        size: f32,
-        line_height: LineHeight,
-        font: Font,
-        bounds: Size,
-        shaping: Shaping,
-        point: Point,
-        _nearest_only: bool,
-    ) -> Option<Hit> {
-        let mut cache = self.cache.borrow_mut();
-
-        let line_height = f32::from(line_height.to_absolute(Pixels(size)));
-
-        let (_, entry) = cache.allocate(
-            &mut self.font_system.borrow_mut(),
-            Key {
-                content,
-                size,
-                line_height,
-                font,
-                bounds,
-                shaping,
-            },
-            Purpose::Measuring,
-        );
-
-        let cursor = entry.buffer.hit(point.x, point.y)?;
-
-        Some(Hit::CharOffset(cursor.index))
-    }
 }
-
-fn measure(buffer: &glyphon::Buffer) -> Size {
-    let (width, total_lines) = buffer
-        .layout_runs()
-        .fold((0.0, 0usize), |(width, total_lines), run| {
-            (run.line_w.max(width), total_lines + 1)
-        });
-
-    Size::new(width, total_lines as f32 * buffer.metrics().line_height)
-}
-
-fn to_family(family: font::Family) -> glyphon::Family<'static> {
-    match family {
-        font::Family::Name(name) => glyphon::Family::Name(name),
-        font::Family::SansSerif => glyphon::Family::SansSerif,
-        font::Family::Serif => glyphon::Family::Serif,
-        font::Family::Cursive => glyphon::Family::Cursive,
-        font::Family::Fantasy => glyphon::Family::Fantasy,
-        font::Family::Monospace => glyphon::Family::Monospace,
-    }
-}
-
-fn to_weight(weight: font::Weight) -> glyphon::Weight {
-    match weight {
-        font::Weight::Thin => glyphon::Weight::THIN,
-        font::Weight::ExtraLight => glyphon::Weight::EXTRA_LIGHT,
-        font::Weight::Light => glyphon::Weight::LIGHT,
-        font::Weight::Normal => glyphon::Weight::NORMAL,
-        font::Weight::Medium => glyphon::Weight::MEDIUM,
-        font::Weight::Semibold => glyphon::Weight::SEMIBOLD,
-        font::Weight::Bold => glyphon::Weight::BOLD,
-        font::Weight::ExtraBold => glyphon::Weight::EXTRA_BOLD,
-        font::Weight::Black => glyphon::Weight::BLACK,
-    }
-}
-
-fn to_stretch(stretch: font::Stretch) -> glyphon::Stretch {
-    match stretch {
-        font::Stretch::UltraCondensed => glyphon::Stretch::UltraCondensed,
-        font::Stretch::ExtraCondensed => glyphon::Stretch::ExtraCondensed,
-        font::Stretch::Condensed => glyphon::Stretch::Condensed,
-        font::Stretch::SemiCondensed => glyphon::Stretch::SemiCondensed,
-        font::Stretch::Normal => glyphon::Stretch::Normal,
-        font::Stretch::SemiExpanded => glyphon::Stretch::SemiExpanded,
-        font::Stretch::Expanded => glyphon::Stretch::Expanded,
-        font::Stretch::ExtraExpanded => glyphon::Stretch::ExtraExpanded,
-        font::Stretch::UltraExpanded => glyphon::Stretch::UltraExpanded,
-    }
-}
-
-fn to_style(style: font::Style) -> glyphon::Style {
-    match style {
-        font::Style::Normal => glyphon::Style::Normal,
-        font::Style::Italic => glyphon::Style::Italic,
-        font::Style::Oblique => glyphon::Style::Oblique,
-    }
-}
-
-fn to_shaping(shaping: Shaping) -> glyphon::Shaping {
-    match shaping {
-        Shaping::Basic => glyphon::Shaping::Basic,
-        Shaping::Advanced => glyphon::Shaping::Advanced,
-    }
-}
-
-struct Cache {
-    entries: FxHashMap<KeyHash, Entry>,
-    aliases: FxHashMap<KeyHash, KeyHash>,
-    recently_measured: FxHashSet<KeyHash>,
-    recently_drawn: FxHashSet<KeyHash>,
-    hasher: HashBuilder,
-}
-
-struct Entry {
-    buffer: glyphon::Buffer,
-    bounds: Size,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum Purpose {
-    Measuring,
-    Drawing,
-}
-
-#[cfg(not(target_arch = "wasm32"))]
-type HashBuilder = twox_hash::RandomXxHashBuilder64;
-
-#[cfg(target_arch = "wasm32")]
-type HashBuilder = std::hash::BuildHasherDefault<twox_hash::XxHash64>;
-
-impl Cache {
-    fn new() -> Self {
-        Self {
-            entries: FxHashMap::default(),
-            aliases: FxHashMap::default(),
-            recently_measured: FxHashSet::default(),
-            recently_drawn: FxHashSet::default(),
-            hasher: HashBuilder::default(),
-        }
-    }
-
-    fn get(&self, key: &KeyHash) -> Option<&Entry> {
-        self.entries.get(key)
-    }
-
-    fn allocate(
-        &mut self,
-        font_system: &mut glyphon::FontSystem,
-        key: Key<'_>,
-        purpose: Purpose,
-    ) -> (KeyHash, &mut Entry) {
-        let hash = key.hash(self.hasher.build_hasher());
-
-        let recently_used = match purpose {
-            Purpose::Measuring => &mut self.recently_measured,
-            Purpose::Drawing => &mut self.recently_drawn,
-        };
-
-        if let Some(hash) = self.aliases.get(&hash) {
-            let _ = recently_used.insert(*hash);
-
-            return (*hash, self.entries.get_mut(hash).unwrap());
-        }
-
-        if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
-            let metrics = glyphon::Metrics::new(key.size, key.line_height);
-            let mut buffer = glyphon::Buffer::new(font_system, metrics);
-
-            buffer.set_size(
-                font_system,
-                key.bounds.width,
-                key.bounds.height.max(key.line_height),
-            );
-            buffer.set_text(
-                font_system,
-                key.content,
-                glyphon::Attrs::new()
-                    .family(to_family(key.font.family))
-                    .weight(to_weight(key.font.weight))
-                    .stretch(to_stretch(key.font.stretch))
-                    .style(to_style(key.font.style)),
-                to_shaping(key.shaping),
-            );
-
-            let bounds = measure(&buffer);
-            let _ = entry.insert(Entry { buffer, bounds });
-
-            for bounds in [
-                bounds,
-                Size {
-                    width: key.bounds.width,
-                    ..bounds
-                },
-            ] {
-                if key.bounds != bounds {
-                    let _ = self.aliases.insert(
-                        Key { bounds, ..key }.hash(self.hasher.build_hasher()),
-                        hash,
-                    );
-                }
-            }
-        }
-
-        let _ = recently_used.insert(hash);
-
-        (hash, self.entries.get_mut(&hash).unwrap())
-    }
-
-    fn trim(&mut self, purpose: Purpose) {
-        self.entries.retain(|key, _| {
-            self.recently_measured.contains(key)
-                || self.recently_drawn.contains(key)
-        });
-        self.aliases.retain(|_, value| {
-            self.recently_measured.contains(value)
-                || self.recently_drawn.contains(value)
-        });
-
-        match purpose {
-            Purpose::Measuring => {
-                self.recently_measured.clear();
-            }
-            Purpose::Drawing => {
-                self.recently_drawn.clear();
-            }
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy)]
-struct Key<'a> {
-    content: &'a str,
-    size: f32,
-    line_height: f32,
-    font: Font,
-    bounds: Size,
-    shaping: Shaping,
-}
-
-impl Key<'_> {
-    fn hash<H: Hasher>(self, mut hasher: H) -> KeyHash {
-        self.content.hash(&mut hasher);
-        self.size.to_bits().hash(&mut hasher);
-        self.line_height.to_bits().hash(&mut hasher);
-        self.font.hash(&mut hasher);
-        self.bounds.width.to_bits().hash(&mut hasher);
-        self.bounds.height.to_bits().hash(&mut hasher);
-        self.shaping.hash(&mut hasher);
-
-        hasher.finish()
-    }
-}
-
-type KeyHash = u64;
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index cd5b20cc..09e11fdc 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -216,7 +216,14 @@ impl<Theme> graphics::Compositor for Compositor<Theme> {
     ) -> Result<(Self, Self::Renderer), Error> {
         let (compositor, backend) = new(settings, compatible_window)?;
 
-        Ok((compositor, Renderer::new(backend)))
+        Ok((
+            compositor,
+            Renderer::new(
+                backend,
+                settings.default_font,
+                settings.default_text_size,
+            ),
+        ))
     }
 
     fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
-- 
cgit 


From 89acf0217e0acd92a82bff1fd516cd4266c0878a Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Wed, 30 Aug 2023 05:06:08 +0200
Subject: Use `min_bounds` for cached text

---
 wgpu/src/text.rs | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index da2062fe..ee352368 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -59,7 +59,7 @@ impl Pipeline {
         device: &wgpu::Device,
         queue: &wgpu::Queue,
         sections: &[Text<'_>],
-        bounds: Rectangle,
+        layer_bounds: Rectangle,
         scale_factor: f32,
         target_size: Size<u32>,
     ) {
@@ -98,8 +98,8 @@ impl Pipeline {
                             ),
                             font: text.font,
                             bounds: Size {
-                                width: bounds.width,
-                                height: bounds.height,
+                                width: text.bounds.width,
+                                height: text.bounds.height,
                             },
                             shaping: text.shaping,
                         },
@@ -110,7 +110,7 @@ impl Pipeline {
             })
             .collect();
 
-        let layer_bounds = bounds * scale_factor;
+        let layer_bounds = layer_bounds * scale_factor;
 
         let text_areas = sections.iter().zip(allocations.iter()).filter_map(
             |(section, allocation)| {
@@ -144,11 +144,14 @@ impl Pipeline {
                             return None;
                         };
 
-                        let buffer = cache.get(key).expect("Get cached buffer");
+                        let entry = cache.get(key).expect("Get cached buffer");
 
                         (
-                            buffer,
-                            text.bounds,
+                            &entry.buffer,
+                            Rectangle::new(
+                                text.bounds.position(),
+                                entry.min_bounds,
+                            ),
                             text.horizontal_alignment,
                             text.vertical_alignment,
                             text.color,
-- 
cgit 


From 020fb3c37794fcfa2670c3c0ada949aee95855a0 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Sun, 3 Sep 2023 01:01:33 +0200
Subject: Fix `iced_wgpu` device selection on Wasm

---
 wgpu/src/window/compositor.rs | 33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index cd5b20cc..5202c7ef 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -6,8 +6,6 @@ use crate::graphics::compositor;
 use crate::graphics::{Error, Viewport};
 use crate::{Backend, Primitive, Renderer, Settings};
 
-use futures::stream::{self, StreamExt};
-
 use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
 
 use std::marker::PhantomData;
@@ -95,14 +93,17 @@ impl<Theme> Compositor<Theme> {
         let limits =
             [wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
 
-        let limits = limits.into_iter().map(|limits| wgpu::Limits {
-            max_bind_groups: 2,
-            ..limits
-        });
+        let mut limits = limits
+            .into_iter()
+            .map(|limits| wgpu::Limits {
+                max_bind_groups: 2,
+                ..limits
+            })
+            .into_iter();
 
-        let (device, queue) = stream::iter(limits)
-            .filter_map(|limits| async {
-                adapter.request_device(
+        let (device, queue) = loop {
+            if let Some(limits) = limits.next() {
+                let device = adapter.request_device(
                     &wgpu::DeviceDescriptor {
                         label: Some(
                             "iced_wgpu::window::compositor device descriptor",
@@ -111,11 +112,15 @@ impl<Theme> Compositor<Theme> {
                         limits,
                     },
                     None,
-                ).await.ok()
-            })
-            .boxed()
-            .next()
-            .await?;
+                ).await.ok();
+
+                if let Some(device) = device {
+                    break Some(device);
+                }
+            }
+
+            break None;
+        }?;
 
         Some(Compositor {
             instance,
-- 
cgit 


From 0b28080d3ebda4dda24c9ac9c38e3195d3499be7 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Sun, 3 Sep 2023 01:05:17 +0200
Subject: Remove redundant `into_iter` call in `iced_wgpu`

---
 wgpu/src/window/compositor.rs | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 5202c7ef..cacfeef2 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -93,13 +93,10 @@ impl<Theme> Compositor<Theme> {
         let limits =
             [wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
 
-        let mut limits = limits
-            .into_iter()
-            .map(|limits| wgpu::Limits {
-                max_bind_groups: 2,
-                ..limits
-            })
-            .into_iter();
+        let mut limits = limits.into_iter().map(|limits| wgpu::Limits {
+            max_bind_groups: 2,
+            ..limits
+        });
 
         let (device, queue) = loop {
             if let Some(limits) = limits.next() {
-- 
cgit 


From 9b9b37e6f83b5e5a8811feb17b484c6b11fa3b8b Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Sun, 3 Sep 2023 01:14:22 +0200
Subject: Fix adapter selection loop in `iced_wgpu`

---
 wgpu/src/window/compositor.rs | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index cacfeef2..9e9c63db 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -98,8 +98,9 @@ impl<Theme> Compositor<Theme> {
             ..limits
         });
 
-        let (device, queue) = loop {
-            if let Some(limits) = limits.next() {
+        let (device, queue) =
+            loop {
+                let limits = limits.next()?;
                 let device = adapter.request_device(
                     &wgpu::DeviceDescriptor {
                         label: Some(
@@ -114,10 +115,7 @@ impl<Theme> Compositor<Theme> {
                 if let Some(device) = device {
                     break Some(device);
                 }
-            }
-
-            break None;
-        }?;
+            }?;
 
         Some(Compositor {
             instance,
-- 
cgit 


From 8129e2c208d9b13dbd32a309058b0547c723dede Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Sun, 3 Sep 2023 08:08:27 +0200
Subject: Implement `draw_paragraph` in `iced_tiny_skia`

---
 wgpu/src/text.rs | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index ee352368..a1ec511b 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -159,29 +159,28 @@ impl Pipeline {
                     }
                 };
 
-                let x = bounds.x * scale_factor;
-                let y = bounds.y * scale_factor;
-
-                let max_width = bounds.width * scale_factor;
-                let total_height = bounds.height * scale_factor;
+                let bounds = bounds * scale_factor;
 
                 let left = match horizontal_alignment {
-                    alignment::Horizontal::Left => x,
-                    alignment::Horizontal::Center => x - max_width / 2.0,
-                    alignment::Horizontal::Right => x - max_width,
+                    alignment::Horizontal::Left => bounds.x,
+                    alignment::Horizontal::Center => {
+                        bounds.x - bounds.width / 2.0
+                    }
+                    alignment::Horizontal::Right => bounds.x - bounds.width,
                 };
 
                 let top = match vertical_alignment {
-                    alignment::Vertical::Top => y,
-                    alignment::Vertical::Center => y - total_height / 2.0,
-                    alignment::Vertical::Bottom => y - total_height,
+                    alignment::Vertical::Top => bounds.y,
+                    alignment::Vertical::Center => {
+                        bounds.y - bounds.height / 2.0
+                    }
+                    alignment::Vertical::Bottom => bounds.y - bounds.height,
                 };
 
                 let section_bounds = Rectangle {
                     x: left,
                     y: top,
-                    width: max_width,
-                    height: total_height,
+                    ..bounds
                 };
 
                 let clip_bounds = layer_bounds.intersection(&section_bounds)?;
-- 
cgit 


From 76cec1b1fd533dda37aa53c40ef7665ed3b406b6 Mon Sep 17 00:00:00 2001
From: David Huculak <davidhuculak5@gmail.com>
Date: Sun, 3 Sep 2023 19:32:38 -0400
Subject: use @interpolate(flat) attribute as per the WebGPU spec:

User-defined vertex outputs and fragment inputs of scalar or vector integer type must always be specified as @interpolate(flat)

https://www.w3.org/TR/WGSL/#interpolation
---
 wgpu/src/shader/quad.wgsl     | 22 +++++++++++-----------
 wgpu/src/shader/triangle.wgsl | 20 ++++++++++----------
 2 files changed, 21 insertions(+), 21 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index fb402158..87055339 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -147,12 +147,12 @@ fn solid_fs_main(
 
 struct GradientVertexInput {
     @location(0) v_pos: vec2<f32>,
-    @location(1) colors_1: vec4<u32>,
-    @location(2) colors_2: vec4<u32>,
-    @location(3) colors_3: vec4<u32>,
-    @location(4) colors_4: vec4<u32>,
-    @location(5) offsets: vec4<u32>,
-    @location(6) direction: vec4<f32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
+    @location(6) @interpolate(flat) direction: vec4<f32>,
     @location(7) position_and_scale: vec4<f32>,
     @location(8) border_color: vec4<f32>,
     @location(9) border_radius: vec4<f32>,
@@ -161,11 +161,11 @@ struct GradientVertexInput {
 
 struct GradientVertexOutput {
     @builtin(position) position: vec4<f32>,
-    @location(1) colors_1: vec4<u32>,
-    @location(2) colors_2: vec4<u32>,
-    @location(3) colors_3: vec4<u32>,
-    @location(4) colors_4: vec4<u32>,
-    @location(5) offsets: vec4<u32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
     @location(6) direction: vec4<f32>,
     @location(7) position_and_scale: vec4<f32>,
     @location(8) border_color: vec4<f32>,
diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl
index 9f512d14..3a2b9845 100644
--- a/wgpu/src/shader/triangle.wgsl
+++ b/wgpu/src/shader/triangle.wgsl
@@ -38,22 +38,22 @@ fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
 
 struct GradientVertexInput {
     @location(0) v_pos: vec2<f32>,
-    @location(1) colors_1: vec4<u32>,
-    @location(2) colors_2: vec4<u32>,
-    @location(3) colors_3: vec4<u32>,
-    @location(4) colors_4: vec4<u32>,
-    @location(5) offsets: vec4<u32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
     @location(6) direction: vec4<f32>,
 }
 
 struct GradientVertexOutput {
     @builtin(position) position: vec4<f32>,
     @location(0) raw_position: vec2<f32>,
-    @location(1) colors_1: vec4<u32>,
-    @location(2) colors_2: vec4<u32>,
-    @location(3) colors_3: vec4<u32>,
-    @location(4) colors_4: vec4<u32>,
-    @location(5) offsets: vec4<u32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
     @location(6) direction: vec4<f32>,
 }
 
-- 
cgit 


From ef429fbea6e39efcfdc58fdd8a2fe365fd5314d9 Mon Sep 17 00:00:00 2001
From: Josh Megnauth <jo.sh@tutanota.com>
Date: Mon, 4 Sep 2023 02:58:54 -0400
Subject: Ensure LineHeight > 0.0 for the WGPU renderer

---
 wgpu/src/text.rs | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index fb13545d..08a32b5e 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -96,7 +96,8 @@ impl Pipeline {
                             section
                                 .line_height
                                 .to_absolute(Pixels(section.size)),
-                        ),
+                        )
+                        .max(f32::MIN_POSITIVE),
                         font: section.font,
                         bounds: Size {
                             width: section.bounds.width,
@@ -238,7 +239,8 @@ impl Pipeline {
     ) -> Size {
         let mut cache = self.cache.borrow_mut();
 
-        let line_height = f32::from(line_height.to_absolute(Pixels(size)));
+        let line_height = f32::from(line_height.to_absolute(Pixels(size)))
+            .max(f32::MIN_POSITIVE);
 
         let (_, entry) = cache.allocate(
             &mut self.font_system.borrow_mut(),
@@ -269,7 +271,8 @@ impl Pipeline {
     ) -> Option<Hit> {
         let mut cache = self.cache.borrow_mut();
 
-        let line_height = f32::from(line_height.to_absolute(Pixels(size)));
+        let line_height = f32::from(line_height.to_absolute(Pixels(size)))
+            .max(f32::MIN_POSITIVE);
 
         let (_, entry) = cache.allocate(
             &mut self.font_system.borrow_mut(),
-- 
cgit 


From bdf18554feadb631fdae5b427ac7a92a5546ade1 Mon Sep 17 00:00:00 2001
From: Josh Megnauth <jo.sh@tutanota.com>
Date: Mon, 4 Sep 2023 23:47:44 -0400
Subject: Check LineHeight > 0.0 before allocating text

---
 wgpu/src/text.rs | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 08a32b5e..9c42be0e 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -96,8 +96,7 @@ impl Pipeline {
                             section
                                 .line_height
                                 .to_absolute(Pixels(section.size)),
-                        )
-                        .max(f32::MIN_POSITIVE),
+                        ),
                         font: section.font,
                         bounds: Size {
                             width: section.bounds.width,
@@ -239,8 +238,7 @@ impl Pipeline {
     ) -> Size {
         let mut cache = self.cache.borrow_mut();
 
-        let line_height = f32::from(line_height.to_absolute(Pixels(size)))
-            .max(f32::MIN_POSITIVE);
+        let line_height = f32::from(line_height.to_absolute(Pixels(size)));
 
         let (_, entry) = cache.allocate(
             &mut self.font_system.borrow_mut(),
@@ -271,8 +269,7 @@ impl Pipeline {
     ) -> Option<Hit> {
         let mut cache = self.cache.borrow_mut();
 
-        let line_height = f32::from(line_height.to_absolute(Pixels(size)))
-            .max(f32::MIN_POSITIVE);
+        let line_height = f32::from(line_height.to_absolute(Pixels(size)));
 
         let (_, entry) = cache.allocate(
             &mut self.font_system.borrow_mut(),
@@ -417,7 +414,10 @@ impl Cache {
         }
 
         if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) {
-            let metrics = glyphon::Metrics::new(key.size, key.line_height);
+            let metrics = glyphon::Metrics::new(
+                key.size,
+                key.line_height.max(f32::MIN_POSITIVE),
+            );
             let mut buffer = glyphon::Buffer::new(font_system, metrics);
 
             buffer.set_size(
-- 
cgit 


From 5371fae21a4c1110a37e7183e794cba234598d9c Mon Sep 17 00:00:00 2001
From: ripytide <james.forsterer@gmail.com>
Date: Tue, 5 Sep 2023 10:49:50 +0100
Subject: added a Frame::scale_nonuniform method

---
 wgpu/src/geometry.rs | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index e421e0b0..2cd07a27 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -444,7 +444,7 @@ impl Frame {
         self.transforms.current.is_identity = false;
     }
 
-    /// Applies a scaling to the current transform of the [`Frame`].
+    /// Applies a uniform scaling to the current transform of the [`Frame`].
     #[inline]
     pub fn scale(&mut self, scale: f32) {
         self.transforms.current.raw =
@@ -452,6 +452,14 @@ impl Frame {
         self.transforms.current.is_identity = false;
     }
 
+    /// Applies a non-uniform scaling to the current transform of the [`Frame`].
+    #[inline]
+    pub fn scale_nonuniform(&mut self, scale: Vector) {
+        self.transforms.current.raw =
+            self.transforms.current.raw.pre_scale(scale.x, scale.y);
+        self.transforms.current.is_identity = false;
+    }
+
     /// Produces the [`Primitive`] representing everything drawn on the [`Frame`].
     pub fn into_primitive(self) -> Primitive {
         Primitive::Group {
-- 
cgit 


From 87800095e27353557adb39ef42ee6f82a0075bc1 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Thu, 7 Sep 2023 05:43:03 +0200
Subject: Remove unnecessary `interpolate(flat)` in `quad.wgsl`

---
 wgpu/src/shader/quad.wgsl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index 87055339..023b5a6d 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -152,7 +152,7 @@ struct GradientVertexInput {
     @location(3) @interpolate(flat) colors_3: vec4<u32>,
     @location(4) @interpolate(flat) colors_4: vec4<u32>,
     @location(5) @interpolate(flat) offsets: vec4<u32>,
-    @location(6) @interpolate(flat) direction: vec4<f32>,
+    @location(6) direction: vec4<f32>,
     @location(7) position_and_scale: vec4<f32>,
     @location(8) border_color: vec4<f32>,
     @location(9) border_radius: vec4<f32>,
-- 
cgit 


From 1f263051b6c2d2f2a02633d8a6277c772ae8e7f9 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Thu, 7 Sep 2023 05:45:51 +0200
Subject: Implement `scale` in terms of `scale_nonuniform`

---
 wgpu/src/geometry.rs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index 2cd07a27..64b98469 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -447,9 +447,7 @@ impl Frame {
     /// Applies a uniform scaling to the current transform of the [`Frame`].
     #[inline]
     pub fn scale(&mut self, scale: f32) {
-        self.transforms.current.raw =
-            self.transforms.current.raw.pre_scale(scale, scale);
-        self.transforms.current.is_identity = false;
+        self.scale_nonuniform(Vector { x: scale, y: scale });
     }
 
     /// Applies a non-uniform scaling to the current transform of the [`Frame`].
-- 
cgit 


From 09965b686ea6bf82e6c13ed5331bbeb059848e4f Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Thu, 7 Sep 2023 05:51:39 +0200
Subject: Make `scale` methods in `Frame` generic over `f32` and `Vector`

---
 wgpu/src/geometry.rs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index 64b98469..c3e16f8c 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -446,13 +446,17 @@ impl Frame {
 
     /// Applies a uniform scaling to the current transform of the [`Frame`].
     #[inline]
-    pub fn scale(&mut self, scale: f32) {
+    pub fn scale(&mut self, scale: impl Into<f32>) {
+        let scale = scale.into();
+
         self.scale_nonuniform(Vector { x: scale, y: scale });
     }
 
     /// Applies a non-uniform scaling to the current transform of the [`Frame`].
     #[inline]
-    pub fn scale_nonuniform(&mut self, scale: Vector) {
+    pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
+        let scale = scale.into();
+
         self.transforms.current.raw =
             self.transforms.current.raw.pre_scale(scale.x, scale.y);
         self.transforms.current.is_identity = false;
-- 
cgit 


From 181708a1c0f4920f7491df4516b0de3f61993391 Mon Sep 17 00:00:00 2001
From: Matthias Vogelgesang <matthias.vogelgesang@gmail.com>
Date: Mon, 28 Aug 2023 22:56:47 +0200
Subject: Compute gradients in Oklab color space

---
 wgpu/src/shader/quad.wgsl | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index 023b5a6d..cba7e5a4 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -230,6 +230,18 @@ fn gradient(
     let unit = normalize(v1);
     let coord_offset = dot(unit, v2) / length(v1);
 
+    let to_lms: mat3x4<f32> = mat3x4<f32>(
+        vec4<f32>(0.4121656120,  0.2118591070,  0.0883097947, 0.0),
+        vec4<f32>(0.5362752080,  0.6807189584,  0.2818474174, 0.0),
+        vec4<f32>(0.0514575653,  0.1074065790,  0.6302613616, 0.0),
+    );
+
+    let to_rgb: mat3x4<f32> = mat3x4<f32>(
+        vec4<f32>( 4.0767245293, -3.3072168827,  0.2307590544, 0.0),
+        vec4<f32>(-1.2681437731,  2.6093323231, -0.3411344290, 0.0),
+        vec4<f32>(-0.0041119885, -0.7034763098,  1.7068625689, 0.0),
+    );
+
     //need to store these as a var to use dynamic indexing in a loop
     //this is already added to wgsl spec but not in wgpu yet
     var colors_arr = colors;
@@ -248,11 +260,15 @@ fn gradient(
         }
 
         if (curr_offset <= coord_offset && coord_offset <= next_offset) {
-            color = mix(colors_arr[i], colors_arr[i+1], smoothstep(
-                curr_offset,
-                next_offset,
-                coord_offset,
-            ));
+            // blend in OKLab
+            let factor = smoothstep(curr_offset, next_offset, coord_offset);
+            let lms_a = pow(colors_arr[i] * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
+            let lms_b = pow(colors_arr[i+1] * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
+            let mixed = mix(lms_a, lms_b, factor);
+
+            // back to sRGB
+            color = to_rgb * (mixed * mixed * mixed);
+            color.a = mix(colors_arr[i].a, colors_arr[i+1].a, factor);
         }
 
         if (coord_offset >= offsets_arr[last_index]) {
-- 
cgit 


From 10d0b257f929296b1991a440f62c87487c0076dc Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Thu, 7 Sep 2023 07:24:32 +0200
Subject: Use Oklab color interpolation only with `color::GAMMA_CORRECTION`

---
 wgpu/src/quad/gradient.rs              |  19 +-
 wgpu/src/quad/solid.rs                 |   6 +-
 wgpu/src/shader/color/linear_rgb.wgsl  |   3 +
 wgpu/src/shader/color/oklab.wgsl       |  26 +++
 wgpu/src/shader/quad.wgsl              | 322 ---------------------------------
 wgpu/src/shader/quad/gradient.wgsl     | 205 +++++++++++++++++++++
 wgpu/src/shader/quad/solid.wgsl        |  99 ++++++++++
 wgpu/src/shader/triangle.wgsl          | 160 ----------------
 wgpu/src/shader/triangle/gradient.wgsl | 134 ++++++++++++++
 wgpu/src/shader/triangle/solid.wgsl    |  24 +++
 wgpu/src/triangle.rs                   |  35 +++-
 11 files changed, 544 insertions(+), 489 deletions(-)
 create mode 100644 wgpu/src/shader/color/linear_rgb.wgsl
 create mode 100644 wgpu/src/shader/color/oklab.wgsl
 create mode 100644 wgpu/src/shader/quad/gradient.wgsl
 create mode 100644 wgpu/src/shader/quad/solid.wgsl
 create mode 100644 wgpu/src/shader/triangle/gradient.wgsl
 create mode 100644 wgpu/src/shader/triangle/solid.wgsl

(limited to 'wgpu/src')

diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs
index 6db37252..a8e83d01 100644
--- a/wgpu/src/quad/gradient.rs
+++ b/wgpu/src/quad/gradient.rs
@@ -1,3 +1,4 @@
+use crate::graphics::color;
 use crate::graphics::gradient;
 use crate::quad::{self, Quad};
 use crate::Buffer;
@@ -78,7 +79,23 @@ impl Pipeline {
             device.create_shader_module(wgpu::ShaderModuleDescriptor {
                 label: Some("iced_wgpu.quad.gradient.shader"),
                 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
-                    include_str!("../shader/quad.wgsl"),
+                    if color::GAMMA_CORRECTION {
+                        concat!(
+                            include_str!("../shader/quad.wgsl"),
+                            "\n",
+                            include_str!("../shader/quad/gradient.wgsl"),
+                            "\n",
+                            include_str!("../shader/color/oklab.wgsl")
+                        )
+                    } else {
+                        concat!(
+                            include_str!("../shader/quad.wgsl"),
+                            "\n",
+                            include_str!("../shader/quad/gradient.wgsl"),
+                            "\n",
+                            include_str!("../shader/color/linear_rgb.wgsl")
+                        )
+                    },
                 )),
             });
 
diff --git a/wgpu/src/quad/solid.rs b/wgpu/src/quad/solid.rs
index f8f1e3a5..9bc6b466 100644
--- a/wgpu/src/quad/solid.rs
+++ b/wgpu/src/quad/solid.rs
@@ -72,7 +72,11 @@ impl Pipeline {
             device.create_shader_module(wgpu::ShaderModuleDescriptor {
                 label: Some("iced_wgpu.quad.solid.shader"),
                 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
-                    include_str!("../shader/quad.wgsl"),
+                    concat!(
+                        include_str!("../shader/quad.wgsl"),
+                        "\n",
+                        include_str!("../shader/quad/solid.wgsl"),
+                    ),
                 )),
             });
 
diff --git a/wgpu/src/shader/color/linear_rgb.wgsl b/wgpu/src/shader/color/linear_rgb.wgsl
new file mode 100644
index 00000000..a5cf45d4
--- /dev/null
+++ b/wgpu/src/shader/color/linear_rgb.wgsl
@@ -0,0 +1,3 @@
+fn interpolate_color(from_: vec4<f32>, to_: vec4<f32>, factor: f32) -> vec4<f32> {
+    return mix(from_, to_, factor);
+}
diff --git a/wgpu/src/shader/color/oklab.wgsl b/wgpu/src/shader/color/oklab.wgsl
new file mode 100644
index 00000000..0dc37ba6
--- /dev/null
+++ b/wgpu/src/shader/color/oklab.wgsl
@@ -0,0 +1,26 @@
+const to_lms = mat3x4<f32>(
+    vec4<f32>(0.4121656120,  0.2118591070,  0.0883097947, 0.0),
+    vec4<f32>(0.5362752080,  0.6807189584,  0.2818474174, 0.0),
+    vec4<f32>(0.0514575653,  0.1074065790,  0.6302613616, 0.0),
+);
+
+const to_rgb = mat3x4<f32>(
+    vec4<f32>( 4.0767245293, -3.3072168827,  0.2307590544, 0.0),
+    vec4<f32>(-1.2681437731,  2.6093323231, -0.3411344290, 0.0),
+    vec4<f32>(-0.0041119885, -0.7034763098,  1.7068625689, 0.0),
+);
+
+fn interpolate_color(from_: vec4<f32>, to_: vec4<f32>, factor: f32) -> vec4<f32> {
+    // To Oklab
+    let lms_a = pow(from_ * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
+    let lms_b = pow(to_ * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
+    let mixed = mix(lms_a, lms_b, factor);
+
+    // Back to linear RGB
+    var color = to_rgb * (mixed * mixed * mixed);
+
+    // Alpha interpolation
+    color.a = mix(from_.a, to_.a, factor);
+
+    return color;
+}
diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl
index cba7e5a4..f919cfe2 100644
--- a/wgpu/src/shader/quad.wgsl
+++ b/wgpu/src/shader/quad.wgsl
@@ -37,325 +37,3 @@ fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>)
     rx = select(rx, ry, position.y > center.y);
     return rx;
 }
-
-fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
-    let rg: vec2<f32> = unpack2x16float(color.x);
-    let ba: vec2<f32> = unpack2x16float(color.y);
-
-    return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
-}
-
-struct SolidVertexInput {
-    @location(0) v_pos: vec2<f32>,
-    @location(1) color: vec4<f32>,
-    @location(2) pos: vec2<f32>,
-    @location(3) scale: vec2<f32>,
-    @location(4) border_color: vec4<f32>,
-    @location(5) border_radius: vec4<f32>,
-    @location(6) border_width: f32,
-}
-
-struct SolidVertexOutput {
-    @builtin(position) position: vec4<f32>,
-    @location(0) color: vec4<f32>,
-    @location(1) border_color: vec4<f32>,
-    @location(2) pos: vec2<f32>,
-    @location(3) scale: vec2<f32>,
-    @location(4) border_radius: vec4<f32>,
-    @location(5) border_width: f32,
-}
-
-@vertex
-fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
-    var out: SolidVertexOutput;
-
-    var pos: vec2<f32> = input.pos * globals.scale;
-    var scale: vec2<f32> = input.scale * globals.scale;
-
-    var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
-    var border_radius: vec4<f32> = vec4<f32>(
-        min(input.border_radius.x, min_border_radius),
-        min(input.border_radius.y, min_border_radius),
-        min(input.border_radius.z, min_border_radius),
-        min(input.border_radius.w, min_border_radius)
-    );
-
-    var transform: mat4x4<f32> = mat4x4<f32>(
-        vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
-        vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
-        vec4<f32>(0.0, 0.0, 1.0, 0.0),
-        vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
-    );
-
-    out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
-    out.color = input.color;
-    out.border_color = input.border_color;
-    out.pos = pos;
-    out.scale = scale;
-    out.border_radius = border_radius * globals.scale;
-    out.border_width = input.border_width * globals.scale;
-
-    return out;
-}
-
-@fragment
-fn solid_fs_main(
-    input: SolidVertexOutput
-) -> @location(0) vec4<f32> {
-    var mixed_color: vec4<f32> = input.color;
-
-    var border_radius = select_border_radius(
-        input.border_radius,
-        input.position.xy,
-        (input.pos + input.scale * 0.5).xy
-    );
-
-    if (input.border_width > 0.0) {
-        var internal_border: f32 = max(border_radius - input.border_width, 0.0);
-
-        var internal_distance: f32 = distance_alg(
-            input.position.xy,
-            input.pos + vec2<f32>(input.border_width, input.border_width),
-            input.scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
-            internal_border
-        );
-
-        var border_mix: f32 = smoothstep(
-            max(internal_border - 0.5, 0.0),
-            internal_border + 0.5,
-            internal_distance
-        );
-
-        mixed_color = mix(input.color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
-    }
-
-    var dist: f32 = distance_alg(
-        vec2<f32>(input.position.x, input.position.y),
-        input.pos,
-        input.scale,
-        border_radius
-    );
-
-    var radius_alpha: f32 = 1.0 - smoothstep(
-        max(border_radius - 0.5, 0.0),
-        border_radius + 0.5,
-        dist
-    );
-
-    return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
-}
-
-struct GradientVertexInput {
-    @location(0) v_pos: vec2<f32>,
-    @location(1) @interpolate(flat) colors_1: vec4<u32>,
-    @location(2) @interpolate(flat) colors_2: vec4<u32>,
-    @location(3) @interpolate(flat) colors_3: vec4<u32>,
-    @location(4) @interpolate(flat) colors_4: vec4<u32>,
-    @location(5) @interpolate(flat) offsets: vec4<u32>,
-    @location(6) direction: vec4<f32>,
-    @location(7) position_and_scale: vec4<f32>,
-    @location(8) border_color: vec4<f32>,
-    @location(9) border_radius: vec4<f32>,
-    @location(10) border_width: f32,
-}
-
-struct GradientVertexOutput {
-    @builtin(position) position: vec4<f32>,
-    @location(1) @interpolate(flat) colors_1: vec4<u32>,
-    @location(2) @interpolate(flat) colors_2: vec4<u32>,
-    @location(3) @interpolate(flat) colors_3: vec4<u32>,
-    @location(4) @interpolate(flat) colors_4: vec4<u32>,
-    @location(5) @interpolate(flat) offsets: vec4<u32>,
-    @location(6) direction: vec4<f32>,
-    @location(7) position_and_scale: vec4<f32>,
-    @location(8) border_color: vec4<f32>,
-    @location(9) border_radius: vec4<f32>,
-    @location(10) border_width: f32,
-}
-
-@vertex
-fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
-    var out: GradientVertexOutput;
-
-    var pos: vec2<f32> = input.position_and_scale.xy * globals.scale;
-    var scale: vec2<f32> = input.position_and_scale.zw * globals.scale;
-
-    var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5;
-    var border_radius: vec4<f32> = vec4<f32>(
-        min(input.border_radius.x, min_border_radius),
-        min(input.border_radius.y, min_border_radius),
-        min(input.border_radius.z, min_border_radius),
-        min(input.border_radius.w, min_border_radius)
-    );
-
-    var transform: mat4x4<f32> = mat4x4<f32>(
-        vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
-        vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
-        vec4<f32>(0.0, 0.0, 1.0, 0.0),
-        vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
-    );
-
-    out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
-    out.colors_1 = input.colors_1;
-    out.colors_2 = input.colors_2;
-    out.colors_3 = input.colors_3;
-    out.colors_4 = input.colors_4;
-    out.offsets = input.offsets;
-    out.direction = input.direction * globals.scale;
-    out.position_and_scale = vec4<f32>(pos, scale);
-    out.border_color = input.border_color;
-    out.border_radius = border_radius * globals.scale;
-    out.border_width = input.border_width * globals.scale;
-
-    return out;
-}
-
-fn random(coords: vec2<f32>) -> f32 {
-    return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
-}
-
-/// Returns the current interpolated color with a max 8-stop gradient
-fn gradient(
-    raw_position: vec2<f32>,
-    direction: vec4<f32>,
-    colors: array<vec4<f32>, 8>,
-    offsets: array<f32, 8>,
-    last_index: i32
-) -> vec4<f32> {
-    let start = direction.xy;
-    let end = direction.zw;
-
-    let v1 = end - start;
-    let v2 = raw_position - start;
-    let unit = normalize(v1);
-    let coord_offset = dot(unit, v2) / length(v1);
-
-    let to_lms: mat3x4<f32> = mat3x4<f32>(
-        vec4<f32>(0.4121656120,  0.2118591070,  0.0883097947, 0.0),
-        vec4<f32>(0.5362752080,  0.6807189584,  0.2818474174, 0.0),
-        vec4<f32>(0.0514575653,  0.1074065790,  0.6302613616, 0.0),
-    );
-
-    let to_rgb: mat3x4<f32> = mat3x4<f32>(
-        vec4<f32>( 4.0767245293, -3.3072168827,  0.2307590544, 0.0),
-        vec4<f32>(-1.2681437731,  2.6093323231, -0.3411344290, 0.0),
-        vec4<f32>(-0.0041119885, -0.7034763098,  1.7068625689, 0.0),
-    );
-
-    //need to store these as a var to use dynamic indexing in a loop
-    //this is already added to wgsl spec but not in wgpu yet
-    var colors_arr = colors;
-    var offsets_arr = offsets;
-
-    var color: vec4<f32>;
-
-    let noise_granularity: f32 = 0.3/255.0;
-
-    for (var i: i32 = 0; i < last_index; i++) {
-        let curr_offset = offsets_arr[i];
-        let next_offset = offsets_arr[i+1];
-
-        if (coord_offset <= offsets_arr[0]) {
-            color = colors_arr[0];
-        }
-
-        if (curr_offset <= coord_offset && coord_offset <= next_offset) {
-            // blend in OKLab
-            let factor = smoothstep(curr_offset, next_offset, coord_offset);
-            let lms_a = pow(colors_arr[i] * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
-            let lms_b = pow(colors_arr[i+1] * to_lms, vec3<f32>(1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0));
-            let mixed = mix(lms_a, lms_b, factor);
-
-            // back to sRGB
-            color = to_rgb * (mixed * mixed * mixed);
-            color.a = mix(colors_arr[i].a, colors_arr[i+1].a, factor);
-        }
-
-        if (coord_offset >= offsets_arr[last_index]) {
-            color = colors_arr[last_index];
-        }
-    }
-
-    return color + mix(-noise_granularity, noise_granularity, random(raw_position));
-}
-
-@fragment
-fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
-    let colors = array<vec4<f32>, 8>(
-        unpack_u32(input.colors_1.xy),
-        unpack_u32(input.colors_1.zw),
-        unpack_u32(input.colors_2.xy),
-        unpack_u32(input.colors_2.zw),
-        unpack_u32(input.colors_3.xy),
-        unpack_u32(input.colors_3.zw),
-        unpack_u32(input.colors_4.xy),
-        unpack_u32(input.colors_4.zw),
-    );
-
-    let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
-    let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
-
-    var offsets = array<f32, 8>(
-        offsets_1.x,
-        offsets_1.y,
-        offsets_1.z,
-        offsets_1.w,
-        offsets_2.x,
-        offsets_2.y,
-        offsets_2.z,
-        offsets_2.w,
-    );
-
-    //TODO could just pass this in to the shader but is probably more performant to just check it here
-    var last_index = 7;
-    for (var i: i32 = 0; i <= 7; i++) {
-        if (offsets[i] > 1.0) {
-            last_index = i - 1;
-            break;
-        }
-    }
-
-    var mixed_color: vec4<f32> = gradient(input.position.xy, input.direction, colors, offsets, last_index);
-
-    let pos = input.position_and_scale.xy;
-    let scale = input.position_and_scale.zw;
-
-    var border_radius = select_border_radius(
-        input.border_radius,
-        input.position.xy,
-        (pos + scale * 0.5).xy
-    );
-
-    if (input.border_width > 0.0) {
-        var internal_border: f32 = max(border_radius - input.border_width, 0.0);
-
-        var internal_distance: f32 = distance_alg(
-            input.position.xy,
-            pos + vec2<f32>(input.border_width, input.border_width),
-            scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
-            internal_border
-        );
-
-        var border_mix: f32 = smoothstep(
-            max(internal_border - 0.5, 0.0),
-            internal_border + 0.5,
-            internal_distance
-        );
-
-        mixed_color = mix(mixed_color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
-    }
-
-    var dist: f32 = distance_alg(
-        input.position.xy,
-        pos,
-        scale,
-        border_radius
-    );
-
-    var radius_alpha: f32 = 1.0 - smoothstep(
-        max(border_radius - 0.5, 0.0),
-        border_radius + 0.5,
-        dist);
-
-    return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
-}
diff --git a/wgpu/src/shader/quad/gradient.wgsl b/wgpu/src/shader/quad/gradient.wgsl
new file mode 100644
index 00000000..0754e97f
--- /dev/null
+++ b/wgpu/src/shader/quad/gradient.wgsl
@@ -0,0 +1,205 @@
+struct GradientVertexInput {
+    @location(0) v_pos: vec2<f32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
+    @location(6) direction: vec4<f32>,
+    @location(7) position_and_scale: vec4<f32>,
+    @location(8) border_color: vec4<f32>,
+    @location(9) border_radius: vec4<f32>,
+    @location(10) border_width: f32,
+}
+
+struct GradientVertexOutput {
+    @builtin(position) position: vec4<f32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
+    @location(6) direction: vec4<f32>,
+    @location(7) position_and_scale: vec4<f32>,
+    @location(8) border_color: vec4<f32>,
+    @location(9) border_radius: vec4<f32>,
+    @location(10) border_width: f32,
+}
+
+@vertex
+fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
+    var out: GradientVertexOutput;
+
+    var pos: vec2<f32> = input.position_and_scale.xy * globals.scale;
+    var scale: vec2<f32> = input.position_and_scale.zw * globals.scale;
+
+    var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5;
+    var border_radius: vec4<f32> = vec4<f32>(
+        min(input.border_radius.x, min_border_radius),
+        min(input.border_radius.y, min_border_radius),
+        min(input.border_radius.z, min_border_radius),
+        min(input.border_radius.w, min_border_radius)
+    );
+
+    var transform: mat4x4<f32> = mat4x4<f32>(
+        vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
+        vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
+        vec4<f32>(0.0, 0.0, 1.0, 0.0),
+        vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
+    );
+
+    out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+    out.colors_1 = input.colors_1;
+    out.colors_2 = input.colors_2;
+    out.colors_3 = input.colors_3;
+    out.colors_4 = input.colors_4;
+    out.offsets = input.offsets;
+    out.direction = input.direction * globals.scale;
+    out.position_and_scale = vec4<f32>(pos, scale);
+    out.border_color = input.border_color;
+    out.border_radius = border_radius * globals.scale;
+    out.border_width = input.border_width * globals.scale;
+
+    return out;
+}
+
+fn random(coords: vec2<f32>) -> f32 {
+    return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
+}
+
+/// Returns the current interpolated color with a max 8-stop gradient
+fn gradient(
+    raw_position: vec2<f32>,
+    direction: vec4<f32>,
+    colors: array<vec4<f32>, 8>,
+    offsets: array<f32, 8>,
+    last_index: i32
+) -> vec4<f32> {
+    let start = direction.xy;
+    let end = direction.zw;
+
+    let v1 = end - start;
+    let v2 = raw_position - start;
+    let unit = normalize(v1);
+    let coord_offset = dot(unit, v2) / length(v1);
+
+    //need to store these as a var to use dynamic indexing in a loop
+    //this is already added to wgsl spec but not in wgpu yet
+    var colors_arr = colors;
+    var offsets_arr = offsets;
+
+    var color: vec4<f32>;
+
+    let noise_granularity: f32 = 0.3/255.0;
+
+    for (var i: i32 = 0; i < last_index; i++) {
+        let curr_offset = offsets_arr[i];
+        let next_offset = offsets_arr[i+1];
+
+        if (coord_offset <= offsets_arr[0]) {
+            color = colors_arr[0];
+        }
+
+        if (curr_offset <= coord_offset && coord_offset <= next_offset) {
+            let from_ = colors_arr[i];
+            let to_ = colors_arr[i+1];
+            let factor = smoothstep(curr_offset, next_offset, coord_offset);
+
+            color = interpolate_color(from_, to_, factor);
+        }
+
+        if (coord_offset >= offsets_arr[last_index]) {
+            color = colors_arr[last_index];
+        }
+    }
+
+    return color + mix(-noise_granularity, noise_granularity, random(raw_position));
+}
+
+@fragment
+fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
+    let colors = array<vec4<f32>, 8>(
+        unpack_u32(input.colors_1.xy),
+        unpack_u32(input.colors_1.zw),
+        unpack_u32(input.colors_2.xy),
+        unpack_u32(input.colors_2.zw),
+        unpack_u32(input.colors_3.xy),
+        unpack_u32(input.colors_3.zw),
+        unpack_u32(input.colors_4.xy),
+        unpack_u32(input.colors_4.zw),
+    );
+
+    let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
+    let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
+
+    var offsets = array<f32, 8>(
+        offsets_1.x,
+        offsets_1.y,
+        offsets_1.z,
+        offsets_1.w,
+        offsets_2.x,
+        offsets_2.y,
+        offsets_2.z,
+        offsets_2.w,
+    );
+
+    //TODO could just pass this in to the shader but is probably more performant to just check it here
+    var last_index = 7;
+    for (var i: i32 = 0; i <= 7; i++) {
+        if (offsets[i] > 1.0) {
+            last_index = i - 1;
+            break;
+        }
+    }
+
+    var mixed_color: vec4<f32> = gradient(input.position.xy, input.direction, colors, offsets, last_index);
+
+    let pos = input.position_and_scale.xy;
+    let scale = input.position_and_scale.zw;
+
+    var border_radius = select_border_radius(
+        input.border_radius,
+        input.position.xy,
+        (pos + scale * 0.5).xy
+    );
+
+    if (input.border_width > 0.0) {
+        var internal_border: f32 = max(border_radius - input.border_width, 0.0);
+
+        var internal_distance: f32 = distance_alg(
+            input.position.xy,
+            pos + vec2<f32>(input.border_width, input.border_width),
+            scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
+            internal_border
+        );
+
+        var border_mix: f32 = smoothstep(
+            max(internal_border - 0.5, 0.0),
+            internal_border + 0.5,
+            internal_distance
+        );
+
+        mixed_color = mix(mixed_color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
+    }
+
+    var dist: f32 = distance_alg(
+        input.position.xy,
+        pos,
+        scale,
+        border_radius
+    );
+
+    var radius_alpha: f32 = 1.0 - smoothstep(
+        max(border_radius - 0.5, 0.0),
+        border_radius + 0.5,
+        dist);
+
+    return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
+}
+
+fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
+    let rg: vec2<f32> = unpack2x16float(color.x);
+    let ba: vec2<f32> = unpack2x16float(color.y);
+
+    return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
+}
diff --git a/wgpu/src/shader/quad/solid.wgsl b/wgpu/src/shader/quad/solid.wgsl
new file mode 100644
index 00000000..ebd6d877
--- /dev/null
+++ b/wgpu/src/shader/quad/solid.wgsl
@@ -0,0 +1,99 @@
+struct SolidVertexInput {
+    @location(0) v_pos: vec2<f32>,
+    @location(1) color: vec4<f32>,
+    @location(2) pos: vec2<f32>,
+    @location(3) scale: vec2<f32>,
+    @location(4) border_color: vec4<f32>,
+    @location(5) border_radius: vec4<f32>,
+    @location(6) border_width: f32,
+}
+
+struct SolidVertexOutput {
+    @builtin(position) position: vec4<f32>,
+    @location(0) color: vec4<f32>,
+    @location(1) border_color: vec4<f32>,
+    @location(2) pos: vec2<f32>,
+    @location(3) scale: vec2<f32>,
+    @location(4) border_radius: vec4<f32>,
+    @location(5) border_width: f32,
+}
+
+@vertex
+fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
+    var out: SolidVertexOutput;
+
+    var pos: vec2<f32> = input.pos * globals.scale;
+    var scale: vec2<f32> = input.scale * globals.scale;
+
+    var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
+    var border_radius: vec4<f32> = vec4<f32>(
+        min(input.border_radius.x, min_border_radius),
+        min(input.border_radius.y, min_border_radius),
+        min(input.border_radius.z, min_border_radius),
+        min(input.border_radius.w, min_border_radius)
+    );
+
+    var transform: mat4x4<f32> = mat4x4<f32>(
+        vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
+        vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
+        vec4<f32>(0.0, 0.0, 1.0, 0.0),
+        vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
+    );
+
+    out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+    out.color = input.color;
+    out.border_color = input.border_color;
+    out.pos = pos;
+    out.scale = scale;
+    out.border_radius = border_radius * globals.scale;
+    out.border_width = input.border_width * globals.scale;
+
+    return out;
+}
+
+@fragment
+fn solid_fs_main(
+    input: SolidVertexOutput
+) -> @location(0) vec4<f32> {
+    var mixed_color: vec4<f32> = input.color;
+
+    var border_radius = select_border_radius(
+        input.border_radius,
+        input.position.xy,
+        (input.pos + input.scale * 0.5).xy
+    );
+
+    if (input.border_width > 0.0) {
+        var internal_border: f32 = max(border_radius - input.border_width, 0.0);
+
+        var internal_distance: f32 = distance_alg(
+            input.position.xy,
+            input.pos + vec2<f32>(input.border_width, input.border_width),
+            input.scale - vec2<f32>(input.border_width * 2.0, input.border_width * 2.0),
+            internal_border
+        );
+
+        var border_mix: f32 = smoothstep(
+            max(internal_border - 0.5, 0.0),
+            internal_border + 0.5,
+            internal_distance
+        );
+
+        mixed_color = mix(input.color, input.border_color, vec4<f32>(border_mix, border_mix, border_mix, border_mix));
+    }
+
+    var dist: f32 = distance_alg(
+        vec2<f32>(input.position.x, input.position.y),
+        input.pos,
+        input.scale,
+        border_radius
+    );
+
+    var radius_alpha: f32 = 1.0 - smoothstep(
+        max(border_radius - 0.5, 0.0),
+        border_radius + 0.5,
+        dist
+    );
+
+    return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
+}
diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl
index 3a2b9845..e4c19344 100644
--- a/wgpu/src/shader/triangle.wgsl
+++ b/wgpu/src/shader/triangle.wgsl
@@ -3,163 +3,3 @@ struct Globals {
 }
 
 @group(0) @binding(0) var<uniform> globals: Globals;
-
-fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
-    let rg: vec2<f32> = unpack2x16float(color.x);
-    let ba: vec2<f32> = unpack2x16float(color.y);
-
-    return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
-}
-
-struct SolidVertexInput {
-    @location(0) position: vec2<f32>,
-    @location(1) color: vec4<f32>,
-}
-
-struct SolidVertexOutput {
-    @builtin(position) position: vec4<f32>,
-    @location(0) color: vec4<f32>,
-}
-
-@vertex
-fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
-    var out: SolidVertexOutput;
-
-    out.color = input.color;
-    out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
-
-    return out;
-}
-
-@fragment
-fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
-    return input.color;
-}
-
-struct GradientVertexInput {
-    @location(0) v_pos: vec2<f32>,
-    @location(1) @interpolate(flat) colors_1: vec4<u32>,
-    @location(2) @interpolate(flat) colors_2: vec4<u32>,
-    @location(3) @interpolate(flat) colors_3: vec4<u32>,
-    @location(4) @interpolate(flat) colors_4: vec4<u32>,
-    @location(5) @interpolate(flat) offsets: vec4<u32>,
-    @location(6) direction: vec4<f32>,
-}
-
-struct GradientVertexOutput {
-    @builtin(position) position: vec4<f32>,
-    @location(0) raw_position: vec2<f32>,
-    @location(1) @interpolate(flat) colors_1: vec4<u32>,
-    @location(2) @interpolate(flat) colors_2: vec4<u32>,
-    @location(3) @interpolate(flat) colors_3: vec4<u32>,
-    @location(4) @interpolate(flat) colors_4: vec4<u32>,
-    @location(5) @interpolate(flat) offsets: vec4<u32>,
-    @location(6) direction: vec4<f32>,
-}
-
-@vertex
-fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
-    var output: GradientVertexOutput;
-
-    output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
-    output.raw_position = input.v_pos;
-    output.colors_1 = input.colors_1;
-    output.colors_2 = input.colors_2;
-    output.colors_3 = input.colors_3;
-    output.colors_4 = input.colors_4;
-    output.offsets = input.offsets;
-    output.direction = input.direction;
-
-    return output;
-}
-
-fn random(coords: vec2<f32>) -> f32 {
-    return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
-}
-
-/// Returns the current interpolated color with a max 8-stop gradient
-fn gradient(
-    raw_position: vec2<f32>,
-    direction: vec4<f32>,
-    colors: array<vec4<f32>, 8>,
-    offsets: array<f32, 8>,
-    last_index: i32
-) -> vec4<f32> {
-    let start = direction.xy;
-    let end = direction.zw;
-
-    let v1 = end - start;
-    let v2 = raw_position - start;
-    let unit = normalize(v1);
-    let coord_offset = dot(unit, v2) / length(v1);
-
-    //need to store these as a var to use dynamic indexing in a loop
-    //this is already added to wgsl spec but not in wgpu yet
-    var colors_arr = colors;
-    var offsets_arr = offsets;
-
-    var color: vec4<f32>;
-
-    let noise_granularity: f32 = 0.3/255.0;
-
-    for (var i: i32 = 0; i < last_index; i++) {
-        let curr_offset = offsets_arr[i];
-        let next_offset = offsets_arr[i+1];
-
-        if (coord_offset <= offsets_arr[0]) {
-            color = colors_arr[0];
-        }
-
-        if (curr_offset <= coord_offset && coord_offset <= next_offset) {
-            color = mix(colors_arr[i], colors_arr[i+1], smoothstep(
-                curr_offset,
-                next_offset,
-                coord_offset,
-            ));
-        }
-
-        if (coord_offset >= offsets_arr[last_index]) {
-            color = colors_arr[last_index];
-        }
-    }
-
-    return color + mix(-noise_granularity, noise_granularity, random(raw_position));
-}
-
-@fragment
-fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
-    let colors = array<vec4<f32>, 8>(
-        unpack_u32(input.colors_1.xy),
-        unpack_u32(input.colors_1.zw),
-        unpack_u32(input.colors_2.xy),
-        unpack_u32(input.colors_2.zw),
-        unpack_u32(input.colors_3.xy),
-        unpack_u32(input.colors_3.zw),
-        unpack_u32(input.colors_4.xy),
-        unpack_u32(input.colors_4.zw),
-    );
-
-    let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
-    let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
-
-    var offsets = array<f32, 8>(
-        offsets_1.x,
-        offsets_1.y,
-        offsets_1.z,
-        offsets_1.w,
-        offsets_2.x,
-        offsets_2.y,
-        offsets_2.z,
-        offsets_2.w,
-    );
-
-    var last_index = 7;
-    for (var i: i32 = 0; i <= 7; i++) {
-        if (offsets[i] >= 1.0) {
-            last_index = i;
-            break;
-        }
-    }
-
-    return gradient(input.raw_position, input.direction, colors, offsets, last_index);
-}
diff --git a/wgpu/src/shader/triangle/gradient.wgsl b/wgpu/src/shader/triangle/gradient.wgsl
new file mode 100644
index 00000000..1a8ae3b5
--- /dev/null
+++ b/wgpu/src/shader/triangle/gradient.wgsl
@@ -0,0 +1,134 @@
+struct GradientVertexInput {
+    @location(0) v_pos: vec2<f32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
+    @location(6) direction: vec4<f32>,
+}
+
+struct GradientVertexOutput {
+    @builtin(position) position: vec4<f32>,
+    @location(0) raw_position: vec2<f32>,
+    @location(1) @interpolate(flat) colors_1: vec4<u32>,
+    @location(2) @interpolate(flat) colors_2: vec4<u32>,
+    @location(3) @interpolate(flat) colors_3: vec4<u32>,
+    @location(4) @interpolate(flat) colors_4: vec4<u32>,
+    @location(5) @interpolate(flat) offsets: vec4<u32>,
+    @location(6) direction: vec4<f32>,
+}
+
+@vertex
+fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
+    var output: GradientVertexOutput;
+
+    output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
+    output.raw_position = input.v_pos;
+    output.colors_1 = input.colors_1;
+    output.colors_2 = input.colors_2;
+    output.colors_3 = input.colors_3;
+    output.colors_4 = input.colors_4;
+    output.offsets = input.offsets;
+    output.direction = input.direction;
+
+    return output;
+}
+
+/// Returns the current interpolated color with a max 8-stop gradient
+fn gradient(
+    raw_position: vec2<f32>,
+    direction: vec4<f32>,
+    colors: array<vec4<f32>, 8>,
+    offsets: array<f32, 8>,
+    last_index: i32
+) -> vec4<f32> {
+    let start = direction.xy;
+    let end = direction.zw;
+
+    let v1 = end - start;
+    let v2 = raw_position - start;
+    let unit = normalize(v1);
+    let coord_offset = dot(unit, v2) / length(v1);
+
+    //need to store these as a var to use dynamic indexing in a loop
+    //this is already added to wgsl spec but not in wgpu yet
+    var colors_arr = colors;
+    var offsets_arr = offsets;
+
+    var color: vec4<f32>;
+
+    let noise_granularity: f32 = 0.3/255.0;
+
+    for (var i: i32 = 0; i < last_index; i++) {
+        let curr_offset = offsets_arr[i];
+        let next_offset = offsets_arr[i+1];
+
+        if (coord_offset <= offsets_arr[0]) {
+            color = colors_arr[0];
+        }
+
+        if (curr_offset <= coord_offset && coord_offset <= next_offset) {
+            let from_ = colors_arr[i];
+            let to_ = colors_arr[i+1];
+            let factor = smoothstep(curr_offset, next_offset, coord_offset);
+
+            color = interpolate_color(from_, to_, factor);
+        }
+
+        if (coord_offset >= offsets_arr[last_index]) {
+            color = colors_arr[last_index];
+        }
+    }
+
+    return color + mix(-noise_granularity, noise_granularity, random(raw_position));
+}
+
+@fragment
+fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
+    let colors = array<vec4<f32>, 8>(
+        unpack_u32(input.colors_1.xy),
+        unpack_u32(input.colors_1.zw),
+        unpack_u32(input.colors_2.xy),
+        unpack_u32(input.colors_2.zw),
+        unpack_u32(input.colors_3.xy),
+        unpack_u32(input.colors_3.zw),
+        unpack_u32(input.colors_4.xy),
+        unpack_u32(input.colors_4.zw),
+    );
+
+    let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
+    let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
+
+    var offsets = array<f32, 8>(
+        offsets_1.x,
+        offsets_1.y,
+        offsets_1.z,
+        offsets_1.w,
+        offsets_2.x,
+        offsets_2.y,
+        offsets_2.z,
+        offsets_2.w,
+    );
+
+    var last_index = 7;
+    for (var i: i32 = 0; i <= 7; i++) {
+        if (offsets[i] >= 1.0) {
+            last_index = i;
+            break;
+        }
+    }
+
+    return gradient(input.raw_position, input.direction, colors, offsets, last_index);
+}
+
+fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
+    let rg: vec2<f32> = unpack2x16float(color.x);
+    let ba: vec2<f32> = unpack2x16float(color.y);
+
+    return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
+}
+
+fn random(coords: vec2<f32>) -> f32 {
+    return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453);
+}
diff --git a/wgpu/src/shader/triangle/solid.wgsl b/wgpu/src/shader/triangle/solid.wgsl
new file mode 100644
index 00000000..9ef81982
--- /dev/null
+++ b/wgpu/src/shader/triangle/solid.wgsl
@@ -0,0 +1,24 @@
+struct SolidVertexInput {
+    @location(0) position: vec2<f32>,
+    @location(1) color: vec4<f32>,
+}
+
+struct SolidVertexOutput {
+    @builtin(position) position: vec4<f32>,
+    @location(0) color: vec4<f32>,
+}
+
+@vertex
+fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
+    var out: SolidVertexOutput;
+
+    out.color = input.color;
+    out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
+
+    return out;
+}
+
+@fragment
+fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
+    return input.color;
+}
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index d8b23dfe..d430e607 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -487,8 +487,10 @@ mod solid {
                 device.create_shader_module(wgpu::ShaderModuleDescriptor {
                     label: Some("iced_wgpu.triangle.solid.shader"),
                     source: wgpu::ShaderSource::Wgsl(
-                        std::borrow::Cow::Borrowed(include_str!(
-                            "shader/triangle.wgsl"
+                        std::borrow::Cow::Borrowed(concat!(
+                            include_str!("shader/triangle.wgsl"),
+                            "\n",
+                            include_str!("shader/triangle/solid.wgsl"),
                         )),
                     ),
                 });
@@ -537,6 +539,7 @@ mod solid {
 }
 
 mod gradient {
+    use crate::graphics::color;
     use crate::graphics::mesh;
     use crate::graphics::Antialiasing;
     use crate::triangle;
@@ -633,9 +636,31 @@ mod gradient {
                 device.create_shader_module(wgpu::ShaderModuleDescriptor {
                     label: Some("iced_wgpu.triangle.gradient.shader"),
                     source: wgpu::ShaderSource::Wgsl(
-                        std::borrow::Cow::Borrowed(include_str!(
-                            "shader/triangle.wgsl"
-                        )),
+                        std::borrow::Cow::Borrowed(
+                            if color::GAMMA_CORRECTION {
+                                concat!(
+                                    include_str!("shader/triangle.wgsl"),
+                                    "\n",
+                                    include_str!(
+                                        "shader/triangle/gradient.wgsl"
+                                    ),
+                                    "\n",
+                                    include_str!("shader/color/oklab.wgsl")
+                                )
+                            } else {
+                                concat!(
+                                    include_str!("shader/triangle.wgsl"),
+                                    "\n",
+                                    include_str!(
+                                        "shader/triangle/gradient.wgsl"
+                                    ),
+                                    "\n",
+                                    include_str!(
+                                        "shader/color/linear_rgb.wgsl"
+                                    )
+                                )
+                            },
+                        ),
                     ),
                 });
 
-- 
cgit 


From 3450987355be7fe029db112474d06613929b54c7 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector0193@gmail.com>
Date: Sat, 9 Sep 2023 11:21:32 +0200
Subject: Invalidate existing paragraphs when new fonts are loaded

---
 wgpu/src/text.rs | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index a1ec511b..bd4f3e06 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -7,7 +7,6 @@ use crate::layer::Text;
 
 use std::borrow::Cow;
 use std::cell::RefCell;
-use std::sync::Arc;
 
 #[allow(missing_debug_implementations)]
 pub struct Pipeline {
@@ -47,9 +46,7 @@ impl Pipeline {
     }
 
     pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
-        let _ = self.font_system.get_mut().db_mut().load_font_source(
-            glyphon::fontdb::Source::Binary(Arc::new(bytes.into_owned())),
-        );
+        self.font_system.load_font(bytes);
 
         self.cache = RefCell::new(Cache::new());
     }
-- 
cgit 


From 89d9f1d7d2202029028a487df1dd11b0665a7517 Mon Sep 17 00:00:00 2001
From: Matthias Vogelgesang <matthias.vogelgesang@gmail.com>
Date: Sat, 9 Sep 2023 12:24:47 +0200
Subject: Fix majority of unresolved documentation links

---
 wgpu/src/geometry.rs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index c3e16f8c..63a59c05 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -310,13 +310,11 @@ impl Frame {
     /// resulting glyphs will not be rotated or scaled properly.
     ///
     /// Additionally, all text will be rendered on top of all the layers of
-    /// a [`Canvas`]. Therefore, it is currently only meant to be used for
+    /// a `Canvas`. Therefore, it is currently only meant to be used for
     /// overlays, which is the most common use case.
     ///
     /// Support for vectorial text is planned, and should address all these
     /// limitations.
-    ///
-    /// [`Canvas`]: crate::widget::Canvas
     pub fn fill_text(&mut self, text: impl Into<Text>) {
         let text = text.into();
 
-- 
cgit 


From f60884f6f8639f75258c264bf4a15591351ef05b Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Sat, 9 Sep 2023 20:58:45 +0200
Subject: Deny `broken_intradoc_links` and verify documentation in CI

---
 wgpu/src/lib.rs | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index d1e4b7af..b9f54560 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -20,6 +20,7 @@
 #![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,
@@ -29,9 +30,9 @@
     clippy::from_over_into,
     clippy::needless_borrow,
     clippy::new_without_default,
-    clippy::useless_conversion
+    clippy::useless_conversion,
+    rustdoc::broken_intra_doc_links
 )]
-#![forbid(rust_2018_idioms)]
 #![allow(clippy::inherent_to_string, clippy::type_complexity)]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
 pub mod layer;
-- 
cgit 


From 346af3f8b0baa418fd37b878bc2930ff0bd57cc0 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Mon, 11 Sep 2023 02:47:24 +0200
Subject: Make `FontSystem` global and simplify `Paragraph` API

---
 wgpu/src/backend.rs |  5 -----
 wgpu/src/text.rs    | 17 ++++++++---------
 2 files changed, 8 insertions(+), 14 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 65c63f19..3d1755e1 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -1,5 +1,4 @@
 use crate::core::{Color, Size};
-use crate::graphics;
 use crate::graphics::backend;
 use crate::graphics::color;
 use crate::graphics::{Transformation, Viewport};
@@ -310,10 +309,6 @@ impl crate::graphics::Backend for Backend {
 }
 
 impl backend::Text for Backend {
-    fn font_system(&self) -> &graphics::text::FontSystem {
-        self.text_pipeline.font_system()
-    }
-
     fn load_font(&mut self, font: Cow<'static, [u8]>) {
         self.text_pipeline.load_font(font);
     }
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index bd4f3e06..5c9f4d7e 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -2,7 +2,7 @@ use crate::core::alignment;
 use crate::core::{Rectangle, Size};
 use crate::graphics::color;
 use crate::graphics::text::cache::{self, Cache};
-use crate::graphics::text::{FontSystem, Paragraph};
+use crate::graphics::text::{font_system, Paragraph};
 use crate::layer::Text;
 
 use std::borrow::Cow;
@@ -10,7 +10,6 @@ use std::cell::RefCell;
 
 #[allow(missing_debug_implementations)]
 pub struct Pipeline {
-    font_system: FontSystem,
     renderers: Vec<glyphon::TextRenderer>,
     atlas: glyphon::TextAtlas,
     prepare_layer: usize,
@@ -24,7 +23,6 @@ impl Pipeline {
         format: wgpu::TextureFormat,
     ) -> Self {
         Pipeline {
-            font_system: FontSystem::new(),
             renderers: Vec::new(),
             atlas: glyphon::TextAtlas::with_color_mode(
                 device,
@@ -41,12 +39,11 @@ impl Pipeline {
         }
     }
 
-    pub fn font_system(&self) -> &FontSystem {
-        &self.font_system
-    }
-
     pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) {
-        self.font_system.load_font(bytes);
+        font_system()
+            .write()
+            .expect("Write font system")
+            .load_font(bytes);
 
         self.cache = RefCell::new(Cache::new());
     }
@@ -69,7 +66,9 @@ impl Pipeline {
             ));
         }
 
-        let font_system = self.font_system.get_mut();
+        let mut font_system = font_system().write().expect("Write font system");
+        let font_system = font_system.raw();
+
         let renderer = &mut self.renderers[self.prepare_layer];
         let cache = self.cache.get_mut();
 
-- 
cgit 


From 6448429103c9c82b90040ac5a5a097bdded23f82 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 12 Sep 2023 14:51:00 +0200
Subject: Draft `Editor` API and `TextEditor` widget

---
 wgpu/src/layer.rs      | 15 ++++++++++++++-
 wgpu/src/layer/text.rs |  8 +++++++-
 wgpu/src/text.rs       | 28 +++++++++++++++++++++++++---
 3 files changed, 46 insertions(+), 5 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index 7a5a0f7c..10b3332d 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -120,12 +120,25 @@ impl<'a> Layer<'a> {
             } => {
                 let layer = &mut layers[current_layer];
 
-                layer.text.push(Text::Managed {
+                layer.text.push(Text::Paragraph {
                     paragraph: paragraph.clone(),
                     position: *position + translation,
                     color: *color,
                 });
             }
+            Primitive::Editor {
+                editor,
+                position,
+                color,
+            } => {
+                let layer = &mut layers[current_layer];
+
+                layer.text.push(Text::Editor {
+                    editor: editor.clone(),
+                    position: *position + translation,
+                    color: *color,
+                });
+            }
             Primitive::Text {
                 content,
                 bounds,
diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs
index b61615d6..d46b39da 100644
--- a/wgpu/src/layer/text.rs
+++ b/wgpu/src/layer/text.rs
@@ -1,16 +1,22 @@
 use crate::core::alignment;
 use crate::core::text;
 use crate::core::{Color, Font, Pixels, Point, Rectangle};
+use crate::graphics::text::editor;
 use crate::graphics::text::paragraph;
 
 /// A paragraph of text.
 #[derive(Debug, Clone)]
 pub enum Text<'a> {
-    Managed {
+    Paragraph {
         paragraph: paragraph::Weak,
         position: Point,
         color: Color,
     },
+    Editor {
+        editor: editor::Weak,
+        position: Point,
+        color: Color,
+    },
     Cached(Cached<'a>),
 }
 
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 5c9f4d7e..397c38dd 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -2,7 +2,7 @@ use crate::core::alignment;
 use crate::core::{Rectangle, Size};
 use crate::graphics::color;
 use crate::graphics::text::cache::{self, Cache};
-use crate::graphics::text::{font_system, Paragraph};
+use crate::graphics::text::{font_system, Editor, Paragraph};
 use crate::layer::Text;
 
 use std::borrow::Cow;
@@ -74,15 +74,19 @@ impl Pipeline {
 
         enum Allocation {
             Paragraph(Paragraph),
+            Editor(Editor),
             Cache(cache::KeyHash),
         }
 
         let allocations: Vec<_> = sections
             .iter()
             .map(|section| match section {
-                Text::Managed { paragraph, .. } => {
+                Text::Paragraph { paragraph, .. } => {
                     paragraph.upgrade().map(Allocation::Paragraph)
                 }
+                Text::Editor { editor, .. } => {
+                    editor.upgrade().map(Allocation::Editor)
+                }
                 Text::Cached(text) => {
                     let (key, _) = cache.allocate(
                         font_system,
@@ -117,7 +121,7 @@ impl Pipeline {
                     vertical_alignment,
                     color,
                 ) = match section {
-                    Text::Managed {
+                    Text::Paragraph {
                         position, color, ..
                     } => {
                         use crate::core::text::Paragraph as _;
@@ -135,6 +139,24 @@ impl Pipeline {
                             *color,
                         )
                     }
+                    Text::Editor {
+                        position, color, ..
+                    } => {
+                        use crate::core::text::Editor as _;
+
+                        let Some(Allocation::Editor(editor)) = allocation
+                        else {
+                            return None;
+                        };
+
+                        (
+                            editor.buffer(),
+                            Rectangle::new(*position, editor.min_bounds()),
+                            alignment::Horizontal::Left,
+                            alignment::Vertical::Top,
+                            *color,
+                        )
+                    }
                     Text::Cached(text) => {
                         let Some(Allocation::Cache(key)) = allocation else {
                             return None;
-- 
cgit 


From c7d02e24e6f8265c205a68bd97b2643d40ae30ee Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Thu, 14 Sep 2023 18:57:09 +0200
Subject: Remove `Editor::min_bounds` and use `bounds` instead

---
 wgpu/src/text.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 397c38dd..581df0cb 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -151,7 +151,7 @@ impl Pipeline {
 
                         (
                             editor.buffer(),
-                            Rectangle::new(*position, editor.min_bounds()),
+                            Rectangle::new(*position, editor.bounds()),
                             alignment::Horizontal::Left,
                             alignment::Vertical::Top,
                             *color,
-- 
cgit 


From 3d6b9637c3b1c9f3c654a3ecef7a247cfd6edef3 Mon Sep 17 00:00:00 2001
From: Yuri Astrakhan <YuriAstrakhan@gmail.com>
Date: Tue, 19 Sep 2023 01:31:10 -0400
Subject: Chore: Inline format args for ease of reading

A minor cleanup to inline all simple cases of format arguments. Makes the format strings just a bit easier to read.
---
 wgpu/src/image/atlas.rs       | 8 ++++----
 wgpu/src/image/vector.rs      | 2 +-
 wgpu/src/window/compositor.rs | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index e3de1290..1253496b 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -86,7 +86,7 @@ impl Atlas {
             entry
         };
 
-        log::info!("Allocated atlas entry: {:?}", 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
@@ -139,13 +139,13 @@ impl Atlas {
             }
         }
 
-        log::info!("Current atlas: {:?}", self);
+        log::info!("Current atlas: {self:?}");
 
         Some(entry)
     }
 
     pub fn remove(&mut self, entry: &Entry) {
-        log::info!("Removing atlas entry: {:?}", entry);
+        log::info!("Removing atlas entry: {entry:?}");
 
         match entry {
             Entry::Contiguous(allocation) => {
@@ -258,7 +258,7 @@ impl Atlas {
     }
 
     fn deallocate(&mut self, allocation: &Allocation) {
-        log::info!("Deallocating atlas: {:?}", allocation);
+        log::info!("Deallocating atlas: {allocation:?}");
 
         match allocation {
             Allocation::Full { layer } => {
diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 2c03d36b..6582bb82 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -152,7 +152,7 @@ impl Cache {
                 let allocation =
                     atlas.upload(device, encoder, width, height, &rgba)?;
 
-                log::debug!("allocating {} {}x{}", id, width, height);
+                log::debug!("allocating {id} {width}x{height}");
 
                 let _ = self.svg_hits.insert(id);
                 let _ = self.rasterized_hits.insert(key);
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index a9521a15..1ddbe5fe 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -35,7 +35,7 @@ impl<Theme> Compositor<Theme> {
             ..Default::default()
         });
 
-        log::info!("{:#?}", settings);
+        log::info!("{settings:#?}");
 
         #[cfg(not(target_arch = "wasm32"))]
         if log::max_level() >= log::LevelFilter::Info {
@@ -43,7 +43,7 @@ impl<Theme> Compositor<Theme> {
                 .enumerate_adapters(settings.internal_backend)
                 .map(|adapter| adapter.get_info())
                 .collect();
-            log::info!("Available adapters: {:#?}", available_adapters);
+            log::info!("Available adapters: {available_adapters:#?}");
         }
 
         #[allow(unsafe_code)]
@@ -83,7 +83,7 @@ impl<Theme> Compositor<Theme> {
             })
         })?;
 
-        log::info!("Selected format: {:?}", format);
+        log::info!("Selected format: {format:?}");
 
         #[cfg(target_arch = "wasm32")]
         let limits = [wgpu::Limits::downlevel_webgl2_defaults()
-- 
cgit 


From c997aad85d7ee6e77085e50e5e599002549d228f Mon Sep 17 00:00:00 2001
From: Yuri Astrakhan <YuriAstrakhan@gmail.com>
Date: Tue, 19 Sep 2023 01:46:46 -0400
Subject: Chore: Apply clippy map transformations

Convert `.map().unwrap_or()` to `.map_or()` and similar transformations.
---
 wgpu/src/image/vector.rs | 2 +-
 wgpu/src/triangle.rs     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index 2c03d36b..1ac82bc7 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -56,7 +56,7 @@ impl Cache {
                         .ok()
                 });
 
-                tree.map(Svg::Loaded).unwrap_or(Svg::NotFound)
+                tree.map_or(Svg::NotFound, Svg::Loaded)
             }
             svg::Data::Bytes(bytes) => {
                 match usvg::Tree::from_data(bytes, &usvg::Options::default()) {
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index d430e607..7e1bd9cc 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -349,7 +349,7 @@ fn multisample_state(
     antialiasing: Option<Antialiasing>,
 ) -> wgpu::MultisampleState {
     wgpu::MultisampleState {
-        count: antialiasing.map(|a| a.sample_count()).unwrap_or(1),
+        count: antialiasing.map_or(1, Antialiasing::sample_count),
         mask: !0,
         alpha_to_coverage_enabled: false,
     }
-- 
cgit 


From be340a8cd822be1ea0fe4c1b1f3a62ca66d705b4 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 19 Sep 2023 23:00:20 +0200
Subject: Fix gamma correction for colored glyphs in `iced_wgpu`

---
 wgpu/src/text.rs | 13 ++-----------
 1 file changed, 2 insertions(+), 11 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 581df0cb..f746be63 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -2,7 +2,7 @@ use crate::core::alignment;
 use crate::core::{Rectangle, Size};
 use crate::graphics::color;
 use crate::graphics::text::cache::{self, Cache};
-use crate::graphics::text::{font_system, Editor, Paragraph};
+use crate::graphics::text::{font_system, to_color, Editor, Paragraph};
 use crate::layer::Text;
 
 use std::borrow::Cow;
@@ -214,16 +214,7 @@ impl Pipeline {
                         right: (clip_bounds.x + clip_bounds.width) as i32,
                         bottom: (clip_bounds.y + clip_bounds.height) as i32,
                     },
-                    default_color: {
-                        let [r, g, b, a] = color::pack(color).components();
-
-                        glyphon::Color::rgba(
-                            (r * 255.0) as u8,
-                            (g * 255.0) as u8,
-                            (b * 255.0) as u8,
-                            (a * 255.0) as u8,
-                        )
-                    },
+                    default_color: to_color(color),
                 })
             },
         );
-- 
cgit 


From 34f07b60273d6cfe13834af54cd0e24d34569387 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 04:11:52 +0200
Subject: Fix `clippy::semicolon_if_nothing_returned`

---
 wgpu/src/buffer.rs      | 2 +-
 wgpu/src/geometry.rs    | 4 ++--
 wgpu/src/image/atlas.rs | 2 +-
 wgpu/src/layer.rs       | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs
index 94122187..ef00c58f 100644
--- a/wgpu/src/buffer.rs
+++ b/wgpu/src/buffer.rs
@@ -87,7 +87,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
 
     /// Clears any temporary data (i.e. offsets) from the buffer.
     pub fn clear(&mut self) {
-        self.offsets.clear()
+        self.offsets.clear();
     }
 
     /// Returns the offset at `index`, if it exists.
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index 63a59c05..655362b7 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -480,7 +480,7 @@ impl Frame {
                                 },
                                 size: self.size,
                             }),
-                        ))
+                        ));
                     }
                 }
                 Buffer::Gradient(buffer) => {
@@ -493,7 +493,7 @@ impl Frame {
                                 },
                                 size: self.size,
                             }),
-                        ))
+                        ));
                     }
                 }
             }
diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index 1253496b..e8ca4bd3 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -104,7 +104,7 @@ impl Atlas {
 
             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 {
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index 7a5a0f7c..d20dbe66 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -202,7 +202,7 @@ impl<'a> Layer<'a> {
                         translation,
                         primitive,
                         current_layer,
-                    )
+                    );
                 }
             }
             Primitive::Clip { bounds, content } => {
-- 
cgit 


From 42ed90bc6f92b2085d193e7f143430b8d3847c21 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 04:51:08 +0200
Subject: Fix `clippy::default_trait_access`

---
 wgpu/src/color.rs | 6 +++---
 wgpu/src/text.rs  | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs
index a1025601..20827e3c 100644
--- a/wgpu/src/color.rs
+++ b/wgpu/src/color.rs
@@ -12,7 +12,7 @@ pub fn convert(
 
     let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
         label: Some("iced_wgpu.offscreen.sampler"),
-        ..Default::default()
+        ..wgpu::SamplerDescriptor::default()
     });
 
     //sampler in 0
@@ -102,10 +102,10 @@ pub fn convert(
             primitive: wgpu::PrimitiveState {
                 topology: wgpu::PrimitiveTopology::TriangleList,
                 front_face: wgpu::FrontFace::Cw,
-                ..Default::default()
+                ..wgpu::PrimitiveState::default()
             },
             depth_stencil: None,
-            multisample: Default::default(),
+            multisample: wgpu::MultisampleState::default(),
             multiview: None,
         });
 
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index bd4f3e06..2a530cad 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -64,7 +64,7 @@ impl Pipeline {
             self.renderers.push(glyphon::TextRenderer::new(
                 &mut self.atlas,
                 device,
-                Default::default(),
+                wgpu::MultisampleState::default(),
                 None,
             ));
         }
-- 
cgit 


From caed50b277495e4375975f3f4e271b8fcbc0c33f Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 05:03:25 +0200
Subject: Fix `clippy::match-wildcard-for-single-variants`

---
 wgpu/src/image/atlas.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs
index e8ca4bd3..789e35b4 100644
--- a/wgpu/src/image/atlas.rs
+++ b/wgpu/src/image/atlas.rs
@@ -237,7 +237,7 @@ impl Atlas {
                         }));
                     }
                 }
-                _ => {}
+                Layer::Full => {}
             }
         }
 
-- 
cgit 


From 14ba939e674ec4d9ca53b506ffa3259d30216e85 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 05:19:24 +0200
Subject: Fix `clippy::unreadable_literal`

---
 wgpu/src/triangle.rs | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index 7e1bd9cc..f8014ceb 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -329,12 +329,12 @@ impl Pipeline {
 
 fn fragment_target(
     texture_format: wgpu::TextureFormat,
-) -> Option<wgpu::ColorTargetState> {
-    Some(wgpu::ColorTargetState {
+) -> wgpu::ColorTargetState {
+    wgpu::ColorTargetState {
         format: texture_format,
         blend: Some(wgpu::BlendState::ALPHA_BLENDING),
         write_mask: wgpu::ColorWrites::ALL,
-    })
+    }
 }
 
 fn primitive_state() -> wgpu::PrimitiveState {
@@ -521,7 +521,7 @@ mod solid {
                         fragment: Some(wgpu::FragmentState {
                             module: &shader,
                             entry_point: "solid_fs_main",
-                            targets: &[triangle::fragment_target(format)],
+                            targets: &[Some(triangle::fragment_target(format))],
                         }),
                         primitive: triangle::primitive_state(),
                         depth_stencil: None,
@@ -698,7 +698,7 @@ mod gradient {
                     fragment: Some(wgpu::FragmentState {
                         module: &shader,
                         entry_point: "gradient_fs_main",
-                        targets: &[triangle::fragment_target(format)],
+                        targets: &[Some(triangle::fragment_target(format))],
                     }),
                     primitive: triangle::primitive_state(),
                     depth_stencil: None,
-- 
cgit 


From b27762554627b8e89f2b840b1a8a512e22d4cd87 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 16:26:43 +0200
Subject: Revert "Chore: Apply clippy map transformations"

This reverts commit c997aad85d7ee6e77085e50e5e599002549d228f.
---
 wgpu/src/image/vector.rs | 2 +-
 wgpu/src/triangle.rs     | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image/vector.rs b/wgpu/src/image/vector.rs
index e8baae4f..6582bb82 100644
--- a/wgpu/src/image/vector.rs
+++ b/wgpu/src/image/vector.rs
@@ -56,7 +56,7 @@ impl Cache {
                         .ok()
                 });
 
-                tree.map_or(Svg::NotFound, Svg::Loaded)
+                tree.map(Svg::Loaded).unwrap_or(Svg::NotFound)
             }
             svg::Data::Bytes(bytes) => {
                 match usvg::Tree::from_data(bytes, &usvg::Options::default()) {
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index f8014ceb..c55b93bf 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -349,7 +349,7 @@ fn multisample_state(
     antialiasing: Option<Antialiasing>,
 ) -> wgpu::MultisampleState {
     wgpu::MultisampleState {
-        count: antialiasing.map_or(1, Antialiasing::sample_count),
+        count: antialiasing.map(|a| a.sample_count()).unwrap_or(1),
         mask: !0,
         alpha_to_coverage_enabled: false,
     }
-- 
cgit 


From b8ddd158da1b4e73e67fd090f8d36ed07f191874 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 16:27:54 +0200
Subject: Simplify `map` call in `iced_wgpu::triangle`

---
 wgpu/src/triangle.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index c55b93bf..644c9f84 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -349,7 +349,7 @@ fn multisample_state(
     antialiasing: Option<Antialiasing>,
 ) -> wgpu::MultisampleState {
     wgpu::MultisampleState {
-        count: antialiasing.map(|a| a.sample_count()).unwrap_or(1),
+        count: antialiasing.map(Antialiasing::sample_count).unwrap_or(1),
         mask: !0,
         alpha_to_coverage_enabled: false,
     }
-- 
cgit 


From f137d71e8fb926e784680d56d1cfa6817c3710a1 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Wed, 20 Sep 2023 16:40:03 +0200
Subject: Centralize `clippy` lints in `.cargo/config.toml`

---
 wgpu/src/lib.rs | 6 ------
 1 file changed, 6 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 2f483751..6d26723e 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -26,14 +26,8 @@
     //missing_docs,
     unsafe_code,
     unused_results,
-    clippy::extra_unused_lifetimes,
-    clippy::from_over_into,
-    clippy::needless_borrow,
-    clippy::new_without_default,
-    clippy::useless_conversion,
     rustdoc::broken_intra_doc_links
 )]
-#![allow(clippy::inherent_to_string, clippy::type_complexity)]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
 pub mod layer;
 pub mod primitive;
-- 
cgit 


From 86b877517feb15b2155c6cfef29246a3f281c8ae Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Fri, 27 Oct 2023 03:21:40 +0200
Subject: Update `wgpu` to `0.18` and `cosmic-text` to `0.10`

---
 wgpu/src/backend.rs       | 8 ++++++--
 wgpu/src/color.rs         | 4 +++-
 wgpu/src/triangle.rs      | 7 ++++++-
 wgpu/src/triangle/msaa.rs | 4 +++-
 4 files changed, 18 insertions(+), 5 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 65c63f19..32b8a189 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -222,10 +222,12 @@ impl Backend {
                             }),
                             None => wgpu::LoadOp::Load,
                         },
-                        store: true,
+                        store: wgpu::StoreOp::Store,
                     },
                 })],
                 depth_stencil_attachment: None,
+                timestamp_writes: None,
+                occlusion_query_set: None,
             },
         ));
 
@@ -271,11 +273,13 @@ impl Backend {
                                 resolve_target: None,
                                 ops: wgpu::Operations {
                                     load: wgpu::LoadOp::Load,
-                                    store: true,
+                                    store: wgpu::StoreOp::Store,
                                 },
                             },
                         )],
                         depth_stencil_attachment: None,
+                        timestamp_writes: None,
+                        occlusion_query_set: None,
                     },
                 ));
             }
diff --git a/wgpu/src/color.rs b/wgpu/src/color.rs
index 20827e3c..4598b0a6 100644
--- a/wgpu/src/color.rs
+++ b/wgpu/src/color.rs
@@ -143,10 +143,12 @@ pub fn convert(
             resolve_target: None,
             ops: wgpu::Operations {
                 load: wgpu::LoadOp::Load,
-                store: true,
+                store: wgpu::StoreOp::Store,
             },
         })],
         depth_stencil_attachment: None,
+        timestamp_writes: None,
+        occlusion_query_set: None,
     });
 
     pass.set_pipeline(&pipeline);
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index 644c9f84..69270a73 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -300,10 +300,15 @@ impl Pipeline {
                         wgpu::RenderPassColorAttachment {
                             view: attachment,
                             resolve_target,
-                            ops: wgpu::Operations { load, store: true },
+                            ops: wgpu::Operations {
+                                load,
+                                store: wgpu::StoreOp::Store,
+                            },
                         },
                     )],
                     depth_stencil_attachment: None,
+                    timestamp_writes: None,
+                    occlusion_query_set: None,
                 });
 
             let layer = &mut self.layers[layer];
diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs
index 320b5b12..14abd20b 100644
--- a/wgpu/src/triangle/msaa.rs
+++ b/wgpu/src/triangle/msaa.rs
@@ -167,10 +167,12 @@ impl Blit {
                     resolve_target: None,
                     ops: wgpu::Operations {
                         load: wgpu::LoadOp::Load,
-                        store: true,
+                        store: wgpu::StoreOp::Store,
                     },
                 })],
                 depth_stencil_attachment: None,
+                timestamp_writes: None,
+                occlusion_query_set: None,
             });
 
         render_pass.set_pipeline(&self.pipeline);
-- 
cgit 


From 625cd745f38215b1cb8f629cdc6d2fa41c9a739a Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Fri, 27 Oct 2023 05:04:14 +0200
Subject: Write documentation for the new text APIs

---
 wgpu/src/layer/text.rs | 7 ++++++-
 wgpu/src/lib.rs        | 2 +-
 2 files changed, 7 insertions(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs
index d46b39da..66417cec 100644
--- a/wgpu/src/layer/text.rs
+++ b/wgpu/src/layer/text.rs
@@ -4,19 +4,24 @@ use crate::core::{Color, Font, Pixels, Point, Rectangle};
 use crate::graphics::text::editor;
 use crate::graphics::text::paragraph;
 
-/// A paragraph of text.
+/// A text primitive.
 #[derive(Debug, Clone)]
 pub enum Text<'a> {
+    /// A paragraph.
+    #[allow(missing_docs)]
     Paragraph {
         paragraph: paragraph::Weak,
         position: Point,
         color: Color,
     },
+    /// An editor.
+    #[allow(missing_docs)]
     Editor {
         editor: editor::Weak,
         position: Point,
         color: Color,
     },
+    /// A cached text.
     Cached(Cached<'a>),
 }
 
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 6d26723e..424dfeb3 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -23,7 +23,7 @@
 #![forbid(rust_2018_idioms)]
 #![deny(
     missing_debug_implementations,
-    //missing_docs,
+    missing_docs,
     unsafe_code,
     unused_results,
     rustdoc::broken_intra_doc_links
-- 
cgit 


From 5759096a4c33935fcdf5f96606143e4f21159186 Mon Sep 17 00:00:00 2001
From: Remmirad <remmirad@posteo.net>
Date: Wed, 31 May 2023 15:46:21 +0200
Subject: Implement texture filtering options

---
 wgpu/src/image.rs | 69 ++++++++++++++++++++++++++++++++++++-------------------
 1 file changed, 45 insertions(+), 24 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 553ba330..a0fe7e83 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -7,6 +7,7 @@ mod raster;
 mod vector;
 
 use atlas::Atlas;
+use iced_graphics::core::image::{TextureFilter, FilterMethod};
 
 use crate::core::{Rectangle, Size};
 use crate::graphics::Transformation;
@@ -14,6 +15,7 @@ use crate::layer;
 use crate::Buffer;
 
 use std::cell::RefCell;
+use std::collections::HashMap;
 use std::mem;
 
 use bytemuck::{Pod, Zeroable};
@@ -37,7 +39,7 @@ pub struct Pipeline {
     pipeline: wgpu::RenderPipeline,
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
-    sampler: wgpu::Sampler,
+    sampler: HashMap<TextureFilter,wgpu::Sampler>,
     texture: wgpu::BindGroup,
     texture_version: usize,
     texture_atlas: Atlas,
@@ -142,15 +144,32 @@ impl Pipeline {
     pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
         use wgpu::util::DeviceExt;
 
-        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
-            address_mode_u: wgpu::AddressMode::ClampToEdge,
-            address_mode_v: wgpu::AddressMode::ClampToEdge,
-            address_mode_w: wgpu::AddressMode::ClampToEdge,
-            mag_filter: wgpu::FilterMode::Linear,
-            min_filter: wgpu::FilterMode::Linear,
-            mipmap_filter: wgpu::FilterMode::Linear,
-            ..Default::default()
-        });
+        let to_wgpu = |method: FilterMethod| {
+            match method {
+                FilterMethod::Linear => wgpu::FilterMode::Linear,
+                FilterMethod::Nearest => wgpu::FilterMode::Nearest,
+            }
+        };
+
+        let mut sampler = HashMap::new();
+
+        let filter = [FilterMethod::Linear, FilterMethod::Nearest];
+        for min in 0..filter.len() {
+            for mag in 0..filter.len() {
+                let _ = sampler.insert(TextureFilter {min: filter[min], mag: filter[mag]}, 
+                    device.create_sampler(&wgpu::SamplerDescriptor {
+                        address_mode_u: wgpu::AddressMode::ClampToEdge,
+                        address_mode_v: wgpu::AddressMode::ClampToEdge,
+                        address_mode_w: wgpu::AddressMode::ClampToEdge,
+                        mag_filter: to_wgpu(filter[mag]),
+                        min_filter: to_wgpu(filter[min]),
+                        mipmap_filter: wgpu::FilterMode::Linear,
+                        ..Default::default()
+                    }
+                ));
+            }
+        }
+
 
         let constant_layout =
             device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@@ -355,7 +374,7 @@ impl Pipeline {
         #[cfg(feature = "tracing")]
         let _ = info_span!("Wgpu::Image", "DRAW").entered();
 
-        let instances: &mut Vec<Instance> = &mut Vec::new();
+        let instances: &mut HashMap<TextureFilter,Vec<Instance>> = &mut HashMap::new();
 
         #[cfg(feature = "image")]
         let mut raster_cache = self.raster_cache.borrow_mut();
@@ -377,7 +396,7 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             [bounds.width, bounds.height],
                             atlas_entry,
-                            instances,
+                            instances.entry(handle.filter().clone()).or_insert(Vec::new()),
                         );
                     }
                 }
@@ -405,7 +424,7 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             size,
                             atlas_entry,
-                            instances,
+                            instances.entry(TextureFilter::default()).or_insert(Vec::new()),
                         );
                     }
                 }
@@ -438,18 +457,20 @@ impl Pipeline {
             self.texture_version = texture_version;
         }
 
-        if self.layers.len() <= self.prepare_layer {
-            self.layers.push(Layer::new(
-                device,
-                &self.constant_layout,
-                &self.sampler,
-            ));
+        for (filter, instances) in instances.iter_mut() {
+            if self.layers.len() <= self.prepare_layer {
+                self.layers.push(Layer::new(
+                    device,
+                    &self.constant_layout,
+                    &self.sampler.get(filter).expect("Sampler is registered"),
+                ));
+            }
+    
+            let layer = &mut self.layers[self.prepare_layer];
+            layer.prepare(device, queue, &instances, transformation);
+    
+            self.prepare_layer += 1;
         }
-
-        let layer = &mut self.layers[self.prepare_layer];
-        layer.prepare(device, queue, instances, transformation);
-
-        self.prepare_layer += 1;
     }
 
     pub fn render<'a>(
-- 
cgit 


From 4b32a488808e371313ce78e727c9d98ab2eb759e Mon Sep 17 00:00:00 2001
From: Remmirad <remmirad@posteo.net>
Date: Fri, 4 Aug 2023 13:50:16 +0200
Subject: Fix clippy + fmt

---
 wgpu/src/image.rs | 42 ++++++++++++++++++++++++------------------
 1 file changed, 24 insertions(+), 18 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index a0fe7e83..a3168001 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -7,7 +7,7 @@ mod raster;
 mod vector;
 
 use atlas::Atlas;
-use iced_graphics::core::image::{TextureFilter, FilterMethod};
+use iced_graphics::core::image::{FilterMethod, TextureFilter};
 
 use crate::core::{Rectangle, Size};
 use crate::graphics::Transformation;
@@ -39,7 +39,7 @@ pub struct Pipeline {
     pipeline: wgpu::RenderPipeline,
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
-    sampler: HashMap<TextureFilter,wgpu::Sampler>,
+    sampler: HashMap<TextureFilter, wgpu::Sampler>,
     texture: wgpu::BindGroup,
     texture_version: usize,
     texture_atlas: Atlas,
@@ -144,11 +144,9 @@ impl Pipeline {
     pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
         use wgpu::util::DeviceExt;
 
-        let to_wgpu = |method: FilterMethod| {
-            match method {
-                FilterMethod::Linear => wgpu::FilterMode::Linear,
-                FilterMethod::Nearest => wgpu::FilterMode::Nearest,
-            }
+        let to_wgpu = |method: FilterMethod| match method {
+            FilterMethod::Linear => wgpu::FilterMode::Linear,
+            FilterMethod::Nearest => wgpu::FilterMode::Nearest,
         };
 
         let mut sampler = HashMap::new();
@@ -156,7 +154,11 @@ impl Pipeline {
         let filter = [FilterMethod::Linear, FilterMethod::Nearest];
         for min in 0..filter.len() {
             for mag in 0..filter.len() {
-                let _ = sampler.insert(TextureFilter {min: filter[min], mag: filter[mag]}, 
+                let _ = sampler.insert(
+                    TextureFilter {
+                        min: filter[min],
+                        mag: filter[mag],
+                    },
                     device.create_sampler(&wgpu::SamplerDescriptor {
                         address_mode_u: wgpu::AddressMode::ClampToEdge,
                         address_mode_v: wgpu::AddressMode::ClampToEdge,
@@ -165,12 +167,11 @@ impl Pipeline {
                         min_filter: to_wgpu(filter[min]),
                         mipmap_filter: wgpu::FilterMode::Linear,
                         ..Default::default()
-                    }
-                ));
+                    }),
+                );
             }
         }
 
-
         let constant_layout =
             device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
                 label: Some("iced_wgpu::image constants layout"),
@@ -374,7 +375,8 @@ impl Pipeline {
         #[cfg(feature = "tracing")]
         let _ = info_span!("Wgpu::Image", "DRAW").entered();
 
-        let instances: &mut HashMap<TextureFilter,Vec<Instance>> = &mut HashMap::new();
+        let instances: &mut HashMap<TextureFilter, Vec<Instance>> =
+            &mut HashMap::new();
 
         #[cfg(feature = "image")]
         let mut raster_cache = self.raster_cache.borrow_mut();
@@ -396,7 +398,9 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             [bounds.width, bounds.height],
                             atlas_entry,
-                            instances.entry(handle.filter().clone()).or_insert(Vec::new()),
+                            instances
+                                .entry(handle.filter().clone())
+                                .or_insert(Vec::new()),
                         );
                     }
                 }
@@ -424,7 +428,9 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             size,
                             atlas_entry,
-                            instances.entry(TextureFilter::default()).or_insert(Vec::new()),
+                            instances
+                                .entry(TextureFilter::default())
+                                .or_insert(Vec::new()),
                         );
                     }
                 }
@@ -462,13 +468,13 @@ impl Pipeline {
                 self.layers.push(Layer::new(
                     device,
                     &self.constant_layout,
-                    &self.sampler.get(filter).expect("Sampler is registered"),
+                    self.sampler.get(filter).expect("Sampler is registered"),
                 ));
             }
-    
+
             let layer = &mut self.layers[self.prepare_layer];
-            layer.prepare(device, queue, &instances, transformation);
-    
+            layer.prepare(device, queue, instances, transformation);
+
             self.prepare_layer += 1;
         }
     }
-- 
cgit 


From e5d3e75d826e9fad8a0da5dd538aa542059dd034 Mon Sep 17 00:00:00 2001
From: Remmirad <remmirad@posteo.net>
Date: Mon, 25 Sep 2023 21:54:50 +0200
Subject: fix design for wgpu backend

---
 wgpu/src/image.rs | 133 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 77 insertions(+), 56 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index a3168001..0aa7f899 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -8,6 +8,7 @@ mod vector;
 
 use atlas::Atlas;
 use iced_graphics::core::image::{FilterMethod, TextureFilter};
+use wgpu::Sampler;
 
 use crate::core::{Rectangle, Size};
 use crate::graphics::Transformation;
@@ -15,7 +16,6 @@ use crate::layer;
 use crate::Buffer;
 
 use std::cell::RefCell;
-use std::collections::HashMap;
 use std::mem;
 
 use bytemuck::{Pod, Zeroable};
@@ -29,6 +29,8 @@ use crate::core::svg;
 #[cfg(feature = "tracing")]
 use tracing::info_span;
 
+const SAMPLER_COUNT: usize = 4;
+
 #[derive(Debug)]
 pub struct Pipeline {
     #[cfg(feature = "image")]
@@ -39,14 +41,14 @@ pub struct Pipeline {
     pipeline: wgpu::RenderPipeline,
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
-    sampler: HashMap<TextureFilter, wgpu::Sampler>,
+    sampler: [wgpu::Sampler; SAMPLER_COUNT],
     texture: wgpu::BindGroup,
     texture_version: usize,
     texture_atlas: Atlas,
     texture_layout: wgpu::BindGroupLayout,
     constant_layout: wgpu::BindGroupLayout,
 
-    layers: Vec<Layer>,
+    layers: Vec<[Option<Layer>; SAMPLER_COUNT]>,
     prepare_layer: usize,
 }
 
@@ -149,28 +151,32 @@ impl Pipeline {
             FilterMethod::Nearest => wgpu::FilterMode::Nearest,
         };
 
-        let mut sampler = HashMap::new();
+        let mut sampler: [Option<Sampler>; SAMPLER_COUNT] =
+            [None, None, None, None];
 
         let filter = [FilterMethod::Linear, FilterMethod::Nearest];
         for min in 0..filter.len() {
             for mag in 0..filter.len() {
-                let _ = sampler.insert(
-                    TextureFilter {
-                        min: filter[min],
-                        mag: filter[mag],
-                    },
-                    device.create_sampler(&wgpu::SamplerDescriptor {
-                        address_mode_u: wgpu::AddressMode::ClampToEdge,
-                        address_mode_v: wgpu::AddressMode::ClampToEdge,
-                        address_mode_w: wgpu::AddressMode::ClampToEdge,
-                        mag_filter: to_wgpu(filter[mag]),
-                        min_filter: to_wgpu(filter[min]),
-                        mipmap_filter: wgpu::FilterMode::Linear,
-                        ..Default::default()
-                    }),
-                );
+                sampler[to_index(&TextureFilter {
+                    min: filter[min],
+                    mag: filter[mag],
+                })] = Some(device.create_sampler(&wgpu::SamplerDescriptor {
+                    address_mode_u: wgpu::AddressMode::ClampToEdge,
+                    address_mode_v: wgpu::AddressMode::ClampToEdge,
+                    address_mode_w: wgpu::AddressMode::ClampToEdge,
+                    mag_filter: to_wgpu(filter[mag]),
+                    min_filter: to_wgpu(filter[min]),
+                    mipmap_filter: wgpu::FilterMode::Linear,
+                    ..Default::default()
+                }));
             }
         }
+        let sampler = [
+            sampler[0].take().unwrap(),
+            sampler[1].take().unwrap(),
+            sampler[2].take().unwrap(),
+            sampler[3].take().unwrap(),
+        ];
 
         let constant_layout =
             device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@@ -375,8 +381,8 @@ impl Pipeline {
         #[cfg(feature = "tracing")]
         let _ = info_span!("Wgpu::Image", "DRAW").entered();
 
-        let instances: &mut HashMap<TextureFilter, Vec<Instance>> =
-            &mut HashMap::new();
+        let mut instances: [Vec<Instance>; SAMPLER_COUNT] =
+            [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
 
         #[cfg(feature = "image")]
         let mut raster_cache = self.raster_cache.borrow_mut();
@@ -398,9 +404,7 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             [bounds.width, bounds.height],
                             atlas_entry,
-                            instances
-                                .entry(handle.filter().clone())
-                                .or_insert(Vec::new()),
+                            &mut instances[to_index(handle.filter())],
                         );
                     }
                 }
@@ -428,9 +432,7 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             size,
                             atlas_entry,
-                            instances
-                                .entry(TextureFilter::default())
-                                .or_insert(Vec::new()),
+                            &mut instances[to_index(&TextureFilter::default())],
                         );
                     }
                 }
@@ -463,20 +465,26 @@ impl Pipeline {
             self.texture_version = texture_version;
         }
 
-        for (filter, instances) in instances.iter_mut() {
-            if self.layers.len() <= self.prepare_layer {
-                self.layers.push(Layer::new(
-                    device,
-                    &self.constant_layout,
-                    self.sampler.get(filter).expect("Sampler is registered"),
-                ));
+        if self.layers.len() <= self.prepare_layer {
+            self.layers.push([None, None, None, None]);
+        }
+        for (i, instances) in instances.iter_mut().enumerate() {
+            let layer = &mut self.layers[self.prepare_layer][i];
+            if !instances.is_empty() {
+                if layer.is_none() {
+                    *layer = Some(Layer::new(
+                        device,
+                        &self.constant_layout,
+                        &self.sampler[i],
+                    ))
+                }
             }
 
-            let layer = &mut self.layers[self.prepare_layer];
-            layer.prepare(device, queue, instances, transformation);
-
-            self.prepare_layer += 1;
+            if let Some(layer) = layer {
+                layer.prepare(device, queue, instances, transformation);
+            }
         }
+        self.prepare_layer += 1;
     }
 
     pub fn render<'a>(
@@ -485,24 +493,29 @@ impl Pipeline {
         bounds: Rectangle<u32>,
         render_pass: &mut wgpu::RenderPass<'a>,
     ) {
-        if let Some(layer) = self.layers.get(layer) {
-            render_pass.set_pipeline(&self.pipeline);
-
-            render_pass.set_scissor_rect(
-                bounds.x,
-                bounds.y,
-                bounds.width,
-                bounds.height,
-            );
-
-            render_pass.set_bind_group(1, &self.texture, &[]);
-            render_pass.set_index_buffer(
-                self.indices.slice(..),
-                wgpu::IndexFormat::Uint16,
-            );
-            render_pass.set_vertex_buffer(0, self.vertices.slice(..));
-
-            layer.render(render_pass);
+        if let Some(layer_group) = self.layers.get(layer) {
+            for (i, layer) in layer_group.iter().enumerate() {
+                if let Some(layer) = layer {
+                    println!("Render {i}");
+                    render_pass.set_pipeline(&self.pipeline);
+
+                    render_pass.set_scissor_rect(
+                        bounds.x,
+                        bounds.y,
+                        bounds.width,
+                        bounds.height,
+                    );
+
+                    render_pass.set_bind_group(1, &self.texture, &[]);
+                    render_pass.set_index_buffer(
+                        self.indices.slice(..),
+                        wgpu::IndexFormat::Uint16,
+                    );
+                    render_pass.set_vertex_buffer(0, self.vertices.slice(..));
+
+                    layer.render(render_pass);
+                }
+            }
         }
     }
 
@@ -517,6 +530,14 @@ impl Pipeline {
     }
 }
 
+fn to_index(filter: &TextureFilter) -> usize {
+    let to_index = |m| match m {
+        FilterMethod::Linear => 0,
+        FilterMethod::Nearest => 1,
+    };
+    return (to_index(filter.mag) << 1) | (to_index(filter.min));
+}
+
 #[repr(C)]
 #[derive(Clone, Copy, Zeroable, Pod)]
 pub struct Vertex {
-- 
cgit 


From 75c9afc608a4a9ff44d60a8fb6f4a5819f05bf79 Mon Sep 17 00:00:00 2001
From: Remmirad <remmirad@posteo.net>
Date: Mon, 25 Sep 2023 22:03:22 +0200
Subject: Remove debug traces

---
 wgpu/src/image.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 0aa7f899..6768a714 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -494,9 +494,8 @@ impl Pipeline {
         render_pass: &mut wgpu::RenderPass<'a>,
     ) {
         if let Some(layer_group) = self.layers.get(layer) {
-            for (i, layer) in layer_group.iter().enumerate() {
+            for layer in layer_group.iter() {
                 if let Some(layer) = layer {
-                    println!("Render {i}");
                     render_pass.set_pipeline(&self.pipeline);
 
                     render_pass.set_scissor_rect(
-- 
cgit 


From a5125d6fea824df1191777fe3eb53a2f748208b9 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Sat, 11 Nov 2023 07:02:01 +0100
Subject: Refactor texture image filtering

- Support only `Linear` or `Nearest`
- Simplify `Layer` groups
- Move `FilterMethod` to `Image` and `image::Viewer`
---
 wgpu/src/image.rs       | 238 +++++++++++++++++++++++++++---------------------
 wgpu/src/layer.rs       |   7 +-
 wgpu/src/layer/image.rs |   3 +
 3 files changed, 141 insertions(+), 107 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 6768a714..1a88c6ae 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -7,8 +7,6 @@ mod raster;
 mod vector;
 
 use atlas::Atlas;
-use iced_graphics::core::image::{FilterMethod, TextureFilter};
-use wgpu::Sampler;
 
 use crate::core::{Rectangle, Size};
 use crate::graphics::Transformation;
@@ -29,8 +27,6 @@ use crate::core::svg;
 #[cfg(feature = "tracing")]
 use tracing::info_span;
 
-const SAMPLER_COUNT: usize = 4;
-
 #[derive(Debug)]
 pub struct Pipeline {
     #[cfg(feature = "image")]
@@ -41,30 +37,31 @@ pub struct Pipeline {
     pipeline: wgpu::RenderPipeline,
     vertices: wgpu::Buffer,
     indices: wgpu::Buffer,
-    sampler: [wgpu::Sampler; SAMPLER_COUNT],
+    nearest_sampler: wgpu::Sampler,
+    linear_sampler: wgpu::Sampler,
     texture: wgpu::BindGroup,
     texture_version: usize,
     texture_atlas: Atlas,
     texture_layout: wgpu::BindGroupLayout,
     constant_layout: wgpu::BindGroupLayout,
 
-    layers: Vec<[Option<Layer>; SAMPLER_COUNT]>,
+    layers: Vec<Layer>,
     prepare_layer: usize,
 }
 
 #[derive(Debug)]
 struct Layer {
     uniforms: wgpu::Buffer,
-    constants: wgpu::BindGroup,
-    instances: Buffer<Instance>,
-    instance_count: usize,
+    nearest: Data,
+    linear: Data,
 }
 
 impl Layer {
     fn new(
         device: &wgpu::Device,
         constant_layout: &wgpu::BindGroupLayout,
-        sampler: &wgpu::Sampler,
+        nearest_sampler: &wgpu::Sampler,
+        linear_sampler: &wgpu::Sampler,
     ) -> Self {
         let uniforms = device.create_buffer(&wgpu::BufferDescriptor {
             label: Some("iced_wgpu::image uniforms buffer"),
@@ -73,6 +70,59 @@ impl Layer {
             mapped_at_creation: false,
         });
 
+        let nearest =
+            Data::new(device, constant_layout, nearest_sampler, &uniforms);
+
+        let linear =
+            Data::new(device, constant_layout, linear_sampler, &uniforms);
+
+        Self {
+            uniforms,
+            nearest,
+            linear,
+        }
+    }
+
+    fn prepare(
+        &mut self,
+        device: &wgpu::Device,
+        queue: &wgpu::Queue,
+        nearest_instances: &[Instance],
+        linear_instances: &[Instance],
+        transformation: Transformation,
+    ) {
+        queue.write_buffer(
+            &self.uniforms,
+            0,
+            bytemuck::bytes_of(&Uniforms {
+                transform: transformation.into(),
+            }),
+        );
+
+        self.nearest.upload(device, queue, nearest_instances);
+        self.linear.upload(device, queue, linear_instances);
+    }
+
+    fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
+        self.nearest.render(render_pass);
+        self.linear.render(render_pass);
+    }
+}
+
+#[derive(Debug)]
+struct Data {
+    constants: wgpu::BindGroup,
+    instances: Buffer<Instance>,
+    instance_count: usize,
+}
+
+impl Data {
+    pub fn new(
+        device: &wgpu::Device,
+        constant_layout: &wgpu::BindGroupLayout,
+        sampler: &wgpu::Sampler,
+        uniforms: &wgpu::Buffer,
+    ) -> Self {
         let constants = device.create_bind_group(&wgpu::BindGroupDescriptor {
             label: Some("iced_wgpu::image constants bind group"),
             layout: constant_layout,
@@ -102,28 +152,18 @@ impl Layer {
         );
 
         Self {
-            uniforms,
             constants,
             instances,
             instance_count: 0,
         }
     }
 
-    fn prepare(
+    fn upload(
         &mut self,
         device: &wgpu::Device,
         queue: &wgpu::Queue,
         instances: &[Instance],
-        transformation: Transformation,
     ) {
-        queue.write_buffer(
-            &self.uniforms,
-            0,
-            bytemuck::bytes_of(&Uniforms {
-                transform: transformation.into(),
-            }),
-        );
-
         let _ = self.instances.resize(device, instances.len());
         let _ = self.instances.write(queue, 0, instances);
 
@@ -146,37 +186,25 @@ impl Pipeline {
     pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
         use wgpu::util::DeviceExt;
 
-        let to_wgpu = |method: FilterMethod| match method {
-            FilterMethod::Linear => wgpu::FilterMode::Linear,
-            FilterMethod::Nearest => wgpu::FilterMode::Nearest,
-        };
-
-        let mut sampler: [Option<Sampler>; SAMPLER_COUNT] =
-            [None, None, None, None];
-
-        let filter = [FilterMethod::Linear, FilterMethod::Nearest];
-        for min in 0..filter.len() {
-            for mag in 0..filter.len() {
-                sampler[to_index(&TextureFilter {
-                    min: filter[min],
-                    mag: filter[mag],
-                })] = Some(device.create_sampler(&wgpu::SamplerDescriptor {
-                    address_mode_u: wgpu::AddressMode::ClampToEdge,
-                    address_mode_v: wgpu::AddressMode::ClampToEdge,
-                    address_mode_w: wgpu::AddressMode::ClampToEdge,
-                    mag_filter: to_wgpu(filter[mag]),
-                    min_filter: to_wgpu(filter[min]),
-                    mipmap_filter: wgpu::FilterMode::Linear,
-                    ..Default::default()
-                }));
-            }
-        }
-        let sampler = [
-            sampler[0].take().unwrap(),
-            sampler[1].take().unwrap(),
-            sampler[2].take().unwrap(),
-            sampler[3].take().unwrap(),
-        ];
+        let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+            address_mode_u: wgpu::AddressMode::ClampToEdge,
+            address_mode_v: wgpu::AddressMode::ClampToEdge,
+            address_mode_w: wgpu::AddressMode::ClampToEdge,
+            min_filter: wgpu::FilterMode::Nearest,
+            mag_filter: wgpu::FilterMode::Nearest,
+            mipmap_filter: wgpu::FilterMode::Nearest,
+            ..Default::default()
+        });
+
+        let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
+            address_mode_u: wgpu::AddressMode::ClampToEdge,
+            address_mode_v: wgpu::AddressMode::ClampToEdge,
+            address_mode_w: wgpu::AddressMode::ClampToEdge,
+            min_filter: wgpu::FilterMode::Linear,
+            mag_filter: wgpu::FilterMode::Linear,
+            mipmap_filter: wgpu::FilterMode::Linear,
+            ..Default::default()
+        });
 
         let constant_layout =
             device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@@ -338,7 +366,8 @@ impl Pipeline {
             pipeline,
             vertices,
             indices,
-            sampler,
+            nearest_sampler,
+            linear_sampler,
             texture,
             texture_version: texture_atlas.layer_count(),
             texture_atlas,
@@ -381,8 +410,8 @@ impl Pipeline {
         #[cfg(feature = "tracing")]
         let _ = info_span!("Wgpu::Image", "DRAW").entered();
 
-        let mut instances: [Vec<Instance>; SAMPLER_COUNT] =
-            [Vec::new(), Vec::new(), Vec::new(), Vec::new()];
+        let nearest_instances: &mut Vec<Instance> = &mut Vec::new();
+        let linear_instances: &mut Vec<Instance> = &mut Vec::new();
 
         #[cfg(feature = "image")]
         let mut raster_cache = self.raster_cache.borrow_mut();
@@ -393,7 +422,11 @@ impl Pipeline {
         for image in images {
             match &image {
                 #[cfg(feature = "image")]
-                layer::Image::Raster { handle, bounds } => {
+                layer::Image::Raster {
+                    handle,
+                    filter_method,
+                    bounds,
+                } => {
                     if let Some(atlas_entry) = raster_cache.upload(
                         device,
                         encoder,
@@ -404,7 +437,12 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             [bounds.width, bounds.height],
                             atlas_entry,
-                            &mut instances[to_index(handle.filter())],
+                            match filter_method {
+                                image::FilterMethod::Nearest => {
+                                    nearest_instances
+                                }
+                                image::FilterMethod::Linear => linear_instances,
+                            },
                         );
                     }
                 }
@@ -432,7 +470,7 @@ impl Pipeline {
                             [bounds.x, bounds.y],
                             size,
                             atlas_entry,
-                            &mut instances[to_index(&TextureFilter::default())],
+                            nearest_instances,
                         );
                     }
                 }
@@ -441,7 +479,7 @@ impl Pipeline {
             }
         }
 
-        if instances.is_empty() {
+        if nearest_instances.is_empty() && linear_instances.is_empty() {
             return;
         }
 
@@ -466,24 +504,24 @@ impl Pipeline {
         }
 
         if self.layers.len() <= self.prepare_layer {
-            self.layers.push([None, None, None, None]);
+            self.layers.push(Layer::new(
+                device,
+                &self.constant_layout,
+                &self.nearest_sampler,
+                &self.linear_sampler,
+            ));
         }
-        for (i, instances) in instances.iter_mut().enumerate() {
-            let layer = &mut self.layers[self.prepare_layer][i];
-            if !instances.is_empty() {
-                if layer.is_none() {
-                    *layer = Some(Layer::new(
-                        device,
-                        &self.constant_layout,
-                        &self.sampler[i],
-                    ))
-                }
-            }
 
-            if let Some(layer) = layer {
-                layer.prepare(device, queue, instances, transformation);
-            }
-        }
+        let layer = &mut self.layers[self.prepare_layer];
+
+        layer.prepare(
+            device,
+            queue,
+            &nearest_instances,
+            &linear_instances,
+            transformation,
+        );
+
         self.prepare_layer += 1;
     }
 
@@ -493,28 +531,24 @@ impl Pipeline {
         bounds: Rectangle<u32>,
         render_pass: &mut wgpu::RenderPass<'a>,
     ) {
-        if let Some(layer_group) = self.layers.get(layer) {
-            for layer in layer_group.iter() {
-                if let Some(layer) = layer {
-                    render_pass.set_pipeline(&self.pipeline);
-
-                    render_pass.set_scissor_rect(
-                        bounds.x,
-                        bounds.y,
-                        bounds.width,
-                        bounds.height,
-                    );
-
-                    render_pass.set_bind_group(1, &self.texture, &[]);
-                    render_pass.set_index_buffer(
-                        self.indices.slice(..),
-                        wgpu::IndexFormat::Uint16,
-                    );
-                    render_pass.set_vertex_buffer(0, self.vertices.slice(..));
-
-                    layer.render(render_pass);
-                }
-            }
+        if let Some(layer) = self.layers.get(layer) {
+            render_pass.set_pipeline(&self.pipeline);
+
+            render_pass.set_scissor_rect(
+                bounds.x,
+                bounds.y,
+                bounds.width,
+                bounds.height,
+            );
+
+            render_pass.set_bind_group(1, &self.texture, &[]);
+            render_pass.set_index_buffer(
+                self.indices.slice(..),
+                wgpu::IndexFormat::Uint16,
+            );
+            render_pass.set_vertex_buffer(0, self.vertices.slice(..));
+
+            layer.render(render_pass);
         }
     }
 
@@ -529,14 +563,6 @@ impl Pipeline {
     }
 }
 
-fn to_index(filter: &TextureFilter) -> usize {
-    let to_index = |m| match m {
-        FilterMethod::Linear => 0,
-        FilterMethod::Nearest => 1,
-    };
-    return (to_index(filter.mag) << 1) | (to_index(filter.min));
-}
-
 #[repr(C)]
 #[derive(Clone, Copy, Zeroable, Pod)]
 pub struct Vertex {
@@ -571,7 +597,7 @@ struct Instance {
 }
 
 impl Instance {
-    pub const INITIAL: usize = 1_000;
+    pub const INITIAL: usize = 20;
 }
 
 #[repr(C)]
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index b251538e..286801e6 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -186,11 +186,16 @@ impl<'a> Layer<'a> {
 
                 layer.quads.add(quad, background);
             }
-            Primitive::Image { handle, bounds } => {
+            Primitive::Image {
+                handle,
+                filter_method,
+                bounds,
+            } => {
                 let layer = &mut layers[current_layer];
 
                 layer.images.push(Image::Raster {
                     handle: handle.clone(),
+                    filter_method: *filter_method,
                     bounds: *bounds + translation,
                 });
             }
diff --git a/wgpu/src/layer/image.rs b/wgpu/src/layer/image.rs
index 0de589f8..facbe192 100644
--- a/wgpu/src/layer/image.rs
+++ b/wgpu/src/layer/image.rs
@@ -10,6 +10,9 @@ pub enum Image {
         /// The handle of a raster image.
         handle: image::Handle,
 
+        /// The filter method of a raster image.
+        filter_method: image::FilterMethod,
+
         /// The bounds of the image.
         bounds: Rectangle,
     },
-- 
cgit 


From 9d560c813566ba04be3e23ae1b14861365485b57 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Sat, 11 Nov 2023 07:27:38 +0100
Subject: Fix unnecessary references in `iced_wgpu::image`

---
 wgpu/src/image.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 1a88c6ae..b78802c7 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -131,7 +131,7 @@ impl Data {
                     binding: 0,
                     resource: wgpu::BindingResource::Buffer(
                         wgpu::BufferBinding {
-                            buffer: &uniforms,
+                            buffer: uniforms,
                             offset: 0,
                             size: None,
                         },
@@ -517,8 +517,8 @@ impl Pipeline {
         layer.prepare(
             device,
             queue,
-            &nearest_instances,
-            &linear_instances,
+            nearest_instances,
+            linear_instances,
             transformation,
         );
 
-- 
cgit 


From 781ef1f94c4859aeeb852f801b72be095b8ff82b Mon Sep 17 00:00:00 2001
From: Bingus <shankern@protonmail.com>
Date: Thu, 14 Sep 2023 13:58:36 -0700
Subject: Added support for custom shader widget for iced_wgpu backend.

---
 wgpu/src/backend.rs           | 64 +++++++++++++++++++++++++++++++++++++++--
 wgpu/src/custom.rs            | 66 +++++++++++++++++++++++++++++++++++++++++++
 wgpu/src/layer.rs             | 16 +++++++++++
 wgpu/src/lib.rs               |  1 +
 wgpu/src/primitive.rs         | 36 +++++++++++++++++++++++
 wgpu/src/window/compositor.rs |  2 ++
 6 files changed, 182 insertions(+), 3 deletions(-)
 create mode 100644 wgpu/src/custom.rs

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 2bd29f42..907611d9 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -3,9 +3,7 @@ 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::{custom, quad, text, triangle};
 use crate::{Layer, Settings};
 
 #[cfg(feature = "tracing")]
@@ -25,6 +23,7 @@ pub struct Backend {
     quad_pipeline: quad::Pipeline,
     text_pipeline: text::Pipeline,
     triangle_pipeline: triangle::Pipeline,
+    pipeline_storage: custom::Storage,
 
     #[cfg(any(feature = "image", feature = "svg"))]
     image_pipeline: image::Pipeline,
@@ -50,6 +49,7 @@ impl Backend {
             quad_pipeline,
             text_pipeline,
             triangle_pipeline,
+            pipeline_storage: custom::Storage::default(),
 
             #[cfg(any(feature = "image", feature = "svg"))]
             image_pipeline,
@@ -66,6 +66,7 @@ impl Backend {
         queue: &wgpu::Queue,
         encoder: &mut wgpu::CommandEncoder,
         clear_color: Option<Color>,
+        format: wgpu::TextureFormat,
         frame: &wgpu::TextureView,
         primitives: &[Primitive],
         viewport: &Viewport,
@@ -88,6 +89,7 @@ impl Backend {
         self.prepare(
             device,
             queue,
+            format,
             encoder,
             scale_factor,
             target_size,
@@ -117,6 +119,7 @@ impl Backend {
         &mut self,
         device: &wgpu::Device,
         queue: &wgpu::Queue,
+        format: wgpu::TextureFormat,
         _encoder: &mut wgpu::CommandEncoder,
         scale_factor: f32,
         target_size: Size<u32>,
@@ -179,6 +182,20 @@ impl Backend {
                     target_size,
                 );
             }
+
+            if !layer.shaders.is_empty() {
+                for shader in &layer.shaders {
+                    shader.primitive.prepare(
+                        format,
+                        device,
+                        queue,
+                        target_size,
+                        scale_factor,
+                        transformation,
+                        &mut self.pipeline_storage,
+                    );
+                }
+            }
         }
     }
 
@@ -302,6 +319,47 @@ impl Backend {
 
                 text_layer += 1;
             }
+
+            // kill render pass to let custom shaders get mut access to encoder
+            let _ = ManuallyDrop::into_inner(render_pass);
+
+            if !layer.shaders.is_empty() {
+                for shader in &layer.shaders {
+                    //This extra check is needed since each custom pipeline must set it's own
+                    //scissor rect, which will panic if bounds.w/h < 1
+                    let bounds = shader.bounds * scale_factor;
+
+                    if bounds.width < 1.0 || bounds.height < 1.0 {
+                        continue;
+                    }
+
+                    shader.primitive.render(
+                        &self.pipeline_storage,
+                        bounds.into(),
+                        target,
+                        target_size,
+                        encoder,
+                    );
+                }
+            }
+
+            // recreate and continue processing layers
+            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,
+                },
+            ));
         }
 
         let _ = ManuallyDrop::into_inner(render_pass);
diff --git a/wgpu/src/custom.rs b/wgpu/src/custom.rs
new file mode 100644
index 00000000..65dd0496
--- /dev/null
+++ b/wgpu/src/custom.rs
@@ -0,0 +1,66 @@
+use crate::core::{Rectangle, Size};
+use crate::graphics::Transformation;
+use std::any::{Any, TypeId};
+use std::collections::HashMap;
+use std::fmt::Debug;
+
+/// Stores custom, user-provided pipelines.
+#[derive(Default, Debug)]
+pub struct Storage {
+    pipelines: HashMap<TypeId, Box<dyn Any>>,
+}
+
+impl Storage {
+    /// Returns `true` if `Storage` contains a pipeline with type `T`.
+    pub fn has<T: 'static>(&self) -> bool {
+        self.pipelines.get(&TypeId::of::<T>()).is_some()
+    }
+
+    /// Inserts the pipeline `T` in to [`Storage`].
+    pub fn store<T: 'static>(&mut self, pipeline: T) {
+        let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
+    }
+
+    /// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
+    pub fn get<T: 'static>(&self) -> Option<&T> {
+        self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
+            pipeline
+                .downcast_ref::<T>()
+                .expect("Pipeline with this type does not exist in Storage.")
+        })
+    }
+
+    /// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
+    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
+        self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
+            pipeline
+                .downcast_mut::<T>()
+                .expect("Pipeline with this type does not exist in Storage.")
+        })
+    }
+}
+
+/// A set of methods which allows a [`Primitive`] to be rendered.
+pub trait Primitive: Debug + Send + Sync + 'static {
+    /// Processes the [`Primitive`], allowing for GPU buffer allocation.
+    fn prepare(
+        &self,
+        format: wgpu::TextureFormat,
+        device: &wgpu::Device,
+        queue: &wgpu::Queue,
+        target_size: Size<u32>,
+        scale_factor: f32,
+        transform: Transformation,
+        storage: &mut Storage,
+    );
+
+    /// Renders the [`Primitive`].
+    fn render(
+        &self,
+        storage: &Storage,
+        bounds: Rectangle<u32>,
+        target: &wgpu::TextureView,
+        target_size: Size<u32>,
+        encoder: &mut wgpu::CommandEncoder,
+    );
+}
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index 286801e6..d451cbfd 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -34,6 +34,9 @@ pub struct Layer<'a> {
 
     /// The images of the [`Layer`].
     pub images: Vec<Image>,
+
+    /// The custom shader primitives of this [`Layer`].
+    pub shaders: Vec<primitive::Shader>,
 }
 
 impl<'a> Layer<'a> {
@@ -45,6 +48,7 @@ impl<'a> Layer<'a> {
             meshes: Vec::new(),
             text: Vec::new(),
             images: Vec::new(),
+            shaders: Vec::new(),
         }
     }
 
@@ -308,6 +312,18 @@ impl<'a> Layer<'a> {
                         }
                     }
                 },
+                primitive::Custom::Shader(shader) => {
+                    let layer = &mut layers[current_layer];
+
+                    let bounds = Rectangle::new(
+                        Point::new(translation.x, translation.y),
+                        shader.bounds.size(),
+                    );
+
+                    if layer.bounds.intersection(&bounds).is_some() {
+                        layer.shaders.push(shader.clone());
+                    }
+                }
             },
         }
     }
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 424dfeb3..13d8e886 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -29,6 +29,7 @@
     rustdoc::broken_intra_doc_links
 )]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
+pub mod custom;
 pub mod layer;
 pub mod primitive;
 pub mod settings;
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
index 8dbf3008..4347dcda 100644
--- a/wgpu/src/primitive.rs
+++ b/wgpu/src/primitive.rs
@@ -1,6 +1,10 @@
 //! Draw using different graphical primitives.
 use crate::core::Rectangle;
+use crate::custom;
 use crate::graphics::{Damage, Mesh};
+use std::any::Any;
+use std::fmt::Debug;
+use std::sync::Arc;
 
 /// The graphical primitives supported by `iced_wgpu`.
 pub type Primitive = crate::graphics::Primitive<Custom>;
@@ -10,12 +14,44 @@ pub type Primitive = crate::graphics::Primitive<Custom>;
 pub enum Custom {
     /// A mesh primitive.
     Mesh(Mesh),
+    /// A custom shader primitive
+    Shader(Shader),
+}
+
+impl Custom {
+    /// Create a custom [`Shader`] primitive.
+    pub fn shader<P: custom::Primitive>(
+        bounds: Rectangle,
+        primitive: P,
+    ) -> Self {
+        Self::Shader(Shader {
+            bounds,
+            primitive: Arc::new(primitive),
+        })
+    }
 }
 
 impl Damage for Custom {
     fn bounds(&self) -> Rectangle {
         match self {
             Self::Mesh(mesh) => mesh.bounds(),
+            Self::Shader(shader) => shader.bounds,
         }
     }
 }
+
+#[derive(Clone, Debug)]
+/// A custom primitive which can be used to render primitives associated with a custom pipeline.
+pub struct Shader {
+    /// The bounds of the [`Shader`].
+    pub bounds: Rectangle,
+
+    /// The [`custom::Primitive`] to render.
+    pub primitive: Arc<dyn custom::Primitive>,
+}
+
+impl PartialEq for Shader {
+    fn eq(&self, other: &Self) -> bool {
+        self.primitive.type_id() == other.primitive.type_id()
+    }
+}
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 1ddbe5fe..90d64e17 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -178,6 +178,7 @@ pub fn present<Theme, T: AsRef<str>>(
                 &compositor.queue,
                 &mut encoder,
                 Some(background_color),
+                frame.texture.format(),
                 view,
                 primitives,
                 viewport,
@@ -357,6 +358,7 @@ pub fn screenshot<Theme, T: AsRef<str>>(
         &compositor.queue,
         &mut encoder,
         Some(background_color),
+        texture.format(),
         &view,
         primitives,
         viewport,
-- 
cgit 


From 36e85215932079fa324cfefb620602ad79f7df3d Mon Sep 17 00:00:00 2001
From: Bingus <shankern@protonmail.com>
Date: Mon, 18 Sep 2023 09:04:28 -0700
Subject: Removed `Into` for Rectangle<f32> from u32

---
 wgpu/src/backend.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 907611d9..ace2ef95 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -335,7 +335,7 @@ impl Backend {
 
                     shader.primitive.render(
                         &self.pipeline_storage,
-                        bounds.into(),
+                        bounds.snap(),
                         target,
                         target_size,
                         encoder,
-- 
cgit 


From de9420e7df7d909bca611c360182dec54c5b1aae Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 11:33:04 +0100
Subject: Fix latest `wgpu` changes

---
 wgpu/src/backend.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index ace2ef95..27ef0b3c 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -353,11 +353,13 @@ impl Backend {
                             resolve_target: None,
                             ops: wgpu::Operations {
                                 load: wgpu::LoadOp::Load,
-                                store: true,
+                                store: wgpu::StoreOp::Store,
                             },
                         },
                     )],
                     depth_stencil_attachment: None,
+                    timestamp_writes: None,
+                    occlusion_query_set: None,
                 },
             ));
         }
-- 
cgit 


From 46a48af97fa472e1158e07d4deb988c5601197e0 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 11:34:15 +0100
Subject: Write missing documentation for `custom` module in `iced_wgpu`

---
 wgpu/src/custom.rs | 1 +
 1 file changed, 1 insertion(+)

(limited to 'wgpu/src')

diff --git a/wgpu/src/custom.rs b/wgpu/src/custom.rs
index 65dd0496..65a6f133 100644
--- a/wgpu/src/custom.rs
+++ b/wgpu/src/custom.rs
@@ -1,3 +1,4 @@
+//! Draw custom primitives.
 use crate::core::{Rectangle, Size};
 use crate::graphics::Transformation;
 use std::any::{Any, TypeId};
-- 
cgit 


From 9489e29e6619b14ed9f41a8887c4b34158266f71 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 12:49:49 +0100
Subject: Re-organize `custom` module as `pipeline` module

... and move `Shader` widget to `iced_widget` crate
---
 wgpu/src/backend.rs            |  29 +++++-----
 wgpu/src/custom.rs             |  63 +---------------------
 wgpu/src/layer.rs              |  19 ++++---
 wgpu/src/lib.rs                |   1 -
 wgpu/src/primitive.rs          |  43 +++------------
 wgpu/src/primitive/pipeline.rs | 117 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 154 insertions(+), 118 deletions(-)
 create mode 100644 wgpu/src/primitive/pipeline.rs

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 27ef0b3c..f89bcee1 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -2,8 +2,11 @@ use crate::core::{Color, Size};
 use crate::graphics::backend;
 use crate::graphics::color;
 use crate::graphics::{Transformation, Viewport};
+use crate::primitive::pipeline;
 use crate::primitive::{self, Primitive};
-use crate::{custom, quad, text, triangle};
+use crate::quad;
+use crate::text;
+use crate::triangle;
 use crate::{Layer, Settings};
 
 #[cfg(feature = "tracing")]
@@ -23,7 +26,7 @@ pub struct Backend {
     quad_pipeline: quad::Pipeline,
     text_pipeline: text::Pipeline,
     triangle_pipeline: triangle::Pipeline,
-    pipeline_storage: custom::Storage,
+    pipeline_storage: pipeline::Storage,
 
     #[cfg(any(feature = "image", feature = "svg"))]
     image_pipeline: image::Pipeline,
@@ -49,7 +52,7 @@ impl Backend {
             quad_pipeline,
             text_pipeline,
             triangle_pipeline,
-            pipeline_storage: custom::Storage::default(),
+            pipeline_storage: pipeline::Storage::default(),
 
             #[cfg(any(feature = "image", feature = "svg"))]
             image_pipeline,
@@ -183,9 +186,9 @@ impl Backend {
                 );
             }
 
-            if !layer.shaders.is_empty() {
-                for shader in &layer.shaders {
-                    shader.primitive.prepare(
+            if !layer.pipelines.is_empty() {
+                for pipeline in &layer.pipelines {
+                    pipeline.primitive.prepare(
                         format,
                         device,
                         queue,
@@ -323,19 +326,17 @@ impl Backend {
             // kill render pass to let custom shaders get mut access to encoder
             let _ = ManuallyDrop::into_inner(render_pass);
 
-            if !layer.shaders.is_empty() {
-                for shader in &layer.shaders {
-                    //This extra check is needed since each custom pipeline must set it's own
-                    //scissor rect, which will panic if bounds.w/h < 1
-                    let bounds = shader.bounds * scale_factor;
+            if !layer.pipelines.is_empty() {
+                for pipeline in &layer.pipelines {
+                    let bounds = (pipeline.bounds * scale_factor).snap();
 
-                    if bounds.width < 1.0 || bounds.height < 1.0 {
+                    if bounds.width < 1 || bounds.height < 1 {
                         continue;
                     }
 
-                    shader.primitive.render(
+                    pipeline.primitive.render(
                         &self.pipeline_storage,
-                        bounds.snap(),
+                        bounds,
                         target,
                         target_size,
                         encoder,
diff --git a/wgpu/src/custom.rs b/wgpu/src/custom.rs
index 65a6f133..98e2b396 100644
--- a/wgpu/src/custom.rs
+++ b/wgpu/src/custom.rs
@@ -1,67 +1,8 @@
 //! Draw custom primitives.
 use crate::core::{Rectangle, Size};
 use crate::graphics::Transformation;
+use crate::primitive;
+
 use std::any::{Any, TypeId};
 use std::collections::HashMap;
 use std::fmt::Debug;
-
-/// Stores custom, user-provided pipelines.
-#[derive(Default, Debug)]
-pub struct Storage {
-    pipelines: HashMap<TypeId, Box<dyn Any>>,
-}
-
-impl Storage {
-    /// Returns `true` if `Storage` contains a pipeline with type `T`.
-    pub fn has<T: 'static>(&self) -> bool {
-        self.pipelines.get(&TypeId::of::<T>()).is_some()
-    }
-
-    /// Inserts the pipeline `T` in to [`Storage`].
-    pub fn store<T: 'static>(&mut self, pipeline: T) {
-        let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
-    }
-
-    /// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
-    pub fn get<T: 'static>(&self) -> Option<&T> {
-        self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
-            pipeline
-                .downcast_ref::<T>()
-                .expect("Pipeline with this type does not exist in Storage.")
-        })
-    }
-
-    /// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
-    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
-        self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
-            pipeline
-                .downcast_mut::<T>()
-                .expect("Pipeline with this type does not exist in Storage.")
-        })
-    }
-}
-
-/// A set of methods which allows a [`Primitive`] to be rendered.
-pub trait Primitive: Debug + Send + Sync + 'static {
-    /// Processes the [`Primitive`], allowing for GPU buffer allocation.
-    fn prepare(
-        &self,
-        format: wgpu::TextureFormat,
-        device: &wgpu::Device,
-        queue: &wgpu::Queue,
-        target_size: Size<u32>,
-        scale_factor: f32,
-        transform: Transformation,
-        storage: &mut Storage,
-    );
-
-    /// Renders the [`Primitive`].
-    fn render(
-        &self,
-        storage: &Storage,
-        bounds: Rectangle<u32>,
-        target: &wgpu::TextureView,
-        target_size: Size<u32>,
-        encoder: &mut wgpu::CommandEncoder,
-    );
-}
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index d451cbfd..33aaf670 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -35,8 +35,8 @@ pub struct Layer<'a> {
     /// The images of the [`Layer`].
     pub images: Vec<Image>,
 
-    /// The custom shader primitives of this [`Layer`].
-    pub shaders: Vec<primitive::Shader>,
+    /// The custom pipelines of this [`Layer`].
+    pub pipelines: Vec<primitive::Pipeline>,
 }
 
 impl<'a> Layer<'a> {
@@ -48,7 +48,7 @@ impl<'a> Layer<'a> {
             meshes: Vec::new(),
             text: Vec::new(),
             images: Vec::new(),
-            shaders: Vec::new(),
+            pipelines: Vec::new(),
         }
     }
 
@@ -312,16 +312,21 @@ impl<'a> Layer<'a> {
                         }
                     }
                 },
-                primitive::Custom::Shader(shader) => {
+                primitive::Custom::Pipeline(pipeline) => {
                     let layer = &mut layers[current_layer];
 
                     let bounds = Rectangle::new(
                         Point::new(translation.x, translation.y),
-                        shader.bounds.size(),
+                        pipeline.bounds.size(),
                     );
 
-                    if layer.bounds.intersection(&bounds).is_some() {
-                        layer.shaders.push(shader.clone());
+                    if let Some(clip_bounds) =
+                        layer.bounds.intersection(&bounds)
+                    {
+                        layer.pipelines.push(primitive::Pipeline {
+                            bounds: clip_bounds,
+                            primitive: pipeline.primitive.clone(),
+                        });
                     }
                 }
             },
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 13d8e886..424dfeb3 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -29,7 +29,6 @@
     rustdoc::broken_intra_doc_links
 )]
 #![cfg_attr(docsrs, feature(doc_auto_cfg))]
-pub mod custom;
 pub mod layer;
 pub mod primitive;
 pub mod settings;
diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs
index 4347dcda..fff927ea 100644
--- a/wgpu/src/primitive.rs
+++ b/wgpu/src/primitive.rs
@@ -1,10 +1,12 @@
 //! Draw using different graphical primitives.
+pub mod pipeline;
+
+pub use pipeline::Pipeline;
+
 use crate::core::Rectangle;
-use crate::custom;
 use crate::graphics::{Damage, Mesh};
-use std::any::Any;
+
 use std::fmt::Debug;
-use std::sync::Arc;
 
 /// The graphical primitives supported by `iced_wgpu`.
 pub type Primitive = crate::graphics::Primitive<Custom>;
@@ -14,44 +16,15 @@ pub type Primitive = crate::graphics::Primitive<Custom>;
 pub enum Custom {
     /// A mesh primitive.
     Mesh(Mesh),
-    /// A custom shader primitive
-    Shader(Shader),
-}
-
-impl Custom {
-    /// Create a custom [`Shader`] primitive.
-    pub fn shader<P: custom::Primitive>(
-        bounds: Rectangle,
-        primitive: P,
-    ) -> Self {
-        Self::Shader(Shader {
-            bounds,
-            primitive: Arc::new(primitive),
-        })
-    }
+    /// A custom pipeline primitive.
+    Pipeline(Pipeline),
 }
 
 impl Damage for Custom {
     fn bounds(&self) -> Rectangle {
         match self {
             Self::Mesh(mesh) => mesh.bounds(),
-            Self::Shader(shader) => shader.bounds,
+            Self::Pipeline(pipeline) => pipeline.bounds,
         }
     }
 }
-
-#[derive(Clone, Debug)]
-/// A custom primitive which can be used to render primitives associated with a custom pipeline.
-pub struct Shader {
-    /// The bounds of the [`Shader`].
-    pub bounds: Rectangle,
-
-    /// The [`custom::Primitive`] to render.
-    pub primitive: Arc<dyn custom::Primitive>,
-}
-
-impl PartialEq for Shader {
-    fn eq(&self, other: &Self) -> bool {
-        self.primitive.type_id() == other.primitive.type_id()
-    }
-}
diff --git a/wgpu/src/primitive/pipeline.rs b/wgpu/src/primitive/pipeline.rs
new file mode 100644
index 00000000..b59aeff1
--- /dev/null
+++ b/wgpu/src/primitive/pipeline.rs
@@ -0,0 +1,117 @@
+//! Draw primitives using custom pipelines.
+use crate::core::{Rectangle, Size};
+use crate::graphics::Transformation;
+
+use std::any::{Any, TypeId};
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::sync::Arc;
+
+#[derive(Clone, Debug)]
+/// A custom primitive which can be used to render primitives associated with a custom pipeline.
+pub struct Pipeline {
+    /// The bounds of the [`Shader`].
+    pub bounds: Rectangle,
+
+    /// The [`custom::Primitive`] to render.
+    pub primitive: Arc<dyn Primitive>,
+}
+
+impl Pipeline {
+    /// Creates a new [`Pipeline`] with the given [`Primitive`].
+    pub fn new(bounds: Rectangle, primitive: impl Primitive) -> Self {
+        Pipeline {
+            bounds,
+            primitive: Arc::new(primitive),
+        }
+    }
+}
+
+impl PartialEq for Pipeline {
+    fn eq(&self, other: &Self) -> bool {
+        self.primitive.type_id() == other.primitive.type_id()
+    }
+}
+
+/// A set of methods which allows a [`Primitive`] to be rendered.
+pub trait Primitive: Debug + Send + Sync + 'static {
+    /// Processes the [`Primitive`], allowing for GPU buffer allocation.
+    fn prepare(
+        &self,
+        format: wgpu::TextureFormat,
+        device: &wgpu::Device,
+        queue: &wgpu::Queue,
+        target_size: Size<u32>,
+        scale_factor: f32,
+        transform: Transformation,
+        storage: &mut Storage,
+    );
+
+    /// Renders the [`Primitive`].
+    fn render(
+        &self,
+        storage: &Storage,
+        bounds: Rectangle<u32>,
+        target: &wgpu::TextureView,
+        target_size: Size<u32>,
+        encoder: &mut wgpu::CommandEncoder,
+    );
+}
+
+/// A renderer than can draw custom pipeline primitives.
+pub trait Renderer: crate::core::Renderer {
+    /// Draws a custom pipeline primitive.
+    fn draw_pipeline_primitive(
+        &mut self,
+        bounds: Rectangle,
+        primitive: impl Primitive,
+    );
+}
+
+impl<Theme> Renderer for crate::Renderer<Theme> {
+    fn draw_pipeline_primitive(
+        &mut self,
+        bounds: Rectangle,
+        primitive: impl Primitive,
+    ) {
+        self.draw_primitive(super::Primitive::Custom(super::Custom::Pipeline(
+            Pipeline::new(bounds, primitive),
+        )));
+    }
+}
+
+/// Stores custom, user-provided pipelines.
+#[derive(Default, Debug)]
+pub struct Storage {
+    pipelines: HashMap<TypeId, Box<dyn Any>>,
+}
+
+impl Storage {
+    /// Returns `true` if `Storage` contains a pipeline with type `T`.
+    pub fn has<T: 'static>(&self) -> bool {
+        self.pipelines.get(&TypeId::of::<T>()).is_some()
+    }
+
+    /// Inserts the pipeline `T` in to [`Storage`].
+    pub fn store<T: 'static>(&mut self, pipeline: T) {
+        let _ = self.pipelines.insert(TypeId::of::<T>(), Box::new(pipeline));
+    }
+
+    /// Returns a reference to pipeline with type `T` if it exists in [`Storage`].
+    pub fn get<T: 'static>(&self) -> Option<&T> {
+        self.pipelines.get(&TypeId::of::<T>()).map(|pipeline| {
+            pipeline
+                .downcast_ref::<T>()
+                .expect("Pipeline with this type does not exist in Storage.")
+        })
+    }
+
+    /// Returns a mutable reference to pipeline `T` if it exists in [`Storage`].
+    pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
+        self.pipelines.get_mut(&TypeId::of::<T>()).map(|pipeline| {
+            pipeline
+                .downcast_mut::<T>()
+                .expect("Pipeline with this type does not exist in Storage.")
+        })
+    }
+}
-- 
cgit 


From 280d3736d59b62c4087fe980c187953cc2be83d2 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 13:23:28 +0100
Subject: Fix broken intra-doc links

---
 wgpu/src/primitive/pipeline.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/primitive/pipeline.rs b/wgpu/src/primitive/pipeline.rs
index b59aeff1..5dbd6697 100644
--- a/wgpu/src/primitive/pipeline.rs
+++ b/wgpu/src/primitive/pipeline.rs
@@ -10,10 +10,10 @@ use std::sync::Arc;
 #[derive(Clone, Debug)]
 /// A custom primitive which can be used to render primitives associated with a custom pipeline.
 pub struct Pipeline {
-    /// The bounds of the [`Shader`].
+    /// The bounds of the [`Pipeline`].
     pub bounds: Rectangle,
 
-    /// The [`custom::Primitive`] to render.
+    /// The [`Primitive`] to render.
     pub primitive: Arc<dyn Primitive>,
 }
 
-- 
cgit 


From fee3bf0df4e3099ea74def738be8743b2b72d78a Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 14:47:29 +0100
Subject: Kill current render pass only when custom pipelines are present in
 layer

---
 wgpu/src/backend.rs | 42 ++++++++++++++++++++----------------------
 1 file changed, 20 insertions(+), 22 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index f89bcee1..91ae777b 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -323,10 +323,9 @@ impl Backend {
                 text_layer += 1;
             }
 
-            // kill render pass to let custom shaders get mut access to encoder
-            let _ = ManuallyDrop::into_inner(render_pass);
-
             if !layer.pipelines.is_empty() {
+                let _ = ManuallyDrop::into_inner(render_pass);
+
                 for pipeline in &layer.pipelines {
                     let bounds = (pipeline.bounds * scale_factor).snap();
 
@@ -342,27 +341,26 @@ impl Backend {
                         encoder,
                     );
                 }
-            }
 
-            // recreate and continue processing layers
-            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: wgpu::StoreOp::Store,
+                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: wgpu::StoreOp::Store,
+                                },
                             },
-                        },
-                    )],
-                    depth_stencil_attachment: None,
-                    timestamp_writes: None,
-                    occlusion_query_set: None,
-                },
-            ));
+                        )],
+                        depth_stencil_attachment: None,
+                        timestamp_writes: None,
+                        occlusion_query_set: None,
+                    },
+                ));
+            }
         }
 
         let _ = ManuallyDrop::into_inner(render_pass);
-- 
cgit 


From b1b2467b45e16185cc17df00b4c75700435cd46e Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 14:50:57 +0100
Subject: Fix render pass label in `iced_wgpu`

---
 wgpu/src/backend.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 91ae777b..88caad06 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -222,7 +222,7 @@ impl Backend {
 
         let mut render_pass = ManuallyDrop::new(encoder.begin_render_pass(
             &wgpu::RenderPassDescriptor {
-                label: Some("iced_wgpu::quad render pass"),
+                label: Some("iced_wgpu render pass"),
                 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
                     view: target,
                     resolve_target: None,
@@ -285,7 +285,7 @@ impl Backend {
 
                 render_pass = ManuallyDrop::new(encoder.begin_render_pass(
                     &wgpu::RenderPassDescriptor {
-                        label: Some("iced_wgpu::quad render pass"),
+                        label: Some("iced_wgpu render pass"),
                         color_attachments: &[Some(
                             wgpu::RenderPassColorAttachment {
                                 view: target,
@@ -344,7 +344,7 @@ impl Backend {
 
                 render_pass = ManuallyDrop::new(encoder.begin_render_pass(
                     &wgpu::RenderPassDescriptor {
-                        label: Some("iced_wgpu::quad render pass"),
+                        label: Some("iced_wgpu render pass"),
                         color_attachments: &[Some(
                             wgpu::RenderPassColorAttachment {
                                 view: target,
-- 
cgit 


From 8f384c83be242f9318685530ee52dd6c27b2bb62 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 14 Nov 2023 15:54:10 +0100
Subject: Remove unsused `custom.rs` file in `iced_wgpu`

---
 wgpu/src/custom.rs | 8 --------
 1 file changed, 8 deletions(-)
 delete mode 100644 wgpu/src/custom.rs

(limited to 'wgpu/src')

diff --git a/wgpu/src/custom.rs b/wgpu/src/custom.rs
deleted file mode 100644
index 98e2b396..00000000
--- a/wgpu/src/custom.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//! Draw custom primitives.
-use crate::core::{Rectangle, Size};
-use crate::graphics::Transformation;
-use crate::primitive;
-
-use std::any::{Any, TypeId};
-use std::collections::HashMap;
-use std::fmt::Debug;
-- 
cgit 


From ab7dae554cac801aeed5d9aa4d3850d50be86263 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Tue, 28 Nov 2023 23:13:38 +0100
Subject: Provide actual bounds to `Shader` primitives

... and allow for proper translation and scissoring.
---
 wgpu/src/backend.rs            |  8 ++++----
 wgpu/src/layer.rs              | 15 +++++++--------
 wgpu/src/layer/pipeline.rs     | 17 +++++++++++++++++
 wgpu/src/primitive/pipeline.rs |  5 ++---
 4 files changed, 30 insertions(+), 15 deletions(-)
 create mode 100644 wgpu/src/layer/pipeline.rs

(limited to 'wgpu/src')

diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 88caad06..25134d68 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -192,9 +192,9 @@ impl Backend {
                         format,
                         device,
                         queue,
+                        pipeline.bounds,
                         target_size,
                         scale_factor,
-                        transformation,
                         &mut self.pipeline_storage,
                     );
                 }
@@ -327,17 +327,17 @@ impl Backend {
                 let _ = ManuallyDrop::into_inner(render_pass);
 
                 for pipeline in &layer.pipelines {
-                    let bounds = (pipeline.bounds * scale_factor).snap();
+                    let viewport = (pipeline.viewport * scale_factor).snap();
 
-                    if bounds.width < 1 || bounds.height < 1 {
+                    if viewport.width < 1 || viewport.height < 1 {
                         continue;
                     }
 
                     pipeline.primitive.render(
                         &self.pipeline_storage,
-                        bounds,
                         target,
                         target_size,
+                        viewport,
                         encoder,
                     );
                 }
diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs
index 33aaf670..98e49f1a 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -1,11 +1,13 @@
 //! Organize rendering primitives into a flattened list of layers.
 mod image;
+mod pipeline;
 mod text;
 
 pub mod mesh;
 
 pub use image::Image;
 pub use mesh::Mesh;
+pub use pipeline::Pipeline;
 pub use text::Text;
 
 use crate::core;
@@ -36,7 +38,7 @@ pub struct Layer<'a> {
     pub images: Vec<Image>,
 
     /// The custom pipelines of this [`Layer`].
-    pub pipelines: Vec<primitive::Pipeline>,
+    pub pipelines: Vec<Pipeline>,
 }
 
 impl<'a> Layer<'a> {
@@ -314,17 +316,14 @@ impl<'a> Layer<'a> {
                 },
                 primitive::Custom::Pipeline(pipeline) => {
                     let layer = &mut layers[current_layer];
-
-                    let bounds = Rectangle::new(
-                        Point::new(translation.x, translation.y),
-                        pipeline.bounds.size(),
-                    );
+                    let bounds = pipeline.bounds + translation;
 
                     if let Some(clip_bounds) =
                         layer.bounds.intersection(&bounds)
                     {
-                        layer.pipelines.push(primitive::Pipeline {
-                            bounds: clip_bounds,
+                        layer.pipelines.push(Pipeline {
+                            bounds,
+                            viewport: clip_bounds,
                             primitive: pipeline.primitive.clone(),
                         });
                     }
diff --git a/wgpu/src/layer/pipeline.rs b/wgpu/src/layer/pipeline.rs
new file mode 100644
index 00000000..6dfe6750
--- /dev/null
+++ b/wgpu/src/layer/pipeline.rs
@@ -0,0 +1,17 @@
+use crate::core::Rectangle;
+use crate::primitive::pipeline::Primitive;
+
+use std::sync::Arc;
+
+#[derive(Clone, Debug)]
+/// A custom primitive which can be used to render primitives associated with a custom pipeline.
+pub struct Pipeline {
+    /// The bounds of the [`Pipeline`].
+    pub bounds: Rectangle,
+
+    /// The viewport of the [`Pipeline`].
+    pub viewport: Rectangle,
+
+    /// The [`Primitive`] to render.
+    pub primitive: Arc<dyn Primitive>,
+}
diff --git a/wgpu/src/primitive/pipeline.rs b/wgpu/src/primitive/pipeline.rs
index 5dbd6697..302e38f6 100644
--- a/wgpu/src/primitive/pipeline.rs
+++ b/wgpu/src/primitive/pipeline.rs
@@ -1,6 +1,5 @@
 //! Draw primitives using custom pipelines.
 use crate::core::{Rectangle, Size};
-use crate::graphics::Transformation;
 
 use std::any::{Any, TypeId};
 use std::collections::HashMap;
@@ -41,9 +40,9 @@ pub trait Primitive: Debug + Send + Sync + 'static {
         format: wgpu::TextureFormat,
         device: &wgpu::Device,
         queue: &wgpu::Queue,
+        bounds: Rectangle,
         target_size: Size<u32>,
         scale_factor: f32,
-        transform: Transformation,
         storage: &mut Storage,
     );
 
@@ -51,9 +50,9 @@ pub trait Primitive: Debug + Send + Sync + 'static {
     fn render(
         &self,
         storage: &Storage,
-        bounds: Rectangle<u32>,
         target: &wgpu::TextureView,
         target_size: Size<u32>,
+        viewport: Rectangle<u32>,
         encoder: &mut wgpu::CommandEncoder,
     );
 }
-- 
cgit