1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 14:49:06 -04:00

Make wgpu the default renderer for eframe and egui.rs (#7615)

* Closes https://github.com/emilk/egui/issues/5889

See the above issue for motivation.

To use glow instead, disable the default features of `eframe` and opt-in
to `glow`.

This also changes egui.rs to use wgpu, which means WebGPU when
available, and WebGL otherwise
This commit is contained in:
Emil Ernerfeldt
2025-11-13 11:16:23 +01:00
committed by GitHub
parent 51b0d0e4b9
commit 9ef610e16b
9 changed files with 90 additions and 43 deletions

View File

@@ -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"

View File

@@ -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)).
##
## This overrides the `glow` feature.
## 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`.
##
## Switching from `wgpu (the default)` to `glow` can significantly reduce your binary size
## (including the .wasm of a web app).
## See <https://github.com/emilk/egui/issues/5889> for more details.
##
## By default, eframe will prefer WebGPU over WebGL, but
## you can configure this at run-time with [`NativeOptions::wgpu_options`].

View File

@@ -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;

View File

@@ -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<dyn WebPainter>,
pub(crate) input: super::WebInput,
app: Box<dyn epi::App>,
pub(crate) needs_repaint: std::sync::Arc<NeedRepaint>,
@@ -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<Self, String> {
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<dyn WebPainter>
}
#[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<dyn WebPainter>
}
};
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<NeedRepaint> =

View File

@@ -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::*;

View File

@@ -20,7 +20,7 @@ impl WebPainterGlow {
self.painter.gl()
}
pub async fn new(
pub fn new(
_ctx: egui::Context,
canvas: HtmlCanvasElement,
options: &WebOptions,

View File

@@ -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<RenderState> {
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<Self, String> {
log::debug!("Creating wgpu painter");

View File

@@ -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"]

View File

@@ -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