diff --git a/.gitignore b/.gitignore index d116bd44f4..3b44b99e68 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,6 @@ libraries/audio-client/external/*/* !libraries/audio-client/external/*/readme.txt gvr-interface/assets/oculussig* -gvr-interface/libs/* \ No newline at end of file +gvr-interface/libs/* + +TAGS \ No newline at end of file diff --git a/examples/planets.js b/examples/planets.js new file mode 100644 index 0000000000..87ed774c93 --- /dev/null +++ b/examples/planets.js @@ -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); \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 4bbce7ef5b..7bccab8267 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -200,6 +200,7 @@ bool setupEssentials(int& argc, char** argv) { auto lodManager = DependencyManager::set(); auto jsConsole = DependencyManager::set(); auto dialogsManager = DependencyManager::set(); + auto bandwidthRecorder = DependencyManager::set(); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) auto speechRecognizer = DependencyManager::set(); #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 = DependencyManager::get(); + 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()->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()->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(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"; diff --git a/interface/src/Application.h b/interface/src/Application.h index 9d5783232b..faafb412ce 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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; diff --git a/interface/src/BandwidthRecorder.cpp b/interface/src/BandwidthRecorder.cpp deleted file mode 100644 index db59c680f5..0000000000 --- a/interface/src/BandwidthRecorder.cpp +++ /dev/null @@ -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 -#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)); -} diff --git a/interface/src/BandwidthRecorder.h b/interface/src/BandwidthRecorder.h deleted file mode 100644 index a64a07fd84..0000000000 --- a/interface/src/BandwidthRecorder.h +++ /dev/null @@ -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 - -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 diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 4b4e830dd2..9c56f8bc8d 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -40,8 +40,7 @@ void DatagramProcessor::processDatagrams() { while (DependencyManager::get()->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().data(), "processAvatarMixerDatagram", Q_ARG(const QByteArray&, incomingPacket), Q_ARG(const QWeakPointer&, avatarMixer)); } - - application->_bandwidthRecorder.avatarsChannel->input.updateValue(incomingPacket.size()); - application->_bandwidthRecorder.totalChannel->input.updateValue(incomingPacket.size()); break; } case PacketTypeDomainConnectionDenied: { diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index c81286b579..df8632d875 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -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()->writeDatagram(data, _node); } - Application::getInstance()->getBandwidthRecorder()->metavoxelsChannel->output.updateValue(data.size()); - Application::getInstance()->getBandwidthRecorder()->totalChannel->output.updateValue(data.size()); } } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2d2fdf5e74..10af97a1e6 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -897,6 +897,7 @@ void ApplicationOverlay::renderAudioMeter() { void ApplicationOverlay::renderStatsAndLogs() { Application* application = Application::getInstance(); + QSharedPointer bandwidthRecorder = DependencyManager::get(); auto glCanvas = DependencyManager::get(); 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); } diff --git a/interface/src/ui/BandwidthDialog.cpp b/interface/src/ui/BandwidthDialog.cpp index 8b1afc9abf..044774fad6 100644 --- a/interface/src/ui/BandwidthDialog.cpp +++ b/interface/src/ui/BandwidthDialog.cpp @@ -21,42 +21,54 @@ #include -BandwidthDialog::ChannelDisplay::ChannelDisplay(BandwidthRecorder::Channel *ch, QFormLayout* form) { - this->ch = ch; - this->label = setupLabel(form); -} +BandwidthChannelDisplay::BandwidthChannelDisplay(QVector 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 = DependencyManager::get(); + + 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 = DependencyManager::get(); + + _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()); } diff --git a/interface/src/ui/BandwidthDialog.h b/interface/src/ui/BandwidthDialog.h index a171c98e12..cf4c34b0a9 100644 --- a/interface/src/ui/BandwidthDialog.h +++ b/interface/src/ui/BandwidthDialog.h @@ -15,36 +15,61 @@ #include #include #include +#include +#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 nodeTypesToFollow, + QFormLayout* form, + char const* const caption, char const* unitCaption, float unitScale, unsigned colorRGBA); + void paint(); + + private: + QVector _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 diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index fa04429bf2..752e205a6a 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -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) { diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 7f957b1050..e6f25d93b2 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -26,6 +26,7 @@ #include #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(); @@ -217,6 +218,8 @@ void Stats::display( QLocale locale(QLocale::English); std::stringstream octreeStats; + QSharedPointer bandwidthRecorder = DependencyManager::get(); + 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()->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"); } diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 136baf448b..d6da8d8cc6 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -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, diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 162ae6da77..27bd0c9102 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -9,15 +9,12 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include - #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()->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()->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()->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(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) ; diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index dcb8ab8b0c..a09c0c7528 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -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 _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 \ No newline at end of file +#endif // hifi_BillboardOverlay_h diff --git a/libraries/networking/src/BandwidthRecorder.cpp b/libraries/networking/src/BandwidthRecorder.cpp new file mode 100644 index 0000000000..f03aaa4150 --- /dev/null +++ b/libraries/networking/src/BandwidthRecorder.cpp @@ -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 +#include +#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; iupdateInputAverage(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; igetAverageInputPacketsPerSecond(); + } + } + return result; +} + +float BandwidthRecorder::getTotalAverageOutputPacketsPerSecond() { + float result = 0.0f; + for (uint i=0; igetAverageOutputPacketsPerSecond(); + } + } + return result; +} + +float BandwidthRecorder::getTotalAverageInputKilobitsPerSecond(){ + float result = 0.0f; + for (uint i=0; igetAverageInputKilobitsPerSecond(); + } + } + return result; +} + +float BandwidthRecorder::getTotalAverageOutputKilobitsPerSecond(){ + float result = 0.0f; + for (uint i=0; igetAverageOutputKilobitsPerSecond(); + } + } + 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; +} diff --git a/libraries/networking/src/BandwidthRecorder.h b/libraries/networking/src/BandwidthRecorder.h new file mode 100644 index 0000000000..a7f51fbb45 --- /dev/null +++ b/libraries/networking/src/BandwidthRecorder.h @@ -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 +#include +#include +#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 diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index bca23c1084..05dd6ced19 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -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()); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 1c841637df..f80f0367c7 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -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 diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 2095acdf66..697ef99b6d 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -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) { diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 6399c80edc..3ff8d771e0 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -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; diff --git a/libraries/networking/src/ThreadedAssignment.cpp b/libraries/networking/src/ThreadedAssignment.cpp index 21f520babb..ea94a8e22c 100644 --- a/libraries/networking/src/ThreadedAssignment.cpp +++ b/libraries/networking/src/ThreadedAssignment.cpp @@ -83,6 +83,7 @@ void ThreadedAssignment::addPacketStatsAndSendStatsPacket(QJsonObject &statsObje auto nodeList = DependencyManager::get(); float packetsPerSecond, bytesPerSecond; + // XXX can BandwidthRecorder be used for this? nodeList->getPacketStats(packetsPerSecond, bytesPerSecond); nodeList->resetPacketStats(); diff --git a/tools/refresh-tags.sh b/tools/refresh-tags.sh new file mode 100755 index 0000000000..d3157fa179 --- /dev/null +++ b/tools/refresh-tags.sh @@ -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