Fix web compilation issues

This commit is contained in:
Maximilian Ammann 2022-10-30 22:10:08 +01:00
parent 8645a506ea
commit 8af27b29cf
14 changed files with 208 additions and 175 deletions

View File

@ -4,7 +4,10 @@ use criterion::{criterion_group, criterion_main, Criterion};
use maplibre::{
coords::{WorldTileCoords, ZoomLevel},
error::Error,
headless::{utils::HeadlessPipelineProcessor, HeadlessEnvironment, HeadlessMapWindowConfig},
headless::{
create_headless_renderer, environment::HeadlessEnvironment, map::HeadlessMap,
window::HeadlessMapWindowConfig,
},
io::{
apc::SchedulerAsyncProcedureCall,
pipeline::{PipelineContext, Processable},
@ -12,43 +15,32 @@ use maplibre::{
tile_pipelines::build_vector_tile_pipeline,
TileRequest,
},
kernel::{Kernel, KernelBuilder},
platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler},
render::settings::{RendererSettings, TextureFormat},
render::{
builder::{InitializedRenderer, RenderBuilder},
settings::{RendererSettings, TextureFormat},
},
style::Style,
window::WindowSize,
MapBuilder,
};
fn headless_render(c: &mut Criterion) {
c.bench_function("headless_render", |b| {
let mut map = run_multithreaded(async {
let client = ReqwestHttpClient::new(None);
let (mut map, tile) = run_multithreaded(async {
let (kernel, renderer) = create_headless_renderer(1000, None).await;
let style = Style::default();
let mut map = HeadlessMap::new(style, renderer, kernel).unwrap();
let mut map = MapBuilder::<
HeadlessEnvironment<_, _, _, SchedulerAsyncProcedureCall<_, _>>,
>::new()
.with_map_window_config(HeadlessMapWindowConfig {
size: WindowSize::new(1000, 1000).unwrap(),
})
.with_http_client(client.clone())
.with_apc(SchedulerAsyncProcedureCall::new(
client,
TokioScheduler::new(),
))
.with_scheduler(TokioScheduler::new())
.with_renderer_settings(RendererSettings {
texture_format: TextureFormat::Rgba8UnormSrgb,
..RendererSettings::default()
})
.build()
.initialize_headless()
.await;
map.map_schedule
.fetch_process(&WorldTileCoords::from((0, 0, ZoomLevel::default())))
let tile = map
.fetch_tile(
WorldTileCoords::from((0, 0, ZoomLevel::default())),
&["water"],
)
.await
.expect("Failed to fetch and process!");
map
(map, tile)
});
b.to_async(
@ -58,7 +50,7 @@ fn headless_render(c: &mut Criterion) {
.unwrap(),
)
.iter(|| {
match map.map_schedule_mut().update_and_redraw() {
match map.render_tile(tile.clone()) {
Ok(_) => {}
Err(Error::Render(e)) => {
eprintln!("{}", e);

View File

@ -1,7 +1,7 @@
use maplibre::{
coords::{LatLon, WorldTileCoords},
error::Error,
headless::{map::HeadlessMap, window::HeadlessMapWindowConfig},
headless::{create_headless_renderer, map::HeadlessMap, window::HeadlessMapWindowConfig},
io::apc::SchedulerAsyncProcedureCall,
kernel::KernelBuilder,
platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler},
@ -17,24 +17,7 @@ use maplibre_winit::WinitEnvironment;
use tile_grid::{extent_wgs84_to_merc, Extent, GridIterator};
pub async fn run_headless(tile_size: u32, min: LatLon, max: LatLon) {
let client = ReqwestHttpClient::new(None);
let kernel = KernelBuilder::new()
.with_map_window_config(HeadlessMapWindowConfig::new(
WindowSize::new(tile_size, tile_size).unwrap(),
))
.with_http_client(client.clone())
.with_apc(SchedulerAsyncProcedureCall::new(
client,
TokioScheduler::new(),
))
.with_scheduler(TokioScheduler::new())
.build();
let renderer = RenderBuilder::new()
.build()
.initialize_headless_with(&kernel)
.await
.expect("Failed to initialize renderer");
let (kernel, renderer) = create_headless_renderer(tile_size, None).await;
let style = Style::default();

View File

@ -18,7 +18,7 @@ wasm-bindgen = "0.2.81"
wasm-bindgen-futures = "0.4.31"
[dependencies]
maplibre = { path = "../maplibre", version = "0.0.2" }
maplibre = { path = "../maplibre", version = "0.0.2", default-features = false }
winit = { version = "0.27.2", default-features = false }
cgmath = "0.18.0"
instant = { version = "0.1.12", features = ["wasm-bindgen"] } # TODO: Untrusted dependency

View File

@ -38,35 +38,6 @@ pub use noweb::*;
#[cfg(target_arch = "wasm32")]
pub use web::*;
#[cfg(not(target_arch = "wasm32"))]
pub struct WinitMapWindowConfig<ET> {
title: String,
phantom_et: PhantomData<ET>,
}
#[cfg(not(target_arch = "wasm32"))]
impl<ET> WinitMapWindowConfig<ET> {
pub fn new(title: String) -> Self {
Self {
title,
phantom_et: Default::default(),
}
}
}
#[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 }
}
}
pub struct WinitMapWindow<ET: 'static> {
window: RawWinitWindow,
event_loop: Option<WinitEventLoop<ET>>,
@ -78,6 +49,22 @@ impl<ET> WinitMapWindow<ET> {
}
}
impl<ET> HeadedMapWindow for WinitMapWindow<ET> {
type RawWindow = RawWinitWindow;
fn raw(&self) -> &Self::RawWindow {
&self.window
}
fn request_redraw(&self) {
self.window.request_redraw()
}
fn id(&self) -> u64 {
self.window.id().into()
}
}
pub struct WinitEventLoop<ET: 'static> {
event_loop: RawWinitEventLoop<ET>,
}

View File

@ -3,6 +3,8 @@
//! * Platform Events like suspend/resume
//! * Render a new frame
use std::marker::PhantomData;
use maplibre::{
event_loop::EventLoop,
headless::map::HeadlessMap,
@ -16,10 +18,25 @@ use maplibre::{
};
use winit::window::WindowBuilder;
use super::{RawWinitEventLoop, RawWinitWindow, WinitMapWindow, WinitMapWindowConfig};
use super::{RawWinitEventLoop, RawWinitWindow, WinitMapWindow};
use crate::{WinitEnvironment, WinitEventLoop};
impl<T> MapWindow for WinitMapWindow<T> {
pub struct WinitMapWindowConfig<ET> {
title: String,
phantom_et: PhantomData<ET>,
}
impl<ET> WinitMapWindowConfig<ET> {
pub fn new(title: String) -> Self {
Self {
title,
phantom_et: Default::default(),
}
}
}
impl<ET> MapWindow for WinitMapWindow<ET> {
fn size(&self) -> WindowSize {
let size = self.window.inner_size();
#[cfg(target_os = "android")]
@ -34,21 +51,6 @@ impl<T> MapWindow for WinitMapWindow<T> {
window_size
}
}
impl<T> HeadedMapWindow for WinitMapWindow<T> {
type RawWindow = RawWinitWindow;
fn raw(&self) -> &Self::RawWindow {
&self.window
}
fn request_redraw(&self) {
self.window.request_redraw()
}
fn id(&self) -> u64 {
self.window.id().into()
}
}
impl<ET: 'static> MapWindowConfig for WinitMapWindowConfig<ET> {
type MapWindow = WinitMapWindow<ET>;

View File

@ -1,42 +1,54 @@
use std::marker::PhantomData;
use maplibre::window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize};
use winit::{platform::web::WindowBuilderExtWebSys, window::WindowBuilder};
use super::{RawWinitEventLoop, RawWinitWindow, WinitMapWindow, WinitMapWindowConfig};
use super::{RawWinitEventLoop, RawWinitWindow, WinitMapWindow};
use crate::WinitEventLoop;
impl MapWindowConfig for WinitMapWindowConfig {
type MapWindow = WinitMapWindow;
pub struct WinitMapWindowConfig<ET> {
canvas_id: String,
phantom_et: PhantomData<ET>,
}
impl<ET: 'static> WinitMapWindowConfig<ET> {
pub fn new(canvas_id: String) -> Self {
Self {
canvas_id,
phantom_et: Default::default(),
}
}
}
impl<ET: 'static> MapWindowConfig for WinitMapWindowConfig<ET> {
type MapWindow = WinitMapWindow<ET>;
fn create(&self) -> Self::MapWindow {
let event_loop = RawWinitEventLoop::new();
let raw_event_loop = winit::event_loop::EventLoopBuilder::<ET>::with_user_event().build();
let window: winit::window::Window = WindowBuilder::new()
.with_canvas(Some(get_canvas(&self.canvas_id)))
.build(&event_loop)
.build(&raw_event_loop)
.unwrap();
let size = get_body_size().unwrap();
window.set_inner_size(size);
Self::MapWindow {
window,
event_loop: Some(event_loop),
event_loop: Some(WinitEventLoop {
event_loop: raw_event_loop,
}),
}
}
}
impl MapWindow for WinitMapWindow {
impl<ET: 'static> MapWindow for WinitMapWindow<ET> {
fn size(&self) -> WindowSize {
let size = self.window.inner_size();
WindowSize::new(size.width, size.height).expect("failed to get window dimensions.")
}
}
impl HeadedMapWindow for WinitMapWindow {
type RawWindow = RawWinitWindow;
fn raw(&self) -> &Self::RawWindow {
&self.window
}
}
pub fn get_body_size() -> Option<winit::dpi::LogicalSize<i32>> {
let web_window: web_sys::Window = web_sys::window().unwrap();

View File

@ -1,6 +1,41 @@
use crate::{
headless::{environment::HeadlessEnvironment, window::HeadlessMapWindowConfig},
io::apc::SchedulerAsyncProcedureCall,
kernel::{Kernel, KernelBuilder},
platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler},
render::{builder::RenderBuilder, Renderer},
window::WindowSize,
};
mod graph_node;
mod stage;
pub mod environment;
pub mod map;
pub mod window;
pub async fn create_headless_renderer(
tile_size: u32,
cache_path: Option<String>,
) -> (Kernel<HeadlessEnvironment>, Renderer) {
let client = ReqwestHttpClient::new(cache_path);
let kernel = KernelBuilder::new()
.with_map_window_config(HeadlessMapWindowConfig::new(
WindowSize::new(tile_size, tile_size).unwrap(),
))
.with_http_client(client.clone())
.with_apc(SchedulerAsyncProcedureCall::new(
client,
TokioScheduler::new(),
))
.with_scheduler(TokioScheduler::new())
.build();
let renderer = RenderBuilder::new()
.build()
.initialize_headless_with(&kernel)
.await
.expect("Failed to initialize renderer");
(kernel, renderer)
}

View File

@ -11,6 +11,7 @@ use crate::{
};
/// A layer which is stored for future use.
#[derive(Clone)]
pub enum StoredLayer {
UnavailableLayer {
coords: WorldTileCoords,
@ -41,7 +42,7 @@ impl StoredLayer {
}
}
#[derive(Eq, PartialEq)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum TileStatus {
Pending,
Failed,
@ -49,6 +50,7 @@ pub enum TileStatus {
}
/// Stores multiple [StoredLayers](StoredLayer).
#[derive(Clone)]
pub struct StoredTile {
coords: WorldTileCoords,
layers: Vec<StoredLayer>,

View File

@ -64,4 +64,8 @@ impl<E: Environment> Map<E> {
pub fn context_mut(&mut self) -> &mut MapContext {
&mut self.map_context
}
pub fn kernel(&self) -> &Rc<Kernel<E>> {
&self.kernel
}
}

View File

@ -23,6 +23,7 @@ impl From<reqwest_middleware::Error> for Error {
impl ReqwestHttpClient {
/// cache_path: Under which path should we cache requests.
// TODO: Use Into<Path> instead of String
pub fn new(cache_path: Option<String>) -> Self {
let mut builder = reqwest_middleware::ClientBuilder::new(Client::new());

View File

@ -237,6 +237,7 @@ const esbuild = async (name, globalName = undefined) => {
}
const start = async () => {
try {
console.log("Creating WASM...")
wasmPack();
@ -257,10 +258,9 @@ const start = async () => {
console.log("Emitting TypeScript types...")
emitTypeScript();
} catch (e) {
console.error("Failed to start building: " + e.message)
}
}
try {
const _ = start()
} catch (e) {
console.log("Failed to start building: " + e.message)
}
const _ = start()

View File

@ -31,18 +31,19 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY / PAGES, shared: true})
await maplibre.default(wasmPath, memory)
maplibre.run(await maplibre.create_map(() => {
let init_result = await maplibre.init_maplibre(() => {
return workerPath ? new Worker(workerPath, {
type: 'module'
}) : MultithreadedPoolWorker();
}))
});
maplibre.run(init_result)
} else {
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
await maplibre.default(wasmPath, memory);
let callbacks: {worker_callback?: (message: MessageEvent) => void} = {}
let map = await maplibre.create_map(() => {
let init_result = await maplibre.init_maplibre(() => {
let worker: Worker = workerPath ? new Worker(workerPath, {
type: 'module'
}) : PoolWorker();
@ -54,16 +55,16 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
return worker;
})
let clonedMap = maplibre.clone_map(map)
// MAJOR FIXME: cloning is not possible let clonedMap = maplibre.clone_map(init_result)
callbacks.worker_callback = (message) => {
/*callbacks.worker_callback = (message) => {
let tag = message.data[0];
let data = new Uint8Array(message.data[1]);
// @ts-ignore TODO unsync_main_entry may not be defined
maplibre.singlethreaded_main_entry(clonedMap, tag, data)
}
}*/
maplibre.run(map)
maplibre.run(init_result)
}
}

View File

@ -1,8 +1,15 @@
#![feature(allocator_api, new_uninit)]
use std::{borrow::BorrowMut, cell::RefCell, mem, ops::Deref, panic, rc::Rc};
use std::{borrow::BorrowMut, cell::RefCell, mem, ops::Deref, rc::Rc};
use maplibre::{io::scheduler::NopScheduler, Map, MapBuilder};
use maplibre::{
event_loop::EventLoop,
io::{apc::SchedulerAsyncProcedureCall, scheduler::NopScheduler},
kernel::{Kernel, KernelBuilder},
map::Map,
render::builder::{InitializedRenderer, RenderBuilder},
style::Style,
};
use maplibre_winit::{WinitEnvironment, WinitMapWindowConfig};
use wasm_bindgen::prelude::*;
@ -32,44 +39,47 @@ pub fn wasm_bindgen_start() {
if console_log::init_with_level(log::Level::Info).is_err() {
// Failed to initialize logging. No need to log a message.
}
panic::set_hook(Box::new(console_error_panic_hook::hook));
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
#[cfg(any(feature = "trace"))]
enable_tracing();
}
#[cfg(not(target_feature = "atomics"))]
pub type MapType = Map<
WinitEnvironment<
NopScheduler,
WHATWGFetchHttpClient,
platform::singlethreaded::transferables::LinearTransferables,
platform::singlethreaded::apc::PassingAsyncProcedureCall,
>,
type CurrentEnvironment = WinitEnvironment<
NopScheduler,
WHATWGFetchHttpClient,
platform::singlethreaded::apc::PassingAsyncProcedureCall,
(),
>;
#[cfg(target_feature = "atomics")]
pub type MapType = Map<
WinitEnvironment<
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler,
type CurrentEnvironment = WinitEnvironment<
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler,
WHATWGFetchHttpClient,
maplibre::io::apc::SchedulerAsyncProcedureCall<
WHATWGFetchHttpClient,
maplibre::io::transferables::DefaultTransferables,
maplibre::io::apc::SchedulerAsyncProcedureCall<
WHATWGFetchHttpClient,
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler,
>,
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler,
>,
(),
>;
pub type MapType = Map<CurrentEnvironment>;
pub struct InitResult {
initialized: InitializedRenderer<WinitMapWindowConfig<()>>,
kernel: Kernel<CurrentEnvironment>,
}
#[wasm_bindgen]
pub async fn create_map(new_worker: js_sys::Function) -> u32 {
let mut builder = MapBuilder::new()
pub async fn init_maplibre(new_worker: js_sys::Function) -> u32 {
let mut kernel_builder = KernelBuilder::new()
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
.with_http_client(WHATWGFetchHttpClient::new());
#[cfg(target_feature = "atomics")]
{
builder = builder
kernel_builder = kernel_builder
.with_apc(maplibre::io::apc::SchedulerAsyncProcedureCall::new(
WHATWGFetchHttpClient::new(),
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(
@ -83,29 +93,38 @@ pub async fn create_map(new_worker: js_sys::Function) -> u32 {
#[cfg(not(target_feature = "atomics"))]
{
builder = builder
kernel_builder = kernel_builder
.with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4))
.with_scheduler(NopScheduler);
}
let map: MapType = builder.build().initialize().await;
let kernel: Kernel<WinitEnvironment<_, _, _, ()>> = kernel_builder.build();
Rc::into_raw(Rc::new(RefCell::new(map))) as u32
Box::into_raw(Box::new(InitResult {
initialized: RenderBuilder::new()
.build()
.initialize_with(&kernel)
.await
.expect("Failed to initialize renderer")
.unwarp(),
kernel,
})) as u32
}
#[wasm_bindgen]
pub unsafe fn clone_map(map_ptr: *const RefCell<MapType>) -> *const RefCell<MapType> {
let mut map = Rc::from_raw(map_ptr);
let cloned = Rc::into_raw(map.clone());
mem::forget(map); // TODO: Enforce forget, else the map gets dropped after calling such a function
cloned
}
pub unsafe fn run(init_ptr: *mut InitResult) {
let mut init_result = Box::from_raw(init_ptr);
#[wasm_bindgen]
pub unsafe fn run(map_ptr: *const RefCell<MapType>) {
let mut map = Rc::from_raw(map_ptr);
let InitializedRenderer {
mut window,
renderer,
} = init_result.initialized;
let map: MapType = Map::new(Style::default(), init_result.kernel, renderer).unwrap();
map.deref().borrow().run();
window
.take_event_loop()
.expect("Event loop is not available")
.run(window, map, None)
}
#[cfg(test)]

View File

@ -131,9 +131,8 @@ impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
serialized_array.set(&Uint8Array::view(serialized), 0);
}
let global = js_sys::global()
.try_into::<DedicatedWorkerGlobalScope>()
.map_err(|e| Error::APC);
let global: DedicatedWorkerGlobalScope =
js_sys::global().dyn_into().map_err(|e| Error::APC)?;
let array = js_sys::Array::new();
array.push(&JsValue::from(tag as u32));
array.push(&serialized_array_buffer);
@ -149,7 +148,7 @@ pub struct PassingAsyncProcedureCall {
new_worker: Box<dyn Fn() -> Worker>,
workers: Vec<Worker>,
received: Vec<Message<UsedTransferables>>,
received: RefCell<Vec<Message<UsedTransferables>>>, // FIXME (wasm-executor): Is RefCell fine?
}
impl PassingAsyncProcedureCall {
@ -176,16 +175,17 @@ impl PassingAsyncProcedureCall {
Self {
new_worker: create_new_worker,
workers,
received: vec![],
received: RefCell::new(vec![]),
}
}
}
impl AsyncProcedureCall<UsedTransferables, UsedHttpClient> for PassingAsyncProcedureCall {
impl AsyncProcedureCall<UsedHttpClient> for PassingAsyncProcedureCall {
type Context = UsedContext;
type Transferables = UsedTransferables;
fn receive(&mut self) -> Option<Message<UsedTransferables>> {
self.received.pop()
fn receive(&self) -> Option<Message<UsedTransferables>> {
self.received.borrow_mut().pop()
}
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
@ -208,7 +208,7 @@ pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> R
let input = serde_json::from_str::<Input>(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
let context = PassingContext {
source_client: SourceClient::Http(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
source_client: SourceClient::new(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
};
(procedure)(input, context).await;
@ -231,16 +231,11 @@ pub unsafe fn singlethreaded_main_entry(
data,
);
map.deref()
.borrow()
.map_schedule()
.deref()
.borrow()
.apc
.deref()
.borrow_mut()
.received
.push(message);
let map = map.deref().borrow();
let kernel = map.kernel();
// MAJOR FIXME: Fix mutability
// kernel.apc().received.push(message);
mem::forget(map); // FIXME (wasm-executor): Enforce this somehow