Merge branch 'master' into beacon

This commit is contained in:
Héctor Ramón Jiménez 2024-05-09 12:32:25 +02:00
commit aaf396256e
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
284 changed files with 18747 additions and 15450 deletions

View file

@ -1,21 +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 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 {
@ -25,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()
});
@ -36,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();
@ -47,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();
@ -90,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:?}"
@ -109,74 +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();
)
.await;
if let Some(device) = device {
break Some(device);
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,
});
}
}?;
Err(error) => {
errors.push((required_limits, error));
}
}
}
Some(Compositor {
instance,
settings,
adapter,
device,
queue,
format,
alpha_mode,
})
}
/// 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.
pub fn new<W: compositor::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> {
let compositor = futures::executor::block_on(Compositor::request(
settings,
Some(compatible_window),
))
.ok_or(Error::GraphicsAdapterNotFound)?;
Ok(compositor)
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(
compositor: &mut Compositor,
backend: &mut Backend,
renderer: &mut Renderer,
surface: &mut wgpu::Surface<'static>,
primitives: &[Primitive],
viewport: &Viewport,
background_color: Color,
) -> Result<(), compositor::SurfaceError> {
@ -192,19 +226,20 @@ pub fn present(
.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,
);
// 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 +260,41 @@ pub fn present(
}
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,
) -> 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 +334,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,
},
);
}
@ -299,16 +355,7 @@ impl graphics::Compositor for Compositor {
viewport: &Viewport,
background_color: Color,
) -> Result<(), compositor::SurfaceError> {
renderer.with_primitives(|backend, primitives| {
present(
self,
backend,
surface,
primitives,
viewport,
background_color,
)
})
present(self, renderer, surface, viewport, background_color)
}
fn screenshot(
@ -318,9 +365,7 @@ impl graphics::Compositor for Compositor {
viewport: &Viewport,
background_color: Color,
) -> Vec<u8> {
renderer.with_primitives(|backend, primitives| {
screenshot(self, backend, primitives, viewport, background_color)
})
screenshot(self, renderer, viewport, background_color)
}
}
@ -328,18 +373,11 @@ impl graphics::Compositor for Compositor {
///
/// Returns RGBA bytes of the texture data.
pub fn screenshot(
compositor: &Compositor,
backend: &mut Backend,
primitives: &[Primitive],
compositor: &mut Compositor,
renderer: &mut Renderer,
viewport: &Viewport,
background_color: Color,
) -> 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 {
@ -363,14 +401,20 @@ pub fn screenshot(
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,
);
@ -407,7 +451,7 @@ pub fn screenshot(
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, |_| {});