mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add first working version which does not need shared memory on web
This commit is contained in:
parent
d0c86492f7
commit
7dc7346597
@ -1,3 +1,6 @@
|
||||
use maplibre::environment::DefaultTransferables;
|
||||
|
||||
use maplibre::platform::apc::TokioAsyncProcedureCall;
|
||||
use maplibre::{
|
||||
platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler},
|
||||
MapBuilder,
|
||||
@ -5,9 +8,10 @@ use maplibre::{
|
||||
use maplibre_winit::winit::{WinitEnvironment, WinitMapWindowConfig};
|
||||
|
||||
pub async fn run_headed() {
|
||||
MapBuilder::<WinitEnvironment<_, _>>::new()
|
||||
MapBuilder::<WinitEnvironment<_, _, _, TokioAsyncProcedureCall>>::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_apc(TokioAsyncProcedureCall::new())
|
||||
.with_scheduler(TokioScheduler::new())
|
||||
.build()
|
||||
.initialize()
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use maplibre::headless::HeadlessEnvironment;
|
||||
use maplibre::platform::apc::TokioAsyncProcedureCall;
|
||||
use maplibre::{
|
||||
coords::{LatLon, WorldTileCoords},
|
||||
error::Error,
|
||||
@ -13,11 +14,12 @@ use maplibre_winit::winit::WinitEnvironment;
|
||||
use tile_grid::{extent_wgs84_to_merc, Extent, GridIterator};
|
||||
|
||||
pub async fn run_headless(tile_size: u32, min: LatLon, max: LatLon) {
|
||||
let mut map = MapBuilder::<HeadlessEnvironment<_, _>>::new()
|
||||
let mut map = MapBuilder::<HeadlessEnvironment<_, _, _, TokioAsyncProcedureCall>>::new()
|
||||
.with_map_window_config(HeadlessMapWindowConfig {
|
||||
size: WindowSize::new(tile_size, tile_size).unwrap(),
|
||||
})
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_apc(TokioAsyncProcedureCall::new())
|
||||
.with_scheduler(TokioScheduler::new())
|
||||
.with_renderer_settings(RendererSettings {
|
||||
texture_format: TextureFormat::Rgba8UnormSrgb,
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
use instant::Instant;
|
||||
use maplibre::environment::{DefaultTransferables, Environment};
|
||||
use maplibre::io::apc::Transferable;
|
||||
use maplibre::io::apc::{AsyncProcedureCall, Transferable};
|
||||
use maplibre::io::scheduler::Scheduler;
|
||||
use maplibre::platform::apc::TokioAsyncProcedureCall;
|
||||
use maplibre::io::transferables::Transferables;
|
||||
use maplibre::{
|
||||
error::Error,
|
||||
io::source_client::HttpClient,
|
||||
map_schedule::InteractiveMapSchedule,
|
||||
window::{EventLoop, HeadedMapWindow, MapWindowConfig},
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
@ -66,17 +69,26 @@ impl WinitMapWindow {
|
||||
pub type WinitWindow = winit::window::Window;
|
||||
pub type WinitEventLoop = winit::event_loop::EventLoop<()>;
|
||||
|
||||
pub struct WinitEnvironment<S: Scheduler, HC: HttpClient> {
|
||||
pub struct WinitEnvironment<
|
||||
S: Scheduler,
|
||||
HC: HttpClient,
|
||||
T: Transferables,
|
||||
APC: AsyncProcedureCall<T, HC>,
|
||||
> {
|
||||
phantom_s: PhantomData<S>,
|
||||
phantom_hc: PhantomData<HC>,
|
||||
phantom_t: PhantomData<T>,
|
||||
phantom_apc: PhantomData<APC>,
|
||||
}
|
||||
|
||||
impl<S: Scheduler, HC: HttpClient> Environment for WinitEnvironment<S, HC> {
|
||||
impl<S: Scheduler, HC: HttpClient, T: Transferables, APC: AsyncProcedureCall<T, HC>> Environment
|
||||
for WinitEnvironment<S, HC, T, APC>
|
||||
{
|
||||
type MapWindowConfig = WinitMapWindowConfig;
|
||||
type AsyncProcedureCall = TokioAsyncProcedureCall<DefaultTransferables>;
|
||||
type AsyncProcedureCall = APC;
|
||||
type Scheduler = S;
|
||||
type HttpClient = HC;
|
||||
type Transferables = DefaultTransferables;
|
||||
type Transferables = T;
|
||||
}
|
||||
|
||||
///Main (platform-specific) main loop which handles:
|
||||
@ -87,7 +99,11 @@ impl<E: Environment> EventLoop<E> for WinitMapWindow
|
||||
where
|
||||
E::MapWindowConfig: MapWindowConfig<MapWindow = WinitMapWindow>,
|
||||
{
|
||||
fn run(mut self, mut map_schedule: InteractiveMapSchedule<E>, max_frames: Option<u64>) {
|
||||
fn run(
|
||||
mut self,
|
||||
map_schedule: Rc<RefCell<InteractiveMapSchedule<E>>>,
|
||||
max_frames: Option<u64>,
|
||||
) {
|
||||
let mut last_render_time = Instant::now();
|
||||
let mut current_frame: u64 = 0;
|
||||
|
||||
@ -96,6 +112,8 @@ where
|
||||
self.take_event_loop()
|
||||
.unwrap()
|
||||
.run(move |event, _, control_flow| {
|
||||
let mut map_schedule = map_schedule.deref().borrow_mut();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if !map_schedule.is_initialized() && event == Event::Resumed {
|
||||
use tokio::{runtime::Handle, task};
|
||||
|
||||
@ -19,6 +19,7 @@ headless = ["png"]
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android", target_os = "windows"))'.dependencies]
|
||||
tokio = { version = "1.20.1", features = ["macros", "rt", "rt-multi-thread", "sync", "time"] }
|
||||
tokio-util = { version = "0.7.1", features = ["rt"] }
|
||||
env_logger = "0.9.0"
|
||||
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "gzip"] }
|
||||
reqwest-middleware-cache = "0.1.1"
|
||||
|
||||
@ -6,6 +6,7 @@ use std::{
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use cgmath::{num_traits::Pow, AbsDiffEq, Matrix4, Point3, Vector3};
|
||||
|
||||
use crate::{
|
||||
@ -15,7 +16,7 @@ use crate::{
|
||||
SignificantlyDifferent,
|
||||
},
|
||||
};
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub const EXTENT_UINT: u32 = 4096;
|
||||
pub const EXTENT_SINT: i32 = EXTENT_UINT as i32;
|
||||
@ -68,7 +69,23 @@ impl fmt::Debug for Quadkey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug, Default, Serialize)]
|
||||
// FIXME: does Pod and Zeroable make sense?
|
||||
#[derive(
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Pod,
|
||||
Zeroable,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct ZoomLevel(u8);
|
||||
|
||||
impl ZoomLevel {
|
||||
@ -290,7 +307,22 @@ impl From<(u32, u32, ZoomLevel)> for TileCoords {
|
||||
/// # Coordinate System Origin
|
||||
///
|
||||
/// The origin of the coordinate system is in the upper-left corner.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize)]
|
||||
// FIXME: does Zeroable make sense?
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Zeroable,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct WorldTileCoords {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{HttpClient, MapWindowConfig, Scheduler};
|
||||
pub trait Environment: 'static {
|
||||
type MapWindowConfig: MapWindowConfig;
|
||||
|
||||
type AsyncProcedureCall: AsyncProcedureCall<Self::Transferables>;
|
||||
type AsyncProcedureCall: AsyncProcedureCall<Self::Transferables, Self::HttpClient>;
|
||||
type Scheduler: Scheduler;
|
||||
type HttpClient: HttpClient;
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ use tokio::{runtime::Handle, task};
|
||||
use wgpu::{BufferAsyncError, BufferSlice};
|
||||
|
||||
use crate::environment::DefaultTransferables;
|
||||
use crate::io::apc::AsyncProcedureCall;
|
||||
use crate::io::transferables::Transferables;
|
||||
use crate::platform::apc::TokioAsyncProcedureCall;
|
||||
use crate::{
|
||||
context::{MapContext, ViewState},
|
||||
@ -62,17 +64,26 @@ impl MapWindow for HeadlessMapWindow {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HeadlessEnvironment<S: Scheduler, HC: HttpClient> {
|
||||
pub struct HeadlessEnvironment<
|
||||
S: Scheduler,
|
||||
HC: HttpClient,
|
||||
T: Transferables,
|
||||
APC: AsyncProcedureCall<T, HC>,
|
||||
> {
|
||||
phantom_s: PhantomData<S>,
|
||||
phantom_hc: PhantomData<HC>,
|
||||
phantom_t: PhantomData<T>,
|
||||
phantom_apc: PhantomData<APC>,
|
||||
}
|
||||
|
||||
impl<S: Scheduler, HC: HttpClient> Environment for HeadlessEnvironment<S, HC> {
|
||||
impl<S: Scheduler, HC: HttpClient, T: Transferables, APC: AsyncProcedureCall<T, HC>> Environment
|
||||
for HeadlessEnvironment<S, HC, T, APC>
|
||||
{
|
||||
type MapWindowConfig = HeadlessMapWindowConfig;
|
||||
type AsyncProcedureCall = TokioAsyncProcedureCall<DefaultTransferables>;
|
||||
type AsyncProcedureCall = APC;
|
||||
type Scheduler = S;
|
||||
type HttpClient = HC;
|
||||
type Transferables = DefaultTransferables;
|
||||
type Transferables = T;
|
||||
}
|
||||
|
||||
pub struct HeadlessMap<E: Environment> {
|
||||
@ -329,9 +340,9 @@ pub mod utils {
|
||||
) {
|
||||
self.layers.push(StoredLayer::TessellatedLayer {
|
||||
coords: *coords,
|
||||
layer_name: layer_data.name,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
use crate::coords::WorldTileCoords;
|
||||
use crate::io::source_client::{HttpSourceClient, SourceClient};
|
||||
use crate::io::transferables::Transferables;
|
||||
use crate::Environment;
|
||||
use serde::Serialize;
|
||||
use crate::io::TileRequest;
|
||||
use crate::{Environment, HttpClient};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
@ -16,23 +18,29 @@ pub enum Transferable<T: Transferables> {
|
||||
TessellatedLayer(T::TessellatedLayer),
|
||||
}
|
||||
|
||||
pub trait Context<T: Transferables> {
|
||||
fn send(&self, data: Transferable<T>);
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum Input {
|
||||
TileRequest(TileRequest),
|
||||
}
|
||||
|
||||
pub type AsyncProcedure<I, C> =
|
||||
fn(input: I, context: C) -> Pin<Box<dyn Future<Output = ()> + Send>>;
|
||||
pub trait Context<T: Transferables, HC: HttpClient> {
|
||||
fn send(&self, data: Transferable<T>);
|
||||
|
||||
pub trait AsyncProcedureCall<T: Transferables> {
|
||||
type Context: Context<T> + Send;
|
||||
fn source_client(&self) -> &SourceClient<HC>;
|
||||
}
|
||||
|
||||
fn new() -> Self;
|
||||
pub type AsyncProcedure<T, HC> =
|
||||
fn(input: Input, context: Box<dyn Context<T, HC>>) -> Pin<Box<dyn Future<Output = ()>>>;
|
||||
|
||||
fn receive(&self) -> Option<Transferable<T>>;
|
||||
pub trait AsyncProcedureCall<T: Transferables, HC: HttpClient>: 'static {
|
||||
type Context: Context<T, HC> + Send;
|
||||
|
||||
fn schedule<I: Send + Serialize + 'static>(
|
||||
fn receive(&mut self) -> Option<Box<Transferable<T>>>; // FIXME remove box
|
||||
|
||||
fn schedule(
|
||||
&self,
|
||||
input: I,
|
||||
procedure: AsyncProcedure<I, Self::Context>,
|
||||
input: Input,
|
||||
procedure: AsyncProcedure<T, HC>,
|
||||
http_client: HttpSourceClient<HC>,
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
use std::{collections::HashSet, fmt};
|
||||
|
||||
use crate::coords::WorldTileCoords;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod apc;
|
||||
pub mod geometry_index;
|
||||
@ -19,7 +19,7 @@ pub mod transferables;
|
||||
pub use geozero::mvt::tile::Layer as RawLayer;
|
||||
|
||||
/// A request for a tile at the given coordinates and in the given layers.
|
||||
#[derive(Clone, Serialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct TileRequest {
|
||||
pub coords: WorldTileCoords,
|
||||
pub layers: HashSet<String>,
|
||||
|
||||
@ -23,3 +23,14 @@ pub trait Scheduler: 'static {
|
||||
where
|
||||
T: Future<Output = ()> + 'static;
|
||||
}
|
||||
|
||||
pub struct NopScheduler;
|
||||
|
||||
impl Scheduler for NopScheduler {
|
||||
fn schedule<T>(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
Err(Error::Schedule)
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,10 +18,10 @@ pub enum StoredLayer {
|
||||
},
|
||||
TessellatedLayer {
|
||||
coords: WorldTileCoords,
|
||||
layer_name: String,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
/// Holds for each feature the count of indices.
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: tile::Layer,
|
||||
},
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ impl StoredLayer {
|
||||
pub fn layer_name(&self) -> &str {
|
||||
match self {
|
||||
StoredLayer::UnavailableLayer { layer_name, .. } => layer_name.as_str(),
|
||||
StoredLayer::TessellatedLayer { layer_data, .. } => &layer_data.name,
|
||||
StoredLayer::TessellatedLayer { layer_name, .. } => layer_name.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ pub struct DefaultTessellatedLayer {
|
||||
pub buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
/// Holds for each feature the count of indices.
|
||||
pub feature_indices: Vec<u32>,
|
||||
pub layer_data: tile::Layer,
|
||||
pub layer_data: Layer, // FIXME: Introduce a better structure for this
|
||||
}
|
||||
|
||||
impl TessellatedLayer for DefaultTessellatedLayer {
|
||||
@ -85,9 +85,9 @@ impl TessellatedLayer for DefaultTessellatedLayer {
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
StoredLayer::TessellatedLayer {
|
||||
coords: self.coords,
|
||||
layer_name: self.layer_data.name,
|
||||
buffer: self.buffer,
|
||||
feature_indices: self.feature_indices,
|
||||
layer_data: self.layer_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,6 +27,9 @@ use crate::{
|
||||
style::Style,
|
||||
window::{EventLoop, HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize},
|
||||
};
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub mod context;
|
||||
pub mod coords;
|
||||
@ -56,11 +59,14 @@ pub(crate) mod tessellation;
|
||||
|
||||
pub mod environment;
|
||||
|
||||
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<E: Environment> {
|
||||
map_schedule: InteractiveMapSchedule<E>,
|
||||
window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
|
||||
// FIXME: Avoid RefCell, change ownership model!
|
||||
map_schedule: Rc<RefCell<InteractiveMapSchedule<E>>>,
|
||||
window: RefCell<Option<<E::MapWindowConfig as MapWindowConfig>::MapWindow>>,
|
||||
}
|
||||
|
||||
impl<E: Environment> Map<E>
|
||||
@ -68,7 +74,7 @@ where
|
||||
<E::MapWindowConfig as MapWindowConfig>::MapWindow: EventLoop<E>,
|
||||
{
|
||||
/// Starts the [`crate::map_schedule::MapState`] Runnable with the configured event loop.
|
||||
pub fn run(self) {
|
||||
pub fn run(&self) {
|
||||
self.run_with_optionally_max_frames(None);
|
||||
}
|
||||
|
||||
@ -77,7 +83,7 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_frames` - Maximum number of frames per second.
|
||||
pub fn run_with_max_frames(self, max_frames: u64) {
|
||||
pub fn run_with_max_frames(&self, max_frames: u64) {
|
||||
self.run_with_optionally_max_frames(Some(max_frames));
|
||||
}
|
||||
|
||||
@ -86,22 +92,27 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_frames` - Optional maximum number of frames per second.
|
||||
pub fn run_with_optionally_max_frames(self, max_frames: Option<u64>) {
|
||||
self.window.run(self.map_schedule, max_frames);
|
||||
pub fn run_with_optionally_max_frames(&self, max_frames: Option<u64>) {
|
||||
self.window
|
||||
.borrow_mut()
|
||||
.take()
|
||||
.unwrap()
|
||||
.run(self.map_schedule.clone(), max_frames);
|
||||
}
|
||||
|
||||
pub fn map_schedule(&self) -> &InteractiveMapSchedule<E> {
|
||||
&self.map_schedule
|
||||
pub fn map_schedule(&self) -> Rc<RefCell<InteractiveMapSchedule<E>>> {
|
||||
self.map_schedule.clone()
|
||||
}
|
||||
|
||||
pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule<E> {
|
||||
/* pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule<E> {
|
||||
&mut self.map_schedule
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Stores the map configuration before the map's state has been fully initialized.
|
||||
pub struct UninitializedMap<E: Environment> {
|
||||
scheduler: E::Scheduler,
|
||||
apc: E::AsyncProcedureCall,
|
||||
http_client: E::HttpClient,
|
||||
style: Style,
|
||||
|
||||
@ -131,17 +142,18 @@ where
|
||||
.await
|
||||
.ok();
|
||||
Map {
|
||||
map_schedule: InteractiveMapSchedule::new(
|
||||
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,
|
||||
))),
|
||||
window: RefCell::new(Some(window)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,6 +187,7 @@ impl<E: Environment> UninitializedMap<E> {
|
||||
|
||||
pub struct MapBuilder<E: Environment> {
|
||||
scheduler: Option<E::Scheduler>,
|
||||
apc: Option<E::AsyncProcedureCall>,
|
||||
http_client: Option<E::HttpClient>,
|
||||
style: Option<Style>,
|
||||
|
||||
@ -187,6 +200,7 @@ impl<E: Environment> MapBuilder<E> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
scheduler: None,
|
||||
apc: None,
|
||||
http_client: None,
|
||||
style: None,
|
||||
map_window_config: None,
|
||||
@ -215,6 +229,11 @@ impl<E: Environment> MapBuilder<E> {
|
||||
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
|
||||
@ -227,13 +246,11 @@ impl<E: Environment> MapBuilder<E> {
|
||||
|
||||
/// Builds the UninitializedMap with the given configuration.
|
||||
pub fn build(self) -> UninitializedMap<E> {
|
||||
let scheduler = self.scheduler.unwrap();
|
||||
let style = self.style.unwrap_or_default();
|
||||
|
||||
UninitializedMap {
|
||||
scheduler,
|
||||
scheduler: self.scheduler.unwrap(),
|
||||
apc: self.apc.unwrap(),
|
||||
http_client: self.http_client.unwrap(),
|
||||
style,
|
||||
style: self.style.unwrap_or_default(),
|
||||
wgpu_settings: self.wgpu_settings.unwrap_or_default(),
|
||||
renderer_settings: self.renderer_settings.unwrap_or_default(),
|
||||
map_window_config: self.map_window_config.unwrap(),
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::{marker::PhantomData, mem};
|
||||
|
||||
use crate::{
|
||||
@ -21,6 +23,9 @@ use crate::{
|
||||
pub struct InteractiveMapSchedule<E: Environment> {
|
||||
map_window_config: E::MapWindowConfig,
|
||||
|
||||
// FIXME: avoid RefCell, change ownership model
|
||||
pub apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
|
||||
map_context: EventuallyMapContext,
|
||||
|
||||
schedule: Schedule,
|
||||
@ -34,6 +39,7 @@ impl<E: Environment> InteractiveMapSchedule<E> {
|
||||
window_size: WindowSize,
|
||||
renderer: Option<Renderer>,
|
||||
scheduler: E::Scheduler,
|
||||
apc: E::AsyncProcedureCall,
|
||||
http_client: E::HttpClient,
|
||||
style: Style,
|
||||
wgpu_settings: WgpuSettings,
|
||||
@ -50,14 +56,17 @@ impl<E: Environment> InteractiveMapSchedule<E> {
|
||||
let tile_repository = TileRepository::new();
|
||||
let mut schedule = Schedule::default();
|
||||
|
||||
let apc = Rc::new(RefCell::new(apc)); // TODO: remove refcell, rc
|
||||
|
||||
let http_source_client: HttpSourceClient<E::HttpClient> =
|
||||
HttpSourceClient::new(http_client);
|
||||
register_stages::<E>(&mut schedule, http_source_client, Box::new(scheduler));
|
||||
register_stages::<E>(&mut schedule, http_source_client, apc.clone());
|
||||
|
||||
let graph = create_default_render_graph().unwrap();
|
||||
register_default_render_stages(graph, &mut schedule);
|
||||
|
||||
Self {
|
||||
apc,
|
||||
map_window_config,
|
||||
map_context: match renderer {
|
||||
None => EventuallyMapContext::Premature(PrematureMapContext {
|
||||
@ -116,6 +125,10 @@ impl<E: Environment> InteractiveMapSchedule<E> {
|
||||
_ => panic!("should not happen"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apc(&self) -> &Rc<RefCell<E::AsyncProcedureCall>> {
|
||||
&self.apc
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Environment> InteractiveMapSchedule<E>
|
||||
|
||||
@ -1,18 +1,22 @@
|
||||
use crate::environment::DefaultTransferables;
|
||||
use crate::io::apc::{AsyncProcedure, AsyncProcedureCall, Context, Transferable};
|
||||
use crate::io::apc::{AsyncProcedure, AsyncProcedureCall, Context, Input, Transferable};
|
||||
use crate::io::source_client::{HttpSourceClient, SourceClient};
|
||||
use crate::io::transferables::Transferables;
|
||||
use crate::Environment;
|
||||
use crate::{Environment, HttpClient};
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use tokio_util::task::LocalPoolHandle;
|
||||
|
||||
// FIXME: Make this generic using the Schedule
|
||||
#[derive(Clone)]
|
||||
pub struct TokioContext<T: Transferables> {
|
||||
pub struct TokioContext<T: Transferables, HC: HttpClient> {
|
||||
sender: Sender<Transferable<T>>,
|
||||
source_client: SourceClient<HC>,
|
||||
}
|
||||
|
||||
impl<T: Transferables> Context<T> for TokioContext<T>
|
||||
impl<T: Transferables, HC: HttpClient> Context<T, HC> for TokioContext<T, HC>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
@ -20,46 +24,55 @@ where
|
||||
self.sender.send(data).unwrap();
|
||||
log::debug!("sent");
|
||||
}
|
||||
|
||||
fn source_client(&self) -> &SourceClient<HC> {
|
||||
&self.source_client
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TokioAsyncProcedureCall<T: Transferables> {
|
||||
channel: (Sender<Transferable<T>>, Receiver<Transferable<T>>),
|
||||
pub struct TokioAsyncProcedureCall {
|
||||
channel: (
|
||||
Sender<Transferable<DefaultTransferables>>,
|
||||
Receiver<Transferable<DefaultTransferables>>,
|
||||
),
|
||||
pool: LocalPoolHandle,
|
||||
}
|
||||
|
||||
impl<T: Transferables> TokioAsyncProcedureCall<T> {
|
||||
impl TokioAsyncProcedureCall {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
channel: mpsc::channel(),
|
||||
pool: LocalPoolHandle::new(4),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transferables> AsyncProcedureCall<T> for TokioAsyncProcedureCall<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
type Context = TokioContext<T>;
|
||||
impl<HC: HttpClient> AsyncProcedureCall<DefaultTransferables, HC> for TokioAsyncProcedureCall {
|
||||
type Context = TokioContext<DefaultTransferables, HC>;
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
channel: mpsc::channel(),
|
||||
}
|
||||
}
|
||||
|
||||
fn receive(&self) -> Option<Transferable<T>> {
|
||||
fn receive(&mut self) -> Option<Box<Transferable<DefaultTransferables>>> {
|
||||
let transferred = self.channel.1.try_recv().ok()?;
|
||||
log::debug!("received");
|
||||
Some(transferred)
|
||||
Some(Box::new(transferred))
|
||||
}
|
||||
|
||||
fn schedule<I: Serialize + Send + 'static>(
|
||||
fn schedule(
|
||||
&self,
|
||||
input: I,
|
||||
procedure: AsyncProcedure<I, TokioContext<T>>,
|
||||
input: Input,
|
||||
procedure: AsyncProcedure<DefaultTransferables, HC>,
|
||||
http_client: HttpSourceClient<HC>,
|
||||
) {
|
||||
let sender = self.channel.0.clone();
|
||||
tokio::task::spawn(async move {
|
||||
(procedure)(input, TokioContext { sender }).await;
|
||||
|
||||
self.pool.spawn_pinned(move || async move {
|
||||
(procedure)(
|
||||
input,
|
||||
Box::new(TokioContext {
|
||||
sender,
|
||||
source_client: SourceClient::Http(http_client),
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,7 +187,6 @@ impl UploadStage {
|
||||
StoredLayer::TessellatedLayer {
|
||||
coords,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
buffer,
|
||||
..
|
||||
} => {
|
||||
@ -197,17 +196,16 @@ impl UploadStage {
|
||||
);
|
||||
|
||||
let guard = allocate_feature_metadata.enter();
|
||||
let feature_metadata = layer_data
|
||||
.features
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, _feature)| {
|
||||
iter::repeat(ShaderFeatureStyle {
|
||||
color: color.unwrap(),
|
||||
let feature_metadata =
|
||||
(0..feature_indices.len()) // FIXME: Iterate over actual featrues
|
||||
.enumerate()
|
||||
.flat_map(|(i, _feature)| {
|
||||
iter::repeat(ShaderFeatureStyle {
|
||||
color: color.unwrap(),
|
||||
})
|
||||
.take(feature_indices[i] as usize)
|
||||
})
|
||||
.take(feature_indices[i] as usize)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
drop(guard);
|
||||
|
||||
tracing::trace!("Allocating geometry at {}", &coords);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
//! [Stages](Stage) for requesting and preparing data
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
|
||||
@ -13,7 +14,6 @@ use crate::io::transferables::{
|
||||
DefaultTessellatedLayer, DefaultTileTessellated, DefaultUnavailableLayer, TessellatedLayer,
|
||||
TileTessellated, UnavailableLayer,
|
||||
};
|
||||
use crate::platform::apc::{TokioAsyncProcedureCall, TokioContext};
|
||||
use crate::{
|
||||
coords::{WorldCoords, WorldTileCoords, Zoom, ZoomLevel},
|
||||
error::Error,
|
||||
@ -38,10 +38,8 @@ mod request_stage;
|
||||
pub fn register_stages<E: Environment>(
|
||||
schedule: &mut Schedule,
|
||||
http_source_client: HttpSourceClient<E::HttpClient>,
|
||||
scheduler: Box<E::Scheduler>,
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
) {
|
||||
let apc = Rc::new(E::AsyncProcedureCall::new());
|
||||
|
||||
schedule.add_stage(
|
||||
"request",
|
||||
RequestStage::<E>::new(http_source_client, apc.clone()),
|
||||
@ -50,7 +48,7 @@ pub fn register_stages<E: Environment>(
|
||||
}
|
||||
|
||||
pub struct HeadedPipelineProcessor<E: Environment> {
|
||||
context: <E::AsyncProcedureCall as AsyncProcedureCall<E::Transferables>>::Context,
|
||||
context: Box<dyn Context<E::Transferables, E::HttpClient>>, // TODO: remove box
|
||||
}
|
||||
|
||||
impl<E: Environment> PipelineProcessor for HeadedPipelineProcessor<E> {
|
||||
@ -76,7 +74,6 @@ impl<E: Environment> PipelineProcessor for HeadedPipelineProcessor<E> {
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: tile::Layer,
|
||||
) {
|
||||
log::info!("layer_tesselation_finished");
|
||||
self.context.send(Transferable::TessellatedLayer(
|
||||
<E::Transferables as Transferables>::TessellatedLayer::new(
|
||||
*coords,
|
||||
@ -99,6 +96,7 @@ impl<E: Environment> PipelineProcessor for HeadedPipelineProcessor<E> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: clean this up
|
||||
///// Stores and provides access to the thread safe data shared between the schedulers.
|
||||
//[derive(Clone)]
|
||||
//pub struct SharedThreadState {
|
||||
|
||||
@ -3,14 +3,17 @@
|
||||
use crate::io::apc::{AsyncProcedureCall, Transferable};
|
||||
use crate::io::transferables::{TessellatedLayer, TileTessellated, UnavailableLayer};
|
||||
use crate::{context::MapContext, io::tile_repository::StoredLayer, schedule::Stage, Environment};
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct PopulateTileStore<E: Environment> {
|
||||
apc: Rc<E::AsyncProcedureCall>,
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
}
|
||||
|
||||
impl<E: Environment> PopulateTileStore<E> {
|
||||
pub fn new(apc: Rc<E::AsyncProcedureCall>) -> Self {
|
||||
pub fn new(apc: Rc<RefCell<E::AsyncProcedureCall>>) -> Self {
|
||||
Self { apc }
|
||||
}
|
||||
}
|
||||
@ -22,30 +25,39 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
|
||||
tile_repository, ..
|
||||
}: &mut MapContext,
|
||||
) {
|
||||
if let Some(result) = self.apc.receive() {
|
||||
match result {
|
||||
Transferable::TileTessellated(tranferred) => {
|
||||
let coords = tranferred.coords();
|
||||
tile_repository.success(coords);
|
||||
tracing::trace!("Tile at {} finished loading", coords);
|
||||
}
|
||||
Transferable::UnavailableLayer(tranferred) => {
|
||||
let layer: StoredLayer = tranferred.to_stored_layer();
|
||||
tracing::debug!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
}
|
||||
Transferable::TessellatedLayer(data) => {
|
||||
let layer: StoredLayer = data.to_stored_layer();
|
||||
tracing::debug!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
if let Ok(mut apc) = self.apc.deref().try_borrow_mut() {
|
||||
if let Some(result) = apc.receive() {
|
||||
match *result {
|
||||
Transferable::TileTessellated(tranferred) => {
|
||||
let coords = tranferred.coords();
|
||||
tile_repository.success(coords);
|
||||
tracing::trace!("Tile at {} finished loading", coords);
|
||||
log::warn!("Tile at {} finished loading", coords);
|
||||
}
|
||||
// FIXME: deduplicate
|
||||
Transferable::UnavailableLayer(tranferred) => {
|
||||
let layer: StoredLayer = tranferred.to_stored_layer();
|
||||
tracing::debug!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
}
|
||||
Transferable::TessellatedLayer(data) => {
|
||||
let layer: StoredLayer = data.to_stored_layer();
|
||||
tracing::debug!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
log::warn!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
//! Requests tiles which are currently in view
|
||||
|
||||
use crate::coords::ZoomLevel;
|
||||
use crate::io::apc::AsyncProcedureCall;
|
||||
use crate::io::apc::{AsyncProcedureCall, Context, Input};
|
||||
use crate::io::pipeline::PipelineContext;
|
||||
use crate::io::pipeline::Processable;
|
||||
use crate::io::tile_pipelines::build_vector_tile_pipeline;
|
||||
use crate::platform::http_client::ReqwestHttpClient;
|
||||
use crate::stages::HeadedPipelineProcessor;
|
||||
use crate::{
|
||||
context::MapContext,
|
||||
@ -19,22 +18,25 @@ use crate::{
|
||||
schedule::Stage,
|
||||
Environment, HttpClient, Scheduler, Style,
|
||||
};
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::process::Output;
|
||||
use std::rc::Rc;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub struct RequestStage<E: Environment> {
|
||||
apc: Rc<E::AsyncProcedureCall>,
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
http_source_client: HttpSourceClient<E::HttpClient>,
|
||||
}
|
||||
|
||||
impl<E: Environment> RequestStage<E> {
|
||||
pub fn new(
|
||||
http_source_client: HttpSourceClient<E::HttpClient>,
|
||||
apc: Rc<E::AsyncProcedureCall>,
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
apc,
|
||||
@ -68,13 +70,19 @@ impl<E: Environment> Stage for RequestStage<E> {
|
||||
}
|
||||
|
||||
pub fn schedule<E: Environment>(
|
||||
input: TileRequest,
|
||||
context: <E::AsyncProcedureCall as AsyncProcedureCall<E::Transferables>>::Context,
|
||||
) -> Pin<Box<(dyn Future<Output = ()> + Send + 'static)>> {
|
||||
input: Input,
|
||||
context: Box<dyn Context<E::Transferables, E::HttpClient>>, // TODO: remove box
|
||||
) -> Pin<Box<(dyn Future<Output = ()> + 'static)>> {
|
||||
// FIXME: improve input handling
|
||||
let input = match input {
|
||||
Input::TileRequest(input) => Some(input),
|
||||
_ => None,
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
Box::pin(async move {
|
||||
let coords = input.coords;
|
||||
let client = SourceClient::Http(HttpSourceClient::new(ReqwestHttpClient::new(None)));
|
||||
let request_id = 0;
|
||||
let client = context.source_client();
|
||||
|
||||
match client.fetch(&coords).await {
|
||||
Ok(data) => {
|
||||
@ -131,12 +139,13 @@ impl<E: Environment> RequestStage<E> {
|
||||
tile_repository.create_tile(coords);
|
||||
|
||||
tracing::info!("new tile request: {}", &coords);
|
||||
self.apc.schedule(
|
||||
TileRequest {
|
||||
self.apc.deref().borrow().schedule(
|
||||
Input::TileRequest(TileRequest {
|
||||
coords: *coords,
|
||||
layers: layers.clone(),
|
||||
},
|
||||
}),
|
||||
schedule::<E>,
|
||||
self.http_source_client.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -56,6 +56,21 @@ impl<V, I> OverAlignedVertexBuffer<V, I> {
|
||||
usable_indices: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_slices(vertices: &[V], indices: &[I], usable_indices: u32) -> Self
|
||||
where
|
||||
V: Copy,
|
||||
I: Copy,
|
||||
{
|
||||
// FIXME, make this fn not needed
|
||||
let mut buffers = VertexBuffers::with_capacity(0, 0);
|
||||
buffers.vertices = Vec::from(vertices);
|
||||
buffers.indices = Vec::from(indices);
|
||||
Self {
|
||||
buffer: buffers,
|
||||
usable_indices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Pod, I: Pod> From<VertexBuffers<V, I>> for OverAlignedVertexBuffer<V, I> {
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
//! Utilities for the window system.
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{Environment, HttpClient, InteractiveMapSchedule};
|
||||
|
||||
@ -28,7 +30,8 @@ pub trait MapWindowConfig: 'static {
|
||||
/// The event loop is responsible for processing events and propagating them to the map renderer.
|
||||
/// Only non-headless windows use an [`EventLoop`].
|
||||
pub trait EventLoop<E: Environment> {
|
||||
fn run(self, map_schedule: InteractiveMapSchedule<E>, max_frames: Option<u64>);
|
||||
// FIXME: Avoid Rc, change ownership model
|
||||
fn run(self, map_schedule: Rc<RefCell<InteractiveMapSchedule<E>>>, max_frames: Option<u64>);
|
||||
}
|
||||
|
||||
/// Window size with a width and an height in pixels.
|
||||
|
||||
@ -38,6 +38,10 @@ wasm-bindgen = "0.2.81"
|
||||
wasm-bindgen-futures = "0.4.31"
|
||||
console_log = { version = "0.2.0", features = ["color"] }
|
||||
tracing-wasm = { version = "0.2.1", optional = true } # FIXME: Low quality dependency
|
||||
serde = "1.0.144" # FIXME: Remove
|
||||
serde_json = "1.0.85"
|
||||
bytemuck = "1.12.1"
|
||||
bytemuck_derive = "1.2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.31"
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import {create_pool_scheduler, run} from "../wasm/maplibre"
|
||||
import {run} from "../wasm/maplibre"
|
||||
import {Spector} from "spectorjs"
|
||||
import {checkRequirements, checkWasmFeatures} from "../browser";
|
||||
import init from "../wasm/maplibre";
|
||||
import {preventDefaultTouchActions} from "../canvas";
|
||||
// @ts-ignore esbuild plugin is handling this
|
||||
import PoolWorker from './pool.worker.js';
|
||||
import PoolWorker from './multithreaded-pool.worker.js';
|
||||
|
||||
const initializeSharedModule = async (wasmPath) => {
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
@ -33,11 +33,9 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
|
||||
|
||||
await initializeSharedModule(wasmPath);
|
||||
|
||||
const schedulerPtr = create_pool_scheduler(() => {
|
||||
await run(() => {
|
||||
return workerPath ? new Worker(workerPath, {
|
||||
type: 'module'
|
||||
}) : PoolWorker();
|
||||
})
|
||||
|
||||
await run(schedulerPtr)
|
||||
}
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
import init, {worker_entry} from "../wasm/maplibre"
|
||||
|
||||
const initializeExisting = async (module: string, memory?: string) => {
|
||||
await init(module, memory)
|
||||
}
|
||||
import init, {sync_worker_entry} from "../wasm/maplibre"
|
||||
|
||||
onmessage = async message => {
|
||||
const initialised = initializeExisting(message.data[0], message.data[1]).catch(err => {
|
||||
const initialised = init(message.data[0], message.data[1]).catch(err => {
|
||||
// Propagate to main `onerror`:
|
||||
setTimeout(() => {
|
||||
throw err;
|
||||
@ -17,6 +13,6 @@ onmessage = async message => {
|
||||
self.onmessage = async message => {
|
||||
// This will queue further commands up until the module is fully initialised:
|
||||
await initialised;
|
||||
await worker_entry(message.data);
|
||||
await sync_worker_entry(message.data);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import init, {create_scheduler, run} from "../wasm/maplibre"
|
||||
import init, {run, create_map, clone_map, unsync_main_entry} from "../wasm/maplibre"
|
||||
import {Spector} from "spectorjs"
|
||||
import {checkRequirements, checkWasmFeatures} from "../browser";
|
||||
import {preventDefaultTouchActions} from "../canvas";
|
||||
@ -22,31 +22,29 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
|
||||
}
|
||||
|
||||
preventDefaultTouchActions();
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: false})
|
||||
await init(wasmPath, memory);
|
||||
|
||||
await init(wasmPath);
|
||||
let callback = [undefined]
|
||||
|
||||
const schedulerPtr = create_scheduler(() => {
|
||||
let map = await create_map(() => {
|
||||
let worker = workerPath ? new Worker(workerPath, {
|
||||
type: 'module'
|
||||
}) : PoolWorker();
|
||||
|
||||
let memories = []
|
||||
|
||||
worker.onmessage = (message) => {
|
||||
console.warn("new message");
|
||||
//let uint8Array = new Uint8Array(message.data[0], message.data[1]);
|
||||
|
||||
memories.push(message.data[0])
|
||||
|
||||
|
||||
console.warn(memories.map(v => new Uint8Array(v, message.data[1])[0]));
|
||||
console.warn(memories[0] == memories[1]);
|
||||
|
||||
worker.postMessage("test")
|
||||
worker.onmessage = (message) => {
|
||||
callback[0](message)
|
||||
}
|
||||
|
||||
return worker;
|
||||
})
|
||||
|
||||
await run(schedulerPtr)
|
||||
let clonedMap = clone_map(map)
|
||||
|
||||
callback[0] = (message) => {
|
||||
unsync_main_entry(clonedMap, message.data[0], new Uint8Array(message.data[1]))
|
||||
}
|
||||
|
||||
run(map)
|
||||
}
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import init, {worker_entry} from "../wasm/maplibre"
|
||||
import init, {unsync_worker_entry} from "../wasm/maplibre"
|
||||
|
||||
const initializeExisting = async (module: string) => {
|
||||
await init(module)
|
||||
|
||||
}
|
||||
|
||||
onmessage = async message => {
|
||||
const initialised = initializeExisting(message.data[0]).catch(err => {
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: false})
|
||||
const initialised = await init(message.data[0], memory).catch(err => {
|
||||
// Propagate to main `onerror`:
|
||||
setTimeout(() => {
|
||||
throw err;
|
||||
@ -17,6 +19,8 @@ onmessage = async message => {
|
||||
self.onmessage = async message => {
|
||||
// This will queue further commands up until the module is fully initialised:
|
||||
await initialised;
|
||||
await worker_entry();
|
||||
let procedure_ptr = message.data[0];
|
||||
let input = message.data[1];
|
||||
await unsync_worker_entry(procedure_ptr, input);
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
use std::panic;
|
||||
#![feature(allocator_api, new_uninit)]
|
||||
|
||||
use maplibre::{io::scheduler::Scheduler, MapBuilder};
|
||||
use std::borrow::BorrowMut;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::{mem, panic};
|
||||
|
||||
use maplibre::io::scheduler::NopScheduler;
|
||||
use maplibre::{Map, MapBuilder};
|
||||
use maplibre_winit::winit::{WinitEnvironment, WinitMapWindowConfig};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::platform::unsync::UnsyncScheduler;
|
||||
use crate::platform::{http_client::WHATWGFetchHttpClient, NopScheduler};
|
||||
use crate::platform::http_client::WHATWGFetchHttpClient;
|
||||
use crate::platform::unsync::apc::PassingAsyncProcedureCall;
|
||||
use crate::platform::unsync::transferables::LinearTransferables;
|
||||
use crate::platform::AsyncProcedureCall;
|
||||
|
||||
mod error;
|
||||
mod platform;
|
||||
@ -13,8 +22,6 @@ mod platform;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
compile_error!("web works only on wasm32.");
|
||||
|
||||
type CurrentScheduler = UnsyncScheduler;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
fn enable_tracing() {
|
||||
use tracing_subscriber::{layer::SubscriberExt, Registry};
|
||||
@ -39,27 +46,52 @@ pub fn wasm_bindgen_start() {
|
||||
enable_tracing();
|
||||
}
|
||||
|
||||
pub type MapType = Map<
|
||||
WinitEnvironment<
|
||||
NopScheduler,
|
||||
WHATWGFetchHttpClient,
|
||||
LinearTransferables,
|
||||
PassingAsyncProcedureCall,
|
||||
>,
|
||||
>;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_scheduler(new_worker: js_sys::Function) -> *mut CurrentScheduler {
|
||||
let scheduler = UnsyncScheduler::new(new_worker);
|
||||
Box::into_raw(Box::new(scheduler))
|
||||
pub async fn create_map(new_worker: js_sys::Function) -> u32 {
|
||||
#[cfg(target_feature = "atomics")]
|
||||
let scheduler = platform::Scheduler::new(new_worker.clone());
|
||||
#[cfg(target_feature = "atomics")]
|
||||
let apc = AsyncProcedureCall::new(scheduler);
|
||||
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
let apc = AsyncProcedureCall::new(new_worker);
|
||||
|
||||
// Either call forget or the main loop to keep worker loop alive
|
||||
let map: MapType = MapBuilder::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(WHATWGFetchHttpClient::new())
|
||||
.with_scheduler(NopScheduler)
|
||||
.with_apc(apc)
|
||||
.build()
|
||||
.initialize()
|
||||
.await;
|
||||
|
||||
Rc::into_raw(Rc::new(RefCell::new(map))) as u32
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn run(scheduler_ptr: *mut CurrentScheduler) {
|
||||
let scheduler = unsafe { Box::from_raw(scheduler_ptr) };
|
||||
pub fn clone_map(map_ptr: *const RefCell<MapType>) -> *const RefCell<MapType> {
|
||||
let mut map = unsafe { Rc::from_raw(map_ptr) };
|
||||
let rc = map.clone();
|
||||
let cloned = Rc::into_raw(rc);
|
||||
mem::forget(map);
|
||||
cloned
|
||||
}
|
||||
|
||||
// Either call forget or the main loop to keep worker loop alive
|
||||
MapBuilder::<WinitEnvironment<_, _>>::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(WHATWGFetchHttpClient::new())
|
||||
.with_scheduler(*scheduler)
|
||||
.build()
|
||||
.initialize()
|
||||
.await
|
||||
.run();
|
||||
#[wasm_bindgen]
|
||||
pub fn run(map_ptr: *const RefCell<MapType>) {
|
||||
let mut map = unsafe { Rc::from_raw(map_ptr) };
|
||||
|
||||
// std::mem::forget(scheduler);
|
||||
map.deref().borrow().run();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
use maplibre::error::Error;
|
||||
use maplibre::io::scheduler::Scheduler;
|
||||
use std::future::Future;
|
||||
|
||||
pub mod http_client;
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub mod sync;
|
||||
#[cfg(target_feature = "atomics")]
|
||||
type Scheduler = sync::pool_scheduler::WebWorkerPoolScheduler;
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub type AsyncProcedureCall = sync::apc::AtomicAsyncProcedureCall;
|
||||
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
pub mod unsync;
|
||||
|
||||
pub struct NopScheduler;
|
||||
|
||||
impl Scheduler for NopScheduler {
|
||||
fn schedule<T>(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
Err(Error::Schedule)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
pub type AsyncProcedureCall = unsync::apc::PassingAsyncProcedureCall;
|
||||
|
||||
78
web/src/platform/sync/apc.rs
Normal file
78
web/src/platform/sync/apc.rs
Normal file
@ -0,0 +1,78 @@
|
||||
use crate::platform::sync::pool_scheduler::WebWorkerPoolScheduler;
|
||||
use maplibre::environment::DefaultTransferables;
|
||||
use maplibre::environment::Environment;
|
||||
use maplibre::io::apc::{AsyncProcedure, AsyncProcedureCall, Context, Transferable};
|
||||
use maplibre::io::scheduler::Scheduler;
|
||||
use maplibre::io::source_client::{HttpClient, HttpSourceClient, SourceClient};
|
||||
use maplibre::io::transferables::Transferables;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AtomicContext<T: Transferables, HC: HttpClient> {
|
||||
sender: Sender<Transferable<T>>,
|
||||
source_client: SourceClient<HC>,
|
||||
}
|
||||
|
||||
impl<T: Transferables, HC: HttpClient> Context<T, HC> for AtomicContext<T, HC>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
fn send(&self, data: Transferable<T>) {
|
||||
self.sender.send(data).unwrap();
|
||||
}
|
||||
|
||||
fn source_client(&self) -> &SourceClient<HC> {
|
||||
&self.source_client
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AtomicAsyncProcedureCall {
|
||||
channel: (
|
||||
Sender<Transferable<DefaultTransferables>>,
|
||||
Receiver<Transferable<DefaultTransferables>>,
|
||||
),
|
||||
scheduler: WebWorkerPoolScheduler,
|
||||
}
|
||||
|
||||
impl AtomicAsyncProcedureCall {
|
||||
pub fn new(scheduler: WebWorkerPoolScheduler) -> Self {
|
||||
Self {
|
||||
channel: mpsc::channel(),
|
||||
scheduler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<HC: HttpClient> AsyncProcedureCall<DefaultTransferables, HC> for AtomicAsyncProcedureCall {
|
||||
type Context = AtomicContext<DefaultTransferables, HC>;
|
||||
|
||||
fn receive(&self) -> Option<Transferable<DefaultTransferables>> {
|
||||
let transferred = self.channel.1.try_recv().ok()?;
|
||||
Some(transferred)
|
||||
}
|
||||
|
||||
fn schedule<I: Serialize + Send + 'static>(
|
||||
&self,
|
||||
input: I,
|
||||
procedure: AsyncProcedure<I, AtomicContext<DefaultTransferables, HC>>,
|
||||
http_client: HttpSourceClient<HC>,
|
||||
) {
|
||||
let sender = self.channel.0.clone();
|
||||
|
||||
self.scheduler
|
||||
.schedule(move || async move {
|
||||
(procedure)(
|
||||
input,
|
||||
AtomicContext {
|
||||
sender,
|
||||
source_client: SourceClient::Http(http_client),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@ -1,2 +1,3 @@
|
||||
pub mod apc;
|
||||
pub mod pool;
|
||||
pub mod pool_scheduler;
|
||||
|
||||
@ -148,7 +148,7 @@ impl PoolState {
|
||||
|
||||
/// Entry point invoked by the worker.
|
||||
#[wasm_bindgen]
|
||||
pub async fn worker_entry(ptr: u32) -> Result<(), JsValue> {
|
||||
pub async fn sync_worker_entry(ptr: u32) -> Result<(), JsValue> {
|
||||
let ptr = unsafe { Box::from_raw(ptr as *mut Work) };
|
||||
JsFuture::from((ptr.func)()).await?;
|
||||
Ok(())
|
||||
|
||||
186
web/src/platform/unsync/apc.rs
Normal file
186
web/src/platform/unsync/apc.rs
Normal file
@ -0,0 +1,186 @@
|
||||
use crate::platform::unsync::transferables::{
|
||||
InnerData, LinearTessellatedLayer, LinearTransferables,
|
||||
};
|
||||
use crate::{MapType, WHATWGFetchHttpClient};
|
||||
use js_sys::Uint8Array;
|
||||
use maplibre::environment::Environment;
|
||||
use maplibre::io::apc::{AsyncProcedure, AsyncProcedureCall, Context, Input, Transferable};
|
||||
use maplibre::io::scheduler::Scheduler;
|
||||
use maplibre::io::source_client::{HttpClient, HttpSourceClient, SourceClient};
|
||||
use maplibre::io::transferables::Transferables;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::mem::{size_of, MaybeUninit};
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{DedicatedWorkerGlobalScope, Worker};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PassingContext<HC: HttpClient> {
|
||||
source_client: SourceClient<HC>,
|
||||
}
|
||||
|
||||
impl<HC: HttpClient> Context<LinearTransferables, HC> for PassingContext<HC> {
|
||||
fn send(&self, data: Transferable<LinearTransferables>) {
|
||||
// TODO: send back to main thread via postMessage
|
||||
|
||||
let (tag, serialized): (u32, &[u8]) = match &data {
|
||||
Transferable::TileTessellated(data) => (1, bytemuck::bytes_of(data)),
|
||||
Transferable::UnavailableLayer(data) => (2, bytemuck::bytes_of(data)),
|
||||
Transferable::TessellatedLayer(data) => (3, bytemuck::bytes_of(data.data.as_ref())),
|
||||
};
|
||||
|
||||
let serialized_array_buffer = js_sys::ArrayBuffer::new(serialized.len() as u32);
|
||||
let serialized_array = js_sys::Uint8Array::new(&serialized_array_buffer);
|
||||
unsafe {
|
||||
serialized_array.set(&Uint8Array::view(serialized), 0);
|
||||
}
|
||||
|
||||
let global = js_sys::global().unchecked_into::<DedicatedWorkerGlobalScope>();
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&JsValue::from(tag));
|
||||
array.push(&serialized_array_buffer);
|
||||
global.post_message(&array).unwrap();
|
||||
}
|
||||
|
||||
fn source_client(&self) -> &SourceClient<HC> {
|
||||
&self.source_client
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PassingAsyncProcedureCall {
|
||||
new_worker: Box<dyn Fn() -> Worker>,
|
||||
workers: Vec<Worker>,
|
||||
|
||||
received: Vec<Box<Transferable<LinearTransferables>>>,
|
||||
}
|
||||
|
||||
impl PassingAsyncProcedureCall {
|
||||
pub fn new(new_worker: js_sys::Function) -> Self {
|
||||
let create_new_worker = Box::new(move || {
|
||||
new_worker
|
||||
.call0(&JsValue::undefined())
|
||||
.unwrap()
|
||||
.dyn_into::<Worker>()
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let worker = create_new_worker();
|
||||
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&wasm_bindgen::module());
|
||||
worker.post_message(&array).unwrap();
|
||||
|
||||
Self {
|
||||
new_worker: create_new_worker,
|
||||
workers: vec![worker],
|
||||
received: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<HC: HttpClient> AsyncProcedureCall<LinearTransferables, HC> for PassingAsyncProcedureCall {
|
||||
type Context = PassingContext<HC>;
|
||||
|
||||
fn receive(&mut self) -> Option<Box<Transferable<LinearTransferables>>> {
|
||||
self.received.pop()
|
||||
}
|
||||
|
||||
fn schedule(
|
||||
&self,
|
||||
input: Input,
|
||||
procedure: AsyncProcedure<LinearTransferables, HC>,
|
||||
http_client: HttpSourceClient<HC>, // FIXME
|
||||
) {
|
||||
let procedure_ptr =
|
||||
procedure as *mut AsyncProcedure<LinearTransferables, WHATWGFetchHttpClient> as u32; // TODO: is u32 fine?
|
||||
let input = serde_json::to_string(&input).unwrap();
|
||||
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&JsValue::from(procedure_ptr));
|
||||
array.push(&JsValue::from(input));
|
||||
|
||||
self.workers[0].post_message(&array).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry point invoked by the worker.
|
||||
#[wasm_bindgen]
|
||||
pub async fn unsync_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JsValue> {
|
||||
log::info!("worker_entry unsync");
|
||||
|
||||
let procedure: AsyncProcedure<LinearTransferables, WHATWGFetchHttpClient> =
|
||||
unsafe { std::mem::transmute(procedure_ptr) };
|
||||
|
||||
let input = serde_json::from_str::<Input>(&input).unwrap();
|
||||
|
||||
let context = PassingContext {
|
||||
source_client: SourceClient::Http(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
|
||||
};
|
||||
|
||||
(procedure)(input, Box::new(context)).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Entry point invoked by the main thread.
|
||||
#[wasm_bindgen]
|
||||
pub fn unsync_main_entry(
|
||||
map_ptr: *const RefCell<MapType>,
|
||||
tag: u32,
|
||||
data: Uint8Array,
|
||||
) -> Result<(), JsValue> {
|
||||
let mut map = unsafe { Rc::from_raw(map_ptr) };
|
||||
|
||||
let transferred = match tag {
|
||||
3 => Some(Transferable::<LinearTransferables>::TessellatedLayer(
|
||||
LinearTessellatedLayer {
|
||||
data: unsafe {
|
||||
let mut uninit = Box::<InnerData>::new_zeroed();
|
||||
data.raw_copy_to_ptr(uninit.as_mut_ptr() as *mut u8);
|
||||
let x = uninit.assume_init();
|
||||
|
||||
x
|
||||
},
|
||||
},
|
||||
)),
|
||||
1 => Some(Transferable::<LinearTransferables>::TileTessellated(
|
||||
*bytemuck::from_bytes::<<LinearTransferables as Transferables>::TileTessellated>(
|
||||
&data.to_vec(),
|
||||
),
|
||||
)),
|
||||
2 => Some(Transferable::<LinearTransferables>::UnavailableLayer(
|
||||
*bytemuck::from_bytes::<<LinearTransferables as Transferables>::UnavailableLayer>(
|
||||
&data.to_vec(),
|
||||
),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
// FIXME: avoid this borrow mess
|
||||
map.deref()
|
||||
.borrow()
|
||||
.map_schedule()
|
||||
.deref()
|
||||
.borrow()
|
||||
.apc
|
||||
.deref()
|
||||
.borrow_mut()
|
||||
.received
|
||||
.push(Box::new(transferred)); // FIXME: remove box
|
||||
|
||||
mem::forget(map);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1,95 +1,2 @@
|
||||
use js_sys::{ArrayBuffer, Uint8Array};
|
||||
use maplibre::error::Error;
|
||||
use maplibre::io::scheduler::Scheduler;
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::{DedicatedWorkerGlobalScope, Worker};
|
||||
|
||||
pub struct UnsyncScheduler {
|
||||
new_worker: Box<dyn Fn() -> Worker>,
|
||||
workers: RefCell<Vec<Worker>>,
|
||||
}
|
||||
|
||||
impl UnsyncScheduler {
|
||||
pub fn new(new_worker: js_sys::Function) -> Self {
|
||||
let create_new_worker = Box::new(move || {
|
||||
new_worker
|
||||
.call0(&JsValue::undefined())
|
||||
.unwrap()
|
||||
.dyn_into::<Worker>()
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let worker = create_new_worker();
|
||||
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&wasm_bindgen::module());
|
||||
worker.post_message(&array).unwrap();
|
||||
|
||||
log::info!("new unsync");
|
||||
|
||||
Self {
|
||||
new_worker: create_new_worker,
|
||||
workers: RefCell::new(vec![worker]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheduler for UnsyncScheduler {
|
||||
fn schedule<T>(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
self.workers.borrow()[0]
|
||||
.post_message(&JsValue::undefined())
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static VEC: RefCell<Vec<u8>> = RefCell::new(vec![165u8, 162, 145, 224, 111]);
|
||||
}
|
||||
|
||||
/// Entry point invoked by the worker.
|
||||
#[wasm_bindgen]
|
||||
pub async fn worker_entry() -> Result<(), JsValue> {
|
||||
log::info!("worker_entry unsync");
|
||||
|
||||
//let vec = vec![165u8, 162, 145, 224, 111];
|
||||
|
||||
VEC.with(|d| {
|
||||
let mut ref_mut = d.borrow_mut();
|
||||
ref_mut[0] += 1;
|
||||
|
||||
unsafe {
|
||||
let uint8 = Uint8Array::view(&ref_mut);
|
||||
|
||||
let array_buffer = uint8.buffer();
|
||||
|
||||
log::info!(
|
||||
"{}",
|
||||
array_buffer
|
||||
== wasm_bindgen::memory()
|
||||
.dyn_into::<js_sys::WebAssembly::Memory>()
|
||||
.unwrap()
|
||||
.buffer()
|
||||
.dyn_into::<ArrayBuffer>()
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let global = js_sys::global().unchecked_into::<DedicatedWorkerGlobalScope>();
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&array_buffer);
|
||||
array.push(&JsValue::from(uint8.byte_offset()));
|
||||
global.post_message(&array).unwrap();
|
||||
};
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub mod apc;
|
||||
pub mod transferables;
|
||||
|
||||
156
web/src/platform/unsync/transferables.rs
Normal file
156
web/src/platform/unsync/transferables.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use bytemuck::{TransparentWrapper, Zeroable};
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use maplibre::benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer};
|
||||
use maplibre::coords::WorldTileCoords;
|
||||
use maplibre::io::tile_repository::StoredLayer;
|
||||
use maplibre::io::transferables::{
|
||||
TessellatedLayer, TileTessellated, Transferables, UnavailableLayer,
|
||||
};
|
||||
use maplibre::render::ShaderVertex;
|
||||
use maplibre::tile::Layer;
|
||||
|
||||
// FIXME: properly do this!, fix this whole file
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct WrapperWorldTileCoords(WorldTileCoords);
|
||||
unsafe impl TransparentWrapper<WorldTileCoords> for WrapperWorldTileCoords {}
|
||||
unsafe impl bytemuck::Zeroable for WrapperWorldTileCoords {}
|
||||
unsafe impl bytemuck::Pod for WrapperWorldTileCoords {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct LongVertexShader([ShaderVertex; 15000]);
|
||||
unsafe impl TransparentWrapper<[ShaderVertex; 15000]> for LongVertexShader {}
|
||||
unsafe impl bytemuck::Zeroable for LongVertexShader {}
|
||||
unsafe impl bytemuck::Pod for LongVertexShader {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct LongIndices([IndexDataType; 40000]);
|
||||
unsafe impl TransparentWrapper<[IndexDataType; 40000]> for LongIndices {}
|
||||
unsafe impl bytemuck::Zeroable for LongIndices {}
|
||||
unsafe impl bytemuck::Pod for LongIndices {}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct LinearTileTessellated {
|
||||
pub coords: WrapperWorldTileCoords,
|
||||
}
|
||||
|
||||
impl TileTessellated for LinearTileTessellated {
|
||||
fn new(coords: WorldTileCoords) -> Self {
|
||||
Self {
|
||||
coords: WrapperWorldTileCoords::wrap(coords),
|
||||
}
|
||||
}
|
||||
|
||||
fn coords(&self) -> &WorldTileCoords {
|
||||
WrapperWorldTileCoords::peel_ref(&self.coords)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct LinearUnavailableLayer {
|
||||
pub coords: WrapperWorldTileCoords,
|
||||
pub layer_name: [u8; 32],
|
||||
}
|
||||
|
||||
impl UnavailableLayer for LinearUnavailableLayer {
|
||||
fn new(coords: WorldTileCoords, layer_name: String) -> Self {
|
||||
let mut new_layer_name = [0; 32];
|
||||
new_layer_name[0..layer_name.len()].clone_from_slice(layer_name.as_bytes());
|
||||
Self {
|
||||
coords: WrapperWorldTileCoords::wrap(coords),
|
||||
layer_name: new_layer_name,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
StoredLayer::UnavailableLayer {
|
||||
coords: WrapperWorldTileCoords::peel(self.coords),
|
||||
layer_name: String::from_utf8(Vec::from(self.layer_name)).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct InnerData {
|
||||
pub coords: WrapperWorldTileCoords,
|
||||
pub layer_name: [u8; 32],
|
||||
pub layer_name_len: usize,
|
||||
pub vertices: LongVertexShader,
|
||||
pub vertices_len: usize,
|
||||
pub indices: LongIndices,
|
||||
pub indices_len: usize,
|
||||
pub usable_indices: u32,
|
||||
/// Holds for each feature the count of indices.
|
||||
pub feature_indices: [u32; 2048],
|
||||
pub feature_indices_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LinearTessellatedLayer {
|
||||
pub data: Box<InnerData>,
|
||||
}
|
||||
|
||||
impl TessellatedLayer for LinearTessellatedLayer {
|
||||
fn new(
|
||||
coords: WorldTileCoords,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: Layer,
|
||||
) -> Self {
|
||||
let mut data = Box::new(InnerData {
|
||||
coords: WrapperWorldTileCoords::wrap(coords),
|
||||
|
||||
layer_name: [0; 32],
|
||||
layer_name_len: layer_data.name.len(),
|
||||
|
||||
vertices: LongVertexShader::wrap([ShaderVertex::zeroed(); 15000]),
|
||||
vertices_len: buffer.buffer.vertices.len(),
|
||||
|
||||
indices: LongIndices::wrap([IndexDataType::zeroed(); 40000]),
|
||||
indices_len: buffer.buffer.indices.len(),
|
||||
|
||||
usable_indices: buffer.usable_indices,
|
||||
|
||||
feature_indices: [0u32; 2048],
|
||||
feature_indices_len: feature_indices.len(),
|
||||
});
|
||||
|
||||
data.vertices.0[0..buffer.buffer.vertices.len()].clone_from_slice(&buffer.buffer.vertices);
|
||||
data.indices.0[0..buffer.buffer.indices.len()].clone_from_slice(&buffer.buffer.indices);
|
||||
data.feature_indices[0..feature_indices.len()].clone_from_slice(&feature_indices);
|
||||
data.layer_name[0..layer_data.name.len()].clone_from_slice(layer_data.name.as_bytes());
|
||||
|
||||
Self { data }
|
||||
}
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
let layer = StoredLayer::TessellatedLayer {
|
||||
coords: WrapperWorldTileCoords::peel(self.data.coords),
|
||||
layer_name: String::from_utf8(Vec::from(
|
||||
&self.data.layer_name[..self.data.layer_name_len],
|
||||
))
|
||||
.unwrap(),
|
||||
buffer: OverAlignedVertexBuffer::from_slices(
|
||||
&self.data.vertices.0[..self.data.vertices_len],
|
||||
&self.data.indices.0[..self.data.indices_len],
|
||||
self.data.usable_indices,
|
||||
),
|
||||
feature_indices: Vec::from(&self.data.feature_indices[..self.data.feature_indices_len]),
|
||||
};
|
||||
|
||||
layer
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LinearTransferables;
|
||||
|
||||
impl Transferables for LinearTransferables {
|
||||
type TileTessellated = LinearTileTessellated;
|
||||
type UnavailableLayer = LinearUnavailableLayer;
|
||||
type TessellatedLayer = LinearTessellatedLayer;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user