/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; self.onmessage = e => { const { id, task, args } = e.data; switch (task) { case "plotTimestampsGraph": plotTimestampsGraph(id, args); break; default: self.postMessage({ id, error: e.message + "\n" + e.stack }); break; } }; /** * @see LineGraphWidget.prototype.setDataFromTimestamps in Graphs.jsm * @param number id * @param number width * @param number height * @param array timestamps * @param number interval */ function plotTimestampsGraph(id, args) { let plottedData = plotTimestamps(args.timestamps, args.interval); let plottedMinMaxSum = getMinMaxSum(plottedData); let sparsifiedData = sparsifyLineData(plottedData, plottedMinMaxSum, args); let response = { id, plottedData: sparsifiedData, plottedMinMaxSum }; self.postMessage(response); } /** * Gets the min, max and average of the values in an array. * @param array source * @return object */ function getMinMaxSum(source) { let totalTicks = source.length; let maxValue = Number.MIN_SAFE_INTEGER; let minValue = Number.MAX_SAFE_INTEGER; let avgValue = 0; let sumValues = 0; for (let { value } of source) { maxValue = Math.max(value, maxValue); minValue = Math.min(value, minValue); sumValues += value; } avgValue = sumValues / totalTicks; return { minValue, maxValue, avgValue }; } /** * Reduce a data source for a line graph, based off of a minimum distance * between the points to render. */ function sparsifyLineData(plottedData, plottedMinMaxSum, options) { let { width: graphWidth, height: graphHeight } = options; let { dataOffsetX, dampenValuesFactor } = options; let { minSquaredDistanceBetweenPoints } = options; let result = []; let totalTicks = plottedData.length; let maxValue = plottedMinMaxSum.maxValue; let firstTick = totalTicks ? plottedData[0].delta : 0; let lastTick = totalTicks ? plottedData[totalTicks - 1].delta : 0; let dataScaleX = graphWidth / (lastTick - dataOffsetX); let dataScaleY = graphHeight / maxValue * dampenValuesFactor; let prevX = 0; let prevY = 0; for (let { delta, value } of plottedData) { let currX = (delta - dataOffsetX) * dataScaleX; let currY = graphHeight - value * dataScaleY; if (delta == firstTick || delta == lastTick) { result.push({ delta, value }); continue; } let dist = distSquared(prevX, prevY, currX, currY); if (dist >= minSquaredDistanceBetweenPoints) { result.push({ delta, value }); prevX = currX; prevY = currY; } } return result; } /** * Takes a list of numbers and plots them on a line graph representing * the rate of occurences in a specified interval. * * XXX: Copied almost verbatim from toolkit/devtools/server/actors/framerate.js * Remove that dead code after the Performance panel lands, bug 1075567. * * @param array timestamps * A list of numbers representing time, ordered ascending. For example, * this can be the raw data received from the framerate actor, which * represents the elapsed time on each refresh driver tick. * @param number interval * The maximum amount of time to wait between calculations. * @param number clamp * The maximum allowed value. * @return array * A collection of { delta, value } objects representing the * plotted value at every delta time. */ function plotTimestamps(timestamps, interval = 100, clamp = 60) { let timeline = []; let totalTicks = timestamps.length; // If the refresh driver didn't get a chance to tick before the // recording was stopped, assume rate was 0. if (totalTicks == 0) { timeline.push({ delta: 0, value: 0 }); timeline.push({ delta: interval, value: 0 }); return timeline; } let frameCount = 0; let prevTime = +timestamps[0]; for (let i = 1; i < totalTicks; i++) { let currTime = +timestamps[i]; frameCount++; let elapsedTime = currTime - prevTime; if (elapsedTime < interval) { continue; } let rate = Math.min(1000 / (elapsedTime / frameCount), clamp); timeline.push({ delta: prevTime, value: rate }); timeline.push({ delta: currTime, value: rate }); frameCount = 0; prevTime = currTime; } return timeline; } /** * Calculates the squared distance between two 2D points. */ function distSquared(x0, y0, x1, y1) { let xs = x1 - x0; let ys = y1 - y0; return xs * xs + ys * ys; }