From 3f5022630b72ed0185d15fa96231285cfd263134 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Wed, 4 Mar 2020 16:06:18 +0100 Subject: [PATCH] Create a basic example for OffscreenCanvas rendering --- examples/mvtlayer.worker.js | 75 ++++++++++++++++++++++++++++ examples/offscreen-canvas-tiles.html | 9 ++++ examples/offscreen-canvas-tiles.js | 63 +++++++++++++++++++++++ package-lock.json | 22 ++++++++ package.json | 1 + 5 files changed, 170 insertions(+) create mode 100644 examples/mvtlayer.worker.js create mode 100644 examples/offscreen-canvas-tiles.html create mode 100644 examples/offscreen-canvas-tiles.js diff --git a/examples/mvtlayer.worker.js b/examples/mvtlayer.worker.js new file mode 100644 index 0000000000..6de3774f1d --- /dev/null +++ b/examples/mvtlayer.worker.js @@ -0,0 +1,75 @@ +import VectorTileLayer from '../src/ol/layer/VectorTile.js'; +import VectorTileSource from '../src/ol/source/VectorTile.js'; +import MVT from '../src/ol/format/MVT.js'; +import {Projection} from '../src/ol/proj.js'; +import TileQueue from '../src/ol/TileQueue.js'; +import {getTilePriority as tilePriorityFunction} from '../src/ol/TileQueue.js'; + +const key = 'pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q'; + +/** @type {any} */ +const worker = self; + +let frameState; + +function getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) { + return tilePriorityFunction(frameState, tile, tileSourceKey, tileCenter, tileResolution); +} + +const layer = new VectorTileLayer({ + declutter: true, + source: new VectorTileSource({ + format: new MVT(), + url: 'https://{a-d}.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/' + + '{z}/{x}/{y}.vector.pbf?access_token=' + key + }) +}); +const tileQueue = new TileQueue(getTilePriority, function() { + worker.postMessage({type: 'render'}); +}); +const maxTotalLoading = 8; +const maxNewLoads = 2; + +const renderer = layer.getRenderer(); + +renderer.useContainer = function(target, transform, opacity) { + target.style = {}; + this.canvas = target; + this.context = target.getContext('2d'); + this.container = { + firstElementChild: target + }; + worker.postMessage({ + type: 'transform-opacity', + transform: transform, + opacity: opacity + }); +}; + +let canvas; +let rendering = false; + +worker.onmessage = function(event) { + if (rendering) { + // drop this frame + return; + } + if (event.data.canvas) { + canvas = event.data.canvas; + } else { + frameState = event.data.frameState; + frameState.tileQueue = tileQueue; + frameState.viewState.projection.__proto__ = Projection.prototype; + rendering = true; + requestAnimationFrame(function() { + renderer.renderFrame(frameState, canvas); + if (tileQueue.getTilesLoading() < maxTotalLoading) { + tileQueue.reprioritize(); // FIXME only call if view has changed + tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads); + } + rendering = false; + }); + } +}; + +export let create; diff --git a/examples/offscreen-canvas-tiles.html b/examples/offscreen-canvas-tiles.html new file mode 100644 index 0000000000..231da2f14f --- /dev/null +++ b/examples/offscreen-canvas-tiles.html @@ -0,0 +1,9 @@ +--- +layout: example.html +title: Vector tiles rendered in an offscreen canvas +shortdesc: Example of a map that delegates rendering to a worker. +docs: > + The map in this example is rendered in a web worker, using `OffscreenCanvas`. **Note:** This is currently only supported in Chrome and Edge. +tags: "worker, offscreencanvas, vector-tiles" +--- +
diff --git a/examples/offscreen-canvas-tiles.js b/examples/offscreen-canvas-tiles.js new file mode 100644 index 0000000000..af9f43e03f --- /dev/null +++ b/examples/offscreen-canvas-tiles.js @@ -0,0 +1,63 @@ +import Map from '../src/ol/Map.js'; +import View from '../src/ol/View.js'; +import Layer from '../src/ol/layer/Layer.js'; +//eslint-disable-next-line +import Worker from 'worker-loader!./mvtlayer.worker.js'; + +const mvtLayerWorker = new Worker(); + +function getCircularReplacer() { + const seen = new WeakSet(); + return function(key, value) { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return '[circular]'; + } + seen.add(value); + } + return value; + }; +} + +let container, canvas; + +const map = new Map({ + layers: [ + new Layer({ + render: function(frameState) { + if (!container) { + container = document.createElement('div'); + container.style.position = 'absolute'; + container.style.width = '100%'; + container.style.height = '100%'; + canvas = document.createElement('canvas'); + canvas.style.position = 'absolute'; + canvas.style.left = '0'; + canvas.style.transformOrigin = 'top left'; + container.appendChild(canvas); + const offscreen = canvas.transferControlToOffscreen(); + mvtLayerWorker.postMessage({ + canvas: offscreen + }, [offscreen]); + } + mvtLayerWorker.postMessage({ + frameState: JSON.parse(JSON.stringify(frameState, getCircularReplacer())) + }); + return container; + } + }) + ], + target: 'map', + view: new View({ + center: [0, 0], + zoom: 2 + }) +}); +mvtLayerWorker.addEventListener('message', message => { + if (message.data.type === 'render') { + map.render(); + } else if (canvas && message.data.type === 'transform-opacity') { + canvas.style.transform = message.data.transform; + canvas.style.opacity = message.data.opacity; + } +}); diff --git a/package-lock.json b/package-lock.json index 9bced339b2..450a9667f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12693,6 +12693,28 @@ "errno": "~0.1.7" } }, + "worker-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-2.0.0.tgz", + "integrity": "sha512-tnvNp4K3KQOpfRnD20m8xltE3eWh89Ye+5oj7wXEEHKac1P4oZ6p9oTj8/8ExqoSBnk9nu5Pr4nKfQ1hn2APJw==", + "dev": true, + "requires": { + "loader-utils": "^1.0.0", + "schema-utils": "^0.4.0" + }, + "dependencies": { + "schema-utils": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index 2fa01fd358..d9adcfa006 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,7 @@ "webpack-cli": "^3.3.2", "webpack-dev-middleware": "^3.6.2", "webpack-dev-server": "^3.3.1", + "worker-loader": "^2.0.0", "yargs": "^15.0.2" }, "eslintConfig": {