Skip to content

Commit

Permalink
Improve error message when using an under-powered GPU (#6252)
Browse files Browse the repository at this point in the history
### What
Using `scripts/fetch_crashes.py` to look at our analytics, I noticed
that missing wgpu capabilities would lead to a assert/panic, which is
not a nice way to report errors to users.

I added an egui issue to improve this further in the future:
* emilk/egui#4474

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6252?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6252?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!

- [PR Build Summary](https://build.rerun.io/pr/6252)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)

To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
  • Loading branch information
emilk authored May 10, 2024
1 parent 0f34aaa commit efba98a
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 42 deletions.
78 changes: 43 additions & 35 deletions crates/re_renderer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl RenderContext {
device: Arc<wgpu::Device>,
queue: Arc<wgpu::Queue>,
config: RenderContextConfig,
) -> Self {
) -> Result<Self, String> {
re_tracing::profile_function!();

let frame_index_for_uncaptured_errors = Arc::new(AtomicU64::new(STARTUP_FRAME_IDX));
Expand Down Expand Up @@ -155,38 +155,46 @@ impl RenderContext {
let global_bindings = GlobalBindings::new(&gpu_resources, &device);

// Validate capabilities of the device.
assert!(
config.device_caps.limits().check_limits(&device.limits()),
"The given device doesn't support the required limits for the given hardware caps {:?}.
Required:
{:?}
Actual:
{:?}",
config.device_caps,
config.device_caps.limits(),
device.limits(),
);
assert!(
device.features().contains(config.device_caps.features()),
"The given device doesn't support the required features for the given hardware caps {:?}.
Required:
{:?}
Actual:
{:?}",
config.device_caps,
config.device_caps.features(),
device.features(),
);
assert!(adapter.get_downlevel_capabilities().flags.contains(config.device_caps.required_downlevel_capabilities().flags),
"The given device doesn't support the required downlevel capabilities for the given hardware caps {:?}.
Required:
{:?}
Actual:
{:?}",
config.device_caps,
config.device_caps.required_downlevel_capabilities(),
adapter.get_downlevel_capabilities(),
);
if !config.device_caps.limits().check_limits(&device.limits()) {
return Err(format!(
"The given device doesn't support the required limits for the given hardware caps {:?}.
Required:
{:?}
Actual:
{:?}",
config.device_caps,
config.device_caps.limits(),
device.limits(),
));
}
if !device.features().contains(config.device_caps.features()) {
return Err(format!(
"The given device doesn't support the required features for the given hardware caps {:?}.
Required:
{:?}
Actual:
{:?}",
config.device_caps,
config.device_caps.features(),
device.features(),
));
}
if !adapter
.get_downlevel_capabilities()
.flags
.contains(config.device_caps.required_downlevel_capabilities().flags)
{
return Err(format!(
"The given device doesn't support the required downlevel capabilities for the given hardware caps {:?}.
Required:
{:?}
Actual:
{:?}",
config.device_caps,
config.device_caps.required_downlevel_capabilities(),
adapter.get_downlevel_capabilities(),
));
}

let resolver = crate::new_recommended_file_resolver();
let mesh_manager = RwLock::new(MeshManager::new());
Expand Down Expand Up @@ -222,7 +230,7 @@ impl RenderContext {
Self::GPU_READBACK_BELT_DEFAULT_CHUNK_SIZE.unwrap(),
));

RenderContext {
Ok(RenderContext {
device,
queue,
config,
Expand All @@ -240,7 +248,7 @@ impl RenderContext {
active_frame,
frame_index_for_uncaptured_errors,
gpu_resources,
}
})
}

fn poll_device(&mut self) {
Expand Down
3 changes: 2 additions & 1 deletion crates/re_renderer_examples/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ impl<E: Example + 'static> Application<E> {
output_format_color,
device_caps,
},
);
)
.map_err(|err| anyhow::format_err!("{err}"))?;

let example = E::new(&re_ctx);

Expand Down
21 changes: 18 additions & 3 deletions crates/re_viewer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,23 +198,38 @@ pub(crate) fn wgpu_options(force_wgpu_backend: Option<String>) -> egui_wgpu::Wgp

/// Customize eframe and egui to suit the rerun viewer.
#[must_use]
pub fn customize_eframe(cc: &eframe::CreationContext<'_>) -> re_ui::ReUi {
pub fn customize_eframe_and_setup_renderer(cc: &eframe::CreationContext<'_>) -> re_ui::ReUi {
re_tracing::profile_function!();

if let Some(render_state) = &cc.wgpu_render_state {
use re_renderer::{config::RenderContextConfig, RenderContext};

let paint_callback_resources = &mut render_state.renderer.write().callback_resources;

paint_callback_resources.insert(RenderContext::new(
let render_ctx = RenderContext::new(
&render_state.adapter,
render_state.device.clone(),
render_state.queue.clone(),
RenderContextConfig {
output_format_color: render_state.target_format,
device_caps: re_renderer::config::DeviceCaps::from_adapter(&render_state.adapter),
},
));
);

match render_ctx {
Ok(render_ctx) => {
paint_callback_resources.insert(render_ctx);
}
Err(err) => {
re_log::error!("Failed to create render context: {err}");

#[allow(clippy::exit)]
{
// TODO(egui#4474): return errors to eframe -> `main`
std::process::exit(1);
}
}
};
}

re_ui::ReUi::load_and_apply(&cc.egui_ctx)
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn run_native_app(
window_title,
native_options,
Box::new(move |cc| {
let re_ui = crate::customize_eframe(cc);
let re_ui = crate::customize_eframe_and_setup_renderer(cc);
app_creator(cc, re_ui)
}),
)
Expand Down
2 changes: 1 addition & 1 deletion crates/re_viewer/src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ fn create_app(
expect_data_soon: None,
force_wgpu_backend: None,
};
let re_ui = crate::customize_eframe(cc);
let re_ui = crate::customize_eframe_and_setup_renderer(cc);

let mut app = crate::App::new(build_info, &app_env, startup_options, re_ui, cc.storage);

Expand Down
2 changes: 1 addition & 1 deletion examples/rust/extend_viewer_ui/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
window_title,
native_options,
Box::new(move |cc| {
let re_ui = re_viewer::customize_eframe(cc);
let re_ui = re_viewer::customize_eframe_and_setup_renderer(cc);

let mut rerun_app = re_viewer::App::new(
re_viewer::build_info(),
Expand Down

0 comments on commit efba98a

Please sign in to comment.