Move platform specific code to web (#63)

* Add a view state for input processing

* Move platform specific web code to web crate

* Run clippy

* Fix apple and android

* Move error function

* Do not enable all features

* Remove header from .h file

* Refactor to use where clauses
This commit is contained in:
Max Ammann 2022-05-02 20:29:05 +02:00 committed by GitHub
parent 7df6ca49d3
commit bac7ffb8c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 499 additions and 396 deletions

View File

@ -20,4 +20,4 @@ runs:
run: mdbook build
- name: API Documentation
shell: bash
run: cargo doc -p maplibre --no-deps --lib --all-features --document-private-items
run: cargo doc -p maplibre --no-deps --lib --document-private-items

View File

@ -1,10 +1,11 @@
use jni::objects::JClass;
use jni::JNIEnv;
use log::Level;
use maplibre::platform::http_client::ReqwestHttpClient;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::window::FromWindow;
use maplibre::{MapBuilder, ScheduleMethod, TokioScheduleMethod};
use maplibre::MapBuilder;
use std::ffi::CString;
pub use std::time::Instant;
#[cfg(not(target_os = "android"))]
compile_error!("android works only on android.");
@ -14,7 +15,8 @@ pub fn android_main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
MapBuilder::from_window("A fantastic window!")
.with_schedule_method(ScheduleMethod::Tokio(TokioScheduleMethod::new()))
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.run_sync();
}

View File

@ -1,6 +1,7 @@
use maplibre::platform::http_client::ReqwestHttpClient;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::window::FromWindow;
use maplibre::{MapBuilder, ScheduleMethod, TokioScheduleMethod};
pub use std::time::Instant;
use maplibre::MapBuilder;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
compile_error!("apple works only on macOS and iOS.");
@ -10,7 +11,8 @@ pub fn maplibre_apple_main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
MapBuilder::from_window("A fantastic window!")
.with_schedule_method(ScheduleMethod::Tokio(TokioScheduleMethod::new()))
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.run_sync();
}

View File

@ -1,10 +1,3 @@
//
// maplibre_rs.h
// maplibre-rs
//
// Created by Ehrenamtskarte on 22.04.22.
//
#import <Foundation/Foundation.h>
//! Project version number for maplibre_rs.

View File

@ -1,5 +1,7 @@
use maplibre::platform::http_client::ReqwestHttpClient;
use maplibre::platform::schedule_method::TokioScheduleMethod;
use maplibre::window::FromWindow;
use maplibre::{MapBuilder, ScheduleMethod, TokioScheduleMethod};
use maplibre::MapBuilder;
#[cfg(feature = "enable-tracing")]
fn enable_tracing() {
@ -11,6 +13,22 @@ fn enable_tracing() {
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
}
fn run_in_window() {
MapBuilder::from_window("A fantastic window!")
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.run_sync();
}
fn run_headless() {
MapBuilder::from_window("A fantastic window!")
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.run_sync();
}
fn main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
@ -18,7 +36,8 @@ fn main() {
enable_tracing();
MapBuilder::from_window("A fantastic window!")
.with_schedule_method(ScheduleMethod::Tokio(TokioScheduleMethod::new()))
.with_http_client(ReqwestHttpClient::new(None))
.with_schedule_method(TokioScheduleMethod::new())
.build()
.run_sync();
}

View File

@ -11,23 +11,15 @@ readme = "../README.md"
[features]
web-webgl = ["wgpu/webgl"]
# Enable tracing using tracy on desktop/mobile and the chrome profiler on web
enable-tracing = [ "tracing-subscriber", "tracing-tracy", "tracy-client", "tracing-wasm"]
default = []
enable-tracing = [ "tracing-subscriber", "tracing-tracy", "tracy-client"]
no-thread-safe-futures = []
[target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1"
web-sys = { version = "0.3", features = [
"Window",
"Worker", "WorkerGlobalScope", "DedicatedWorkerGlobalScope", "MessageEvent",
"Request", "RequestInit", "RequestMode", "Response", "Headers",
"ErrorEvent"
"Window"
] }
js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
console_log = { version = "0.2", features = ["color"] }
tracing-wasm = { version = "0.2", optional = true } # FIXME: Low quality dependency
instant = { version = "0.1", features = ["wasm-bindgen"] } # FIXME: Untrusted dependency
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android"))'.dependencies]
tokio = { version = "1.17", features = ["macros", "rt", "rt-multi-thread", "sync", "time"] }
@ -47,6 +39,9 @@ reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"
winit = { version = "0.26", default-features = false, features = ["x11", "wayland"] }
[dependencies]
async-trait = "0.1"
instant = { version = "0.1", features = ["wasm-bindgen"] } # FIXME: Untrusted dependency
winit = { version = "0.26", default-features = false }
raw-window-handle = "0.4"

View File

@ -2,6 +2,7 @@
use std::time::Duration;
use crate::coords::Zoom;
use cgmath::Vector2;
use winit::event::{DeviceEvent, KeyboardInput, TouchPhase, WindowEvent};
@ -11,7 +12,8 @@ use crate::input::query_handler::QueryHandler;
use crate::input::shift_handler::ShiftHandler;
use crate::input::tilt_handler::TiltHandler;
use crate::input::zoom_handler::ZoomHandler;
use crate::map_state::MapState;
use crate::map_state::{MapState, ViewState};
use crate::render::camera::Camera;
use crate::MapWindow;
mod pan_handler;
@ -125,12 +127,12 @@ impl InputController {
}
pub trait UpdateState {
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, dt: Duration);
fn update_state(&mut self, state: &mut ViewState, dt: Duration);
}
impl UpdateState for InputController {
#[tracing::instrument(skip_all)]
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, dt: Duration) {
fn update_state(&mut self, state: &mut ViewState, dt: Duration) {
self.pan_handler.update_state(state, dt);
self.pinch_handler.update_state(state, dt);
self.zoom_handler.update_state(state, dt);

View File

@ -1,6 +1,6 @@
use super::UpdateState;
use crate::map_state::MapState;
use crate::map_state::{MapState, ViewState};
use crate::render::camera::Camera;
use crate::MapWindow;
@ -17,7 +17,7 @@ pub struct PanHandler {
}
impl UpdateState for PanHandler {
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, _dt: Duration) {
fn update_state(&mut self, state: &mut ViewState, _dt: Duration) {
if !self.is_panning {
return;
}
@ -26,8 +26,7 @@ impl UpdateState for PanHandler {
if let (Some(window_position), Some(start_window_position)) =
(self.window_position, self.start_window_position)
{
let perspective = state.perspective();
let view_proj = reference_camera.calc_view_proj(perspective);
let view_proj = state.view_projection();
let inverted_view_proj = view_proj.invert();
let delta = if let (Some(start), Some(current)) = (
@ -42,17 +41,17 @@ impl UpdateState for PanHandler {
};
if self.start_camera_position.is_none() {
self.start_camera_position = Some(state.camera().position.to_vec());
self.start_camera_position = Some(state.camera.position.to_vec());
}
if let Some(start_camera_position) = self.start_camera_position {
state.camera_mut().position = Point3::from_vec(
state.camera.position = Point3::from_vec(
start_camera_position + Vector3::new(delta.x, delta.y, 0.0),
);
}
}
} else {
self.reference_camera = Some(state.camera().clone());
self.reference_camera = Some(state.camera.clone());
}
}
}

View File

@ -1,12 +1,13 @@
use super::UpdateState;
use crate::map_state::ViewState;
use crate::{MapState, MapWindow};
use std::time::Duration;
pub struct PinchHandler {}
impl UpdateState for PinchHandler {
fn update_state<W: MapWindow>(&mut self, _state: &mut MapState<W>, _dt: Duration) {
fn update_state(&mut self, _state: &mut ViewState, _dt: Duration) {
// TODO
}
}

View File

@ -1,7 +1,7 @@
use cgmath::Vector2;
use crate::input::UpdateState;
use crate::map_state::MapState;
use crate::map_state::{MapState, ViewState};
use crate::MapWindow;
use std::time::Duration;
use winit::event::{ElementState, MouseButton};
@ -57,18 +57,17 @@ impl QueryHandler {
}
impl UpdateState for QueryHandler {
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, _dt: Duration) {
fn update_state(&mut self, state: &mut ViewState, _dt: Duration) {
if self.clicking {
if let Some(window_position) = self.window_position {
let perspective = &state.perspective();
let view_proj = state.camera().calc_view_proj(perspective);
let view_proj = state.view_projection();
let inverted_view_proj = view_proj.invert();
let _z = state.visible_level(); // FIXME: can be wrong, if tiles of different z are visible
let _zoom = state.zoom();
if let Some(_coordinates) = state
.camera()
.camera
.window_to_world_at_ground(&window_position, &inverted_view_proj)
{
/*state

View File

@ -1,5 +1,6 @@
use super::UpdateState;
use crate::map_state::ViewState;
use crate::{MapState, MapWindow};
use cgmath::{Vector3, Zero};
use std::time::Duration;
@ -12,11 +13,11 @@ pub struct ShiftHandler {
}
impl UpdateState for ShiftHandler {
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, dt: Duration) {
fn update_state(&mut self, state: &mut ViewState, dt: Duration) {
let dt = dt.as_secs_f64() * (1.0 / self.speed);
let delta = self.camera_translate * dt;
state.camera_mut().position += delta;
state.camera.position += delta;
self.camera_translate -= delta;
}
}

View File

@ -1,7 +1,9 @@
use super::UpdateState;
use crate::map_state::MapState;
use crate::map_state::{MapState, ViewState};
use crate::coords::Zoom;
use crate::render::camera::Camera;
use crate::MapWindow;
use cgmath::{Deg, Rad, Zero};
use std::time::Duration;
@ -14,11 +16,11 @@ pub struct TiltHandler {
}
impl UpdateState for TiltHandler {
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, dt: Duration) {
fn update_state(&mut self, state: &mut ViewState, dt: Duration) {
let dt = dt.as_secs_f64() * (1.0 / self.speed);
let delta = self.delta_pitch * dt;
state.camera_mut().pitch += Rad::from(delta);
state.camera.pitch += Rad::from(delta);
self.delta_pitch -= delta;
}
}

View File

@ -1,8 +1,9 @@
use super::UpdateState;
use crate::coords::Zoom;
use crate::map_state::MapState;
use crate::map_state::{MapState, ViewState};
use crate::render::camera::Camera;
use crate::MapWindow;
use cgmath::{Vector2, Vector3};
use std::time::Duration;
@ -14,7 +15,7 @@ pub struct ZoomHandler {
}
impl UpdateState for ZoomHandler {
fn update_state<W: MapWindow>(&mut self, state: &mut MapState<W>, _dt: Duration) {
fn update_state(&mut self, state: &mut ViewState, dt: Duration) {
if let Some(zoom_delta) = self.zoom_delta {
if let Some(window_position) = self.window_position {
let current_zoom = state.zoom();
@ -23,13 +24,12 @@ impl UpdateState for ZoomHandler {
state.update_zoom(next_zoom);
self.zoom_delta = None;
let perspective = &state.perspective();
let view_proj = state.camera().calc_view_proj(perspective);
let view_proj = state.view_projection();
let inverted_view_proj = view_proj.invert();
let camera = state.camera_mut();
if let Some(cursor_position) =
camera.window_to_world_at_ground(&window_position, &inverted_view_proj)
if let Some(cursor_position) = state
.camera
.window_to_world_at_ground(&window_position, &inverted_view_proj)
{
let scale = current_zoom.scale_delta(&next_zoom);
@ -39,7 +39,7 @@ impl UpdateState for ZoomHandler {
cursor_position.z,
) - cursor_position;
camera.position += delta;
state.camera.position += delta;
}
}
}

View File

@ -4,74 +4,42 @@ use crate::error::Error;
use crate::io::shared_thread_state::SharedThreadState;
pub struct Scheduler {
schedule_method: ScheduleMethod,
pub struct Scheduler<SM>
where
SM: ScheduleMethod,
{
schedule_method: SM,
}
impl Scheduler {
pub fn new(schedule_method: ScheduleMethod) -> Self {
impl<SM> Scheduler<SM>
where
SM: ScheduleMethod,
{
pub fn new(schedule_method: SM) -> Self {
Self { schedule_method }
}
pub fn schedule_method(&self) -> &ScheduleMethod {
pub fn schedule_method(&self) -> &SM {
&self.schedule_method
}
}
pub enum ScheduleMethod {
#[cfg(not(target_arch = "wasm32"))]
Tokio(crate::platform::schedule_method::TokioScheduleMethod),
#[cfg(target_arch = "wasm32")]
WebWorkerPool(crate::platform::schedule_method::WebWorkerPoolScheduleMethod),
}
impl Default for ScheduleMethod {
fn default() -> Self {
#[cfg(not(target_arch = "wasm32"))]
{
ScheduleMethod::Tokio(crate::platform::schedule_method::TokioScheduleMethod::new())
}
#[cfg(target_arch = "wasm32")]
{
panic!("No default ScheduleMethod on web")
}
}
}
impl ScheduleMethod {
#[cfg(target_arch = "wasm32")]
pub fn schedule<T>(
pub trait ScheduleMethod: 'static {
#[cfg(not(feature = "no-thread-safe-futures"))]
fn schedule<T>(
&self,
shared_thread_state: SharedThreadState,
future_factory: impl (FnOnce(SharedThreadState) -> T) + Send + 'static,
) -> Result<(), Error>
where
T: Future<Output = ()> + 'static,
{
match self {
ScheduleMethod::WebWorkerPool(method) => {
Ok(method.schedule(shared_thread_state, future_factory))
}
_ => Err(Error::Schedule),
}
}
T: Future<Output = ()> + Send + 'static;
#[cfg(not(target_arch = "wasm32"))]
pub fn schedule<T>(
#[cfg(feature = "no-thread-safe-futures")]
fn schedule<T>(
&self,
shared_thread_state: SharedThreadState,
future_factory: impl (FnOnce(SharedThreadState) -> T) + Send + 'static,
) -> Result<(), Error>
where
T: Future + Send + 'static,
T::Output: Send + 'static,
{
match self {
ScheduleMethod::Tokio(method) => {
method.schedule(shared_thread_state, future_factory);
Ok(())
}
_ => Err(Error::Schedule),
}
}
T: Future<Output = ()> + 'static;
}

View File

@ -1,22 +1,45 @@
use crate::coords::WorldTileCoords;
use crate::error::Error;
use crate::style::source::TileAddressingScheme;
use async_trait::async_trait;
pub struct HttpSourceClient {
#[cfg(not(target_arch = "wasm32"))]
inner_client: crate::platform::http_client::ReqwestHttpClient,
#[cfg(target_arch = "wasm32")]
inner_client: crate::platform::http_client::WHATWGFetchHttpClient,
pub type HTTPClientFactory<HC> = dyn Fn() -> HC;
// On the web platform futures are not thread-safe (i.e. not Send). This means we need to tell
// async_trait that these bounds should not be placed on the async trait:
// https://github.com/dtolnay/async-trait/blob/b70720c4c1cc0d810b7446efda44f81310ee7bf2/README.md#non-threadsafe-futures
//
// Users of this library can decide whether futures from the HTTPClient are thread-safe or not via
// the future "no-thread-safe-futures". Tokio futures are thread-safe.
#[cfg_attr(feature = "no-thread-safe-futures", async_trait(?Send))]
#[cfg_attr(not(feature = "no-thread-safe-futures"), async_trait)]
pub trait HTTPClient: Clone + Sync + Send + 'static {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error>;
}
pub enum SourceClient {
Http(HttpSourceClient),
#[derive(Clone)]
pub struct HttpSourceClient<HC>
where
HC: HTTPClient,
{
inner_client: HC,
}
#[derive(Clone)]
pub enum SourceClient<HC>
where
HC: HTTPClient,
{
Http(HttpSourceClient<HC>),
Mbtiles {
// TODO
},
}
impl SourceClient {
impl<HC> SourceClient<HC>
where
HC: HTTPClient,
{
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, Error> {
match self {
SourceClient::Http(client) => client.fetch(coords).await,
@ -25,13 +48,13 @@ impl SourceClient {
}
}
impl HttpSourceClient {
pub fn new() -> Self {
impl<HC> HttpSourceClient<HC>
where
HC: HTTPClient,
{
pub fn new(http_client: HC) -> Self {
Self {
#[cfg(not(target_arch = "wasm32"))]
inner_client: crate::platform::http_client::ReqwestHttpClient::new(None),
#[cfg(target_arch = "wasm32")]
inner_client: crate::platform::http_client::WHATWGFetchHttpClient::new(),
inner_client: http_client,
}
}

View File

@ -1,42 +1,46 @@
use crate::io::scheduler::Scheduler;
use crate::io::scheduler::{ScheduleMethod, Scheduler};
use crate::io::source_client::HTTPClient;
use crate::map_state::{MapState, Runnable};
use crate::render::render_state::RenderState;
use crate::style::Style;
use crate::window::{MapWindow, WindowFactory, WindowSize};
use std::marker::PhantomData;
mod input;
pub mod coords;
pub mod error;
pub mod io;
pub mod platform;
pub mod style;
pub mod window;
pub(crate) mod coords;
pub(crate) mod error;
pub(crate) mod io;
// Used for benchmarking
pub mod benchmarking;
// Internal modules
pub(crate) mod input;
pub(crate) mod map_state;
pub(crate) mod platform;
pub(crate) mod render;
pub(crate) mod style;
pub(crate) mod tessellation;
pub(crate) mod tilejson;
pub(crate) mod util;
pub(crate) mod winit;
// Used for benchmarking
pub mod benchmarking;
pub mod window;
use crate::map_state::{MapState, Runnable};
use crate::render::render_state::RenderState;
use crate::style::Style;
use crate::window::{WindowFactory, WindowSize};
pub use io::scheduler::ScheduleMethod;
pub use platform::schedule_method::*;
pub trait MapWindow {
fn size(&self) -> Option<WindowSize>;
}
pub struct Map<W: MapWindow, E> {
map_state: MapState<W>,
pub struct Map<W, E, SM, HC>
where
W: MapWindow,
SM: ScheduleMethod,
HC: HTTPClient,
{
map_state: MapState<W, SM, HC>,
event_loop: E,
}
impl<W: MapWindow, E> Map<W, E>
impl<W, E, SM, HC> Map<W, E, SM, HC>
where
MapState<W>: Runnable<E>,
MapState<W, SM, HC>: Runnable<E>,
W: MapWindow,
SM: ScheduleMethod,
HC: HTTPClient,
{
pub fn run(self) {
self.run_with_optionally_max_frames(None);
@ -51,18 +55,25 @@ where
}
}
pub struct UninitializedMap<W, E> {
pub struct UninitializedMap<W, E, SM, HC>
where
SM: ScheduleMethod,
HC: HTTPClient,
{
window: W,
event_loop: E,
scheduler: Scheduler,
scheduler: Scheduler<SM>,
http_client: HC,
style: Style,
}
impl<W, E> UninitializedMap<W, E>
impl<W, E, SM, HC> UninitializedMap<W, E, SM, HC>
where
W: MapWindow + raw_window_handle::HasRawWindowHandle,
SM: ScheduleMethod,
HC: HTTPClient,
{
pub async fn initialize(self) -> Map<W, E> {
pub async fn initialize(self) -> Map<W, E, SM, HC> {
#[cfg(target_os = "android")]
// On android we can not get the dimensions of the window initially. Therefore, we use a
// fallback until the window is ready to deliver its correct bounds.
@ -81,6 +92,7 @@ where
window_size,
render_state,
self.scheduler,
self.http_client,
self.style,
),
event_loop: self.event_loop,
@ -89,10 +101,12 @@ where
}
#[cfg(not(target_arch = "wasm32"))]
impl<W, E> UninitializedMap<W, E>
impl<W, E, SM, HC> UninitializedMap<W, E, SM, HC>
where
W: MapWindow + raw_window_handle::HasRawWindowHandle,
MapState<W>: Runnable<E>,
MapState<W, SM, HC>: Runnable<E>,
SM: ScheduleMethod,
HC: HTTPClient,
{
pub fn run_sync(self) {
self.run_sync_with_optionally_max_frames(None);
@ -121,33 +135,45 @@ where
}
}
pub struct MapBuilder<W, E> {
pub struct MapBuilder<W, E, SM, HC>
where
SM: ScheduleMethod,
{
window_factory: Box<WindowFactory<W, E>>,
schedule_method: Option<ScheduleMethod>,
scheduler: Option<Scheduler>,
schedule_method: Option<SM>,
scheduler: Option<Scheduler<SM>>,
http_client: Option<HC>,
style: Option<Style>,
}
impl<W, E> MapBuilder<W, E>
impl<W, E, SM, HC> MapBuilder<W, E, SM, HC>
where
MapState<W>: Runnable<E>,
MapState<W, SM, HC>: Runnable<E>,
W: MapWindow + raw_window_handle::HasRawWindowHandle,
SM: ScheduleMethod,
HC: HTTPClient,
{
pub(crate) fn new(create_window: Box<WindowFactory<W, E>>) -> Self {
pub fn new(create_window: Box<WindowFactory<W, E>>) -> Self {
Self {
window_factory: create_window,
schedule_method: None,
scheduler: None,
http_client: None,
style: None,
}
}
pub fn with_schedule_method(mut self, schedule_method: ScheduleMethod) -> Self {
pub fn with_schedule_method(mut self, schedule_method: SM) -> Self {
self.schedule_method = Some(schedule_method);
self
}
pub fn with_existing_scheduler(mut self, scheduler: Scheduler) -> Self {
pub fn with_http_client(mut self, http_client: HC) -> Self {
self.http_client = Some(http_client);
self
}
pub fn with_existing_scheduler(mut self, scheduler: Scheduler<SM>) -> Self {
self.scheduler = Some(scheduler);
self
}
@ -157,18 +183,19 @@ where
self
}
pub fn build(self) -> UninitializedMap<W, E> {
pub fn build(self) -> UninitializedMap<W, E, SM, HC> {
let (window, event_loop) = (self.window_factory)();
let scheduler = self
.scheduler
.unwrap_or_else(|| Scheduler::new(self.schedule_method.unwrap_or_default()));
.unwrap_or_else(|| Scheduler::new(self.schedule_method.unwrap()));
let style = self.style.unwrap_or_default();
UninitializedMap {
window,
event_loop,
scheduler,
http_client: self.http_client.unwrap(),
style,
}
}

View File

@ -1,53 +1,87 @@
use crate::coords::{ViewRegion, WorldTileCoords, Zoom, TILE_SIZE};
use crate::io::scheduler::Scheduler;
use crate::render::camera;
use crate::render::camera::{Camera, Perspective};
use crate::render::render_state::RenderState;
use crate::util::ChangeObserver;
use crate::{MapWindow, WindowSize};
use std::collections::HashSet;
use std::sync::{mpsc, Arc, Mutex};
use crate::error::Error;
use crate::io::geometry_index::GeometryIndex;
use crate::io::scheduler::Scheduler;
use crate::io::shared_thread_state::SharedThreadState;
use crate::io::source_client::{HttpSourceClient, SourceClient};
use crate::io::source_client::{HTTPClient, HttpSourceClient, SourceClient};
use crate::io::tile_cache::TileCache;
use crate::io::tile_request_state::TileRequestState;
use crate::io::{TessellateMessage, TileRequest, TileTessellateMessage};
use crate::render::camera;
use crate::render::camera::{Camera, Perspective, ViewProjection};
use crate::render::render_state::RenderState;
use crate::style::Style;
use crate::util::ChangeObserver;
use crate::{MapWindow, ScheduleMethod, WindowSize};
use std::collections::HashSet;
use std::sync;
use std::sync::{mpsc, Arc, Mutex};
use wgpu::SurfaceError;
pub trait Runnable<E> {
fn run(self, event_loop: E, max_frames: Option<u64>);
}
pub struct MapState<W: MapWindow> {
pub struct ViewState {
zoom: ChangeObserver<Zoom>,
pub camera: ChangeObserver<Camera>,
pub perspective: Perspective,
}
impl ViewState {
pub fn view_projection(&self) -> ViewProjection {
self.camera.calc_view_proj(&self.perspective)
}
pub fn visible_level(&self) -> u8 {
self.zoom.level()
}
pub fn zoom(&self) -> Zoom {
*self.zoom
}
pub fn update_zoom(&mut self, new_zoom: Zoom) {
*self.zoom = new_zoom;
log::info!("zoom: {}", new_zoom);
}
}
pub struct MapState<W, SM, HC>
where
W: MapWindow,
SM: ScheduleMethod,
HC: HTTPClient,
{
window: W,
zoom: ChangeObserver<Zoom>,
camera: ChangeObserver<Camera>,
perspective: Perspective,
view_state: ViewState,
render_state: Option<RenderState>,
scheduler: Scheduler,
scheduler: Scheduler<SM>,
message_receiver: mpsc::Receiver<TessellateMessage>,
shared_thread_state: SharedThreadState,
tile_cache: TileCache,
source_client: SourceClient<HC>,
style: Style,
try_failed: bool,
}
impl<W: MapWindow> MapState<W> {
impl<W, SM, HC> MapState<W, SM, HC>
where
W: MapWindow,
SM: ScheduleMethod,
HC: HTTPClient,
{
pub fn new(
window: W,
window_size: WindowSize,
render_state: Option<RenderState>,
scheduler: Scheduler,
scheduler: Scheduler<SM>,
http_client: HC,
style: Style,
) -> Self {
let camera = camera::Camera::new(
@ -71,9 +105,11 @@ impl<W: MapWindow> MapState<W> {
Self {
window,
zoom: ChangeObserver::default(),
camera: ChangeObserver::new(camera),
perspective,
view_state: ViewState {
zoom: ChangeObserver::default(),
camera: ChangeObserver::new(camera),
perspective,
},
render_state,
scheduler,
@ -89,6 +125,7 @@ impl<W: MapWindow> MapState<W> {
style,
try_failed: false,
source_client: SourceClient::Http(HttpSourceClient::new(http_client)),
}
}
@ -158,14 +195,17 @@ impl<W: MapWindow> MapState<W> {
let render_setup_span = tracing::span!(tracing::Level::TRACE, "setup view region");
let _guard = render_setup_span.enter();
let visible_level = self.visible_level();
let visible_level = self.view_state.visible_level();
let view_proj = self.camera.calc_view_proj(&self.perspective);
let view_proj = self.view_state.view_projection();
let view_region = self
.view_state
.camera
.view_region_bounding_box(&view_proj.invert())
.map(|bounding_box| ViewRegion::new(bounding_box, 0, *self.zoom, visible_level));
.map(|bounding_box| {
ViewRegion::new(bounding_box, 0, *self.view_state.zoom, visible_level)
});
drop(_guard);
@ -175,7 +215,7 @@ impl<W: MapWindow> MapState<W> {
.expect("render state not yet initialized. Call reinitialize().")
.upload_tile_geometry(view_region, &self.style, &self.tile_cache);
let zoom = self.zoom();
let zoom = self.view_state.zoom();
self.render_state_mut()
.update_tile_view_pattern(view_region, &view_proj, zoom);
@ -185,17 +225,21 @@ impl<W: MapWindow> MapState<W> {
// TODO: Could we draw inspiration from StagingBelt (https://docs.rs/wgpu/latest/wgpu/util/struct.StagingBelt.html)?
// TODO: What is StagingBelt for?
if self.camera.did_change(0.05) || self.zoom.did_change(0.05) || self.try_failed {
if self.view_state.camera.did_change(0.05)
|| self.view_state.zoom.did_change(0.05)
|| self.try_failed
{
if let Some(view_region) = &view_region {
// FIXME: We also need to request tiles from layers above if we are over the maximum zoom level
self.try_failed = self.request_tiles_in_view(view_region);
}
self.render_state().update_globals(&view_proj, &self.camera);
self.render_state()
.update_globals(&view_proj, &self.view_state.camera);
}
self.camera.update_reference();
self.zoom.update_reference();
self.view_state.camera.update_reference();
self.view_state.zoom.update_reference();
}
fn try_request_tile(
@ -224,7 +268,7 @@ impl<W: MapWindow> MapState<W> {
);
}*/
let client = SourceClient::Http(HttpSourceClient::new());
let client = self.source_client.clone();
let coords = *coords;
self.scheduler
@ -253,13 +297,13 @@ impl<W: MapWindow> MapState<W> {
}
pub fn resize(&mut self, width: u32, height: u32) {
self.perspective.resize(width, height);
self.camera.resize(width, height);
self.view_state.perspective.resize(width, height);
self.view_state.camera.resize(width, height);
self.render_state_mut().resize(width, height)
}
pub fn scheduler(&self) -> &Scheduler {
pub fn scheduler(&self) -> &Scheduler<SM> {
&self.scheduler
}
@ -267,30 +311,6 @@ impl<W: MapWindow> MapState<W> {
&self.window
}
pub fn camera(&self) -> &Camera {
&self.camera
}
pub fn camera_mut(&mut self) -> &mut Camera {
&mut self.camera
}
pub fn perspective(&self) -> &Perspective {
&self.perspective
}
pub fn zoom(&self) -> Zoom {
*self.zoom
}
pub fn visible_level(&self) -> u8 {
self.zoom.level()
}
pub fn update_zoom(&mut self, new_zoom: Zoom) {
*self.zoom = new_zoom;
log::info!("zoom: {}", new_zoom);
}
pub fn suspend(&mut self) {
self.render_state_mut().suspend();
}
@ -308,11 +328,21 @@ impl<W: MapWindow> MapState<W> {
pub fn render_state_mut(&mut self) -> &'_ mut RenderState {
self.render_state.as_mut().unwrap()
}
pub fn view_state(&self) -> &ViewState {
&self.view_state
}
pub fn view_state_mut(&mut self) -> &mut ViewState {
&mut self.view_state
}
}
impl<W: MapWindow> MapState<W>
impl<W, SM, HC> MapState<W, SM, HC>
where
W: raw_window_handle::HasRawWindowHandle,
W: MapWindow + raw_window_handle::HasRawWindowHandle,
SM: ScheduleMethod,
HC: HTTPClient,
{
pub fn recreate_surface(&mut self) {
self.render_state

View File

@ -26,18 +26,19 @@ pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8
)))]
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
#[cfg(target_arch = "wasm32")]
pub use web::*;
#[cfg(not(target_arch = "wasm32"))]
pub use noweb::*;
mod noweb;
pub mod http_client {
#[cfg(not(target_arch = "wasm32"))]
pub use super::noweb::http_client::*;
}
pub mod schedule_method {
#[cfg(not(target_arch = "wasm32"))]
pub use super::noweb::schedule_method::*;
}
// FIXME: This limit is enforced by WebGL. Actually this makes sense!
// FIXME: This can also be achieved by _pad attributes in shader_ffi.rs
pub const MIN_BUFFER_SIZE: u64 = 32;
#[cfg(not(target_arch = "wasm32"))]
mod noweb;
#[cfg(target_arch = "wasm32")]
mod web;

View File

@ -1,9 +1,12 @@
use crate::error::Error;
use crate::HTTPClient;
use async_trait::async_trait;
use reqwest::{Client, StatusCode};
use reqwest_middleware::ClientWithMiddleware;
use reqwest_middleware_cache::managers::CACacheManager;
use reqwest_middleware_cache::{Cache, CacheMode};
#[derive(Clone)]
pub struct ReqwestHttpClient {
client: ClientWithMiddleware,
}
@ -35,8 +38,11 @@ impl ReqwestHttpClient {
client: builder.build(),
}
}
}
pub async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
#[async_trait]
impl HTTPClient for ReqwestHttpClient {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
let response = self.client.get(url).send().await?;
match response.error_for_status() {
Ok(response) => {

View File

@ -2,5 +2,3 @@
pub mod http_client;
pub mod schedule_method;
pub use std::time::Instant;

View File

@ -1,4 +1,7 @@
use crate::error::Error;
use crate::io::shared_thread_state::SharedThreadState;
use crate::ScheduleMethod;
use std::future::Future;
pub struct TokioScheduleMethod;
@ -6,15 +9,18 @@ impl TokioScheduleMethod {
pub fn new() -> Self {
Self {}
}
}
pub fn schedule<T>(
impl ScheduleMethod for TokioScheduleMethod {
fn schedule<T>(
&self,
shared_thread_state: SharedThreadState,
future_factory: impl (FnOnce(SharedThreadState) -> T) + Send + 'static,
) where
T: std::future::Future + Send + 'static,
T::Output: Send + 'static,
future_factory: impl FnOnce(SharedThreadState) -> T + Send + 'static,
) -> Result<(), Error>
where
T: Future<Output = ()> + Send + 'static,
{
tokio::task::spawn(future_factory(shared_thread_state));
Ok(())
}
}

View File

@ -1,70 +0,0 @@
use std::panic;
use crate::style::source::TileAddressingScheme;
use console_error_panic_hook;
pub use instant::Instant;
use schedule_method::WebWorkerPoolScheduleMethod;
use wasm_bindgen::prelude::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::Window as WebSysWindow;
use web_sys::Worker;
use crate::io::scheduler::ScheduleMethod;
use crate::io::scheduler::Scheduler;
use crate::window::FromCanvas;
use crate::MapBuilder;
pub mod http_client;
pub mod legacy_webworker_fetcher;
mod pool;
pub mod schedule_method;
#[cfg(feature = "enable-tracing")]
fn enable_tracing() {
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;
let mut builder = tracing_wasm::WASMLayerConfigBuilder::new();
builder.set_report_logs_in_timings(true);
builder.set_console_config(tracing_wasm::ConsoleConfig::NoReporting);
tracing::subscriber::set_global_default(
Registry::default().with(tracing_wasm::WASMLayer::new(builder.build())),
);
}
#[wasm_bindgen(start)]
pub fn wasm_bindgen_start() {
if let Err(_) = console_log::init_with_level(log::Level::Info) {
// Failed to initialize logging. No need to log a message.
}
panic::set_hook(Box::new(console_error_panic_hook::hook));
#[cfg(any(feature = "enable-tracing"))]
enable_tracing();
}
#[wasm_bindgen]
pub fn create_pool_scheduler(new_worker: js_sys::Function) -> *mut Scheduler {
let scheduler = Box::new(Scheduler::new(ScheduleMethod::WebWorkerPool(
WebWorkerPoolScheduleMethod::new(new_worker),
)));
let scheduler_ptr = Box::into_raw(scheduler);
return scheduler_ptr;
}
#[wasm_bindgen]
pub async fn run(scheduler_ptr: *mut Scheduler) {
let scheduler: Box<Scheduler> = unsafe { Box::from_raw(scheduler_ptr) };
// Either call forget or the main loop to keep worker loop alive
MapBuilder::from_canvas("maplibre")
.with_existing_scheduler(*scheduler)
.build()
.initialize()
.await
.run();
// std::mem::forget(scheduler);
}

View File

@ -1,7 +1,6 @@
use instant::Instant;
use std::time::Duration;
use crate::platform::Instant;
/// Measures the frames per second.
///
/// # Example

View File

@ -1,3 +1,7 @@
pub trait MapWindow {
fn size(&self) -> Option<WindowSize>;
}
#[derive(Clone, Copy)]
pub struct WindowSize {
width: u32,

View File

@ -3,6 +3,7 @@
//! * Platform Events like suspend/resume
//! * Render a new frame
use instant::Instant;
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
@ -10,10 +11,9 @@ use winit::window::WindowBuilder;
use crate::input::{InputController, UpdateState};
use crate::map_state::{MapState, Runnable};
use crate::platform::Instant;
use crate::window::FromWindow;
use crate::{MapBuilder, MapWindow, WindowSize};
use crate::{HTTPClient, MapBuilder, MapWindow, ScheduleMethod, WindowSize};
impl MapWindow for winit::window::Window {
fn size(&self) -> Option<WindowSize> {
@ -22,7 +22,11 @@ impl MapWindow for winit::window::Window {
}
}
impl Runnable<winit::event_loop::EventLoop<()>> for MapState<winit::window::Window> {
impl<SM, HC> Runnable<winit::event_loop::EventLoop<()>> for MapState<winit::window::Window, SM, HC>
where
SM: ScheduleMethod,
HC: HTTPClient,
{
fn run(mut self, event_loop: winit::event_loop::EventLoop<()>, max_frames: Option<u64>) {
let mut last_render_time = Instant::now();
let mut current_frame: u64 = 0;
@ -85,7 +89,7 @@ impl Runnable<winit::event_loop::EventLoop<()>> for MapState<winit::window::Wind
let dt = now - last_render_time;
last_render_time = now;
input_controller.update_state(&mut self, dt);
input_controller.update_state(self.view_state_mut(), dt);
match self.update_and_redraw() {
Ok(_) => {}
@ -131,7 +135,12 @@ impl Runnable<winit::event_loop::EventLoop<()>> for MapState<winit::window::Wind
}
#[cfg(not(target_arch = "wasm32"))]
impl FromWindow for MapBuilder<winit::window::Window, winit::event_loop::EventLoop<()>> {
impl<SM, HC> FromWindow
for MapBuilder<winit::window::Window, winit::event_loop::EventLoop<()>, SM, HC>
where
SM: ScheduleMethod,
HC: HTTPClient,
{
fn from_window(title: &'static str) -> Self {
let event_loop = EventLoop::new();
Self::new(Box::new(move || {
@ -169,8 +178,11 @@ pub fn get_canvas(element_id: &'static str) -> web_sys::HtmlCanvasElement {
}
#[cfg(target_arch = "wasm32")]
impl crate::window::FromCanvas
for MapBuilder<winit::window::Window, winit::event_loop::EventLoop<()>>
impl<SM, HC> crate::window::FromCanvas
for MapBuilder<winit::window::Window, winit::event_loop::EventLoop<()>, SM, HC>
where
SM: ScheduleMethod,
HC: HTTPClient,
{
fn from_canvas(dom_id: &'static str) -> Self {
let event_loop = EventLoop::new();

View File

@ -8,7 +8,8 @@ publish = false
[features]
web-webgl = ["maplibre/web-webgl"]
enable-tracing = ["maplibre/enable-tracing"]
enable-tracing = ["maplibre/enable-tracing", "tracing-wasm"]
default = []
[package.metadata.wasm-pack.profile.release]
wasm-opt = true
@ -17,5 +18,20 @@ wasm-opt = true
crate-type = ["cdylib", "rlib"]
[dependencies]
async-trait = "0.1"
maplibre = { path = "../maplibre", features = ["no-thread-safe-futures"] }
winit = "*"
log = "*"
maplibre = { path = "../maplibre" }
console_error_panic_hook = "0.1"
web-sys = { version = "0.3", features = [
"Window",
"Worker", "WorkerGlobalScope", "DedicatedWorkerGlobalScope", "MessageEvent",
"Request", "RequestInit", "RequestMode", "Response", "Headers",
"ErrorEvent"
] }
js-sys = "0.3"
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
console_log = { version = "0.2", features = ["color"] }
tracing-wasm = { version = "0.2", optional = true } # FIXME: Low quality dependency

16
web/src/error.rs Normal file
View File

@ -0,0 +1,16 @@
//! Errors which can happen in various parts of the library.
use js_sys::Error as JSError;
use wasm_bindgen::prelude::*;
use wasm_bindgen::{JsCast, JsValue};
#[derive(Debug)]
pub struct WebError(pub String);
impl From<JsValue> for WebError {
fn from(maybe_error: JsValue) -> Self {
assert!(maybe_error.is_instance_of::<JSError>());
let error: JSError = maybe_error.dyn_into().unwrap();
WebError(error.message().as_string().unwrap())
}
}

View File

@ -1,4 +1,66 @@
pub use maplibre::*;
use crate::platform::http_client::WHATWGFetchHttpClient;
use crate::platform::schedule_method::WebWorkerPoolScheduleMethod;
use maplibre::io::scheduler::Scheduler;
use maplibre::window::FromCanvas;
use maplibre::MapBuilder;
use std::panic;
use wasm_bindgen::prelude::*;
mod error;
mod platform;
#[cfg(not(target_arch = "wasm32"))]
compile_error!("web works only on wasm32.");
#[cfg(feature = "enable-tracing")]
fn enable_tracing() {
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::Registry;
let mut builder = tracing_wasm::WASMLayerConfigBuilder::new();
builder.set_report_logs_in_timings(true);
builder.set_console_config(tracing_wasm::ConsoleConfig::NoReporting);
tracing::subscriber::set_global_default(
Registry::default().with(tracing_wasm::WASMLayer::new(builder.build())),
);
}
#[wasm_bindgen(start)]
pub fn wasm_bindgen_start() {
if console_log::init_with_level(log::Level::Info).is_err() {
// Failed to initialize logging. No need to log a message.
}
panic::set_hook(Box::new(console_error_panic_hook::hook));
#[cfg(any(feature = "enable-tracing"))]
enable_tracing();
}
#[wasm_bindgen]
pub fn create_pool_scheduler(
new_worker: js_sys::Function,
) -> *mut Scheduler<WebWorkerPoolScheduleMethod> {
let scheduler = Box::new(Scheduler::new(WebWorkerPoolScheduleMethod::new(new_worker)));
Box::into_raw(scheduler)
}
#[wasm_bindgen]
pub async fn run(scheduler_ptr: *mut Scheduler<WebWorkerPoolScheduleMethod>) {
let scheduler: Box<Scheduler<WebWorkerPoolScheduleMethod>> =
unsafe { Box::from_raw(scheduler_ptr) };
// Either call forget or the main loop to keep worker loop alive
MapBuilder::from_canvas("maplibre")
.with_http_client(WHATWGFetchHttpClient::new())
.with_existing_scheduler(*scheduler)
.build()
.initialize()
.await
.run();
// std::mem::forget(scheduler);
}

View File

@ -1,54 +1,28 @@
use std::thread::Thread;
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
use js_sys::{ArrayBuffer, Uint8Array};
use maplibre::io::source_client::HTTPClient;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::Worker;
use web_sys::{Request, RequestInit, RequestMode, Response, WorkerGlobalScope};
use crate::coords::{TileCoords, WorldTileCoords};
use crate::error::Error;
use crate::io::scheduler::{ScheduleMethod, Scheduler};
use crate::io::tile_cache::TileCache;
use crate::io::TileRequestID;
use web_sys::{Request, RequestInit, Response, WorkerGlobalScope};
use super::pool::WorkerPool;
use crate::error::WebError;
use async_trait::async_trait;
use maplibre::error::Error;
pub struct WHATWGFetchHttpClient {}
impl From<JsValue> for Error {
fn from(maybe_error: JsValue) -> Self {
assert!(maybe_error.is_instance_of::<JSError>());
let error: JSError = maybe_error.dyn_into().unwrap();
Error::Network(error.message().as_string().unwrap())
}
}
impl WHATWGFetchHttpClient {
pub fn new() -> Self {
Self {}
}
pub async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
let maybe_array_buffer = Self::whatwg_fetch(url).await?;
assert!(maybe_array_buffer.is_instance_of::<ArrayBuffer>());
let array_buffer: ArrayBuffer = maybe_array_buffer.dyn_into().unwrap();
// Copy data to Vec<u8>
let buffer: Uint8Array = Uint8Array::new(&array_buffer);
let mut output: Vec<u8> = vec![0; array_buffer.byte_length() as usize];
buffer.copy_to(output.as_mut_slice());
Ok(output)
}
async fn whatwg_fetch(url: &str) -> Result<JsValue, JsValue> {
async fn fetch_array_buffer(url: &str) -> Result<JsValue, JsValue> {
let mut opts = RequestInit::new();
opts.method("GET");
let request = Request::new_with_str_and_init(&url, &opts)?;
let request = Request::new_with_str_and_init(url, &opts)?;
// Get the global scope
let global = js_sys::global();
@ -64,4 +38,33 @@ impl WHATWGFetchHttpClient {
let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?;
Ok(maybe_array_buffer)
}
async fn fetch_bytes(&self, url: &str) -> Result<Vec<u8>, WebError> {
let maybe_array_buffer = Self::fetch_array_buffer(url).await?;
assert!(maybe_array_buffer.is_instance_of::<ArrayBuffer>());
let array_buffer: ArrayBuffer = maybe_array_buffer.dyn_into().unwrap();
// Copy data to Vec<u8>
let buffer: Uint8Array = Uint8Array::new(&array_buffer);
let mut output: Vec<u8> = vec![0; array_buffer.byte_length() as usize];
buffer.copy_to(output.as_mut_slice());
Ok(output)
}
}
impl Clone for WHATWGFetchHttpClient {
fn clone(&self) -> Self {
WHATWGFetchHttpClient {}
}
}
#[async_trait(?Send)]
impl HTTPClient for WHATWGFetchHttpClient {
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
self.fetch_bytes(url)
.await
.map_err(|WebError(msg)| Error::Network(msg))
}
}

View File

@ -1,28 +1,13 @@
use std::panic;
use std::thread::Thread;
use super::schedule_method::WebWorkerPoolScheduleMethod;
use crate::style::source::TileAddressingScheme;
use console_error_panic_hook;
pub use instant::Instant;
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::Window as WebSysWindow;
use web_sys::Worker;
use web_sys::{Request, RequestInit, RequestMode, Response, WorkerGlobalScope};
use crate::coords::{TileCoords, WorldTileCoords};
use crate::error::Error;
use crate::io::scheduler::ScheduleMethod;
use crate::io::scheduler::Scheduler;
use crate::io::shared_thread_state::SharedThreadState;
use crate::io::tile_cache::TileCache;
use crate::io::TileRequestID;
use crate::MapBuilder;
use maplibre::coords::TileCoords;
use super::pool::WorkerPool;
use maplibre::io::scheduler::Scheduler;
use maplibre::io::shared_thread_state::SharedThreadState;
use maplibre::io::TileRequestID;
#[wasm_bindgen]
extern "C" {
@ -41,8 +26,8 @@ pub fn new_thread_local_state(scheduler_ptr: *mut Scheduler) -> *mut SharedThrea
}*/
#[wasm_bindgen]
pub fn new_thread_local_state(scheduler_ptr: *mut Scheduler) -> u32 {
return 0;
pub fn new_thread_local_state(_scheduler_ptr: *mut Scheduler<WebWorkerPoolScheduleMethod>) -> u32 {
0
}
#[wasm_bindgen]

4
web/src/platform/mod.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod http_client;
pub mod legacy_webworker_fetcher;
pub mod pool;
pub mod schedule_method;

View File

@ -3,7 +3,7 @@
//! Adopted from [wasm-bindgen example](https://github.com/rustwasm/wasm-bindgen/blob/0eba2efe45801b71f8873bc368c58a8ed8e894ff/examples/raytrace-parallel/src/pool.rs)
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
use js_sys::Promise;

View File

@ -1,18 +1,13 @@
use std::thread::Thread;
use std::future::Future;
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::Worker;
use web_sys::{Request, RequestInit, RequestMode, Response, WorkerGlobalScope};
use crate::coords::{TileCoords, WorldTileCoords};
use crate::error::Error;
use crate::io::scheduler::{ScheduleMethod, Scheduler};
use crate::io::shared_thread_state::SharedThreadState;
use crate::io::tile_cache::TileCache;
use crate::io::TileRequestID;
use web_sys::Worker;
use maplibre::error::Error;
use maplibre::io::scheduler::ScheduleMethod;
use maplibre::io::shared_thread_state::SharedThreadState;
use super::pool::WorkerPool;
@ -36,14 +31,16 @@ impl WebWorkerPoolScheduleMethod {
.unwrap(),
}
}
}
pub fn schedule<T>(
impl ScheduleMethod for WebWorkerPoolScheduleMethod {
fn schedule<T>(
&self,
shared_thread_state: SharedThreadState,
future_factory: impl (FnOnce(SharedThreadState) -> T) + Send + 'static,
) where
T: std::future::Future + 'static,
T::Output: Send + 'static,
) -> Result<(), Error>
where
T: Future<Output = ()> + 'static,
{
self.pool
.run(move || {
@ -53,5 +50,6 @@ impl WebWorkerPoolScheduleMethod {
})
})
.unwrap();
Ok(())
}
}