mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
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:
parent
7df6ca49d3
commit
bac7ffb8c1
2
.github/actions/docs/action.yml
vendored
2
.github/actions/docs/action.yml
vendored
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) => {
|
||||
|
||||
@ -2,5 +2,3 @@
|
||||
|
||||
pub mod http_client;
|
||||
pub mod schedule_method;
|
||||
|
||||
pub use std::time::Instant;
|
||||
|
||||
@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
use instant::Instant;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::platform::Instant;
|
||||
|
||||
/// Measures the frames per second.
|
||||
///
|
||||
/// # Example
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
pub trait MapWindow {
|
||||
fn size(&self) -> Option<WindowSize>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct WindowSize {
|
||||
width: u32,
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
16
web/src/error.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
}
|
||||
@ -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
4
web/src/platform/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod http_client;
|
||||
pub mod legacy_webworker_fetcher;
|
||||
pub mod pool;
|
||||
pub mod schedule_method;
|
||||
@ -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;
|
||||
@ -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(())
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user