diff --git a/benchmarks/benches/data.rs b/benchmarks/benches/data.rs index 453bbc62..a58677ea 100644 --- a/benchmarks/benches/data.rs +++ b/benchmarks/benches/data.rs @@ -4,7 +4,6 @@ use criterion::{criterion_group, criterion_main, Criterion}; use maplibre::{ benchmarking::io::static_tile_fetcher::StaticTileFetcher, coords::{TileCoords, ZoomLevel}, - error::Error, io::{ pipeline::{PipelineContext, PipelineProcessor, Processable}, tile_pipelines::{ParseTile, TessellateLayer}, diff --git a/benchmarks/benches/render.rs b/benchmarks/benches/render.rs index 1ccc6c24..e74681d4 100644 --- a/benchmarks/benches/render.rs +++ b/benchmarks/benches/render.rs @@ -1,7 +1,6 @@ use criterion::{criterion_group, criterion_main, Criterion}; use maplibre::{ coords::{WorldTileCoords, ZoomLevel}, - error::Error, headless::{create_headless_renderer, map::HeadlessMap}, platform::run_multithreaded, style::Style, @@ -15,32 +14,20 @@ fn headless_render(c: &mut Criterion) { let map = HeadlessMap::new(style, renderer, kernel, false).unwrap(); let tile = map - .fetch_tile( - WorldTileCoords::from((0, 0, ZoomLevel::default())), - &["water"], - ) + .fetch_tile(WorldTileCoords::from((0, 0, ZoomLevel::default()))) .await - .expect("Failed to fetch and process!"); + .expect("Failed to fetch!"); + + let tile = map + .process_tile(tile, &["water"]) + .await + .expect("Failed to process!"); (map, tile) }); - b.to_async( - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(), - ) - .iter(|| { - match map.render_tile(tile.clone()) { - Ok(_) => {} - Err(Error::Render(e)) => { - eprintln!("{}", e); - if e.should_exit() {} - } - e => eprintln!("{:?}", e), - }; - async {} + b.iter(|| { + map.render_tile(tile.clone()); }); }); } diff --git a/maplibre-demo/src/headless.rs b/maplibre-demo/src/headless.rs index f4c13a2c..2f8e4967 100644 --- a/maplibre-demo/src/headless.rs +++ b/maplibre-demo/src/headless.rs @@ -32,17 +32,20 @@ pub async fn run_headless(tile_size: u32, min: LatLon, max: LatLon) { for (z, x, y) in GridIterator::new(10, 10, tile_limits) { let coords = WorldTileCoords::from((x as i32, y as i32, z.into())); println!("Rendering {}", &coords); + + let tile = map.fetch_tile(coords).await.expect("Failed to fetch!"); + let tile = map - .fetch_tile( - coords, + .process_tile( + tile, &requested_layers .iter() .map(|layer| layer.as_str()) .collect::>(), ) .await - .expect("Failed to fetch and process"); + .expect("Failed to process!"); - map.render_tile(tile).expect("Rendering failed"); + map.render_tile(tile); } } diff --git a/maplibre-winit/src/lib.rs b/maplibre-winit/src/lib.rs index f028eeef..139e56bf 100644 --- a/maplibre-winit/src/lib.rs +++ b/maplibre-winit/src/lib.rs @@ -5,8 +5,7 @@ use std::{fmt::Debug, marker::PhantomData}; use instant::Instant; use maplibre::{ environment::Environment, - error::Error, - event_loop::{EventLoop, EventLoopProxy}, + event_loop::{EventLoop, EventLoopProxy, SendEventError}, io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient}, map::Map, window::{HeadedMapWindow, MapWindowConfig}, @@ -84,16 +83,10 @@ impl EventLoop for WinitEventLoop { #[cfg(target_os = "android")] if !map.has_renderer() && event == Event::Resumed { use tokio::{runtime::Handle, task}; - use maplibre::render::settings::WgpuSettings; - use maplibre::render::builder::RendererBuilder; task::block_in_place(|| { Handle::current().block_on(async { - map.initialize_renderer(RendererBuilder::new() - .with_wgpu_settings(WgpuSettings { - backends: Some(maplibre::render::settings::Backends::VULKAN), // FIXME: Change - ..WgpuSettings::default() - })).await.unwrap(); + map.initialize_renderer().await.unwrap(); }) }); return; @@ -146,16 +139,8 @@ impl EventLoop for WinitEventLoop { input_controller.update_state(map_context, dt); } - match map.run_schedule() { - Ok(_) => {} - Err(Error::Render(e)) => { - eprintln!("{}", e); - if e.should_exit() { - *control_flow = ControlFlow::Exit; - } - } - e => eprintln!("{:?}", e) - }; + // TODO: Maybe handle gracefully + map.run_schedule().expect("Failed to run schedule!"); if let Some(max_frames) = max_frames { if current_frame >= max_frames { @@ -193,8 +178,10 @@ pub struct WinitEventLoopProxy { } impl EventLoopProxy for WinitEventLoopProxy { - fn send_event(&self, event: ET) -> Result<(), Error> { - self.proxy.send_event(event).map_err(|e| Error::EventLoop) + fn send_event(&self, event: ET) -> Result<(), SendEventError> { + self.proxy + .send_event(event) + .map_err(|_e| SendEventError::Closed) } } diff --git a/maplibre-winit/src/noweb.rs b/maplibre-winit/src/noweb.rs index ff8c9f36..ec5de0a3 100644 --- a/maplibre-winit/src/noweb.rs +++ b/maplibre-winit/src/noweb.rs @@ -11,6 +11,7 @@ use maplibre::{ kernel::{Kernel, KernelBuilder}, map::Map, platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler}, + render::{builder::RendererBuilder, settings::WgpuSettings}, style::Style, window::{MapWindow, MapWindowConfig, WindowSize}, }; @@ -82,18 +83,16 @@ pub fn run_headed_map(cache_path: Option) { .with_scheduler(TokioScheduler::new()) .build(); - let mut map = Map::new(Style::default(), kernel).unwrap(); + let renderer_builder = RendererBuilder::new().with_wgpu_settings(WgpuSettings { + backends: Some(maplibre::render::settings::Backends::all()), + ..WgpuSettings::default() + }); + + let mut map = Map::new(Style::default(), kernel, renderer_builder).unwrap(); #[cfg(not(target_os = "android"))] { - use maplibre::render::{builder::RendererBuilder, settings::WgpuSettings}; - - map.initialize_renderer(RendererBuilder::new().with_wgpu_settings(WgpuSettings { - backends: Some(maplibre::render::settings::Backends::VULKAN), // FIXME: Change - ..WgpuSettings::default() - })) - .await - .unwrap(); + map.initialize_renderer().await.unwrap(); } map.window_mut() diff --git a/maplibre/Cargo.toml b/maplibre/Cargo.toml index f4c516ae..a3dae933 100644 --- a/maplibre/Cargo.toml +++ b/maplibre/Cargo.toml @@ -66,6 +66,7 @@ log = "0.4.17" # Utils bytemuck = "1.12.1" bytemuck_derive = "1.2.1" +thiserror = "1.0" # Static tiles inclusion include_dir = "0.7.2" @@ -79,7 +80,6 @@ csscolorparser = { version = "0.6.2", features = ["serde", "cint"] } cint = "0.3.1" # Required by bevy renderer -thiserror = "1.0.32" downcast-rs = "1.2.0" smallvec = "1.9.0" diff --git a/maplibre/src/coords.rs b/maplibre/src/coords.rs index 8ff77620..e0426462 100644 --- a/maplibre/src/coords.rs +++ b/maplibre/src/coords.rs @@ -127,9 +127,9 @@ impl From for ZoomLevel { } } -impl Into for ZoomLevel { - fn into(self) -> u8 { - self.0 +impl From for u8 { + fn from(val: ZoomLevel) -> Self { + val.0 } } diff --git a/maplibre/src/error.rs b/maplibre/src/error.rs deleted file mode 100644 index 55a8ddf9..00000000 --- a/maplibre/src/error.rs +++ /dev/null @@ -1,40 +0,0 @@ -//! Errors which can happen in various parts of the library. - -use std::{borrow::Cow, sync::mpsc::SendError}; - -use lyon::tessellation::TessellationError; - -use crate::render::error::RenderError; - -/// Enumeration of errors which can happen during the operation of the library. -#[derive(Debug)] -pub enum Error { - APC, - Scheduler, - EventLoop, - Network(String), - Tesselation(TessellationError), - Render(RenderError), - Generic(Cow<'static, str>), -} - -impl From for Error -where - E: Into, -{ - fn from(e: E) -> Self { - Error::Render(e.into()) - } -} - -impl From for Error { - fn from(e: TessellationError) -> Self { - Error::Tesselation(e) - } -} - -impl From> for Error { - fn from(_e: SendError) -> Self { - Error::Scheduler - } -} diff --git a/maplibre/src/event_loop.rs b/maplibre/src/event_loop.rs index 56ef6ccc..12f710c5 100644 --- a/maplibre/src/event_loop.rs +++ b/maplibre/src/event_loop.rs @@ -1,6 +1,7 @@ +use thiserror::Error; + use crate::{ environment::Environment, - error::Error, map::Map, window::{HeadedMapWindow, MapWindowConfig}, }; @@ -12,8 +13,16 @@ pub trait EventLoopConfig { fn create_proxy() -> Self::EventLoopProxy; } +/// When sending events to an event loop errors can occur. +#[derive(Error, Debug)] +pub enum SendEventError { + /// The event loop was already closed + #[error("event loop is closed")] + Closed, +} + pub trait EventLoopProxy { - fn send_event(&self, event: T) -> Result<(), Error>; + fn send_event(&self, event: T) -> Result<(), SendEventError>; } pub trait EventLoop { diff --git a/maplibre/src/headless/map.rs b/maplibre/src/headless/map.rs index da682b35..37916e55 100644 --- a/maplibre/src/headless/map.rs +++ b/maplibre/src/headless/map.rs @@ -3,18 +3,19 @@ use std::collections::HashSet; use crate::{ context::MapContext, coords::{WorldCoords, WorldTileCoords, Zoom, TILE_SIZE}, - error::Error, headless::{ environment::HeadlessEnvironment, graph_node::CopySurfaceBufferNode, stage::WriteSurfaceBufferStage, }, io::{ - pipeline::{PipelineContext, PipelineProcessor, Processable}, + pipeline::{PipelineContext, PipelineError, PipelineProcessor, Processable}, + source_client::SourceFetchError, tile_pipelines::build_vector_tile_pipeline, tile_repository::{StoredLayer, StoredTile}, RawLayer, TileRequest, }, kernel::Kernel, + map::MapError, render::{ create_default_render_graph, draw_graph, eventually::Eventually, register_default_render_stages, stages::RenderStageLabel, Renderer, ShaderVertex, @@ -37,7 +38,7 @@ impl HeadlessMap { renderer: Renderer, kernel: Kernel, write_to_disk: bool, - ) -> Result { + ) -> Result { let window_size = renderer.state().surface().size(); let world = World::new( @@ -47,7 +48,7 @@ impl HeadlessMap { cgmath::Deg(0.0), ); - let mut graph = create_default_render_graph()?; + let mut graph = create_default_render_graph().map_err(|e| MapError::RenderGraphInit(e))?; let draw_graph = graph .get_sub_graph_mut(draw_graph::NAME) .expect("Subgraph does not exist"); @@ -74,7 +75,7 @@ impl HeadlessMap { }) } - pub fn render_tile(&mut self, tile: StoredTile) -> Result<(), Error> { + pub fn render_tile(&mut self, tile: StoredTile) { let context = &mut self.map_context; if let Eventually::Initialized(pool) = context.renderer.state.buffer_pool_mut() { @@ -88,18 +89,19 @@ impl HeadlessMap { context.world.tile_repository.put_tile(tile); self.schedule.run(&mut self.map_context); - Ok(()) } - pub async fn fetch_tile( - &self, - coords: WorldTileCoords, - source_layers: &[&str], - ) -> Result { + pub async fn fetch_tile(&self, coords: WorldTileCoords) -> Result, SourceFetchError> { let source_client = self.kernel.source_client(); - let data = source_client.fetch(&coords).await?.into_boxed_slice(); + Ok(source_client.fetch(&coords).await?.into_boxed_slice()) + } + pub async fn process_tile( + &self, + tile_data: Box<[u8]>, + source_layers: &[&str], + ) -> Result { let mut pipeline_context = PipelineContext::new(HeadlessPipelineProcessor::default()); let pipeline = build_vector_tile_pipeline(); @@ -113,7 +115,7 @@ impl HeadlessMap { .map(|layer| layer.to_string()) .collect::>(), }, - data, + tile_data, ), &mut pipeline_context, )?; @@ -138,7 +140,7 @@ impl PipelineProcessor for HeadlessPipelineProcessor { buffer: OverAlignedVertexBuffer, feature_indices: Vec, layer_data: RawLayer, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { self.layers.push(StoredLayer::TessellatedLayer { coords: *coords, layer_name: layer_data.name, diff --git a/maplibre/src/headless/stage.rs b/maplibre/src/headless/stage.rs index a11a3713..6dda3a3c 100644 --- a/maplibre/src/headless/stage.rs +++ b/maplibre/src/headless/stage.rs @@ -1,7 +1,5 @@ use std::sync::Arc; -use tokio::{runtime::Handle, task}; - use crate::{ context::MapContext, render::{ @@ -44,19 +42,21 @@ impl Stage for WriteSurfaceBufferStage { let device = device.clone(); let current_frame = self.frame; + let buffer_slice = buffered_texture.map_async(&device); + let padded_buffer = buffer_slice.get_mapped_range(); + if self.write_to_disk { - task::block_in_place(|| { - Handle::current().block_on(async { - buffered_texture - .create_png( - &device, - format!("frame_{}.png", current_frame).as_str(), - ) - .await; - }) - }); + buffered_texture.write_png( + &padded_buffer, + format!("frame_{}.png", current_frame).as_str(), + ); } + // With the current interface, we have to make sure all mapped views are + // dropped before we unmap the buffer. + drop(padded_buffer); + buffered_texture.unmap(); + self.frame += 1; } } diff --git a/maplibre/src/io/apc.rs b/maplibre/src/io/apc.rs index 4e7e5be5..1c7df9d5 100644 --- a/maplibre/src/io/apc.rs +++ b/maplibre/src/io/apc.rs @@ -8,15 +8,13 @@ use std::{ }; use serde::{Deserialize, Serialize}; +use thiserror::Error; -use crate::{ - error::Error, - io::{ - scheduler::Scheduler, - source_client::{HttpClient, HttpSourceClient, SourceClient}, - transferables::{DefaultTransferables, Transferables}, - TileRequest, - }, +use crate::io::{ + scheduler::Scheduler, + source_client::{HttpClient, HttpSourceClient, SourceClient}, + transferables::{DefaultTransferables, Transferables}, + TileRequest, }; /// The result of the tessellation of a tile. This is sent as a message from a worker to the caller @@ -41,18 +39,47 @@ pub enum Input { NotYetImplemented, // TODO: Placeholder, should be removed when second input is added } +#[derive(Error, Debug)] +pub enum SendError { + #[error("could not transmit data")] + Transmission, +} + /// Allows sending messages from workers to back to the caller. pub trait Context: Send + 'static { /// Send a message back to the caller. - fn send(&self, data: Message) -> Result<(), Error>; + fn send(&self, data: Message) -> Result<(), SendError>; fn source_client(&self) -> &SourceClient; } +#[derive(Error, Debug)] +pub enum ProcedureError { + /// The [`Input`] is not compatible with the procedure + #[error("provided input is not compatible with procedure")] + IncompatibleInput, + #[error("execution of procedure failed")] + Execution(Box), + #[error("sending data failed")] + Send(SendError), +} + #[cfg(feature = "thread-safe-futures")] -pub type AsyncProcedureFuture = Pin> + Send + 'static)>>; +pub type AsyncProcedureFuture = + Pin> + Send + 'static)>>; #[cfg(not(feature = "thread-safe-futures"))] -pub type AsyncProcedureFuture = Pin> + 'static)>>; +pub type AsyncProcedureFuture = + Pin> + 'static)>>; + +#[derive(Error, Debug)] +pub enum CallError { + #[error("scheduling work failed")] + Schedule, + #[error("serializing data failed")] + Serialize(Box), + #[error("deserializing failed")] + Deserialize(Box), +} /// Type definitions for asynchronous procedure calls. These functions can be called in an /// [`AsyncProcedureCall`]. Functions of this type are required to be statically available at @@ -109,7 +136,8 @@ pub trait AsyncProcedureCall: 'static { /// Call an [`AsyncProcedure`] using some [`Input`]. This function is non-blocking and /// returns immediately. - fn call(&self, input: Input, procedure: AsyncProcedure); + fn call(&self, input: Input, procedure: AsyncProcedure) + -> Result<(), CallError>; } #[derive(Clone)] @@ -119,8 +147,8 @@ pub struct SchedulerContext { } impl Context for SchedulerContext { - fn send(&self, data: Message) -> Result<(), Error> { - self.sender.send(data).map_err(|_e| Error::APC) + fn send(&self, data: Message) -> Result<(), SendError> { + self.sender.send(data).map_err(|_e| SendError::Transmission) } fn source_client(&self) -> &SourceClient { @@ -156,13 +184,17 @@ impl AsyncProcedureCall for SchedulerAsyncProc Some(transferred) } - fn call(&self, input: Input, procedure: AsyncProcedure) { + fn call( + &self, + input: Input, + procedure: AsyncProcedure, + ) -> Result<(), CallError> { let sender = self.channel.0.clone(); let client = self.http_client.clone(); // TODO (perf): do not clone each time self.scheduler .schedule(move || async move { - (procedure)( + procedure( input, SchedulerContext { sender, @@ -172,6 +204,6 @@ impl AsyncProcedureCall for SchedulerAsyncProc .await .unwrap(); }) - .unwrap(); + .map_err(|_e| CallError::Schedule) } } diff --git a/maplibre/src/io/pipeline.rs b/maplibre/src/io/pipeline.rs index 909cb877..580c7c4b 100644 --- a/maplibre/src/io/pipeline.rs +++ b/maplibre/src/io/pipeline.rs @@ -2,25 +2,35 @@ use std::marker::PhantomData; use downcast_rs::Downcast; use geozero::mvt::tile; +use thiserror::Error; use crate::{ coords::WorldTileCoords, - error::Error, - io::geometry_index::IndexedGeometry, + io::{apc::SendError, geometry_index::IndexedGeometry}, render::ShaderVertex, tessellation::{IndexDataType, OverAlignedVertexBuffer}, }; +#[derive(Error, Debug)] +pub enum PipelineError { + /// Sending of results failed + #[error("sending data back from pipeline failed")] + SendError(SendError), + /// Error during processing of the pipeline + #[error("processing data in pipeline failed")] + Processing(Box), +} + /// Processes events which happen during the pipeline execution pub trait PipelineProcessor: Downcast { - fn tile_finished(&mut self, _coords: &WorldTileCoords) -> Result<(), Error> { + fn tile_finished(&mut self, _coords: &WorldTileCoords) -> Result<(), PipelineError> { Ok(()) } fn layer_unavailable( &mut self, _coords: &WorldTileCoords, _layer_name: &str, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { Ok(()) } fn layer_tesselation_finished( @@ -29,14 +39,14 @@ pub trait PipelineProcessor: Downcast { _buffer: OverAlignedVertexBuffer, _feature_indices: Vec, _layer_data: tile::Layer, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { Ok(()) } fn layer_indexing_finished( &mut self, _coords: &WorldTileCoords, _geometries: Vec>, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { Ok(()) } } @@ -76,7 +86,7 @@ pub trait Processable { &self, input: Self::Input, context: &mut PipelineContext, - ) -> Result; + ) -> Result; } /// A pipeline which consists of multiple steps. Steps are [`Processable`] workloads. Later steps @@ -112,7 +122,7 @@ where &self, input: Self::Input, context: &mut PipelineContext, - ) -> Result { + ) -> Result { let output = self.step.process(input, context)?; self.next_step.process(output, context) } @@ -139,7 +149,7 @@ impl Processable for PipelineEnd { &self, input: Self::Input, _context: &mut PipelineContext, - ) -> Result { + ) -> Result { Ok(input) } } @@ -152,7 +162,7 @@ impl Processable for &fn(input: I, context: &mut PipelineContext) -> O { &self, input: Self::Input, context: &mut PipelineContext, - ) -> Result { + ) -> Result { Ok((self)(input, context)) } } @@ -165,7 +175,7 @@ impl Processable for fn(input: I, context: &mut PipelineContext) -> O { &self, input: Self::Input, context: &mut PipelineContext, - ) -> Result { + ) -> Result { Ok((self)(input, context)) } } @@ -203,7 +213,7 @@ where &self, input: Self::Input, context: &mut PipelineContext, - ) -> Result { + ) -> Result { Ok((self.func)(input, context)) } } diff --git a/maplibre/src/io/scheduler.rs b/maplibre/src/io/scheduler.rs index 99f09551..eeec9f87 100644 --- a/maplibre/src/io/scheduler.rs +++ b/maplibre/src/io/scheduler.rs @@ -2,7 +2,15 @@ use std::future::Future; -use crate::error::Error; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ScheduleError { + #[error("scheduling work failed")] + Scheduling(Box), + #[error("scheduler is not implemented on this platform")] + NotImplemented, +} /// Async/await scheduler. /// Can schedule a task from a future factory and a shared state. @@ -11,7 +19,7 @@ pub trait Scheduler: 'static { fn schedule( &self, future_factory: impl (FnOnce() -> T) + Send + 'static, - ) -> Result<(), Error> + ) -> Result<(), ScheduleError> where T: Future + Send + 'static; @@ -19,7 +27,7 @@ pub trait Scheduler: 'static { fn schedule( &self, future_factory: impl (FnOnce() -> T) + Send + 'static, - ) -> Result<(), Error> + ) -> Result<(), ScheduleError> where T: Future + 'static; } @@ -27,10 +35,13 @@ pub trait Scheduler: 'static { pub struct NopScheduler; impl Scheduler for NopScheduler { - fn schedule(&self, _future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error> + fn schedule( + &self, + _future_factory: impl FnOnce() -> T + Send + 'static, + ) -> Result<(), ScheduleError> where T: Future + 'static, { - Err(Error::Scheduler) + Err(ScheduleError::NotImplemented) } } diff --git a/maplibre/src/io/source_client.rs b/maplibre/src/io/source_client.rs index 54de0593..eb882788 100644 --- a/maplibre/src/io/source_client.rs +++ b/maplibre/src/io/source_client.rs @@ -1,8 +1,9 @@ //! HTTP client. use async_trait::async_trait; +use thiserror::Error; -use crate::{coords::WorldTileCoords, error::Error, style::source::TileAddressingScheme}; +use crate::{coords::WorldTileCoords, style::source::TileAddressingScheme}; /// A closure that returns a HTTP client. pub type HTTPClientFactory = dyn Fn() -> HC; @@ -16,7 +17,7 @@ pub type HTTPClientFactory = dyn Fn() -> HC; #[cfg_attr(not(feature = "thread-safe-futures"), async_trait(?Send))] #[cfg_attr(feature = "thread-safe-futures", async_trait)] pub trait HttpClient: Clone + Sync + Send + 'static { - async fn fetch(&self, url: &str) -> Result, Error>; + async fn fetch(&self, url: &str) -> Result, SourceFetchError>; } /// Gives access to the HTTP client which can be of multiple types, @@ -29,6 +30,10 @@ where inner_client: HC, } +#[derive(Error, Debug)] +#[error("failed to fetch from source")] +pub struct SourceFetchError(#[source] pub Box); + /// Defines the different types of HTTP clients such as basic HTTP and Mbtiles. /// More types might be coming such as S3 and other cloud http clients. #[derive(Clone)] @@ -47,7 +52,7 @@ where Self { http } } - pub async fn fetch(&self, coords: &WorldTileCoords) -> Result, Error> { + pub async fn fetch(&self, coords: &WorldTileCoords) -> Result, SourceFetchError> { self.http.fetch(coords).await } } @@ -62,7 +67,7 @@ where } } - pub async fn fetch(&self, coords: &WorldTileCoords) -> Result, Error> { + pub async fn fetch(&self, coords: &WorldTileCoords) -> Result, SourceFetchError> { let tile_coords = coords.into_tile(TileAddressingScheme::TMS).unwrap(); self.inner_client .fetch( diff --git a/maplibre/src/io/static_tile_fetcher.rs b/maplibre/src/io/static_tile_fetcher.rs index 7b9fa39f..53d0c9b4 100644 --- a/maplibre/src/io/static_tile_fetcher.rs +++ b/maplibre/src/io/static_tile_fetcher.rs @@ -1,17 +1,32 @@ //! Static tile fetcher -use std::{concat, env}; +use std::{ + concat, env, + fmt::{Display, Formatter}, +}; #[cfg(static_tiles_found)] use include_dir::include_dir; use include_dir::Dir; -use crate::{coords::TileCoords, error::Error}; +use crate::coords::TileCoords; #[cfg(static_tiles_found)] static TILES: Dir = include_dir!("$OUT_DIR/extracted-tiles"); #[cfg(not(static_tiles_found))] static TILES: Dir = Dir::new("/path", &[]); +#[derive(Debug)] +pub enum StaticFetchError { + /// Tile was not found in the static content + NotFound, +} + +impl Display for StaticFetchError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + /// Load PBF files which were statically embedded in the `build.rs` #[derive(Default)] pub struct StaticTileFetcher; @@ -25,15 +40,15 @@ impl StaticTileFetcher { Self {} } - /// Fetch the tile static file asynchrounously and returns a vector of bytes or a network error if the file + /// Fetch the tile static file asynchronously and returns a vector of bytes or a network error if the file /// could not be fetched. - pub async fn fetch_tile(&self, coords: &TileCoords) -> Result, Error> { + pub async fn fetch_tile(&self, coords: &TileCoords) -> Result, StaticFetchError> { self.sync_fetch_tile(coords) } /// Fetch the tile static file and returns a vector of bytes or a network error if the file /// could not be fetched. - pub fn sync_fetch_tile(&self, coords: &TileCoords) -> Result, Error> { + pub fn sync_fetch_tile(&self, coords: &TileCoords) -> Result, StaticFetchError> { if TILES.entries().is_empty() { panic!( "There are not tiles statically embedded in this binary! StaticTileFetcher will \ @@ -43,9 +58,7 @@ impl StaticTileFetcher { let tile = TILES .get_file(format!("{}/{}/{}.{}", coords.z, coords.x, coords.y, "pbf")) - .ok_or_else(|| { - Error::Network("Failed to load tile from within the binary".to_string()) - })?; + .ok_or_else(|| StaticFetchError::NotFound)?; Ok(Vec::from(tile.contents())) } } diff --git a/maplibre/src/io/tile_pipelines.rs b/maplibre/src/io/tile_pipelines.rs index eb0b090b..b603b140 100644 --- a/maplibre/src/io/tile_pipelines.rs +++ b/maplibre/src/io/tile_pipelines.rs @@ -4,10 +4,9 @@ use geozero::GeozeroDatasource; use prost::Message; use crate::{ - error::Error, io::{ geometry_index::IndexProcessor, - pipeline::{DataPipeline, PipelineContext, PipelineEnd, Processable}, + pipeline::{DataPipeline, PipelineContext, PipelineEnd, PipelineError, Processable}, TileRequest, }, tessellation::{zero_tessellator::ZeroTessellator, IndexDataType}, @@ -24,7 +23,7 @@ impl Processable for ParseTile { &self, (tile_request, data): Self::Input, _context: &mut PipelineContext, - ) -> Result { + ) -> Result { let tile = geozero::mvt::Tile::decode(data.as_ref()).expect("failed to load tile"); Ok((tile_request, tile)) } @@ -41,7 +40,7 @@ impl Processable for IndexLayer { &self, (tile_request, mut tile): Self::Input, context: &mut PipelineContext, - ) -> Result { + ) -> Result { let mut index = IndexProcessor::new(); for layer in &mut tile.layers { @@ -70,7 +69,7 @@ impl Processable for TessellateLayer { &self, (tile_request, mut tile): Self::Input, context: &mut PipelineContext, - ) -> Result { + ) -> Result { let coords = &tile_request.coords; for layer in &mut tile.layers { diff --git a/maplibre/src/lib.rs b/maplibre/src/lib.rs index 2f7dd672..6a0b962d 100644 --- a/maplibre/src/lib.rs +++ b/maplibre/src/lib.rs @@ -23,7 +23,6 @@ pub(crate) mod tessellation; pub mod context; pub mod coords; -pub mod error; #[cfg(feature = "headless")] pub mod headless; pub mod io; diff --git a/maplibre/src/map.rs b/maplibre/src/map.rs index 0e5b48ab..a2efcfd7 100644 --- a/maplibre/src/map.rs +++ b/maplibre/src/map.rs @@ -1,16 +1,19 @@ use std::rc::Rc; +use thiserror::Error; + use crate::{ context::MapContext, coords::{LatLon, Zoom}, environment::Environment, - error::Error, kernel::Kernel, render::{ builder::{ InitializationResult, InitializedRenderer, RendererBuilder, UninitializedRenderer, }, - create_default_render_graph, register_default_render_stages, + create_default_render_graph, + graph::RenderGraphError, + register_default_render_stages, }, schedule::{Schedule, Stage}, stages::register_stages, @@ -19,9 +22,23 @@ use crate::{ world::World, }; +#[derive(Error, Debug)] +pub enum MapError { + /// No need to set renderer again + #[error("renderer was already set for this map")] + RendererAlreadySet, + #[error("initializing render graph failed")] + RenderGraphInit(RenderGraphError), + #[error("initializing device failed")] + DeviceInit, +} + pub enum MapContextState { Ready(MapContext), - Pending { style: Style }, + Pending { + style: Style, + renderer_builder: RendererBuilder, + }, } pub struct Map { @@ -35,7 +52,11 @@ impl Map where <::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow, { - pub fn new(style: Style, kernel: Kernel) -> Result { + pub fn new( + style: Style, + kernel: Kernel, + renderer_builder: RendererBuilder, + ) -> Result { let mut schedule = Schedule::default(); let graph = create_default_render_graph().unwrap(); // TODO: Remove unwrap @@ -49,26 +70,30 @@ where let map = Self { kernel, - map_context: MapContextState::Pending { style }, + map_context: MapContextState::Pending { + style, + renderer_builder, + }, schedule, window, }; Ok(map) } - pub async fn initialize_renderer( - &mut self, - render_builder: RendererBuilder, - ) -> Result<(), Error> { - let result = render_builder - .build() - .initialize_renderer::(&self.window) - .await - .expect("Failed to initialize renderer"); - + pub async fn initialize_renderer(&mut self) -> Result<(), MapError> { match &mut self.map_context { - MapContextState::Ready(_) => Err(Error::Generic("Renderer is already set".into())), - MapContextState::Pending { style } => { + MapContextState::Ready(_) => Err(MapError::RendererAlreadySet), + MapContextState::Pending { + style, + renderer_builder, + } => { + let init_result = renderer_builder + .clone() // Cloning because we want to be able to build multiple times maybe + .build() + .initialize_renderer::(&self.window) + .await + .map_err(|e| MapError::DeviceInit)?; + let window_size = self.window.size(); let center = style.center.unwrap_or_default(); @@ -80,7 +105,7 @@ where cgmath::Deg::(style.pitch.unwrap_or_default()), ); - match result { + match init_result { InitializationResult::Initialized(InitializedRenderer { renderer, .. }) => { self.map_context = MapContextState::Ready(MapContext { world, @@ -111,33 +136,27 @@ where } #[tracing::instrument(name = "update_and_redraw", skip_all)] - pub fn run_schedule(&mut self) -> Result<(), Error> { + pub fn run_schedule(&mut self) -> Result<(), MapError> { match &mut self.map_context { MapContextState::Ready(map_context) => { self.schedule.run(map_context); Ok(()) } - MapContextState::Pending { .. } => { - Err(Error::Generic("Renderer is already set".into())) - } + MapContextState::Pending { .. } => Err(MapError::RendererAlreadySet), } } - pub fn context(&self) -> Result<&MapContext, Error> { + pub fn context(&self) -> Result<&MapContext, MapError> { match &self.map_context { MapContextState::Ready(map_context) => Ok(map_context), - MapContextState::Pending { .. } => { - Err(Error::Generic("Renderer is already set".into())) - } + MapContextState::Pending { .. } => Err(MapError::RendererAlreadySet), } } - pub fn context_mut(&mut self) -> Result<&mut MapContext, Error> { + pub fn context_mut(&mut self) -> Result<&mut MapContext, MapError> { match &mut self.map_context { MapContextState::Ready(map_context) => Ok(map_context), - MapContextState::Pending { .. } => { - Err(Error::Generic("Renderer is already set".into())) - } + MapContextState::Pending { .. } => Err(MapError::RendererAlreadySet), } } diff --git a/maplibre/src/platform/noweb/http_client.rs b/maplibre/src/platform/noweb/http_client.rs index b5941dcd..6db5fd5a 100644 --- a/maplibre/src/platform/noweb/http_client.rs +++ b/maplibre/src/platform/noweb/http_client.rs @@ -3,21 +3,22 @@ use reqwest::{Client, StatusCode}; use reqwest_middleware::ClientWithMiddleware; use reqwest_middleware_cache::{managers::CACacheManager, Cache, CacheMode}; -use crate::{error::Error, io::source_client::HttpClient}; +use crate::io::source_client::{HttpClient, SourceFetchError}; #[derive(Clone)] pub struct ReqwestHttpClient { client: ClientWithMiddleware, } -impl From for Error { + +impl From for SourceFetchError { fn from(err: reqwest::Error) -> Self { - Error::Network(err.to_string()) + SourceFetchError(Box::new(err)) } } -impl From for Error { +impl From for SourceFetchError { fn from(err: reqwest_middleware::Error) -> Self { - Error::Network(err.to_string()) + SourceFetchError(Box::new(err)) } } @@ -43,7 +44,7 @@ impl ReqwestHttpClient { #[cfg_attr(not(feature = "thread-safe-futures"), async_trait(?Send))] #[cfg_attr(feature = "thread-safe-futures", async_trait)] impl HttpClient for ReqwestHttpClient { - async fn fetch(&self, url: &str) -> Result, Error> { + async fn fetch(&self, url: &str) -> Result, SourceFetchError> { let response = self.client.get(url).send().await?; match response.error_for_status() { Ok(response) => { @@ -54,7 +55,7 @@ impl HttpClient for ReqwestHttpClient { let body = response.bytes().await?; Ok(Vec::from(body.as_ref())) } - Err(e) => Err(Error::Network(e.to_string())), + Err(e) => Err(SourceFetchError(Box::new(e))), } } } diff --git a/maplibre/src/platform/noweb/scheduler.rs b/maplibre/src/platform/noweb/scheduler.rs index 3999959d..0d7a249d 100644 --- a/maplibre/src/platform/noweb/scheduler.rs +++ b/maplibre/src/platform/noweb/scheduler.rs @@ -1,6 +1,6 @@ use std::future::Future; -use crate::{error::Error, io::scheduler::Scheduler}; +use crate::io::scheduler::{ScheduleError, Scheduler}; /// Multi-threading with Tokio. pub struct TokioScheduler; @@ -13,7 +13,10 @@ impl TokioScheduler { impl Scheduler for TokioScheduler { #[cfg(feature = "thread-safe-futures")] - fn schedule(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error> + fn schedule( + &self, + future_factory: impl FnOnce() -> T + Send + 'static, + ) -> Result<(), ScheduleError> where T: Future + Send + 'static, { @@ -23,7 +26,10 @@ impl Scheduler for TokioScheduler { // FIXME: Provide a working implementation #[cfg(not(feature = "thread-safe-futures"))] - fn schedule(&self, _future_factory: impl FnOnce() -> T + 'static) -> Result<(), Error> + fn schedule( + &self, + _future_factory: impl FnOnce() -> T + 'static, + ) -> Result<(), ScheduleError> where T: Future + 'static, { diff --git a/maplibre/src/render/builder.rs b/maplibre/src/render/builder.rs index 2c079171..dc92521f 100644 --- a/maplibre/src/render/builder.rs +++ b/maplibre/src/render/builder.rs @@ -1,12 +1,13 @@ use crate::{ - error::Error, render::{ + error::RenderError, settings::{RendererSettings, WgpuSettings}, Renderer, }, window::{HeadedMapWindow, MapWindowConfig}, }; +#[derive(Clone)] pub struct RendererBuilder { wgpu_settings: Option, renderer_settings: Option, @@ -87,7 +88,7 @@ impl UninitializedRenderer { pub async fn initialize_renderer( self, existing_window: &MWC::MapWindow, - ) -> Result + ) -> Result where MWC: MapWindowConfig, ::MapWindow: HeadedMapWindow, @@ -109,7 +110,7 @@ impl UninitializedRenderer { pub(crate) async fn initialize_headless( self, existing_window: &MWC::MapWindow, - ) -> Result + ) -> Result where MWC: MapWindowConfig, { diff --git a/maplibre/src/render/error.rs b/maplibre/src/render/error.rs index 3bd2c81c..afe3153f 100644 --- a/maplibre/src/render/error.rs +++ b/maplibre/src/render/error.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{error::Error, render::graph::RenderGraphError}; +use crate::render::graph::RenderGraphError; #[derive(Debug)] pub enum RenderError { @@ -37,14 +37,14 @@ impl From for RenderError { } } -impl From for Error { +impl From for RenderError { fn from(e: wgpu::SurfaceError) -> Self { - Error::Render(RenderError::Surface(e)) + RenderError::Surface(e) } } -impl From for Error { +impl From for RenderError { fn from(e: wgpu::RequestDeviceError) -> Self { - Error::Render(RenderError::Device(e)) + RenderError::Device(e) } } diff --git a/maplibre/src/render/graph/node_slot.rs b/maplibre/src/render/graph/node_slot.rs index 1fcd0b33..f306daf7 100644 --- a/maplibre/src/render/graph/node_slot.rs +++ b/maplibre/src/render/graph/node_slot.rs @@ -159,14 +159,14 @@ impl SlotInfos { /// Retrieves the [`SlotInfo`] for the provided label. pub fn get_slot(&self, label: impl Into) -> Option<&SlotInfo> { let label = label.into(); - let index = self.get_slot_index(&label)?; + let index = self.get_slot_index(label)?; self.slots.get(index) } /// Retrieves the [`SlotInfo`] for the provided label mutably. pub fn get_slot_mut(&mut self, label: impl Into) -> Option<&mut SlotInfo> { let label = label.into(); - let index = self.get_slot_index(&label)?; + let index = self.get_slot_index(label)?; self.slots.get_mut(index) } diff --git a/maplibre/src/render/mod.rs b/maplibre/src/render/mod.rs index 691ff07f..672a9ccb 100644 --- a/maplibre/src/render/mod.rs +++ b/maplibre/src/render/mod.rs @@ -20,8 +20,6 @@ use std::sync::Arc; -use log::info; - use crate::{ render::{ eventually::Eventually, @@ -246,10 +244,9 @@ impl Renderer { let adapter = instance .request_adapter(request_adapter_options) .await - .expect("Unable to find a GPU! Make sure you have installed required drivers!"); + .ok_or_else(|| wgpu::RequestDeviceError)?; let adapter_info = adapter.get_info(); - info!("{:?}", adapter_info); #[cfg(not(target_arch = "wasm32"))] let trace_path = if settings.record_trace { diff --git a/maplibre/src/render/resource/surface.rs b/maplibre/src/render/resource/surface.rs index a168d43b..3d46edb8 100644 --- a/maplibre/src/render/resource/surface.rs +++ b/maplibre/src/render/resource/surface.rs @@ -67,13 +67,7 @@ pub struct BufferedTextureHead { #[cfg(feature = "headless")] impl BufferedTextureHead { - pub async fn create_png<'a>( - &self, - device: &wgpu::Device, - png_output_path: &str, - // device: &wgpu::Device, - ) { - use std::{fs::File, io::Write}; + pub fn map_async<'a>(&self, device: &wgpu::Device) -> wgpu::BufferSlice { // Note that we're not calling `.await` here. let buffer_slice = self.output_buffer.slice(..); buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); @@ -82,8 +76,15 @@ impl BufferedTextureHead { // In an actual application, `device.poll(...)` should // be called in an event loop or on another thread. device.poll(wgpu::Maintain::Wait); - let padded_buffer = buffer_slice.get_mapped_range(); + buffer_slice + } + pub fn unmap(&self) { + self.output_buffer.unmap(); + } + + pub fn write_png<'a>(&self, padded_buffer: &wgpu::BufferView<'a>, png_output_path: &str) { + use std::{fs::File, io::Write}; let mut png_encoder = png::Encoder::new( File::create(png_output_path).unwrap(), // TODO: Remove unwrap self.buffer_dimensions.width as u32, @@ -104,12 +105,6 @@ impl BufferedTextureHead { .unwrap(); // TODO: Remove unwrap } png_writer.finish().unwrap(); // TODO: Remove unwrap - - // With the current interface, we have to make sure all mapped views are - // dropped before we unmap the buffer. - drop(padded_buffer); - - self.output_buffer.unmap(); } } diff --git a/maplibre/src/render/stages/graph_runner_stage.rs b/maplibre/src/render/stages/graph_runner_stage.rs index bebdbae4..357f94dc 100644 --- a/maplibre/src/render/stages/graph_runner_stage.rs +++ b/maplibre/src/render/stages/graph_runner_stage.rs @@ -51,6 +51,8 @@ impl Stage for GraphRunnerStage { } } + // TODO: Replace panic with a graceful exit in the event loop + // if e.should_exit() { *control_flow = ControlFlow::Exit; } panic!("Error running render graph: {:?}", e); } diff --git a/maplibre/src/stages/mod.rs b/maplibre/src/stages/mod.rs index 718849ba..b81d056b 100644 --- a/maplibre/src/stages/mod.rs +++ b/maplibre/src/stages/mod.rs @@ -8,11 +8,10 @@ use request_stage::RequestStage; use crate::{ coords::WorldTileCoords, environment::Environment, - error::Error, io::{ apc::{Context, Message}, geometry_index::{IndexedGeometry, TileIndex}, - pipeline::PipelineProcessor, + pipeline::{PipelineError, PipelineProcessor}, source_client::HttpClient, transferables::{ LayerIndexed, LayerTessellated, LayerUnavailable, TileTessellated, Transferables, @@ -43,23 +42,25 @@ pub struct HeadedPipelineProcessor> PipelineProcessor for HeadedPipelineProcessor { - fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), Error> { + fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), PipelineError> { self.context .send(Message::TileTessellated(T::TileTessellated::build_from( *coords, ))) + .map_err(|e| PipelineError::Processing(Box::new(e))) } fn layer_unavailable( &mut self, coords: &WorldTileCoords, layer_name: &str, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { self.context .send(Message::LayerUnavailable(T::LayerUnavailable::build_from( *coords, layer_name.to_owned(), ))) + .map_err(|e| PipelineError::Processing(Box::new(e))) } fn layer_tesselation_finished( @@ -68,7 +69,7 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context> PipelineProcessor buffer: OverAlignedVertexBuffer, feature_indices: Vec, layer_data: tile::Layer, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { self.context .send(Message::LayerTessellated(T::LayerTessellated::build_from( *coords, @@ -76,17 +77,19 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context> PipelineProcessor feature_indices, layer_data, ))) + .map_err(|e| PipelineError::Processing(Box::new(e))) } fn layer_indexing_finished( &mut self, coords: &WorldTileCoords, geometries: Vec>, - ) -> Result<(), Error> { + ) -> Result<(), PipelineError> { self.context .send(Message::LayerIndexed(T::LayerIndexed::build_from( *coords, TileIndex::Linear { list: geometries }, ))) + .map_err(|e| PipelineError::Processing(Box::new(e))) } } diff --git a/maplibre/src/stages/request_stage.rs b/maplibre/src/stages/request_stage.rs index c1e2d0ec..f4a6cb29 100644 --- a/maplibre/src/stages/request_stage.rs +++ b/maplibre/src/stages/request_stage.rs @@ -6,9 +6,8 @@ use crate::{ context::MapContext, coords::{ViewRegion, WorldTileCoords}, environment::Environment, - error::Error, io::{ - apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, Message}, + apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, Message, ProcedureError}, pipeline::{PipelineContext, Processable}, tile_pipelines::build_vector_tile_pipeline, tile_repository::TileRepository, @@ -71,7 +70,7 @@ pub fn schedule< ) -> AsyncProcedureFuture { Box::pin(async move { let Input::TileRequest(input) = input else { - return Err(Error::APC) + return Err(ProcedureError::IncompatibleInput) }; let coords = input.coords; @@ -87,7 +86,9 @@ pub fn schedule< phantom_hc: Default::default(), }); let pipeline = build_vector_tile_pipeline(); - pipeline.process((input, data), &mut pipeline_context)?; + pipeline + .process((input, data), &mut pipeline_context) + .map_err(|e| ProcedureError::Execution(Box::new(e)))?; } Err(e) => { log::error!("{:?}", &e); @@ -100,7 +101,7 @@ pub fn schedule< input.coords, to_load.to_string(), )), - )?; + ).map_err(ProcedureError::Send)?; } } } @@ -126,8 +127,7 @@ impl RequestStage { for coords in view_region.iter() { if coords.build_quad_key().is_some() { // TODO: Make tesselation depend on style? - self.request_tile(tile_repository, coords, &source_layers) - .unwrap(); // TODO: Remove unwrap + self.request_tile(tile_repository, coords, &source_layers); } } } @@ -137,7 +137,7 @@ impl RequestStage { tile_repository: &mut TileRepository, coords: WorldTileCoords, layers: &HashSet, - ) -> Result<(), Error> { + ) { /* TODO: is this still required? if !tile_repository.is_layers_missing(coords, layers) { return Ok(false); @@ -147,20 +147,19 @@ impl RequestStage { tile_repository.create_tile(coords); tracing::info!("new tile request: {}", &coords); - self.kernel.apc().call( - Input::TileRequest(TileRequest { - coords, - layers: layers.clone(), - }), - schedule::< - E, - >::Context, - >, - ); + self.kernel + .apc() + .call( + Input::TileRequest(TileRequest { + coords, + layers: layers.clone(), + }), + schedule::< + E, + >::Context, + >, + ) + .unwrap(); // TODO: Remove unwrap } - - Ok(()) } } diff --git a/maplibre/src/tessellation/mod.rs b/maplibre/src/tessellation/mod.rs index 531fa01c..56bfce8b 100644 --- a/maplibre/src/tessellation/mod.rs +++ b/maplibre/src/tessellation/mod.rs @@ -1,13 +1,11 @@ //! Tessellation for lines and polygons is implemented here. -use std::ops::Add; - use bytemuck::Pod; use lyon::tessellation::{ FillVertex, FillVertexConstructor, StrokeVertex, StrokeVertexConstructor, VertexBuffers, }; -use crate::{error::Error, render::ShaderVertex}; +use crate::render::ShaderVertex; pub mod zero_tessellator; @@ -16,14 +14,6 @@ const DEFAULT_TOLERANCE: f32 = 0.02; /// Vertex buffers index data type. pub type IndexDataType = u32; // Must match INDEX_FORMAT -/// An element that can be tessellated into vertex buffers. -pub trait Tessellated { - /// Returns a vertex buffer which represents some object like a layer. Each object can contain - /// multiple features. For each feature also the amount of indices is returned. - /// - fn tessellate(&self) -> Result<(VertexBuffers, Vec), Error>; -} - /// Constructor for Fill and Stroke vertices. pub struct VertexConstructor {} diff --git a/web/Cargo.toml b/web/Cargo.toml index d73eeed5..dff2f52a 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -30,6 +30,8 @@ maplibre-winit = { path = "../maplibre-winit", version = "0.1.0" } log = "0.4.17" rand = { version = "0.7", features = ["wasm-bindgen"] } +thiserror = "1.0" + console_error_panic_hook = "0.1.7" # Exact version requirement can be removed as soon as https://github.com/gfx-rs/wgpu/pull/2954 is merged web-sys = { version = "0.3.58", features = [ @@ -42,7 +44,7 @@ js-sys = "0.3.58" wasm-bindgen = "0.2.81" wasm-bindgen-futures = "0.4.31" console_log = { version = "0.2.0", features = ["color"] } -tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency +tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency (remove in a separate PR!) # For passing Inputs in AsyncProcedureCalls serde_json = "1.0.85" flatbuffers = "22.10.26" diff --git a/web/lib/package-lock.json b/web/lib/package-lock.json index 88d03b89..8270393a 100644 --- a/web/lib/package-lock.json +++ b/web/lib/package-lock.json @@ -71,9 +71,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz", - "integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", "cpu": [ "arm" ], @@ -87,9 +87,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz", - "integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", "cpu": [ "loong64" ], @@ -139,9 +139,9 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", @@ -265,9 +265,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz", - "integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, "hasInstallScript": true, "bin": { @@ -277,34 +277,34 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/android-arm": "0.15.13", - "@esbuild/linux-loong64": "0.15.13", - "esbuild-android-64": "0.15.13", - "esbuild-android-arm64": "0.15.13", - "esbuild-darwin-64": "0.15.13", - "esbuild-darwin-arm64": "0.15.13", - "esbuild-freebsd-64": "0.15.13", - "esbuild-freebsd-arm64": "0.15.13", - "esbuild-linux-32": "0.15.13", - "esbuild-linux-64": "0.15.13", - "esbuild-linux-arm": "0.15.13", - "esbuild-linux-arm64": "0.15.13", - "esbuild-linux-mips64le": "0.15.13", - "esbuild-linux-ppc64le": "0.15.13", - "esbuild-linux-riscv64": "0.15.13", - "esbuild-linux-s390x": "0.15.13", - "esbuild-netbsd-64": "0.15.13", - "esbuild-openbsd-64": "0.15.13", - "esbuild-sunos-64": "0.15.13", - "esbuild-windows-32": "0.15.13", - "esbuild-windows-64": "0.15.13", - "esbuild-windows-arm64": "0.15.13" + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" } }, "node_modules/esbuild-android-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz", - "integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", + "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", "cpu": [ "x64" ], @@ -318,9 +318,9 @@ } }, "node_modules/esbuild-android-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz", - "integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", + "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/esbuild-darwin-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz", - "integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", + "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", "cpu": [ "x64" ], @@ -350,9 +350,9 @@ } }, "node_modules/esbuild-darwin-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz", - "integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", + "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", "cpu": [ "arm64" ], @@ -366,9 +366,9 @@ } }, "node_modules/esbuild-freebsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz", - "integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", + "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", "cpu": [ "x64" ], @@ -382,9 +382,9 @@ } }, "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz", - "integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", + "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", "cpu": [ "arm64" ], @@ -398,9 +398,9 @@ } }, "node_modules/esbuild-linux-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz", - "integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", + "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", "cpu": [ "ia32" ], @@ -414,9 +414,9 @@ } }, "node_modules/esbuild-linux-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz", - "integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", "cpu": [ "x64" ], @@ -430,9 +430,9 @@ } }, "node_modules/esbuild-linux-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz", - "integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", + "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", "cpu": [ "arm" ], @@ -446,9 +446,9 @@ } }, "node_modules/esbuild-linux-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz", - "integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", + "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", "cpu": [ "arm64" ], @@ -462,9 +462,9 @@ } }, "node_modules/esbuild-linux-mips64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz", - "integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", + "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", "cpu": [ "mips64el" ], @@ -478,9 +478,9 @@ } }, "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz", - "integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", + "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", "cpu": [ "ppc64" ], @@ -494,9 +494,9 @@ } }, "node_modules/esbuild-linux-riscv64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz", - "integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", + "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", "cpu": [ "riscv64" ], @@ -510,9 +510,9 @@ } }, "node_modules/esbuild-linux-s390x": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz", - "integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", + "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", "cpu": [ "s390x" ], @@ -526,9 +526,9 @@ } }, "node_modules/esbuild-netbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz", - "integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", + "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", "cpu": [ "x64" ], @@ -542,9 +542,9 @@ } }, "node_modules/esbuild-openbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz", - "integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", + "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", "cpu": [ "x64" ], @@ -568,9 +568,9 @@ } }, "node_modules/esbuild-sunos-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz", - "integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", + "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", "cpu": [ "x64" ], @@ -584,9 +584,9 @@ } }, "node_modules/esbuild-windows-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz", - "integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", + "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", "cpu": [ "ia32" ], @@ -600,9 +600,9 @@ } }, "node_modules/esbuild-windows-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz", - "integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", + "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", "cpu": [ "x64" ], @@ -616,9 +616,9 @@ } }, "node_modules/esbuild-windows-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz", - "integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", + "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", "cpu": [ "arm64" ], @@ -947,9 +947,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -1056,16 +1056,16 @@ "dev": true }, "@esbuild/android-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz", - "integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.18.tgz", + "integrity": "sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz", - "integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz", + "integrity": "sha512-L4jVKS82XVhw2nvzLg/19ClLWg0y27ulRwuP7lcyL6AbUWB5aPglXY3M21mauDQMDfRLs8cQmeT03r/+X3cZYQ==", "dev": true, "optional": true }, @@ -1094,9 +1094,9 @@ } }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -1184,144 +1184,144 @@ "dev": true }, "esbuild": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz", - "integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.18.tgz", + "integrity": "sha512-x/R72SmW3sSFRm5zrrIjAhCeQSAWoni3CmHEqfQrZIQTM3lVCdehdwuIqaOtfC2slvpdlLa62GYoN8SxT23m6Q==", "dev": true, "requires": { - "@esbuild/android-arm": "0.15.13", - "@esbuild/linux-loong64": "0.15.13", - "esbuild-android-64": "0.15.13", - "esbuild-android-arm64": "0.15.13", - "esbuild-darwin-64": "0.15.13", - "esbuild-darwin-arm64": "0.15.13", - "esbuild-freebsd-64": "0.15.13", - "esbuild-freebsd-arm64": "0.15.13", - "esbuild-linux-32": "0.15.13", - "esbuild-linux-64": "0.15.13", - "esbuild-linux-arm": "0.15.13", - "esbuild-linux-arm64": "0.15.13", - "esbuild-linux-mips64le": "0.15.13", - "esbuild-linux-ppc64le": "0.15.13", - "esbuild-linux-riscv64": "0.15.13", - "esbuild-linux-s390x": "0.15.13", - "esbuild-netbsd-64": "0.15.13", - "esbuild-openbsd-64": "0.15.13", - "esbuild-sunos-64": "0.15.13", - "esbuild-windows-32": "0.15.13", - "esbuild-windows-64": "0.15.13", - "esbuild-windows-arm64": "0.15.13" + "@esbuild/android-arm": "0.15.18", + "@esbuild/linux-loong64": "0.15.18", + "esbuild-android-64": "0.15.18", + "esbuild-android-arm64": "0.15.18", + "esbuild-darwin-64": "0.15.18", + "esbuild-darwin-arm64": "0.15.18", + "esbuild-freebsd-64": "0.15.18", + "esbuild-freebsd-arm64": "0.15.18", + "esbuild-linux-32": "0.15.18", + "esbuild-linux-64": "0.15.18", + "esbuild-linux-arm": "0.15.18", + "esbuild-linux-arm64": "0.15.18", + "esbuild-linux-mips64le": "0.15.18", + "esbuild-linux-ppc64le": "0.15.18", + "esbuild-linux-riscv64": "0.15.18", + "esbuild-linux-s390x": "0.15.18", + "esbuild-netbsd-64": "0.15.18", + "esbuild-openbsd-64": "0.15.18", + "esbuild-sunos-64": "0.15.18", + "esbuild-windows-32": "0.15.18", + "esbuild-windows-64": "0.15.18", + "esbuild-windows-arm64": "0.15.18" } }, "esbuild-android-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz", - "integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.18.tgz", + "integrity": "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA==", "dev": true, "optional": true }, "esbuild-android-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz", - "integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.18.tgz", + "integrity": "sha512-G4xu89B8FCzav9XU8EjsXacCKSG2FT7wW9J6hOc18soEHJdtWu03L3TQDGf0geNxfLTtxENKBzMSq9LlbjS8OQ==", "dev": true, "optional": true }, "esbuild-darwin-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz", - "integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz", + "integrity": "sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==", "dev": true, "optional": true }, "esbuild-darwin-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz", - "integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz", + "integrity": "sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA==", "dev": true, "optional": true }, "esbuild-freebsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz", - "integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.18.tgz", + "integrity": "sha512-TT3uBUxkteAjR1QbsmvSsjpKjOX6UkCstr8nMr+q7zi3NuZ1oIpa8U41Y8I8dJH2fJgdC3Dj3CXO5biLQpfdZA==", "dev": true, "optional": true }, "esbuild-freebsd-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz", - "integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.18.tgz", + "integrity": "sha512-R/oVr+X3Tkh+S0+tL41wRMbdWtpWB8hEAMsOXDumSSa6qJR89U0S/PpLXrGF7Wk/JykfpWNokERUpCeHDl47wA==", "dev": true, "optional": true }, "esbuild-linux-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz", - "integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.18.tgz", + "integrity": "sha512-lphF3HiCSYtaa9p1DtXndiQEeQDKPl9eN/XNoBf2amEghugNuqXNZA/ZovthNE2aa4EN43WroO0B85xVSjYkbg==", "dev": true, "optional": true }, "esbuild-linux-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz", - "integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.18.tgz", + "integrity": "sha512-hNSeP97IviD7oxLKFuii5sDPJ+QHeiFTFLoLm7NZQligur8poNOWGIgpQ7Qf8Balb69hptMZzyOBIPtY09GZYw==", "dev": true, "optional": true }, "esbuild-linux-arm": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz", - "integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.18.tgz", + "integrity": "sha512-UH779gstRblS4aoS2qpMl3wjg7U0j+ygu3GjIeTonCcN79ZvpPee12Qun3vcdxX+37O5LFxz39XeW2I9bybMVA==", "dev": true, "optional": true }, "esbuild-linux-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz", - "integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.18.tgz", + "integrity": "sha512-54qr8kg/6ilcxd+0V3h9rjT4qmjc0CccMVWrjOEM/pEcUzt8X62HfBSeZfT2ECpM7104mk4yfQXkosY8Quptug==", "dev": true, "optional": true }, "esbuild-linux-mips64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz", - "integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.18.tgz", + "integrity": "sha512-Mk6Ppwzzz3YbMl/ZZL2P0q1tnYqh/trYZ1VfNP47C31yT0K8t9s7Z077QrDA/guU60tGNp2GOwCQnp+DYv7bxQ==", "dev": true, "optional": true }, "esbuild-linux-ppc64le": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz", - "integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.18.tgz", + "integrity": "sha512-b0XkN4pL9WUulPTa/VKHx2wLCgvIAbgwABGnKMY19WhKZPT+8BxhZdqz6EgkqCLld7X5qiCY2F/bfpUUlnFZ9w==", "dev": true, "optional": true }, "esbuild-linux-riscv64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz", - "integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.18.tgz", + "integrity": "sha512-ba2COaoF5wL6VLZWn04k+ACZjZ6NYniMSQStodFKH/Pu6RxzQqzsmjR1t9QC89VYJxBeyVPTaHuBMCejl3O/xg==", "dev": true, "optional": true }, "esbuild-linux-s390x": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz", - "integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.18.tgz", + "integrity": "sha512-VbpGuXEl5FCs1wDVp93O8UIzl3ZrglgnSQ+Hu79g7hZu6te6/YHgVJxCM2SqfIila0J3k0csfnf8VD2W7u2kzQ==", "dev": true, "optional": true }, "esbuild-netbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz", - "integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.18.tgz", + "integrity": "sha512-98ukeCdvdX7wr1vUYQzKo4kQ0N2p27H7I11maINv73fVEXt2kyh4K4m9f35U1K43Xc2QGXlzAw0K9yoU7JUjOg==", "dev": true, "optional": true }, "esbuild-openbsd-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz", - "integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.18.tgz", + "integrity": "sha512-yK5NCcH31Uae076AyQAXeJzt/vxIo9+omZRKj1pauhk3ITuADzuOx5N2fdHrAKPxN+zH3w96uFKlY7yIn490xQ==", "dev": true, "optional": true }, @@ -1336,30 +1336,30 @@ } }, "esbuild-sunos-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz", - "integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.18.tgz", + "integrity": "sha512-On22LLFlBeLNj/YF3FT+cXcyKPEI263nflYlAhz5crxtp3yRG1Ugfr7ITyxmCmjm4vbN/dGrb/B7w7U8yJR9yw==", "dev": true, "optional": true }, "esbuild-windows-32": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz", - "integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.18.tgz", + "integrity": "sha512-o+eyLu2MjVny/nt+E0uPnBxYuJHBvho8vWsC2lV61A7wwTWC3jkN2w36jtA+yv1UgYkHRihPuQsL23hsCYGcOQ==", "dev": true, "optional": true }, "esbuild-windows-64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz", - "integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.18.tgz", + "integrity": "sha512-qinug1iTTaIIrCorAUjR0fcBk24fjzEedFYhhispP8Oc7SFvs+XeW3YpAKiKp8dRpizl4YYAhxMjlftAMJiaUw==", "dev": true, "optional": true }, "esbuild-windows-arm64": { - "version": "0.15.13", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz", - "integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==", + "version": "0.15.18", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.18.tgz", + "integrity": "sha512-q9bsYzegpZcLziq0zgUi5KqGVtfhjxGbnksaBFYmWLxeV/S1fK4OLdq2DFYnXcLMjlZw2L0jLsk1eGoB522WXQ==", "dev": true, "optional": true }, @@ -1582,9 +1582,9 @@ } }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true }, "wasm-feature-detect": { diff --git a/web/lib/src/index.ts b/web/lib/src/index.ts index a6ffc3ad..05409dac 100644 --- a/web/lib/src/index.ts +++ b/web/lib/src/index.ts @@ -46,10 +46,16 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st }) : PoolWorker(); worker.onmessage = (message: MessageEvent) => { - // WARNING: Do not modify in_transfer_obj! - let in_transfer_obj = message.data; - // @ts-ignore TODO singlethreaded_main_entry may not be defined - maplibre.singlethreaded_main_entry(ptr, in_transfer_obj) + // WARNING: Do not modify data passed from Rust! + let in_transfer = message.data; + + const main_entry = maplibre["singlethreaded_main_entry"]; + + if (!main_entry) { + throw Error("singlethreaded_main_entry is not defined. Maybe the Rust build used the wrong build configuration.") + } + + main_entry(ptr, in_transfer) } return worker; diff --git a/web/lib/src/multithreaded/multithreaded-pool.worker.ts b/web/lib/src/multithreaded/multithreaded-pool.worker.ts index 90659348..de24f345 100644 --- a/web/lib/src/multithreaded/multithreaded-pool.worker.ts +++ b/web/lib/src/multithreaded/multithreaded-pool.worker.ts @@ -13,7 +13,13 @@ onmessage = async message => { self.onmessage = async message => { // This will queue further commands up until the module is fully initialised: await initialised; - // @ts-ignore TODO may not exist - await maplibre.multithreaded_worker_entry(message.data); + + const worker_entry = maplibre["multithreaded_worker_entry"] + + if (!worker_entry) { + throw Error("multithreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.") + } + + await worker_entry(message.data); }; } diff --git a/web/lib/src/singlethreaded/pool.worker.ts b/web/lib/src/singlethreaded/pool.worker.ts index e08cf102..beacbc47 100644 --- a/web/lib/src/singlethreaded/pool.worker.ts +++ b/web/lib/src/singlethreaded/pool.worker.ts @@ -15,9 +15,17 @@ onmessage = async message => { self.onmessage = async message => { // This will queue further commands up until the module is fully initialised: await initialised; - let procedure_ptr = message.data[0]; - let input = message.data[1]; - // @ts-ignore TODO - await maplibre.singlethreaded_worker_entry(procedure_ptr, input); + + // WARNING: Do not modify data passed from Rust! + const procedure_ptr = message.data[0]; + const input = message.data[1]; + + const worker_entry = maplibre["singlethreaded_worker_entry"]; + + if (!worker_entry) { + throw Error("singlethreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.") + } + + await worker_entry(procedure_ptr, input); }; } diff --git a/web/src/error.rs b/web/src/error.rs index 1d1c3764..0a40425d 100644 --- a/web/src/error.rs +++ b/web/src/error.rs @@ -1,15 +1,88 @@ -//! Errors which can happen in various parts of the library. +//! Errors from JS world. -use js_sys::Error as JSError; +use std::{ + borrow::Cow, + error::Error, + fmt::{Display, Formatter}, +}; + +use js_sys::{Error as JSError, TypeError}; +use maplibre::io::apc::{CallError, ProcedureError}; +use thiserror::Error; use wasm_bindgen::{JsCast, JsValue}; -#[derive(Debug)] -pub struct WebError(pub String); +#[derive(Error, Debug)] +pub enum WebError { + #[error("JS error type is unknown")] + UnknownErrorType, + /// Returned if the message is not valid, e.g. if it it is not valid UTF-8. + #[error("message string in error is invalid")] + InvalidMessage, + /// TypeError like it is defined in JS + #[error("TypeError from JS")] + TypeError(Cow<'static, str>), + /// Any other Error + #[error("Error from JS")] + GenericError(Cow<'static, str>), +} impl From for WebError { - fn from(maybe_error: JsValue) -> Self { - assert!(maybe_error.is_instance_of::()); - let error: JSError = maybe_error.dyn_into().unwrap(); // TODO: Remove unwrap - WebError(error.message().as_string().unwrap()) // TODO: remove unwrap + fn from(value: JsValue) -> Self { + if let Some(error) = value.dyn_ref::() { + let Some(message) = error + .message() + .as_string() else { return WebError::InvalidMessage; }; + + WebError::TypeError(message.into()) + } else if let Some(error) = value.dyn_ref::() { + let Some(message) = error + .message() + .as_string() else { return WebError::InvalidMessage; }; + + WebError::GenericError(message.into()) + } else { + WebError::UnknownErrorType + } + } +} + +/// Wraps several unrelated errors and implements Into. This should be used in Rust +/// functions called from JS-land as return error type. +#[derive(Debug)] +pub enum WrappedError { + ProcedureError(ProcedureError), + CallError(CallError), + WebError(WebError), +} + +impl Display for WrappedError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "Error from Rust: {:?}", self) + } +} + +impl Error for WrappedError {} + +impl From for JsValue { + fn from(val: WrappedError) -> Self { + JsValue::from_str(&val.to_string()) + } +} + +impl From for WrappedError { + fn from(e: CallError) -> Self { + WrappedError::CallError(e) + } +} + +impl From for WrappedError { + fn from(e: ProcedureError) -> Self { + WrappedError::ProcedureError(e) + } +} + +impl From for WrappedError { + fn from(e: WebError) -> Self { + WrappedError::WebError(e) } } diff --git a/web/src/lib.rs b/web/src/lib.rs index d4df7b0b..f99e18e0 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -11,7 +11,7 @@ use maplibre::{ use maplibre_winit::{WinitEnvironment, WinitMapWindowConfig}; use wasm_bindgen::prelude::*; -use crate::platform::http_client::WHATWGFetchHttpClient; +use crate::{error::WrappedError, platform::http_client::WHATWGFetchHttpClient}; mod error; mod platform; @@ -65,7 +65,7 @@ type CurrentEnvironment = WinitEnvironment< pub type MapType = Map; #[wasm_bindgen] -pub async fn run_maplibre(new_worker: js_sys::Function) { +pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), WrappedError> { let mut kernel_builder = KernelBuilder::new() .with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string())) .with_http_client(WHATWGFetchHttpClient::new()); @@ -77,31 +77,31 @@ pub async fn run_maplibre(new_worker: js_sys::Function) { WHATWGFetchHttpClient::new(), platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new( new_worker.clone(), - ), + )?, )) .with_scheduler( - platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker), + platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker)?, ); } #[cfg(not(target_feature = "atomics"))] { kernel_builder = kernel_builder - .with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4)) + .with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4)?) .with_scheduler(maplibre::io::scheduler::NopScheduler); } let kernel: Kernel> = kernel_builder.build(); - let mut map: MapType = Map::new(Style::default(), kernel).unwrap(); - map.initialize_renderer(RendererBuilder::new()) - .await - .unwrap(); + let mut map: MapType = Map::new(Style::default(), kernel, RendererBuilder::new()).unwrap(); + map.initialize_renderer().await.unwrap(); map.window_mut() .take_event_loop() .expect("Event loop is not available") - .run(map, None) + .run(map, None); + + Ok(()) } #[cfg(test)] diff --git a/web/src/platform/http_client.rs b/web/src/platform/http_client.rs index a172771a..0397f1b8 100644 --- a/web/src/platform/http_client.rs +++ b/web/src/platform/http_client.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; use js_sys::{ArrayBuffer, Uint8Array}; -use maplibre::{error::Error, io::source_client::HttpClient}; +use maplibre::io::source_client::{HttpClient, SourceFetchError}; use wasm_bindgen::{prelude::*, JsCast}; use wasm_bindgen_futures::JsFuture; use web_sys::{Request, RequestInit, Response, WorkerGlobalScope}; @@ -14,7 +14,7 @@ impl WHATWGFetchHttpClient { Self {} } - async fn fetch_array_buffer(url: &str) -> Result { + async fn fetch_array_buffer(url: &str) -> Result { let mut opts = RequestInit::new(); opts.method("GET"); @@ -22,13 +22,15 @@ impl WHATWGFetchHttpClient { // Get the global scope let global = js_sys::global(); - assert!(global.is_instance_of::()); - let scope = global.dyn_into::().unwrap(); // TODO: remove unwrap + let scope = global + .dyn_into::() + .map_err(|_e| WebError::TypeError("Unable to cast to WorkerGlobalScope".into()))?; // Call fetch on global scope let maybe_response = JsFuture::from(scope.fetch_with_request(&request)).await?; - assert!(maybe_response.is_instance_of::()); - let response: Response = maybe_response.dyn_into().unwrap(); // TODO: remove unwrap + let response: Response = maybe_response + .dyn_into() + .map_err(|_e| WebError::TypeError("Unable to cast to Response".into()))?; // Get ArrayBuffer let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?; @@ -38,8 +40,9 @@ impl WHATWGFetchHttpClient { async fn fetch_bytes(&self, url: &str) -> Result, WebError> { let maybe_array_buffer = Self::fetch_array_buffer(url).await?; - assert!(maybe_array_buffer.is_instance_of::()); - let array_buffer: ArrayBuffer = maybe_array_buffer.dyn_into().unwrap(); // TODO: remove unwrap + let array_buffer: ArrayBuffer = maybe_array_buffer + .dyn_into() + .map_err(|_e| WebError::TypeError("Unable to cast to ArrayBuffer".into()))?; // Copy data to Vec let buffer: Uint8Array = Uint8Array::new(&array_buffer); @@ -58,9 +61,9 @@ impl Clone for WHATWGFetchHttpClient { #[async_trait(?Send)] impl HttpClient for WHATWGFetchHttpClient { - async fn fetch(&self, url: &str) -> Result, Error> { + async fn fetch(&self, url: &str) -> Result, SourceFetchError> { self.fetch_bytes(url) .await - .map_err(|WebError(msg)| Error::Network(msg)) + .map_err(|e| SourceFetchError(Box::new(e))) } } diff --git a/web/src/platform/multithreaded/pool.rs b/web/src/platform/multithreaded/pool.rs index d6aad1d5..7cb104e6 100644 --- a/web/src/platform/multithreaded/pool.rs +++ b/web/src/platform/multithreaded/pool.rs @@ -9,14 +9,19 @@ use rand::prelude::*; use wasm_bindgen::prelude::*; use web_sys::Worker; +use crate::error::WebError; + #[wasm_bindgen()] extern "C" { #[wasm_bindgen(js_name = newWorker)] fn new_worker() -> JsValue; } +type NewWorker = Box Result>; +type Execute = Box Promise) + Send>; + pub struct WorkerPool { - new_worker: Box Worker>, + new_worker: NewWorker, state: Rc, } @@ -35,7 +40,7 @@ impl PoolState { } pub struct Work { - func: Box Promise) + Send>, + func: Execute, } impl Work { @@ -55,7 +60,7 @@ impl WorkerPool { /// /// Returns any error that may happen while a JS web worker is created and a /// message is sent to it. - pub fn new(initial: usize, new_worker: Box Worker>) -> Result { + pub fn new(initial: usize, new_worker: NewWorker) -> Result { let pool = WorkerPool { new_worker, state: Rc::new(PoolState { @@ -78,9 +83,9 @@ impl WorkerPool { /// /// Returns any error that may happen while a JS web worker is created and a /// message is sent to it. - fn spawn(&self) -> Result<(), JsValue> { + fn spawn(&self) -> Result<(), WebError> { log::info!("spawning new worker"); - let worker = (self.new_worker)(); + let worker = (self.new_worker)()?; // With a worker spun up send it the module/memory so it can start // instantiating the wasm module. Later it might receive further @@ -104,7 +109,7 @@ impl WorkerPool { /// /// Returns any error that may happen while a JS web worker is created and a /// message is sent to it. - fn worker(&self) -> Result { + fn worker(&self) -> Result { let workers = self.state.workers.borrow(); let result = workers.choose(&mut thread_rng()); @@ -130,7 +135,7 @@ impl WorkerPool { /// /// Returns any error that may happen while a JS web worker is created and a /// message is sent to it. - pub fn execute(&self, f: impl (FnOnce() -> Promise) + Send + 'static) -> Result<(), JsValue> { + pub fn execute(&self, f: impl (FnOnce() -> Promise) + Send + 'static) -> Result<(), WebError> { let worker = self.worker()?; let work = Work { func: Box::new(f) }; let work_ptr = Box::into_raw(Box::new(work)); @@ -140,7 +145,7 @@ impl WorkerPool { unsafe { drop(Box::from_raw(work_ptr)); } - Err(e) + Err(e.into()) } } } diff --git a/web/src/platform/multithreaded/pool_scheduler.rs b/web/src/platform/multithreaded/pool_scheduler.rs index 7688f546..b36d396e 100644 --- a/web/src/platform/multithreaded/pool_scheduler.rs +++ b/web/src/platform/multithreaded/pool_scheduler.rs @@ -1,30 +1,29 @@ use std::future::Future; -use maplibre::{error::Error, io::scheduler::Scheduler}; +use maplibre::{benchmarking::io::scheduler::ScheduleError, io::scheduler::Scheduler}; use wasm_bindgen::{prelude::*, JsCast}; use web_sys::Worker; use super::pool::WorkerPool; +use crate::error::WebError; pub struct WebWorkerPoolScheduler { pool: WorkerPool, } impl WebWorkerPoolScheduler { - pub fn new(new_worker: js_sys::Function) -> Self { - // TODO: Are expects here oke? + pub fn new(new_worker: js_sys::Function) -> Result { let pool = WorkerPool::new( 1, Box::new(move || { new_worker .call0(&JsValue::undefined()) - .expect("Unable to call new_worker function") + .map_err(WebError::from)? .dyn_into::() - .expect("new_worker function did not return a Worker") + .map_err(|_e| WebError::TypeError("Unable to cast to Worker".into())) }), - ) - .expect("Unable to create WorkerPool"); - Self { pool } + )?; + Ok(Self { pool }) } } @@ -32,7 +31,7 @@ impl Scheduler for WebWorkerPoolScheduler { fn schedule( &self, future_factory: impl (FnOnce() -> T) + Send + 'static, - ) -> Result<(), Error> + ) -> Result<(), ScheduleError> where T: Future + 'static, { @@ -43,6 +42,6 @@ impl Scheduler for WebWorkerPoolScheduler { Ok(JsValue::undefined()) }) }) - .map_err(|e| Error::Scheduler) + .map_err(|e| ScheduleError::Scheduling(Box::new(e))) } } diff --git a/web/src/platform/multithreaded/wasm_entries.rs b/web/src/platform/multithreaded/wasm_entries.rs index da096765..84f257e4 100644 --- a/web/src/platform/multithreaded/wasm_entries.rs +++ b/web/src/platform/multithreaded/wasm_entries.rs @@ -1,12 +1,15 @@ +use maplibre::io::apc::CallError; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; -use crate::platform::multithreaded::pool::Work; +use crate::{platform::multithreaded::pool::Work, WrappedError}; /// Entry point invoked by the worker. #[wasm_bindgen] -pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), JsValue> { +pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), WrappedError> { let work = unsafe { Box::from_raw(ptr as *mut Work) }; - JsFuture::from(work.execute()).await?; + JsFuture::from(work.execute()) + .await + .map_err(|_e| CallError::Schedule)?; Ok(()) } diff --git a/web/src/platform/singlethreaded/apc.rs b/web/src/platform/singlethreaded/apc.rs index f09f7e2e..e3518326 100644 --- a/web/src/platform/singlethreaded/apc.rs +++ b/web/src/platform/singlethreaded/apc.rs @@ -1,54 +1,71 @@ use std::{cell::RefCell, rc::Rc}; use js_sys::{ArrayBuffer, Uint8Array}; -use log::error; -use maplibre::{ - error::Error, - io::{ - apc::{AsyncProcedure, AsyncProcedureCall, Context, Input, Message}, - source_client::SourceClient, - }, +use maplibre::io::{ + apc::{AsyncProcedure, AsyncProcedureCall, CallError, Context, Input, Message, SendError}, + source_client::SourceClient, }; +use rand::{prelude::SliceRandom, thread_rng}; +use thiserror::Error; use wasm_bindgen::{JsCast, JsValue}; use web_sys::{DedicatedWorkerGlobalScope, Worker}; -use crate::platform::singlethreaded::{ - transferables::FlatBufferTransferable, UsedContext, UsedHttpClient, UsedTransferables, +use crate::{ + error::WebError, + platform::singlethreaded::{ + transferables::FlatBufferTransferable, UsedContext, UsedHttpClient, UsedTransferables, + }, }; +/// Error which happens during serialization or deserialization of the tag +#[derive(Error, Debug)] +#[error("failed to deserialize message tag")] +pub struct MessageTagDeserializeError; + #[derive(Debug)] -pub enum SerializedMessageTag { +pub enum MessageTag { TileTessellated = 1, LayerUnavailable = 2, LayerTessellated = 3, LayerIndexed = 4, } -impl SerializedMessageTag { +impl MessageTag { pub fn from_message(message: &Message) -> Self { match message { - Message::TileTessellated(_) => SerializedMessageTag::TileTessellated, - Message::LayerUnavailable(_) => SerializedMessageTag::LayerUnavailable, - Message::LayerTessellated(_) => SerializedMessageTag::LayerTessellated, - Message::LayerIndexed(_) => SerializedMessageTag::LayerIndexed, + Message::TileTessellated(_) => MessageTag::TileTessellated, + Message::LayerUnavailable(_) => MessageTag::LayerUnavailable, + Message::LayerTessellated(_) => MessageTag::LayerTessellated, + Message::LayerIndexed(_) => MessageTag::LayerIndexed, } } - pub fn from_u32(tag: u32) -> Option { + pub fn create_message( + &self, + transferable: FlatBufferTransferable, + ) -> Message { + // TODO: Verify that data matches tag + match self { + MessageTag::TileTessellated => { + Message::::TileTessellated(transferable) + } + MessageTag::LayerUnavailable => { + Message::::LayerUnavailable(transferable) + } + MessageTag::LayerTessellated => { + Message::::LayerTessellated(transferable) + } + MessageTag::LayerIndexed => Message::::LayerIndexed(transferable), + } + } + + pub fn from_u32(tag: u32) -> Result { match tag { - x if x == SerializedMessageTag::LayerUnavailable as u32 => { - Some(SerializedMessageTag::LayerUnavailable) - } - x if x == SerializedMessageTag::LayerTessellated as u32 => { - Some(SerializedMessageTag::LayerTessellated) - } - x if x == SerializedMessageTag::TileTessellated as u32 => { - Some(SerializedMessageTag::TileTessellated) - } - x if x == SerializedMessageTag::LayerIndexed as u32 => { - Some(SerializedMessageTag::LayerIndexed) - } - _ => None, + x if x == MessageTag::LayerUnavailable as u32 => Ok(MessageTag::LayerUnavailable), + x if x == MessageTag::LayerTessellated as u32 => Ok(MessageTag::LayerTessellated), + x if x == MessageTag::TileTessellated as u32 => Ok(MessageTag::TileTessellated), + x if x == MessageTag::LayerIndexed as u32 => Ok(MessageTag::LayerIndexed), + _ => Err(MessageTagDeserializeError), } } } @@ -59,10 +76,10 @@ pub struct PassingContext { } impl Context for PassingContext { - fn send(&self, message: Message) -> Result<(), Error> { - let tag = SerializedMessageTag::from_message(&message); + fn send(&self, message: Message) -> Result<(), SendError> { + let tag = MessageTag::from_message(&message); let transferable = FlatBufferTransferable::from_message(message); - let data = &transferable.data[transferable.start..]; + let data = transferable.data(); let buffer = ArrayBuffer::new(data.len() as u32); let byte_buffer = Uint8Array::new(&buffer); @@ -70,26 +87,15 @@ impl Context for PassingContext { byte_buffer.set(&Uint8Array::view(data), 0); } - let tag = tag as u32; - - let global: DedicatedWorkerGlobalScope = - js_sys::global().dyn_into().map_err(|_e| Error::APC)?; - - let transfer_obj = js_sys::Array::new(); - transfer_obj.push(&JsValue::from(tag)); - - transfer_obj.push(&buffer); - - let transfer = js_sys::Array::new(); - transfer.push(&buffer); - - // TODO: Verify transfer + let global: DedicatedWorkerGlobalScope = js_sys::global() + .dyn_into() + .map_err(|_e| SendError::Transmission)?; global - .post_message_with_transfer(&transfer_obj, &transfer) - .map_err(|e| { - error!("{:?}", e); - Error::APC - }) + .post_message_with_transfer( + &js_sys::Array::of2(&JsValue::from(tag as u32), &buffer), + &js_sys::Array::of1(&buffer), + ) + .map_err(|_e| SendError::Transmission) } fn source_client(&self) -> &SourceClient { @@ -97,47 +103,48 @@ impl Context for PassingContext { } } +type NewWorker = Box Result>; pub type ReceivedType = RefCell>>; +#[derive(Error, Debug)] +pub enum PassingAPCError { + #[error("creating a worker failed")] + Worker, +} + pub struct PassingAsyncProcedureCall { - new_worker: Box Worker>, workers: Vec, - received: Rc, // FIXME (wasm-executor): Is RefCell fine? + received: Rc, // FIXME: Is RefCell fine? } impl PassingAsyncProcedureCall { - pub fn new(new_worker: js_sys::Function, initial_workers: u8) -> Self { + pub fn new(new_worker: js_sys::Function, initial_workers: usize) -> Result { let received = Rc::new(RefCell::new(vec![])); let received_ref = received.clone(); - let create_new_worker = Box::new(move || { + let create_new_worker = || { new_worker .call1( &JsValue::undefined(), &JsValue::from(Rc::into_raw(received_ref.clone()) as u32), ) - .unwrap() // FIXME (wasm-executor): Remove unwrap + .map_err(WebError::from)? .dyn_into::() - .unwrap() // FIXME (wasm-executor): Remove unwrap - }); + .map_err(|_e| WebError::TypeError("Unable to cast to Worker".into())) + }; - let workers = (0..initial_workers) - .map(|_| { - let worker: Worker = create_new_worker(); + let mut workers = Vec::with_capacity(initial_workers); - let array = js_sys::Array::new(); - array.push(&wasm_bindgen::module()); - worker.post_message(&array).unwrap(); // FIXME (wasm-executor): Remove unwrap - worker - }) - .collect::>(); + for _ in 0..initial_workers { + let worker: Worker = create_new_worker()?; - Self { - new_worker: create_new_worker, - workers, - received, + let array = js_sys::Array::of1(&wasm_bindgen::module()); + worker.post_message(&array).map_err(WebError::from)?; + workers.push(worker); } + + Ok(Self { workers, received }) } } @@ -152,14 +159,23 @@ impl AsyncProcedureCall for PassingAsyncProcedureCall { .pop() } - fn call(&self, input: Input, procedure: AsyncProcedure) { - let procedure_ptr = procedure as *mut AsyncProcedure as u32; // FIXME (wasm-executor): is u32 fine, define an overflow safe function? - let input = serde_json::to_string(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap + fn call( + &self, + input: Input, + procedure: AsyncProcedure, + ) -> Result<(), CallError> { + let procedure_ptr = procedure as *mut AsyncProcedure as u32; // FIXME: is u32 fine, define an overflow safe function? + let input = serde_json::to_string(&input).map_err(|e| CallError::Serialize(Box::new(e)))?; - let array = js_sys::Array::new(); - array.push(&JsValue::from(procedure_ptr)); - array.push(&JsValue::from(input)); + let message = js_sys::Array::of2(&JsValue::from(procedure_ptr), &JsValue::from(input)); - self.workers[0].post_message(&array).unwrap(); // FIXME (wasm-executor): Remove unwrap + let worker = self + .workers + .choose(&mut thread_rng()) + .ok_or(CallError::Schedule)?; + + worker + .post_message(&message) + .map_err(|_e| CallError::Schedule) } } diff --git a/web/src/platform/singlethreaded/transferables.rs b/web/src/platform/singlethreaded/transferables.rs index 8bec6359..2ec8836d 100644 --- a/web/src/platform/singlethreaded/transferables.rs +++ b/web/src/platform/singlethreaded/transferables.rs @@ -1,4 +1,5 @@ use flatbuffers::FlatBufferBuilder; +use js_sys::{ArrayBuffer, Uint8Array}; use maplibre::{ benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer}, coords::WorldTileCoords, @@ -57,11 +58,24 @@ pub mod tile_tessellated_generated { } pub struct FlatBufferTransferable { - pub data: Vec, - pub start: usize, + data: Vec, + start: usize, } impl FlatBufferTransferable { + pub fn from_array_buffer(buffer: ArrayBuffer) -> Self { + let buffer = Uint8Array::new(&buffer); + + FlatBufferTransferable { + data: buffer.to_vec(), + start: 0, + } + } + + pub fn data(&self) -> &[u8] { + &self.data[self.start..] + } + pub fn from_message(message: Message) -> Self { match message { Message::TileTessellated(transferable) => transferable, @@ -220,7 +234,7 @@ impl LayerIndexed for FlatBufferTransferable { } fn to_tile_index(self) -> TileIndex { - TileIndex::Linear { list: vec![] } // TODO + TileIndex::Linear { list: vec![] } // TODO index } } diff --git a/web/src/platform/singlethreaded/wasm_entries.rs b/web/src/platform/singlethreaded/wasm_entries.rs index 74e98c1a..f1bd7e60 100644 --- a/web/src/platform/singlethreaded/wasm_entries.rs +++ b/web/src/platform/singlethreaded/wasm_entries.rs @@ -1,95 +1,79 @@ use std::{mem, rc::Rc}; -use js_sys::{ArrayBuffer, Uint8Array}; -use log::{error, info}; +use js_sys::ArrayBuffer; use maplibre::{ benchmarking::io::{ - apc::{AsyncProcedure, Input, Message}, + apc::{AsyncProcedure, Input}, source_client::{HttpSourceClient, SourceClient}, }, - io::transferables::Transferables, + io::apc::CallError, }; +use thiserror::Error; use wasm_bindgen::{prelude::*, JsCast}; use crate::{ + error::WrappedError, platform::singlethreaded::{ - apc::{ReceivedType, SerializedMessageTag}, + apc::{MessageTag, ReceivedType}, transferables::FlatBufferTransferable, - PassingContext, UsedContext, UsedTransferables, + PassingContext, UsedContext, }, WHATWGFetchHttpClient, }; /// Entry point invoked by the worker. #[wasm_bindgen] -pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JsValue> { +pub async fn singlethreaded_worker_entry( + procedure_ptr: u32, + input: String, +) -> Result<(), WrappedError> { let procedure: AsyncProcedure = unsafe { mem::transmute(procedure_ptr) }; - let input = serde_json::from_str::(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap + let input = + serde_json::from_str::(&input).map_err(|e| CallError::Deserialize(Box::new(e)))?; let context = PassingContext { source_client: SourceClient::new(HttpSourceClient::new(WHATWGFetchHttpClient::new())), }; - let result = (procedure)(input, context).await; - - if let Err(e) = result { - error!("{:?}", e); // TODO handle better - } + procedure(input, context).await?; Ok(()) } +#[derive(Error, Debug)] +#[error("unable to deserialize message sent by postMessage()")] +pub struct DeserializeMessage; + /// Entry point invoked by the main thread. #[wasm_bindgen] pub unsafe fn singlethreaded_main_entry( received_ptr: *const ReceivedType, - in_transfer_obj: js_sys::Array, -) -> Result<(), JsValue> { - // FIXME (wasm-executor): Can we make this call safe? check if it was cloned before? + in_transfer: js_sys::Array, +) -> Result<(), WrappedError> { + let tag = in_transfer + .get(0) + .as_f64() + .ok_or_else(|| CallError::Deserialize(Box::new(DeserializeMessage)))? as u32; // TODO: Is this cast fine? + let buffer: ArrayBuffer = in_transfer + .get(1) + .dyn_into() + .map_err(|_e| CallError::Deserialize(Box::new(DeserializeMessage)))?; + + let tag = MessageTag::from_u32(tag).map_err(|e| CallError::Deserialize(Box::new(e)))?; + + let message = tag.create_message(FlatBufferTransferable::from_array_buffer(buffer)); + + // FIXME: Can we make this call safe? check if it was cloned before? let received: Rc = Rc::from_raw(received_ptr); - let tag = in_transfer_obj.get(0).as_f64().unwrap() as u32; - let tag = SerializedMessageTag::from_u32(tag).unwrap(); - - info!("singlethreaded_main_entry {:?}", tag); - - let buffer: ArrayBuffer = in_transfer_obj.get(1).dyn_into().unwrap(); - let buffer = Uint8Array::new(&buffer); - - type TileTessellated = ::TileTessellated; - type UnavailableLayer = ::LayerUnavailable; - type IndexedLayer = ::LayerIndexed; - - let transferable = FlatBufferTransferable { - data: buffer.to_vec(), - start: 0, - }; - - // TODO: Verify that data matches tag - - let message = match tag { - SerializedMessageTag::TileTessellated => { - Message::::TileTessellated(transferable) - } - SerializedMessageTag::LayerUnavailable => { - Message::::LayerUnavailable(transferable) - } - SerializedMessageTag::LayerTessellated => { - Message::::LayerTessellated(transferable) - } - SerializedMessageTag::LayerIndexed => { - Message::::LayerIndexed(transferable) - } - }; - // MAJOR FIXME: Fix mutability received .try_borrow_mut() .expect("Failed to borrow in singlethreaded_main_entry") .push(message); - mem::forget(received); // FIXME (wasm-executor): Enforce this somehow + mem::forget(received); // FIXME: Enforce this somehow Ok(()) }