mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Use channels for communication
This commit is contained in:
parent
901c3c5437
commit
903545ca4a
2
.gitignore
vendored
2
.gitignore
vendored
@ -17,6 +17,6 @@ target/
|
|||||||
dist/
|
dist/
|
||||||
|
|
||||||
# Cache by reqwest-middleware-cache
|
# Cache by reqwest-middleware-cache
|
||||||
*cache*
|
*reqwest*cache*
|
||||||
|
|
||||||
logs/
|
logs/
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
use mapr::io::worker_loop::WorkerLoop;
|
|
||||||
use mapr::main_loop;
|
use mapr::main_loop;
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
|
use std::thread;
|
||||||
|
use tokio::runtime::Handle;
|
||||||
|
|
||||||
|
use mapr::io::tile_cache::TileCache;
|
||||||
|
use mapr::io::web_tile_fetcher::WebTileFetcher;
|
||||||
|
use mapr::io::workflow::{DownloadTessellateLoop, TileRequestDispatcher, Workflow};
|
||||||
|
use mapr::io::{HttpFetcherConfig, TileFetcher};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
use winit::window::WindowBuilder;
|
use winit::window::WindowBuilder;
|
||||||
@ -15,10 +21,24 @@ async fn main() {
|
|||||||
.build(&event_loop)
|
.build(&event_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut cache_io = WorkerLoop::new();
|
let workflow = Workflow::create();
|
||||||
let cache_main = cache_io.clone();
|
let download_tessellate_loop = workflow.download_tessellate_loop;
|
||||||
|
let tile_request_dispatcher = workflow.tile_request_dispatcher;
|
||||||
|
let layer_result_receiver = workflow.layer_result_receiver;
|
||||||
|
|
||||||
let join_handle = task::spawn(async move { cache_io.run_loop().await });
|
let join_handle = task::spawn_blocking(move || {
|
||||||
main_loop::setup(window, event_loop, Box::new(cache_main)).await;
|
Handle::current().block_on(async move {
|
||||||
join_handle.await.unwrap();
|
download_tessellate_loop.run_loop().await;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
main_loop::setup(
|
||||||
|
window,
|
||||||
|
event_loop,
|
||||||
|
Box::new(tile_request_dispatcher),
|
||||||
|
Box::new(layer_result_receiver),
|
||||||
|
Box::new(TileCache::new()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
join_handle.await.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,9 @@ use crate::error::Error;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
pub mod static_tile_fetcher;
|
pub mod static_tile_fetcher;
|
||||||
|
pub mod tile_cache;
|
||||||
pub mod web_tile_fetcher;
|
pub mod web_tile_fetcher;
|
||||||
pub mod worker_loop;
|
pub mod workflow;
|
||||||
|
|
||||||
pub struct HttpFetcherConfig {
|
pub struct HttpFetcherConfig {
|
||||||
/// Under which path should we cache requests.
|
/// Under which path should we cache requests.
|
||||||
|
|||||||
78
src/io/tile_cache.rs
Normal file
78
src/io/tile_cache.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use crate::coords::TileCoords;
|
||||||
|
use crate::io::workflow::LayerResult;
|
||||||
|
use std::collections::{btree_map, BTreeMap};
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TileCache {
|
||||||
|
store: Arc<Mutex<BTreeMap<TileCoords, Vec<LayerResult>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileCache {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
store: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn push(&self, result: LayerResult) -> bool {
|
||||||
|
if let Ok(mut map) = self.store.lock() {
|
||||||
|
match map.entry(result.get_tile_coords()) {
|
||||||
|
btree_map::Entry::Vacant(entry) => {
|
||||||
|
entry.insert(vec![result]);
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(mut entry) => {
|
||||||
|
entry.get_mut().push(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_tessellated_layers_at(
|
||||||
|
&self,
|
||||||
|
coords: &TileCoords,
|
||||||
|
skip_layers: &Vec<String>,
|
||||||
|
) -> Vec<LayerResult> {
|
||||||
|
let mut ret = Vec::new();
|
||||||
|
if let Ok(map) = self.store.try_lock() {
|
||||||
|
if let Some(results) = map.get(coords) {
|
||||||
|
for result in results {
|
||||||
|
if !skip_layers.contains(&result.layer_name().to_string()) {
|
||||||
|
ret.push(result.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_missing_tessellated_layer_names_at(
|
||||||
|
&self,
|
||||||
|
coords: &TileCoords,
|
||||||
|
layers: &Vec<String>,
|
||||||
|
) -> Vec<String> {
|
||||||
|
if let Ok(loaded) = self.store.try_lock() {
|
||||||
|
if let Some(tessellated_layers) = loaded.get(coords) {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for layer in layers {
|
||||||
|
if tessellated_layers
|
||||||
|
.iter()
|
||||||
|
.find(|tessellated_layer| tessellated_layer.layer_name() == layer)
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
result.push(layer.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
layers.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,223 +8,25 @@ use crate::coords::TileCoords;
|
|||||||
use vector_tile::parse_tile_bytes;
|
use vector_tile::parse_tile_bytes;
|
||||||
use vector_tile::tile::Layer;
|
use vector_tile::tile::Layer;
|
||||||
|
|
||||||
|
use crate::io::tile_cache::TileCache;
|
||||||
use crate::io::web_tile_fetcher::WebTileFetcher;
|
use crate::io::web_tile_fetcher::WebTileFetcher;
|
||||||
|
use crate::io::workflow::{LayerResult, TileRequest};
|
||||||
use crate::io::{HttpFetcherConfig, TileFetcher};
|
use crate::io::{HttpFetcherConfig, TileFetcher};
|
||||||
use crate::render::ShaderVertex;
|
use crate::render::ShaderVertex;
|
||||||
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer, Tessellated};
|
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer, Tessellated};
|
||||||
use std::collections::btree_map::Entry;
|
use std::collections::btree_map::Entry;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
|
||||||
#[derive(Clone)]
|
/*impl Drop for WorkerLoop {
|
||||||
pub enum TessellationResult {
|
|
||||||
Unavailable(EmptyLayer),
|
|
||||||
TessellatedLayer(TessellatedLayer),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TessellationResult {
|
|
||||||
pub fn get_tile_coords(&self) -> TileCoords {
|
|
||||||
match self {
|
|
||||||
TessellationResult::Unavailable(result) => result.coords,
|
|
||||||
TessellationResult::TessellatedLayer(result) => result.coords,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn layer_name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
TessellationResult::Unavailable(result) => result.layer_name.as_str(),
|
|
||||||
TessellationResult::TessellatedLayer(result) => result.layer_data.name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TessellatedLayer {
|
|
||||||
pub coords: TileCoords,
|
|
||||||
pub buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
|
||||||
/// Holds for each feature the count of indices
|
|
||||||
pub feature_indices: Vec<u32>,
|
|
||||||
pub layer_data: Layer,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct EmptyLayer {
|
|
||||||
pub coords: TileCoords,
|
|
||||||
pub layer_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TileResultStore {
|
|
||||||
store: Mutex<BTreeMap<TileCoords, Vec<TessellationResult>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TileResultStore {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
store: Mutex::new(BTreeMap::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&self, result: TessellationResult) -> bool {
|
|
||||||
if let Ok(mut map) = self.store.lock() {
|
|
||||||
match map.entry(result.get_tile_coords()) {
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
entry.insert(vec![result]);
|
|
||||||
}
|
|
||||||
Entry::Occupied(mut entry) => {
|
|
||||||
entry.get_mut().push(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_tessellated_layers_at(
|
|
||||||
&self,
|
|
||||||
coords: &TileCoords,
|
|
||||||
skip_layers: &Vec<String>,
|
|
||||||
) -> Vec<TessellationResult> {
|
|
||||||
let mut ret = Vec::new();
|
|
||||||
if let Ok(map) = self.store.try_lock() {
|
|
||||||
if let Some(results) = map.get(coords) {
|
|
||||||
for result in results {
|
|
||||||
if !skip_layers.contains(&result.layer_name().to_string()) {
|
|
||||||
ret.push(result.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_missing_tessellated_layer_names_at(
|
|
||||||
&self,
|
|
||||||
coords: &TileCoords,
|
|
||||||
layers: &Vec<String>,
|
|
||||||
) -> Vec<String> {
|
|
||||||
if let Ok(loaded) = self.store.try_lock() {
|
|
||||||
if let Some(tessellated_layers) = loaded.get(coords) {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
for layer in layers {
|
|
||||||
if tessellated_layers
|
|
||||||
.iter()
|
|
||||||
.find(|tessellated_layer| tessellated_layer.layer_name() == layer)
|
|
||||||
.is_none()
|
|
||||||
{
|
|
||||||
result.push(layer.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
layers.clone()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TileRequest(pub TileCoords, pub Vec<String>);
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct WorkerLoop {
|
|
||||||
requests: Arc<RequestQueue<TileRequest>>,
|
|
||||||
tile_result_store: Arc<TileResultStore>,
|
|
||||||
pending_tiles: Arc<Mutex<HashSet<TileCoords>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for WorkerLoop {
|
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
error!("WorkerLoop dropped. This should only happen when the application is stopped!");
|
error!("WorkerLoop dropped. This should only happen when the application is stopped!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
impl WorkerLoop {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
requests: Arc::new(RequestQueue::new()),
|
|
||||||
tile_result_store: Arc::new(TileResultStore::new()),
|
|
||||||
pending_tiles: Arc::new(Mutex::new(HashSet::new())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spin_fetch(&self, tile_request: TileRequest) {
|
|
||||||
let TileRequest(coords, layers) = &tile_request;
|
|
||||||
|
|
||||||
if let Ok(mut pending_tiles) = self.pending_tiles.try_lock() {
|
|
||||||
if pending_tiles.contains(&coords) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pending_tiles.insert(*coords);
|
|
||||||
|
|
||||||
let missing_layers = self
|
|
||||||
.tile_result_store
|
|
||||||
.get_missing_tessellated_layer_names_at(&coords, &layers);
|
|
||||||
|
|
||||||
if missing_layers.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("new tile request: {}", &coords);
|
|
||||||
self.requests.spin_push(tile_request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tessellated_layers_at(
|
|
||||||
&self,
|
|
||||||
coords: &TileCoords,
|
|
||||||
skip_layers: &Vec<String>,
|
|
||||||
) -> Vec<TessellationResult> {
|
|
||||||
self.tile_result_store
|
|
||||||
.get_tessellated_layers_at(coords, skip_layers)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run_loop(&mut self) {
|
|
||||||
let fetcher = WebTileFetcher::new(HttpFetcherConfig {
|
|
||||||
cache_path: "/tmp/mapr-cache".to_string(),
|
|
||||||
});
|
|
||||||
// let fetcher = StaticTileFetcher::new();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
while let Some(TileRequest(coords, layers_to_load)) = self.requests.pop() {
|
|
||||||
match fetcher.fetch_tile(&coords).await {
|
|
||||||
Ok(data) => {
|
|
||||||
info!("preparing tile {} with {}bytes", &coords, data.len());
|
|
||||||
let tile = parse_tile_bytes(data.as_slice()).expect("failed to load tile");
|
|
||||||
|
|
||||||
for to_load in layers_to_load {
|
|
||||||
if let Some(layer) = tile
|
|
||||||
.layers()
|
|
||||||
.iter()
|
|
||||||
.find(|layer| to_load.as_str() == layer.name())
|
|
||||||
{
|
|
||||||
if let Some((buffer, feature_indices)) = layer.tessellate() {
|
|
||||||
self.tile_result_store.push(
|
|
||||||
TessellationResult::TessellatedLayer(TessellatedLayer {
|
|
||||||
coords,
|
|
||||||
buffer: buffer.into(),
|
|
||||||
feature_indices,
|
|
||||||
layer_data: layer.clone(),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!("layer ready: {:?}", &coords);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!("layer failed: {:?}", &err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RequestQueue<T: Send> {
|
struct RequestQueue<T: Send> {
|
||||||
queue: Mutex<VecDeque<T>>,
|
queue: Mutex<VecDeque<T>>,
|
||||||
/// Condvar is also supported on WASM
|
|
||||||
/// ([see here]( https://github.com/rust-lang/rust/blob/effea9a2a0d501db5722d507690a1a66236933bf/library/std/src/sys/wasm/atomics/condvar.rs))!
|
|
||||||
cvar: Condvar,
|
cvar: Condvar,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
168
src/io/workflow.rs
Normal file
168
src/io/workflow.rs
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
/// Describes through which channels work-requests travel. It describes the flow of work.
|
||||||
|
use crate::coords::TileCoords;
|
||||||
|
use crate::io::tile_cache::TileCache;
|
||||||
|
use crate::io::web_tile_fetcher::WebTileFetcher;
|
||||||
|
use crate::io::{HttpFetcherConfig, TileFetcher};
|
||||||
|
use crate::render::ShaderVertex;
|
||||||
|
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer, Tessellated};
|
||||||
|
use log::{error, info};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use std::sync::mpsc::{channel, Receiver, SendError, Sender};
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use vector_tile::parse_tile_bytes;
|
||||||
|
use vector_tile::tile::Layer;
|
||||||
|
|
||||||
|
pub struct Workflow {
|
||||||
|
pub layer_result_receiver: Receiver<LayerResult>,
|
||||||
|
pub tile_request_dispatcher: TileRequestDispatcher,
|
||||||
|
pub download_tessellate_loop: DownloadTessellateLoop,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Workflow {
|
||||||
|
pub fn create() -> Self {
|
||||||
|
let (tile_request_sender, tile_request_receiver) = channel();
|
||||||
|
|
||||||
|
let tile_request_dispatcher = TileRequestDispatcher::new(tile_request_sender);
|
||||||
|
|
||||||
|
let (layer_result_sender, layer_result_receiver) = channel();
|
||||||
|
|
||||||
|
let download_tessellate_loop =
|
||||||
|
DownloadTessellateLoop::new(tile_request_receiver, layer_result_sender);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
layer_result_receiver,
|
||||||
|
tile_request_dispatcher,
|
||||||
|
download_tessellate_loop,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum LayerResult {
|
||||||
|
EmptyLayer {
|
||||||
|
coords: TileCoords,
|
||||||
|
layer_name: String,
|
||||||
|
},
|
||||||
|
TessellatedLayer {
|
||||||
|
coords: TileCoords,
|
||||||
|
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||||
|
/// Holds for each feature the count of indices
|
||||||
|
feature_indices: Vec<u32>,
|
||||||
|
layer_data: Layer,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LayerResult {
|
||||||
|
pub fn get_tile_coords(&self) -> TileCoords {
|
||||||
|
match self {
|
||||||
|
LayerResult::EmptyLayer { coords, .. } => *coords,
|
||||||
|
LayerResult::TessellatedLayer { coords, .. } => *coords,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layer_name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
LayerResult::EmptyLayer { layer_name, .. } => layer_name.as_str(),
|
||||||
|
LayerResult::TessellatedLayer { layer_data, .. } => layer_data.name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TileRequest(pub TileCoords, pub Vec<String>);
|
||||||
|
|
||||||
|
pub struct TileRequestDispatcher {
|
||||||
|
request_sender: Sender<TileRequest>,
|
||||||
|
pending_tiles: HashSet<TileCoords>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TileRequestDispatcher {
|
||||||
|
pub fn new(request_sender: Sender<TileRequest>) -> Self {
|
||||||
|
Self {
|
||||||
|
pending_tiles: Default::default(),
|
||||||
|
request_sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn request_tile(
|
||||||
|
&mut self,
|
||||||
|
tile_request: TileRequest,
|
||||||
|
tile_cache: &TileCache,
|
||||||
|
) -> Result<(), SendError<TileRequest>> {
|
||||||
|
let TileRequest(coords, layers) = &tile_request;
|
||||||
|
|
||||||
|
let missing_layers = tile_cache.get_missing_tessellated_layer_names_at(&coords, &layers);
|
||||||
|
|
||||||
|
if missing_layers.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.pending_tiles.contains(&coords) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
self.pending_tiles.insert(*coords);
|
||||||
|
|
||||||
|
info!("new tile request: {}", &coords);
|
||||||
|
self.request_sender.send(tile_request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DownloadTessellateLoop {
|
||||||
|
request_receiver: Receiver<TileRequest>,
|
||||||
|
result_sender: Sender<LayerResult>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DownloadTessellateLoop {
|
||||||
|
pub fn new(
|
||||||
|
request_receiver: Receiver<TileRequest>,
|
||||||
|
result_sender: Sender<LayerResult>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
request_receiver,
|
||||||
|
result_sender,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run_loop(&self) {
|
||||||
|
let fetcher = WebTileFetcher::new(HttpFetcherConfig {
|
||||||
|
cache_path: "/tmp/mapr-cache".to_string(),
|
||||||
|
});
|
||||||
|
// let fetcher = StaticTileFetcher::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Internally uses Condvar probably: Condvar is also supported on WASM
|
||||||
|
// see https://github.com/rust-lang/rust/blob/effea9a2a0d501db5722d507690a1a66236933bf/library/std/src/sys/wasm/atomics/condvar.rs
|
||||||
|
if let TileRequest(coords, layers_to_load) = self.request_receiver.recv().unwrap() {
|
||||||
|
// TODO remove unwrap
|
||||||
|
match fetcher.fetch_tile(&coords).await {
|
||||||
|
Ok(data) => {
|
||||||
|
info!("preparing tile {} with {}bytes", &coords, data.len());
|
||||||
|
let tile = parse_tile_bytes(data.as_slice()).expect("failed to load tile");
|
||||||
|
|
||||||
|
for to_load in layers_to_load {
|
||||||
|
if let Some(layer) = tile
|
||||||
|
.layers()
|
||||||
|
.iter()
|
||||||
|
.find(|layer| to_load.as_str() == layer.name())
|
||||||
|
{
|
||||||
|
if let Some((buffer, feature_indices)) = layer.tessellate() {
|
||||||
|
self.result_sender
|
||||||
|
.send(LayerResult::TessellatedLayer {
|
||||||
|
coords,
|
||||||
|
buffer: buffer.into(),
|
||||||
|
feature_indices,
|
||||||
|
layer_data: layer.clone(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("layer ready: {:?}", &coords);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("layer failed: {:?}", &err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,18 +4,23 @@
|
|||||||
//! * Render a new frame
|
//! * Render a new frame
|
||||||
|
|
||||||
use log::{error, info, trace};
|
use log::{error, info, trace};
|
||||||
|
use std::sync::mpsc::Receiver;
|
||||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::input::{InputController, UpdateState};
|
use crate::input::{InputController, UpdateState};
|
||||||
use crate::io::worker_loop::WorkerLoop;
|
use crate::io::tile_cache::TileCache;
|
||||||
|
use crate::io::workflow::{LayerResult, TileRequestDispatcher, Workflow};
|
||||||
use crate::platform::Instant;
|
use crate::platform::Instant;
|
||||||
use crate::render::render_state::RenderState;
|
use crate::render::render_state::RenderState;
|
||||||
|
|
||||||
pub async fn setup(
|
pub async fn setup(
|
||||||
window: winit::window::Window,
|
window: winit::window::Window,
|
||||||
event_loop: EventLoop<()>,
|
event_loop: EventLoop<()>,
|
||||||
mut worker_loop: Box<WorkerLoop>,
|
mut dispatcher: Box<TileRequestDispatcher>,
|
||||||
|
receiver: Box<Receiver<LayerResult>>,
|
||||||
|
tile_cache: Box<TileCache>,
|
||||||
) {
|
) {
|
||||||
info!("== mapr ==");
|
info!("== mapr ==");
|
||||||
|
|
||||||
@ -85,8 +90,13 @@ pub async fn setup(
|
|||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let dt = now - last_render_time;
|
let dt = now - last_render_time;
|
||||||
last_render_time = now;
|
last_render_time = now;
|
||||||
|
|
||||||
|
while let Ok(d) = receiver.try_recv() {
|
||||||
|
tile_cache.push(d);
|
||||||
|
}
|
||||||
|
|
||||||
input.update_state(state, dt);
|
input.update_state(state, dt);
|
||||||
state.upload_tile_geometry(&mut worker_loop);
|
state.upload_tile_geometry(&mut dispatcher, &tile_cache);
|
||||||
match state.render() {
|
match state.render() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(wgpu::SurfaceError::Lost) => {
|
Err(wgpu::SurfaceError::Lost) => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use crate::io::worker_loop::WorkerLoop;
|
use crate::io::worker_loop::DownloadTessellateLoop;
|
||||||
use crate::main_loop;
|
use crate::main_loop;
|
||||||
pub use std::time::Instant;
|
pub use std::time::Instant;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
@ -19,10 +19,10 @@ pub async fn main() {
|
|||||||
.build(&event_loop)
|
.build(&event_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut worker_loop = WorkerLoop::new();
|
let mut worker_loop = DownloadTessellateLoop::new();
|
||||||
let worker_loop_main = worker_loop.clone();
|
let worker_loop_main = worker_loop.clone();
|
||||||
|
|
||||||
let join_handle = task::spawn(async move { worker_loop.run_loop().await });
|
let join_handle = task::spawn(async move { worker_loop.run_loop().await });
|
||||||
main_loop::setup(window, event_loop, Box::new(worker_loop_main)).await;
|
main_loop::setup(window, event_loop, Box::new(worker_loop_main), , ).await;
|
||||||
join_handle.await.unwrap();
|
join_handle.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
use winit::window::WindowBuilder;
|
use winit::window::WindowBuilder;
|
||||||
|
|
||||||
use crate::io::worker_loop::WorkerLoop;
|
use crate::io::worker_loop::DownloadTessellateLoop;
|
||||||
use crate::main_loop;
|
use crate::main_loop;
|
||||||
pub use std::time::Instant;
|
pub use std::time::Instant;
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
@ -20,10 +20,10 @@ pub async fn mapr_apple_main() {
|
|||||||
.build(&event_loop)
|
.build(&event_loop)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut worker_loop = WorkerLoop::new();
|
let mut worker_loop = DownloadTessellateLoop::new();
|
||||||
let worker_loop_main = worker_loop.clone();
|
let worker_loop_main = worker_loop.clone();
|
||||||
|
|
||||||
let join_handle = task::spawn(async move { worker_loop.run_loop().await });
|
let join_handle = task::spawn(async move { worker_loop.run_loop().await });
|
||||||
main_loop::setup(window, event_loop, Box::new(worker_loop_main)).await;
|
main_loop::setup(window, event_loop, Box::new(worker_loop_main), , ).await;
|
||||||
join_handle.await.unwrap();
|
join_handle.await.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
mod http_fetcher;
|
mod http_fetcher;
|
||||||
mod worker_loop;
|
mod workflow;
|
||||||
|
|
||||||
use std::panic;
|
use std::panic;
|
||||||
|
|
||||||
@ -9,7 +9,6 @@ use winit::event_loop::EventLoop;
|
|||||||
use winit::platform::web::WindowBuilderExtWebSys;
|
use winit::platform::web::WindowBuilderExtWebSys;
|
||||||
use winit::window::{Window, WindowBuilder};
|
use winit::window::{Window, WindowBuilder};
|
||||||
|
|
||||||
use crate::io::worker_loop::WorkerLoop;
|
|
||||||
use console_error_panic_hook;
|
use console_error_panic_hook;
|
||||||
pub use instant::Instant;
|
pub use instant::Instant;
|
||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
@ -24,6 +23,8 @@ pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8
|
|||||||
#[cfg(feature = "web-webgl")]
|
#[cfg(feature = "web-webgl")]
|
||||||
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
|
pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
|
||||||
|
|
||||||
|
use crate::io::tile_cache::TileCache;
|
||||||
|
use crate::io::workflow::Workflow;
|
||||||
pub use http_fetcher::PlatformHttpFetcher;
|
pub use http_fetcher::PlatformHttpFetcher;
|
||||||
|
|
||||||
#[wasm_bindgen(start)]
|
#[wasm_bindgen(start)]
|
||||||
@ -35,8 +36,8 @@ pub fn start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn run(worker_loop_ptr: *mut WorkerLoop) {
|
pub async fn run(workflow_ptr: *mut Workflow) {
|
||||||
let worker_loop: Box<WorkerLoop> = unsafe { Box::from_raw(worker_loop_ptr) };
|
let workflow: Box<Workflow> = unsafe { Box::from_raw(workflow_ptr) };
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
|
|
||||||
let web_window: WebSysWindow = web_sys::window().unwrap();
|
let web_window: WebSysWindow = web_sys::window().unwrap();
|
||||||
@ -60,6 +61,13 @@ pub async fn run(worker_loop_ptr: *mut WorkerLoop) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Either call forget or the main loop to keep worker loop alive
|
// Either call forget or the main loop to keep worker loop alive
|
||||||
crate::main_loop::setup(window, event_loop, worker_loop).await;
|
crate::main_loop::setup(
|
||||||
// std::mem::forget(worker_loop);
|
window,
|
||||||
|
event_loop,
|
||||||
|
Box::new(workflow.tile_request_dispatcher),
|
||||||
|
Box::new(workflow.layer_result_receiver),
|
||||||
|
Box::new(TileCache::new()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
// std::mem::forget(workflow);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
use crate::io::worker_loop::WorkerLoop;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub fn create_worker_loop() -> *mut WorkerLoop {
|
|
||||||
let worker_loop = Box::new(WorkerLoop::new());
|
|
||||||
let ptr = Box::into_raw(worker_loop);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub async fn run_worker_loop(worker_loop_ptr: *mut WorkerLoop) {
|
|
||||||
let mut worker_loop: Box<WorkerLoop> = unsafe { Box::from_raw(worker_loop_ptr) };
|
|
||||||
|
|
||||||
// Either call forget or the worker loop to keep it alive
|
|
||||||
worker_loop.run_loop().await;
|
|
||||||
std::mem::forget(worker_loop);
|
|
||||||
}
|
|
||||||
18
src/platform/web/workflow.rs
Normal file
18
src/platform/web/workflow.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use crate::io::workflow::Workflow;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn create_workflow() -> *mut Workflow {
|
||||||
|
let workflow = Box::new(Workflow::create());
|
||||||
|
let workflow_ptr = Box::into_raw(workflow);
|
||||||
|
return workflow_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub async fn run_worker_loop(workflow_ptr: *mut Workflow) {
|
||||||
|
let mut workflow: Box<Workflow> = unsafe { Box::from_raw(workflow_ptr) };
|
||||||
|
|
||||||
|
// Either call forget or the worker loop to keep it alive
|
||||||
|
workflow.download_tessellate_loop.run_loop().await;
|
||||||
|
//std::mem::forget(workflow);
|
||||||
|
}
|
||||||
@ -3,11 +3,12 @@ use std::default::Default;
|
|||||||
use std::{cmp, iter};
|
use std::{cmp, iter};
|
||||||
|
|
||||||
use crate::coords::{TileCoords, ViewRegion, WorldCoords, WorldTileCoords};
|
use crate::coords::{TileCoords, ViewRegion, WorldCoords, WorldTileCoords};
|
||||||
|
use crate::io::tile_cache::TileCache;
|
||||||
|
use crate::io::workflow::{LayerResult, TileRequest, TileRequestDispatcher, Workflow};
|
||||||
use wgpu::{Buffer, Limits, Queue};
|
use wgpu::{Buffer, Limits, Queue};
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::io::worker_loop::{TessellationResult, TileRequest, WorkerLoop};
|
|
||||||
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
|
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
|
||||||
use crate::render::camera;
|
use crate::render::camera;
|
||||||
@ -344,7 +345,11 @@ impl RenderState {
|
|||||||
|
|
||||||
// TODO: Could we draw inspiration from StagingBelt (https://docs.rs/wgpu/latest/wgpu/util/struct.StagingBelt.html)?
|
// TODO: Could we draw inspiration from StagingBelt (https://docs.rs/wgpu/latest/wgpu/util/struct.StagingBelt.html)?
|
||||||
// TODO: What is StagingBelt for?
|
// TODO: What is StagingBelt for?
|
||||||
pub fn upload_tile_geometry(&mut self, worker_loop: &mut WorkerLoop) {
|
pub fn upload_tile_geometry(
|
||||||
|
&mut self,
|
||||||
|
dispatcher: &mut TileRequestDispatcher,
|
||||||
|
tile_cache: &TileCache,
|
||||||
|
) {
|
||||||
let visible_z = self.visible_z();
|
let visible_z = self.visible_z();
|
||||||
let view_region = self
|
let view_region = self
|
||||||
.camera
|
.camera
|
||||||
@ -363,7 +368,7 @@ impl RenderState {
|
|||||||
"water".to_string(),
|
"water".to_string(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
worker_loop.spin_fetch(tile_request);
|
dispatcher.request_tile(tile_request, &tile_cache).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,21 +395,26 @@ impl RenderState {
|
|||||||
for tile_coords in view_region.iter() {
|
for tile_coords in view_region.iter() {
|
||||||
let loaded_layers = self.buffer_pool.get_loaded_layers(&tile_coords);
|
let loaded_layers = self.buffer_pool.get_loaded_layers(&tile_coords);
|
||||||
|
|
||||||
let layers = worker_loop.get_tessellated_layers_at(&tile_coords, &loaded_layers);
|
let layers = tile_cache.get_tessellated_layers_at(&tile_coords, &loaded_layers);
|
||||||
for result in layers {
|
for result in layers {
|
||||||
match result {
|
match result {
|
||||||
TessellationResult::Unavailable(_) => {}
|
LayerResult::EmptyLayer { .. } => {}
|
||||||
TessellationResult::TessellatedLayer(layer) => {
|
LayerResult::TessellatedLayer {
|
||||||
let world_coords = layer.coords.into_world_tile();
|
coords,
|
||||||
|
feature_indices,
|
||||||
|
layer_data,
|
||||||
|
buffer,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let world_coords = coords.into_world_tile();
|
||||||
|
|
||||||
let feature_metadata = layer
|
let feature_metadata = layer_data
|
||||||
.layer_data
|
|
||||||
.features()
|
.features()
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.flat_map(|(i, _feature)| {
|
.flat_map(|(i, _feature)| {
|
||||||
iter::repeat(ShaderFeatureStyle {
|
iter::repeat(ShaderFeatureStyle {
|
||||||
color: match layer.layer_data.name() {
|
color: match layer_data.name() {
|
||||||
"transportation" => [1.0, 0.0, 0.0, 1.0],
|
"transportation" => [1.0, 0.0, 0.0, 1.0],
|
||||||
"building" => [0.0, 1.0, 1.0, 1.0],
|
"building" => [0.0, 1.0, 1.0, 1.0],
|
||||||
"boundary" => [0.0, 0.0, 0.0, 1.0],
|
"boundary" => [0.0, 0.0, 0.0, 1.0],
|
||||||
@ -413,7 +423,7 @@ impl RenderState {
|
|||||||
_ => [0.0, 0.0, 0.0, 0.0],
|
_ => [0.0, 0.0, 0.0, 0.0],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.take(*layer.feature_indices.get(i).unwrap() as usize)
|
.take(*feature_indices.get(i).unwrap() as usize)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
@ -426,9 +436,9 @@ impl RenderState {
|
|||||||
|
|
||||||
self.buffer_pool.allocate_tile_geometry(
|
self.buffer_pool.allocate_tile_geometry(
|
||||||
&self.queue,
|
&self.queue,
|
||||||
layer.coords,
|
coords,
|
||||||
layer.layer_data.name(),
|
layer_data.name(),
|
||||||
&layer.buffer,
|
&buffer,
|
||||||
ShaderTileMetadata::new(transform.into()),
|
ShaderTileMetadata::new(transform.into()),
|
||||||
&feature_metadata,
|
&feature_metadata,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -59,17 +59,17 @@ const start = async () => {
|
|||||||
type: "module",
|
type: "module",
|
||||||
});
|
});
|
||||||
|
|
||||||
let workerLoopPtr = module.create_worker_loop();
|
let workflowPtr = module.create_workflow();
|
||||||
|
|
||||||
console.log("Starting cache-worker")
|
console.log("Starting cache-worker")
|
||||||
worker.postMessage({type: "init", memory, workerLoopPtr});
|
worker.postMessage({type: "init", memory, workflowPtr: workflowPtr});
|
||||||
|
|
||||||
document.body.querySelectorAll("canvas").forEach(canvas => {
|
document.body.querySelectorAll("canvas").forEach(canvas => {
|
||||||
canvas.addEventListener("touchstart", e => e.preventDefault());
|
canvas.addEventListener("touchstart", e => e.preventDefault());
|
||||||
canvas.addEventListener("touchmove", e => e.preventDefault());
|
canvas.addEventListener("touchmove", e => e.preventDefault());
|
||||||
})
|
})
|
||||||
|
|
||||||
await module.run(workerLoopPtr);
|
await module.run(workflowPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
start().then(r => console.log("started via wasm"));
|
start().then(r => console.log("started via wasm"));
|
||||||
|
|||||||
@ -11,8 +11,8 @@ onmessage = async message => {
|
|||||||
}
|
}
|
||||||
initialized = true;
|
initialized = true;
|
||||||
const module = await init(undefined, data.memory);
|
const module = await init(undefined, data.memory);
|
||||||
let workerLoopPtr = data.workerLoopPtr;
|
let workflowPtr = data.workflowPtr;
|
||||||
console.log("Started WorkerLoop: " + workerLoopPtr)
|
console.log("Started WorkerLoop: " + workflowPtr)
|
||||||
module.run_worker_loop(workerLoopPtr);
|
module.run_worker_loop(workflowPtr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user