From 8547226176123bc1d37b1dbc777061a4c2134129 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sun, 5 Nov 2023 20:46:18 -0500 Subject: [PATCH] Rewrite Example Event Loop (#4634) --- Cargo.lock | 12 + Cargo.toml | 2 + examples/common/Cargo.toml | 3 + examples/common/src/framework.rs | 682 ++++++++++++++++--------------- xtask/Cargo.lock | 312 ++++++-------- 5 files changed, 489 insertions(+), 522 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9263cc6fa..81e40c462 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index a5ab8ce6f..07a279330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/examples/common/Cargo.toml b/examples/common/Cargo.toml index 7c0d54985..55d928179 100644 --- a/examples/common/Cargo.toml +++ b/examples/common/Cargo.toml @@ -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 diff --git a/examples/common/src/framework.rs b/examples/common/src/framework.rs index bced1eaa8..a1e719a03 100644 --- a/examples/common/src/framework.rs +++ b/examples/common/src/framework.rs @@ -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(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 = 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, + config: Option, +} + +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) { + 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, - surface: wgpu::Surface, adapter: wgpu::Adapter, device: wgpu::Device, queue: wgpu::Queue, - #[cfg(target_arch = "wasm32")] - offscreen_canvas_setup: Option, } +impl ExampleContext { + /// Initializes the example context. + async fn init_async(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(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 = 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::() - .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( - #[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(title: &str) { + init_logger(); + let window_loop = EventLoopWrapper::new(title); + let mut surface = SurfaceWrapper::new(); + let context = ExampleContext::init_async::(&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( }, .. } 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(title: &str) { - let setup = pollster::block_on(setup::(title)); - start::(setup); -} - -#[cfg(target_arch = "wasm32")] -pub fn run(title: &str) { - let title = title.to_owned(); - wasm_bindgen_futures::spawn_local(async move { - let setup = setup::(&title).await; - let start_closure = Closure::once_into_js(move || start::(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::().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(title: &'static str) { + cfg_if::cfg_if! { + if #[cfg(target_arch = "wasm32")] { + wasm_bindgen_futures::spawn_local(async move { start::(title).await }) + } else { + pollster::block_on(start::(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")] diff --git a/xtask/Cargo.lock b/xtask/Cargo.lock index c213ff96b..9ee4a72e9 100644 --- a/xtask/Cargo.lock +++ b/xtask/Cargo.lock @@ -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"