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/
|
||||
|
||||
# Cache by reqwest-middleware-cache
|
||||
*cache*
|
||||
*reqwest*cache*
|
||||
|
||||
logs/
|
||||
|
||||
@ -1,6 +1,12 @@
|
||||
use mapr::io::worker_loop::WorkerLoop;
|
||||
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 winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
@ -15,10 +21,24 @@ async fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut cache_io = WorkerLoop::new();
|
||||
let cache_main = cache_io.clone();
|
||||
let workflow = Workflow::create();
|
||||
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 });
|
||||
main_loop::setup(window, event_loop, Box::new(cache_main)).await;
|
||||
join_handle.await.unwrap();
|
||||
let join_handle = task::spawn_blocking(move || {
|
||||
Handle::current().block_on(async move {
|
||||
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;
|
||||
|
||||
pub mod static_tile_fetcher;
|
||||
pub mod tile_cache;
|
||||
pub mod web_tile_fetcher;
|
||||
pub mod worker_loop;
|
||||
pub mod workflow;
|
||||
|
||||
pub struct HttpFetcherConfig {
|
||||
/// 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::tile::Layer;
|
||||
|
||||
use crate::io::tile_cache::TileCache;
|
||||
use crate::io::web_tile_fetcher::WebTileFetcher;
|
||||
use crate::io::workflow::{LayerResult, TileRequest};
|
||||
use crate::io::{HttpFetcherConfig, TileFetcher};
|
||||
use crate::render::ShaderVertex;
|
||||
use crate::tessellation::{IndexDataType, OverAlignedVertexBuffer, Tessellated};
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
|
||||
#[derive(Clone)]
|
||||
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 {
|
||||
/*impl Drop for WorkerLoop {
|
||||
fn drop(&mut self) {
|
||||
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> {
|
||||
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,
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
use log::{error, info, trace};
|
||||
use std::sync::mpsc::Receiver;
|
||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::Window;
|
||||
|
||||
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::render::render_state::RenderState;
|
||||
|
||||
pub async fn setup(
|
||||
window: winit::window::Window,
|
||||
event_loop: EventLoop<()>,
|
||||
mut worker_loop: Box<WorkerLoop>,
|
||||
mut dispatcher: Box<TileRequestDispatcher>,
|
||||
receiver: Box<Receiver<LayerResult>>,
|
||||
tile_cache: Box<TileCache>,
|
||||
) {
|
||||
info!("== mapr ==");
|
||||
|
||||
@ -85,8 +90,13 @@ pub async fn setup(
|
||||
let now = Instant::now();
|
||||
let dt = now - last_render_time;
|
||||
last_render_time = now;
|
||||
|
||||
while let Ok(d) = receiver.try_recv() {
|
||||
tile_cache.push(d);
|
||||
}
|
||||
|
||||
input.update_state(state, dt);
|
||||
state.upload_tile_geometry(&mut worker_loop);
|
||||
state.upload_tile_geometry(&mut dispatcher, &tile_cache);
|
||||
match state.render() {
|
||||
Ok(_) => {}
|
||||
Err(wgpu::SurfaceError::Lost) => {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::io::worker_loop::WorkerLoop;
|
||||
use crate::io::worker_loop::DownloadTessellateLoop;
|
||||
use crate::main_loop;
|
||||
pub use std::time::Instant;
|
||||
use tokio::task;
|
||||
@ -19,10 +19,10 @@ pub async fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut worker_loop = WorkerLoop::new();
|
||||
let mut worker_loop = DownloadTessellateLoop::new();
|
||||
let worker_loop_main = worker_loop.clone();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
use crate::io::worker_loop::WorkerLoop;
|
||||
use crate::io::worker_loop::DownloadTessellateLoop;
|
||||
use crate::main_loop;
|
||||
pub use std::time::Instant;
|
||||
use tokio::task;
|
||||
@ -20,10 +20,10 @@ pub async fn mapr_apple_main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut worker_loop = WorkerLoop::new();
|
||||
let mut worker_loop = DownloadTessellateLoop::new();
|
||||
let worker_loop_main = worker_loop.clone();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
mod http_fetcher;
|
||||
mod worker_loop;
|
||||
mod workflow;
|
||||
|
||||
use std::panic;
|
||||
|
||||
@ -9,7 +9,6 @@ use winit::event_loop::EventLoop;
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
use winit::window::{Window, WindowBuilder};
|
||||
|
||||
use crate::io::worker_loop::WorkerLoop;
|
||||
use console_error_panic_hook;
|
||||
pub use instant::Instant;
|
||||
use wasm_bindgen::prelude::*;
|
||||
@ -24,6 +23,8 @@ pub const COLOR_TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8
|
||||
#[cfg(feature = "web-webgl")]
|
||||
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;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
@ -35,8 +36,8 @@ pub fn start() {
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn run(worker_loop_ptr: *mut WorkerLoop) {
|
||||
let worker_loop: Box<WorkerLoop> = unsafe { Box::from_raw(worker_loop_ptr) };
|
||||
pub async fn run(workflow_ptr: *mut Workflow) {
|
||||
let workflow: Box<Workflow> = unsafe { Box::from_raw(workflow_ptr) };
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
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
|
||||
crate::main_loop::setup(window, event_loop, worker_loop).await;
|
||||
// std::mem::forget(worker_loop);
|
||||
crate::main_loop::setup(
|
||||
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 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 winit::dpi::PhysicalSize;
|
||||
use winit::window::Window;
|
||||
|
||||
use crate::io::worker_loop::{TessellationResult, TileRequest, WorkerLoop};
|
||||
use crate::platform::{COLOR_TEXTURE_FORMAT, MIN_BUFFER_SIZE};
|
||||
use crate::render::buffer_pool::{BackingBufferDescriptor, BufferPool};
|
||||
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: 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 view_region = self
|
||||
.camera
|
||||
@ -363,7 +368,7 @@ impl RenderState {
|
||||
"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() {
|
||||
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 {
|
||||
match result {
|
||||
TessellationResult::Unavailable(_) => {}
|
||||
TessellationResult::TessellatedLayer(layer) => {
|
||||
let world_coords = layer.coords.into_world_tile();
|
||||
LayerResult::EmptyLayer { .. } => {}
|
||||
LayerResult::TessellatedLayer {
|
||||
coords,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
buffer,
|
||||
..
|
||||
} => {
|
||||
let world_coords = coords.into_world_tile();
|
||||
|
||||
let feature_metadata = layer
|
||||
.layer_data
|
||||
let feature_metadata = layer_data
|
||||
.features()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, _feature)| {
|
||||
iter::repeat(ShaderFeatureStyle {
|
||||
color: match layer.layer_data.name() {
|
||||
color: match layer_data.name() {
|
||||
"transportation" => [1.0, 0.0, 0.0, 1.0],
|
||||
"building" => [0.0, 1.0, 1.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],
|
||||
},
|
||||
})
|
||||
.take(*layer.feature_indices.get(i).unwrap() as usize)
|
||||
.take(*feature_indices.get(i).unwrap() as usize)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -426,9 +436,9 @@ impl RenderState {
|
||||
|
||||
self.buffer_pool.allocate_tile_geometry(
|
||||
&self.queue,
|
||||
layer.coords,
|
||||
layer.layer_data.name(),
|
||||
&layer.buffer,
|
||||
coords,
|
||||
layer_data.name(),
|
||||
&buffer,
|
||||
ShaderTileMetadata::new(transform.into()),
|
||||
&feature_metadata,
|
||||
);
|
||||
|
||||
@ -59,17 +59,17 @@ const start = async () => {
|
||||
type: "module",
|
||||
});
|
||||
|
||||
let workerLoopPtr = module.create_worker_loop();
|
||||
let workflowPtr = module.create_workflow();
|
||||
|
||||
console.log("Starting cache-worker")
|
||||
worker.postMessage({type: "init", memory, workerLoopPtr});
|
||||
worker.postMessage({type: "init", memory, workflowPtr: workflowPtr});
|
||||
|
||||
document.body.querySelectorAll("canvas").forEach(canvas => {
|
||||
canvas.addEventListener("touchstart", 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"));
|
||||
|
||||
@ -11,8 +11,8 @@ onmessage = async message => {
|
||||
}
|
||||
initialized = true;
|
||||
const module = await init(undefined, data.memory);
|
||||
let workerLoopPtr = data.workerLoopPtr;
|
||||
console.log("Started WorkerLoop: " + workerLoopPtr)
|
||||
module.run_worker_loop(workerLoopPtr);
|
||||
let workflowPtr = data.workflowPtr;
|
||||
console.log("Started WorkerLoop: " + workflowPtr)
|
||||
module.run_worker_loop(workflowPtr);
|
||||
}
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user