Use channels for communication

This commit is contained in:
Maximilian Ammann 2022-03-08 20:10:30 +01:00
parent 901c3c5437
commit 903545ca4a
15 changed files with 362 additions and 265 deletions

2
.gitignore vendored
View File

@ -17,6 +17,6 @@ target/
dist/
# Cache by reqwest-middleware-cache
*cache*
*reqwest*cache*
logs/

View File

@ -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()
}

View File

@ -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
View 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()
}
}
}

View File

@ -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
View 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);
}
}
}
}
}
}

View File

@ -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) => {

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View 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);
}

View File

@ -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,
);

View File

@ -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"));

View File

@ -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);
}
};