Merge pull request #9705 from ahocevar/vectortile-render-optimizations

Vector tile renderer optimizations and fixes
This commit is contained in:
Andreas Hocevar
2019-06-24 09:50:56 +02:00
committed by GitHub
4 changed files with 46 additions and 39 deletions
+9 -6
View File
@@ -6,7 +6,7 @@ import ImageState from './ImageState.js';
import {listenOnce, unlistenByKey} from './events.js'; import {listenOnce, unlistenByKey} from './events.js';
import EventType from './events/EventType.js'; import EventType from './events/EventType.js';
import {getHeight} from './extent.js'; import {getHeight} from './extent.js';
import {SAFARI} from './has.js'; import {IMAGE_DECODE} from './has.js';
/** /**
@@ -159,10 +159,7 @@ class ImageWrapper extends ImageBase {
export function listenImage(image, loadHandler, errorHandler) { export function listenImage(image, loadHandler, errorHandler) {
const img = /** @type {HTMLImageElement} */ (image); const img = /** @type {HTMLImageElement} */ (image);
// The decode function is supported by Safari but not when the image is a svg file. if (IMAGE_DECODE) {
// FIXME: remove `!SAFARI` in the test when this bug is fixed upstream.
// See: https://bugs.webkit.org/show_bug.cgi?id=198527
if (!SAFARI && img.decode) {
const promise = img.decode(); const promise = img.decode();
let listening = true; let listening = true;
const unlisten = function() { const unlisten = function() {
@@ -174,7 +171,13 @@ export function listenImage(image, loadHandler, errorHandler) {
} }
}).catch(function(error) { }).catch(function(error) {
if (listening) { if (listening) {
errorHandler(); // FIXME: Unconditionally call errorHandler() when this bug is fixed upstream:
// https://bugs.webkit.org/show_bug.cgi?id=198527
if (error.name === 'EncodingError' && error.message === 'Invalid image type.') {
loadHandler();
} else {
errorHandler();
}
} }
}); });
return unlisten; return unlisten;
+2 -2
View File
@@ -22,7 +22,7 @@ import {listen, unlistenByKey, unlisten} from './events.js';
import EventType from './events/EventType.js'; import EventType from './events/EventType.js';
import {createEmpty, clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js'; import {createEmpty, clone, createOrUpdateEmpty, equals, getForViewAndSize, isEmpty} from './extent.js';
import {TRUE} from './functions.js'; import {TRUE} from './functions.js';
import {DEVICE_PIXEL_RATIO} from './has.js'; import {DEVICE_PIXEL_RATIO, IMAGE_DECODE} from './has.js';
import LayerGroup from './layer/Group.js'; import LayerGroup from './layer/Group.js';
import {hasArea} from './size.js'; import {hasArea} from './size.js';
import {DROP} from './structs/PriorityQueue.js'; import {DROP} from './structs/PriorityQueue.js';
@@ -967,7 +967,7 @@ class PluggableMap extends BaseObject {
if (frameState) { if (frameState) {
const hints = frameState.viewHints; const hints = frameState.viewHints;
if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) { if (hints[ViewHint.ANIMATING] || hints[ViewHint.INTERACTING]) {
const lowOnFrameBudget = Date.now() - frameState.time > 8; const lowOnFrameBudget = !IMAGE_DECODE && Date.now() - frameState.time > 8;
maxTotalLoading = lowOnFrameBudget ? 0 : 8; maxTotalLoading = lowOnFrameBudget ? 0 : 8;
maxNewLoads = lowOnFrameBudget ? 0 : 2; maxNewLoads = lowOnFrameBudget ? 0 : 2;
} }
+6
View File
@@ -38,3 +38,9 @@ export const MAC = ua.indexOf('macintosh') !== -1;
* @api * @api
*/ */
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1; export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
/**
* Image.prototype.decode() is supported.
* @type {boolean}
*/
export const IMAGE_DECODE = typeof Image !== 'undefined' && Image.prototype.decode;
+29 -31
View File
@@ -7,6 +7,7 @@ import TileState from '../../TileState.js';
import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js'; import {createEmpty, equals, getIntersection, getTopLeft} from '../../extent.js';
import CanvasLayerRenderer from './Layer.js'; import CanvasLayerRenderer from './Layer.js';
import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js'; import {apply as applyTransform, compose as composeTransform, makeInverse, toString as transformToString} from '../../transform.js';
import {numberSafeCompareFunction} from '../../array.js';
/** /**
* @classdesc * @classdesc
@@ -264,15 +265,7 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
this.renderedTiles.length = 0; this.renderedTiles.length = 0;
/** @type {Array<number>} */ /** @type {Array<number>} */
let zs = Object.keys(tilesToDrawByZ).map(Number); let zs = Object.keys(tilesToDrawByZ).map(Number);
zs.sort(function(a, b) { zs.sort(numberSafeCompareFunction);
if (a === z) {
return 1;
} else if (b === z) {
return -1;
} else {
return a > b ? 1 : a < b ? -1 : 0;
}
});
let clips, clipZs, currentClip; let clips, clipZs, currentClip;
if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) { if (layerState.opacity === 1 && (!this.containerReused || tileSource.getOpaque(frameState.viewState.projection))) {
@@ -311,32 +304,37 @@ class CanvasTileLayerRenderer extends CanvasLayerRenderer {
const h = nextY - y; const h = nextY - y;
const transition = z === currentZ; const transition = z === currentZ;
if (clips && (!transition || tile.getAlpha(getUid(this), frameState.time) === 1)) { const inTransition = transition && tile.getAlpha(getUid(this), frameState.time) !== 1;
// Clip mask for regions in this tile that already filled by a higher z tile if (!inTransition) {
context.save(); if (clips) {
currentClip = [x, y, x + w, y, x + w, y + h, x, y + h]; // Clip mask for regions in this tile that already filled by a higher z tile
for (let i = 0, ii = clips.length; i < ii; ++i) { context.save();
if (z !== currentZ && currentZ < clipZs[i]) { currentClip = [x, y, x + w, y, x + w, y + h, x, y + h];
const clip = clips[i]; for (let i = 0, ii = clips.length; i < ii; ++i) {
context.beginPath(); if (z !== currentZ && currentZ < clipZs[i]) {
// counter-clockwise (outer ring) for current tile const clip = clips[i];
context.moveTo(currentClip[0], currentClip[1]); context.beginPath();
context.lineTo(currentClip[2], currentClip[3]); // counter-clockwise (outer ring) for current tile
context.lineTo(currentClip[4], currentClip[5]); context.moveTo(currentClip[0], currentClip[1]);
context.lineTo(currentClip[6], currentClip[7]); context.lineTo(currentClip[2], currentClip[3]);
// clockwise (inner ring) for higher z tile context.lineTo(currentClip[4], currentClip[5]);
context.moveTo(clip[6], clip[7]); context.lineTo(currentClip[6], currentClip[7]);
context.lineTo(clip[4], clip[5]); // clockwise (inner ring) for higher z tile
context.lineTo(clip[2], clip[3]); context.moveTo(clip[6], clip[7]);
context.lineTo(clip[0], clip[1]); context.lineTo(clip[4], clip[5]);
context.clip(); context.lineTo(clip[2], clip[3]);
context.lineTo(clip[0], clip[1]);
context.clip();
}
} }
clips.push(currentClip);
clipZs.push(currentZ);
} else {
context.clearRect(x, y, w, h);
} }
clips.push(currentClip);
clipZs.push(currentZ);
} }
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity); this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity);
if (clips) { if (clips && !inTransition) {
context.restore(); context.restore();
} }
this.renderedTiles.push(tile); this.renderedTiles.push(tile);