mirror of
https://github.com/maplibre/maplibre-rs.git
synced 2025-12-08 19:05:57 +00:00
Prepare JS lib
This commit is contained in:
parent
ea52e9c96d
commit
1f3423a70c
@ -1,15 +1,8 @@
|
||||
[target.wasm32-unknown-unknown]
|
||||
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",
|
||||
"--cfg=web_sys_unstable_apis"
|
||||
]
|
||||
runner = 'wasm-bindgen-test-runner'
|
||||
|
||||
|
||||
[profile.wasm-dev]
|
||||
inherits = "dev"
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -277,4 +277,4 @@ Example config in `package.json:
|
||||
|
||||
### Rollup
|
||||
|
||||
Not yet evaluated
|
||||
Not yet evaluated
|
||||
|
||||
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,6 +2,7 @@ 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 {dirname} from "path";
|
||||
@ -9,32 +10,30 @@ import {fileURLToPath} from "url";
|
||||
|
||||
let argv = yargs(process.argv.slice(2))
|
||||
.option('watch', {
|
||||
alias: 'w',
|
||||
type: 'boolean',
|
||||
description: 'Enable watching'
|
||||
})
|
||||
.option('release', {
|
||||
alias: 'r',
|
||||
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'
|
||||
})
|
||||
@ -44,6 +43,7 @@ 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.")
|
||||
@ -56,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()
|
||||
],
|
||||
@ -119,11 +129,22 @@ const wasmPack = () => {
|
||||
let outDirectory = `${getLibDirectory()}/src/wasm`;
|
||||
let profile = release ? "wasm-release" : "wasm-dev"
|
||||
|
||||
let cargo = spawnSync('cargo', ["build",
|
||||
// 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.
|
||||
"-C", "link-args=--shared-memory --import-memory",
|
||||
]`
|
||||
|
||||
let cargo = spawnSync('cargo', [
|
||||
...(multithreaded ? ["--config", multithreaded_config] : []),
|
||||
"build",
|
||||
"-p", "web", "--lib",
|
||||
"--target", "wasm32-unknown-unknown",
|
||||
"--profile", profile,
|
||||
"--features", `${webgl ? "web-webgl" : ""}`,
|
||||
"--features", `${webgl ? "web-webgl," : ""}`,
|
||||
"-Z", "build-std=std,panic_abort"
|
||||
], {
|
||||
cwd: '.',
|
||||
@ -206,7 +227,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.")
|
||||
|
||||
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);
|
||||
}
|
||||
@ -1,92 +1,12 @@
|
||||
import init, {create_pool_scheduler, run} from "./wasm/maplibre"
|
||||
import {create_pool_scheduler, run} from "./wasm/maplibre"
|
||||
import {Spector} from "spectorjs"
|
||||
import {WebWorkerMessageType} from "./worker-types"
|
||||
import {
|
||||
bigInt,
|
||||
bulkMemory,
|
||||
exceptions,
|
||||
multiValue,
|
||||
mutableGlobals,
|
||||
referenceTypes,
|
||||
saturatedFloatToInt,
|
||||
signExtensions,
|
||||
simd,
|
||||
tailCall,
|
||||
threads
|
||||
} from "wasm-feature-detect"
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-ignore esbuild plugin is handling this
|
||||
import MultithreadedPoolWorker from './multithreaded-pool.worker.js';
|
||||
// @ts-ignore esbuild plugin is handling this
|
||||
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
|
||||
}
|
||||
import {initialize} from "./module";
|
||||
import {checkRequirements, checkWasmFeatures} from "./browser";
|
||||
|
||||
const preventDefaultTouchActions = () => {
|
||||
document.body.querySelectorAll("canvas").forEach(canvas => {
|
||||
@ -98,23 +18,28 @@ const preventDefaultTouchActions = () => {
|
||||
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
|
||||
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: true})
|
||||
await init(wasmPath, memory)
|
||||
await initialize(wasmPath);
|
||||
|
||||
const schedulerPtr = create_pool_scheduler(() => {
|
||||
let worker = workerPath ? new PoolWorker(workerPath, {
|
||||
type: 'module'
|
||||
}) : PoolWorker({name: "test"});
|
||||
let CurrentWorker = MULTITHREADED ? MultithreadedPoolWorker : PoolWorker;
|
||||
|
||||
return worker;
|
||||
return workerPath ? new Worker(workerPath, {
|
||||
type: 'module'
|
||||
}) : CurrentWorker();
|
||||
})
|
||||
|
||||
await run(schedulerPtr)
|
||||
|
||||
28
web/lib/src/module.ts
Normal file
28
web/lib/src/module.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import init from "./wasm/maplibre";
|
||||
|
||||
export const initialize = async (wasmPath: string) => {
|
||||
if (MULTITHREADED) {
|
||||
await initializeSharedModule(wasmPath)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
await init(wasmPath)
|
||||
}
|
||||
}
|
||||
|
||||
export const initializeExisting = async (module: string, memory?: string) => {
|
||||
if (MULTITHREADED) {
|
||||
// @ts-ignore
|
||||
await init(module, memory)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
await init(module)
|
||||
}
|
||||
}
|
||||
|
||||
const initializeSharedModule = async (wasmPath) => {
|
||||
let MEMORY_PAGES = 16 * 1024
|
||||
|
||||
const memory = new WebAssembly.Memory({initial: 1024, maximum: MEMORY_PAGES, shared: true})
|
||||
// @ts-ignore
|
||||
await init(wasmPath, memory)
|
||||
}
|
||||
19
web/lib/src/multithreaded-pool.worker.ts
Normal file
19
web/lib/src/multithreaded-pool.worker.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {worker_entry} from "./wasm/maplibre"
|
||||
import {initializeExisting} from "./module";
|
||||
|
||||
onmessage = async message => {
|
||||
const initialised = initializeExisting(message.data[0], message.data[1]).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;
|
||||
await worker_entry(message.data);
|
||||
};
|
||||
}
|
||||
@ -1,18 +1,5 @@
|
||||
import init, {worker_entry} from "./wasm/maplibre"
|
||||
import {initializeExisting} from "./module";
|
||||
|
||||
onmessage = async message => {
|
||||
const initialised = init(message.data[0], message.data[1]).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;
|
||||
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],
|
||||
})
|
||||
]
|
||||
})
|
||||
);
|
||||
*/
|
||||
Loading…
x
Reference in New Issue
Block a user