forked from mirrors/gecko-dev
Bug 1375903 - Enable eslint on testing/talos - additional changes; r=jmaher
This commit is contained in:
parent
f227d92d5a
commit
1a7d0e6a5d
33 changed files with 493 additions and 449 deletions
|
|
@ -306,6 +306,7 @@ testing/modules/sinon-2.3.2.js
|
|||
# octothorpe used for pref file comment causes parsing error
|
||||
testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
|
||||
testing/talos/talos/scripts/jszip.min.js
|
||||
testing/talos/talos/startup_test/sessionrestore/profile/sessionstore.js
|
||||
testing/talos/talos/tests/canvasmark/**
|
||||
testing/talos/talos/tests/dromaeo/**
|
||||
testing/talos/talos/tests/v8_7/**
|
||||
|
|
|
|||
26
testing/talos/.eslintrc.js
Normal file
26
testing/talos/.eslintrc.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
|
||||
globals: {
|
||||
"Cc": false,
|
||||
"Ci": false,
|
||||
"Cu": false,
|
||||
"content": true,
|
||||
"dumpLog": false,
|
||||
"netscape": false,
|
||||
"addMessageListener": false,
|
||||
"goQuitApplication": false,
|
||||
"MozillaFileLogger": false,
|
||||
"Profiler": true,
|
||||
"Services": false,
|
||||
"gBrowser": false,
|
||||
"removeMessageListener": false,
|
||||
"sendAsyncMessage": false,
|
||||
"sendSyncMessage": false,
|
||||
"TalosPowersContent": true,
|
||||
"TalosPowersParent": true,
|
||||
"TalosContentProfiler": true,
|
||||
"tpRecordTime": true
|
||||
}
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// prefs in user.js don't get recognized until the second browser launch
|
||||
// which is too late for our purposes of using quit.js. Loading the principals
|
||||
// from prefs.js avoids this issue.
|
||||
/* globals user_pref */
|
||||
user_pref("capability.principal.codebase.p0.granted", "UniversalPreferencesWrite UniversalXPConnect UniversalPreferencesRead");
|
||||
user_pref("capability.principal.codebase.p0.id", "file://");
|
||||
user_pref("capability.principal.codebase.p0.subjectName", "");
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<script src="scripts/xpigen.js"></script>
|
||||
|
||||
<script>
|
||||
/* import-globals-from scripts/xpigen.js */
|
||||
var base = "tests/tart/addon/";
|
||||
var files = [
|
||||
"chrome.manifest",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<script src="scripts/xpigen.js"></script>
|
||||
|
||||
<script>
|
||||
/* import-globals-from scripts/xpigen.js */
|
||||
var base = "startup_test/tresize/addon/";
|
||||
var files = [
|
||||
"chrome.manifest",
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ var Profiler;
|
|||
},
|
||||
finishTestAsync: function Profiler__finishTest() {
|
||||
if (!(_profiler && enabled)) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
Services.profiler.getProfileDataAsync().then((profile) => {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* import-globals-from pageloader.js */
|
||||
|
||||
var gChildProcess = true;
|
||||
var gMemCallback = null;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
* 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/. */
|
||||
|
||||
/* import-globals-from memory.js */
|
||||
/* import-globals-from report.js */
|
||||
|
||||
try {
|
||||
if (Cc === undefined) {
|
||||
var Cc = Components.classes;
|
||||
|
|
@ -106,6 +109,7 @@ SingleTimeout.prototype.clear = function() {
|
|||
};
|
||||
|
||||
var failTimeout = new SingleTimeout();
|
||||
var renderReport;
|
||||
|
||||
function plInit() {
|
||||
if (running) {
|
||||
|
|
@ -214,8 +218,7 @@ function plInit() {
|
|||
toolbars = "titlebar,resizable";
|
||||
}
|
||||
|
||||
browserWindow = wwatch.openWindow
|
||||
(null, "chrome://browser/content/", "_blank",
|
||||
browserWindow = wwatch.openWindow(null, "chrome://browser/content/", "_blank",
|
||||
`chrome,${toolbars},dialog=no,width=${winWidth},height=${winHeight}`, blank);
|
||||
|
||||
gPaintWindow = browserWindow;
|
||||
|
|
@ -314,6 +317,7 @@ var ContentListener = {
|
|||
case "PageLoader:LoadEvent": return plLoadHandlerMessage(message);
|
||||
case "PageLoader:RecordTime": return plRecordTimeMessage(message);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -525,7 +529,7 @@ function plRecordTime(time) {
|
|||
report.recordTime(recordedName, time);
|
||||
}
|
||||
if (noisy) {
|
||||
dumpLine("Cycle " + (cycle + 1) + "(" + pageCycle + ")" + ": loaded " + pageName + " (next: " + nextName + ")");
|
||||
dumpLine("Cycle " + (cycle + 1) + "(" + pageCycle + "): loaded " + pageName + " (next: " + nextName + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ function Report() {
|
|||
}
|
||||
|
||||
Report.prototype.pageNames = function() {
|
||||
var retval = new Array();
|
||||
var retval = [];
|
||||
for (var page in this.timeVals) {
|
||||
retval.push(page);
|
||||
}
|
||||
|
|
@ -153,7 +153,7 @@ Report.prototype.getReportSummary = function() {
|
|||
|
||||
Report.prototype.recordTime = function(pageName, ms) {
|
||||
if (this.timeVals[pageName] == undefined) {
|
||||
this.timeVals[pageName] = new Array();
|
||||
this.timeVals[pageName] = [];
|
||||
}
|
||||
this.timeVals[pageName].push(ms);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,6 @@ function testScroll(target, stepSize, opt_reportFunc, opt_numSteps) {
|
|||
|
||||
return new Promise(function(resolve, reject) {
|
||||
setSmooth();
|
||||
var startts = Date.now();
|
||||
|
||||
var handle = -1;
|
||||
startFrameTimeRecording(function(rv) {
|
||||
|
|
@ -224,7 +223,6 @@ function testScroll(target, stepSize, opt_reportFunc, opt_numSteps) {
|
|||
|
||||
// Get the measurements after APZ_MEASURE_MS of scrolling
|
||||
setTimeout(function() {
|
||||
var endts = Date.now();
|
||||
|
||||
stopFrameTimeRecording(handle, function(intervals) {
|
||||
function average(arr) {
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ var PageLoaderCmdLineModule =
|
|||
compMgr = compMgr.QueryInterface(nsIComponentRegistrar);
|
||||
|
||||
compMgr.unregisterFactoryLocation(TP_CMDLINE_CLSID, fileSpec);
|
||||
catman = Components.classes[CATMAN_CONTRACTID].getService(nsICategoryManager);
|
||||
var catman = Components.classes[CATMAN_CONTRACTID].getService(nsICategoryManager);
|
||||
catman.deleteCategoryEntry("command-line-handler",
|
||||
"m-tp", true);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ window.talosDebug = {
|
|||
|
||||
median(values) {
|
||||
var clone = values.slice(0);
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
var sorted = clone.sort(function(a, b) { return (a > b) ? 1 : ((a < b) ? -1 : 0); });
|
||||
var len = values.length;
|
||||
if (!len)
|
||||
|
|
@ -76,14 +77,14 @@ window.talosDebug = {
|
|||
|
||||
var stableFrom = -1;
|
||||
var overallAverage = d.median(values);
|
||||
var overallStd = d.stddev(values, overallAverage);
|
||||
// var overallStd = d.stddev(values, overallAverage);
|
||||
for (var winWidth = MIN_WIDTH; winWidth < (MAX_WIDTH + 1); winWidth++) {
|
||||
var prevStd = windowStd(0, winWidth);
|
||||
for (var i = 1; i < values.length - winWidth - 3; i++) {
|
||||
var w0 = windowStd(i + 0, winWidth);
|
||||
var w1 = windowStd(i + 1, winWidth);
|
||||
var w2 = windowStd(i + 2, winWidth);
|
||||
var currWindow = values.slice(i, i + winWidth);
|
||||
// var currWindow = values.slice(i, i + winWidth);
|
||||
if (w0 >= prevStd && !(w1 < w0 && w2 < w1)) {
|
||||
if (i > stableFrom)
|
||||
stableFrom = i;
|
||||
|
|
@ -135,7 +136,7 @@ window.talosDebug = {
|
|||
var warmup = (d.ignore >= 0) ? d.ignore : d.detectWarmup(collection);
|
||||
if (warmup >= 0) {
|
||||
res += "\n\nWarmup " + ((d.ignore >= 0) ? "requested: " : "auto-detected: ") + warmup;
|
||||
warmedUp = collection.slice(warmup);
|
||||
var warmedUp = collection.slice(warmup);
|
||||
if (warmup) {
|
||||
res += "\nAfter ignoring first " + (warmup > 1 ? (warmup + " items") : "item") + ":\n";
|
||||
res += d.statsDisplay(warmedUp);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
* 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/. */
|
||||
|
||||
/* globals JSZip */
|
||||
|
||||
/* eslint-disable no-nested-ternary */
|
||||
|
||||
// base: relative or absolute path (http[s] or file, untested with ftp)
|
||||
// files: array of file names relative to base to include at the zip
|
||||
// callbacks: object with optional functions:
|
||||
|
|
@ -11,13 +15,6 @@ function createXpiDataUri(base, files, callbacks) {
|
|||
// Synchronous XHR for http[s]/file (untested ftp), throws on any error
|
||||
// Note that on firefox, file:// XHR can't access files outside base dir
|
||||
function readBinFile(url) {
|
||||
// The DOM will qualify the URI for us if it's relative (not IE6)
|
||||
function isFileUri(uri) {
|
||||
var a = document.createElement("a");
|
||||
a.href = uri;
|
||||
return a.href.toLowerCase().indexOf("file://") == 0;
|
||||
}
|
||||
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("GET", url, false);
|
||||
r.requestType = "arraybuffer";
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ async function reportTimes() {
|
|||
avg = (avg / count).toFixed(2);
|
||||
min = min.toFixed(2);
|
||||
max = max.toFixed(2);
|
||||
med = calcMedian(openTimes);
|
||||
var med = calcMedian(openTimes);
|
||||
|
||||
if (auto) {
|
||||
dumpLog("__start_report" + openTimes.join("|") + "__end_report");
|
||||
|
|
@ -97,6 +97,7 @@ async function childIsOpen() {
|
|||
scheduleNextWindow();
|
||||
}
|
||||
|
||||
/* eslint-disable no-useless-concat */
|
||||
var kidHTML = "<html><meta charset='utf-8'><script>" +
|
||||
"var e = 'MozAfterPaint';" +
|
||||
"function done() {" +
|
||||
|
|
@ -106,6 +107,7 @@ var kidHTML = "<html><meta charset='utf-8'><script>" +
|
|||
"}" +
|
||||
"window.addEventListener(e, done, true);" +
|
||||
"</" + "script>TPAINT</html>";
|
||||
/* eslint-enable no-useless-concat */
|
||||
var kidURI = "data:text/html," + encodeURI(kidHTML);
|
||||
|
||||
async function openWindow() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
* and average to stdout or logfile.
|
||||
*/
|
||||
|
||||
var dataSet = new Array();
|
||||
var dataSet = [];
|
||||
var windowSize = 425;
|
||||
var resizeIncrement = 2;
|
||||
var count = 0;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
// This file is loaded as a framescript
|
||||
|
||||
/* globals docShell */
|
||||
|
||||
var { interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -188,7 +188,6 @@ TalosPowersService.prototype = {
|
|||
|
||||
case "Profiler:Finish": {
|
||||
// The test is done. Dump the profile.
|
||||
let profileFile = data.profileFile;
|
||||
this.profilerFinish(data.profileFile).then(() => {
|
||||
mm.sendAsyncMessage(ACK_NAME, { name });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/* globals acc:true, gAccService:true, nsIAccessible:true, nsIDOMNode:true */
|
||||
gAccService = 0;
|
||||
|
||||
// Make sure not to touch Components before potentially invoking enablePrivilege,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
<title>accessibility perf for dhtml</title>
|
||||
<script src="a11y.js"></script>
|
||||
<script>
|
||||
/* import-globals-from a11y.js */
|
||||
// based on: http://hacks.mozilla.org/2010/05/better-performance-with-lazy-frame-construction/
|
||||
var ppDate = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -76,8 +76,11 @@
|
|||
|
||||
</body>
|
||||
<script>
|
||||
/* import-globals-from a11y.js */
|
||||
window.onload = function() {
|
||||
setTimeout("mutateTable()", 100);
|
||||
setTimeout(function() {
|
||||
mutateTable();
|
||||
}, 100);
|
||||
};
|
||||
function mutateTable() {
|
||||
var htmTable = document.getElementById("datatable");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// This file is the common bits for the test runner frontend, originally
|
||||
// extracted out of the tart.html frontend when creating the damp test.
|
||||
|
||||
/* globals updateConfig, defaultConfig, config */ /* from damp.html */
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ var testsInfo = {
|
|||
function updateConfig() {
|
||||
config = {subtests: []};
|
||||
for (var test in defaultConfig.subtests) {
|
||||
if ($("subtest-" + test).checked) {
|
||||
if ($("subtest-" + test).checked) { // eslint-disable-line no-undef
|
||||
config.subtests.push(test);
|
||||
}
|
||||
}
|
||||
|
||||
var repeat = $("repeat").value;
|
||||
var repeat = $("repeat").value; // eslint-disable-line no-undef
|
||||
config.repeat = isNaN(repeat) ? 1 : repeat;
|
||||
|
||||
// use 1ms rest as a minimum.
|
||||
var rest = $("rest").value;
|
||||
var rest = $("rest").value; // eslint-disable-line no-undef
|
||||
config.rest = Math.max(1, isNaN(rest) ? defaultConfig.rest : rest);
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
|
|||
const SIMPLE_URL = webserver + "/tests/devtools/addon/content/pages/simple.html";
|
||||
const COMPLICATED_URL = webserver + "/tests/tp5n/bild.de/www.bild.de/index.html";
|
||||
|
||||
/* globals res:true */
|
||||
|
||||
function Damp() {
|
||||
// Path to the temp file where the heap snapshot file is saved. Set by
|
||||
// saveHeapSnapshot and read by readHeapSnapshot.
|
||||
|
|
@ -32,9 +34,8 @@ Damp.prototype = {
|
|||
let tab = this._win.gBrowser.selectedTab = this._win.gBrowser.addTab(url);
|
||||
let browser = tab.linkedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
resolve(tab);
|
||||
}, true);
|
||||
}, {capture: true, once: true});
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -47,14 +48,12 @@ Damp.prototype = {
|
|||
let startReloadTimestamp = performance.now();
|
||||
return new Promise((resolve, reject) => {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let self = this;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
let stopReloadTimestamp = performance.now();
|
||||
resolve({
|
||||
time: stopReloadTimestamp - startReloadTimestamp
|
||||
});
|
||||
}, true);
|
||||
}, {capture: true, once: true});
|
||||
browser.reload();
|
||||
});
|
||||
},
|
||||
|
|
@ -181,8 +180,7 @@ Damp.prototype = {
|
|||
let TOTAL_MESSAGES = 100;
|
||||
let tab = yield this.testSetup(SIMPLE_URL);
|
||||
let messageManager = tab.linkedBrowser.messageManager;
|
||||
let {toolbox} = yield this.openToolbox("webconsole");
|
||||
let webconsole = toolbox.getPanel("webconsole");
|
||||
yield this.openToolbox("webconsole");
|
||||
|
||||
// Load a frame script using a data URI so we can do logs
|
||||
// from the page. So this is running in content.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<head>
|
||||
<script src="util.js"></script>
|
||||
<script>
|
||||
/* import-globals-from util.js */
|
||||
window.onload = function() {
|
||||
document.head.appendChild(build_rule("span > div", 10000, "{ color: blue; } "));
|
||||
let dom = build_dom(5000, "div");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<head>
|
||||
<script src="util.js"></script>
|
||||
<script>
|
||||
/* import-globals-from util.js */
|
||||
window.onload = function() {
|
||||
document.head.appendChild(build_rule("span div", 10000, "{ color: blue; } "));
|
||||
let dom = build_dom(5000, "div");
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ function goQuitApplication(waitForSafeBrowsing) {
|
|||
// frame script to pick up to quit the whole browser.
|
||||
var event = new CustomEvent("TalosQuitApplication", {bubbles: true, detail: {waitForSafeBrowsing}});
|
||||
document.dispatchEvent(event);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (waitForSafeBrowsing) {
|
||||
|
|
|
|||
|
|
@ -152,7 +152,6 @@ var TabPaint = {
|
|||
* with the time (in ms) it took to open the tab from the parent.
|
||||
*/
|
||||
openTabFromParent(gBrowser) {
|
||||
let win = gBrowser.ownerGlobal;
|
||||
return new Promise((resolve) => {
|
||||
this.Profiler.resume("tabpaint parent start");
|
||||
|
||||
|
|
@ -181,7 +180,6 @@ var TabPaint = {
|
|||
* with the time (in ms) it took to open the tab from content.
|
||||
*/
|
||||
openTabFromContent(gBrowser) {
|
||||
let win = gBrowser.ownerGlobal;
|
||||
return new Promise((resolve) => {
|
||||
this.Profiler.resume("tabpaint content start");
|
||||
|
||||
|
|
@ -227,7 +225,7 @@ var TabPaint = {
|
|||
}
|
||||
}, true);
|
||||
|
||||
tab.ownerDocument.defaultView.gBrowser.removeTab(tab);
|
||||
tab.ownerGlobal.gBrowser.removeTab(tab);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
|
||||
/* globals APP_SHUTDOWN */
|
||||
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
|
@ -41,9 +43,8 @@ var windowListener = {
|
|||
function promiseOneEvent(target, eventName, capture) {
|
||||
let deferred = Promise.defer();
|
||||
target.addEventListener(eventName, function handler(event) {
|
||||
target.removeEventListener(eventName, handler, capture);
|
||||
deferred.resolve();
|
||||
}, capture);
|
||||
}, {capture, once: true});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
|
@ -269,9 +270,8 @@ function waitForTabSwitchDone(browser) {
|
|||
return new Promise((resolve) => {
|
||||
let gBrowser = browser.ownerGlobal.gBrowser;
|
||||
gBrowser.addEventListener("TabSwitchDone", function onTabSwitchDone() {
|
||||
gBrowser.removeEventListener("TabSwitchDone", onTabSwitchDone);
|
||||
resolve();
|
||||
});
|
||||
}, {once: true});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ function updateConfig() {
|
|||
config.repeat = isNaN(repeat) ? 1 : repeat;
|
||||
|
||||
var rest = $("rest").value;
|
||||
config.rest = isNaN(rest) ? 500 : (rest ? rest : 1); // 500ms default, use 1ms as minimum
|
||||
config.rest = isNaN(rest) ? 500 : rest; // 500ms default, use 1ms as minimum
|
||||
config.rest = config.rest ? config.rest : 1;
|
||||
|
||||
config.tickle = $("tickle").checked;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@
|
|||
let aboutNewTabService = Components.classes["@mozilla.org/browser/aboutnewtab-service;1"]
|
||||
.getService(Components.interfaces.nsIAboutNewTabService);
|
||||
|
||||
/* globals res:true, sequenceArray:true */
|
||||
|
||||
function Tart() {
|
||||
}
|
||||
|
||||
|
|
@ -272,7 +274,6 @@ Tart.prototype = {
|
|||
var sumLastHalf = 0;
|
||||
var countLastHalf = 0;
|
||||
var sumMost = 0;
|
||||
var countMost = 0;
|
||||
var sum = 0;
|
||||
for (var i = intervals.length - 1; i >= 0; i--) {
|
||||
sum += intervals[i];
|
||||
|
|
@ -282,13 +283,11 @@ Tart.prototype = {
|
|||
}
|
||||
if (sumMost < referenceDuration * .85) {
|
||||
sumMost += intervals[i];
|
||||
countMost++;
|
||||
}
|
||||
}
|
||||
dump("overall: " + sum + "\n");
|
||||
|
||||
var averageLastHalf = countLastHalf ? sumLastHalf / countLastHalf : 0;
|
||||
var averageMost = countMost ? sumMost / countMost : 0;
|
||||
var averageOverall = intervals.length ? sum / intervals.length : 0;
|
||||
var durationDiff = Math.abs(recordingAbsoluteDuration - referenceDuration);
|
||||
|
||||
|
|
@ -351,6 +350,7 @@ Tart.prototype = {
|
|||
|
||||
tickleLoop();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ var viewModeIndex = 0;
|
|||
var viewMode = [1, 1.1, 2];
|
||||
var testResult = {names: [], values: []};
|
||||
|
||||
/* globals readyToStart:true */
|
||||
|
||||
function init() {
|
||||
TalosPowersContent.focus(content_focused)
|
||||
}
|
||||
|
|
@ -136,6 +138,7 @@ function reportResult() {
|
|||
// Local run in a plain browser, display the formatted report
|
||||
alert(msg);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,404 +1,404 @@
|
|||
<html>
|
||||
<!--
|
||||
* 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/.
|
||||
|
||||
Version 1.1
|
||||
- Added viewport rotation.
|
||||
- Adapted for talos with with 4 runs on combinations of alpha/antialias
|
||||
|
||||
Version 1.0
|
||||
- Benoit Jacob's WebGL tutorial demos: http://bjacob.github.io/webgl-tutorial/12-texture.html
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<script src="../../../../scripts/Profiler.js"></script>
|
||||
<script type="x-shader/x-vertex" id="vertexShader">
|
||||
attribute vec3 vertexPosition;
|
||||
attribute vec3 normalVector;
|
||||
attribute vec2 textureCoord;
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
varying vec3 varyingNormalVector;
|
||||
varying vec2 varyingTextureCoord;
|
||||
void main(void) {
|
||||
gl_Position = projection * modelview * vec4(vertexPosition, 1.0);
|
||||
varyingNormalVector = normalVector;
|
||||
varyingTextureCoord = textureCoord;
|
||||
}
|
||||
</script>
|
||||
<script type="x-shader/x-fragment" id="fragmentShader">
|
||||
precision mediump float;
|
||||
varying vec3 varyingNormalVector;
|
||||
uniform vec3 lightDirection;
|
||||
uniform sampler2D grassTextureSampler;
|
||||
varying vec2 varyingTextureCoord;
|
||||
void main(void) {
|
||||
vec3 grassColor = texture2D(grassTextureSampler, varyingTextureCoord).rgb;
|
||||
const float ambientLight = 0.3;
|
||||
const float diffuseLight = 0.7;
|
||||
float c = clamp(dot(normalize(varyingNormalVector), lightDirection), 0.0, 1.0);
|
||||
vec3 resultColor = grassColor * (c * diffuseLight + ambientLight);
|
||||
gl_FragColor = vec4(resultColor, 1);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var gl;
|
||||
|
||||
var modelviewUniformLoc;
|
||||
var projectionUniformLoc;
|
||||
var lightDirectionUniformLoc;
|
||||
var grassTextureSamplerUniformLoc;
|
||||
|
||||
var modelviewMatrix = new Float32Array(16);
|
||||
var projectionMatrix = new Float32Array(16);
|
||||
|
||||
var terrainSize = 32;
|
||||
var aspectRatio;
|
||||
|
||||
function startTest(alpha, antialias, doneCallback) {
|
||||
gl = document.getElementById("c").getContext("webgl", {alpha: alpha, antialias: antialias});
|
||||
|
||||
var grassImage = document.getElementById("grass");
|
||||
var grassTexture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, grassTexture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, grassImage);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
|
||||
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
var vertexShaderString = document.getElementById("vertexShader").text;
|
||||
gl.shaderSource(vertexShader, vertexShaderString);
|
||||
gl.compileShader(vertexShader);
|
||||
|
||||
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
var fragmentShaderString = document.getElementById("fragmentShader").text;
|
||||
gl.shaderSource(fragmentShader, fragmentShaderString);
|
||||
gl.compileShader(fragmentShader);
|
||||
|
||||
var program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
var vertexPositionAttrLoc = gl.getAttribLocation(program, "vertexPosition");
|
||||
gl.enableVertexAttribArray(vertexPositionAttrLoc);
|
||||
var normalVectorAttrLoc = gl.getAttribLocation(program, "normalVector");
|
||||
gl.enableVertexAttribArray(normalVectorAttrLoc);
|
||||
var textureCoordAttrLoc = gl.getAttribLocation(program, "textureCoord");
|
||||
gl.enableVertexAttribArray(textureCoordAttrLoc);
|
||||
|
||||
modelviewUniformLoc = gl.getUniformLocation(program, "modelview");
|
||||
projectionUniformLoc = gl.getUniformLocation(program, "projection");
|
||||
lightDirectionUniformLoc = gl.getUniformLocation(program, "lightDirection");
|
||||
grassTextureSamplerUniformLoc = gl.getUniformLocation(program, "grassTextureSampler");
|
||||
|
||||
var vertices = new Float32Array(terrainSize * terrainSize * 3);
|
||||
var normalVectors = new Float32Array(terrainSize * terrainSize * 3);
|
||||
var textureCoords = new Float32Array(terrainSize * terrainSize * 2);
|
||||
|
||||
for (var i = 0; i < terrainSize; i++) {
|
||||
for (var j = 0; j < terrainSize; j++) {
|
||||
var a = 2 * Math.PI * i / terrainSize;
|
||||
var b = 2 * Math.PI * j / terrainSize;
|
||||
var height = 4 * Math.cos(a) + 6 * Math.sin(b) + Math.cos(4 * a) + Math.sin(5 * b);
|
||||
vertices[3 * (i + terrainSize * j) + 0] = i;
|
||||
vertices[3 * (i + terrainSize * j) + 1] = height;
|
||||
vertices[3 * (i + terrainSize * j) + 2] = j;
|
||||
|
||||
var d_y_d_x = (2 * Math.PI / terrainSize) * (- 4 * Math.sin(a) - 4 * Math.sin(4 * a));
|
||||
var d_y_d_z = (2 * Math.PI / terrainSize) * (6 * Math.cos(b) + 5 * Math.cos(5 * b));
|
||||
var normal_x = d_y_d_x;
|
||||
var normal_y = -1;
|
||||
var normal_z = d_y_d_z;
|
||||
var normal_length = Math.sqrt(normal_x * normal_x + normal_y * normal_y + normal_z * normal_z);
|
||||
normalVectors[3 * (i + terrainSize * j) + 0] = normal_x / normal_length;
|
||||
normalVectors[3 * (i + terrainSize * j) + 1] = normal_y / normal_length;
|
||||
normalVectors[3 * (i + terrainSize * j) + 2] = normal_z / normal_length;
|
||||
|
||||
var textureRepeatingSpeed = 0.5;
|
||||
textureCoords[2 * (i + terrainSize * j) + 0] = i * textureRepeatingSpeed;
|
||||
textureCoords[2 * (i + terrainSize * j) + 1] = j * textureRepeatingSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
var vertexPositionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(vertexPositionAttrLoc, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
var normalVectorBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, normalVectorBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, normalVectors, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(normalVectorAttrLoc, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
var textureCoordBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(textureCoordAttrLoc, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
var indexBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
var indices = [];
|
||||
for (var i = 0; i < terrainSize - 1; i++) {
|
||||
for (var j = 0; j < terrainSize - 1; j++) {
|
||||
indices.push(i + terrainSize * j);
|
||||
indices.push(i+1 + terrainSize * j);
|
||||
indices.push(i+1 + terrainSize * (j+1));
|
||||
|
||||
indices.push(i + terrainSize * j);
|
||||
indices.push(i+1 + terrainSize * (j+1));
|
||||
indices.push(i + terrainSize * (j+1));
|
||||
}
|
||||
}
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
|
||||
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
|
||||
onresize();
|
||||
window.addEventListener("resize", onresize);
|
||||
|
||||
startTestLoop(doneCallback);
|
||||
}
|
||||
|
||||
function average(arr) {
|
||||
var sum = 0;
|
||||
for (var i in arr)
|
||||
sum += arr[i];
|
||||
return sum / (arr.length || 1);
|
||||
}
|
||||
|
||||
// We use sample stddev and not population stddev because
|
||||
// well.. it's a sample and we can't collect all/infinite number of frames.
|
||||
function sampleStdDev(arr) {
|
||||
if (arr.length <= 1) {
|
||||
return 0;
|
||||
}
|
||||
var avg = average(arr);
|
||||
|
||||
var squareDiffArr = arr.map( function(v) { return Math.pow(v - avg, 2); } );
|
||||
var sum = squareDiffArr.reduce( function(a, b) { return a + b; } );
|
||||
var rv = Math.sqrt(sum / (arr.length - 1));
|
||||
return rv;
|
||||
}
|
||||
|
||||
const PRETEST_DELAY_MS = 500;
|
||||
const WARMUP_TIMESTAMPS = 30; // Must be at least 2
|
||||
const MEASURED_FRAMES = 100;
|
||||
|
||||
var gDoneCallback = function placeholder(intervals) {};
|
||||
var gCurrentTimestamp = 0;
|
||||
var gResultTimestamps = [];
|
||||
|
||||
function startTestLoop(doneCallback) {
|
||||
gDoneCallback = doneCallback;
|
||||
gCurrentTimestamp = 0;
|
||||
gResultTimestamps = new Array(WARMUP_TIMESTAMPS + MEASURED_FRAMES);
|
||||
|
||||
Profiler.resume("Starting requestAnimationFrame loop", true);
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function draw(timestamp) {
|
||||
// It's possible that under some implementations (even if not our current one),
|
||||
// the rAF callback arg will be in some way "optimized", e.g. always point to the
|
||||
// estimated next vsync timestamp, in order to allow the callee to have less
|
||||
// jitter in its time-dependent positioning/processing.
|
||||
// Such behaviour would harm our measurements, especially with vsync off.
|
||||
// performance.now() will not have this potential issue and is high-resolution.
|
||||
gResultTimestamps[gCurrentTimestamp++] = performance.now();
|
||||
var recordedTimestamps = gCurrentTimestamp;
|
||||
if (recordedTimestamps >= WARMUP_TIMESTAMPS + MEASURED_FRAMES) {
|
||||
Profiler.pause("Stopping requestAnimationFrame loop", true);
|
||||
var intervals = [];
|
||||
var lastWarmupTimestampId = WARMUP_TIMESTAMPS - 1;
|
||||
for (var i = lastWarmupTimestampId + 1; i < gResultTimestamps.length; i++) {
|
||||
intervals.push(gResultTimestamps[i] - gResultTimestamps[i - 1]);
|
||||
}
|
||||
|
||||
gDoneCallback(intervals);
|
||||
return;
|
||||
}
|
||||
|
||||
// Used for rendering reproducible frames which are independent of actual performance (timestamps).
|
||||
var simulatedTimestamp = gCurrentTimestamp * 1000 / 60;
|
||||
|
||||
var speed = 0.001;
|
||||
var angle = simulatedTimestamp * speed;
|
||||
var c = Math.cos(angle / 10);
|
||||
var s = Math.sin(angle / 10);
|
||||
var light_x = Math.cos(angle);
|
||||
var light_y = -1;
|
||||
var light_z = Math.sin(angle);
|
||||
var l = Math.sqrt(light_x * light_x + light_y * light_y + light_z * light_z);
|
||||
light_x /= l;
|
||||
light_y /= l;
|
||||
light_z /= l;
|
||||
gl.uniform3f(lightDirectionUniformLoc, light_x, light_y, light_z);
|
||||
|
||||
modelviewMatrix[0] = c;
|
||||
modelviewMatrix[1] = 0;
|
||||
modelviewMatrix[2] = s;
|
||||
modelviewMatrix[3] = 0;
|
||||
|
||||
modelviewMatrix[4] = 0;
|
||||
modelviewMatrix[5] = 1;
|
||||
modelviewMatrix[6] = 0;
|
||||
modelviewMatrix[7] = 0;
|
||||
|
||||
modelviewMatrix[8] = -s;
|
||||
modelviewMatrix[9] = 0;
|
||||
modelviewMatrix[10] = c;
|
||||
modelviewMatrix[11] = 0;
|
||||
|
||||
modelviewMatrix[12] = - terrainSize / 2;
|
||||
modelviewMatrix[13] = 0;
|
||||
modelviewMatrix[14] = - terrainSize;
|
||||
modelviewMatrix[15] = 1;
|
||||
|
||||
gl.uniformMatrix4fv(modelviewUniformLoc, false, modelviewMatrix);
|
||||
|
||||
var fieldOfViewY = Math.PI / 4;
|
||||
aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight;
|
||||
var zNear = 1;
|
||||
var zFar = terrainSize;
|
||||
|
||||
projectionMatrix[0] = fieldOfViewY / aspectRatio;
|
||||
projectionMatrix[1] = 0;
|
||||
projectionMatrix[2] = 0;
|
||||
projectionMatrix[3] = 0;
|
||||
|
||||
projectionMatrix[4] = 0;
|
||||
projectionMatrix[5] = fieldOfViewY;
|
||||
projectionMatrix[6] = 0;
|
||||
projectionMatrix[7] = 0;
|
||||
|
||||
projectionMatrix[8] = 0;
|
||||
projectionMatrix[9] = 0;
|
||||
projectionMatrix[10] = (zNear + zFar) / (zNear - zFar);
|
||||
projectionMatrix[11] = -1;
|
||||
|
||||
projectionMatrix[12] = 0;
|
||||
projectionMatrix[13] = 0;
|
||||
projectionMatrix[14] = 2 * zNear * zFar / (zNear - zFar);
|
||||
projectionMatrix[15] = 0;
|
||||
|
||||
gl.uniformMatrix4fv(projectionUniformLoc, false, projectionMatrix);
|
||||
|
||||
gl.uniform1f(grassTextureSamplerUniformLoc, 0);
|
||||
|
||||
gl.clearColor(0.4, 0.6, 1.0, 0.5);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.TRIANGLES, (terrainSize - 1) * (terrainSize - 1) * 6, gl.UNSIGNED_SHORT, 0);
|
||||
|
||||
if (gCurrentTimestamp == 1) {
|
||||
// First frame only - wait a bit (after rendering the scene once)
|
||||
requestAnimationFrame(function() {
|
||||
setTimeout(requestAnimationFrame, PRETEST_DELAY_MS, draw);
|
||||
});
|
||||
} else {
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
}
|
||||
|
||||
function onresize() {
|
||||
var canvas = document.getElementById("c");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
aspectRatio = canvas.width / canvas.height;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
var gResults = {values: [], names: [], raw: []};
|
||||
|
||||
function setupAndRun(alpha, antialias, name, doneCallback) {
|
||||
// Remove the old canvas if exists, and create a new one for the new run
|
||||
var c = document.getElementById("c");
|
||||
if (c)
|
||||
c.parentNode.removeChild(c);
|
||||
c = document.createElement("canvas");
|
||||
c.id = "c";
|
||||
document.body.insertBefore(c, document.body.firstChild)
|
||||
|
||||
// Trigger the run with specified args, with callback function to process the result
|
||||
startTest(alpha, antialias, function(intervals) {
|
||||
gResults.names.push(name);
|
||||
gResults.raw.push(intervals);
|
||||
setTimeout(doneCallback, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function reportResults(results) {
|
||||
// Format a nice human-readable report
|
||||
var msg = "";
|
||||
for (var i = 0; i < results.names.length; i++) {
|
||||
var data = results.raw[i];
|
||||
var avg = average(data);
|
||||
gResults.values.push(avg); // This is the only "official" reported value for this set
|
||||
var sd = sampleStdDev(data);
|
||||
|
||||
// Compose a nice human readable message. Not used officially anywhere.
|
||||
msg += results.names[i] + "= Average: " + avg.toFixed(2)
|
||||
+ " stddev: " + sd.toFixed(1) + " (" + (100 * sd / avg).toFixed(1) + "%)"
|
||||
+ "\nIntervals: " + data.map(function(v) {
|
||||
// Display with 1 decimal point digit, and make excessively big values noticeable.
|
||||
var value = v.toFixed(1);
|
||||
// Not scientific, but intervals over 2 * average are.. undesirable.
|
||||
var threshold = avg * 2;
|
||||
return v < threshold ? value : " [_" + value + "_] ";
|
||||
}).join(" ")
|
||||
+ "\n\n";
|
||||
}
|
||||
dump(msg); // Put the readable report at talos run-log
|
||||
|
||||
if (window.tpRecordTime) {
|
||||
// within talos - report the results
|
||||
return tpRecordTime(results.values.join(","), 0, results.names.join(","));
|
||||
} else {
|
||||
// Local run in a plain browser, display the formatted report
|
||||
alert(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// The full test starts here
|
||||
function test() {
|
||||
// We initially hide the <body>, to reduce the chance of spinning our wheels
|
||||
// with incremental ASAP-paint-mode paints during pageload. Now that onload
|
||||
// has fired, we un-hide it:
|
||||
document.body.style.display = "";
|
||||
|
||||
gResults = {values: [], names: [], raw: []};
|
||||
// This test measures average frame interval during WebGL animation as follows:
|
||||
// 1. Creates a new WebGL canvas.
|
||||
// 2. setup the scene and render 1 frame.
|
||||
// 3. Idle a bit (500ms).
|
||||
// 4. Render/Animate several (129) frames using requestAnimationFrame.
|
||||
// 5. Average the intervals of the last (100) frames <-- the result.
|
||||
//
|
||||
// The reason for the initial idle is to allow some internal cleanups (like CC/GC etc).
|
||||
// The unmeasured warmup rendering intervals are to allow Firefox to settle at a consistent rate.
|
||||
// The idle + warmup are common practices in benchmarking where we're mostly interested
|
||||
// in the stable/consistent throughput rather than in the throughput during transition state
|
||||
// from idle to iterating.
|
||||
|
||||
// Run the same sequence 4 times for all combination of alpha and antialias
|
||||
// (Not using promises chaining for better compatibility, ending up with nesting instead)
|
||||
// talos unfortunately trims common prefixes, so we prefix with a running number to keep the full name
|
||||
setupAndRun(false, false, "0.WebGL-terrain-alpha-no-AA-no", function() {
|
||||
setupAndRun(false, true, "1.WebGL-terrain-alpha-no-AA-yes", function() {
|
||||
setupAndRun(true, false, "2.WebGL-terrain-alpha-yes-AA-no", function() {
|
||||
setupAndRun(true, true, "3.WebGL-terrain-alpha-yes-AA-yes", function() {
|
||||
reportResults(gResults);
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="test();" style="overflow:hidden; margin:0; display:none">
|
||||
<canvas id="c"></canvas>
|
||||
<img src="grass.jpeg" style="display:none" id="grass"/>
|
||||
</body>
|
||||
<html>
|
||||
<!--
|
||||
* 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/.
|
||||
|
||||
Version 1.1
|
||||
- Added viewport rotation.
|
||||
- Adapted for talos with with 4 runs on combinations of alpha/antialias
|
||||
|
||||
Version 1.0
|
||||
- Benoit Jacob's WebGL tutorial demos: http://bjacob.github.io/webgl-tutorial/12-texture.html
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<script src="../../../../scripts/Profiler.js"></script>
|
||||
<script type="x-shader/x-vertex" id="vertexShader">
|
||||
attribute vec3 vertexPosition;
|
||||
attribute vec3 normalVector;
|
||||
attribute vec2 textureCoord;
|
||||
uniform mat4 modelview;
|
||||
uniform mat4 projection;
|
||||
varying vec3 varyingNormalVector;
|
||||
varying vec2 varyingTextureCoord;
|
||||
void main(void) {
|
||||
gl_Position = projection * modelview * vec4(vertexPosition, 1.0);
|
||||
varyingNormalVector = normalVector;
|
||||
varyingTextureCoord = textureCoord;
|
||||
}
|
||||
</script>
|
||||
<script type="x-shader/x-fragment" id="fragmentShader">
|
||||
precision mediump float;
|
||||
varying vec3 varyingNormalVector;
|
||||
uniform vec3 lightDirection;
|
||||
uniform sampler2D grassTextureSampler;
|
||||
varying vec2 varyingTextureCoord;
|
||||
void main(void) {
|
||||
vec3 grassColor = texture2D(grassTextureSampler, varyingTextureCoord).rgb;
|
||||
const float ambientLight = 0.3;
|
||||
const float diffuseLight = 0.7;
|
||||
float c = clamp(dot(normalize(varyingNormalVector), lightDirection), 0.0, 1.0);
|
||||
vec3 resultColor = grassColor * (c * diffuseLight + ambientLight);
|
||||
gl_FragColor = vec4(resultColor, 1);
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
var gl;
|
||||
|
||||
var modelviewUniformLoc;
|
||||
var projectionUniformLoc;
|
||||
var lightDirectionUniformLoc;
|
||||
var grassTextureSamplerUniformLoc;
|
||||
|
||||
var modelviewMatrix = new Float32Array(16);
|
||||
var projectionMatrix = new Float32Array(16);
|
||||
|
||||
var terrainSize = 32;
|
||||
var aspectRatio;
|
||||
|
||||
function startTest(alpha, antialias, doneCallback) {
|
||||
gl = document.getElementById("c").getContext("webgl", {alpha, antialias});
|
||||
|
||||
var grassImage = document.getElementById("grass");
|
||||
var grassTexture = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, grassTexture);
|
||||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, grassImage);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
|
||||
gl.generateMipmap(gl.TEXTURE_2D);
|
||||
|
||||
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
|
||||
var vertexShaderString = document.getElementById("vertexShader").text;
|
||||
gl.shaderSource(vertexShader, vertexShaderString);
|
||||
gl.compileShader(vertexShader);
|
||||
|
||||
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
|
||||
var fragmentShaderString = document.getElementById("fragmentShader").text;
|
||||
gl.shaderSource(fragmentShader, fragmentShaderString);
|
||||
gl.compileShader(fragmentShader);
|
||||
|
||||
var program = gl.createProgram();
|
||||
gl.attachShader(program, vertexShader);
|
||||
gl.attachShader(program, fragmentShader);
|
||||
gl.linkProgram(program);
|
||||
gl.useProgram(program);
|
||||
|
||||
var vertexPositionAttrLoc = gl.getAttribLocation(program, "vertexPosition");
|
||||
gl.enableVertexAttribArray(vertexPositionAttrLoc);
|
||||
var normalVectorAttrLoc = gl.getAttribLocation(program, "normalVector");
|
||||
gl.enableVertexAttribArray(normalVectorAttrLoc);
|
||||
var textureCoordAttrLoc = gl.getAttribLocation(program, "textureCoord");
|
||||
gl.enableVertexAttribArray(textureCoordAttrLoc);
|
||||
|
||||
modelviewUniformLoc = gl.getUniformLocation(program, "modelview");
|
||||
projectionUniformLoc = gl.getUniformLocation(program, "projection");
|
||||
lightDirectionUniformLoc = gl.getUniformLocation(program, "lightDirection");
|
||||
grassTextureSamplerUniformLoc = gl.getUniformLocation(program, "grassTextureSampler");
|
||||
|
||||
var vertices = new Float32Array(terrainSize * terrainSize * 3);
|
||||
var normalVectors = new Float32Array(terrainSize * terrainSize * 3);
|
||||
var textureCoords = new Float32Array(terrainSize * terrainSize * 2);
|
||||
|
||||
for (var i = 0; i < terrainSize; i++) {
|
||||
for (var j = 0; j < terrainSize; j++) {
|
||||
var a = 2 * Math.PI * i / terrainSize;
|
||||
var b = 2 * Math.PI * j / terrainSize;
|
||||
var height = 4 * Math.cos(a) + 6 * Math.sin(b) + Math.cos(4 * a) + Math.sin(5 * b);
|
||||
vertices[3 * (i + terrainSize * j) + 0] = i;
|
||||
vertices[3 * (i + terrainSize * j) + 1] = height;
|
||||
vertices[3 * (i + terrainSize * j) + 2] = j;
|
||||
|
||||
var d_y_d_x = (2 * Math.PI / terrainSize) * (-4 * Math.sin(a) - 4 * Math.sin(4 * a));
|
||||
var d_y_d_z = (2 * Math.PI / terrainSize) * (6 * Math.cos(b) + 5 * Math.cos(5 * b));
|
||||
var normal_x = d_y_d_x;
|
||||
var normal_y = -1;
|
||||
var normal_z = d_y_d_z;
|
||||
var normal_length = Math.sqrt(normal_x * normal_x + normal_y * normal_y + normal_z * normal_z);
|
||||
normalVectors[3 * (i + terrainSize * j) + 0] = normal_x / normal_length;
|
||||
normalVectors[3 * (i + terrainSize * j) + 1] = normal_y / normal_length;
|
||||
normalVectors[3 * (i + terrainSize * j) + 2] = normal_z / normal_length;
|
||||
|
||||
var textureRepeatingSpeed = 0.5;
|
||||
textureCoords[2 * (i + terrainSize * j) + 0] = i * textureRepeatingSpeed;
|
||||
textureCoords[2 * (i + terrainSize * j) + 1] = j * textureRepeatingSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
var vertexPositionBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(vertexPositionAttrLoc, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
var normalVectorBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, normalVectorBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, normalVectors, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(normalVectorAttrLoc, 3, gl.FLOAT, false, 0, 0);
|
||||
|
||||
var textureCoordBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoordBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, textureCoords, gl.STATIC_DRAW);
|
||||
gl.vertexAttribPointer(textureCoordAttrLoc, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
var indexBuffer = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
||||
var indices = [];
|
||||
for (var k = 0; k < terrainSize - k; k++) {
|
||||
for (var l = 0; l < terrainSize - 1; l++) {
|
||||
indices.push(k + terrainSize * l);
|
||||
indices.push(k + 1 + terrainSize * l);
|
||||
indices.push(k + 1 + terrainSize * (l + 1));
|
||||
|
||||
indices.push(k + terrainSize * l);
|
||||
indices.push(k + 1 + terrainSize * (l + 1));
|
||||
indices.push(k + terrainSize * (l + 1));
|
||||
}
|
||||
}
|
||||
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
|
||||
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
|
||||
onresize();
|
||||
window.addEventListener("resize", onresize);
|
||||
|
||||
startTestLoop(doneCallback);
|
||||
}
|
||||
|
||||
function average(arr) {
|
||||
var sum = 0;
|
||||
for (var i in arr)
|
||||
sum += arr[i];
|
||||
return sum / (arr.length || 1);
|
||||
}
|
||||
|
||||
// We use sample stddev and not population stddev because
|
||||
// well.. it's a sample and we can't collect all/infinite number of frames.
|
||||
function sampleStdDev(arr) {
|
||||
if (arr.length <= 1) {
|
||||
return 0;
|
||||
}
|
||||
var avg = average(arr);
|
||||
|
||||
var squareDiffArr = arr.map( function(v) { return Math.pow(v - avg, 2); } );
|
||||
var sum = squareDiffArr.reduce( function(a, b) { return a + b; } );
|
||||
var rv = Math.sqrt(sum / (arr.length - 1));
|
||||
return rv;
|
||||
}
|
||||
|
||||
const PRETEST_DELAY_MS = 500;
|
||||
const WARMUP_TIMESTAMPS = 30; // Must be at least 2
|
||||
const MEASURED_FRAMES = 100;
|
||||
|
||||
var gDoneCallback = function placeholder(intervals) {};
|
||||
var gCurrentTimestamp = 0;
|
||||
var gResultTimestamps = [];
|
||||
|
||||
function startTestLoop(doneCallback) {
|
||||
gDoneCallback = doneCallback;
|
||||
gCurrentTimestamp = 0;
|
||||
gResultTimestamps = new Array(WARMUP_TIMESTAMPS + MEASURED_FRAMES);
|
||||
|
||||
Profiler.resume("Starting requestAnimationFrame loop", true);
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function draw(timestamp) {
|
||||
// It's possible that under some implementations (even if not our current one),
|
||||
// the rAF callback arg will be in some way "optimized", e.g. always point to the
|
||||
// estimated next vsync timestamp, in order to allow the callee to have less
|
||||
// jitter in its time-dependent positioning/processing.
|
||||
// Such behaviour would harm our measurements, especially with vsync off.
|
||||
// performance.now() will not have this potential issue and is high-resolution.
|
||||
gResultTimestamps[gCurrentTimestamp++] = performance.now();
|
||||
var recordedTimestamps = gCurrentTimestamp;
|
||||
if (recordedTimestamps >= WARMUP_TIMESTAMPS + MEASURED_FRAMES) {
|
||||
Profiler.pause("Stopping requestAnimationFrame loop", true);
|
||||
var intervals = [];
|
||||
var lastWarmupTimestampId = WARMUP_TIMESTAMPS - 1;
|
||||
for (var i = lastWarmupTimestampId + 1; i < gResultTimestamps.length; i++) {
|
||||
intervals.push(gResultTimestamps[i] - gResultTimestamps[i - 1]);
|
||||
}
|
||||
|
||||
gDoneCallback(intervals);
|
||||
return;
|
||||
}
|
||||
|
||||
// Used for rendering reproducible frames which are independent of actual performance (timestamps).
|
||||
var simulatedTimestamp = gCurrentTimestamp * 1000 / 60;
|
||||
|
||||
var speed = 0.001;
|
||||
var angle = simulatedTimestamp * speed;
|
||||
var c = Math.cos(angle / 10);
|
||||
var s = Math.sin(angle / 10);
|
||||
var light_x = Math.cos(angle);
|
||||
var light_y = -1;
|
||||
var light_z = Math.sin(angle);
|
||||
var l = Math.sqrt(light_x * light_x + light_y * light_y + light_z * light_z);
|
||||
light_x /= l;
|
||||
light_y /= l;
|
||||
light_z /= l;
|
||||
gl.uniform3f(lightDirectionUniformLoc, light_x, light_y, light_z);
|
||||
|
||||
modelviewMatrix[0] = c;
|
||||
modelviewMatrix[1] = 0;
|
||||
modelviewMatrix[2] = s;
|
||||
modelviewMatrix[3] = 0;
|
||||
|
||||
modelviewMatrix[4] = 0;
|
||||
modelviewMatrix[5] = 1;
|
||||
modelviewMatrix[6] = 0;
|
||||
modelviewMatrix[7] = 0;
|
||||
|
||||
modelviewMatrix[8] = -s;
|
||||
modelviewMatrix[9] = 0;
|
||||
modelviewMatrix[10] = c;
|
||||
modelviewMatrix[11] = 0;
|
||||
|
||||
modelviewMatrix[12] = -terrainSize / 2;
|
||||
modelviewMatrix[13] = 0;
|
||||
modelviewMatrix[14] = -terrainSize;
|
||||
modelviewMatrix[15] = 1;
|
||||
|
||||
gl.uniformMatrix4fv(modelviewUniformLoc, false, modelviewMatrix);
|
||||
|
||||
var fieldOfViewY = Math.PI / 4;
|
||||
aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight;
|
||||
var zNear = 1;
|
||||
var zFar = terrainSize;
|
||||
|
||||
projectionMatrix[0] = fieldOfViewY / aspectRatio;
|
||||
projectionMatrix[1] = 0;
|
||||
projectionMatrix[2] = 0;
|
||||
projectionMatrix[3] = 0;
|
||||
|
||||
projectionMatrix[4] = 0;
|
||||
projectionMatrix[5] = fieldOfViewY;
|
||||
projectionMatrix[6] = 0;
|
||||
projectionMatrix[7] = 0;
|
||||
|
||||
projectionMatrix[8] = 0;
|
||||
projectionMatrix[9] = 0;
|
||||
projectionMatrix[10] = (zNear + zFar) / (zNear - zFar);
|
||||
projectionMatrix[11] = -1;
|
||||
|
||||
projectionMatrix[12] = 0;
|
||||
projectionMatrix[13] = 0;
|
||||
projectionMatrix[14] = 2 * zNear * zFar / (zNear - zFar);
|
||||
projectionMatrix[15] = 0;
|
||||
|
||||
gl.uniformMatrix4fv(projectionUniformLoc, false, projectionMatrix);
|
||||
|
||||
gl.uniform1f(grassTextureSamplerUniformLoc, 0);
|
||||
|
||||
gl.clearColor(0.4, 0.6, 1.0, 0.5);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.drawElements(gl.TRIANGLES, (terrainSize - 1) * (terrainSize - 1) * 6, gl.UNSIGNED_SHORT, 0);
|
||||
|
||||
if (gCurrentTimestamp == 1) {
|
||||
// First frame only - wait a bit (after rendering the scene once)
|
||||
requestAnimationFrame(function() {
|
||||
setTimeout(requestAnimationFrame, PRETEST_DELAY_MS, draw);
|
||||
});
|
||||
} else {
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
}
|
||||
|
||||
function onresize() {
|
||||
var canvas = document.getElementById("c");
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
aspectRatio = canvas.width / canvas.height;
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
}
|
||||
|
||||
var gResults = {values: [], names: [], raw: []};
|
||||
|
||||
function setupAndRun(alpha, antialias, name, doneCallback) {
|
||||
// Remove the old canvas if exists, and create a new one for the new run
|
||||
var c = document.getElementById("c");
|
||||
if (c)
|
||||
c.remove();
|
||||
c = document.createElement("canvas");
|
||||
c.id = "c";
|
||||
document.body.insertBefore(c, document.body.firstChild)
|
||||
|
||||
// Trigger the run with specified args, with callback function to process the result
|
||||
startTest(alpha, antialias, function(intervals) {
|
||||
gResults.names.push(name);
|
||||
gResults.raw.push(intervals);
|
||||
setTimeout(doneCallback, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function reportResults(results) {
|
||||
// Format a nice human-readable report
|
||||
var msg = "";
|
||||
for (var i = 0; i < results.names.length; i++) {
|
||||
var data = results.raw[i];
|
||||
var avg = average(data);
|
||||
gResults.values.push(avg); // This is the only "official" reported value for this set
|
||||
var sd = sampleStdDev(data);
|
||||
|
||||
// Compose a nice human readable message. Not used officially anywhere.
|
||||
msg += results.names[i] + "= Average: " + avg.toFixed(2)
|
||||
+ " stddev: " + sd.toFixed(1) + " (" + (100 * sd / avg).toFixed(1) + "%)"
|
||||
+ "\nIntervals: " + data.map(function(v) {
|
||||
// Display with 1 decimal point digit, and make excessively big values noticeable.
|
||||
var value = v.toFixed(1);
|
||||
// Not scientific, but intervals over 2 * average are.. undesirable.
|
||||
var threshold = avg * 2;
|
||||
return v < threshold ? value : " [_" + value + "_] ";
|
||||
}).join(" ")
|
||||
+ "\n\n";
|
||||
}
|
||||
dump(msg); // Put the readable report at talos run-log
|
||||
|
||||
if (window.tpRecordTime) {
|
||||
// within talos - report the results
|
||||
return tpRecordTime(results.values.join(","), 0, results.names.join(","));
|
||||
}
|
||||
// Local run in a plain browser, display the formatted report
|
||||
alert(msg);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// The full test starts here
|
||||
function test() {
|
||||
// We initially hide the <body>, to reduce the chance of spinning our wheels
|
||||
// with incremental ASAP-paint-mode paints during pageload. Now that onload
|
||||
// has fired, we un-hide it:
|
||||
document.body.style.display = "";
|
||||
|
||||
gResults = {values: [], names: [], raw: []};
|
||||
// This test measures average frame interval during WebGL animation as follows:
|
||||
// 1. Creates a new WebGL canvas.
|
||||
// 2. setup the scene and render 1 frame.
|
||||
// 3. Idle a bit (500ms).
|
||||
// 4. Render/Animate several (129) frames using requestAnimationFrame.
|
||||
// 5. Average the intervals of the last (100) frames <-- the result.
|
||||
//
|
||||
// The reason for the initial idle is to allow some internal cleanups (like CC/GC etc).
|
||||
// The unmeasured warmup rendering intervals are to allow Firefox to settle at a consistent rate.
|
||||
// The idle + warmup are common practices in benchmarking where we're mostly interested
|
||||
// in the stable/consistent throughput rather than in the throughput during transition state
|
||||
// from idle to iterating.
|
||||
|
||||
// Run the same sequence 4 times for all combination of alpha and antialias
|
||||
// (Not using promises chaining for better compatibility, ending up with nesting instead)
|
||||
// talos unfortunately trims common prefixes, so we prefix with a running number to keep the full name
|
||||
setupAndRun(false, false, "0.WebGL-terrain-alpha-no-AA-no", function() {
|
||||
setupAndRun(false, true, "1.WebGL-terrain-alpha-no-AA-yes", function() {
|
||||
setupAndRun(true, false, "2.WebGL-terrain-alpha-yes-AA-no", function() {
|
||||
setupAndRun(true, true, "3.WebGL-terrain-alpha-yes-AA-yes", function() {
|
||||
reportResults(gResults);
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="test();" style="overflow:hidden; margin:0; display:none">
|
||||
<canvas id="c"></canvas>
|
||||
<img src="grass.jpeg" style="display:none" id="grass"/>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ function reportResult(totalTime, totalTicks) {
|
|||
// Local run in a plain browser, display the formatted report
|
||||
alert("[talos glvideo result] " + text);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue