mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Update wgpu to 0.19 (#3824)
* Closes https://github.com/emilk/egui/issues/3675 --------- Co-authored-by: Andreas Reich <r_andreas2@web.de> Co-authored-by: Mingun <Alexander_Sergey@mail.ru>
This commit is contained in:
@@ -1,5 +1,17 @@
|
||||
//! This crates provides bindings between [`egui`](https://github.com/emilk/egui) and [wgpu](https://crates.io/crates/wgpu).
|
||||
//!
|
||||
//! If you're targeting WebGL you also need to turn on the
|
||||
//! `webgl` feature of the `wgpu` crate:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! # Enable both WebGL and WebGPU backends on web.
|
||||
//! wgpu = { version = "*", features = ["webgpu", "webgl"] }
|
||||
//! ```
|
||||
//!
|
||||
//! You can control whether WebGL or WebGPU will be picked at runtime by setting
|
||||
//! [`WgpuConfiguration::supported_backends`].
|
||||
//! The default is to prefer WebGPU and fall back on WebGL.
|
||||
//!
|
||||
//! ## Feature flags
|
||||
#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
|
||||
//!
|
||||
@@ -21,6 +33,7 @@ use std::sync::Arc;
|
||||
|
||||
use epaint::mutex::RwLock;
|
||||
|
||||
/// An error produced by egui-wgpu.
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum WgpuError {
|
||||
#[error("Failed to create wgpu adapter, no suitable adapter found.")]
|
||||
@@ -34,6 +47,10 @@ pub enum WgpuError {
|
||||
|
||||
#[error(transparent)]
|
||||
CreateSurfaceError(#[from] wgpu::CreateSurfaceError),
|
||||
|
||||
#[cfg(feature = "winit")]
|
||||
#[error(transparent)]
|
||||
HandleError(#[from] ::winit::raw_window_handle::HandleError),
|
||||
}
|
||||
|
||||
/// Access to the render state for egui.
|
||||
@@ -42,6 +59,13 @@ pub struct RenderState {
|
||||
/// Wgpu adapter used for rendering.
|
||||
pub adapter: Arc<wgpu::Adapter>,
|
||||
|
||||
/// All the available adapters.
|
||||
///
|
||||
/// This is not available on web.
|
||||
/// On web, we always select WebGPU is available, then fall back to WebGL if not.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub available_adapters: Arc<[wgpu::Adapter]>,
|
||||
|
||||
/// Wgpu device used for rendering, created from the adapter.
|
||||
pub device: Arc<wgpu::Device>,
|
||||
|
||||
@@ -63,14 +87,15 @@ impl RenderState {
|
||||
pub async fn create(
|
||||
config: &WgpuConfiguration,
|
||||
instance: &wgpu::Instance,
|
||||
surface: &wgpu::Surface,
|
||||
surface: &wgpu::Surface<'static>,
|
||||
depth_format: Option<wgpu::TextureFormat>,
|
||||
msaa_samples: u32,
|
||||
) -> Result<Self, WgpuError> {
|
||||
crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function`
|
||||
|
||||
// This is always an empty list on web.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let adapters: Vec<_> = instance.enumerate_adapters(wgpu::Backends::all()).collect();
|
||||
let available_adapters = instance.enumerate_adapters(wgpu::Backends::all());
|
||||
|
||||
let adapter = {
|
||||
crate::profile_scope!("request_adapter");
|
||||
@@ -83,18 +108,18 @@ impl RenderState {
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if adapters.is_empty() {
|
||||
if available_adapters.is_empty() {
|
||||
log::info!("No wgpu adapters found");
|
||||
} else if adapters.len() == 1 {
|
||||
} else if available_adapters.len() == 1 {
|
||||
log::info!(
|
||||
"The only available wgpu adapter was not suitable: {}",
|
||||
adapter_info_summary(&adapters[0].get_info())
|
||||
adapter_info_summary(&available_adapters[0].get_info())
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
"No suitable wgpu adapter found out of the {} available ones: {}",
|
||||
adapters.len(),
|
||||
describe_adapters(&adapters)
|
||||
available_adapters.len(),
|
||||
describe_adapters(&available_adapters)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,7 +134,7 @@ impl RenderState {
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if adapters.len() == 1 {
|
||||
if available_adapters.len() == 1 {
|
||||
log::debug!(
|
||||
"Picked the only available wgpu adapter: {}",
|
||||
adapter_info_summary(&adapter.get_info())
|
||||
@@ -117,8 +142,8 @@ impl RenderState {
|
||||
} else {
|
||||
log::info!(
|
||||
"There were {} available wgpu adapters: {}",
|
||||
adapters.len(),
|
||||
describe_adapters(&adapters)
|
||||
available_adapters.len(),
|
||||
describe_adapters(&available_adapters)
|
||||
);
|
||||
log::debug!(
|
||||
"Picked wgpu adapter: {}",
|
||||
@@ -143,6 +168,8 @@ impl RenderState {
|
||||
|
||||
Ok(Self {
|
||||
adapter: Arc::new(adapter),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
available_adapters: available_adapters.into(),
|
||||
device: Arc::new(device),
|
||||
queue: Arc::new(queue),
|
||||
target_format,
|
||||
@@ -180,12 +207,19 @@ pub enum SurfaceErrorAction {
|
||||
|
||||
/// Configuration for using wgpu with eframe or the egui-wgpu winit feature.
|
||||
///
|
||||
/// This can be configured with the environment variables:
|
||||
/// This can also be configured with the environment variables:
|
||||
/// * `WGPU_BACKEND`: `vulkan`, `dx11`, `dx12`, `metal`, `opengl`, `webgpu`
|
||||
/// * `WGPU_POWER_PREF`: `low`, `high` or `none`
|
||||
#[derive(Clone)]
|
||||
pub struct WgpuConfiguration {
|
||||
/// Backends that should be supported (wgpu will pick one of these)
|
||||
/// Backends that should be supported (wgpu will pick one of these).
|
||||
///
|
||||
/// For instance, if you only want to support WebGL (and not WebGPU),
|
||||
/// you can set this to [`wgpu::Backends::GL`].
|
||||
///
|
||||
/// By default on web, WebGPU will be used if available.
|
||||
/// WebGL will only be used as a fallback,
|
||||
/// and only if you have enabled the `webgl` feature of crate `wgpu`.
|
||||
pub supported_backends: wgpu::Backends,
|
||||
|
||||
/// Configuration passed on device request, given an adapter
|
||||
@@ -215,7 +249,7 @@ impl Default for WgpuConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
// Add GL backend, primarily because WebGPU is not stable enough yet.
|
||||
// (note however, that the GL backend needs to be opted-in via a wgpu feature flag)
|
||||
// (note however, that the GL backend needs to be opted-in via the wgpu feature flag "webgl")
|
||||
supported_backends: wgpu::util::backend_bits_from_env()
|
||||
.unwrap_or(wgpu::Backends::PRIMARY | wgpu::Backends::GL),
|
||||
|
||||
@@ -228,8 +262,8 @@ impl Default for WgpuConfiguration {
|
||||
|
||||
wgpu::DeviceDescriptor {
|
||||
label: Some("egui wgpu device"),
|
||||
features: wgpu::Features::default(),
|
||||
limits: wgpu::Limits {
|
||||
required_features: wgpu::Features::default(),
|
||||
required_limits: wgpu::Limits {
|
||||
// When using a depth buffer, we have to be able to create a texture
|
||||
// large enough for the entire surface, and we want to support 4k+ displays.
|
||||
max_texture_dimension_2d: 8192,
|
||||
|
||||
@@ -7,8 +7,14 @@ use epaint::{ahash::HashMap, emath::NumExt, PaintCallbackInfo, Primitive, Vertex
|
||||
use wgpu;
|
||||
use wgpu::util::DeviceExt as _;
|
||||
|
||||
/// You can use this for storage when implementing [`CallbackTrait`].
|
||||
pub type CallbackResources = type_map::concurrent::TypeMap;
|
||||
|
||||
/// You can use this to do custom `wgpu` rendering in an egui app.
|
||||
///
|
||||
/// Implement [`CallbackTrait`] and call [`Callback::new_paint_callback`].
|
||||
///
|
||||
/// This can be turned into a [`epaint::PaintCallback`] and [`epaint::Shape`].
|
||||
pub struct Callback(Box<dyn CallbackTrait>);
|
||||
|
||||
impl Callback {
|
||||
|
||||
@@ -5,7 +5,7 @@ use egui::{ViewportId, ViewportIdMap, ViewportIdSet};
|
||||
use crate::{renderer, RenderState, SurfaceErrorAction, WgpuConfiguration};
|
||||
|
||||
struct SurfaceState {
|
||||
surface: wgpu::Surface,
|
||||
surface: wgpu::Surface<'static>,
|
||||
alpha_mode: wgpu::CompositeAlphaMode,
|
||||
width: u32,
|
||||
height: u32,
|
||||
@@ -151,16 +151,23 @@ impl Painter {
|
||||
} else {
|
||||
wgpu::TextureUsages::RENDER_ATTACHMENT
|
||||
};
|
||||
|
||||
let width = surface_state.width;
|
||||
let height = surface_state.height;
|
||||
|
||||
surface_state.surface.configure(
|
||||
&render_state.device,
|
||||
&wgpu::SurfaceConfiguration {
|
||||
// TODO(emilk): expose `desired_maximum_frame_latency` to eframe users
|
||||
usage,
|
||||
format: render_state.target_format,
|
||||
width: surface_state.width,
|
||||
height: surface_state.height,
|
||||
present_mode,
|
||||
alpha_mode: surface_state.alpha_mode,
|
||||
view_formats: vec![render_state.target_format],
|
||||
..surface_state
|
||||
.surface
|
||||
.get_default_config(&render_state.adapter, width, height)
|
||||
.expect("The surface isn't supported by this adapter")
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -189,79 +196,15 @@ impl Painter {
|
||||
pub async fn set_window(
|
||||
&mut self,
|
||||
viewport_id: ViewportId,
|
||||
window: Option<&winit::window::Window>,
|
||||
window: Option<Arc<winit::window::Window>>,
|
||||
) -> Result<(), crate::WgpuError> {
|
||||
crate::profile_scope!("Painter::set_window"); // profle_function gives bad names for async functions
|
||||
crate::profile_scope!("Painter::set_window"); // profile_function gives bad names for async functions
|
||||
|
||||
if let Some(window) = window {
|
||||
let size = window.inner_size();
|
||||
if self.surfaces.get(&viewport_id).is_none() {
|
||||
let surface = unsafe {
|
||||
crate::profile_scope!("create_surface");
|
||||
self.instance.create_surface(&window)?
|
||||
};
|
||||
|
||||
let render_state = if let Some(render_state) = &self.render_state {
|
||||
render_state
|
||||
} else {
|
||||
let render_state = RenderState::create(
|
||||
&self.configuration,
|
||||
&self.instance,
|
||||
&surface,
|
||||
self.depth_format,
|
||||
self.msaa_samples,
|
||||
)
|
||||
.await?;
|
||||
self.render_state.get_or_insert(render_state)
|
||||
};
|
||||
|
||||
let alpha_mode = if self.support_transparent_backbuffer {
|
||||
let supported_alpha_modes =
|
||||
surface.get_capabilities(&render_state.adapter).alpha_modes;
|
||||
|
||||
// Prefer pre multiplied over post multiplied!
|
||||
if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
|
||||
wgpu::CompositeAlphaMode::PreMultiplied
|
||||
} else if supported_alpha_modes
|
||||
.contains(&wgpu::CompositeAlphaMode::PostMultiplied)
|
||||
{
|
||||
wgpu::CompositeAlphaMode::PostMultiplied
|
||||
} else {
|
||||
log::warn!("Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency.");
|
||||
wgpu::CompositeAlphaMode::Auto
|
||||
}
|
||||
} else {
|
||||
wgpu::CompositeAlphaMode::Auto
|
||||
};
|
||||
|
||||
let supports_screenshot =
|
||||
!matches!(render_state.adapter.get_info().backend, wgpu::Backend::Gl);
|
||||
|
||||
self.surfaces.insert(
|
||||
viewport_id,
|
||||
SurfaceState {
|
||||
surface,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
alpha_mode,
|
||||
supports_screenshot,
|
||||
},
|
||||
);
|
||||
|
||||
let Some(width) = NonZeroU32::new(size.width) else {
|
||||
log::debug!("The window width was zero; skipping generate textures");
|
||||
return Ok(());
|
||||
};
|
||||
let Some(height) = NonZeroU32::new(size.height) else {
|
||||
log::debug!("The window height was zero; skipping generate textures");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
self.resize_and_generate_depth_texture_view_and_msaa_view(
|
||||
viewport_id,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
let surface = self.instance.create_surface(window)?;
|
||||
self.add_surface(surface, viewport_id, size).await?;
|
||||
}
|
||||
} else {
|
||||
log::warn!("No window - clearing all surfaces");
|
||||
@@ -270,6 +213,93 @@ impl Painter {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Updates (or clears) the [`winit::window::Window`] associated with the [`Painter`] without taking ownership of the window.
|
||||
///
|
||||
/// Like [`set_window`](Self::set_window) except:
|
||||
///
|
||||
/// # Safety
|
||||
/// The user is responsible for ensuring that the window is alive for as long as it is set.
|
||||
pub async unsafe fn set_window_unsafe(
|
||||
&mut self,
|
||||
viewport_id: ViewportId,
|
||||
window: Option<&winit::window::Window>,
|
||||
) -> Result<(), crate::WgpuError> {
|
||||
crate::profile_scope!("Painter::set_window_unsafe"); // profile_function gives bad names for async functions
|
||||
|
||||
if let Some(window) = window {
|
||||
let size = window.inner_size();
|
||||
if self.surfaces.get(&viewport_id).is_none() {
|
||||
let surface = unsafe {
|
||||
self.instance
|
||||
.create_surface_unsafe(wgpu::SurfaceTargetUnsafe::from_window(&window)?)?
|
||||
};
|
||||
self.add_surface(surface, viewport_id, size).await?;
|
||||
}
|
||||
} else {
|
||||
log::warn!("No window - clearing all surfaces");
|
||||
self.surfaces.clear();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_surface(
|
||||
&mut self,
|
||||
surface: wgpu::Surface<'static>,
|
||||
viewport_id: ViewportId,
|
||||
size: winit::dpi::PhysicalSize<u32>,
|
||||
) -> Result<(), crate::WgpuError> {
|
||||
let render_state = if let Some(render_state) = &self.render_state {
|
||||
render_state
|
||||
} else {
|
||||
let render_state = RenderState::create(
|
||||
&self.configuration,
|
||||
&self.instance,
|
||||
&surface,
|
||||
self.depth_format,
|
||||
self.msaa_samples,
|
||||
)
|
||||
.await?;
|
||||
self.render_state.get_or_insert(render_state)
|
||||
};
|
||||
let alpha_mode = if self.support_transparent_backbuffer {
|
||||
let supported_alpha_modes = surface.get_capabilities(&render_state.adapter).alpha_modes;
|
||||
|
||||
// Prefer pre multiplied over post multiplied!
|
||||
if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PreMultiplied) {
|
||||
wgpu::CompositeAlphaMode::PreMultiplied
|
||||
} else if supported_alpha_modes.contains(&wgpu::CompositeAlphaMode::PostMultiplied) {
|
||||
wgpu::CompositeAlphaMode::PostMultiplied
|
||||
} else {
|
||||
log::warn!("Transparent window was requested, but the active wgpu surface does not support a `CompositeAlphaMode` with transparency.");
|
||||
wgpu::CompositeAlphaMode::Auto
|
||||
}
|
||||
} else {
|
||||
wgpu::CompositeAlphaMode::Auto
|
||||
};
|
||||
let supports_screenshot =
|
||||
!matches!(render_state.adapter.get_info().backend, wgpu::Backend::Gl);
|
||||
self.surfaces.insert(
|
||||
viewport_id,
|
||||
SurfaceState {
|
||||
surface,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
alpha_mode,
|
||||
supports_screenshot,
|
||||
},
|
||||
);
|
||||
let Some(width) = NonZeroU32::new(size.width) else {
|
||||
log::debug!("The window width was zero; skipping generate textures");
|
||||
return Ok(());
|
||||
};
|
||||
let Some(height) = NonZeroU32::new(size.height) else {
|
||||
log::debug!("The window height was zero; skipping generate textures");
|
||||
return Ok(());
|
||||
};
|
||||
self.resize_and_generate_depth_texture_view_and_msaa_view(viewport_id, width, height);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the maximum texture dimension supported if known
|
||||
///
|
||||
/// This API will only return a known dimension after `set_window()` has been called
|
||||
|
||||
Reference in New Issue
Block a user