/* 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"; // No sense updating the overview more often than receiving data from the // backend. Make sure this isn't lower than DEFAULT_TIMELINE_DATA_PULL_TIMEOUT // in toolkit/devtools/server/actors/timeline.js const OVERVIEW_UPDATE_INTERVAL = 200; // ms const FRAMERATE_GRAPH_LOW_RES_INTERVAL = 100; // ms const FRAMERATE_GRAPH_HIGH_RES_INTERVAL = 16; // ms const FRAMERATE_GRAPH_HEIGHT = 45; // px const MARKERS_GRAPH_HEADER_HEIGHT = 12; // px const MARKERS_GRAPH_BODY_HEIGHT = 45; // 9px * 5 groups const MARKERS_GROUP_VERTICAL_PADDING = 3.5; // px const MEMORY_GRAPH_HEIGHT = 30; // px const GRAPH_SCROLL_EVENTS_DRAIN = 50; // ms /** * View handler for the overview panel's time view, displaying * framerate, markers and memory over time. */ let OverviewView = { /** * Sets up the view with event binding. */ initialize: Task.async(function *() { this._onRecordingStarted = this._onRecordingStarted.bind(this); this._onRecordingStopped = this._onRecordingStopped.bind(this); this._onRecordingTick = this._onRecordingTick.bind(this); this._onGraphMouseUp = this._onGraphMouseUp.bind(this); this._onGraphScroll = this._onGraphScroll.bind(this); yield this._showFramerateGraph(); yield this._showMarkersGraph(); yield this._showMemoryGraph(); this.framerateGraph.on("mouseup", this._onGraphMouseUp); this.framerateGraph.on("scroll", this._onGraphScroll); this.markersOverview.on("mouseup", this._onGraphMouseUp); this.markersOverview.on("scroll", this._onGraphScroll); this.memoryOverview.on("mouseup", this._onGraphMouseUp); this.memoryOverview.on("scroll", this._onGraphScroll); PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted); PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped); }), /** * Unbinds events. */ destroy: function () { this.framerateGraph.off("mouseup", this._onGraphMouseUp); this.framerateGraph.off("scroll", this._onGraphScroll); this.markersOverview.off("mouseup", this._onGraphMouseUp); this.markersOverview.off("scroll", this._onGraphScroll); this.memoryOverview.off("mouseup", this._onGraphMouseUp); this.memoryOverview.off("scroll", this._onGraphScroll); clearNamedTimeout("graph-scroll"); PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted); PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped); }, /** * Sets up the framerate graph. */ _showFramerateGraph: Task.async(function *() { this.framerateGraph = new LineGraphWidget($("#time-framerate"), L10N.getStr("graphs.fps")); this.framerateGraph.fixedHeight = FRAMERATE_GRAPH_HEIGHT; yield this.framerateGraph.ready(); }), /** * Sets up the markers overivew graph. */ _showMarkersGraph: Task.async(function *() { this.markersOverview = new MarkersOverview($("#markers-overview")); this.markersOverview.headerHeight = MARKERS_GRAPH_HEADER_HEIGHT; this.markersOverview.bodyHeight = MARKERS_GRAPH_BODY_HEIGHT; this.markersOverview.groupPadding = MARKERS_GROUP_VERTICAL_PADDING; yield this.markersOverview.ready(); CanvasGraphUtils.linkAnimation(this.framerateGraph, this.markersOverview); CanvasGraphUtils.linkSelection(this.framerateGraph, this.markersOverview); }), /** * Sets up the memory overview graph. */ _showMemoryGraph: Task.async(function *() { this.memoryOverview = new MemoryOverview($("#memory-overview")); this.memoryOverview.fixedHeight = MEMORY_GRAPH_HEIGHT; yield this.memoryOverview.ready(); CanvasGraphUtils.linkAnimation(this.framerateGraph, this.memoryOverview); CanvasGraphUtils.linkSelection(this.framerateGraph, this.memoryOverview); }), /** * Method for handling all the set up for rendering the overview graphs. * * @param number resolution * The fps graph resolution. @see Graphs.jsm */ render: Task.async(function *(resolution) { let interval = PerformanceController.getInterval(); let markers = PerformanceController.getMarkers(); let memory = PerformanceController.getMemory(); let timestamps = PerformanceController.getTicks(); this.markersOverview.setData({ interval, markers }); this.emit(EVENTS.MARKERS_GRAPH_RENDERED); this.memoryOverview.setData({ interval, memory }); this.emit(EVENTS.MEMORY_GRAPH_RENDERED); yield this.framerateGraph.setDataFromTimestamps(timestamps, resolution); this.emit(EVENTS.FRAMERATE_GRAPH_RENDERED); // Finished rendering all graphs in this overview. this.emit(EVENTS.OVERVIEW_RENDERED); }), /** * Called at most every OVERVIEW_UPDATE_INTERVAL milliseconds * and uses data fetched from the controller to render * data into all the corresponding overview graphs. */ _onRecordingTick: Task.async(function *() { yield this.render(FRAMERATE_GRAPH_LOW_RES_INTERVAL); this._prepareNextTick(); }), /** * Fired when the graph selection has changed. Called by * mouseup and scroll events. */ _onSelectionChange: function () { if (this.framerateGraph.hasSelection()) { let { min: beginAt, max: endAt } = this.framerateGraph.getMappedSelection(); this.emit(EVENTS.OVERVIEW_RANGE_SELECTED, { beginAt, endAt }); } else { this.emit(EVENTS.OVERVIEW_RANGE_CLEARED); } }, /** * Listener handling the "mouseup" event for the framerate graph. * Fires an event to be handled elsewhere. */ _onGraphMouseUp: function () { // Only fire a selection change event if the selection is actually enabled. if (this.framerateGraph.selectionEnabled) { this._onSelectionChange(); } }, /** * Listener handling the "scroll" event for the framerate graph. * Fires a debounced event to be handled elsewhere. */ _onGraphScroll: function () { setNamedTimeout("graph-scroll", GRAPH_SCROLL_EVENTS_DRAIN, () => { this._onSelectionChange(); }); }, /** * Called to refresh the timer to keep firing _onRecordingTick. */ _prepareNextTick: function () { // Check here to see if there's still a _timeoutId, incase // `stop` was called before the _prepareNextTick call was executed. if (this._timeoutId) { this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL); } }, /** * Called when recording starts. */ _onRecordingStarted: function () { this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL); this.framerateGraph.dropSelection(); this.framerateGraph.selectionEnabled = false; this.markersOverview.selectionEnabled = false; this.memoryOverview.selectionEnabled = false; }, /** * Called when recording stops. */ _onRecordingStopped: function () { clearTimeout(this._timeoutId); this._timeoutId = null; this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL); this.framerateGraph.selectionEnabled = true; this.markersOverview.selectionEnabled = true; this.memoryOverview.selectionEnabled = true; } }; // Decorates the OverviewView as an EventEmitter EventEmitter.decorate(OverviewView);