From 9b4bcd287a7f4822314e158990d1dc023d5aab51 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Mar 2023 22:10:13 +0100 Subject: Introduce backend feature flags in `iced_renderer` --- Cargo.toml | 6 +++ renderer/Cargo.toml | 6 ++- renderer/src/backend.rs | 76 +++++++++++------------------- renderer/src/compositor.rs | 112 +++++++++++++++++++++++++++++++++------------ renderer/src/geometry.rs | 41 +++++++++++------ renderer/src/lib.rs | 3 ++ 6 files changed, 149 insertions(+), 95 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a677569a..38c35f43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,11 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] +default = ["wgpu", "tiny-skia"] +# Enable the `wgpu` GPU-accelerated renderer backend +wgpu = ["iced_renderer/wgpu"] +# Enable the `tiny-skia` software renderer backend +tiny-skia = ["iced_renderer/tiny-skia"] # Enables the `Image` widget image = ["iced_widget/image", "image_rs"] # Enables the `Svg` widget @@ -58,6 +63,7 @@ members = [ [dependencies] iced_core = { version = "0.8", path = "core" } iced_futures = { version = "0.6", path = "futures" } +iced_renderer = { version = "0.1", path = "renderer" } iced_widget = { version = "0.1", path = "widget" } iced_winit = { version = "0.8", path = "winit", features = ["application"] } thiserror = "1" diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 560bf2e1..629c11ba 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -4,9 +4,11 @@ version = "0.1.0" edition = "2021" [features] +wgpu = ["iced_wgpu"] +tiny-skia = ["iced_tiny_skia"] image = ["iced_wgpu/image", "iced_tiny_skia/image"] svg = ["iced_wgpu/svg", "iced_tiny_skia/svg"] -geometry = ["iced_wgpu/geometry", "iced_tiny_skia/geometry"] +geometry = ["iced_graphics/geometry", "iced_wgpu?/geometry", "iced_tiny_skia?/geometry"] tracing = ["iced_wgpu/tracing"] [dependencies] @@ -20,7 +22,9 @@ path = "../graphics" [dependencies.iced_wgpu] version = "0.9" path = "../wgpu" +optional = true [dependencies.iced_tiny_skia] version = "0.1" path = "../tiny_skia" +optional = true diff --git a/renderer/src/backend.rs b/renderer/src/backend.rs index bf5da322..e77b708b 100644 --- a/renderer/src/backend.rs +++ b/renderer/src/backend.rs @@ -6,16 +6,26 @@ use std::borrow::Cow; #[allow(clippy::large_enum_variant)] pub enum Backend { + #[cfg(feature = "wgpu")] Wgpu(iced_wgpu::Backend), + #[cfg(feature = "tiny-skia")] TinySkia(iced_tiny_skia::Backend), } +macro_rules! delegate { + ($backend:expr, $name:ident, $body:expr) => { + match $backend { + #[cfg(feature = "wgpu")] + Self::Wgpu($name) => $body, + #[cfg(feature = "tiny-skia")] + Self::TinySkia($name) => $body, + } + }; +} + impl iced_graphics::Backend for Backend { fn trim_measurements(&mut self) { - match self { - Self::Wgpu(backend) => backend.trim_measurements(), - Self::TinySkia(backend) => backend.trim_measurements(), - } + delegate!(self, backend, backend.trim_measurements()); } } @@ -25,17 +35,11 @@ impl backend::Text for Backend { const ARROW_DOWN_ICON: char = '\u{e800}'; fn default_font(&self) -> Font { - match self { - Self::Wgpu(backend) => backend.default_font(), - Self::TinySkia(backend) => backend.default_font(), - } + delegate!(self, backend, backend.default_font()) } fn default_size(&self) -> f32 { - match self { - Self::Wgpu(backend) => backend.default_size(), - Self::TinySkia(backend) => backend.default_size(), - } + delegate!(self, backend, backend.default_size()) } fn measure( @@ -45,14 +49,7 @@ impl backend::Text for Backend { font: Font, bounds: Size, ) -> (f32, f32) { - match self { - Self::Wgpu(backend) => { - backend.measure(contents, size, font, bounds) - } - Self::TinySkia(backend) => { - backend.measure(contents, size, font, bounds) - } - } + delegate!(self, backend, backend.measure(contents, size, font, bounds)) } fn hit_test( @@ -64,45 +61,29 @@ impl backend::Text for Backend { position: Point, nearest_only: bool, ) -> Option { - match self { - Self::Wgpu(backend) => backend.hit_test( - contents, - size, - font, - bounds, - position, - nearest_only, - ), - Self::TinySkia(backend) => backend.hit_test( + delegate!( + self, + backend, + backend.hit_test( contents, size, font, bounds, position, - nearest_only, - ), - } + nearest_only + ) + ) } fn load_font(&mut self, font: Cow<'static, [u8]>) { - match self { - Self::Wgpu(backend) => { - backend.load_font(font); - } - Self::TinySkia(backend) => { - backend.load_font(font); - } - } + delegate!(self, backend, backend.load_font(font)); } } #[cfg(feature = "image")] impl backend::Image for Backend { fn dimensions(&self, handle: &crate::core::image::Handle) -> Size { - match self { - Self::Wgpu(backend) => backend.dimensions(handle), - Self::TinySkia(backend) => backend.dimensions(handle), - } + delegate!(self, backend, backend.dimensions(handle)) } } @@ -112,9 +93,6 @@ impl backend::Svg for Backend { &self, handle: &crate::core::svg::Handle, ) -> Size { - match self { - Self::Wgpu(backend) => backend.viewport_dimensions(handle), - Self::TinySkia(backend) => backend.viewport_dimensions(handle), - } + delegate!(self, backend, backend.viewport_dimensions(handle)) } } diff --git a/renderer/src/compositor.rs b/renderer/src/compositor.rs index 0cdcb293..218e7e33 100644 --- a/renderer/src/compositor.rs +++ b/renderer/src/compositor.rs @@ -1,17 +1,21 @@ use crate::core::Color; use crate::graphics::compositor::{Information, SurfaceError}; use crate::graphics::{Error, Viewport}; -use crate::{Backend, Renderer, Settings}; +use crate::{Renderer, Settings}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; pub enum Compositor { + #[cfg(feature = "wgpu")] Wgpu(iced_wgpu::window::Compositor), + #[cfg(feature = "tiny-skia")] TinySkia(iced_tiny_skia::window::Compositor), } pub enum Surface { + #[cfg(feature = "wgpu")] Wgpu(iced_wgpu::window::Surface), + #[cfg(feature = "tiny-skia")] TinySkia(iced_tiny_skia::window::Surface), } @@ -22,32 +26,65 @@ impl crate::graphics::Compositor for Compositor { fn new( settings: Self::Settings, - _compatible_window: Option<&W>, + compatible_window: Option<&W>, ) -> Result<(Self, Self::Renderer), Error> { - //let (compositor, backend) = iced_wgpu::window::compositor::new( - // iced_wgpu::Settings { - // default_font: settings.default_font, - // default_text_size: settings.default_text_size, - // antialiasing: settings.antialiasing, - // ..iced_wgpu::Settings::from_env() - // }, - // compatible_window, - //)?; - - //Ok(( - // Self::Wgpu(compositor), - // Renderer::new(Backend::Wgpu(backend)), - //)) - let (compositor, backend) = - iced_tiny_skia::window::compositor::new(iced_tiny_skia::Settings { - default_font: settings.default_font, - default_text_size: settings.default_text_size, - }); - - Ok(( - Self::TinySkia(compositor), - Renderer::new(Backend::TinySkia(backend)), - )) + #[cfg(feature = "wgpu")] + let new_wgpu = |settings: Self::Settings, compatible_window| { + let (compositor, backend) = iced_wgpu::window::compositor::new( + iced_wgpu::Settings { + default_font: settings.default_font, + default_text_size: settings.default_text_size, + antialiasing: settings.antialiasing, + ..iced_wgpu::Settings::from_env() + }, + compatible_window, + )?; + + Ok(( + Self::Wgpu(compositor), + Renderer::new(crate::Backend::Wgpu(backend)), + )) + }; + + #[cfg(feature = "tiny-skia")] + let new_tiny_skia = |settings: Self::Settings, _compatible_window| { + let (compositor, backend) = iced_tiny_skia::window::compositor::new( + iced_tiny_skia::Settings { + default_font: settings.default_font, + default_text_size: settings.default_text_size, + }, + ); + + Ok(( + Self::TinySkia(compositor), + Renderer::new(crate::Backend::TinySkia(backend)), + )) + }; + + let fail = |_, _| Err(Error::GraphicsAdapterNotFound); + + let candidates = &[ + #[cfg(feature = "wgpu")] + new_wgpu, + #[cfg(feature = "tiny-skia")] + new_tiny_skia, + fail, + ]; + + let mut error = Error::GraphicsAdapterNotFound; + + for candidate in candidates { + match candidate(settings, compatible_window) { + Ok((compositor, renderer)) => { + return Ok((compositor, renderer)) + } + Err(new_error) => { + error = new_error; + } + } + } + + Err(error) } fn create_surface( @@ -57,9 +94,11 @@ impl crate::graphics::Compositor for Compositor { height: u32, ) -> Surface { match self { + #[cfg(feature = "wgpu")] Self::Wgpu(compositor) => { Surface::Wgpu(compositor.create_surface(window, width, height)) } + #[cfg(feature = "tiny-skia")] Self::TinySkia(compositor) => Surface::TinySkia( compositor.create_surface(window, width, height), ), @@ -73,19 +112,26 @@ impl crate::graphics::Compositor for Compositor { height: u32, ) { match (self, surface) { + #[cfg(feature = "wgpu")] (Self::Wgpu(compositor), Surface::Wgpu(surface)) => { compositor.configure_surface(surface, width, height); } + #[cfg(feature = "tiny-skia")] (Self::TinySkia(compositor), Surface::TinySkia(surface)) => { compositor.configure_surface(surface, width, height); } - _ => unreachable!(), + #[allow(unreachable_patterns)] + _ => panic!( + "The provided surface is not compatible with the compositor." + ), } } fn fetch_information(&self) -> Information { match self { + #[cfg(feature = "wgpu")] Self::Wgpu(compositor) => compositor.fetch_information(), + #[cfg(feature = "tiny-skia")] Self::TinySkia(compositor) => compositor.fetch_information(), } } @@ -100,9 +146,10 @@ impl crate::graphics::Compositor for Compositor { ) -> Result<(), SurfaceError> { renderer.with_primitives(|backend, primitives| { match (self, backend, surface) { + #[cfg(feature = "wgpu")] ( Self::Wgpu(compositor), - Backend::Wgpu(backend), + crate::Backend::Wgpu(backend), Surface::Wgpu(surface), ) => iced_wgpu::window::compositor::present( compositor, @@ -113,9 +160,10 @@ impl crate::graphics::Compositor for Compositor { background_color, overlay, ), + #[cfg(feature = "tiny-skia")] ( Self::TinySkia(compositor), - Backend::TinySkia(backend), + crate::Backend::TinySkia(backend), Surface::TinySkia(surface), ) => iced_tiny_skia::window::compositor::present( compositor, @@ -126,7 +174,11 @@ impl crate::graphics::Compositor for Compositor { background_color, overlay, ), - _ => unreachable!(), + #[allow(unreachable_patterns)] + _ => panic!( + "The provided renderer or surface are not compatible \ + with the compositor." + ), } }) } diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs index 361fc86b..21ef2c06 100644 --- a/renderer/src/geometry.rs +++ b/renderer/src/geometry.rs @@ -7,14 +7,18 @@ use crate::graphics::geometry::{Fill, Geometry, Path, Stroke, Text}; use crate::Backend; pub enum Frame { + #[cfg(feature = "wgpu")] Wgpu(iced_wgpu::geometry::Frame), + #[cfg(feature = "tiny-skia")] TinySkia(iced_tiny_skia::geometry::Frame), } macro_rules! delegate { - ($frame:expr, $name:ident => $body:expr) => { + ($frame:expr, $name:ident, $body:expr) => { match $frame { + #[cfg(feature = "wgpu")] Self::Wgpu($name) => $body, + #[cfg(feature = "tiny-skia")] Self::TinySkia($name) => $body, } }; @@ -23,9 +27,11 @@ macro_rules! delegate { impl Frame { pub fn new(renderer: &crate::Renderer, size: Size) -> Self { match renderer.backend() { + #[cfg(feature = "wgpu")] Backend::Wgpu(_) => { Frame::Wgpu(iced_wgpu::geometry::Frame::new(size)) } + #[cfg(feature = "tiny-skia")] Backend::TinySkia(_) => { Frame::TinySkia(iced_tiny_skia::geometry::Frame::new(size)) } @@ -35,31 +41,31 @@ impl Frame { /// Returns the width of the [`Frame`]. #[inline] pub fn width(&self) -> f32 { - delegate!(self, frame => frame.width()) + delegate!(self, frame, frame.width()) } /// Returns the height of the [`Frame`]. #[inline] pub fn height(&self) -> f32 { - delegate!(self, frame => frame.height()) + delegate!(self, frame, frame.height()) } /// Returns the dimensions of the [`Frame`]. #[inline] pub fn size(&self) -> Size { - delegate!(self, frame => frame.size()) + delegate!(self, frame, frame.size()) } /// Returns the coordinate of the center of the [`Frame`]. #[inline] pub fn center(&self) -> Point { - delegate!(self, frame => frame.center()) + delegate!(self, frame, frame.center()) } /// Draws the given [`Path`] on the [`Frame`] by filling it with the /// provided style. pub fn fill(&mut self, path: &Path, fill: impl Into) { - delegate!(self, frame => frame.fill(path, fill)); + delegate!(self, frame, frame.fill(path, fill)); } /// Draws an axis-aligned rectangle given its top-left corner coordinate and @@ -70,13 +76,13 @@ impl Frame { size: Size, fill: impl Into, ) { - delegate!(self, frame => frame.fill_rectangle(top_left, size, fill)); + delegate!(self, frame, frame.fill_rectangle(top_left, size, fill)); } /// 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>) { - delegate!(self, frame => frame.stroke(path, stroke)); + delegate!(self, frame, frame.stroke(path, stroke)); } /// Draws the characters of the given [`Text`] on the [`Frame`], filling @@ -95,7 +101,7 @@ impl Frame { /// /// [`Canvas`]: crate::widget::Canvas pub fn fill_text(&mut self, text: impl Into) { - delegate!(self, frame => frame.fill_text(text)); + delegate!(self, frame, frame.fill_text(text)); } /// Stores the current transform of the [`Frame`] and executes the given @@ -105,11 +111,11 @@ impl Frame { /// operations in different coordinate systems. #[inline] pub fn with_save(&mut self, f: impl FnOnce(&mut Frame)) { - delegate!(self, frame => frame.push_transform()); + delegate!(self, frame, frame.push_transform()); f(self); - delegate!(self, frame => frame.pop_transform()); + delegate!(self, frame, frame.pop_transform()); } /// Executes the given drawing operations within a [`Rectangle`] region, @@ -121,9 +127,11 @@ impl Frame { #[inline] pub fn with_clip(&mut self, region: Rectangle, f: impl FnOnce(&mut Frame)) { let mut frame = match self { + #[cfg(feature = "wgpu")] Self::Wgpu(_) => { Self::Wgpu(iced_wgpu::geometry::Frame::new(region.size())) } + #[cfg(feature = "tiny-skia")] Self::TinySkia(_) => Self::TinySkia( iced_tiny_skia::geometry::Frame::new(region.size()), ), @@ -134,12 +142,15 @@ impl Frame { let translation = Vector::new(region.x, region.y); match (self, frame) { + #[cfg(feature = "wgpu")] (Self::Wgpu(target), Self::Wgpu(frame)) => { target.clip(frame, translation); } + #[cfg(feature = "tiny-skia")] (Self::TinySkia(target), Self::TinySkia(frame)) => { target.clip(frame, translation); } + #[allow(unreachable_patterns)] _ => unreachable!(), }; } @@ -147,22 +158,22 @@ impl Frame { /// Applies a translation to the current transform of the [`Frame`]. #[inline] pub fn translate(&mut self, translation: Vector) { - delegate!(self, frame => frame.translate(translation)); + delegate!(self, frame, frame.translate(translation)); } /// Applies a rotation in radians to the current transform of the [`Frame`]. #[inline] pub fn rotate(&mut self, angle: f32) { - delegate!(self, frame => frame.rotate(angle)); + delegate!(self, frame, frame.rotate(angle)); } /// Applies a scaling to the current transform of the [`Frame`]. #[inline] pub fn scale(&mut self, scale: f32) { - delegate!(self, frame => frame.scale(scale)); + delegate!(self, frame, frame.scale(scale)); } pub fn into_geometry(self) -> Geometry { - Geometry(delegate!(self, frame => frame.into_primitive())) + Geometry(delegate!(self, frame, frame.into_primitive())) } } diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index 22ec7bd1..ba737b11 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -1,3 +1,6 @@ +#[cfg(not(any(feature = "wgpu", feature = "tiny-skia")))] +compile_error!("No backend selected. Enable at least one backend feature: `wgpu` or `tiny-skia`."); + pub mod compositor; #[cfg(feature = "geometry")] -- cgit