summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-03-24 08:04:28 +0100
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-03-24 08:04:28 +0100
commit4f5b63f1f4cd7d3ab72289c697f4abc767114eca (patch)
tree876f9502936b24f63ef21bb2b41e16b222750444
parent441e9237cd1c9c9b61d9b144b5b4dafa236ace28 (diff)
downloadiced-4f5b63f1f4cd7d3ab72289c697f4abc767114eca.tar.gz
iced-4f5b63f1f4cd7d3ab72289c697f4abc767114eca.tar.bz2
iced-4f5b63f1f4cd7d3ab72289c697f4abc767114eca.zip
Reintroduce backend selection through `ICED_BACKEND` env var
-rw-r--r--graphics/src/compositor.rs15
-rw-r--r--graphics/src/error.rs27
-rw-r--r--graphics/src/lib.rs2
-rw-r--r--renderer/src/fallback.rs51
-rw-r--r--tiny_skia/src/window/compositor.rs21
-rw-r--r--wgpu/Cargo.toml1
-rw-r--r--wgpu/src/settings.rs24
-rw-r--r--wgpu/src/window/compositor.rs142
8 files changed, 198 insertions, 85 deletions
diff --git a/graphics/src/compositor.rs b/graphics/src/compositor.rs
index 8c67cd16..86472a58 100644
--- a/graphics/src/compositor.rs
+++ b/graphics/src/compositor.rs
@@ -20,6 +20,18 @@ pub trait Compositor: Sized {
fn new<W: Window + Clone>(
settings: Settings,
compatible_window: W,
+ ) -> impl Future<Output = Result<Self, Error>> {
+ Self::with_backend(settings, compatible_window, None)
+ }
+
+ /// Creates a new [`Compositor`] with a backend preference.
+ ///
+ /// If the backend does not match the preference, it will return
+ /// [`Error::GraphicsAdapterNotFound`].
+ fn with_backend<W: Window + Clone>(
+ _settings: Settings,
+ _compatible_window: W,
+ _backend: Option<&str>,
) -> impl Future<Output = Result<Self, Error>>;
/// Creates a [`Self::Renderer`] for the [`Compositor`].
@@ -130,9 +142,10 @@ impl Compositor for () {
type Renderer = ();
type Surface = ();
- async fn new<W: Window + Clone>(
+ async fn with_backend<W: Window + Clone>(
_settings: Settings,
_compatible_window: W,
+ _preffered_backend: Option<&str>,
) -> Result<Self, Error> {
Ok(())
}
diff --git a/graphics/src/error.rs b/graphics/src/error.rs
index c6ea98a3..6ea1d3a4 100644
--- a/graphics/src/error.rs
+++ b/graphics/src/error.rs
@@ -1,5 +1,7 @@
+//! See what can go wrong when creating graphical backends.
+
/// An error that occurred while creating an application's graphical context.
-#[derive(Debug, thiserror::Error)]
+#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum Error {
/// The requested backend version is not supported.
#[error("the requested backend version is not supported")]
@@ -11,9 +13,30 @@ pub enum Error {
/// A suitable graphics adapter or device could not be found.
#[error("a suitable graphics adapter or device could not be found")]
- GraphicsAdapterNotFound,
+ GraphicsAdapterNotFound {
+ /// The name of the backend where the error happened
+ backend: &'static str,
+ /// The reason why this backend could not be used
+ reason: Reason,
+ },
/// An error occurred in the context's internal backend
#[error("an error occurred in the context's internal backend")]
BackendError(String),
+
+ /// Multiple errors occurred
+ #[error("multiple errors occurred: {0:?}")]
+ List(Vec<Self>),
+}
+
+/// The reason why a graphics adapter could not be found
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Reason {
+ /// The backend did not match the preference
+ DidNotMatch {
+ /// The preferred backend
+ preferred_backend: String,
+ },
+ /// The request to create the backend failed
+ RequestFailed(String),
}
diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs
index 2e476f8c..d7f2f439 100644
--- a/graphics/src/lib.rs
+++ b/graphics/src/lib.rs
@@ -18,7 +18,6 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
mod antialiasing;
mod cached;
-mod error;
mod primitive;
mod settings;
mod viewport;
@@ -27,6 +26,7 @@ pub mod backend;
pub mod color;
pub mod compositor;
pub mod damage;
+pub mod error;
pub mod gradient;
pub mod mesh;
pub mod renderer;
diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs
index ca445746..ef9cc9a9 100644
--- a/renderer/src/fallback.rs
+++ b/renderer/src/fallback.rs
@@ -204,18 +204,55 @@ where
type Renderer = Renderer<L::Renderer, R::Renderer>;
type Surface = Surface<L::Surface, R::Surface>;
- async fn new<W: compositor::Window + Clone>(
+ async fn with_backend<W: compositor::Window + Clone>(
settings: graphics::Settings,
compatible_window: W,
+ backend: Option<&str>,
) -> Result<Self, graphics::Error> {
- if let Ok(left) = L::new(settings, compatible_window.clone())
- .await
- .map(Self::Left)
- {
- return Ok(left);
+ use std::env;
+
+ let backends = backend
+ .map(str::to_owned)
+ .or_else(|| env::var("ICED_BACKEND").ok());
+
+ let mut candidates: Vec<_> = backends
+ .map(|backends| {
+ backends
+ .split(',')
+ .filter(|candidate| !candidate.is_empty())
+ .map(str::to_owned)
+ .map(Some)
+ .collect()
+ })
+ .unwrap_or_default();
+
+ if candidates.is_empty() {
+ candidates.push(None);
}
- R::new(settings, compatible_window).await.map(Self::Right)
+ let mut errors = vec![];
+
+ for backend in candidates.iter().map(Option::as_deref) {
+ match L::with_backend(settings, compatible_window.clone(), backend)
+ .await
+ {
+ Ok(compositor) => return Ok(Self::Left(compositor)),
+ Err(error) => {
+ errors.push(error);
+ }
+ }
+
+ match R::with_backend(settings, compatible_window.clone(), backend)
+ .await
+ {
+ Ok(compositor) => return Ok(Self::Right(compositor)),
+ Err(error) => {
+ errors.push(error);
+ }
+ }
+ }
+
+ Err(graphics::Error::List(errors))
}
fn create_renderer(&self) -> Self::Renderer {
diff --git a/tiny_skia/src/window/compositor.rs b/tiny_skia/src/window/compositor.rs
index 0c08097b..25c57dc1 100644
--- a/tiny_skia/src/window/compositor.rs
+++ b/tiny_skia/src/window/compositor.rs
@@ -1,11 +1,11 @@
use crate::core::{Color, Rectangle, Size};
use crate::graphics::compositor::{self, Information};
use crate::graphics::damage;
-use crate::graphics::{self, Error, Viewport};
+use crate::graphics::error::{self, Error};
+use crate::graphics::{self, Viewport};
use crate::{Backend, Primitive, Renderer, Settings};
use std::collections::VecDeque;
-use std::future::{self, Future};
use std::num::NonZeroU32;
pub struct Compositor {
@@ -28,11 +28,22 @@ impl crate::graphics::Compositor for Compositor {
type Renderer = Renderer;
type Surface = Surface;
- fn new<W: compositor::Window>(
+ async fn with_backend<W: compositor::Window>(
settings: graphics::Settings,
compatible_window: W,
- ) -> impl Future<Output = Result<Self, Error>> {
- future::ready(Ok(new(settings.into(), compatible_window)))
+ backend: Option<&str>,
+ ) -> Result<Self, Error> {
+ match backend {
+ None | Some("tiny-skia") | Some("tiny_skia") => {
+ Ok(new(settings.into(), compatible_window))
+ }
+ Some(backend) => Err(Error::GraphicsAdapterNotFound {
+ backend: "tiny-skia",
+ reason: error::Reason::DidNotMatch {
+ preferred_backend: backend.to_owned(),
+ },
+ }),
+ }
}
fn create_renderer(&self) -> Self::Renderer {
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/settings.rs b/wgpu/src/settings.rs
index 9943aa3e..828d9e09 100644
--- a/wgpu/src/settings.rs
+++ b/wgpu/src/settings.rs
@@ -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 {
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 74dfadba..7b769923 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -2,11 +2,10 @@
use crate::core::{Color, Size};
use crate::graphics::color;
use crate::graphics::compositor;
-use crate::graphics::{self, 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 {
@@ -19,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`].
///
@@ -26,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()
@@ -48,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();
@@ -96,7 +125,8 @@ impl Compositor {
};
format.zip(Some(preferred_alpha))
- })?;
+ })
+ .ok_or(Error::IncompatibleSurface)?;
log::info!(
"Selected format: {format:?} with alpha mode: {alpha_mode:?}"
@@ -110,39 +140,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`].
@@ -163,9 +200,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`].
@@ -227,11 +262,28 @@ impl graphics::Compositor for Compositor {
type Renderer = Renderer;
type Surface = wgpu::Surface<'static>;
- fn new<W: compositor::Window>(
+ async fn with_backend<W: compositor::Window>(
settings: graphics::Settings,
compatible_window: W,
- ) -> impl Future<Output = Result<Self, Error>> {
- new(settings.into(), 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 {