mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Improve and fix http cache & add offscreen kernel config (#307)
* Switch HTTP cache crate * Set compiler on linux * Start work on offscreen kernel sharing * Finish basic config sharing mechanism on web and native; not really used on web so far though
This commit is contained in:
parent
972fd528c9
commit
151ee9fb77
@ -32,7 +32,7 @@ authors = ["Maximilian Ammann <max@maxammann.org>"]
|
|||||||
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
|
||||||
|
|
||||||
# FIXME: Untrusted dependency, 0.2.x doesn't compile with cache middleware
|
# FIXME: Untrusted dependency, 0.2.x doesn't compile with cache middleware
|
||||||
reqwest-middleware = "0.1.6"
|
reqwest-middleware = "0.3.2"
|
||||||
|
|
||||||
winit = { version = "0.30", default-features = false, features = ["rwh_06"] }
|
winit = { version = "0.30", default-features = false, features = ["rwh_06"] }
|
||||||
|
|
||||||
@ -67,8 +67,7 @@ lyon = { version = "1.0.1", features = [] }
|
|||||||
naga = { version = "0.13.0", features = ["wgsl-in"] }
|
naga = { version = "0.13.0", features = ["wgsl-in"] }
|
||||||
android_logger = "0.13.3"
|
android_logger = "0.13.3"
|
||||||
png = { version = "0.17.10" }
|
png = { version = "0.17.10" }
|
||||||
reqwest = { version = "0.11.20", default-features = false, features = ["rustls-tls", "gzip"] } # Use rusttls on android because cross compiling is difficult
|
reqwest = { version = "0.12.5", default-features = false, features = ["rustls-tls", "gzip"] } # Use rusttls on android because cross compiling is difficult
|
||||||
reqwest-middleware-cache = "0.1.1" # FIXME: Untrusted dependency
|
|
||||||
rstar = "0.11.0"
|
rstar = "0.11.0"
|
||||||
rusqlite = { version = "0.29.0" }
|
rusqlite = { version = "0.29.0" }
|
||||||
serde = { version = "1.0.188", features = ["derive"] }
|
serde = { version = "1.0.188", features = ["derive"] }
|
||||||
@ -88,6 +87,7 @@ wasm-bindgen-futures = "0.4"
|
|||||||
wasm-bindgen-test = "0.3"
|
wasm-bindgen-test = "0.3"
|
||||||
web-sys = "0.3" # Individual features are customized in each crate
|
web-sys = "0.3" # Individual features are customized in each crate
|
||||||
wgpu = "0.19.4"
|
wgpu = "0.19.4"
|
||||||
|
http-cache-reqwest = "0.14.0"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
|
|||||||
@ -14,7 +14,7 @@ pub fn android_main(app: android_activity::AndroidApp) {
|
|||||||
android_logger::Config::default().with_max_level(log::LevelFilter::Info),
|
android_logger::Config::default().with_max_level(log::LevelFilter::Info),
|
||||||
);
|
);
|
||||||
log::log!(Level::Info, "maplibre starting");
|
log::log!(Level::Info, "maplibre starting");
|
||||||
run_headed_map(
|
run_headed_map::<String>(
|
||||||
None,
|
None,
|
||||||
WinitMapWindowConfig::new("maplibre".to_string(), app),
|
WinitMapWindowConfig::new("maplibre".to_string(), app),
|
||||||
WgpuSettings {
|
WgpuSettings {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ compile_error!("apple works only on macOS and iOS.");
|
|||||||
pub fn maplibre_apple_main() {
|
pub fn maplibre_apple_main() {
|
||||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
run_headed_map(
|
run_headed_map::<String>(
|
||||||
None,
|
None,
|
||||||
WinitMapWindowConfig::new("maplibre".to_string()),
|
WinitMapWindowConfig::new("maplibre".to_string()),
|
||||||
WgpuSettings {
|
WgpuSettings {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#![deny(unused_imports)]
|
#![deny(unused_imports)]
|
||||||
|
|
||||||
use std::io::ErrorKind;
|
use std::{io::ErrorKind, path::PathBuf};
|
||||||
|
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use maplibre::{coords::LatLon, render::settings::WgpuSettings};
|
use maplibre::{coords::LatLon, render::settings::WgpuSettings};
|
||||||
@ -64,7 +64,7 @@ fn main() {
|
|||||||
// matches just as you would the top level cmd
|
// matches just as you would the top level cmd
|
||||||
match &cli.command {
|
match &cli.command {
|
||||||
Commands::Headed {} => run_headed_map(
|
Commands::Headed {} => run_headed_map(
|
||||||
None,
|
Some(PathBuf::from("./maplibre-cache".to_string())),
|
||||||
WinitMapWindowConfig::new("maplibre".to_string()),
|
WinitMapWindowConfig::new("maplibre".to_string()),
|
||||||
WgpuSettings {
|
WgpuSettings {
|
||||||
backends: Some(maplibre::render::settings::Backends::all()),
|
backends: Some(maplibre::render::settings::Backends::all()),
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use std::{fmt::Debug, marker::PhantomData};
|
|||||||
|
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use maplibre::{
|
use maplibre::{
|
||||||
environment::{Environment, OffscreenKernelEnvironment},
|
environment::{Environment, OffscreenKernel},
|
||||||
event_loop::{EventLoop, EventLoopProxy, SendEventError},
|
event_loop::{EventLoop, EventLoopProxy, SendEventError},
|
||||||
io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient},
|
io::{apc::AsyncProcedureCall, scheduler::Scheduler, source_client::HttpClient},
|
||||||
map::Map,
|
map::Map,
|
||||||
@ -207,7 +207,7 @@ impl<ET: 'static> EventLoopProxy<ET> for WinitEventLoopProxy<ET> {
|
|||||||
pub struct WinitEnvironment<
|
pub struct WinitEnvironment<
|
||||||
S: Scheduler,
|
S: Scheduler,
|
||||||
HC: HttpClient,
|
HC: HttpClient,
|
||||||
K: OffscreenKernelEnvironment,
|
K: OffscreenKernel,
|
||||||
APC: AsyncProcedureCall<K>,
|
APC: AsyncProcedureCall<K>,
|
||||||
ET,
|
ET,
|
||||||
> {
|
> {
|
||||||
@ -221,7 +221,7 @@ pub struct WinitEnvironment<
|
|||||||
impl<
|
impl<
|
||||||
S: Scheduler,
|
S: Scheduler,
|
||||||
HC: HttpClient,
|
HC: HttpClient,
|
||||||
K: OffscreenKernelEnvironment,
|
K: OffscreenKernel,
|
||||||
APC: AsyncProcedureCall<K>,
|
APC: AsyncProcedureCall<K>,
|
||||||
ET: 'static + Clone,
|
ET: 'static + Clone,
|
||||||
> Environment for WinitEnvironment<S, HC, K, APC, ET>
|
> Environment for WinitEnvironment<S, HC, K, APC, ET>
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
//! * Platform Events like suspend/resume
|
//! * Platform Events like suspend/resume
|
||||||
//! * Render a new frame
|
//! * Render a new frame
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::{marker::PhantomData, path::PathBuf};
|
||||||
|
|
||||||
use maplibre::{
|
use maplibre::{
|
||||||
|
environment::OffscreenKernelConfig,
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
io::apc::SchedulerAsyncProcedureCall,
|
io::apc::SchedulerAsyncProcedureCall,
|
||||||
kernel::{Kernel, KernelBuilder},
|
kernel::{Kernel, KernelBuilder},
|
||||||
@ -98,21 +99,29 @@ impl<ET: 'static + Clone> MapWindowConfig for WinitMapWindowConfig<ET> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_headed_map(
|
pub fn run_headed_map<P>(
|
||||||
cache_path: Option<String>,
|
cache_path: Option<P>,
|
||||||
window_config: WinitMapWindowConfig<()>,
|
window_config: WinitMapWindowConfig<()>,
|
||||||
wgpu_settings: WgpuSettings,
|
wgpu_settings: WgpuSettings,
|
||||||
) {
|
) where
|
||||||
|
P: Into<PathBuf>,
|
||||||
|
{
|
||||||
run_multithreaded(async {
|
run_multithreaded(async {
|
||||||
type Environment<S, HC, APC> =
|
type Environment<S, HC, APC> =
|
||||||
WinitEnvironment<S, HC, ReqwestOffscreenKernelEnvironment, APC, ()>;
|
WinitEnvironment<S, HC, ReqwestOffscreenKernelEnvironment, APC, ()>;
|
||||||
|
|
||||||
let client = ReqwestHttpClient::new(cache_path);
|
let cache_path = cache_path.map(|path| path.into());
|
||||||
|
let client = ReqwestHttpClient::new(cache_path.clone());
|
||||||
|
|
||||||
let kernel: Kernel<Environment<_, _, _>> = KernelBuilder::new()
|
let kernel: Kernel<Environment<_, _, _>> = KernelBuilder::new()
|
||||||
.with_map_window_config(window_config)
|
.with_map_window_config(window_config)
|
||||||
.with_http_client(client.clone())
|
.with_http_client(client.clone())
|
||||||
.with_apc(SchedulerAsyncProcedureCall::new(TokioScheduler::new()))
|
.with_apc(SchedulerAsyncProcedureCall::new(
|
||||||
|
TokioScheduler::new(),
|
||||||
|
OffscreenKernelConfig {
|
||||||
|
cache_directory: cache_path.map(|path| path.to_str().unwrap().to_string()),
|
||||||
|
},
|
||||||
|
))
|
||||||
.with_scheduler(TokioScheduler::new())
|
.with_scheduler(TokioScheduler::new())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ tokio = { workspace = true, features = ["macros", "rt", "rt-multi-thread", "sync
|
|||||||
tokio-util.workspace = true
|
tokio-util.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
reqwest-middleware-cache.workspace = true
|
http-cache-reqwest.workspace = true
|
||||||
reqwest-middleware.workspace = true
|
reqwest-middleware.workspace = true
|
||||||
tracing-tracy = { workspace = true, optional = true }
|
tracing-tracy = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
io::{
|
io::{
|
||||||
apc::AsyncProcedureCall,
|
apc::AsyncProcedureCall,
|
||||||
@ -24,12 +26,17 @@ pub trait Environment: 'static {
|
|||||||
|
|
||||||
type HttpClient: HttpClient;
|
type HttpClient: HttpClient;
|
||||||
|
|
||||||
type OffscreenKernelEnvironment: OffscreenKernelEnvironment;
|
type OffscreenKernelEnvironment: OffscreenKernel;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait OffscreenKernelEnvironment: Send + Sync + 'static {
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct OffscreenKernelConfig {
|
||||||
|
pub cache_directory: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OffscreenKernel: Send + Sync + 'static {
|
||||||
type HttpClient: HttpClient;
|
type HttpClient: HttpClient;
|
||||||
fn create() -> Self;
|
fn create(config: OffscreenKernelConfig) -> Self;
|
||||||
|
|
||||||
fn source_client(&self) -> SourceClient<Self::HttpClient>;
|
fn source_client(&self) -> SourceClient<Self::HttpClient>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
environment::OffscreenKernelConfig,
|
||||||
headless::{
|
headless::{
|
||||||
environment::HeadlessEnvironment,
|
environment::HeadlessEnvironment,
|
||||||
graph_node::CopySurfaceBufferNode,
|
graph_node::CopySurfaceBufferNode,
|
||||||
@ -37,7 +38,12 @@ pub async fn create_headless_renderer(
|
|||||||
PhysicalSize::new(tile_size, tile_size).unwrap(),
|
PhysicalSize::new(tile_size, tile_size).unwrap(),
|
||||||
))
|
))
|
||||||
.with_http_client(client.clone())
|
.with_http_client(client.clone())
|
||||||
.with_apc(SchedulerAsyncProcedureCall::new(TokioScheduler::new()))
|
.with_apc(SchedulerAsyncProcedureCall::new(
|
||||||
|
TokioScheduler::new(),
|
||||||
|
OffscreenKernelConfig {
|
||||||
|
cache_directory: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
.with_scheduler(TokioScheduler::new())
|
.with_scheduler(TokioScheduler::new())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@ -16,8 +16,11 @@ use serde::{Deserialize, Serialize};
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
coords::WorldTileCoords, define_label, environment::OffscreenKernelEnvironment,
|
coords::WorldTileCoords,
|
||||||
io::scheduler::Scheduler, style::Style,
|
define_label,
|
||||||
|
environment::{OffscreenKernel, OffscreenKernelConfig},
|
||||||
|
io::scheduler::Scheduler,
|
||||||
|
style::Style,
|
||||||
};
|
};
|
||||||
|
|
||||||
define_label!(MessageTag);
|
define_label!(MessageTag);
|
||||||
@ -164,7 +167,7 @@ pub type AsyncProcedure<K, C> = fn(input: Input, context: C, kernel: K) -> Async
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
// TODO: Rename to AsyncProcedureCaller?
|
// TODO: Rename to AsyncProcedureCaller?
|
||||||
pub trait AsyncProcedureCall<K: OffscreenKernelEnvironment>: 'static {
|
pub trait AsyncProcedureCall<K: OffscreenKernel>: 'static {
|
||||||
type Context: Context + Send + Clone;
|
type Context: Context + Send + Clone;
|
||||||
|
|
||||||
type ReceiveIterator<F: FnMut(&Message) -> bool>: Iterator<Item = Message>;
|
type ReceiveIterator<F: FnMut(&Message) -> bool>: Iterator<Item = Message>;
|
||||||
@ -194,27 +197,29 @@ impl Context for SchedulerContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SchedulerAsyncProcedureCall<K: OffscreenKernelEnvironment, S: Scheduler> {
|
// An APC that uses a scheduler to execute work asynchronously.
|
||||||
|
// An async sender and receiver to exchange return values of calls.
|
||||||
|
pub struct SchedulerAsyncProcedureCall<K: OffscreenKernel, S: Scheduler> {
|
||||||
channel: (Sender<Message>, Receiver<Message>),
|
channel: (Sender<Message>, Receiver<Message>),
|
||||||
buffer: RefCell<Vec<Message>>,
|
buffer: RefCell<Vec<Message>>,
|
||||||
scheduler: S,
|
scheduler: S,
|
||||||
phantom_k: PhantomData<K>,
|
phantom_k: PhantomData<K>,
|
||||||
|
offscreen_kernel_config: OffscreenKernelConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: OffscreenKernelEnvironment, S: Scheduler> SchedulerAsyncProcedureCall<K, S> {
|
impl<K: OffscreenKernel, S: Scheduler> SchedulerAsyncProcedureCall<K, S> {
|
||||||
pub fn new(scheduler: S) -> Self {
|
pub fn new(scheduler: S, offscreen_kernel_config: OffscreenKernelConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
channel: mpsc::channel(),
|
channel: mpsc::channel(),
|
||||||
buffer: RefCell::new(Vec::new()),
|
buffer: RefCell::new(Vec::new()),
|
||||||
phantom_k: PhantomData::default(),
|
phantom_k: PhantomData::default(),
|
||||||
scheduler,
|
scheduler,
|
||||||
|
offscreen_kernel_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: OffscreenKernelEnvironment, S: Scheduler> AsyncProcedureCall<K>
|
impl<K: OffscreenKernel, S: Scheduler> AsyncProcedureCall<K> for SchedulerAsyncProcedureCall<K, S> {
|
||||||
for SchedulerAsyncProcedureCall<K, S>
|
|
||||||
{
|
|
||||||
type Context = SchedulerContext;
|
type Context = SchedulerContext;
|
||||||
type ReceiveIterator<F: FnMut(&Message) -> bool> = IntoIter<Message>;
|
type ReceiveIterator<F: FnMut(&Message) -> bool> = IntoIter<Message>;
|
||||||
|
|
||||||
@ -255,12 +260,14 @@ impl<K: OffscreenKernelEnvironment, S: Scheduler> AsyncProcedureCall<K>
|
|||||||
procedure: AsyncProcedure<K, Self::Context>,
|
procedure: AsyncProcedure<K, Self::Context>,
|
||||||
) -> Result<(), CallError> {
|
) -> Result<(), CallError> {
|
||||||
let sender = self.channel.0.clone();
|
let sender = self.channel.0.clone();
|
||||||
|
let offscreen_kernel_config = self.offscreen_kernel_config.clone();
|
||||||
|
|
||||||
self.scheduler
|
self.scheduler
|
||||||
.schedule(move || async move {
|
.schedule(move || async move {
|
||||||
log::info!("Processing on thread: {:?}", std::thread::current().name());
|
log::info!("Processing on thread: {:?}", std::thread::current().name());
|
||||||
|
|
||||||
procedure(input, SchedulerContext { sender }, K::create())
|
let kernel = K::create(offscreen_kernel_config);
|
||||||
|
procedure(input, SchedulerContext { sender }, kernel)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use http_cache_reqwest::{CACacheManager, Cache, CacheMode, HttpCache, HttpCacheOptions};
|
||||||
use reqwest::{Client, StatusCode};
|
use reqwest::{Client, StatusCode};
|
||||||
use reqwest_middleware::ClientWithMiddleware;
|
use reqwest_middleware::ClientWithMiddleware;
|
||||||
use reqwest_middleware_cache::{managers::CACacheManager, Cache, CacheMode};
|
|
||||||
|
|
||||||
use crate::io::source_client::{HttpClient, SourceFetchError};
|
use crate::io::source_client::{HttpClient, SourceFetchError};
|
||||||
|
|
||||||
@ -24,20 +26,24 @@ impl From<reqwest_middleware::Error> for SourceFetchError {
|
|||||||
|
|
||||||
impl ReqwestHttpClient {
|
impl ReqwestHttpClient {
|
||||||
/// cache_path: Under which path should we cache requests.
|
/// cache_path: Under which path should we cache requests.
|
||||||
// TODO: Use Into<Path> instead of String
|
pub fn new<P>(cache_path: Option<P>) -> Self
|
||||||
pub fn new(cache_path: Option<String>) -> Self {
|
where
|
||||||
|
P: Into<PathBuf>,
|
||||||
|
{
|
||||||
let mut builder = reqwest_middleware::ClientBuilder::new(Client::new());
|
let mut builder = reqwest_middleware::ClientBuilder::new(Client::new());
|
||||||
|
|
||||||
if let Some(cache_path) = cache_path {
|
if let Some(cache_path) = cache_path {
|
||||||
builder = builder.with(Cache {
|
builder = builder.with(Cache(HttpCache {
|
||||||
mode: CacheMode::Default,
|
mode: CacheMode::Default,
|
||||||
cache_manager: CACacheManager { path: cache_path },
|
manager: CACacheManager {
|
||||||
});
|
path: cache_path.into(),
|
||||||
|
},
|
||||||
|
options: HttpCacheOptions::default(),
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
let client = builder.build();
|
||||||
|
|
||||||
Self {
|
Self { client }
|
||||||
client: builder.build(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
environment::OffscreenKernelEnvironment,
|
environment::{OffscreenKernel, OffscreenKernelConfig},
|
||||||
io::source_client::{HttpSourceClient, SourceClient},
|
io::source_client::{HttpSourceClient, SourceClient},
|
||||||
platform::http_client::ReqwestHttpClient,
|
platform::http_client::ReqwestHttpClient,
|
||||||
};
|
};
|
||||||
@ -35,16 +35,18 @@ pub fn run_multithreaded<F: Future>(future: F) -> F::Output {
|
|||||||
.block_on(future)
|
.block_on(future)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ReqwestOffscreenKernelEnvironment;
|
pub struct ReqwestOffscreenKernelEnvironment(OffscreenKernelConfig);
|
||||||
|
|
||||||
impl OffscreenKernelEnvironment for ReqwestOffscreenKernelEnvironment {
|
impl OffscreenKernel for ReqwestOffscreenKernelEnvironment {
|
||||||
type HttpClient = ReqwestHttpClient;
|
type HttpClient = ReqwestHttpClient;
|
||||||
|
|
||||||
fn create() -> Self {
|
fn create(config: OffscreenKernelConfig) -> Self {
|
||||||
ReqwestOffscreenKernelEnvironment
|
ReqwestOffscreenKernelEnvironment(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_client(&self) -> SourceClient<Self::HttpClient> {
|
fn source_client(&self) -> SourceClient<Self::HttpClient> {
|
||||||
SourceClient::new(HttpSourceClient::new(ReqwestHttpClient::new(None)))
|
SourceClient::new(HttpSourceClient::new(ReqwestHttpClient::new::<String>(
|
||||||
|
self.0.cache_directory.clone(),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use std::{borrow::Cow, collections::HashSet, marker::PhantomData, rc::Rc};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::MapContext,
|
context::MapContext,
|
||||||
environment::{Environment, OffscreenKernelEnvironment},
|
environment::{Environment, OffscreenKernel},
|
||||||
io::{
|
io::{
|
||||||
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, ProcedureError},
|
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, ProcedureError},
|
||||||
source_type::{RasterSource, SourceType},
|
source_type::{RasterSource, SourceType},
|
||||||
@ -101,11 +101,7 @@ impl<E: Environment, T: RasterTransferables> System for RequestSystem<E, T> {
|
|||||||
view_state.update_references();
|
view_state.update_references();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn fetch_raster_apc<
|
pub fn fetch_raster_apc<K: OffscreenKernel, T: RasterTransferables, C: Context + Clone + Send>(
|
||||||
K: OffscreenKernelEnvironment,
|
|
||||||
T: RasterTransferables,
|
|
||||||
C: Context + Clone + Send,
|
|
||||||
>(
|
|
||||||
input: Input,
|
input: Input,
|
||||||
context: C,
|
context: C,
|
||||||
kernel: K,
|
kernel: K,
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use std::{borrow::Cow, collections::HashSet, marker::PhantomData, rc::Rc};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::MapContext,
|
context::MapContext,
|
||||||
environment::{Environment, OffscreenKernelEnvironment},
|
environment::{Environment, OffscreenKernel},
|
||||||
io::{
|
io::{
|
||||||
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, ProcedureError},
|
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, ProcedureError},
|
||||||
source_type::{SourceType, TessellateSource},
|
source_type::{SourceType, TessellateSource},
|
||||||
@ -103,11 +103,7 @@ impl<E: Environment, T: VectorTransferables> System for RequestSystem<E, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_vector_apc<
|
pub fn fetch_vector_apc<K: OffscreenKernel, T: VectorTransferables, C: Context + Clone + Send>(
|
||||||
K: OffscreenKernelEnvironment,
|
|
||||||
T: VectorTransferables,
|
|
||||||
C: Context + Clone + Send,
|
|
||||||
>(
|
|
||||||
input: Input,
|
input: Input,
|
||||||
context: C,
|
context: C,
|
||||||
kernel: K,
|
kernel: K,
|
||||||
|
|||||||
20
shell.nix
20
shell.nix
@ -2,7 +2,10 @@
|
|||||||
# The repository supports direnv (https://direnv.net/). If your IDE supports direnv,
|
# The repository supports direnv (https://direnv.net/). If your IDE supports direnv,
|
||||||
# then you do not need to care about dependencies.
|
# then you do not need to care about dependencies.
|
||||||
|
|
||||||
{ pkgs ? import <nixpkgs> { } }:
|
{ pkgs ? import <nixpkgs> {
|
||||||
|
overlays = [];
|
||||||
|
}
|
||||||
|
}:
|
||||||
with pkgs;
|
with pkgs;
|
||||||
let
|
let
|
||||||
unstable = import
|
unstable = import
|
||||||
@ -15,15 +18,19 @@ in
|
|||||||
# Wew are using the host clang on macOS; the Nix clang adds a clag that breaks cross compilation here:
|
# Wew are using the host clang on macOS; the Nix clang adds a clag that breaks cross compilation here:
|
||||||
# https://github.com/NixOS/nixpkgs/blob/362cb82b75394680990cbe89f40fe65d35f66617/pkgs/build-support/cc-wrapper/default.nix#L490
|
# https://github.com/NixOS/nixpkgs/blob/362cb82b75394680990cbe89f40fe65d35f66617/pkgs/build-support/cc-wrapper/default.nix#L490
|
||||||
# It caused this error during the compilation of ring: clang-15: error: invalid argument '-mmacos-version-min=11.0' not allowed with '-miphoneos-version-min=7.0'
|
# It caused this error during the compilation of ring: clang-15: error: invalid argument '-mmacos-version-min=11.0' not allowed with '-miphoneos-version-min=7.0'
|
||||||
stdenv = stdenvNoCC;
|
stdenv = if stdenv.isDarwin then stdenvNoCC else llvmPackages_16.stdenv;
|
||||||
}) {
|
}) {
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
# Tools
|
# Tools
|
||||||
unstable.rustup
|
rustup
|
||||||
unstable.just
|
unstable.just
|
||||||
unstable.nodejs
|
unstable.nodejs
|
||||||
unstable.mdbook
|
unstable.mdbook
|
||||||
# unstable.wasm-bindgen-cli # we need wasm-bindgen-cli@0.2.92, so pull it from cargo instead
|
(pkgs.wasm-bindgen-cli.override {
|
||||||
|
version = "0.2.92"; # This needs to match the wasm-bindgen version of the web module
|
||||||
|
hash = "sha256-1VwY8vQy7soKEgbki4LD+v259751kKxSxmo/gqE6yV0=";
|
||||||
|
cargoHash = "sha256-aACJ+lYNEU8FFBs158G1/JG8sc6Rq080PeKCMnwdpH0=";
|
||||||
|
})
|
||||||
unstable.tracy
|
unstable.tracy
|
||||||
unstable.nixpkgs-fmt # To format this file: nixpkgs-fmt *.nix
|
unstable.nixpkgs-fmt # To format this file: nixpkgs-fmt *.nix
|
||||||
# System dependencies
|
# System dependencies
|
||||||
@ -32,14 +39,15 @@ in
|
|||||||
|
|
||||||
pkgs.jdk17
|
pkgs.jdk17
|
||||||
|
|
||||||
|
unstable.sqlite
|
||||||
|
unstable.pkg-config
|
||||||
|
] ++ lib.optionals stdenv.isLinux [
|
||||||
unstable.xorg.libXrandr
|
unstable.xorg.libXrandr
|
||||||
unstable.xorg.libXi
|
unstable.xorg.libXi
|
||||||
unstable.xorg.libXcursor
|
unstable.xorg.libXcursor
|
||||||
unstable.xorg.libX11
|
unstable.xorg.libX11
|
||||||
unstable.libxkbcommon
|
unstable.libxkbcommon
|
||||||
unstable.sqlite
|
|
||||||
unstable.wayland
|
unstable.wayland
|
||||||
unstable.pkg-config
|
|
||||||
];
|
];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ pkgs.lib.makeLibraryPath [ unstable.libxkbcommon ] }";
|
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${ pkgs.lib.makeLibraryPath [ unstable.libxkbcommon ] }";
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {dirname} from "path";
|
|||||||
import {fileURLToPath} from "url";
|
import {fileURLToPath} from "url";
|
||||||
|
|
||||||
let argv = yargs(process.argv.slice(2))
|
let argv = yargs(process.argv.slice(2))
|
||||||
|
.strict(true)
|
||||||
.option('release', {
|
.option('release', {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
description: 'Release mode'
|
description: 'Release mode'
|
||||||
@ -140,6 +141,8 @@ const wasmPack = () => {
|
|||||||
"-C", "link-args=--shared-memory --import-memory --max-memory=209715200"
|
"-C", "link-args=--shared-memory --import-memory --max-memory=209715200"
|
||||||
]`
|
]`
|
||||||
|
|
||||||
|
spawnTool('cargo', ["--version"])
|
||||||
|
|
||||||
let cargo = spawnTool('cargo', [
|
let cargo = spawnTool('cargo', [
|
||||||
...(multithreaded ? ["--config", multithreaded_config] : []),
|
...(multithreaded ? ["--config", multithreaded_config] : []),
|
||||||
"build",
|
"build",
|
||||||
|
|||||||
@ -40,22 +40,23 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
|
|||||||
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
|
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
|
||||||
await maplibre.default(wasmPath, memory);
|
await maplibre.default(wasmPath, memory);
|
||||||
|
|
||||||
await maplibre.run_maplibre((ptr) => {
|
await maplibre.run_maplibre((received_ptr: number) => {
|
||||||
let worker: Worker = workerPath ? new Worker(workerPath, {
|
let worker: Worker = workerPath ? new Worker(workerPath, {
|
||||||
type: 'module',
|
type: 'module',
|
||||||
}) : PoolWorker(); // Setting a "name" for this webworker is not yet supported, because it needs support from esbuild-plugin-inline-worker
|
}) : PoolWorker(); // Setting a "name" for this webworker is not yet supported, because it needs support from esbuild-plugin-inline-worker
|
||||||
|
|
||||||
worker.onmessage = (message: MessageEvent) => {
|
// Handle messages coming back from the Worker
|
||||||
|
worker.onmessage = (message: MessageEvent<[tag: number, buffer: ArrayBuffer]>) => {
|
||||||
// WARNING: Do not modify data passed from Rust!
|
// WARNING: Do not modify data passed from Rust!
|
||||||
let in_transfer = message.data;
|
let data = message.data;
|
||||||
|
|
||||||
const main_entry = maplibre["singlethreaded_main_entry"];
|
const receive_data: (received_ptr: number, tag: number, buffer: ArrayBuffer) => void = maplibre["singlethreaded_receive_data"];
|
||||||
|
|
||||||
if (!main_entry) {
|
if (!receive_data) {
|
||||||
throw Error("singlethreaded_main_entry is not defined. Maybe the Rust build used the wrong build configuration.")
|
throw Error("singlethreaded_main_entry is not defined. Maybe the Rust build used the wrong build configuration.")
|
||||||
}
|
}
|
||||||
|
|
||||||
main_entry(ptr, in_transfer)
|
receive_data(received_ptr, data[0], data[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
return worker;
|
return worker;
|
||||||
|
|||||||
@ -1,25 +1,41 @@
|
|||||||
import * as maplibre from "../wasm/maplibre"
|
import * as maplibre from "../wasm/maplibre"
|
||||||
|
|
||||||
onmessage = async message => {
|
type MessageData = {type: 'wasm_init', module: WebAssembly.Module, memory: WebAssembly.Memory}
|
||||||
const initialised = maplibre.default(message.data[0], message.data[1]).catch(err => {
|
| {type: 'call', work_ptr: number}
|
||||||
// Propagate to main `onerror`:
|
|
||||||
setTimeout(() => {
|
let initialised: Promise<maplibre.InitOutput> = null
|
||||||
|
|
||||||
|
onmessage = async (message: MessageEvent<MessageData>) => {
|
||||||
|
|
||||||
|
if (initialised) {
|
||||||
|
// This will queue further commands up until the module is fully initialised:
|
||||||
|
await initialised;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = message.data.type;
|
||||||
|
if (type === 'wasm_init') {
|
||||||
|
const data = message.data;
|
||||||
|
const module = data.module;
|
||||||
|
const memory = data.memory;
|
||||||
|
const initialised = maplibre.default(module, memory).catch(err => {
|
||||||
|
// Propagate to main `onerror`:
|
||||||
|
setTimeout(() => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
// Rethrow to keep promise rejected and prevent execution of further commands:
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
// Rethrow to keep promise rejected and prevent execution of further commands:
|
} else if (type === 'call') {
|
||||||
throw err;
|
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
|
||||||
});
|
|
||||||
|
|
||||||
self.onmessage = async message => {
|
|
||||||
// This will queue further commands up until the module is fully initialised:
|
// This will queue further commands up until the module is fully initialised:
|
||||||
await initialised;
|
await initialised;
|
||||||
|
|
||||||
const worker_entry = maplibre["multithreaded_worker_entry"]
|
const process_data: (msg: any) => Promise<void> = maplibre["multithreaded_process_data"]
|
||||||
|
|
||||||
if (!worker_entry) {
|
if (!process_data) {
|
||||||
throw Error("multithreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.")
|
throw Error("multithreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.")
|
||||||
}
|
}
|
||||||
|
|
||||||
await worker_entry(message.data);
|
await process_data(work_ptr);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
@ -1,31 +1,54 @@
|
|||||||
import * as maplibre from "../wasm/maplibre"
|
import * as maplibre from "../wasm/maplibre"
|
||||||
|
|
||||||
onmessage = async message => {
|
type MessageData = { type: 'wasm_init', module: WebAssembly.Module }
|
||||||
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
|
| { type: 'kernel_config', config: string }
|
||||||
let module = message.data[0];
|
| { type: 'call', procedure_ptr: number, input: string }
|
||||||
const initialised = maplibre.default(module, memory).catch(err => {
|
|
||||||
// Propagate to main `onerror`:
|
|
||||||
setTimeout(() => {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
// Rethrow to keep promise rejected and prevent execution of further commands:
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
self.onmessage = async message => {
|
let initialised: Promise<maplibre.InitOutput> = null
|
||||||
|
|
||||||
|
onmessage = async (message: MessageEvent<MessageData>) => {
|
||||||
|
|
||||||
|
if (initialised) {
|
||||||
// This will queue further commands up until the module is fully initialised:
|
// This will queue further commands up until the module is fully initialised:
|
||||||
await initialised;
|
await initialised;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = message.data.type;
|
||||||
|
if (type === 'wasm_init') {
|
||||||
|
const data = message.data;
|
||||||
|
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
|
||||||
|
let module = data.module;
|
||||||
|
initialised = maplibre.default(module, memory).catch(err => {
|
||||||
|
// Propagate to main `onerror`:
|
||||||
|
setTimeout(() => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
// Rethrow to keep promise rejected and prevent execution of further commands:
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
} else if (type === 'call') {
|
||||||
|
const data = message.data;
|
||||||
// WARNING: Do not modify data passed from Rust!
|
// WARNING: Do not modify data passed from Rust!
|
||||||
const procedure_ptr = message.data[0];
|
const procedure_ptr = data.procedure_ptr;
|
||||||
const input = message.data[1];
|
const input = data.input;
|
||||||
|
|
||||||
const worker_entry = maplibre["singlethreaded_worker_entry"];
|
const process_data: (procedure_ptr: number, input: string) => Promise<void> = maplibre["singlethreaded_process_data"];
|
||||||
|
|
||||||
if (!worker_entry) {
|
if (!process_data) {
|
||||||
throw Error("singlethreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.")
|
throw Error("singlethreaded_worker_entry is not defined. Maybe the Rust build used the wrong build configuration.")
|
||||||
}
|
}
|
||||||
|
|
||||||
await worker_entry(procedure_ptr, input);
|
await process_data(procedure_ptr, input);
|
||||||
};
|
} else if (type === 'kernel_config') {
|
||||||
|
const data = message.data;
|
||||||
|
|
||||||
|
const set_kernel_config: (config: string) => void = maplibre["set_kernel_config"];
|
||||||
|
|
||||||
|
if (!set_kernel_config) {
|
||||||
|
throw Error("set_kernel_config is not defined. Maybe the Rust build used the wrong build configuration.")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
set_kernel_config(data.config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
#![deny(unused_imports)]
|
#![deny(unused_imports)]
|
||||||
|
|
||||||
use maplibre::{
|
use maplibre::{
|
||||||
environment::OffscreenKernelEnvironment,
|
environment::{OffscreenKernel, OffscreenKernelConfig},
|
||||||
event_loop::EventLoop,
|
event_loop::EventLoop,
|
||||||
io::source_client::{HttpSourceClient, SourceClient},
|
io::source_client::{HttpSourceClient, SourceClient},
|
||||||
kernel::{Kernel, KernelBuilder},
|
kernel::{Kernel, KernelBuilder},
|
||||||
@ -49,10 +49,10 @@ pub fn wasm_bindgen_start() {
|
|||||||
|
|
||||||
pub struct WHATWGOffscreenKernelEnvironment;
|
pub struct WHATWGOffscreenKernelEnvironment;
|
||||||
|
|
||||||
impl OffscreenKernelEnvironment for WHATWGOffscreenKernelEnvironment {
|
impl OffscreenKernel for WHATWGOffscreenKernelEnvironment {
|
||||||
type HttpClient = WHATWGFetchHttpClient;
|
type HttpClient = WHATWGFetchHttpClient;
|
||||||
|
|
||||||
fn create() -> Self {
|
fn create(config: OffscreenKernelConfig) -> Self {
|
||||||
WHATWGOffscreenKernelEnvironment
|
WHATWGOffscreenKernelEnvironment
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +90,10 @@ pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), JSError> {
|
|||||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||||
.with_http_client(WHATWGFetchHttpClient::default());
|
.with_http_client(WHATWGFetchHttpClient::default());
|
||||||
|
|
||||||
|
let offscreen_kernel_config = OffscreenKernelConfig {
|
||||||
|
cache_directory: None,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(target_feature = "atomics")]
|
#[cfg(target_feature = "atomics")]
|
||||||
{
|
{
|
||||||
kernel_builder = kernel_builder
|
kernel_builder = kernel_builder
|
||||||
@ -97,6 +101,7 @@ pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), JSError> {
|
|||||||
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(
|
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(
|
||||||
new_worker.clone(),
|
new_worker.clone(),
|
||||||
)?,
|
)?,
|
||||||
|
offscreen_kernel_config,
|
||||||
))
|
))
|
||||||
.with_scheduler(
|
.with_scheduler(
|
||||||
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker)?,
|
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker)?,
|
||||||
@ -106,7 +111,13 @@ pub async fn run_maplibre(new_worker: js_sys::Function) -> Result<(), JSError> {
|
|||||||
#[cfg(not(target_feature = "atomics"))]
|
#[cfg(not(target_feature = "atomics"))]
|
||||||
{
|
{
|
||||||
kernel_builder = kernel_builder
|
kernel_builder = kernel_builder
|
||||||
.with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4)?)
|
.with_apc(
|
||||||
|
platform::singlethreaded::apc::PassingAsyncProcedureCall::new(
|
||||||
|
new_worker,
|
||||||
|
4,
|
||||||
|
offscreen_kernel_config,
|
||||||
|
)?,
|
||||||
|
)
|
||||||
.with_scheduler(maplibre::io::scheduler::NopScheduler);
|
.with_scheduler(maplibre::io::scheduler::NopScheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,10 +90,14 @@ impl WorkerPool {
|
|||||||
// With a worker spun up send it the module/memory so it can start
|
// With a worker spun up send it the module/memory so it can start
|
||||||
// instantiating the wasm module. Later it might receive further
|
// instantiating the wasm module. Later it might receive further
|
||||||
// messages about code to run on the wasm module.
|
// messages about code to run on the wasm module.
|
||||||
let array = js_sys::Array::new();
|
worker.post_message(
|
||||||
array.push(&wasm_bindgen::module());
|
&js_sys::Object::from_entries(&js_sys::Array::of3(
|
||||||
array.push(&wasm_bindgen::memory());
|
&js_sys::Array::of2(&JsValue::from("type"), &js_sys::JsString::from("wasm_init")),
|
||||||
worker.post_message(&array)?;
|
&js_sys::Array::of2(&JsValue::from("module"), &wasm_bindgen::module()),
|
||||||
|
&js_sys::Array::of2(&JsValue::from("memory"), &wasm_bindgen::memory()),
|
||||||
|
))
|
||||||
|
.expect("can not fail"),
|
||||||
|
)?;
|
||||||
|
|
||||||
self.state.push(worker);
|
self.state.push(worker);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -139,7 +143,13 @@ impl WorkerPool {
|
|||||||
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(&JsValue::from(work_ptr as u32)) {
|
match worker.post_message(
|
||||||
|
&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("work_ptr"), &JsValue::from(work_ptr as u32)),
|
||||||
|
))
|
||||||
|
.expect("can not fail"),
|
||||||
|
) {
|
||||||
Ok(()) => Ok(()),
|
Ok(()) => Ok(()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|||||||
@ -6,8 +6,8 @@ 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_worker_entry(ptr: u32) -> Result<(), JSError> {
|
pub async fn multithreaded_process_data(work_ptr: *mut Work) -> Result<(), JSError> {
|
||||||
let work = unsafe { Box::from_raw(ptr as *mut Work) };
|
let work = unsafe { Box::from_raw(work_ptr) };
|
||||||
JsFuture::from(work.execute())
|
JsFuture::from(work.execute())
|
||||||
.await
|
.await
|
||||||
.map_err(|_e| CallError::Schedule)?;
|
.map_err(|_e| CallError::Schedule)?;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc, vec::IntoIter};
|
|||||||
use js_sys::{ArrayBuffer, Uint8Array};
|
use js_sys::{ArrayBuffer, Uint8Array};
|
||||||
use log::error;
|
use log::error;
|
||||||
use maplibre::{
|
use maplibre::{
|
||||||
environment::OffscreenKernelEnvironment,
|
environment::{OffscreenKernel, OffscreenKernelConfig},
|
||||||
io::{
|
io::{
|
||||||
apc::{
|
apc::{
|
||||||
AsyncProcedure, AsyncProcedureCall, CallError, Context, Input, IntoMessage, Message,
|
AsyncProcedure, AsyncProcedureCall, CallError, Context, Input, IntoMessage, Message,
|
||||||
@ -136,15 +136,18 @@ pub struct PassingAsyncProcedureCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PassingAsyncProcedureCall {
|
impl PassingAsyncProcedureCall {
|
||||||
pub fn new(new_worker: js_sys::Function, initial_workers: usize) -> Result<Self, WebError> {
|
pub fn new(
|
||||||
|
new_worker: js_sys::Function,
|
||||||
|
initial_workers: usize,
|
||||||
|
config: OffscreenKernelConfig,
|
||||||
|
) -> Result<Self, WebError> {
|
||||||
let received = Rc::new(RefCell::new(vec![]));
|
let received = Rc::new(RefCell::new(vec![]));
|
||||||
let received_ref = received.clone();
|
|
||||||
|
|
||||||
let create_new_worker = || {
|
let create_new_worker = || {
|
||||||
new_worker
|
new_worker
|
||||||
.call1(
|
.call1(
|
||||||
&JsValue::undefined(),
|
&JsValue::undefined(),
|
||||||
&JsValue::from(Rc::into_raw(received_ref.clone()) as u32),
|
&JsValue::from(Rc::into_raw(received.clone())),
|
||||||
)
|
)
|
||||||
.map_err(WebError::from)?
|
.map_err(WebError::from)?
|
||||||
.dyn_into::<Worker>()
|
.dyn_into::<Worker>()
|
||||||
@ -156,8 +159,35 @@ impl PassingAsyncProcedureCall {
|
|||||||
for _ in 0..initial_workers {
|
for _ in 0..initial_workers {
|
||||||
let worker: Worker = create_new_worker()?;
|
let worker: Worker = create_new_worker()?;
|
||||||
|
|
||||||
let array = js_sys::Array::of1(&wasm_bindgen::module());
|
worker
|
||||||
worker.post_message(&array).map_err(WebError::from)?;
|
.post_message(
|
||||||
|
&js_sys::Object::from_entries(&js_sys::Array::of2(
|
||||||
|
&js_sys::Array::of2(
|
||||||
|
&JsValue::from("type"),
|
||||||
|
&js_sys::JsString::from("wasm_init"),
|
||||||
|
),
|
||||||
|
&js_sys::Array::of2(&JsValue::from("module"), &wasm_bindgen::module()),
|
||||||
|
))
|
||||||
|
.expect("can not fail"),
|
||||||
|
)
|
||||||
|
.map_err(WebError::from)?;
|
||||||
|
|
||||||
|
worker
|
||||||
|
.post_message(
|
||||||
|
&js_sys::Object::from_entries(&js_sys::Array::of2(
|
||||||
|
&js_sys::Array::of2(
|
||||||
|
&JsValue::from("type"),
|
||||||
|
&js_sys::JsString::from("kernel_config"),
|
||||||
|
),
|
||||||
|
&js_sys::Array::of2(
|
||||||
|
&JsValue::from("config"),
|
||||||
|
&js_sys::JsString::from(serde_json::to_string(&config).expect("TODO")), // FIXME: Handle this error correctly
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.expect("can not fail"),
|
||||||
|
)
|
||||||
|
.map_err(WebError::from)?;
|
||||||
|
|
||||||
workers.push(worker);
|
workers.push(worker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +199,7 @@ impl PassingAsyncProcedureCall {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: OffscreenKernelEnvironment> AsyncProcedureCall<K> for PassingAsyncProcedureCall {
|
impl<K: OffscreenKernel> AsyncProcedureCall<K> for PassingAsyncProcedureCall {
|
||||||
type Context = UsedContext;
|
type Context = UsedContext;
|
||||||
type ReceiveIterator<F: FnMut(&Message) -> bool> = IntoIter<Message>;
|
type ReceiveIterator<F: FnMut(&Message) -> bool> = IntoIter<Message>;
|
||||||
|
|
||||||
@ -213,10 +243,18 @@ impl<K: OffscreenKernelEnvironment> AsyncProcedureCall<K> for PassingAsyncProced
|
|||||||
input: Input,
|
input: Input,
|
||||||
procedure: AsyncProcedure<K, UsedContext>,
|
procedure: AsyncProcedure<K, UsedContext>,
|
||||||
) -> Result<(), CallError> {
|
) -> Result<(), CallError> {
|
||||||
let procedure_ptr = procedure as *mut AsyncProcedure<K, UsedContext> as u32; // FIXME: is u32 fine, define an overflow safe function?
|
let procedure_ptr = procedure as *mut AsyncProcedure<K, UsedContext>; // TODO: Verify how these poitner are converted to pointers
|
||||||
let input = serde_json::to_string(&input).map_err(|e| CallError::Serialize(Box::new(e)))?;
|
let input = serde_json::to_string(&input).map_err(|e| CallError::Serialize(Box::new(e)))?;
|
||||||
|
|
||||||
let message = js_sys::Array::of2(&JsValue::from(procedure_ptr), &JsValue::from(input));
|
let message = js_sys::Object::from_entries(&js_sys::Array::of3(
|
||||||
|
&js_sys::Array::of2(&JsValue::from("type"), &JsValue::from("call")),
|
||||||
|
&js_sys::Array::of2(
|
||||||
|
&JsValue::from("procedure_ptr"),
|
||||||
|
&JsValue::from(procedure_ptr),
|
||||||
|
),
|
||||||
|
&js_sys::Array::of2(&JsValue::from("input"), &JsValue::from(input)),
|
||||||
|
))
|
||||||
|
.expect("can not fail");
|
||||||
|
|
||||||
let worker = self
|
let worker = self
|
||||||
.workers
|
.workers
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
use std::{mem, rc::Rc};
|
use std::{mem, rc::Rc, sync::OnceLock};
|
||||||
|
|
||||||
use js_sys::ArrayBuffer;
|
|
||||||
use log::error;
|
use log::error;
|
||||||
use maplibre::{
|
use maplibre::{
|
||||||
benchmarking::io::{
|
benchmarking::io::{
|
||||||
apc::{AsyncProcedure, Input, Message},
|
apc::{AsyncProcedure, Input, Message},
|
||||||
source_client::{HttpSourceClient, SourceClient},
|
source_client::{HttpSourceClient, SourceClient},
|
||||||
},
|
},
|
||||||
environment::OffscreenKernelEnvironment,
|
environment::OffscreenKernel,
|
||||||
io::apc::CallError,
|
io::apc::CallError,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
@ -27,9 +26,20 @@ use crate::{
|
|||||||
WHATWGFetchHttpClient,
|
WHATWGFetchHttpClient,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Entry point invoked by the worker.
|
static CONFIG: OnceLock<String> = OnceLock::new();
|
||||||
|
|
||||||
|
fn kernel_config() -> &'static str {
|
||||||
|
CONFIG.get().map(move |t| t.as_str()).unwrap_or("{}")
|
||||||
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JSError> {
|
pub fn set_kernel_config(config: String) {
|
||||||
|
CONFIG.set(config).expect("failed to set kernel config")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Entry point invoked by the worker. Processes data and sends the result back to the main thread.
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub async fn singlethreaded_process_data(procedure_ptr: u32, input: String) -> Result<(), JSError> {
|
||||||
let procedure: AsyncProcedure<UsedOffscreenKernelEnvironment, UsedContext> =
|
let procedure: AsyncProcedure<UsedOffscreenKernelEnvironment, UsedContext> =
|
||||||
unsafe { mem::transmute(procedure_ptr) };
|
unsafe { mem::transmute(procedure_ptr) };
|
||||||
|
|
||||||
@ -53,7 +63,12 @@ pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> R
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
procedure(input, context, UsedOffscreenKernelEnvironment::create()).await?;
|
procedure(
|
||||||
|
input,
|
||||||
|
context,
|
||||||
|
UsedOffscreenKernelEnvironment::create(serde_json::from_str(&kernel_config()).unwrap()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -62,21 +77,14 @@ pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> R
|
|||||||
#[error("unable to deserialize message sent by postMessage()")]
|
#[error("unable to deserialize message sent by postMessage()")]
|
||||||
pub struct DeserializeMessage;
|
pub struct DeserializeMessage;
|
||||||
|
|
||||||
/// Entry point invoked by the main thread.
|
/// Entry point invoked by the main thread. Receives data on the main thread and makes it available
|
||||||
|
/// to the renderer.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub unsafe fn singlethreaded_main_entry(
|
pub fn singlethreaded_receive_data(
|
||||||
received_ptr: *const ReceivedType,
|
received_ptr: *const ReceivedType,
|
||||||
in_transfer: js_sys::Array,
|
tag: u32,
|
||||||
|
buffer: js_sys::ArrayBuffer,
|
||||||
) -> Result<(), JSError> {
|
) -> Result<(), JSError> {
|
||||||
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 = WebMessageTag::from_u32(tag).map_err(|e| CallError::Deserialize(Box::new(e)))?;
|
let tag = WebMessageTag::from_u32(tag).map_err(|e| CallError::Deserialize(Box::new(e)))?;
|
||||||
|
|
||||||
log::debug!(
|
log::debug!(
|
||||||
@ -90,7 +98,7 @@ pub unsafe fn singlethreaded_main_entry(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// FIXME: Can we make this call safe? check if it was cloned before?
|
// FIXME: Can we make this call safe? check if it was cloned before?
|
||||||
let received: Rc<ReceivedType> = Rc::from_raw(received_ptr);
|
let received: Rc<ReceivedType> = unsafe { Rc::from_raw(received_ptr) };
|
||||||
|
|
||||||
// MAJOR FIXME: Fix mutability
|
// MAJOR FIXME: Fix mutability
|
||||||
received
|
received
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user