summaryrefslogtreecommitdiffstats
path: root/wgpu/src/window/compositor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'wgpu/src/window/compositor.rs')
-rw-r--r--wgpu/src/window/compositor.rs250
1 files changed, 144 insertions, 106 deletions
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 328ad781..2e938c77 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -1,23 +1,49 @@
//! 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::{Backend, Primitive, Renderer, Settings};
-
-use std::future::Future;
+use crate::graphics::error;
+use crate::graphics::{self, Viewport};
+use crate::settings::{self, Settings};
+use crate::{Engine, Renderer};
/// A window graphics backend for iced powered by `wgpu`.
#[allow(missing_debug_implementations)]
pub struct Compositor {
- settings: Settings,
instance: wgpu::Instance,
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
format: wgpu::TextureFormat,
alpha_mode: wgpu::CompositeAlphaMode,
+ engine: Engine,
+ settings: Settings,
+}
+
+/// 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 {
@@ -27,9 +53,9 @@ 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,
+ backends: settings.backends,
..Default::default()
});
@@ -38,7 +64,7 @@ impl Compositor {
#[cfg(not(target_arch = "wasm32"))]
if log::max_level() >= log::LevelFilter::Info {
let available_adapters: Vec<_> = instance
- .enumerate_adapters(settings.internal_backend)
+ .enumerate_adapters(settings.backends)
.iter()
.map(wgpu::Adapter::get_info)
.collect();
@@ -49,23 +75,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();
@@ -92,12 +122,17 @@ impl Compositor {
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
{
wgpu::CompositeAlphaMode::PostMultiplied
+ } else if alpha_modes
+ .contains(&wgpu::CompositeAlphaMode::PreMultiplied)
+ {
+ wgpu::CompositeAlphaMode::PreMultiplied
} else {
wgpu::CompositeAlphaMode::Auto
};
format.zip(Some(preferred_alpha))
- })?;
+ })
+ .ok_or(Error::IncompatibleSurface)?;
log::info!(
"Selected format: {format:?} with alpha mode: {alpha_mode:?}"
@@ -111,70 +146,71 @@ 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)) => {
+ let engine = Engine::new(
+ &adapter,
+ &device,
+ &queue,
+ format,
+ settings.antialiasing,
+ );
+
+ return Ok(Compositor {
+ instance,
+ adapter,
+ device,
+ queue,
+ format,
+ alpha_mode,
+ engine,
+ settings,
+ });
}
- }?;
-
- Some(Compositor {
- instance,
- settings,
- adapter,
- device,
- queue,
- format,
- alpha_mode,
- })
- }
+ Err(error) => {
+ errors.push((required_limits, error));
+ }
+ }
+ }
- /// Creates a new rendering [`Backend`] for this [`Compositor`].
- pub fn create_backend(&self) -> Backend {
- Backend::new(
- &self.adapter,
- &self.device,
- &self.queue,
- self.settings,
- self.format,
- )
+ Err(Error::RequestDeviceFailed(errors))
}
}
-/// Creates a [`Compositor`] and its [`Backend`] for the given [`Settings`] and
-/// window.
+/// Creates a [`Compositor`] with the given [`Settings`] and window.
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`].
+/// Presents the given primitives with the given [`Compositor`].
pub fn present<T: AsRef<str>>(
compositor: &mut Compositor,
- backend: &mut Backend,
+ renderer: &mut Renderer,
surface: &mut wgpu::Surface<'static>,
- primitives: &[Primitive],
viewport: &Viewport,
background_color: Color,
overlay: &[T],
@@ -191,20 +227,21 @@ pub fn present<T: AsRef<str>>(
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
- backend.present(
+ renderer.present(
+ &mut compositor.engine,
&compositor.device,
&compositor.queue,
&mut encoder,
Some(background_color),
frame.texture.format(),
view,
- primitives,
viewport,
overlay,
);
- // Submit work
- let _submission = compositor.queue.submit(Some(encoder.finish()));
+ let _ = compositor.engine.submit(&compositor.queue, encoder);
+
+ // Present the frame
frame.present();
Ok(())
@@ -225,20 +262,41 @@ 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") => {
+ let mut settings = Settings::from(settings);
+
+ if let Some(backends) = wgpu::util::backend_bits_from_env() {
+ settings.backends = backends;
+ }
+
+ if let Some(present_mode) = settings::present_mode_from_env() {
+ settings.present_mode = present_mode;
+ }
+
+ Ok(new(settings, 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 {
Renderer::new(
- self.create_backend(),
+ &self.device,
+ &self.engine,
self.settings.default_font,
self.settings.default_text_size,
)
@@ -278,7 +336,7 @@ impl graphics::Compositor for Compositor {
height,
alpha_mode: self.alpha_mode,
view_formats: vec![],
- desired_maximum_frame_latency: 2,
+ desired_maximum_frame_latency: 1,
},
);
}
@@ -300,17 +358,7 @@ impl graphics::Compositor for Compositor {
background_color: Color,
overlay: &[T],
) -> Result<(), compositor::SurfaceError> {
- renderer.with_primitives(|backend, primitives| {
- present(
- self,
- backend,
- surface,
- primitives,
- viewport,
- background_color,
- overlay,
- )
- })
+ present(self, renderer, surface, viewport, background_color, overlay)
}
fn screenshot<T: AsRef<str>>(
@@ -321,16 +369,7 @@ impl graphics::Compositor for Compositor {
background_color: Color,
overlay: &[T],
) -> Vec<u8> {
- renderer.with_primitives(|backend, primitives| {
- screenshot(
- self,
- backend,
- primitives,
- viewport,
- background_color,
- overlay,
- )
- })
+ screenshot(self, renderer, viewport, background_color, overlay)
}
}
@@ -338,19 +377,12 @@ impl graphics::Compositor for Compositor {
///
/// Returns RGBA bytes of the texture data.
pub fn screenshot<T: AsRef<str>>(
- compositor: &Compositor,
- backend: &mut Backend,
- primitives: &[Primitive],
+ compositor: &mut Compositor,
+ renderer: &mut Renderer,
viewport: &Viewport,
background_color: Color,
overlay: &[T],
) -> Vec<u8> {
- let mut encoder = compositor.device.create_command_encoder(
- &wgpu::CommandEncoderDescriptor {
- label: Some("iced_wgpu.offscreen.encoder"),
- },
- );
-
let dimensions = BufferDimensions::new(viewport.physical_size());
let texture_extent = wgpu::Extent3d {
@@ -374,14 +406,20 @@ pub fn screenshot<T: AsRef<str>>(
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
- backend.present(
+ let mut encoder = compositor.device.create_command_encoder(
+ &wgpu::CommandEncoderDescriptor {
+ label: Some("iced_wgpu.offscreen.encoder"),
+ },
+ );
+
+ renderer.present(
+ &mut compositor.engine,
&compositor.device,
&compositor.queue,
&mut encoder,
Some(background_color),
texture.format(),
&view,
- primitives,
viewport,
overlay,
);
@@ -419,7 +457,7 @@ pub fn screenshot<T: AsRef<str>>(
texture_extent,
);
- let index = compositor.queue.submit(Some(encoder.finish()));
+ let index = compositor.engine.submit(&compositor.queue, encoder);
let slice = output_buffer.slice(..);
slice.map_async(wgpu::MapMode::Read, |_| {});