Remove TileFetcher and fix compilation on web and non-web

This commit is contained in:
Maximilian Ammann 2022-03-12 18:52:29 +01:00
parent cc22e55133
commit a4d6469c47
10 changed files with 179 additions and 201 deletions

View File

@ -12,37 +12,6 @@ use vector_tile::tile::Layer;
pub mod scheduler;
pub mod static_tile_fetcher;
pub mod tile_cache;
pub mod web_tile_fetcher;
pub struct HttpFetcherConfig {
/// Under which path should we cache requests.
pub cache_path: String,
}
impl Default for HttpFetcherConfig {
fn default() -> Self {
Self {
cache_path: ".".to_string(),
}
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait HttpFetcher {
fn new(config: HttpFetcherConfig) -> Self;
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error>;
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
pub trait TileFetcher {
fn new(config: HttpFetcherConfig) -> Self;
async fn fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, Error>;
fn sync_fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, Error>;
}
#[derive(Clone)]
pub enum TileResult {

View File

@ -13,14 +13,46 @@ use vector_tile::tile::Layer;
/// Describes through which channels work-requests travel. It describes the flow of work.
use crate::coords::{TileCoords, WorldTileCoords};
use crate::io::tile_cache::TileCache;
use crate::io::web_tile_fetcher::WebTileFetcher;
use crate::io::{
HttpFetcherConfig, LayerResult, TileFetcher, TileRequest, TileRequestID, TileResult,
};
use crate::io::{LayerResult, TileRequest, TileRequestID, TileResult};
use crate::render::ShaderVertex;
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer, Tessellated};
pub enum ScheduleMethod {
#[cfg(not(any(
target_os = "android",
all(target_arch = "aarch64", not(target_os = "android")),
target_arch = "wasm32"
)))]
Tokio(crate::platform::TokioScheduleMethod),
#[cfg(target_arch = "wasm32")]
WebWorker(crate::platform::WebWorkerScheduleMethod),
}
impl ScheduleMethod {
pub fn schedule_tile_request(
&self,
scheduler: &IOScheduler,
request_id: TileRequestID,
coords: TileCoords,
) {
match self {
#[cfg(not(any(
target_os = "android",
all(target_arch = "aarch64", not(target_os = "android")),
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)
}
}
}
}
pub struct ThreadLocalTessellatorState {
tile_request_state: Arc<Mutex<TileRequestState>>,
layer_result_sender: Sender<LayerResult>,
@ -96,6 +128,7 @@ pub struct IOScheduler {
layer_result_receiver: Receiver<LayerResult>,
tile_request_state: Arc<Mutex<TileRequestState>>,
tile_cache: TileCache,
schedule_method: ScheduleMethod,
}
const _: () = {
@ -113,13 +146,14 @@ impl Drop for IOScheduler {
}
impl IOScheduler {
pub fn create() -> Self {
pub fn new(schedule_method: ScheduleMethod) -> Self {
let (layer_result_sender, layer_result_receiver) = channel();
Self {
layer_result_sender,
layer_result_receiver,
tile_request_state: Arc::new(Mutex::new(TileRequestState::new())),
tile_cache: TileCache::new(),
schedule_method,
}
}
@ -154,32 +188,10 @@ 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(tile_request.clone()) {
info!("new tile request: {}", &coords);
let tile_coords = coords.into_tile(TileAdressingScheme::TMS);
/* crate::platform::schedule_tile_request(
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(),
id,
);*/
let state = self.new_tessellator_state();
tokio::task::spawn(async move {
let fetcher = WebTileFetcher::new(HttpFetcherConfig {
cache_path: "/tmp/mapr-cache".to_string(),
});
if let Ok(data) = fetcher.fetch_tile(&tile_coords).await {
state
.tessellate_layers(id, data.into_boxed_slice())
.unwrap();
}
});
self.schedule_method
.schedule_tile_request(self, id, tile_coords)
}
break;

View File

@ -1,15 +1,11 @@
use std::concat;
use std::env;
use async_trait::async_trait;
use include_dir::{include_dir, Dir};
use log::error;
use crate::coords::TileCoords;
use crate::error::Error;
use crate::io::HttpFetcherConfig;
use super::TileFetcher;
static TILES: Dir = include_dir!("$OUT_DIR/extracted-tiles");
@ -19,12 +15,8 @@ impl StaticTileFetcher {
pub fn get_source_path() -> &'static str {
concat!(env!("OUT_DIR"), "/extracted-tiles")
}
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl TileFetcher for StaticTileFetcher {
fn new(_config: HttpFetcherConfig) -> Self {
fn new() -> Self {
Self {}
}
@ -49,9 +41,10 @@ impl TileFetcher for StaticTileFetcher {
#[cfg(test)]
mod tests {
use style_spec::source::TileAdressingScheme;
use crate::coords::WorldTileCoords;
use crate::io::{HttpFetcherConfig, TileFetcher};
use style_spec::source::TileAdressingScheme;
use super::StaticTileFetcher;

View File

@ -1,49 +0,0 @@
use crate::coords::TileCoords;
use crate::error::Error;
use crate::io::{HttpFetcher, HttpFetcherConfig, TileFetcher};
use crate::platform::PlatformHttpFetcher;
use async_trait::async_trait;
pub struct WebTileFetcher {
http_fetcher: PlatformHttpFetcher,
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl TileFetcher for WebTileFetcher {
fn new(config: HttpFetcherConfig) -> Self {
Self {
http_fetcher: PlatformHttpFetcher::new(config),
}
}
async fn fetch_tile(&self, coords: &TileCoords) -> Result<Vec<u8>, Error> {
self.http_fetcher
.fetch(
format!(
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
x = coords.x,
y = coords.y,
z = coords.z
)
.as_str(),
)
.await
}
fn sync_fetch_tile(&self, _coords: &TileCoords) -> Result<Vec<u8>, Error> {
panic!("Unable to fetch sync from the web!");
}
}
#[cfg(test)]
mod tests {
use super::WebTileFetcher;
use crate::io::{HttpFetcherConfig, TileFetcher};
#[tokio::test]
async fn test_tiles_available() {
let fetcher = WebTileFetcher::new(HttpFetcherConfig::default());
assert!(fetcher.fetch_tile(&(0, 0, 0).into()).await.is_ok()); // World overview
}
}

View File

@ -19,7 +19,7 @@ pub async fn main() {
.build(&event_loop)
.unwrap();
let mut scheduler = IOScheduler::create();
let mut scheduler = IOScheduler::new();
let download_tessellate_loop = scheduler.take_download_loop();
let join_handle = task::spawn_blocking(move || {

View File

@ -20,7 +20,7 @@ pub async fn mapr_apple_main() {
.build(&event_loop)
.unwrap();
let mut scheduler = IOScheduler::create();
let mut scheduler = IOScheduler::new();
let download_tessellate_loop = scheduler.take_download_loop();
let join_handle = task::spawn_blocking(move || {

View File

@ -1,7 +1,8 @@
//! Module which is used if android, apple and web is not used.
use crate::io::scheduler::IOScheduler;
use crate::io::scheduler::{IOScheduler, ScheduleMethod};
use crate::main_loop;
use crate::platform::TokioScheduleMethod;
use log::error;
pub use std::time::Instant;
use tokio::runtime::Handle;
@ -22,7 +23,9 @@ pub async fn mapr_generic_main() {
.build(&event_loop)
.unwrap();
let mut scheduler = IOScheduler::create();
let mut scheduler = IOScheduler::new(ScheduleMethod::Tokio(TokioScheduleMethod::new(
"/tmp/mapr_cache".to_string(),
)));
/* let join_handle = task::spawn_blocking(move || {
Handle::current().block_on(async move {

View File

@ -1,5 +1,6 @@
//! Module which is used target platform is not web related.
use crate::coords::TileCoords;
use async_trait::async_trait;
use reqwest::{Client, StatusCode};
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
@ -7,7 +8,8 @@ use reqwest_middleware_cache::managers::CACacheManager;
use reqwest_middleware_cache::{Cache, CacheMode};
use crate::error::Error;
use crate::io::{HttpFetcher, HttpFetcherConfig};
use crate::io::scheduler::IOScheduler;
use crate::io::{TileRequest, TileRequestID};
impl From<reqwest::Error> for Error {
fn from(err: reqwest::Error) -> Self {
@ -21,23 +23,20 @@ impl From<reqwest_middleware::Error> for Error {
}
}
pub struct PlatformHttpFetcher {
pub struct TokioScheduleMethod {
client: ClientWithMiddleware,
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl HttpFetcher for PlatformHttpFetcher {
fn new(config: HttpFetcherConfig) -> Self {
impl TokioScheduleMethod {
/// cache_path: Under which path should we cache requests.
pub fn new(cache_path: String) -> Self {
let mut builder = ClientBuilder::new(Client::new());
// FIXME: Cache only works on desktop so far
if cfg!(not(any(target_os = "android", target_arch = "aarch64"))) {
builder = builder.with(Cache {
mode: CacheMode::Default,
cache_manager: CACacheManager {
path: config.cache_path,
},
cache_manager: CACacheManager { path: cache_path },
});
}
@ -46,12 +45,43 @@ impl HttpFetcher for PlatformHttpFetcher {
}
}
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
let response = self.client.get(url).send().await?;
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
.tessellate_layers(request_id, data.into_boxed_slice())
.unwrap();
} else {
// TODO Error
}
});
}
}

View File

@ -1,55 +0,0 @@
use crate::error::Error;
use crate::io::{HttpFetcher, HttpFetcherConfig};
use async_trait::async_trait;
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response, WorkerGlobalScope};
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 PlatformHttpFetcher;
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl HttpFetcher for PlatformHttpFetcher {
fn new(_config: HttpFetcherConfig) -> Self {
Self {}
}
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
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?;
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)
}
}

View File

@ -1,5 +1,3 @@
mod http_fetcher;
use std::panic;
use log::error;
@ -12,10 +10,17 @@ use winit::window::{Window, WindowBuilder};
use console_error_panic_hook;
pub use instant::Instant;
use style_spec::source::TileAdressingScheme;
use wasm_bindgen::prelude::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use web_sys::Window as WebSysWindow;
use crate::coords::{TileCoords, WorldTileCoords};
use crate::io::scheduler::{IOScheduler, ScheduleMethod, ThreadLocalTessellatorState};
use crate::io::tile_cache::TileCache;
use crate::io::TileRequestID;
// WebGPU
#[cfg(not(feature = "web-webgl"))]
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8Unorm;
@ -24,13 +29,6 @@ pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8
#[cfg(feature = "web-webgl")]
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
use crate::coords::{TileCoords, WorldTileCoords};
use crate::io::scheduler::{IOScheduler, ThreadLocalTessellatorState, TileResult};
use crate::io::tile_cache::TileCache;
pub use http_fetcher::PlatformHttpFetcher;
use style_spec::source::TileAdressingScheme;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
if let Err(_) = console_log::init_with_level(Level::Info) {
@ -46,7 +44,9 @@ extern "C" {
#[wasm_bindgen]
pub fn create_scheduler() -> *mut IOScheduler {
let scheduler = Box::new(IOScheduler::create());
let scheduler = Box::new(IOScheduler::new(ScheduleMethod::WebWorker(
WebWorkerScheduleMethod::new(),
)));
let scheduler_ptr = Box::into_raw(scheduler);
return scheduler_ptr;
}
@ -61,6 +61,32 @@ pub fn new_tessellator_state(workflow_ptr: *mut IOScheduler) -> *mut ThreadLocal
return tessellator_state_ptr;
}
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,
)
}
}
#[wasm_bindgen]
pub fn tessellate_layers(
tessellator_state_ptr: *mut ThreadLocalTessellatorState,
@ -70,7 +96,9 @@ pub fn tessellate_layers(
let tessellator_state: Box<ThreadLocalTessellatorState> =
unsafe { Box::from_raw(tessellator_state_ptr) };
tessellator_state.tessellate_layers(request_id, data);
tessellator_state
.tessellate_layers(request_id, data)
.unwrap();
// Call forget such that workflow does not get deallocated
std::mem::forget(tessellator_state);
@ -105,3 +133,50 @@ pub async fn run(workflow_ptr: *mut IOScheduler) {
crate::main_loop::setup(window, event_loop, workflow).await;
// std::mem::forget(workflow);
}
/*use crate::error::Error;
use js_sys::{ArrayBuffer, Error as JSError, Uint8Array};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, RequestMode, Response, WorkerGlobalScope};
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())
}
}
async fn fetch(&self, url: &str) -> Result<Vec<u8>, Error> {
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?;
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)
}*/