Skip to content

Commit

Permalink
Better crash reports on Web + WebGPU support detection (#1975)
Browse files Browse the repository at this point in the history
* setup web crash handler

* webgpu support detection

* fix comment override

* fix unnecessary indentation change

* remove accidentally added determine_url again

* handle failure to load wasm when checking for rendering backend on error
  • Loading branch information
Wumpf authored May 2, 2023
1 parent bb450a9 commit cc769d5
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 115 deletions.
2 changes: 0 additions & 2 deletions crates/re_viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ pub use misc::profiler::Profiler;

#[cfg(target_arch = "wasm32")]
mod web;
#[cfg(target_arch = "wasm32")]
pub use web::start;

// ---------------------------------------------------------------------------

Expand Down
242 changes: 139 additions & 103 deletions crates/re_viewer/src/web.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use eframe::wasm_bindgen::{self, prelude::*};
use eframe::{
wasm_bindgen::{self, prelude::*},
web::AppRunnerRef,
};

use std::sync::Arc;

use re_memory::AccountingAllocator;
Expand All @@ -7,111 +11,143 @@ use re_memory::AccountingAllocator;
static GLOBAL: AccountingAllocator<std::alloc::System> =
AccountingAllocator::new(std::alloc::System);

/// This is the entry-point for all the Wasm.
///
/// This is called once from the HTML.
/// It loads the app, installs some callbacks, then returns.
/// The `url` is an optional URL to either an .rrd file over http, or a Rerun WebSocket server.
#[wasm_bindgen]
pub async fn start(
canvas_id: &str,
url: Option<String>,
) -> std::result::Result<(), eframe::wasm_bindgen::JsValue> {
// Make sure panics are logged using `console.error`.
console_error_panic_hook::set_once();

re_log::setup_web_logging();

let web_options = eframe::WebOptions {
follow_system_theme: false,
default_theme: eframe::Theme::Dark,
wgpu_options: crate::wgpu_options(),
depth_buffer: 0,
};

eframe::start_web(
canvas_id,
web_options,
Box::new(move |cc| {
let build_info = re_build_info::build_info!();
let app_env = crate::AppEnvironment::Web;
let persist_state = get_persist_state(&cc.integration_info);
let startup_options = crate::StartupOptions {
memory_limit: re_memory::MemoryLimit {
// On wasm32 we only have 4GB of memory to play around with.
limit: Some(2_500_000_000),
},
persist_state,
};
let re_ui = crate::customize_eframe(cc);
let url = url.unwrap_or_else(|| get_url(&cc.integration_info));

match categorize_uri(url) {
EndpointCategory::HttpRrd(url) => {
// Download an .rrd file over http:
let (tx, rx) =
re_smart_channel::smart_channel(re_smart_channel::Source::RrdHttpStream {
url: url.clone(),
});
let egui_ctx = cc.egui_ctx.clone();
re_log_encoding::stream_rrd_from_http::stream_rrd_from_http(
url,
Arc::new(move |msg| {
egui_ctx.request_repaint(); // wake up ui thread
tx.send(msg).ok();
}),
);

Box::new(crate::App::from_receiver(
build_info,
&app_env,
startup_options,
re_ui,
cc.storage,
rx,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
))
}
EndpointCategory::WebEventListener => {
// Process an rrd when it's posted via `window.postMessage`
let (tx, rx) = re_smart_channel::smart_channel(
re_smart_channel::Source::RrdWebEventListener,
);
let egui_ctx = cc.egui_ctx.clone();
re_log_encoding::stream_rrd_from_http::stream_rrd_from_event_listener(
Arc::new(move |msg| {
egui_ctx.request_repaint(); // wake up ui thread
tx.send(msg).ok();
}),
);

Box::new(crate::App::from_receiver(
build_info,
&app_env,
startup_options,
re_ui,
cc.storage,
rx,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
))
}
EndpointCategory::WebSocket(url) => {
// Connect to a Rerun server over WebSockets.
Box::new(crate::RemoteViewerApp::new(
build_info,
app_env,
startup_options,
re_ui,
cc.storage,
url,
))
pub struct WebHandle {
runner: AppRunnerRef,
}

#[wasm_bindgen]
impl WebHandle {
/// This is the entry-point for all the Wasm.
///
/// This is called once from the HTML.
/// It loads the app, installs some callbacks, then returns.
/// The `url` is an optional URL to either an .rrd file over http, or a Rerun WebSocket server.
#[wasm_bindgen(constructor)]
pub async fn new(
canvas_id: &str,
url: Option<String>,
) -> Result<WebHandle, wasm_bindgen::JsValue> {
// Make sure panics are logged using `console.error`.
console_error_panic_hook::set_once();

re_log::setup_web_logging();

let web_options = eframe::WebOptions {
follow_system_theme: false,
default_theme: eframe::Theme::Dark,
wgpu_options: crate::wgpu_options(),
depth_buffer: 0,
};

let runner = eframe::start_web(
canvas_id,
web_options,
Box::new(move |cc| {
let build_info = re_build_info::build_info!();
let app_env = crate::AppEnvironment::Web;
let persist_state = get_persist_state(&cc.integration_info);
let startup_options = crate::StartupOptions {
memory_limit: re_memory::MemoryLimit {
// On wasm32 we only have 4GB of memory to play around with.
limit: Some(2_500_000_000),
},
persist_state,
};
let re_ui = crate::customize_eframe(cc);
let url = url.unwrap_or_else(|| get_url(&cc.integration_info));

match categorize_uri(url) {
EndpointCategory::HttpRrd(url) => {
// Download an .rrd file over http:
let (tx, rx) = re_smart_channel::smart_channel(
re_smart_channel::Source::RrdHttpStream { url: url.clone() },
);
let egui_ctx = cc.egui_ctx.clone();
re_log_encoding::stream_rrd_from_http::stream_rrd_from_http(
url,
Arc::new(move |msg| {
egui_ctx.request_repaint(); // wake up ui thread
tx.send(msg).ok();
}),
);

Box::new(crate::App::from_receiver(
build_info,
&app_env,
startup_options,
re_ui,
cc.storage,
rx,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
))
}
EndpointCategory::WebEventListener => {
// Process an rrd when it's posted via `window.postMessage`
let (tx, rx) = re_smart_channel::smart_channel(
re_smart_channel::Source::RrdWebEventListener,
);
let egui_ctx = cc.egui_ctx.clone();
re_log_encoding::stream_rrd_from_http::stream_rrd_from_event_listener(
Arc::new(move |msg| {
egui_ctx.request_repaint(); // wake up ui thread
tx.send(msg).ok();
}),
);

Box::new(crate::App::from_receiver(
build_info,
&app_env,
startup_options,
re_ui,
cc.storage,
rx,
std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
))
}
EndpointCategory::WebSocket(url) => {
// Connect to a Rerun server over WebSockets.
Box::new(crate::RemoteViewerApp::new(
build_info,
app_env,
startup_options,
re_ui,
cc.storage,
url,
))
}
}
}
}),
)
.await?;
}),
)
.await?;

Ok(WebHandle { runner })
}

#[wasm_bindgen]
pub fn destroy(&self) {
self.runner.destroy();
}

#[wasm_bindgen]
pub fn has_panicked(&self) -> bool {
self.runner.panic_summary().is_some()
}

#[wasm_bindgen]
pub fn panic_message(&self) -> Option<String> {
self.runner.panic_summary().map(|s| s.message())
}

Ok(())
#[wasm_bindgen]
pub fn panic_callstack(&self) -> Option<String> {
self.runner.panic_summary().map(|s| s.callstack())
}
}

#[wasm_bindgen]
pub fn is_webgpu_build() -> bool {
!cfg!(feature = "webgl")
}

enum EndpointCategory {
Expand Down
66 changes: 61 additions & 5 deletions web_viewer/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -171,17 +171,65 @@


function on_wasm_loaded() {
console.debug("wasm loaded. starting app…");
// WebGPU version is currently only supported on browsers with WebGPU support, there is no dynamic fallback to WebGL.
if (wasm_bindgen.is_webgpu_build() && typeof navigator.gpu === 'undefined') {
console.debug("`navigator.gpu` is undefined. This indicates lack of WebGPU support.");
document.getElementById("center_text").innerHTML = `
<p>
Missing WebGPU support.
</p>
<p style="font-size:18px">
This version of Rerun requires WebGPU support which is not available in your browser.
Either try a different browser or use the WebGL version of Rerun.
</p>`;
return;
}

console.debug("Wasm loaded. Starting app…");

// This call installs a bunch of callbacks and then returns:
wasm_bindgen.start("the_canvas_id", determine_url());
let handle = new wasm_bindgen.WebHandle("the_canvas_id", determine_url());
handle.then(on_app_started).catch(on_wasm_error);
}

function on_app_started(handle) {
// Call `handle.destroy()` to stop. Uncomment to quick result:
// setTimeout(() => { handle.destroy(); handle.free()) }, 2000)

console.debug("app started.");
document.getElementById("center_text").remove();
console.debug("App started.");
document.getElementById("center_text").innerHTML = '';

if (window.location !== window.parent.location) {
window.parent.postMessage("READY", "*");
}

function check_for_panic() {
if (handle.has_panicked()) {
console.error("Rerun has crashed");

// Rerun already logs the panic message and callstack, but you
// can access them like this if you want to show them in the html:
// console.error(`${handle.panic_message()}`);
// console.error(`${handle.panic_callstack()}`);

document.getElementById("the_canvas_id").remove();
document.getElementById("center_text").innerHTML = `
<p>
Rerun has crashed.
</p>
<p style="font-size:14px">
See the console for details.
</p>
<p style="font-size:14px">
Reload the page to try again.
</p>`;
} else {
let delay_ms = 1000;
setTimeout(check_for_panic, delay_ms);
}
}

check_for_panic();
}

function determine_url() {
Expand Down Expand Up @@ -215,6 +263,14 @@

function on_wasm_error(error) {
console.error("Failed to start: " + error);

let render_backend_name = "WebGPU/WebGL";
try {
render_backend_name = wasm_bindgen.is_webgpu_build() ? "WebGPU" : "WebGL";
} catch (e) {
// loading the wasm probably failed.
}

document.getElementById("center_text").innerHTML = `
<p>
An error occurred during loading:
Expand All @@ -223,7 +279,7 @@
${error}
</p>
<p style="font-size:14px">
Make sure you use a modern browser with WebGL and Wasm enabled.
Make sure you use a modern browser with ${render_backend_name} and Wasm enabled.
</p>`;
}
</script>
Expand Down
Loading

1 comment on commit cc769d5

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Rust Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.25.

Benchmark suite Current: cc769d5 Previous: bb450a9 Ratio
datastore/num_rows=1000/num_instances=1000/packed=false/insert/default 3451377 ns/iter (± 34487) 2501111 ns/iter (± 2953) 1.38
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at/default 379 ns/iter (± 14) 300 ns/iter (± 2) 1.26
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/primary/default 260 ns/iter (± 0) 202 ns/iter (± 0) 1.29
datastore/num_rows=1000/num_instances=1000/packed=false/latest_at_missing/secondaries/default 420 ns/iter (± 0) 321 ns/iter (± 0) 1.31
datastore/num_rows=1000/num_instances=1000/packed=false/range/default 3560866 ns/iter (± 41938) 2551926 ns/iter (± 5525) 1.40
datastore/num_rows=1000/num_instances=1000/gc/default 2386955 ns/iter (± 2421) 1555681 ns/iter (± 5728) 1.53
mono_points_arrow/generate_message_bundles 27403552 ns/iter (± 681496) 20583941 ns/iter (± 712764) 1.33
mono_points_arrow_batched/generate_message_bundles 20013128 ns/iter (± 1099147) 15887541 ns/iter (± 30553) 1.26
mono_points_arrow_batched/generate_messages 4406477 ns/iter (± 207658) 2916342 ns/iter (± 9469) 1.51
mono_points_arrow_batched/encode_total 26855916 ns/iter (± 958000) 20384395 ns/iter (± 80099) 1.32
mono_points_arrow_batched/decode_log_msg 781012 ns/iter (± 2836) 474381 ns/iter (± 1295) 1.65
mono_points_arrow_batched/decode_message_bundles 7629704 ns/iter (± 129290) 6049457 ns/iter (± 5920) 1.26
mono_points_arrow_batched/decode_total 8460944 ns/iter (± 132623) 6630231 ns/iter (± 9696) 1.28
arrow_mono_points/insert 2290946153 ns/iter (± 6332315) 1587335485 ns/iter (± 7858791) 1.44
arrow_mono_points/query 1171553 ns/iter (± 9681) 884149 ns/iter (± 1104) 1.33

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.