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:
parent
f71cbc2475
commit
4c82519fb8
7 changed files with 164 additions and 91 deletions
|
@ -15,6 +15,7 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C
|
||||||
* Web: you can access your application from JS using `AppRunner::app_mut`. See `crates/egui_demo_app/src/lib.rs`.
|
* Web: you can access your application from JS using `AppRunner::app_mut`. See `crates/egui_demo_app/src/lib.rs`.
|
||||||
* Web: You can now use WebGL on top of `wgpu` by enabling the `wgpu` feature (and disabling `glow` via disabling default features) ([#2107](https://github.com/emilk/egui/pull/2107)).
|
* Web: You can now use WebGL on top of `wgpu` by enabling the `wgpu` feature (and disabling `glow` via disabling default features) ([#2107](https://github.com/emilk/egui/pull/2107)).
|
||||||
* Web: Add `WebInfo::user_agent` ([#2202](https://github.com/emilk/egui/pull/2202)).
|
* Web: Add `WebInfo::user_agent` ([#2202](https://github.com/emilk/egui/pull/2202)).
|
||||||
|
* * Wgpu device/adapter/surface creation has now various configuration options exposed via `NativeOptions/WebOptions::wgpu_options` ([#2207](https://github.com/emilk/egui/pull/2207)).
|
||||||
|
|
||||||
|
|
||||||
## 0.19.0 - 2022-08-20
|
## 0.19.0 - 2022-08-20
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::any::Any;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use crate::native::run::RequestRepaintEvent;
|
pub use crate::native::run::RequestRepaintEvent;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub use winit::event_loop::EventLoopBuilder;
|
pub use winit::event_loop::EventLoopBuilder;
|
||||||
|
|
||||||
|
@ -364,6 +365,10 @@ pub struct NativeOptions {
|
||||||
///
|
///
|
||||||
/// Wayland desktop currently not supported.
|
/// Wayland desktop currently not supported.
|
||||||
pub centered: bool,
|
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"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
@ -372,6 +377,8 @@ impl Clone for NativeOptions {
|
||||||
Self {
|
Self {
|
||||||
icon_data: self.icon_data.clone(),
|
icon_data: self.icon_data.clone(),
|
||||||
event_loop_builder: None, // Skip any builder callbacks if cloning
|
event_loop_builder: None, // Skip any builder callbacks if cloning
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
wgpu_options: self.wgpu_options.clone(),
|
||||||
..*self
|
..*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,6 +416,8 @@ impl Default for NativeOptions {
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
shader_version: None,
|
shader_version: None,
|
||||||
centered: false,
|
centered: false,
|
||||||
|
#[cfg(feature = "wgpu")]
|
||||||
|
wgpu_options: egui_wgpu::WgpuConfiguration::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -459,6 +468,10 @@ pub struct WebOptions {
|
||||||
/// Default: [`WebGlContextOption::BestFirst`].
|
/// Default: [`WebGlContextOption::BestFirst`].
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
pub webgl_context_option: WebGlContextOption,
|
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")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
@ -467,8 +480,26 @@ impl Default for WebOptions {
|
||||||
Self {
|
Self {
|
||||||
follow_system_theme: true,
|
follow_system_theme: true,
|
||||||
default_theme: Theme::Dark,
|
default_theme: Theme::Dark,
|
||||||
|
|
||||||
#[cfg(feature = "glow")]
|
#[cfg(feature = "glow")]
|
||||||
webgl_context_option: WebGlContextOption::BestFirst,
|
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
|
/// a Resumed event. On Android this ensures that any graphics state is only
|
||||||
/// initialized once the application has an associated `SurfaceView`.
|
/// initialized once the application has an associated `SurfaceView`.
|
||||||
struct WgpuWinitRunning {
|
struct WgpuWinitRunning {
|
||||||
painter: egui_wgpu::winit::Painter<'static>,
|
painter: egui_wgpu::winit::Painter,
|
||||||
integration: epi_integration::EpiIntegration,
|
integration: epi_integration::EpiIntegration,
|
||||||
app: Box<dyn epi::App>,
|
app: Box<dyn epi::App>,
|
||||||
}
|
}
|
||||||
|
@ -723,23 +723,10 @@ mod wgpu_integration {
|
||||||
storage: Option<Box<dyn epi::Storage>>,
|
storage: Option<Box<dyn epi::Storage>>,
|
||||||
window: winit::window::Window,
|
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)]
|
#[allow(unsafe_code, unused_mut, unused_unsafe)]
|
||||||
let painter = unsafe {
|
let painter = unsafe {
|
||||||
let mut painter = egui_wgpu::winit::Painter::new(
|
let mut painter = egui_wgpu::winit::Painter::new(
|
||||||
wgpu::Backends::PRIMARY | wgpu::Backends::GL,
|
self.native_options.wgpu_options.clone(),
|
||||||
wgpu::PowerPreference::HighPerformance,
|
|
||||||
wgpu::DeviceDescriptor {
|
|
||||||
label: None,
|
|
||||||
features: wgpu::Features::default(),
|
|
||||||
limits,
|
|
||||||
},
|
|
||||||
wgpu::PresentMode::Fifo,
|
|
||||||
self.native_options.multisampling.max(1) as _,
|
self.native_options.multisampling.max(1) as _,
|
||||||
self.native_options.depth_buffer,
|
self.native_options.depth_buffer,
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@ use wasm_bindgen::JsValue;
|
||||||
use web_sys::HtmlCanvasElement;
|
use web_sys::HtmlCanvasElement;
|
||||||
|
|
||||||
use egui::{mutex::RwLock, Rgba};
|
use egui::{mutex::RwLock, Rgba};
|
||||||
use egui_wgpu::{renderer::ScreenDescriptor, RenderState};
|
use egui_wgpu::{renderer::ScreenDescriptor, RenderState, SurfaceErrorAction};
|
||||||
|
|
||||||
use crate::WebOptions;
|
use crate::WebOptions;
|
||||||
|
|
||||||
|
@ -14,9 +14,10 @@ pub(crate) struct WebPainterWgpu {
|
||||||
canvas: HtmlCanvasElement,
|
canvas: HtmlCanvasElement,
|
||||||
canvas_id: String,
|
canvas_id: String,
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface,
|
||||||
surface_size: [u32; 2],
|
surface_configuration: wgpu::SurfaceConfiguration,
|
||||||
limits: wgpu::Limits,
|
limits: wgpu::Limits,
|
||||||
render_state: Option<RenderState>,
|
render_state: Option<RenderState>,
|
||||||
|
on_surface_error: Arc<dyn Fn(wgpu::SurfaceError) -> SurfaceErrorAction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebPainterWgpu {
|
impl WebPainterWgpu {
|
||||||
|
@ -26,30 +27,27 @@ impl WebPainterWgpu {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)] // only used if `wgpu` is the only active feature.
|
#[allow(unused)] // only used if `wgpu` is the only active feature.
|
||||||
pub async fn new(canvas_id: &str, _options: &WebOptions) -> Result<Self, String> {
|
pub async fn new(canvas_id: &str, options: &WebOptions) -> Result<Self, String> {
|
||||||
tracing::debug!("Creating wgpu painter with WebGL backend…");
|
tracing::debug!("Creating wgpu painter");
|
||||||
|
|
||||||
let canvas = super::canvas_element_or_die(canvas_id);
|
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 instance = wgpu::Instance::new(options.wgpu_options.backends);
|
||||||
let backends = wgpu::Backends::GL; //wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all);
|
|
||||||
let instance = wgpu::Instance::new(backends);
|
|
||||||
let surface = instance.create_surface_from_canvas(&canvas);
|
let surface = instance.create_surface_from_canvas(&canvas);
|
||||||
|
|
||||||
let adapter =
|
let adapter = instance
|
||||||
wgpu::util::initialize_adapter_from_env_or_default(&instance, backends, Some(&surface))
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
.await
|
power_preference: options.wgpu_options.power_preference,
|
||||||
.ok_or_else(|| "No suitable GPU adapters found on the system".to_owned())?;
|
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
|
let (device, queue) = adapter
|
||||||
.request_device(
|
.request_device(
|
||||||
&wgpu::DeviceDescriptor {
|
&options.wgpu_options.device_descriptor,
|
||||||
label: Some("egui_webpainter"),
|
None, // Capture doesn't work in the browser environment.
|
||||||
features: wgpu::Features::empty(),
|
|
||||||
limits: limits.clone(),
|
|
||||||
},
|
|
||||||
None, // No capture exposed so far - unclear how we can expose this in a browser environment (?)
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format!("Failed to find wgpu device: {}", err))?;
|
.map_err(|err| format!("Failed to find wgpu device: {}", err))?;
|
||||||
|
@ -65,6 +63,15 @@ impl WebPainterWgpu {
|
||||||
renderer: Arc::new(RwLock::new(renderer)),
|
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.");
|
tracing::debug!("wgpu painter initialized.");
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -72,8 +79,9 @@ impl WebPainterWgpu {
|
||||||
canvas_id: canvas_id.to_owned(),
|
canvas_id: canvas_id.to_owned(),
|
||||||
render_state: Some(render_state),
|
render_state: Some(render_state),
|
||||||
surface,
|
surface,
|
||||||
surface_size: [0, 0],
|
surface_configuration,
|
||||||
limits,
|
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
|
// Resize surface if needed
|
||||||
let canvas_size = [self.canvas.width(), self.canvas.height()];
|
let size_in_pixels = [self.canvas.width(), self.canvas.height()];
|
||||||
if canvas_size != self.surface_size {
|
if size_in_pixels[0] != self.surface_configuration.width
|
||||||
self.surface.configure(
|
|| size_in_pixels[1] != self.surface_configuration.height
|
||||||
&render_state.device,
|
{
|
||||||
&wgpu::SurfaceConfiguration {
|
self.surface_configuration.width = size_in_pixels[0];
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
self.surface_configuration.height = size_in_pixels[1];
|
||||||
format: render_state.target_format,
|
self.surface
|
||||||
width: canvas_size[0],
|
.configure(&render_state.device, &self.surface_configuration);
|
||||||
height: canvas_size[1],
|
|
||||||
present_mode: wgpu::PresentMode::Fifo,
|
|
||||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.surface_size = canvas_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = self.surface.get_current_texture().map_err(|err| {
|
let frame = match self.surface.get_current_texture() {
|
||||||
JsValue::from_str(&format!(
|
Ok(frame) => frame,
|
||||||
"Failed to acquire next swap chain texture: {}",
|
#[allow(clippy::single_match_else)]
|
||||||
err
|
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 =
|
let mut encoder =
|
||||||
render_state
|
render_state
|
||||||
|
@ -135,10 +145,9 @@ impl WebPainter for WebPainterWgpu {
|
||||||
|
|
||||||
// Upload all resources for the GPU.
|
// Upload all resources for the GPU.
|
||||||
let screen_descriptor = ScreenDescriptor {
|
let screen_descriptor = ScreenDescriptor {
|
||||||
size_in_pixels: canvas_size,
|
size_in_pixels,
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut renderer = render_state.renderer.write();
|
let mut renderer = render_state.renderer.write();
|
||||||
for (id, image_delta) in &textures_delta.set {
|
for (id, image_delta) in &textures_delta.set {
|
||||||
|
|
|
@ -11,6 +11,7 @@ All notable changes to the `egui-wgpu` integration will be noted in this file.
|
||||||
* `PrepareCallback` now passes `wgpu::CommandEncoder` ([#2136](https://github.com/emilk/egui/pull/2136))
|
* `PrepareCallback` now passes `wgpu::CommandEncoder` ([#2136](https://github.com/emilk/egui/pull/2136))
|
||||||
* Only a single vertex & index buffer is now created and resized when necessary (previously, vertex/index buffers were allocated for every mesh) ([#2148](https://github.com/emilk/egui/pull/2148)).
|
* Only a single vertex & index buffer is now created and resized when necessary (previously, vertex/index buffers were allocated for every mesh) ([#2148](https://github.com/emilk/egui/pull/2148)).
|
||||||
* `Renderer::update_texture` no longer creates a new `wgpu::Sampler` with every new texture ([#2198](https://github.com/emilk/egui/pull/2198))
|
* `Renderer::update_texture` no longer creates a new `wgpu::Sampler` with every new texture ([#2198](https://github.com/emilk/egui/pull/2198))
|
||||||
|
* `Painter`'s instance/device/adapter/surface creation is now configurable via `WgpuConfiguration` ([#2207](https://github.com/emilk/egui/pull/2207))
|
||||||
|
|
||||||
## 0.19.0 - 2022-08-20
|
## 0.19.0 - 2022-08-20
|
||||||
* Enables deferred render + surface state initialization for Android ([#1634](https://github.com/emilk/egui/pull/1634)).
|
* Enables deferred render + surface state initialization for Android ([#1634](https://github.com/emilk/egui/pull/1634)).
|
||||||
|
|
|
@ -20,8 +20,7 @@ pub mod winit;
|
||||||
use egui::mutex::RwLock;
|
use egui::mutex::RwLock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Access to the render state for egui, which can be useful in combination with
|
/// Access to the render state for egui.
|
||||||
/// [`egui::PaintCallback`]s for custom rendering using WGPU.
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderState {
|
pub struct RenderState {
|
||||||
pub device: Arc<wgpu::Device>,
|
pub device: Arc<wgpu::Device>,
|
||||||
|
@ -30,6 +29,60 @@ pub struct RenderState {
|
||||||
pub renderer: Arc<RwLock<Renderer>>,
|
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
|
/// Find the framebuffer format that egui prefers
|
||||||
pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
|
pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
|
||||||
for &format in formats {
|
for &format in formats {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use egui::mutex::RwLock;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use wgpu::{Adapter, Instance, Surface};
|
use wgpu::{Adapter, Instance, Surface};
|
||||||
|
|
||||||
use crate::{renderer, RenderState, Renderer};
|
use crate::{renderer, RenderState, Renderer, SurfaceErrorAction, WgpuConfiguration};
|
||||||
|
|
||||||
struct SurfaceState {
|
struct SurfaceState {
|
||||||
surface: Surface,
|
surface: Surface,
|
||||||
|
@ -15,10 +15,8 @@ struct SurfaceState {
|
||||||
/// Everything you need to paint egui with [`wgpu`] on [`winit`].
|
/// Everything you need to paint egui with [`wgpu`] on [`winit`].
|
||||||
///
|
///
|
||||||
/// Alternatively you can use [`crate::renderer`] directly.
|
/// Alternatively you can use [`crate::renderer`] directly.
|
||||||
pub struct Painter<'a> {
|
pub struct Painter {
|
||||||
power_preference: wgpu::PowerPreference,
|
configuration: WgpuConfiguration,
|
||||||
device_descriptor: wgpu::DeviceDescriptor<'a>,
|
|
||||||
present_mode: wgpu::PresentMode,
|
|
||||||
msaa_samples: u32,
|
msaa_samples: u32,
|
||||||
depth_format: Option<wgpu::TextureFormat>,
|
depth_format: Option<wgpu::TextureFormat>,
|
||||||
depth_texture_view: Option<wgpu::TextureView>,
|
depth_texture_view: Option<wgpu::TextureView>,
|
||||||
|
@ -29,7 +27,7 @@ pub struct Painter<'a> {
|
||||||
surface_state: Option<SurfaceState>,
|
surface_state: Option<SurfaceState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Painter<'a> {
|
impl Painter {
|
||||||
/// Manages [`wgpu`] state, including surface state, required to render egui.
|
/// Manages [`wgpu`] state, including surface state, required to render egui.
|
||||||
///
|
///
|
||||||
/// Only the [`wgpu::Instance`] is initialized here. Device selection and the initialization
|
/// 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
|
/// [`set_window()`](Self::set_window) once you have
|
||||||
/// a [`winit::window::Window`] with a valid `.raw_window_handle()`
|
/// a [`winit::window::Window`] with a valid `.raw_window_handle()`
|
||||||
/// associated.
|
/// associated.
|
||||||
pub fn new(
|
pub fn new(configuration: WgpuConfiguration, msaa_samples: u32, depth_bits: u8) -> Self {
|
||||||
backends: wgpu::Backends,
|
let instance = wgpu::Instance::new(configuration.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);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
power_preference,
|
configuration,
|
||||||
device_descriptor,
|
|
||||||
present_mode,
|
|
||||||
msaa_samples,
|
msaa_samples,
|
||||||
depth_format: (depth_bits > 0).then(|| wgpu::TextureFormat::Depth32Float),
|
depth_format: (depth_bits > 0).then(|| wgpu::TextureFormat::Depth32Float),
|
||||||
depth_texture_view: None,
|
depth_texture_view: None,
|
||||||
|
@ -80,7 +69,8 @@ impl<'a> Painter<'a> {
|
||||||
target_format: wgpu::TextureFormat,
|
target_format: wgpu::TextureFormat,
|
||||||
) -> RenderState {
|
) -> RenderState {
|
||||||
let (device, queue) =
|
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);
|
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) {
|
fn ensure_render_state_for_surface(&mut self, surface: &Surface) {
|
||||||
self.adapter.get_or_insert_with(|| {
|
self.adapter.get_or_insert_with(|| {
|
||||||
pollster::block_on(self.instance.request_adapter(&wgpu::RequestAdapterOptions {
|
pollster::block_on(self.instance.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
power_preference: self.power_preference,
|
power_preference: self.configuration.power_preference,
|
||||||
compatible_surface: Some(surface),
|
compatible_surface: Some(surface),
|
||||||
force_fallback_adapter: false,
|
force_fallback_adapter: false,
|
||||||
}))
|
}))
|
||||||
|
@ -130,7 +120,7 @@ impl<'a> Painter<'a> {
|
||||||
format,
|
format,
|
||||||
width: width_in_pixels,
|
width: width_in_pixels,
|
||||||
height: height_in_pixels,
|
height: height_in_pixels,
|
||||||
present_mode: self.present_mode,
|
present_mode: self.configuration.present_mode,
|
||||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -245,19 +235,20 @@ impl<'a> Painter<'a> {
|
||||||
Some(rs) => rs,
|
Some(rs) => rs,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
let (width, height) = (surface_state.width, surface_state.height);
|
||||||
|
|
||||||
let output_frame = match surface_state.surface.get_current_texture() {
|
let output_frame = match surface_state.surface.get_current_texture() {
|
||||||
Ok(frame) => frame,
|
Ok(frame) => frame,
|
||||||
Err(wgpu::SurfaceError::Outdated) => {
|
#[allow(clippy::single_match_else)]
|
||||||
// This error occurs when the app is minimized on Windows.
|
Err(e) => match (*self.configuration.on_surface_error)(e) {
|
||||||
// Silently return here to prevent spamming the console with:
|
SurfaceErrorAction::RecreateSurface => {
|
||||||
// "The underlying surface has changed, and therefore the swap chain must be updated"
|
self.configure_surface(width, height);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
SurfaceErrorAction::SkipFrame => {
|
||||||
tracing::warn!("Dropped frame with error: {e}");
|
return;
|
||||||
return;
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut encoder =
|
let mut encoder =
|
||||||
|
@ -269,7 +260,7 @@ impl<'a> Painter<'a> {
|
||||||
|
|
||||||
// Upload all resources for the GPU.
|
// Upload all resources for the GPU.
|
||||||
let screen_descriptor = renderer::ScreenDescriptor {
|
let screen_descriptor = renderer::ScreenDescriptor {
|
||||||
size_in_pixels: [surface_state.width, surface_state.height],
|
size_in_pixels: [width, height],
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue