Rewrite Example Event Loop (#4634)

This commit is contained in:
Connor Fitzgerald 2023-11-05 20:46:18 -05:00 committed by GitHub
parent dfa7e2f8fb
commit 8547226176
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 489 additions and 522 deletions

12
Cargo.lock generated
View File

@ -1276,6 +1276,15 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "fern"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
dependencies = [
"log",
]
[[package]]
name = "fixedbitset"
version = "0.4.2"
@ -4134,9 +4143,11 @@ dependencies = [
name = "wgpu-example"
version = "0.18.0"
dependencies = [
"cfg-if",
"console_error_panic_hook",
"console_log",
"env_logger",
"fern",
"js-sys",
"log",
"png",
@ -4144,6 +4155,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"web-time",
"wgpu",
"wgpu-hal",
"wgpu-test",

View File

@ -80,6 +80,7 @@ ctor = "0.2"
# https://github.com/SiegeEngine/ddsfile/issues/15 (Updated dependencies)
ddsfile = { version = "0.5.2-unstable", git = "https://github.com/SiegeEngine/ddsfile.git", rev = "9b597930edc00502391cbb1a39708dadde0fd0ff" }
env_logger = "0.10"
fern = "0.6"
flume = "0.11"
futures-lite = "1"
futures-intrusive = "0.5"
@ -160,6 +161,7 @@ js-sys = "0.3.65"
wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4.38"
wasm-bindgen-test = "0.3"
web-time = "0.2.3"
web-sys = "0.3.64"
# deno dependencies

View File

@ -11,10 +11,12 @@ license.workspace = true
publish = false
[dependencies]
cfg-if.workspace = true
env_logger.workspace = true
log.workspace = true
pollster.workspace = true
png.workspace = true
web-time.workspace = true
winit.workspace = true
wgpu.workspace = true
wgpu-test.workspace = true
@ -22,6 +24,7 @@ wgpu-test.workspace = true
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook.workspace = true
console_log.workspace = true
fern.workspace = true
js-sys.workspace = true
wasm-bindgen.workspace = true
wasm-bindgen-futures.workspace = true

View File

@ -1,34 +1,13 @@
#[cfg(target_arch = "wasm32")]
use std::str::FromStr;
#[cfg(not(target_arch = "wasm32"))]
use std::time::Instant;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]
use web_sys::{ImageBitmapRenderingContext, OffscreenCanvas};
use wgpu::{WasmNotSend, WasmNotSync};
use wgpu::{Instance, Surface, WasmNotSend, WasmNotSync};
use wgpu_test::GpuTestConfiguration;
use winit::{
event::{self, KeyEvent, WindowEvent},
event_loop::{ControlFlow, EventLoop},
dpi::PhysicalSize,
event::{Event, KeyEvent, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
keyboard::{Key, NamedKey},
window::Window,
};
#[allow(dead_code)]
pub fn cast_slice<T>(data: &[T]) -> &[u8] {
use std::{mem::size_of_val, slice::from_raw_parts};
unsafe { from_raw_parts(data.as_ptr() as *const u8, size_of_val(data)) }
}
#[allow(dead_code)]
pub enum ShaderStage {
Vertex,
Fragment,
Compute,
}
pub trait Example: 'static + Sized {
const SRGB: bool = true;
@ -71,256 +50,364 @@ pub trait Example: 'static + Sized {
fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue);
}
struct Setup {
_window: Window,
// Initialize logging in platform dependant ways.
fn init_logger() {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
// As we don't have an environment to pull logging level from, we use the query string.
let query_string = web_sys::window().unwrap().location().search().unwrap();
let query_level: Option<log::LevelFilter> = parse_url_query_string(&query_string, "RUST_LOG")
.and_then(|x| x.parse().ok());
// We keep wgpu at Error level, as it's very noisy.
let base_level = query_level.unwrap_or(log::LevelFilter::Info);
let wgpu_level = query_level.unwrap_or(log::LevelFilter::Error);
// On web, we use fern, as console_log doesn't have filtering on a per-module level.
fern::Dispatch::new()
.level(base_level)
.level_for("wgpu_core", wgpu_level)
.level_for("wgpu_hal", wgpu_level)
.level_for("naga", wgpu_level)
.chain(fern::Output::call(console_log::log))
.apply()
.unwrap();
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
} else {
// parse_default_env will read the RUST_LOG environment variable and apply it on top
// of these default filters.
env_logger::builder()
.filter_level(log::LevelFilter::Info)
// We keep wgpu at Error level, as it's very noisy.
.filter_module("wgpu_core", log::LevelFilter::Error)
.filter_module("wgpu_hal", log::LevelFilter::Error)
.filter_module("naga", log::LevelFilter::Error)
.parse_default_env()
.init();
}
}
}
struct EventLoopWrapper {
event_loop: EventLoop<()>,
window: Window,
}
impl EventLoopWrapper {
pub fn new(title: &str) -> Self {
let event_loop = EventLoop::new().unwrap();
let mut builder = winit::window::WindowBuilder::new();
builder = builder.with_title(title);
let window = builder.build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
let canvas = window.canvas().expect("Couldn't get canvas");
canvas.style().set_css_text("height: 100%; width: 100%;");
// On wasm, append the canvas to the document body
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| body.append_child(&canvas).ok())
.expect("couldn't append canvas to document body");
}
Self { event_loop, window }
}
}
/// Wrapper type which manages the surface and surface configuration.
///
/// As surface usage varies per platform, wrapping this up cleans up the event loop code.
struct SurfaceWrapper {
surface: Option<wgpu::Surface>,
config: Option<wgpu::SurfaceConfiguration>,
}
impl SurfaceWrapper {
/// Create a new surface wrapper with no surface or configuration.
fn new() -> Self {
Self {
surface: None,
config: None,
}
}
/// Called after the instance is created, but before we request an adapter.
///
/// On wasm, we need to create the surface here, as the WebGL backend needs
/// a surface (and hence a canvas) to be present to create the adapter.
///
/// We cannot unconditionally create a surface here, as Android requires
/// us to wait until we recieve the `Resumed` event to do so.
fn pre_adapter(&mut self, instance: &Instance, window: &Window) {
if cfg!(target_arch = "wasm32") {
self.surface = Some(unsafe { instance.create_surface(&window).unwrap() });
}
}
/// Check if the event is the start condition for the surface.
fn start_condition(e: &Event<()>) -> bool {
match e {
// On all other platforms, we can create the surface immediately.
Event::NewEvents(StartCause::Init) => !cfg!(target_os = "android"),
// On android we need to wait for a resumed event to create the surface.
Event::Resumed => cfg!(target_os = "android"),
_ => false,
}
}
/// Called when an event which matches [`Self::start_condition`] is recieved.
///
/// On all native platforms, this is where we create the surface.
///
/// Additionally, we configure the surface based on the (now valid) window size.
fn resume(&mut self, context: &ExampleContext, window: &Window, srgb: bool) {
// Window size is only actually valid after we enter the event loop.
let window_size = window.inner_size();
log::info!("Surface resume {window_size:?}");
// We didn't create the surface in pre_adapter, so we need to do so now.
if !cfg!(target_arch = "wasm32") {
self.surface = Some(unsafe { context.instance.create_surface(&window).unwrap() });
}
// From here on, self.surface should be Some.
let surface = self.surface.as_ref().unwrap();
// Get the default configuration,
let mut config = surface
.get_default_config(&context.adapter, window_size.width, window_size.height)
.expect("Surface isn't supported by the adapter.");
if srgb {
// Not all platforms (WebGPU) support sRGB swapchains, so we need to use view formats
let view_format = config.format.add_srgb_suffix();
config.view_formats.push(view_format);
} else {
// All platforms support non-sRGB swapchains, so we can just use the format directly.
let format = config.format.remove_srgb_suffix();
config.format = format;
config.view_formats.push(format);
};
surface.configure(&context.device, &config);
self.config = Some(config);
}
/// Resize the surface, making sure to not resize to zero.
fn resize(&mut self, context: &ExampleContext, size: PhysicalSize<u32>) {
log::info!("Surface resize {size:?}");
let config = self.config.as_mut().unwrap();
config.width = size.width.max(1);
config.height = size.height.max(1);
let surface = self.surface.as_ref().unwrap();
surface.configure(&context.device, config);
}
/// Acquire the next surface texture.
fn acquire(&mut self, context: &ExampleContext) -> wgpu::SurfaceTexture {
let surface = self.surface.as_ref().unwrap();
match surface.get_current_texture() {
Ok(frame) => frame,
Err(_) => {
surface.configure(&context.device, self.config());
surface
.get_current_texture()
.expect("Failed to acquire next surface texture!")
}
}
}
/// On suspend on android, we drop the surface, as it's no longer valid.
///
/// A suspend event is always followed by at least one resume event.
fn suspend(&mut self) {
if cfg!(target_os = "android") {
self.surface = None;
}
}
fn get(&self) -> Option<&Surface> {
self.surface.as_ref()
}
fn config(&self) -> &wgpu::SurfaceConfiguration {
self.config.as_ref().unwrap()
}
}
/// Context containing global wgpu resources.
struct ExampleContext {
instance: wgpu::Instance,
size: winit::dpi::PhysicalSize<u32>,
surface: wgpu::Surface,
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
#[cfg(target_arch = "wasm32")]
offscreen_canvas_setup: Option<OffscreenCanvasSetup>,
}
impl ExampleContext {
/// Initializes the example context.
async fn init_async<E: Example>(surface: &mut SurfaceWrapper, window: &Window) -> Self {
log::info!("Initializing wgpu...");
#[cfg(target_arch = "wasm32")]
struct OffscreenCanvasSetup {
offscreen_canvas: OffscreenCanvas,
bitmap_renderer: ImageBitmapRenderingContext,
}
let backends = wgpu::util::backend_bits_from_env().unwrap_or_default();
let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default();
async fn setup<E: Example>(title: &str) -> Setup {
#[cfg(not(target_arch = "wasm32"))]
{
env_logger::init();
};
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
flags: wgpu::InstanceFlags::from_build_config().with_env(),
dx12_shader_compiler,
gles_minor_version,
});
surface.pre_adapter(&instance, window);
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, surface.get())
.await
.expect("No suitable GPU adapters found on the system!");
let event_loop = EventLoop::new().unwrap();
let mut builder = winit::window::WindowBuilder::new();
builder = builder.with_title(title);
#[cfg(windows_OFF)] // TODO
{
use winit::platform::windows::WindowBuilderExtWindows;
builder = builder.with_no_redirection_bitmap(true);
}
let window = builder.build(&event_loop).unwrap();
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
let query_string = web_sys::window().unwrap().location().search().unwrap();
let level: log::Level = parse_url_query_string(&query_string, "RUST_LOG")
.and_then(|x| x.parse().ok())
.unwrap_or(log::Level::Error);
console_log::init_with_level(level).expect("could not initialize logger");
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
// On wasm, append the canvas to the document body
web_sys::window()
.and_then(|win| win.document())
.and_then(|doc| doc.body())
.and_then(|body| {
body.append_child(&web_sys::Element::from(
window.canvas().expect("Couldn't get canvas"),
))
.ok()
})
.expect("couldn't append canvas to document body");
}
#[cfg(target_arch = "wasm32")]
let mut offscreen_canvas_setup: Option<OffscreenCanvasSetup> = None;
#[cfg(target_arch = "wasm32")]
{
use winit::platform::web::WindowExtWebSys;
let query_string = web_sys::window().unwrap().location().search().unwrap();
if let Some(offscreen_canvas_param) =
parse_url_query_string(&query_string, "offscreen_canvas")
{
if FromStr::from_str(offscreen_canvas_param) == Ok(true) {
log::info!("Creating OffscreenCanvasSetup");
let offscreen_canvas =
OffscreenCanvas::new(1024, 768).expect("couldn't create OffscreenCanvas");
let bitmap_renderer = window
.canvas()
.expect("Couldn't get html canvas")
.get_context("bitmaprenderer")
.expect("couldn't create ImageBitmapRenderingContext (Result)")
.expect("couldn't create ImageBitmapRenderingContext (Option)")
.dyn_into::<ImageBitmapRenderingContext>()
.expect("couldn't convert into ImageBitmapRenderingContext");
offscreen_canvas_setup = Some(OffscreenCanvasSetup {
offscreen_canvas,
bitmap_renderer,
})
}
}
};
log::info!("Initializing the surface...");
let backends = wgpu::util::backend_bits_from_env().unwrap_or_default();
let dx12_shader_compiler = wgpu::util::dx12_shader_compiler_from_env().unwrap_or_default();
let gles_minor_version = wgpu::util::gles_minor_version_from_env().unwrap_or_default();
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends,
flags: wgpu::InstanceFlags::from_build_config().with_env(),
dx12_shader_compiler,
gles_minor_version,
});
let (size, surface) = unsafe {
let size = window.inner_size();
#[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))]
let surface = instance.create_surface(&window).unwrap();
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
let surface = {
if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup {
log::info!("Creating surface from OffscreenCanvas");
instance.create_surface_from_offscreen_canvas(
offscreen_canvas_setup.offscreen_canvas.clone(),
)
} else {
instance.create_surface(&window)
}
}
.unwrap();
(size, surface)
};
let adapter = wgpu::util::initialize_adapter_from_env_or_default(&instance, Some(&surface))
.await
.expect("No suitable GPU adapters found on the system!");
#[cfg(not(target_arch = "wasm32"))]
{
let adapter_info = adapter.get_info();
println!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
}
log::info!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
let optional_features = E::optional_features();
let required_features = E::required_features();
let adapter_features = adapter.features();
assert!(
adapter_features.contains(required_features),
"Adapter does not support required features for this example: {:?}",
required_features - adapter_features
);
let optional_features = E::optional_features();
let required_features = E::required_features();
let adapter_features = adapter.features();
assert!(
adapter_features.contains(required_features),
"Adapter does not support required features for this example: {:?}",
required_features - adapter_features
);
let required_downlevel_capabilities = E::required_downlevel_capabilities();
let downlevel_capabilities = adapter.get_downlevel_capabilities();
assert!(
downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
"Adapter does not support the minimum shader model required to run this example: {:?}",
required_downlevel_capabilities.shader_model
);
assert!(
downlevel_capabilities
.flags
.contains(required_downlevel_capabilities.flags),
"Adapter does not support the downlevel capabilities required to run this example: {:?}",
required_downlevel_capabilities.flags - downlevel_capabilities.flags
);
let required_downlevel_capabilities = E::required_downlevel_capabilities();
let downlevel_capabilities = adapter.get_downlevel_capabilities();
assert!(
downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
"Adapter does not support the minimum shader model required to run this example: {:?}",
required_downlevel_capabilities.shader_model
);
assert!(
downlevel_capabilities
.flags
.contains(required_downlevel_capabilities.flags),
"Adapter does not support the downlevel capabilities required to run this example: {:?}",
required_downlevel_capabilities.flags - downlevel_capabilities.flags
);
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
let needed_limits = E::required_limits().using_resolution(adapter.limits());
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the surface.
let needed_limits = E::required_limits().using_resolution(adapter.limits());
let trace_dir = std::env::var("WGPU_TRACE");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: (optional_features & adapter_features) | required_features,
limits: needed_limits,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
)
.await
.expect("Unable to find a suitable GPU adapter!");
let trace_dir = std::env::var("WGPU_TRACE");
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
features: (optional_features & adapter_features) | required_features,
limits: needed_limits,
},
trace_dir.ok().as_ref().map(std::path::Path::new),
)
.await
.expect("Unable to find a suitable GPU adapter!");
Setup {
_window: window,
event_loop,
instance,
size,
surface,
adapter,
device,
queue,
#[cfg(target_arch = "wasm32")]
offscreen_canvas_setup,
Self {
instance,
adapter,
device,
queue,
}
}
}
fn start<E: Example>(
#[cfg(not(target_arch = "wasm32"))] Setup {
event_loop,
instance,
size,
surface,
adapter,
device,
queue,
..
}: Setup,
#[cfg(target_arch = "wasm32")] Setup {
event_loop,
instance,
size,
surface,
adapter,
device,
queue,
offscreen_canvas_setup,
..
}: Setup,
) {
let mut config = surface
.get_default_config(&adapter, size.width, size.height)
.expect("Surface isn't supported by the adapter.");
let surface_view_format = if E::SRGB {
config.format.add_srgb_suffix()
} else {
config.format.remove_srgb_suffix()
};
config.format = surface_view_format;
config.view_formats.push(surface_view_format);
surface.configure(&device, &config);
struct FrameCounter {
// Instant of the last time we printed the frame time.
last_printed_instant: web_time::Instant,
// Number of frames since the last time we printed the frame time.
frame_count: u32,
}
log::info!("Initializing the example...");
let mut example = E::init(&config, &adapter, &device, &queue);
impl FrameCounter {
fn new() -> Self {
Self {
last_printed_instant: web_time::Instant::now(),
frame_count: 0,
}
}
#[cfg(not(target_arch = "wasm32"))]
let mut last_frame_inst = Instant::now();
#[cfg(not(target_arch = "wasm32"))]
let (mut frame_count, mut accum_time) = (0, 0.0);
fn update(&mut self) {
self.frame_count += 1;
let new_instant = web_time::Instant::now();
let elasped_secs = (new_instant - self.last_printed_instant).as_secs_f32();
if elasped_secs > 1.0 {
let elapsed_ms = elasped_secs * 1000.0;
let frame_time = elapsed_ms / self.frame_count as f32;
let fps = self.frame_count as f32 / elasped_secs;
log::info!("Frame time {:.2}ms ({:.1} FPS)", frame_time, fps);
log::info!("Entering render loop...");
event_loop
.run(move |event, target| {
let _ = (&instance, &adapter); // force ownership by the closure
self.last_printed_instant = new_instant;
self.frame_count = 0;
}
}
}
async fn start<E: Example>(title: &str) {
init_logger();
let window_loop = EventLoopWrapper::new(title);
let mut surface = SurfaceWrapper::new();
let context = ExampleContext::init_async::<E>(&mut surface, &window_loop.window).await;
let mut frame_counter = FrameCounter::new();
// We wait to create the example until we have a valid surface.
let mut example = None;
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use winit::platform::web::EventLoopExtWebSys;
let event_loop_function = EventLoop::spawn;
} else {
let event_loop_function = EventLoop::run;
}
}
log::info!("Entering event loop...");
// On native this is a result, but on wasm it's a unit type.
#[allow(clippy::let_unit_value)]
let _ = (event_loop_function)(
window_loop.event_loop,
move |event: Event<()>, target: &EventLoopWindowTarget<()>| {
// We set to refresh as fast as possible.
target.set_control_flow(ControlFlow::Poll);
if cfg!(feature = "metal-auto-capture") {
target.exit();
};
match event {
event::Event::WindowEvent {
event: WindowEvent::Resized(size),
..
} => {
config.width = size.width.max(1);
config.height = size.height.max(1);
example.resize(&config, &device, &queue);
surface.configure(&device, &config);
ref e if SurfaceWrapper::start_condition(e) => {
surface.resume(&context, &window_loop.window, E::SRGB);
// If we haven't created the example yet, do so now.
if example.is_none() {
example = Some(E::init(
surface.config(),
&context.adapter,
&context.device,
&context.queue,
));
}
}
event::Event::WindowEvent { event, .. } => match event {
Event::Suspended => {
surface.suspend();
}
Event::WindowEvent { event, .. } => match event {
WindowEvent::Resized(size) => {
surface.resize(&context, size);
example.as_mut().unwrap().resize(
surface.config(),
&context.device,
&context.queue,
);
window_loop.window.request_redraw();
}
WindowEvent::KeyboardInput {
event:
KeyEvent {
@ -341,97 +428,42 @@ fn start<E: Example>(
},
..
} if s == "r" => {
println!("{:#?}", instance.generate_report());
println!("{:#?}", context.instance.generate_report());
}
event::WindowEvent::RedrawRequested => {
#[cfg(not(target_arch = "wasm32"))]
{
accum_time += last_frame_inst.elapsed().as_secs_f32();
last_frame_inst = Instant::now();
frame_count += 1;
if frame_count == 100 {
println!(
"Avg frame time {}ms",
accum_time * 1000.0 / frame_count as f32
);
accum_time = 0.0;
frame_count = 0;
}
}
WindowEvent::RedrawRequested => {
frame_counter.update();
let frame = match surface.get_current_texture() {
Ok(frame) => frame,
Err(_) => {
surface.configure(&device, &config);
surface
.get_current_texture()
.expect("Failed to acquire next surface texture!")
}
};
let frame = surface.acquire(&context);
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor {
format: Some(surface_view_format),
format: Some(surface.config().view_formats[0]),
..wgpu::TextureViewDescriptor::default()
});
example.render(&view, &device, &queue);
example
.as_mut()
.unwrap()
.render(&view, &context.device, &context.queue);
frame.present();
#[cfg(target_arch = "wasm32")]
{
if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup {
let image_bitmap = offscreen_canvas_setup
.offscreen_canvas
.transfer_to_image_bitmap()
.expect("couldn't transfer offscreen canvas to image bitmap.");
offscreen_canvas_setup
.bitmap_renderer
.transfer_from_image_bitmap(&image_bitmap);
log::info!("Transferring OffscreenCanvas to ImageBitmapRenderer");
}
}
window_loop.window.request_redraw();
}
_ => example.update(event),
_ => example.as_mut().unwrap().update(event),
},
_ => {}
}
})
.unwrap();
},
);
}
#[cfg(not(target_arch = "wasm32"))]
pub fn run<E: Example>(title: &str) {
let setup = pollster::block_on(setup::<E>(title));
start::<E>(setup);
}
#[cfg(target_arch = "wasm32")]
pub fn run<E: Example>(title: &str) {
let title = title.to_owned();
wasm_bindgen_futures::spawn_local(async move {
let setup = setup::<E>(&title).await;
let start_closure = Closure::once_into_js(move || start::<E>(setup));
// make sure to handle JS exceptions thrown inside start.
// Otherwise wasm_bindgen_futures Queue would break and never handle any tasks again.
// This is required, because winit uses JS exception for control flow to escape from `run`.
if let Err(error) = call_catch(&start_closure) {
let is_control_flow_exception = error.dyn_ref::<js_sys::Error>().map_or(false, |e| {
e.message().includes("Using exceptions for control flow", 0)
});
if !is_control_flow_exception {
web_sys::console::error_1(&error);
}
pub fn run<E: Example>(title: &'static str) {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
wasm_bindgen_futures::spawn_local(async move { start::<E>(title).await })
} else {
pollster::block_on(start::<E>(title));
}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(catch, js_namespace = Function, js_name = "prototype.call.call")]
fn call_catch(this: &JsValue) -> Result<(), JsValue>;
}
});
}
}
#[cfg(target_arch = "wasm32")]

312
xtask/Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]]
name = "anyhow"
version = "1.0.71"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "base64"
@ -25,10 +25,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.4.3"
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cargo-run-wasm"
@ -41,12 +47,6 @@ dependencies = [
"wasm-bindgen-cli-support",
]
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -70,33 +70,19 @@ dependencies = [
[[package]]
name = "errno"
version = "0.3.1"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "1.9.0"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "heck"
@ -107,43 +93,17 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "itoa"
version = "1.0.6"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "leb128"
@ -153,21 +113,21 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.145"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "log"
version = "0.4.18"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "pico-args"
@ -177,29 +137,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]]
name = "proc-macro2"
version = "1.0.59"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.28"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
@ -210,23 +170,22 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.37.25"
version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035"
checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
"bitflags",
"bitflags 2.4.1",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys 0.48.0",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.13"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "safemem"
@ -236,15 +195,29 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "serde"
version = "1.0.163"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.38",
]
[[package]]
name = "serde_json"
version = "1.0.96"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@ -263,23 +236,34 @@ dependencies = [
]
[[package]]
name = "tempfile"
version = "3.5.0"
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
"windows-sys 0.45.0",
"windows-sys",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
@ -310,14 +294,14 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
]
[[package]]
name = "wasm-bindgen-cli-support"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d21c60239a09bf9bab8dfa752be4e6c637db22296b9ded493800090448692da9"
checksum = "f2252adf46913da7b729caf556b81cedd1335165576e6446d84618e8835d89dd"
dependencies = [
"anyhow",
"base64",
@ -337,9 +321,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-externref-xform"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bafbe1984f67cc12645f12ab65e6145e8ddce1ab265d0be58435f25bb0ce2608"
checksum = "43f3b73cf8fcb86da78c6649c74acef205723f57af99b9f549b2609c83fe7815"
dependencies = [
"anyhow",
"walrus",
@ -347,9 +331,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-multi-value-xform"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "581419e3995571a1d2d066e360ca1c0c09da097f5a53c98e6f00d96eddaf0ffe"
checksum = "930dd8e8226379aebb7d512f31b9241a3c59a1801452932e5a15bebfd3b708fb"
dependencies = [
"anyhow",
"walrus",
@ -357,15 +341,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
[[package]]
name = "wasm-bindgen-threads-xform"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e05d272073981137e8426cf2a6830d43d1f84f988a050b2f8b210f0e266b8983"
checksum = "759b1e9784f903a7890bcf147aa7c8c529a6318a2db05f88c054194a3e6c6d57"
dependencies = [
"anyhow",
"walrus",
@ -374,9 +358,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-wasm-conventions"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e9c65b1ff5041ea824ca24c519948aec16fb6611c617d601623c0657dfcd47b"
checksum = "2dc12bc175c837239520b8aa9dcfb68a025fcf56a718a02551a75a972711c816"
dependencies = [
"anyhow",
"walrus",
@ -384,9 +368,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-wasm-interpreter"
version = "0.2.87"
version = "0.2.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c5c796220738ab5d44666f37205728a74141c0039d1166bcf8110b26bafaa1e"
checksum = "6a5510ab88377b4e3160a7e5d90a876d0a1da2d9b9b67495f437246714c0980f"
dependencies = [
"anyhow",
"log",
@ -400,152 +384,86 @@ version = "0.77.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fe3d5405e9ea6c1317a656d6e0820912d8b7b3607823a7596117c8f666daf6f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "xshell"
version = "0.2.3"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "962c039b3a7b16cf4e9a4248397c6585c07547412e7d6a6e035389a802dcfe90"
checksum = "ce2107fe03e558353b4c71ad7626d58ed82efaf56c54134228608893c77023ad"
dependencies = [
"xshell-macros",
]
[[package]]
name = "xshell-macros"
version = "0.2.3"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbabb1cbd15a1d6d12d9ed6b35cc6777d4af87ab3ba155ea37215f20beab80c"
checksum = "7e2c411759b501fb9501aac2b1b2d287a6e93e5bdcf13c25306b23e1b716dd0e"
[[package]]
name = "xtask"