mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Merge pull request #174 from maxammann/wasm-executor
* Experiment with single threaded executor * Avoid exiting process * Use custom profile and remove wasm-pack * Update run configs * Prepare JS lib * Remove run config * Disable geometry index * Conditionally enable multithreaded implementation * Add run config * Add conditional compilation * Introduce environment * Remove ScheduleMethod and replace with Scheduler * Introduce AsyncProcedureCall * Update run config * Add experiment for unsync scheduling * Always import memory * Add first working version which does not need shared memory on web * Fix linux CI script * Remove maximum memory limits * Fix multithreading in web * Fix tracy dependency versions * Run formatter * Add a lot of TODO notes * Deduplicate workflow for more demos * Fix formatting * Selectively install targets * Fix wasm-bindgen install * Do not build std lib by default * Restructure web module to include JS code for multithreaded and non-multithreaded * Make multithreading named consistantly * Add more jobs * Do not rebuild std on check and test * Mark functions unsafe * Add runner for wasm32 again * Fix check expression * Finish CI setup * Add type for the tag * Update documentation * Switch to TS config for webpack * Fix android checks
This commit is contained in:
commit
2696d8133b
@ -2,10 +2,20 @@
|
||||
rustflags = [
|
||||
# Enabled unstable APIs from web_sys
|
||||
"--cfg=web_sys_unstable_apis",
|
||||
# Enables features which are required for shared-memory
|
||||
"-C", "target-feature=+atomics,+bulk-memory,+mutable-globals",
|
||||
# Enables the possibility to import memory into wasm.
|
||||
# Without --shared-memory it is not possible to use shared WebAssembly.Memory.
|
||||
"-C", "link-args=--shared-memory --import-memory",
|
||||
"-C", "link-args=--import-memory",
|
||||
]
|
||||
runner = 'wasm-bindgen-test-runner'
|
||||
|
||||
[profile.wasm-dev]
|
||||
inherits = "dev"
|
||||
opt-level = 's'
|
||||
debug = true
|
||||
debug-assertions = true
|
||||
overflow-checks = true
|
||||
panic = 'abort'
|
||||
|
||||
[profile.wasm-release]
|
||||
inherits = "release"
|
||||
opt-level = 's'
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
|
||||
2
.github/actions/cloudflare-deploy/action.yml
vendored
2
.github/actions/cloudflare-deploy/action.yml
vendored
@ -1,5 +1,5 @@
|
||||
name: deploy
|
||||
description: Deploy on maxammann.org
|
||||
description: Deploy on cloudflare
|
||||
|
||||
inputs:
|
||||
project:
|
||||
|
||||
2
.github/workflows/build-deploy-docs.yml
vendored
2
.github/workflows/build-deploy-docs.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: just stable-toolchain
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Setup mdBook
|
||||
uses: peaceiris/actions-mdbook@v1
|
||||
|
||||
8
.github/workflows/demo-linux.yml
vendored
8
.github/workflows/demo-linux.yml
vendored
@ -12,14 +12,16 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: |
|
||||
just stable-toolchain
|
||||
just stable-targets x86_64-unknown-linux-gnu
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install Dependencies
|
||||
shell: bash
|
||||
run: sudo apt-get install -y libwayland-dev libxkbcommon-dev # Required for winit
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: cargo build -p maplibre-demo
|
||||
run: cargo build -p maplibre-demo --release --target x86_64-unknown-linux-gnu
|
||||
- name: Check
|
||||
shell: bash
|
||||
run: just check maplibre-demo x86_64-unknown-linux-gnu
|
||||
@ -29,4 +31,4 @@ jobs:
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: maplibre-rs
|
||||
path: target/x86_64-unknown-linux-gnu/debug/maplibre-demo
|
||||
path: target/x86_64-unknown-linux-gnu/release/maplibre-demo
|
||||
|
||||
4
.github/workflows/demo-macos.yml
vendored
4
.github/workflows/demo-macos.yml
vendored
@ -14,7 +14,9 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: |
|
||||
just stable-toolchain
|
||||
just stable-targets x86_64-apple-darwin
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build
|
||||
shell: bash
|
||||
|
||||
4
.github/workflows/demo-windows.yml
vendored
4
.github/workflows/demo-windows.yml
vendored
@ -12,7 +12,9 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: |
|
||||
just stable-toolchain
|
||||
just stable-targets x86_64-pc-windows-msvc
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: ilammy/msvc-dev-cmd@v1 # Provide access to lib.exe
|
||||
- name: Build
|
||||
|
||||
8
.github/workflows/library-android.yml
vendored
8
.github/workflows/library-android.yml
vendored
@ -12,7 +12,9 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install nightly toolchain
|
||||
shell: bash
|
||||
run: just nightly-toolchain-android
|
||||
run: |
|
||||
just nightly-toolchain
|
||||
just nightly-targets x86_64-linux-android aarch64-linux-android i686-linux-android
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Set NDK Version
|
||||
shell: bash
|
||||
@ -30,13 +32,13 @@ jobs:
|
||||
run: |
|
||||
env "AR_x86_64-linux-android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" \
|
||||
env "CC_x86_64-linux-android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android30-clang" \
|
||||
just check maplibre-android x86_64-linux-android
|
||||
just nightly-check maplibre-android x86_64-linux-android ""
|
||||
- name: Check aarch64
|
||||
shell: bash
|
||||
run: |
|
||||
env "AR_aarch64-linux-android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-ar" \
|
||||
env "CC_aarch64-linux-android=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang" \
|
||||
just check maplibre-android aarch64-linux-android
|
||||
just nightly-check maplibre-android aarch64-linux-android ""
|
||||
# FIXME: Requires cross-compilation
|
||||
#- name: Test
|
||||
# shell: bash
|
||||
|
||||
4
.github/workflows/library-apple.yml
vendored
4
.github/workflows/library-apple.yml
vendored
@ -15,7 +15,9 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: |
|
||||
just stable-toolchain
|
||||
just stable-targets x86_64-apple-darwin aarch64-apple-darwin x86_64-apple-ios aarch64-apple-ios aarch64-apple-ios-sim
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build
|
||||
shell: bash
|
||||
|
||||
81
.github/workflows/library-web.yml
vendored
81
.github/workflows/library-web.yml
vendored
@ -11,70 +11,58 @@ on:
|
||||
deploy:
|
||||
required: true
|
||||
type: boolean
|
||||
name:
|
||||
required: true
|
||||
type: string
|
||||
webgl:
|
||||
required: true
|
||||
type: boolean
|
||||
multithreaded:
|
||||
required: true
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
library-webgl:
|
||||
name: Build WebGL
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install nightly toolchain
|
||||
shell: bash
|
||||
run: just nightly-toolchain
|
||||
run: |
|
||||
just nightly-toolchain
|
||||
just nightly-targets wasm32-unknown-unknown
|
||||
- name: Install rust sources (build-std)
|
||||
if: inputs.multithreaded
|
||||
shell: bash
|
||||
run: |
|
||||
just nightly-install-src
|
||||
- name: Install wasm-bindgen
|
||||
shell: bash
|
||||
run: |
|
||||
# Install wasm-bindgen with test runner
|
||||
cargo install wasm-bindgen-cli # We want the latest version, as Cargo uses the latest version of wasm-bindgen
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build lib
|
||||
shell: bash
|
||||
run: just web-lib build-webgl
|
||||
run: just web-lib build --release ${{ inputs.webgl && '--webgl' || '' }} ${{ inputs.multithreaded && '--multithreaded' || '' }}
|
||||
- name: Build demo
|
||||
shell: bash
|
||||
run: just web-demo build
|
||||
- name: Check
|
||||
shell: bash
|
||||
run: just web-check "web-webgl"
|
||||
run: just nightly-check web wasm32-unknown-unknown ${{ inputs.webgl && 'web-webgl' || '""' }}
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
# Install test runner
|
||||
cargo install wasm-bindgen-cli # We want the latest version, as Cargo uses the latest version of wasm-bindgen
|
||||
just web-test "web-webgl"
|
||||
run: just web-test "web-webgl"
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: webgl-demo
|
||||
path: web/demo/dist/
|
||||
|
||||
library-webgpu:
|
||||
name: Build WebGPU
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install nightly toolchain
|
||||
shell: bash
|
||||
run: just nightly-toolchain
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Build lib
|
||||
shell: bash
|
||||
run: just web-lib build
|
||||
- name: Build demo
|
||||
shell: bash
|
||||
run: just web-demo build
|
||||
- name: Check
|
||||
shell: bash
|
||||
run: just web-check ""
|
||||
- name: Test
|
||||
shell: bash
|
||||
run: |
|
||||
# Install test runner
|
||||
cargo install wasm-bindgen-cli # We want the latest version, as Cargo uses the latest version of wasm-bindgen
|
||||
just web-test ""
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: webgpu-demo
|
||||
name: ${{ inputs.name }}
|
||||
path: web/demo/dist/
|
||||
|
||||
deploy:
|
||||
needs: [library-webgl, library-webgpu]
|
||||
needs: [build]
|
||||
if: inputs.deploy
|
||||
name: Deploy
|
||||
runs-on: ubuntu-20.04
|
||||
@ -82,13 +70,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: webgl-demo
|
||||
path: demo/webgl
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: webgpu-demo
|
||||
path: demo/webgpu
|
||||
name: ${{ inputs.name }}
|
||||
path: demo
|
||||
- name: Set HTTP Headers for Cloudflare
|
||||
if: inputs.multithreaded
|
||||
shell: bash
|
||||
run: |
|
||||
echo "/*
|
||||
@ -97,7 +82,7 @@ jobs:
|
||||
- name: Deploy
|
||||
uses: ./.github/actions/cloudflare-deploy
|
||||
with:
|
||||
project: maplibre-rs-demos
|
||||
project: ${{ inputs.name }}
|
||||
source: demo
|
||||
CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }}
|
||||
CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }}
|
||||
|
||||
21
.github/workflows/on_main_push.yml
vendored
21
.github/workflows/on_main_push.yml
vendored
@ -24,9 +24,28 @@ jobs:
|
||||
secrets: inherit
|
||||
library-android:
|
||||
uses: ./.github/workflows/library-android.yml
|
||||
library-web:
|
||||
library-web-webgl:
|
||||
uses: ./.github/workflows/library-web.yml
|
||||
with:
|
||||
name: maplibre-rs-demo-webgl
|
||||
webgl: true
|
||||
multithreaded: false
|
||||
deploy: true
|
||||
secrets: inherit
|
||||
library-web-webgl-multithreaded:
|
||||
uses: ./.github/workflows/library-web.yml
|
||||
with:
|
||||
name: maplibre-rs-demo-webgl-multithreaded
|
||||
webgl: true
|
||||
multithreaded: true
|
||||
deploy: true
|
||||
secrets: inherit
|
||||
library-web-webgpu:
|
||||
uses: ./.github/workflows/library-web.yml
|
||||
with:
|
||||
name: maplibre-rs-demo-webgpu
|
||||
webgl: false
|
||||
multithreaded: false
|
||||
deploy: true
|
||||
secrets: inherit
|
||||
library-apple:
|
||||
|
||||
19
.github/workflows/on_pull_request.yml
vendored
19
.github/workflows/on_pull_request.yml
vendored
@ -19,9 +19,26 @@ jobs:
|
||||
deploy: false
|
||||
library-android:
|
||||
uses: ./.github/workflows/library-android.yml
|
||||
library-web:
|
||||
library-web-webgl:
|
||||
uses: ./.github/workflows/library-web.yml
|
||||
with:
|
||||
name: maplibre-rs-demo-webgl
|
||||
webgl: true
|
||||
multithreaded: false
|
||||
deploy: false
|
||||
library-web-webgl-multithreaded:
|
||||
uses: ./.github/workflows/library-web.yml
|
||||
with:
|
||||
name: maplibre-rs-demo-webgl-multithreaded
|
||||
webgl: true
|
||||
multithreaded: true
|
||||
deploy: false
|
||||
library-web-webgpu:
|
||||
uses: ./.github/workflows/library-web.yml
|
||||
with:
|
||||
name: maplibre-rs-demo-webgpu
|
||||
webgl: false
|
||||
multithreaded: false
|
||||
deploy: false
|
||||
library-apple:
|
||||
uses: ./.github/workflows/library-apple.yml
|
||||
|
||||
4
.github/workflows/run-benchmarks.yml
vendored
4
.github/workflows/run-benchmarks.yml
vendored
@ -12,7 +12,9 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: |
|
||||
just stable-toolchain
|
||||
just stable-targets x86_64-unknown-linux-gnu
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install GPU Drivers
|
||||
uses: ./.github/actions/install-driver
|
||||
|
||||
2
.github/workflows/run-checks.yml
vendored
2
.github/workflows/run-checks.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: just stable-toolchain
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Format
|
||||
shell: bash
|
||||
|
||||
4
.github/workflows/run-tests.yml
vendored
4
.github/workflows/run-tests.yml
vendored
@ -12,7 +12,9 @@ jobs:
|
||||
- uses: extractions/setup-just@v1
|
||||
- name: Install toolchain
|
||||
shell: bash
|
||||
run: just default-toolchain
|
||||
run: |
|
||||
just stable-toolchain
|
||||
just stable-targets x86_64-unknown-linux-gnu
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install GPU Drivers
|
||||
uses: ./.github/actions/install-driver
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Build WASM" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="build -p web --features web-webgl --lib --release --target wasm32-unknown-unknown -Z build-std=std,panic_abort" />
|
||||
<configuration default="false" name="Build WASM (single-threaded)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="build -p web --features web-webgl --lib --profile wasm-dev --target wasm32-unknown-unknown" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
8
.idea/runConfigurations/Run_demo__debug_.xml
generated
8
.idea/runConfigurations/Run_demo__debug_.xml
generated
@ -1,6 +1,6 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run demo (debug " type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run -p maplibre-demo" />
|
||||
<configuration default="false" name="Run demo (debug)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run -p maplibre-demo -- headed" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
@ -9,7 +9,9 @@
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<envs>
|
||||
<env name="RUST_LOG" value="debug" />
|
||||
</envs>
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run headed demo (debug+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<configuration default="false" name="Run demo (debug+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run -p maplibre-demo --features trace -- headed" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
@ -1,5 +1,5 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run headed demo (release+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<configuration default="false" name="Run demo (release+enable-tracing)" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run -p maplibre-demo --release --features trace -- headed" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
19
.idea/runConfigurations/Run_headless_demo__debug_enable_tracing__.xml
generated
Normal file
19
.idea/runConfigurations/Run_headless_demo__debug_enable_tracing__.xml
generated
Normal file
@ -0,0 +1,19 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run headless demo (debug+enable-tracing) " type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run -p maplibre-demo --features trace -- headless" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@ -20,7 +20,6 @@ lto = true
|
||||
codegen-units = 1
|
||||
opt-level = 's'
|
||||
panic = "abort"
|
||||
|
||||
strip = "debuginfo"
|
||||
|
||||
[profile.bench]
|
||||
|
||||
@ -3,13 +3,11 @@ use std::ffi::CString;
|
||||
use jni::{objects::JClass, JNIEnv};
|
||||
use log::Level;
|
||||
use maplibre::{
|
||||
platform::{
|
||||
http_client::ReqwestHttpClient, run_multithreaded, schedule_method::TokioScheduleMethod,
|
||||
},
|
||||
platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler},
|
||||
render::settings::{Backends, WgpuSettings},
|
||||
MapBuilder,
|
||||
};
|
||||
use maplibre_winit::winit::WinitMapWindowConfig;
|
||||
use maplibre_winit::winit::{run_headed_map, WinitMapWindowConfig};
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
compile_error!("android works only on android.");
|
||||
@ -18,20 +16,8 @@ compile_error!("android works only on android.");
|
||||
pub fn android_main() {
|
||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||
|
||||
run_multithreaded(async {
|
||||
MapBuilder::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre android".to_string()))
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_schedule_method(TokioScheduleMethod::new())
|
||||
.with_wgpu_settings(WgpuSettings {
|
||||
backends: Some(Backends::VULKAN),
|
||||
..WgpuSettings::default()
|
||||
})
|
||||
.build()
|
||||
.initialize()
|
||||
.await
|
||||
.run()
|
||||
})
|
||||
// TODO: Maybe requires: Some(Backends::VULKAN)
|
||||
run_headed_map(None);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
use maplibre::{
|
||||
platform::{
|
||||
http_client::ReqwestHttpClient, run_multithreaded, schedule_method::TokioScheduleMethod,
|
||||
},
|
||||
io::apc::SchedulerAsyncProcedureCall,
|
||||
platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler},
|
||||
MapBuilder,
|
||||
};
|
||||
use maplibre_winit::winit::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
|
||||
use maplibre_winit::winit::{
|
||||
run_headed_map, WinitEnvironment, WinitEventLoop, WinitMapWindow, WinitMapWindowConfig,
|
||||
WinitWindow,
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||
compile_error!("apple works only on macOS and iOS.");
|
||||
@ -13,14 +15,5 @@ compile_error!("apple works only on macOS and iOS.");
|
||||
pub fn maplibre_apple_main() {
|
||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||
|
||||
run_multithreaded(async {
|
||||
MapBuilder::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre apple".to_string()))
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_schedule_method(TokioScheduleMethod::new())
|
||||
.build()
|
||||
.initialize()
|
||||
.await
|
||||
.run()
|
||||
})
|
||||
run_headed_map(None);
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ fn parse_tile(c: &mut Criterion) {
|
||||
.unwrap()
|
||||
.into_boxed_slice();
|
||||
ParseTile::default().process(
|
||||
(request, 0, data),
|
||||
(request, data),
|
||||
&mut PipelineContext::new(DummyPipelineProcessor),
|
||||
);
|
||||
})
|
||||
@ -58,7 +58,7 @@ fn tessellate_tile(c: &mut Criterion) {
|
||||
.unwrap()
|
||||
.into_boxed_slice();
|
||||
let parsed = ParseTile::default().process(
|
||||
(request, 0, data),
|
||||
(request, data),
|
||||
&mut PipelineContext::new(DummyPipelineProcessor),
|
||||
);
|
||||
|
||||
|
||||
@ -4,16 +4,15 @@ use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use maplibre::{
|
||||
coords::{WorldTileCoords, ZoomLevel},
|
||||
error::Error,
|
||||
headless::{utils::HeadlessPipelineProcessor, HeadlessMapWindowConfig},
|
||||
headless::{utils::HeadlessPipelineProcessor, HeadlessEnvironment, HeadlessMapWindowConfig},
|
||||
io::{
|
||||
apc::SchedulerAsyncProcedureCall,
|
||||
pipeline::{PipelineContext, Processable},
|
||||
source_client::HttpSourceClient,
|
||||
tile_pipelines::build_vector_tile_pipeline,
|
||||
TileRequest,
|
||||
},
|
||||
platform::{
|
||||
http_client::ReqwestHttpClient, run_multithreaded, schedule_method::TokioScheduleMethod,
|
||||
},
|
||||
platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler},
|
||||
render::settings::{RendererSettings, TextureFormat},
|
||||
window::WindowSize,
|
||||
MapBuilder,
|
||||
@ -22,19 +21,27 @@ use maplibre::{
|
||||
fn headless_render(c: &mut Criterion) {
|
||||
c.bench_function("headless_render", |b| {
|
||||
let mut map = run_multithreaded(async {
|
||||
let mut map = MapBuilder::new()
|
||||
.with_map_window_config(HeadlessMapWindowConfig {
|
||||
size: WindowSize::new(1000, 1000).unwrap(),
|
||||
})
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_schedule_method(TokioScheduleMethod::new())
|
||||
.with_renderer_settings(RendererSettings {
|
||||
texture_format: TextureFormat::Rgba8UnormSrgb,
|
||||
..RendererSettings::default()
|
||||
})
|
||||
.build()
|
||||
.initialize_headless()
|
||||
.await;
|
||||
let client = ReqwestHttpClient::new(None);
|
||||
|
||||
let mut map = MapBuilder::<
|
||||
HeadlessEnvironment<_, _, _, SchedulerAsyncProcedureCall<_, _>>,
|
||||
>::new()
|
||||
.with_map_window_config(HeadlessMapWindowConfig {
|
||||
size: WindowSize::new(1000, 1000).unwrap(),
|
||||
})
|
||||
.with_http_client(client.clone())
|
||||
.with_apc(SchedulerAsyncProcedureCall::new(
|
||||
client,
|
||||
TokioScheduler::new(),
|
||||
))
|
||||
.with_scheduler(TokioScheduler::new())
|
||||
.with_renderer_settings(RendererSettings {
|
||||
texture_format: TextureFormat::Rgba8UnormSrgb,
|
||||
..RendererSettings::default()
|
||||
})
|
||||
.build()
|
||||
.initialize_headless()
|
||||
.await;
|
||||
|
||||
map.map_schedule
|
||||
.fetch_process(&WorldTileCoords::from((0, 0, ZoomLevel::default())))
|
||||
|
||||
@ -11,7 +11,7 @@ module it can resolve WebAssembly files or WebWorkers dynamically.
|
||||
The following syntax is used to resolve referenced WebWorkers:
|
||||
|
||||
```ts
|
||||
new Worker(new URL("./pool.worker.ts", import.meta.url), {
|
||||
new Worker(new URL("./multithreaded-pool.worker.ts", import.meta.url), {
|
||||
type: 'module'
|
||||
});
|
||||
```
|
||||
@ -107,7 +107,7 @@ See config in `web/lib/build.mjs` for an example usage.
|
||||
### Babel & TypeScript
|
||||
|
||||
Babel and TypeScript both can produce ESM modules, but they **fail with transforming references within the source code**
|
||||
like `new URL("./pool.worker.ts", import.meta.url)`. There exist some Babel plugins, but none of them is stable.
|
||||
like `new URL("./multithreaded-pool.worker.ts", import.meta.url)`. There exist some Babel plugins, but none of them is stable.
|
||||
Therefore, we actually need a proper bundler which supports outputting ESM modules.
|
||||
The only stable solution to this is Parcel. Parcel also has good documentation around the bundling of WebWorkers.
|
||||
|
||||
|
||||
@ -102,18 +102,35 @@ click on run. This will start the MacOS application.
|
||||
|
||||
## Web (WebGL, WebGPU)
|
||||
|
||||
If you have a browser which already supports a recent version of the WebGPU specification then you can start a
|
||||
development server using the following commands.
|
||||
You need to first build the library for WebGL or WebGPU. Optionally, you can also enabled multi-threading support,
|
||||
which requires that the library is used in a secure environment:
|
||||
[isSecureContext](https://developer.mozilla.org/en-US/docs/Web/API/isSecureContext)
|
||||
and [crossOriginIsolated](https://developer.mozilla.org/en-US/docs/Web/API/crossOriginIsolated).
|
||||
The demo runs this such an environment.
|
||||
|
||||
If you have a browser which already supports a recent version of the WebGPU specification then you can build the library
|
||||
with WebGPU:
|
||||
|
||||
```bash
|
||||
cd web
|
||||
npm run start
|
||||
just web-lib build # WebGPU
|
||||
```
|
||||
|
||||
If you want to run maplibre-rs with WebGL which is supported on every major browser, then you have to use the following
|
||||
command.
|
||||
If not, then you must enable WebGL support:
|
||||
|
||||
|
||||
```bash
|
||||
just web-lib build --webgl # WebGL
|
||||
just web-lib build --webgl --multithreaded # WebGL + multithreaded
|
||||
```
|
||||
|
||||
Instead of building it is also possible to watch for changes. The same flags like with `web-lib build` are supported:
|
||||
|
||||
```bash
|
||||
just web-lib watch --webgl
|
||||
```
|
||||
|
||||
After building the library you can run the demo server:
|
||||
|
||||
```bash
|
||||
just web-lib build
|
||||
just web-demo start
|
||||
```
|
||||
|
||||
65
justfile
65
justfile
@ -5,15 +5,38 @@
|
||||
set shell := ["bash", "-c"]
|
||||
|
||||
export NIGHTLY_TOOLCHAIN := "nightly-2022-07-03"
|
||||
export STABLE_TOOLCHAIN := "1.62"
|
||||
|
||||
export CARGO_TERM_COLOR := "always"
|
||||
export RUST_BACKTRACE := "1"
|
||||
|
||||
install-clippy:
|
||||
rustup component add clippy
|
||||
|
||||
install-nightly-clippy:
|
||||
stable-toolchain:
|
||||
rustup toolchain install $STABLE_TOOLCHAIN
|
||||
|
||||
stable-targets *FLAGS:
|
||||
rustup toolchain install $STABLE_TOOLCHAIN --target {{FLAGS}}
|
||||
|
||||
stable-install-clippy:
|
||||
rustup component add clippy --toolchain $STABLE_TOOLCHAIN
|
||||
|
||||
|
||||
nightly-toolchain:
|
||||
rustup toolchain install $NIGHTLY_TOOLCHAIN
|
||||
|
||||
nightly-targets *FLAGS:
|
||||
rustup toolchain install $NIGHTLY_TOOLCHAIN --target {{FLAGS}}
|
||||
|
||||
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:
|
||||
rustup component add clippy --toolchain $NIGHTLY_TOOLCHAIN
|
||||
|
||||
|
||||
fixup:
|
||||
cargo clippy --no-deps -p maplibre --fix
|
||||
cargo clippy --allow-dirty --no-deps -p maplibre-winit --fix
|
||||
@ -27,36 +50,25 @@ fixup:
|
||||
cargo clippy --allow-dirty --no-deps -p maplibre-winit --target x86_64-linux-android --fix
|
||||
cargo clippy --allow-dirty --no-deps -p maplibre-android --target x86_64-linux-android --fix
|
||||
|
||||
check PROJECT ARCH: install-clippy
|
||||
check PROJECT ARCH: stable-install-clippy
|
||||
cargo clippy --no-deps -p {{PROJECT}} --target {{ARCH}}
|
||||
|
||||
nightly-check PROJECT ARCH FEATURES: nightly-toolchain nightly-install-clippy
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cargo clippy --no-deps -p {{PROJECT}} --features "{{FEATURES}}" --target {{ARCH}}
|
||||
|
||||
test PROJECT ARCH:
|
||||
cargo test -p {{PROJECT}} --target {{ARCH}}
|
||||
|
||||
benchmark:
|
||||
cargo bench -p benchmarks
|
||||
|
||||
install-rustfmt: nightly-toolchain
|
||||
rustup component add rustfmt --toolchain $NIGHTLY_TOOLCHAIN
|
||||
|
||||
fmt: install-rustfmt
|
||||
fmt: nightly-install-rustfmt
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cargo fmt
|
||||
|
||||
fmt-check: install-rustfmt
|
||||
fmt-check: nightly-install-rustfmt
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cargo fmt -- --check
|
||||
|
||||
default-toolchain:
|
||||
# Setups the toolchain from rust-toolchain.toml
|
||||
cargo --version > /dev/null
|
||||
|
||||
nightly-toolchain:
|
||||
rustup install $NIGHTLY_TOOLCHAIN
|
||||
rustup component add rust-src --toolchain $NIGHTLY_TOOLCHAIN
|
||||
|
||||
nightly-toolchain-android: nightly-toolchain
|
||||
rustup target add --toolchain $NIGHTLY_TOOLCHAIN x86_64-linux-android
|
||||
rustup target add --toolchain $NIGHTLY_TOOLCHAIN aarch64-linux-android
|
||||
rustup target add --toolchain $NIGHTLY_TOOLCHAIN i686-linux-android
|
||||
|
||||
web-install PROJECT:
|
||||
cd web/{{PROJECT}} && npm install
|
||||
@ -65,19 +77,16 @@ web-install PROJECT:
|
||||
# Example: just web-lib build-webgl
|
||||
# Example: just web-lib watch
|
||||
# Example: just web-lib watch-webgl
|
||||
web-lib TARGET: nightly-toolchain (web-install "lib")
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cd web/lib && npm run {{TARGET}}
|
||||
web-lib TARGET *FLAGS: nightly-toolchain (web-install "lib")
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cd web/lib && npm run {{TARGET}} -- {{FLAGS}}
|
||||
|
||||
# Example: just web-demo start
|
||||
# Example: just web-demo build
|
||||
web-demo TARGET: (web-install "demo")
|
||||
cd web/demo && npm run {{TARGET}}
|
||||
|
||||
web-check FEATURES: nightly-toolchain install-nightly-clippy
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cargo clippy --no-deps -p web --features "{{FEATURES}}" --target wasm32-unknown-unknown -Z build-std=std,panic_abort
|
||||
web-demo TARGET *FLAGS: (web-install "demo")
|
||||
cd web/demo && npm run {{TARGET}} -- {{FLAGS}}
|
||||
|
||||
web-test FEATURES: nightly-toolchain
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cargo test -p web --features "{{FEATURES}}" --target wasm32-unknown-unknown -Z build-std=std,panic_abort
|
||||
export RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN && cargo test -p web --features "{{FEATURES}}" --target wasm32-unknown-unknown
|
||||
|
||||
#profile-bench:
|
||||
# cargo flamegraph --bench render -- --bench
|
||||
|
||||
@ -10,7 +10,7 @@ readme = "../README.md"
|
||||
|
||||
[features]
|
||||
web-webgl = ["maplibre/web-webgl"]
|
||||
trace = ["maplibre/trace", "tracing-subscriber", "tracing-tracy", "tracy-client"]
|
||||
trace = ["maplibre/trace"]
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.9.0"
|
||||
@ -19,10 +19,4 @@ maplibre-winit = { path = "../maplibre-winit", version = "0.0.1" }
|
||||
|
||||
tile-grid = "0.3"
|
||||
|
||||
tracing = "0.1.35"
|
||||
tracing-subscriber = { version = "0.3.14", optional = true }
|
||||
|
||||
tracing-tracy = { version = "0.10", optional = true }
|
||||
tracy-client = { version = "0.14", optional = true }
|
||||
|
||||
clap = { version = "3.2.12", features = ["derive"] }
|
||||
@ -1,16 +0,0 @@
|
||||
use maplibre::{
|
||||
platform::{http_client::ReqwestHttpClient, schedule_method::TokioScheduleMethod},
|
||||
MapBuilder,
|
||||
};
|
||||
use maplibre_winit::winit::WinitMapWindowConfig;
|
||||
|
||||
pub async fn run_headed() {
|
||||
MapBuilder::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_schedule_method(TokioScheduleMethod::new())
|
||||
.build()
|
||||
.initialize()
|
||||
.await
|
||||
.run()
|
||||
}
|
||||
@ -1,29 +1,37 @@
|
||||
use maplibre::{
|
||||
coords::{LatLon, WorldTileCoords},
|
||||
error::Error,
|
||||
headless::HeadlessMapWindowConfig,
|
||||
platform::{http_client::ReqwestHttpClient, schedule_method::TokioScheduleMethod},
|
||||
headless::{HeadlessEnvironment, HeadlessMapWindowConfig},
|
||||
io::apc::SchedulerAsyncProcedureCall,
|
||||
platform::{http_client::ReqwestHttpClient, scheduler::TokioScheduler},
|
||||
render::settings::{RendererSettings, TextureFormat},
|
||||
util::grid::google_mercator,
|
||||
window::WindowSize,
|
||||
MapBuilder,
|
||||
};
|
||||
use maplibre_winit::winit::WinitEnvironment;
|
||||
use tile_grid::{extent_wgs84_to_merc, Extent, GridIterator};
|
||||
|
||||
pub async fn run_headless(tile_size: u32, min: LatLon, max: LatLon) {
|
||||
let mut map = MapBuilder::new()
|
||||
.with_map_window_config(HeadlessMapWindowConfig {
|
||||
size: WindowSize::new(tile_size, tile_size).unwrap(),
|
||||
})
|
||||
.with_http_client(ReqwestHttpClient::new(None))
|
||||
.with_schedule_method(TokioScheduleMethod::new())
|
||||
.with_renderer_settings(RendererSettings {
|
||||
texture_format: TextureFormat::Rgba8UnormSrgb,
|
||||
..RendererSettings::default()
|
||||
})
|
||||
.build()
|
||||
.initialize_headless()
|
||||
.await;
|
||||
let client = ReqwestHttpClient::new(None);
|
||||
let mut map =
|
||||
MapBuilder::<HeadlessEnvironment<_, _, _, SchedulerAsyncProcedureCall<_, _>>>::new()
|
||||
.with_map_window_config(HeadlessMapWindowConfig {
|
||||
size: WindowSize::new(tile_size, tile_size).unwrap(),
|
||||
})
|
||||
.with_http_client(client.clone())
|
||||
.with_apc(SchedulerAsyncProcedureCall::new(
|
||||
client,
|
||||
TokioScheduler::new(),
|
||||
)) // FIXME (wasm-executor): avoid passing client and scheduler here
|
||||
.with_scheduler(TokioScheduler::new())
|
||||
.with_renderer_settings(RendererSettings {
|
||||
texture_format: TextureFormat::Rgba8UnormSrgb,
|
||||
..RendererSettings::default()
|
||||
})
|
||||
.build()
|
||||
.initialize_headless()
|
||||
.await;
|
||||
|
||||
let tile_limits = google_mercator().tile_limits(
|
||||
extent_wgs84_to_merc(&Extent {
|
||||
|
||||
@ -2,21 +2,12 @@ use std::io::ErrorKind;
|
||||
|
||||
use clap::{builder::ValueParser, Parser, Subcommand};
|
||||
use maplibre::{coords::LatLon, platform::run_multithreaded};
|
||||
use maplibre_winit::winit::run_headed_map;
|
||||
|
||||
use crate::{headed::run_headed, headless::run_headless};
|
||||
use crate::headless::run_headless;
|
||||
|
||||
mod headed;
|
||||
mod headless;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
fn enable_tracing() {
|
||||
use tracing_subscriber::{layer::SubscriberExt, Registry};
|
||||
|
||||
let subscriber = Registry::default().with(tracing_tracy::TracyLayer::new());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
#[clap(propagate_version = true)]
|
||||
@ -63,16 +54,14 @@ fn main() {
|
||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
enable_tracing();
|
||||
maplibre::platform::trace::enable_tracing();
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
||||
// You can check for the existence of subcommands, and if found use their
|
||||
// matches just as you would the top level cmd
|
||||
match &cli.command {
|
||||
Commands::Headed {} => {
|
||||
run_multithreaded(async { run_headed().await });
|
||||
}
|
||||
Commands::Headed {} => run_headed_map(None),
|
||||
Commands::Headless {
|
||||
tile_size,
|
||||
min,
|
||||
|
||||
@ -21,5 +21,5 @@ wasm-bindgen-futures = "0.4.31"
|
||||
maplibre = { path = "../maplibre", version = "0.0.2" }
|
||||
winit = { version = "0.27.2", default-features = false }
|
||||
cgmath = "0.18.0"
|
||||
instant = { version = "0.1.12", features = ["wasm-bindgen"] } # FIXME: Untrusted dependency
|
||||
instant = { version = "0.1.12", features = ["wasm-bindgen"] } # TODO: Untrusted dependency
|
||||
log = "0.4.17"
|
||||
|
||||
@ -71,9 +71,9 @@ impl UpdateState for QueryHandler {
|
||||
&inverted_view_proj,
|
||||
false,
|
||||
) {
|
||||
// TODO reenable
|
||||
/*state
|
||||
.scheduler()
|
||||
.schedule_method()
|
||||
.schedule(state.scheduler(), move |thread_local| async move {
|
||||
if let Some(geometries) = thread_local.query_point(
|
||||
&WorldCoords {
|
||||
|
||||
@ -1,7 +1,15 @@
|
||||
use std::{cell::RefCell, marker::PhantomData, ops::Deref, rc::Rc};
|
||||
|
||||
use instant::Instant;
|
||||
use maplibre::{
|
||||
environment::Environment,
|
||||
error::Error,
|
||||
io::{scheduler::ScheduleMethod, source_client::HttpClient},
|
||||
io::{
|
||||
apc::{AsyncProcedureCall, Message},
|
||||
scheduler::Scheduler,
|
||||
source_client::HttpClient,
|
||||
transferables::{DefaultTransferables, Transferables},
|
||||
},
|
||||
map_schedule::InteractiveMapSchedule,
|
||||
window::{EventLoop, HeadedMapWindow, MapWindowConfig},
|
||||
};
|
||||
@ -52,28 +60,48 @@ pub struct WinitMapWindow {
|
||||
event_loop: Option<WinitEventLoop>,
|
||||
}
|
||||
|
||||
pub type WinitWindow = winit::window::Window;
|
||||
pub type WinitEventLoop = winit::event_loop::EventLoop<()>;
|
||||
|
||||
impl WinitMapWindow {
|
||||
pub fn take_event_loop(&mut self) -> Option<WinitEventLoop> {
|
||||
self.event_loop.take()
|
||||
}
|
||||
}
|
||||
|
||||
pub type WinitWindow = winit::window::Window;
|
||||
pub type WinitEventLoop = winit::event_loop::EventLoop<()>;
|
||||
|
||||
pub struct WinitEnvironment<
|
||||
S: Scheduler,
|
||||
HC: HttpClient,
|
||||
T: Transferables,
|
||||
APC: AsyncProcedureCall<T, HC>,
|
||||
> {
|
||||
phantom_s: PhantomData<S>,
|
||||
phantom_hc: PhantomData<HC>,
|
||||
phantom_t: PhantomData<T>,
|
||||
phantom_apc: PhantomData<APC>,
|
||||
}
|
||||
|
||||
impl<S: Scheduler, HC: HttpClient, T: Transferables, APC: AsyncProcedureCall<T, HC>> Environment
|
||||
for WinitEnvironment<S, HC, T, APC>
|
||||
{
|
||||
type MapWindowConfig = WinitMapWindowConfig;
|
||||
type AsyncProcedureCall = APC;
|
||||
type Scheduler = S;
|
||||
type HttpClient = HC;
|
||||
type Transferables = T;
|
||||
}
|
||||
|
||||
///Main (platform-specific) main loop which handles:
|
||||
///* Input (Mouse/Keyboard)
|
||||
///* Platform Events like suspend/resume
|
||||
///* Render a new frame
|
||||
impl<MWC, SM, HC> EventLoop<MWC, SM, HC> for WinitMapWindow
|
||||
impl<E: Environment> EventLoop<E> for WinitMapWindow
|
||||
where
|
||||
MWC: MapWindowConfig<MapWindow = WinitMapWindow>,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
E::MapWindowConfig: MapWindowConfig<MapWindow = WinitMapWindow>,
|
||||
{
|
||||
fn run(
|
||||
mut self,
|
||||
mut map_schedule: InteractiveMapSchedule<MWC, SM, HC>,
|
||||
map_schedule: Rc<RefCell<InteractiveMapSchedule<E>>>,
|
||||
max_frames: Option<u64>,
|
||||
) {
|
||||
let mut last_render_time = Instant::now();
|
||||
@ -84,6 +112,8 @@ where
|
||||
self.take_event_loop()
|
||||
.unwrap()
|
||||
.run(move |event, _, control_flow| {
|
||||
let mut map_schedule = map_schedule.deref().borrow_mut();
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if !map_schedule.is_initialized() && event == Event::Resumed {
|
||||
use tokio::{runtime::Handle, task};
|
||||
|
||||
@ -3,10 +3,16 @@
|
||||
//! * Platform Events like suspend/resume
|
||||
//! * Render a new frame
|
||||
|
||||
use maplibre::window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize};
|
||||
use maplibre::{
|
||||
io::apc::SchedulerAsyncProcedureCall,
|
||||
platform::{http_client::ReqwestHttpClient, run_multithreaded, scheduler::TokioScheduler},
|
||||
window::{HeadedMapWindow, MapWindow, MapWindowConfig, WindowSize},
|
||||
MapBuilder,
|
||||
};
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
use super::{WinitEventLoop, WinitMapWindow, WinitMapWindowConfig, WinitWindow};
|
||||
use crate::winit::WinitEnvironment;
|
||||
|
||||
impl MapWindow for WinitMapWindow {
|
||||
fn size(&self) -> WindowSize {
|
||||
@ -47,3 +53,21 @@ impl MapWindowConfig for WinitMapWindowConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_headed_map(cache_path: Option<String>) {
|
||||
run_multithreaded(async {
|
||||
let client = ReqwestHttpClient::new(cache_path);
|
||||
MapBuilder::<WinitEnvironment<_, _, _, SchedulerAsyncProcedureCall<_, _>>>::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(client.clone())
|
||||
.with_apc(SchedulerAsyncProcedureCall::new(
|
||||
client,
|
||||
TokioScheduler::new(),
|
||||
))
|
||||
.with_scheduler(TokioScheduler::new())
|
||||
.build()
|
||||
.initialize()
|
||||
.await
|
||||
.run()
|
||||
})
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ headless = ["png"]
|
||||
|
||||
[target.'cfg(any(target_os = "macos", target_os = "ios", target_os = "linux", target_os = "android", target_os = "windows"))'.dependencies]
|
||||
tokio = { version = "1.20.1", features = ["macros", "rt", "rt-multi-thread", "sync", "time"] }
|
||||
tokio-util = { version = "0.7.1", features = ["rt"] }
|
||||
env_logger = "0.9.0"
|
||||
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "gzip"] }
|
||||
reqwest-middleware-cache = "0.1.1" # FIXME: Untrusted dependency
|
||||
@ -32,7 +33,7 @@ reqwest = { version = "0.11.11", default-features = false, features = ["rustls-t
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.57"
|
||||
instant = { version = "0.1.12", features = ["wasm-bindgen"] } # FIXME: Untrusted dependency
|
||||
instant = { version = "0.1.12", features = ["wasm-bindgen"] } # TODO: Untrusted dependency
|
||||
|
||||
# Tracing
|
||||
tracing = "0.1.36"
|
||||
|
||||
@ -6,7 +6,9 @@ use std::{
|
||||
fmt::{Display, Formatter},
|
||||
};
|
||||
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use cgmath::{num_traits::Pow, AbsDiffEq, Matrix4, Point3, Vector3};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
style::source::TileAddressingScheme,
|
||||
@ -67,7 +69,23 @@ impl fmt::Debug for Quadkey {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Ord, PartialOrd, Eq, PartialEq, Hash, Copy, Clone, Debug, Default)]
|
||||
// FIXME: does Pod and Zeroable make sense?
|
||||
#[derive(
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Copy,
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Pod,
|
||||
Zeroable,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct ZoomLevel(u8);
|
||||
|
||||
impl ZoomLevel {
|
||||
@ -83,7 +101,7 @@ impl std::ops::Add<u8> for ZoomLevel {
|
||||
type Output = ZoomLevel;
|
||||
|
||||
fn add(self, rhs: u8) -> Self::Output {
|
||||
let zoom_level = self.0.checked_add(rhs).unwrap();
|
||||
let zoom_level = self.0.checked_add(rhs).expect("zoom level overflowed");
|
||||
ZoomLevel(zoom_level)
|
||||
}
|
||||
}
|
||||
@ -92,7 +110,7 @@ impl std::ops::Sub<u8> for ZoomLevel {
|
||||
type Output = ZoomLevel;
|
||||
|
||||
fn sub(self, rhs: u8) -> Self::Output {
|
||||
let zoom_level = self.0.checked_sub(rhs).unwrap();
|
||||
let zoom_level = self.0.checked_sub(rhs).expect("zoom level underflowed");
|
||||
ZoomLevel(zoom_level)
|
||||
}
|
||||
}
|
||||
@ -289,7 +307,22 @@ impl From<(u32, u32, ZoomLevel)> for TileCoords {
|
||||
/// # Coordinate System Origin
|
||||
///
|
||||
/// The origin of the coordinate system is in the upper-left corner.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
// FIXME: does Zeroable make sense?
|
||||
#[derive(
|
||||
Clone,
|
||||
Copy,
|
||||
Debug,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Default,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Zeroable,
|
||||
)]
|
||||
#[repr(C)]
|
||||
pub struct WorldTileCoords {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
|
||||
19
maplibre/src/environment.rs
Normal file
19
maplibre/src/environment.rs
Normal file
@ -0,0 +1,19 @@
|
||||
use crate::{
|
||||
io::{
|
||||
apc::AsyncProcedureCall,
|
||||
transferables::{
|
||||
DefaultTessellatedLayer, DefaultTileTessellated, DefaultUnavailableLayer, Transferables,
|
||||
},
|
||||
},
|
||||
HttpClient, MapWindowConfig, Scheduler,
|
||||
};
|
||||
|
||||
pub trait Environment: 'static {
|
||||
type MapWindowConfig: MapWindowConfig;
|
||||
|
||||
type AsyncProcedureCall: AsyncProcedureCall<Self::Transferables, Self::HttpClient>;
|
||||
type Scheduler: Scheduler;
|
||||
type HttpClient: HttpClient;
|
||||
|
||||
type Transferables: Transferables;
|
||||
}
|
||||
@ -4,6 +4,7 @@ use std::{
|
||||
future::Future,
|
||||
io::Write,
|
||||
iter,
|
||||
marker::PhantomData,
|
||||
ops::{Deref, Range},
|
||||
sync::Arc,
|
||||
};
|
||||
@ -17,11 +18,12 @@ use crate::{
|
||||
error::Error,
|
||||
headless::utils::HeadlessPipelineProcessor,
|
||||
io::{
|
||||
apc::{AsyncProcedureCall, SchedulerAsyncProcedureCall},
|
||||
pipeline::{PipelineContext, Processable},
|
||||
source_client::HttpSourceClient,
|
||||
tile_pipelines::build_vector_tile_pipeline,
|
||||
tile_repository::{StoredLayer, TileRepository},
|
||||
tile_request_state::TileRequestState,
|
||||
transferables::{DefaultTransferables, Transferables},
|
||||
TileRequest,
|
||||
},
|
||||
render::{
|
||||
@ -35,7 +37,7 @@ use crate::{
|
||||
RenderState,
|
||||
},
|
||||
schedule::{Schedule, Stage},
|
||||
HttpClient, MapWindow, MapWindowConfig, Renderer, ScheduleMethod, Scheduler, Style, WindowSize,
|
||||
Environment, HttpClient, MapWindow, MapWindowConfig, Renderer, Scheduler, Style, WindowSize,
|
||||
};
|
||||
|
||||
pub struct HeadlessMapWindowConfig {
|
||||
@ -60,56 +62,57 @@ impl MapWindow for HeadlessMapWindow {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HeadlessMap<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
pub struct HeadlessEnvironment<
|
||||
S: Scheduler,
|
||||
HC: HttpClient,
|
||||
{
|
||||
pub map_schedule: HeadlessMapSchedule<MWC, SM, HC>,
|
||||
pub window: MWC::MapWindow,
|
||||
T: Transferables,
|
||||
APC: AsyncProcedureCall<T, HC>,
|
||||
> {
|
||||
phantom_s: PhantomData<S>,
|
||||
phantom_hc: PhantomData<HC>,
|
||||
phantom_t: PhantomData<T>,
|
||||
phantom_apc: PhantomData<APC>,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> HeadlessMap<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
impl<S: Scheduler, HC: HttpClient, T: Transferables, APC: AsyncProcedureCall<T, HC>> Environment
|
||||
for HeadlessEnvironment<S, HC, T, APC>
|
||||
{
|
||||
pub fn map_schedule_mut(&mut self) -> &mut HeadlessMapSchedule<MWC, SM, HC> {
|
||||
type MapWindowConfig = HeadlessMapWindowConfig;
|
||||
type AsyncProcedureCall = APC;
|
||||
type Scheduler = S;
|
||||
type HttpClient = HC;
|
||||
type Transferables = T;
|
||||
}
|
||||
|
||||
pub struct HeadlessMap<E: Environment> {
|
||||
pub map_schedule: HeadlessMapSchedule<E>,
|
||||
pub window: <E::MapWindowConfig as MapWindowConfig>::MapWindow,
|
||||
}
|
||||
|
||||
impl<E: Environment> HeadlessMap<E> {
|
||||
pub fn map_schedule_mut(&mut self) -> &mut HeadlessMapSchedule<E> {
|
||||
&mut self.map_schedule
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the state of the map, dispatches tile fetching and caching, tessellation and drawing.
|
||||
pub struct HeadlessMapSchedule<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
map_window_config: MWC,
|
||||
pub struct HeadlessMapSchedule<E: Environment> {
|
||||
map_window_config: E::MapWindowConfig,
|
||||
|
||||
pub map_context: MapContext,
|
||||
|
||||
schedule: Schedule,
|
||||
scheduler: Scheduler<SM>,
|
||||
http_client: HC,
|
||||
tile_request_state: TileRequestState,
|
||||
scheduler: E::Scheduler,
|
||||
http_client: E::HttpClient,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> HeadlessMapSchedule<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
impl<E: Environment> HeadlessMapSchedule<E> {
|
||||
pub fn new(
|
||||
map_window_config: MWC,
|
||||
map_window_config: E::MapWindowConfig,
|
||||
window_size: WindowSize,
|
||||
renderer: Renderer,
|
||||
scheduler: Scheduler<SM>,
|
||||
http_client: HC,
|
||||
scheduler: E::Scheduler,
|
||||
http_client: E::HttpClient,
|
||||
style: Style,
|
||||
) -> Self {
|
||||
let view_state = ViewState::new(
|
||||
@ -122,12 +125,12 @@ where
|
||||
let tile_repository = TileRepository::new();
|
||||
let mut schedule = Schedule::default();
|
||||
|
||||
let mut graph = create_default_render_graph().unwrap();
|
||||
let draw_graph = graph.get_sub_graph_mut(draw_graph::NAME).unwrap();
|
||||
let mut graph = create_default_render_graph().unwrap(); // TODO: remove unwrap
|
||||
let draw_graph = graph.get_sub_graph_mut(draw_graph::NAME).unwrap(); // TODO: remove unwrap
|
||||
draw_graph.add_node(draw_graph::node::COPY, CopySurfaceBufferNode::default());
|
||||
draw_graph
|
||||
.add_node_edge(draw_graph::node::MAIN_PASS, draw_graph::node::COPY)
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: remove unwrap
|
||||
|
||||
register_default_render_stages(graph, &mut schedule);
|
||||
|
||||
@ -147,7 +150,6 @@ where
|
||||
schedule,
|
||||
scheduler,
|
||||
http_client,
|
||||
tile_request_state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,10 +162,10 @@ where
|
||||
pub fn schedule(&self) -> &Schedule {
|
||||
&self.schedule
|
||||
}
|
||||
pub fn scheduler(&self) -> &Scheduler<SM> {
|
||||
pub fn scheduler(&self) -> &E::Scheduler {
|
||||
&self.scheduler
|
||||
}
|
||||
pub fn http_client(&self) -> &HC {
|
||||
pub fn http_client(&self) -> &E::HttpClient {
|
||||
&self.http_client
|
||||
}
|
||||
|
||||
@ -176,13 +178,13 @@ where
|
||||
.filter_map(|layer| layer.source_layer.clone())
|
||||
.collect();
|
||||
|
||||
let http_source_client: HttpSourceClient<HC> =
|
||||
let http_source_client: HttpSourceClient<E::HttpClient> =
|
||||
HttpSourceClient::new(self.http_client.clone());
|
||||
|
||||
let data = http_source_client
|
||||
.fetch(&coords)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap() // TODO: remove unwrap
|
||||
.into_boxed_slice();
|
||||
|
||||
let mut pipeline_context = PipelineContext::new(HeadlessPipelineProcessor::default());
|
||||
@ -193,15 +195,11 @@ where
|
||||
layers: source_layers,
|
||||
};
|
||||
|
||||
let request_id = self
|
||||
.tile_request_state
|
||||
.start_tile_request(request.clone())?;
|
||||
pipeline.process((request, request_id, data), &mut pipeline_context);
|
||||
self.tile_request_state.finish_tile_request(request_id);
|
||||
pipeline.process((request, data), &mut pipeline_context);
|
||||
|
||||
let mut processor = pipeline_context
|
||||
.take_processor::<HeadlessPipelineProcessor>()
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: remove unwrap
|
||||
|
||||
if let Eventually::Initialized(pool) = self.map_context.renderer.state.buffer_pool_mut() {
|
||||
pool.clear();
|
||||
@ -210,6 +208,9 @@ where
|
||||
self.map_context.tile_repository.clear();
|
||||
|
||||
while let Some(layer) = processor.layers.pop() {
|
||||
self.map_context
|
||||
.tile_repository
|
||||
.create_tile(&layer.get_coords());
|
||||
self.map_context
|
||||
.tile_repository
|
||||
.put_tessellated_layer(layer);
|
||||
@ -261,7 +262,7 @@ impl Node for CopySurfaceBufferNode {
|
||||
std::num::NonZeroU32::new(
|
||||
buffered_texture.buffer_dimensions.padded_bytes_per_row as u32,
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(), // TODO: remove unwrap
|
||||
),
|
||||
rows_per_image: None,
|
||||
},
|
||||
@ -340,9 +341,9 @@ pub mod utils {
|
||||
) {
|
||||
self.layers.push(StoredLayer::TessellatedLayer {
|
||||
coords: *coords,
|
||||
layer_name: layer_data.name,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
121
maplibre/src/io/apc.rs
Normal file
121
maplibre/src/io/apc.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{
|
||||
mpsc,
|
||||
mpsc::{Receiver, Sender},
|
||||
},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
coords::WorldTileCoords,
|
||||
io::{
|
||||
source_client::{HttpSourceClient, SourceClient},
|
||||
transferables::{DefaultTransferables, Transferables},
|
||||
TileRequest,
|
||||
},
|
||||
Environment, HttpClient, Scheduler,
|
||||
};
|
||||
|
||||
/// The result of the tessellation of a tile.
|
||||
/// `TessellatedLayer` contains the result of the tessellation for a specific layer, otherwise
|
||||
/// `UnavailableLayer` if the layer doesn't exist.
|
||||
#[derive(Clone)]
|
||||
pub enum Message<T: Transferables> {
|
||||
TileTessellated(T::TileTessellated),
|
||||
UnavailableLayer(T::UnavailableLayer),
|
||||
TessellatedLayer(T::TessellatedLayer),
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub enum Input {
|
||||
TileRequest(TileRequest),
|
||||
}
|
||||
|
||||
pub trait Context<T: Transferables, HC: HttpClient>: Send + 'static {
|
||||
fn send(&self, data: Message<T>);
|
||||
|
||||
fn source_client(&self) -> &SourceClient<HC>;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-thread-safe-futures"))]
|
||||
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>;
|
||||
#[cfg(feature = "no-thread-safe-futures")]
|
||||
pub type AsyncProcedureFuture = Pin<Box<(dyn Future<Output = ()> + 'static)>>;
|
||||
|
||||
pub type AsyncProcedure<C> = fn(input: Input, context: C) -> AsyncProcedureFuture;
|
||||
|
||||
pub trait AsyncProcedureCall<T: Transferables, HC: HttpClient>: 'static {
|
||||
type Context: Context<T, HC> + Send;
|
||||
|
||||
fn receive(&mut self) -> Option<Message<T>>;
|
||||
|
||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SchedulerContext<T: Transferables, HC: HttpClient> {
|
||||
sender: Sender<Message<T>>,
|
||||
source_client: SourceClient<HC>,
|
||||
}
|
||||
|
||||
impl<T: Transferables, HC: HttpClient> Context<T, HC> for SchedulerContext<T, HC> {
|
||||
fn send(&self, data: Message<T>) {
|
||||
self.sender.send(data).unwrap(); // FIXME (wasm-executor): Remove unwrap
|
||||
}
|
||||
|
||||
fn source_client(&self) -> &SourceClient<HC> {
|
||||
&self.source_client
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SchedulerAsyncProcedureCall<HC: HttpClient, S: Scheduler> {
|
||||
channel: (
|
||||
Sender<Message<DefaultTransferables>>,
|
||||
Receiver<Message<DefaultTransferables>>,
|
||||
),
|
||||
http_client: HC,
|
||||
scheduler: S,
|
||||
}
|
||||
|
||||
impl<HC: HttpClient, S: Scheduler> SchedulerAsyncProcedureCall<HC, S> {
|
||||
pub fn new(http_client: HC, scheduler: S) -> Self {
|
||||
Self {
|
||||
channel: mpsc::channel(),
|
||||
http_client,
|
||||
scheduler,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<HC: HttpClient, S: Scheduler> AsyncProcedureCall<DefaultTransferables, HC>
|
||||
for SchedulerAsyncProcedureCall<HC, S>
|
||||
{
|
||||
type Context = SchedulerContext<DefaultTransferables, HC>;
|
||||
|
||||
fn receive(&mut self) -> Option<Message<DefaultTransferables>> {
|
||||
let transferred = self.channel.1.try_recv().ok()?;
|
||||
Some(transferred)
|
||||
}
|
||||
|
||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||
let sender = self.channel.0.clone();
|
||||
let client = self.http_client.clone(); // FIXME (wasm-executor): do not clone each time
|
||||
|
||||
self.scheduler
|
||||
.schedule(move || async move {
|
||||
(procedure)(
|
||||
input,
|
||||
SchedulerContext {
|
||||
sender,
|
||||
source_client: SourceClient::Http(HttpSourceClient::new(client)),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,11 @@
|
||||
|
||||
use std::{collections::HashSet, fmt};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::coords::WorldTileCoords;
|
||||
|
||||
pub mod apc;
|
||||
pub mod geometry_index;
|
||||
pub mod pipeline;
|
||||
pub mod scheduler;
|
||||
@ -12,12 +15,12 @@ pub mod source_client;
|
||||
pub mod static_tile_fetcher;
|
||||
pub mod tile_pipelines;
|
||||
pub mod tile_repository;
|
||||
pub mod tile_request_state;
|
||||
pub mod transferables;
|
||||
|
||||
pub use geozero::mvt::tile::Layer as RawLayer;
|
||||
|
||||
/// A request for a tile at the given coordinates and in the given layers.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct TileRequest {
|
||||
pub coords: WorldTileCoords,
|
||||
pub layers: HashSet<String>,
|
||||
@ -28,6 +31,3 @@ impl fmt::Debug for TileRequest {
|
||||
write!(f, "TileRequest({}, {:?})", &self.coords, &self.layers)
|
||||
}
|
||||
}
|
||||
|
||||
/// The ID format for a tile request.
|
||||
pub type TileRequestID = u32;
|
||||
|
||||
@ -5,14 +5,14 @@ use geozero::mvt::tile;
|
||||
|
||||
use crate::{
|
||||
coords::WorldTileCoords,
|
||||
io::{geometry_index::IndexedGeometry, TileRequestID},
|
||||
io::geometry_index::IndexedGeometry,
|
||||
render::ShaderVertex,
|
||||
tessellation::{IndexDataType, OverAlignedVertexBuffer},
|
||||
};
|
||||
|
||||
/// Processes events which happen during the pipeline execution
|
||||
pub trait PipelineProcessor: Downcast {
|
||||
fn tile_finished(&mut self, _request_id: TileRequestID, _coords: &WorldTileCoords) {}
|
||||
fn tile_finished(&mut self, _coords: &WorldTileCoords) {}
|
||||
fn layer_unavailable(&mut self, _coords: &WorldTileCoords, _layer_name: &str) {}
|
||||
fn layer_tesselation_finished(
|
||||
&mut self,
|
||||
@ -30,8 +30,6 @@ pub trait PipelineProcessor: Downcast {
|
||||
}
|
||||
}
|
||||
|
||||
impl_downcast!(PipelineProcessor);
|
||||
|
||||
/// Context which is available to each step within a [`DataPipeline`]
|
||||
pub struct PipelineContext {
|
||||
processor: Box<dyn PipelineProcessor>,
|
||||
|
||||
@ -5,28 +5,8 @@ use std::future::Future;
|
||||
use crate::error::Error;
|
||||
|
||||
/// Async/await scheduler.
|
||||
pub struct Scheduler<SM>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
{
|
||||
schedule_method: SM,
|
||||
}
|
||||
|
||||
impl<SM> Scheduler<SM>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
{
|
||||
pub fn new(schedule_method: SM) -> Self {
|
||||
Self { schedule_method }
|
||||
}
|
||||
|
||||
pub fn schedule_method(&self) -> &SM {
|
||||
&self.schedule_method
|
||||
}
|
||||
}
|
||||
|
||||
/// Can schedule a task from a future factory and a shared state.
|
||||
pub trait ScheduleMethod: 'static {
|
||||
pub trait Scheduler: 'static {
|
||||
#[cfg(not(feature = "no-thread-safe-futures"))]
|
||||
fn schedule<T>(
|
||||
&self,
|
||||
@ -43,3 +23,14 @@ pub trait ScheduleMethod: 'static {
|
||||
where
|
||||
T: Future<Output = ()> + 'static;
|
||||
}
|
||||
|
||||
pub struct NopScheduler;
|
||||
|
||||
impl Scheduler for NopScheduler {
|
||||
fn schedule<T>(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + 'static,
|
||||
{
|
||||
Err(Error::Schedule)
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@ impl StaticTileFetcher {
|
||||
.ok_or_else(|| {
|
||||
Error::Network("Failed to load tile from within the binary".to_string())
|
||||
})?;
|
||||
Ok(Vec::from(tile.contents())) // TODO: Unnecessary copy
|
||||
Ok(Vec::from(tile.contents()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ use crate::{
|
||||
io::{
|
||||
geometry_index::IndexProcessor,
|
||||
pipeline::{DataPipeline, PipelineContext, PipelineEnd, Processable},
|
||||
TileRequest, TileRequestID,
|
||||
TileRequest,
|
||||
},
|
||||
tessellation::{zero_tessellator::ZeroTessellator, IndexDataType},
|
||||
};
|
||||
@ -16,17 +16,17 @@ use crate::{
|
||||
pub struct ParseTile;
|
||||
|
||||
impl Processable for ParseTile {
|
||||
type Input = (TileRequest, TileRequestID, Box<[u8]>);
|
||||
type Output = (TileRequest, TileRequestID, geozero::mvt::Tile);
|
||||
type Input = (TileRequest, Box<[u8]>);
|
||||
type Output = (TileRequest, geozero::mvt::Tile);
|
||||
|
||||
// TODO (perf): Maybe force inline
|
||||
fn process(
|
||||
&self,
|
||||
(tile_request, request_id, data): Self::Input,
|
||||
(tile_request, data): Self::Input,
|
||||
_context: &mut PipelineContext,
|
||||
) -> Self::Output {
|
||||
let tile = geozero::mvt::Tile::decode(data.as_ref()).expect("failed to load tile");
|
||||
(tile_request, request_id, tile)
|
||||
(tile_request, tile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,13 +34,13 @@ impl Processable for ParseTile {
|
||||
pub struct IndexLayer;
|
||||
|
||||
impl Processable for IndexLayer {
|
||||
type Input = (TileRequest, TileRequestID, geozero::mvt::Tile);
|
||||
type Output = (TileRequest, TileRequestID, geozero::mvt::Tile);
|
||||
type Input = (TileRequest, geozero::mvt::Tile);
|
||||
type Output = (TileRequest, geozero::mvt::Tile);
|
||||
|
||||
// TODO (perf): Maybe force inline
|
||||
fn process(
|
||||
&self,
|
||||
(tile_request, request_id, tile): Self::Input,
|
||||
(tile_request, tile): Self::Input,
|
||||
context: &mut PipelineContext,
|
||||
) -> Self::Output {
|
||||
let index = IndexProcessor::new();
|
||||
@ -48,7 +48,7 @@ impl Processable for IndexLayer {
|
||||
context
|
||||
.processor_mut()
|
||||
.layer_indexing_finished(&tile_request.coords, index.get_geometries());
|
||||
(tile_request, request_id, tile)
|
||||
(tile_request, tile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,13 +56,13 @@ impl Processable for IndexLayer {
|
||||
pub struct TessellateLayer;
|
||||
|
||||
impl Processable for TessellateLayer {
|
||||
type Input = (TileRequest, TileRequestID, geozero::mvt::Tile);
|
||||
type Output = (TileRequest, TileRequestID, geozero::mvt::Tile);
|
||||
type Input = (TileRequest, geozero::mvt::Tile);
|
||||
type Output = (TileRequest, geozero::mvt::Tile);
|
||||
|
||||
// TODO (perf): Maybe force inline
|
||||
fn process(
|
||||
&self,
|
||||
(tile_request, request_id, mut tile): Self::Input,
|
||||
(tile_request, mut tile): Self::Input,
|
||||
context: &mut PipelineContext,
|
||||
) -> Self::Output {
|
||||
let coords = &tile_request.coords;
|
||||
@ -118,11 +118,9 @@ impl Processable for TessellateLayer {
|
||||
|
||||
tracing::info!("tile tessellated at {} finished", &tile_request.coords);
|
||||
|
||||
context
|
||||
.processor_mut()
|
||||
.tile_finished(request_id, &tile_request.coords);
|
||||
context.processor_mut().tile_finished(&tile_request.coords);
|
||||
|
||||
(tile_request, request_id, tile)
|
||||
(tile_request, tile)
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +157,6 @@ mod tests {
|
||||
coords: (0, 0, ZoomLevel::default()).into(),
|
||||
layers: Default::default(),
|
||||
},
|
||||
0,
|
||||
Box::new([0]),
|
||||
),
|
||||
&mut context,
|
||||
|
||||
@ -18,10 +18,10 @@ pub enum StoredLayer {
|
||||
},
|
||||
TessellatedLayer {
|
||||
coords: WorldTileCoords,
|
||||
layer_name: String,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
/// Holds for each feature the count of indices.
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: tile::Layer,
|
||||
},
|
||||
}
|
||||
|
||||
@ -36,20 +36,29 @@ impl StoredLayer {
|
||||
pub fn layer_name(&self) -> &str {
|
||||
match self {
|
||||
StoredLayer::UnavailableLayer { layer_name, .. } => layer_name.as_str(),
|
||||
StoredLayer::TessellatedLayer { layer_data, .. } => &layer_data.name,
|
||||
StoredLayer::TessellatedLayer { layer_name, .. } => layer_name.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub enum TileStatus {
|
||||
Pending,
|
||||
Failed,
|
||||
Success,
|
||||
}
|
||||
|
||||
/// Stores multiple [StoredLayers](StoredLayer).
|
||||
pub struct StoredTile {
|
||||
layers: Vec<StoredLayer>,
|
||||
status: TileStatus,
|
||||
}
|
||||
|
||||
impl StoredTile {
|
||||
pub fn new(first_layer: StoredLayer) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
layers: vec![first_layer],
|
||||
layers: vec![],
|
||||
status: TileStatus::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,7 +93,7 @@ impl TileRepository {
|
||||
{
|
||||
match entry {
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(StoredTile::new(layer));
|
||||
panic!("Can not add a tessellated layer if no request has been started before.")
|
||||
}
|
||||
btree_map::Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().layers.push(layer);
|
||||
@ -105,6 +114,46 @@ impl TileRepository {
|
||||
.map(|results| results.layers.iter())
|
||||
}
|
||||
|
||||
/// Create a new tile.
|
||||
pub fn create_tile(&mut self, coords: &WorldTileCoords) -> bool {
|
||||
if let Some(entry) = coords.build_quad_key().map(|key| self.tree.entry(key)) {
|
||||
match entry {
|
||||
btree_map::Entry::Vacant(entry) => {
|
||||
entry.insert(StoredTile::new());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Checks if a layer has been fetched.
|
||||
pub fn needs_fetching(&self, coords: &WorldTileCoords) -> bool {
|
||||
if let Some(_) = coords.build_quad_key().and_then(|key| self.tree.get(&key)) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn success(&mut self, coords: &WorldTileCoords) {
|
||||
if let Some(cached_tile) = coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree.get_mut(&key))
|
||||
{
|
||||
cached_tile.status = TileStatus::Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a layer has been fetched.
|
||||
pub fn fail(&mut self, coords: &WorldTileCoords) {
|
||||
if let Some(cached_tile) = coords
|
||||
.build_quad_key()
|
||||
.and_then(|key| self.tree.get_mut(&key))
|
||||
{
|
||||
cached_tile.status = TileStatus::Failed;
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes all the cached tessellate layers that are not contained within the given
|
||||
/// layers hashset.
|
||||
pub fn retain_missing_layer_names(
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
//! Tile request state.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{
|
||||
coords::WorldTileCoords,
|
||||
io::{TileRequest, TileRequestID},
|
||||
};
|
||||
|
||||
/// Stores a map of pending requests, coords and the current tile being requested.
|
||||
#[derive(Default)]
|
||||
pub struct TileRequestState {
|
||||
current_id: TileRequestID,
|
||||
pending_tile_requests: HashMap<TileRequestID, TileRequest>,
|
||||
pending_coords: HashSet<WorldTileCoords>,
|
||||
}
|
||||
|
||||
impl TileRequestState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_id: 1,
|
||||
pending_tile_requests: Default::default(),
|
||||
pending_coords: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_tile_request_pending(&self, coords: &WorldTileCoords) -> bool {
|
||||
self.pending_coords.contains(coords)
|
||||
}
|
||||
|
||||
pub fn start_tile_request(&mut self, tile_request: TileRequest) -> Option<TileRequestID> {
|
||||
if self.is_tile_request_pending(&tile_request.coords) {
|
||||
return None;
|
||||
}
|
||||
|
||||
self.pending_coords.insert(tile_request.coords);
|
||||
let id = self.current_id;
|
||||
self.pending_tile_requests.insert(id, tile_request);
|
||||
self.current_id += 1;
|
||||
Some(id)
|
||||
}
|
||||
|
||||
pub fn finish_tile_request(&mut self, id: TileRequestID) -> Option<TileRequest> {
|
||||
self.pending_tile_requests.remove(&id).map(|request| {
|
||||
self.pending_coords.remove(&request.coords);
|
||||
request
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_tile_request(&self, id: TileRequestID) -> Option<&TileRequest> {
|
||||
self.pending_tile_requests.get(&id)
|
||||
}
|
||||
}
|
||||
110
maplibre/src/io/transferables.rs
Normal file
110
maplibre/src/io/transferables.rs
Normal file
@ -0,0 +1,110 @@
|
||||
use geozero::mvt::{tile, tile::Layer};
|
||||
|
||||
use crate::{
|
||||
coords::WorldTileCoords,
|
||||
io::tile_repository::StoredLayer,
|
||||
render::ShaderVertex,
|
||||
tessellation::{IndexDataType, OverAlignedVertexBuffer},
|
||||
};
|
||||
|
||||
pub trait TileTessellated: Send {
|
||||
fn new(coords: WorldTileCoords) -> Self;
|
||||
|
||||
fn coords(&self) -> &WorldTileCoords;
|
||||
}
|
||||
|
||||
pub trait UnavailableLayer: Send {
|
||||
fn new(coords: WorldTileCoords, layer_name: String) -> Self;
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer;
|
||||
}
|
||||
|
||||
pub trait TessellatedLayer: Send {
|
||||
fn new(
|
||||
coords: WorldTileCoords,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: tile::Layer,
|
||||
) -> Self;
|
||||
fn to_stored_layer(self) -> StoredLayer;
|
||||
}
|
||||
|
||||
pub struct DefaultTileTessellated {
|
||||
pub coords: WorldTileCoords,
|
||||
}
|
||||
|
||||
impl TileTessellated for DefaultTileTessellated {
|
||||
fn new(coords: WorldTileCoords) -> Self {
|
||||
Self { coords }
|
||||
}
|
||||
|
||||
fn coords(&self) -> &WorldTileCoords {
|
||||
&self.coords
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultUnavailableLayer {
|
||||
pub coords: WorldTileCoords,
|
||||
pub layer_name: String,
|
||||
}
|
||||
|
||||
impl UnavailableLayer for DefaultUnavailableLayer {
|
||||
fn new(coords: WorldTileCoords, layer_name: String) -> Self {
|
||||
Self { coords, layer_name }
|
||||
}
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
StoredLayer::UnavailableLayer {
|
||||
coords: self.coords,
|
||||
layer_name: self.layer_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DefaultTessellatedLayer {
|
||||
pub coords: WorldTileCoords,
|
||||
pub buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
/// Holds for each feature the count of indices.
|
||||
pub feature_indices: Vec<u32>,
|
||||
pub layer_data: Layer, // FIXME (wasm-executor): Introduce a better structure for this
|
||||
}
|
||||
|
||||
impl TessellatedLayer for DefaultTessellatedLayer {
|
||||
fn new(
|
||||
coords: WorldTileCoords,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: Layer,
|
||||
) -> Self {
|
||||
Self {
|
||||
coords,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
StoredLayer::TessellatedLayer {
|
||||
coords: self.coords,
|
||||
layer_name: self.layer_data.name,
|
||||
buffer: self.buffer,
|
||||
feature_indices: self.feature_indices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Transferables: 'static {
|
||||
type TileTessellated: TileTessellated;
|
||||
type UnavailableLayer: UnavailableLayer;
|
||||
type TessellatedLayer: TessellatedLayer;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DefaultTransferables;
|
||||
|
||||
impl Transferables for DefaultTransferables {
|
||||
type TileTessellated = DefaultTileTessellated;
|
||||
type UnavailableLayer = DefaultUnavailableLayer;
|
||||
type TessellatedLayer = DefaultTessellatedLayer;
|
||||
}
|
||||
@ -16,11 +16,15 @@
|
||||
//! maplibre = "0.0.2"
|
||||
//! ```
|
||||
|
||||
use std::{
|
||||
borrow::{Borrow, BorrowMut},
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
io::{
|
||||
scheduler::{ScheduleMethod, Scheduler},
|
||||
source_client::HttpClient,
|
||||
},
|
||||
environment::Environment,
|
||||
io::{scheduler::Scheduler, source_client::HttpClient},
|
||||
map_schedule::InteractiveMapSchedule,
|
||||
render::{
|
||||
settings::{RendererSettings, WgpuSettings},
|
||||
@ -56,29 +60,24 @@ pub mod benchmarking;
|
||||
// Internal modules
|
||||
pub(crate) mod tessellation;
|
||||
|
||||
pub mod environment;
|
||||
|
||||
pub use geozero::mvt::tile;
|
||||
|
||||
/// The [`Map`] defines the public interface of the map renderer.
|
||||
// DO NOT IMPLEMENT INTERNALS ON THIS STRUCT.
|
||||
pub struct Map<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
map_schedule: InteractiveMapSchedule<MWC, SM, HC>,
|
||||
window: MWC::MapWindow,
|
||||
pub struct Map<E: Environment> {
|
||||
// FIXME (wasm-executor): Avoid RefCell, change ownership model!
|
||||
map_schedule: Rc<RefCell<InteractiveMapSchedule<E>>>,
|
||||
window: RefCell<Option<<E::MapWindowConfig as MapWindowConfig>::MapWindow>>,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> Map<MWC, SM, HC>
|
||||
impl<E: Environment> Map<E>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
<E::MapWindowConfig as MapWindowConfig>::MapWindow: EventLoop<E>,
|
||||
{
|
||||
/// Starts the [`crate::map_schedule::MapState`] Runnable with the configured event loop.
|
||||
pub fn run(self)
|
||||
where
|
||||
MWC::MapWindow: EventLoop<MWC, SM, HC>,
|
||||
{
|
||||
pub fn run(&self) {
|
||||
self.run_with_optionally_max_frames(None);
|
||||
}
|
||||
|
||||
@ -87,10 +86,7 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_frames` - Maximum number of frames per second.
|
||||
pub fn run_with_max_frames(self, max_frames: u64)
|
||||
where
|
||||
MWC::MapWindow: EventLoop<MWC, SM, HC>,
|
||||
{
|
||||
pub fn run_with_max_frames(&self, max_frames: u64) {
|
||||
self.run_with_optionally_max_frames(Some(max_frames));
|
||||
}
|
||||
|
||||
@ -99,51 +95,42 @@ where
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `max_frames` - Optional maximum number of frames per second.
|
||||
pub fn run_with_optionally_max_frames(self, max_frames: Option<u64>)
|
||||
where
|
||||
MWC::MapWindow: EventLoop<MWC, SM, HC>,
|
||||
{
|
||||
self.window.run(self.map_schedule, max_frames);
|
||||
pub fn run_with_optionally_max_frames(&self, max_frames: Option<u64>) {
|
||||
self.window
|
||||
.borrow_mut()
|
||||
.take()
|
||||
.unwrap() // FIXME (wasm-executor): Remove unwrap
|
||||
.run(self.map_schedule.clone(), max_frames);
|
||||
}
|
||||
|
||||
pub fn map_schedule(&self) -> &InteractiveMapSchedule<MWC, SM, HC> {
|
||||
&self.map_schedule
|
||||
pub fn map_schedule(&self) -> Rc<RefCell<InteractiveMapSchedule<E>>> {
|
||||
self.map_schedule.clone()
|
||||
}
|
||||
|
||||
pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule<MWC, SM, HC> {
|
||||
/* pub fn map_schedule_mut(&mut self) -> &mut InteractiveMapSchedule<E> {
|
||||
&mut self.map_schedule
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Stores the map configuration before the map's state has been fully initialized.
|
||||
pub struct UninitializedMap<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
scheduler: Scheduler<SM>,
|
||||
http_client: HC,
|
||||
pub struct UninitializedMap<E: Environment> {
|
||||
scheduler: E::Scheduler,
|
||||
apc: E::AsyncProcedureCall,
|
||||
http_client: E::HttpClient,
|
||||
style: Style,
|
||||
|
||||
wgpu_settings: WgpuSettings,
|
||||
renderer_settings: RendererSettings,
|
||||
map_window_config: MWC,
|
||||
map_window_config: E::MapWindowConfig,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> UninitializedMap<MWC, SM, HC>
|
||||
impl<E: Environment> UninitializedMap<E>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
|
||||
{
|
||||
/// Initializes the whole rendering pipeline for the given configuration.
|
||||
/// Returns the initialized map, ready to be run.
|
||||
pub async fn initialize(self) -> Map<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
|
||||
{
|
||||
pub async fn initialize(self) -> Map<E> {
|
||||
let window = self.map_window_config.create();
|
||||
let window_size = window.size();
|
||||
|
||||
@ -158,22 +145,25 @@ where
|
||||
.await
|
||||
.ok();
|
||||
Map {
|
||||
map_schedule: InteractiveMapSchedule::new(
|
||||
map_schedule: Rc::new(RefCell::new(InteractiveMapSchedule::new(
|
||||
self.map_window_config,
|
||||
window_size,
|
||||
renderer,
|
||||
self.scheduler,
|
||||
self.apc,
|
||||
self.http_client,
|
||||
self.style,
|
||||
self.wgpu_settings,
|
||||
self.renderer_settings,
|
||||
),
|
||||
window,
|
||||
))),
|
||||
window: RefCell::new(Some(window)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "headless")]
|
||||
pub async fn initialize_headless(self) -> headless::HeadlessMap<MWC, SM, HC> {
|
||||
#[cfg(feature = "headless")]
|
||||
impl<E: Environment> UninitializedMap<E> {
|
||||
pub async fn initialize_headless(self) -> headless::HeadlessMap<E> {
|
||||
let window = self.map_window_config.create();
|
||||
let window_size = window.size();
|
||||
|
||||
@ -198,30 +188,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MapBuilder<MWC, SM, HC>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
{
|
||||
schedule_method: Option<SM>,
|
||||
scheduler: Option<Scheduler<SM>>,
|
||||
http_client: Option<HC>,
|
||||
pub struct MapBuilder<E: Environment> {
|
||||
scheduler: Option<E::Scheduler>,
|
||||
apc: Option<E::AsyncProcedureCall>,
|
||||
http_client: Option<E::HttpClient>,
|
||||
style: Option<Style>,
|
||||
|
||||
map_window_config: Option<MWC>,
|
||||
map_window_config: Option<E::MapWindowConfig>,
|
||||
wgpu_settings: Option<WgpuSettings>,
|
||||
renderer_settings: Option<RendererSettings>,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> MapBuilder<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
impl<E: Environment> MapBuilder<E> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
schedule_method: None,
|
||||
scheduler: None,
|
||||
apc: None,
|
||||
http_client: None,
|
||||
style: None,
|
||||
map_window_config: None,
|
||||
@ -230,7 +212,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_map_window_config(mut self, map_window_config: MWC) -> Self {
|
||||
pub fn with_map_window_config(mut self, map_window_config: E::MapWindowConfig) -> Self {
|
||||
self.map_window_config = Some(map_window_config);
|
||||
self
|
||||
}
|
||||
@ -245,40 +227,36 @@ where
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_schedule_method(mut self, schedule_method: SM) -> Self {
|
||||
self.schedule_method = Some(schedule_method);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_http_client(mut self, http_client: HC) -> Self {
|
||||
self.http_client = Some(http_client);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_existing_scheduler(mut self, scheduler: Scheduler<SM>) -> Self {
|
||||
pub fn with_scheduler(mut self, scheduler: E::Scheduler) -> Self {
|
||||
self.scheduler = Some(scheduler);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_apc(mut self, apc: E::AsyncProcedureCall) -> Self {
|
||||
self.apc = Some(apc);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_http_client(mut self, http_client: E::HttpClient) -> Self {
|
||||
self.http_client = Some(http_client);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_style(mut self, style: Style) -> Self {
|
||||
self.style = Some(style);
|
||||
self
|
||||
}
|
||||
|
||||
/// Builds the UninitializedMap with the given configuration.
|
||||
pub fn build(self) -> UninitializedMap<MWC, SM, HC> {
|
||||
let scheduler = self
|
||||
.scheduler
|
||||
.unwrap_or_else(|| Scheduler::new(self.schedule_method.unwrap()));
|
||||
let style = self.style.unwrap_or_default();
|
||||
|
||||
pub fn build(self) -> UninitializedMap<E> {
|
||||
UninitializedMap {
|
||||
scheduler,
|
||||
http_client: self.http_client.unwrap(),
|
||||
style,
|
||||
scheduler: self.scheduler.unwrap(), // TODO: Remove unwrap
|
||||
apc: self.apc.unwrap(), // TODO: Remove unwrap
|
||||
http_client: self.http_client.unwrap(), // TODO: Remove unwrap
|
||||
style: self.style.unwrap_or_default(),
|
||||
wgpu_settings: self.wgpu_settings.unwrap_or_default(),
|
||||
renderer_settings: self.renderer_settings.unwrap_or_default(),
|
||||
map_window_config: self.map_window_config.unwrap(),
|
||||
map_window_config: self.map_window_config.unwrap(), // TODO: Remove unwrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::{marker::PhantomData, mem};
|
||||
use std::{cell::RefCell, marker::PhantomData, mem, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
context::{MapContext, ViewState},
|
||||
@ -13,41 +13,32 @@ use crate::{
|
||||
schedule::{Schedule, Stage},
|
||||
stages::register_stages,
|
||||
style::Style,
|
||||
HeadedMapWindow, MapWindowConfig, Renderer, RendererSettings, ScheduleMethod, WgpuSettings,
|
||||
Environment, HeadedMapWindow, MapWindowConfig, Renderer, RendererSettings, WgpuSettings,
|
||||
WindowSize,
|
||||
};
|
||||
|
||||
/// Stores the state of the map, dispatches tile fetching and caching, tessellation and drawing.
|
||||
pub struct InteractiveMapSchedule<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
map_window_config: MWC,
|
||||
pub struct InteractiveMapSchedule<E: Environment> {
|
||||
map_window_config: E::MapWindowConfig,
|
||||
|
||||
// FIXME (wasm-executor): avoid RefCell, change ownership model
|
||||
pub apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
|
||||
map_context: EventuallyMapContext,
|
||||
|
||||
schedule: Schedule,
|
||||
|
||||
phantom_sm: PhantomData<SM>,
|
||||
phantom_hc: PhantomData<HC>,
|
||||
|
||||
suspended: bool,
|
||||
}
|
||||
|
||||
impl<MWC, SM, HC> InteractiveMapSchedule<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
impl<E: Environment> InteractiveMapSchedule<E> {
|
||||
pub fn new(
|
||||
map_window_config: MWC,
|
||||
map_window_config: E::MapWindowConfig,
|
||||
window_size: WindowSize,
|
||||
renderer: Option<Renderer>,
|
||||
scheduler: Scheduler<SM>,
|
||||
http_client: HC,
|
||||
scheduler: E::Scheduler, // TODO: unused
|
||||
apc: E::AsyncProcedureCall,
|
||||
http_client: E::HttpClient,
|
||||
style: Style,
|
||||
wgpu_settings: WgpuSettings,
|
||||
renderer_settings: RendererSettings,
|
||||
@ -63,13 +54,17 @@ where
|
||||
let tile_repository = TileRepository::new();
|
||||
let mut schedule = Schedule::default();
|
||||
|
||||
let http_source_client: HttpSourceClient<HC> = HttpSourceClient::new(http_client);
|
||||
register_stages(&mut schedule, http_source_client, Box::new(scheduler));
|
||||
let apc = Rc::new(RefCell::new(apc));
|
||||
|
||||
let graph = create_default_render_graph().unwrap();
|
||||
let http_source_client: HttpSourceClient<E::HttpClient> =
|
||||
HttpSourceClient::new(http_client);
|
||||
register_stages::<E>(&mut schedule, http_source_client, apc.clone());
|
||||
|
||||
let graph = create_default_render_graph().unwrap(); // TODO: Remove unwrap
|
||||
register_default_render_stages(graph, &mut schedule);
|
||||
|
||||
Self {
|
||||
apc,
|
||||
map_window_config,
|
||||
map_context: match renderer {
|
||||
None => EventuallyMapContext::Premature(PrematureMapContext {
|
||||
@ -87,8 +82,6 @@ where
|
||||
}),
|
||||
},
|
||||
schedule,
|
||||
phantom_sm: Default::default(),
|
||||
phantom_hc: Default::default(),
|
||||
suspended: false,
|
||||
}
|
||||
}
|
||||
@ -116,23 +109,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suspend(&mut self) {
|
||||
self.suspended = true;
|
||||
}
|
||||
|
||||
pub fn resume(&mut self, window: &MWC::MapWindow)
|
||||
where
|
||||
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
|
||||
{
|
||||
if let EventuallyMapContext::Full(map_context) = &mut self.map_context {
|
||||
let renderer = &mut map_context.renderer;
|
||||
renderer
|
||||
.state
|
||||
.recreate_surface::<MWC::MapWindow>(window, &renderer.instance);
|
||||
self.suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_initialized(&self) -> bool {
|
||||
match &self.map_context {
|
||||
EventuallyMapContext::Full(_) => true,
|
||||
@ -140,10 +116,36 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn late_init(&mut self) -> bool
|
||||
where
|
||||
<MWC as MapWindowConfig>::MapWindow: HeadedMapWindow,
|
||||
{
|
||||
pub fn view_state_mut(&mut self) -> &mut ViewState {
|
||||
match &mut self.map_context {
|
||||
EventuallyMapContext::Full(MapContext { view_state, .. }) => view_state,
|
||||
EventuallyMapContext::Premature(PrematureMapContext { view_state, .. }) => view_state,
|
||||
_ => panic!("should not happen"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apc(&self) -> &Rc<RefCell<E::AsyncProcedureCall>> {
|
||||
&self.apc
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Environment> InteractiveMapSchedule<E>
|
||||
where
|
||||
<E::MapWindowConfig as MapWindowConfig>::MapWindow: HeadedMapWindow,
|
||||
{
|
||||
pub fn suspend(&mut self) {
|
||||
self.suspended = true;
|
||||
}
|
||||
|
||||
pub fn resume(&mut self, window: &<E::MapWindowConfig as MapWindowConfig>::MapWindow) {
|
||||
if let EventuallyMapContext::Full(map_context) = &mut self.map_context {
|
||||
let renderer = &mut map_context.renderer;
|
||||
renderer.state.recreate_surface(window, &renderer.instance);
|
||||
self.suspended = false;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn late_init(&mut self) -> bool {
|
||||
match &self.map_context {
|
||||
EventuallyMapContext::Full(_) => false,
|
||||
EventuallyMapContext::Premature(PrematureMapContext {
|
||||
@ -155,21 +157,13 @@ where
|
||||
let renderer =
|
||||
Renderer::initialize(&window, wgpu_settings.clone(), renderer_settings.clone())
|
||||
.await
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: Remove unwrap
|
||||
self.map_context.make_full(renderer);
|
||||
true
|
||||
}
|
||||
EventuallyMapContext::_Uninitialized => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_state_mut(&mut self) -> &mut ViewState {
|
||||
match &mut self.map_context {
|
||||
EventuallyMapContext::Full(MapContext { view_state, .. }) => view_state,
|
||||
EventuallyMapContext::Premature(PrematureMapContext { view_state, .. }) => view_state,
|
||||
_ => panic!("should not happen"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrematureMapContext {
|
||||
|
||||
@ -36,9 +36,14 @@ pub mod http_client {
|
||||
}
|
||||
|
||||
/// Scheduler for non-web targets.
|
||||
pub mod schedule_method {
|
||||
pub mod scheduler {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use super::noweb::schedule_method::*;
|
||||
pub use super::noweb::scheduler::*;
|
||||
}
|
||||
|
||||
pub mod trace {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use super::noweb::trace::*;
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
use std::future::Future;
|
||||
|
||||
pub mod http_client;
|
||||
pub mod schedule_method;
|
||||
pub mod scheduler;
|
||||
pub mod trace;
|
||||
|
||||
pub fn run_multithreaded<F: Future>(future: F) -> F::Output {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
use std::future::Future;
|
||||
|
||||
use crate::{error::Error, ScheduleMethod};
|
||||
use crate::{error::Error, Scheduler};
|
||||
|
||||
/// Multi-threading with Tokio.
|
||||
pub struct TokioScheduleMethod;
|
||||
pub struct TokioScheduler;
|
||||
|
||||
impl TokioScheduleMethod {
|
||||
impl TokioScheduler {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScheduleMethod for TokioScheduleMethod {
|
||||
impl Scheduler for TokioScheduler {
|
||||
fn schedule<T>(&self, future_factory: impl FnOnce() -> T + Send + 'static) -> Result<(), Error>
|
||||
where
|
||||
T: Future<Output = ()> + Send + 'static,
|
||||
8
maplibre/src/platform/noweb/trace.rs
Normal file
8
maplibre/src/platform/noweb/trace.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#[cfg(feature = "trace")]
|
||||
pub fn enable_tracing() {
|
||||
use tracing_subscriber::{layer::SubscriberExt, Registry};
|
||||
|
||||
let subscriber = Registry::default().with(tracing_tracy::TracyLayer::new());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
|
||||
}
|
||||
@ -191,7 +191,7 @@ impl<Q: Queue<B>, B, V: Pod, I: Pod, TM: Pod, FM: Pod> BufferPool<Q, B, V, I, TM
|
||||
self.index.get_layers(coords).map(|layers| {
|
||||
layers
|
||||
.iter()
|
||||
.map(|entry| entry.style_layer.source_layer.as_ref().unwrap().as_str())
|
||||
.map(|entry| entry.style_layer.source_layer.as_ref().unwrap().as_str()) // TODO: Remove unwrap
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ impl BufferedTextureHead {
|
||||
let padded_buffer = buffer_slice.get_mapped_range();
|
||||
|
||||
let mut png_encoder = png::Encoder::new(
|
||||
File::create(png_output_path).unwrap(),
|
||||
File::create(png_output_path).unwrap(), // TODO: Remove unwrap
|
||||
self.buffer_dimensions.width as u32,
|
||||
self.buffer_dimensions.height as u32,
|
||||
);
|
||||
@ -92,17 +92,17 @@ impl BufferedTextureHead {
|
||||
png_encoder.set_color(png::ColorType::Rgba);
|
||||
let mut png_writer = png_encoder
|
||||
.write_header()
|
||||
.unwrap()
|
||||
.unwrap() // TODO: Remove unwrap
|
||||
.into_stream_writer_with_size(self.buffer_dimensions.unpadded_bytes_per_row)
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: Remove unwrap
|
||||
|
||||
// from the padded_buffer we write just the unpadded bytes into the image
|
||||
for chunk in padded_buffer.chunks(self.buffer_dimensions.padded_bytes_per_row) {
|
||||
png_writer
|
||||
.write_all(&chunk[..self.buffer_dimensions.unpadded_bytes_per_row])
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: Remove unwrap
|
||||
}
|
||||
png_writer.finish().unwrap();
|
||||
png_writer.finish().unwrap(); // TODO: Remove unwrap
|
||||
|
||||
// With the current interface, we have to make sure all mapped views are
|
||||
// dropped before we unmap the buffer.
|
||||
|
||||
@ -44,7 +44,7 @@ impl Stage for UploadStage {
|
||||
.position
|
||||
.to_homogeneous()
|
||||
.cast::<f32>()
|
||||
.unwrap()
|
||||
.unwrap() // TODO: Remove unwrap
|
||||
.into(),
|
||||
))]),
|
||||
);
|
||||
@ -168,7 +168,7 @@ impl UploadStage {
|
||||
})
|
||||
{
|
||||
for style_layer in &style.layers {
|
||||
let source_layer = style_layer.source_layer.as_ref().unwrap();
|
||||
let source_layer = style_layer.source_layer.as_ref().unwrap(); // TODO: Remove unwrap
|
||||
|
||||
if let Some(message) = available_layers
|
||||
.iter()
|
||||
@ -187,7 +187,6 @@ impl UploadStage {
|
||||
StoredLayer::TessellatedLayer {
|
||||
coords,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
buffer,
|
||||
..
|
||||
} => {
|
||||
@ -197,17 +196,16 @@ impl UploadStage {
|
||||
);
|
||||
|
||||
let guard = allocate_feature_metadata.enter();
|
||||
let feature_metadata = layer_data
|
||||
.features
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, _feature)| {
|
||||
iter::repeat(ShaderFeatureStyle {
|
||||
color: color.unwrap(),
|
||||
let feature_metadata =
|
||||
(0..feature_indices.len()) // FIXME: Iterate over actual featrues
|
||||
.enumerate()
|
||||
.flat_map(|(i, _feature)| {
|
||||
iter::repeat(ShaderFeatureStyle {
|
||||
color: color.unwrap(),
|
||||
})
|
||||
.take(feature_indices[i] as usize)
|
||||
})
|
||||
.take(feature_indices[i] as usize)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
.collect::<Vec<_>>();
|
||||
drop(guard);
|
||||
|
||||
tracing::trace!("Allocating geometry at {}", &coords);
|
||||
|
||||
@ -166,8 +166,8 @@ impl<Q: Queue<B>, B> TileViewPattern<Q, B> {
|
||||
|
||||
let raw_buffer = bytemuck::cast_slice(buffer.as_slice());
|
||||
if raw_buffer.len() as wgpu::BufferAddress > self.buffer.inner_size {
|
||||
/* FIXME: We need to avoid this case by either choosing a proper size
|
||||
(DEFAULT_TILE_VIEW_SIZE), or resizing the buffer */
|
||||
/* TODO: We need to avoid this case by either choosing a proper size
|
||||
TODO: (DEFAULT_TILE_VIEW_SIZE), or resizing the buffer */
|
||||
panic!("Buffer is too small to store the tile pattern!");
|
||||
}
|
||||
queue.write_buffer(&self.buffer.inner, 0, raw_buffer);
|
||||
|
||||
@ -250,7 +250,7 @@ impl Schedule {
|
||||
for label in &self.stage_order {
|
||||
#[cfg(feature = "trace")]
|
||||
let _stage_span = tracing::info_span!("stage", name = ?label).entered();
|
||||
let stage = self.stages.get_mut(label).unwrap();
|
||||
let stage = self.stages.get_mut(label).unwrap(); // TODO: Remove unwrap
|
||||
stage.run(context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
use std::{fmt, sync::mpsc};
|
||||
|
||||
use geozero::mvt::tile;
|
||||
|
||||
use crate::{
|
||||
coords::WorldTileCoords,
|
||||
io::{tile_repository::StoredLayer, TileRequestID},
|
||||
render::ShaderVertex,
|
||||
tessellation::{IndexDataType, OverAlignedVertexBuffer},
|
||||
};
|
||||
|
||||
pub type MessageSender = mpsc::Sender<TessellateMessage>;
|
||||
pub type MessageReceiver = mpsc::Receiver<TessellateMessage>;
|
||||
|
||||
/// [crate::io::TileTessellateMessage] or [crate::io::LayerTessellateMessage] tessellation message.
|
||||
pub enum TessellateMessage {
|
||||
Tile(TileTessellateMessage),
|
||||
Layer(LayerTessellateMessage),
|
||||
}
|
||||
|
||||
/// The result of the tessellation of a tile.
|
||||
pub struct TileTessellateMessage {
|
||||
pub request_id: TileRequestID,
|
||||
pub coords: WorldTileCoords,
|
||||
}
|
||||
|
||||
/// `TessellatedLayer` contains the result of the tessellation for a specific layer, otherwise
|
||||
/// `UnavailableLayer` if the layer doesn't exist.
|
||||
pub enum LayerTessellateMessage {
|
||||
UnavailableLayer {
|
||||
coords: WorldTileCoords,
|
||||
layer_name: String,
|
||||
},
|
||||
TessellatedLayer {
|
||||
coords: WorldTileCoords,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
/// Holds for each feature the count of indices.
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: tile::Layer,
|
||||
},
|
||||
}
|
||||
|
||||
impl Into<StoredLayer> for LayerTessellateMessage {
|
||||
fn into(self) -> StoredLayer {
|
||||
match self {
|
||||
LayerTessellateMessage::UnavailableLayer { coords, layer_name } => {
|
||||
StoredLayer::UnavailableLayer { coords, layer_name }
|
||||
}
|
||||
LayerTessellateMessage::TessellatedLayer {
|
||||
coords,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
} => StoredLayer::TessellatedLayer {
|
||||
coords,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for LayerTessellateMessage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"LayerTessellateMessage{}",
|
||||
match self {
|
||||
LayerTessellateMessage::UnavailableLayer { coords, .. } => coords,
|
||||
LayerTessellateMessage::TessellatedLayer { coords, .. } => coords,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,11 @@
|
||||
//! [Stages](Stage) for requesting and preparing data
|
||||
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
rc::Rc,
|
||||
sync::{mpsc, Arc, Mutex},
|
||||
};
|
||||
|
||||
use geozero::{mvt::tile, GeozeroDatasource};
|
||||
use request_stage::RequestStage;
|
||||
@ -9,78 +14,61 @@ use crate::{
|
||||
coords::{WorldCoords, WorldTileCoords, Zoom, ZoomLevel},
|
||||
error::Error,
|
||||
io::{
|
||||
apc::{AsyncProcedureCall, Context, Message},
|
||||
geometry_index::{GeometryIndex, IndexedGeometry, TileIndex},
|
||||
pipeline::{PipelineContext, PipelineProcessor, Processable},
|
||||
source_client::HttpSourceClient,
|
||||
tile_pipelines::build_vector_tile_pipeline,
|
||||
tile_request_state::TileRequestState,
|
||||
TileRequest, TileRequestID,
|
||||
transferables::{
|
||||
DefaultTessellatedLayer, DefaultTileTessellated, DefaultTransferables,
|
||||
DefaultUnavailableLayer, TessellatedLayer, TileTessellated, Transferables,
|
||||
UnavailableLayer,
|
||||
},
|
||||
TileRequest,
|
||||
},
|
||||
render::ShaderVertex,
|
||||
schedule::Schedule,
|
||||
stages::{
|
||||
message::{
|
||||
LayerTessellateMessage, MessageReceiver, MessageSender, TessellateMessage,
|
||||
TileTessellateMessage,
|
||||
},
|
||||
populate_tile_store_stage::PopulateTileStore,
|
||||
},
|
||||
stages::populate_tile_store_stage::PopulateTileStore,
|
||||
tessellation::{IndexDataType, OverAlignedVertexBuffer},
|
||||
HttpClient, ScheduleMethod, Scheduler,
|
||||
Environment, HttpClient, Scheduler,
|
||||
};
|
||||
|
||||
mod message;
|
||||
mod populate_tile_store_stage;
|
||||
mod request_stage;
|
||||
|
||||
/// Register stages required for requesting and preparing new tiles.
|
||||
pub fn register_stages<HC: HttpClient, SM: ScheduleMethod>(
|
||||
pub fn register_stages<E: Environment>(
|
||||
schedule: &mut Schedule,
|
||||
http_source_client: HttpSourceClient<HC>,
|
||||
scheduler: Box<Scheduler<SM>>,
|
||||
http_source_client: HttpSourceClient<E::HttpClient>,
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
) {
|
||||
let (message_sender, message_receiver): (MessageSender, MessageReceiver) = mpsc::channel();
|
||||
let shared_thread_state = SharedThreadState {
|
||||
tile_request_state: Arc::new(Mutex::new(TileRequestState::new())),
|
||||
message_sender,
|
||||
geometry_index: Arc::new(Mutex::new(GeometryIndex::new())),
|
||||
};
|
||||
|
||||
schedule.add_stage(
|
||||
"request",
|
||||
RequestStage::new(shared_thread_state.clone(), http_source_client, *scheduler),
|
||||
);
|
||||
schedule.add_stage(
|
||||
"populate_tile_store",
|
||||
PopulateTileStore::new(shared_thread_state, message_receiver),
|
||||
RequestStage::<E>::new(http_source_client, apc.clone()),
|
||||
);
|
||||
schedule.add_stage("populate_tile_store", PopulateTileStore::<E>::new(apc));
|
||||
}
|
||||
|
||||
pub struct HeadedPipelineProcessor {
|
||||
state: SharedThreadState,
|
||||
pub struct HeadedPipelineProcessor<T: Transferables, HC: HttpClient, C: Context<T, HC>> {
|
||||
context: C,
|
||||
phantom_t: PhantomData<T>,
|
||||
phantom_hc: PhantomData<HC>,
|
||||
}
|
||||
|
||||
impl PipelineProcessor for HeadedPipelineProcessor {
|
||||
fn tile_finished(&mut self, request_id: TileRequestID, coords: &WorldTileCoords) {
|
||||
self.state
|
||||
.message_sender
|
||||
.send(TessellateMessage::Tile(TileTessellateMessage {
|
||||
request_id,
|
||||
coords: *coords,
|
||||
}))
|
||||
.unwrap();
|
||||
impl<'c, T: Transferables, HC: HttpClient, C: Context<T, HC>> PipelineProcessor
|
||||
for HeadedPipelineProcessor<T, HC, C>
|
||||
{
|
||||
fn tile_finished(&mut self, coords: &WorldTileCoords) {
|
||||
self.context
|
||||
.send(Message::TileTessellated(T::TileTessellated::new(*coords)))
|
||||
}
|
||||
|
||||
fn layer_unavailable(&mut self, coords: &WorldTileCoords, layer_name: &str) {
|
||||
self.state
|
||||
.message_sender
|
||||
.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::UnavailableLayer {
|
||||
coords: *coords,
|
||||
layer_name: layer_name.to_owned(),
|
||||
},
|
||||
))
|
||||
.unwrap();
|
||||
self.context
|
||||
.send(Message::UnavailableLayer(T::UnavailableLayer::new(
|
||||
*coords,
|
||||
layer_name.to_owned(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn layer_tesselation_finished(
|
||||
@ -90,17 +78,13 @@ impl PipelineProcessor for HeadedPipelineProcessor {
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: tile::Layer,
|
||||
) {
|
||||
self.state
|
||||
.message_sender
|
||||
.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::TessellatedLayer {
|
||||
coords: *coords,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
},
|
||||
))
|
||||
.unwrap();
|
||||
self.context
|
||||
.send(Message::TessellatedLayer(T::TessellatedLayer::new(
|
||||
*coords,
|
||||
buffer,
|
||||
feature_indices,
|
||||
layer_data,
|
||||
)))
|
||||
}
|
||||
|
||||
fn layer_indexing_finished(
|
||||
@ -108,80 +92,32 @@ impl PipelineProcessor for HeadedPipelineProcessor {
|
||||
coords: &WorldTileCoords,
|
||||
geometries: Vec<IndexedGeometry<f64>>,
|
||||
) {
|
||||
if let Ok(mut geometry_index) = self.state.geometry_index.lock() {
|
||||
// FIXME (wasm-executor): Readd
|
||||
/* if let Ok(mut geometry_index) = self.state.geometry_index.lock() {
|
||||
geometry_index.index_tile(coords, TileIndex::Linear { list: geometries })
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores and provides access to the thread safe data shared between the schedulers.
|
||||
#[derive(Clone)]
|
||||
pub struct SharedThreadState {
|
||||
pub tile_request_state: Arc<Mutex<TileRequestState>>,
|
||||
pub message_sender: mpsc::Sender<TessellateMessage>,
|
||||
pub geometry_index: Arc<Mutex<GeometryIndex>>,
|
||||
}
|
||||
|
||||
impl SharedThreadState {
|
||||
fn get_tile_request(&self, request_id: TileRequestID) -> Option<TileRequest> {
|
||||
self.tile_request_state
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|tile_request_state| tile_request_state.get_tile_request(request_id).cloned())
|
||||
// FIXME (wasm-executor): Readd
|
||||
/*pub fn query_point(
|
||||
&self,
|
||||
world_coords: &WorldCoords,
|
||||
z: ZoomLevel,
|
||||
zoom: Zoom,
|
||||
) -> Option<Vec<IndexedGeometry<f64>>> {
|
||||
if let Ok(geometry_index) = self.geometry_index.lock() {
|
||||
geometry_index
|
||||
.query_point(world_coords, z, zoom)
|
||||
.map(|geometries| {
|
||||
geometries
|
||||
.iter()
|
||||
.cloned()
|
||||
.cloned()
|
||||
.collect::<Vec<IndexedGeometry<f64>>>()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn process_tile(&self, request_id: TileRequestID, data: Box<[u8]>) -> Result<(), Error> {
|
||||
if let Some(tile_request) = self.get_tile_request(request_id) {
|
||||
let mut pipeline_context = PipelineContext::new(HeadedPipelineProcessor {
|
||||
state: self.clone(),
|
||||
});
|
||||
let pipeline = build_vector_tile_pipeline();
|
||||
pipeline.process((tile_request, request_id, data), &mut pipeline_context);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tile_unavailable(
|
||||
&self,
|
||||
coords: &WorldTileCoords,
|
||||
request_id: TileRequestID,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(tile_request) = self.get_tile_request(request_id) {
|
||||
for to_load in &tile_request.layers {
|
||||
tracing::warn!("layer {} at {} unavailable", to_load, coords);
|
||||
self.message_sender.send(TessellateMessage::Layer(
|
||||
LayerTessellateMessage::UnavailableLayer {
|
||||
coords: tile_request.coords,
|
||||
layer_name: to_load.to_string(),
|
||||
},
|
||||
))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub fn query_point(
|
||||
&self,
|
||||
world_coords: &WorldCoords,
|
||||
z: ZoomLevel,
|
||||
zoom: Zoom,
|
||||
) -> Option<Vec<IndexedGeometry<f64>>> {
|
||||
if let Ok(geometry_index) = self.geometry_index.lock() {
|
||||
geometry_index
|
||||
.query_point(world_coords, z, zoom)
|
||||
.map(|geometries| {
|
||||
geometries
|
||||
.iter()
|
||||
.cloned()
|
||||
.cloned()
|
||||
.collect::<Vec<IndexedGeometry<f64>>>()
|
||||
})
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
//}
|
||||
|
||||
@ -1,49 +1,69 @@
|
||||
//! Receives data from async threads and populates the [`crate::io::tile_repository::TileRepository`].
|
||||
|
||||
use super::{MessageReceiver, SharedThreadState, TessellateMessage, TileTessellateMessage};
|
||||
use crate::{context::MapContext, io::tile_repository::StoredLayer, schedule::Stage};
|
||||
use std::{borrow::BorrowMut, cell::RefCell, ops::Deref, rc::Rc};
|
||||
|
||||
pub struct PopulateTileStore {
|
||||
shared_thread_state: SharedThreadState,
|
||||
message_receiver: MessageReceiver,
|
||||
use crate::{
|
||||
context::MapContext,
|
||||
io::{
|
||||
apc::{AsyncProcedureCall, Message},
|
||||
tile_repository::StoredLayer,
|
||||
transferables::{TessellatedLayer, TileTessellated, UnavailableLayer},
|
||||
},
|
||||
schedule::Stage,
|
||||
Environment,
|
||||
};
|
||||
|
||||
pub struct PopulateTileStore<E: Environment> {
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
}
|
||||
|
||||
impl PopulateTileStore {
|
||||
pub fn new(shared_thread_state: SharedThreadState, message_receiver: MessageReceiver) -> Self {
|
||||
Self {
|
||||
shared_thread_state,
|
||||
message_receiver,
|
||||
}
|
||||
impl<E: Environment> PopulateTileStore<E> {
|
||||
pub fn new(apc: Rc<RefCell<E::AsyncProcedureCall>>) -> Self {
|
||||
Self { apc }
|
||||
}
|
||||
}
|
||||
|
||||
impl Stage for PopulateTileStore {
|
||||
impl<E: Environment> Stage for PopulateTileStore<E> {
|
||||
fn run(
|
||||
&mut self,
|
||||
MapContext {
|
||||
tile_repository, ..
|
||||
}: &mut MapContext,
|
||||
) {
|
||||
if let Ok(result) = self.message_receiver.try_recv() {
|
||||
match result {
|
||||
TessellateMessage::Layer(layer_result) => {
|
||||
let layer: StoredLayer = layer_result.into();
|
||||
tracing::trace!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
}
|
||||
TessellateMessage::Tile(TileTessellateMessage { request_id, coords }) => loop {
|
||||
if let Ok(mut tile_request_state) =
|
||||
self.shared_thread_state.tile_request_state.try_lock()
|
||||
{
|
||||
tile_request_state.finish_tile_request(request_id);
|
||||
if let Ok(mut apc) = self.apc.deref().try_borrow_mut() {
|
||||
if let Some(result) = apc.receive() {
|
||||
match result {
|
||||
Message::TileTessellated(tranferred) => {
|
||||
let coords = tranferred.coords();
|
||||
tile_repository.success(coords);
|
||||
tracing::trace!("Tile at {} finished loading", coords);
|
||||
break;
|
||||
log::warn!("Tile at {} finished loading", coords);
|
||||
}
|
||||
},
|
||||
// FIXME: deduplicate
|
||||
Message::UnavailableLayer(tranferred) => {
|
||||
let layer: StoredLayer = tranferred.to_stored_layer();
|
||||
tracing::debug!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
}
|
||||
Message::TessellatedLayer(data) => {
|
||||
let layer: StoredLayer = data.to_stored_layer();
|
||||
tracing::debug!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
log::warn!(
|
||||
"Layer {} at {} reached main thread",
|
||||
layer.layer_name(),
|
||||
layer.get_coords()
|
||||
);
|
||||
tile_repository.put_tessellated_layer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,56 +1,53 @@
|
||||
//! Requests tiles which are currently in view
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cell::RefCell,
|
||||
collections::{HashMap, HashSet},
|
||||
future::Future,
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
process::Output,
|
||||
rc::Rc,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
context::MapContext,
|
||||
coords::{ViewRegion, WorldTileCoords},
|
||||
coords::{ViewRegion, WorldTileCoords, ZoomLevel},
|
||||
error::Error,
|
||||
io::{
|
||||
apc::{AsyncProcedureCall, AsyncProcedureFuture, Context, Input, Message},
|
||||
pipeline::{PipelineContext, Processable},
|
||||
source_client::{HttpSourceClient, SourceClient},
|
||||
tile_pipelines::build_vector_tile_pipeline,
|
||||
tile_repository::TileRepository,
|
||||
transferables::{Transferables, UnavailableLayer},
|
||||
TileRequest,
|
||||
},
|
||||
schedule::Stage,
|
||||
stages::SharedThreadState,
|
||||
HttpClient, ScheduleMethod, Scheduler, Style,
|
||||
stages::HeadedPipelineProcessor,
|
||||
Environment, HttpClient, Scheduler, Style,
|
||||
};
|
||||
|
||||
pub struct RequestStage<SM, HC>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
shared_thread_state: SharedThreadState,
|
||||
scheduler: Scheduler<SM>,
|
||||
http_source_client: HttpSourceClient<HC>,
|
||||
try_failed: bool,
|
||||
pub struct RequestStage<E: Environment> {
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
http_source_client: HttpSourceClient<E::HttpClient>,
|
||||
}
|
||||
|
||||
impl<SM, HC> RequestStage<SM, HC>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
impl<E: Environment> RequestStage<E> {
|
||||
pub fn new(
|
||||
shared_thread_state: SharedThreadState,
|
||||
http_source_client: HttpSourceClient<HC>,
|
||||
scheduler: Scheduler<SM>,
|
||||
http_source_client: HttpSourceClient<E::HttpClient>,
|
||||
apc: Rc<RefCell<E::AsyncProcedureCall>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
shared_thread_state,
|
||||
scheduler,
|
||||
apc,
|
||||
http_source_client,
|
||||
try_failed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<SM, HC> Stage for RequestStage<SM, HC>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
impl<E: Environment> Stage for RequestStage<E> {
|
||||
fn run(
|
||||
&mut self,
|
||||
MapContext {
|
||||
@ -62,11 +59,10 @@ where
|
||||
) {
|
||||
let view_region = view_state.create_view_region();
|
||||
|
||||
if view_state.camera.did_change(0.05) || view_state.zoom.did_change(0.05) || self.try_failed
|
||||
{
|
||||
if view_state.camera.did_change(0.05) || view_state.zoom.did_change(0.05) {
|
||||
if let Some(view_region) = &view_region {
|
||||
// FIXME: We also need to request tiles from layers above if we are over the maximum zoom level
|
||||
self.try_failed = self.request_tiles_in_view(tile_repository, style, view_region);
|
||||
self.request_tiles_in_view(tile_repository, style, view_region);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,20 +71,58 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<SM, HC> RequestStage<SM, HC>
|
||||
where
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
pub fn schedule<E: Environment, C: Context<E::Transferables, E::HttpClient>>(
|
||||
input: Input,
|
||||
context: C,
|
||||
) -> AsyncProcedureFuture {
|
||||
// FIXME: improve input handling
|
||||
let input = match input {
|
||||
Input::TileRequest(input) => Some(input),
|
||||
_ => None,
|
||||
}
|
||||
.unwrap(); // FIXME (wasm-executor): Remove unwrap
|
||||
|
||||
Box::pin(async move {
|
||||
let coords = input.coords;
|
||||
let client = context.source_client();
|
||||
|
||||
match client.fetch(&coords).await {
|
||||
Ok(data) => {
|
||||
let data = data.into_boxed_slice();
|
||||
|
||||
let mut pipeline_context = PipelineContext::new(HeadedPipelineProcessor {
|
||||
context,
|
||||
phantom_t: Default::default(),
|
||||
phantom_hc: Default::default(),
|
||||
});
|
||||
let pipeline = build_vector_tile_pipeline();
|
||||
pipeline.process((input, data), &mut pipeline_context);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", &e);
|
||||
for to_load in &input.layers {
|
||||
tracing::warn!("layer {} at {} unavailable", to_load, coords);
|
||||
context.send(Message::UnavailableLayer(
|
||||
<E::Transferables as Transferables>::UnavailableLayer::new(
|
||||
input.coords,
|
||||
to_load.to_string(),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
impl<E: Environment> RequestStage<E> {
|
||||
/// Request tiles which are currently in view.
|
||||
#[tracing::instrument(skip_all)]
|
||||
fn request_tiles_in_view(
|
||||
&self,
|
||||
tile_repository: &TileRepository,
|
||||
tile_repository: &mut TileRepository,
|
||||
style: &Style,
|
||||
view_region: &ViewRegion,
|
||||
) -> bool {
|
||||
let mut try_failed = false;
|
||||
) {
|
||||
let source_layers: HashSet<String> = style
|
||||
.layers
|
||||
.iter()
|
||||
@ -98,66 +132,41 @@ where
|
||||
for coords in view_region.iter() {
|
||||
if coords.build_quad_key().is_some() {
|
||||
// TODO: Make tesselation depend on style?
|
||||
try_failed = self
|
||||
.try_request_tile(tile_repository, &coords, &source_layers)
|
||||
.unwrap();
|
||||
self.request_tile(tile_repository, &coords, &source_layers)
|
||||
.unwrap(); // TODO: Remove unwrap
|
||||
}
|
||||
}
|
||||
try_failed
|
||||
}
|
||||
|
||||
fn try_request_tile(
|
||||
fn request_tile(
|
||||
&self,
|
||||
tile_repository: &TileRepository,
|
||||
tile_repository: &mut TileRepository,
|
||||
coords: &WorldTileCoords,
|
||||
layers: &HashSet<String>,
|
||||
) -> Result<bool, Error> {
|
||||
if !tile_repository.is_layers_missing(coords, layers) {
|
||||
) -> Result<(), Error> {
|
||||
/* if !tile_repository.is_layers_missing(coords, layers) {
|
||||
return Ok(false);
|
||||
}*/
|
||||
|
||||
if tile_repository.needs_fetching(&coords) {
|
||||
tile_repository.create_tile(coords);
|
||||
|
||||
tracing::info!("new tile request: {}", &coords);
|
||||
self.apc.deref().borrow().schedule(
|
||||
Input::TileRequest(TileRequest {
|
||||
coords: *coords,
|
||||
layers: layers.clone(),
|
||||
}),
|
||||
schedule::<
|
||||
E,
|
||||
<E::AsyncProcedureCall as AsyncProcedureCall<
|
||||
E::Transferables,
|
||||
E::HttpClient,
|
||||
>>::Context,
|
||||
>,
|
||||
);
|
||||
}
|
||||
|
||||
if let Ok(mut tile_request_state) = self.shared_thread_state.tile_request_state.try_lock() {
|
||||
if let Some(request_id) = tile_request_state.start_tile_request(TileRequest {
|
||||
coords: *coords,
|
||||
layers: layers.clone(),
|
||||
}) {
|
||||
tracing::info!("new tile request: {}", &coords);
|
||||
|
||||
// The following snippet can be added instead of the next code block to demonstrate
|
||||
// an understanable approach of fetching
|
||||
/*#[cfg(target_arch = "wasm32")]
|
||||
if let Some(tile_coords) = coords.into_tile(TileAddressingScheme::TMS) {
|
||||
crate::platform::legacy_webworker_fetcher::request_tile(
|
||||
request_id,
|
||||
tile_coords,
|
||||
);
|
||||
}*/
|
||||
|
||||
let client = SourceClient::Http(self.http_source_client.clone());
|
||||
let coords = *coords;
|
||||
|
||||
let state = self.shared_thread_state.clone();
|
||||
self.scheduler
|
||||
.schedule_method()
|
||||
.schedule(Box::new(move || {
|
||||
Box::pin(async move {
|
||||
match client.fetch(&coords).await {
|
||||
Ok(data) => state
|
||||
.process_tile(request_id, data.into_boxed_slice())
|
||||
.unwrap(),
|
||||
Err(e) => {
|
||||
log::error!("{:?}", &e);
|
||||
state.tile_unavailable(&coords, request_id).unwrap()
|
||||
}
|
||||
}
|
||||
})
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
} else {
|
||||
Ok(true)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,21 @@ impl<V, I> OverAlignedVertexBuffer<V, I> {
|
||||
usable_indices: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_slices(vertices: &[V], indices: &[I], usable_indices: u32) -> Self
|
||||
where
|
||||
V: Copy,
|
||||
I: Copy,
|
||||
{
|
||||
// FIXME (wasm-executor), make this fn not needed
|
||||
let mut buffers = VertexBuffers::with_capacity(0, 0);
|
||||
buffers.vertices = Vec::from(vertices);
|
||||
buffers.indices = Vec::from(indices);
|
||||
Self {
|
||||
buffer: buffers,
|
||||
usable_indices,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Pod, I: Pod> From<VertexBuffers<V, I>> for OverAlignedVertexBuffer<V, I> {
|
||||
|
||||
@ -64,7 +64,7 @@ impl<I: std::ops::Add + From<lyon::tessellation::VertexId> + MaxIndex> ZeroTesse
|
||||
&StrokeOptions::tolerance(DEFAULT_TOLERANCE),
|
||||
&mut BuffersBuilder::new(&mut self.buffer, VertexConstructor {}),
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: Remove unwrap
|
||||
}
|
||||
|
||||
fn end(&mut self, close: bool) {
|
||||
@ -83,7 +83,7 @@ impl<I: std::ops::Add + From<lyon::tessellation::VertexId> + MaxIndex> ZeroTesse
|
||||
&FillOptions::tolerance(DEFAULT_TOLERANCE).with_fill_rule(FillRule::NonZero),
|
||||
&mut BuffersBuilder::new(&mut self.buffer, VertexConstructor {}),
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap(); // TODO: Remove unwrap
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
//! Utilities for the window system.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
use crate::{HttpClient, InteractiveMapSchedule, ScheduleMethod};
|
||||
use crate::{Environment, HttpClient, InteractiveMapSchedule};
|
||||
|
||||
/// Window of a certain [`WindowSize`]. This can either be a proper window or a headless one.
|
||||
pub trait MapWindow {
|
||||
@ -27,13 +29,9 @@ pub trait MapWindowConfig: 'static {
|
||||
|
||||
/// The event loop is responsible for processing events and propagating them to the map renderer.
|
||||
/// Only non-headless windows use an [`EventLoop`].
|
||||
pub trait EventLoop<MWC, SM, HC>
|
||||
where
|
||||
MWC: MapWindowConfig,
|
||||
SM: ScheduleMethod,
|
||||
HC: HttpClient,
|
||||
{
|
||||
fn run(self, map_schedule: InteractiveMapSchedule<MWC, SM, HC>, max_frames: Option<u64>);
|
||||
pub trait EventLoop<E: Environment> {
|
||||
// FIXME (wasm-executor): Avoid Rc, change ownership model
|
||||
fn run(self, map_schedule: Rc<RefCell<InteractiveMapSchedule<E>>>, max_frames: Option<u64>);
|
||||
}
|
||||
|
||||
/// Window size with a width and an height in pixels.
|
||||
|
||||
@ -1,7 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "1.62"
|
||||
targets = [
|
||||
"wasm32-unknown-unknown", "x86_64-unknown-linux-gnu", "x86_64-linux-android", "aarch64-linux-android",
|
||||
"x86_64-apple-darwin", "aarch64-apple-darwin", "x86_64-apple-ios", "aarch64-apple-ios", "aarch64-apple-ios-sim"
|
||||
]
|
||||
components = [ "rust-src" ] # rust-src is required for WASM and android builds which recompile the stdlib
|
||||
|
||||
@ -37,7 +37,11 @@ js-sys = "0.3.58"
|
||||
wasm-bindgen = "0.2.81"
|
||||
wasm-bindgen-futures = "0.4.31"
|
||||
console_log = { version = "0.2.0", features = ["color"] }
|
||||
tracing-wasm = { version = "0.2.1", optional = true } # FIXME: Low quality dependency
|
||||
tracing-wasm = { version = "0.2.1", optional = true } # TODO: Low quality dependency
|
||||
# For passing Inputs in AsyncProcedureCalls
|
||||
serde_json = "1.0.85"
|
||||
bytemuck = "1.12.1" # FIXME (wasm-executor): Remove
|
||||
bytemuck_derive = "1.2.1" # FIXME (wasm-executor): Remove
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.31"
|
||||
|
||||
303
web/demo/package-lock.json
generated
303
web/demo/package-lock.json
generated
@ -12,11 +12,14 @@
|
||||
"maplibre-rs": "file:../lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.20",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-loader": "^9.2.6",
|
||||
"typescript": "^4.5.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.8.3",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.6.0"
|
||||
@ -27,6 +30,7 @@
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binaryen": "^110.0.0",
|
||||
"spectorjs": "^0.9.27",
|
||||
"wasm-feature-detect": "^1.2.11"
|
||||
},
|
||||
@ -36,10 +40,31 @@
|
||||
"esbuild": "^0.14.38",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
"typescript": "^4.5.4",
|
||||
"wasm-pack": "^0.10.2",
|
||||
"yargs": "^17.5.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@ -148,6 +173,30 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
|
||||
@ -263,9 +312,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "18.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz",
|
||||
"integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==",
|
||||
"version": "18.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.20.tgz",
|
||||
"integrity": "sha512-adzY4vLLr5Uivmx8+zfSJ5fbdgKxX8UMtjtl+17n0B1q1Nz8JEmE151vefMdpD+1gyh+77weN4qEhej/O7budQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
@ -314,6 +363,17 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/webpack": {
|
||||
"version": "5.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz",
|
||||
"integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"tapable": "^2.2.0",
|
||||
"webpack": "^5"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
@ -551,6 +611,15 @@
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
@ -645,6 +714,12 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
||||
@ -1088,6 +1163,12 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -1185,6 +1266,15 @@
|
||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@ -2424,6 +2514,12 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/maplibre-rs": {
|
||||
"resolved": "../lib",
|
||||
"link": true
|
||||
@ -3768,6 +3864,49 @@
|
||||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
@ -3788,9 +3927,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz",
|
||||
"integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==",
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
|
||||
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
@ -3874,6 +4013,12 @@
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@ -4233,9 +4378,39 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@discoveryjs/json-ext": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
|
||||
@ -4323,6 +4498,30 @@
|
||||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
|
||||
"dev": true
|
||||
},
|
||||
"@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
|
||||
"dev": true
|
||||
},
|
||||
"@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
|
||||
"dev": true
|
||||
},
|
||||
"@tsconfig/node16": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
|
||||
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/body-parser": {
|
||||
"version": "1.19.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
|
||||
@ -4438,9 +4637,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "18.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz",
|
||||
"integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==",
|
||||
"version": "18.7.20",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.20.tgz",
|
||||
"integrity": "sha512-adzY4vLLr5Uivmx8+zfSJ5fbdgKxX8UMtjtl+17n0B1q1Nz8JEmE151vefMdpD+1gyh+77weN4qEhej/O7budQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/qs": {
|
||||
@ -4489,6 +4688,17 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/webpack": {
|
||||
"version": "5.28.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz",
|
||||
"integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"tapable": "^2.2.0",
|
||||
"webpack": "^5"
|
||||
}
|
||||
},
|
||||
"@types/ws": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz",
|
||||
@ -4702,6 +4912,12 @@
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"acorn-walk": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
|
||||
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
|
||||
"dev": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "8.11.0",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
|
||||
@ -4763,6 +4979,12 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
|
||||
@ -5095,6 +5317,12 @@
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"dev": true
|
||||
},
|
||||
"create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -5167,6 +5395,12 @@
|
||||
"integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
@ -6097,17 +6331,23 @@
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"maplibre-rs": {
|
||||
"version": "file:../lib",
|
||||
"requires": {
|
||||
"@chialab/esbuild-plugin-meta-url": "^0.15.28",
|
||||
"binaryen": "^110.0.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"esbuild": "^0.14.38",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
"spectorjs": "^0.9.27",
|
||||
"typescript": "^4.5.4",
|
||||
"wasm-feature-detect": "^1.2.11",
|
||||
"wasm-pack": "^0.10.2",
|
||||
"yargs": "^17.5.1"
|
||||
}
|
||||
},
|
||||
@ -7102,6 +7342,27 @@
|
||||
"semver": "^7.3.4"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
|
||||
@ -7119,9 +7380,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz",
|
||||
"integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==",
|
||||
"version": "4.8.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
|
||||
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
|
||||
"dev": true
|
||||
},
|
||||
"unpipe": {
|
||||
@ -7173,6 +7434,12 @@
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"dev": true
|
||||
},
|
||||
"v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
|
||||
"dev": true
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
@ -7412,6 +7679,12 @@
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,11 +20,14 @@
|
||||
"maplibre-rs": "file:../lib"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^18.7.20",
|
||||
"@types/webpack": "^5.28.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"ts-loader": "^9.2.6",
|
||||
"typescript": "^4.5.4",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.8.3",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.6.0"
|
||||
|
||||
@ -6,5 +6,10 @@
|
||||
"target": "es5",
|
||||
"allowJs": true,
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,17 @@
|
||||
const path = require("path");
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
//const CopyPlugin = require("copy-webpack-plugin");
|
||||
const webpack = require("webpack");
|
||||
import * as path from 'path';
|
||||
import * as webpack from 'webpack';
|
||||
import * as HtmlWebpackPlugin from 'html-webpack-plugin';
|
||||
|
||||
let dist = path.join(__dirname, 'dist/');
|
||||
module.exports = (env) => ({
|
||||
const config: (env: any) => webpack.Configuration = (env) => ({
|
||||
mode: "development",
|
||||
entry: {
|
||||
main: "./index.ts",
|
||||
},
|
||||
experiments: {
|
||||
asyncWebAssembly: env.cjs ? false : true
|
||||
asyncWebAssembly: !env.cjs
|
||||
},
|
||||
stats: 'minimal',
|
||||
performance: {
|
||||
maxEntrypointSize: 400000,
|
||||
maxAssetSize: 400000000,
|
||||
@ -66,3 +66,5 @@ module.exports = (env) => ({
|
||||
}),
|
||||
]
|
||||
});
|
||||
|
||||
export default config;
|
||||
2
web/lib/.gitignore
vendored
2
web/lib/.gitignore
vendored
@ -4,6 +4,6 @@ libs/maplibre*
|
||||
|
||||
dist
|
||||
|
||||
src/wasm-pack
|
||||
src/wasm
|
||||
|
||||
.parcel-cache
|
||||
1
web/lib/@types/env/index.d.ts
vendored
1
web/lib/@types/env/index.d.ts
vendored
@ -1 +1,2 @@
|
||||
declare const WEBGL: boolean
|
||||
declare const MULTITHREADED: boolean
|
||||
|
||||
@ -2,35 +2,38 @@ import {build} from 'esbuild';
|
||||
import metaUrlPlugin from '@chialab/esbuild-plugin-meta-url';
|
||||
import inlineWorker from 'esbuild-plugin-inline-worker';
|
||||
import yargs from "yargs";
|
||||
import process from "process";
|
||||
import chokidar from "chokidar";
|
||||
import {spawnSync} from "child_process"
|
||||
import {unlink} from "fs";
|
||||
import {dirname} from "path";
|
||||
import {fileURLToPath} from "url";
|
||||
|
||||
let argv = yargs(process.argv.slice(2))
|
||||
.option('watch', {
|
||||
alias: 'w',
|
||||
type: 'boolean',
|
||||
description: 'Enable watching'
|
||||
})
|
||||
.option('release', {
|
||||
type: 'boolean',
|
||||
description: 'Release mode'
|
||||
})
|
||||
.option('webgl', {
|
||||
alias: 'g',
|
||||
type: 'boolean',
|
||||
description: 'Enable webgl'
|
||||
})
|
||||
.option('multithreaded', {
|
||||
type: 'boolean',
|
||||
description: 'Enable multithreaded support'
|
||||
})
|
||||
.option('esm', {
|
||||
alias: 'e',
|
||||
type: 'boolean',
|
||||
description: 'Enable esm'
|
||||
})
|
||||
.option('cjs', {
|
||||
alias: 'c',
|
||||
type: 'boolean',
|
||||
description: 'Enable cjs'
|
||||
})
|
||||
.option('iife', {
|
||||
alias: 'i',
|
||||
type: 'boolean',
|
||||
description: 'Enable iife'
|
||||
})
|
||||
@ -39,6 +42,8 @@ let argv = yargs(process.argv.slice(2))
|
||||
let esm = argv.esm;
|
||||
let iife = argv.iife;
|
||||
let cjs = argv.cjs;
|
||||
let release = argv.release;
|
||||
let multithreaded = argv.multithreaded;
|
||||
|
||||
if (!esm && !iife && !cjs) {
|
||||
console.warn("Enabling ESM bundling as no other bundle is enabled.")
|
||||
@ -51,19 +56,29 @@ if (webgl) {
|
||||
console.log("WebGL support enabled.")
|
||||
}
|
||||
|
||||
let baseSettings = {
|
||||
entryPoints: ['src/index.ts'],
|
||||
bundle: true,
|
||||
if (multithreaded) {
|
||||
console.log("multithreaded support enabled.")
|
||||
}
|
||||
|
||||
let baseConfig = {
|
||||
platform: "browser",
|
||||
bundle: true,
|
||||
assetNames: "assets/[name]",
|
||||
define: {"WEBGL": `${webgl}`},
|
||||
define: {
|
||||
WEBGL: `${webgl}`,
|
||||
MULTITHREADED: `${multithreaded}`
|
||||
},
|
||||
}
|
||||
|
||||
let config = {
|
||||
...baseConfig,
|
||||
entryPoints:['src/index.ts'],
|
||||
incremental: argv.watch,
|
||||
plugins: [
|
||||
inlineWorker({
|
||||
format: "cjs", platform: "browser",
|
||||
...baseConfig,
|
||||
format: "cjs",
|
||||
target: 'es2022',
|
||||
bundle: true,
|
||||
assetNames: "assets/[name]",
|
||||
}),
|
||||
metaUrlPlugin()
|
||||
],
|
||||
@ -98,8 +113,8 @@ const emitTypeScript = () => {
|
||||
"--",
|
||||
"-m", "es2022",
|
||||
"-outDir", outDirectory,
|
||||
"--declaration",
|
||||
"--emitDeclarationOnly"
|
||||
|
||||
], {
|
||||
cwd: '.',
|
||||
stdio: 'inherit',
|
||||
@ -107,36 +122,74 @@ const emitTypeScript = () => {
|
||||
|
||||
if (child.status !== 0) {
|
||||
console.error("Failed to execute tsc")
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Do not continue if one step fails
|
||||
const wasmPack = () => {
|
||||
let outDirectory = `${getLibDirectory()}/src/wasm-pack`;
|
||||
let outDirectory = `${getLibDirectory()}/src/wasm`;
|
||||
let profile = release ? "wasm-release" : "wasm-dev"
|
||||
|
||||
let child = spawnSync('npm', ["exec",
|
||||
"wasm-pack","--",
|
||||
// language=toml
|
||||
let multithreaded_config = `target.wasm32-unknown-unknown.rustflags = [
|
||||
# Enables features which are required for shared-memory
|
||||
"-C", "target-feature=+atomics,+bulk-memory,+mutable-globals",
|
||||
# Enables the possibility to import memory into wasm.
|
||||
# Without --shared-memory it is not possible to use shared WebAssembly.Memory.
|
||||
# Set maximum memory to 200MB
|
||||
"-C", "link-args=--shared-memory --import-memory --max-memory=209715200"
|
||||
]`
|
||||
|
||||
let cargo = spawnSync('cargo', [
|
||||
...(multithreaded ? ["--config", multithreaded_config] : []),
|
||||
"build",
|
||||
"--out-name", "index",
|
||||
"--out-dir", outDirectory,
|
||||
getWebDirectory(),
|
||||
"--target", "web",
|
||||
"--",
|
||||
"--features", `${webgl ? "web-webgl" : ""}`,
|
||||
"-Z", "build-std=std,panic_abort"
|
||||
"-p", "web", "--lib",
|
||||
"--target", "wasm32-unknown-unknown",
|
||||
"--profile", profile,
|
||||
"--features", `${webgl ? "web-webgl," : ""}`,
|
||||
...(multithreaded ? ["-Z", "build-std=std,panic_abort"] : []),
|
||||
], {
|
||||
cwd: '.',
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
if (child.status !== 0) {
|
||||
console.error("Failed to execute wasm-pack")
|
||||
if (cargo.status !== 0) {
|
||||
console.error("Failed to execute cargo build")
|
||||
}
|
||||
|
||||
let wasmbindgen = spawnSync('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) {
|
||||
console.error("Failed to execute wasm-bindgen")
|
||||
}
|
||||
|
||||
if (release) {
|
||||
console.log("Running wasm-opt")
|
||||
let wasmOpt = spawnSync('npm', ["exec",
|
||||
"wasm-opt", "--",
|
||||
`${outDirectory}/maplibre_bg.wasm`,
|
||||
"-o", `${outDirectory}/maplibre_bg.wasm`,
|
||||
"-O"
|
||||
], {
|
||||
cwd: '.',
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
if (wasmOpt.status !== 0) {
|
||||
console.error("Failed to execute wasm-opt")
|
||||
}
|
||||
}
|
||||
|
||||
// Having package.json within another npm package is not supported. Remove that.
|
||||
unlink(`${getLibDirectory()}/src/wasm-pack/package.json`, (err) => {
|
||||
if (err) throw err;
|
||||
})
|
||||
}
|
||||
|
||||
const watchResult = async (result) => {
|
||||
@ -149,17 +202,22 @@ const watchResult = async (result) => {
|
||||
});
|
||||
|
||||
const update = async (path) => {
|
||||
console.log(`Updating: ${path}`)
|
||||
if (path.endsWith(".rs")) {
|
||||
console.log("Rebuilding Rust...")
|
||||
wasmPack();
|
||||
try {
|
||||
console.log(`Updating: ${path}`)
|
||||
if (path.endsWith(".rs")) {
|
||||
console.log("Rebuilding Rust...")
|
||||
wasmPack();
|
||||
}
|
||||
|
||||
console.log("Rebuilding...")
|
||||
await result.rebuild();
|
||||
|
||||
console.log("Emitting TypeScript types...")
|
||||
emitTypeScript();
|
||||
} catch (e) {
|
||||
console.error("Error while updating:")
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
console.log("Rebuilding...")
|
||||
await result.rebuild();
|
||||
|
||||
console.log("Emitting TypeScript types...")
|
||||
emitTypeScript();
|
||||
}
|
||||
|
||||
console.log("Watching...")
|
||||
@ -171,7 +229,7 @@ const watchResult = async (result) => {
|
||||
}
|
||||
|
||||
const esbuild = async (name, globalName = undefined) => {
|
||||
let result = await build({...baseSettings, format: name, globalName, outfile: `dist/esbuild-${name}/module.js`,});
|
||||
let result = await build({...config, format: name, globalName, outfile: `dist/esbuild-${name}/module.js`,});
|
||||
|
||||
if (argv.watch) {
|
||||
console.log("Watching is enabled.")
|
||||
@ -180,7 +238,7 @@ const esbuild = async (name, globalName = undefined) => {
|
||||
}
|
||||
|
||||
const start = async () => {
|
||||
console.log("Running wasm-pack...")
|
||||
console.log("Creating WASM...")
|
||||
wasmPack();
|
||||
|
||||
if (esm) {
|
||||
|
||||
454
web/lib/package-lock.json
generated
454
web/lib/package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"binaryen": "^110.0.0",
|
||||
"spectorjs": "^0.9.27",
|
||||
"wasm-feature-detect": "^1.2.11"
|
||||
},
|
||||
@ -18,7 +19,6 @@
|
||||
"esbuild": "^0.14.38",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
"typescript": "^4.5.4",
|
||||
"wasm-pack": "^0.10.2",
|
||||
"yargs": "^17.5.1"
|
||||
}
|
||||
},
|
||||
@ -135,21 +135,6 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
@ -159,28 +144,13 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-install": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz",
|
||||
"integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"tar": "^6.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
"node_modules/binaryen": {
|
||||
"version": "110.0.0",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-110.0.0.tgz",
|
||||
"integrity": "sha512-b1rQFvnjNIfuW0UYSBgwLIrKD9PaG0iEzWXyoWLBFp/HRdvgD+7LGUnxG+/yKz1+tyiTdRm/lFRxsmYXaULIUg==",
|
||||
"bin": {
|
||||
"wasm-opt": "bin/wasm-opt",
|
||||
"wasm2js": "bin/wasm2js"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
@ -222,15 +192,6 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
@ -266,12 +227,6 @@
|
||||
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
@ -707,44 +662,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
@ -768,26 +685,6 @@
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@ -800,22 +697,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@ -894,55 +775,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz",
|
||||
"integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"mkdirp": "bin/cmd.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@ -952,15 +784,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
@ -1006,15 +829,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
@ -1060,21 +874,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
@ -1115,23 +914,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "6.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^3.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@ -1162,19 +944,6 @@
|
||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
|
||||
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
|
||||
},
|
||||
"node_modules/wasm-pack": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.3.tgz",
|
||||
"integrity": "sha512-dg1PPyp+QwWrhfHsgG12K/y5xzwfaAoK1yuVC/DUAuQsDy5JywWDuA7Y/ionGwQz+JBZVw8jknaKBnaxaJfwTA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"binary-install": "^0.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"wasm-pack": "run.js"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@ -1192,12 +961,6 @@
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
@ -1207,12 +970,6 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.5.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
|
||||
@ -1319,47 +1076,16 @@
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.4",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"dev": true
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true
|
||||
},
|
||||
"binary-install": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/binary-install/-/binary-install-0.1.1.tgz",
|
||||
"integrity": "sha512-DqED0D/6LrS+BHDkKn34vhRqOGjy5gTMgvYZsGK2TpNbdPuz4h+MRlNgGv5QBRd7pWq/jylM4eKNCizgAq3kNQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"axios": "^0.21.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"tar": "^6.1.0"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
"binaryen": {
|
||||
"version": "110.0.0",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-110.0.0.tgz",
|
||||
"integrity": "sha512-b1rQFvnjNIfuW0UYSBgwLIrKD9PaG0iEzWXyoWLBFp/HRdvgD+7LGUnxG+/yKz1+tyiTdRm/lFRxsmYXaULIUg=="
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
@ -1386,12 +1112,6 @@
|
||||
"readdirp": "~3.6.0"
|
||||
}
|
||||
},
|
||||
"chownr": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
@ -1424,12 +1144,6 @@
|
||||
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||
"dev": true
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
|
||||
@ -1657,27 +1371,6 @@
|
||||
"path-exists": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
|
||||
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
|
||||
"dev": true
|
||||
},
|
||||
"fs-minipass": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minipass": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
||||
"dev": true
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
@ -1691,20 +1384,6 @@
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.2.3",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
|
||||
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.1.1",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
@ -1714,22 +1393,6 @@
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
@ -1784,55 +1447,12 @@
|
||||
"semver": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"minipass": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz",
|
||||
"integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"minizlib": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
|
||||
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minipass": "^3.0.0",
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
@ -1863,12 +1483,6 @@
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
@ -1899,15 +1513,6 @@
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
|
||||
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
@ -1939,20 +1544,6 @@
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tar": {
|
||||
"version": "6.1.11",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz",
|
||||
"integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chownr": "^2.0.0",
|
||||
"fs-minipass": "^2.0.0",
|
||||
"minipass": "^3.0.0",
|
||||
"minizlib": "^2.1.1",
|
||||
"mkdirp": "^1.0.3",
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@ -1973,15 +1564,6 @@
|
||||
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.2.11.tgz",
|
||||
"integrity": "sha512-HUqwaodrQGaZgz1lZaNioIkog9tkeEJjrM3eq4aUL04whXOVDRc/o2EGb/8kV0QX411iAYWEqq7fMBmJ6dKS6w=="
|
||||
},
|
||||
"wasm-pack": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.10.3.tgz",
|
||||
"integrity": "sha512-dg1PPyp+QwWrhfHsgG12K/y5xzwfaAoK1yuVC/DUAuQsDy5JywWDuA7Y/ionGwQz+JBZVw8jknaKBnaxaJfwTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"binary-install": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@ -1993,24 +1575,12 @@
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.5.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz",
|
||||
|
||||
@ -16,16 +16,16 @@
|
||||
"main": "dist/esbuild-cjs/main.js",
|
||||
"types": "dist/ts/index.d.ts",
|
||||
"dependencies": {
|
||||
"binaryen": "^110.0.0",
|
||||
"spectorjs": "^0.9.27",
|
||||
"wasm-feature-detect": "^1.2.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chokidar": "^3.5.3",
|
||||
"@chialab/esbuild-plugin-meta-url": "^0.15.28",
|
||||
"chokidar": "^3.5.3",
|
||||
"esbuild": "^0.14.38",
|
||||
"esbuild-plugin-inline-worker": "^0.1.1",
|
||||
"typescript": "^4.5.4",
|
||||
"wasm-pack": "^0.10.2",
|
||||
"yargs": "^17.5.1"
|
||||
},
|
||||
"repository": {
|
||||
|
||||
73
web/lib/src/browser.ts
Normal file
73
web/lib/src/browser.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import {
|
||||
bigInt,
|
||||
bulkMemory,
|
||||
exceptions,
|
||||
multiValue,
|
||||
mutableGlobals,
|
||||
referenceTypes,
|
||||
saturatedFloatToInt,
|
||||
signExtensions,
|
||||
simd,
|
||||
tailCall,
|
||||
threads
|
||||
} from "wasm-feature-detect"
|
||||
|
||||
|
||||
export const checkRequirements = () => {
|
||||
if (MULTITHREADED) {
|
||||
if (!isSecureContext) {
|
||||
return "isSecureContext is false!"
|
||||
}
|
||||
|
||||
if (!crossOriginIsolated) {
|
||||
return "crossOriginIsolated is false! " +
|
||||
"The Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy HTTP headers are required."
|
||||
}
|
||||
}
|
||||
|
||||
if (WEBGL) {
|
||||
if (!isWebGLSupported()) {
|
||||
return "WebGL is not supported in this Browser!"
|
||||
}
|
||||
} else {
|
||||
if (!("gpu" in navigator)) {
|
||||
return "WebGPU is not supported in this Browser!"
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const isWebGLSupported = () => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.getContext("webgl")
|
||||
return true
|
||||
} catch (x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export const checkWasmFeatures = async () => {
|
||||
const checkFeature = async function (featureName: string, feature: () => Promise<boolean>) {
|
||||
let result = await feature();
|
||||
let msg = `The feature ${featureName} returned: ${result}`;
|
||||
if (result) {
|
||||
console.log(msg);
|
||||
} else {
|
||||
console.warn(msg);
|
||||
}
|
||||
}
|
||||
|
||||
await checkFeature("bulkMemory", bulkMemory);
|
||||
await checkFeature("exceptions", exceptions);
|
||||
await checkFeature("multiValue", multiValue);
|
||||
await checkFeature("mutableGlobals", mutableGlobals);
|
||||
await checkFeature("referenceTypes", referenceTypes);
|
||||
await checkFeature("saturatedFloatToInt", saturatedFloatToInt);
|
||||
await checkFeature("signExtensions", signExtensions);
|
||||
await checkFeature("simd", simd);
|
||||
await checkFeature("tailCall", tailCall);
|
||||
await checkFeature("threads", threads);
|
||||
await checkFeature("bigInt", bigInt);
|
||||
}
|
||||
6
web/lib/src/canvas.ts
Normal file
6
web/lib/src/canvas.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const preventDefaultTouchActions = () => {
|
||||
document.body.querySelectorAll("canvas").forEach(canvas => {
|
||||
canvas.addEventListener("touchstart", e => e.preventDefault())
|
||||
canvas.addEventListener("touchmove", e => e.preventDefault())
|
||||
})
|
||||
}
|
||||
@ -1,148 +1,69 @@
|
||||
import init, {create_pool_scheduler, run} from "./wasm-pack"
|
||||
import * as maplibre from "./wasm/maplibre"
|
||||
import {Spector} from "spectorjs"
|
||||
import {WebWorkerMessageType} from "./types"
|
||||
import {
|
||||
bigInt,
|
||||
bulkMemory,
|
||||
exceptions,
|
||||
multiValue,
|
||||
mutableGlobals,
|
||||
referenceTypes,
|
||||
saturatedFloatToInt,
|
||||
signExtensions,
|
||||
simd,
|
||||
tailCall,
|
||||
threads
|
||||
} from "wasm-feature-detect"
|
||||
|
||||
// @ts-ignore
|
||||
import PoolWorker from './pool.worker.js';
|
||||
|
||||
const isWebGLSupported = () => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.getContext("webgl")
|
||||
return true
|
||||
} catch (x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const checkWasmFeatures = async () => {
|
||||
const checkFeature = async function (featureName: string, feature: () => Promise<boolean>) {
|
||||
let result = await feature();
|
||||
let msg = `The feature ${featureName} returned: ${result}`;
|
||||
if (result) {
|
||||
console.log(msg);
|
||||
} else {
|
||||
console.warn(msg);
|
||||
}
|
||||
}
|
||||
|
||||
await checkFeature("bulkMemory", bulkMemory);
|
||||
await checkFeature("exceptions", exceptions);
|
||||
await checkFeature("multiValue", multiValue);
|
||||
await checkFeature("mutableGlobals", mutableGlobals);
|
||||
await checkFeature("referenceTypes", referenceTypes);
|
||||
await checkFeature("saturatedFloatToInt", saturatedFloatToInt);
|
||||
await checkFeature("signExtensions", signExtensions);
|
||||
await checkFeature("simd", simd);
|
||||
await checkFeature("tailCall", tailCall);
|
||||
await checkFeature("threads", threads);
|
||||
await checkFeature("bigInt", bigInt);
|
||||
}
|
||||
|
||||
const alertUser = (message: string) => {
|
||||
console.error(message)
|
||||
alert(message)
|
||||
}
|
||||
|
||||
const checkRequirements = () => {
|
||||
if (!isSecureContext) {
|
||||
alertUser("isSecureContext is false!")
|
||||
return false
|
||||
}
|
||||
|
||||
if (!crossOriginIsolated) {
|
||||
alertUser("crossOriginIsolated is false! " +
|
||||
"The Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy HTTP headers are required.")
|
||||
return false
|
||||
}
|
||||
|
||||
if (WEBGL) {
|
||||
if (!isWebGLSupported()) {
|
||||
alertUser("WebGL is not supported in this Browser!")
|
||||
return false
|
||||
}
|
||||
|
||||
let spector = new Spector()
|
||||
spector.displayUI()
|
||||
} else {
|
||||
if (!("gpu" in navigator)) {
|
||||
let message = "WebGPU is not supported in this Browser!"
|
||||
alertUser(message)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
const preventDefaultTouchActions = () => {
|
||||
document.body.querySelectorAll("canvas").forEach(canvas => {
|
||||
canvas.addEventListener("touchstart", e => e.preventDefault())
|
||||
canvas.addEventListener("touchmove", e => e.preventDefault())
|
||||
})
|
||||
}
|
||||
/*
|
||||
let WORKER_COUNT = 4
|
||||
const createWorker = (id: number, memory: WebAssembly.Memory) => {
|
||||
const worker = new Worker(new URL('./legacy.worker.ts', import.meta.url), {
|
||||
type: "module",
|
||||
})
|
||||
worker.postMessage({type: "init", memory} as WebWorkerMessageType)
|
||||
|
||||
return worker
|
||||
}
|
||||
|
||||
const setupLegacyWebWorker = (schedulerPtr: number, memory: WebAssembly.Memory) => {
|
||||
let workers: [number, Worker][] = Array.from(
|
||||
new Array(WORKER_COUNT).keys(),
|
||||
(id) => [new_thread_local_state(schedulerPtr), createWorker(id, memory)]
|
||||
)
|
||||
|
||||
window.schedule_tile_request = (url: string, request_id: number) => {
|
||||
const [state, worker] = workers[Math.floor(Math.random() * workers.length)]
|
||||
worker.postMessage({
|
||||
type: "fetch_tile",
|
||||
threadLocalState: state,
|
||||
url,
|
||||
request_id
|
||||
} as WebWorkerMessageType)
|
||||
}
|
||||
}*/
|
||||
import {checkRequirements, checkWasmFeatures} from "./browser";
|
||||
import {preventDefaultTouchActions} from "./canvas";
|
||||
// @ts-ignore esbuild plugin is handling this
|
||||
import MultithreadedPoolWorker from './multithreaded/multithreaded-pool.worker.js';
|
||||
// @ts-ignore esbuild plugin is handling this
|
||||
import PoolWorker from './singlethreaded/pool.worker.js';
|
||||
|
||||
export const startMapLibre = async (wasmPath: string | undefined, workerPath: string | undefined) => {
|
||||
await checkWasmFeatures()
|
||||
|
||||
if (!checkRequirements()) {
|
||||
let message = checkRequirements();
|
||||
if (message) {
|
||||
console.error(message)
|
||||
alert(message)
|
||||
return
|
||||
}
|
||||
|
||||
if (WEBGL) {
|
||||
let spector = new Spector()
|
||||
spector.displayUI()
|
||||
}
|
||||
|
||||
preventDefaultTouchActions();
|
||||
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
if (MULTITHREADED) {
|
||||
const MEMORY = 209715200; // 200MB
|
||||
const PAGES = 64 * 1024;
|
||||
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: true})
|
||||
await init(wasmPath, memory)
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY / PAGES, shared: true})
|
||||
await maplibre.default(wasmPath, memory)
|
||||
|
||||
const schedulerPtr = create_pool_scheduler(() => {
|
||||
return workerPath ? new PoolWorker(workerPath, {
|
||||
type: 'module'
|
||||
}) : PoolWorker();
|
||||
})
|
||||
maplibre.run(await maplibre.create_map(() => {
|
||||
return workerPath ? new Worker(workerPath, {
|
||||
type: 'module'
|
||||
}) : MultithreadedPoolWorker();
|
||||
}))
|
||||
} else {
|
||||
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
|
||||
await maplibre.default(wasmPath, memory);
|
||||
|
||||
// setupLegacyWebWorker(schedulerPtr, memory)
|
||||
let callbacks: {worker_callback?: (message: MessageEvent) => void} = {}
|
||||
|
||||
await run(schedulerPtr)
|
||||
let map = await maplibre.create_map(() => {
|
||||
let worker: Worker = workerPath ? new Worker(workerPath, {
|
||||
type: 'module'
|
||||
}) : PoolWorker();
|
||||
|
||||
worker.onmessage = (message: MessageEvent) => {
|
||||
callbacks.worker_callback(message)
|
||||
}
|
||||
|
||||
return worker;
|
||||
})
|
||||
|
||||
let clonedMap = maplibre.clone_map(map)
|
||||
|
||||
callbacks.worker_callback = (message) => {
|
||||
let tag = message.data[0];
|
||||
let data = new Uint8Array(message.data[1]);
|
||||
|
||||
// @ts-ignore TODO unsync_main_entry may not be defined
|
||||
maplibre.singlethreaded_main_entry(clonedMap, tag, data)
|
||||
}
|
||||
|
||||
maplibre.run(map)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
/*import init, {InitOutput, tessellate_layers} from "./wasm-pack"
|
||||
import {WebWorkerMessageType} from "./types"
|
||||
|
||||
let module: Promise<InitOutput> = null
|
||||
|
||||
onmessage = async message => {
|
||||
let messageData: WebWorkerMessageType = message.data
|
||||
console.dir(messageData)
|
||||
|
||||
switch (messageData.type) {
|
||||
case "init":
|
||||
if (module != null) {
|
||||
return
|
||||
}
|
||||
module = init(undefined, messageData.memory)
|
||||
break
|
||||
case "fetch_tile":
|
||||
let {threadLocalState, url, request_id} = messageData
|
||||
await module
|
||||
|
||||
console.log("Fetching from " + self.name)
|
||||
|
||||
let result = await fetch(url)
|
||||
let buffer = await result.arrayBuffer()
|
||||
|
||||
tessellate_layers(threadLocalState, request_id, new Uint8Array(buffer))
|
||||
break
|
||||
default:
|
||||
console.warn("WebWorker received unknown message!")
|
||||
break
|
||||
}
|
||||
}*/
|
||||
@ -1,7 +1,7 @@
|
||||
import init, {child_entry_point} from "./wasm-pack"
|
||||
import * as maplibre from "../wasm/maplibre"
|
||||
|
||||
onmessage = async message => {
|
||||
const initialised = init(message.data[0], message.data[1]).catch(err => {
|
||||
const initialised = maplibre.default(message.data[0], message.data[1]).catch(err => {
|
||||
// Propagate to main `onerror`:
|
||||
setTimeout(() => {
|
||||
throw err;
|
||||
@ -13,6 +13,7 @@ onmessage = async message => {
|
||||
self.onmessage = async message => {
|
||||
// This will queue further commands up until the module is fully initialised:
|
||||
await initialised;
|
||||
child_entry_point(message.data);
|
||||
// @ts-ignore TODO may not exist
|
||||
await maplibre.multithreaded_worker_entry(message.data);
|
||||
};
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
/*
|
||||
import {registerRoute} from 'workbox-routing';
|
||||
import {CacheFirst} from 'workbox-strategies';
|
||||
import {CacheableResponsePlugin} from 'workbox-cacheable-response';
|
||||
|
||||
registerRoute(
|
||||
({url}) => url.pathname.endsWith('pbf'),
|
||||
new CacheFirst({
|
||||
cacheName: 'pbf-cache',
|
||||
plugins: [
|
||||
new CacheableResponsePlugin({
|
||||
statuses: [0, 200],
|
||||
})
|
||||
]
|
||||
})
|
||||
);
|
||||
*/
|
||||
23
web/lib/src/singlethreaded/pool.worker.ts
Normal file
23
web/lib/src/singlethreaded/pool.worker.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import * as maplibre from "../wasm/maplibre"
|
||||
|
||||
onmessage = async message => {
|
||||
const memory = new WebAssembly.Memory({initial: 1024, shared: false})
|
||||
let module = message.data[0];
|
||||
const initialised = maplibre.default(module, memory).catch(err => {
|
||||
// Propagate to main `onerror`:
|
||||
setTimeout(() => {
|
||||
throw err;
|
||||
});
|
||||
// Rethrow to keep promise rejected and prevent execution of further commands:
|
||||
throw err;
|
||||
});
|
||||
|
||||
self.onmessage = async message => {
|
||||
// This will queue further commands up until the module is fully initialised:
|
||||
await initialised;
|
||||
let procedure_ptr = message.data[0];
|
||||
let input = message.data[1];
|
||||
// @ts-ignore TODO
|
||||
await maplibre.singlethreaded_worker_entry(procedure_ptr, input);
|
||||
};
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
export type WebWorkerMessageType = {
|
||||
type: 'init',
|
||||
memory: WebAssembly.Memory
|
||||
} | {
|
||||
type: 'fetch_tile',
|
||||
threadLocalState: number,
|
||||
url: string,
|
||||
request_id: number,
|
||||
}
|
||||
@ -9,7 +9,7 @@ pub struct WebError(pub String);
|
||||
impl From<JsValue> for WebError {
|
||||
fn from(maybe_error: JsValue) -> Self {
|
||||
assert!(maybe_error.is_instance_of::<JSError>());
|
||||
let error: JSError = maybe_error.dyn_into().unwrap();
|
||||
WebError(error.message().as_string().unwrap())
|
||||
let error: JSError = maybe_error.dyn_into().unwrap(); // TODO: Remove unwrap
|
||||
WebError(error.message().as_string().unwrap()) // TODO: remove unwrap
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
use std::panic;
|
||||
#![feature(allocator_api, new_uninit)]
|
||||
|
||||
use maplibre::{io::scheduler::Scheduler, MapBuilder};
|
||||
use maplibre_winit::winit::WinitMapWindowConfig;
|
||||
use std::{borrow::BorrowMut, cell::RefCell, mem, ops::Deref, panic, rc::Rc};
|
||||
|
||||
use maplibre::{io::scheduler::NopScheduler, Map, MapBuilder};
|
||||
use maplibre_winit::winit::{WinitEnvironment, WinitMapWindowConfig};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::platform::{
|
||||
http_client::WHATWGFetchHttpClient, schedule_method::WebWorkerPoolScheduleMethod,
|
||||
};
|
||||
use crate::platform::http_client::WHATWGFetchHttpClient;
|
||||
|
||||
mod error;
|
||||
mod platform;
|
||||
@ -38,31 +38,76 @@ pub fn wasm_bindgen_start() {
|
||||
enable_tracing();
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn create_pool_scheduler(
|
||||
new_worker: js_sys::Function,
|
||||
) -> *mut Scheduler<WebWorkerPoolScheduleMethod> {
|
||||
let scheduler = Box::new(Scheduler::new(WebWorkerPoolScheduleMethod::new(new_worker)));
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
pub type MapType = Map<
|
||||
WinitEnvironment<
|
||||
NopScheduler,
|
||||
WHATWGFetchHttpClient,
|
||||
platform::singlethreaded::transferables::LinearTransferables,
|
||||
platform::singlethreaded::apc::PassingAsyncProcedureCall,
|
||||
>,
|
||||
>;
|
||||
|
||||
Box::into_raw(scheduler)
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub type MapType = Map<
|
||||
WinitEnvironment<
|
||||
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler,
|
||||
WHATWGFetchHttpClient,
|
||||
maplibre::io::transferables::DefaultTransferables,
|
||||
maplibre::io::apc::SchedulerAsyncProcedureCall<
|
||||
WHATWGFetchHttpClient,
|
||||
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler,
|
||||
>,
|
||||
>,
|
||||
>;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn create_map(new_worker: js_sys::Function) -> u32 {
|
||||
// Either call forget or the main loop to keep worker loop alive
|
||||
let mut builder = MapBuilder::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(WHATWGFetchHttpClient::new());
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
{
|
||||
builder = builder
|
||||
.with_apc(maplibre::io::apc::SchedulerAsyncProcedureCall::new(
|
||||
WHATWGFetchHttpClient::new(),
|
||||
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(
|
||||
new_worker.clone(),
|
||||
),
|
||||
))
|
||||
.with_scheduler(
|
||||
platform::multithreaded::pool_scheduler::WebWorkerPoolScheduler::new(new_worker),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
{
|
||||
builder = builder
|
||||
.with_apc(platform::singlethreaded::apc::PassingAsyncProcedureCall::new(new_worker, 4))
|
||||
.with_scheduler(NopScheduler);
|
||||
}
|
||||
|
||||
let map: MapType = builder.build().initialize().await;
|
||||
|
||||
Rc::into_raw(Rc::new(RefCell::new(map))) as u32
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub async fn run(scheduler_ptr: *mut Scheduler<WebWorkerPoolScheduleMethod>) {
|
||||
let scheduler: Box<Scheduler<WebWorkerPoolScheduleMethod>> =
|
||||
unsafe { Box::from_raw(scheduler_ptr) };
|
||||
pub unsafe fn clone_map(map_ptr: *const RefCell<MapType>) -> *const RefCell<MapType> {
|
||||
let mut map = Rc::from_raw(map_ptr);
|
||||
let rc = map.clone();
|
||||
let cloned = Rc::into_raw(rc);
|
||||
mem::forget(map);
|
||||
cloned
|
||||
}
|
||||
|
||||
// Either call forget or the main loop to keep worker loop alive
|
||||
MapBuilder::new()
|
||||
.with_map_window_config(WinitMapWindowConfig::new("maplibre".to_string()))
|
||||
.with_http_client(WHATWGFetchHttpClient::new())
|
||||
.with_existing_scheduler(*scheduler)
|
||||
.build()
|
||||
.initialize()
|
||||
.await
|
||||
.run();
|
||||
#[wasm_bindgen]
|
||||
pub unsafe fn run(map_ptr: *const RefCell<MapType>) {
|
||||
let mut map = Rc::from_raw(map_ptr);
|
||||
|
||||
// std::mem::forget(scheduler);
|
||||
map.deref().borrow().run();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -23,12 +23,12 @@ impl WHATWGFetchHttpClient {
|
||||
// Get the global scope
|
||||
let global = js_sys::global();
|
||||
assert!(global.is_instance_of::<WorkerGlobalScope>());
|
||||
let scope = global.dyn_into::<WorkerGlobalScope>().unwrap();
|
||||
let scope = global.dyn_into::<WorkerGlobalScope>().unwrap(); // TODO: remove unwrap
|
||||
|
||||
// Call fetch on global scope
|
||||
let maybe_response = JsFuture::from(scope.fetch_with_request(&request)).await?;
|
||||
assert!(maybe_response.is_instance_of::<Response>());
|
||||
let response: Response = maybe_response.dyn_into().unwrap();
|
||||
let response: Response = maybe_response.dyn_into().unwrap(); // TODO: remove unwrap
|
||||
|
||||
// Get ArrayBuffer
|
||||
let maybe_array_buffer = JsFuture::from(response.array_buffer()?).await?;
|
||||
@ -39,7 +39,7 @@ impl WHATWGFetchHttpClient {
|
||||
let maybe_array_buffer = Self::fetch_array_buffer(url).await?;
|
||||
|
||||
assert!(maybe_array_buffer.is_instance_of::<ArrayBuffer>());
|
||||
let array_buffer: ArrayBuffer = maybe_array_buffer.dyn_into().unwrap();
|
||||
let array_buffer: ArrayBuffer = maybe_array_buffer.dyn_into().unwrap(); // TODO: remove unwrap
|
||||
|
||||
// Copy data to Vec<u8>
|
||||
let buffer: Uint8Array = Uint8Array::new(&array_buffer);
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
use maplibre::{
|
||||
coords::TileCoords,
|
||||
io::{scheduler::Scheduler, TileRequestID},
|
||||
stages::SharedThreadState,
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use super::schedule_method::WebWorkerPoolScheduleMethod;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
fn schedule_tile_request(url: &str, request_id: u32);
|
||||
}
|
||||
|
||||
// FIXME
|
||||
/*#[wasm_bindgen]
|
||||
pub fn new_thread_local_state(scheduler_ptr: *mut Scheduler) -> *mut SharedThreadState {
|
||||
let scheduler: Box<Scheduler> = unsafe { Box::from_raw(scheduler_ptr) };
|
||||
let state = Box::new(scheduler.new_thread_local_state());
|
||||
let state_ptr = Box::into_raw(state);
|
||||
// Call forget such that scheduler does not get deallocated
|
||||
std::mem::forget(scheduler);
|
||||
return state_ptr;
|
||||
}*/
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn new_thread_local_state(_scheduler_ptr: *mut Scheduler<WebWorkerPoolScheduleMethod>) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn tessellate_layers(state_ptr: *mut SharedThreadState, request_id: u32, data: Box<[u8]>) {
|
||||
let state: Box<SharedThreadState> = unsafe { Box::from_raw(state_ptr) };
|
||||
|
||||
state.process_tile(request_id, data).unwrap();
|
||||
|
||||
// Call forget such that scheduler does not get deallocated
|
||||
std::mem::forget(state);
|
||||
}
|
||||
|
||||
pub fn request_tile(request_id: TileRequestID, coords: TileCoords) {
|
||||
schedule_tile_request(
|
||||
format!(
|
||||
"https://maps.tuerantuer.org/europe_germany/{z}/{x}/{y}.pbf",
|
||||
x = coords.x,
|
||||
y = coords.y,
|
||||
z = coords.z,
|
||||
)
|
||||
.as_str(),
|
||||
request_id,
|
||||
)
|
||||
}
|
||||
@ -1,3 +1,10 @@
|
||||
use std::future::Future;
|
||||
|
||||
use maplibre::error::Error;
|
||||
pub mod http_client;
|
||||
pub mod pool;
|
||||
pub mod schedule_method;
|
||||
|
||||
#[cfg(target_feature = "atomics")]
|
||||
pub mod multithreaded;
|
||||
|
||||
#[cfg(not(target_feature = "atomics"))]
|
||||
pub mod singlethreaded;
|
||||
|
||||
2
web/src/platform/multithreaded/mod.rs
Normal file
2
web/src/platform/multithreaded/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod pool;
|
||||
pub mod pool_scheduler;
|
||||
@ -2,7 +2,7 @@
|
||||
//! web workers which can be used to execute work.
|
||||
//! Adopted from [wasm-bindgen example](https://github.com/rustwasm/wasm-bindgen/blob/0eba2efe45801b71f8873bc368c58a8ed8e894ff/examples/raytrace-parallel/src/pool.rs)
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use std::{borrow::BorrowMut, cell::RefCell, future::Future, rc::Rc};
|
||||
|
||||
use js_sys::Promise;
|
||||
use rand::prelude::*;
|
||||
@ -23,7 +23,6 @@ pub struct WorkerPool {
|
||||
|
||||
struct PoolState {
|
||||
workers: RefCell<Vec<Worker>>,
|
||||
callback: Closure<dyn FnMut(Event)>,
|
||||
}
|
||||
|
||||
struct Work {
|
||||
@ -46,9 +45,6 @@ impl WorkerPool {
|
||||
new_worker,
|
||||
state: Rc::new(PoolState {
|
||||
workers: RefCell::new(Vec::with_capacity(initial)),
|
||||
callback: Closure::wrap(Box::new(|event: Event| {
|
||||
log::error!("unhandled event: {}", event.type_());
|
||||
}) as Box<dyn FnMut(Event)>),
|
||||
}),
|
||||
};
|
||||
for _ in 0..initial {
|
||||
@ -95,13 +91,13 @@ impl WorkerPool {
|
||||
/// message is sent to it.
|
||||
fn worker(&self) -> Result<Worker, JsValue> {
|
||||
let workers = self.state.workers.borrow();
|
||||
let result = match workers.choose(&mut rand::thread_rng()) {
|
||||
let result = match workers.choose(&mut thread_rng()) {
|
||||
Some(worker) => Some(worker),
|
||||
None => None,
|
||||
};
|
||||
|
||||
if result.is_none() {
|
||||
self.spawn();
|
||||
self.spawn()?;
|
||||
}
|
||||
|
||||
match result {
|
||||
@ -124,13 +120,13 @@ impl WorkerPool {
|
||||
/// message is sent to it.
|
||||
pub fn execute(&self, f: impl (FnOnce() -> Promise) + Send + 'static) -> Result<(), JsValue> {
|
||||
let worker = self.worker()?;
|
||||
let work = Box::new(Work { func: Box::new(f) });
|
||||
let ptr = Box::into_raw(work);
|
||||
match worker.post_message(&JsValue::from(ptr as u32)) {
|
||||
let work = Work { func: Box::new(f) };
|
||||
let work_ptr = Box::into_raw(Box::new(work));
|
||||
match worker.post_message(&JsValue::from(work_ptr as u32)) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
drop(Box::from_raw(ptr));
|
||||
drop(Box::from_raw(work_ptr));
|
||||
}
|
||||
Err(e)
|
||||
}
|
||||
@ -140,23 +136,18 @@ impl WorkerPool {
|
||||
|
||||
impl PoolState {
|
||||
fn push(&self, worker: Worker) {
|
||||
//worker.set_onmessage(Some(self.callback.as_ref().unchecked_ref()));
|
||||
//worker.set_onerror(Some(self.callback.as_ref().unchecked_ref()));
|
||||
let mut workers = self.workers.borrow_mut();
|
||||
for existing_worker in workers.iter() {
|
||||
assert!(existing_worker as &JsValue != &worker as &JsValue);
|
||||
assert_ne!(existing_worker as &JsValue, &worker as &JsValue);
|
||||
}
|
||||
workers.push(worker);
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry point invoked by `worker.js`, a bit of a hack but see the "TODO" above
|
||||
/// about `worker.js` in general.
|
||||
/// Entry point invoked by the worker.
|
||||
#[wasm_bindgen]
|
||||
pub async fn child_entry_point(ptr: u32) -> Result<(), JsValue> {
|
||||
pub async fn multithreaded_worker_entry(ptr: u32) -> Result<(), JsValue> {
|
||||
let ptr = unsafe { Box::from_raw(ptr as *mut Work) };
|
||||
//let global = js_sys::global().unchecked_into::<DedicatedWorkerGlobalScope>();
|
||||
JsFuture::from((ptr.func)()).await?;
|
||||
//global.post_message(&JsValue::undefined())?;
|
||||
Ok(())
|
||||
}
|
||||
@ -1,34 +1,35 @@
|
||||
use std::future::Future;
|
||||
|
||||
use maplibre::{error::Error, io::scheduler::ScheduleMethod};
|
||||
use log::warn;
|
||||
use maplibre::{error::Error, io::scheduler::Scheduler};
|
||||
use wasm_bindgen::{prelude::*, JsCast};
|
||||
use web_sys::Worker;
|
||||
|
||||
use super::pool::WorkerPool;
|
||||
|
||||
pub struct WebWorkerPoolScheduleMethod {
|
||||
pub struct WebWorkerPoolScheduler {
|
||||
pool: WorkerPool,
|
||||
}
|
||||
|
||||
impl WebWorkerPoolScheduleMethod {
|
||||
impl WebWorkerPoolScheduler {
|
||||
pub fn new(new_worker: js_sys::Function) -> Self {
|
||||
Self {
|
||||
pool: WorkerPool::new(
|
||||
4,
|
||||
1,
|
||||
Box::new(move || {
|
||||
new_worker
|
||||
.call0(&JsValue::undefined())
|
||||
.unwrap()
|
||||
.unwrap() // FIXME (wasm-executor): Remove unwrap
|
||||
.dyn_into::<Worker>()
|
||||
.unwrap()
|
||||
.unwrap() // FIXME (wasm-executor): remove unwrap
|
||||
}),
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap(), // FIXME (wasm-executor): Remove unwrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScheduleMethod for WebWorkerPoolScheduleMethod {
|
||||
impl Scheduler for WebWorkerPoolScheduler {
|
||||
fn schedule<T>(
|
||||
&self,
|
||||
future_factory: impl (FnOnce() -> T) + Send + 'static,
|
||||
@ -43,7 +44,7 @@ impl ScheduleMethod for WebWorkerPoolScheduleMethod {
|
||||
Ok(JsValue::undefined())
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
.unwrap(); // FIXME (wasm-executor): remove unwrap
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
245
web/src/platform/singlethreaded/apc.rs
Normal file
245
web/src/platform/singlethreaded/apc.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use std::{
|
||||
any::TypeId,
|
||||
borrow::Borrow,
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
marker::PhantomData,
|
||||
mem,
|
||||
mem::{size_of, MaybeUninit},
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
mpsc,
|
||||
mpsc::{Receiver, Sender},
|
||||
},
|
||||
};
|
||||
|
||||
use js_sys::Uint8Array;
|
||||
use maplibre::{
|
||||
environment::Environment,
|
||||
io::{
|
||||
apc::{AsyncProcedure, AsyncProcedureCall, Context, Input, Message},
|
||||
scheduler::Scheduler,
|
||||
source_client::{HttpClient, HttpSourceClient, SourceClient},
|
||||
transferables::Transferables,
|
||||
},
|
||||
};
|
||||
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
||||
use web_sys::{DedicatedWorkerGlobalScope, Worker};
|
||||
|
||||
use crate::{
|
||||
platform::singlethreaded::transferables::{
|
||||
InnerData, LinearTessellatedLayer, LinearTransferables,
|
||||
},
|
||||
MapType, WHATWGFetchHttpClient,
|
||||
};
|
||||
|
||||
type UsedTransferables = LinearTransferables;
|
||||
type UsedHttpClient = WHATWGFetchHttpClient;
|
||||
type UsedContext = PassingContext;
|
||||
|
||||
enum SerializedMessageTag {
|
||||
TileTessellated = 1,
|
||||
UnavailableLayer = 2,
|
||||
TessellatedLayer = 3,
|
||||
}
|
||||
|
||||
impl SerializedMessageTag {
|
||||
fn from_u32(tag: u32) -> Option<Self> {
|
||||
match tag {
|
||||
x if x == SerializedMessageTag::UnavailableLayer as u32 => {
|
||||
Some(SerializedMessageTag::UnavailableLayer)
|
||||
}
|
||||
x if x == SerializedMessageTag::TessellatedLayer as u32 => {
|
||||
Some(SerializedMessageTag::TessellatedLayer)
|
||||
}
|
||||
x if x == SerializedMessageTag::TileTessellated as u32 => {
|
||||
Some(SerializedMessageTag::TileTessellated)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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] {
|
||||
match self {
|
||||
Message::TileTessellated(data) => bytemuck::bytes_of(data),
|
||||
Message::UnavailableLayer(data) => bytemuck::bytes_of(data),
|
||||
Message::TessellatedLayer(data) => bytemuck::bytes_of(data.data.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize(tag: SerializedMessageTag, data: Uint8Array) -> Message<UsedTransferables> {
|
||||
match tag {
|
||||
SerializedMessageTag::TileTessellated => {
|
||||
Message::<UsedTransferables>::TileTessellated(*bytemuck::from_bytes::<
|
||||
<UsedTransferables as Transferables>::TileTessellated,
|
||||
>(&data.to_vec()))
|
||||
}
|
||||
SerializedMessageTag::UnavailableLayer => {
|
||||
Message::<UsedTransferables>::UnavailableLayer(*bytemuck::from_bytes::<
|
||||
<UsedTransferables as Transferables>::UnavailableLayer,
|
||||
>(&data.to_vec()))
|
||||
}
|
||||
SerializedMessageTag::TessellatedLayer => {
|
||||
Message::<UsedTransferables>::TessellatedLayer(LinearTessellatedLayer {
|
||||
data: unsafe {
|
||||
let mut uninit = Box::<InnerData>::new_zeroed();
|
||||
data.raw_copy_to_ptr(uninit.as_mut_ptr() as *mut u8);
|
||||
let x = uninit.assume_init();
|
||||
|
||||
x
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tag(&self) -> SerializedMessageTag {
|
||||
match self {
|
||||
Message::TileTessellated(_) => SerializedMessageTag::TileTessellated,
|
||||
Message::UnavailableLayer(_) => SerializedMessageTag::UnavailableLayer,
|
||||
Message::TessellatedLayer(_) => SerializedMessageTag::TessellatedLayer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PassingContext {
|
||||
source_client: SourceClient<UsedHttpClient>,
|
||||
}
|
||||
|
||||
impl Context<UsedTransferables, UsedHttpClient> for PassingContext {
|
||||
fn send(&self, data: Message<UsedTransferables>) {
|
||||
let tag = data.tag();
|
||||
let serialized = data.serialize();
|
||||
|
||||
let serialized_array_buffer = js_sys::ArrayBuffer::new(serialized.len() as u32);
|
||||
let serialized_array = js_sys::Uint8Array::new(&serialized_array_buffer);
|
||||
unsafe {
|
||||
serialized_array.set(&Uint8Array::view(serialized), 0);
|
||||
}
|
||||
|
||||
let global = js_sys::global().unchecked_into::<DedicatedWorkerGlobalScope>(); // FIXME (wasm-executor): Remove unchecked
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&JsValue::from(tag as u32));
|
||||
array.push(&serialized_array_buffer);
|
||||
global.post_message(&array).unwrap(); // FIXME (wasm-executor) Remove unwrap
|
||||
}
|
||||
|
||||
fn source_client(&self) -> &SourceClient<UsedHttpClient> {
|
||||
&self.source_client
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PassingAsyncProcedureCall {
|
||||
new_worker: Box<dyn Fn() -> Worker>,
|
||||
workers: Vec<Worker>,
|
||||
|
||||
received: Vec<Message<UsedTransferables>>,
|
||||
}
|
||||
|
||||
impl PassingAsyncProcedureCall {
|
||||
pub fn new(new_worker: js_sys::Function, initial_workers: u8) -> Self {
|
||||
let create_new_worker = Box::new(move || {
|
||||
new_worker
|
||||
.call0(&JsValue::undefined())
|
||||
.unwrap() // FIXME (wasm-executor): Remove unwrap
|
||||
.dyn_into::<Worker>()
|
||||
.unwrap() // FIXME (wasm-executor): Remove unwrap
|
||||
});
|
||||
|
||||
let workers = (0..initial_workers)
|
||||
.map(|_| {
|
||||
let worker: Worker = create_new_worker();
|
||||
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&wasm_bindgen::module());
|
||||
worker.post_message(&array).unwrap(); // FIXME (wasm-executor): Remove unwrap
|
||||
worker
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Self {
|
||||
new_worker: create_new_worker,
|
||||
workers,
|
||||
received: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncProcedureCall<UsedTransferables, UsedHttpClient> for PassingAsyncProcedureCall {
|
||||
type Context = UsedContext;
|
||||
|
||||
fn receive(&mut self) -> Option<Message<UsedTransferables>> {
|
||||
self.received.pop()
|
||||
}
|
||||
|
||||
fn schedule(&self, input: Input, procedure: AsyncProcedure<Self::Context>) {
|
||||
let procedure_ptr = procedure as *mut AsyncProcedure<Self::Context> as u32; // FIXME (wasm-executor): is u32 fine, define an overflow safe function?
|
||||
let input = serde_json::to_string(&input).unwrap(); // FIXME (wasm-executor): Remove unwrap
|
||||
|
||||
let array = js_sys::Array::new();
|
||||
array.push(&JsValue::from(procedure_ptr));
|
||||
array.push(&JsValue::from(input));
|
||||
|
||||
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::Http(HttpSourceClient::new(WHATWGFetchHttpClient::new())),
|
||||
};
|
||||
|
||||
(procedure)(input, context).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Entry point invoked by the main thread.
|
||||
#[wasm_bindgen]
|
||||
pub unsafe fn singlethreaded_main_entry(
|
||||
map_ptr: *const RefCell<MapType>,
|
||||
type_id: u32,
|
||||
data: Uint8Array,
|
||||
) -> Result<(), JsValue> {
|
||||
// FIXME (wasm-executor): Can we make this call safe? check if it was cloned before?
|
||||
let mut map = Rc::from_raw(map_ptr);
|
||||
|
||||
let message = Message::<UsedTransferables>::deserialize(
|
||||
SerializedMessageTag::from_u32(type_id).unwrap(),
|
||||
data,
|
||||
);
|
||||
|
||||
map.deref()
|
||||
.borrow()
|
||||
.map_schedule()
|
||||
.deref()
|
||||
.borrow()
|
||||
.apc
|
||||
.deref()
|
||||
.borrow_mut()
|
||||
.received
|
||||
.push(message);
|
||||
|
||||
mem::forget(map); // FIXME (wasm-executor): Enforce this somehow
|
||||
|
||||
Ok(())
|
||||
}
|
||||
2
web/src/platform/singlethreaded/mod.rs
Normal file
2
web/src/platform/singlethreaded/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod apc;
|
||||
pub mod transferables;
|
||||
158
web/src/platform/singlethreaded/transferables.rs
Normal file
158
web/src/platform/singlethreaded/transferables.rs
Normal file
@ -0,0 +1,158 @@
|
||||
use bytemuck::{TransparentWrapper, Zeroable};
|
||||
use bytemuck_derive::{Pod, Zeroable};
|
||||
use maplibre::{
|
||||
benchmarking::tessellation::{IndexDataType, OverAlignedVertexBuffer},
|
||||
coords::WorldTileCoords,
|
||||
io::{
|
||||
tile_repository::StoredLayer,
|
||||
transferables::{TessellatedLayer, TileTessellated, Transferables, UnavailableLayer},
|
||||
},
|
||||
render::ShaderVertex,
|
||||
tile::Layer,
|
||||
};
|
||||
|
||||
// FIXME (wasm-executor): properly do this!, fix this whole file
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(transparent)]
|
||||
pub struct WrapperWorldTileCoords(WorldTileCoords);
|
||||
unsafe impl TransparentWrapper<WorldTileCoords> for WrapperWorldTileCoords {}
|
||||
unsafe impl bytemuck::Zeroable for WrapperWorldTileCoords {}
|
||||
unsafe impl bytemuck::Pod for WrapperWorldTileCoords {}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct LongVertexShader([ShaderVertex; 15000]);
|
||||
unsafe impl TransparentWrapper<[ShaderVertex; 15000]> for LongVertexShader {}
|
||||
unsafe impl bytemuck::Zeroable for LongVertexShader {}
|
||||
unsafe impl bytemuck::Pod for LongVertexShader {}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct LongIndices([IndexDataType; 40000]);
|
||||
unsafe impl TransparentWrapper<[IndexDataType; 40000]> for LongIndices {}
|
||||
unsafe impl bytemuck::Zeroable for LongIndices {}
|
||||
unsafe impl bytemuck::Pod for LongIndices {}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct LinearTileTessellated {
|
||||
pub coords: WrapperWorldTileCoords,
|
||||
}
|
||||
|
||||
impl TileTessellated for LinearTileTessellated {
|
||||
fn new(coords: WorldTileCoords) -> Self {
|
||||
Self {
|
||||
coords: WrapperWorldTileCoords::wrap(coords),
|
||||
}
|
||||
}
|
||||
|
||||
fn coords(&self) -> &WorldTileCoords {
|
||||
WrapperWorldTileCoords::peel_ref(&self.coords)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct LinearUnavailableLayer {
|
||||
pub coords: WrapperWorldTileCoords,
|
||||
pub layer_name: [u8; 32],
|
||||
}
|
||||
|
||||
impl UnavailableLayer for LinearUnavailableLayer {
|
||||
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: WrapperWorldTileCoords::wrap(coords),
|
||||
layer_name: new_layer_name,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
StoredLayer::UnavailableLayer {
|
||||
coords: WrapperWorldTileCoords::peel(self.coords),
|
||||
layer_name: String::from_utf8(Vec::from(self.layer_name)).unwrap(), // FIXME (wasm-executor): Remove unwrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
pub struct InnerData {
|
||||
pub coords: WrapperWorldTileCoords,
|
||||
pub layer_name: [u8; 32],
|
||||
pub layer_name_len: usize,
|
||||
pub vertices: LongVertexShader,
|
||||
pub vertices_len: usize,
|
||||
pub indices: LongIndices,
|
||||
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 LinearTessellatedLayer {
|
||||
pub data: Box<InnerData>,
|
||||
}
|
||||
|
||||
impl TessellatedLayer for LinearTessellatedLayer {
|
||||
fn new(
|
||||
coords: WorldTileCoords,
|
||||
buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
|
||||
feature_indices: Vec<u32>,
|
||||
layer_data: Layer,
|
||||
) -> Self {
|
||||
let mut data = Box::new(InnerData {
|
||||
coords: WrapperWorldTileCoords::wrap(coords),
|
||||
|
||||
layer_name: [0; 32],
|
||||
layer_name_len: layer_data.name.len(),
|
||||
|
||||
vertices: LongVertexShader::wrap([ShaderVertex::zeroed(); 15000]),
|
||||
vertices_len: buffer.buffer.vertices.len(),
|
||||
|
||||
indices: LongIndices::wrap([IndexDataType::zeroed(); 40000]),
|
||||
indices_len: buffer.buffer.indices.len(),
|
||||
|
||||
usable_indices: buffer.usable_indices,
|
||||
|
||||
feature_indices: [0u32; 2048],
|
||||
feature_indices_len: feature_indices.len(),
|
||||
});
|
||||
|
||||
data.vertices.0[0..buffer.buffer.vertices.len()].clone_from_slice(&buffer.buffer.vertices);
|
||||
data.indices.0[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 }
|
||||
}
|
||||
|
||||
fn to_stored_layer(self) -> StoredLayer {
|
||||
let layer = StoredLayer::TessellatedLayer {
|
||||
coords: WrapperWorldTileCoords::peel(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.0[..self.data.vertices_len],
|
||||
&self.data.indices.0[..self.data.indices_len],
|
||||
self.data.usable_indices,
|
||||
),
|
||||
feature_indices: Vec::from(&self.data.feature_indices[..self.data.feature_indices_len]),
|
||||
};
|
||||
|
||||
layer
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LinearTransferables;
|
||||
|
||||
impl Transferables for LinearTransferables {
|
||||
type TileTessellated = LinearTileTessellated;
|
||||
type UnavailableLayer = LinearUnavailableLayer;
|
||||
type TessellatedLayer = LinearTessellatedLayer;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user