Fix regressions: Transferable flatbuffers (#221)

* Add geometry index again

* Switch to info level for run config

* Add setting for present_mode

* Remove unused file

* Adjust web to new message

* Update package-lock.json

* One of the first working versions with a large buffer

* Add features to fix builds

* Switch to flatbuffers for data passing

* Cleanup and move entries to wasm_entries

* Install protobuf and flatbuffers

* Fix windows CI

* Fix clippy errors and warnings for generated code
This commit is contained in:
Max Ammann 2022-12-11 21:11:49 +01:00 committed by GitHub
parent 1b73ad5908
commit 9496ef0e71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 641 additions and 464 deletions

View File

@ -47,7 +47,7 @@ runs:
INSTALL_PATH="~/.binstall/"
mkdir -p "$INSTALL_PATH"
if [[ $FILE == *"zip"* ]]; then
unzip /tmp/binstall.bin -d "$INSTALL_PATH"
unzip -o /tmp/binstall.bin -d "$INSTALL_PATH"
elif [[ $FILE == *"tgz"* ]]; then
tar xf /tmp/binstall.bin -C "$INSTALL_PATH"
else

View File

@ -0,0 +1,17 @@
name: install-flatbuffers
description: Install flatbuffers
runs:
using: "composite"
steps:
- name: Install flatbuffers
shell: bash
run: |
git clone https://github.com/google/flatbuffers.git
cd flatbuffers
git checkout v22.12.06
cmake -G "Unix Makefiles"
make
./flattests
sudo make install
flatc --version

View File

@ -0,0 +1,32 @@
name: install-system-dependencies
description: Install system dependencies
runs:
using: "composite"
steps:
- name: Install System Dependencies (Windows)
if: runner.os == 'Windows'
shell: bash
run: pacman -S --noconfirm mingw-w64-x86_64-protobuf
- name: Install System Dependencies (Ubuntu)
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get install -y \
protobuf-compiler \
libwayland-dev libxkbcommon-dev # Required for winit
- name: Install System Dependencies (Ubuntu)
if: runner.os == 'macOS'
shell: bash
run: brew install protobuf
- name: Check Dependencies
if: runner.os != 'Windows'
shell: bash
run: |
protoc --version
- name: Check Dependencies
if: runner.os == 'Windows'
shell: powershell
run: |
protoc.exe --version

View File

@ -21,8 +21,7 @@ jobs:
with:
name: mdbook
- name: Install Dependencies
shell: bash
run: sudo apt-get install -y libwayland-dev libxkbcommon-dev # Required for winit
uses: ./.github/actions/install-system-dependencies
- name: Build
working-directory: docs
shell: bash

View File

@ -14,8 +14,7 @@ jobs:
with:
targets: x86_64-unknown-linux-gnu
- name: Install Dependencies
shell: bash
run: sudo apt-get install -y libwayland-dev libxkbcommon-dev # Required for winit
uses: ./.github/actions/install-system-dependencies
- name: Build
shell: bash
run: cargo build -p maplibre-demo --release --target x86_64-unknown-linux-gnu

View File

@ -13,6 +13,8 @@ jobs:
uses: ./.github/actions/setup
with:
targets: x86_64-apple-darwin
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Build
shell: bash
run: cd apple/xcode && xcodebuild -scheme "example (macOS)" build CODE_SIGNING_ALLOWED=NO MACOSX_DEPLOYMENT_TARGET=10.9 -derivedDataPath build

View File

@ -10,7 +10,9 @@ jobs:
steps:
- name: Switch to msys2
shell: powershell
run: echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
run: |
echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install mysys2 dependencies
shell: powershell
run: pacman -S --noconfirm unzip
@ -22,6 +24,8 @@ jobs:
uses: ./.github/actions/setup
with:
targets: x86_64-pc-windows-msvc
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Build
shell: powershell
run: cargo build -p maplibre-demo --release --target x86_64-pc-windows-msvc

View File

@ -14,6 +14,8 @@ jobs:
with:
nightly: true
targets: x86_64-linux-android aarch64-linux-android i686-linux-android
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Set NDK Version
shell: bash
run: |

View File

@ -3,9 +3,6 @@ name: Build apple library
on:
workflow_call:
jobs:
library-apple:
runs-on: macos-12
@ -37,6 +34,8 @@ jobs:
uses: ./.github/actions/setup
with:
targets: ${{ matrix.target }} ${{ matrix.fat-target }}
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Swift Version
shell: bash
run: swift --version

View File

@ -32,11 +32,10 @@ jobs:
with:
nightly: true
targets: wasm32-unknown-unknown
- name: Install rust sources (build-std)
if: inputs.multithreaded
shell: bash
run: |
just nightly-install-src
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install flatbuffers
uses: ./.github/actions/install-flatbuffers
- name: Install wasm-bindgen
uses: ./.github/actions/cargo-install
# Install wasm-bindgen with test runner
@ -49,7 +48,7 @@ jobs:
- name: Build demo
shell: bash
run: just web-demo build
- name: Check
- name: Check # TODO: Also check here multithreaded configurations
shell: bash
run: just nightly-check web wasm32-unknown-unknown ${{ inputs.webgl && 'web-webgl' || '""' }}
- name: Test

View File

@ -13,6 +13,8 @@ jobs:
uses: ./.github/actions/setup
with:
targets: x86_64-unknown-linux-gnu
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install GPU Drivers
uses: ./.github/actions/install-driver
- name: Download test data

View File

@ -13,6 +13,8 @@ jobs:
uses: ./.github/actions/setup
with:
targets: x86_64-unknown-linux-gnu
- name: Install Dependencies
uses: ./.github/actions/install-system-dependencies
- name: Install GPU Drivers
uses: ./.github/actions/install-driver
- name: Test Vulkan

View File

@ -32,13 +32,12 @@ nightly-override-toolchain: nightly-toolchain
nightly-targets *FLAGS: nightly-toolchain
rustup toolchain install $NIGHTLY_TOOLCHAIN --target {{FLAGS}}
# We sometimes build the stdlib with nightly
rustup component add rust-src --toolchain $NIGHTLY_TOOLCHAIN
nightly-install-rustfmt: nightly-toolchain
rustup component add rustfmt --toolchain $NIGHTLY_TOOLCHAIN
nightly-install-src: nightly-toolchain
rustup component add rust-src --toolchain $NIGHTLY_TOOLCHAIN
nightly-install-clippy: stable-toolchain
rustup component add clippy --toolchain $NIGHTLY_TOOLCHAIN

View File

@ -48,6 +48,10 @@ impl Processable for IndexLayer {
layer.process(&mut index).unwrap();
}
for layer in &mut tile.layers {
layer.process(&mut index).unwrap();
}
context
.processor_mut()
.layer_indexing_finished(&tile_request.coords, index.get_geometries())?;

View File

@ -111,7 +111,8 @@ impl TileRepository {
{
match entry {
btree_map::Entry::Vacant(_entry) => {
panic!("Can not add a tessellated layer if no request has been started before.")
panic!("Can not add a tessellated layer at {} if no request has been started before. \
We might received a tile which was not requested.", layer.get_coords())
}
btree_map::Entry::Occupied(mut entry) => {
entry.get_mut().layers.push(layer);

View File

@ -8,34 +8,45 @@ use crate::{
};
pub trait TileTessellated: Send {
fn new(coords: WorldTileCoords) -> Self;
fn build_from(coords: WorldTileCoords) -> Self
where
Self: Sized;
fn coords(&self) -> &WorldTileCoords;
fn coords(&self) -> WorldTileCoords;
}
pub trait UnavailableLayer: Send {
fn new(coords: WorldTileCoords, layer_name: String) -> Self;
pub trait LayerUnavailable: Send {
fn build_from(coords: WorldTileCoords, layer_name: String) -> Self
where
Self: Sized;
fn coords(&self) -> &WorldTileCoords;
fn coords(&self) -> WorldTileCoords;
fn layer_name(&self) -> &str;
fn to_stored_layer(self) -> StoredLayer;
}
pub trait TessellatedLayer: Send {
fn new(
pub trait LayerTessellated: Send {
fn build_from(
coords: WorldTileCoords,
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
feature_indices: Vec<u32>,
layer_data: Layer,
) -> Self;
) -> Self
where
Self: Sized;
fn coords(&self) -> &WorldTileCoords;
fn coords(&self) -> WorldTileCoords;
fn to_stored_layer(self) -> StoredLayer;
}
pub trait IndexedLayer: Send + From<(WorldTileCoords, TileIndex)> {
fn coords(&self) -> &WorldTileCoords;
pub trait LayerIndexed: Send {
fn build_from(coords: WorldTileCoords, index: TileIndex) -> Self
where
Self: Sized;
fn coords(&self) -> WorldTileCoords;
fn to_tile_index(self) -> TileIndex;
}
@ -45,12 +56,12 @@ pub struct DefaultTileTessellated {
}
impl TileTessellated for DefaultTileTessellated {
fn new(coords: WorldTileCoords) -> Self {
fn build_from(coords: WorldTileCoords) -> Self {
Self { coords }
}
fn coords(&self) -> &WorldTileCoords {
&self.coords
fn coords(&self) -> WorldTileCoords {
self.coords
}
}
@ -59,13 +70,17 @@ pub struct DefaultLayerUnavailable {
pub layer_name: String,
}
impl UnavailableLayer for DefaultLayerUnavailable {
fn new(coords: WorldTileCoords, layer_name: String) -> Self {
impl LayerUnavailable for DefaultLayerUnavailable {
fn build_from(coords: WorldTileCoords, layer_name: String) -> Self {
Self { coords, layer_name }
}
fn coords(&self) -> &WorldTileCoords {
&self.coords
fn coords(&self) -> WorldTileCoords {
self.coords
}
fn layer_name(&self) -> &str {
&self.layer_name
}
fn to_stored_layer(self) -> StoredLayer {
@ -76,6 +91,7 @@ impl UnavailableLayer for DefaultLayerUnavailable {
}
}
#[derive(Clone)]
pub struct DefaultLayerTesselated {
pub coords: WorldTileCoords,
pub buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
@ -84,8 +100,8 @@ pub struct DefaultLayerTesselated {
pub layer_data: Layer, // FIXME (perf): Introduce a better structure for this
}
impl TessellatedLayer for DefaultLayerTesselated {
fn new(
impl LayerTessellated for DefaultLayerTesselated {
fn build_from(
coords: WorldTileCoords,
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
feature_indices: Vec<u32>,
@ -99,8 +115,8 @@ impl TessellatedLayer for DefaultLayerTesselated {
}
}
fn coords(&self) -> &WorldTileCoords {
&self.coords
fn coords(&self) -> WorldTileCoords {
self.coords
}
fn to_stored_layer(self) -> StoredLayer {
@ -118,15 +134,13 @@ pub struct DefaultLayerIndexed {
index: TileIndex,
}
impl From<(WorldTileCoords, TileIndex)> for DefaultLayerIndexed {
fn from((coords, index): (WorldTileCoords, TileIndex)) -> Self {
impl LayerIndexed for DefaultLayerIndexed {
fn build_from(coords: WorldTileCoords, index: TileIndex) -> Self {
Self { coords, index }
}
}
impl IndexedLayer for DefaultLayerIndexed {
fn coords(&self) -> &WorldTileCoords {
&self.coords
fn coords(&self) -> WorldTileCoords {
self.coords
}
fn to_tile_index(self) -> TileIndex {
@ -136,9 +150,9 @@ impl IndexedLayer for DefaultLayerIndexed {
pub trait Transferables: 'static {
type TileTessellated: TileTessellated;
type LayerUnavailable: UnavailableLayer;
type LayerTessellated: TessellatedLayer;
type LayerIndexed: IndexedLayer;
type LayerUnavailable: LayerUnavailable;
type LayerTessellated: LayerTessellated;
type LayerIndexed: LayerIndexed;
}
#[derive(Copy, Clone)]

View File

@ -14,7 +14,9 @@ use crate::{
geometry_index::{IndexedGeometry, TileIndex},
pipeline::PipelineProcessor,
source_client::HttpClient,
transferables::{TessellatedLayer, TileTessellated, Transferables, UnavailableLayer},
transferables::{
LayerIndexed, LayerTessellated, LayerUnavailable, TileTessellated, Transferables,
},
},
kernel::Kernel,
render::ShaderVertex,
@ -43,7 +45,9 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
{
fn tile_finished(&mut self, coords: &WorldTileCoords) -> Result<(), Error> {
self.context
.send(Message::TileTessellated(T::TileTessellated::new(*coords)))
.send(Message::TileTessellated(T::TileTessellated::build_from(
*coords,
)))
}
fn layer_unavailable(
@ -52,7 +56,7 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
layer_name: &str,
) -> Result<(), Error> {
self.context
.send(Message::LayerUnavailable(T::LayerUnavailable::new(
.send(Message::LayerUnavailable(T::LayerUnavailable::build_from(
*coords,
layer_name.to_owned(),
)))
@ -66,7 +70,7 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
layer_data: tile::Layer,
) -> Result<(), Error> {
self.context
.send(Message::LayerTessellated(T::LayerTessellated::new(
.send(Message::LayerTessellated(T::LayerTessellated::build_from(
*coords,
buffer,
feature_indices,
@ -80,9 +84,9 @@ impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
geometries: Vec<IndexedGeometry<f64>>,
) -> Result<(), Error> {
self.context
.send(Message::LayerIndexed(T::LayerIndexed::from((
.send(Message::LayerIndexed(T::LayerIndexed::build_from(
*coords,
TileIndex::Linear { list: geometries },
))))
)))
}
}

View File

@ -8,7 +8,7 @@ use crate::{
io::{
apc::{AsyncProcedureCall, Message},
tile_repository::StoredLayer,
transferables::{IndexedLayer, TessellatedLayer, TileTessellated, UnavailableLayer},
transferables::{LayerIndexed, LayerTessellated, LayerUnavailable, TileTessellated},
},
kernel::Kernel,
schedule::Stage,
@ -46,7 +46,7 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
tracing::trace!("Tile at {} finished loading", coords);
log::warn!("Tile at {} finished loading", coords);
tile_repository.mark_tile_succeeded(coords);
tile_repository.mark_tile_succeeded(&coords);
}
// FIXME: deduplicate
Message::LayerUnavailable(message) => {
@ -77,7 +77,7 @@ impl<E: Environment> Stage for PopulateTileStore<E> {
tile_repository.put_layer(layer);
}
Message::LayerIndexed(message) => {
let coords = *message.coords();
let coords = message.coords();
log::warn!("Layer index at {} reached main thread", coords);

View File

@ -12,7 +12,7 @@ use crate::{
pipeline::{PipelineContext, Processable},
tile_pipelines::build_vector_tile_pipeline,
tile_repository::TileRepository,
transferables::{Transferables, UnavailableLayer},
transferables::{LayerUnavailable, Transferables},
TileRequest,
},
kernel::Kernel,
@ -96,7 +96,7 @@ pub fn schedule<
context.send(
Message::LayerUnavailable(<<E::AsyncProcedureCall as AsyncProcedureCall<
E::HttpClient,
>>::Transferables as Transferables>::LayerUnavailable::new(
>>::Transferables as Transferables>::LayerUnavailable::build_from(
input.coords,
to_load.to_string(),
)),

View File

@ -57,14 +57,18 @@ impl<V, I> OverAlignedVertexBuffer<V, I> {
}
}
pub fn from_slices(vertices: &[V], indices: &[I], usable_indices: u32) -> Self
pub fn from_iters<IV, II>(vertices: IV, indices: II, usable_indices: u32) -> Self
where
V: Copy,
I: Copy,
IV: IntoIterator<Item = V>,
II: IntoIterator<Item = I>,
IV::IntoIter: ExactSizeIterator,
II::IntoIter: ExactSizeIterator,
{
let mut buffers = VertexBuffers::with_capacity(0, 0);
buffers.vertices = Vec::from(vertices);
buffers.indices = Vec::from(indices);
let vertices = vertices.into_iter();
let indices = indices.into_iter();
let mut buffers = VertexBuffers::with_capacity(vertices.len(), indices.len());
buffers.vertices.extend(vertices);
buffers.indices.extend(indices);
Self {
buffer: buffers,
usable_indices,

View File

@ -2,6 +2,7 @@
name = "web"
version = "0.1.0"
publish = false
build = "build.rs"
description.workspace = true
edition.workspace = true
@ -44,6 +45,10 @@ console_log = { version = "0.2.0", features = ["color"] }
tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency
# For passing Inputs in AsyncProcedureCalls
serde_json = "1.0.85"
flatbuffers = "22.10.26"
[build-dependencies]
flatc-rust = "0.2.0"
[dev-dependencies]
wasm-bindgen-test = "0.3.31"

26
web/build.rs Normal file
View File

@ -0,0 +1,26 @@
use std::{env, fs, path::Path};
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
println!("cargo:rerun-if-changed=./flatbuffer");
let flatbuffer = fs::read_dir("./flatbuffer")
.unwrap()
.map(|entry| entry.unwrap().path())
.collect::<Vec<_>>();
flatc_rust::run(flatc_rust::Args {
inputs: &flatbuffer
.iter()
.map(|buf| buf.as_path())
.collect::<Vec<_>>(),
out_dir: Path::new(&out_dir),
extra: &[
"--include-prefix",
"platform::singlethreaded::transferables",
],
..Default::default()
})
.expect("flatc");
}

View File

@ -26,6 +26,7 @@
}
},
"../lib": {
"name": "maplibre-rs",
"version": "0.0.1",
"license": "MIT",
"dependencies": {

8
web/flatbuffer/basic.fbs Normal file
View File

@ -0,0 +1,8 @@
//namespace transferables;
struct FlatWorldTileCoords {
x: int;
y: int;
z: ubyte;
}

View File

@ -0,0 +1,10 @@
include "basic.fbs";
//namespace transferables;
table FlatLayerIndexed {
coords: FlatWorldTileCoords;
}
root_type FlatLayerIndexed;

View File

@ -0,0 +1,18 @@
include "basic.fbs";
struct FlatShaderVertex {
position: [float:2];
normal: [float:2];
}
table FlatLayerTessellated {
coords: FlatWorldTileCoords;
layer_name: string;
vertices: [FlatShaderVertex];
indices: [uint];
usable_indices: uint;
// Holds for each feature the count of indices.
feature_indices: [uint];
}
root_type FlatLayerTessellated;

View File

@ -0,0 +1,10 @@
include "basic.fbs";
//namespace transferables;
table FlatLayerUnavailable {
coords: FlatWorldTileCoords;
layer_name: string;
}
root_type FlatLayerUnavailable;

View File

@ -0,0 +1,9 @@
include "basic.fbs";
//namespace transferables;
table FlatTileTessellated {
coords: FlatWorldTileCoords;
}
root_type FlatTileTessellated;

View File

@ -72,7 +72,7 @@ let baseConfig = {
let config = {
...baseConfig,
entryPoints:['src/index.ts'],
entryPoints: ['src/index.ts'],
incremental: argv.watch,
plugins: [
inlineWorker({
@ -108,23 +108,28 @@ const getLibDirectory = () => {
const emitTypeScript = () => {
let outDirectory = `${getLibDirectory()}/dist/ts`;
let child = spawnSync('npm', ["exec",
let child = spawnTool('npm', ["exec",
"tsc",
"--",
"-m", "es2022",
"-outDir", outDirectory,
"--declaration",
"--emitDeclarationOnly"
], {
cwd: '.',
stdio: 'inherit',
});
]);
if (child.status !== 0) {
throw new Error("Failed to execute tsc")
}
}
const spawnTool = (program, args) => {
console.debug(`Executing: ${program} ${args.join(" ")}`)
return spawnSync(program, args, {
cwd: '.',
stdio: 'inherit',
})
}
// TODO: Do not continue if one step fails
const wasmPack = () => {
let outDirectory = `${getLibDirectory()}/src/wasm`;
@ -140,7 +145,7 @@ const wasmPack = () => {
"-C", "link-args=--shared-memory --import-memory --max-memory=209715200"
]`
let cargo = spawnSync('cargo', [
let cargo = spawnTool('cargo', [
...(multithreaded ? ["--config", multithreaded_config] : []),
"build",
"-p", "web", "--lib",
@ -148,26 +153,20 @@ const wasmPack = () => {
"--profile", profile,
"--features", `${webgl ? "web-webgl," : ""}`,
...(multithreaded ? ["-Z", "build-std=std,panic_abort"] : []),
], {
cwd: '.',
stdio: 'inherit',
});
]);
if (cargo.status !== 0) {
throw new Error("Failed to execute cargo build")
}
let wasmbindgen = spawnSync('wasm-bindgen', [
let wasmbindgen = spawnTool('wasm-bindgen', [
`${getProjectDirectory()}/target/wasm32-unknown-unknown/${profile}/web.wasm`,
"--out-name", "maplibre",
"--out-dir", outDirectory,
"--typescript",
"--target", "web",
"--debug",
], {
cwd: '.',
stdio: 'inherit',
});
]);
if (wasmbindgen.status !== 0) {
throw new Error("Failed to execute wasm-bindgen")
@ -175,15 +174,12 @@ const wasmPack = () => {
if (release) {
console.log("Running wasm-opt")
let wasmOpt = spawnSync('npm', ["exec",
let wasmOpt = spawnTool('npm', ["exec",
"wasm-opt", "--",
`${outDirectory}/maplibre_bg.wasm`,
"-o", `${outDirectory}/maplibre_bg.wasm`,
"-O"
], {
cwd: '.',
stdio: 'inherit',
});
]);
if (wasmOpt.status !== 0) {
throw new Error("Failed to execute wasm-opt")
@ -238,26 +234,26 @@ const esbuild = async (name, globalName = undefined) => {
const start = async () => {
try {
console.log("Creating WASM...")
wasmPack();
console.log("Creating WASM...")
wasmPack();
if (esm) {
console.log("Building esm bundle...")
await esbuild("esm")
}
if (esm) {
console.log("Building esm bundle...")
await esbuild("esm")
}
if (cjs) {
console.log("Building cjs bundle...")
await esbuild("cjs")
}
if (cjs) {
console.log("Building cjs bundle...")
await esbuild("cjs")
}
if (iife) {
console.log("Building iife bundle...")
await esbuild("iife", "maplibre")
}
if (iife) {
console.log("Building iife bundle...")
await esbuild("iife", "maplibre")
}
console.log("Emitting TypeScript types...")
emitTypeScript();
console.log("Emitting TypeScript types...")
emitTypeScript();
} catch (e) {
console.error("Failed to start building: " + e.message)
process.exit(1)

View File

@ -46,11 +46,10 @@ export const startMapLibre = async (wasmPath: string | undefined, workerPath: st
}) : PoolWorker();
worker.onmessage = (message: MessageEvent) => {
let tag = message.data[0];
let data = new Uint8Array(message.data[1]);
// WARNING: Do not modify in_transfer_obj!
let in_transfer_obj = message.data;
// @ts-ignore TODO singlethreaded_main_entry may not be defined
maplibre.singlethreaded_main_entry(ptr, tag, data)
maplibre.singlethreaded_main_entry(ptr, in_transfer_obj)
}
return worker;

View File

@ -1,5 +1,5 @@
#![deny(unused_imports)]
#![feature(allocator_api, new_uninit)]
#![feature(new_uninit)]
use maplibre::{
event_loop::EventLoop,

View File

@ -1,2 +1,3 @@
pub mod pool;
pub mod pool_scheduler;
pub mod wasm_entries;

View File

@ -7,7 +7,6 @@ use std::{cell::RefCell, rc::Rc};
use js_sys::Promise;
use rand::prelude::*;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::Worker;
#[wasm_bindgen()]
@ -25,10 +24,26 @@ struct PoolState {
workers: RefCell<Vec<Worker>>,
}
struct Work {
impl PoolState {
fn push(&self, worker: Worker) {
let mut workers = self.workers.borrow_mut();
for existing_worker in workers.iter() {
assert_ne!(existing_worker as &JsValue, &worker as &JsValue);
}
workers.push(worker);
}
}
pub struct Work {
func: Box<dyn (FnOnce() -> Promise) + Send>,
}
impl Work {
pub fn execute(self) -> Promise {
(self.func)()
}
}
impl WorkerPool {
/// Creates a new `WorkerPool` which immediately creates `initial` workers.
///
@ -130,21 +145,3 @@ impl WorkerPool {
}
}
}
impl PoolState {
fn push(&self, worker: Worker) {
let mut workers = self.workers.borrow_mut();
for existing_worker in workers.iter() {
assert_ne!(existing_worker as &JsValue, &worker as &JsValue);
}
workers.push(worker);
}
}
/// Entry point invoked by the worker.
#[wasm_bindgen]
pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), JsValue> {
let ptr = unsafe { Box::from_raw(ptr as *mut Work) };
JsFuture::from((ptr.func)()).await?;
Ok(())
}

View File

@ -0,0 +1,12 @@
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use crate::platform::multithreaded::pool::Work;
/// Entry point invoked by the worker.
#[wasm_bindgen]
pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), JsValue> {
let work = unsafe { Box::from_raw(ptr as *mut Work) };
JsFuture::from(work.execute()).await?;
Ok(())
}

View File

@ -1,32 +1,23 @@
use std::{cell::RefCell, mem, mem::size_of, rc::Rc, slice};
use std::{cell::RefCell, rc::Rc};
use js_sys::Uint8Array;
use log::info;
use js_sys::{ArrayBuffer, Uint8Array};
use log::error;
use maplibre::{
error::Error,
io::{
apc::{AsyncProcedure, AsyncProcedureCall, Context, Input, Message},
source_client::{HttpSourceClient, SourceClient},
transferables::Transferables,
source_client::SourceClient,
},
};
use wasm_bindgen::{prelude::*, JsCast, JsValue};
use wasm_bindgen::{JsCast, JsValue};
use web_sys::{DedicatedWorkerGlobalScope, Worker};
use crate::{
platform::singlethreaded::transferables::{
InnerData, LinearLayerIndexed, LinearLayerTesselated, LinearLayerUnavailable,
LinearTileTessellated, LinearTransferables,
},
WHATWGFetchHttpClient,
use crate::platform::singlethreaded::{
transferables::FlatBufferTransferable, UsedContext, UsedHttpClient, UsedTransferables,
};
type UsedTransferables = LinearTransferables;
type UsedHttpClient = WHATWGFetchHttpClient;
type UsedContext = PassingContext;
#[derive(Debug)]
enum SerializedMessageTag {
pub enum SerializedMessageTag {
TileTessellated = 1,
LayerUnavailable = 2,
LayerTessellated = 3,
@ -34,7 +25,16 @@ enum SerializedMessageTag {
}
impl SerializedMessageTag {
fn from_u32(tag: u32) -> Option<Self> {
pub fn from_message(message: &Message<UsedTransferables>) -> Self {
match message {
Message::TileTessellated(_) => SerializedMessageTag::TileTessellated,
Message::LayerUnavailable(_) => SerializedMessageTag::LayerUnavailable,
Message::LayerTessellated(_) => SerializedMessageTag::LayerTessellated,
Message::LayerIndexed(_) => SerializedMessageTag::LayerIndexed,
}
}
pub fn from_u32(tag: u32) -> Option<Self> {
match tag {
x if x == SerializedMessageTag::LayerUnavailable as u32 => {
Some(SerializedMessageTag::LayerUnavailable)
@ -53,107 +53,43 @@ impl SerializedMessageTag {
}
}
trait SerializableMessage {
fn serialize(&self) -> &[u8];
fn deserialize(tag: SerializedMessageTag, data: Uint8Array) -> Message<UsedTransferables>;
fn tag(&self) -> SerializedMessageTag;
}
impl SerializableMessage for Message<LinearTransferables> {
fn serialize(&self) -> &[u8] {
unsafe {
match self {
// TODO https://github.com/Lokathor/bytemuck/blob/518baf9c0b73c92b4ea4406fe15e005c6d71535a/src/internal.rs#L333
Message::TileTessellated(message) => slice::from_raw_parts(
message as *const LinearTileTessellated as *mut u8,
size_of::<LinearTileTessellated>(),
),
Message::LayerUnavailable(message) => slice::from_raw_parts(
message as *const LinearLayerUnavailable as *mut u8,
size_of::<LinearLayerUnavailable>(),
),
Message::LayerTessellated(message) => slice::from_raw_parts(
message.data.as_ref() as *const InnerData as *mut u8,
size_of::<InnerData>(),
),
Message::LayerIndexed(message) => slice::from_raw_parts(
message as *const LinearLayerIndexed as *mut u8,
size_of::<LinearLayerIndexed>(),
),
}
}
}
fn deserialize(tag: SerializedMessageTag, data: Uint8Array) -> Message<UsedTransferables> {
type TileTessellated = <UsedTransferables as Transferables>::TileTessellated;
type UnavailableLayer = <UsedTransferables as Transferables>::LayerUnavailable;
type IndexedLayer = <UsedTransferables as Transferables>::LayerIndexed;
unsafe {
// TODO: https://github.com/Lokathor/bytemuck/blob/518baf9c0b73c92b4ea4406fe15e005c6d71535a/src/internal.rs#L159
match tag {
SerializedMessageTag::TileTessellated => {
Message::<UsedTransferables>::TileTessellated(
(&*(data.to_vec().as_slice() as *const [u8] as *const TileTessellated))
.clone(),
)
}
SerializedMessageTag::LayerUnavailable => {
Message::<UsedTransferables>::LayerUnavailable(
(&*(data.to_vec().as_slice() as *const [u8] as *const UnavailableLayer))
.clone(),
)
}
SerializedMessageTag::LayerTessellated => {
Message::<UsedTransferables>::LayerTessellated(LinearLayerTesselated {
data: unsafe {
let mut uninit = Box::<InnerData>::new_zeroed();
data.raw_copy_to_ptr(uninit.as_mut_ptr() as *mut u8);
uninit.assume_init()
},
})
}
SerializedMessageTag::LayerIndexed => Message::<UsedTransferables>::LayerIndexed(
(&*(data.to_vec().as_slice() as *const [u8] as *const IndexedLayer)).clone(),
),
}
}
}
fn tag(&self) -> SerializedMessageTag {
match self {
Message::TileTessellated(_) => SerializedMessageTag::TileTessellated,
Message::LayerUnavailable(_) => SerializedMessageTag::LayerUnavailable,
Message::LayerTessellated(_) => SerializedMessageTag::LayerTessellated,
Message::LayerIndexed(_) => SerializedMessageTag::LayerIndexed,
}
}
}
#[derive(Clone)]
pub struct PassingContext {
source_client: SourceClient<UsedHttpClient>,
pub source_client: SourceClient<UsedHttpClient>,
}
impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
fn send(&self, data: Message<UsedTransferables>) -> Result<(), Error> {
let tag = data.tag();
let serialized = data.serialize();
fn send(&self, message: Message<UsedTransferables>) -> Result<(), Error> {
let tag = SerializedMessageTag::from_message(&message);
let transferable = FlatBufferTransferable::from_message(message);
let data = &transferable.data[transferable.start..];
let serialized_array_buffer = js_sys::ArrayBuffer::new(serialized.len() as u32);
let serialized_array = js_sys::Uint8Array::new(&serialized_array_buffer);
let buffer = ArrayBuffer::new(data.len() as u32);
let byte_buffer = Uint8Array::new(&buffer);
unsafe {
serialized_array.set(&Uint8Array::view(serialized), 0);
byte_buffer.set(&Uint8Array::view(data), 0);
}
let tag = tag as u32;
let global: DedicatedWorkerGlobalScope =
js_sys::global().dyn_into().map_err(|_e| Error::APC)?;
let array = js_sys::Array::new();
array.push(&JsValue::from(tag as u32));
array.push(&serialized_array_buffer);
global.post_message(&array).map_err(|_e| Error::APC)
let transfer_obj = js_sys::Array::new();
transfer_obj.push(&JsValue::from(tag));
transfer_obj.push(&buffer);
let transfer = js_sys::Array::new();
transfer.push(&buffer);
// TODO: Verify transfer
global
.post_message_with_transfer(&transfer_obj, &transfer)
.map_err(|e| {
error!("{:?}", e);
Error::APC
})
}
fn source_client(&self) -> &SourceClient<UsedHttpClient> {
@ -161,7 +97,7 @@ impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
}
}
type ReceivedType = RefCell<Vec<Message<UsedTransferables>>>;
pub type ReceivedType = RefCell<Vec<Message<UsedTransferables>>>;
pub struct PassingAsyncProcedureCall {
new_worker: Box<dyn Fn() -> Worker>,
@ -210,7 +146,10 @@ impl AsyncProcedureCall<UsedHttpClient> for PassingAsyncProcedureCall {
type Transferables = UsedTransferables;
fn receive(&self) -> Option<Message<UsedTransferables>> {
self.received.borrow_mut().pop()
self.received
.try_borrow_mut()
.expect("Failed to borrow in receive of APC")
.pop()
}
fn call(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
@ -224,44 +163,3 @@ impl AsyncProcedureCall<UsedHttpClient> for PassingAsyncProcedureCall {
self.workers[0].post_message(&array).unwrap(); // FIXME (wasm-executor): Remove unwrap
}
}
/// Entry point invoked by the worker.
#[wasm_bindgen]
pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JsValue> {
let procedure: AsyncProcedure<UsedContext> = unsafe { std::mem::transmute(procedure_ptr) };
let input = serde_json::from_str::<Input>(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
let context = PassingContext {
source_client: SourceClient::new(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
};
(procedure)(input, context).await;
Ok(())
}
/// Entry point invoked by the main thread.
#[wasm_bindgen]
pub unsafe fn singlethreaded_main_entry(
received_ptr: *const ReceivedType,
type_id: u32,
data: Uint8Array,
) -> Result<(), JsValue> {
// FIXME (wasm-executor): Can we make this call safe? check if it was cloned before?
let received: Rc<ReceivedType> = Rc::from_raw(received_ptr);
let message = Message::<UsedTransferables>::deserialize(
SerializedMessageTag::from_u32(type_id).unwrap(),
data,
);
info!("singlethreaded_main_entry {:?}", message.tag());
// MAJOR FIXME: Fix mutability
received.borrow_mut().push(message);
mem::forget(received); // FIXME (wasm-executor): Enforce this somehow
Ok(())
}

View File

@ -1,2 +1,12 @@
use crate::platform::{
http_client::WHATWGFetchHttpClient,
singlethreaded::{apc::PassingContext, transferables::FlatTransferables},
};
pub mod apc;
pub mod transferables;
pub mod wasm_entries;
pub type UsedTransferables = FlatTransferables;
pub type UsedHttpClient = WHATWGFetchHttpClient;
pub type UsedContext = PassingContext;

View File

@ -1,236 +1,235 @@
use log::warn;
use flatbuffers::FlatBufferBuilder;
use maplibre::{
benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer},
coords::WorldTileCoords,
io::{
apc::Message,
geometry_index::TileIndex,
tile_repository::StoredLayer,
transferables::{
IndexedLayer, TessellatedLayer, TileTessellated, Transferables, UnavailableLayer,
LayerIndexed, LayerTessellated, LayerUnavailable, TileTessellated, Transferables,
},
},
render::ShaderVertex,
tile::Layer,
};
#[derive(Copy, Clone)]
pub struct LinearTileTessellated {
pub coords: WorldTileCoords,
pub _padding: u8,
}
use crate::platform::singlethreaded::{
transferables::{
basic_generated::*, layer_indexed_generated::*, layer_tessellated_generated::*,
layer_unavailable_generated::*, tile_tessellated_generated::*,
},
UsedTransferables,
};
impl TileTessellated for LinearTileTessellated {
fn new(coords: WorldTileCoords) -> Self {
Self {
coords,
_padding: 0,
pub mod basic_generated {
#![allow(unused, unused_imports, clippy::all)]
use maplibre::coords::{WorldTileCoords, ZoomLevel};
include!(concat!(env!("OUT_DIR"), "/basic_generated.rs"));
impl Into<WorldTileCoords> for &FlatWorldTileCoords {
fn into(self) -> WorldTileCoords {
WorldTileCoords {
x: self.x(),
y: self.y(),
z: ZoomLevel::new(self.z()),
}
}
}
fn coords(&self) -> &WorldTileCoords {
&self.coords
}
}
pub mod layer_indexed_generated {
#![allow(unused, unused_imports, clippy::all)]
include!(concat!(env!("OUT_DIR"), "/layer_indexed_generated.rs"));
}
pub mod layer_tessellated_generated {
#![allow(unused, unused_imports, clippy::all)]
include!(concat!(env!("OUT_DIR"), "/layer_tessellated_generated.rs"));
}
pub mod layer_unavailable_generated {
#![allow(unused, unused_imports, clippy::all)]
include!(concat!(env!("OUT_DIR"), "/layer_unavailable_generated.rs"));
}
pub mod tile_tessellated_generated {
#![allow(unused, unused_imports, clippy::all)]
include!(concat!(env!("OUT_DIR"), "/tile_tessellated_generated.rs"));
}
#[derive(Copy, Clone)]
pub struct LinearLayerUnavailable {
pub coords: WorldTileCoords,
pub layer_name: [u8; 32],
pub struct FlatBufferTransferable {
pub data: Vec<u8>,
pub start: usize,
}
impl UnavailableLayer for LinearLayerUnavailable {
fn new(coords: WorldTileCoords, layer_name: String) -> Self {
let mut new_layer_name = [0; 32];
new_layer_name[0..layer_name.len()].clone_from_slice(layer_name.as_bytes());
Self {
coords,
layer_name: new_layer_name,
impl FlatBufferTransferable {
pub fn from_message(message: Message<UsedTransferables>) -> Self {
match message {
Message::TileTessellated(transferable) => transferable,
Message::LayerUnavailable(transferable) => transferable,
Message::LayerTessellated(transferable) => transferable,
Message::LayerIndexed(transferable) => transferable,
}
}
}
fn coords(&self) -> &WorldTileCoords {
&self.coords
impl TileTessellated for FlatBufferTransferable {
fn build_from(coords: WorldTileCoords) -> Self {
let mut inner_builder = FlatBufferBuilder::with_capacity(1024);
let mut builder = FlatTileTessellatedBuilder::new(&mut inner_builder);
builder.add_coords(&FlatWorldTileCoords::new(
coords.x,
coords.y,
coords.z.into(),
));
let root = builder.finish();
inner_builder.finish(root, None);
let (data, start) = inner_builder.collapse();
FlatBufferTransferable { data, start }
}
fn coords(&self) -> WorldTileCoords {
let data = unsafe { root_as_flat_tile_tessellated_unchecked(&self.data[self.start..]) };
data.coords().unwrap().into()
}
}
impl LayerUnavailable for FlatBufferTransferable {
fn build_from(coords: WorldTileCoords, layer_name: String) -> Self {
let mut inner_builder = FlatBufferBuilder::with_capacity(1024);
let layer_name = inner_builder.create_string(&layer_name);
let mut builder = FlatLayerUnavailableBuilder::new(&mut inner_builder);
builder.add_coords(&FlatWorldTileCoords::new(
coords.x,
coords.y,
coords.z.into(),
));
builder.add_layer_name(layer_name);
let root = builder.finish();
inner_builder.finish(root, None);
let (data, start) = inner_builder.collapse();
FlatBufferTransferable { data, start }
}
fn coords(&self) -> WorldTileCoords {
let data = unsafe { root_as_flat_layer_unavailable_unchecked(&self.data[self.start..]) };
data.coords().unwrap().into()
}
fn layer_name(&self) -> &str {
let data = unsafe { root_as_flat_layer_unavailable_unchecked(&self.data[self.start..]) };
data.layer_name().expect("property must be set")
}
fn to_stored_layer(self) -> StoredLayer {
StoredLayer::UnavailableLayer {
coords: self.coords,
layer_name: String::from_utf8(Vec::from(self.layer_name)).unwrap(), // FIXME (wasm-executor): Remove unwrap
layer_name: self.layer_name().to_owned(),
coords: LayerUnavailable::coords(&self),
}
}
}
#[derive(Copy, Clone)]
pub struct InnerData {
pub coords: WorldTileCoords,
pub layer_name: [u8; 32],
pub layer_name_len: usize,
pub vertices: [ShaderVertex; 15000],
pub vertices_len: usize,
pub indices: [IndexDataType; 40000],
pub indices_len: usize,
pub usable_indices: u32,
/// Holds for each feature the count of indices.
pub feature_indices: [u32; 2048],
pub feature_indices_len: usize,
}
#[derive(Clone)]
pub struct LinearLayerTesselated {
pub data: Box<InnerData>,
}
impl TessellatedLayer for LinearLayerTesselated {
fn new(
impl LayerTessellated for FlatBufferTransferable {
fn build_from(
coords: WorldTileCoords,
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
feature_indices: Vec<u32>,
layer_data: Layer,
) -> Self {
let mut data = Box::new(InnerData {
coords,
let mut inner_builder = FlatBufferBuilder::with_capacity(1024);
layer_name: [0; 32],
layer_name_len: layer_data.name.len(),
let vertices = inner_builder.create_vector(
&buffer
.buffer
.vertices
.iter()
.map(|vertex| FlatShaderVertex::new(&vertex.position, &vertex.normal))
.collect::<Vec<_>>(),
);
let indices = inner_builder.create_vector(&buffer.buffer.indices);
let feature_indices = inner_builder.create_vector(&feature_indices);
let layer_name = inner_builder.create_string(&layer_data.name);
vertices: [ShaderVertex::new([0.0, 0.0], [0.0, 0.0]); 15000],
vertices_len: buffer.buffer.vertices.len(),
let mut builder = FlatLayerTessellatedBuilder::new(&mut inner_builder);
indices: [0; 40000],
indices_len: buffer.buffer.indices.len(),
builder.add_coords(&FlatWorldTileCoords::new(
coords.x,
coords.y,
coords.z.into(),
));
builder.add_layer_name(layer_name);
builder.add_vertices(vertices);
builder.add_indices(indices);
builder.add_feature_indices(feature_indices);
builder.add_usable_indices(buffer.usable_indices);
let root = builder.finish();
usable_indices: buffer.usable_indices,
feature_indices: [0u32; 2048],
feature_indices_len: feature_indices.len(),
});
if buffer.buffer.vertices.len() > 15000 {
warn!("vertices too large");
return Self {
data: Box::new(InnerData {
coords,
layer_name: [0; 32],
layer_name_len: 0,
vertices: [ShaderVertex::new([0.0, 0.0], [0.0, 0.0]); 15000],
vertices_len: 0,
indices: [0; 40000],
indices_len: 0,
usable_indices: 0,
feature_indices: [0u32; 2048],
feature_indices_len: 0,
}),
};
}
if buffer.buffer.indices.len() > 40000 {
warn!("indices too large");
return Self {
data: Box::new(InnerData {
coords,
layer_name: [0; 32],
layer_name_len: 0,
vertices: [ShaderVertex::new([0.0, 0.0], [0.0, 0.0]); 15000],
vertices_len: 0,
indices: [0; 40000],
indices_len: 0,
usable_indices: 0,
feature_indices: [0u32; 2048],
feature_indices_len: 0,
}),
};
}
if feature_indices.len() > 2048 {
warn!("feature_indices too large");
return Self {
data: Box::new(InnerData {
coords,
layer_name: [0; 32],
layer_name_len: 0,
vertices: [ShaderVertex::new([0.0, 0.0], [0.0, 0.0]); 15000],
vertices_len: 0,
indices: [0; 40000],
indices_len: 0,
usable_indices: 0,
feature_indices: [0u32; 2048],
feature_indices_len: 0,
}),
};
}
data.vertices[0..buffer.buffer.vertices.len()].clone_from_slice(&buffer.buffer.vertices);
data.indices[0..buffer.buffer.indices.len()].clone_from_slice(&buffer.buffer.indices);
data.feature_indices[0..feature_indices.len()].clone_from_slice(&feature_indices);
data.layer_name[0..layer_data.name.len()].clone_from_slice(layer_data.name.as_bytes());
Self { data }
inner_builder.finish(root, None);
let (data, start) = inner_builder.collapse();
FlatBufferTransferable { data, start }
}
fn coords(&self) -> &WorldTileCoords {
&self.data.coords
fn coords(&self) -> WorldTileCoords {
let data = unsafe { root_as_flat_layer_tessellated_unchecked(&self.data[self.start..]) };
data.coords().unwrap().into()
}
fn to_stored_layer(self) -> StoredLayer {
// TODO: Avoid copies here
let data = unsafe { root_as_flat_layer_tessellated_unchecked(&self.data[self.start..]) };
let vertices = data
.vertices()
.unwrap()
.iter()
.map(|vertex| ShaderVertex::new(vertex.position().into(), vertex.normal().into()));
let indices = data.indices().unwrap();
let feature_indices: Vec<u32> = data.feature_indices().unwrap().iter().collect();
let usable_indices = data.usable_indices();
StoredLayer::TessellatedLayer {
coords: self.data.coords,
layer_name: String::from_utf8(Vec::from(
&self.data.layer_name[..self.data.layer_name_len],
))
.unwrap(), // FIXME (wasm-executor): Remove unwrap
buffer: OverAlignedVertexBuffer::from_slices(
&self.data.vertices[..self.data.vertices_len],
&self.data.indices[..self.data.indices_len],
self.data.usable_indices,
),
feature_indices: Vec::from(&self.data.feature_indices[..self.data.feature_indices_len]),
coords: LayerTessellated::coords(&self),
layer_name: data.layer_name().unwrap().to_owned(),
buffer: OverAlignedVertexBuffer::from_iters(vertices, indices, usable_indices),
feature_indices,
}
}
}
impl LayerIndexed for FlatBufferTransferable {
fn build_from(coords: WorldTileCoords, _index: TileIndex) -> Self {
let mut inner_builder = FlatBufferBuilder::with_capacity(1024);
let mut builder = FlatLayerIndexedBuilder::new(&mut inner_builder);
// TODO index
builder.add_coords(&FlatWorldTileCoords::new(
coords.x,
coords.y,
coords.z.into(),
));
let root = builder.finish();
inner_builder.finish(root, None);
let (data, start) = inner_builder.collapse();
FlatBufferTransferable { data, start }
}
fn coords(&self) -> WorldTileCoords {
let data = unsafe { root_as_flat_layer_indexed_unchecked(&self.data[self.start..]) };
data.coords().unwrap().into()
}
fn to_tile_index(self) -> TileIndex {
TileIndex::Linear { list: vec![] } // TODO
}
}
#[derive(Copy, Clone)]
pub struct LinearLayerIndexed {
pub coords: WorldTileCoords,
}
impl From<(WorldTileCoords, TileIndex)> for LinearLayerIndexed {
fn from((coords, _index): (WorldTileCoords, TileIndex)) -> Self {
Self { coords }
}
}
impl IndexedLayer for LinearLayerIndexed {
fn coords(&self) -> &WorldTileCoords {
&self.coords
}
fn to_tile_index(self) -> TileIndex {
// FIXME replace this stub implementation
TileIndex::Linear { list: vec![] }
}
}
pub struct LinearTransferables;
impl Transferables for LinearTransferables {
type TileTessellated = LinearTileTessellated;
type LayerUnavailable = LinearLayerUnavailable;
type LayerTessellated = LinearLayerTesselated;
type LayerIndexed = LinearLayerIndexed;
pub struct FlatTransferables;
impl Transferables for FlatTransferables {
type TileTessellated = FlatBufferTransferable;
type LayerUnavailable = FlatBufferTransferable;
type LayerTessellated = FlatBufferTransferable;
type LayerIndexed = FlatBufferTransferable;
}

View File

@ -0,0 +1,95 @@
use std::{mem, rc::Rc};
use js_sys::{ArrayBuffer, Uint8Array};
use log::{error, info};
use maplibre::{
benchmarking::io::{
apc::{AsyncProcedure, Input, Message},
source_client::{HttpSourceClient, SourceClient},
},
io::transferables::Transferables,
};
use wasm_bindgen::{prelude::*, JsCast};
use crate::{
platform::singlethreaded::{
apc::{ReceivedType, SerializedMessageTag},
transferables::FlatBufferTransferable,
PassingContext, UsedContext, UsedTransferables,
},
WHATWGFetchHttpClient,
};
/// Entry point invoked by the worker.
#[wasm_bindgen]
pub async fn singlethreaded_worker_entry(procedure_ptr: u32, input: String) -> Result<(), JsValue> {
let procedure: AsyncProcedure<UsedContext> = unsafe { mem::transmute(procedure_ptr) };
let input = serde_json::from_str::<Input>(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
let context = PassingContext {
source_client: SourceClient::new(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
};
let result = (procedure)(input, context).await;
if let Err(e) = result {
error!("{:?}", e); // TODO handle better
}
Ok(())
}
/// Entry point invoked by the main thread.
#[wasm_bindgen]
pub unsafe fn singlethreaded_main_entry(
received_ptr: *const ReceivedType,
in_transfer_obj: js_sys::Array,
) -> Result<(), JsValue> {
// FIXME (wasm-executor): Can we make this call safe? check if it was cloned before?
let received: Rc<ReceivedType> = Rc::from_raw(received_ptr);
let tag = in_transfer_obj.get(0).as_f64().unwrap() as u32;
let tag = SerializedMessageTag::from_u32(tag).unwrap();
info!("singlethreaded_main_entry {:?}", tag);
let buffer: ArrayBuffer = in_transfer_obj.get(1).dyn_into().unwrap();
let buffer = Uint8Array::new(&buffer);
type TileTessellated = <UsedTransferables as Transferables>::TileTessellated;
type UnavailableLayer = <UsedTransferables as Transferables>::LayerUnavailable;
type IndexedLayer = <UsedTransferables as Transferables>::LayerIndexed;
let transferable = FlatBufferTransferable {
data: buffer.to_vec(),
start: 0,
};
// TODO: Verify that data matches tag
let message = match tag {
SerializedMessageTag::TileTessellated => {
Message::<UsedTransferables>::TileTessellated(transferable)
}
SerializedMessageTag::LayerUnavailable => {
Message::<UsedTransferables>::LayerUnavailable(transferable)
}
SerializedMessageTag::LayerTessellated => {
Message::<UsedTransferables>::LayerTessellated(transferable)
}
SerializedMessageTag::LayerIndexed => {
Message::<UsedTransferables>::LayerIndexed(transferable)
}
};
// MAJOR FIXME: Fix mutability
received
.try_borrow_mut()
.expect("Failed to borrow in singlethreaded_main_entry")
.push(message);
mem::forget(received); // FIXME (wasm-executor): Enforce this somehow
Ok(())
}