mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 22:00:03 -04:00
configurable wgpu backend (#2207)
* introduce new wgpu configuration option to allow configuring wgpu renderer * use new options with wgpu web painter * use on_surface_error callback * changelog update * cleanup * changelog and comment fixes
This commit is contained in:
@@ -20,8 +20,7 @@ pub mod winit;
|
||||
use egui::mutex::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Access to the render state for egui, which can be useful in combination with
|
||||
/// [`egui::PaintCallback`]s for custom rendering using WGPU.
|
||||
/// Access to the render state for egui.
|
||||
#[derive(Clone)]
|
||||
pub struct RenderState {
|
||||
pub device: Arc<wgpu::Device>,
|
||||
@@ -30,6 +29,60 @@ pub struct RenderState {
|
||||
pub renderer: Arc<RwLock<Renderer>>,
|
||||
}
|
||||
|
||||
/// Specifies which action should be taken as consequence of a [`wgpu::SurfaceError`]
|
||||
pub enum SurfaceErrorAction {
|
||||
/// Do nothing and skip the current frame.
|
||||
SkipFrame,
|
||||
|
||||
/// Instructs egui to recreate the surface, then skip the current frame.
|
||||
RecreateSurface,
|
||||
}
|
||||
|
||||
/// Configuration for using wgpu with eframe or the egui-wgpu winit feature.
|
||||
#[derive(Clone)]
|
||||
pub struct WgpuConfiguration {
|
||||
/// Configuration passed on device request.
|
||||
pub device_descriptor: wgpu::DeviceDescriptor<'static>,
|
||||
|
||||
/// Backends that should be supported (wgpu will pick one of these)
|
||||
pub backends: wgpu::Backends,
|
||||
|
||||
/// Present mode used for the primary surface.
|
||||
pub present_mode: wgpu::PresentMode,
|
||||
|
||||
/// Power preference for the adapter.
|
||||
pub power_preference: wgpu::PowerPreference,
|
||||
|
||||
/// Callback for surface errors.
|
||||
pub on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
|
||||
}
|
||||
|
||||
impl Default for WgpuConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
device_descriptor: wgpu::DeviceDescriptor {
|
||||
label: Some("egui wgpu device"),
|
||||
features: wgpu::Features::default(),
|
||||
limits: wgpu::Limits::default(),
|
||||
},
|
||||
backends: wgpu::Backends::PRIMARY | wgpu::Backends::GL,
|
||||
present_mode: wgpu::PresentMode::AutoVsync,
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
|
||||
on_surface_error: Arc::new(|err| {
|
||||
if err == wgpu::SurfaceError::Outdated {
|
||||
// This error occurs when the app is minimized on Windows.
|
||||
// Silently return here to prevent spamming the console with:
|
||||
// "The underlying surface has changed, and therefore the swap chain must be updated"
|
||||
} else {
|
||||
tracing::warn!("Dropped frame with error: {err}");
|
||||
}
|
||||
SurfaceErrorAction::SkipFrame
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the framebuffer format that egui prefers
|
||||
pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
|
||||
for &format in formats {
|
||||
|
||||
@@ -4,7 +4,7 @@ use egui::mutex::RwLock;
|
||||
use tracing::error;
|
||||
use wgpu::{Adapter, Instance, Surface};
|
||||
|
||||
use crate::{renderer, RenderState, Renderer};
|
||||
use crate::{renderer, RenderState, Renderer, SurfaceErrorAction, WgpuConfiguration};
|
||||
|
||||
struct SurfaceState {
|
||||
surface: Surface,
|
||||
@@ -15,10 +15,8 @@ struct SurfaceState {
|
||||
/// Everything you need to paint egui with [`wgpu`] on [`winit`].
|
||||
///
|
||||
/// Alternatively you can use [`crate::renderer`] directly.
|
||||
pub struct Painter<'a> {
|
||||
power_preference: wgpu::PowerPreference,
|
||||
device_descriptor: wgpu::DeviceDescriptor<'a>,
|
||||
present_mode: wgpu::PresentMode,
|
||||
pub struct Painter {
|
||||
configuration: WgpuConfiguration,
|
||||
msaa_samples: u32,
|
||||
depth_format: Option<wgpu::TextureFormat>,
|
||||
depth_texture_view: Option<wgpu::TextureView>,
|
||||
@@ -29,7 +27,7 @@ pub struct Painter<'a> {
|
||||
surface_state: Option<SurfaceState>,
|
||||
}
|
||||
|
||||
impl<'a> Painter<'a> {
|
||||
impl Painter {
|
||||
/// Manages [`wgpu`] state, including surface state, required to render egui.
|
||||
///
|
||||
/// Only the [`wgpu::Instance`] is initialized here. Device selection and the initialization
|
||||
@@ -42,20 +40,11 @@ impl<'a> Painter<'a> {
|
||||
/// [`set_window()`](Self::set_window) once you have
|
||||
/// a [`winit::window::Window`] with a valid `.raw_window_handle()`
|
||||
/// associated.
|
||||
pub fn new(
|
||||
backends: wgpu::Backends,
|
||||
power_preference: wgpu::PowerPreference,
|
||||
device_descriptor: wgpu::DeviceDescriptor<'a>,
|
||||
present_mode: wgpu::PresentMode,
|
||||
msaa_samples: u32,
|
||||
depth_bits: u8,
|
||||
) -> Self {
|
||||
let instance = wgpu::Instance::new(backends);
|
||||
pub fn new(configuration: WgpuConfiguration, msaa_samples: u32, depth_bits: u8) -> Self {
|
||||
let instance = wgpu::Instance::new(configuration.backends);
|
||||
|
||||
Self {
|
||||
power_preference,
|
||||
device_descriptor,
|
||||
present_mode,
|
||||
configuration,
|
||||
msaa_samples,
|
||||
depth_format: (depth_bits > 0).then(|| wgpu::TextureFormat::Depth32Float),
|
||||
depth_texture_view: None,
|
||||
@@ -80,7 +69,8 @@ impl<'a> Painter<'a> {
|
||||
target_format: wgpu::TextureFormat,
|
||||
) -> RenderState {
|
||||
let (device, queue) =
|
||||
pollster::block_on(adapter.request_device(&self.device_descriptor, None)).unwrap();
|
||||
pollster::block_on(adapter.request_device(&self.configuration.device_descriptor, None))
|
||||
.unwrap();
|
||||
|
||||
let renderer = Renderer::new(&device, target_format, self.depth_format, self.msaa_samples);
|
||||
|
||||
@@ -100,7 +90,7 @@ impl<'a> Painter<'a> {
|
||||
fn ensure_render_state_for_surface(&mut self, surface: &Surface) {
|
||||
self.adapter.get_or_insert_with(|| {
|
||||
pollster::block_on(self.instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: self.power_preference,
|
||||
power_preference: self.configuration.power_preference,
|
||||
compatible_surface: Some(surface),
|
||||
force_fallback_adapter: false,
|
||||
}))
|
||||
@@ -130,7 +120,7 @@ impl<'a> Painter<'a> {
|
||||
format,
|
||||
width: width_in_pixels,
|
||||
height: height_in_pixels,
|
||||
present_mode: self.present_mode,
|
||||
present_mode: self.configuration.present_mode,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
};
|
||||
|
||||
@@ -245,19 +235,20 @@ impl<'a> Painter<'a> {
|
||||
Some(rs) => rs,
|
||||
None => return,
|
||||
};
|
||||
let (width, height) = (surface_state.width, surface_state.height);
|
||||
|
||||
let output_frame = match surface_state.surface.get_current_texture() {
|
||||
Ok(frame) => frame,
|
||||
Err(wgpu::SurfaceError::Outdated) => {
|
||||
// This error occurs when the app is minimized on Windows.
|
||||
// Silently return here to prevent spamming the console with:
|
||||
// "The underlying surface has changed, and therefore the swap chain must be updated"
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("Dropped frame with error: {e}");
|
||||
return;
|
||||
}
|
||||
#[allow(clippy::single_match_else)]
|
||||
Err(e) => match (*self.configuration.on_surface_error)(e) {
|
||||
SurfaceErrorAction::RecreateSurface => {
|
||||
self.configure_surface(width, height);
|
||||
return;
|
||||
}
|
||||
SurfaceErrorAction::SkipFrame => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut encoder =
|
||||
@@ -269,7 +260,7 @@ impl<'a> Painter<'a> {
|
||||
|
||||
// Upload all resources for the GPU.
|
||||
let screen_descriptor = renderer::ScreenDescriptor {
|
||||
size_in_pixels: [surface_state.width, surface_state.height],
|
||||
size_in_pixels: [width, height],
|
||||
pixels_per_point,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user