From 63667fd3f6b1275c9d450d1f57b4dae7ec40aee1 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 31 Oct 2019 19:58:09 -0700 Subject: [PATCH 01/34] Initial commit of stats gathering --- src/ui/map.js | 24 +++++++++++++++- src/util/performance.js | 61 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/ui/map.js b/src/ui/map.js index cd131a24f1c..8139a3803b1 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -26,6 +26,8 @@ import {Event, ErrorEvent} from '../util/evented'; import {MapMouseEvent} from './events'; import TaskQueue from '../util/task_queue'; import webpSupported from '../util/webp_supported'; +import performance, {PerformanceMarkers, getPerformanceMetrics} from '../util/performance'; + import {setCacheLimits} from '../util/tile_request_cache'; import type {PointLike} from '@mapbox/point-geometry'; @@ -44,6 +46,7 @@ import type DragPanHandler from './handler/drag_pan'; import type KeyboardHandler from './handler/keyboard'; import type DoubleClickZoomHandler from './handler/dblclick_zoom'; import type TouchZoomRotateHandler from './handler/touch_zoom_rotate'; +import type {PerformanceMetrics} from '../util/performance'; import type {TaskID} from '../util/task_queue'; import type {Cancelable} from '../types/cancelable'; import type { @@ -265,6 +268,7 @@ class Map extends Camera { _sourcesDirty: ?boolean; _placementDirty: ?boolean; _loaded: boolean; + _contentLoaded: boolean; _trackResize: boolean; _preserveDrawingBuffer: boolean; _failIfMajorPerformanceCaveat: boolean; @@ -326,6 +330,8 @@ class Map extends Camera { touchZoomRotate: TouchZoomRotateHandler; constructor(options: MapOptions) { + performance.mark(PerformanceMarkers.create); + options = extend({}, defaultOptions, options); if (options.minZoom != null && options.maxZoom != null && options.minZoom > options.maxZoom) { @@ -1993,6 +1999,7 @@ class Map extends Camera { if (this.loaded() && !this._loaded) { this._loaded = true; + performance.mark(PerformanceMarkers.load); this.fire(new Event('load')); } @@ -2012,11 +2019,20 @@ class Map extends Camera { // Even though `_styleDirty` and `_sourcesDirty` are reset in this // method, synchronous events fired during Style#update or // Style#_updateSources could have caused them to be set again. - if (this._sourcesDirty || this._repaint || this._styleDirty || this._placementDirty) { + const somethingDirty = this._sourcesDirty || this._styleDirty || this._placementDirty; + if (somethingDirty || this._repaint) { this.triggerRepaint(); } else if (!this.isMoving() && this.loaded()) { this.fire(new Event('idle')); } + + if (this._loaded && !this._contentLoaded && !somethingDirty) { + this._contentLoaded = true; + performance.mark(PerformanceMarkers.fullLoad); + this.fire(new Event('content.load')); + } + + performance.frame(); return this; } @@ -2052,9 +2068,15 @@ class Map extends Camera { removeNode(this._controlContainer); removeNode(this._missingCSSCanary); this._container.classList.remove('mapboxgl-map'); + + performance.clearMetrics(); this.fire(new Event('remove')); } + extractPerformanceMetrics(): PerformanceMetrics { + return getPerformanceMetrics(); + } + /** * Trigger the rendering of a single frame. Use this method with custom layers to * repaint the map when the layer changes. Calling this multiple times before the diff --git a/src/util/performance.js b/src/util/performance.js index 053ed28d8f8..16eabf34bd8 100644 --- a/src/util/performance.js +++ b/src/util/performance.js @@ -5,6 +5,7 @@ import type {RequestParameters} from '../util/ajax'; // Wraps performance to facilitate testing // Not incorporated into browser.js because the latter is poisonous when used outside the main thread const performanceExists = typeof performance !== 'undefined'; + const wrapper = {}; wrapper.getEntriesByName = (url: string) => { @@ -42,6 +43,66 @@ wrapper.clearMeasures = (name: string) => { return false; }; +export const PerformanceMarkers = { + create: 'create', + load: 'load', + fullLoad: 'full-load' +}; + +let lastFrameTime = null; +let frameTimes = []; + +wrapper.frame = () => { + const currTimestamp = performance.now(); + if (lastFrameTime != null) { + const frameTime = currTimestamp - lastFrameTime; + frameTimes.push(frameTime); + } + lastFrameTime = currTimestamp; +}; + +wrapper.clearMetrics = () => { + lastFrameTime = null; + frameTimes = []; + wrapper.clearMeasures('loadTime'); + wrapper.clearMeasures('fullLoadTime'); + + for (const marker in PerformanceMarkers) { + wrapper.clearMarks(PerformanceMarkers[marker]); + } +}; + +export type PerformanceMetrics = { + loadTime: number, + fullLoadTime: number, + frameTimes: Array, + fps: number, + onePercentLowFps: number +} + +export function getPerformanceMetrics(): PerformanceMetrics { + const loadTime = wrapper.measure('loadTime', PerformanceMarkers.create, PerformanceMarkers.load).duration; + const fullLoadTime = wrapper.measure('fullLoadTime', PerformanceMarkers.create, PerformanceMarkers.fullLoad).duration; + const totalFrames = frameTimes.length; + + const avgFrameTime = frameTimes.reduce((prev, curr) => prev + curr, 0) / totalFrames / 1000; + const fps = 1 / avgFrameTime; + + // Sort frametimes, in descending order to get the 1% slowest frames + const onePercentLowFrameTimes = frameTimes.slice().sort((a, b) => b - a) + .slice(0, totalFrames * 0.01 | 0); + + const onePercentLowFrameTime = onePercentLowFrameTimes.reduce((prev, curr) => prev + curr, 0) / onePercentLowFrameTimes.length / 1000; + const onePercentLowFps = 1 / onePercentLowFrameTime; + return { + loadTime, + fullLoadTime, + frameTimes, + fps, + onePercentLowFps + }; +} + /** * Safe wrapper for the performance resource timing API in web workers with graceful degradation * From 2c1c842794ffe6ad774bb57d49fd28ed7bfd0673 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 1 Nov 2019 14:01:28 -0700 Subject: [PATCH 02/34] Fix failing tests and remove unnecessary `performance` wrapper --- src/source/geojson_worker_source.js | 4 +- src/source/vector_tile_worker_source.js | 4 +- src/ui/map.js | 8 +- src/util/performance.js | 158 +++++++++--------------- src/util/window.js | 7 ++ 5 files changed, 74 insertions(+), 107 deletions(-) diff --git a/src/source/geojson_worker_source.js b/src/source/geojson_worker_source.js index be29d9a2c61..550fecdf5ab 100644 --- a/src/source/geojson_worker_source.js +++ b/src/source/geojson_worker_source.js @@ -2,7 +2,7 @@ import {getJSON} from '../util/ajax'; -import performance from '../util/performance'; +import {RequestPerformance} from '../util/performance'; import rewind from '@mapbox/geojson-rewind'; import GeoJSONWrapper from './geojson_wrapper'; import vtpbf from 'vt-pbf'; @@ -161,7 +161,7 @@ class GeoJSONWorkerSource extends VectorTileWorkerSource { delete this._pendingLoadDataParams; const perf = (params && params.request && params.request.collectResourceTiming) ? - new performance.Performance(params.request) : false; + new RequestPerformance(params.request) : false; this.loadGeoJSON(params, (err: ?Error, data: ?Object) => { if (err || !data) { diff --git a/src/source/vector_tile_worker_source.js b/src/source/vector_tile_worker_source.js index cfd597fa0aa..9cc29ac5890 100644 --- a/src/source/vector_tile_worker_source.js +++ b/src/source/vector_tile_worker_source.js @@ -6,7 +6,7 @@ import vt from '@mapbox/vector-tile'; import Protobuf from 'pbf'; import WorkerTile from './worker_tile'; import {extend} from '../util/util'; -import performance from '../util/performance'; +import {RequestPerformance} from '../util/performance'; import type { WorkerSource, @@ -104,7 +104,7 @@ class VectorTileWorkerSource implements WorkerSource { this.loading = {}; const perf = (params && params.request && params.request.collectResourceTiming) ? - new performance.Performance(params.request) : false; + new RequestPerformance(params.request) : false; const workerTile = this.loading[uid] = new WorkerTile(params); workerTile.abort = this.loadVectorData(params, (err, response) => { diff --git a/src/ui/map.js b/src/ui/map.js index 8139a3803b1..5829e934581 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -26,7 +26,7 @@ import {Event, ErrorEvent} from '../util/evented'; import {MapMouseEvent} from './events'; import TaskQueue from '../util/task_queue'; import webpSupported from '../util/webp_supported'; -import performance, {PerformanceMarkers, getPerformanceMetrics} from '../util/performance'; +import performance, {PerformanceMarkers, PerformanceUtils} from '../util/performance'; import {setCacheLimits} from '../util/tile_request_cache'; @@ -2032,7 +2032,7 @@ class Map extends Camera { this.fire(new Event('content.load')); } - performance.frame(); + PerformanceUtils.frame(); return this; } @@ -2069,12 +2069,12 @@ class Map extends Camera { removeNode(this._missingCSSCanary); this._container.classList.remove('mapboxgl-map'); - performance.clearMetrics(); + PerformanceUtils.clearMetrics(); this.fire(new Event('remove')); } extractPerformanceMetrics(): PerformanceMetrics { - return getPerformanceMetrics(); + return PerformanceUtils.getPerformanceMetrics(); } /** diff --git a/src/util/performance.js b/src/util/performance.js index 16eabf34bd8..71dbb865de4 100644 --- a/src/util/performance.js +++ b/src/util/performance.js @@ -1,47 +1,17 @@ // @flow +import window from '../util/window'; import type {RequestParameters} from '../util/ajax'; -// Wraps performance to facilitate testing -// Not incorporated into browser.js because the latter is poisonous when used outside the main thread -const performanceExists = typeof performance !== 'undefined'; +const performance = window.performance; -const wrapper = {}; - -wrapper.getEntriesByName = (url: string) => { - if (performanceExists && performance && performance.getEntriesByName) - return performance.getEntriesByName(url); - else - return false; -}; - -wrapper.mark = (name: string) => { - if (performanceExists && performance && performance.mark) - return performance.mark(name); - else - return false; -}; - -wrapper.measure = (name: string, startMark: string, endMark: string) => { - if (performanceExists && performance && performance.measure) - return performance.measure(name, startMark, endMark); - else - return false; -}; - -wrapper.clearMarks = (name: string) => { - if (performanceExists && performance && performance.clearMarks) - return performance.clearMarks(name); - else - return false; -}; - -wrapper.clearMeasures = (name: string) => { - if (performanceExists && performance && performance.clearMeasures) - return performance.clearMeasures(name); - else - return false; -}; +export type PerformanceMetrics = { + loadTime: number, + fullLoadTime: number, + frameTimes: Array, + fps: number, + onePercentLowFps: number +} export const PerformanceMarkers = { create: 'create', @@ -52,64 +22,56 @@ export const PerformanceMarkers = { let lastFrameTime = null; let frameTimes = []; -wrapper.frame = () => { - const currTimestamp = performance.now(); - if (lastFrameTime != null) { - const frameTime = currTimestamp - lastFrameTime; - frameTimes.push(frameTime); - } - lastFrameTime = currTimestamp; -}; - -wrapper.clearMetrics = () => { - lastFrameTime = null; - frameTimes = []; - wrapper.clearMeasures('loadTime'); - wrapper.clearMeasures('fullLoadTime'); - - for (const marker in PerformanceMarkers) { - wrapper.clearMarks(PerformanceMarkers[marker]); +export const PerformanceUtils = { + frame() { + const currTimestamp = performance.now(); + if (lastFrameTime != null) { + const frameTime = currTimestamp - lastFrameTime; + frameTimes.push(frameTime); + } + lastFrameTime = currTimestamp; + }, + clearMetrics() { + lastFrameTime = null; + frameTimes = []; + performance.clearMeasures('loadTime'); + performance.clearMeasures('fullLoadTime'); + + for (const marker in PerformanceMarkers) { + performance.clearMarks(PerformanceMarkers[marker]); + } + }, + getPerformanceMetrics(): PerformanceMetrics { + const loadTime = performance.measure('loadTime', PerformanceMarkers.create, PerformanceMarkers.load).duration; + const fullLoadTime = performance.measure('fullLoadTime', PerformanceMarkers.create, PerformanceMarkers.fullLoad).duration; + const totalFrames = frameTimes.length; + + const avgFrameTime = frameTimes.reduce((prev, curr) => prev + curr, 0) / totalFrames / 1000; + const fps = 1 / avgFrameTime; + + // Sort frametimes, in descending order to get the 1% slowest frames + const onePercentLowFrameTimes = frameTimes.slice().sort((a, b) => b - a) + .slice(0, totalFrames * 0.01 | 0); + + const onePercentLowFrameTime = onePercentLowFrameTimes.reduce((prev, curr) => prev + curr, 0) / onePercentLowFrameTimes.length / 1000; + const onePercentLowFps = 1 / onePercentLowFrameTime; + return { + loadTime, + fullLoadTime, + frameTimes, + fps, + onePercentLowFps + }; } }; -export type PerformanceMetrics = { - loadTime: number, - fullLoadTime: number, - frameTimes: Array, - fps: number, - onePercentLowFps: number -} - -export function getPerformanceMetrics(): PerformanceMetrics { - const loadTime = wrapper.measure('loadTime', PerformanceMarkers.create, PerformanceMarkers.load).duration; - const fullLoadTime = wrapper.measure('fullLoadTime', PerformanceMarkers.create, PerformanceMarkers.fullLoad).duration; - const totalFrames = frameTimes.length; - - const avgFrameTime = frameTimes.reduce((prev, curr) => prev + curr, 0) / totalFrames / 1000; - const fps = 1 / avgFrameTime; - - // Sort frametimes, in descending order to get the 1% slowest frames - const onePercentLowFrameTimes = frameTimes.slice().sort((a, b) => b - a) - .slice(0, totalFrames * 0.01 | 0); - - const onePercentLowFrameTime = onePercentLowFrameTimes.reduce((prev, curr) => prev + curr, 0) / onePercentLowFrameTimes.length / 1000; - const onePercentLowFps = 1 / onePercentLowFrameTime; - return { - loadTime, - fullLoadTime, - frameTimes, - fps, - onePercentLowFps - }; -} - /** * Safe wrapper for the performance resource timing API in web workers with graceful degradation * * @param {RequestParameters} request * @private */ -class Performance { +export class RequestPerformance { _marks: {start: string, end: string, measure: string}; constructor (request: RequestParameters) { @@ -119,28 +81,26 @@ class Performance { measure: request.url.toString() }; - wrapper.mark(this._marks.start); + performance.mark(this._marks.start); } finish() { - wrapper.mark(this._marks.end); - let resourceTimingData = wrapper.getEntriesByName(this._marks.measure); + performance.mark(this._marks.end); + let resourceTimingData = performance.getEntriesByName(this._marks.measure); // fallback if web worker implementation of perf.getEntriesByName returns empty if (resourceTimingData.length === 0) { - wrapper.measure(this._marks.measure, this._marks.start, this._marks.end); - resourceTimingData = wrapper.getEntriesByName(this._marks.measure); + performance.measure(this._marks.measure, this._marks.start, this._marks.end); + resourceTimingData = performance.getEntriesByName(this._marks.measure); // cleanup - wrapper.clearMarks(this._marks.start); - wrapper.clearMarks(this._marks.end); - wrapper.clearMeasures(this._marks.measure); + performance.clearMarks(this._marks.start); + performance.clearMarks(this._marks.end); + performance.clearMeasures(this._marks.measure); } return resourceTimingData; } } -wrapper.Performance = Performance; - -export default wrapper; +export default performance; diff --git a/src/util/window.js b/src/util/window.js index 684a170f7cd..9adde95db2c 100644 --- a/src/util/window.js +++ b/src/util/window.js @@ -87,6 +87,13 @@ function restore(): Window { window.restore = restore; + // window.performance = {}; + window.performance.getEntriesByName = function() {}; + window.performance.mark = function() {}; + window.performance.measure = function() {}; + window.performance.clearMarks = function() {}; + window.performance.clearMeasures = function() {}; + window.ImageData = window.ImageData || function() { return false; }; window.ImageBitmap = window.ImageBitmap || function() { return false; }; window.WebGLFramebuffer = window.WebGLFramebuffer || Object; From b2a79730d77a3589525d49d4f4742f07f8343f81 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 1 Nov 2019 16:09:00 -0700 Subject: [PATCH 03/34] Strip performance probes from production build --- build/rollup_plugins.js | 10 ++++++++-- package.json | 1 + src/ui/map.js | 8 ++++---- src/util/performance.js | 5 ++++- src/util/window.js | 1 - yarn.lock | 13 +++++++++++-- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/build/rollup_plugins.js b/build/rollup_plugins.js index fef9a4868d1..9f8ba4165a9 100644 --- a/build/rollup_plugins.js +++ b/build/rollup_plugins.js @@ -1,3 +1,4 @@ +// @flow import flowRemoveTypes from '@mapbox/flow-remove-types'; import buble from 'rollup-plugin-buble'; @@ -5,9 +6,10 @@ import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import unassert from 'rollup-plugin-unassert'; import json from 'rollup-plugin-json'; -import { terser } from 'rollup-plugin-terser'; +import {terser} from 'rollup-plugin-terser'; import minifyStyleSpec from './rollup_plugin_minify_style_spec'; -import { createFilter } from 'rollup-pluginutils'; +import {createFilter} from 'rollup-pluginutils'; +import strip from '@rollup/plugin-strip'; // Common set of plugins/transformations shared across different rollup // builds (main mapboxgl bundle, style-spec package, benchmarks bundle) @@ -16,6 +18,10 @@ export const plugins = (minified, production) => [ flow(), minifyStyleSpec(), json(), + production ? strip({ + sourceMap: true, + functions: ['PerformanceUtils.*'] + }) : false, glsl('./src/shaders/*.glsl', production), buble({transforms: {dangerousForOf: true}, objectAssign: "Object.assign"}), minified ? terser() : false, diff --git a/package.json b/package.json index c465e40bf5f..ab66421543e 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@mapbox/mapbox-gl-rtl-text": "^0.2.1", "@mapbox/mapbox-gl-test-suite": "file:test/integration", "@octokit/rest": "^16.30.1", + "@rollup/plugin-strip": "^1.3.1", "address": "^1.1.2", "babel-eslint": "^10.0.1", "benchmark": "^2.1.4", diff --git a/src/ui/map.js b/src/ui/map.js index 5829e934581..54ac44b5cc2 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -26,7 +26,7 @@ import {Event, ErrorEvent} from '../util/evented'; import {MapMouseEvent} from './events'; import TaskQueue from '../util/task_queue'; import webpSupported from '../util/webp_supported'; -import performance, {PerformanceMarkers, PerformanceUtils} from '../util/performance'; +import {PerformanceMarkers, PerformanceUtils} from '../util/performance'; import {setCacheLimits} from '../util/tile_request_cache'; @@ -330,7 +330,7 @@ class Map extends Camera { touchZoomRotate: TouchZoomRotateHandler; constructor(options: MapOptions) { - performance.mark(PerformanceMarkers.create); + PerformanceUtils.mark(PerformanceMarkers.create); options = extend({}, defaultOptions, options); @@ -1999,7 +1999,7 @@ class Map extends Camera { if (this.loaded() && !this._loaded) { this._loaded = true; - performance.mark(PerformanceMarkers.load); + PerformanceUtils.mark(PerformanceMarkers.load); this.fire(new Event('load')); } @@ -2028,7 +2028,7 @@ class Map extends Camera { if (this._loaded && !this._contentLoaded && !somethingDirty) { this._contentLoaded = true; - performance.mark(PerformanceMarkers.fullLoad); + PerformanceUtils.mark(PerformanceMarkers.fullLoad); this.fire(new Event('content.load')); } diff --git a/src/util/performance.js b/src/util/performance.js index 71dbb865de4..a07f13c2c6b 100644 --- a/src/util/performance.js +++ b/src/util/performance.js @@ -16,13 +16,16 @@ export type PerformanceMetrics = { export const PerformanceMarkers = { create: 'create', load: 'load', - fullLoad: 'full-load' + fullLoad: 'fullLoad' }; let lastFrameTime = null; let frameTimes = []; export const PerformanceUtils = { + mark(marker: $Keys) { + performance.mark(marker); + }, frame() { const currTimestamp = performance.now(); if (lastFrameTime != null) { diff --git a/src/util/window.js b/src/util/window.js index 9adde95db2c..00dcd87b213 100644 --- a/src/util/window.js +++ b/src/util/window.js @@ -87,7 +87,6 @@ function restore(): Window { window.restore = restore; - // window.performance = {}; window.performance.getEntriesByName = function() {}; window.performance.mark = function() {}; window.performance.measure = function() {}; diff --git a/yarn.lock b/yarn.lock index e459a995103..b8009a22f55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1124,6 +1124,15 @@ dependencies: "@types/node" "^12.11.1" +"@rollup/plugin-strip@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@rollup/plugin-strip/-/plugin-strip-1.3.1.tgz#ef7b01c81c837863aec3885db4ac47a7e87a0ec5" + integrity sha512-JzjtQSdRVkrm/17SscKN0iEdnmvsV/9kLfAVx/MOilsdKI8EPY5x3hnLIhi2i/hXjXE4IlJxkv582nGxmt9qUg== + dependencies: + estree-walker "^0.6.0" + magic-string "^0.25.1" + rollup-pluginutils "^2.8.1" + "@sinonjs/commons@^1", "@sinonjs/commons@^1.3.0", "@sinonjs/commons@^1.4.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.6.0.tgz#ec7670432ae9c8eb710400d112c201a362d83393" @@ -4004,7 +4013,7 @@ estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estree-walker@^0.6.1: +estree-walker@^0.6.0, estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== @@ -6165,7 +6174,7 @@ macos-release@^2.2.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.3.0.tgz#eb1930b036c0800adebccd5f17bc4c12de8bb71f" integrity sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA== -magic-string@^0.25.2, magic-string@^0.25.3: +magic-string@^0.25.1, magic-string@^0.25.2, magic-string@^0.25.3: version "0.25.4" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.4.tgz#325b8a0a79fc423db109b77fd5a19183b7ba5143" integrity sha512-oycWO9nEVAP2RVPbIoDoA4Y7LFIJ3xRYov93gAyJhZkET1tNuB0u7uWkZS2LpBWTJUWnmau/To8ECWRC+jKNfw== From 276273925146181e4ec2bc1f99d2fc851cd2d124 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 1 Nov 2019 16:31:48 -0700 Subject: [PATCH 04/34] Rollback accidental //@flow annotation --- build/rollup_plugins.js | 1 - 1 file changed, 1 deletion(-) diff --git a/build/rollup_plugins.js b/build/rollup_plugins.js index 9f8ba4165a9..2a860b7b800 100644 --- a/build/rollup_plugins.js +++ b/build/rollup_plugins.js @@ -1,4 +1,3 @@ -// @flow import flowRemoveTypes from '@mapbox/flow-remove-types'; import buble from 'rollup-plugin-buble'; From 740f770cfd6494d3eb8c0bf4b52a8ebcc2fd0a49 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Mon, 4 Nov 2019 19:19:00 -0800 Subject: [PATCH 05/34] First hacky version of automated metrics suite * added a metrics runner harness * added rollup config to build metrics runner * added metrics page --- bench/dist/.gitkeep | 0 bench/dist/metrics-suite.js | 169 +++++++++++++++++++++ bench/lib/metrics-harness.js | 65 ++++++++ bench/metrics.html | 13 ++ bench/performance-metrics/suite.json | 17 +++ bench/rollup.config.metrics.js | 14 ++ package.json | 1 + src/ui/map.js | 4 + test/integration/lib/operation-handlers.js | 17 +++ 9 files changed, 300 insertions(+) create mode 100644 bench/dist/.gitkeep create mode 100644 bench/dist/metrics-suite.js create mode 100644 bench/lib/metrics-harness.js create mode 100644 bench/metrics.html create mode 100644 bench/performance-metrics/suite.json create mode 100644 bench/rollup.config.metrics.js diff --git a/bench/dist/.gitkeep b/bench/dist/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/bench/dist/metrics-suite.js b/bench/dist/metrics-suite.js new file mode 100644 index 00000000000..5f7ca042206 --- /dev/null +++ b/bench/dist/metrics-suite.js @@ -0,0 +1,169 @@ +(function (global, factory) { +typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : +typeof define === 'function' && define.amd ? define(['exports'], factory) : +(global = global || self, factory(global.metrics = {})); +}(this, (function (exports) { 'use strict'; + +var suite = { + "streets-fly-to-sf-oakland": { + width: 1024, + height: 1024, + center: [ + -122.397218, + 37.791459 + ], + zoom: 18, + style: "mapbox://styles/mapbox/streets-v11", + operations: [ + [ + "contentLoad" + ], + [ + "flyTo", + { + center: [ + -122.271378, + 37.804376 + ], + zoom: 17 + } + ], + [ + "idle" + ] + ] +} +}; + +function handleOperation(map, operations, opIndex, doneCb) { + var operation = operations[opIndex]; + var opName = operation[0]; + //Delegate to special handler if one is available + if (opName in operationHandlers) { + operationHandlers[opName](map, operation.slice(1), function () { + doneCb(opIndex); + }); + } else { + map[opName].apply(map, operation.slice(1)); + doneCb(opIndex); + } +} + +var operationHandlers = { + wait: function wait(map, params, doneCb) { + var wait = function() { + if (map.loaded()) { + doneCb(); + } else { + map.once('render', wait); + } + }; + wait(); + }, + contentLoad: function contentLoad(map, params, doneCb) { + if (map.contentLoaded()) { + doneCb(); + } else { + map.once('content.load', doneCb); + } + }, + idle: function idle(map, params, doneCb) { + var idle = function() { + if (!map.isMoving()) { + doneCb(); + } else { + map.once('render', idle); + } + }; + idle(); + } +}; + +function applyOperations(map, operations, doneCb) { + // No operations specified, end immediately adn invoke doneCb. + if (!operations || operations.length === 0) { + doneCb(); + return; + } + + // Start recursive chain + var scheduleNextOperation = function (lastOpIndex) { + if (lastOpIndex === operations.length - 1) { + // Stop recusive chain when at the end of the operations + doneCb(); + return; + } + + handleOperation(map, operations, ++lastOpIndex, scheduleNextOperation); + }; + scheduleNextOperation(-1); +} + +// + +//Used to warm-up the browser cache for consistent tile-load timings +var NUM_WARMUP_RUNS = 5; + +var NUM_ACTUAL_RUNS = 5; + +function runMetrics() { + var suiteList = []; + for (var runName in suite) { + suiteList.push(suite[runName]); + } + var totalRuns = NUM_WARMUP_RUNS + NUM_ACTUAL_RUNS; + var currIndex = 0; + + var startRun = function() { + executeRun(suiteList[0], function (metrics) { + if (currIndex >= NUM_WARMUP_RUNS) { + console.log(metrics); + } + + currIndex++; + if (currIndex < totalRuns) { + startRun(); + } + }); + }; + startRun(); +} + +function executeRun(options, finishCb) { + + //1. Create and position the container, floating at the top left + var container = document.createElement('div'); + container.style.position = 'fixed'; + container.style.left = '10px'; + container.style.top = '10px'; + container.style.width = (options.width) + "px"; + container.style.height = (options.height) + "px"; + document.body.appendChild(container); + + var mapOptions = parseOptions(container, options); + var map = new mapboxgl.Map(mapOptions); + map.repaint = true; + applyOperations(map, options.operations, function () { + var metrics = map.extractPerformanceMetrics(); + map.remove(); + map = null; + document.body.removeChild(container); + finishCb(metrics); + }); +} + +function parseOptions(container, options) { + var copy = JSON.parse(JSON.stringify(options)); + delete copy.width; + delete copy.height; + delete copy.operations; + copy.container = container; + return copy; +} + +exports.runMetrics = runMetrics; + +Object.defineProperty(exports, '__esModule', { value: true }); + +}))); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1zdWl0ZS5qcyIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC9pbnRlZ3JhdGlvbi9saWIvb3BlcmF0aW9uLWhhbmRsZXJzLmpzIiwiLi4vbGliL21ldHJpY3MtaGFybmVzcy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBoYW5kbGVPcGVyYXRpb24obWFwLCBvcGVyYXRpb25zLCBvcEluZGV4LCBkb25lQ2IpIHtcbiAgICBjb25zdCBvcGVyYXRpb24gPSBvcGVyYXRpb25zW29wSW5kZXhdO1xuICAgIGNvbnN0IG9wTmFtZSA9IG9wZXJhdGlvblswXTtcbiAgICAvL0RlbGVnYXRlIHRvIHNwZWNpYWwgaGFuZGxlciBpZiBvbmUgaXMgYXZhaWxhYmxlXG4gICAgaWYgKG9wTmFtZSBpbiBvcGVyYXRpb25IYW5kbGVycykge1xuICAgICAgICBvcGVyYXRpb25IYW5kbGVyc1tvcE5hbWVdKG1hcCwgb3BlcmF0aW9uLnNsaWNlKDEpLCAoKSA9PiB7XG4gICAgICAgICAgICBkb25lQ2Iob3BJbmRleCk7XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIG1hcFtvcE5hbWVdKC4uLm9wZXJhdGlvbi5zbGljZSgxKSk7XG4gICAgICAgIGRvbmVDYihvcEluZGV4KTtcbiAgICB9XG59XG5cbmV4cG9ydCBjb25zdCBvcGVyYXRpb25IYW5kbGVycyA9IHtcbiAgICB3YWl0KG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgY29uc3Qgd2FpdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaWYgKG1hcC5sb2FkZWQoKSkge1xuICAgICAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtYXAub25jZSgncmVuZGVyJywgd2FpdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHdhaXQoKTtcbiAgICB9LFxuICAgIGNvbnRlbnRMb2FkKG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgaWYgKG1hcC5jb250ZW50TG9hZGVkKCkpIHtcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWFwLm9uY2UoJ2NvbnRlbnQubG9hZCcsIGRvbmVDYik7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGlkbGUobWFwLCBwYXJhbXMsIGRvbmVDYikge1xuICAgICAgICBjb25zdCBpZGxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoIW1hcC5pc01vdmluZygpKSB7XG4gICAgICAgICAgICAgICAgZG9uZUNiKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG1hcC5vbmNlKCdyZW5kZXInLCBpZGxlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgaWRsZSgpO1xuICAgIH1cbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBhcHBseU9wZXJhdGlvbnMobWFwLCBvcGVyYXRpb25zLCBkb25lQ2IpIHtcbiAgICAvLyBObyBvcGVyYXRpb25zIHNwZWNpZmllZCwgZW5kIGltbWVkaWF0ZWx5IGFkbiBpbnZva2UgZG9uZUNiLlxuICAgIGlmICghb3BlcmF0aW9ucyB8fCBvcGVyYXRpb25zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBkb25lQ2IoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHJlY3Vyc2l2ZSBjaGFpblxuICAgIGNvbnN0IHNjaGVkdWxlTmV4dE9wZXJhdGlvbiA9IChsYXN0T3BJbmRleCkgPT4ge1xuICAgICAgICBpZiAobGFzdE9wSW5kZXggPT09IG9wZXJhdGlvbnMubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgLy8gU3RvcCByZWN1c2l2ZSBjaGFpbiB3aGVuIGF0IHRoZSBlbmQgb2YgdGhlIG9wZXJhdGlvbnNcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaGFuZGxlT3BlcmF0aW9uKG1hcCwgb3BlcmF0aW9ucywgKytsYXN0T3BJbmRleCwgc2NoZWR1bGVOZXh0T3BlcmF0aW9uKTtcbiAgICB9O1xuICAgIHNjaGVkdWxlTmV4dE9wZXJhdGlvbigtMSk7XG59XG4iLCIvLyBAZmxvd1xuLyogZ2xvYmFsIG1hcGJveGdsOnJlYWRvbmx5ICovXG5pbXBvcnQgc3VpdGUgZnJvbSAnLi4vcGVyZm9ybWFuY2UtbWV0cmljcy9zdWl0ZS5qc29uJztcbmltcG9ydCB7YXBwbHlPcGVyYXRpb25zfSBmcm9tICcuLi8uLi90ZXN0L2ludGVncmF0aW9uL2xpYi9vcGVyYXRpb24taGFuZGxlcnMnO1xuXG4vL1VzZWQgdG8gd2FybS11cCB0aGUgYnJvd3NlciBjYWNoZSBmb3IgY29uc2lzdGVudCB0aWxlLWxvYWQgdGltaW5nc1xuY29uc3QgTlVNX1dBUk1VUF9SVU5TID0gNTtcblxuY29uc3QgTlVNX0FDVFVBTF9SVU5TID0gNTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJ1bk1ldHJpY3MoKSB7XG4gICAgY29uc3Qgc3VpdGVMaXN0ID0gW107XG4gICAgZm9yIChjb25zdCBydW5OYW1lIGluIHN1aXRlKSB7XG4gICAgICAgIHN1aXRlTGlzdC5wdXNoKHN1aXRlW3J1bk5hbWVdKTtcbiAgICB9XG4gICAgY29uc3QgdG90YWxSdW5zID0gTlVNX1dBUk1VUF9SVU5TICsgTlVNX0FDVFVBTF9SVU5TO1xuICAgIGxldCBjdXJySW5kZXggPSAwO1xuXG4gICAgY29uc3Qgc3RhcnRSdW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgZXhlY3V0ZVJ1bihzdWl0ZUxpc3RbMF0sIChtZXRyaWNzKSA9PiB7XG4gICAgICAgICAgICBpZiAoY3VyckluZGV4ID49IE5VTV9XQVJNVVBfUlVOUykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKG1ldHJpY3MpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjdXJySW5kZXgrKztcbiAgICAgICAgICAgIGlmIChjdXJySW5kZXggPCB0b3RhbFJ1bnMpIHtcbiAgICAgICAgICAgICAgICBzdGFydFJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuICAgIHN0YXJ0UnVuKCk7XG59XG5cbmZ1bmN0aW9uIGV4ZWN1dGVSdW4ob3B0aW9ucywgZmluaXNoQ2IpIHtcblxuICAgIC8vMS4gQ3JlYXRlIGFuZCBwb3NpdGlvbiB0aGUgY29udGFpbmVyLCBmbG9hdGluZyBhdCB0aGUgdG9wIGxlZnRcbiAgICBjb25zdCBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBjb250YWluZXIuc3R5bGUucG9zaXRpb24gPSAnZml4ZWQnO1xuICAgIGNvbnRhaW5lci5zdHlsZS5sZWZ0ID0gJzEwcHgnO1xuICAgIGNvbnRhaW5lci5zdHlsZS50b3AgPSAnMTBweCc7XG4gICAgY29udGFpbmVyLnN0eWxlLndpZHRoID0gYCR7b3B0aW9ucy53aWR0aH1weGA7XG4gICAgY29udGFpbmVyLnN0eWxlLmhlaWdodCA9IGAke29wdGlvbnMuaGVpZ2h0fXB4YDtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG5cbiAgICBjb25zdCBtYXBPcHRpb25zID0gcGFyc2VPcHRpb25zKGNvbnRhaW5lciwgb3B0aW9ucyk7XG4gICAgbGV0IG1hcCA9IG5ldyBtYXBib3hnbC5NYXAobWFwT3B0aW9ucyk7XG4gICAgbWFwLnJlcGFpbnQgPSB0cnVlO1xuICAgIGFwcGx5T3BlcmF0aW9ucyhtYXAsIG9wdGlvbnMub3BlcmF0aW9ucywgKCkgPT4ge1xuICAgICAgICBjb25zdCBtZXRyaWNzID0gbWFwLmV4dHJhY3RQZXJmb3JtYW5jZU1ldHJpY3MoKTtcbiAgICAgICAgbWFwLnJlbW92ZSgpO1xuICAgICAgICBtYXAgPSBudWxsO1xuICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGNvbnRhaW5lcik7XG4gICAgICAgIGZpbmlzaENiKG1ldHJpY3MpO1xuICAgIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJzZU9wdGlvbnMoY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgY29uc3QgY29weSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob3B0aW9ucykpO1xuICAgIGRlbGV0ZSBjb3B5LndpZHRoO1xuICAgIGRlbGV0ZSBjb3B5LmhlaWdodDtcbiAgICBkZWxldGUgY29weS5vcGVyYXRpb25zO1xuICAgIGNvcHkuY29udGFpbmVyID0gY29udGFpbmVyO1xuICAgIHJldHVybiBjb3B5O1xufVxuXG4iXSwibmFtZXMiOlsiY29uc3QiLCJsZXQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxTQUFTLGVBQWUsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUU7SUFDdkRBLElBQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN0Q0EsSUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDOztJQUU1QixJQUFJLE1BQU0sSUFBSSxpQkFBaUIsRUFBRTtRQUM3QixpQkFBaUIsQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsY0FBSztZQUNsRCxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDbkIsQ0FBQyxDQUFDO0tBQ04sTUFBTTtRQUNILEdBQUcsQ0FBQyxNQUFNLE9BQUMsQ0FBQyxLQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDbkI7Q0FDSjs7QUFFRCxBQUFPQSxJQUFNLGlCQUFpQixHQUFHO0lBQzdCLG1CQUFJLENBQUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUU7UUFDdEJBLElBQU0sSUFBSSxHQUFHLFdBQVc7WUFDcEIsSUFBSSxHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUU7Z0JBQ2QsTUFBTSxFQUFFLENBQUM7YUFDWixNQUFNO2dCQUNILEdBQUcsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO2FBQzVCO1NBQ0osQ0FBQztRQUNGLElBQUksRUFBRSxDQUFDO0tBQ1Y7SUFDRCxpQ0FBVyxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFO1FBQzdCLElBQUksR0FBRyxDQUFDLGFBQWEsRUFBRSxFQUFFO1lBQ3JCLE1BQU0sRUFBRSxDQUFDO1NBQ1osTUFBTTtZQUNILEdBQUcsQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1NBQ3BDO0tBQ0o7SUFDRCxtQkFBSSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFO1FBQ3RCQSxJQUFNLElBQUksR0FBRyxXQUFXO1lBQ3BCLElBQUksQ0FBQyxHQUFHLENBQUMsUUFBUSxFQUFFLEVBQUU7Z0JBQ2pCLE1BQU0sRUFBRSxDQUFDO2FBQ1osTUFBTTtnQkFDSCxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQzthQUM1QjtTQUNKLENBQUM7UUFDRixJQUFJLEVBQUUsQ0FBQztLQUNWO0NBQ0osQ0FBQzs7QUFFRixBQUFPLFNBQVMsZUFBZSxDQUFDLEdBQUcsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFOztJQUVyRCxJQUFJLENBQUMsVUFBVSxJQUFJLFVBQVUsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFO1FBQ3hDLE1BQU0sRUFBRSxDQUFDO1FBQ1QsT0FBTztLQUNWOzs7SUFHREEsSUFBTSxxQkFBcUIsYUFBSSxXQUFXLEVBQUU7UUFDeEMsSUFBSSxXQUFXLEtBQUssVUFBVSxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUU7O1lBRXZDLE1BQU0sRUFBRSxDQUFDO1lBQ1QsT0FBTztTQUNWOztRQUVELGVBQWUsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLEVBQUUsV0FBVyxFQUFFLHFCQUFxQixDQUFDLENBQUM7S0FDMUUsQ0FBQztJQUNGLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7Q0FDN0I7O0FDOUREOzs7QUFNQUEsSUFBTSxlQUFlLEdBQUcsQ0FBQyxDQUFDOztBQUUxQkEsSUFBTSxlQUFlLEdBQUcsQ0FBQyxDQUFDOztBQUUxQixBQUFPLFNBQVMsVUFBVSxHQUFHO0lBQ3pCQSxJQUFNLFNBQVMsR0FBRyxFQUFFLENBQUM7SUFDckIsS0FBS0EsSUFBTSxPQUFPLElBQUksS0FBSyxFQUFFO1FBQ3pCLFNBQVMsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7S0FDbEM7SUFDREEsSUFBTSxTQUFTLEdBQUcsZUFBZSxHQUFHLGVBQWUsQ0FBQztJQUNwREMsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDOztJQUVsQkQsSUFBTSxRQUFRLEdBQUcsV0FBVztRQUN4QixVQUFVLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxZQUFHLE9BQU8sRUFBRTtZQUMvQixJQUFJLFNBQVMsSUFBSSxlQUFlLEVBQUU7Z0JBQzlCLE9BQU8sQ0FBQyxHQUFHLENBQUMsT0FBTyxDQUFDLENBQUM7YUFDeEI7O1lBRUQsU0FBUyxFQUFFLENBQUM7WUFDWixJQUFJLFNBQVMsR0FBRyxTQUFTLEVBQUU7Z0JBQ3ZCLFFBQVEsRUFBRSxDQUFDO2FBQ2Q7U0FDSixDQUFDLENBQUM7S0FDTixDQUFDO0lBQ0YsUUFBUSxFQUFFLENBQUM7Q0FDZDs7QUFFRCxTQUFTLFVBQVUsQ0FBQyxPQUFPLEVBQUUsUUFBUSxFQUFFOzs7SUFHbkNBLElBQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDaEQsU0FBUyxDQUFDLEtBQUssQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO0lBQ25DLFNBQVMsQ0FBQyxLQUFLLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQztJQUM5QixTQUFTLENBQUMsS0FBSyxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUM7SUFDN0IsU0FBUyxDQUFDLEtBQUssQ0FBQyxLQUFLLEdBQUcsQ0FBRyxPQUFPLENBQUMsYUFBUyxDQUFDO0lBQzdDLFNBQVMsQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUcsT0FBTyxDQUFDLGNBQVUsQ0FBQztJQUMvQyxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQzs7SUFFckNBLElBQU0sVUFBVSxHQUFHLFlBQVksQ0FBQyxTQUFTLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDcERDLElBQUksR0FBRyxHQUFHLElBQUksUUFBUSxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN2QyxHQUFHLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztJQUNuQixlQUFlLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxVQUFVLGNBQUs7UUFDeENELElBQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyx5QkFBeUIsRUFBRSxDQUFDO1FBQ2hELEdBQUcsQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNiLEdBQUcsR0FBRyxJQUFJLENBQUM7UUFDWCxRQUFRLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNyQyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDckIsQ0FBQyxDQUFDO0NBQ047O0FBRUQsU0FBUyxZQUFZLENBQUMsU0FBUyxFQUFFLE9BQU8sRUFBRTtJQUN0Q0EsSUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7SUFDakQsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDO0lBQ2xCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNuQixPQUFPLElBQUksQ0FBQyxVQUFVLENBQUM7SUFDdkIsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7SUFDM0IsT0FBTyxJQUFJLENBQUM7Q0FDZjs7Ozs7Ozs7Ozs7OyJ9 diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js new file mode 100644 index 00000000000..ec55f8e2f8b --- /dev/null +++ b/bench/lib/metrics-harness.js @@ -0,0 +1,65 @@ +// @flow +/* global mapboxgl:readonly */ +import suite from '../performance-metrics/suite.json'; +import {applyOperations} from '../../test/integration/lib/operation-handlers'; + +//Used to warm-up the browser cache for consistent tile-load timings +const NUM_WARMUP_RUNS = 5; + +const NUM_ACTUAL_RUNS = 5; + +export function runMetrics() { + const suiteList = []; + for (const runName in suite) { + suiteList.push(suite[runName]); + } + const totalRuns = NUM_WARMUP_RUNS + NUM_ACTUAL_RUNS; + let currIndex = 0; + + const startRun = function() { + executeRun(suiteList[0], (metrics) => { + if (currIndex >= NUM_WARMUP_RUNS) { + console.log(metrics); + } + + currIndex++; + if (currIndex < totalRuns) { + startRun(); + } + }); + }; + startRun(); +} + +function executeRun(options, finishCb) { + + //1. Create and position the container, floating at the top left + const container = document.createElement('div'); + container.style.position = 'fixed'; + container.style.left = '10px'; + container.style.top = '10px'; + container.style.width = `${options.width}px`; + container.style.height = `${options.height}px`; + document.body.appendChild(container); + + const mapOptions = parseOptions(container, options); + let map = new mapboxgl.Map(mapOptions); + map.repaint = true; + applyOperations(map, options.operations, () => { + const metrics = map.extractPerformanceMetrics(); + map.remove(); + map = null; + document.body.removeChild(container); + finishCb(metrics); + }); +} + +function parseOptions(container, options) { + const copy = JSON.parse(JSON.stringify(options)); + delete copy.width; + delete copy.height; + delete copy.operations; + copy.container = container; + return copy; +} + diff --git a/bench/metrics.html b/bench/metrics.html new file mode 100644 index 00000000000..9948e328053 --- /dev/null +++ b/bench/metrics.html @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/bench/performance-metrics/suite.json b/bench/performance-metrics/suite.json new file mode 100644 index 00000000000..3964917eac2 --- /dev/null +++ b/bench/performance-metrics/suite.json @@ -0,0 +1,17 @@ +{ + "streets-fly-to-sf-oakland": { + "width": 1024, + "height": 1024, + "center": [-122.397218, 37.791459], + "zoom": 18, + "style": "mapbox://styles/mapbox/streets-v11", + "operations": [ + ["contentLoad"], + ["flyTo", { + "center": [-122.271378, 37.804376], + "zoom": 17 + }], + ["idle"] + ] + } +} \ No newline at end of file diff --git a/bench/rollup.config.metrics.js b/bench/rollup.config.metrics.js new file mode 100644 index 00000000000..1e23ff77f82 --- /dev/null +++ b/bench/rollup.config.metrics.js @@ -0,0 +1,14 @@ +import {plugins} from '../build/rollup_plugins'; + +export default { + input: 'bench/lib/metrics-harness.js', + output: { + name: 'metrics', + format: 'umd', + sourcemap: 'inline', + indent: false, + file: 'bench/dist/metrics-suite.js' + }, + plugins: plugins(false, false), + external: [ 'mapboxgl' ] +}; diff --git a/package.json b/package.json index ab66421543e..d77c49baf7f 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "build-prod-min": "rollup -c --environment BUILD:production,MINIFY:true", "build-csp": "rollup -c rollup.config.csp.js", "build-query-suite": "rollup -c test/integration/rollup.config.test.js", + "watch-metrics-suite": "rollup -c bench/rollup.config.metrics.js --watch", "build-flow-types": "cp build/mapbox-gl.js.flow dist/mapbox-gl.js.flow && cp build/mapbox-gl.js.flow dist/mapbox-gl-dev.js.flow", "build-css": "postcss -o dist/mapbox-gl.css src/css/mapbox-gl.css", "build-style-spec": "cd src/style-spec && npm run build && cd ../.. && mkdir -p dist/style-spec && cp src/style-spec/dist/* dist/style-spec", diff --git a/src/ui/map.js b/src/ui/map.js index 54ac44b5cc2..0a7ab36631a 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -1897,6 +1897,10 @@ class Map extends Camera { return !this._styleDirty && !this._sourcesDirty && !!this.style && this.style.loaded(); } + contentLoaded(): boolean { + return !!this._contentLoaded; + } + /** * Update this map's style and sources, and re-render the map. * diff --git a/test/integration/lib/operation-handlers.js b/test/integration/lib/operation-handlers.js index 0b85fdc2361..04bdf3fb0ef 100644 --- a/test/integration/lib/operation-handlers.js +++ b/test/integration/lib/operation-handlers.js @@ -22,6 +22,23 @@ export const operationHandlers = { } }; wait(); + }, + contentLoad(map, params, doneCb) { + if (map.contentLoaded()) { + doneCb(); + } else { + map.once('content.load', doneCb); + } + }, + idle(map, params, doneCb) { + const idle = function() { + if (!map.isMoving()) { + doneCb(); + } else { + map.once('render', idle); + } + }; + idle(); } }; From fd77dfe98ea0d73d3c77069dd8985497a12488fc Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 5 Nov 2019 16:15:45 -0800 Subject: [PATCH 06/34] Test with slower pan animation --- .gitignore | 2 ++ bench/dist/metrics-suite.js | 27 ++++++++++++++++++++------- bench/performance-metrics/suite.json | 15 ++++++++++----- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 399e49380e6..eff9cb39e0c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ test/integration/dist/**/*.js test/integration/dist/**/*.json .eslintcache src/style-spec/dist/index.js +bench/dist/**/*.json +bench/dist/**/*.js _batfish_site _batfish_tmp _site diff --git a/bench/dist/metrics-suite.js b/bench/dist/metrics-suite.js index 5f7ca042206..75ecc1494e3 100644 --- a/bench/dist/metrics-suite.js +++ b/bench/dist/metrics-suite.js @@ -5,27 +5,40 @@ typeof define === 'function' && define.amd ? define(['exports'], factory) : }(this, (function (exports) { 'use strict'; var suite = { - "streets-fly-to-sf-oakland": { + "slow-pans": { width: 1024, height: 1024, center: [ -122.397218, 37.791459 ], - zoom: 18, + zoom: 15, style: "mapbox://styles/mapbox/streets-v11", operations: [ [ "contentLoad" ], [ - "flyTo", + "easeTo", { center: [ - -122.271378, - 37.804376 + -122.4556, + 37.71272 ], - zoom: 17 + duration: 10000 + } + ], + [ + "idle" + ], + [ + "easeTo", + { + center: [ + -122.459915, + 37.779474 + ], + duration: 10000 } ], [ @@ -166,4 +179,4 @@ exports.runMetrics = runMetrics; Object.defineProperty(exports, '__esModule', { value: true }); }))); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1zdWl0ZS5qcyIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC9pbnRlZ3JhdGlvbi9saWIvb3BlcmF0aW9uLWhhbmRsZXJzLmpzIiwiLi4vbGliL21ldHJpY3MtaGFybmVzcy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBoYW5kbGVPcGVyYXRpb24obWFwLCBvcGVyYXRpb25zLCBvcEluZGV4LCBkb25lQ2IpIHtcbiAgICBjb25zdCBvcGVyYXRpb24gPSBvcGVyYXRpb25zW29wSW5kZXhdO1xuICAgIGNvbnN0IG9wTmFtZSA9IG9wZXJhdGlvblswXTtcbiAgICAvL0RlbGVnYXRlIHRvIHNwZWNpYWwgaGFuZGxlciBpZiBvbmUgaXMgYXZhaWxhYmxlXG4gICAgaWYgKG9wTmFtZSBpbiBvcGVyYXRpb25IYW5kbGVycykge1xuICAgICAgICBvcGVyYXRpb25IYW5kbGVyc1tvcE5hbWVdKG1hcCwgb3BlcmF0aW9uLnNsaWNlKDEpLCAoKSA9PiB7XG4gICAgICAgICAgICBkb25lQ2Iob3BJbmRleCk7XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIG1hcFtvcE5hbWVdKC4uLm9wZXJhdGlvbi5zbGljZSgxKSk7XG4gICAgICAgIGRvbmVDYihvcEluZGV4KTtcbiAgICB9XG59XG5cbmV4cG9ydCBjb25zdCBvcGVyYXRpb25IYW5kbGVycyA9IHtcbiAgICB3YWl0KG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgY29uc3Qgd2FpdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaWYgKG1hcC5sb2FkZWQoKSkge1xuICAgICAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtYXAub25jZSgncmVuZGVyJywgd2FpdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHdhaXQoKTtcbiAgICB9LFxuICAgIGNvbnRlbnRMb2FkKG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgaWYgKG1hcC5jb250ZW50TG9hZGVkKCkpIHtcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWFwLm9uY2UoJ2NvbnRlbnQubG9hZCcsIGRvbmVDYik7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGlkbGUobWFwLCBwYXJhbXMsIGRvbmVDYikge1xuICAgICAgICBjb25zdCBpZGxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoIW1hcC5pc01vdmluZygpKSB7XG4gICAgICAgICAgICAgICAgZG9uZUNiKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG1hcC5vbmNlKCdyZW5kZXInLCBpZGxlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgaWRsZSgpO1xuICAgIH1cbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBhcHBseU9wZXJhdGlvbnMobWFwLCBvcGVyYXRpb25zLCBkb25lQ2IpIHtcbiAgICAvLyBObyBvcGVyYXRpb25zIHNwZWNpZmllZCwgZW5kIGltbWVkaWF0ZWx5IGFkbiBpbnZva2UgZG9uZUNiLlxuICAgIGlmICghb3BlcmF0aW9ucyB8fCBvcGVyYXRpb25zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBkb25lQ2IoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHJlY3Vyc2l2ZSBjaGFpblxuICAgIGNvbnN0IHNjaGVkdWxlTmV4dE9wZXJhdGlvbiA9IChsYXN0T3BJbmRleCkgPT4ge1xuICAgICAgICBpZiAobGFzdE9wSW5kZXggPT09IG9wZXJhdGlvbnMubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgLy8gU3RvcCByZWN1c2l2ZSBjaGFpbiB3aGVuIGF0IHRoZSBlbmQgb2YgdGhlIG9wZXJhdGlvbnNcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaGFuZGxlT3BlcmF0aW9uKG1hcCwgb3BlcmF0aW9ucywgKytsYXN0T3BJbmRleCwgc2NoZWR1bGVOZXh0T3BlcmF0aW9uKTtcbiAgICB9O1xuICAgIHNjaGVkdWxlTmV4dE9wZXJhdGlvbigtMSk7XG59XG4iLCIvLyBAZmxvd1xuLyogZ2xvYmFsIG1hcGJveGdsOnJlYWRvbmx5ICovXG5pbXBvcnQgc3VpdGUgZnJvbSAnLi4vcGVyZm9ybWFuY2UtbWV0cmljcy9zdWl0ZS5qc29uJztcbmltcG9ydCB7YXBwbHlPcGVyYXRpb25zfSBmcm9tICcuLi8uLi90ZXN0L2ludGVncmF0aW9uL2xpYi9vcGVyYXRpb24taGFuZGxlcnMnO1xuXG4vL1VzZWQgdG8gd2FybS11cCB0aGUgYnJvd3NlciBjYWNoZSBmb3IgY29uc2lzdGVudCB0aWxlLWxvYWQgdGltaW5nc1xuY29uc3QgTlVNX1dBUk1VUF9SVU5TID0gNTtcblxuY29uc3QgTlVNX0FDVFVBTF9SVU5TID0gNTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJ1bk1ldHJpY3MoKSB7XG4gICAgY29uc3Qgc3VpdGVMaXN0ID0gW107XG4gICAgZm9yIChjb25zdCBydW5OYW1lIGluIHN1aXRlKSB7XG4gICAgICAgIHN1aXRlTGlzdC5wdXNoKHN1aXRlW3J1bk5hbWVdKTtcbiAgICB9XG4gICAgY29uc3QgdG90YWxSdW5zID0gTlVNX1dBUk1VUF9SVU5TICsgTlVNX0FDVFVBTF9SVU5TO1xuICAgIGxldCBjdXJySW5kZXggPSAwO1xuXG4gICAgY29uc3Qgc3RhcnRSdW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgZXhlY3V0ZVJ1bihzdWl0ZUxpc3RbMF0sIChtZXRyaWNzKSA9PiB7XG4gICAgICAgICAgICBpZiAoY3VyckluZGV4ID49IE5VTV9XQVJNVVBfUlVOUykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKG1ldHJpY3MpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjdXJySW5kZXgrKztcbiAgICAgICAgICAgIGlmIChjdXJySW5kZXggPCB0b3RhbFJ1bnMpIHtcbiAgICAgICAgICAgICAgICBzdGFydFJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuICAgIHN0YXJ0UnVuKCk7XG59XG5cbmZ1bmN0aW9uIGV4ZWN1dGVSdW4ob3B0aW9ucywgZmluaXNoQ2IpIHtcblxuICAgIC8vMS4gQ3JlYXRlIGFuZCBwb3NpdGlvbiB0aGUgY29udGFpbmVyLCBmbG9hdGluZyBhdCB0aGUgdG9wIGxlZnRcbiAgICBjb25zdCBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBjb250YWluZXIuc3R5bGUucG9zaXRpb24gPSAnZml4ZWQnO1xuICAgIGNvbnRhaW5lci5zdHlsZS5sZWZ0ID0gJzEwcHgnO1xuICAgIGNvbnRhaW5lci5zdHlsZS50b3AgPSAnMTBweCc7XG4gICAgY29udGFpbmVyLnN0eWxlLndpZHRoID0gYCR7b3B0aW9ucy53aWR0aH1weGA7XG4gICAgY29udGFpbmVyLnN0eWxlLmhlaWdodCA9IGAke29wdGlvbnMuaGVpZ2h0fXB4YDtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG5cbiAgICBjb25zdCBtYXBPcHRpb25zID0gcGFyc2VPcHRpb25zKGNvbnRhaW5lciwgb3B0aW9ucyk7XG4gICAgbGV0IG1hcCA9IG5ldyBtYXBib3hnbC5NYXAobWFwT3B0aW9ucyk7XG4gICAgbWFwLnJlcGFpbnQgPSB0cnVlO1xuICAgIGFwcGx5T3BlcmF0aW9ucyhtYXAsIG9wdGlvbnMub3BlcmF0aW9ucywgKCkgPT4ge1xuICAgICAgICBjb25zdCBtZXRyaWNzID0gbWFwLmV4dHJhY3RQZXJmb3JtYW5jZU1ldHJpY3MoKTtcbiAgICAgICAgbWFwLnJlbW92ZSgpO1xuICAgICAgICBtYXAgPSBudWxsO1xuICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGNvbnRhaW5lcik7XG4gICAgICAgIGZpbmlzaENiKG1ldHJpY3MpO1xuICAgIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJzZU9wdGlvbnMoY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgY29uc3QgY29weSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob3B0aW9ucykpO1xuICAgIGRlbGV0ZSBjb3B5LndpZHRoO1xuICAgIGRlbGV0ZSBjb3B5LmhlaWdodDtcbiAgICBkZWxldGUgY29weS5vcGVyYXRpb25zO1xuICAgIGNvcHkuY29udGFpbmVyID0gY29udGFpbmVyO1xuICAgIHJldHVybiBjb3B5O1xufVxuXG4iXSwibmFtZXMiOlsiY29uc3QiLCJsZXQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxTQUFTLGVBQWUsQ0FBQyxHQUFHLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUU7SUFDdkRBLElBQU0sU0FBUyxHQUFHLFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN0Q0EsSUFBTSxNQUFNLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDOztJQUU1QixJQUFJLE1BQU0sSUFBSSxpQkFBaUIsRUFBRTtRQUM3QixpQkFBaUIsQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsY0FBSztZQUNsRCxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7U0FDbkIsQ0FBQyxDQUFDO0tBQ04sTUFBTTtRQUNILEdBQUcsQ0FBQyxNQUFNLE9BQUMsQ0FBQyxLQUFHLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNuQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUM7S0FDbkI7Q0FDSjs7QUFFRCxBQUFPQSxJQUFNLGlCQUFpQixHQUFHO0lBQzdCLG1CQUFJLENBQUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUU7UUFDdEJBLElBQU0sSUFBSSxHQUFHLFdBQVc7WUFDcEIsSUFBSSxHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUU7Z0JB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 +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1zdWl0ZS5qcyIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC9pbnRlZ3JhdGlvbi9saWIvb3BlcmF0aW9uLWhhbmRsZXJzLmpzIiwiLi4vbGliL21ldHJpY3MtaGFybmVzcy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBoYW5kbGVPcGVyYXRpb24obWFwLCBvcGVyYXRpb25zLCBvcEluZGV4LCBkb25lQ2IpIHtcbiAgICBjb25zdCBvcGVyYXRpb24gPSBvcGVyYXRpb25zW29wSW5kZXhdO1xuICAgIGNvbnN0IG9wTmFtZSA9IG9wZXJhdGlvblswXTtcbiAgICAvL0RlbGVnYXRlIHRvIHNwZWNpYWwgaGFuZGxlciBpZiBvbmUgaXMgYXZhaWxhYmxlXG4gICAgaWYgKG9wTmFtZSBpbiBvcGVyYXRpb25IYW5kbGVycykge1xuICAgICAgICBvcGVyYXRpb25IYW5kbGVyc1tvcE5hbWVdKG1hcCwgb3BlcmF0aW9uLnNsaWNlKDEpLCAoKSA9PiB7XG4gICAgICAgICAgICBkb25lQ2Iob3BJbmRleCk7XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIG1hcFtvcE5hbWVdKC4uLm9wZXJhdGlvbi5zbGljZSgxKSk7XG4gICAgICAgIGRvbmVDYihvcEluZGV4KTtcbiAgICB9XG59XG5cbmV4cG9ydCBjb25zdCBvcGVyYXRpb25IYW5kbGVycyA9IHtcbiAgICB3YWl0KG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgY29uc3Qgd2FpdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaWYgKG1hcC5sb2FkZWQoKSkge1xuICAgICAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtYXAub25jZSgncmVuZGVyJywgd2FpdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHdhaXQoKTtcbiAgICB9LFxuICAgIGNvbnRlbnRMb2FkKG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgaWYgKG1hcC5jb250ZW50TG9hZGVkKCkpIHtcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWFwLm9uY2UoJ2NvbnRlbnQubG9hZCcsIGRvbmVDYik7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGlkbGUobWFwLCBwYXJhbXMsIGRvbmVDYikge1xuICAgICAgICBjb25zdCBpZGxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoIW1hcC5pc01vdmluZygpKSB7XG4gICAgICAgICAgICAgICAgZG9uZUNiKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG1hcC5vbmNlKCdyZW5kZXInLCBpZGxlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgaWRsZSgpO1xuICAgIH1cbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBhcHBseU9wZXJhdGlvbnMobWFwLCBvcGVyYXRpb25zLCBkb25lQ2IpIHtcbiAgICAvLyBObyBvcGVyYXRpb25zIHNwZWNpZmllZCwgZW5kIGltbWVkaWF0ZWx5IGFkbiBpbnZva2UgZG9uZUNiLlxuICAgIGlmICghb3BlcmF0aW9ucyB8fCBvcGVyYXRpb25zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBkb25lQ2IoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHJlY3Vyc2l2ZSBjaGFpblxuICAgIGNvbnN0IHNjaGVkdWxlTmV4dE9wZXJhdGlvbiA9IChsYXN0T3BJbmRleCkgPT4ge1xuICAgICAgICBpZiAobGFzdE9wSW5kZXggPT09IG9wZXJhdGlvbnMubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgLy8gU3RvcCByZWN1c2l2ZSBjaGFpbiB3aGVuIGF0IHRoZSBlbmQgb2YgdGhlIG9wZXJhdGlvbnNcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaGFuZGxlT3BlcmF0aW9uKG1hcCwgb3BlcmF0aW9ucywgKytsYXN0T3BJbmRleCwgc2NoZWR1bGVOZXh0T3BlcmF0aW9uKTtcbiAgICB9O1xuICAgIHNjaGVkdWxlTmV4dE9wZXJhdGlvbigtMSk7XG59XG4iLCIvLyBAZmxvd1xuLyogZ2xvYmFsIG1hcGJveGdsOnJlYWRvbmx5ICovXG5pbXBvcnQgc3VpdGUgZnJvbSAnLi4vcGVyZm9ybWFuY2UtbWV0cmljcy9zdWl0ZS5qc29uJztcbmltcG9ydCB7YXBwbHlPcGVyYXRpb25zfSBmcm9tICcuLi8uLi90ZXN0L2ludGVncmF0aW9uL2xpYi9vcGVyYXRpb24taGFuZGxlcnMnO1xuXG4vL1VzZWQgdG8gd2FybS11cCB0aGUgYnJvd3NlciBjYWNoZSBmb3IgY29uc2lzdGVudCB0aWxlLWxvYWQgdGltaW5nc1xuY29uc3QgTlVNX1dBUk1VUF9SVU5TID0gNTtcblxuY29uc3QgTlVNX0FDVFVBTF9SVU5TID0gNTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJ1bk1ldHJpY3MoKSB7XG4gICAgY29uc3Qgc3VpdGVMaXN0ID0gW107XG4gICAgZm9yIChjb25zdCBydW5OYW1lIGluIHN1aXRlKSB7XG4gICAgICAgIHN1aXRlTGlzdC5wdXNoKHN1aXRlW3J1bk5hbWVdKTtcbiAgICB9XG4gICAgY29uc3QgdG90YWxSdW5zID0gTlVNX1dBUk1VUF9SVU5TICsgTlVNX0FDVFVBTF9SVU5TO1xuICAgIGxldCBjdXJySW5kZXggPSAwO1xuXG4gICAgY29uc3Qgc3RhcnRSdW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgZXhlY3V0ZVJ1bihzdWl0ZUxpc3RbMF0sIChtZXRyaWNzKSA9PiB7XG4gICAgICAgICAgICBpZiAoY3VyckluZGV4ID49IE5VTV9XQVJNVVBfUlVOUykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKG1ldHJpY3MpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjdXJySW5kZXgrKztcbiAgICAgICAgICAgIGlmIChjdXJySW5kZXggPCB0b3RhbFJ1bnMpIHtcbiAgICAgICAgICAgICAgICBzdGFydFJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuICAgIHN0YXJ0UnVuKCk7XG59XG5cbmZ1bmN0aW9uIGV4ZWN1dGVSdW4ob3B0aW9ucywgZmluaXNoQ2IpIHtcblxuICAgIC8vMS4gQ3JlYXRlIGFuZCBwb3NpdGlvbiB0aGUgY29udGFpbmVyLCBmbG9hdGluZyBhdCB0aGUgdG9wIGxlZnRcbiAgICBjb25zdCBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBjb250YWluZXIuc3R5bGUucG9zaXRpb24gPSAnZml4ZWQnO1xuICAgIGNvbnRhaW5lci5zdHlsZS5sZWZ0ID0gJzEwcHgnO1xuICAgIGNvbnRhaW5lci5zdHlsZS50b3AgPSAnMTBweCc7XG4gICAgY29udGFpbmVyLnN0eWxlLndpZHRoID0gYCR7b3B0aW9ucy53aWR0aH1weGA7XG4gICAgY29udGFpbmVyLnN0eWxlLmhlaWdodCA9IGAke29wdGlvbnMuaGVpZ2h0fXB4YDtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG5cbiAgICBjb25zdCBtYXBPcHRpb25zID0gcGFyc2VPcHRpb25zKGNvbnRhaW5lciwgb3B0aW9ucyk7XG4gICAgbGV0IG1hcCA9IG5ldyBtYXBib3hnbC5NYXAobWFwT3B0aW9ucyk7XG4gICAgbWFwLnJlcGFpbnQgPSB0cnVlO1xuICAgIGFwcGx5T3BlcmF0aW9ucyhtYXAsIG9wdGlvbnMub3BlcmF0aW9ucywgKCkgPT4ge1xuICAgICAgICBjb25zdCBtZXRyaWNzID0gbWFwLmV4dHJhY3RQZXJmb3JtYW5jZU1ldHJpY3MoKTtcbiAgICAgICAgbWFwLnJlbW92ZSgpO1xuICAgICAgICBtYXAgPSBudWxsO1xuICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGNvbnRhaW5lcik7XG4gICAgICAgIGZpbmlzaENiKG1ldHJpY3MpO1xuICAgIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJzZU9wdGlvbnMoY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgY29uc3QgY29weSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob3B0aW9ucykpO1xuICAgIGRlbGV0ZSBjb3B5LndpZHRoO1xuICAgIGRlbGV0ZSBjb3B5LmhlaWdodDtcbiAgICBkZWxldGUgY29weS5vcGVyYXRpb25zO1xuICAgIGNvcHkuY29udGFpbmVyID0gY29udGFpbmVyO1xuICAgIHJldHVybiBjb3B5O1xufVxuXG4iXSwibmFtZXMiOlsiY29uc3QiLCJsZXQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsU0FBUyxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFO0lBQ3ZEQSxJQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdENBLElBQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQzs7SUFFNUIsSUFBSSxNQUFNLElBQUksaUJBQWlCLEVBQUU7UUFDN0IsaUJBQWlCLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLGNBQUs7WUFDbEQsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQ25CLENBQUMsQ0FBQztLQUNOLE1BQU07UUFDSCxHQUFHLENBQUMsTUFBTSxPQUFDLENBQUMsS0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0tBQ25CO0NBQ0o7O0FBRUQsQUFBT0EsSUFBTSxpQkFBaUIsR0FBRztJQUM3QixtQkFBSSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFO1FBQ3RCQSxJQUFNLElBQUksR0FBRyxXQUFXO1lBQ3BCLElBQUksR0FBRyxDQUFDLE1BQU0s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 diff --git a/bench/performance-metrics/suite.json b/bench/performance-metrics/suite.json index 3964917eac2..d8a1b29eb31 100644 --- a/bench/performance-metrics/suite.json +++ b/bench/performance-metrics/suite.json @@ -1,15 +1,20 @@ { - "streets-fly-to-sf-oakland": { + "slow-pans": { "width": 1024, "height": 1024, "center": [-122.397218, 37.791459], - "zoom": 18, + "zoom": 15, "style": "mapbox://styles/mapbox/streets-v11", "operations": [ ["contentLoad"], - ["flyTo", { - "center": [-122.271378, 37.804376], - "zoom": 17 + ["easeTo", { + "center": [-122.4556, 37.71272], + "duration": 10000 + }], + ["idle"], + ["easeTo", { + "center": [-122.459915, 37.779474], + "duration": 10000 }], ["idle"] ] From 91be5fff67f54b763415f2d194f6865133d8e8fa Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 5 Nov 2019 16:27:45 -0800 Subject: [PATCH 07/34] Refactor `generateFixtureJson` to take split root and suite paths --- test/integration/lib/generate-fixture-json.js | 16 +++++++++------- test/integration/testem.js | 11 ++++++----- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/test/integration/lib/generate-fixture-json.js b/test/integration/lib/generate-fixture-json.js index c279593b5ad..080b08d9069 100644 --- a/test/integration/lib/generate-fixture-json.js +++ b/test/integration/lib/generate-fixture-json.js @@ -10,15 +10,16 @@ exports.generateFixtureJson = generateFixtureJson; exports.getAllFixtureGlobs = getAllFixtureGlobs; /** - * Analyzes the contents of the specified `directory` ,and inlines + * Analyzes the contents of the specified `path.join(rootDirectory, suiteDirectory)` ,and inlines * the contents into a single json file which can then be imported and built into a bundle * to be shipped to the browser. * - * @param {string} directory + * @param {string} rootDirectory + * @param {string} suiteDirectory * @param {boolean} includeImages */ -function generateFixtureJson(directory, includeImages = false) { - const globs = getAllFixtureGlobs(directory); +function generateFixtureJson(rootDirectory, suiteDirectory, outputDirectory = 'test/integration/dist', includeImages = false) { + const globs = getAllFixtureGlobs(rootDirectory, suiteDirectory); const jsonPaths = globs[0]; const imagePaths = globs[1]; //Extract the filedata into a flat dictionary @@ -59,7 +60,7 @@ function generateFixtureJson(directory, includeImages = false) { // Re-nest by directory path, each directory path defines a testcase. const result = {}; for (const fullPath in allFiles) { - const testName = path.dirname(fullPath).replace('test/integration/', ''); + const testName = path.dirname(fullPath).replace(rootDirectory, ''); //Skip if test is malformed if (malformedTests[testName]) { continue; } @@ -73,7 +74,7 @@ function generateFixtureJson(directory, includeImages = false) { } const outputStr = JSON.stringify(result, null, 4); - const outputPath = path.join('test/integration/dist', OUTPUT_FILE); + const outputPath = path.join(outputDirectory, OUTPUT_FILE); return new Promise((resolve, reject) => { fs.writeFile(outputPath, outputStr, {encoding: 'utf8'}, (err) => { @@ -84,7 +85,8 @@ function generateFixtureJson(directory, includeImages = false) { }); } -function getAllFixtureGlobs(basePath) { +function getAllFixtureGlobs(rootDirectory, suiteDirectory) { + const basePath = path.join(rootDirectory, suiteDirectory); const jsonPaths = path.join(basePath, '/**/*.json'); const imagePaths = path.join(basePath, '/**/*.png'); diff --git a/test/integration/testem.js b/test/integration/testem.js index 09fe0135e9e..ec262ff7b0d 100644 --- a/test/integration/testem.js +++ b/test/integration/testem.js @@ -12,7 +12,8 @@ const notifier = require('node-notifier'); const rollupDevConfig = require('../../rollup.config').default; const rollupTestConfig = require('./rollup.config.test').default; -const fixturePath = 'test/integration/query-tests'; +const rootFixturePath = 'test/integration/'; +const suitePath = 'query-tests'; const fixtureBuildInterval = 2000; let beforeHookInvoked = false; @@ -79,7 +80,7 @@ module.exports = { // Retuns a promise that resolves when all artifacts are built function buildArtifactsCi() { //1. Compile fixture data into a json file, so it can be bundled - generateFixtureJson(fixturePath, {}); + generateFixtureJson(rootFixturePath, suitePath); //2. Build tape const tapePromise = buildTape(); //3. Build test artifacts in parallel @@ -94,15 +95,15 @@ function buildArtifactsDev() { return buildTape().then(() => { // A promise that resolves on the first build of fixtures.json return new Promise((resolve, reject) => { - fixtureWatcher = chokidar.watch(getAllFixtureGlobs(fixturePath)); + fixtureWatcher = chokidar.watch(getAllFixtureGlobs(rootFixturePath, suitePath)); let needsRebuild = false; fixtureWatcher.on('ready', () => { - generateFixtureJson(fixturePath); + generateFixtureJson(rootFixturePath, suitePath); //Throttle calls to `generateFixtureJson` to run every 2s setInterval(() => { if (needsRebuild) { - generateFixtureJson(fixturePath); + generateFixtureJson(rootFixturePath, suitePath); needsRebuild = false; } }, fixtureBuildInterval); From e43f15ef46d51079b38b96c9f9edfd8c018878ec Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 5 Nov 2019 19:01:37 -0800 Subject: [PATCH 08/34] Handle running of refactored metrics suite. --- bench/lib/build-fixtures.js | 41 +++++++++++++ bench/lib/metrics-harness.js | 60 +++++++++++++------ .../fast-fly-to/style.json | 23 +++++++ .../performance-metrics/slow-pans/style.json | 25 ++++++++ bench/performance-metrics/suite.json | 22 ------- package.json | 6 +- 6 files changed, 134 insertions(+), 43 deletions(-) create mode 100644 bench/lib/build-fixtures.js create mode 100644 bench/performance-metrics/fast-fly-to/style.json create mode 100644 bench/performance-metrics/slow-pans/style.json delete mode 100644 bench/performance-metrics/suite.json diff --git a/bench/lib/build-fixtures.js b/bench/lib/build-fixtures.js new file mode 100644 index 00000000000..8dc015dc7c6 --- /dev/null +++ b/bench/lib/build-fixtures.js @@ -0,0 +1,41 @@ +/* eslint-disable import/no-commonjs */ +const {generateFixtureJson, getAllFixtureGlobs} = require('../../test/integration/lib/generate-fixture-json'); +const chokidar = require('chokidar'); + +const rootFixturePath = 'bench/'; +const suitePath = 'performance-metrics'; +const outputDirectory = 'bench/dist'; +const colors = require('chalk'); + +const watch = process.argv.includes('--watch'); + +if (watch) { + const fixtureWatcher = chokidar.watch(getAllFixtureGlobs(rootFixturePath, suitePath)); + console.log(colors.yellow(`Building benchmark fixtures......`)); + let needsRebuild = false; + fixtureWatcher.on('ready', () => { + generateFixtureJson(rootFixturePath, suitePath, outputDirectory); + console.log(colors.green(`Successfully built benchmark fixtures into bench/dist/fixtures.json`)); + //Throttle calls to `generateFixtureJson` to run every 1s + setInterval(() => { + if (needsRebuild) { + generateFixtureJson(rootFixturePath, suitePath, outputDirectory); + console.log(colors.green(`Successfully built benchmark fixtures into bench/dist/fixtures.json`)); + needsRebuild = false; + } + }, 1000); + + //Flag needs rebuild when anything changes + fixtureWatcher.on('all', () => { + needsRebuild = true; + console.log(colors.yellow(`Building benchmark fixtures......`)); + }); + }); + fixtureWatcher.on('error', (e) => { + console.log(colors.red(`Error occurred while generating fixture: ${e}`)); + }); +} else { + generateFixtureJson(rootFixturePath, suitePath, outputDirectory); + console.log(colors.green(`Successfully built benchmark fixtures into bench/dist/fixtures.json`)); +} + diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index ec55f8e2f8b..285448784d3 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -1,6 +1,6 @@ // @flow /* global mapboxgl:readonly */ -import suite from '../performance-metrics/suite.json'; +import suite from '../dist/fixtures.json'; import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings @@ -14,38 +14,48 @@ export function runMetrics() { suiteList.push(suite[runName]); } const totalRuns = NUM_WARMUP_RUNS + NUM_ACTUAL_RUNS; - let currIndex = 0; + let currSuiteIndex = 0; + let runCtr = 0; const startRun = function() { - executeRun(suiteList[0], (metrics) => { - if (currIndex >= NUM_WARMUP_RUNS) { + executeRun(suiteList[currSuiteIndex], (metrics) => { + if (runCtr >= NUM_WARMUP_RUNS) { console.log(metrics); } - currIndex++; - if (currIndex < totalRuns) { - startRun(); + runCtr++; + + //Done with runs on this suite so reset state and move to next suite + if (runCtr === totalRuns) { + currSuiteIndex++; + runCtr = 0; + + //Last suite so exit out + if (currSuiteIndex === suiteList.length) { + return; + } } + startRun(); }); }; startRun(); } -function executeRun(options, finishCb) { +function executeRun(fixture, finishCb) { //1. Create and position the container, floating at the top left const container = document.createElement('div'); container.style.position = 'fixed'; container.style.left = '10px'; container.style.top = '10px'; - container.style.width = `${options.width}px`; - container.style.height = `${options.height}px`; + container.style.width = `${fixture.style.metadata.test.width}px`; + container.style.height = `${fixture.style.metadata.test.height}px`; document.body.appendChild(container); - const mapOptions = parseOptions(container, options); + const {mapOptions, operations} = parseFixture(container, fixture); let map = new mapboxgl.Map(mapOptions); map.repaint = true; - applyOperations(map, options.operations, () => { + applyOperations(map, operations, () => { const metrics = map.extractPerformanceMetrics(); map.remove(); map = null; @@ -54,12 +64,24 @@ function executeRun(options, finishCb) { }); } -function parseOptions(container, options) { - const copy = JSON.parse(JSON.stringify(options)); - delete copy.width; - delete copy.height; - delete copy.operations; - copy.container = container; - return copy; +function parseFixture(container, fixture) { + const isStyleURL = fixture.style['style-url'] != null; + + // Use the entire fixture as the style, if no explicit style-url is specified, else grab initial viewport state + // conditions from the style and pass them to the map constructor. + const style = isStyleURL ? fixture.style['style-url'] : fixture.style; + + const mapOptions = {style, container}; + const mapOptionsStyleOverlapParams = [ 'center', 'zoom', 'bearing', 'pitch']; + if (isStyleURL) { + for (const param of mapOptionsStyleOverlapParams) { + if (fixture.style[param] != null) { + mapOptions[param] = fixture.style[param]; + } + } + } + const operations = fixture.style.metadata.test.operations; + + return {mapOptions, operations}; } diff --git a/bench/performance-metrics/fast-fly-to/style.json b/bench/performance-metrics/fast-fly-to/style.json new file mode 100644 index 00000000000..5f69db9b8dd --- /dev/null +++ b/bench/performance-metrics/fast-fly-to/style.json @@ -0,0 +1,23 @@ +{ + "version": 8, + "center": [-122.397218, 37.791459], + "zoom": 15, + "style-url": "mapbox://styles/mapbox/streets-v11", + "metadata": { + "test": { + "width": 1024, + "height": 1024, + "operations": [ + ["contentLoad"], + ["flyTo", { + "center": [-122.4556, 37.71272] + }], + ["idle"], + ["flyTo", { + "center": [-122.459915, 37.779474] + }], + ["idle"] + ] + } + } +} \ No newline at end of file diff --git a/bench/performance-metrics/slow-pans/style.json b/bench/performance-metrics/slow-pans/style.json new file mode 100644 index 00000000000..0a824b4c580 --- /dev/null +++ b/bench/performance-metrics/slow-pans/style.json @@ -0,0 +1,25 @@ +{ + "version": 8, + "center": [-122.397218, 37.791459], + "zoom": 15, + "style-url": "mapbox://styles/mapbox/streets-v11", + "metadata": { + "test": { + "width": 1024, + "height": 1024, + "operations": [ + ["contentLoad"], + ["easeTo", { + "center": [-122.4556, 37.71272], + "duration": 10000 + }], + ["idle"], + ["easeTo", { + "center": [-122.459915, 37.779474], + "duration": 10000 + }], + ["idle"] + ] + } + } +} \ No newline at end of file diff --git a/bench/performance-metrics/suite.json b/bench/performance-metrics/suite.json deleted file mode 100644 index d8a1b29eb31..00000000000 --- a/bench/performance-metrics/suite.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "slow-pans": { - "width": 1024, - "height": 1024, - "center": [-122.397218, 37.791459], - "zoom": 15, - "style": "mapbox://styles/mapbox/streets-v11", - "operations": [ - ["contentLoad"], - ["easeTo", { - "center": [-122.4556, 37.71272], - "duration": 10000 - }], - ["idle"], - ["easeTo", { - "center": [-122.459915, 37.779474], - "duration": 10000 - }], - ["idle"] - ] - } -} \ No newline at end of file diff --git a/package.json b/package.json index d77c49baf7f..feaab4571a6 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,8 @@ "build-prod-min": "rollup -c --environment BUILD:production,MINIFY:true", "build-csp": "rollup -c rollup.config.csp.js", "build-query-suite": "rollup -c test/integration/rollup.config.test.js", - "watch-metrics-suite": "rollup -c bench/rollup.config.metrics.js --watch", + "watch-metrics": "rollup -c bench/rollup.config.metrics.js --watch", + "watch-metrics-fixtures": "node bench/lib/build-fixtures.js --watch", "build-flow-types": "cp build/mapbox-gl.js.flow dist/mapbox-gl.js.flow && cp build/mapbox-gl.js.flow dist/mapbox-gl-dev.js.flow", "build-css": "postcss -o dist/mapbox-gl.css src/css/mapbox-gl.css", "build-style-spec": "cd src/style-spec && npm run build && cd ../.. && mkdir -p dist/style-spec && cp src/style-spec/dist/* dist/style-spec", @@ -124,7 +125,8 @@ "start-server": "st --no-cache -H 0.0.0.0 --port 9966 --index index.html .", "start": "run-p build-token watch-css watch-query watch-benchmarks start-server", "start-debug": "run-p build-token watch-css watch-query start-server", - "start-bench": "run-p build-token watch-benchmarks start-server", + "start-bench": "run-p build-token watch-metrics start-server", + "start-metrics": "run-p build-token watch-metrics-fixtures watch-metrics start-server", "start-release": "run-s build-token build-prod-min build-css print-release-url start-server", "lint": "eslint --cache --ignore-path .gitignore src test bench debug/*.html", "lint-docs": "documentation lint src/index.js", From 68786860d515c33fc838d02d0b3a5b5511a87df6 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 5 Nov 2019 19:04:44 -0800 Subject: [PATCH 09/34] Cleanup flow error and accidental checkin of generated file --- bench/dist/metrics-suite.js | 182 ----------------------------------- bench/lib/metrics-harness.js | 1 - 2 files changed, 183 deletions(-) delete mode 100644 bench/dist/metrics-suite.js diff --git a/bench/dist/metrics-suite.js b/bench/dist/metrics-suite.js deleted file mode 100644 index 75ecc1494e3..00000000000 --- a/bench/dist/metrics-suite.js +++ /dev/null @@ -1,182 +0,0 @@ -(function (global, factory) { -typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : -typeof define === 'function' && define.amd ? define(['exports'], factory) : -(global = global || self, factory(global.metrics = {})); -}(this, (function (exports) { 'use strict'; - -var suite = { - "slow-pans": { - width: 1024, - height: 1024, - center: [ - -122.397218, - 37.791459 - ], - zoom: 15, - style: "mapbox://styles/mapbox/streets-v11", - operations: [ - [ - "contentLoad" - ], - [ - "easeTo", - { - center: [ - -122.4556, - 37.71272 - ], - duration: 10000 - } - ], - [ - "idle" - ], - [ - "easeTo", - { - center: [ - -122.459915, - 37.779474 - ], - duration: 10000 - } - ], - [ - "idle" - ] - ] -} -}; - -function handleOperation(map, operations, opIndex, doneCb) { - var operation = operations[opIndex]; - var opName = operation[0]; - //Delegate to special handler if one is available - if (opName in operationHandlers) { - operationHandlers[opName](map, operation.slice(1), function () { - doneCb(opIndex); - }); - } else { - map[opName].apply(map, operation.slice(1)); - doneCb(opIndex); - } -} - -var operationHandlers = { - wait: function wait(map, params, doneCb) { - var wait = function() { - if (map.loaded()) { - doneCb(); - } else { - map.once('render', wait); - } - }; - wait(); - }, - contentLoad: function contentLoad(map, params, doneCb) { - if (map.contentLoaded()) { - doneCb(); - } else { - map.once('content.load', doneCb); - } - }, - idle: function idle(map, params, doneCb) { - var idle = function() { - if (!map.isMoving()) { - doneCb(); - } else { - map.once('render', idle); - } - }; - idle(); - } -}; - -function applyOperations(map, operations, doneCb) { - // No operations specified, end immediately adn invoke doneCb. - if (!operations || operations.length === 0) { - doneCb(); - return; - } - - // Start recursive chain - var scheduleNextOperation = function (lastOpIndex) { - if (lastOpIndex === operations.length - 1) { - // Stop recusive chain when at the end of the operations - doneCb(); - return; - } - - handleOperation(map, operations, ++lastOpIndex, scheduleNextOperation); - }; - scheduleNextOperation(-1); -} - -// - -//Used to warm-up the browser cache for consistent tile-load timings -var NUM_WARMUP_RUNS = 5; - -var NUM_ACTUAL_RUNS = 5; - -function runMetrics() { - var suiteList = []; - for (var runName in suite) { - suiteList.push(suite[runName]); - } - var totalRuns = NUM_WARMUP_RUNS + NUM_ACTUAL_RUNS; - var currIndex = 0; - - var startRun = function() { - executeRun(suiteList[0], function (metrics) { - if (currIndex >= NUM_WARMUP_RUNS) { - console.log(metrics); - } - - currIndex++; - if (currIndex < totalRuns) { - startRun(); - } - }); - }; - startRun(); -} - -function executeRun(options, finishCb) { - - //1. Create and position the container, floating at the top left - var container = document.createElement('div'); - container.style.position = 'fixed'; - container.style.left = '10px'; - container.style.top = '10px'; - container.style.width = (options.width) + "px"; - container.style.height = (options.height) + "px"; - document.body.appendChild(container); - - var mapOptions = parseOptions(container, options); - var map = new mapboxgl.Map(mapOptions); - map.repaint = true; - applyOperations(map, options.operations, function () { - var metrics = map.extractPerformanceMetrics(); - map.remove(); - map = null; - document.body.removeChild(container); - finishCb(metrics); - }); -} - -function parseOptions(container, options) { - var copy = JSON.parse(JSON.stringify(options)); - delete copy.width; - delete copy.height; - delete copy.operations; - copy.container = container; - return copy; -} - -exports.runMetrics = runMetrics; - -Object.defineProperty(exports, '__esModule', { value: true }); - -}))); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWV0cmljcy1zdWl0ZS5qcyIsInNvdXJjZXMiOlsiLi4vLi4vdGVzdC9pbnRlZ3JhdGlvbi9saWIvb3BlcmF0aW9uLWhhbmRsZXJzLmpzIiwiLi4vbGliL21ldHJpY3MtaGFybmVzcy5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJmdW5jdGlvbiBoYW5kbGVPcGVyYXRpb24obWFwLCBvcGVyYXRpb25zLCBvcEluZGV4LCBkb25lQ2IpIHtcbiAgICBjb25zdCBvcGVyYXRpb24gPSBvcGVyYXRpb25zW29wSW5kZXhdO1xuICAgIGNvbnN0IG9wTmFtZSA9IG9wZXJhdGlvblswXTtcbiAgICAvL0RlbGVnYXRlIHRvIHNwZWNpYWwgaGFuZGxlciBpZiBvbmUgaXMgYXZhaWxhYmxlXG4gICAgaWYgKG9wTmFtZSBpbiBvcGVyYXRpb25IYW5kbGVycykge1xuICAgICAgICBvcGVyYXRpb25IYW5kbGVyc1tvcE5hbWVdKG1hcCwgb3BlcmF0aW9uLnNsaWNlKDEpLCAoKSA9PiB7XG4gICAgICAgICAgICBkb25lQ2Iob3BJbmRleCk7XG4gICAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIG1hcFtvcE5hbWVdKC4uLm9wZXJhdGlvbi5zbGljZSgxKSk7XG4gICAgICAgIGRvbmVDYihvcEluZGV4KTtcbiAgICB9XG59XG5cbmV4cG9ydCBjb25zdCBvcGVyYXRpb25IYW5kbGVycyA9IHtcbiAgICB3YWl0KG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgY29uc3Qgd2FpdCA9IGZ1bmN0aW9uKCkge1xuICAgICAgICAgICAgaWYgKG1hcC5sb2FkZWQoKSkge1xuICAgICAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBtYXAub25jZSgncmVuZGVyJywgd2FpdCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHdhaXQoKTtcbiAgICB9LFxuICAgIGNvbnRlbnRMb2FkKG1hcCwgcGFyYW1zLCBkb25lQ2IpIHtcbiAgICAgICAgaWYgKG1hcC5jb250ZW50TG9hZGVkKCkpIHtcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWFwLm9uY2UoJ2NvbnRlbnQubG9hZCcsIGRvbmVDYik7XG4gICAgICAgIH1cbiAgICB9LFxuICAgIGlkbGUobWFwLCBwYXJhbXMsIGRvbmVDYikge1xuICAgICAgICBjb25zdCBpZGxlID0gZnVuY3Rpb24oKSB7XG4gICAgICAgICAgICBpZiAoIW1hcC5pc01vdmluZygpKSB7XG4gICAgICAgICAgICAgICAgZG9uZUNiKCk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIG1hcC5vbmNlKCdyZW5kZXInLCBpZGxlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgaWRsZSgpO1xuICAgIH1cbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBhcHBseU9wZXJhdGlvbnMobWFwLCBvcGVyYXRpb25zLCBkb25lQ2IpIHtcbiAgICAvLyBObyBvcGVyYXRpb25zIHNwZWNpZmllZCwgZW5kIGltbWVkaWF0ZWx5IGFkbiBpbnZva2UgZG9uZUNiLlxuICAgIGlmICghb3BlcmF0aW9ucyB8fCBvcGVyYXRpb25zLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICBkb25lQ2IoKTtcbiAgICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIFN0YXJ0IHJlY3Vyc2l2ZSBjaGFpblxuICAgIGNvbnN0IHNjaGVkdWxlTmV4dE9wZXJhdGlvbiA9IChsYXN0T3BJbmRleCkgPT4ge1xuICAgICAgICBpZiAobGFzdE9wSW5kZXggPT09IG9wZXJhdGlvbnMubGVuZ3RoIC0gMSkge1xuICAgICAgICAgICAgLy8gU3RvcCByZWN1c2l2ZSBjaGFpbiB3aGVuIGF0IHRoZSBlbmQgb2YgdGhlIG9wZXJhdGlvbnNcbiAgICAgICAgICAgIGRvbmVDYigpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaGFuZGxlT3BlcmF0aW9uKG1hcCwgb3BlcmF0aW9ucywgKytsYXN0T3BJbmRleCwgc2NoZWR1bGVOZXh0T3BlcmF0aW9uKTtcbiAgICB9O1xuICAgIHNjaGVkdWxlTmV4dE9wZXJhdGlvbigtMSk7XG59XG4iLCIvLyBAZmxvd1xuLyogZ2xvYmFsIG1hcGJveGdsOnJlYWRvbmx5ICovXG5pbXBvcnQgc3VpdGUgZnJvbSAnLi4vcGVyZm9ybWFuY2UtbWV0cmljcy9zdWl0ZS5qc29uJztcbmltcG9ydCB7YXBwbHlPcGVyYXRpb25zfSBmcm9tICcuLi8uLi90ZXN0L2ludGVncmF0aW9uL2xpYi9vcGVyYXRpb24taGFuZGxlcnMnO1xuXG4vL1VzZWQgdG8gd2FybS11cCB0aGUgYnJvd3NlciBjYWNoZSBmb3IgY29uc2lzdGVudCB0aWxlLWxvYWQgdGltaW5nc1xuY29uc3QgTlVNX1dBUk1VUF9SVU5TID0gNTtcblxuY29uc3QgTlVNX0FDVFVBTF9SVU5TID0gNTtcblxuZXhwb3J0IGZ1bmN0aW9uIHJ1bk1ldHJpY3MoKSB7XG4gICAgY29uc3Qgc3VpdGVMaXN0ID0gW107XG4gICAgZm9yIChjb25zdCBydW5OYW1lIGluIHN1aXRlKSB7XG4gICAgICAgIHN1aXRlTGlzdC5wdXNoKHN1aXRlW3J1bk5hbWVdKTtcbiAgICB9XG4gICAgY29uc3QgdG90YWxSdW5zID0gTlVNX1dBUk1VUF9SVU5TICsgTlVNX0FDVFVBTF9SVU5TO1xuICAgIGxldCBjdXJySW5kZXggPSAwO1xuXG4gICAgY29uc3Qgc3RhcnRSdW4gPSBmdW5jdGlvbigpIHtcbiAgICAgICAgZXhlY3V0ZVJ1bihzdWl0ZUxpc3RbMF0sIChtZXRyaWNzKSA9PiB7XG4gICAgICAgICAgICBpZiAoY3VyckluZGV4ID49IE5VTV9XQVJNVVBfUlVOUykge1xuICAgICAgICAgICAgICAgIGNvbnNvbGUubG9nKG1ldHJpY3MpO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjdXJySW5kZXgrKztcbiAgICAgICAgICAgIGlmIChjdXJySW5kZXggPCB0b3RhbFJ1bnMpIHtcbiAgICAgICAgICAgICAgICBzdGFydFJ1bigpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICB9O1xuICAgIHN0YXJ0UnVuKCk7XG59XG5cbmZ1bmN0aW9uIGV4ZWN1dGVSdW4ob3B0aW9ucywgZmluaXNoQ2IpIHtcblxuICAgIC8vMS4gQ3JlYXRlIGFuZCBwb3NpdGlvbiB0aGUgY29udGFpbmVyLCBmbG9hdGluZyBhdCB0aGUgdG9wIGxlZnRcbiAgICBjb25zdCBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdkaXYnKTtcbiAgICBjb250YWluZXIuc3R5bGUucG9zaXRpb24gPSAnZml4ZWQnO1xuICAgIGNvbnRhaW5lci5zdHlsZS5sZWZ0ID0gJzEwcHgnO1xuICAgIGNvbnRhaW5lci5zdHlsZS50b3AgPSAnMTBweCc7XG4gICAgY29udGFpbmVyLnN0eWxlLndpZHRoID0gYCR7b3B0aW9ucy53aWR0aH1weGA7XG4gICAgY29udGFpbmVyLnN0eWxlLmhlaWdodCA9IGAke29wdGlvbnMuaGVpZ2h0fXB4YDtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG5cbiAgICBjb25zdCBtYXBPcHRpb25zID0gcGFyc2VPcHRpb25zKGNvbnRhaW5lciwgb3B0aW9ucyk7XG4gICAgbGV0IG1hcCA9IG5ldyBtYXBib3hnbC5NYXAobWFwT3B0aW9ucyk7XG4gICAgbWFwLnJlcGFpbnQgPSB0cnVlO1xuICAgIGFwcGx5T3BlcmF0aW9ucyhtYXAsIG9wdGlvbnMub3BlcmF0aW9ucywgKCkgPT4ge1xuICAgICAgICBjb25zdCBtZXRyaWNzID0gbWFwLmV4dHJhY3RQZXJmb3JtYW5jZU1ldHJpY3MoKTtcbiAgICAgICAgbWFwLnJlbW92ZSgpO1xuICAgICAgICBtYXAgPSBudWxsO1xuICAgICAgICBkb2N1bWVudC5ib2R5LnJlbW92ZUNoaWxkKGNvbnRhaW5lcik7XG4gICAgICAgIGZpbmlzaENiKG1ldHJpY3MpO1xuICAgIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJzZU9wdGlvbnMoY29udGFpbmVyLCBvcHRpb25zKSB7XG4gICAgY29uc3QgY29weSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkob3B0aW9ucykpO1xuICAgIGRlbGV0ZSBjb3B5LndpZHRoO1xuICAgIGRlbGV0ZSBjb3B5LmhlaWdodDtcbiAgICBkZWxldGUgY29weS5vcGVyYXRpb25zO1xuICAgIGNvcHkuY29udGFpbmVyID0gY29udGFpbmVyO1xuICAgIHJldHVybiBjb3B5O1xufVxuXG4iXSwibmFtZXMiOlsiY29uc3QiLCJsZXQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsU0FBUyxlQUFlLENBQUMsR0FBRyxFQUFFLFVBQVUsRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFO0lBQ3ZEQSxJQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdENBLElBQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQzs7SUFFNUIsSUFBSSxNQUFNLElBQUksaUJBQWlCLEVBQUU7UUFDN0IsaUJBQWlCLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLGNBQUs7WUFDbEQsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1NBQ25CLENBQUMsQ0FBQztLQUNOLE1BQU07UUFDSCxHQUFHLENBQUMsTUFBTSxPQUFDLENBQUMsS0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0tBQ25CO0NBQ0o7O0FBRUQsQUFBT0EsSUFBTSxpQkFBaUIsR0FBRztJQUM3QixtQkFBSSxDQUFDLEdBQUcsRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFO1FBQ3RCQSxJQUFNLElBQUksR0FBRyxXQUFXO1lBQ3BCLElBQUksR0FBRyxDQUFDLE1BQU0s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 diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index 285448784d3..db4ec463781 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -1,4 +1,3 @@ -// @flow /* global mapboxgl:readonly */ import suite from '../dist/fixtures.json'; import {applyOperations} from '../../test/integration/lib/operation-handlers'; From 2a6c7eced8d3b12947bd4d6f4503935a1f621ff0 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Tue, 5 Nov 2019 19:11:30 -0800 Subject: [PATCH 10/34] Fix lint fail due to missing fixtures.json --- bench/lib/metrics-harness.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index db4ec463781..6dba1f4228f 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -1,4 +1,6 @@ /* global mapboxgl:readonly */ +/* eslint-disable import/no-unresolved */ +// fixtures.json is automatically generated before this file gets built import suite from '../dist/fixtures.json'; import {applyOperations} from '../../test/integration/lib/operation-handlers'; From cbbbbfc523ed78bb0d8d03de2efabe825e55f198 Mon Sep 17 00:00:00 2001 From: Andrew Harvey Date: Thu, 7 Nov 2019 12:52:18 +1100 Subject: [PATCH 11/34] min and max pitch options (#8834) --- src/geo/transform.js | 25 +++++++- src/ui/map.js | 98 ++++++++++++++++++++++++++++++- test/unit/geo/transform.test.js | 3 + test/unit/ui/camera.test.js | 8 +-- test/unit/ui/map.test.js | 100 +++++++++++++++++++++++++++++++- 5 files changed, 223 insertions(+), 11 deletions(-) diff --git a/src/geo/transform.js b/src/geo/transform.js index 9d73c0af302..25fe5c948a8 100644 --- a/src/geo/transform.js +++ b/src/geo/transform.js @@ -46,12 +46,14 @@ class Transform { _renderWorldCopies: boolean; _minZoom: number; _maxZoom: number; + _minPitch: number; + _maxPitch: number; _center: LngLat; _constraining: boolean; _posMatrixCache: {[number]: Float32Array}; _alignedPosMatrixCache: {[number]: Float32Array}; - constructor(minZoom: ?number, maxZoom: ?number, renderWorldCopies: boolean | void) { + constructor(minZoom: ?number, maxZoom: ?number, minPitch: ?number, maxPitch: ?number, renderWorldCopies: boolean | void) { this.tileSize = 512; // constant this.maxValidLatitude = 85.051129; // constant @@ -59,6 +61,9 @@ class Transform { this._minZoom = minZoom || 0; this._maxZoom = maxZoom || 22; + this._minPitch = (minPitch === undefined || minPitch === null) ? 0 : minPitch; + this._maxPitch = (maxPitch === undefined || maxPitch === null) ? 60 : maxPitch; + this.setMaxBounds(); this.width = 0; @@ -74,7 +79,7 @@ class Transform { } clone(): Transform { - const clone = new Transform(this._minZoom, this._maxZoom, this._renderWorldCopies); + const clone = new Transform(this._minZoom, this._maxZoom, this._minPitch, this.maxPitch, this._renderWorldCopies); clone.tileSize = this.tileSize; clone.latRange = this.latRange; clone.width = this.width; @@ -103,6 +108,20 @@ class Transform { this.zoom = Math.min(this.zoom, zoom); } + get minPitch(): number { return this._minPitch; } + set minPitch(pitch: number) { + if (this._minPitch === pitch) return; + this._minPitch = pitch; + this.pitch = Math.max(this.pitch, pitch); + } + + get maxPitch(): number { return this._maxPitch; } + set maxPitch(pitch: number) { + if (this._maxPitch === pitch) return; + this._maxPitch = pitch; + this.pitch = Math.min(this.pitch, pitch); + } + get renderWorldCopies(): boolean { return this._renderWorldCopies; } set renderWorldCopies(renderWorldCopies?: ?boolean) { if (renderWorldCopies === undefined) { @@ -145,7 +164,7 @@ class Transform { return this._pitch / Math.PI * 180; } set pitch(pitch: number) { - const p = clamp(pitch, 0, 60) / 180 * Math.PI; + const p = clamp(pitch, this.minPitch, this.maxPitch) / 180 * Math.PI; if (this._pitch === p) return; this._unmodified = false; this._pitch = p; diff --git a/src/ui/map.js b/src/ui/map.js index 2427740485a..001ccc76e86 100755 --- a/src/ui/map.js +++ b/src/ui/map.js @@ -80,6 +80,8 @@ type MapOptions = { scrollZoom?: boolean, minZoom?: ?number, maxZoom?: ?number, + minPitch?: ?number, + maxPitch?: ?number, boxZoom?: boolean, dragRotate?: boolean, dragPan?: DragPanOptions, @@ -99,6 +101,11 @@ type MapOptions = { const defaultMinZoom = 0; const defaultMaxZoom = 22; + +// the default values, but also the valid range +const defaultMinPitch = 0; +const defaultMaxPitch = 60; + const defaultOptions = { center: [0, 0], zoom: 0, @@ -108,6 +115,9 @@ const defaultOptions = { minZoom: defaultMinZoom, maxZoom: defaultMaxZoom, + minPitch: defaultMinPitch, + maxPitch: defaultMaxPitch, + interactive: true, scrollZoom: true, boxZoom: true, @@ -150,6 +160,8 @@ const defaultOptions = { * @param {HTMLElement|string} options.container The HTML element in which Mapbox GL JS will render the map, or the element's string `id`. The specified element must have no children. * @param {number} [options.minZoom=0] The minimum zoom level of the map (0-24). * @param {number} [options.maxZoom=22] The maximum zoom level of the map (0-24). + * @param {number} [options.minPitch=0] The minimum pitch of the map (0-60). + * @param {number} [options.maxPitch=60] The maximum pitch of the map (0-60). * @param {Object|string} [options.style] The map's Mapbox style. This must be an a JSON object conforming to * the schema described in the [Mapbox Style Specification](https://mapbox.com/mapbox-gl-style-spec/), or a URL to * such JSON. @@ -329,10 +341,22 @@ class Map extends Camera { options = extend({}, defaultOptions, options); if (options.minZoom != null && options.maxZoom != null && options.minZoom > options.maxZoom) { - throw new Error(`maxZoom must be greater than minZoom`); + throw new Error(`maxZoom must be greater than or equal to minZoom`); + } + + if (options.minPitch != null && options.maxPitch != null && options.minPitch > options.maxPitch) { + throw new Error(`maxPitch must be greater than or equal to minPitch`); + } + + if (options.minPitch != null && options.minPitch < defaultMinPitch) { + throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`); + } + + if (options.maxPitch != null && options.maxPitch > defaultMaxPitch) { + throw new Error(`maxPitch must be less than or equal to ${defaultMaxPitch}`); } - const transform = new Transform(options.minZoom, options.maxZoom, options.renderWorldCopies); + const transform = new Transform(options.minZoom, options.maxZoom, options.minPitch, options.maxPitch, options.renderWorldCopies); super(transform, options); this._interactive = options.interactive; @@ -651,6 +675,76 @@ class Map extends Camera { */ getMaxZoom() { return this.transform.maxZoom; } + /** + * Sets or clears the map's minimum pitch. + * If the map's current pitch is lower than the new minimum, + * the map will pitch to the new minimum. + * + * @param {number | null | undefined} minPitch The minimum pitch to set (0-60). + * If `null` or `undefined` is provided, the function removes the current minimum pitch (i.e. sets it to 0). + * @returns {Map} `this` + */ + setMinPitch(minPitch?: ?number) { + + minPitch = minPitch === null || minPitch === undefined ? defaultMinPitch : minPitch; + + if (minPitch < defaultMinPitch) { + throw new Error(`minPitch must be greater than or equal to ${defaultMinPitch}`); + } + + if (minPitch >= defaultMinPitch && minPitch <= this.transform.maxPitch) { + this.transform.minPitch = minPitch; + this._update(); + + if (this.getPitch() < minPitch) this.setPitch(minPitch); + + return this; + + } else throw new Error(`minPitch must be between ${defaultMinPitch} and the current maxPitch, inclusive`); + } + + /** + * Returns the map's minimum allowable pitch. + * + * @returns {number} minPitch + */ + getMinPitch() { return this.transform.minPitch; } + + /** + * Sets or clears the map's maximum pitch. + * If the map's current pitch is higher than the new maximum, + * the map will pitch to the new maximum. + * + * @param {number | null | undefined} maxPitch The maximum pitch to set. + * If `null` or `undefined` is provided, the function removes the current maximum pitch (sets it to 60). + * @returns {Map} `this` + */ + setMaxPitch(maxPitch?: ?number) { + + maxPitch = maxPitch === null || maxPitch === undefined ? defaultMaxPitch : maxPitch; + + if (maxPitch > defaultMaxPitch) { + throw new Error(`maxPitch must be less than or equal to ${defaultMaxPitch}`); + } + + if (maxPitch >= this.transform.minPitch) { + this.transform.maxPitch = maxPitch; + this._update(); + + if (this.getPitch() > maxPitch) this.setPitch(maxPitch); + + return this; + + } else throw new Error(`maxPitch must be greater than the current minPitch`); + } + + /** + * Returns the map's maximum allowable pitch. + * + * @returns {number} maxPitch + */ + getMaxPitch() { return this.transform.maxPitch; } + /** * Returns the state of `renderWorldCopies`. If `true`, multiple copies of the world will be rendered side by side beyond -180 and 180 degrees longitude. If set to `false`: * - When the map is zoomed out far enough that a single representation of the world does not fill the map's entire diff --git a/test/unit/geo/transform.test.js b/test/unit/geo/transform.test.js index ac7fcbaa13d..bfb7f82e29c 100644 --- a/test/unit/geo/transform.test.js +++ b/test/unit/geo/transform.test.js @@ -16,6 +16,7 @@ test('transform', (t) => { t.equal(transform.worldSize, 512, 'worldSize'); t.equal(transform.width, 500, 'width'); t.equal(transform.minZoom, 0, 'minZoom'); + t.equal(transform.minPitch, 0, 'minPitch'); t.equal(transform.bearing, 0, 'bearing'); t.equal(transform.bearing = 1, 1, 'set bearing'); t.equal(transform.bearing, 1, 'bearing'); @@ -26,6 +27,8 @@ test('transform', (t) => { t.equal(transform.minZoom, 10); t.deepEqual(transform.center, {lng: 0, lat: 0}); t.equal(transform.maxZoom, 10); + t.equal(transform.minPitch = 10, 10); + t.equal(transform.maxPitch = 10, 10); t.equal(transform.size.equals(new Point(500, 500)), true); t.equal(transform.centerPoint.equals(new Point(250, 250)), true); t.equal(transform.scaleZoom(0), -Infinity); diff --git a/test/unit/ui/camera.test.js b/test/unit/ui/camera.test.js index a9f82c6192f..c801dd87f16 100644 --- a/test/unit/ui/camera.test.js +++ b/test/unit/ui/camera.test.js @@ -18,7 +18,7 @@ test('camera', (t) => { function createCamera(options) { options = options || {}; - const transform = new Transform(0, 20, options.renderWorldCopies); + const transform = new Transform(0, 20, 0, 60, options.renderWorldCopies); transform.resize(512, 512); const camera = attachSimulateFrame(new Camera(transform, {})) @@ -950,7 +950,7 @@ test('camera', (t) => { }); t.test('does not throw when cameras current zoom is above maxzoom and an offset creates infinite zoom out factor', (t) => { - const transform = new Transform(0, 20.9999, true); + const transform = new Transform(0, 20.9999, 0, 60, true); transform.resize(512, 512); const camera = attachSimulateFrame(new Camera(transform, {})) .jumpTo({zoom: 21, center:[0, 0]}); @@ -1518,7 +1518,7 @@ test('camera', (t) => { }); t.test('respects transform\'s maxZoom', (t) => { - const transform = new Transform(2, 10, false); + const transform = new Transform(2, 10, 0, 60, false); transform.resize(512, 512); const camera = attachSimulateFrame(new Camera(transform, {})); @@ -1544,7 +1544,7 @@ test('camera', (t) => { }); t.test('respects transform\'s minZoom', (t) => { - const transform = new Transform(2, 10, false); + const transform = new Transform(2, 10, 0, 60, false); transform.resize(512, 512); const camera = attachSimulateFrame(new Camera(transform, {})); diff --git a/test/unit/ui/map.test.js b/test/unit/ui/map.test.js index 23a5ea1c719..cbb5c512b43 100755 --- a/test/unit/ui/map.test.js +++ b/test/unit/ui/map.test.js @@ -807,14 +807,110 @@ test('Map', (t) => { t.test('throw on maxZoom smaller than minZoom at init', (t) => { t.throws(() => { createMap(t, {minZoom:10, maxZoom:5}); - }, new Error(`maxZoom must be greater than minZoom`)); + }, new Error(`maxZoom must be greater than or equal to minZoom`)); t.end(); }); t.test('throw on maxZoom smaller than minZoom at init with falsey maxZoom', (t) => { t.throws(() => { createMap(t, {minZoom:1, maxZoom:0}); - }, new Error(`maxZoom must be greater than minZoom`)); + }, new Error(`maxZoom must be greater than or equal to minZoom`)); + t.end(); + }); + + t.test('#setMinPitch', (t) => { + const map = createMap(t, {pitch: 20}); + map.setMinPitch(10); + map.setPitch(0); + t.equal(map.getPitch(), 10); + t.end(); + }); + + t.test('unset minPitch', (t) => { + const map = createMap(t, {minPitch: 20}); + map.setMinPitch(null); + map.setPitch(0); + t.equal(map.getPitch(), 0); + t.end(); + }); + + t.test('#getMinPitch', (t) => { + const map = createMap(t, {pitch: 0}); + t.equal(map.getMinPitch(), 0, 'returns default value'); + map.setMinPitch(10); + t.equal(map.getMinPitch(), 10, 'returns custom value'); + t.end(); + }); + + t.test('ignore minPitchs over maxPitch', (t) => { + const map = createMap(t, {pitch: 0, maxPitch: 10}); + t.throws(() => { + map.setMinPitch(20); + }); + map.setPitch(0); + t.equal(map.getPitch(), 0); + t.end(); + }); + + t.test('#setMaxPitch', (t) => { + const map = createMap(t, {pitch: 0}); + map.setMaxPitch(10); + map.setPitch(20); + t.equal(map.getPitch(), 10); + t.end(); + }); + + t.test('unset maxPitch', (t) => { + const map = createMap(t, {maxPitch:10}); + map.setMaxPitch(null); + map.setPitch(20); + t.equal(map.getPitch(), 20); + t.end(); + }); + + t.test('#getMaxPitch', (t) => { + const map = createMap(t, {pitch: 0}); + t.equal(map.getMaxPitch(), 60, 'returns default value'); + map.setMaxPitch(10); + t.equal(map.getMaxPitch(), 10, 'returns custom value'); + t.end(); + }); + + t.test('ignore maxPitchs over minPitch', (t) => { + const map = createMap(t, {minPitch:10}); + t.throws(() => { + map.setMaxPitch(0); + }); + map.setPitch(10); + t.equal(map.getPitch(), 10); + t.end(); + }); + + t.test('throw on maxPitch smaller than minPitch at init', (t) => { + t.throws(() => { + createMap(t, {minPitch: 10, maxPitch: 5}); + }, new Error(`maxPitch must be greater than or equal to minPitch`)); + t.end(); + }); + + t.test('throw on maxPitch smaller than minPitch at init with falsey maxPitch', (t) => { + t.throws(() => { + createMap(t, {minPitch: 1, maxPitch: 0}); + }, new Error(`maxPitch must be greater than or equal to minPitch`)); + t.end(); + }); + + t.test('throw on maxPitch greater than valid maxPitch at init', (t) => { + t.throws(() => { + createMap(t, {maxPitch: 90}); + }, new Error(`maxPitch must be less than or equal to 60`)); + t.end(); + }); + + t.test('throw on minPitch less than valid minPitch at init', (t) => { + t.throws(() => { + createMap(t, {minPitch: -10}); + }, new Error(`minPitch must be greater than or equal to 0`)); t.end(); }); From 364f8a4c2b8d86d37c96c797868cb16a86c03789 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 6 Nov 2019 18:30:33 -0800 Subject: [PATCH 12/34] Revert performance marks in createStyleLayer --- src/style/create_style_layer.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/style/create_style_layer.js b/src/style/create_style_layer.js index 24ed4a17751..aae084e6e8d 100644 --- a/src/style/create_style_layer.js +++ b/src/style/create_style_layer.js @@ -27,13 +27,10 @@ const subclasses = { }; export default function createStyleLayer(layer: LayerSpecification | CustomLayerInterface) { - performance.mark(layer.id); if (layer.type === 'custom') { return new CustomStyleLayer(layer); } else { - const layerInt = new subclasses[layer.type](layer); - performance.measure(`create:${layer.id}`, layer.id); - return layerInt; + return new subclasses[layer.type](layer); } } From 37eda90c55161e3d3dea76727271a441b45eeea4 Mon Sep 17 00:00:00 2001 From: Nicholas Latham Date: Fri, 8 Nov 2019 15:08:45 +0000 Subject: [PATCH 13/34] Change maximum of function_stop from 22 to 24 (#8908) --- src/style-spec/reference/v8.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/style-spec/reference/v8.json b/src/style-spec/reference/v8.json index 9aeaef52032..3b7c17d144e 100644 --- a/src/style-spec/reference/v8.json +++ b/src/style-spec/reference/v8.json @@ -2528,7 +2528,7 @@ "function_stop": { "type": "array", "minimum": 0, - "maximum": 22, + "maximum": 24, "value": [ "number", "color" From 4a8fe0cae867b1ee64b35cf137cea0e1d90b81df Mon Sep 17 00:00:00 2001 From: Tristen Brown Date: Fri, 8 Nov 2019 10:46:47 -0500 Subject: [PATCH 14/34] Upgrade @mapbox/gazetteer to v4.0.4 (#8955) --- bench/styles/benchmarks.js | 4 +- bench/versions/benchmarks.js | 8 ++-- package.json | 2 +- yarn.lock | 85 +++++++++++++++++++++++++++++++++--- 4 files changed, 87 insertions(+), 12 deletions(-) diff --git a/bench/styles/benchmarks.js b/bench/styles/benchmarks.js index aa9fd3aadcd..f72997a160a 100644 --- a/bench/styles/benchmarks.js +++ b/bench/styles/benchmarks.js @@ -1,7 +1,7 @@ import mapboxgl from '../../src'; import accessToken from '../lib/access_token'; import locationsWithTileID from '../lib/locations_with_tile_id'; -import styleBenchmarkLocations from '@mapbox/gazetteer/mapbox-streets/style-benchmark-locations.json'; +import {benchmark} from '@mapbox/gazetteer'; import StyleLayerCreate from '../benchmarks/style_layer_create'; import Validate from '../benchmarks/style_validate'; import Layout from '../benchmarks/layout'; @@ -11,7 +11,7 @@ import QueryBox from '../benchmarks/query_box'; import getWorkerPool from '../../src/util/global_worker_pool'; -const locations = locationsWithTileID(styleBenchmarkLocations.features); +const locations = locationsWithTileID(benchmark.features); mapboxgl.accessToken = accessToken; diff --git a/bench/versions/benchmarks.js b/bench/versions/benchmarks.js index acf2ca55e0b..90cce0c95ce 100644 --- a/bench/versions/benchmarks.js +++ b/bench/versions/benchmarks.js @@ -1,7 +1,7 @@ import mapboxgl from '../../src'; import accessToken from '../lib/access_token'; import locationsWithTileID from '../lib/locations_with_tile_id'; -import styleBenchmarkLocations from '@mapbox/gazetteer/mapbox-streets/style-benchmark-locations.json'; +import {benchmark} from '@mapbox/gazetteer'; import Layout from '../benchmarks/layout'; import LayoutDDS from '../benchmarks/layout_dds'; import SymbolLayout from '../benchmarks/symbol_layout'; @@ -21,7 +21,7 @@ import FilterEvaluate from '../benchmarks/filter_evaluate'; import getWorkerPool from '../../src/util/global_worker_pool'; -const styleLocations = locationsWithTileID(styleBenchmarkLocations.features); +const styleLocations = locationsWithTileID(benchmark.features); mapboxgl.accessToken = accessToken; @@ -29,9 +29,9 @@ window.mapboxglBenchmarks = window.mapboxglBenchmarks || {}; const version = process.env.BENCHMARK_VERSION; -function register(name, benchmark) { +function register(name, bench) { window.mapboxglBenchmarks[name] = window.mapboxglBenchmarks[name] || {}; - window.mapboxglBenchmarks[name][version] = benchmark; + window.mapboxglBenchmarks[name][version] = bench; } const style = 'mapbox://styles/mapbox/streets-v10'; diff --git a/package.json b/package.json index 541ea1fadfb..e0ca6bc7552 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ }, "devDependencies": { "@mapbox/flow-remove-types": "^1.3.0-await.upstream.2", - "@mapbox/gazetteer": "^3.1.2", + "@mapbox/gazetteer": "^4.0.4", "@mapbox/mapbox-gl-rtl-text": "^0.2.1", "@mapbox/mapbox-gl-test-suite": "file:test/integration", "@octokit/rest": "^16.30.1", diff --git a/yarn.lock b/yarn.lock index 7f810112023..629cad1493c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -954,10 +954,12 @@ pirates "^3.0.2" vlq "^0.2.1" -"@mapbox/gazetteer@^3.1.2": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@mapbox/gazetteer/-/gazetteer-3.2.0.tgz#2675cc56853e262ac55384572dacfec13d068c77" - integrity sha512-iXstzYyHQCHcdgN0ovVJ6uI0oj0+et/l4b361xSNeJuP9tUgfl61hB5wi+ps94DlpBaTt3SFDx9MKj5lOeBgeA== +"@mapbox/gazetteer@^4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@mapbox/gazetteer/-/gazetteer-4.0.4.tgz#511b059d5f2a58e0b4d7ec6c1562c846a888fb82" + integrity sha512-XzOaDedR2EAfjlVApDn+cV6Upi8tsa8J6fBRWHxzmG0ekdC03jvqXkOKPS/P4BkUDyBD6bMLMJ8WH5A0iZNmwQ== + dependencies: + "@mapbox/geojsonhint" "^2.2.0" "@mapbox/geojson-area@0.2.2": version "0.2.2" @@ -981,6 +983,17 @@ resolved "https://registry.yarnpkg.com/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz#9aecf642cb00eab1080a57c4f949a65b4a5846d6" integrity sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw== +"@mapbox/geojsonhint@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@mapbox/geojsonhint/-/geojsonhint-2.2.0.tgz#75ca94706e9a56e6debf4e1c78fabdc67978b883" + integrity sha512-8qQYRB+/2z2JsN5s6D0WAnpo69+3V3nvJsSFLwMB1dsaWz1V4oZeuoje9srbYAxxL8PXCwIywfhYa3GxOkBv5Q== + dependencies: + concat-stream "^1.6.1" + jsonlint-lines "1.7.1" + minimist "1.2.0" + vfile "^4.0.0" + vfile-reporter "^5.1.1" + "@mapbox/jsonlint-lines-primitives@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz#ce56e539f83552b58d10d672ea4d6fc9adc7b234" @@ -1224,6 +1237,11 @@ JSONStream@^1.0.3: jsonparse "^1.2.0" through ">=2.2.7 <3" +"JSV@>= 4.0.x": + version "4.0.2" + resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" + integrity sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c= + abab@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.2.tgz#a2fba1b122c69a85caa02d10f9270c7219709a9d" @@ -1363,6 +1381,11 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178" + integrity sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg= + ansicolors@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" @@ -2194,6 +2217,15 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f" + integrity sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8= + dependencies: + ansi-styles "~1.0.0" + has-color "~0.1.0" + strip-ansi "~0.1.0" + character-entities-html4@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.3.tgz#5ce6e01618e47048ac22f34f7f39db5c6fd679ef" @@ -4755,6 +4787,11 @@ has-binary2@~1.0.2: dependencies: isarray "2.0.1" +has-color@~0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" + integrity sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8= + has-cors@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" @@ -5769,6 +5806,14 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= +jsonlint-lines@1.7.1: + version "1.7.1" + resolved "https://registry.yarnpkg.com/jsonlint-lines/-/jsonlint-lines-1.7.1.tgz#507de680d3fb8c4be1641cc57d6f679f29f178ff" + integrity sha1-UH3mgNP7jEvhZBzFfW9nnynxeP8= + dependencies: + JSV ">= 4.0.x" + nomnom ">= 1.5.x" + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -6748,6 +6793,14 @@ node-releases@^1.1.38: dependencies: semver "^6.3.0" +"nomnom@>= 1.5.x": + version "1.8.1" + resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7" + integrity sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc= + dependencies: + chalk "~0.4.0" + underscore "~1.6.0" + noop-logger@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" @@ -9571,6 +9624,11 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" + integrity sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE= + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -9696,7 +9754,7 @@ supercluster@^7.0.0: dependencies: kdbush "^3.0.0" -supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: +supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -10265,6 +10323,11 @@ underscore@>=1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== +underscore@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8" + integrity sha1-izixDKze9jM3uLJOT/htRa6lKag= + unherit@^1.0.4: version "1.1.2" resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.2.tgz#14f1f397253ee4ec95cec167762e77df83678449" @@ -10574,6 +10637,18 @@ vfile-message@^2.0.0: "@types/unist" "^2.0.2" unist-util-stringify-position "^2.0.0" +vfile-reporter@^5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-5.1.2.tgz#80f1db5cbe8f9c12f2f30cce3e2cd18353a48519" + integrity sha512-b15sTuss1wOPWVlyWOvu+n6wGJ/eTYngz3uqMLimQvxZ+Q5oFQGYZZP1o3dR9sk58G5+wej0UPCZSwQBX/mzrQ== + dependencies: + repeat-string "^1.5.0" + string-width "^2.0.0" + supports-color "^5.0.0" + unist-util-stringify-position "^2.0.0" + vfile-sort "^2.1.2" + vfile-statistics "^1.1.0" + vfile-reporter@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/vfile-reporter/-/vfile-reporter-6.0.0.tgz#753119f51dec9289b7508b457afc0cddf5e07f2e" From da00339d942eee23df88e4d45af69cd050fe70d5 Mon Sep 17 00:00:00 2001 From: Tristen Brown Date: Fri, 8 Nov 2019 13:31:22 -0500 Subject: [PATCH 15/34] Get named export (#8957) --- bench/styles/benchmarks.js | 2 +- bench/versions/benchmarks.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bench/styles/benchmarks.js b/bench/styles/benchmarks.js index f72997a160a..d537bf05979 100644 --- a/bench/styles/benchmarks.js +++ b/bench/styles/benchmarks.js @@ -11,7 +11,7 @@ import QueryBox from '../benchmarks/query_box'; import getWorkerPool from '../../src/util/global_worker_pool'; -const locations = locationsWithTileID(benchmark.features); +const locations = locationsWithTileID(benchmark.styleBenchmarkLocations.features); mapboxgl.accessToken = accessToken; diff --git a/bench/versions/benchmarks.js b/bench/versions/benchmarks.js index 90cce0c95ce..e5d103a9d4a 100644 --- a/bench/versions/benchmarks.js +++ b/bench/versions/benchmarks.js @@ -21,7 +21,7 @@ import FilterEvaluate from '../benchmarks/filter_evaluate'; import getWorkerPool from '../../src/util/global_worker_pool'; -const styleLocations = locationsWithTileID(benchmark.features); +const styleLocations = locationsWithTileID(benchmark.styleBenchmarkLocations.features); mapboxgl.accessToken = accessToken; From 0abe854478d4197c1d15f4faab0a7650f0ba4c4c Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Nov 2019 10:14:33 -0500 Subject: [PATCH 16/34] fix codegen unknown type issue (#8959) fix #8958 --- build/generate-style-code.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/generate-style-code.js b/build/generate-style-code.js index 42dc2d48045..dac319d0e17 100644 --- a/build/generate-style-code.js +++ b/build/generate-style-code.js @@ -32,7 +32,7 @@ global.flowType = function (property) { return `Color`; case 'formatted': return `Formatted`; - case 'image': + case 'resolvedImage': return `ResolvedImage`; case 'array': if (property.length) { From d5248e19aee3ddeee83a823b056c9ca49248e87a Mon Sep 17 00:00:00 2001 From: Ansis Brammanis Date: Mon, 11 Nov 2019 15:22:11 -0500 Subject: [PATCH 17/34] dedup featureMap by moving it to the config set (#8965) move featureMap from ProgramConfiguration to ProgramConfigurationSet. All configurations within a set have the same vertex layout (because they go together with the same vertex buffers). This doesn't matter too much because the only time a set has more than one programconfiguration is when multiple layers have identical layout properties. The main goal here is to make the relationship a tiny bit clearer. --- src/data/program_configuration.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/data/program_configuration.js b/src/data/program_configuration.js index 04dd5c05cfb..3a0b7e78f9b 100644 --- a/src/data/program_configuration.js +++ b/src/data/program_configuration.js @@ -557,15 +557,11 @@ export default class ProgramConfiguration { layoutAttributes: Array; _buffers: Array; - _featureMap: FeaturePositionMap; - _bufferOffset: number; constructor() { this.binders = {}; this.cacheKey = ''; this._buffers = []; - this._featureMap = new FeaturePositionMap(); - this._bufferOffset = 0; } static createDynamic(layer: Layer, zoom: number, filterProperties: (string) => boolean) { @@ -617,10 +613,6 @@ export default class ProgramConfiguration { const binder = this.binders[property]; binder.populatePaintArray(newLength, feature, imagePositions, formattedSection); } - if (feature.id !== undefined) { - this._featureMap.add(+feature.id, index, this._bufferOffset, newLength); - } - this._bufferOffset = newLength; } setConstantPatternPositions(posTo: ImagePosition, posFrom: ImagePosition) { for (const property in this.binders) { @@ -629,10 +621,10 @@ export default class ProgramConfiguration { } } - updatePaintArrays(featureStates: FeatureStates, vtLayer: VectorTileLayer, layer: TypedStyleLayer, imagePositions: {[string]: ImagePosition}): boolean { + updatePaintArrays(featureStates: FeatureStates, featureMap: FeaturePositionMap, vtLayer: VectorTileLayer, layer: TypedStyleLayer, imagePositions: {[string]: ImagePosition}): boolean { let dirty: boolean = false; for (const id in featureStates) { - const positions = this._featureMap.getPositions(+id); + const positions = featureMap.getPositions(+id); for (const pos of positions) { const feature = vtLayer.feature(pos.index); @@ -734,6 +726,8 @@ export default class ProgramConfiguration { export class ProgramConfigurationSet { programConfigurations: {[string]: ProgramConfiguration}; needsUpload: boolean; + _featureMap: FeaturePositionMap; + _bufferOffset: number; constructor(layoutAttributes: Array, layers: $ReadOnlyArray, zoom: number, filterProperties: (string) => boolean = () => true) { this.programConfigurations = {}; @@ -742,18 +736,26 @@ export class ProgramConfigurationSet { this.programConfigurations[layer.id].layoutAttributes = layoutAttributes; } this.needsUpload = false; + this._featureMap = new FeaturePositionMap(); + this._bufferOffset = 0; } populatePaintArrays(length: number, feature: Feature, index: number, imagePositions: {[string]: ImagePosition}, formattedSection?: FormattedSection) { for (const key in this.programConfigurations) { this.programConfigurations[key].populatePaintArrays(length, feature, index, imagePositions, formattedSection); } + + if (feature.id !== undefined) { + this._featureMap.add(+feature.id, index, this._bufferOffset, length); + } + this._bufferOffset = length; + this.needsUpload = true; } updatePaintArrays(featureStates: FeatureStates, vtLayer: VectorTileLayer, layers: $ReadOnlyArray, imagePositions: {[string]: ImagePosition}) { for (const layer of layers) { - this.needsUpload = this.programConfigurations[layer.id].updatePaintArrays(featureStates, vtLayer, layer, imagePositions) || this.needsUpload; + this.needsUpload = this.programConfigurations[layer.id].updatePaintArrays(featureStates, this._featureMap, vtLayer, layer, imagePositions) || this.needsUpload; } } From 1dbf2c9b7337b4c8528730522ef8581ac8d0f657 Mon Sep 17 00:00:00 2001 From: ryanhamley Date: Mon, 11 Nov 2019 15:54:24 -0800 Subject: [PATCH 18/34] Begin integrating with Puppeteer --- bench/lib/metrics-harness.js | 3 +- bench/metrics-puppeteer.js | 82 ++++++++++++++++++++++++++++++++++ bench/metrics.html | 22 ++++----- bench/rollup.config.metrics.js | 2 +- package.json | 6 ++- rollup.config.js | 4 +- 6 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 bench/metrics-puppeteer.js diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index 6dba1f4228f..e1e4553274a 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -33,6 +33,7 @@ export function runMetrics() { //Last suite so exit out if (currSuiteIndex === suiteList.length) { + console.log('exit'); return; } } @@ -43,7 +44,6 @@ export function runMetrics() { } function executeRun(fixture, finishCb) { - //1. Create and position the container, floating at the top left const container = document.createElement('div'); container.style.position = 'fixed'; @@ -85,4 +85,3 @@ function parseFixture(container, fixture) { return {mapOptions, operations}; } - diff --git a/bench/metrics-puppeteer.js b/bench/metrics-puppeteer.js new file mode 100644 index 00000000000..e54eab53ce2 --- /dev/null +++ b/bench/metrics-puppeteer.js @@ -0,0 +1,82 @@ +/* eslint-disable import/no-commonjs */ + +const puppeteer = require('puppeteer'); +const fs = require('fs'); +const zlib = require('zlib'); +const mapboxGLJSSrc = fs.readFileSync('dist/mapbox-gl-dev.js', 'utf8'); +const mapboxMetricsSrc = fs.readFileSync('bench/dist/metrics-suite.js', 'utf8'); +const benchSrc = fs.readFileSync('bench/metrics.html', 'utf8'); +const {execSync} = require('child_process'); + +const benchHTML = benchSrc + .replace(/`) + .replace(/`); + +// function waitForConsole(page) { +// return new Promise((resolve) => { +// function onConsole(msg) { +// page.removeListener('console', onConsole); +// resolve(msg.text()); +// } +// page.on('console', onConsole); +// }); +// } + +async function countInstancesInMemory(page, prototype) { + // Query all buffer instances into an array + const instances = await page.queryObjects(prototype); + // Count amount of buffer objects in heap + const count = await page.evaluate((buffers) => buffers.length, instances); + await instances.dispose(); + await prototype.dispose(); + + return count; +} + +(async () => { + const browser = await puppeteer.launch({ + headless: false, + args: ['--no-sandbox', '--disable-setuid-sandbox'] + }); + const page = await browser.newPage(); + const messages = []; + + // console.log('collecting stats...'); + await page.setViewport({width: 600, height: 600, deviceScaleFactor: 2}); + await page.setContent(benchHTML); + + const onConsole = async (msg) => { + // Get a handle to the ArrayBuffer object prototype + const arrayBufferPrototype = page.evaluateHandle(() => ArrayBuffer.prototype); + const arrayBufferCount = countInstancesInMemory(page, arrayBufferPrototype); + + // Get a handle to the Object prototype + const objectPrototype = page.evaluateHandle(() => Object.prototype); + const objectCount = countInstancesInMemory(page, objectPrototype); + + const metrics = page.metrics(); + const promises = Promise.all([arrayBufferPrototype, arrayBufferCount, objectPrototype, objectCount, metrics]); + + promises.then((output) => { + messages.push(output); + }); + + if (msg.text() === 'exit') { + fs.writeFileSync('bench/dist/metrics-log.txt', JSON.stringify(messages)); + await page.close(); + await browser.close(); + } else { + messages.push(msg.text()); + } + }; + page.on('console', onConsole); + // const stats = JSON.parse(await waitForConsole(page)); + // stats["bundle_size"] = mapboxGLJSSrc.length + mapboxGLCSSSrc.length; + // stats["bundle_size_gz"] = zlib.gzipSync(mapboxGLJSSrc).length + zlib.gzipSync(mapboxGLCSSSrc).length; + // stats.dt = execSync('git show --no-patch --no-notes --pretty=\'%cI\' HEAD').toString().substring(0, 19); + // stats.commit = execSync('git rev-parse --short HEAD').toString().trim(); + // stats.message = execSync('git show -s --format=%s HEAD').toString().trim(); + // console.log(JSON.stringify(stats, null, 2)); + // + // fs.writeFileSync('data.json.gz', zlib.gzipSync(JSON.stringify(stats))); +})(); diff --git a/bench/metrics.html b/bench/metrics.html index 9948e328053..92e0d775b86 100644 --- a/bench/metrics.html +++ b/bench/metrics.html @@ -1,13 +1,15 @@ - - - - - - - + + + + + + + + + diff --git a/bench/rollup.config.metrics.js b/bench/rollup.config.metrics.js index 1e23ff77f82..3136f159d90 100644 --- a/bench/rollup.config.metrics.js +++ b/bench/rollup.config.metrics.js @@ -5,7 +5,7 @@ export default { output: { name: 'metrics', format: 'umd', - sourcemap: 'inline', + sourcemap: false, indent: false, file: 'bench/dist/metrics-suite.js' }, diff --git a/package.json b/package.json index fea9f3fc550..0ef66fc32c9 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,9 @@ "build-csp": "rollup -c rollup.config.csp.js", "build-query-suite": "rollup -c test/integration/rollup.config.test.js", "watch-metrics": "rollup -c bench/rollup.config.metrics.js --watch", + "build-metrics": "rollup -c bench/rollup.config.metrics.js", "watch-metrics-fixtures": "node bench/lib/build-fixtures.js --watch", + "build-metrics-fixtures": "node bench/lib/build-fixtures.js", "build-flow-types": "cp build/mapbox-gl.js.flow dist/mapbox-gl.js.flow && cp build/mapbox-gl.js.flow dist/mapbox-gl-dev.js.flow", "build-css": "postcss -o dist/mapbox-gl.css src/css/mapbox-gl.css", "build-style-spec": "cd src/style-spec && npm run build && cd ../.. && mkdir -p dist/style-spec && cp src/style-spec/dist/* dist/style-spec", @@ -127,8 +129,10 @@ "start-debug": "run-p build-token watch-css watch-dev start-server", "start-tests": "run-p build-token watch-css watch-query start-server", "start-bench": "run-p build-token watch-benchmarks start-server", - "start-metrics": "run-p build-token watch-metrics-fixtures watch-metrics start-server", + "start-metrics": "run-p watch-metrics-fixtures watch-metrics watch-dev start-server", "start-release": "run-s build-token build-prod-min build-css print-release-url start-server", + "run-metrics": "run-s build-dev build-metrics-fixtures build-metrics start-puppeteer", + "start-puppeteer": "node --inspect-brk bench/metrics-puppeteer.js", "lint": "eslint --cache --ignore-path .gitignore src test bench debug/*.html", "lint-docs": "documentation lint src/index.js", "lint-css": "stylelint 'src/css/mapbox-gl.css'", diff --git a/rollup.config.js b/rollup.config.js index b103700d52c..df64e05ab72 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -23,7 +23,7 @@ export default [{ output: { dir: 'rollup/build/mapboxgl', format: 'amd', - sourcemap: 'inline', + sourcemap: false, indent: false, chunkFileNames: 'shared.js' }, @@ -38,7 +38,7 @@ export default [{ name: 'mapboxgl', file: outputFile, format: 'umd', - sourcemap: production ? true : 'inline', + sourcemap: false, indent: false, intro: fs.readFileSync(require.resolve('./rollup/bundle_prelude.js'), 'utf8'), banner From 65293f7dfc1eb2b78e8734ef88019422fb6542bf Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Mon, 11 Nov 2019 18:11:56 -0800 Subject: [PATCH 19/34] - Fix pupeteer integrations - Add circle job to run metrics --- .circleci/config.yml | 15 ++ bench/dist/metrics-log.txt | 263 +++++++++++++++++++++++++++++++++++ bench/lib/metrics-harness.js | 7 +- bench/metrics-puppeteer.js | 42 +++--- package.json | 2 +- src/util/performance.js | 8 +- 6 files changed, 312 insertions(+), 25 deletions(-) create mode 100644 bench/dist/metrics-log.txt diff --git a/.circleci/config.yml b/.circleci/config.yml index a2837118f30..44a9ccc46d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,6 +48,12 @@ workflows: filters: tags: only: /.*/ + - collect-metrics: + requires: + - prepare + filters: + tags: + only: /.*/ - test-render: requires: - prepare @@ -198,6 +204,15 @@ jobs: at: . - run: yarn run test-unit + collect-metrics: + <<: *defaults + steps: + - attach_workspace: + at: . + - run: yarn run run-metrics + - store_artifacts: + path: "bench/dist/render-tests/metrics-log.txt" + test-render: <<: *defaults steps: diff --git a/bench/dist/metrics-log.txt b/bench/dist/metrics-log.txt new file mode 100644 index 00000000000..d4673c7aad5 --- /dev/null +++ b/bench/dist/metrics-log.txt @@ -0,0 +1,263 @@ +[ + "Failed to load resource: the server responded with a status of 404 (Not Found)", + [ + { + "Timestamp": 157555.570466, + "Documents": 3, + "Frames": 1, + "JSEventListeners": 30, + "Nodes": 43, + "LayoutCount": 4, + "RecalcStyleCount": 6, + "LayoutDuration": 0.000563, + "RecalcStyleDuration": 0.000869, + "ScriptDuration": 0.076864, + "TaskDuration": 0.123222, + "JSHeapUsedSize": 9885672, + "JSHeapTotalSize": 13938688 + } + ], + { + "name": "bench/performance-metrics/fast-fly-to", + "metrics": { + "loadTime": 1198.7100000260398, + "fullLoadTime": 1632.1550000284333, + "fps": 50.09157824352116, + "onePercentLowFps": 5.143812422224039 + } + }, + [ + { + "Timestamp": 157597.959473, + "Documents": 2, + "Frames": 2, + "JSEventListeners": 36, + "Nodes": 75, + "LayoutCount": 21, + "RecalcStyleCount": 51, + "LayoutDuration": 0.039526, + "RecalcStyleDuration": 0.009662, + "ScriptDuration": 21.903852, + "TaskDuration": 26.381299, + "JSHeapUsedSize": 67633920, + "JSHeapTotalSize": 96571392 + } + ], + { + "name": "bench/performance-metrics/fast-fly-to", + "metrics": { + "loadTime": 1268.6800000083167, + "fullLoadTime": 1701.18000000366, + "fps": 50.74459330171142, + "onePercentLowFps": 6.654540614476485 + } + }, + [ + { + "Timestamp": 157604.917494, + "Documents": 2, + "Frames": 2, + "JSEventListeners": 36, + "Nodes": 75, + "LayoutCount": 24, + "RecalcStyleCount": 59, + "LayoutDuration": 0.04003, + "RecalcStyleDuration": 0.010353, + "ScriptDuration": 25.309844, + "TaskDuration": 30.482658, + "JSHeapUsedSize": 62835624, + "JSHeapTotalSize": 93687808 + } + ], + { + "name": "bench/performance-metrics/fast-fly-to", + "metrics": { + "loadTime": 1226.040000008652, + "fullLoadTime": 1596.5399999986403, + "fps": 49.659530135442495, + "onePercentLowFps": 5.609101702494516 + } + }, + [ + { + "Timestamp": 157611.810745, + "Documents": 2, + "Frames": 2, + "JSEventListeners": 36, + "Nodes": 75, + "LayoutCount": 27, + "RecalcStyleCount": 67, + "LayoutDuration": 0.040619, + "RecalcStyleDuration": 0.011043, + "ScriptDuration": 28.911215, + "TaskDuration": 34.759203, + "JSHeapUsedSize": 58719808, + "JSHeapTotalSize": 91590656 + } + ], + { + "name": "bench/performance-metrics/fast-fly-to", + "metrics": { + "loadTime": 1414.9999999790452, + "fullLoadTime": 1779.179999983171, + "fps": 48.23035046430122, + "onePercentLowFps": 5.379670226385568 + } + }, + [ + { + "Timestamp": 157618.820695, + "Documents": 2, + "Frames": 2, + "JSEventListeners": 36, + "Nodes": 75, + "LayoutCount": 30, + "RecalcStyleCount": 75, + "LayoutDuration": 0.041069, + "RecalcStyleDuration": 0.01184, + "ScriptDuration": 32.369158, + "TaskDuration": 38.899358, + "JSHeapUsedSize": 54189456, + "JSHeapTotalSize": 88317952 + } + ], + { + "name": "bench/performance-metrics/fast-fly-to", + "metrics": { + "loadTime": 1155.174999992596, + "fullLoadTime": 1704.4450000103097, + "fps": 48.9834485217963, + "onePercentLowFps": 5.7788436532785905 + } + }, + [ + { + "Timestamp": 157625.803025, + "Documents": 2, + "Frames": 2, + "JSEventListeners": 36, + "Nodes": 75, + "LayoutCount": 33, + "RecalcStyleCount": 83, + "LayoutDuration": 0.041557, + "RecalcStyleDuration": 0.012646, + "ScriptDuration": 35.869482, + "TaskDuration": 43.16665, + "JSHeapUsedSize": 57960312, + "JSHeapTotalSize": 89493504 + } + ], + { + "name": "bench/performance-metrics/slow-pans", + "metrics": { + "loadTime": 1205.9749999898486, + "fullLoadTime": 1725.639999989653, + "fps": 56.74349933152391, + "onePercentLowFps": 13.110670447391433 + } + }, + [ + { + "Timestamp": 157755.755932, + "Documents": 3, + "Frames": 3, + "JSEventListeners": 34, + "Nodes": 71, + "LayoutCount": 50, + "RecalcStyleCount": 130, + "LayoutDuration": 0.044453, + "RecalcStyleDuration": 0.017802, + "ScriptDuration": 82.792259, + "TaskDuration": 99.772535, + "JSHeapUsedSize": 68773904, + "JSHeapTotalSize": 97619968 + } + ], + { + "name": "bench/performance-metrics/slow-pans", + "metrics": { + "loadTime": 1200.4200000083074, + "fullLoadTime": 1771.9550000037998, + "fps": 56.776200482442746, + "onePercentLowFps": 12.378280243326008 + } + }, + [ + { + "Timestamp": 157777.607595, + "Documents": 3, + "Frames": 3, + "JSEventListeners": 34, + "Nodes": 70, + "LayoutCount": 53, + "RecalcStyleCount": 138, + "LayoutDuration": 0.044924, + "RecalcStyleDuration": 0.018536, + "ScriptDuration": 90.547293, + "TaskDuration": 109.080892, + "JSHeapUsedSize": 61398752, + "JSHeapTotalSize": 93949952 + } + ], + { + "name": "bench/performance-metrics/slow-pans", + "metrics": { + "loadTime": 1098.2500000100117, + "fullLoadTime": 1463.770000002114, + "fps": 56.69477841095845, + "onePercentLowFps": 12.673468094982109 + } + }, + [ + { + "Timestamp": 157799.142135, + "Documents": 3, + "Frames": 3, + "JSEventListeners": 36, + "Nodes": 79, + "LayoutCount": 57, + "RecalcStyleCount": 148, + "LayoutDuration": 0.045524, + "RecalcStyleDuration": 0.019605, + "ScriptDuration": 98.243726, + "TaskDuration": 118.247566, + "JSHeapUsedSize": 60161456, + "JSHeapTotalSize": 92114944 + } + ], + { + "name": "bench/performance-metrics/slow-pans", + "metrics": { + "loadTime": 1221.8099999881815, + "fullLoadTime": 1770.6749999779277, + "fps": 56.606979488800604, + "onePercentLowFps": 13.148811676055805 + } + }, + [ + { + "Timestamp": 157820.95373, + "Documents": 3, + "Frames": 3, + "JSEventListeners": 34, + "Nodes": 71, + "LayoutCount": 59, + "RecalcStyleCount": 154, + "LayoutDuration": 0.045855, + "RecalcStyleDuration": 0.02011, + "ScriptDuration": 106.006306, + "TaskDuration": 127.56653, + "JSHeapUsedSize": 44255936, + "JSHeapTotalSize": 93687808 + } + ], + { + "name": "bench/performance-metrics/slow-pans", + "metrics": { + "loadTime": 1249.3799999938346, + "fullLoadTime": 1685.0200000044424, + "fps": 57.03787896619529, + "onePercentLowFps": 13.379940124948703 + } + } +] \ No newline at end of file diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index e1e4553274a..a4ec5640f93 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -20,8 +20,13 @@ export function runMetrics() { let runCtr = 0; const startRun = function() { executeRun(suiteList[currSuiteIndex], (metrics) => { + + if (runCtr >= NUM_WARMUP_RUNS) { - console.log(metrics); + console.log(JSON.stringify({ + name: suiteList[currSuiteIndex].style.metadata.test.testName, + metrics + })); } runCtr++; diff --git a/bench/metrics-puppeteer.js b/bench/metrics-puppeteer.js index e54eab53ce2..6bdaad4b012 100644 --- a/bench/metrics-puppeteer.js +++ b/bench/metrics-puppeteer.js @@ -2,15 +2,9 @@ const puppeteer = require('puppeteer'); const fs = require('fs'); -const zlib = require('zlib'); -const mapboxGLJSSrc = fs.readFileSync('dist/mapbox-gl-dev.js', 'utf8'); -const mapboxMetricsSrc = fs.readFileSync('bench/dist/metrics-suite.js', 'utf8'); -const benchSrc = fs.readFileSync('bench/metrics.html', 'utf8'); -const {execSync} = require('child_process'); +const st = require('st'); +const {createServer} = require('http'); -const benchHTML = benchSrc - .replace(/`) - .replace(/`); // function waitForConsole(page) { // return new Promise((resolve) => { @@ -34,39 +28,49 @@ async function countInstancesInMemory(page, prototype) { } (async () => { + const server = createServer(st({ + path: process.cwd(), + cache: false, + index: 'index.html' + })).listen(9966); + const browser = await puppeteer.launch({ - headless: false, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); const messages = []; // console.log('collecting stats...'); - await page.setViewport({width: 600, height: 600, deviceScaleFactor: 2}); - await page.setContent(benchHTML); + await page.setViewport({width: 1024, height: 2014, deviceScaleFactor: 2}); + await page.goto('http://localhost:9966/bench/metrics.html'); const onConsole = async (msg) => { // Get a handle to the ArrayBuffer object prototype - const arrayBufferPrototype = page.evaluateHandle(() => ArrayBuffer.prototype); - const arrayBufferCount = countInstancesInMemory(page, arrayBufferPrototype); + // const arrayBufferPrototype = page.evaluateHandle(() => ArrayBuffer.prototype); + // const arrayBufferCount = countInstancesInMemory(page, arrayBufferPrototype); - // Get a handle to the Object prototype - const objectPrototype = page.evaluateHandle(() => Object.prototype); - const objectCount = countInstancesInMemory(page, objectPrototype); + // // Get a handle to the Object prototype + // const objectPrototype = page.evaluateHandle(() => Object.prototype); + // const objectCount = countInstancesInMemory(page, objectPrototype); const metrics = page.metrics(); - const promises = Promise.all([arrayBufferPrototype, arrayBufferCount, objectPrototype, objectCount, metrics]); + const promises = Promise.all([metrics]); promises.then((output) => { messages.push(output); }); if (msg.text() === 'exit') { - fs.writeFileSync('bench/dist/metrics-log.txt', JSON.stringify(messages)); + fs.writeFileSync('bench/dist/metrics-log.txt', JSON.stringify(messages, null, 2)); + server.close(); await page.close(); await browser.close(); } else { - messages.push(msg.text()); + try{ + messages.push(JSON.parse(msg.text())); + }catch(e){ + messages.push(msg.text()); + } } }; page.on('console', onConsole); diff --git a/package.json b/package.json index 0ef66fc32c9..9ce72f4d880 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "start-metrics": "run-p watch-metrics-fixtures watch-metrics watch-dev start-server", "start-release": "run-s build-token build-prod-min build-css print-release-url start-server", "run-metrics": "run-s build-dev build-metrics-fixtures build-metrics start-puppeteer", - "start-puppeteer": "node --inspect-brk bench/metrics-puppeteer.js", + "start-puppeteer": "node bench/metrics-puppeteer.js", "lint": "eslint --cache --ignore-path .gitignore src test bench debug/*.html", "lint-docs": "documentation lint src/index.js", "lint-css": "stylelint 'src/css/mapbox-gl.css'", diff --git a/src/util/performance.js b/src/util/performance.js index a07f13c2c6b..4113b9b3a38 100644 --- a/src/util/performance.js +++ b/src/util/performance.js @@ -8,7 +8,6 @@ const performance = window.performance; export type PerformanceMetrics = { loadTime: number, fullLoadTime: number, - frameTimes: Array, fps: number, onePercentLowFps: number } @@ -45,8 +44,10 @@ export const PerformanceUtils = { } }, getPerformanceMetrics(): PerformanceMetrics { - const loadTime = performance.measure('loadTime', PerformanceMarkers.create, PerformanceMarkers.load).duration; - const fullLoadTime = performance.measure('fullLoadTime', PerformanceMarkers.create, PerformanceMarkers.fullLoad).duration; + performance.measure('loadTime', PerformanceMarkers.create, PerformanceMarkers.load); + const loadTime = performance.getEntriesByName('loadTime')[0].duration; + performance.measure('fullLoadTime', PerformanceMarkers.create, PerformanceMarkers.fullLoad); + const fullLoadTime = performance.getEntriesByName('fullLoadTime')[0].duration; const totalFrames = frameTimes.length; const avgFrameTime = frameTimes.reduce((prev, curr) => prev + curr, 0) / totalFrames / 1000; @@ -61,7 +62,6 @@ export const PerformanceUtils = { return { loadTime, fullLoadTime, - frameTimes, fps, onePercentLowFps }; From a8e4b4275a4db58e3d7cc9a569029589b643cdf9 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Mon, 11 Nov 2019 18:22:41 -0800 Subject: [PATCH 20/34] Fix metrics path --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 44a9ccc46d6..799dbdad64d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -211,7 +211,7 @@ jobs: at: . - run: yarn run run-metrics - store_artifacts: - path: "bench/dist/render-tests/metrics-log.txt" + path: "bench/dist/metrics-log.txt" test-render: <<: *defaults From fc252f1695469656fd4b8995c11b968d21ad0c4e Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 13 Nov 2019 19:33:46 -0800 Subject: [PATCH 21/34] Cleanup of computed metrics: - Implement messaging system between browser<->puppeteer - Calculate mean and std-deviation of metrics for final output --- .circleci/config.yml | 2 +- bench/lib/metrics-harness.js | 24 +++--- bench/metrics-puppeteer.js | 144 +++++++++++++++++++++++++---------- 3 files changed, 116 insertions(+), 54 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 799dbdad64d..994d47d470c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -211,7 +211,7 @@ jobs: at: . - run: yarn run run-metrics - store_artifacts: - path: "bench/dist/metrics-log.txt" + path: "bench/dist/metrics-summary.json" test-render: <<: *defaults diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index a4ec5640f93..cbc40c1e6f4 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,7 +7,10 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = 5; -const NUM_ACTUAL_RUNS = 5; +const NUM_ACTUAL_RUNS = 15; + +// This namespace ised to store functions/data that can be accessed and invoked by pupeteer. +window.mbglMetrics = {}; export function runMetrics() { const suiteList = []; @@ -18,19 +21,20 @@ export function runMetrics() { let currSuiteIndex = 0; let runCtr = 0; - const startRun = function() { + const nextRun = window.mbglMetrics.nextRun = function() { executeRun(suiteList[currSuiteIndex], (metrics) => { - - if (runCtr >= NUM_WARMUP_RUNS) { - console.log(JSON.stringify({ + // Send a command to puppeteer using the `[PUPPETEER|]:` console message + console.log(`[PUPPETEER|RUN_FINISHED]:${JSON.stringify({ name: suiteList[currSuiteIndex].style.metadata.test.testName, metrics - })); + })}`); + } else { + // Move to next run automatically if we're still warming upz + nextRun(); } runCtr++; - //Done with runs on this suite so reset state and move to next suite if (runCtr === totalRuns) { currSuiteIndex++; @@ -38,14 +42,12 @@ export function runMetrics() { //Last suite so exit out if (currSuiteIndex === suiteList.length) { - console.log('exit'); - return; + console.log('[PUPPETEER|SUITE_FINISHED]'); } } - startRun(); }); }; - startRun(); + nextRun(); } function executeRun(fixture, finishCb) { diff --git a/bench/metrics-puppeteer.js b/bench/metrics-puppeteer.js index 6bdaad4b012..45907dd74c1 100644 --- a/bench/metrics-puppeteer.js +++ b/bench/metrics-puppeteer.js @@ -6,16 +6,6 @@ const st = require('st'); const {createServer} = require('http'); -// function waitForConsole(page) { -// return new Promise((resolve) => { -// function onConsole(msg) { -// page.removeListener('console', onConsole); -// resolve(msg.text()); -// } -// page.on('console', onConsole); -// }); -// } - async function countInstancesInMemory(page, prototype) { // Query all buffer instances into an array const instances = await page.queryObjects(prototype); @@ -27,6 +17,15 @@ async function countInstancesInMemory(page, prototype) { return count; } +async function getJsHeapStats(page) { + const {JSHeapUsedSize, JSHeapTotalSize} = await page.metrics(); + return { + memoryUsed: JSHeapUsedSize * 1e-6, + memoryTotal: JSHeapTotalSize * 1e-6, + percentMemoryUsed: JSHeapUsedSize / JSHeapTotalSize + }; +} + (async () => { const server = createServer(st({ path: process.cwd(), @@ -35,16 +34,70 @@ async function countInstancesInMemory(page, prototype) { })).listen(9966); const browser = await puppeteer.launch({ + headless:false, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); - const messages = []; - - // console.log('collecting stats...'); await page.setViewport({width: 1024, height: 2014, deviceScaleFactor: 2}); await page.goto('http://localhost:9966/bench/metrics.html'); + const runResults = {}; + const onConsole = async (msg) => { + const messageText = msg.text(); + + //Only process console message that contain the PUPETEER keybord + if (messageText.includes('PUPPETEER')) { + const commandTxt = messageText.substring( + messageText.indexOf('['), + messageText.indexOf(']') + ); + const command = commandTxt.split('|')[1]; + + switch (command) { + case 'RUN_FINISHED': + getJsHeapStats(page).then((memoryStats) => { + // 1. combine results from browser and pupeteer profiling + const json = JSON.parse(messageText.substring(messageText.indexOf(':') + 1, messageText.length)); + const runName = json.name; + const allStats = {...memoryStats, ...json.metrics}; + + if (!runResults[runName]) { + runResults[runName] = []; + } + runResults[runName].push(allStats); + + // 2. Message Browser to move to next run + page.evaluate(() => window.mbglMetrics.nextRun()); + }); + + break; + case 'SUITE_FINISHED': + await page.close(); + await browser.close(); + server.close(); + + const suiteSummary = {}; + for (const runName in runResults) { + const stats = runResults[runName]; + const avg = mean(stats); + const dev = stdDev(stats, avg); + + const runSummary = {}; + for (const statName in avg) { + runSummary[statName] = {average: avg[statName], standardDeviation: dev[statName]}; + } + suiteSummary[runName] = runSummary; + } + + console.log(suiteSummary); + fs.writeFileSync('bench/dist/metrics-summary.json', JSON.stringify(suiteSummary, null, 2)); + break; + default: + console.log(`${command} is unknown`); + } + + } // Get a handle to the ArrayBuffer object prototype // const arrayBufferPrototype = page.evaluateHandle(() => ArrayBuffer.prototype); // const arrayBufferCount = countInstancesInMemory(page, arrayBufferPrototype); @@ -52,35 +105,42 @@ async function countInstancesInMemory(page, prototype) { // // Get a handle to the Object prototype // const objectPrototype = page.evaluateHandle(() => Object.prototype); // const objectCount = countInstancesInMemory(page, objectPrototype); - - const metrics = page.metrics(); - const promises = Promise.all([metrics]); - - promises.then((output) => { - messages.push(output); - }); - - if (msg.text() === 'exit') { - fs.writeFileSync('bench/dist/metrics-log.txt', JSON.stringify(messages, null, 2)); - server.close(); - await page.close(); - await browser.close(); - } else { - try{ - messages.push(JSON.parse(msg.text())); - }catch(e){ - messages.push(msg.text()); - } - } }; page.on('console', onConsole); - // const stats = JSON.parse(await waitForConsole(page)); - // stats["bundle_size"] = mapboxGLJSSrc.length + mapboxGLCSSSrc.length; - // stats["bundle_size_gz"] = zlib.gzipSync(mapboxGLJSSrc).length + zlib.gzipSync(mapboxGLCSSSrc).length; - // stats.dt = execSync('git show --no-patch --no-notes --pretty=\'%cI\' HEAD').toString().substring(0, 19); - // stats.commit = execSync('git rev-parse --short HEAD').toString().trim(); - // stats.message = execSync('git show -s --format=%s HEAD').toString().trim(); - // console.log(JSON.stringify(stats, null, 2)); - // - // fs.writeFileSync('data.json.gz', zlib.gzipSync(JSON.stringify(stats))); })(); + +function mean(stats) { + const sum = {}; + for (const statName in stats[0]) { + sum[statName] = 0; + } + + for (const stat of stats) { + for (const statName in sum) { + sum[statName] += stat[statName]; + } + } + + for (const statName in sum) { + sum[statName] /= stats.length; + } + return sum; +} + +function stdDev(stats, avg) { + // square deviations + const deviations = stats.map((stat) => { + const dev = {}; + for (const statName in avg) { + dev[statName] = Math.pow(stat[statName] - avg[statName], 2); + } + return dev; + }); + + const variance = mean(deviations); + for (const statName in variance) { + variance[statName] = Math.sqrt(variance[statName]); + } + + return variance; +} From 9897c9ddd661b4fc8240aec47deded377f7e3e51 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 13 Nov 2019 20:55:52 -0800 Subject: [PATCH 22/34] - add timeout to metrics job --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 994d47d470c..f952cbb98b3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -210,6 +210,7 @@ jobs: - attach_workspace: at: . - run: yarn run run-metrics + - no_output_timeout: 20m - store_artifacts: path: "bench/dist/metrics-summary.json" From 89f8ed2ae0b5e63fed46b2b0f35e336d589c8642 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 13 Nov 2019 21:01:25 -0800 Subject: [PATCH 23/34] Fix timeout syntax --- .circleci/config.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f952cbb98b3..7f54f9fbdc4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -209,8 +209,9 @@ jobs: steps: - attach_workspace: at: . - - run: yarn run run-metrics - - no_output_timeout: 20m + - run: + command: yarn run run-metrics + no_output_timeout: 20m - store_artifacts: path: "bench/dist/metrics-summary.json" From 11307fe052eea4d984ca4f99825f2922280c219a Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Wed, 13 Nov 2019 22:31:36 -0800 Subject: [PATCH 24/34] Make it headless --- bench/metrics-puppeteer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/bench/metrics-puppeteer.js b/bench/metrics-puppeteer.js index 45907dd74c1..210672dae9b 100644 --- a/bench/metrics-puppeteer.js +++ b/bench/metrics-puppeteer.js @@ -34,7 +34,6 @@ async function getJsHeapStats(page) { })).listen(9966); const browser = await puppeteer.launch({ - headless:false, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); From 5a276fae5705d85e13d1461a5fcccee49313696a Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 14 Nov 2019 15:53:49 -0800 Subject: [PATCH 25/34] - Fix memory profiling happening after map destroy - add persisting of raw metrics --- .circleci/config.yml | 2 ++ bench/lib/metrics-harness.js | 47 +++++++++++++++++++++--------------- bench/metrics-puppeteer.js | 4 ++- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7f54f9fbdc4..cc3b29d5d76 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -214,6 +214,8 @@ jobs: no_output_timeout: 20m - store_artifacts: path: "bench/dist/metrics-summary.json" + - store_artifacts: + path: "bench/dist/metrics-raw.json" test-render: <<: *defaults diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index cbc40c1e6f4..3862cb9c7cb 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,12 +7,20 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = 5; -const NUM_ACTUAL_RUNS = 15; +const NUM_ACTUAL_RUNS = 2; // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; export function runMetrics() { + //Create and position the container, floating at the top left + const container = document.createElement('div'); + container.style.position = 'fixed'; + container.style.left = '10px'; + container.style.top = '10px'; + document.body.appendChild(container); + let map = null; + const suiteList = []; for (const runName in suite) { suiteList.push(suite[runName]); @@ -22,7 +30,23 @@ export function runMetrics() { let currSuiteIndex = 0; let runCtr = 0; const nextRun = window.mbglMetrics.nextRun = function() { - executeRun(suiteList[currSuiteIndex], (metrics) => { + // Teardown and re-create the map if one existed from a previous run + // We teardown the map lazily because puppeteer performs the memory profiling at the end of a run. + // and we want the map to still be active and holding onto its resources when that happens. + if (map) { + map.remove(); + map = null; + } + const fixture = suiteList[currSuiteIndex]; + const {mapOptions, operations} = parseFixture(container, fixture); + // Update size of container as per fixture + container.style.width = `${fixture.style.metadata.test.width}px`; + container.style.height = `${fixture.style.metadata.test.height}px`; + + map = new mapboxgl.Map(mapOptions); + map.repaint = true; + + executeRun(fixture, operations, map, container, (metrics) => { if (runCtr >= NUM_WARMUP_RUNS) { // Send a command to puppeteer using the `[PUPPETEER|]:` console message console.log(`[PUPPETEER|RUN_FINISHED]:${JSON.stringify({ @@ -30,7 +54,7 @@ export function runMetrics() { metrics })}`); } else { - // Move to next run automatically if we're still warming upz + // Move to next run automatically if we're still warming up nextRun(); } @@ -50,24 +74,9 @@ export function runMetrics() { nextRun(); } -function executeRun(fixture, finishCb) { - //1. Create and position the container, floating at the top left - const container = document.createElement('div'); - container.style.position = 'fixed'; - container.style.left = '10px'; - container.style.top = '10px'; - container.style.width = `${fixture.style.metadata.test.width}px`; - container.style.height = `${fixture.style.metadata.test.height}px`; - document.body.appendChild(container); - - const {mapOptions, operations} = parseFixture(container, fixture); - let map = new mapboxgl.Map(mapOptions); - map.repaint = true; +function executeRun(fixture, operations, map, container, finishCb) { applyOperations(map, operations, () => { const metrics = map.extractPerformanceMetrics(); - map.remove(); - map = null; - document.body.removeChild(container); finishCb(metrics); }); } diff --git a/bench/metrics-puppeteer.js b/bench/metrics-puppeteer.js index 210672dae9b..8f9eb0bf0d3 100644 --- a/bench/metrics-puppeteer.js +++ b/bench/metrics-puppeteer.js @@ -34,10 +34,11 @@ async function getJsHeapStats(page) { })).listen(9966); const browser = await puppeteer.launch({ + headless: false, args: ['--no-sandbox', '--disable-setuid-sandbox'] }); const page = await browser.newPage(); - await page.setViewport({width: 1024, height: 2014, deviceScaleFactor: 2}); + await page.setViewport({width: 1024, height: 1024, deviceScaleFactor: 2}); await page.goto('http://localhost:9966/bench/metrics.html'); const runResults = {}; @@ -91,6 +92,7 @@ async function getJsHeapStats(page) { console.log(suiteSummary); fs.writeFileSync('bench/dist/metrics-summary.json', JSON.stringify(suiteSummary, null, 2)); + fs.writeFileSync('bench/dist/metrics-raw.json', JSON.stringify(runResults, null, 2)); break; default: console.log(`${command} is unknown`); From 04d703ad0f302ea48030b52db0d0b0250943b783 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 14 Nov 2019 16:33:35 -0800 Subject: [PATCH 26/34] Up runs to 5 --- bench/lib/metrics-harness.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index 3862cb9c7cb..c174162c6ff 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,7 +7,7 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = 5; -const NUM_ACTUAL_RUNS = 2; +const NUM_ACTUAL_RUNS = 5; // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; From 84ad6b32db10b3aee78e9c6bfbe2832cd5d62cf8 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 14 Nov 2019 17:02:29 -0800 Subject: [PATCH 27/34] Up runs to 10 --- bench/lib/metrics-harness.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index c174162c6ff..f847059f1c1 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,7 +7,7 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = 5; -const NUM_ACTUAL_RUNS = 5; +const NUM_ACTUAL_RUNS = 10; // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; From cc1131529bbdf89010e86ec6f1cffe849c70005f Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Thu, 14 Nov 2019 17:21:58 -0800 Subject: [PATCH 28/34] Up runs to 15 --- bench/lib/metrics-harness.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index f847059f1c1..78e83d9fc2f 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,7 +7,7 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = 5; -const NUM_ACTUAL_RUNS = 10; +const NUM_ACTUAL_RUNS = 15; // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; From 05ecd973f680947747394f45276dc41ebed248c8 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 15 Nov 2019 18:39:46 -0800 Subject: [PATCH 29/34] Up runs to 20 --- bench/lib/metrics-harness.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index 78e83d9fc2f..6e02b92ce9c 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,7 +7,7 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = 5; -const NUM_ACTUAL_RUNS = 15; +const NUM_ACTUAL_RUNS = 20; // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; From 83250d743fe88ae6a145e5d366eaf70d92ee6f34 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 15 Nov 2019 18:55:16 -0800 Subject: [PATCH 30/34] Test data variablity based on warmup runs --- .circleci/config.yml | 69 +++++++++++++++++++++++++++++++++- bench/lib/metrics-harness.js | 4 +- bench/rollup.config.metrics.js | 8 +++- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index cc3b29d5d76..5a2e0c1e080 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,7 +48,25 @@ workflows: filters: tags: only: /.*/ - - collect-metrics: + - collect-metrics-one: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-five: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-ten: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-fifteen: requires: - prepare filters: @@ -204,12 +222,59 @@ jobs: at: . - run: yarn run test-unit - collect-metrics: + collect-metrics-one: + <<: *defaults + steps: + - attach_workspace: + at: . + - run: + environment: + NUM_WARMUP_RUNS: 1 + command: yarn run run-metrics + no_output_timeout: 20m + - store_artifacts: + path: "bench/dist/metrics-summary.json" + - store_artifacts: + path: "bench/dist/metrics-raw.json" + + collect-metrics-five: + <<: *defaults + steps: + - attach_workspace: + at: . + - run: + environment: + NUM_WARMUP_RUNS: 5 + command: yarn run run-metrics + no_output_timeout: 20m + - store_artifacts: + path: "bench/dist/metrics-summary.json" + - store_artifacts: + path: "bench/dist/metrics-raw.json" + + collect-metrics-ten: + <<: *defaults + steps: + - attach_workspace: + at: . + - run: + environment: + NUM_WARMUP_RUNS: 10 + command: yarn run run-metrics + no_output_timeout: 20m + - store_artifacts: + path: "bench/dist/metrics-summary.json" + - store_artifacts: + path: "bench/dist/metrics-raw.json" + + collect-metrics-fifteen: <<: *defaults steps: - attach_workspace: at: . - run: + environment: + NUM_WARMUP_RUNS: 15 command: yarn run run-metrics no_output_timeout: 20m - store_artifacts: diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index 6e02b92ce9c..5f4d8d21d93 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -5,9 +5,9 @@ import suite from '../dist/fixtures.json'; import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings -const NUM_WARMUP_RUNS = 5; +const NUM_WARMUP_RUNS = parseInt(process.env.NUM_WARMUP_RUNS); -const NUM_ACTUAL_RUNS = 20; +const NUM_ACTUAL_RUNS = 10; // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; diff --git a/bench/rollup.config.metrics.js b/bench/rollup.config.metrics.js index 3136f159d90..e56059b8fd5 100644 --- a/bench/rollup.config.metrics.js +++ b/bench/rollup.config.metrics.js @@ -1,4 +1,10 @@ import {plugins} from '../build/rollup_plugins'; +import replace from 'rollup-plugin-replace'; +const replaceConfig = { + 'process.env.NUM_WARMUP_RUNS': JSON.stringify(process.env.NUM_WARMUP_RUNS) +}; + +const allPlugins = plugins(false, false).concat(replace(replaceConfig)); export default { input: 'bench/lib/metrics-harness.js', @@ -9,6 +15,6 @@ export default { indent: false, file: 'bench/dist/metrics-suite.js' }, - plugins: plugins(false, false), + plugins: allPlugins, external: [ 'mapboxgl' ] }; From 7ce56bc697608e03817ece3e8b136d4413975b63 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Fri, 15 Nov 2019 23:53:33 -0800 Subject: [PATCH 31/34] Measure run-to-run variance --- .circleci/config.yml | 10 +++++++--- bench/lib/metrics-harness.js | 2 +- bench/rollup.config.metrics.js | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a2e0c1e080..364d8a755a8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -229,7 +229,8 @@ jobs: at: . - run: environment: - NUM_WARMUP_RUNS: 1 + NUM_ACTUAL_RUNS: 10 + NUM_WARMUP_RUNS: 10 command: yarn run run-metrics no_output_timeout: 20m - store_artifacts: @@ -244,7 +245,8 @@ jobs: at: . - run: environment: - NUM_WARMUP_RUNS: 5 + NUM_ACTUAL_RUNS: 10 + NUM_WARMUP_RUNS: 10 command: yarn run run-metrics no_output_timeout: 20m - store_artifacts: @@ -259,6 +261,7 @@ jobs: at: . - run: environment: + NUM_ACTUAL_RUNS: 10 NUM_WARMUP_RUNS: 10 command: yarn run run-metrics no_output_timeout: 20m @@ -274,7 +277,8 @@ jobs: at: . - run: environment: - NUM_WARMUP_RUNS: 15 + NUM_ACTUAL_RUNS: 10 + NUM_WARMUP_RUNS: 10 command: yarn run run-metrics no_output_timeout: 20m - store_artifacts: diff --git a/bench/lib/metrics-harness.js b/bench/lib/metrics-harness.js index 5f4d8d21d93..767873e5a31 100644 --- a/bench/lib/metrics-harness.js +++ b/bench/lib/metrics-harness.js @@ -7,7 +7,7 @@ import {applyOperations} from '../../test/integration/lib/operation-handlers'; //Used to warm-up the browser cache for consistent tile-load timings const NUM_WARMUP_RUNS = parseInt(process.env.NUM_WARMUP_RUNS); -const NUM_ACTUAL_RUNS = 10; +const NUM_ACTUAL_RUNS = parseInt(process.env.NUM_ACTUAL_RUNS); // This namespace ised to store functions/data that can be accessed and invoked by pupeteer. window.mbglMetrics = {}; diff --git a/bench/rollup.config.metrics.js b/bench/rollup.config.metrics.js index e56059b8fd5..ef30bf9e45a 100644 --- a/bench/rollup.config.metrics.js +++ b/bench/rollup.config.metrics.js @@ -1,7 +1,8 @@ import {plugins} from '../build/rollup_plugins'; import replace from 'rollup-plugin-replace'; const replaceConfig = { - 'process.env.NUM_WARMUP_RUNS': JSON.stringify(process.env.NUM_WARMUP_RUNS) + 'process.env.NUM_WARMUP_RUNS': JSON.stringify(process.env.NUM_WARMUP_RUNS), + 'process.env.NUM_ACTUAL_RUNS': JSON.stringify(process.env.NUM_ACTUAL_RUNS) }; const allPlugins = plugins(false, false).concat(replace(replaceConfig)); From bf72ebed84a7ceee2a9581fe1b6c008798842c92 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Sun, 17 Nov 2019 23:28:09 -0800 Subject: [PATCH 32/34] massive fan out of metrics jobs --- .circleci/config.yml | 220 ++++++++++++++++++++++++++++++++----------- 1 file changed, 164 insertions(+), 56 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 364d8a755a8..c35183a935a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,24 +54,120 @@ workflows: filters: tags: only: /.*/ + - collect-metrics-two: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-three: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-four: + requires: + - prepare + filters: + tags: + only: /.*/ - collect-metrics-five: requires: - prepare filters: tags: only: /.*/ + - collect-metrics-six: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-seven: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-eight: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-nine: + requires: + - prepare + filters: + tags: + only: /.*/ - collect-metrics-ten: requires: - prepare filters: tags: only: /.*/ + - collect-metrics-eleven: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-twelve: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-thirteen: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-fourteen: + requires: + - prepare + filters: + tags: + only: /.*/ - collect-metrics-fifteen: requires: - prepare filters: tags: only: /.*/ + - collect-metrics-sixteen: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-seventeen: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-eighteen: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-nineteen: + requires: + - prepare + filters: + tags: + only: /.*/ + - collect-metrics-twenty: + requires: + - prepare + filters: + tags: + only: /.*/ - test-render: requires: - prepare @@ -126,6 +222,22 @@ defaults: &defaults - image: circleci/node:10.16-browsers working_directory: ~/mapbox-gl-js +collect-metrics: + <<: *defaults + steps: + - attach_workspace: + at: . + - run: + environment: + NUM_ACTUAL_RUNS: 10 + NUM_WARMUP_RUNS: 10 + command: yarn run run-metrics + no_output_timeout: 20m + - store_artifacts: + path: "bench/dist/metrics-summary.json" + - store_artifacts: + path: "bench/dist/metrics-raw.json" + jobs: prepare: <<: *defaults @@ -223,68 +335,64 @@ jobs: - run: yarn run test-unit collect-metrics-one: - <<: *defaults - steps: - - attach_workspace: - at: . - - run: - environment: - NUM_ACTUAL_RUNS: 10 - NUM_WARMUP_RUNS: 10 - command: yarn run run-metrics - no_output_timeout: 20m - - store_artifacts: - path: "bench/dist/metrics-summary.json" - - store_artifacts: - path: "bench/dist/metrics-raw.json" + <<: *collect-metrics + + collect-metrics-two: + <<: *collect-metrics + + collect-metrics-three: + <<: *collect-metrics + + collect-metrics-four: + <<: *collect-metrics collect-metrics-five: - <<: *defaults - steps: - - attach_workspace: - at: . - - run: - environment: - NUM_ACTUAL_RUNS: 10 - NUM_WARMUP_RUNS: 10 - command: yarn run run-metrics - no_output_timeout: 20m - - store_artifacts: - path: "bench/dist/metrics-summary.json" - - store_artifacts: - path: "bench/dist/metrics-raw.json" + <<: *collect-metrics + + collect-metrics-six: + <<: *collect-metrics + + collect-metrics-seven: + <<: *collect-metrics + + collect-metrics-eight: + <<: *collect-metrics + + collect-metrics-nine: + <<: *collect-metrics collect-metrics-ten: - <<: *defaults - steps: - - attach_workspace: - at: . - - run: - environment: - NUM_ACTUAL_RUNS: 10 - NUM_WARMUP_RUNS: 10 - command: yarn run run-metrics - no_output_timeout: 20m - - store_artifacts: - path: "bench/dist/metrics-summary.json" - - store_artifacts: - path: "bench/dist/metrics-raw.json" + <<: *collect-metrics + + collect-metrics-eleven: + <<: *collect-metrics + + collect-metrics-twelve: + <<: *collect-metrics + + collect-metrics-thirteen: + <<: *collect-metrics + + collect-metrics-fourteen: + <<: *collect-metrics collect-metrics-fifteen: - <<: *defaults - steps: - - attach_workspace: - at: . - - run: - environment: - NUM_ACTUAL_RUNS: 10 - NUM_WARMUP_RUNS: 10 - command: yarn run run-metrics - no_output_timeout: 20m - - store_artifacts: - path: "bench/dist/metrics-summary.json" - - store_artifacts: - path: "bench/dist/metrics-raw.json" + <<: *collect-metrics + + collect-metrics-sixteen: + <<: *collect-metrics + + collect-metrics-seventeen: + <<: *collect-metrics + + collect-metrics-eighteen: + <<: *collect-metrics + + collect-metrics-nineteen: + <<: *collect-metrics + + collect-metrics-twenty: + <<: *collect-metrics test-render: <<: *defaults From 249425c923327d5b96ebe65f127209dd6b6cd613 Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Sun, 17 Nov 2019 23:29:40 -0800 Subject: [PATCH 33/34] fix spacing --- .circleci/config.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c35183a935a..adb5a824c91 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -224,19 +224,19 @@ defaults: &defaults collect-metrics: <<: *defaults - steps: - - attach_workspace: - at: . - - run: - environment: - NUM_ACTUAL_RUNS: 10 - NUM_WARMUP_RUNS: 10 - command: yarn run run-metrics - no_output_timeout: 20m - - store_artifacts: - path: "bench/dist/metrics-summary.json" - - store_artifacts: - path: "bench/dist/metrics-raw.json" + steps: + - attach_workspace: + at: . + - run: + environment: + NUM_ACTUAL_RUNS: 10 + NUM_WARMUP_RUNS: 10 + command: yarn run run-metrics + no_output_timeout: 20m + - store_artifacts: + path: "bench/dist/metrics-summary.json" + - store_artifacts: + path: "bench/dist/metrics-raw.json" jobs: prepare: From c37b83add2d15a80cc173203dcbacac1f34978eb Mon Sep 17 00:00:00 2001 From: Arindam Bose Date: Sun, 17 Nov 2019 23:30:25 -0800 Subject: [PATCH 34/34] add alias --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index adb5a824c91..f5e96ecbcb2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -222,7 +222,7 @@ defaults: &defaults - image: circleci/node:10.16-browsers working_directory: ~/mapbox-gl-js -collect-metrics: +collect-metrics: &collect-metrics <<: *defaults steps: - attach_workspace: