diff --git a/Cargo.toml b/Cargo.toml index dfdb595c302..1569299ec11 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -232,6 +232,7 @@ iter_on_single_items = "warn" iter_over_hash_type = "warn" iter_without_into_iter = "warn" large_digit_groups = "warn" +large_futures = "warn" large_include_file = "warn" large_stack_arrays = "warn" large_stack_frames = "warn" @@ -329,6 +330,7 @@ unnecessary_semicolon = "warn" unnecessary_struct_initialization = "warn" unnecessary_wraps = "warn" unnested_or_patterns = "warn" +unused_async = "warn" unused_peekable = "warn" unused_rounding = "warn" unused_self = "warn" diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index fbd8ffa8736..dcfab445c61 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -28,9 +28,9 @@ workspace = true default = [ "accesskit", "default_fonts", - "glow", "wayland", # Required for Linux support (including CI!) "web_screen_reader", + "wgpu", "winit/default", "x11", ] @@ -52,7 +52,11 @@ android-native-activity = ["egui-winit/android-native-activity"] ## If you plan on specifying your own fonts you may disable this feature. default_fonts = ["egui/default_fonts"] -## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow). +## Enable [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/main/crates/egui_glow). +## +## There is generally no need to enable both the `wgpu` and `glow` features, +## but if you do you can pick the renderer to use with [`NativeOptions::renderer`] +## and `WebOptions::renderer`. glow = ["dep:egui_glow", "dep:glow", "dep:glutin-winit", "dep:glutin"] ## Enable saving app state to disk. @@ -74,9 +78,15 @@ wayland = [ ## For other platforms, use the `accesskit` feature instead. web_screen_reader = ["web-sys/SpeechSynthesis", "web-sys/SpeechSynthesisUtterance"] -## Use [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu)). +## Enable [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/main/crates/egui-wgpu)). +## +## There is generally no need to enable both the `wgpu` and `glow` features, +## but if you do you can pick the renderer to use with [`NativeOptions::renderer`] +## and `WebOptions::renderer`. ## -## This overrides the `glow` feature. +## Switching from `wgpu (the default)` to `glow` can significantly reduce your binary size +## (including the .wasm of a web app). +## See for more details. ## ## By default, eframe will prefer WebGPU over WebGL, but ## you can configure this at run-time with [`NativeOptions::wgpu_options`]. diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 5e6adb1b446..d6c9a10b84c 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -471,6 +471,10 @@ impl Default for NativeOptions { /// Options when using `eframe` in a web page. #[cfg(target_arch = "wasm32")] pub struct WebOptions { + /// What rendering backend to use. + #[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))] + pub renderer: Renderer, + /// Sets the number of bits in the depth buffer. /// /// `egui` doesn't need the depth buffer, so the default value is 0. @@ -519,6 +523,9 @@ pub struct WebOptions { impl Default for WebOptions { fn default() -> Self { Self { + #[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))] + renderer: Renderer::default(), + depth_buffer: 0, #[cfg(feature = "glow")] @@ -592,8 +599,8 @@ impl Default for Renderer { #[cfg(feature = "wgpu_no_default_features")] return Self::Wgpu; - // By default, only the `glow` feature is enabled, so if the user added `wgpu` to the feature list - // they probably wanted to use wgpu: + // It's weird that the user has enabled both glow and wgpu, + // but let's pick the better of the two (wgpu): #[cfg(feature = "glow")] #[cfg(feature = "wgpu_no_default_features")] return Self::Wgpu; diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index d8c20920513..8a10a90efba 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -1,15 +1,15 @@ use egui::{TexturesDelta, UserData, ViewportCommand}; -use crate::{App, epi}; +use crate::{App, epi, web::web_painter::WebPainter}; -use super::{NeedRepaint, now_sec, text_agent::TextAgent, web_painter::WebPainter as _}; +use super::{NeedRepaint, now_sec, text_agent::TextAgent}; pub struct AppRunner { #[allow(dead_code, clippy::allow_attributes)] pub(crate) web_options: crate::WebOptions, pub(crate) frame: epi::Frame, egui_ctx: egui::Context, - painter: super::ActiveWebPainter, + painter: Box, pub(crate) input: super::WebInput, app: Box, pub(crate) needs_repaint: std::sync::Arc, @@ -34,6 +34,10 @@ impl Drop for AppRunner { impl AppRunner { /// # Errors /// Failure to initialize WebGL renderer, or failure to create app. + #[cfg_attr( + not(feature = "wgpu_no_default_features"), + expect(clippy::unused_async) + )] pub async fn new( canvas: web_sys::HtmlCanvasElement, web_options: crate::WebOptions, @@ -41,7 +45,41 @@ impl AppRunner { text_agent: TextAgent, ) -> Result { let egui_ctx = egui::Context::default(); - let painter = super::ActiveWebPainter::new(egui_ctx.clone(), canvas, &web_options).await?; + + #[allow(clippy::allow_attributes, unused_assignments)] + #[cfg(feature = "glow")] + let mut gl = None; + + #[allow(clippy::allow_attributes, unused_assignments)] + #[cfg(feature = "wgpu_no_default_features")] + let mut wgpu_render_state = None; + + let painter = match web_options.renderer { + #[cfg(feature = "glow")] + epi::Renderer::Glow => { + log::debug!("Using the glow renderer"); + let painter = super::web_painter_glow::WebPainterGlow::new( + egui_ctx.clone(), + canvas, + &web_options, + )?; + gl = Some(painter.gl().clone()); + Box::new(painter) as Box + } + + #[cfg(feature = "wgpu_no_default_features")] + epi::Renderer::Wgpu => { + log::debug!("Using the wgpu renderer"); + let painter = super::web_painter_wgpu::WebPainterWgpu::new( + egui_ctx.clone(), + canvas, + &web_options, + ) + .await?; + wgpu_render_state = painter.render_state(); + Box::new(painter) as Box + } + }; let info = epi::IntegrationInfo { web_info: epi::WebInfo { @@ -79,15 +117,13 @@ impl AppRunner { storage: Some(&storage), #[cfg(feature = "glow")] - gl: Some(painter.gl().clone()), + gl: gl.clone(), #[cfg(feature = "glow")] get_proc_address: None, - #[cfg(all(feature = "wgpu_no_default_features", not(feature = "glow")))] - wgpu_render_state: painter.render_state(), - #[cfg(all(feature = "wgpu_no_default_features", feature = "glow"))] - wgpu_render_state: None, + #[cfg(feature = "wgpu_no_default_features")] + wgpu_render_state: wgpu_render_state.clone(), }; let app = app_creator(&cc).map_err(|err| err.to_string())?; @@ -96,12 +132,10 @@ impl AppRunner { storage: Some(Box::new(storage)), #[cfg(feature = "glow")] - gl: Some(painter.gl().clone()), + gl, - #[cfg(all(feature = "wgpu_no_default_features", not(feature = "glow")))] - wgpu_render_state: painter.render_state(), - #[cfg(all(feature = "wgpu_no_default_features", feature = "glow"))] - wgpu_render_state: None, + #[cfg(feature = "wgpu_no_default_features")] + wgpu_render_state, }; let needs_repaint: std::sync::Arc = diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index 1cfdbb3f30e..ac4c637db3d 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -30,13 +30,9 @@ mod web_painter; #[cfg(feature = "glow")] mod web_painter_glow; -#[cfg(feature = "glow")] -pub(crate) type ActiveWebPainter = web_painter_glow::WebPainterGlow; #[cfg(feature = "wgpu_no_default_features")] mod web_painter_wgpu; -#[cfg(all(feature = "wgpu_no_default_features", not(feature = "glow")))] -pub(crate) type ActiveWebPainter = web_painter_wgpu::WebPainterWgpu; pub use backend::*; diff --git a/crates/eframe/src/web/web_painter_glow.rs b/crates/eframe/src/web/web_painter_glow.rs index ca6e11bf555..e2fc4a6f2bf 100644 --- a/crates/eframe/src/web/web_painter_glow.rs +++ b/crates/eframe/src/web/web_painter_glow.rs @@ -20,7 +20,7 @@ impl WebPainterGlow { self.painter.gl() } - pub async fn new( + pub fn new( _ctx: egui::Context, canvas: HtmlCanvasElement, options: &WebOptions, diff --git a/crates/eframe/src/web/web_painter_wgpu.rs b/crates/eframe/src/web/web_painter_wgpu.rs index 9faba9dd789..efecd12ee4b 100644 --- a/crates/eframe/src/web/web_painter_wgpu.rs +++ b/crates/eframe/src/web/web_painter_wgpu.rs @@ -1,13 +1,15 @@ use std::sync::Arc; -use super::web_painter::WebPainter; -use crate::WebOptions; use egui::{Event, UserData, ViewportId}; -use egui_wgpu::capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel}; -use egui_wgpu::{RenderState, SurfaceErrorAction}; +use egui_wgpu::{ + RenderState, SurfaceErrorAction, + capture::{CaptureReceiver, CaptureSender, CaptureState, capture_channel}, +}; use wasm_bindgen::JsValue; use web_sys::HtmlCanvasElement; +use super::web_painter::WebPainter; + pub(crate) struct WebPainterWgpu { canvas: HtmlCanvasElement, surface: wgpu::Surface<'static>, @@ -23,7 +25,6 @@ pub(crate) struct WebPainterWgpu { } impl WebPainterWgpu { - #[expect(unused)] // only used if `wgpu` is the only active feature. pub fn render_state(&self) -> Option { self.render_state.clone() } @@ -55,11 +56,10 @@ impl WebPainterWgpu { }) } - #[expect(unused)] // only used if `wgpu` is the only active feature. pub async fn new( ctx: egui::Context, canvas: web_sys::HtmlCanvasElement, - options: &WebOptions, + options: &crate::WebOptions, ) -> Result { log::debug!("Creating wgpu painter"); diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index 8eb441ac56a..7cde4638322 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -23,7 +23,7 @@ crate-type = ["cdylib", "rlib"] [features] -default = ["glow", "persistence"] +default = ["wgpu", "persistence"] # image_viewer adds about 0.9 MB of WASM web_app = ["http", "persistence"] diff --git a/scripts/build_demo_web.sh b/scripts/build_demo_web.sh index b6eb7197a73..4ba3846dba7 100755 --- a/scripts/build_demo_web.sh +++ b/scripts/build_demo_web.sh @@ -13,13 +13,13 @@ OPEN=false OPTIMIZE=false BUILD=debug BUILD_FLAGS="" -WGPU=false +GLOW=false WASM_OPT_FLAGS="-O2 --fast-math" while test $# -gt 0; do case "$1" in -h|--help) - echo "build_demo_web.sh [--release] [--wgpu] [--open]" + echo "build_demo_web.sh [--release] [--glow] [--open]" echo "" echo " -g: Keep debug symbols even with --release." echo " These are useful profiling and size trimming." @@ -29,9 +29,7 @@ while test $# -gt 0; do echo " --release: Build with --release, and then run wasm-opt." echo " NOTE: --release also removes debug symbols, unless you also use -g." echo "" - echo " --wgpu: Build a binary using wgpu instead of glow/webgl." - echo " The resulting binary will automatically use WebGPU if available and" - echo " fall back to a WebGL emulation layer otherwise." + echo " --glow: Build a binary using glow instead of wgpu." exit 0 ;; @@ -52,9 +50,9 @@ while test $# -gt 0; do BUILD_FLAGS="--release" ;; - --wgpu) + --glow) shift - WGPU=true + GLOW=true ;; *) @@ -66,10 +64,10 @@ done OUT_FILE_NAME="egui_demo_app" -if [[ "${WGPU}" == true ]]; then - FEATURES="${FEATURES},wgpu" -else +if [[ "${GLOW}" == true ]]; then FEATURES="${FEATURES},glow" +else + FEATURES="${FEATURES},wgpu" fi FINAL_WASM_PATH=web_demo/${OUT_FILE_NAME}_bg.wasm