gnome-shell/tests/shell/core.js

309 lines
9.8 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* eslint camelcase: ["error", { properties: "never", allow: ["^script_", "^malloc", "^glx", "^clutter"] }] */
import System from 'system';
const Main = imports.ui.main;
import * as Scripting from 'resource://org/gnome/shell/ui/scripting.js';
// This performance script measure the most important (core) performance
// metrics for the shell. By looking at the output metrics of this script
// someone should be able to get an idea of how well the shell is performing
// on a particular system.
export var METRICS = {
overviewLatencyFirst: {
description: 'Time to first frame after triggering overview, first time',
units: 'us',
},
overviewFpsFirst: {
description: 'Frame rate when going to the overview, first time',
units: 'frames / s',
},
overviewLatencySubsequent: {
description: 'Time to first frame after triggering overview, second time',
units: 'us',
},
overviewFpsSubsequent: {
description: 'Frames rate when going to the overview, second time',
units: 'frames / s',
},
overviewFps5Windows: {
description: 'Frames rate when going to the overview, 5 windows open',
units: 'frames / s',
},
overviewFps10Windows: {
description: 'Frames rate when going to the overview, 10 windows open',
units: 'frames / s',
},
overviewFps5Maximized: {
description: 'Frames rate when going to the overview, 5 maximized windows open',
units: 'frames / s',
},
overviewFps10Maximized: {
description: 'Frames rate when going to the overview, 10 maximized windows open',
units: 'frames / s',
},
overviewFps5Alpha: {
description: 'Frames rate when going to the overview, 5 alpha-transparent windows open',
units: 'frames / s',
},
overviewFps10Alpha: {
description: 'Frames rate when going to the overview, 10 alpha-transparent windows open',
units: 'frames / s',
},
usedAfterOverview: {
description: "Malloc'ed bytes after the overview is shown once",
units: 'B',
},
leakedAfterOverview: {
description: "Additional malloc'ed bytes the second time the overview is shown",
units: 'B',
},
applicationsShowTimeFirst: {
description: 'Time to switch to applications view, first time',
units: 'us',
},
applicationsShowTimeSubsequent: {
description: 'Time to switch to applications view, second time',
units: 'us',
},
};
const WINDOW_CONFIGS = [{
width: 640, height: 480,
alpha: false, maximized: false, count: 1, metric: 'overviewFpsSubsequent',
}, {
width: 640, height: 480,
alpha: false, maximized: false, count: 5, metric: 'overviewFps5Windows',
}, {
width: 640, height: 480,
alpha: false, maximized: false, count: 10, metric: 'overviewFps10Windows',
}, {
width: 640, height: 480,
alpha: false, maximized: true, count: 5, metric: 'overviewFps5Maximized',
}, {
width: 640, height: 480,
alpha: false, maximized: true, count: 10, metric: 'overviewFps10Maximized',
}, {
width: 640, height: 480,
alpha: true, maximized: false, count: 5, metric: 'overviewFps5Alpha',
}, {
width: 640, height: 480,
alpha: true, maximized: false, count: 10, metric: 'overviewFps10Alpha',
}];
/** @returns {void} */
export async function run() {
/* eslint-disable no-await-in-loop */
Scripting.defineScriptEvent('overviewShowStart', 'Starting to show the overview');
Scripting.defineScriptEvent('overviewShowDone', 'Overview finished showing');
Scripting.defineScriptEvent('afterShowHide', 'After a show/hide cycle for the overview');
Scripting.defineScriptEvent('applicationsShowStart', 'Starting to switch to applications view');
Scripting.defineScriptEvent('applicationsShowDone', 'Done switching to applications view');
// Enable recording of timestamps for different points in the frame cycle
global.frame_timestamps = true;
Main.overview.connect('shown', () => {
Scripting.scriptEvent('overviewShowDone');
});
await Scripting.sleep(1000);
for (let i = 0; i < 2 * WINDOW_CONFIGS.length; i++) {
// We go to the overview twice for each configuration; the first time
// to calculate the mipmaps for the windows, the second time to get
// a clean set of numbers.
if ((i % 2) === 0) {
let config = WINDOW_CONFIGS[i / 2];
await Scripting.destroyTestWindows();
for (let k = 0; k < config.count; k++) {
await Scripting.createTestWindow({
width: config.width,
height: config.height,
alpha: config.alpha,
maximized: config.maximized,
});
}
await Scripting.waitTestWindows();
await Scripting.sleep(1000);
await Scripting.waitLeisure();
}
Scripting.scriptEvent('overviewShowStart');
Main.overview.show();
await Scripting.waitLeisure();
Main.overview.hide();
await Scripting.waitLeisure();
System.gc();
await Scripting.sleep(1000);
Scripting.collectStatistics();
Scripting.scriptEvent('afterShowHide');
}
await Scripting.destroyTestWindows();
await Scripting.sleep(1000);
Main.overview.show();
await Scripting.waitLeisure();
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = true;
await Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
// eslint-disable-next-line require-atomic-updates
Main.overview.dash.showAppsButton.checked = false;
await Scripting.waitLeisure();
}
/* eslint-enable no-await-in-loop */
}
let showingOverview = false;
let finishedShowingOverview = false;
let overviewShowStart;
let overviewFrames;
let overviewLatency;
let mallocUsedSize = 0;
let overviewShowCount = 0;
let haveSwapComplete = false;
let applicationsShowStart;
let applicationsShowCount = 0;
/**
* @param {number} time - event timestamp
* @returns {void}
*/
export function script_overviewShowStart(time) {
showingOverview = true;
finishedShowingOverview = false;
overviewShowStart = time;
overviewFrames = 0;
}
/**
* @param {number} _time - event timestamp
* @returns {void}
*/
export function script_overviewShowDone(_time) {
// We've set up the state at the end of the zoom out, but we
// need to wait for one more frame to paint before we count
// ourselves as done.
finishedShowingOverview = true;
}
/**
* @param {number} time - event timestamp
* @returns {void}
*/
export function script_applicationsShowStart(time) {
applicationsShowStart = time;
}
/** @returns {void} */
/**
* @param {number} time - event timestamp
* @returns {void}
*/
export function script_applicationsShowDone(time) {
applicationsShowCount++;
if (applicationsShowCount === 1)
METRICS.applicationsShowTimeFirst.value = time - applicationsShowStart;
else
METRICS.applicationsShowTimeSubsequent.value = time - applicationsShowStart;
}
/** @returns {void} */
/**
* @param {number} _time - event timestamp
* @returns {void}
*/
export function script_afterShowHide(_time) {
if (overviewShowCount === 1)
METRICS.usedAfterOverview.value = mallocUsedSize;
else
METRICS.leakedAfterOverview.value = mallocUsedSize - METRICS.usedAfterOverview.value;
}
/**
* @param {number} time - event timestamp
* @param {number} bytes - event data
* @returns {void}
*/
export function malloc_usedSize(time, bytes) {
mallocUsedSize = bytes;
}
/**
* @param {number} time - event timestamp
* @returns {void}
*/
function _frameDone(time) {
if (showingOverview) {
if (overviewFrames === 0)
overviewLatency = time - overviewShowStart;
overviewFrames++;
}
if (finishedShowingOverview) {
showingOverview = false;
finishedShowingOverview = false;
overviewShowCount++;
let dt = (time - (overviewShowStart + overviewLatency)) / 1000000;
// If we see a start frame and an end frame, that would
// be 1 frame for a FPS computation, hence the '- 1'
let fps = (overviewFrames - 1) / dt;
if (overviewShowCount === 1) {
METRICS.overviewLatencyFirst.value = overviewLatency;
METRICS.overviewFpsFirst.value = fps;
} else if (overviewShowCount === 2) {
METRICS.overviewLatencySubsequent.value = overviewLatency;
}
// Other than overviewFpsFirst, we collect FPS metrics the second
// we show each window configuration. overviewShowCount is 1,2,3...
if (overviewShowCount % 2 === 0) {
let config = WINDOW_CONFIGS[(overviewShowCount / 2) - 1];
METRICS[config.metric].value = fps;
}
}
}
/**
* @param {number} time - event timestamp
* @param {number} swapTime - event data
* @returns {void}
*/
export function glx_swapComplete(time, swapTime) {
haveSwapComplete = true;
_frameDone(swapTime);
}
/**
* @param {number} time - event timestamp
* @returns {void}
*/
export function clutter_stagePaintDone(time) {
// If we aren't receiving GLXBufferSwapComplete events, then we approximate
// the time the user sees a frame with the time we finished doing drawing
// commands for the frame. This doesn't take into account the time for
// the GPU to finish painting, and the time for waiting for the buffer
// swap, but if this are uniform - every frame takes the same time to draw -
// then it won't upset our FPS calculation, though the latency value
// will be slightly too low.
if (!haveSwapComplete)
_frameDone(time);
}