Reintroduce backend selection through ICED_BACKEND env var
This commit is contained in:
parent
441e9237cd
commit
4f5b63f1f4
8 changed files with 196 additions and 83 deletions
|
|
@ -20,6 +20,18 @@ pub trait Compositor: Sized {
|
||||||
fn new<W: Window + Clone>(
|
fn new<W: Window + Clone>(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
compatible_window: W,
|
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>>;
|
) -> impl Future<Output = Result<Self, Error>>;
|
||||||
|
|
||||||
/// Creates a [`Self::Renderer`] for the [`Compositor`].
|
/// Creates a [`Self::Renderer`] for the [`Compositor`].
|
||||||
|
|
@ -130,9 +142,10 @@ impl Compositor for () {
|
||||||
type Renderer = ();
|
type Renderer = ();
|
||||||
type Surface = ();
|
type Surface = ();
|
||||||
|
|
||||||
async fn new<W: Window + Clone>(
|
async fn with_backend<W: Window + Clone>(
|
||||||
_settings: Settings,
|
_settings: Settings,
|
||||||
_compatible_window: W,
|
_compatible_window: W,
|
||||||
|
_preffered_backend: Option<&str>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
//! See what can go wrong when creating graphical backends.
|
||||||
|
|
||||||
/// An error that occurred while creating an application's graphical context.
|
/// 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 {
|
pub enum Error {
|
||||||
/// The requested backend version is not supported.
|
/// The requested backend version is not supported.
|
||||||
#[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.
|
/// A suitable graphics adapter or device could not be found.
|
||||||
#[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
|
/// An error occurred in the context's internal backend
|
||||||
#[error("an error occurred in the context's internal backend")]
|
#[error("an error occurred in the context's internal backend")]
|
||||||
BackendError(String),
|
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),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
mod antialiasing;
|
mod antialiasing;
|
||||||
mod cached;
|
mod cached;
|
||||||
mod error;
|
|
||||||
mod primitive;
|
mod primitive;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod viewport;
|
mod viewport;
|
||||||
|
|
@ -27,6 +26,7 @@ pub mod backend;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod damage;
|
pub mod damage;
|
||||||
|
pub mod error;
|
||||||
pub mod gradient;
|
pub mod gradient;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
|
|
||||||
|
|
@ -204,18 +204,55 @@ where
|
||||||
type Renderer = Renderer<L::Renderer, R::Renderer>;
|
type Renderer = Renderer<L::Renderer, R::Renderer>;
|
||||||
type Surface = Surface<L::Surface, R::Surface>;
|
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,
|
settings: graphics::Settings,
|
||||||
compatible_window: W,
|
compatible_window: W,
|
||||||
|
backend: Option<&str>,
|
||||||
) -> Result<Self, graphics::Error> {
|
) -> Result<Self, graphics::Error> {
|
||||||
if let Ok(left) = L::new(settings, compatible_window.clone())
|
use std::env;
|
||||||
.await
|
|
||||||
.map(Self::Left)
|
let backends = backend
|
||||||
{
|
.map(str::to_owned)
|
||||||
return Ok(left);
|
.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 {
|
fn create_renderer(&self) -> Self::Renderer {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::core::{Color, Rectangle, Size};
|
use crate::core::{Color, Rectangle, Size};
|
||||||
use crate::graphics::compositor::{self, Information};
|
use crate::graphics::compositor::{self, Information};
|
||||||
use crate::graphics::damage;
|
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 crate::{Backend, Primitive, Renderer, Settings};
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::future::{self, Future};
|
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
pub struct Compositor {
|
pub struct Compositor {
|
||||||
|
|
@ -28,11 +28,22 @@ impl crate::graphics::Compositor for Compositor {
|
||||||
type Renderer = Renderer;
|
type Renderer = Renderer;
|
||||||
type Surface = Surface;
|
type Surface = Surface;
|
||||||
|
|
||||||
fn new<W: compositor::Window>(
|
async fn with_backend<W: compositor::Window>(
|
||||||
settings: graphics::Settings,
|
settings: graphics::Settings,
|
||||||
compatible_window: W,
|
compatible_window: W,
|
||||||
) -> impl Future<Output = Result<Self, Error>> {
|
backend: Option<&str>,
|
||||||
future::ready(Ok(new(settings.into(), compatible_window)))
|
) -> 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 {
|
fn create_renderer(&self) -> Self::Renderer {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ glyphon.workspace = true
|
||||||
guillotiere.workspace = true
|
guillotiere.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
once_cell.workspace = true
|
once_cell.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
wgpu.workspace = true
|
wgpu.workspace = true
|
||||||
|
|
||||||
lyon.workspace = true
|
lyon.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -29,30 +29,6 @@ pub struct Settings {
|
||||||
pub antialiasing: Option<Antialiasing>,
|
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 {
|
impl Default for Settings {
|
||||||
fn default() -> Settings {
|
fn default() -> Settings {
|
||||||
Settings {
|
Settings {
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@
|
||||||
use crate::core::{Color, Size};
|
use crate::core::{Color, Size};
|
||||||
use crate::graphics::color;
|
use crate::graphics::color;
|
||||||
use crate::graphics::compositor;
|
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 crate::{Backend, Primitive, Renderer, Settings};
|
||||||
|
|
||||||
use std::future::Future;
|
|
||||||
|
|
||||||
/// A window graphics backend for iced powered by `wgpu`.
|
/// A window graphics backend for iced powered by `wgpu`.
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub struct Compositor {
|
pub struct Compositor {
|
||||||
|
|
@ -19,6 +18,32 @@ pub struct Compositor {
|
||||||
alpha_mode: wgpu::CompositeAlphaMode,
|
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 {
|
impl Compositor {
|
||||||
/// Requests a new [`Compositor`] with the given [`Settings`].
|
/// Requests a new [`Compositor`] with the given [`Settings`].
|
||||||
///
|
///
|
||||||
|
|
@ -26,7 +51,7 @@ impl Compositor {
|
||||||
pub async fn request<W: compositor::Window>(
|
pub async fn request<W: compositor::Window>(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
compatible_window: Option<W>,
|
compatible_window: Option<W>,
|
||||||
) -> Option<Self> {
|
) -> Result<Self, Error> {
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||||
backends: settings.internal_backend,
|
backends: settings.internal_backend,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -48,23 +73,27 @@ impl Compositor {
|
||||||
let compatible_surface = compatible_window
|
let compatible_surface = compatible_window
|
||||||
.and_then(|window| instance.create_surface(window).ok());
|
.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
|
let adapter = instance
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
.request_adapter(&adapter_options)
|
||||||
power_preference: wgpu::util::power_preference_from_env()
|
.await
|
||||||
.unwrap_or(if settings.antialiasing.is_none() {
|
.ok_or(Error::NoAdapterFound(format!("{:?}", adapter_options)))?;
|
||||||
wgpu::PowerPreference::LowPower
|
|
||||||
} else {
|
|
||||||
wgpu::PowerPreference::HighPerformance
|
|
||||||
}),
|
|
||||||
compatible_surface: compatible_surface.as_ref(),
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
log::info!("Selected: {:#?}", adapter.get_info());
|
log::info!("Selected: {:#?}", adapter.get_info());
|
||||||
|
|
||||||
let (format, alpha_mode) =
|
let (format, alpha_mode) = compatible_surface
|
||||||
compatible_surface.as_ref().and_then(|surface| {
|
.as_ref()
|
||||||
|
.and_then(|surface| {
|
||||||
let capabilities = surface.get_capabilities(&adapter);
|
let capabilities = surface.get_capabilities(&adapter);
|
||||||
|
|
||||||
let mut formats = capabilities.formats.iter().copied();
|
let mut formats = capabilities.formats.iter().copied();
|
||||||
|
|
@ -96,7 +125,8 @@ impl Compositor {
|
||||||
};
|
};
|
||||||
|
|
||||||
format.zip(Some(preferred_alpha))
|
format.zip(Some(preferred_alpha))
|
||||||
})?;
|
})
|
||||||
|
.ok_or(Error::IncompatibleSurface)?;
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"Selected format: {format:?} with alpha mode: {alpha_mode:?}"
|
"Selected format: {format:?} with alpha mode: {alpha_mode:?}"
|
||||||
|
|
@ -110,39 +140,46 @@ impl Compositor {
|
||||||
let limits =
|
let limits =
|
||||||
[wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
|
[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,
|
max_bind_groups: 2,
|
||||||
..limits
|
..limits
|
||||||
});
|
});
|
||||||
|
|
||||||
let (device, queue) =
|
let mut errors = Vec::new();
|
||||||
loop {
|
|
||||||
let required_limits = limits.next()?;
|
for required_limits in limits {
|
||||||
let device = adapter.request_device(
|
let result = adapter
|
||||||
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&wgpu::DeviceDescriptor {
|
||||||
label: Some(
|
label: Some(
|
||||||
"iced_wgpu::window::compositor device descriptor",
|
"iced_wgpu::window::compositor device descriptor",
|
||||||
),
|
),
|
||||||
required_features: wgpu::Features::empty(),
|
required_features: wgpu::Features::empty(),
|
||||||
required_limits,
|
required_limits: required_limits.clone(),
|
||||||
},
|
},
|
||||||
None,
|
None,
|
||||||
).await.ok();
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
if let Some(device) = device {
|
match result {
|
||||||
break Some(device);
|
Ok((device, queue)) => {
|
||||||
|
return Ok(Compositor {
|
||||||
|
instance,
|
||||||
|
settings,
|
||||||
|
adapter,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
format,
|
||||||
|
alpha_mode,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}?;
|
Err(error) => {
|
||||||
|
errors.push((required_limits, error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(Compositor {
|
Err(Error::RequestDeviceFailed(errors))
|
||||||
instance,
|
|
||||||
settings,
|
|
||||||
adapter,
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
format,
|
|
||||||
alpha_mode,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new rendering [`Backend`] for this [`Compositor`].
|
/// Creates a new rendering [`Backend`] for this [`Compositor`].
|
||||||
|
|
@ -163,9 +200,7 @@ pub async fn new<W: compositor::Window>(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
compatible_window: W,
|
compatible_window: W,
|
||||||
) -> Result<Compositor, Error> {
|
) -> Result<Compositor, Error> {
|
||||||
Compositor::request(settings, Some(compatible_window))
|
Compositor::request(settings, Some(compatible_window)).await
|
||||||
.await
|
|
||||||
.ok_or(Error::GraphicsAdapterNotFound)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Presents the given primitives with the given [`Compositor`] and [`Backend`].
|
/// Presents the given primitives with the given [`Compositor`] and [`Backend`].
|
||||||
|
|
@ -227,11 +262,28 @@ impl graphics::Compositor for Compositor {
|
||||||
type Renderer = Renderer;
|
type Renderer = Renderer;
|
||||||
type Surface = wgpu::Surface<'static>;
|
type Surface = wgpu::Surface<'static>;
|
||||||
|
|
||||||
fn new<W: compositor::Window>(
|
async fn with_backend<W: compositor::Window>(
|
||||||
settings: graphics::Settings,
|
settings: graphics::Settings,
|
||||||
compatible_window: W,
|
compatible_window: W,
|
||||||
) -> impl Future<Output = Result<Self, Error>> {
|
backend: Option<&str>,
|
||||||
new(settings.into(), compatible_window)
|
) -> 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 {
|
fn create_renderer(&self) -> Self::Renderer {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue