192 lines · 7.3 KB · JavaScript
RawDownload
"use strict";/*jslint vars: true, plusplus: true*//*globals Script, MyAvatar, Quat, Render, ScriptDiscoveryService, Window, LODManager, Entities, print*/////  loadedMachine.js//  scripts/developer/tests/////  Created by Howard Stearns on 6/6/16.//  Copyright 2016 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html////  Characterises the performance of heavily loaded or struggling machines.//  There is no point in running this on a machine that producing the target 60 or 90 hz rendering rate.////  The script examines several source of load, including each running script and a couple of Entity types.//  It turns all of these off, as well as LOD management, and twirls in place to get a baseline render rate.//  Then it turns each load on, one at a time, and records a new render rate.//  At the end, it reports the ordered results (in a popup), restores the original loads and LOD management, and quits.////  Small performance changes are not meaningful.//  If you think you find something that is significant, you should probably run again to make sure that it isn't random variation.//  You can also compare (e.g., the baseline) for different conditions such as domain, version, server settings, etc.//  This does not profile scripts as they are used -- it merely measures the effect of having the script loaded and running quietly. var NUMBER_OF_TWIRLS_PER_LOAD = 10;var MILLISECONDS_PER_SECOND = 1000;var WAIT_TIME_MILLISECONDS = MILLISECONDS_PER_SECOND;var accumulatedRotation = 0;var start;var frames = 0;var config = Render.getConfig("Stats");var thisPath = Script.resolvePath('');var scriptData = ScriptDiscoveryService.getRunning();var scripts = scriptData.filter(function (datum) { return datum.name !== 'defaultScripts.js'; }).map(function (script) { return script.path; });// If defaultScripts.js is running, we leave it running, because restarting it safely is a mess.var otherScripts = scripts.filter(function (path) { return path !== thisPath; });var numberLeftRunning = scriptData.length - otherScripts.length;print('initially running', otherScripts.length, 'scripts. Leaving', numberLeftRunning, 'and stopping otherScripts');var typedEntities = {Light: [], ParticleEffect: []};var interestingTypes = Object.keys(typedEntities);var propertiedEntities = {dynamic: []};var interestingProperties = Object.keys(propertiedEntities);var loads = ['ignore', 'baseline'].concat(otherScripts, interestingTypes, interestingProperties);var loadIndex = 0, nLoads = loads.length, load;var results = [];var initialLodIsAutomatic = LODManager.getAutomaticLODAdjust();var SEARCH_RADIUS = 17000;var DEFAULT_LOD = 32768 * 400;LODManager.setAutomaticLODAdjust(false);LODManager.setOctreeSizeScale(DEFAULT_LOD); // Fill the typedEnties with the entityIDs that are already visible. It would be nice if this were more efficient.var allEntities = Entities.findEntities(MyAvatar.position, SEARCH_RADIUS);print('Searching', allEntities.length, 'entities for', interestingTypes, 'and', interestingProperties);var propertiesToGet = ['type', 'visible'].concat(interestingProperties);allEntities.forEach(function (entityID) {    var properties = Entities.getEntityProperties(entityID, propertiesToGet);    if (properties.visible) {        if (interestingTypes.indexOf(properties.type) >= 0) {            typedEntities[properties.type].push(entityID);        } else {            interestingProperties.forEach(function (property) {                if (entityID && properties[property]) {                    propertiedEntities[property].push(entityID);                    entityID = false; // Put in only one bin                }            });        }    }});allEntities = undefined; // free theminterestingTypes.forEach(function (type) {    print('There are', typedEntities[type].length, type, 'entities.');});interestingProperties.forEach(function (property) {    print('There are', propertiedEntities[property].length, property, 'entities.');});function toggleVisibility(type, on) {    typedEntities[type].forEach(function (entityID) {        Entities.editEntity(entityID, {visible: on});    });}function toggleProperty(property, on) {    propertiedEntities[property].forEach(function (entityID) {        var properties = {};        properties[property] = on;        Entities.editEntity(entityID, properties);    });}function restoreOneTest(load) {    print('restore', load);    switch (load) {    case 'baseline':    case 'ignore':  // ignore is used to do a twirl to make sure everything is loaded.        break;    case 'Light':    case 'ParticleEffect':        toggleVisibility(load, true);        break;    case 'dynamic':        toggleProperty(load, 1);        break;    default:        Script.load(load);    }}function undoOneTest(load) {    print('stop', load);    switch (load) {    case 'baseline':    case 'ignore':        break;    case 'Light':    case 'ParticleEffect':        toggleVisibility(load, false);        break;    case 'dynamic':        toggleProperty(load, 0);        break;    default:        ScriptDiscoveryService.stopScript(load);    }}loads.forEach(undoOneTest); function finalReport() {    var baseline = results[0];    results.forEach(function (result) {        result.ratio = (baseline.fps / result.fps);    });    results.sort(function (a, b) { return b.ratio - a.ratio; });    var report = 'Performance Report:\nBaseline: ' + baseline.fps.toFixed(1) + ' fps.';    results.forEach(function (result) {        report += '\n' + result.ratio.toFixed(2) + ' (' + result.fps.toFixed(1) + ' fps over ' + result.elapsed + ' seconds) for ' + result.name;    });    Window.alert(report);    LODManager.setAutomaticLODAdjust(initialLodIsAutomatic);    loads.forEach(restoreOneTest);    Script.stop();} function onNewStats() { // Accumulates frames on signal during load test    frames++;}var DEGREES_PER_FULL_TWIRL = 360;var DEGREES_PER_UPDATE = 6;function onUpdate() { // Spins on update signal during load test    MyAvatar.orientation = Quat.fromPitchYawRollDegrees(0, accumulatedRotation, 0);    accumulatedRotation += DEGREES_PER_UPDATE;    if (accumulatedRotation >= (DEGREES_PER_FULL_TWIRL * NUMBER_OF_TWIRLS_PER_LOAD)) {        tearDown();    }}function startTest() {    print('start', load);    accumulatedRotation = frames = 0;    start = Date.now();    Script.update.connect(onUpdate);    config.newStats.connect(onNewStats);} function setup() {    if (loadIndex >= nLoads) {        return finalReport();    }    load = loads[loadIndex];    restoreOneTest(load);    Script.setTimeout(startTest, WAIT_TIME_MILLISECONDS);}function tearDown() {    var now = Date.now();    var elapsed = (now - start) / MILLISECONDS_PER_SECOND;    Script.update.disconnect(onUpdate);    config.newStats.disconnect(onNewStats);    if (load !== 'ignore') {        results.push({name: load, fps: frames / elapsed, elapsed: elapsed});    }    undoOneTest(load);    loadIndex++;    setup();}function waitUntilStopped() { // Wait until all the scripts have stopped    var count = ScriptDiscoveryService.getRunning().length;    if (count === numberLeftRunning) {        return setup();    }    print('Still', count, 'scripts running');    Script.setTimeout(waitUntilStopped, WAIT_TIME_MILLISECONDS);}waitUntilStopped();