mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 21:30: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:
@@ -11,6 +11,7 @@ use std::any::Any;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use crate::native::run::RequestRepaintEvent;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use winit::event_loop::EventLoopBuilder;
|
||||
|
||||
@@ -364,6 +365,10 @@ pub struct NativeOptions {
|
||||
///
|
||||
/// Wayland desktop currently not supported.
|
||||
pub centered: bool,
|
||||
|
||||
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
|
||||
#[cfg(feature = "wgpu")]
|
||||
pub wgpu_options: egui_wgpu::WgpuConfiguration,
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -372,6 +377,8 @@ impl Clone for NativeOptions {
|
||||
Self {
|
||||
icon_data: self.icon_data.clone(),
|
||||
event_loop_builder: None, // Skip any builder callbacks if cloning
|
||||
#[cfg(feature = "wgpu")]
|
||||
wgpu_options: self.wgpu_options.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
@@ -409,6 +416,8 @@ impl Default for NativeOptions {
|
||||
#[cfg(feature = "glow")]
|
||||
shader_version: None,
|
||||
centered: false,
|
||||
#[cfg(feature = "wgpu")]
|
||||
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -459,6 +468,10 @@ pub struct WebOptions {
|
||||
/// Default: [`WebGlContextOption::BestFirst`].
|
||||
#[cfg(feature = "glow")]
|
||||
pub webgl_context_option: WebGlContextOption,
|
||||
|
||||
/// Configures wgpu instance/device/adapter/surface creation and renderloop.
|
||||
#[cfg(feature = "wgpu")]
|
||||
pub wgpu_options: egui_wgpu::WgpuConfiguration,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@@ -467,8 +480,26 @@ impl Default for WebOptions {
|
||||
Self {
|
||||
follow_system_theme: true,
|
||||
default_theme: Theme::Dark,
|
||||
|
||||
#[cfg(feature = "glow")]
|
||||
webgl_context_option: WebGlContextOption::BestFirst,
|
||||
|
||||
#[cfg(feature = "wgpu")]
|
||||
wgpu_options: egui_wgpu::WgpuConfiguration {
|
||||
// WebGPU is not stable enough yet, use WebGL emulation
|
||||
backends: wgpu::Backends::GL,
|
||||
device_descriptor: wgpu::DeviceDescriptor {
|
||||
label: Some("egui wgpu device"),
|
||||
features: wgpu::Features::default(),
|
||||
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,
|
||||
..wgpu::Limits::downlevel_webgl2_defaults()
|
||||
},
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,7 +647,7 @@ mod wgpu_integration {
|
||||
/// a Resumed event. On Android this ensures that any graphics state is only
|
||||
/// initialized once the application has an associated `SurfaceView`.
|
||||
struct WgpuWinitRunning {
|
||||
painter: egui_wgpu::winit::Painter<'static>,
|
||||
painter: egui_wgpu::winit::Painter,
|
||||
integration: epi_integration::EpiIntegration,
|
||||
app: Box<dyn epi::App>,
|
||||
}
|
||||
@@ -723,23 +723,10 @@ mod wgpu_integration {
|
||||
storage: Option<Box<dyn epi::Storage>>,
|
||||
window: winit::window::Window,
|
||||
) {
|
||||
let mut limits = wgpu::Limits::downlevel_webgl2_defaults();
|
||||
if self.native_options.depth_buffer > 0 {
|
||||
// When using a depth buffer, we have to be able to create a texture large enough for the entire surface.
|
||||
limits.max_texture_dimension_2d = 8192;
|
||||
}
|
||||
|
||||
#[allow(unsafe_code, unused_mut, unused_unsafe)]
|
||||
let painter = unsafe {
|
||||
let mut painter = egui_wgpu::winit::Painter::new(
|
||||
wgpu::Backends::PRIMARY | wgpu::Backends::GL,
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features: wgpu::Features::default(),
|
||||
limits,
|
||||
},
|
||||
wgpu::PresentMode::Fifo,
|
||||
self.native_options.wgpu_options.clone(),
|
||||
self.native_options.multisampling.max(1) as _,
|
||||
self.native_options.depth_buffer,
|
||||
);
|
||||
|
||||
@@ -4,7 +4,7 @@ use wasm_bindgen::JsValue;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
use egui::{mutex::RwLock, Rgba};
|
||||
use egui_wgpu::{renderer::ScreenDescriptor, RenderState};
|
||||
use egui_wgpu::{renderer::ScreenDescriptor, RenderState, SurfaceErrorAction};
|
||||
|
||||
use crate::WebOptions;
|
||||
|
||||
@@ -14,9 +14,10 @@ pub(crate) struct WebPainterWgpu {
|
||||
canvas: HtmlCanvasElement,
|
||||
canvas_id: String,
|
||||
surface: wgpu::Surface,
|
||||
surface_size: [u32; 2],
|
||||
surface_configuration: wgpu::SurfaceConfiguration,
|
||||
limits: wgpu::Limits,
|
||||
render_state: Option<RenderState>,
|
||||
on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
|
||||
}
|
||||
|
||||
impl WebPainterWgpu {
|
||||
@@ -26,30 +27,27 @@ impl WebPainterWgpu {
|
||||
}
|
||||
|
||||
#[allow(unused)] // only used if `wgpu` is the only active feature.
|
||||
pub async fn new(canvas_id: &str, _options: &WebOptions) -> Result<Self, String> {
|
||||
tracing::debug!("Creating wgpu painter with WebGL backend…");
|
||||
pub async fn new(canvas_id: &str, options: &WebOptions) -> Result<Self, String> {
|
||||
tracing::debug!("Creating wgpu painter");
|
||||
|
||||
let canvas = super::canvas_element_or_die(canvas_id);
|
||||
let limits = wgpu::Limits::downlevel_webgl2_defaults(); // TODO(Wumpf): Expose to eframe user
|
||||
|
||||
// TODO(Wumpf): Should be able to switch between WebGL & WebGPU (only)
|
||||
let backends = wgpu::Backends::GL; //wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all);
|
||||
let instance = wgpu::Instance::new(backends);
|
||||
let instance = wgpu::Instance::new(options.wgpu_options.backends);
|
||||
let surface = instance.create_surface_from_canvas(&canvas);
|
||||
|
||||
let adapter =
|
||||
wgpu::util::initialize_adapter_from_env_or_default(&instance, backends, Some(&surface))
|
||||
.await
|
||||
.ok_or_else(|| "No suitable GPU adapters found on the system".to_owned())?;
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: options.wgpu_options.power_preference,
|
||||
force_fallback_adapter: false,
|
||||
compatible_surface: None,
|
||||
})
|
||||
.await
|
||||
.ok_or_else(|| "No suitable GPU adapters found on the system".to_owned())?;
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: Some("egui_webpainter"),
|
||||
features: wgpu::Features::empty(),
|
||||
limits: limits.clone(),
|
||||
},
|
||||
None, // No capture exposed so far - unclear how we can expose this in a browser environment (?)
|
||||
&options.wgpu_options.device_descriptor,
|
||||
None, // Capture doesn't work in the browser environment.
|
||||
)
|
||||
.await
|
||||
.map_err(|err| format!("Failed to find wgpu device: {}", err))?;
|
||||
@@ -65,6 +63,15 @@ impl WebPainterWgpu {
|
||||
renderer: Arc::new(RwLock::new(renderer)),
|
||||
};
|
||||
|
||||
let surface_configuration = wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: target_format,
|
||||
width: 0,
|
||||
height: 0,
|
||||
present_mode: options.wgpu_options.present_mode,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
};
|
||||
|
||||
tracing::debug!("wgpu painter initialized.");
|
||||
|
||||
Ok(Self {
|
||||
@@ -72,8 +79,9 @@ impl WebPainterWgpu {
|
||||
canvas_id: canvas_id.to_owned(),
|
||||
render_state: Some(render_state),
|
||||
surface,
|
||||
surface_size: [0, 0],
|
||||
limits,
|
||||
surface_configuration,
|
||||
limits: options.wgpu_options.device_descriptor.limits.clone(),
|
||||
on_surface_error: options.wgpu_options.on_surface_error.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -103,28 +111,30 @@ impl WebPainter for WebPainterWgpu {
|
||||
};
|
||||
|
||||
// Resize surface if needed
|
||||
let canvas_size = [self.canvas.width(), self.canvas.height()];
|
||||
if canvas_size != self.surface_size {
|
||||
self.surface.configure(
|
||||
&render_state.device,
|
||||
&wgpu::SurfaceConfiguration {
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||
format: render_state.target_format,
|
||||
width: canvas_size[0],
|
||||
height: canvas_size[1],
|
||||
present_mode: wgpu::PresentMode::Fifo,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
},
|
||||
);
|
||||
self.surface_size = canvas_size;
|
||||
let size_in_pixels = [self.canvas.width(), self.canvas.height()];
|
||||
if size_in_pixels[0] != self.surface_configuration.width
|
||||
|| size_in_pixels[1] != self.surface_configuration.height
|
||||
{
|
||||
self.surface_configuration.width = size_in_pixels[0];
|
||||
self.surface_configuration.height = size_in_pixels[1];
|
||||
self.surface
|
||||
.configure(&render_state.device, &self.surface_configuration);
|
||||
}
|
||||
|
||||
let frame = self.surface.get_current_texture().map_err(|err| {
|
||||
JsValue::from_str(&format!(
|
||||
"Failed to acquire next swap chain texture: {}",
|
||||
err
|
||||
))
|
||||
})?;
|
||||
let frame = match self.surface.get_current_texture() {
|
||||
Ok(frame) => frame,
|
||||
#[allow(clippy::single_match_else)]
|
||||
Err(e) => match (*self.on_surface_error)(e) {
|
||||
SurfaceErrorAction::RecreateSurface => {
|
||||
self.surface
|
||||
.configure(&render_state.device, &self.surface_configuration);
|
||||
return Ok(());
|
||||
}
|
||||
SurfaceErrorAction::SkipFrame => {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut encoder =
|
||||
render_state
|
||||
@@ -135,10 +145,9 @@ impl WebPainter for WebPainterWgpu {
|
||||
|
||||
// Upload all resources for the GPU.
|
||||
let screen_descriptor = ScreenDescriptor {
|
||||
size_in_pixels: canvas_size,
|
||||
size_in_pixels,
|
||||
pixels_per_point,
|
||||
};
|
||||
|
||||
{
|
||||
let mut renderer = render_state.renderer.write();
|
||||
for (id, image_delta) in &textures_delta.set {
|
||||
|
||||
Reference in New Issue
Block a user