From c13418fe7faba078e7789e2419ddedfdfd6f89b5 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jan 2024 18:17:34 +0100 Subject: [PATCH 01/12] allow forcing the graphics backend on the web with a generously parsed string --- crates/re_renderer/src/config.rs | 66 ++++++++++++++++++++++++++++++++ crates/re_viewer/src/lib.rs | 25 +++++++++++- crates/re_viewer/src/native.rs | 4 +- crates/re_viewer/src/web.rs | 4 +- web_viewer/index.html | 8 +++- 5 files changed, 101 insertions(+), 6 deletions(-) diff --git a/crates/re_renderer/src/config.rs b/crates/re_renderer/src/config.rs index e2408bce6b1c..79534a5d9e1e 100644 --- a/crates/re_renderer/src/config.rs +++ b/crates/re_renderer/src/config.rs @@ -230,3 +230,69 @@ pub fn supported_backends() -> wgpu::Backends { wgpu::Backends::GL | wgpu::Backends::BROWSER_WEBGPU } } + +/// Generous parsing of a graphics backend string. +pub fn parse_graphics_backend(backend: &str) -> Option { + match backend.to_lowercase().as_str() { + // "vulcan" is a common typo that we just swallow. We know what you mean ;) + "vulcan" | "vulkan" | "vk" => Some(wgpu::Backend::Vulkan), + + "metal" | "apple" | "mtl" => Some(wgpu::Backend::Metal), + + "dx12" | "dx" | "d3d" | "d3d12" | "directx" => Some(wgpu::Backend::Dx12), + + // We don't want to lie - e.g. `webgl1` should not work! + // This means that `gles`/`gles3` stretches it a bit, but it's still close enough. + // Similarly, we accept both `webgl` & `opengl` on each desktop & web. + // This is a bit dubious but also too much hassle to forbid. + "webgl2" | "webgl" | "opengl" | "gles" | "gles3" | "gl" => Some(wgpu::Backend::Gl), + + "browserwebgpu" | "webgpu" => Some(wgpu::Backend::BrowserWebGpu), + + _ => None, + } +} + +/// Validates that the given backend is applicable for the current build. +/// +/// This is meant as a sanity check of first resort. +/// There are still many other reasons why a backend may not work on a given platform/build combination. +pub fn validate_graphics_backend_applicability(backend: wgpu::Backend) -> Result<(), &'static str> { + match backend { + wgpu::Backend::Empty => { + // This should never happen. + return Err("Cannot run with empty backend."); + } + wgpu::Backend::Vulkan => { + // Through emulation and build configs Vulkan may work everywhere except the web. + if cfg!(target_arch = "wasm32") { + return Err("Can only run with WebGL or WebGPU on the web."); + } + } + wgpu::Backend::Metal => { + if cfg!(target_arch = "wasm32") { + return Err("Can only run with WebGL or WebGPU on the web."); + } + if cfg!(target_os = "linux") || cfg!(target_os = "windows") { + return Err("Cannot run with DX12 backend on Linux & Windows."); + } + } + wgpu::Backend::Dx12 => { + if cfg!(target_arch = "wasm32") { + return Err("Can only run with WebGL or WebGPU on the web."); + } + if cfg!(target_os = "linux") || cfg!(target_os = "mac") { + return Err("Cannot run with DX12 backend on Linux & Mac."); + } + } + wgpu::Backend::Gl => { + // Through emulation and build configs GL may work everywhere. + } + wgpu::Backend::BrowserWebGpu => { + if !cfg!(target_arch = "wasm32") { + return Err("Cannot run with WebGPU backend on native application."); + } + } + } + Ok(()) +} diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index 70a283e58400..c36161be26a8 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -129,8 +129,29 @@ impl AppEnvironment { // --------------------------------------------------------------------------- -pub(crate) fn wgpu_options() -> egui_wgpu::WgpuConfiguration { +fn supported_graphics_backends(force_backend: Option) -> wgpu::Backends { + if let Some(force_backend) = force_backend { + if let Some(backend) = re_renderer::config::parse_graphics_backend(&force_backend) { + if let Err(err) = re_renderer::config::validate_graphics_backend_applicability(backend) + { + re_log::error!("Failed to force rendering backend parsed from {force_backend:?}: {err}.\nWill use default backend instead."); + re_renderer::config::supported_backends() + } else { + re_log::info!("Forcing graphics backend to {backend:?}."); + backend.into() + } + } else { + re_log::error!("Failed to parse rendering backend string {force_backend:?}. Will use default backend."); + re_renderer::config::supported_backends() + } + } else { + re_renderer::config::supported_backends() + } +} + +pub(crate) fn wgpu_options(force_backend: Option) -> egui_wgpu::WgpuConfiguration { re_tracing::profile_function!(); + egui_wgpu::WgpuConfiguration { // When running wgpu on native debug builds, we want some extra control over how // and when a poisoned surface gets recreated. @@ -150,7 +171,7 @@ pub(crate) fn wgpu_options() -> egui_wgpu::WgpuConfiguration { egui_wgpu::SurfaceErrorAction::SkipFrame } }), - supported_backends: re_renderer::config::supported_backends(), + supported_backends: supported_graphics_backends(force_backend), device_descriptor: std::sync::Arc::new(|adapter| re_renderer::config::DeviceCaps::from_adapter(adapter).device_descriptor()), ..Default::default() } diff --git a/crates/re_viewer/src/native.rs b/crates/re_viewer/src/native.rs index 42cff5bf7fc3..a8ab576def97 100644 --- a/crates/re_viewer/src/native.rs +++ b/crates/re_viewer/src/native.rs @@ -21,7 +21,7 @@ pub fn run_native_app(app_creator: AppCreator) -> eframe::Result<()> { ) } -pub fn eframe_options() -> eframe::NativeOptions { +pub fn eframe_options(force_wgpu_backend: Option) -> eframe::NativeOptions { re_tracing::profile_function!(); eframe::NativeOptions { viewport: egui::ViewportBuilder::default() @@ -40,7 +40,7 @@ pub fn eframe_options() -> eframe::NativeOptions { default_theme: eframe::Theme::Dark, renderer: eframe::Renderer::Wgpu, - wgpu_options: crate::wgpu_options(), + wgpu_options: crate::wgpu_options(force_wgpu_backend), depth_buffer: 0, multisampling: 0, // the 3D views do their own MSAA diff --git a/crates/re_viewer/src/web.rs b/crates/re_viewer/src/web.rs index 1e4abfcbe2f1..ac483f407042 100644 --- a/crates/re_viewer/src/web.rs +++ b/crates/re_viewer/src/web.rs @@ -31,17 +31,19 @@ impl WebHandle { /// - `url` is an optional URL to either an .rrd file over http, or a Rerun WebSocket server. /// - `manifest_url` is an optional URL to an `examples_manifest.json` file over http. + /// - `force_backend` is an optional string to force a specific backend, either `webgl` or `webgpu`. #[wasm_bindgen] pub async fn start( &self, canvas_id: &str, url: Option, manifest_url: Option, + force_wgpu_backend: Option, ) -> Result<(), wasm_bindgen::JsValue> { let web_options = eframe::WebOptions { follow_system_theme: false, default_theme: eframe::Theme::Dark, - wgpu_options: crate::wgpu_options(), + wgpu_options: crate::wgpu_options(force_wgpu_backend), depth_buffer: 0, ..Default::default() }; diff --git a/web_viewer/index.html b/web_viewer/index.html index d49217b1279d..60a341b77cbc 100644 --- a/web_viewer/index.html +++ b/web_viewer/index.html @@ -336,7 +336,13 @@ check_for_panic(); - handle.start("the_canvas_id").then(on_app_started).catch(on_wasm_error); + const urlParams = new URLSearchParams(window.location.search); + const forceGraphics = urlParams.get("force-graphics"); + + handle + .start("the_canvas_id", null, null, forceGraphics) + .then(on_app_started) + .catch(on_wasm_error); } function on_app_started(handle) { From 0518331fbdc270ec9a27187051967467f4014e17 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jan 2024 18:32:17 +0100 Subject: [PATCH 02/12] show how to force the graphics backend on a web crash --- web_viewer/index.html | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/web_viewer/index.html b/web_viewer/index.html index 60a341b77cbc..ccc225a1c69d 100644 --- a/web_viewer/index.html +++ b/web_viewer/index.html @@ -218,6 +218,18 @@ load_wasm(); } + const forceGraphicsInfoParagraph = ` +

+ For graphics related issues, try forcing to render with WebGL
+ + ${window.location}?force-graphics=webgl +
+ or with WebGPU
+ + ${window.location}?force-graphics=webgpu + +

`; + function load_wasm() { // We'll defer our execution until the wasm is ready to go. // Here we tell bindgen the path to the wasm file so it can start @@ -325,8 +337,10 @@ See the console for details.

- Reload the page to try again. + Reload the page to try again.
+ Please consider filing a bug report. Thank you!

+ ${forceGraphicsInfoParagraph} `); } else { let delay_ms = 1000; @@ -369,9 +383,11 @@

${error}

-

- Make sure you use a modern browser with either WebGL or WebGPU support enabled. +

+ Reload the page to try again.
+ Please consider filing a
bug report. Thank you!

+ ${forceGraphicsInfoParagraph} `); } From 013e68b52454bc0fa3b0f425fc6c99327b7dad8f Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jan 2024 22:32:19 +0100 Subject: [PATCH 03/12] allow forcing graphics backend through command line (reaching both native and web player) --- crates/re_renderer/src/config.rs | 24 ++++++++++---- crates/re_sdk/src/web_viewer.rs | 6 +++- crates/re_viewer/src/app.rs | 4 +++ crates/re_viewer/src/lib.rs | 4 +-- crates/re_viewer/src/native.rs | 22 +++++++++---- crates/rerun/src/run.rs | 55 ++++++++++++++++++++++---------- web_viewer/index.html | 18 +++++------ 7 files changed, 90 insertions(+), 43 deletions(-) diff --git a/crates/re_renderer/src/config.rs b/crates/re_renderer/src/config.rs index 79534a5d9e1e..933eeda72cb6 100644 --- a/crates/re_renderer/src/config.rs +++ b/crates/re_renderer/src/config.rs @@ -278,15 +278,25 @@ pub fn validate_graphics_backend_applicability(backend: wgpu::Backend) -> Result } } wgpu::Backend::Dx12 => { - if cfg!(target_arch = "wasm32") { - return Err("Can only run with WebGL or WebGPU on the web."); - } - if cfg!(target_os = "linux") || cfg!(target_os = "mac") { - return Err("Cannot run with DX12 backend on Linux & Mac."); - } + // We don't have DX12 enabled right now, but someone could. + // TODO(wgpu#5166): But if we get this wrong we might crash. + // TODO(wgpu#5167): And we also can't query the config. + return Err("DX12 backend is currently not supported."); + + // if cfg!(target_arch = "wasm32") { + // return Err("Can only run with WebGL or WebGPU on the web."); + // } + // if cfg!(target_os = "linux") || cfg!(target_os = "macos") { + // return Err("Cannot run with DX12 backend on Linux & Mac."); + // } } wgpu::Backend::Gl => { - // Through emulation and build configs GL may work everywhere. + // Using Angle Mac might actually run GL, but we don't enable this. + // TODO(wgpu#5166): But if we get this wrong we might crash. + // TODO(wgpu#5167): And we also can't query the config. + if cfg!(target_os = "macos") { + return Err("Cannot run with GL backend on Mac."); + } } wgpu::Backend::BrowserWebGpu => { if !cfg!(target_arch = "wasm32") { diff --git a/crates/re_sdk/src/web_viewer.rs b/crates/re_sdk/src/web_viewer.rs index d1ce6d06b1f4..2b70ddf7592a 100644 --- a/crates/re_sdk/src/web_viewer.rs +++ b/crates/re_sdk/src/web_viewer.rs @@ -79,6 +79,7 @@ impl WebViewerSink { pub async fn host_web_viewer( bind_ip: String, web_port: WebViewerServerPort, + force_wgpu_backend: Option, open_browser: bool, source_url: String, ) -> anyhow::Result<()> { @@ -86,7 +87,10 @@ pub async fn host_web_viewer( let http_web_viewer_url = web_server.server_url(); let web_server_handle = web_server.serve(); - let viewer_url = format!("{http_web_viewer_url}?url={source_url}"); + let mut viewer_url = format!("{http_web_viewer_url}?url={source_url}"); + if let Some(force_graphics) = force_wgpu_backend { + viewer_url = format!("{viewer_url}&renderer={force_graphics}"); + } re_log::info!("Hosting a web-viewer at {viewer_url}"); if open_browser { diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 5530b66bd7eb..5af1c525a5a7 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -56,6 +56,9 @@ pub struct StartupOptions { pub resolution_in_points: Option<[f32; 2]>, pub skip_welcome_screen: bool, + + /// Forces wgpu backend to use the specified graphics API. + pub force_wgpu_backend: Option, } impl Default for StartupOptions { @@ -75,6 +78,7 @@ impl Default for StartupOptions { resolution_in_points: None, skip_welcome_screen: false, + force_wgpu_backend: None, } } } diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index c36161be26a8..bbd2be4a5385 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -134,14 +134,14 @@ fn supported_graphics_backends(force_backend: Option) -> wgpu::Backends if let Some(backend) = re_renderer::config::parse_graphics_backend(&force_backend) { if let Err(err) = re_renderer::config::validate_graphics_backend_applicability(backend) { - re_log::error!("Failed to force rendering backend parsed from {force_backend:?}: {err}.\nWill use default backend instead."); + re_log::error!("Failed to force rendering backend parsed from {force_backend:?}: {err}\nUsing default backend instead."); re_renderer::config::supported_backends() } else { re_log::info!("Forcing graphics backend to {backend:?}."); backend.into() } } else { - re_log::error!("Failed to parse rendering backend string {force_backend:?}. Will use default backend."); + re_log::error!("Failed to parse rendering backend string {force_backend:?}. Using default backend instead."); re_renderer::config::supported_backends() } } else { diff --git a/crates/re_viewer/src/native.rs b/crates/re_viewer/src/native.rs index a8ab576def97..61c1a80dd526 100644 --- a/crates/re_viewer/src/native.rs +++ b/crates/re_viewer/src/native.rs @@ -7,8 +7,11 @@ type AppCreator = Box, re_ui::ReUi) -> Box>; // NOTE: the name of this function is hard-coded in `crates/rerun/src/crash_handler.rs`! -pub fn run_native_app(app_creator: AppCreator) -> eframe::Result<()> { - let native_options = eframe_options(); +pub fn run_native_app( + app_creator: AppCreator, + force_wgpu_backend: Option, +) -> eframe::Result<()> { + let native_options = eframe_options(force_wgpu_backend); let window_title = "Rerun Viewer"; eframe::run_native( @@ -92,9 +95,14 @@ pub fn run_native_viewer_with_messages( for log_msg in log_messages { tx.send(log_msg).ok(); } - run_native_app(Box::new(move |cc, re_ui| { - let mut app = crate::App::new(build_info, &app_env, startup_options, re_ui, cc.storage); - app.add_receiver(rx); - Box::new(app) - })) + + let force_wgpu_backend = startup_options.force_wgpu_backend.clone(); + run_native_app( + Box::new(move |cc, re_ui| { + let mut app = crate::App::new(build_info, &app_env, startup_options, re_ui, cc.storage); + app.add_receiver(rx); + Box::new(app) + }), + force_wgpu_backend, + ) } diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index f8bd8db2bb4b..3b4db8b87ca6 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -181,6 +181,21 @@ If no arguments are given, a server will be hosted which a Rerun SDK can connect #[clap(long, default_value_t = Default::default())] ws_server_port: RerunServerPort, + /// Override the default graphics backend and for a specific one instead. + /// + /// When using `--web-viewer` this should be one of: + /// * `webgpu` + /// * `webgl` + /// + /// When starting a native viewer instead this should be one of: + /// * `vulkan` (Linux & Windows only) + /// * `gl` (Linux & Windows only) + /// * `metal` (macOS only) + // Note that we don't compile with DX12 right now, but we could (we don't since this adds permutation and wgpu still has some issues with it). + // GL could be enabled on MacOS via `angle` but given prior issues with ANGLE this seems to be a bad idea! + #[clap(long)] + force_renderer: Option, + // ---------------------------------------------------------------------------- // Debug-options: /// Ingest data and then quit once the goodbye message has been received. @@ -499,6 +514,7 @@ async fn run_impl( } else { None }, + force_wgpu_backend: None, } }; @@ -534,6 +550,7 @@ async fn run_impl( return host_web_viewer( args.bind.clone(), args.web_viewer_port, + args.force_renderer, true, rerun_server_ws_url, ) @@ -608,6 +625,7 @@ async fn run_impl( let web_server_handle = tokio::spawn(host_web_viewer( args.bind.clone(), args.web_viewer_port, + args.force_renderer, open_browser, _ws_server_url, )); @@ -620,23 +638,26 @@ async fn run_impl( } } else { #[cfg(feature = "native_viewer")] - return re_viewer::run_native_app(Box::new(move |cc, re_ui| { - let mut app = re_viewer::App::new( - _build_info, - &call_source.app_env(), - startup_options, - re_ui, - cc.storage, - ); - for rx in rx { - app.add_receiver(rx); - } - app.set_profiler(profiler); - if let Ok(url) = std::env::var("EXAMPLES_MANIFEST_URL") { - app.set_examples_manifest_url(url); - } - Box::new(app) - })) + return re_viewer::run_native_app( + Box::new(move |cc, re_ui| { + let mut app = re_viewer::App::new( + _build_info, + &call_source.app_env(), + startup_options, + re_ui, + cc.storage, + ); + for rx in rx { + app.add_receiver(rx); + } + app.set_profiler(profiler); + if let Ok(url) = std::env::var("EXAMPLES_MANIFEST_URL") { + app.set_examples_manifest_url(url); + } + Box::new(app) + }), + args.force_renderer, + ) .map_err(|err| err.into()); #[cfg(not(feature = "native_viewer"))] diff --git a/web_viewer/index.html b/web_viewer/index.html index ccc225a1c69d..121aaa25ba11 100644 --- a/web_viewer/index.html +++ b/web_viewer/index.html @@ -218,15 +218,15 @@ load_wasm(); } - const forceGraphicsInfoParagraph = ` + const forceWgpuBackendInfoParagraph = `

For graphics related issues, try forcing to render with WebGL
-
- ${window.location}?force-graphics=webgl + + ${window.location}?renderer=webgl
or with WebGPU
- - ${window.location}?force-graphics=webgpu + + ${window.location}?renderer=webgpu

`; @@ -340,7 +340,7 @@ Reload the page to try again.
Please consider filing a bug report. Thank you!

- ${forceGraphicsInfoParagraph} + ${forceWgpuBackendInfoParagraph} `); } else { let delay_ms = 1000; @@ -351,10 +351,10 @@ check_for_panic(); const urlParams = new URLSearchParams(window.location.search); - const forceGraphics = urlParams.get("force-graphics"); + const forceWgpuBackend = urlParams.get("renderer"); handle - .start("the_canvas_id", null, null, forceGraphics) + .start("the_canvas_id", null, null, forceWgpuBackend) .then(on_app_started) .catch(on_wasm_error); } @@ -387,7 +387,7 @@ Reload the page to try again.
Please consider filing a
bug report. Thank you!

- ${forceGraphicsInfoParagraph} + ${forceWgpuBackendInfoParagraph} `); } From 24f723ea2af686c533010a6fe9524c2144d4b924 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 30 Jan 2024 22:44:40 +0100 Subject: [PATCH 04/12] update graphics issues documentation --- crates/rerun/src/run.rs | 2 +- docs/content/getting-started/troubleshooting.md | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index 3b4db8b87ca6..a3af4103b212 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -23,7 +23,7 @@ Environment variables: RERUN_SHADER_PATH The search path for shader/shader-imports. Only available in developer builds. RERUN_TRACK_ALLOCATIONS Track memory allocations to diagnose memory leaks in the viewer. WARNING: slows down the viewer by a lot! RUST_LOG Change the log level of the viewer, e.g. `RUST_LOG=debug`. - WGPU_BACKEND Overwrites the graphics backend used, must be one of `vulkan`, `metal`, `dx12`, `dx11`, or `gl`. + WGPU_BACKEND Overwrites the graphics backend used, must be one of `vulkan`, `metal` or `gl`. Default is `vulkan` everywhere except on Mac where we use `metal`. What is supported depends on your OS. WGPU_POWER_PREF Overwrites the power setting used for choosing a graphics adapter, must be `high` or `low`. (Default is `high`) diff --git a/docs/content/getting-started/troubleshooting.md b/docs/content/getting-started/troubleshooting.md index 290f8dd06949..b3e0320a34ba 100644 --- a/docs/content/getting-started/troubleshooting.md +++ b/docs/content/getting-started/troubleshooting.md @@ -66,13 +66,16 @@ rerun reset [Wgpu](https://github.com/gfx-rs/wgpu) (the graphics API we use) maintains a list of [known driver issues](https://github.com/gfx-rs/wgpu/wiki/Known-Driver-Issues) and workarounds for them. -The following environment variables overwrite the config we choose for wgpu: -* `WGPU_BACKEND`: Overwrites the graphics backend used, must be one of `vulkan`, `metal`, `dx12`, `dx11`, or `gl`. - Naturally, support depends on your OS. Default is `vulkan` everywhere except on Mac where we use `metal`. +The configuration we use for wgpu can be influenced in the following ways: +* pass `--force-renderer=` on startup: `` must be one of `vulkan`, `metal` or `gl` for native and + either `webgl` or `webgpu` for the web viewer (see also `--web-viewer` argument). + Naturally, support depends on your OS. The default backend is `vulkan` everywhere except on Mac where we use `metal`. + On the web we prefer WebGPU and fall back automatically to WebGL if no support for WebGPU was detected. + * For instance, you can try `rerun --force-renderer=gl` or for the web viewer respectively `rerun --web-viewer --force-renderer=webgl`. + * Alternatively, for the native viewer you can also use the `WGPU_BACKEND` environment variable with the above values. + * The web viewer is configured by the `renderer=` url argument, e.g. [https://app.rerun.io/?renderer=webgl] * `WGPU_POWER_PREF`: Overwrites the power setting used for choosing a graphics adapter, must be `high` or `low`. (Default is `high`) -For instance, you can try `WGPU_BACKEND=gl rerun`. - We recommend setting these only if you're asked to try them or know what you're doing, since we don't support all of these settings equally well. From bcd0cea81d53eea9442fd0367b8021a605f327ab Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jan 2024 11:02:23 +0100 Subject: [PATCH 05/12] allow restarting via button in the browser --- Cargo.lock | 2 ++ crates/re_ui/src/command.rs | 24 +++++++++++++++++++++++- crates/re_viewer/Cargo.toml | 8 ++++++++ crates/re_viewer/src/app.rs | 27 +++++++++++++++++++++++++++ crates/re_viewer/src/lib.rs | 14 +++++++------- crates/re_viewer/src/ui/rerun_menu.rs | 24 ++++++++++++++++++++++-- crates/re_viewer/src/web.rs | 3 ++- 7 files changed, 91 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 81768919be4b..a2e2b01a9f6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5038,7 +5038,9 @@ dependencies = [ "serde_json", "thiserror", "time", + "wasm-bindgen", "wasm-bindgen-futures", + "web-sys", "web-time", "wgpu", ] diff --git a/crates/re_ui/src/command.rs b/crates/re_ui/src/command.rs index b27c5bfdbd77..0a9ed503dd74 100644 --- a/crates/re_ui/src/command.rs +++ b/crates/re_ui/src/command.rs @@ -74,6 +74,12 @@ pub enum UICommand { #[cfg(target_arch = "wasm32")] CopyDirectLink, + + // Graphics options: + #[cfg(target_arch = "wasm32")] + RestartWithWebGl, + #[cfg(target_arch = "wasm32")] + RestartWithWebGpu, } impl UICommand { @@ -201,7 +207,18 @@ impl UICommand { Self::CopyDirectLink => ( "Copy direct link", "Copy a link to the viewer with the URL parameter set to the current .rrd data source." - ) + ), + + #[cfg(target_arch = "wasm32")] + Self::RestartWithWebGl => ( + "Restart with WebGL", + "Reloads the webpage and force WebGL for rendering. All data will be lost." + ), + #[cfg(target_arch = "wasm32")] + Self::RestartWithWebGpu => ( + "Restart with WebGPU", + "Reloads the webpage and force WebGPU for rendering. All data will be lost." + ), } } @@ -285,6 +302,11 @@ impl UICommand { #[cfg(target_arch = "wasm32")] Self::CopyDirectLink => None, + + #[cfg(target_arch = "wasm32")] + Self::RestartWithWebGl => None, + #[cfg(target_arch = "wasm32")] + Self::RestartWithWebGpu => None, } } diff --git a/crates/re_viewer/Cargo.toml b/crates/re_viewer/Cargo.toml index 611ef0558fae..749db3cd6c08 100644 --- a/crates/re_viewer/Cargo.toml +++ b/crates/re_viewer/Cargo.toml @@ -106,7 +106,15 @@ wgpu.workspace = true # web dependencies: [target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true +web-sys = { workspace = true, features = [ + 'Location', + 'Url', + 'UrlSearchParams', + 'Window', +] } + [build-dependencies] re_build_tools.workspace = true diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 5af1c525a5a7..27c2195c8b7a 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -643,9 +643,36 @@ impl App { UICommand::CopyDirectLink => { self.run_copy_direct_link_command(store_context); } + + #[cfg(target_arch = "wasm32")] + UICommand::RestartWithWebGl => { + if Self::set_url_parameter_and_refresh("renderer", "webgl").is_err() { + re_log::error!("Failed to set URL parameter `renderer=webgl` & refresh page."); + } + } + + #[cfg(target_arch = "wasm32")] + UICommand::RestartWithWebGpu => { + if Self::set_url_parameter_and_refresh("renderer", "webgpu").is_err() { + re_log::error!("Failed to set URL parameter `renderer=webgpu` & refresh page."); + } + } } } + #[cfg(target_arch = "wasm32")] + fn set_url_parameter_and_refresh(key: &str, value: &str) -> Result<(), wasm_bindgen::JsValue> { + let Some(window) = web_sys::window() else { + return Err("Failed to get window".into()); + }; + let location = window.location(); + + let mut url = web_sys::Url::new(&location.href()?)?; + url.search_params().set(key, value); + + location.assign(&url.href()) + } + fn run_time_control_command( &mut self, store_context: Option<&StoreContext<'_>>, diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index bbd2be4a5385..75594e77b19f 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -129,19 +129,19 @@ impl AppEnvironment { // --------------------------------------------------------------------------- -fn supported_graphics_backends(force_backend: Option) -> wgpu::Backends { - if let Some(force_backend) = force_backend { - if let Some(backend) = re_renderer::config::parse_graphics_backend(&force_backend) { +fn supported_graphics_backends(force_wgpu_backend: Option) -> wgpu::Backends { + if let Some(force_wgpu_backend) = force_wgpu_backend { + if let Some(backend) = re_renderer::config::parse_graphics_backend(&force_wgpu_backend) { if let Err(err) = re_renderer::config::validate_graphics_backend_applicability(backend) { - re_log::error!("Failed to force rendering backend parsed from {force_backend:?}: {err}\nUsing default backend instead."); + re_log::error!("Failed to force rendering backend parsed from {force_wgpu_backend:?}: {err}\nUsing default backend instead."); re_renderer::config::supported_backends() } else { re_log::info!("Forcing graphics backend to {backend:?}."); backend.into() } } else { - re_log::error!("Failed to parse rendering backend string {force_backend:?}. Using default backend instead."); + re_log::error!("Failed to parse rendering backend string {force_wgpu_backend:?}. Using default backend instead."); re_renderer::config::supported_backends() } } else { @@ -149,7 +149,7 @@ fn supported_graphics_backends(force_backend: Option) -> wgpu::Backends } } -pub(crate) fn wgpu_options(force_backend: Option) -> egui_wgpu::WgpuConfiguration { +pub(crate) fn wgpu_options(force_wgpu_backend: Option) -> egui_wgpu::WgpuConfiguration { re_tracing::profile_function!(); egui_wgpu::WgpuConfiguration { @@ -171,7 +171,7 @@ pub(crate) fn wgpu_options(force_backend: Option) -> egui_wgpu::WgpuConf egui_wgpu::SurfaceErrorAction::SkipFrame } }), - supported_backends: supported_graphics_backends(force_backend), + supported_backends: supported_graphics_backends(force_wgpu_backend), device_descriptor: std::sync::Arc::new(|adapter| re_renderer::config::DeviceCaps::from_adapter(adapter).device_descriptor()), ..Default::default() } diff --git a/crates/re_viewer/src/ui/rerun_menu.rs b/crates/re_viewer/src/ui/rerun_menu.rs index 6c124d8ea587..4a5b41ce7665 100644 --- a/crates/re_viewer/src/ui/rerun_menu.rs +++ b/crates/re_viewer/src/ui/rerun_menu.rs @@ -83,6 +83,7 @@ impl App { &self.command_sender, &self.re_ui, ui, + frame, &mut self.state.app_options, ); }); @@ -236,7 +237,7 @@ fn render_state_ui(ui: &mut egui::Ui, render_state: &egui_wgpu::RenderState) { egui::Grid::new("adapter_info").show(ui, |ui| { ui.label("Backend"); - ui.label(format!("{backend:?}")); + ui.label(backend.to_str()); // TODO(wgpu#5170): Use std::fmt::Display for backend. ui.end_row(); ui.label("Device Type"); @@ -274,7 +275,8 @@ fn render_state_ui(ui: &mut egui::Ui, render_state: &egui_wgpu::RenderState) { let wgpu_adapter_ui = |ui: &mut egui::Ui, adapter: &eframe::wgpu::Adapter| { let info = &adapter.get_info(); - ui.label(format!("{:?}", info.backend)).on_hover_ui(|ui| { + // TODO(wgpu#5170): Use std::fmt::Display for backend. + ui.label(info.backend.to_str()).on_hover_ui(|ui| { wgpu_adapter_details_ui(ui, adapter); }); }; @@ -303,6 +305,7 @@ fn options_menu_ui( command_sender: &re_viewer_context::CommandSender, re_ui: &ReUi, ui: &mut egui::Ui, + frame: &eframe::Frame, app_options: &mut re_viewer_context::AppOptions, ) { re_ui @@ -338,6 +341,23 @@ fn options_menu_ui( ui.label("Experimental features:"); experimental_feature_ui(command_sender, re_ui, ui, app_options); } + + if let Some(_backend) = frame + .wgpu_render_state() + .map(|state| state.adapter.get_info().backend) + { + // Adapter switching only implemented for web so far. + // For native it's less well defined since the application may be embedded in another application that reads arguments differently. + #[cfg(target_arch = "wasm32")] + { + ui.add_space(SPACING); + if _backend == wgpu::Backend::BrowserWebGpu { + UICommand::RestartWithWebGl.menu_button_ui(ui, command_sender); + } else { + UICommand::RestartWithWebGpu.menu_button_ui(ui, command_sender); + } + } + } } fn experimental_feature_ui( diff --git a/crates/re_viewer/src/web.rs b/crates/re_viewer/src/web.rs index ac483f407042..aaf08e53296f 100644 --- a/crates/re_viewer/src/web.rs +++ b/crates/re_viewer/src/web.rs @@ -31,7 +31,7 @@ impl WebHandle { /// - `url` is an optional URL to either an .rrd file over http, or a Rerun WebSocket server. /// - `manifest_url` is an optional URL to an `examples_manifest.json` file over http. - /// - `force_backend` is an optional string to force a specific backend, either `webgl` or `webgpu`. + /// - `force_wgpu_backend` is an optional string to force a specific backend, either `webgl` or `webgpu`. #[wasm_bindgen] pub async fn start( &self, @@ -121,6 +121,7 @@ fn create_app( persist_state: get_persist_state(&cc.integration_info), is_in_notebook: is_in_notebook(&cc.integration_info), skip_welcome_screen: false, + force_wgpu_backend: None, }; let re_ui = crate::customize_eframe(cc); From 4d59990a386adea3707154a3ed158769de461818 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jan 2024 11:15:59 +0100 Subject: [PATCH 06/12] add rerun-release-web shorthand --- .cargo/config.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index 097c2ea3ba0a..18ecd71a6b15 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -10,6 +10,9 @@ rerun-release = "run --package rerun-cli --no-default-features --features native # `cargo rerun-web` is short a convenient shorthand for building & starting the web viewer. rerun-web = "run --package rerun-cli --no-default-features --features web_viewer -- --web-viewer" +# `cargo rerun-release-web` is short a convenient shorthand for building & starting the web viewer in release mode. +rerun-release-web = "run --package rerun-cli --no-default-features --features web_viewer --release -- --web-viewer" + # Run the codegen. Optionally pass `--profile` to it. # NOTE: there are several CI jobs with `command: codegen` with the cargo action codegen = "run --package re_types_builder --" From 6ce13ef60f7ce32c3eda075606f2316b72b49bc5 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jan 2024 11:20:07 +0100 Subject: [PATCH 07/12] warning fix --- crates/re_viewer/src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 27c2195c8b7a..48097eaf5c55 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -667,7 +667,7 @@ impl App { }; let location = window.location(); - let mut url = web_sys::Url::new(&location.href()?)?; + let url = web_sys::Url::new(&location.href()?)?; url.search_params().set(key, value); location.assign(&url.href()) From 6a982fa6c345fb41627d0dea6db7e84e3774d11c Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jan 2024 11:34:43 +0100 Subject: [PATCH 08/12] fix urls shown on crash --- web_viewer/index.html | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/web_viewer/index.html b/web_viewer/index.html index 121aaa25ba11..e95445e3b123 100644 --- a/web_viewer/index.html +++ b/web_viewer/index.html @@ -218,16 +218,18 @@ load_wasm(); } + var urlWebGl = new URL(window.location); + urlWebGl.searchParams.set("renderer", "webgl"); + + var urlWebGpu = new URL(window.location); + urlWebGpu.searchParams.set("renderer", "webgpu"); + const forceWgpuBackendInfoParagraph = `

For graphics related issues, try forcing to render with WebGL
-
- ${window.location}?renderer=webgl -
+ ${urlWebGl}
or with WebGPU
- - ${window.location}?renderer=webgpu - + ${urlWebGpu}

`; function load_wasm() { From db4de5fe82252722a9641d0893e5d3702e87e70d Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 31 Jan 2024 11:36:53 +0100 Subject: [PATCH 09/12] fix custom example viewer compilation --- examples/rust/custom_space_view/src/main.rs | 35 +++++++++++---------- examples/rust/extend_viewer_ui/src/main.rs | 2 +- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/examples/rust/custom_space_view/src/main.rs b/examples/rust/custom_space_view/src/main.rs index dc13199b4e8a..32d151b6c106 100644 --- a/examples/rust/custom_space_view/src/main.rs +++ b/examples/rust/custom_space_view/src/main.rs @@ -40,22 +40,25 @@ async fn main() -> Result<(), Box> { ); println!("Try for example to run: `cargo run -p minimal_options -- --connect` in another terminal instance."); - re_viewer::run_native_app(Box::new(move |cc, re_ui| { - let mut app = re_viewer::App::new( - re_viewer::build_info(), - &app_env, - startup_options, - re_ui, - cc.storage, - ); - app.add_receiver(rx); - - // Register the custom space view - app.add_space_view_class::() - .unwrap(); - - Box::new(app) - }))?; + re_viewer::run_native_app( + Box::new(move |cc, re_ui| { + let mut app = re_viewer::App::new( + re_viewer::build_info(), + &app_env, + startup_options, + re_ui, + cc.storage, + ); + app.add_receiver(rx); + + // Register the custom space view + app.add_space_view_class::() + .unwrap(); + + Box::new(app) + }), + None, + )?; Ok(()) } diff --git a/examples/rust/extend_viewer_ui/src/main.rs b/examples/rust/extend_viewer_ui/src/main.rs index 4387ffb16b5e..f9f075dc18a1 100644 --- a/examples/rust/extend_viewer_ui/src/main.rs +++ b/examples/rust/extend_viewer_ui/src/main.rs @@ -32,7 +32,7 @@ async fn main() -> Result<(), Box> { let native_options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_app_id("rerun_extend_viewer_ui_example"), - ..re_viewer::native::eframe_options() + ..re_viewer::native::eframe_options(None) }; let startup_options = re_viewer::StartupOptions::default(); From 8f2badb268050b14c8393cfe98c98132c524c102 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 1 Feb 2024 10:21:33 +0100 Subject: [PATCH 10/12] change --force-renderer to --renderer --- crates/rerun/src/run.rs | 8 ++++---- docs/content/getting-started/troubleshooting.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index a3af4103b212..c3ef96aaa5b8 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -194,7 +194,7 @@ If no arguments are given, a server will be hosted which a Rerun SDK can connect // Note that we don't compile with DX12 right now, but we could (we don't since this adds permutation and wgpu still has some issues with it). // GL could be enabled on MacOS via `angle` but given prior issues with ANGLE this seems to be a bad idea! #[clap(long)] - force_renderer: Option, + renderer: Option, // ---------------------------------------------------------------------------- // Debug-options: @@ -550,7 +550,7 @@ async fn run_impl( return host_web_viewer( args.bind.clone(), args.web_viewer_port, - args.force_renderer, + args.renderer, true, rerun_server_ws_url, ) @@ -625,7 +625,7 @@ async fn run_impl( let web_server_handle = tokio::spawn(host_web_viewer( args.bind.clone(), args.web_viewer_port, - args.force_renderer, + args.renderer, open_browser, _ws_server_url, )); @@ -656,7 +656,7 @@ async fn run_impl( } Box::new(app) }), - args.force_renderer, + args.renderer, ) .map_err(|err| err.into()); diff --git a/docs/content/getting-started/troubleshooting.md b/docs/content/getting-started/troubleshooting.md index b3e0320a34ba..b3675290c912 100644 --- a/docs/content/getting-started/troubleshooting.md +++ b/docs/content/getting-started/troubleshooting.md @@ -67,11 +67,11 @@ rerun reset [known driver issues](https://github.com/gfx-rs/wgpu/wiki/Known-Driver-Issues) and workarounds for them. The configuration we use for wgpu can be influenced in the following ways: -* pass `--force-renderer=` on startup: `` must be one of `vulkan`, `metal` or `gl` for native and +* pass `--renderer=` on startup: `` must be one of `vulkan`, `metal` or `gl` for native and either `webgl` or `webgpu` for the web viewer (see also `--web-viewer` argument). Naturally, support depends on your OS. The default backend is `vulkan` everywhere except on Mac where we use `metal`. On the web we prefer WebGPU and fall back automatically to WebGL if no support for WebGPU was detected. - * For instance, you can try `rerun --force-renderer=gl` or for the web viewer respectively `rerun --web-viewer --force-renderer=webgl`. + * For instance, you can try `rerun --renderer=gl` or for the web viewer respectively `rerun --web-viewer --renderer=webgl`. * Alternatively, for the native viewer you can also use the `WGPU_BACKEND` environment variable with the above values. * The web viewer is configured by the `renderer=` url argument, e.g. [https://app.rerun.io/?renderer=webgl] * `WGPU_POWER_PREF`: Overwrites the power setting used for choosing a graphics adapter, must be `high` or `low`. (Default is `high`) From 2cbb4e140d078a839975b3810b70161a6685c2ce Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 1 Feb 2024 15:15:06 +0100 Subject: [PATCH 11/12] remove odd commented code (rewrite it when time is nigh) --- crates/re_renderer/src/config.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/re_renderer/src/config.rs b/crates/re_renderer/src/config.rs index 933eeda72cb6..9b0ef6573b02 100644 --- a/crates/re_renderer/src/config.rs +++ b/crates/re_renderer/src/config.rs @@ -282,13 +282,6 @@ pub fn validate_graphics_backend_applicability(backend: wgpu::Backend) -> Result // TODO(wgpu#5166): But if we get this wrong we might crash. // TODO(wgpu#5167): And we also can't query the config. return Err("DX12 backend is currently not supported."); - - // if cfg!(target_arch = "wasm32") { - // return Err("Can only run with WebGL or WebGPU on the web."); - // } - // if cfg!(target_os = "linux") || cfg!(target_os = "macos") { - // return Err("Cannot run with DX12 backend on Linux & Mac."); - // } } wgpu::Backend::Gl => { // Using Angle Mac might actually run GL, but we don't enable this. From 7d50a7f256f9144a5340c896b56d4e64c7beeb74 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Thu, 1 Feb 2024 15:20:59 +0100 Subject: [PATCH 12/12] move set_url_parameter_and_refresh to web_tools --- crates/re_viewer/src/app.rs | 17 ++--------------- crates/re_viewer/src/lib.rs | 3 +++ crates/re_viewer/src/web_tools.rs | 11 +++++++++++ 3 files changed, 16 insertions(+), 15 deletions(-) create mode 100644 crates/re_viewer/src/web_tools.rs diff --git a/crates/re_viewer/src/app.rs b/crates/re_viewer/src/app.rs index 48097eaf5c55..3a563dcf0ea2 100644 --- a/crates/re_viewer/src/app.rs +++ b/crates/re_viewer/src/app.rs @@ -646,33 +646,20 @@ impl App { #[cfg(target_arch = "wasm32")] UICommand::RestartWithWebGl => { - if Self::set_url_parameter_and_refresh("renderer", "webgl").is_err() { + if crate::web_tools::set_url_parameter_and_refresh("renderer", "webgl").is_err() { re_log::error!("Failed to set URL parameter `renderer=webgl` & refresh page."); } } #[cfg(target_arch = "wasm32")] UICommand::RestartWithWebGpu => { - if Self::set_url_parameter_and_refresh("renderer", "webgpu").is_err() { + if crate::web_tools::set_url_parameter_and_refresh("renderer", "webgpu").is_err() { re_log::error!("Failed to set URL parameter `renderer=webgpu` & refresh page."); } } } } - #[cfg(target_arch = "wasm32")] - fn set_url_parameter_and_refresh(key: &str, value: &str) -> Result<(), wasm_bindgen::JsValue> { - let Some(window) = web_sys::window() else { - return Err("Failed to get window".into()); - }; - let location = window.location(); - - let url = web_sys::Url::new(&location.href()?)?; - url.search_params().set(key, value); - - location.assign(&url.href()) - } - fn run_time_control_command( &mut self, store_context: Option<&StoreContext<'_>>, diff --git a/crates/re_viewer/src/lib.rs b/crates/re_viewer/src/lib.rs index 75594e77b19f..dd325eba9612 100644 --- a/crates/re_viewer/src/lib.rs +++ b/crates/re_viewer/src/lib.rs @@ -55,6 +55,9 @@ pub use native::{run_native_app, run_native_viewer_with_messages}; #[cfg(target_arch = "wasm32")] mod web; +#[cfg(target_arch = "wasm32")] +mod web_tools; + // --------------------------------------------------------------------------- /// Information about this version of the crate. diff --git a/crates/re_viewer/src/web_tools.rs b/crates/re_viewer/src/web_tools.rs new file mode 100644 index 000000000000..2575a31a944e --- /dev/null +++ b/crates/re_viewer/src/web_tools.rs @@ -0,0 +1,11 @@ +pub fn set_url_parameter_and_refresh(key: &str, value: &str) -> Result<(), wasm_bindgen::JsValue> { + let Some(window) = web_sys::window() else { + return Err("Failed to get window".into()); + }; + let location = window.location(); + + let url = web_sys::Url::new(&location.href()?)?; + url.search_params().set(key, value); + + location.assign(&url.href()) +}