mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Add documentation
This commit is contained in:
parent
58c852f096
commit
92301d005e
@ -12,6 +12,14 @@ use crate::{
|
|||||||
window::MapWindowConfig,
|
window::MapWindowConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The environment defines which types must be injected into maplibre at compile time.
|
||||||
|
/// Essentially, this trait implements the
|
||||||
|
/// [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) design pattern.
|
||||||
|
/// By instantiating this trait at compile time with concrete types, it is possible to create
|
||||||
|
/// different compile-time instances of maplibre.
|
||||||
|
///
|
||||||
|
/// For example it is possible to change the way tasks are scheduled. It is also possible to change
|
||||||
|
/// the HTTP implementation for fetching tiles over the network.
|
||||||
pub trait Environment: 'static {
|
pub trait Environment: 'static {
|
||||||
type MapWindowConfig: MapWindowConfig;
|
type MapWindowConfig: MapWindowConfig;
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ impl HeadlessMap {
|
|||||||
coords: WorldTileCoords,
|
coords: WorldTileCoords,
|
||||||
source_layers: &[&str],
|
source_layers: &[&str],
|
||||||
) -> Result<StoredTile, Error> {
|
) -> Result<StoredTile, Error> {
|
||||||
let source_client = &self.kernel.source_client;
|
let source_client = self.kernel.source_client();
|
||||||
|
|
||||||
let data = source_client.fetch(&coords).await?.into_boxed_slice();
|
let data = source_client.fetch(&coords).await?.into_boxed_slice();
|
||||||
|
|
||||||
|
|||||||
@ -20,9 +20,12 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The result of the tessellation of a tile.
|
/// The result of the tessellation of a tile. This is sent as a message from a worker to the caller
|
||||||
/// `TessellatedLayer` contains the result of the tessellation for a specific layer, otherwise
|
/// of an [`AsyncProcedure`].
|
||||||
/// `UnavailableLayer` if the layer doesn't exist.
|
///
|
||||||
|
/// * `TessellatedLayer` contains the result of the tessellation for a specific layer.
|
||||||
|
/// * `UnavailableLayer` is sent if a requested layer is not found.
|
||||||
|
/// * `TileTessellated` is sent if processing of a tile finished.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Message<T: Transferables> {
|
pub enum Message<T: Transferables> {
|
||||||
TileTessellated(T::TileTessellated),
|
TileTessellated(T::TileTessellated),
|
||||||
@ -30,12 +33,15 @@ pub enum Message<T: Transferables> {
|
|||||||
TessellatedLayer(T::TessellatedLayer),
|
TessellatedLayer(T::TessellatedLayer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Inputs for an [`AsyncProcedure`]
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub enum Input {
|
pub enum Input {
|
||||||
TileRequest(TileRequest),
|
TileRequest(TileRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows sending messages from workers to back to the caller.
|
||||||
pub trait Context<T: Transferables, HC: HttpClient>: Send + 'static {
|
pub trait Context<T: Transferables, HC: HttpClient>: Send + 'static {
|
||||||
|
/// Send a message back to the caller.
|
||||||
fn send(&self, data: Message<T>);
|
fn send(&self, data: Message<T>);
|
||||||
|
|
||||||
fn source_client(&self) -> &SourceClient<HC>;
|
fn source_client(&self) -> &SourceClient<HC>;
|
||||||
@ -46,15 +52,62 @@ pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + Send + 'stati
|
|||||||
#[cfg(not(feature = "thread-safe-futures"))]
|
#[cfg(not(feature = "thread-safe-futures"))]
|
||||||
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + 'static)>>;
|
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + 'static)>>;
|
||||||
|
|
||||||
|
/// Type definitions for asynchronous procedure calls. These functions can be called in an
|
||||||
|
/// [`AsyncProcedureCall`]. Functions of this type are required to be statically available at
|
||||||
|
/// compile time. It is explicitly not possible to use closures, as they would require special
|
||||||
|
/// serialization which is currently not supported.
|
||||||
pub type AsyncProcedure<C> = fn(input: Input, context: C) -> AsyncProcedureFuture;
|
pub type AsyncProcedure<C> = fn(input: Input, context: C) -> AsyncProcedureFuture;
|
||||||
|
|
||||||
|
/// APCs define an interface for performing work asynchronously.
|
||||||
|
/// This work can be implemented through procedures which can be called asynchronously, hence the
|
||||||
|
/// name AsyncProcedureCall or APC for short.
|
||||||
|
///
|
||||||
|
/// APCs serve as an abstraction for doing work on a separate thread, and then getting responses
|
||||||
|
/// back. An asynchronous procedure call can for example be performed by using message passing. In
|
||||||
|
/// fact this could theoretically work over a network socket.
|
||||||
|
///
|
||||||
|
/// It is possible to schedule work on a remote host by calling [`AsyncProcedureCall::call()`]
|
||||||
|
/// and getting the results back by calling the non-blocking function
|
||||||
|
/// [`AsyncProcedureCall::receive()`]. The [`AsyncProcedureCall::receive()`] function returns a
|
||||||
|
/// struct which implements [`Transferables`].
|
||||||
|
///
|
||||||
|
/// ## Transferables
|
||||||
|
///
|
||||||
|
/// Based on whether the current platform supports shared-memory or not, the implementation of APCs
|
||||||
|
/// might want to send the whole data from the worker to the caller back or just pointers to that
|
||||||
|
/// data. The [`Transferables`] trait allows developers to define that and use different data
|
||||||
|
/// layouts for different platforms.
|
||||||
|
///
|
||||||
|
/// ## Message Passing vs APC
|
||||||
|
///
|
||||||
|
/// One might wonder why this is called [`AsyncProcedureCall`] instead of `MessagePassingInterface`.
|
||||||
|
/// The reason for this is quite simple. We are actually referencing and calling procedures which
|
||||||
|
/// are defined in different threads, processes or hosts. That means, that an [`AsyncProcedureCall`]
|
||||||
|
/// is actually distinct from a `MessagePassingInterface`.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// ## Current Implementations
|
||||||
|
///
|
||||||
|
/// We currently have two implementation for APCs. One uses the Tokio async runtime on native
|
||||||
|
/// targets in [`SchedulerAsyncProcedureCall`].
|
||||||
|
/// For the web we implemented an alternative way to call APCs which is called
|
||||||
|
/// [`PassingAsyncProcedureCall`]. This implementation does not depend on shared-memory compared to
|
||||||
|
/// [`SchedulerAsyncProcedureCall`]. In fact, on the web we are currently not depending on
|
||||||
|
/// shared-memory because that feature is hidden behind feature flags in browsers
|
||||||
|
/// (see [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer).
|
||||||
|
///
|
||||||
|
///
|
||||||
|
// TODO: Rename to AsyncProcedureCaller?
|
||||||
pub trait AsyncProcedureCall<HC: HttpClient>: 'static {
|
pub trait AsyncProcedureCall<HC: HttpClient>: 'static {
|
||||||
type Context: Context<Self::Transferables, HC> + Send;
|
type Context: Context<Self::Transferables, HC> + Send;
|
||||||
type Transferables: Transferables;
|
type Transferables: Transferables;
|
||||||
|
|
||||||
|
/// Try to receive a message non-blocking.
|
||||||
fn receive(&self) -> Option<Message<Self::Transferables>>;
|
fn receive(&self) -> Option<Message<Self::Transferables>>;
|
||||||
|
|
||||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>);
|
/// Call an [`AsyncProcedure`] using some [`Input`]. This function is non-blocking and
|
||||||
|
/// returns immediately.
|
||||||
|
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -101,7 +154,7 @@ impl<HC: HttpClient, S: Scheduler> AsyncProcedureCall<HC> for SchedulerAsyncProc
|
|||||||
Some(transferred)
|
Some(transferred)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||||
let sender = self.channel.0.clone();
|
let sender = self.channel.0.clone();
|
||||||
let client = self.http_client.clone(); // FIXME (wasm-executor): do not clone each time
|
let client = self.http_client.clone(); // FIXME (wasm-executor): do not clone each time
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,40 @@ use crate::{
|
|||||||
io::source_client::{HttpSourceClient, SourceClient},
|
io::source_client::{HttpSourceClient, SourceClient},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Holds references to core constructs of maplibre. Based on the compile-time initialization
|
||||||
|
/// different implementations for handling windows, asynchronous work, or data sources are provided
|
||||||
|
/// through a [`Kernel`].
|
||||||
|
///
|
||||||
|
/// An [`Environment`] defines the types which are used.
|
||||||
|
///
|
||||||
|
/// A Kernel lives as long as a [Map](crate::map::Map) usually. It is shared through out various
|
||||||
|
/// components of the maplibre library.
|
||||||
pub struct Kernel<E: Environment> {
|
pub struct Kernel<E: Environment> {
|
||||||
pub map_window_config: E::MapWindowConfig,
|
map_window_config: E::MapWindowConfig,
|
||||||
pub apc: E::AsyncProcedureCall,
|
apc: E::AsyncProcedureCall,
|
||||||
pub scheduler: E::Scheduler,
|
scheduler: E::Scheduler,
|
||||||
pub source_client: SourceClient<E::HttpClient>,
|
source_client: SourceClient<E::HttpClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: Environment> Kernel<E> {
|
||||||
|
pub fn map_window_config(&self) -> &E::MapWindowConfig {
|
||||||
|
&self.map_window_config
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apc(&self) -> &E::AsyncProcedureCall {
|
||||||
|
&self.apc
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scheduler(&self) -> &E::Scheduler {
|
||||||
|
&self.scheduler
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_client(&self) -> &SourceClient<E::HttpClient> {
|
||||||
|
&self.source_client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A convenient builder for [Kernels](Kernel).
|
||||||
pub struct KernelBuilder<E: Environment> {
|
pub struct KernelBuilder<E: Environment> {
|
||||||
map_window_config: Option<E::MapWindowConfig>,
|
map_window_config: Option<E::MapWindowConfig>,
|
||||||
apc: Option<E::AsyncProcedureCall>,
|
apc: Option<E::AsyncProcedureCall>,
|
||||||
|
|||||||
@ -102,7 +102,7 @@ where
|
|||||||
where
|
where
|
||||||
E: Environment<MapWindowConfig = MWC>,
|
E: Environment<MapWindowConfig = MWC>,
|
||||||
{
|
{
|
||||||
self.initialize(&kernel.map_window_config).await
|
self.initialize(kernel.map_window_config()).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ impl<MWC: MapWindowConfig> UninitializedRenderer<MWC> {
|
|||||||
where
|
where
|
||||||
E: Environment<MapWindowConfig = MWC>,
|
E: Environment<MapWindowConfig = MWC>,
|
||||||
{
|
{
|
||||||
self.initialize_headless(&kernel.map_window_config).await
|
self.initialize_headless(kernel.map_window_config()).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
|
|||||||
..
|
..
|
||||||
}: &mut MapContext,
|
}: &mut MapContext,
|
||||||
) {
|
) {
|
||||||
if let Some(result) = self.kernel.apc.receive() {
|
if let Some(result) = self.kernel.apc().receive() {
|
||||||
match result {
|
match result {
|
||||||
Message::TileTessellated(tranferred) => {
|
Message::TileTessellated(tranferred) => {
|
||||||
let coords = tranferred.coords();
|
let coords = tranferred.coords();
|
||||||
|
|||||||
@ -150,7 +150,8 @@ impl<E: Environment> RequestStage<E> {
|
|||||||
coords: WorldTileCoords,
|
coords: WorldTileCoords,
|
||||||
layers: &HashSet<String>,
|
layers: &HashSet<String>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
/* if !tile_repository.is_layers_missing(coords, layers) {
|
/* TODO: is this still required?
|
||||||
|
if !tile_repository.is_layers_missing(coords, layers) {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
@ -158,7 +159,7 @@ impl<E: Environment> RequestStage<E> {
|
|||||||
tile_repository.create_tile(coords);
|
tile_repository.create_tile(coords);
|
||||||
|
|
||||||
tracing::info!("new tile request: {}", &coords);
|
tracing::info!("new tile request: {}", &coords);
|
||||||
self.kernel.apc.schedule(
|
self.kernel.apc().call(
|
||||||
Input::TileRequest(TileRequest {
|
Input::TileRequest(TileRequest {
|
||||||
coords,
|
coords,
|
||||||
layers: layers.clone(),
|
layers: layers.clone(),
|
||||||
|
|||||||
@ -185,7 +185,7 @@ impl AsyncProcedureCall<UsedTransferables, UsedHttpClient> for PassingAsyncProce
|
|||||||
self.received.pop()
|
self.received.pop()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||||
let procedure_ptr = procedure as *mut AsyncProcedure<Self::Context> as u32; // FIXME (wasm-executor): is u32 fine, define an overflow safe function?
|
let procedure_ptr = procedure as *mut AsyncProcedure<Self::Context> as u32; // FIXME (wasm-executor): is u32 fine, define an overflow safe function?
|
||||||
let input = serde_json::to_string(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
|
let input = serde_json::to_string(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user