mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 01:22:59 +02:00
improvements
This commit is contained in:
parent
2fbb2338c5
commit
9561142c43
1 changed files with 177 additions and 46 deletions
|
@ -17,37 +17,157 @@ var MINIMUM_DESKTOP_FRAMERATE = 57; // frames per second
|
|||
var MINIMUM_HMD_FRAMERATE = 86;
|
||||
var EXPECTED_DESKTOP_FRAMERATE = 60;
|
||||
var EXPECTED_HMD_FRAMERATE = 90;
|
||||
var MAXIMUM_LOAD_TIME = 60; // seconds
|
||||
var MINIMUM_AVATARS = 25; // FIXME: not implemented yet. Requires agent scripts. Idea is to have them organize themselves to the right number.
|
||||
var NOMINAL_LOAD_TIME = 30; // seconds
|
||||
var MAXIMUM_LOAD_TIME = NOMINAL_LOAD_TIME * 2;
|
||||
var MINIMUM_AVATARS = 25; // changeable by prompt
|
||||
|
||||
var version = 2;
|
||||
var DENSITY = 0.3; // square meters per person. Some say 10 sq ft is arm's length (0.9m^2), 4.5 is crowd (0.4m^2), 2.5 is mosh pit (0.2m^2).
|
||||
var SOUND_DATA = {url: "http://howard-stearns.github.io/models/sounds/piano1.wav"};
|
||||
var AVATARS_CHATTERING_AT_ONCE = 4; // How many of the agents should we request to play SOUND at once.
|
||||
var NEXT_SOUND_SPREAD = 500; // millisecond range of how long to wait after one sound finishes, before playing the next
|
||||
var ANIMATION_DATA = {
|
||||
"url": "http://howard-stearns.github.io/models/resources/avatar/animations/idle.fbx",
|
||||
// "url": "http://howard-stearns.github.io/models/resources/avatar/animations/walk_fwd.fbx", // alternative example
|
||||
"startFrame": 0.0,
|
||||
"endFrame": 300.0,
|
||||
"timeScale": 1.0,
|
||||
"loopFlag": true
|
||||
};
|
||||
|
||||
var version = 11;
|
||||
function debug() {
|
||||
print.apply(null, [].concat.apply(['hrs fixme', version], [].map.call(arguments, JSON.stringify)));
|
||||
}
|
||||
|
||||
var cachePlaces = ['localhost', 'welcome'].map(canonicalizePlacename); // For now, list the lighter weight one first.
|
||||
var defaultPlace = location.hostname;
|
||||
var prompt = "domain-check.js version " + version + "\n\nWhat place should we enter?";
|
||||
debug(cachePlaces, defaultPlace, prompt);
|
||||
var entryPlace = Window.prompt(prompt, defaultPlace);
|
||||
var runTribbles = Window.confirm("Run tribbles?\n\n\
|
||||
At most, only one participant should say yes.");
|
||||
MINIMUM_AVATARS = parseInt(Window.prompt("Total avatars (including yourself and any already present)?", MINIMUM_AVATARS.toString()) || "0");
|
||||
AVATARS_CHATTERING_AT_ONCE = MINIMUM_AVATARS ? parseInt(Window.prompt("Number making sound?", Math.min(MINIMUM_AVATARS - 1, AVATARS_CHATTERING_AT_ONCE).toString()) || "0") : 0;
|
||||
|
||||
function canonicalizePlacename(name) {
|
||||
var prefix = 'dev-';
|
||||
name = name.toLowerCase();
|
||||
if (name.indexOf(prefix) === 0) {
|
||||
name = name.slice(prefix.length);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
function placesMatch(a, b) { // handling case and 'dev-' variations
|
||||
return canonicalizePlacename(a) === canonicalizePlacename(b);
|
||||
}
|
||||
function isNowIn(place) { // true if currently in specified place
|
||||
return location.hostname.toLowerCase() === place.toLowerCase();
|
||||
placesMatch(location.hostname, place);
|
||||
}
|
||||
|
||||
var cachePlaces = ['dev-Welcome', 'localhost']; // For now, list the lighter weight one first.
|
||||
var isInCachePlace = cachePlaces.some(isNowIn);
|
||||
var defaultPlace = isInCachePlace ? 'dev-Playa' : location.hostname;
|
||||
var prompt = "domain-check.js version " + version + "\n\nWhat place should we enter?";
|
||||
debug(cachePlaces, isInCachePlace, defaultPlace, prompt);
|
||||
var entryPlace = Window.prompt(prompt, defaultPlace);
|
||||
function go(place) { // handle (dev-)welcome in the appropriate version-specific way
|
||||
debug('go', place);
|
||||
if (placesMatch(place, 'welcome')) {
|
||||
location.goToEntry();
|
||||
} else {
|
||||
location.handleLookupString(place);
|
||||
}
|
||||
}
|
||||
|
||||
var spread = Math.sqrt(MINIMUM_AVATARS * DENSITY); // meters
|
||||
var turnSpread = 90; // How many degrees should turn from front range over.
|
||||
|
||||
function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2.
|
||||
function contains(array, item) { return array.indexOf(item) >= 0; }
|
||||
function without(array, itemsToRemove) { return array.filter(function (item) { return !contains(itemsToRemove, item); }); }
|
||||
function nextAfter(array, id) { // Wrapping next element in array after id.
|
||||
var index = array.indexOf(id) + 1;
|
||||
return array[(index >= array.length) ? 0 : index];
|
||||
}
|
||||
|
||||
var summonedAgents = [];
|
||||
var chattering = [];
|
||||
var MESSAGE_CHANNEL = "io.highfidelity.summon-crowd";
|
||||
function messageSend(message) {
|
||||
Messages.sendMessage(MESSAGE_CHANNEL, JSON.stringify(message));
|
||||
}
|
||||
function messageHandler(channel, messageString, senderID) {
|
||||
if (channel !== MESSAGE_CHANNEL) {
|
||||
return;
|
||||
}
|
||||
debug('message', channel, messageString, senderID);
|
||||
if (MyAvatar.sessionUUID === senderID) { // ignore my own
|
||||
return;
|
||||
}
|
||||
var message = {}, avatarIdentifiers;
|
||||
try {
|
||||
message = JSON.parse(messageString);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
switch (message.key) {
|
||||
case "hello":
|
||||
// There can be avatars we've summoned that do not yet appear in the AvatarList.
|
||||
avatarIdentifiers = without(AvatarList.getAvatarIdentifiers(), summonedAgents);
|
||||
debug('present', avatarIdentifiers, summonedAgents);
|
||||
if ((summonedAgents.length + avatarIdentifiers.length) < MINIMUM_AVATARS ) {
|
||||
var chatter = chattering.length < AVATARS_CHATTERING_AT_ONCE;
|
||||
if (chatter) {
|
||||
chattering.push(senderID);
|
||||
}
|
||||
summonedAgents.push(senderID);
|
||||
messageSend({
|
||||
key: 'SUMMON',
|
||||
rcpt: senderID,
|
||||
position: Vec3.sum(MyAvatar.position, {x: coord(), y: 0, z: coord()}),
|
||||
orientation: Quat.fromPitchYawRollDegrees(0, Quat.safeEulerAngles(MyAvatar.orientation).y + (turnSpread * (Math.random() - 0.5)), 0),
|
||||
soundData: chatter && SOUND_DATA,
|
||||
skeletonModelURL: "http://howard-stearns.github.io/models/resources/meshes/defaultAvatar_full.fst",
|
||||
animationData: ANIMATION_DATA
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "finishedSound": // Give someone else a chance.
|
||||
chattering = without(chattering, [senderID]);
|
||||
Script.setTimeout(function () {
|
||||
messageSend({
|
||||
key: 'SUMMON',
|
||||
rcpt: nextAfter(without(summonedAgents, chattering), senderID),
|
||||
soundData: SOUND_DATA
|
||||
});
|
||||
}, Math.random() * NEXT_SOUND_SPREAD);
|
||||
break;
|
||||
case "HELO":
|
||||
Window.alert("Someone else is summoning avatars.");
|
||||
break;
|
||||
default:
|
||||
print("crowd-agent received unrecognized message:", messageString);
|
||||
}
|
||||
}
|
||||
Messages.subscribe(MESSAGE_CHANNEL);
|
||||
Messages.messageReceived.connect(messageHandler);
|
||||
Script.scriptEnding.connect(function () {
|
||||
debug('stopping agents', summonedAgents);
|
||||
summonedAgents.forEach(function (id) { messageSend({key: 'STOP', rcpt: id}); });
|
||||
debug('agents stopped');
|
||||
Script.setTimeout(function () {
|
||||
Messages.messageReceived.disconnect(messageHandler);
|
||||
Messages.unsubscribe(MESSAGE_CHANNEL);
|
||||
debug('unsubscribed');
|
||||
}, 500);
|
||||
});
|
||||
|
||||
var fail = false, results = "";
|
||||
function addResult(label, actual, minimum, maximum) {
|
||||
function addResult(label, actual, nominal, minimum, maximum) {
|
||||
if ((minimum !== undefined) && (actual < minimum)) {
|
||||
fail = true;
|
||||
fail = ' FAILED: ' + label + ' below ' + minimum;
|
||||
}
|
||||
if ((maximum !== undefined) && (actual > maximum)) {
|
||||
fail = true;
|
||||
fail = ' FAILED: ' + label + ' above ' + maximum;
|
||||
}
|
||||
results += "\n" + label + ": " + actual + " (" + ((100 * actual) / (maximum || minimum)).toFixed(0) + "%)";
|
||||
results += "\n" + label + ": " + actual.toFixed(0) + " (" + ((100 * actual) / nominal).toFixed(0) + "%)";
|
||||
}
|
||||
function giveReport() {
|
||||
Window.alert(entryPlace + (fail ? " FAILED" : " OK") + "\n" + results);
|
||||
Window.alert(entryPlace + (fail || " OK") + "\n" + results + "\nwith " + summonedAgents.length + " avatars added,\nand " + AVATARS_CHATTERING_AT_ONCE + " making noise.");
|
||||
}
|
||||
|
||||
// Tests are performed domain-wide, at full LOD
|
||||
|
@ -122,9 +242,8 @@ function doLoad(place, continuationWithLoadTime) { // Go to place and call conti
|
|||
}
|
||||
};
|
||||
|
||||
debug('go', place);
|
||||
location.hostChanged.connect(waitForLoad);
|
||||
location.handleLookupString(place);
|
||||
go(place);
|
||||
}
|
||||
|
||||
var config = Render.getConfig("Stats");
|
||||
|
@ -133,48 +252,59 @@ function doRender(continuation) {
|
|||
function onNewStats() { // Accumulates frames on signal during load test
|
||||
frames++;
|
||||
}
|
||||
if (MINIMUM_AVATARS) {
|
||||
messageSend({key: 'HELO'}); // Ask agents to report in now.
|
||||
}
|
||||
|
||||
config.newStats.connect(onNewStats);
|
||||
startTwirl(720, 1, 15, 0.08, function () {
|
||||
var end = Date.now();
|
||||
config.newStats.disconnect(onNewStats);
|
||||
addResult('frame rate', 1000 * frames / (end - start),
|
||||
HMD.active ? MINIMUM_HMD_FRAMERATE : MINIMUM_DESKTOP_FRAMERATE,
|
||||
HMD.active ? EXPECTED_HMD_FRAMERATE : EXPECTED_DESKTOP_FRAMERATE);
|
||||
HMD.active ? EXPECTED_HMD_FRAMERATE : EXPECTED_DESKTOP_FRAMERATE,
|
||||
HMD.active ? MINIMUM_HMD_FRAMERATE : MINIMUM_DESKTOP_FRAMERATE);
|
||||
var total = AvatarList.getAvatarIdentifiers().length;
|
||||
if (MINIMUM_AVATARS && !fail) {
|
||||
if (0 === summonedAgents.length) {
|
||||
fail = "FAIL: No agents reported.\n\Please run " + MINIMUM_AVATARS + " instances of\n\
|
||||
http://cdn.highfidelity.com/davidkelly/production/scripts/tests/performance/crowd-agent.js\n\
|
||||
on your domain server.";
|
||||
} else if (total < MINIMUM_AVATARS) {
|
||||
fail = "FAIL: Only " + summonedAgents.length + " of the expected " + (MINIMUM_AVATARS - total) + " agents reported in.";
|
||||
}
|
||||
}
|
||||
continuation();
|
||||
});
|
||||
}
|
||||
|
||||
var TELEPORT_PAUSE = 500;
|
||||
function maybePrepareCache(continuation) {
|
||||
var prepareCache = Window.confirm("Prepare cache?\n\n\
|
||||
Should we start with all and only those items cached that are encountered when visiting:\n" + cachePlaces.join(', ') + "\n\
|
||||
If 'yes', cache will be cleared and we will visit these two, with a turn in each, and wait for everything to be loaded.\n\
|
||||
You would want to say 'no' (and make other preparations) if you were testing these places.");
|
||||
|
||||
if (prepareCache) {
|
||||
function loadNext() {
|
||||
var place = cachePlaces.shift();
|
||||
doLoad(place, function (prepTime) {
|
||||
debug(place, 'ready', prepTime);
|
||||
if (cachePlaces.length) {
|
||||
loadNext();
|
||||
} else {
|
||||
continuation();
|
||||
}
|
||||
});
|
||||
}
|
||||
location.handleLookupString(cachePlaces[cachePlaces.length - 1]);
|
||||
function prepareCache(continuation) {
|
||||
function loadNext() {
|
||||
var place = cachePlaces.shift();
|
||||
doLoad(place, function (prepTime) {
|
||||
debug(place, 'ready', prepTime);
|
||||
if (cachePlaces.length) {
|
||||
loadNext();
|
||||
} else {
|
||||
continuation();
|
||||
}
|
||||
});
|
||||
}
|
||||
// remove entryPlace target from cachePlaces
|
||||
var targetInCache = cachePlaces.indexOf(canonicalizePlacename(entryPlace));
|
||||
if (targetInCache !== -1) {
|
||||
cachePlaces.splice(targetInCache, 1);
|
||||
}
|
||||
debug('cachePlaces', cachePlaces)
|
||||
go(cachePlaces[1] || entryPlace); // Not quite right for entryPlace case (allows some qt pre-caching), but close enough.
|
||||
Script.setTimeout(function () {
|
||||
Menu.triggerOption("Reload Content (Clears all caches)");
|
||||
Script.setTimeout(loadNext, TELEPORT_PAUSE);
|
||||
} else {
|
||||
location.handleLookupString(isNowIn(cachePlaces[0]) ? cachePlaces[1] : cachePlaces[0]);
|
||||
Script.setTimeout(continuation, TELEPORT_PAUSE);
|
||||
}
|
||||
}, TELEPORT_PAUSE);
|
||||
}
|
||||
|
||||
function maybeRunTribbles(continuation) {
|
||||
if (Window.confirm("Run tribbles?\n\n\
|
||||
At most, only one participant should say yes.")) {
|
||||
if (runTribbles) {
|
||||
Script.load('http://cdn.highfidelity.com/davidkelly/production/scripts/tests/performance/tribbles.js');
|
||||
Script.setTimeout(continuation, 3000);
|
||||
} else {
|
||||
|
@ -186,10 +316,11 @@ if (!entryPlace) {
|
|||
Window.alert("domain-check.js cancelled");
|
||||
Script.stop();
|
||||
} else {
|
||||
maybePrepareCache(function (prepTime) {
|
||||
prepareCache(function (prepTime) {
|
||||
debug('cache ready', prepTime);
|
||||
doLoad(entryPlace, function (loadTime) {
|
||||
addResult("load time", loadTime, undefined, MAXIMUM_LOAD_TIME);
|
||||
addResult("load time", loadTime, NOMINAL_LOAD_TIME, undefined, MAXIMUM_LOAD_TIME);
|
||||
LODManager.setAutomaticLODAdjust(initialLodIsAutomatic); // after loading, restore lod.
|
||||
maybeRunTribbles(function () {
|
||||
doRender(function () {
|
||||
giveReport();
|
||||
|
|
Loading…
Reference in a new issue