summaryrefslogtreecommitdiffstats
path: root/wgpu
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector@hecrj.dev>2024-03-25 21:36:44 +0100
committerLibravatar GitHub <noreply@github.com>2024-03-25 21:36:44 +0100
commita2a8381a49ac2dd1cd65eb382b9ee02bbfa17286 (patch)
treee6c24928a42e23ff91eea0fc30b4fbbcb6da024b /wgpu
parent3013463baa71504488a20436beb3db87ecb66df0 (diff)
parent6a4f5ac2081699f7cf20c917b367366ab49eeef1 (diff)
downloadiced-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.toml1
-rw-r--r--wgpu/src/backend.rs13
-rw-r--r--wgpu/src/geometry.rs492
-rw-r--r--wgpu/src/primitive.rs8
-rw-r--r--wgpu/src/primitive/pipeline.rs4
-rw-r--r--wgpu/src/settings.rs37
-rw-r--r--wgpu/src/window/compositor.rs146
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 {