mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Make the scheduler work with future(-factories) for simple scheduling arbitrary workloads
This commit is contained in:
parent
945d3f502c
commit
197a3a3662
2
.idea/runConfigurations/Build_WASM.xml
generated
2
.idea/runConfigurations/Build_WASM.xml
generated
@ -9,7 +9,7 @@
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs>
|
||||
<env name="RUSTUP_TOOLCHAIN" value="nightly-2022-02-26-x86_64-unknown-linux-gnu" />
|
||||
<env name="RUSTUP_TOOLCHAIN" value="nightly-2022-04-04-x86_64-unknown-linux-gnu" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
|
||||
@ -17,9 +17,7 @@ fn main() {
|
||||
enable_tracing();
|
||||
|
||||
MapBuilder::from_window("A fantastic window!")
|
||||
.with_schedule_method(ScheduleMethod::Tokio(TokioScheduleMethod::new(Some(
|
||||
"/tmp/mapr_cache".to_string(),
|
||||
))))
|
||||
.with_schedule_method(ScheduleMethod::Tokio(TokioScheduleMethod::new()))
|
||||
.build()
|
||||
.run_sync();
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ use lyon::tessellation::TessellationError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Schedule,
|
||||
Network(String),
|
||||
File(String),
|
||||
Tesselation(TessellationError),
|
||||
|
||||
@ -13,6 +13,7 @@ use crate::input::tilt_handler::TiltHandler;
|
||||
use crate::input::zoom_handler::ZoomHandler;
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::IOScheduler;
|
||||
|
||||
mod pan_handler;
|
||||
mod pinch_handler;
|
||||
@ -125,17 +126,17 @@ impl InputController {
|
||||
}
|
||||
|
||||
pub trait UpdateState {
|
||||
fn update_state(&mut self, state: &mut RenderState, tile_cache: &TileCache, dt: Duration);
|
||||
fn update_state(&mut self, state: &mut RenderState, scheduler: &IOScheduler, dt: Duration);
|
||||
}
|
||||
|
||||
impl UpdateState for InputController {
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn update_state(&mut self, state: &mut RenderState, tile_cache: &TileCache, dt: Duration) {
|
||||
self.pan_handler.update_state(state, tile_cache, dt);
|
||||
self.pinch_handler.update_state(state, tile_cache, dt);
|
||||
self.zoom_handler.update_state(state, tile_cache, dt);
|
||||
self.tilt_handler.update_state(state, tile_cache, dt);
|
||||
self.shift_handler.update_state(state, tile_cache, dt);
|
||||
self.query_handler.update_state(state, tile_cache, dt);
|
||||
fn update_state(&mut self, state: &mut RenderState, scheduler: &IOScheduler, dt: Duration) {
|
||||
self.pan_handler.update_state(state, scheduler, dt);
|
||||
self.pinch_handler.update_state(state, scheduler, dt);
|
||||
self.zoom_handler.update_state(state, scheduler, dt);
|
||||
self.tilt_handler.update_state(state, scheduler, dt);
|
||||
self.shift_handler.update_state(state, scheduler, dt);
|
||||
self.query_handler.update_state(state, scheduler, dt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ use super::UpdateState;
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::camera::Camera;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::IOScheduler;
|
||||
use cgmath::{EuclideanSpace, Point3, Vector2, Vector3, Zero};
|
||||
use std::time::Duration;
|
||||
use winit::event::{ElementState, MouseButton};
|
||||
@ -15,7 +16,7 @@ pub struct PanHandler {
|
||||
}
|
||||
|
||||
impl UpdateState for PanHandler {
|
||||
fn update_state(&mut self, state: &mut RenderState, _tile_cache: &TileCache, _dt: Duration) {
|
||||
fn update_state(&mut self, state: &mut RenderState, _scheduler: &IOScheduler, _dt: Duration) {
|
||||
if !self.is_panning {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use super::UpdateState;
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::IOScheduler;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct PinchHandler {}
|
||||
|
||||
impl UpdateState for PinchHandler {
|
||||
fn update_state(&mut self, _state: &mut RenderState, _tile_cache: &TileCache, _dt: Duration) {
|
||||
fn update_state(&mut self, _state: &mut RenderState, _scheduler: &IOScheduler, _dt: Duration) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ use crate::io::tile_cache::TileCache;
|
||||
|
||||
use crate::render::render_state::RenderState;
|
||||
|
||||
use crate::IOScheduler;
|
||||
use cgmath::Vector2;
|
||||
use log::info;
|
||||
use std::time::Duration;
|
||||
@ -57,7 +58,7 @@ impl QueryHandler {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update_state(&mut self, state: &mut RenderState, tile_cache: &TileCache, _dt: Duration) {
|
||||
pub fn update_state(&mut self, state: &mut RenderState, scheduler: &IOScheduler, dt: Duration) {
|
||||
if self.clicking {
|
||||
if let Some(window_position) = self.window_position {
|
||||
let perspective = &state.perspective;
|
||||
@ -68,7 +69,7 @@ impl QueryHandler {
|
||||
.camera
|
||||
.window_to_world_at_ground(&window_position, &inverted_view_proj)
|
||||
{
|
||||
let option = tile_cache.query_point(
|
||||
/*let option = tile_cache.query_point(
|
||||
&WorldCoords {
|
||||
x: coordinates.x,
|
||||
y: coordinates.y,
|
||||
@ -83,7 +84,7 @@ impl QueryHandler {
|
||||
.iter()
|
||||
.map(|geometry| &geometry.properties)
|
||||
.collect::<Vec<_>>())
|
||||
);
|
||||
);*/
|
||||
}
|
||||
}
|
||||
self.clicking = false;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use super::UpdateState;
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::IOScheduler;
|
||||
use cgmath::{Vector3, Zero};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -12,7 +13,7 @@ pub struct ShiftHandler {
|
||||
}
|
||||
|
||||
impl UpdateState for ShiftHandler {
|
||||
fn update_state(&mut self, state: &mut RenderState, _tile_cache: &TileCache, dt: Duration) {
|
||||
fn update_state(&mut self, state: &mut RenderState, _scheduler: &IOScheduler, dt: Duration) {
|
||||
let dt = dt.as_secs_f64() * (1.0 / self.speed);
|
||||
|
||||
let delta = self.camera_translate * dt;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use super::UpdateState;
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::IOScheduler;
|
||||
use cgmath::{Deg, Rad, Zero};
|
||||
use std::time::Duration;
|
||||
|
||||
@ -12,7 +13,7 @@ pub struct TiltHandler {
|
||||
}
|
||||
|
||||
impl UpdateState for TiltHandler {
|
||||
fn update_state(&mut self, state: &mut RenderState, _tile_cache: &TileCache, dt: Duration) {
|
||||
fn update_state(&mut self, state: &mut RenderState, _scheduler: &IOScheduler, dt: Duration) {
|
||||
let dt = dt.as_secs_f64() * (1.0 / self.speed);
|
||||
|
||||
let delta = self.delta_pitch * dt;
|
||||
|
||||
@ -2,6 +2,7 @@ use super::UpdateState;
|
||||
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::render::render_state::RenderState;
|
||||
use crate::IOScheduler;
|
||||
use cgmath::num_traits::Pow;
|
||||
use cgmath::{Vector2, Vector3};
|
||||
use std::time::Duration;
|
||||
@ -13,7 +14,7 @@ pub struct ZoomHandler {
|
||||
}
|
||||
|
||||
impl UpdateState for ZoomHandler {
|
||||
fn update_state(&mut self, state: &mut RenderState, _tile_cache: &TileCache, _dt: Duration) {
|
||||
fn update_state(&mut self, state: &mut RenderState, _scheduler: &IOScheduler, _dt: Duration) {
|
||||
if self.zoom_delta != 0.0 {
|
||||
if let Some(window_position) = self.window_position {
|
||||
let current_zoom = state.zoom;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
|
||||
use cgmath::num_traits::Signed;
|
||||
use cgmath::num_traits::{Pow, Signed};
|
||||
use cgmath::Bounded;
|
||||
use geo::prelude::*;
|
||||
use geo_types::{CoordFloat, Coordinate, Geometry, LineString, Point, Polygon};
|
||||
@ -9,9 +9,52 @@ use geozero::geo_types::GeoWriter;
|
||||
use geozero::{ColumnValue, FeatureProcessor, GeomProcessor, PropertyProcessor};
|
||||
use rstar::{Envelope, PointDistance, RTree, RTreeObject, AABB};
|
||||
|
||||
use crate::coords::InnerCoords;
|
||||
use crate::coords::{InnerCoords, Quadkey, WorldCoords, WorldTileCoords, EXTENT, TILE_SIZE};
|
||||
use crate::util::math::bounds_from_points;
|
||||
|
||||
pub struct GeometryIndex {
|
||||
index: BTreeMap<Quadkey, TileIndex>,
|
||||
}
|
||||
|
||||
impl GeometryIndex {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
index: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn index_tile(&mut self, coords: &&WorldTileCoords, tile_index: TileIndex) {
|
||||
coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.index.insert(key, tile_index));
|
||||
}
|
||||
|
||||
pub fn query_point(
|
||||
&self,
|
||||
world_coords: &WorldCoords,
|
||||
z: u8,
|
||||
zoom: f64,
|
||||
) -> Option<Vec<&IndexGeometry<f64>>> {
|
||||
let world_tile_coords = world_coords.into_world_tile(z, zoom);
|
||||
|
||||
if let Some(index) = world_tile_coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.index.get(&key))
|
||||
{
|
||||
let scale = 2.0f64.pow(z as f64 - zoom); // TODO deduplicate
|
||||
|
||||
let delta_x = world_coords.x / TILE_SIZE * scale - world_tile_coords.x as f64;
|
||||
let delta_y = world_coords.y / TILE_SIZE * scale - world_tile_coords.y as f64;
|
||||
|
||||
let x = delta_x * EXTENT;
|
||||
let y = delta_y * EXTENT;
|
||||
Some(index.point_query(InnerCoords { x, y }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TileIndex {
|
||||
Spatial { tree: RTree<IndexGeometry<f64>> },
|
||||
Linear { list: Vec<IndexGeometry<f64>> },
|
||||
|
||||
@ -13,6 +13,7 @@ use vector_tile::tile::Layer;
|
||||
|
||||
mod geometry_index;
|
||||
pub mod scheduler;
|
||||
mod source_client;
|
||||
pub mod static_tile_fetcher;
|
||||
pub mod tile_cache;
|
||||
|
||||
@ -39,18 +40,16 @@ impl fmt::Debug for TileFetchResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TileIndexResult {
|
||||
pub enum TessellateMessage {
|
||||
Tile(TileTessellateMessage),
|
||||
Layer(LayerTessellateMessage),
|
||||
}
|
||||
|
||||
pub struct TileTessellateMessage {
|
||||
request_id: TileRequestID,
|
||||
coords: WorldTileCoords,
|
||||
index: TileIndex,
|
||||
}
|
||||
|
||||
pub enum TileTessellateResult {
|
||||
Tile { request_id: TileRequestID },
|
||||
Layer(LayerTessellateResult),
|
||||
}
|
||||
|
||||
pub enum LayerTessellateResult {
|
||||
pub enum LayerTessellateMessage {
|
||||
UnavailableLayer {
|
||||
coords: WorldTileCoords,
|
||||
layer_name: String,
|
||||
@ -64,24 +63,24 @@ pub enum LayerTessellateResult {
|
||||
},
|
||||
}
|
||||
|
||||
impl fmt::Debug for LayerTessellateResult {
|
||||
impl fmt::Debug for LayerTessellateMessage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "LayerResult{}", self.get_coords())
|
||||
write!(f, "LayerTessellateMessage{}", self.get_coords())
|
||||
}
|
||||
}
|
||||
|
||||
impl LayerTessellateResult {
|
||||
impl LayerTessellateMessage {
|
||||
pub fn get_coords(&self) -> WorldTileCoords {
|
||||
match self {
|
||||
LayerTessellateResult::UnavailableLayer { coords, .. } => *coords,
|
||||
LayerTessellateResult::TessellatedLayer { coords, .. } => *coords,
|
||||
LayerTessellateMessage::UnavailableLayer { coords, .. } => *coords,
|
||||
LayerTessellateMessage::TessellatedLayer { coords, .. } => *coords,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn layer_name(&self) -> &str {
|
||||
match self {
|
||||
LayerTessellateResult::UnavailableLayer { layer_name, .. } => layer_name.as_str(),
|
||||
LayerTessellateResult::TessellatedLayer { layer_data, .. } => layer_data.name(),
|
||||
LayerTessellateMessage::UnavailableLayer { layer_name, .. } => layer_name.as_str(),
|
||||
LayerTessellateMessage::TessellatedLayer { layer_data, .. } => layer_data.name(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,7 +93,7 @@ pub struct TileRequest {
|
||||
|
||||
impl fmt::Debug for TileRequest {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "TileRequest({})", &self.coords)
|
||||
write!(f, "TileRequest({}, {:?})", &self.coords, &self.layers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
|
||||
use geozero::mvt::Tile;
|
||||
use geozero::GeozeroDatasource;
|
||||
@ -13,62 +14,67 @@ use vector_tile::parse_tile_bytes;
|
||||
use crate::coords::{TileCoords, WorldTileCoords};
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::{
|
||||
LayerTessellateResult, TileFetchResult, TileIndexResult, TileRequest, TileRequestID,
|
||||
TileTessellateResult,
|
||||
LayerTessellateMessage, TessellateMessage, TileFetchResult, TileRequest, TileRequestID,
|
||||
TileTessellateMessage,
|
||||
};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::io::geometry_index::{GeometryIndex, IndexProcessor, TileIndex};
|
||||
use crate::io::source_client::{HttpSourceClient, SourceClient};
|
||||
use crate::tessellation::Tessellated;
|
||||
use prost::Message;
|
||||
|
||||
pub enum ScheduleMethod {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Tokio(crate::platform::scheduler::TokioScheduleMethod),
|
||||
Tokio(crate::platform::schedule_method::TokioScheduleMethod),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
WebWorker(crate::platform::scheduler::WebWorkerScheduleMethod),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
WebWorkerPool(crate::platform::scheduler::WebWorkerPoolScheduleMethod),
|
||||
WebWorkerPool(crate::platform::schedule_method::WebWorkerPoolScheduleMethod),
|
||||
}
|
||||
|
||||
impl Default for ScheduleMethod {
|
||||
fn default() -> Self {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
ScheduleMethod::Tokio(crate::platform::scheduler::TokioScheduleMethod::new(None))
|
||||
ScheduleMethod::Tokio(crate::platform::schedule_method::TokioScheduleMethod::new())
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
ScheduleMethod::WebWorker(crate::platform::scheduler::WebWorkerScheduleMethod::new())
|
||||
panic!("No default ScheduleMethod on web")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScheduleMethod {
|
||||
pub fn schedule_tile_request(
|
||||
pub fn schedule_fn<T>(
|
||||
&self,
|
||||
scheduler: &IOScheduler,
|
||||
request_id: TileRequestID,
|
||||
coords: TileCoords,
|
||||
) {
|
||||
future_factory: impl (FnOnce() -> T) + Send + 'static,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
match self {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
ScheduleMethod::WebWorkerPool(method) => Ok(method.schedule(future_factory)),
|
||||
_ => Err(Error::Schedule),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn schedule<T>(&self, future: T) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
match self {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
ScheduleMethod::Tokio(method) => {
|
||||
method.schedule_tile_request(scheduler, request_id, coords)
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
ScheduleMethod::WebWorker(method) => {
|
||||
method.schedule_tile_request(scheduler, request_id, coords)
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
ScheduleMethod::WebWorkerPool(method) => {
|
||||
method.schedule_tile_request(scheduler, request_id, coords)
|
||||
}
|
||||
ScheduleMethod::Tokio(method) => Ok(method.schedule(future)),
|
||||
_ => Err(Error::Schedule),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThreadLocalTessellatorState {
|
||||
tile_request_state: Arc<Mutex<TileRequestState>>,
|
||||
tessellate_result_sender: Sender<TileTessellateResult>,
|
||||
index_result_sender: Sender<TileIndexResult>,
|
||||
tessellate_result_sender: Sender<TessellateMessage>,
|
||||
geometry_index: Arc<Mutex<GeometryIndex>>,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
@ -81,8 +87,6 @@ impl Drop for ThreadLocalTessellatorState {
|
||||
);
|
||||
}
|
||||
}
|
||||
use crate::io::geometry_index::{IndexProcessor, TileIndex};
|
||||
use prost::Message;
|
||||
|
||||
impl ThreadLocalTessellatorState {
|
||||
fn get_tile_request(&self, request_id: TileRequestID) -> Option<TileRequest> {
|
||||
@ -97,7 +101,7 @@ impl ThreadLocalTessellatorState {
|
||||
&self,
|
||||
request_id: TileRequestID,
|
||||
data: Box<[u8]>,
|
||||
) -> Result<(), SendError<TileTessellateResult>> {
|
||||
) -> Result<(), SendError<TessellateMessage>> {
|
||||
if let Some(tile_request) = self.get_tile_request(request_id) {
|
||||
let tile_result = TileFetchResult::Tile {
|
||||
coords: tile_request.coords,
|
||||
@ -105,8 +109,7 @@ impl ThreadLocalTessellatorState {
|
||||
};
|
||||
|
||||
self.tessellate_layers_with_request(&tile_result, &tile_request, request_id)?;
|
||||
|
||||
self.index_geometry(request_id, &tile_result);
|
||||
self.index_geometry(&tile_result);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -115,7 +118,7 @@ impl ThreadLocalTessellatorState {
|
||||
pub fn tile_unavailable(
|
||||
&self,
|
||||
request_id: TileRequestID,
|
||||
) -> Result<(), SendError<TileTessellateResult>> {
|
||||
) -> Result<(), SendError<TessellateMessage>> {
|
||||
if let Some(tile_request) = self.get_tile_request(request_id) {
|
||||
let tile_result = TileFetchResult::Unavailable {
|
||||
coords: tile_request.coords,
|
||||
@ -127,7 +130,7 @@ impl ThreadLocalTessellatorState {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
fn index_geometry(&self, request_id: TileRequestID, tile_result: &TileFetchResult) {
|
||||
fn index_geometry(&self, tile_result: &TileFetchResult) {
|
||||
match tile_result {
|
||||
TileFetchResult::Tile { data, coords } => {
|
||||
let tile: Tile = Tile::decode(data.as_ref()).unwrap();
|
||||
@ -137,18 +140,14 @@ impl ThreadLocalTessellatorState {
|
||||
layer.process(&mut processor).unwrap();
|
||||
}
|
||||
|
||||
self.index_result_sender
|
||||
.send(TileIndexResult {
|
||||
request_id,
|
||||
coords: *coords,
|
||||
/*index: TileIndex::Spatial {
|
||||
tree: processor.build_tree(),
|
||||
},*/
|
||||
index: TileIndex::Linear {
|
||||
if let Some(mut geometry_index) = self.geometry_index.lock().ok() {
|
||||
geometry_index.index_tile(
|
||||
&coords,
|
||||
TileIndex::Linear {
|
||||
list: processor.get_geometries(),
|
||||
},
|
||||
})
|
||||
.unwrap();
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -160,13 +159,13 @@ impl ThreadLocalTessellatorState {
|
||||
tile_result: &TileFetchResult,
|
||||
tile_request: &TileRequest,
|
||||
request_id: TileRequestID,
|
||||
) -> Result<(), SendError<TileTessellateResult>> {
|
||||
) -> Result<(), SendError<TessellateMessage>> {
|
||||
match tile_result {
|
||||
TileFetchResult::Unavailable { coords } => {
|
||||
for to_load in &tile_request.layers {
|
||||
self.tessellate_result_sender
|
||||
.send(TileTessellateResult::Layer(
|
||||
LayerTessellateResult::UnavailableLayer {
|
||||
.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::UnavailableLayer {
|
||||
coords: *coords,
|
||||
layer_name: to_load.to_string(),
|
||||
},
|
||||
@ -186,8 +185,8 @@ impl ThreadLocalTessellatorState {
|
||||
match layer.tessellate() {
|
||||
Ok((buffer, feature_indices)) => {
|
||||
self.tessellate_result_sender
|
||||
.send(TileTessellateResult::Layer(
|
||||
LayerTessellateResult::TessellatedLayer {
|
||||
.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::TessellatedLayer {
|
||||
coords: *coords,
|
||||
buffer: buffer.into(),
|
||||
feature_indices,
|
||||
@ -197,8 +196,8 @@ impl ThreadLocalTessellatorState {
|
||||
}
|
||||
Err(e) => {
|
||||
self.tessellate_result_sender
|
||||
.send(TileTessellateResult::Layer(
|
||||
LayerTessellateResult::UnavailableLayer {
|
||||
.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::UnavailableLayer {
|
||||
coords: *coords,
|
||||
layer_name: to_load.to_string(),
|
||||
},
|
||||
@ -214,8 +213,8 @@ impl ThreadLocalTessellatorState {
|
||||
info!("layer {} ready: {}", to_load, &coords);
|
||||
} else {
|
||||
self.tessellate_result_sender
|
||||
.send(TileTessellateResult::Layer(
|
||||
LayerTessellateResult::UnavailableLayer {
|
||||
.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::UnavailableLayer {
|
||||
coords: *coords,
|
||||
layer_name: to_load.to_string(),
|
||||
},
|
||||
@ -228,16 +227,18 @@ impl ThreadLocalTessellatorState {
|
||||
}
|
||||
|
||||
self.tessellate_result_sender
|
||||
.send(TileTessellateResult::Tile { request_id })?;
|
||||
.send(TessellateMessage::Tile(TileTessellateMessage {
|
||||
request_id,
|
||||
}))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IOScheduler {
|
||||
index_channel: (Sender<TileIndexResult>, Receiver<TileIndexResult>),
|
||||
tessellate_channel: (Sender<TileTessellateResult>, Receiver<TileTessellateResult>),
|
||||
tessellate_channel: (Sender<TessellateMessage>, Receiver<TessellateMessage>),
|
||||
tile_request_state: Arc<Mutex<TileRequestState>>,
|
||||
geometry_index: Arc<Mutex<GeometryIndex>>,
|
||||
tile_cache: TileCache,
|
||||
schedule_method: ScheduleMethod,
|
||||
}
|
||||
@ -253,9 +254,9 @@ const _: () = {
|
||||
impl IOScheduler {
|
||||
pub fn new(schedule_method: ScheduleMethod) -> Self {
|
||||
Self {
|
||||
index_channel: channel(),
|
||||
tessellate_channel: channel(),
|
||||
tile_request_state: Arc::new(Mutex::new(TileRequestState::new())),
|
||||
geometry_index: Arc::new(Mutex::new(GeometryIndex::new())),
|
||||
tile_cache: TileCache::new(),
|
||||
schedule_method,
|
||||
}
|
||||
@ -266,26 +267,22 @@ impl IOScheduler {
|
||||
if let Ok(mut tile_request_state) = self.tile_request_state.try_lock() {
|
||||
if let Ok(result) = self.tessellate_channel.1.try_recv() {
|
||||
match result {
|
||||
TileTessellateResult::Tile { request_id } => {
|
||||
TessellateMessage::Tile(TileTessellateMessage { request_id }) => {
|
||||
tile_request_state.finish_tile_request(request_id);
|
||||
}
|
||||
TileTessellateResult::Layer(layer_result) => {
|
||||
self.tile_cache.put_tessellation_result(layer_result);
|
||||
TessellateMessage::Layer(layer_result) => {
|
||||
self.tile_cache.put_tesselated_layer(layer_result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(result) = self.index_channel.1.try_recv() {
|
||||
self.tile_cache.put_index_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_tessellator_state(&self) -> ThreadLocalTessellatorState {
|
||||
ThreadLocalTessellatorState {
|
||||
tile_request_state: self.tile_request_state.clone(),
|
||||
tessellate_result_sender: self.tessellate_channel.0.clone(),
|
||||
index_result_sender: self.index_channel.0.clone(),
|
||||
geometry_index: self.geometry_index.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,15 +296,41 @@ impl IOScheduler {
|
||||
}
|
||||
|
||||
if let Ok(mut tile_request_state) = self.tile_request_state.try_lock() {
|
||||
if let Some(id) = tile_request_state.start_tile_request(TileRequest {
|
||||
if let Some(request_id) = tile_request_state.start_tile_request(TileRequest {
|
||||
coords: *coords,
|
||||
layers: layers.clone(),
|
||||
}) {
|
||||
if let Some(tile_coords) = coords.into_tile(TileAddressingScheme::TMS) {
|
||||
info!("new tile request: {}", &tile_coords);
|
||||
info!("new tile request: {}", &coords);
|
||||
|
||||
self.schedule_method
|
||||
.schedule_tile_request(self, id, tile_coords);
|
||||
// The following snippet can be added instead of the next code block to demonstrate
|
||||
// an understanable approach of fetching
|
||||
/*#[cfg(target_arch = "wasm32")]
|
||||
if let Some(tile_coords) = coords.into_tile(TileAddressingScheme::TMS) {
|
||||
crate::platform::legacy_webworker_fetcher::request_tile(
|
||||
request_id,
|
||||
tile_coords,
|
||||
);
|
||||
}*/
|
||||
|
||||
{
|
||||
let state = self.new_tessellator_state();
|
||||
let client = SourceClient::Http(HttpSourceClient::new());
|
||||
let copied_coords = *coords;
|
||||
|
||||
let future_fn = move || async move {
|
||||
if let Ok(data) = client.fetch(&copied_coords).await {
|
||||
state
|
||||
.process_tile(request_id, data.into_boxed_slice())
|
||||
.unwrap();
|
||||
} else {
|
||||
state.tile_unavailable(request_id).unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
self.schedule_method.schedule_fn(future_fn);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
self.schedule_method.schedule(future_fn());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
src/io/source_client.rs
Normal file
54
src/io/source_client.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use crate::coords::{TileCoords, WorldTileCoords};
|
||||
use crate::error::Error;
|
||||
use style_spec::source::TileAddressingScheme;
|
||||
|
||||
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 enum SourceClient {
|
||||
Http(HttpSourceClient),
|
||||
Mbtiles {
|
||||
// TODO
|
||||
},
|
||||
}
|
||||
|
||||
impl SourceClient {
|
||||
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, Error> {
|
||||
match self {
|
||||
SourceClient::Http(client) => client.fetch(coords).await,
|
||||
SourceClient::Mbtiles { .. } => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HttpSourceClient {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
inner_client: crate::platform::http_client::ReqwestHttpClient::new(Some(
|
||||
"/tmp/mapr-cache".to_string(), // TODO make path dynamic
|
||||
)),
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
inner_client: crate::platform::http_client::WHATWGFetchHttpClient::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch(&self, coords: &WorldTileCoords) -> Result<Vec<u8>, Error> {
|
||||
let tile_coords = coords.into_tile(TileAddressingScheme::TMS).unwrap();
|
||||
self.inner_client
|
||||
.fetch(
|
||||
format!(
|
||||
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
|
||||
x = tile_coords.x,
|
||||
y = tile_coords.y,
|
||||
z = tile_coords.z
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
@ -1,82 +1,48 @@
|
||||
use crate::coords::{InnerCoords, Quadkey, WorldCoords, WorldTileCoords, EXTENT, TILE_SIZE};
|
||||
use crate::io::geometry_index::IndexGeometry;
|
||||
use crate::io::{LayerTessellateResult, TileIndexResult};
|
||||
use crate::io::LayerTessellateMessage;
|
||||
use cgmath::num_traits::Pow;
|
||||
use std::collections::{btree_map, BTreeMap, HashSet};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TileCache {
|
||||
cache_index: BTreeMap<Quadkey, Vec<LayerTessellateResult>>,
|
||||
tile_geometry_index: BTreeMap<Quadkey, TileIndexResult>,
|
||||
cache: BTreeMap<Quadkey, Vec<LayerTessellateMessage>>,
|
||||
}
|
||||
|
||||
impl TileCache {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
cache_index: BTreeMap::new(),
|
||||
tile_geometry_index: BTreeMap::new(),
|
||||
cache: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_tessellation_result(&mut self, result: LayerTessellateResult) {
|
||||
if let Some(entry) = result
|
||||
pub fn put_tesselated_layer(&mut self, message: LayerTessellateMessage) {
|
||||
if let Some(entry) = message
|
||||
.get_coords()
|
||||
.build_quad_key()
|
||||
.map(|key| self.cache_index.entry(key))
|
||||
.map(|key| self.cache.entry(key))
|
||||
{
|
||||
match entry {
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(vec![result]);
|
||||
entry.insert(vec![message]);
|
||||
}
|
||||
btree_map::Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().push(result);
|
||||
entry.get_mut().push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_index_result(&mut self, result: TileIndexResult) {
|
||||
result
|
||||
.coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tile_geometry_index.insert(key, result));
|
||||
}
|
||||
|
||||
pub fn query_point(
|
||||
&self,
|
||||
world_coords: &WorldCoords,
|
||||
z: u8,
|
||||
zoom: f64,
|
||||
) -> Option<Vec<&IndexGeometry<f64>>> {
|
||||
let world_tile_coords = world_coords.into_world_tile(z, zoom);
|
||||
|
||||
if let Some(index) = world_tile_coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tile_geometry_index.get(&key))
|
||||
{
|
||||
let scale = 2.0.pow(z as f64 - zoom); // TODO deduplicate
|
||||
|
||||
let delta_x = world_coords.x / TILE_SIZE * scale - world_tile_coords.x as f64;
|
||||
let delta_y = world_coords.y / TILE_SIZE * scale - world_tile_coords.y as f64;
|
||||
|
||||
let x = delta_x * EXTENT;
|
||||
let y = delta_y * EXTENT;
|
||||
Some(index.index.point_query(InnerCoords { x, y }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_tile(&self, coords: &WorldTileCoords) -> bool {
|
||||
coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| {
|
||||
self.cache_index.get(&key).and_then(|entries| {
|
||||
self.cache.get(&key).and_then(|entries| {
|
||||
if entries.is_empty() {
|
||||
None
|
||||
} else if entries.iter().all(|entry| match entry {
|
||||
LayerTessellateResult::UnavailableLayer { .. } => true,
|
||||
LayerTessellateResult::TessellatedLayer { .. } => false,
|
||||
LayerTessellateMessage::UnavailableLayer { .. } => true,
|
||||
LayerTessellateMessage::TessellatedLayer { .. } => false,
|
||||
}) {
|
||||
None
|
||||
} else {
|
||||
@ -103,10 +69,10 @@ impl TileCache {
|
||||
pub fn iter_tessellated_layers_at(
|
||||
&self,
|
||||
coords: &WorldTileCoords,
|
||||
) -> Option<impl Iterator<Item = &LayerTessellateResult> + '_> {
|
||||
) -> Option<impl Iterator<Item = &LayerTessellateMessage> + '_> {
|
||||
coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.cache_index.get(&key))
|
||||
.and_then(|key| self.cache.get(&key))
|
||||
.map(|results| results.iter())
|
||||
}
|
||||
|
||||
@ -115,10 +81,7 @@ impl TileCache {
|
||||
coords: &WorldTileCoords,
|
||||
layers: &mut HashSet<String>,
|
||||
) {
|
||||
if let Some(results) = coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.cache_index.get(&key))
|
||||
{
|
||||
if let Some(results) = coords.build_quad_key().and_then(|key| self.cache.get(&key)) {
|
||||
let tessellated_set: HashSet<String> = results
|
||||
.iter()
|
||||
.map(|tessellated_layer| tessellated_layer.layer_name().to_string())
|
||||
@ -129,10 +92,7 @@ impl TileCache {
|
||||
}
|
||||
|
||||
pub fn is_layers_missing(&self, coords: &WorldTileCoords, layers: &HashSet<String>) -> bool {
|
||||
if let Some(results) = coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.cache_index.get(&key))
|
||||
{
|
||||
if let Some(results) = coords.build_quad_key().and_then(|key| self.cache.get(&key)) {
|
||||
let tessellated_set: HashSet<&str> = results
|
||||
.iter()
|
||||
.map(|tessellated_layer| tessellated_layer.layer_name())
|
||||
|
||||
@ -17,7 +17,7 @@ pub(crate) mod util;
|
||||
pub mod benchmarking;
|
||||
|
||||
pub use io::scheduler::ScheduleMethod;
|
||||
pub use platform::scheduler::*;
|
||||
pub use platform::schedule_method::*;
|
||||
use style_spec::Style;
|
||||
|
||||
pub struct Map {
|
||||
|
||||
@ -102,7 +102,7 @@ pub async fn run(
|
||||
|
||||
scheduler.try_populate_cache();
|
||||
|
||||
input.update_state(state, scheduler.get_tile_cache(), dt);
|
||||
input.update_state(state, &scheduler, dt);
|
||||
state.prepare_render_data(&mut scheduler);
|
||||
match state.render() {
|
||||
Ok(_) => {}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::io::scheduler::ScheduleMethod;
|
||||
use crate::platform::scheduler::TokioScheduleMethod;
|
||||
use crate::platform::schedule_method::TokioScheduleMethod;
|
||||
use crate::MapBuilder;
|
||||
pub use std::time::Instant;
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use crate::io::scheduler::ScheduleMethod;
|
||||
use crate::platform::scheduler::TokioScheduleMethod;
|
||||
use crate::platform::schedule_method::TokioScheduleMethod;
|
||||
use crate::MapBuilder;
|
||||
pub use std::time::Instant;
|
||||
|
||||
|
||||
47
src/platform/noweb/http_client.rs
Normal file
47
src/platform/noweb/http_client.rs
Normal file
@ -0,0 +1,47 @@
|
||||
use crate::error::Error;
|
||||
use reqwest::{Client, StatusCode};
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use reqwest_middleware_cache::managers::CACacheManager;
|
||||
use reqwest_middleware_cache::{Cache, CacheMode};
|
||||
|
||||
pub struct ReqwestHttpClient {
|
||||
client: ClientWithMiddleware,
|
||||
}
|
||||
impl From<reqwest::Error> for Error {
|
||||
fn from(err: reqwest::Error) -> Self {
|
||||
Error::Network(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest_middleware::Error> for Error {
|
||||
fn from(err: reqwest_middleware::Error) -> Self {
|
||||
Error::Network(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl ReqwestHttpClient {
|
||||
/// cache_path: Under which path should we cache requests.
|
||||
pub fn new(cache_path: Option<String>) -> Self {
|
||||
let mut builder = reqwest_middleware::ClientBuilder::new(Client::new());
|
||||
|
||||
if let Some(cache_path) = cache_path {
|
||||
builder = builder.with(Cache {
|
||||
mode: CacheMode::Default,
|
||||
cache_manager: CACacheManager { path: cache_path },
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
client: builder.build(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
|
||||
let response = self.client.get(url).send().await?;
|
||||
if response.status() != StatusCode::OK {
|
||||
return Err(Error::Network("response code not 200".to_string()));
|
||||
}
|
||||
let body = response.bytes().await?;
|
||||
Ok(Vec::from(body.as_ref()))
|
||||
}
|
||||
}
|
||||
@ -1,89 +1,6 @@
|
||||
//! Module which is used target platform is not web related.
|
||||
|
||||
pub mod http_client;
|
||||
pub mod schedule_method;
|
||||
|
||||
pub use std::time::Instant;
|
||||
|
||||
pub mod scheduler {
|
||||
use reqwest::{Client, StatusCode};
|
||||
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||
use reqwest_middleware_cache::managers::CACacheManager;
|
||||
use reqwest_middleware_cache::{Cache, CacheMode};
|
||||
|
||||
use crate::coords::TileCoords;
|
||||
use crate::error::Error;
|
||||
use crate::io::scheduler::IOScheduler;
|
||||
use crate::io::TileRequestID;
|
||||
|
||||
impl From<reqwest::Error> for Error {
|
||||
fn from(err: reqwest::Error) -> Self {
|
||||
Error::Network(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest_middleware::Error> for Error {
|
||||
fn from(err: reqwest_middleware::Error) -> Self {
|
||||
Error::Network(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TokioScheduleMethod {
|
||||
client: ClientWithMiddleware,
|
||||
}
|
||||
|
||||
impl TokioScheduleMethod {
|
||||
/// cache_path: Under which path should we cache requests.
|
||||
pub fn new(cache_path: Option<String>) -> Self {
|
||||
let mut builder = ClientBuilder::new(Client::new());
|
||||
|
||||
if let Some(cache_path) = cache_path {
|
||||
builder = builder.with(Cache {
|
||||
mode: CacheMode::Default,
|
||||
cache_manager: CACacheManager { path: cache_path },
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
client: builder.build(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch(client: &ClientWithMiddleware, url: &str) -> Result<Vec<u8>, Error> {
|
||||
let response = client.get(url).send().await?;
|
||||
if response.status() != StatusCode::OK {
|
||||
return Err(Error::Network("response code not 200".to_string()));
|
||||
}
|
||||
let body = response.bytes().await?;
|
||||
Ok(Vec::from(body.as_ref()))
|
||||
}
|
||||
|
||||
pub fn schedule_tile_request(
|
||||
&self,
|
||||
scheduler: &IOScheduler,
|
||||
request_id: TileRequestID,
|
||||
coords: TileCoords,
|
||||
) {
|
||||
let state = scheduler.new_tessellator_state();
|
||||
let client = self.client.clone();
|
||||
|
||||
tokio::task::spawn(async move {
|
||||
if let Ok(data) = Self::fetch(
|
||||
&client,
|
||||
format!(
|
||||
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z
|
||||
)
|
||||
.as_str(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
state
|
||||
.process_tile(request_id, data.into_boxed_slice())
|
||||
.unwrap();
|
||||
} else {
|
||||
state.tile_unavailable(request_id).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
src/platform/noweb/schedule_method.rs
Normal file
26
src/platform/noweb/schedule_method.rs
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
use reqwest::{Client, StatusCode};
|
||||
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||
use reqwest_middleware_cache::managers::CACacheManager;
|
||||
use reqwest_middleware_cache::{Cache, CacheMode};
|
||||
use std::future::Future;
|
||||
|
||||
use crate::coords::TileCoords;
|
||||
use crate::error::Error;
|
||||
use crate::io::scheduler::IOScheduler;
|
||||
use crate::io::TileRequestID;
|
||||
|
||||
pub struct TokioScheduleMethod;
|
||||
|
||||
impl TokioScheduleMethod {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn schedule<T>(&self, future: T)
|
||||
where
|
||||
T: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
tokio::task::spawn(future);
|
||||
}
|
||||
}
|
||||
69
src/platform/web/http_client.rs
Normal file
69
src/platform/web/http_client.rs
Normal file
@ -0,0 +1,69 @@
|
||||
use std::thread::Thread;
|
||||
|
||||
use log::warn;
|
||||
|
||||
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::{IOScheduler, ScheduleMethod, ThreadLocalTessellatorState};
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::TileRequestID;
|
||||
|
||||
use super::pool::WorkerPool;
|
||||
|
||||
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> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
|
||||
let request = Request::new_with_str_and_init(&url, &opts)?;
|
||||
|
||||
// Get the global scope
|
||||
let global = js_sys::global();
|
||||
assert!(global.is_instance_of::<WorkerGlobalScope>());
|
||||
let scope = global.dyn_into::<WorkerGlobalScope>().unwrap();
|
||||
|
||||
// Call fetch on global scope
|
||||
let maybe_response = JsFuture::from(scope.fetch_with_request(&request)).await?;
|
||||
assert!(maybe_response.is_instance_of::<Response>());
|
||||
let response: Response = maybe_response.dyn_into().unwrap();
|
||||
|
||||
// Get ArrayBuffer
|
||||
let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?;
|
||||
Ok(maybe_array_buffer)
|
||||
}
|
||||
}
|
||||
77
src/platform/web/legacy_webworker_fetcher.rs
Normal file
77
src/platform/web/legacy_webworker_fetcher.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use std::panic;
|
||||
use std::thread::Thread;
|
||||
|
||||
use log::error;
|
||||
use log::info;
|
||||
use log::warn;
|
||||
use log::Level;
|
||||
use winit::dpi::LogicalSize;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
use super::schedule_method::WebWorkerPoolScheduleMethod;
|
||||
use console_error_panic_hook;
|
||||
pub use instant::Instant;
|
||||
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
|
||||
use style_spec::source::TileAddressingScheme;
|
||||
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::IOScheduler;
|
||||
use crate::io::scheduler::ScheduleMethod;
|
||||
use crate::io::scheduler::ThreadLocalTessellatorState;
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::TileRequestID;
|
||||
use crate::MapBuilder;
|
||||
|
||||
use super::pool::WorkerPool;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
fn schedule_tile_request(url: &str, request_id: u32);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn new_tessellator_state(scheduler_ptr: *mut IOScheduler) -> *mut ThreadLocalTessellatorState {
|
||||
let scheduler: Box<IOScheduler> = unsafe { Box::from_raw(scheduler_ptr) };
|
||||
let tessellator_state = Box::new(scheduler.new_tessellator_state());
|
||||
let tessellator_state_ptr = Box::into_raw(tessellator_state);
|
||||
// Call forget such that scheduler does not get deallocated
|
||||
std::mem::forget(scheduler);
|
||||
return tessellator_state_ptr;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn tessellate_layers(
|
||||
tessellator_state_ptr: *mut ThreadLocalTessellatorState,
|
||||
request_id: u32,
|
||||
data: Box<[u8]>,
|
||||
) {
|
||||
let tessellator_state: Box<ThreadLocalTessellatorState> =
|
||||
unsafe { Box::from_raw(tessellator_state_ptr) };
|
||||
|
||||
tessellator_state.process_tile(request_id, data).unwrap();
|
||||
|
||||
// Call forget such that scheduler does not get deallocated
|
||||
std::mem::forget(tessellator_state);
|
||||
}
|
||||
|
||||
pub fn request_tile(request_id: TileRequestID, coords: TileCoords) {
|
||||
schedule_tile_request(
|
||||
format!(
|
||||
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z,
|
||||
)
|
||||
.as_str(),
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
@ -1,5 +1,3 @@
|
||||
mod pool;
|
||||
|
||||
use std::panic;
|
||||
|
||||
use log::error;
|
||||
@ -10,14 +8,9 @@ use winit::event_loop::EventLoop;
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
use crate::io::scheduler::IOScheduler;
|
||||
use crate::io::scheduler::ScheduleMethod;
|
||||
use crate::io::scheduler::ThreadLocalTessellatorState;
|
||||
use crate::MapBuilder;
|
||||
use console_error_panic_hook;
|
||||
pub use instant::Instant;
|
||||
use scheduler::WebWorkerPoolScheduleMethod;
|
||||
use scheduler::WebWorkerScheduleMethod;
|
||||
use schedule_method::WebWorkerPoolScheduleMethod;
|
||||
use style_spec::source::TileAddressingScheme;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
@ -25,6 +18,16 @@ use wasm_bindgen::JsCast;
|
||||
use web_sys::Window as WebSysWindow;
|
||||
use web_sys::Worker;
|
||||
|
||||
use crate::io::scheduler::IOScheduler;
|
||||
use crate::io::scheduler::ScheduleMethod;
|
||||
use crate::io::scheduler::ThreadLocalTessellatorState;
|
||||
use crate::MapBuilder;
|
||||
|
||||
pub mod http_client;
|
||||
pub mod legacy_webworker_fetcher;
|
||||
mod pool;
|
||||
pub mod schedule_method;
|
||||
|
||||
// WebGPU
|
||||
#[cfg(not(feature = "web-webgl"))]
|
||||
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm;
|
||||
@ -41,15 +44,6 @@ pub fn wasm_bindgen_start() {
|
||||
panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_scheduler() -> *mut IOScheduler {
|
||||
let scheduler = Box::new(IOScheduler::new(ScheduleMethod::WebWorker(
|
||||
WebWorkerScheduleMethod::new(),
|
||||
)));
|
||||
let scheduler_ptr = Box::into_raw(scheduler);
|
||||
return scheduler_ptr;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_pool_scheduler(new_worker: js_sys::Function) -> *mut IOScheduler {
|
||||
let scheduler = Box::new(IOScheduler::new(ScheduleMethod::WebWorkerPool(
|
||||
@ -92,167 +86,3 @@ pub async fn run(scheduler_ptr: *mut IOScheduler) {
|
||||
|
||||
// std::mem::forget(scheduler);
|
||||
}
|
||||
|
||||
pub mod scheduler {
|
||||
use super::pool::WorkerPool;
|
||||
use crate::coords::{TileCoords, WorldTileCoords};
|
||||
use crate::error::Error;
|
||||
use crate::io::scheduler::{IOScheduler, ScheduleMethod, ThreadLocalTessellatorState};
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::TileRequestID;
|
||||
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
|
||||
use log::warn;
|
||||
use std::thread::Thread;
|
||||
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};
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
pub fn schedule_tile_request(url: &str, request_id: u32);
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn new_tessellator_state(
|
||||
scheduler_ptr: *mut IOScheduler,
|
||||
) -> *mut ThreadLocalTessellatorState {
|
||||
let scheduler: Box<IOScheduler> = unsafe { Box::from_raw(scheduler_ptr) };
|
||||
let tessellator_state = Box::new(scheduler.new_tessellator_state());
|
||||
let tessellator_state_ptr = Box::into_raw(tessellator_state);
|
||||
// Call forget such that scheduler does not get deallocated
|
||||
std::mem::forget(scheduler);
|
||||
return tessellator_state_ptr;
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn tessellate_layers(
|
||||
tessellator_state_ptr: *mut ThreadLocalTessellatorState,
|
||||
request_id: u32,
|
||||
data: Box<[u8]>,
|
||||
) {
|
||||
let tessellator_state: Box<ThreadLocalTessellatorState> =
|
||||
unsafe { Box::from_raw(tessellator_state_ptr) };
|
||||
|
||||
tessellator_state.process_tile(request_id, data).unwrap();
|
||||
|
||||
// Call forget such that scheduler does not get deallocated
|
||||
std::mem::forget(tessellator_state);
|
||||
}
|
||||
|
||||
pub struct WebWorkerScheduleMethod;
|
||||
|
||||
impl WebWorkerScheduleMethod {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn schedule_tile_request(
|
||||
&self,
|
||||
_scheduler: &IOScheduler,
|
||||
request_id: TileRequestID,
|
||||
coords: TileCoords,
|
||||
) {
|
||||
schedule_tile_request(
|
||||
format!(
|
||||
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z,
|
||||
)
|
||||
.as_str(),
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WebWorkerPoolScheduleMethod {
|
||||
pool: WorkerPool,
|
||||
}
|
||||
|
||||
impl WebWorkerPoolScheduleMethod {
|
||||
pub fn new(new_worker: js_sys::Function) -> Self {
|
||||
Self {
|
||||
pool: WorkerPool::new(
|
||||
4,
|
||||
Box::new(move || {
|
||||
new_worker
|
||||
.call0(&JsValue::undefined())
|
||||
.unwrap()
|
||||
.dyn_into::<Worker>()
|
||||
.unwrap()
|
||||
}),
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch(url: &str) -> Result<JsValue, JsValue> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("GET");
|
||||
|
||||
let request = Request::new_with_str_and_init(&url, &opts)?;
|
||||
|
||||
// Get the global scope
|
||||
let global = js_sys::global();
|
||||
assert!(global.is_instance_of::<WorkerGlobalScope>());
|
||||
let scope = global.dyn_into::<WorkerGlobalScope>().unwrap();
|
||||
|
||||
// Call fetch on global scope
|
||||
let maybe_response = JsFuture::from(scope.fetch_with_request(&request)).await?;
|
||||
assert!(maybe_response.is_instance_of::<Response>());
|
||||
let response: Response = maybe_response.dyn_into().unwrap();
|
||||
|
||||
// Get ArrayBuffer
|
||||
let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?;
|
||||
Ok(maybe_array_buffer)
|
||||
}
|
||||
|
||||
pub fn schedule_tile_request(
|
||||
&self,
|
||||
scheduler: &IOScheduler,
|
||||
request_id: TileRequestID,
|
||||
coords: TileCoords,
|
||||
) {
|
||||
let state = scheduler.new_tessellator_state();
|
||||
|
||||
self.pool
|
||||
.run(move || {
|
||||
wasm_bindgen_futures::future_to_promise(async move {
|
||||
let string = format!(
|
||||
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z,
|
||||
);
|
||||
if let Ok(maybe_array_buffer) = Self::fetch(string.as_str()).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());
|
||||
|
||||
state
|
||||
.process_tile(request_id, output.into_boxed_slice())
|
||||
.unwrap();
|
||||
} else {
|
||||
state.tile_unavailable(request_id).unwrap();
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,11 +2,13 @@
|
||||
//! web workers which can be used to execute work.
|
||||
//! Adopted from [wasm-bindgen example](https://github.com/rustwasm/wasm-bindgen/blob/0eba2efe45801b71f8873bc368c58a8ed8e894ff/examples/raytrace-parallel/src/pool.rs)
|
||||
|
||||
use js_sys::Promise;
|
||||
use log::{info, warn};
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
|
||||
use log::{info, warn};
|
||||
|
||||
use js_sys::Promise;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
55
src/platform/web/schedule_method.rs
Normal file
55
src/platform/web/schedule_method.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use std::thread::Thread;
|
||||
|
||||
use log::warn;
|
||||
|
||||
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::{IOScheduler, ScheduleMethod, ThreadLocalTessellatorState};
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::TileRequestID;
|
||||
|
||||
use super::pool::WorkerPool;
|
||||
|
||||
pub struct WebWorkerPoolScheduleMethod {
|
||||
pool: WorkerPool,
|
||||
}
|
||||
|
||||
impl WebWorkerPoolScheduleMethod {
|
||||
pub fn new(new_worker: js_sys::Function) -> Self {
|
||||
Self {
|
||||
pool: WorkerPool::new(
|
||||
4,
|
||||
Box::new(move || {
|
||||
new_worker
|
||||
.call0(&JsValue::undefined())
|
||||
.unwrap()
|
||||
.dyn_into::<Worker>()
|
||||
.unwrap()
|
||||
}),
|
||||
)
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn schedule<T>(&self, future_factory: impl (FnOnce() -> T) + Send + 'static)
|
||||
where
|
||||
T: std::future::Future + 'static,
|
||||
T::Output: Send + 'static,
|
||||
{
|
||||
self.pool
|
||||
.run(move || {
|
||||
wasm_bindgen_futures::future_to_promise(async move {
|
||||
future_factory().await;
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ use style_spec::Style;
|
||||
|
||||
use crate::coords::{ViewRegion, TILE_SIZE};
|
||||
use crate::io::scheduler::IOScheduler;
|
||||
use crate::io::LayerTessellateResult;
|
||||
use crate::io::LayerTessellateMessage;
|
||||
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool, IndexEntry};
|
||||
use crate::render::camera;
|
||||
@ -487,7 +487,7 @@ impl RenderState {
|
||||
for style_layer in &self.style.layers {
|
||||
let source_layer = style_layer.source_layer.as_ref().unwrap();
|
||||
|
||||
if let Some(result) = available_layers
|
||||
if let Some(message) = available_layers
|
||||
.iter()
|
||||
.find(|layer| source_layer.as_str() == layer.layer_name())
|
||||
{
|
||||
@ -497,11 +497,11 @@ impl RenderState {
|
||||
.and_then(|paint| paint.get_color())
|
||||
.map(|color| color.into());
|
||||
|
||||
match result {
|
||||
LayerTessellateResult::UnavailableLayer { coords: _, .. } => {
|
||||
match message {
|
||||
LayerTessellateMessage::UnavailableLayer { coords: _, .. } => {
|
||||
/*self.buffer_pool.mark_layer_unavailable(*coords);*/
|
||||
}
|
||||
LayerTessellateResult::TessellatedLayer {
|
||||
LayerTessellateMessage::TessellatedLayer {
|
||||
coords,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
|
||||
47
web/index.ts
47
web/index.ts
@ -1,4 +1,4 @@
|
||||
import init, {create_pool_scheduler, create_scheduler, new_tessellator_state, run} from "./dist/libs/mapr"
|
||||
import init, {create_pool_scheduler, new_tessellator_state, run} from "./dist/libs/mapr"
|
||||
import {Spector} from "spectorjs"
|
||||
import {WebWorkerMessageType} from "./types"
|
||||
|
||||
@ -72,28 +72,7 @@ const registerServiceWorker = () => {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const start = async () => {
|
||||
if (!checkRequirements()) {
|
||||
return
|
||||
}
|
||||
|
||||
registerServiceWorker()
|
||||
|
||||
preventDefaultTouchActions();
|
||||
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: true})
|
||||
await init(undefined, memory)
|
||||
/*const schedulerPtr = create_pool_scheduler(() => {
|
||||
return new Worker(new URL('./pool_worker.ts', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
})*/
|
||||
|
||||
const schedulerPtr = create_scheduler()
|
||||
|
||||
const setupLegacyWebWorker = (schedulerPtr: number, memory: WebAssembly.Memory) => {
|
||||
let WORKER_COUNT = 4
|
||||
const createWorker = (id: number) => {
|
||||
const worker = new Worker(new URL('./worker.ts', import.meta.url), {
|
||||
@ -119,6 +98,28 @@ const start = async () => {
|
||||
request_id
|
||||
} as WebWorkerMessageType)
|
||||
}
|
||||
}
|
||||
|
||||
const start = async () => {
|
||||
if (!checkRequirements()) {
|
||||
return
|
||||
}
|
||||
|
||||
registerServiceWorker()
|
||||
|
||||
preventDefaultTouchActions();
|
||||
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: true})
|
||||
await init(undefined, memory)
|
||||
const schedulerPtr = create_pool_scheduler(() => {
|
||||
return new Worker(new URL('./pool_worker.ts', import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
})
|
||||
|
||||
setupLegacyWebWorker(schedulerPtr, memory)
|
||||
|
||||
await run(schedulerPtr)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user