mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Fix WASM loading (#320)
* Rename send to send_back and remove use of Promise * Fix bug where wasm loading fails * Bump memory for multi threading
This commit is contained in:
parent
a9dc4a066b
commit
5146e6a36d
@ -21,7 +21,7 @@ const MUNICH_COORDS: TileCoords = TileCoords {
|
|||||||
pub struct DummyContext;
|
pub struct DummyContext;
|
||||||
|
|
||||||
impl Context for DummyContext {
|
impl Context for DummyContext {
|
||||||
fn send<T: IntoMessage>(&self, _message: T) -> Result<(), SendError> {
|
fn send_back<T: IntoMessage>(&self, _message: T) -> Result<(), SendError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -162,7 +162,7 @@ pub struct HeadlessContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Context for HeadlessContext {
|
impl Context for HeadlessContext {
|
||||||
fn send<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
|
fn send_back<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
|
||||||
self.messages.deref().borrow_mut().push(message.into());
|
self.messages.deref().borrow_mut().push(message.into());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -88,7 +88,7 @@ pub enum SendError {
|
|||||||
/// Allows sending messages from workers to back to the caller.
|
/// Allows sending messages from workers to back to the caller.
|
||||||
pub trait Context: 'static {
|
pub trait Context: 'static {
|
||||||
/// Send a message back to the caller.
|
/// Send a message back to the caller.
|
||||||
fn send<T: IntoMessage>(&self, message: T) -> Result<(), SendError>;
|
fn send_back<T: IntoMessage>(&self, message: T) -> Result<(), SendError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
@ -190,7 +190,7 @@ pub struct SchedulerContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Context for SchedulerContext {
|
impl Context for SchedulerContext {
|
||||||
fn send<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
|
fn send_back<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
|
||||||
self.sender
|
self.sender
|
||||||
.send(message.into())
|
.send(message.into())
|
||||||
.map_err(|_e| SendError::Transmission)
|
.map_err(|_e| SendError::Transmission)
|
||||||
@ -282,7 +282,7 @@ pub mod tests {
|
|||||||
pub struct DummyContext;
|
pub struct DummyContext;
|
||||||
|
|
||||||
impl Context for DummyContext {
|
impl Context for DummyContext {
|
||||||
fn send<T: IntoMessage>(&self, _message: T) -> Result<(), SendError> {
|
fn send_back<T: IntoMessage>(&self, _message: T) -> Result<(), SendError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ impl<T: RasterTransferables, C: Context> ProcessRasterContext<T, C> {
|
|||||||
image_data: RgbaImage,
|
image_data: RgbaImage,
|
||||||
) -> Result<(), ProcessRasterError> {
|
) -> Result<(), ProcessRasterError> {
|
||||||
self.context
|
self.context
|
||||||
.send(T::LayerRaster::build_from(*coords, layer_name, image_data))
|
.send_back(T::LayerRaster::build_from(*coords, layer_name, image_data))
|
||||||
.map_err(|e| ProcessRasterError::Processing(Box::new(e)))
|
.map_err(|e| ProcessRasterError::Processing(Box::new(e)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -142,7 +142,7 @@ pub fn fetch_raster_apc<K: OffscreenKernel, T: RasterTransferables, C: Context +
|
|||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
|
|
||||||
context
|
context
|
||||||
.send(<T as RasterTransferables>::LayerRasterMissing::build_from(
|
.send_back(<T as RasterTransferables>::LayerRasterMissing::build_from(
|
||||||
coords,
|
coords,
|
||||||
))
|
))
|
||||||
.map_err(ProcedureError::Send)?;
|
.map_err(ProcedureError::Send)?;
|
||||||
|
|||||||
@ -125,7 +125,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
|
|||||||
|
|
||||||
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), ProcessVectorError> {
|
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), ProcessVectorError> {
|
||||||
self.context
|
self.context
|
||||||
.send(T::TileTessellated::build_from(*coords))
|
.send_back(T::TileTessellated::build_from(*coords))
|
||||||
.map_err(|e| ProcessVectorError::SendError(e))
|
.map_err(|e| ProcessVectorError::SendError(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +135,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
|
|||||||
layer_name: &str,
|
layer_name: &str,
|
||||||
) -> Result<(), ProcessVectorError> {
|
) -> Result<(), ProcessVectorError> {
|
||||||
self.context
|
self.context
|
||||||
.send(T::LayerMissing::build_from(*coords, layer_name.to_owned()))
|
.send_back(T::LayerMissing::build_from(*coords, layer_name.to_owned()))
|
||||||
.map_err(|e| ProcessVectorError::SendError(e))
|
.map_err(|e| ProcessVectorError::SendError(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
|
|||||||
layer_data: tile::Layer,
|
layer_data: tile::Layer,
|
||||||
) -> Result<(), ProcessVectorError> {
|
) -> Result<(), ProcessVectorError> {
|
||||||
self.context
|
self.context
|
||||||
.send(T::LayerTessellated::build_from(
|
.send_back(T::LayerTessellated::build_from(
|
||||||
*coords,
|
*coords,
|
||||||
buffer,
|
buffer,
|
||||||
feature_indices,
|
feature_indices,
|
||||||
@ -162,7 +162,7 @@ impl<T: VectorTransferables, C: Context> ProcessVectorContext<T, C> {
|
|||||||
geometries: Vec<IndexedGeometry<f64>>,
|
geometries: Vec<IndexedGeometry<f64>>,
|
||||||
) -> Result<(), ProcessVectorError> {
|
) -> Result<(), ProcessVectorError> {
|
||||||
self.context
|
self.context
|
||||||
.send(T::LayerIndexed::build_from(
|
.send_back(T::LayerIndexed::build_from(
|
||||||
*coords,
|
*coords,
|
||||||
TileIndex::Linear { list: geometries },
|
TileIndex::Linear { list: geometries },
|
||||||
))
|
))
|
||||||
|
|||||||
@ -151,7 +151,7 @@ pub fn fetch_vector_apc<K: OffscreenKernel, T: VectorTransferables, C: Context +
|
|||||||
log::error!("{e:?}");
|
log::error!("{e:?}");
|
||||||
for to_load in &fill_layers {
|
for to_load in &fill_layers {
|
||||||
context
|
context
|
||||||
.send(<T as VectorTransferables>::LayerMissing::build_from(
|
.send_back(<T as VectorTransferables>::LayerMissing::build_from(
|
||||||
coords,
|
coords,
|
||||||
to_load.to_string(),
|
to_load.to_string(),
|
||||||
))
|
))
|
||||||
|
|||||||
@ -59,6 +59,7 @@ if (multithreaded) {
|
|||||||
let baseConfig = {
|
let baseConfig = {
|
||||||
platform: "browser",
|
platform: "browser",
|
||||||
bundle: true,
|
bundle: true,
|
||||||
|
minify: release,
|
||||||
assetNames: "assets/[name]",
|
assetNames: "assets/[name]",
|
||||||
define: {
|
define: {
|
||||||
WEBGL: `${webgl}`,
|
WEBGL: `${webgl}`,
|
||||||
|
|||||||
@ -25,7 +25,7 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
|
|||||||
preventDefaultTouchActions();
|
preventDefaultTouchActions();
|
||||||
|
|
||||||
if (MULTITHREADED) {
|
if (MULTITHREADED) {
|
||||||
const MEMORY = 209715200; // 200MB
|
const MEMORY = 900 * 1024 * 1024; // 900 MB
|
||||||
const PAGES = 64 * 1024;
|
const PAGES = 64 * 1024;
|
||||||
|
|
||||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY / PAGES, shared: true})
|
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY / PAGES, shared: true})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import * as maplibre from "../wasm/maplibre"
|
import * as maplibre from "../wasm/maplibre"
|
||||||
|
|
||||||
type MessageData = {type: 'wasm_init', module: WebAssembly.Module, memory: WebAssembly.Memory}
|
type MessageData = {type: 'wasm_init', module: WebAssembly.Module, memory: WebAssembly.Memory}
|
||||||
| {type: 'call', work_ptr: number}
|
| {type: 'pool_call', work_ptr: number}
|
||||||
|
|
||||||
let initialised: Promise<maplibre.InitOutput> = null
|
let initialised: Promise<maplibre.InitOutput> = null
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ onmessage = async (message: MessageEvent<MessageData>) => {
|
|||||||
const data = message.data;
|
const data = message.data;
|
||||||
const module = data.module;
|
const module = data.module;
|
||||||
const memory = data.memory;
|
const memory = data.memory;
|
||||||
const initialised = maplibre.default(module, memory).catch(err => {
|
initialised = maplibre.default(module, memory).catch(err => {
|
||||||
// Propagate to main `onerror`:
|
// Propagate to main `onerror`:
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
throw err;
|
throw err;
|
||||||
@ -25,10 +25,8 @@ onmessage = async (message: MessageEvent<MessageData>) => {
|
|||||||
// Rethrow to keep promise rejected and prevent execution of further commands:
|
// Rethrow to keep promise rejected and prevent execution of further commands:
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
} else if (type === 'call') {
|
} else if (type === 'pool_call') {
|
||||||
const work_ptr = message.data.work_ptr; // because memory is shared, this pointer is valid in the memory of the main thread and this worker thread
|
const work_ptr = message.data.work_ptr; // because memory is shared, this pointer is valid in the memory of the main thread and this worker thread
|
||||||
// This will queue further commands up until the module is fully initialised:
|
|
||||||
await initialised;
|
|
||||||
|
|
||||||
const process_data: (msg: any) => Promise<void> = maplibre["multithreaded_process_data"]
|
const process_data: (msg: any) => Promise<void> = maplibre["multithreaded_process_data"]
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use js_sys::Promise;
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use web_sys::Worker;
|
use web_sys::Worker;
|
||||||
@ -17,8 +16,10 @@ extern "C" {
|
|||||||
fn new_worker() -> JsValue;
|
fn new_worker() -> JsValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type PinnedFuture = std::pin::Pin<Box<(dyn std::future::Future<Output = ()> + 'static)>>;
|
||||||
|
|
||||||
type NewWorker = Box<dyn Fn() -> Result<Worker, WebError>>;
|
type NewWorker = Box<dyn Fn() -> Result<Worker, WebError>>;
|
||||||
type Execute = Box<dyn (FnOnce() -> Promise) + Send>;
|
type Execute = Box<dyn (FnOnce() -> PinnedFuture) + Send>;
|
||||||
|
|
||||||
pub struct WorkerPool {
|
pub struct WorkerPool {
|
||||||
new_worker: NewWorker,
|
new_worker: NewWorker,
|
||||||
@ -44,7 +45,7 @@ pub struct Work {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Work {
|
impl Work {
|
||||||
pub fn execute(self) -> Promise {
|
pub fn execute(self) -> PinnedFuture {
|
||||||
(self.func)()
|
(self.func)()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,13 +140,16 @@ impl WorkerPool {
|
|||||||
///
|
///
|
||||||
/// Returns any error that may happen while a JS web worker is created and a
|
/// Returns any error that may happen while a JS web worker is created and a
|
||||||
/// message is sent to it.
|
/// message is sent to it.
|
||||||
pub fn execute(&self, f: impl (FnOnce() -> Promise) + Send + 'static) -> Result<(), WebError> {
|
pub fn execute(
|
||||||
|
&self,
|
||||||
|
f: impl (FnOnce() -> PinnedFuture) + Send + 'static,
|
||||||
|
) -> Result<(), WebError> {
|
||||||
let worker = self.worker()?;
|
let worker = self.worker()?;
|
||||||
let work = Work { func: Box::new(f) };
|
let work = Work { func: Box::new(f) };
|
||||||
let work_ptr = Box::into_raw(Box::new(work));
|
let work_ptr = Box::into_raw(Box::new(work));
|
||||||
match worker.post_message(
|
match worker.post_message(
|
||||||
&js_sys::Object::from_entries(&js_sys::Array::of2(
|
&js_sys::Object::from_entries(&js_sys::Array::of2(
|
||||||
&js_sys::Array::of2(&JsValue::from("type"), &js_sys::JsString::from("call")),
|
&js_sys::Array::of2(&JsValue::from("type"), &js_sys::JsString::from("pool_call")),
|
||||||
&js_sys::Array::of2(&JsValue::from("work_ptr"), &JsValue::from(work_ptr as u32)),
|
&js_sys::Array::of2(&JsValue::from("work_ptr"), &JsValue::from(work_ptr as u32)),
|
||||||
))
|
))
|
||||||
.expect("can not fail"),
|
.expect("can not fail"),
|
||||||
|
|||||||
@ -36,12 +36,7 @@ impl Scheduler for WebWorkerPoolScheduler {
|
|||||||
T: Future<Output = ()> + 'static,
|
T: Future<Output = ()> + 'static,
|
||||||
{
|
{
|
||||||
self.pool
|
self.pool
|
||||||
.execute(move || {
|
.execute(move || Box::pin(future_factory()))
|
||||||
wasm_bindgen_futures::future_to_promise(async move {
|
|
||||||
future_factory().await;
|
|
||||||
Ok(JsValue::undefined())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.map_err(|e| ScheduleError::Scheduling(Box::new(e)))
|
.map_err(|e| ScheduleError::Scheduling(Box::new(e)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
use maplibre::io::apc::CallError;
|
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
use wasm_bindgen_futures::JsFuture;
|
|
||||||
|
|
||||||
use crate::{platform::multithreaded::pool::Work, JSError};
|
use crate::{platform::multithreaded::pool::Work, JSError};
|
||||||
|
|
||||||
/// Entry point invoked by the worker.
|
/// Entry point invoked by the worker.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn multithreaded_process_data(work_ptr: *mut Work) -> Result<(), JSError> {
|
pub async fn multithreaded_process_data(work_ptr: *mut Work) -> Result<(), JSError> {
|
||||||
let work = unsafe { Box::from_raw(work_ptr) };
|
let work: Box<Work> = unsafe { Box::from_raw(work_ptr) };
|
||||||
JsFuture::from(work.execute())
|
work.execute().await;
|
||||||
.await
|
|
||||||
.map_err(|_e| CallError::Schedule)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ pub struct PassingContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Context for PassingContext {
|
impl Context for PassingContext {
|
||||||
fn send<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
|
fn send_back<T: IntoMessage>(&self, message: T) -> Result<(), SendError> {
|
||||||
let message = message.into();
|
let message = message.into();
|
||||||
let tag = if WebMessageTag::LayerRaster.dyn_clone().as_ref() == message.tag() {
|
let tag = if WebMessageTag::LayerRaster.dyn_clone().as_ref() == message.tag() {
|
||||||
&WebMessageTag::LayerRaster
|
&WebMessageTag::LayerRaster
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user