resolve conflicts on merge with upstream master

This commit is contained in:
Stephen Birarda 2015-02-03 10:25:17 -08:00
commit e07ce23825
24 changed files with 675 additions and 406 deletions

4
.gitignore vendored
View file

@ -44,4 +44,6 @@ libraries/audio-client/external/*/*
!libraries/audio-client/external/*/readme.txt
gvr-interface/assets/oculussig*
gvr-interface/libs/*
gvr-interface/libs/*
TAGS

121
examples/planets.js Normal file
View file

@ -0,0 +1,121 @@
//
// planets.js
//
// Created by Philip Rosedale on January 26, 2015
// Copyright 2015 High Fidelity, Inc.
//
// Some planets are created in front of you. A physical object shot or thrown between them will move
// correctly.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var MAX_RANGE = 75.0;
var MAX_TRANSLATION = MAX_RANGE / 20.0;
var LIFETIME = 600;
var DAMPING = 0.0;
var G = 3.0;
// In this section, setup where you want your 'Planets' that will exert gravity on the
// smaller test particles. Use the first one for the simplest 'planets around sun' simulation.
// Add additional planets to make things a lot more complicated!
var planetTypes = [];
planetTypes.push({ radius: 15, red: 0, green: 0, blue: 255, x: 0.0, y:0, z: 0.0 });
//planetTypes.push({ radius: 10, red: 0, green: 255, blue: 0, x: 0.60, y:0, z: 0.60 });
//planetTypes.push({ radius: 10, red: 0, green: 0, blue: 255, x: 0.75, y:0, z: 0.75 });
//planetTypes.push({ radius: 5, red: 255, green: 0, blue: 0, x: 0.25, y:0, z: 0.25 });
//planetTypes.push({ radius: 5, red: 0, green: 255, blue: 255, x: 0, y:0, z: 0 });
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(MAX_RANGE, Quat.getFront(Camera.getOrientation())));
var NUM_INITIAL_PARTICLES = 200;
var PARTICLE_MIN_SIZE = 0.50;
var PARTICLE_MAX_SIZE = 1.50;
var INITIAL_VELOCITY = 5.0;
var planets = [];
var particles = [];
// Create planets that will extert gravity on test particles
for (var i = 0; i < planetTypes.length; i++) {
var rotationalVelocity = 10 + Math.random() * 60;
var position = { x: planetTypes[i].x, y: planetTypes[i].y, z: planetTypes[i].z };
position = Vec3.multiply(MAX_RANGE / 2, position);
position = Vec3.sum(center, position);
planets.push(Entities.addEntity(
{ type: "Sphere",
position: position,
dimensions: { x: planetTypes[i].radius, y: planetTypes[i].radius, z: planetTypes[i].radius },
color: { red: planetTypes[i].red, green: planetTypes[i].green, blue: planetTypes[i].blue },
gravity: { x: 0, y: 0, z: 0 },
angularVelocity: { x: 0, y: rotationalVelocity, z: 0 },
angularDamping: 0.0,
ignoreCollisions: false,
lifetime: LIFETIME,
collisionsWillMove: false }));
}
Script.setTimeout(createParticles, 1000);
function createParticles() {
// Create initial test particles that will move according to gravity from the planets
for (var i = 0; i < NUM_INITIAL_PARTICLES; i++) {
var radius = PARTICLE_MIN_SIZE + Math.random() * PARTICLE_MAX_SIZE;
var gray = Math.random() * 155;
var whichPlanet = Math.floor(Math.random() * planets.length);
var position = { x: (Math.random() - 0.5) * MAX_RANGE, y: (Math.random() - 0.5) * MAX_TRANSLATION, z: (Math.random() - 0.5) * MAX_RANGE };
var separation = Vec3.length(position);
particles.push(Entities.addEntity(
{ type: "Sphere",
position: Vec3.sum(center, position),
dimensions: { x: radius, y: radius, z: radius },
color: { red: 100 + gray, green: 100 + gray, blue: 100 + gray },
gravity: { x: 0, y: 0, z: 0 },
angularVelocity: { x: 0, y: 0, z: 0 },
velocity: Vec3.multiply(INITIAL_VELOCITY * Math.sqrt(separation), Vec3.normalize(Vec3.cross(position, { x: 0, y: 1, z: 0 }))),
ignoreCollisions: false,
damping: DAMPING,
lifetime: LIFETIME,
collisionsWillMove: true }));
}
Script.update.connect(update);
}
function scriptEnding() {
for (var i = 0; i < planetTypes.length; i++) {
Entities.deleteEntity(planets[i]);
}
for (var i = 0; i < particles.length; i++) {
Entities.deleteEntity(particles[i]);
}
}
function update(deltaTime) {
// Apply gravitational force from planets
for (var t = 0; t < particles.length; t++) {
var properties1 = Entities.getEntityProperties(particles[t]);
var velocity = properties1.velocity;
var vColor = Vec3.length(velocity) / 50 * 255;
var dV = { x:0, y:0, z:0 };
var mass1 = Math.pow(properties1.dimensions.x / 2.0, 3.0);
for (var p = 0; p < planets.length; p++) {
var properties2 = Entities.getEntityProperties(planets[p]);
var mass2 = Math.pow(properties2.dimensions.x / 2.0, 3.0);
var between = Vec3.subtract(properties1.position, properties2.position);
var separation = Vec3.length(between);
dV = Vec3.sum(dV, Vec3.multiply(-G * mass1 * mass2 / separation, Vec3.normalize(between)));
}
if (Math.random() < 0.1) {
Entities.editEntity(particles[t], { color: { red: vColor, green: 100, blue: (255 - vColor) }, velocity: Vec3.sum(velocity, Vec3.multiply(deltaTime, dV))});
} else {
Entities.editEntity(particles[t], { velocity: Vec3.sum(velocity, Vec3.multiply(deltaTime, dV))});
}
}
}
Script.scriptEnding.connect(scriptEnding);

View file

@ -200,6 +200,7 @@ bool setupEssentials(int& argc, char** argv) {
auto lodManager = DependencyManager::set<LODManager>();
auto jsConsole = DependencyManager::set<StandAloneJSConsole>();
auto dialogsManager = DependencyManager::set<DialogsManager>();
auto bandwidthRecorder = DependencyManager::set<BandwidthRecorder>();
#if defined(Q_OS_MAC) || defined(Q_OS_WIN)
auto speechRecognizer = DependencyManager::set<SpeechRecognizer>();
#endif
@ -240,10 +241,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_mousePressed(false),
_enableProcessOctreeThread(true),
_octreeProcessor(),
_inPacketsPerSecond(0),
_outPacketsPerSecond(0),
_inBytesPerSecond(0),
_outBytesPerSecond(0),
_nodeBoundsDisplay(this),
_previousScriptLocation(),
_applicationOverlay(),
@ -436,6 +433,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(this, SIGNAL(aboutToQuit()), this, SLOT(saveScripts()));
connect(this, SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
// hook up bandwidth estimator
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
connect(nodeList.data(), SIGNAL(dataSent(const quint8, const int)),
bandwidthRecorder.data(), SLOT(updateOutboundData(const quint8, const int)));
connect(nodeList.data(), SIGNAL(dataReceived(const quint8, const int)),
bandwidthRecorder.data(), SLOT(updateInboundData(const quint8, const int)));
// check first run...
bool firstRun = SettingHandles::firstRun.get();
if (firstRun) {
@ -774,25 +778,8 @@ void Application::updateProjectionMatrix(Camera& camera, bool updateViewFrustum)
void Application::controlledBroadcastToNodes(const QByteArray& packet, const NodeSet& destinationNodeTypes) {
foreach(NodeType_t type, destinationNodeTypes) {
// Perform the broadcast for one type
int nReceivingNodes = DependencyManager::get<NodeList>()->broadcastToNodes(packet, NodeSet() << type);
// Feed number of bytes to corresponding channel of the bandwidth meter, if any (done otherwise)
double bandwidth_amount = nReceivingNodes * packet.size();
switch (type) {
case NodeType::Agent:
case NodeType::AvatarMixer:
_bandwidthRecorder.avatarsChannel->output.updateValue(bandwidth_amount);
_bandwidthRecorder.totalChannel->input.updateValue(bandwidth_amount);
break;
case NodeType::EntityServer:
_bandwidthRecorder.octreeChannel->output.updateValue(bandwidth_amount);
_bandwidthRecorder.totalChannel->output.updateValue(bandwidth_amount);
break;
default:
continue;
}
DependencyManager::get<NodeList>()->broadcastToNodes(packet, NodeSet() << type);
}
}
@ -1393,15 +1380,8 @@ void Application::timer() {
float diffTime = (float)_timerStart.nsecsElapsed() / 1000000000.0f;
_fps = (float)_frameCount / diffTime;
_inPacketsPerSecond = (float) _datagramProcessor.getInPacketCount() / diffTime;
_outPacketsPerSecond = (float) _datagramProcessor.getOutPacketCount() / diffTime;
_inBytesPerSecond = (float) _datagramProcessor.getInByteCount() / diffTime;
_outBytesPerSecond = (float) _datagramProcessor.getOutByteCount() / diffTime;
_frameCount = 0;
_datagramProcessor.resetCounters();
_timerStart.start();
// ask the node list to check in with the domain server
@ -2366,10 +2346,6 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType, Node
// make sure we still have an active socket
nodeList->writeUnverifiedDatagram(reinterpret_cast<const char*>(queryPacket), packetLength, node);
// Feed number of bytes to corresponding channel of the bandwidth meter
_bandwidthRecorder.octreeChannel->output.updateValue(packetLength);
_bandwidthRecorder.totalChannel->output.updateValue(packetLength);
}
});
}
@ -3357,8 +3333,6 @@ int Application::parseOctreeStats(const QByteArray& packet, const SharedNodePoin
}
void Application::packetSent(quint64 length) {
_bandwidthRecorder.octreeChannel->output.updateValue(length);
_bandwidthRecorder.totalChannel->output.updateValue(length);
}
const QString SETTINGS_KEY = "Settings";

View file

@ -198,16 +198,11 @@ public:
bool getLastMouseMoveWasSimulated() const { return _lastMouseMoveWasSimulated; }
FaceTracker* getActiveFaceTracker();
BandwidthRecorder* getBandwidthRecorder() { return &_bandwidthRecorder; }
QSystemTrayIcon* getTrayIcon() { return _trayIcon; }
ApplicationOverlay& getApplicationOverlay() { return _applicationOverlay; }
Overlays& getOverlays() { return _overlays; }
float getFps() const { return _fps; }
float getInPacketsPerSecond() const { return _inPacketsPerSecond; }
float getOutPacketsPerSecond() const { return _outPacketsPerSecond; }
float getInBytesPerSecond() const { return _inBytesPerSecond; }
float getOutBytesPerSecond() const { return _outBytesPerSecond; }
const glm::vec3& getViewMatrixTranslation() const { return _viewMatrixTranslation; }
void setViewMatrixTranslation(const glm::vec3& translation) { _viewMatrixTranslation = translation; }
@ -476,8 +471,6 @@ private:
OctreeQuery _octreeQuery; // NodeData derived class for querying octee cells from octree servers
BandwidthRecorder _bandwidthRecorder;
MyAvatar* _myAvatar; // TODO: move this and relevant code to AvatarManager (or MyAvatar as the case may be)
PrioVR _prioVR;
@ -525,11 +518,6 @@ private:
OctreePacketProcessor _octreeProcessor;
EntityEditPacketSender _entityEditSender;
int _inPacketsPerSecond;
int _outPacketsPerSecond;
int _inBytesPerSecond;
int _outBytesPerSecond;
StDev _idleLoopStdev;
float _idleLoopMeasuredJitter;

View file

@ -1,64 +0,0 @@
//
// BandwidthMeter.cpp
// interface/src/ui
//
// Created by Seth Alves on 2015-1-30
// Copyright 2015 High Fidelity, Inc.
//
// Based on code by Tobias Schwinger
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <GeometryCache.h>
#include "BandwidthRecorder.h"
BandwidthRecorder::Channel::Channel(char const* const caption,
char const* unitCaption,
double unitScale,
unsigned colorRGBA) :
caption(caption),
unitCaption(unitCaption),
unitScale(unitScale),
colorRGBA(colorRGBA)
{
}
BandwidthRecorder::BandwidthRecorder() {
}
BandwidthRecorder::~BandwidthRecorder() {
delete audioChannel;
delete avatarsChannel;
delete octreeChannel;
delete metavoxelsChannel;
delete totalChannel;
}
BandwidthRecorder::Stream::Stream(float msToAverage) : _value(0.0f), _msToAverage(msToAverage) {
_prevTime.start();
}
void BandwidthRecorder::Stream::updateValue(double amount) {
// Determine elapsed time
double dt = (double)_prevTime.nsecsElapsed() / 1000000.0; // ns to ms
// Ignore this value when timer imprecision yields dt = 0
if (dt == 0.0) {
return;
}
_prevTime.start();
// Compute approximate average
_value = glm::mix(_value, amount / dt,
glm::clamp(dt / _msToAverage, 0.0, 1.0));
}

View file

@ -1,56 +0,0 @@
//
// BandwidthRecorder.h
//
// Created by Seth Alves on 2015-1-30
// Copyright 2015 High Fidelity, Inc.
//
// Based on code by Tobias Schwinger
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_BandwidthRecorder_h
#define hifi_BandwidthRecorder_h
#include <QElapsedTimer>
class BandwidthRecorder {
public:
BandwidthRecorder();
~BandwidthRecorder();
// keep track of data rate in one direction
class Stream {
public:
Stream(float msToAverage = 3000.0f);
void updateValue(double amount);
double getValue() const { return _value; }
private:
double _value; // Current value.
double _msToAverage; // Milliseconds to average.
QElapsedTimer _prevTime; // Time of last feed.
};
// keep track of data rate in two directions as well as units and style to use during display
class Channel {
public:
Channel(char const* const caption, char const* unitCaption, double unitScale, unsigned colorRGBA);
Stream input;
Stream output;
char const* const caption;
char const* unitCaption;
double unitScale;
unsigned colorRGBA;
};
// create the channels we keep track of
Channel* audioChannel = new Channel("Audio", "Kbps", 8000.0 / 1024.0, 0x33cc99ff);
Channel* avatarsChannel = new Channel("Avatars", "Kbps", 8000.0 / 1024.0, 0xffef40c0);
Channel* octreeChannel = new Channel("Octree", "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0);
Channel* metavoxelsChannel = new Channel("Metavoxels", "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0);
Channel* totalChannel = new Channel("Total", "Kbps", 8000.0 / 1024.0, 0xd0d0d0a0);
};
#endif

View file

@ -40,8 +40,7 @@ void DatagramProcessor::processDatagrams() {
while (DependencyManager::get<NodeList>()->getNodeSocket().hasPendingDatagrams()) {
incomingPacket.resize(nodeList->getNodeSocket().pendingDatagramSize());
nodeList->getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(),
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
nodeList->readDatagram(incomingPacket, senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
_inPacketCount++;
_inByteCount += incomingPacket.size();
@ -74,7 +73,6 @@ void DatagramProcessor::processDatagrams() {
if (audioMixer) {
audioMixer->setLastHeardMicrostamp(usecTimestampNow());
audioMixer->recordBytesReceived(incomingPacket.size());
}
break;
@ -83,8 +81,6 @@ void DatagramProcessor::processDatagrams() {
// this will keep creatorTokenIDs to IDs mapped correctly
EntityItemID::handleAddEntityResponse(incomingPacket);
application->getEntities()->getTree()->handleAddEntityResponse(incomingPacket);
application->_bandwidthRecorder.octreeChannel->input.updateValue(incomingPacket.size());
application->_bandwidthRecorder.totalChannel->input.updateValue(incomingPacket.size());
break;
case PacketTypeEntityData:
case PacketTypeEntityErase:
@ -98,8 +94,6 @@ void DatagramProcessor::processDatagrams() {
// add this packet to our list of octree packets and process them on the octree data processing
application->_octreeProcessor.queueReceivedPacket(matchedNode, incomingPacket);
}
application->_bandwidthRecorder.octreeChannel->input.updateValue(incomingPacket.size());
application->_bandwidthRecorder.totalChannel->input.updateValue(incomingPacket.size());
break;
}
case PacketTypeMetavoxelData:
@ -114,15 +108,11 @@ void DatagramProcessor::processDatagrams() {
if (avatarMixer) {
avatarMixer->setLastHeardMicrostamp(usecTimestampNow());
avatarMixer->recordBytesReceived(incomingPacket.size());
QMetaObject::invokeMethod(DependencyManager::get<AvatarManager>().data(), "processAvatarMixerDatagram",
Q_ARG(const QByteArray&, incomingPacket),
Q_ARG(const QWeakPointer<Node>&, avatarMixer));
}
application->_bandwidthRecorder.avatarsChannel->input.updateValue(incomingPacket.size());
application->_bandwidthRecorder.totalChannel->input.updateValue(incomingPacket.size());
break;
}
case PacketTypeDomainConnectionDenied: {

View file

@ -899,8 +899,6 @@ int MetavoxelSystemClient::parseData(const QByteArray& packet) {
} else {
QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet));
}
Application::getInstance()->getBandwidthRecorder()->metavoxelsChannel->input.updateValue(packet.size());
Application::getInstance()->getBandwidthRecorder()->totalChannel->input.updateValue(packet.size());
}
return packet.size();
}
@ -1016,8 +1014,6 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) {
} else {
DependencyManager::get<NodeList>()->writeDatagram(data, _node);
}
Application::getInstance()->getBandwidthRecorder()->metavoxelsChannel->output.updateValue(data.size());
Application::getInstance()->getBandwidthRecorder()->totalChannel->output.updateValue(data.size());
}
}

View file

@ -897,6 +897,7 @@ void ApplicationOverlay::renderAudioMeter() {
void ApplicationOverlay::renderStatsAndLogs() {
Application* application = Application::getInstance();
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
auto glCanvas = DependencyManager::get<GLCanvas>();
const OctreePacketProcessor& octreePacketProcessor = application->getOctreePacketProcessor();
@ -912,10 +913,10 @@ void ApplicationOverlay::renderStatsAndLogs() {
int voxelPacketsToProcess = octreePacketProcessor.packetsToProcessCount();
// Onscreen text about position, servers, etc
Stats::getInstance()->display(WHITE_TEXT, horizontalOffset, application->getFps(),
application->getInPacketsPerSecond(),
application->getOutPacketsPerSecond(),
application->getInBytesPerSecond(),
application->getOutBytesPerSecond(),
bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond(),
bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond(),
bandwidthRecorder->getCachedTotalAverageInputKilobitsPerSecond(),
bandwidthRecorder->getCachedTotalAverageOutputKilobitsPerSecond(),
voxelPacketsToProcess);
}

View file

@ -21,42 +21,54 @@
#include <QColor>
BandwidthDialog::ChannelDisplay::ChannelDisplay(BandwidthRecorder::Channel *ch, QFormLayout* form) {
this->ch = ch;
this->label = setupLabel(form);
}
BandwidthChannelDisplay::BandwidthChannelDisplay(QVector<NodeType_t> nodeTypesToFollow,
QFormLayout* form,
char const* const caption, char const* unitCaption,
const float unitScale, unsigned colorRGBA) :
_nodeTypesToFollow(nodeTypesToFollow),
_caption(caption),
_unitCaption(unitCaption),
_unitScale(unitScale),
_colorRGBA(colorRGBA)
{
_label = new QLabel();
_label->setAlignment(Qt::AlignRight);
QLabel* BandwidthDialog::ChannelDisplay::setupLabel(QFormLayout* form) {
QLabel* label = new QLabel();
label->setAlignment(Qt::AlignRight);
QPalette palette = label->palette();
unsigned rgb = ch->colorRGBA >> 8;
QPalette palette = _label->palette();
unsigned rgb = colorRGBA >> 8;
rgb = ((rgb & 0xfefefeu) >> 1) + ((rgb & 0xf8f8f8) >> 3);
palette.setColor(QPalette::WindowText, QColor::fromRgb(rgb));
label->setPalette(palette);
_label->setPalette(palette);
form->addRow((std::string(" ") + ch->caption + " Bandwidth In/Out:").c_str(), label);
return label;
form->addRow(QString(" ") + _caption + " Bandwidth In/Out:", _label);
}
void BandwidthDialog::ChannelDisplay::setLabelText() {
std::string strBuf =
std::to_string ((int) (ch->input.getValue() * ch->unitScale)) + "/" +
std::to_string ((int) (ch->output.getValue() * ch->unitScale)) + " " + ch->unitCaption;
label->setText(strBuf.c_str());
void BandwidthChannelDisplay::bandwidthAverageUpdated() {
float inTotal = 0.;
float outTotal = 0.;
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
for (int i = 0; i < _nodeTypesToFollow.size(); ++i) {
inTotal += bandwidthRecorder->getAverageInputKilobitsPerSecond(_nodeTypesToFollow.at(i));
outTotal += bandwidthRecorder->getAverageOutputKilobitsPerSecond(_nodeTypesToFollow.at(i));
}
_strBuf =
QString("").setNum((int) (inTotal * _unitScale)) + "/" +
QString("").setNum((int) (outTotal * _unitScale)) + " " + _unitCaption;
}
void BandwidthChannelDisplay::paint() {
_label->setText(_strBuf);
}
BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthRecorder* model) :
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint),
_model(model) {
BandwidthDialog::BandwidthDialog(QWidget* parent) :
QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint) {
this->setWindowTitle("Bandwidth Details");
@ -64,30 +76,48 @@ BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthRecorder* model) :
QFormLayout* form = new QFormLayout();
this->QDialog::setLayout(form);
audioChannelDisplay = new ChannelDisplay(_model->audioChannel, form);
avatarsChannelDisplay = new ChannelDisplay(_model->avatarsChannel, form);
octreeChannelDisplay = new ChannelDisplay(_model->octreeChannel, form);
metavoxelsChannelDisplay = new ChannelDisplay(_model->metavoxelsChannel, form);
totalChannelDisplay = new ChannelDisplay(_model->totalChannel, form);
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
_allChannelDisplays[0] = _audioChannelDisplay =
new BandwidthChannelDisplay({NodeType::AudioMixer}, form, "Audio", "Kbps", 1.0, COLOR0);
_allChannelDisplays[1] = _avatarsChannelDisplay =
new BandwidthChannelDisplay({NodeType::Agent, NodeType::AvatarMixer}, form, "Avatars", "Kbps", 1.0, COLOR1);
_allChannelDisplays[2] = _octreeChannelDisplay =
new BandwidthChannelDisplay({NodeType::EntityServer}, form, "Octree", "Kbps", 1.0, COLOR2);
_allChannelDisplays[3] = _octreeChannelDisplay =
new BandwidthChannelDisplay({NodeType::DomainServer}, form, "Domain", "Kbps", 1.0, COLOR2);
_allChannelDisplays[4] = _metavoxelsChannelDisplay =
new BandwidthChannelDisplay({NodeType::MetavoxelServer, NodeType::EnvironmentServer}, form, "Metavoxels", "Kbps", 1.0, COLOR2);
_allChannelDisplays[5] = _otherChannelDisplay =
new BandwidthChannelDisplay({NodeType::Unassigned}, form, "Other", "Kbps", 1.0, COLOR2);
_allChannelDisplays[6] = _totalChannelDisplay =
new BandwidthChannelDisplay({NodeType::DomainServer, NodeType::EntityServer, NodeType::MetavoxelServer,
NodeType::EnvironmentServer, NodeType::AudioMixer, NodeType::Agent,
NodeType::AvatarMixer, NodeType::Unassigned},
form, "Total", "Kbps", 1.0, COLOR2);
connect(averageUpdateTimer, SIGNAL(timeout()), this, SLOT(updateTimerTimeout()));
averageUpdateTimer->start(1000);
}
BandwidthDialog::~BandwidthDialog() {
delete audioChannelDisplay;
delete avatarsChannelDisplay;
delete octreeChannelDisplay;
delete metavoxelsChannelDisplay;
delete totalChannelDisplay;
for (unsigned int i = 0; i < _CHANNELCOUNT; i++) {
delete _allChannelDisplays[i];
}
}
void BandwidthDialog::updateTimerTimeout() {
for (unsigned int i = 0; i < _CHANNELCOUNT; i++) {
_allChannelDisplays[i]->bandwidthAverageUpdated();
}
}
void BandwidthDialog::paintEvent(QPaintEvent* event) {
audioChannelDisplay->setLabelText();
avatarsChannelDisplay->setLabelText();
octreeChannelDisplay->setLabelText();
metavoxelsChannelDisplay->setLabelText();
totalChannelDisplay->setLabelText();
for (unsigned int i=0; i<_CHANNELCOUNT; i++)
_allChannelDisplays[i]->paint();
this->QDialog::paintEvent(event);
this->setFixedSize(this->width(), this->height());
}

View file

@ -15,36 +15,61 @@
#include <QDialog>
#include <QLabel>
#include <QFormLayout>
#include <QVector>
#include "Node.h"
#include "BandwidthRecorder.h"
const unsigned int COLOR0 = 0x33cc99ff;
const unsigned int COLOR1 = 0xffef40c0;
const unsigned int COLOR2 = 0xd0d0d0a0;
class BandwidthChannelDisplay : public QObject {
Q_OBJECT
public:
BandwidthChannelDisplay(QVector<NodeType_t> nodeTypesToFollow,
QFormLayout* form,
char const* const caption, char const* unitCaption, float unitScale, unsigned colorRGBA);
void paint();
private:
QVector<NodeType_t> _nodeTypesToFollow;
QLabel* _label;
QString _strBuf;
char const* const _caption;
char const* _unitCaption;
float const _unitScale;
unsigned _colorRGBA;
public slots:
void bandwidthAverageUpdated();
};
class BandwidthDialog : public QDialog {
Q_OBJECT
public:
// Sets up the UI based on the configuration of the BandwidthRecorder
BandwidthDialog(QWidget* parent, BandwidthRecorder* model);
BandwidthDialog(QWidget* parent);
~BandwidthDialog();
class ChannelDisplay {
public:
ChannelDisplay(BandwidthRecorder::Channel *ch, QFormLayout* form);
QLabel* setupLabel(QFormLayout* form);
void setLabelText();
void paintEvent(QPaintEvent*);
private:
BandwidthRecorder::Channel *ch;
private:
BandwidthChannelDisplay* _audioChannelDisplay;
BandwidthChannelDisplay* _avatarsChannelDisplay;
BandwidthChannelDisplay* _octreeChannelDisplay;
BandwidthChannelDisplay* _domainChannelDisplay;
BandwidthChannelDisplay* _metavoxelsChannelDisplay;
BandwidthChannelDisplay* _otherChannelDisplay;
BandwidthChannelDisplay* _totalChannelDisplay; // sums of all the other channels
QLabel* label;
};
static const unsigned int _CHANNELCOUNT = 7;
BandwidthChannelDisplay* _allChannelDisplays[_CHANNELCOUNT];
ChannelDisplay* audioChannelDisplay;
ChannelDisplay* avatarsChannelDisplay;
ChannelDisplay* octreeChannelDisplay;
ChannelDisplay* metavoxelsChannelDisplay;
// sums of all the other channels
ChannelDisplay* totalChannelDisplay;
signals:
@ -53,15 +78,17 @@ signals:
public slots:
void reject();
void updateTimerTimeout();
protected:
void paintEvent(QPaintEvent*);
// Emits a 'closed' signal when this dialog is closed.
void closeEvent(QCloseEvent*);
private:
BandwidthRecorder* _model;
QTimer* averageUpdateTimer = new QTimer(this);
};
#endif // hifi_BandwidthDialog_h

View file

@ -102,7 +102,7 @@ void DialogsManager::editAnimations() {
void DialogsManager::bandwidthDetails() {
if (! _bandwidthDialog) {
_bandwidthDialog = new BandwidthDialog(qApp->getWindow(), qApp->getBandwidthRecorder());
_bandwidthDialog = new BandwidthDialog(qApp->getWindow());
connect(_bandwidthDialog, SIGNAL(closed()), _bandwidthDialog, SLOT(deleteLater()));
if (_hmdToolsDialog) {

View file

@ -26,6 +26,7 @@
#include <PerfStat.h>
#include "Stats.h"
#include "BandwidthRecorder.h"
#include "InterfaceConfig.h"
#include "Menu.h"
#include "Util.h"
@ -202,8 +203,8 @@ void Stats::display(
float fps,
int inPacketsPerSecond,
int outPacketsPerSecond,
int inBytesPerSecond,
int outBytesPerSecond,
int inKbitsPerSecond,
int outKbitsPerSecond,
int voxelPacketsToProcess)
{
auto glCanvas = DependencyManager::get<GLCanvas>();
@ -217,6 +218,8 @@ void Stats::display(
QLocale locale(QLocale::English);
std::stringstream octreeStats;
QSharedPointer<BandwidthRecorder> bandwidthRecorder = DependencyManager::get<BandwidthRecorder>();
if (_lastHorizontalOffset != horizontalOffset) {
resetWidth(glCanvas->width(), horizontalOffset);
_lastHorizontalOffset = horizontalOffset;
@ -317,8 +320,8 @@ void Stats::display(
sprintf(packetsPerSecondString, "Packets In/Out: %d/%d", inPacketsPerSecond, outPacketsPerSecond);
char averageMegabitsPerSecond[30];
sprintf(averageMegabitsPerSecond, "Mbps In/Out: %3.2f/%3.2f",
(float)inBytesPerSecond * 8.0f / 1000000.0f,
(float)outBytesPerSecond * 8.0f / 1000000.0f);
(float)inKbitsPerSecond * 1.0f / 1000.0f,
(float)outKbitsPerSecond * 1.0f / 1000.0f);
verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, packetsPerSecondString, color);
@ -441,8 +444,10 @@ void Stats::display(
SharedNodePointer avatarMixer = DependencyManager::get<NodeList>()->soloNodeOfType(NodeType::AvatarMixer);
if (avatarMixer) {
sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
roundf(avatarMixer->getAverageKilobitsPerSecond()),
roundf(avatarMixer->getAveragePacketsPerSecond()));
roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)),
roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
} else {
sprintf(avatarMixerStats, "No Avatar Mixer");
}

View file

@ -30,7 +30,7 @@ public:
void checkClick(int mouseX, int mouseY, int mouseDragStartedX, int mouseDragStartedY, int horizontalOffset);
void resetWidth(int width, int horizontalOffset);
void display(const float* color, int horizontalOffset, float fps, int inPacketsPerSecond, int outPacketsPerSecond,
int inBytesPerSecond, int outBytesPerSecond, int voxelPacketsToProcess);
int inKbitsPerSecond, int outKbitsPerSecond, int voxelPacketsToProcess);
bool includeTimingRecord(const QString& name);
Q_INVOKABLE void setMetavoxelStats(int internal, int leaves, int sendProgress,

View file

@ -9,15 +9,12 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <NetworkAccessManager.h>
#include "Application.h"
#include "BillboardOverlay.h"
BillboardOverlay::BillboardOverlay() :
_newTextureNeeded(true),
_fromImage(-1,-1,-1,-1),
_fromImage(),
_scale(1.0f),
_isFacingAvatar(true)
{
@ -27,10 +24,7 @@ BillboardOverlay::BillboardOverlay() :
BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
Base3DOverlay(billboardOverlay),
_url(billboardOverlay->_url),
_billboard(billboardOverlay->_billboard),
_size(),
_billboardTexture(),
_newTextureNeeded(true),
_texture(billboardOverlay->_texture),
_fromImage(billboardOverlay->_fromImage),
_scale(billboardOverlay->_scale),
_isFacingAvatar(billboardOverlay->_isFacingAvatar)
@ -38,41 +32,23 @@ BillboardOverlay::BillboardOverlay(const BillboardOverlay* billboardOverlay) :
}
void BillboardOverlay::render(RenderArgs* args) {
if (!_visible || !_isLoaded) {
if (!_isLoaded) {
_isLoaded = true;
_texture = DependencyManager::get<TextureCache>()->getTexture(_url);
}
if (!_visible || !_texture->isLoaded()) {
return;
}
if (!_billboard.isEmpty()) {
if (_newTextureNeeded && _billboardTexture) {
_billboardTexture.reset();
}
if (!_billboardTexture) {
QImage image = QImage::fromData(_billboard);
if (image.format() != QImage::Format_ARGB32) {
image = image.convertToFormat(QImage::Format_ARGB32);
}
_size = image.size();
if (_fromImage.x() == -1) {
_fromImage.setRect(0, 0, _size.width(), _size.height());
}
_billboardTexture.reset(new Texture());
_newTextureNeeded = false;
glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _size.width(), _size.height(), 0,
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} else {
glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID());
}
}
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
glBindTexture(GL_TEXTURE_2D, _texture->getID());
glPushMatrix(); {
glTranslatef(_position.x, _position.y, _position.z);
glm::quat rotation;
@ -86,39 +62,55 @@ void BillboardOverlay::render(RenderArgs* args) {
glm::vec3 axis = glm::axis(rotation);
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
glScalef(_scale, _scale, _scale);
if (_billboardTexture) {
float maxSize = glm::max(_fromImage.width(), _fromImage.height());
float x = _fromImage.width() / (2.0f * maxSize);
float y = -_fromImage.height() / (2.0f * maxSize);
const float MAX_COLOR = 255.0f;
xColor color = getColor();
float alpha = getAlpha();
glm::vec2 topLeft(-x, -y);
glm::vec2 bottomRight(x, y);
glm::vec2 texCoordTopLeft((float)_fromImage.x() / (float)_size.width(),
(float)_fromImage.y() / (float)_size.height());
glm::vec2 texCoordBottomRight(((float)_fromImage.x() + (float)_fromImage.width()) / (float)_size.width(),
((float)_fromImage.y() + (float)_fromImage.height()) / _size.height());
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
const float MAX_COLOR = 255.0f;
xColor color = getColor();
float alpha = getAlpha();
float imageWidth = _texture->getWidth();
float imageHeight = _texture->getHeight();
QRect fromImage;
if (_fromImage.isNull()) {
fromImage.setX(0);
fromImage.setY(0);
fromImage.setWidth(imageWidth);
fromImage.setHeight(imageHeight);
} else {
float scaleX = imageWidth / _texture->getOriginalWidth();
float scaleY = imageHeight / _texture->getOriginalHeight();
fromImage.setX(scaleX * _fromImage.x());
fromImage.setY(scaleY * _fromImage.y());
fromImage.setWidth(scaleX * _fromImage.width());
fromImage.setHeight(scaleY * _fromImage.height());
}
float maxSize = glm::max(fromImage.width(), fromImage.height());
float x = fromImage.width() / (2.0f * maxSize);
float y = -fromImage.height() / (2.0f * maxSize);
glm::vec2 topLeft(-x, -y);
glm::vec2 bottomRight(x, y);
glm::vec2 texCoordTopLeft(fromImage.x() / imageWidth, fromImage.y() / imageHeight);
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width()) / imageWidth,
(fromImage.y() + fromImage.height()) / imageHeight);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha));
} glPopMatrix();
glDisable(GL_TEXTURE_2D);
glEnable(GL_LIGHTING);
glDisable(GL_ALPHA_TEST);
glBindTexture(GL_TEXTURE_2D, 0);
}
void BillboardOverlay::setProperties(const QScriptValue &properties) {
Base3DOverlay::setProperties(properties);
QScriptValue urlValue = properties.property("url");
if (urlValue.isValid()) {
QString newURL = urlValue.toVariant().toString();
@ -126,39 +118,43 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) {
setBillboardURL(newURL);
}
}
QScriptValue subImageBounds = properties.property("subImage");
if (subImageBounds.isValid()) {
QRect oldSubImageRect = _fromImage;
QRect subImageRect = _fromImage;
if (subImageBounds.property("x").isValid()) {
subImageRect.setX(subImageBounds.property("x").toVariant().toInt());
if (subImageBounds.isNull()) {
_fromImage = QRect();
} else {
subImageRect.setX(oldSubImageRect.x());
QRect oldSubImageRect = _fromImage;
QRect subImageRect = _fromImage;
if (subImageBounds.property("x").isValid()) {
subImageRect.setX(subImageBounds.property("x").toVariant().toInt());
} else {
subImageRect.setX(oldSubImageRect.x());
}
if (subImageBounds.property("y").isValid()) {
subImageRect.setY(subImageBounds.property("y").toVariant().toInt());
} else {
subImageRect.setY(oldSubImageRect.y());
}
if (subImageBounds.property("width").isValid()) {
subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt());
} else {
subImageRect.setWidth(oldSubImageRect.width());
}
if (subImageBounds.property("height").isValid()) {
subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt());
} else {
subImageRect.setHeight(oldSubImageRect.height());
}
setClipFromSource(subImageRect);
}
if (subImageBounds.property("y").isValid()) {
subImageRect.setY(subImageBounds.property("y").toVariant().toInt());
} else {
subImageRect.setY(oldSubImageRect.y());
}
if (subImageBounds.property("width").isValid()) {
subImageRect.setWidth(subImageBounds.property("width").toVariant().toInt());
} else {
subImageRect.setWidth(oldSubImageRect.width());
}
if (subImageBounds.property("height").isValid()) {
subImageRect.setHeight(subImageBounds.property("height").toVariant().toInt());
} else {
subImageRect.setHeight(oldSubImageRect.height());
}
setClipFromSource(subImageRect);
}
QScriptValue scaleValue = properties.property("scale");
if (scaleValue.isValid()) {
_scale = scaleValue.toVariant().toFloat();
}
QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar");
if (isFacingAvatarValue.isValid()) {
_isFacingAvatar = isFacingAvatarValue.toVariant().toBool();
@ -188,35 +184,20 @@ void BillboardOverlay::setURL(const QString& url) {
void BillboardOverlay::setBillboardURL(const QString& url) {
_url = url;
QUrl actualURL = url;
_isLoaded = false;
// clear the billboard if previously set
_billboard.clear();
_newTextureNeeded = true;
QNetworkRequest request(actualURL);
request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT);
QNetworkReply* reply = NetworkAccessManager::getInstance().get(request);
connect(reply, &QNetworkReply::finished, this, &BillboardOverlay::replyFinished);
}
void BillboardOverlay::replyFinished() {
// replace our byte array with the downloaded data
QNetworkReply* reply = static_cast<QNetworkReply*>(sender());
_billboard = reply->readAll();
_isLoaded = true;
reply->deleteLater();
}
bool BillboardOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float& distance, BoxFace& face) {
if (_billboardTexture) {
float maxSize = glm::max(_fromImage.width(), _fromImage.height());
float x = _fromImage.width() / (2.0f * maxSize);
float y = -_fromImage.height() / (2.0f * maxSize);
if (_texture) {
bool isNull = _fromImage.isNull();
float width = isNull ? _texture->getWidth() : _fromImage.width();
float height = isNull ? _texture->getHeight() : _fromImage.height();
float maxSize = glm::max(width, height);
float x = width / (2.0f * maxSize);
float y = -height / (2.0f * maxSize);
float maxDimension = glm::max(x,y);
float scaledDimension = maxDimension * _scale;
glm::vec3 corner = getCenter() - glm::vec3(scaledDimension, scaledDimension, scaledDimension) ;

View file

@ -40,17 +40,11 @@ public:
virtual BillboardOverlay* createClone() const;
private slots:
void replyFinished();
private:
void setBillboardURL(const QString& url);
QString _url;
QByteArray _billboard;
QSize _size;
QScopedPointer<Texture> _billboardTexture;
bool _newTextureNeeded;
NetworkTexturePointer _texture;
QRect _fromImage; // where from in the image to sample
@ -58,4 +52,4 @@ private:
bool _isFacingAvatar;
};
#endif // hifi_BillboardOverlay_h
#endif // hifi_BillboardOverlay_h

View file

@ -0,0 +1,195 @@
//
// BandwidthMeter.cpp
// interface/src/ui
//
// Created by Seth Alves on 2015-1-30
// Copyright 2015 High Fidelity, Inc.
//
// Based on code by Tobias Schwinger
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <QDateTime>
#include "BandwidthRecorder.h"
BandwidthRecorder::Channel::Channel() {
}
float BandwidthRecorder::Channel::getAverageInputPacketsPerSecond() {
float delt = _input.getEventDeltaAverage();
if (delt > 0.0f) {
return (1.0 / delt);
}
return 0.0f;
}
float BandwidthRecorder::Channel::getAverageOutputPacketsPerSecond() {
float delt = _input.getEventDeltaAverage();
if (delt > 0.0f) {
return (1.0 / _output.getEventDeltaAverage());
}
return 0.0f;
}
float BandwidthRecorder::Channel::getAverageInputKilobitsPerSecond() {
return (_input.getAverageSampleValuePerSecond() * (8.0f / 1000));
}
float BandwidthRecorder::Channel::getAverageOutputKilobitsPerSecond() {
return (_output.getAverageSampleValuePerSecond() * (8.0f / 1000));
}
void BandwidthRecorder::Channel::updateInputAverage(const float sample) {
_input.updateAverage(sample);
}
void BandwidthRecorder::Channel::updateOutputAverage(const float sample) {
_output.updateAverage(sample);
}
BandwidthRecorder::BandwidthRecorder() {
for (uint i=0; i<CHANNEL_COUNT; i++)
_channels[ i ] = NULL;
}
BandwidthRecorder::~BandwidthRecorder() {
for (uint i=0; i<CHANNEL_COUNT; i++)
delete _channels[ i ];
}
void BandwidthRecorder::updateInboundData(const quint8 channelType, const int sample) {
Q_ASSERT(channelType < CHANNEL_COUNT);
if (! _channels[channelType]) {
_channels[channelType] = new Channel();
}
_channels[channelType]->updateInputAverage(sample);
}
void BandwidthRecorder::updateOutboundData(const quint8 channelType, const int sample) {
Q_ASSERT(channelType < CHANNEL_COUNT);
if (! _channels[channelType]) {
_channels[channelType] = new Channel();
}
_channels[channelType]->updateOutputAverage(sample);
}
float BandwidthRecorder::getAverageInputPacketsPerSecond(const quint8 channelType) {
Q_ASSERT(channelType < CHANNEL_COUNT);
if (! _channels[channelType]) {
return 0.0f;
}
return _channels[channelType]->getAverageInputPacketsPerSecond();
}
float BandwidthRecorder::getAverageOutputPacketsPerSecond(const quint8 channelType) {
Q_ASSERT(channelType < CHANNEL_COUNT);
if (! _channels[channelType]) {
return 0.0f;
}
return _channels[channelType]->getAverageOutputPacketsPerSecond();
}
float BandwidthRecorder::getAverageInputKilobitsPerSecond(const quint8 channelType) {
Q_ASSERT(channelType < CHANNEL_COUNT);
if (! _channels[channelType]) {
return 0.0f;
}
return _channels[channelType]->getAverageInputKilobitsPerSecond();
}
float BandwidthRecorder::getAverageOutputKilobitsPerSecond(const quint8 channelType) {
Q_ASSERT(channelType < CHANNEL_COUNT);
if (! _channels[channelType]) {
return 0.0f;
}
return _channels[channelType]->getAverageOutputKilobitsPerSecond();
}
float BandwidthRecorder::getTotalAverageInputPacketsPerSecond() {
float result = 0.0f;
for (uint i=0; i<CHANNEL_COUNT; i++) {
if (_channels[i]) {
result += _channels[i]->getAverageInputPacketsPerSecond();
}
}
return result;
}
float BandwidthRecorder::getTotalAverageOutputPacketsPerSecond() {
float result = 0.0f;
for (uint i=0; i<CHANNEL_COUNT; i++) {
if (_channels[i]) {
result += _channels[i]->getAverageOutputPacketsPerSecond();
}
}
return result;
}
float BandwidthRecorder::getTotalAverageInputKilobitsPerSecond(){
float result = 0.0f;
for (uint i=0; i<CHANNEL_COUNT; i++) {
if (_channels[i]) {
result += _channels[i]->getAverageInputKilobitsPerSecond();
}
}
return result;
}
float BandwidthRecorder::getTotalAverageOutputKilobitsPerSecond(){
float result = 0.0f;
for (uint i=0; i<CHANNEL_COUNT; i++) {
if (_channels[i]) {
result += _channels[i]->getAverageOutputKilobitsPerSecond();
}
}
return result;
}
float BandwidthRecorder::getCachedTotalAverageInputPacketsPerSecond() {
static qint64 lastCalculated = 0;
static float cachedValue = 0.0f;
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (now - lastCalculated > 1000.0f) {
lastCalculated = now;
cachedValue = getTotalAverageInputPacketsPerSecond();
}
return cachedValue;
}
float BandwidthRecorder::getCachedTotalAverageOutputPacketsPerSecond() {
static qint64 lastCalculated = 0;
static float cachedValue = 0.0f;
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (now - lastCalculated > 1000.0f) {
lastCalculated = now;
cachedValue = getTotalAverageOutputPacketsPerSecond();
}
return cachedValue;
}
float BandwidthRecorder::getCachedTotalAverageInputKilobitsPerSecond() {
static qint64 lastCalculated = 0;
static float cachedValue = 0.0f;
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (now - lastCalculated > 1000.0f) {
lastCalculated = now;
cachedValue = getTotalAverageInputKilobitsPerSecond();
}
return cachedValue;
}
float BandwidthRecorder::getCachedTotalAverageOutputKilobitsPerSecond() {
static qint64 lastCalculated = 0;
static float cachedValue = 0.0f;
qint64 now = QDateTime::currentMSecsSinceEpoch();
if (now - lastCalculated > 1000.0f) {
lastCalculated = now;
cachedValue = getTotalAverageOutputKilobitsPerSecond();
}
return cachedValue;
}

View file

@ -0,0 +1,77 @@
//
// BandwidthRecorder.h
//
// Created by Seth Alves on 2015-1-30
// Copyright 2015 High Fidelity, Inc.
//
// Based on code by Tobias Schwinger
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_BandwidthRecorder_h
#define hifi_BandwidthRecorder_h
#include <QObject>
#include <QElapsedTimer>
#include <QTimer>
#include "DependencyManager.h"
#include "Node.h"
#include "SimpleMovingAverage.h"
class BandwidthRecorder : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
BandwidthRecorder();
~BandwidthRecorder();
// keep track of data rate in two directions as well as units and style to use during display
class Channel {
public:
Channel();
float getAverageInputPacketsPerSecond();
float getAverageOutputPacketsPerSecond();
float getAverageInputKilobitsPerSecond();
float getAverageOutputKilobitsPerSecond();
void updateInputAverage(const float sample);
void updateOutputAverage(const float sample);
private:
SimpleMovingAverage _input = SimpleMovingAverage();
SimpleMovingAverage _output = SimpleMovingAverage();
};
float getAverageInputPacketsPerSecond(const quint8 channelType);
float getAverageOutputPacketsPerSecond(const quint8 channelType);
float getAverageInputKilobitsPerSecond(const quint8 channelType);
float getAverageOutputKilobitsPerSecond(const quint8 channelType);
float getTotalAverageInputPacketsPerSecond();
float getTotalAverageOutputPacketsPerSecond();
float getTotalAverageInputKilobitsPerSecond();
float getTotalAverageOutputKilobitsPerSecond();
float getCachedTotalAverageInputPacketsPerSecond();
float getCachedTotalAverageOutputPacketsPerSecond();
float getCachedTotalAverageInputKilobitsPerSecond();
float getCachedTotalAverageOutputKilobitsPerSecond();
private:
// one for each possible Node type
static const unsigned int CHANNEL_COUNT = 256;
Channel* _channels[CHANNEL_COUNT];
public slots:
void updateInboundData(const quint8 channelType, const int bytes);
void updateOutboundData(const quint8 channelType, const int bytes);
};
#endif

View file

@ -47,8 +47,6 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
_localSockAddr(),
_publicSockAddr(),
_stunSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT),
_numCollectedPackets(0),
_numCollectedBytes(0),
_packetStatTimer()
{
static bool firstCall = true;
@ -194,6 +192,21 @@ bool LimitedNodeList::packetVersionAndHashMatch(const QByteArray& packet) {
return false;
}
// qint64 LimitedNodeList::readDatagram(char* data, qint64 maxSize, QHostAddress* address = 0, quint16 * port = 0) {
qint64 LimitedNodeList::readDatagram(QByteArray& incomingPacket, QHostAddress* address = 0, quint16 * port = 0) {
qint64 result = getNodeSocket().readDatagram(incomingPacket.data(), incomingPacket.size(), address, port);
SharedNodePointer sendingNode = sendingNodeForPacket(incomingPacket);
if (sendingNode) {
emit dataReceived(sendingNode->getType(), incomingPacket.size());
} else {
emit dataReceived(NodeType::Unassigned, incomingPacket.size());
}
return result;
}
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSockAddr& destinationSockAddr,
const QUuid& connectionSecret) {
QByteArray datagramCopy = datagram;
@ -203,6 +216,7 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSock
replaceHashInPacketGivenConnectionUUID(datagramCopy, connectionSecret);
}
// XXX can BandwidthRecorder be used for this?
// stat collection for packets
++_numCollectedPackets;
_numCollectedBytes += datagram.size();
@ -217,10 +231,11 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const HifiSock
return bytesWritten;
}
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
const HifiSockAddr& overridenSockAddr) {
qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram,
const SharedNodePointer& destinationNode,
const HifiSockAddr& overridenSockAddr) {
if (destinationNode) {
// if we don't have an ovveriden address, assume they want to send to the node's active socket
// if we don't have an overridden address, assume they want to send to the node's active socket
const HifiSockAddr* destinationSockAddr = &overridenSockAddr;
if (overridenSockAddr.isNull()) {
if (destinationNode->getActiveSocket()) {
@ -231,6 +246,8 @@ qint64 LimitedNodeList::writeDatagram(const QByteArray& datagram, const SharedNo
return 0;
}
}
emit dataSent(destinationNode->getType(), datagram.size());
return writeDatagram(datagram, *destinationSockAddr, destinationNode->getConnectionSecret());
}
@ -289,7 +306,6 @@ int LimitedNodeList::updateNodeWithDataFromPacket(const SharedNodePointer& match
QMutexLocker locker(&matchingNode->getMutex());
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
matchingNode->recordBytesReceived(packet.size());
if (!matchingNode->getLinkedData() && linkedDataCreateCallback) {
linkedDataCreateCallback(matchingNode.data());

View file

@ -82,6 +82,8 @@ public:
QUdpSocket& getDTLSSocket();
bool packetVersionAndHashMatch(const QByteArray& packet);
qint64 readDatagram(QByteArray& incomingPacket, QHostAddress* address, quint16 * port);
qint64 writeDatagram(const QByteArray& datagram, const SharedNodePointer& destinationNode,
const HifiSockAddr& overridenSockAddr = HifiSockAddr());
@ -180,6 +182,9 @@ signals:
void localSockAddrChanged(const HifiSockAddr& localSockAddr);
void publicSockAddrChanged(const HifiSockAddr& publicSockAddr);
void dataSent(const quint8 channel_type, const int bytes);
void dataReceived(const quint8 channel_type, const int bytes);
protected:
LimitedNodeList(unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
@ -201,8 +206,11 @@ protected:
HifiSockAddr _localSockAddr;
HifiSockAddr _publicSockAddr;
HifiSockAddr _stunSockAddr;
// XXX can BandwidthRecorder be used for this?
int _numCollectedPackets;
int _numCollectedBytes;
QElapsedTimer _packetStatTimer;
template<typename IteratorLambda>

View file

@ -47,7 +47,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
_activeSocket(NULL),
_symmetricSocket(),
_connectionSecret(),
_bytesReceivedMovingAverage(NULL),
_linkedData(NULL),
_isAlive(true),
_pingMs(-1), // "Uninitialized"
@ -60,31 +59,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
Node::~Node() {
delete _linkedData;
delete _bytesReceivedMovingAverage;
}
void Node::recordBytesReceived(int bytesReceived) {
if (!_bytesReceivedMovingAverage) {
_bytesReceivedMovingAverage = new SimpleMovingAverage(100);
}
_bytesReceivedMovingAverage->updateAverage((float) bytesReceived);
}
float Node::getAveragePacketsPerSecond() {
if (_bytesReceivedMovingAverage) {
return (1 / _bytesReceivedMovingAverage->getEventDeltaAverage());
} else {
return 0;
}
}
float Node::getAverageKilobitsPerSecond() {
if (_bytesReceivedMovingAverage) {
return (_bytesReceivedMovingAverage->getAverageSampleValuePerSecond() * (8.0f / 1000));
} else {
return 0;
}
}
void Node::updateClockSkewUsec(int clockSkewSample) {

View file

@ -63,10 +63,6 @@ public:
bool isAlive() const { return _isAlive; }
void setAlive(bool isAlive) { _isAlive = isAlive; }
void recordBytesReceived(int bytesReceived);
float getAverageKilobitsPerSecond();
float getAveragePacketsPerSecond();
int getPingMs() const { return _pingMs; }
void setPingMs(int pingMs) { _pingMs = pingMs; }
@ -99,7 +95,6 @@ private:
HifiSockAddr _symmetricSocket;
QUuid _connectionSecret;
SimpleMovingAverage* _bytesReceivedMovingAverage;
NodeData* _linkedData;
bool _isAlive;
int _pingMs;

View file

@ -83,6 +83,7 @@ void ThreadedAssignment::addPacketStatsAndSendStatsPacket(QJsonObject &statsObje
auto nodeList = DependencyManager::get<NodeList>();
float packetsPerSecond, bytesPerSecond;
// XXX can BandwidthRecorder be used for this?
nodeList->getPacketStats(packetsPerSecond, bytesPerSecond);
nodeList->resetPacketStats();

14
tools/refresh-tags.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
rm -f TAGS
find . -name *.h -print | while read I
do
etags --append "$I"
done
find . -name *.cpp -print | grep -v 'moc_' | while read I
do
etags --append "$I"
done