diff options
Diffstat (limited to '')
| -rw-r--r-- | wgpu/src/window/compositor.rs | 250 | 
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, |_| {}); | 
