diff --git a/.idea/maplibre-rs.iml b/.idea/maplibre-rs.iml
index b6fc0101..cea86611 100644
--- a/.idea/maplibre-rs.iml
+++ b/.idea/maplibre-rs.iml
@@ -15,6 +15,7 @@
+
diff --git a/Cargo.toml b/Cargo.toml
index cfa26158..de596bc9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ resolver = "2"
members = [
"maplibre",
+ "maplibre-winit",
"maplibre-build-tools",
"maplibre-demo",
diff --git a/android/Cargo.toml b/android/Cargo.toml
index 601fbc6f..348ab557 100644
--- a/android/Cargo.toml
+++ b/android/Cargo.toml
@@ -8,6 +8,7 @@ publish = false
[dependencies]
maplibre = { path = "../maplibre" }
+maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
env_logger = "0.9"
log = "0.4.16"
ndk-glue = "0.5.0" # version is required by winit
diff --git a/android/src/lib.rs b/android/src/lib.rs
index 01d3e760..a8e4237b 100644
--- a/android/src/lib.rs
+++ b/android/src/lib.rs
@@ -2,9 +2,10 @@ use jni::objects::JClass;
use jni::JNIEnv;
use log::Level;
use maplibre::platform::http_client::ReqwestHttpClient;
+use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
-use maplibre::window::FromWindow;
use maplibre::MapBuilder;
+use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
use std::ffi::CString;
#[cfg(not(target_os = "android"))]
@@ -14,11 +15,16 @@ compile_error!("android works only on android.");
pub fn android_main() {
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
- MapBuilder::from_window("A fantastic window!")
- .with_http_client(ReqwestHttpClient::new(None))
- .with_schedule_method(TokioScheduleMethod::new())
- .build()
- .run_sync();
+ run_multithreaded(async {
+ MapBuilder::new()
+ .with_map_window_config(WinitMapWindowConfig::new("maplibre android".to_string()))
+ .with_http_client(ReqwestHttpClient::new(None))
+ .with_schedule_method(TokioScheduleMethod::new())
+ .build()
+ .initialize()
+ .await
+ .run()
+ })
}
#[no_mangle]
diff --git a/apple/Cargo.toml b/apple/Cargo.toml
index be8b6c43..c8751616 100644
--- a/apple/Cargo.toml
+++ b/apple/Cargo.toml
@@ -8,6 +8,8 @@ publish = false
[dependencies]
maplibre = { path = "../maplibre" }
+maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
+
env_logger = "0.9"
[lib]
diff --git a/apple/src/lib.rs b/apple/src/lib.rs
index 5d4a3847..1e0720fc 100644
--- a/apple/src/lib.rs
+++ b/apple/src/lib.rs
@@ -1,7 +1,8 @@
use maplibre::platform::http_client::ReqwestHttpClient;
+use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
-use maplibre::window::FromWindow;
use maplibre::MapBuilder;
+use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
compile_error!("apple works only on macOS and iOS.");
@@ -10,9 +11,14 @@ compile_error!("apple works only on macOS and iOS.");
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_http_client(ReqwestHttpClient::new(None))
- .with_schedule_method(TokioScheduleMethod::new())
- .build()
- .run_sync();
+ run_multithreaded(async {
+ MapBuilder::new()
+ .with_map_window_config(WinitMapWindowConfig::new("maplibre apple".to_string()))
+ .with_http_client(ReqwestHttpClient::new(None))
+ .with_schedule_method(TokioScheduleMethod::new())
+ .build()
+ .initialize()
+ .await
+ .run()
+ })
}
diff --git a/maplibre-demo/Cargo.toml b/maplibre-demo/Cargo.toml
index 67fc0cdb..6e04dabe 100644
--- a/maplibre-demo/Cargo.toml
+++ b/maplibre-demo/Cargo.toml
@@ -15,6 +15,7 @@ enable-tracing = ["maplibre/enable-tracing", "tracing-subscriber", "tracing-trac
[dependencies]
env_logger = "0.9"
maplibre = { path = "../maplibre", version = "0.0.2" }
+maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
tracing = { version = "0.1" }
tracing-subscriber = { version = "0.3", optional = true }
diff --git a/maplibre-demo/src/main.rs b/maplibre-demo/src/main.rs
index 94ecb3e5..9884520f 100644
--- a/maplibre-demo/src/main.rs
+++ b/maplibre-demo/src/main.rs
@@ -1,7 +1,8 @@
use maplibre::platform::http_client::ReqwestHttpClient;
+use maplibre::platform::run_multithreaded;
use maplibre::platform::schedule_method::TokioScheduleMethod;
-use maplibre::window::FromWindow;
use maplibre::MapBuilder;
+use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
#[cfg(feature = "enable-tracing")]
fn enable_tracing() {
@@ -14,19 +15,16 @@ fn enable_tracing() {
}
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();
+ run_multithreaded(async {
+ MapBuilder::new()
+ .with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
+ .with_http_client(ReqwestHttpClient::new(None))
+ .with_schedule_method(TokioScheduleMethod::new())
+ .build()
+ .initialize()
+ .await
+ .run()
+ })
}
fn main() {
@@ -35,9 +33,5 @@ fn main() {
#[cfg(feature = "enable-tracing")]
enable_tracing();
- MapBuilder::from_window("A fantastic window!")
- .with_http_client(ReqwestHttpClient::new(None))
- .with_schedule_method(TokioScheduleMethod::new())
- .build()
- .run_sync();
+ run_in_window()
}
diff --git a/maplibre-winit/Cargo.toml b/maplibre-winit/Cargo.toml
new file mode 100644
index 00000000..de062b8b
--- /dev/null
+++ b/maplibre-winit/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "maplibre-winit"
+version = "0.0.1"
+edition = "2021"
+
+[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android"))'.dependencies]
+tokio = { version = "1.17", features = ["rt"] }
+
+[target.'cfg(target_os = "android")'.dependencies]
+winit = { version = "0.26", default-features = false }
+
+[target.'cfg(target_os = "linux")'.dependencies]
+winit = { version = "0.26", default-features = false, features = ["x11", "wayland"] }
+
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+web-sys = { version = "0.3", features = ["Window"] }
+wasm-bindgen = "0.2"
+wasm-bindgen-futures = "0.4"
+
+[dependencies]
+maplibre = { path = "../maplibre", version = "0.0.2" }
+winit = { version = "0.26", default-features = false }
+cgmath = "0.18.0"
+instant = { version = "0.1", features = ["wasm-bindgen"] } # FIXME: Untrusted dependency
+log = "0.4"
\ No newline at end of file
diff --git a/maplibre/src/input/mod.rs b/maplibre-winit/src/input/mod.rs
similarity index 96%
rename from maplibre/src/input/mod.rs
rename to maplibre-winit/src/input/mod.rs
index bcae2d86..9cf694e7 100644
--- a/maplibre/src/input/mod.rs
+++ b/maplibre-winit/src/input/mod.rs
@@ -2,8 +2,8 @@
use std::time::Duration;
-use crate::coords::Zoom;
use cgmath::Vector2;
+
use winit::event::{DeviceEvent, KeyboardInput, TouchPhase, WindowEvent};
use crate::input::pan_handler::PanHandler;
@@ -12,9 +12,7 @@ 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, ViewState};
-use crate::render::camera::Camera;
-use crate::MapWindow;
+use maplibre::map_state::ViewState;
mod pan_handler;
mod pinch_handler;
@@ -131,7 +129,6 @@ pub trait UpdateState {
}
impl UpdateState for InputController {
- #[tracing::instrument(skip_all)]
fn update_state(&mut self, state: &mut ViewState, dt: Duration) {
self.pan_handler.update_state(state, dt);
self.pinch_handler.update_state(state, dt);
diff --git a/maplibre/src/input/pan_handler.rs b/maplibre-winit/src/input/pan_handler.rs
similarity index 97%
rename from maplibre/src/input/pan_handler.rs
rename to maplibre-winit/src/input/pan_handler.rs
index 3fed29a0..d630c00b 100644
--- a/maplibre/src/input/pan_handler.rs
+++ b/maplibre-winit/src/input/pan_handler.rs
@@ -1,10 +1,10 @@
use super::UpdateState;
-use crate::map_state::{MapState, ViewState};
-use crate::render::camera::Camera;
+use maplibre::map_state::ViewState;
+use maplibre::render::camera::Camera;
-use crate::MapWindow;
use cgmath::{EuclideanSpace, Point3, Vector2, Vector3, Zero};
+
use std::time::Duration;
use winit::event::{ElementState, MouseButton};
diff --git a/maplibre/src/input/pinch_handler.rs b/maplibre-winit/src/input/pinch_handler.rs
similarity index 80%
rename from maplibre/src/input/pinch_handler.rs
rename to maplibre-winit/src/input/pinch_handler.rs
index 565e992c..0d68a24a 100644
--- a/maplibre/src/input/pinch_handler.rs
+++ b/maplibre-winit/src/input/pinch_handler.rs
@@ -1,7 +1,6 @@
use super::UpdateState;
-use crate::map_state::ViewState;
-use crate::{MapState, MapWindow};
+use maplibre::map_state::ViewState;
use std::time::Duration;
pub struct PinchHandler {}
diff --git a/maplibre/src/input/query_handler.rs b/maplibre-winit/src/input/query_handler.rs
similarity index 97%
rename from maplibre/src/input/query_handler.rs
rename to maplibre-winit/src/input/query_handler.rs
index 7c3d34b7..99774ff1 100644
--- a/maplibre/src/input/query_handler.rs
+++ b/maplibre-winit/src/input/query_handler.rs
@@ -1,8 +1,7 @@
use cgmath::Vector2;
use crate::input::UpdateState;
-use crate::map_state::{MapState, ViewState};
-use crate::MapWindow;
+use maplibre::map_state::ViewState;
use std::time::Duration;
use winit::event::{ElementState, MouseButton};
diff --git a/maplibre/src/input/shift_handler.rs b/maplibre-winit/src/input/shift_handler.rs
similarity index 97%
rename from maplibre/src/input/shift_handler.rs
rename to maplibre-winit/src/input/shift_handler.rs
index 660fa511..d3f8a6c8 100644
--- a/maplibre/src/input/shift_handler.rs
+++ b/maplibre-winit/src/input/shift_handler.rs
@@ -1,8 +1,7 @@
use super::UpdateState;
-use crate::map_state::ViewState;
-use crate::{MapState, MapWindow};
use cgmath::{Vector3, Zero};
+use maplibre::map_state::ViewState;
use std::time::Duration;
pub struct ShiftHandler {
diff --git a/maplibre/src/input/tilt_handler.rs b/maplibre-winit/src/input/tilt_handler.rs
similarity index 91%
rename from maplibre/src/input/tilt_handler.rs
rename to maplibre-winit/src/input/tilt_handler.rs
index 44eeb9d6..54b307c8 100644
--- a/maplibre/src/input/tilt_handler.rs
+++ b/maplibre-winit/src/input/tilt_handler.rs
@@ -1,11 +1,9 @@
use super::UpdateState;
-use crate::map_state::{MapState, ViewState};
+use maplibre::map_state::ViewState;
-use crate::coords::Zoom;
-use crate::render::camera::Camera;
-use crate::MapWindow;
use cgmath::{Deg, Rad, Zero};
+
use std::time::Duration;
pub struct TiltHandler {
diff --git a/maplibre/src/input/zoom_handler.rs b/maplibre-winit/src/input/zoom_handler.rs
similarity index 93%
rename from maplibre/src/input/zoom_handler.rs
rename to maplibre-winit/src/input/zoom_handler.rs
index 34e7d5d3..ec00b9ad 100644
--- a/maplibre/src/input/zoom_handler.rs
+++ b/maplibre-winit/src/input/zoom_handler.rs
@@ -1,11 +1,10 @@
use super::UpdateState;
-use crate::coords::Zoom;
-use crate::map_state::{MapState, ViewState};
+use maplibre::coords::Zoom;
+use maplibre::map_state::ViewState;
-use crate::render::camera::Camera;
-use crate::MapWindow;
use cgmath::{Vector2, Vector3};
+
use std::time::Duration;
pub struct ZoomHandler {
@@ -15,7 +14,7 @@ pub struct ZoomHandler {
}
impl UpdateState for ZoomHandler {
- fn update_state(&mut self, state: &mut ViewState, 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();
diff --git a/maplibre-winit/src/lib.rs b/maplibre-winit/src/lib.rs
new file mode 100644
index 00000000..0e7d9675
--- /dev/null
+++ b/maplibre-winit/src/lib.rs
@@ -0,0 +1,2 @@
+pub mod input;
+pub mod winit;
diff --git a/maplibre-winit/src/winit/mod.rs b/maplibre-winit/src/winit/mod.rs
new file mode 100644
index 00000000..c579bff4
--- /dev/null
+++ b/maplibre-winit/src/winit/mod.rs
@@ -0,0 +1,178 @@
+use instant::Instant;
+use maplibre::error::Error;
+use maplibre::io::scheduler::ScheduleMethod;
+use maplibre::io::source_client::HTTPClient;
+use winit::event::{ElementState, KeyboardInput, VirtualKeyCode, WindowEvent};
+use winit::event_loop::ControlFlow;
+
+use crate::input::{InputController, UpdateState};
+use maplibre::map_state::MapState;
+use maplibre::window::{MapWindow, MapWindowConfig, Runnable};
+use winit::event::Event;
+
+#[cfg(target_arch = "wasm32")]
+mod web;
+
+#[cfg(not(target_arch = "wasm32"))]
+mod noweb;
+
+#[cfg(target_arch = "wasm32")]
+pub use web::*;
+
+#[cfg(not(target_arch = "wasm32"))]
+pub use noweb::*;
+
+#[cfg(not(target_arch = "wasm32"))]
+pub struct WinitMapWindowConfig {
+ title: String,
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+impl WinitMapWindowConfig {
+ pub fn new(title: String) -> Self {
+ Self { title }
+ }
+}
+
+#[cfg(target_arch = "wasm32")]
+pub struct WinitMapWindowConfig {
+ canvas_id: String,
+}
+
+#[cfg(target_arch = "wasm32")]
+impl WinitMapWindowConfig {
+ pub fn new(canvas_id: String) -> Self {
+ Self { canvas_id }
+ }
+}
+
+impl MapWindowConfig for WinitMapWindowConfig {
+ type MapWindow = WinitMapWindow;
+}
+
+pub struct WinitMapWindow {
+ window: WinitWindow,
+ event_loop: Option,
+}
+
+pub type WinitWindow = winit::window::Window;
+pub type WinitEventLoop = winit::event_loop::EventLoop<()>;
+
+impl WinitMapWindow {
+ pub fn take_event_loop(&mut self) -> Option {
+ self.event_loop.take()
+ }
+}
+
+///Main (platform-specific) main loop which handles:
+///* Input (Mouse/Keyboard)
+///* Platform Events like suspend/resume
+///* Render a new frame
+impl Runnable for WinitMapWindow
+where
+ MWC: MapWindowConfig,
+ SM: ScheduleMethod,
+ HC: HTTPClient,
+{
+ fn run(mut self, mut map_state: MapState, max_frames: Option) {
+ let mut last_render_time = Instant::now();
+ let mut current_frame: u64 = 0;
+
+ let mut input_controller = InputController::new(0.2, 100.0, 0.1);
+
+ self.take_event_loop()
+ .unwrap()
+ .run(move |event, _, control_flow| {
+ #[cfg(target_os = "android")]
+ if !map_state.is_initialized() && event == Event::Resumed {
+ use tokio::runtime::Handle;
+ use tokio::task;
+
+ let state = task::block_in_place(|| {
+ Handle::current().block_on(async {
+ map_state.reinitialize().await;
+ })
+ });
+ return;
+ }
+
+ match event {
+ Event::DeviceEvent {
+ ref event,
+ .. // We're not using device_id currently
+ } => {
+ input_controller.device_input(event);
+ }
+
+ Event::WindowEvent {
+ ref event,
+ window_id,
+ } if window_id == self.inner().id() => {
+ if !input_controller.window_input(event) {
+ match event {
+ WindowEvent::CloseRequested
+ | WindowEvent::KeyboardInput {
+ input:
+ KeyboardInput {
+ state: ElementState::Pressed,
+ virtual_keycode: Some(VirtualKeyCode::Escape),
+ ..
+ },
+ ..
+ } => *control_flow = ControlFlow::Exit,
+ WindowEvent::Resized(physical_size) => {
+ map_state.resize(physical_size.width, physical_size.height);
+ }
+ WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
+ map_state.resize(new_inner_size.width, new_inner_size.height);
+ }
+ _ => {}
+ }
+ }
+ }
+ Event::RedrawRequested(_) => {
+ let now = Instant::now();
+ let dt = now - last_render_time;
+ last_render_time = now;
+
+ input_controller.update_state(map_state.view_state_mut(), dt);
+
+ match map_state.update_and_redraw() {
+ Ok(_) => {}
+ Err(Error::Render(e)) => {
+ eprintln!("{}", e);
+ if e.should_exit() {
+ *control_flow = ControlFlow::Exit;
+ }
+ }
+ e => eprintln!("{:?}", e)
+ };
+
+ if let Some(max_frames) = max_frames {
+ if current_frame >= max_frames {
+ log::info!("Exiting because maximum frames reached.");
+ *control_flow = ControlFlow::Exit;
+ }
+
+ current_frame += 1;
+ }
+ }
+ Event::Suspended => {
+ map_state.suspend();
+ }
+ Event::Resumed => {
+ map_state.recreate_surface(&self);
+ let size = self.size();
+ map_state.resize(size.width(), size.height());// FIXME: Resumed is also called when the app launches for the first time. Instead of first using a "fake" inner_size() in State::new we should initialize with a proper size from the beginning
+ map_state.resume();
+ }
+ Event::MainEventsCleared => {
+ // RedrawRequested will only trigger once, unless we manually
+ // request it.
+ self.inner().request_redraw();
+ }
+ _ => {}
+ }
+ });
+ }
+}
diff --git a/maplibre-winit/src/winit/noweb.rs b/maplibre-winit/src/winit/noweb.rs
new file mode 100644
index 00000000..4338aded
--- /dev/null
+++ b/maplibre-winit/src/winit/noweb.rs
@@ -0,0 +1,50 @@
+//! Main (platform-specific) main loop which handles:
+//! * Input (Mouse/Keyboard)
+//! * Platform Events like suspend/resume
+//! * Render a new frame
+
+use winit::window::WindowBuilder;
+
+use super::WinitEventLoop;
+use super::WinitMapWindow;
+use super::WinitWindow;
+
+use super::WinitMapWindowConfig;
+use maplibre::window::{MapWindow, WindowSize};
+
+impl MapWindow for WinitMapWindow {
+ type EventLoop = WinitEventLoop;
+ type Window = WinitWindow;
+ type MapWindowConfig = WinitMapWindowConfig;
+
+ fn create(map_window_config: &Self::MapWindowConfig) -> Self {
+ let event_loop = WinitEventLoop::new();
+ let window = WindowBuilder::new()
+ .with_title(&map_window_config.title)
+ .build(&event_loop)
+ .unwrap();
+
+ Self {
+ window,
+ event_loop: Some(event_loop),
+ }
+ }
+
+ fn size(&self) -> WindowSize {
+ let size = self.window.inner_size();
+ #[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.
+ let window_size =
+ WindowSize::new(size.width, size.height).unwrap_or(WindowSize::new(100, 100).unwrap());
+
+ #[cfg(not(target_os = "android"))]
+ let window_size =
+ WindowSize::new(size.width, size.height).expect("failed to get window dimensions.");
+ window_size
+ }
+
+ fn inner(&self) -> &Self::Window {
+ &self.window
+ }
+}
diff --git a/maplibre-winit/src/winit/web.rs b/maplibre-winit/src/winit/web.rs
new file mode 100644
index 00000000..981c8787
--- /dev/null
+++ b/maplibre-winit/src/winit/web.rs
@@ -0,0 +1,63 @@
+use winit::window::WindowBuilder;
+
+use super::WinitEventLoop;
+use super::WinitMapWindow;
+use super::WinitMapWindowConfig;
+use super::WinitWindow;
+
+use maplibre::window::{MapWindow, WindowSize};
+use winit::platform::web::WindowBuilderExtWebSys;
+
+impl MapWindow for WinitMapWindow {
+ type EventLoop = WinitEventLoop;
+ type Window = WinitWindow;
+ type MapWindowConfig = WinitMapWindowConfig;
+
+ fn create(map_window_config: &Self::MapWindowConfig) -> Self {
+ let event_loop = WinitEventLoop::new();
+
+ let window: winit::window::Window = WindowBuilder::new()
+ .with_canvas(Some(get_canvas(&map_window_config.canvas_id)))
+ .build(&event_loop)
+ .unwrap();
+
+ let size = get_body_size().unwrap();
+ window.set_inner_size(size);
+ Self {
+ window,
+ event_loop: Some(event_loop),
+ }
+ }
+
+ fn size(&self) -> WindowSize {
+ let size = self.window.inner_size();
+
+ WindowSize::new(size.width, size.height).expect("failed to get window dimensions.")
+ }
+
+ fn inner(&self) -> &Self::Window {
+ &self.window
+ }
+}
+
+pub fn get_body_size() -> Option> {
+ let web_window: web_sys::Window = web_sys::window().unwrap();
+ let document = web_window.document().unwrap();
+ let body = document.body().unwrap();
+ Some(winit::dpi::LogicalSize {
+ width: body.client_width(),
+ height: body.client_height(),
+ })
+}
+
+pub fn get_canvas(element_id: &str) -> web_sys::HtmlCanvasElement {
+ use wasm_bindgen::JsCast;
+
+ let web_window: web_sys::Window = web_sys::window().unwrap();
+ let document = web_window.document().unwrap();
+ document
+ .get_element_by_id(element_id)
+ .unwrap()
+ .dyn_into::()
+ .unwrap()
+}
diff --git a/maplibre/Cargo.toml b/maplibre/Cargo.toml
index d5578e48..8befc603 100644
--- a/maplibre/Cargo.toml
+++ b/maplibre/Cargo.toml
@@ -14,12 +14,7 @@ web-webgl = ["wgpu/webgl"]
enable-tracing = [ "tracing-subscriber", "tracing-tracy", "tracy-client"]
no-thread-safe-futures = []
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-web-sys = { version = "0.3", features = [
- "Window"
-] }
-wasm-bindgen = "0.2"
-wasm-bindgen-futures = "0.4"
+
[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"] }
@@ -31,19 +26,13 @@ tracing-tracy = { version = "0.8", optional = true }
tracy-client = { version = "0.12.7", optional = true }
[target.'cfg(target_os = "android")'.dependencies]
-winit = { version = "0.26", default-features = false }
# Use rusttls on android because cross compiling is difficult
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "gzip"] }
-[target.'cfg(target_os = "linux")'.dependencies]
-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"
tracing = { version = "0.1" }
diff --git a/maplibre/src/error.rs b/maplibre/src/error.rs
index 79f2d9de..23086c60 100644
--- a/maplibre/src/error.rs
+++ b/maplibre/src/error.rs
@@ -1,13 +1,47 @@
//! Errors which can happen in various parts of the library.
use lyon::tessellation::TessellationError;
+use std::fmt;
+use std::fmt::Formatter;
use std::sync::mpsc::SendError;
+use wgpu::SurfaceError;
+
+#[derive(Debug)]
+pub enum RenderError {
+ Surface(wgpu::SurfaceError),
+}
+
+impl fmt::Display for RenderError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ RenderError::Surface(e) => write!(f, "{}", e),
+ }
+ }
+}
+
+impl RenderError {
+ pub fn should_exit(&self) -> bool {
+ match self {
+ RenderError::Surface(e) => match e {
+ SurfaceError::OutOfMemory => true,
+ _ => false,
+ },
+ }
+ }
+}
#[derive(Debug)]
pub enum Error {
Schedule,
Network(String),
Tesselation(TessellationError),
+ Render(RenderError),
+}
+
+impl From for Error {
+ fn from(e: SurfaceError) -> Self {
+ Error::Render(RenderError::Surface(e))
+ }
}
impl From for Error {
diff --git a/maplibre/src/io/shared_thread_state.rs b/maplibre/src/io/shared_thread_state.rs
index 8050adae..64896940 100644
--- a/maplibre/src/io/shared_thread_state.rs
+++ b/maplibre/src/io/shared_thread_state.rs
@@ -1,16 +1,15 @@
-use crate::coords::{TileCoords, WorldCoords, WorldTileCoords, Zoom};
+use crate::coords::{WorldCoords, WorldTileCoords, Zoom};
use crate::error::Error;
use crate::io::geometry_index::{GeometryIndex, IndexProcessor, IndexedGeometry, TileIndex};
use crate::io::tile_request_state::TileRequestState;
use crate::io::{
- LayerTessellateMessage, TessellateMessage, TileFetchResult, TileRequest, TileRequestID,
- TileTessellateMessage,
+ LayerTessellateMessage, TessellateMessage, TileRequest, TileRequestID, TileTessellateMessage,
};
-use crate::tessellation::Tessellated;
+
use std::collections::HashSet;
use crate::tessellation::zero_tessellator::ZeroTessellator;
-use geozero::mvt::tile;
+
use geozero::GeozeroDatasource;
use prost::Message;
use std::sync::{mpsc, Arc, Mutex};
@@ -41,9 +40,9 @@ impl SharedThreadState {
let mut tile = geozero::mvt::Tile::decode(data.as_ref()).expect("failed to load tile");
- let mut index = IndexProcessor::new();
+ let index = IndexProcessor::new();
- for mut layer in &mut tile.layers {
+ for layer in &mut tile.layers {
let cloned_layer = layer.clone();
let layer_name: &str = &cloned_layer.name;
if !tile_request.layers.contains(layer_name) {
diff --git a/maplibre/src/lib.rs b/maplibre/src/lib.rs
index ab161a5d..0014e2ff 100644
--- a/maplibre/src/lib.rs
+++ b/maplibre/src/lib.rs
@@ -1,9 +1,9 @@
use crate::io::scheduler::{ScheduleMethod, Scheduler};
use crate::io::source_client::HTTPClient;
-use crate::map_state::{MapState, Runnable};
+use crate::map_state::MapState;
use crate::render::render_state::RenderState;
use crate::style::Style;
-use crate::window::{MapWindow, WindowFactory, WindowSize};
+use crate::window::{MapWindow, MapWindowConfig, Runnable, WindowSize};
use std::marker::PhantomData;
pub mod coords;
@@ -17,28 +17,25 @@ pub mod window;
pub mod benchmarking;
// Internal modules
-pub(crate) mod input;
-pub(crate) mod map_state;
-pub(crate) mod render;
+pub mod map_state;
+pub mod render;
pub(crate) mod tessellation;
pub(crate) mod tilejson;
pub(crate) mod util;
-pub(crate) mod winit;
-pub struct Map
+pub struct Map
where
W: MapWindow,
SM: ScheduleMethod,
HC: HTTPClient,
{
- map_state: MapState,
- event_loop: E,
+ map_state: MapState,
+ window: W,
}
-impl Map
+impl Map
where
- MapState: Runnable,
- W: MapWindow,
+ W: MapWindow + Runnable,
SM: ScheduleMethod,
HC: HTTPClient,
{
@@ -51,118 +48,95 @@ where
}
pub fn run_with_optionally_max_frames(self, max_frames: Option) {
- self.map_state.run(self.event_loop, max_frames);
+ self.window.run(self.map_state, max_frames);
}
}
-pub struct UninitializedMap
+pub struct UninitializedMap
where
+ MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HTTPClient,
{
- window: W,
- event_loop: E,
scheduler: Scheduler,
http_client: HC,
style: Style,
+
+ map_window_config: MWC,
}
-impl UninitializedMap
+impl UninitializedMap
where
- W: MapWindow + raw_window_handle::HasRawWindowHandle,
+ MWC: MapWindowConfig,
SM: ScheduleMethod,
HC: HTTPClient,
{
- pub async fn initialize(self) -> Map {
- #[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.
- let window_size = self.window.size().unwrap_or_default();
+ pub async fn initialize(self) -> Map {
+ let instance = wgpu::Instance::new(wgpu::Backends::all());
+ //let instance = wgpu::Instance::new(wgpu::Backends::GL);
+ //let instance = wgpu::Instance::new(wgpu::Backends::VULKAN);
- #[cfg(not(target_os = "android"))]
- let window_size = self
- .window
- .size()
- .expect("failed to get window dimensions.");
+ let window = MWC::MapWindow::create(&self.map_window_config);
+ let window_size = window.size();
- let render_state = RenderState::initialize(&self.window, window_size).await;
+ let surface = unsafe { instance.create_surface(window.inner()) };
+ let surface_config = wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format: crate::platform::COLOR_TEXTURE_FORMAT,
+ width: window_size.width(),
+ height: window_size.height(),
+ // present_mode: wgpu::PresentMode::Mailbox,
+ present_mode: wgpu::PresentMode::Fifo, // VSync
+ };
+
+ let render_state = RenderState::initialize(instance, surface, surface_config).await;
Map {
map_state: MapState::new(
- self.window,
+ self.map_window_config,
window_size,
render_state,
self.scheduler,
self.http_client,
self.style,
),
- event_loop: self.event_loop,
+ window,
}
}
}
-#[cfg(not(target_arch = "wasm32"))]
-impl UninitializedMap
-where
- W: MapWindow + raw_window_handle::HasRawWindowHandle,
- MapState: Runnable,
- SM: ScheduleMethod,
- HC: HTTPClient,
-{
- pub fn run_sync(self) {
- self.run_sync_with_optionally_max_frames(None);
- }
-
- pub fn run_sync_with_max_frames(self, max_frames: u64) {
- self.run_sync_with_optionally_max_frames(Some(max_frames))
- }
-
- fn run_sync_with_optionally_max_frames(self, max_frames: Option) {
- tokio::runtime::Builder::new_multi_thread()
- .worker_threads(4)
- .enable_io()
- .enable_time()
- .on_thread_start(|| {
- #[cfg(feature = "enable-tracing")]
- tracy_client::set_thread_name("tokio-runtime-worker");
- })
- .build()
- .unwrap()
- .block_on(async {
- self.initialize()
- .await
- .run_with_optionally_max_frames(max_frames);
- })
- }
-}
-
-pub struct MapBuilder
+pub struct MapBuilder
where
SM: ScheduleMethod,
{
- window_factory: Box>,
schedule_method: Option,
scheduler: Option>,
http_client: Option,
style: Option