diff options
| author | 2024-03-25 21:36:44 +0100 | |
|---|---|---|
| committer | 2024-03-25 21:36:44 +0100 | |
| commit | a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286 (patch) | |
| tree | e6c24928a42e23ff91eea0fc30b4fbbcb6da024b /wgpu | |
| parent | 3013463baa71504488a20436beb3db87ecb66df0 (diff) | |
| parent | 6a4f5ac2081699f7cf20c917b367366ab49eeef1 (diff) | |
| download | iced-a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286.tar.gz iced-a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286.tar.bz2 iced-a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286.zip  | |
Merge pull request #2351 from iced-rs/custom-renderer-injection
Type-Driven Renderer Fallback
Diffstat (limited to 'wgpu')
| -rw-r--r-- | wgpu/Cargo.toml | 1 | ||||
| -rw-r--r-- | wgpu/src/backend.rs | 13 | ||||
| -rw-r--r-- | wgpu/src/geometry.rs | 492 | ||||
| -rw-r--r-- | wgpu/src/primitive.rs | 8 | ||||
| -rw-r--r-- | wgpu/src/primitive/pipeline.rs | 4 | ||||
| -rw-r--r-- | wgpu/src/settings.rs | 37 | ||||
| -rw-r--r-- | wgpu/src/window/compositor.rs | 146 | 
7 files changed, 349 insertions, 352 deletions
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 4a0d89f0..f6162e0f 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -32,6 +32,7 @@ glyphon.workspace = true  guillotiere.workspace = true  log.workspace = true  once_cell.workspace = true +thiserror.workspace = true  wgpu.workspace = true  lyon.workspace = true diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 09ddbe4d..5019191c 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -7,6 +7,7 @@ use crate::primitive::{self, Primitive};  use crate::quad;  use crate::text;  use crate::triangle; +use crate::window;  use crate::{Layer, Settings};  #[cfg(feature = "tracing")] @@ -371,8 +372,9 @@ impl Backend {      }  } -impl crate::graphics::Backend for Backend { +impl backend::Backend for Backend {      type Primitive = primitive::Custom; +    type Compositor = window::Compositor;  }  impl backend::Text for Backend { @@ -397,3 +399,12 @@ impl backend::Svg for Backend {          self.image_pipeline.viewport_dimensions(handle)      }  } + +#[cfg(feature = "geometry")] +impl crate::graphics::geometry::Backend for Backend { +    type Frame = crate::geometry::Frame; + +    fn new_frame(&self, size: Size) -> Self::Frame { +        crate::geometry::Frame::new(size) +    } +} diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index f4e0fbda..ba56c59d 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -6,7 +6,7 @@ use crate::core::{  use crate::graphics::color;  use crate::graphics::geometry::fill::{self, Fill};  use crate::graphics::geometry::{ -    LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, +    self, LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,  };  use crate::graphics::gradient::{self, Gradient};  use crate::graphics::mesh::{self, Mesh}; @@ -14,6 +14,7 @@ use crate::primitive::{self, Primitive};  use lyon::geom::euclid;  use lyon::tessellation; +  use std::borrow::Cow;  /// A frame for drawing some geometry. @@ -27,191 +28,87 @@ pub struct Frame {      stroke_tessellator: tessellation::StrokeTessellator,  } -enum Buffer { -    Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>), -    Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>), -} - -struct BufferStack { -    stack: Vec<Buffer>, -} - -impl BufferStack { -    fn new() -> Self { -        Self { stack: Vec::new() } -    } - -    fn get_mut(&mut self, style: &Style) -> &mut Buffer { -        match style { -            Style::Solid(_) => match self.stack.last() { -                Some(Buffer::Solid(_)) => {} -                _ => { -                    self.stack.push(Buffer::Solid( -                        tessellation::VertexBuffers::new(), -                    )); -                } -            }, -            Style::Gradient(_) => match self.stack.last() { -                Some(Buffer::Gradient(_)) => {} -                _ => { -                    self.stack.push(Buffer::Gradient( -                        tessellation::VertexBuffers::new(), -                    )); -                } +impl Frame { +    /// Creates a new [`Frame`] with the given [`Size`]. +    pub fn new(size: Size) -> Frame { +        Frame { +            size, +            buffers: BufferStack::new(), +            primitives: Vec::new(), +            transforms: Transforms { +                previous: Vec::new(), +                current: Transform(lyon::math::Transform::identity()),              }, +            fill_tessellator: tessellation::FillTessellator::new(), +            stroke_tessellator: tessellation::StrokeTessellator::new(),          } - -        self.stack.last_mut().unwrap()      } -    fn get_fill<'a>( -        &'a mut self, -        style: &Style, -    ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> { -        match (style, self.get_mut(style)) { -            (Style::Solid(color), Buffer::Solid(buffer)) => { -                Box::new(tessellation::BuffersBuilder::new( -                    buffer, -                    TriangleVertex2DBuilder(color::pack(*color)), -                )) -            } -            (Style::Gradient(gradient), Buffer::Gradient(buffer)) => { -                Box::new(tessellation::BuffersBuilder::new( -                    buffer, -                    GradientVertex2DBuilder { -                        gradient: gradient.pack(), -                    }, -                )) +    fn into_primitives(mut self) -> Vec<Primitive> { +        for buffer in self.buffers.stack { +            match buffer { +                Buffer::Solid(buffer) => { +                    if !buffer.indices.is_empty() { +                        self.primitives.push(Primitive::Custom( +                            primitive::Custom::Mesh(Mesh::Solid { +                                buffers: mesh::Indexed { +                                    vertices: buffer.vertices, +                                    indices: buffer.indices, +                                }, +                                size: self.size, +                            }), +                        )); +                    } +                } +                Buffer::Gradient(buffer) => { +                    if !buffer.indices.is_empty() { +                        self.primitives.push(Primitive::Custom( +                            primitive::Custom::Mesh(Mesh::Gradient { +                                buffers: mesh::Indexed { +                                    vertices: buffer.vertices, +                                    indices: buffer.indices, +                                }, +                                size: self.size, +                            }), +                        )); +                    } +                }              } -            _ => unreachable!(),          } -    } -    fn get_stroke<'a>( -        &'a mut self, -        style: &Style, -    ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> { -        match (style, self.get_mut(style)) { -            (Style::Solid(color), Buffer::Solid(buffer)) => { -                Box::new(tessellation::BuffersBuilder::new( -                    buffer, -                    TriangleVertex2DBuilder(color::pack(*color)), -                )) -            } -            (Style::Gradient(gradient), Buffer::Gradient(buffer)) => { -                Box::new(tessellation::BuffersBuilder::new( -                    buffer, -                    GradientVertex2DBuilder { -                        gradient: gradient.pack(), -                    }, -                )) -            } -            _ => unreachable!(), -        } +        self.primitives      }  } -#[derive(Debug)] -struct Transforms { -    previous: Vec<Transform>, -    current: Transform, -} - -#[derive(Debug, Clone, Copy)] -struct Transform(lyon::math::Transform); - -impl Transform { -    fn is_identity(&self) -> bool { -        self.0 == lyon::math::Transform::identity() -    } - -    fn is_scale_translation(&self) -> bool { -        self.0.m12.abs() < 2.0 * f32::EPSILON -            && self.0.m21.abs() < 2.0 * f32::EPSILON -    } - -    fn scale(&self) -> (f32, f32) { -        (self.0.m11, self.0.m22) -    } - -    fn transform_point(&self, point: Point) -> Point { -        let transformed = self -            .0 -            .transform_point(euclid::Point2D::new(point.x, point.y)); - -        Point { -            x: transformed.x, -            y: transformed.y, -        } -    } - -    fn transform_style(&self, style: Style) -> Style { -        match style { -            Style::Solid(color) => Style::Solid(color), -            Style::Gradient(gradient) => { -                Style::Gradient(self.transform_gradient(gradient)) -            } -        } -    } - -    fn transform_gradient(&self, mut gradient: Gradient) -> Gradient { -        match &mut gradient { -            Gradient::Linear(linear) => { -                linear.start = self.transform_point(linear.start); -                linear.end = self.transform_point(linear.end); -            } -        } - -        gradient -    } -} +impl geometry::frame::Backend for Frame { +    type Geometry = Primitive; -impl Frame {      /// Creates a new empty [`Frame`] with the given dimensions.      ///      /// The default coordinate system of a [`Frame`] has its origin at the      /// top-left corner of its bounds. -    pub fn new(size: Size) -> Frame { -        Frame { -            size, -            buffers: BufferStack::new(), -            primitives: Vec::new(), -            transforms: Transforms { -                previous: Vec::new(), -                current: Transform(lyon::math::Transform::identity()), -            }, -            fill_tessellator: tessellation::FillTessellator::new(), -            stroke_tessellator: tessellation::StrokeTessellator::new(), -        } -    } -    /// Returns the width of the [`Frame`].      #[inline] -    pub fn width(&self) -> f32 { +    fn width(&self) -> f32 {          self.size.width      } -    /// Returns the height of the [`Frame`].      #[inline] -    pub fn height(&self) -> f32 { +    fn height(&self) -> f32 {          self.size.height      } -    /// Returns the dimensions of the [`Frame`].      #[inline] -    pub fn size(&self) -> Size { +    fn size(&self) -> Size {          self.size      } -    /// Returns the coordinate of the center of the [`Frame`].      #[inline] -    pub fn center(&self) -> Point { +    fn center(&self) -> Point {          Point::new(self.size.width / 2.0, self.size.height / 2.0)      } -    /// Draws the given [`Path`] on the [`Frame`] by filling it with the -    /// provided style. -    pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) { +    fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {          let Fill { style, rule } = fill.into();          let mut buffer = self @@ -239,9 +136,7 @@ impl Frame {          .expect("Tessellate path.");      } -    /// Draws an axis-aligned rectangle given its top-left corner coordinate and -    /// its `Size` on the [`Frame`] by filling it with the provided style. -    pub fn fill_rectangle( +    fn fill_rectangle(          &mut self,          top_left: Point,          size: Size, @@ -276,9 +171,7 @@ impl Frame {              .expect("Fill rectangle");      } -    /// Draws the stroke of the given [`Path`] on the [`Frame`] with the -    /// provided style. -    pub fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) { +    fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {          let stroke = stroke.into();          let mut buffer = self @@ -315,20 +208,7 @@ impl Frame {          .expect("Stroke path");      } -    /// Draws the characters of the given [`Text`] on the [`Frame`], filling -    /// them with the given color. -    /// -    /// __Warning:__ Text currently does not work well with rotations and scale -    /// transforms! The position will be correctly transformed, but the -    /// 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 -    /// overlays, which is the most common use case. -    /// -    /// Support for vectorial text is planned, and should address all these -    /// limitations. -    pub fn fill_text(&mut self, text: impl Into<Text>) { +    fn fill_text(&mut self, text: impl Into<Text>) {          let text = text.into();          let (scale_x, scale_y) = self.transforms.current.scale(); @@ -384,57 +264,55 @@ impl Frame {          }      } -    /// Stores the current transform of the [`Frame`] and executes the given -    /// drawing operations, restoring the transform afterwards. -    /// -    /// This method is useful to compose transforms and perform drawing -    /// operations in different coordinate systems.      #[inline] -    pub fn with_save<R>(&mut self, f: impl FnOnce(&mut Frame) -> R) -> R { -        self.push_transform(); - -        let result = f(self); - -        self.pop_transform(); - -        result +    fn translate(&mut self, translation: Vector) { +        self.transforms.current.0 = +            self.transforms +                .current +                .0 +                .pre_translate(lyon::math::Vector::new( +                    translation.x, +                    translation.y, +                ));      } -    /// Pushes the current transform in the transform stack. -    pub fn push_transform(&mut self) { -        self.transforms.previous.push(self.transforms.current); +    #[inline] +    fn rotate(&mut self, angle: impl Into<Radians>) { +        self.transforms.current.0 = self +            .transforms +            .current +            .0 +            .pre_rotate(lyon::math::Angle::radians(angle.into().0));      } -    /// Pops a transform from the transform stack and sets it as the current transform. -    pub fn pop_transform(&mut self) { -        self.transforms.current = self.transforms.previous.pop().unwrap(); +    #[inline] +    fn scale(&mut self, scale: impl Into<f32>) { +        let scale = scale.into(); + +        self.scale_nonuniform(Vector { x: scale, y: scale });      } -    /// Executes the given drawing operations within a [`Rectangle`] region, -    /// clipping any geometry that overflows its bounds. Any transformations -    /// performed are local to the provided closure. -    /// -    /// This method is useful to perform drawing operations that need to be -    /// clipped.      #[inline] -    pub fn with_clip<R>( -        &mut self, -        region: Rectangle, -        f: impl FnOnce(&mut Frame) -> R, -    ) -> R { -        let mut frame = Frame::new(region.size()); +    fn scale_nonuniform(&mut self, scale: impl Into<Vector>) { +        let scale = scale.into(); -        let result = f(&mut frame); +        self.transforms.current.0 = +            self.transforms.current.0.pre_scale(scale.x, scale.y); +    } -        let origin = Point::new(region.x, region.y); +    fn push_transform(&mut self) { +        self.transforms.previous.push(self.transforms.current); +    } -        self.clip(frame, origin); +    fn pop_transform(&mut self) { +        self.transforms.current = self.transforms.previous.pop().unwrap(); +    } -        result +    fn draft(&mut self, size: Size) -> Frame { +        Frame::new(size)      } -    /// Draws the clipped contents of the given [`Frame`] with origin at the given [`Point`]. -    pub fn clip(&mut self, frame: Frame, at: Point) { +    fn paste(&mut self, frame: Frame, at: Point) {          let size = frame.size();          let primitives = frame.into_primitives();          let transformation = Transformation::translate(at.x, at.y); @@ -462,89 +340,151 @@ impl Frame {          });      } -    /// Applies a translation to the current transform of the [`Frame`]. -    #[inline] -    pub fn translate(&mut self, translation: Vector) { -        self.transforms.current.0 = -            self.transforms -                .current -                .0 -                .pre_translate(lyon::math::Vector::new( -                    translation.x, -                    translation.y, -                )); +    fn into_geometry(self) -> Self::Geometry { +        Primitive::Group { +            primitives: self.into_primitives(), +        }      } +} -    /// Applies a rotation in radians to the current transform of the [`Frame`]. -    #[inline] -    pub fn rotate(&mut self, angle: impl Into<Radians>) { -        self.transforms.current.0 = self -            .transforms -            .current -            .0 -            .pre_rotate(lyon::math::Angle::radians(angle.into().0)); +enum Buffer { +    Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>), +    Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>), +} + +struct BufferStack { +    stack: Vec<Buffer>, +} + +impl BufferStack { +    fn new() -> Self { +        Self { stack: Vec::new() }      } -    /// Applies a uniform scaling to the current transform of the [`Frame`]. -    #[inline] -    pub fn scale(&mut self, scale: impl Into<f32>) { -        let scale = scale.into(); +    fn get_mut(&mut self, style: &Style) -> &mut Buffer { +        match style { +            Style::Solid(_) => match self.stack.last() { +                Some(Buffer::Solid(_)) => {} +                _ => { +                    self.stack.push(Buffer::Solid( +                        tessellation::VertexBuffers::new(), +                    )); +                } +            }, +            Style::Gradient(_) => match self.stack.last() { +                Some(Buffer::Gradient(_)) => {} +                _ => { +                    self.stack.push(Buffer::Gradient( +                        tessellation::VertexBuffers::new(), +                    )); +                } +            }, +        } -        self.scale_nonuniform(Vector { x: scale, y: scale }); +        self.stack.last_mut().unwrap()      } -    /// Applies a non-uniform scaling to the current transform of the [`Frame`]. -    #[inline] -    pub fn scale_nonuniform(&mut self, scale: impl Into<Vector>) { -        let scale = scale.into(); +    fn get_fill<'a>( +        &'a mut self, +        style: &Style, +    ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> { +        match (style, self.get_mut(style)) { +            (Style::Solid(color), Buffer::Solid(buffer)) => { +                Box::new(tessellation::BuffersBuilder::new( +                    buffer, +                    TriangleVertex2DBuilder(color::pack(*color)), +                )) +            } +            (Style::Gradient(gradient), Buffer::Gradient(buffer)) => { +                Box::new(tessellation::BuffersBuilder::new( +                    buffer, +                    GradientVertex2DBuilder { +                        gradient: gradient.pack(), +                    }, +                )) +            } +            _ => unreachable!(), +        } +    } -        self.transforms.current.0 = -            self.transforms.current.0.pre_scale(scale.x, scale.y); +    fn get_stroke<'a>( +        &'a mut self, +        style: &Style, +    ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> { +        match (style, self.get_mut(style)) { +            (Style::Solid(color), Buffer::Solid(buffer)) => { +                Box::new(tessellation::BuffersBuilder::new( +                    buffer, +                    TriangleVertex2DBuilder(color::pack(*color)), +                )) +            } +            (Style::Gradient(gradient), Buffer::Gradient(buffer)) => { +                Box::new(tessellation::BuffersBuilder::new( +                    buffer, +                    GradientVertex2DBuilder { +                        gradient: gradient.pack(), +                    }, +                )) +            } +            _ => unreachable!(), +        }      } +} -    /// Produces the [`Primitive`] representing everything drawn on the [`Frame`]. -    pub fn into_primitive(self) -> Primitive { -        Primitive::Group { -            primitives: self.into_primitives(), +#[derive(Debug)] +struct Transforms { +    previous: Vec<Transform>, +    current: Transform, +} + +#[derive(Debug, Clone, Copy)] +struct Transform(lyon::math::Transform); + +impl Transform { +    fn is_identity(&self) -> bool { +        self.0 == lyon::math::Transform::identity() +    } + +    fn is_scale_translation(&self) -> bool { +        self.0.m12.abs() < 2.0 * f32::EPSILON +            && self.0.m21.abs() < 2.0 * f32::EPSILON +    } + +    fn scale(&self) -> (f32, f32) { +        (self.0.m11, self.0.m22) +    } + +    fn transform_point(&self, point: Point) -> Point { +        let transformed = self +            .0 +            .transform_point(euclid::Point2D::new(point.x, point.y)); + +        Point { +            x: transformed.x, +            y: transformed.y,          }      } -    fn into_primitives(mut self) -> Vec<Primitive> { -        for buffer in self.buffers.stack { -            match buffer { -                Buffer::Solid(buffer) => { -                    if !buffer.indices.is_empty() { -                        self.primitives.push(Primitive::Custom( -                            primitive::Custom::Mesh(Mesh::Solid { -                                buffers: mesh::Indexed { -                                    vertices: buffer.vertices, -                                    indices: buffer.indices, -                                }, -                                size: self.size, -                            }), -                        )); -                    } -                } -                Buffer::Gradient(buffer) => { -                    if !buffer.indices.is_empty() { -                        self.primitives.push(Primitive::Custom( -                            primitive::Custom::Mesh(Mesh::Gradient { -                                buffers: mesh::Indexed { -                                    vertices: buffer.vertices, -                                    indices: buffer.indices, -                                }, -                                size: self.size, -                            }), -                        )); -                    } -                } +    fn transform_style(&self, style: Style) -> Style { +        match style { +            Style::Solid(color) => Style::Solid(color), +            Style::Gradient(gradient) => { +                Style::Gradient(self.transform_gradient(gradient))              }          } +    } -        self.primitives +    fn transform_gradient(&self, mut gradient: Gradient) -> Gradient { +        match &mut gradient { +            Gradient::Linear(linear) => { +                linear.start = self.transform_point(linear.start); +                linear.end = self.transform_point(linear.end); +            } +        } + +        gradient      }  } -  struct GradientVertex2DBuilder {      gradient: gradient::Packed,  } diff --git a/wgpu/src/primitive.rs b/wgpu/src/primitive.rs index fff927ea..ee9af93c 100644 --- a/wgpu/src/primitive.rs +++ b/wgpu/src/primitive.rs @@ -28,3 +28,11 @@ impl Damage for Custom {          }      }  } + +impl TryFrom<Mesh> for Custom { +    type Error = &'static str; + +    fn try_from(mesh: Mesh) -> Result<Self, Self::Error> { +        Ok(Custom::Mesh(mesh)) +    } +} diff --git a/wgpu/src/primitive/pipeline.rs b/wgpu/src/primitive/pipeline.rs index c6b7c5e2..814440ba 100644 --- a/wgpu/src/primitive/pipeline.rs +++ b/wgpu/src/primitive/pipeline.rs @@ -1,5 +1,5 @@  //! Draw primitives using custom pipelines. -use crate::core::{Rectangle, Size}; +use crate::core::{self, Rectangle, Size};  use std::any::{Any, TypeId};  use std::collections::HashMap; @@ -58,7 +58,7 @@ pub trait Primitive: Debug + Send + Sync + 'static {  }  /// A renderer than can draw custom pipeline primitives. -pub trait Renderer: crate::core::Renderer { +pub trait Renderer: core::Renderer {      /// Draws a custom pipeline primitive.      fn draw_pipeline_primitive(          &mut self, diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index c9338fec..828d9e09 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -1,6 +1,6 @@  //! Configure a renderer.  use crate::core::{Font, Pixels}; -use crate::graphics::Antialiasing; +use crate::graphics::{self, Antialiasing};  /// The settings of a [`Backend`].  /// @@ -29,30 +29,6 @@ pub struct Settings {      pub antialiasing: Option<Antialiasing>,  } -impl Settings { -    /// Creates new [`Settings`] using environment configuration. -    /// -    /// Specifically: -    /// -    /// - The `internal_backend` can be configured using the `WGPU_BACKEND` -    /// environment variable. If the variable is not set, the primary backend -    /// will be used. The following values are allowed: -    ///     - `vulkan` -    ///     - `metal` -    ///     - `dx12` -    ///     - `dx11` -    ///     - `gl` -    ///     - `webgpu` -    ///     - `primary` -    pub fn from_env() -> Self { -        Settings { -            internal_backend: wgpu::util::backend_bits_from_env() -                .unwrap_or(wgpu::Backends::all()), -            ..Self::default() -        } -    } -} -  impl Default for Settings {      fn default() -> Settings {          Settings { @@ -64,3 +40,14 @@ impl Default for Settings {          }      }  } + +impl From<graphics::Settings> for Settings { +    fn from(settings: graphics::Settings) -> Self { +        Self { +            default_font: settings.default_font, +            default_text_size: settings.default_text_size, +            antialiasing: settings.antialiasing, +            ..Settings::default() +        } +    } +} diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index fa6b9373..9a3e3b34 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -1,13 +1,11 @@  //! Connect a window with a renderer.  use crate::core::{Color, Size}; -use crate::graphics;  use crate::graphics::color;  use crate::graphics::compositor; -use crate::graphics::{Error, Viewport}; +use crate::graphics::error; +use crate::graphics::{self, Viewport};  use crate::{Backend, Primitive, Renderer, Settings}; -use std::future::Future; -  /// A window graphics backend for iced powered by `wgpu`.  #[allow(missing_debug_implementations)]  pub struct Compositor { @@ -20,6 +18,32 @@ pub struct Compositor {      alpha_mode: wgpu::CompositeAlphaMode,  } +/// A compositor error. +#[derive(Debug, Clone, thiserror::Error)] +pub enum Error { +    /// The surface creation failed. +    #[error("the surface creation failed: {0}")] +    SurfaceCreationFailed(#[from] wgpu::CreateSurfaceError), +    /// The surface is not compatible. +    #[error("the surface is not compatible")] +    IncompatibleSurface, +    /// No adapter was found for the options requested. +    #[error("no adapter was found for the options requested: {0:?}")] +    NoAdapterFound(String), +    /// No device request succeeded. +    #[error("no device request succeeded: {0:?}")] +    RequestDeviceFailed(Vec<(wgpu::Limits, wgpu::RequestDeviceError)>), +} + +impl From<Error> for graphics::Error { +    fn from(error: Error) -> Self { +        Self::GraphicsAdapterNotFound { +            backend: "wgpu", +            reason: error::Reason::RequestFailed(error.to_string()), +        } +    } +} +  impl Compositor {      /// Requests a new [`Compositor`] with the given [`Settings`].      /// @@ -27,7 +51,7 @@ impl Compositor {      pub async fn request<W: compositor::Window>(          settings: Settings,          compatible_window: Option<W>, -    ) -> Option<Self> { +    ) -> Result<Self, Error> {          let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {              backends: settings.internal_backend,              ..Default::default() @@ -49,23 +73,27 @@ impl Compositor {          let compatible_surface = compatible_window              .and_then(|window| instance.create_surface(window).ok()); +        let adapter_options = wgpu::RequestAdapterOptions { +            power_preference: wgpu::util::power_preference_from_env() +                .unwrap_or(if settings.antialiasing.is_none() { +                    wgpu::PowerPreference::LowPower +                } else { +                    wgpu::PowerPreference::HighPerformance +                }), +            compatible_surface: compatible_surface.as_ref(), +            force_fallback_adapter: false, +        }; +          let adapter = instance -            .request_adapter(&wgpu::RequestAdapterOptions { -                power_preference: wgpu::util::power_preference_from_env() -                    .unwrap_or(if settings.antialiasing.is_none() { -                        wgpu::PowerPreference::LowPower -                    } else { -                        wgpu::PowerPreference::HighPerformance -                    }), -                compatible_surface: compatible_surface.as_ref(), -                force_fallback_adapter: false, -            }) -            .await?; +            .request_adapter(&adapter_options) +            .await +            .ok_or(Error::NoAdapterFound(format!("{:?}", adapter_options)))?;          log::info!("Selected: {:#?}", adapter.get_info()); -        let (format, alpha_mode) = -            compatible_surface.as_ref().and_then(|surface| { +        let (format, alpha_mode) = compatible_surface +            .as_ref() +            .and_then(|surface| {                  let capabilities = surface.get_capabilities(&adapter);                  let mut formats = capabilities.formats.iter().copied(); @@ -101,7 +129,8 @@ impl Compositor {                  };                  format.zip(Some(preferred_alpha)) -            })?; +            }) +            .ok_or(Error::IncompatibleSurface)?;          log::info!(              "Selected format: {format:?} with alpha mode: {alpha_mode:?}" @@ -115,39 +144,46 @@ impl Compositor {          let limits =              [wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()]; -        let mut limits = limits.into_iter().map(|limits| wgpu::Limits { +        let limits = limits.into_iter().map(|limits| wgpu::Limits {              max_bind_groups: 2,              ..limits          }); -        let (device, queue) = -            loop { -                let required_limits = limits.next()?; -                let device = adapter.request_device( +        let mut errors = Vec::new(); + +        for required_limits in limits { +            let result = adapter +                .request_device(                      &wgpu::DeviceDescriptor {                          label: Some(                              "iced_wgpu::window::compositor device descriptor",                          ),                          required_features: wgpu::Features::empty(), -                        required_limits, +                        required_limits: required_limits.clone(),                      },                      None, -                ).await.ok(); - -                if let Some(device) = device { -                    break Some(device); +                ) +                .await; + +            match result { +                Ok((device, queue)) => { +                    return Ok(Compositor { +                        instance, +                        settings, +                        adapter, +                        device, +                        queue, +                        format, +                        alpha_mode, +                    })                  } -            }?; - -        Some(Compositor { -            instance, -            settings, -            adapter, -            device, -            queue, -            format, -            alpha_mode, -        }) +                Err(error) => { +                    errors.push((required_limits, error)); +                } +            } +        } + +        Err(Error::RequestDeviceFailed(errors))      }      /// Creates a new rendering [`Backend`] for this [`Compositor`]. @@ -168,9 +204,7 @@ pub async fn new<W: compositor::Window>(      settings: Settings,      compatible_window: W,  ) -> Result<Compositor, Error> { -    Compositor::request(settings, Some(compatible_window)) -        .await -        .ok_or(Error::GraphicsAdapterNotFound) +    Compositor::request(settings, Some(compatible_window)).await  }  /// Presents the given primitives with the given [`Compositor`] and [`Backend`]. @@ -229,15 +263,31 @@ pub fn present<T: AsRef<str>>(  }  impl graphics::Compositor for Compositor { -    type Settings = Settings;      type Renderer = Renderer;      type Surface = wgpu::Surface<'static>; -    fn new<W: compositor::Window>( -        settings: Self::Settings, +    async fn with_backend<W: compositor::Window>( +        settings: graphics::Settings,          compatible_window: W, -    ) -> impl Future<Output = Result<Self, Error>> { -        new(settings, compatible_window) +        backend: Option<&str>, +    ) -> Result<Self, graphics::Error> { +        match backend { +            None | Some("wgpu") => Ok(new( +                Settings { +                    internal_backend: wgpu::util::backend_bits_from_env() +                        .unwrap_or(wgpu::Backends::all()), +                    ..settings.into() +                }, +                compatible_window, +            ) +            .await?), +            Some(backend) => Err(graphics::Error::GraphicsAdapterNotFound { +                backend: "wgpu", +                reason: error::Reason::DidNotMatch { +                    preferred_backend: backend.to_owned(), +                }, +            }), +        }      }      fn create_renderer(&self) -> Self::Renderer {  | 
