From f8b38dc8f21b7cd23295d07e5425dd0aa999dc28 Mon Sep 17 00:00:00 2001 From: Maximilian Ammann Date: Sun, 25 Sep 2022 12:10:30 +0200 Subject: [PATCH 01/31] Fix imports, refactor builders, invert feature flag --- maplibre/Cargo.toml | 3 +- maplibre/src/context.rs | 4 +- maplibre/src/environment.rs | 68 +++++- maplibre/src/headless.rs | 7 +- maplibre/src/io/apc.rs | 7 +- maplibre/src/io/scheduler.rs | 4 +- maplibre/src/io/source_client.rs | 6 +- maplibre/src/lib.rs | 222 +----------------- maplibre/src/map.rs | 47 ++++ maplibre/src/map_schedule.rs | 6 +- maplibre/src/platform/noweb/http_client.rs | 3 +- maplibre/src/platform/noweb/scheduler.rs | 3 +- maplibre/src/render/builder.rs | 76 ++++++ maplibre/src/render/mod.rs | 8 +- maplibre/src/render/render_commands.rs | 16 +- maplibre/src/render/render_phase/draw.rs | 3 +- maplibre/src/render/resource/surface.rs | 2 +- maplibre/src/render/stages/extract_stage.rs | 3 +- .../src/render/stages/graph_runner_stage.rs | 2 +- .../src/render/stages/phase_sort_stage.rs | 3 +- maplibre/src/render/stages/queue_stage.rs | 2 +- maplibre/src/render/stages/resource_stage.rs | 2 +- maplibre/src/render/stages/upload_stage.rs | 3 +- maplibre/src/stages/mod.rs | 3 +- .../src/stages/populate_tile_store_stage.rs | 2 +- maplibre/src/stages/request_stage.rs | 3 +- maplibre/src/window.rs | 4 +- web/Cargo.toml | 2 +- 28 files changed, 249 insertions(+), 265 deletions(-) create mode 100644 maplibre/src/map.rs create mode 100644 maplibre/src/render/builder.rs diff --git a/maplibre/Cargo.toml b/maplibre/Cargo.toml index 0367c17d..02a13596 100644 --- a/maplibre/Cargo.toml +++ b/maplibre/Cargo.toml @@ -9,10 +9,11 @@ description = "Native Maps for Web, Mobile and Desktop" readme = "../README.md" [features] +default = ["thread-safe-futures"] web-webgl = ["wgpu/webgl"] # Enable tracing using tracy on desktop/mobile and the chrome profiler on web trace = ["tracing-subscriber", "tracing-tracy", "tracy-client"] -no-thread-safe-futures = [] +thread-safe-futures = [] embed-static-tiles = ["maplibre-build-tools/sqlite"] headless = ["png"] diff --git a/maplibre/src/context.rs b/maplibre/src/context.rs index 5f99755d..8b3f196c 100644 --- a/maplibre/src/context.rs +++ b/maplibre/src/context.rs @@ -2,12 +2,14 @@ use std::ops::Div; use cgmath::Angle; +use crate::render::Renderer; +use crate::style::Style; +use crate::window::WindowSize; use crate::{ coords::{LatLon, ViewRegion, WorldCoords, Zoom, ZoomLevel, TILE_SIZE}, io::tile_repository::TileRepository, render::camera::{Camera, Perspective, ViewProjection}, util::ChangeObserver, - Renderer, Style, WindowSize, }; /// Stores the camera configuration. diff --git a/maplibre/src/environment.rs b/maplibre/src/environment.rs index 42e7697d..cc5ed371 100644 --- a/maplibre/src/environment.rs +++ b/maplibre/src/environment.rs @@ -1,12 +1,12 @@ -use crate::{ - io::{ - apc::AsyncProcedureCall, - transferables::{ - DefaultTessellatedLayer, DefaultTileTessellated, DefaultUnavailableLayer, Transferables, - }, +use crate::io::scheduler::Scheduler; +use crate::io::source_client::HttpClient; +use crate::io::{ + apc::AsyncProcedureCall, + transferables::{ + DefaultTessellatedLayer, DefaultTileTessellated, DefaultUnavailableLayer, Transferables, }, - HttpClient, MapWindowConfig, Scheduler, }; +use crate::window::MapWindowConfig; pub trait Environment: 'static { type MapWindowConfig: MapWindowConfig; @@ -17,3 +17,57 @@ pub trait Environment: 'static { type Transferables: Transferables; } + +pub struct Kernel { + map_window_config: E::MapWindowConfig, + apc: E::AsyncProcedureCall, + scheduler: E::Scheduler, + http_client: E::HttpClient, +} + +pub struct KernelBuilder { + map_window_config: Option, + apc: Option, + scheduler: Option, + http_client: Option, +} + +impl KernelBuilder { + pub fn new() -> Self { + Self { + scheduler: None, + apc: None, + http_client: None, + map_window_config: None, + } + } + + pub fn with_map_window_config(mut self, map_window_config: E::MapWindowConfig) -> Self { + self.map_window_config = Some(map_window_config); + self + } + + pub fn with_scheduler(mut self, scheduler: E::Scheduler) -> Self { + self.scheduler = Some(scheduler); + self + } + + pub fn with_apc(mut self, apc: E::AsyncProcedureCall) -> Self { + self.apc = Some(apc); + self + } + + pub fn with_http_client(mut self, http_client: E::HttpClient) -> Self { + self.http_client = Some(http_client); + self + } + + pub fn build(self) -> Kernel { + Kernel { + scheduler: self.scheduler.unwrap(), // TODO: Remove unwrap + apc: self.apc.unwrap(), // TODO: Remove unwrap + http_client: self.http_client.unwrap(), // TODO: Remove unwrap + map_window_config: self.map_window_config.unwrap(), // TODO: Remove unwrap + } + } +} diff --git a/maplibre/src/headless.rs b/maplibre/src/headless.rs index bf42c702..98b20784 100644 --- a/maplibre/src/headless.rs +++ b/maplibre/src/headless.rs @@ -12,6 +12,12 @@ use std::{ use tokio::{runtime::Handle, task}; use wgpu::{BufferAsyncError, BufferSlice}; +use crate::environment::Environment; +use crate::io::scheduler::Scheduler; +use crate::io::source_client::HttpClient; +use crate::render::Renderer; +use crate::style::Style; +use crate::window::{MapWindow, MapWindowConfig, WindowSize}; use crate::{ context::{MapContext, ViewState}, coords::{LatLon, ViewRegion, WorldCoords, WorldTileCoords, Zoom, TILE_SIZE}, @@ -37,7 +43,6 @@ use crate::{ RenderState, }, schedule::{Schedule, Stage}, - Environment, HttpClient, MapWindow, MapWindowConfig, Renderer, Scheduler, Style, WindowSize, }; pub struct HeadlessMapWindowConfig { diff --git a/maplibre/src/io/apc.rs b/maplibre/src/io/apc.rs index 08f74c39..057153ff 100644 --- a/maplibre/src/io/apc.rs +++ b/maplibre/src/io/apc.rs @@ -10,6 +10,8 @@ use std::{ use serde::{Deserialize, Serialize}; +use crate::io::scheduler::Scheduler; +use crate::io::source_client::HttpClient; use crate::{ coords::WorldTileCoords, io::{ @@ -17,7 +19,6 @@ use crate::{ transferables::{DefaultTransferables, Transferables}, TileRequest, }, - Environment, HttpClient, Scheduler, }; /// The result of the tessellation of a tile. @@ -41,9 +42,9 @@ pub trait Context: Send + 'static { fn source_client(&self) -> &SourceClient; } -#[cfg(not(feature = "no-thread-safe-futures"))] +#[cfg(feature = "thread-safe-futures")] pub type AsyncProcedureFuture = Pin + Send + 'static)>>; -#[cfg(feature = "no-thread-safe-futures")] +#[cfg(not(feature = "thread-safe-futures"))] pub type AsyncProcedureFuture = Pin + 'static)>>; pub type AsyncProcedure = fn(input: Input, context: C) -> AsyncProcedureFuture; diff --git a/maplibre/src/io/scheduler.rs b/maplibre/src/io/scheduler.rs index 3053d361..912436d3 100644 --- a/maplibre/src/io/scheduler.rs +++ b/maplibre/src/io/scheduler.rs @@ -7,7 +7,7 @@ use crate::error::Error; /// Async/await scheduler. /// Can schedule a task from a future factory and a shared state. pub trait Scheduler: 'static { - #[cfg(not(feature = "no-thread-safe-futures"))] + #[cfg(feature = "thread-safe-futures")] fn schedule( &self, future_factory: impl (FnOnce() -> T) + Send + 'static, @@ -15,7 +15,7 @@ pub trait Scheduler: 'static { where T: Future + Send + 'static; - #[cfg(feature = "no-thread-safe-futures")] + #[cfg(not(feature = "thread-safe-futures"))] fn schedule( &self, future_factory: impl (FnOnce() -> T) + Send + 'static, diff --git a/maplibre/src/io/source_client.rs b/maplibre/src/io/source_client.rs index 3f3caaf9..ba432d09 100644 --- a/maplibre/src/io/source_client.rs +++ b/maplibre/src/io/source_client.rs @@ -12,9 +12,9 @@ pub type HTTPClientFactory = dyn Fn() -> HC; /// [https://github.com/dtolnay/async-trait/blob/b70720c4c1cc0d810b7446efda44f81310ee7bf2/README.md#non-threadsafe-futures](https://github.com/dtolnay/async-trait/blob/b70720c4c1cc0d810b7446efda44f81310ee7bf2/README.md#non-threadsafe-futures) /// /// Users of this library can decide whether futures from the HTTPClient are thread-safe or not via -/// the future "no-thread-safe-futures". Tokio futures are thread-safe. -#[cfg_attr(feature = "no-thread-safe-futures", async_trait(?Send))] -#[cfg_attr(not(feature = "no-thread-safe-futures"), async_trait)] +/// the future "thread-safe-futures". Tokio futures are thread-safe. +#[cfg_attr(not(feature = "thread-safe-futures"), async_trait(?Send))] +#[cfg_attr(feature = "thread-safe-futures", async_trait)] pub trait HttpClient: Clone + Sync + Send + 'static { async fn fetch(&self, url: &str) -> Result, Error>; } diff --git a/maplibre/src/lib.rs b/maplibre/src/lib.rs index 082fdcd3..9e8ac6db 100644 --- a/maplibre/src/lib.rs +++ b/maplibre/src/lib.rs @@ -16,24 +16,6 @@ //! maplibre = "0.0.2" //! ``` -use std::{ - borrow::{Borrow, BorrowMut}, - cell::RefCell, - rc::Rc, -}; - -use crate::{ - environment::Environment, - io::{scheduler::Scheduler, source_client::HttpClient}, - map_schedule::InteractiveMapSchedule, - render::{ - settings::{RendererSettings, WgpuSettings}, - RenderState, Renderer, - }, - style::Style, - window::{EventLoop, HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize}, -}; - pub mod context; pub mod coords; pub mod error; @@ -43,7 +25,7 @@ pub mod io; // Exposed because of input handlers in maplibre-winit pub mod map_schedule; pub mod platform; -// Exposed because of camera +// TODO: Exposed because of camera pub mod render; pub mod style; pub mod util; @@ -54,209 +36,13 @@ pub mod schedule; // Exposed because of SharedThreadState pub mod stages; +pub mod environment; + // Used for benchmarking pub mod benchmarking; // Internal modules pub(crate) mod tessellation; -pub mod environment; - +// Export tile format pub use geozero::mvt::tile; - -/// The [`Map`] defines the public interface of the map renderer. -// DO NOT IMPLEMENT INTERNALS ON THIS STRUCT. -pub struct Map { - // FIXME (wasm-executor): Avoid RefCell, change ownership model! - map_schedule: Rc>>, - window: RefCell::MapWindow>>, -} - -impl Map -where - ::MapWindow: EventLoop, -{ - /// Starts the [`crate::map_schedule::MapState`] Runnable with the configured event loop. - pub fn run(&self) { - self.run_with_optionally_max_frames(None); - } - - /// Starts the [`crate::map_schedule::MapState`] Runnable with the configured event loop. - /// - /// # Arguments - /// - /// * `max_frames` - Maximum number of frames per second. - pub fn run_with_max_frames(&self, max_frames: u64) { - self.run_with_optionally_max_frames(Some(max_frames)); - } - - /// Starts the MapState Runnable with the configured event loop. - /// - /// # Arguments - /// - /// * `max_frames` - Optional maximum number of frames per second. - pub fn run_with_optionally_max_frames(&self, max_frames: Option) { - self.window - .borrow_mut() - .take() - .unwrap() // FIXME (wasm-executor): Remove unwrap - .run(self.map_schedule.clone(), max_frames); - } - - pub fn map_schedule(&self) -> Rc>> { - self.map_schedule.clone() - } - - /* pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule { - &mut self.map_schedule - }*/ -} - -/// Stores the map configuration before the map's state has been fully initialized. -pub struct UninitializedMap { - scheduler: E::Scheduler, - apc: E::AsyncProcedureCall, - http_client: E::HttpClient, - style: Style, - - wgpu_settings: WgpuSettings, - renderer_settings: RendererSettings, - map_window_config: E::MapWindowConfig, -} - -impl UninitializedMap -where - ::MapWindow: HeadedMapWindow, -{ - /// Initializes the whole rendering pipeline for the given configuration. - /// Returns the initialized map, ready to be run. - pub async fn initialize(self) -> Map { - let window = self.map_window_config.create(); - let window_size = window.size(); - - #[cfg(target_os = "android")] - let renderer = None; - #[cfg(not(target_os = "android"))] - let renderer = Renderer::initialize( - &window, - self.wgpu_settings.clone(), - self.renderer_settings.clone(), - ) - .await - .ok(); - Map { - map_schedule: Rc::new(RefCell::new(InteractiveMapSchedule::new( - self.map_window_config, - window_size, - renderer, - self.scheduler, - self.apc, - self.http_client, - self.style, - self.wgpu_settings, - self.renderer_settings, - ))), - window: RefCell::new(Some(window)), - } - } -} - -#[cfg(feature = "headless")] -impl UninitializedMap { - pub async fn initialize_headless(self) -> headless::HeadlessMap { - let window = self.map_window_config.create(); - let window_size = window.size(); - - let renderer = Renderer::initialize_headless( - &window, - self.wgpu_settings.clone(), - self.renderer_settings.clone(), - ) - .await - .expect("Failed to initialize renderer"); - headless::HeadlessMap { - map_schedule: headless::HeadlessMapSchedule::new( - self.map_window_config, - window_size, - renderer, - self.scheduler, - self.http_client, - self.style, - ), - window, - } - } -} - -pub struct MapBuilder { - scheduler: Option, - apc: Option, - http_client: Option, - style: Option