From e586b6e29b3004766f708f8eae8899261ada0416 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 20 Oct 2014 12:27:49 -0700 Subject: [PATCH 1/8] Remove nodes with no opaque elements. --- libraries/metavoxels/src/MetavoxelMessages.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libraries/metavoxels/src/MetavoxelMessages.cpp b/libraries/metavoxels/src/MetavoxelMessages.cpp index 17379a930f..299ffbbb80 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.cpp +++ b/libraries/metavoxels/src/MetavoxelMessages.cpp @@ -734,6 +734,22 @@ int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) { } } } + + // if there are no visible colors, we can clear everything + bool foundOpaque = false; + for (const QRgb* src = colorContents.constData(), *end = src + colorContents.size(); src != end; src++) { + if (qAlpha(*src) != 0) { + foundOpaque = true; + break; + } + } + if (!foundOpaque) { + info.outputValues[0] = AttributeValue(_outputs.at(0)); + info.outputValues[1] = AttributeValue(_outputs.at(1)); + info.outputValues[2] = AttributeValue(_outputs.at(2)); + return STOP_RECURSION; + } + VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES)); info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(), encodeInline(newColorPointer)); From d64b43693db7b31b3aa7856e318d9deb5937345f Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 20 Oct 2014 15:33:46 -0700 Subject: [PATCH 2/8] Networking fix. --- assignment-client/src/metavoxels/MetavoxelServer.cpp | 4 ++-- libraries/metavoxels/src/MetavoxelClientManager.cpp | 8 ++++---- libraries/metavoxels/src/MetavoxelMessages.h | 1 + libraries/networking/src/PacketHeaders.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index a74b5bee90..dd35a23630 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -237,7 +237,7 @@ void MetavoxelSession::update() { // go back to the beginning with the current packet and note that there's a delta pending _sequencer.getOutputStream().getUnderlying().device()->seek(start); - MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID }; + MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID, _lod }; out << QVariant::fromValue(msg); _sequencer.endPacket(); @@ -290,7 +290,7 @@ void MetavoxelSession::sendPacketGroup(int alreadySent) { for (int i = 0; i < additionalPackets; i++) { Bitstream& out = _sequencer.startPacket(); if (_reliableDeltaChannel) { - MetavoxelDeltaPendingMessage msg = { _reliableDeltaID }; + MetavoxelDeltaPendingMessage msg = { _reliableDeltaID, _reliableDeltaLOD }; out << QVariant::fromValue(msg); } else { out << QVariant(); diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 57f7b53cc3..2e0146af3b 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -255,12 +255,12 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } } else if (userType == MetavoxelDeltaPendingMessage::Type) { // check the id to make sure this is not a delta we've already processed - int id = message.value().id; - if (id > _reliableDeltaID) { - _reliableDeltaID = id; + MetavoxelDeltaPendingMessage pending = message.value(); + if (pending.id > _reliableDeltaID) { + _reliableDeltaID = pending.id; _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); - _reliableDeltaLOD = getLastAcknowledgedSendRecord()->getLOD(); + _reliableDeltaLOD = pending.lod; PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); _remoteDataLOD = receiveRecord->getLOD(); _remoteData = receiveRecord->getData(); diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index 9a7e254571..b9a44a0e6b 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -68,6 +68,7 @@ class MetavoxelDeltaPendingMessage { public: STREAM int id; + STREAM MetavoxelLOD lod; }; DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaPendingMessage) diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index 283765c265..7f81a4c59e 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -81,7 +81,7 @@ PacketVersion versionForPacketType(PacketType type) { case PacketTypeAudioStreamStats: return 1; case PacketTypeMetavoxelData: - return 7; + return 8; case PacketTypeVoxelData: return VERSION_VOXELS_HAS_FILE_BREAKS; default: From 45fb31cd08b80c434bdbf8bc2c27ad5efd9caed7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Mon, 20 Oct 2014 18:43:41 -0700 Subject: [PATCH 3/8] Basic network simulation for metavoxels. --- interface/src/Menu.cpp | 10 +++ interface/src/Menu.h | 4 ++ interface/src/MetavoxelSystem.cpp | 105 ++++++++++++++++++++++++++-- interface/src/MetavoxelSystem.h | 16 +++++ libraries/metavoxels/src/Endpoint.h | 2 +- 5 files changed, 132 insertions(+), 5 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 3a213cb64f..11ab5769cb 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,7 @@ #include "ui/AttachmentsDialog.h" #include "ui/InfoView.h" #include "ui/MetavoxelEditor.h" +#include "ui/MetavoxelNetworkSimulator.h" #include "ui/ModelsBrowser.h" #include "ui/LoginDialog.h" #include "ui/NodeBounds.h" @@ -431,6 +432,8 @@ Menu::Menu() : QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels"); addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false, Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData())); + addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this, + SLOT(showMetavoxelNetworkSimulator())); QMenu* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); @@ -1378,6 +1381,13 @@ void Menu::showMetavoxelEditor() { _MetavoxelEditor->raise(); } +void Menu::showMetavoxelNetworkSimulator() { + if (!_metavoxelNetworkSimulator) { + _metavoxelNetworkSimulator = new MetavoxelNetworkSimulator(); + } + _metavoxelNetworkSimulator->raise(); +} + void Menu::showScriptEditor() { if(!_ScriptEditor || !_ScriptEditor->isVisible()) { _ScriptEditor = new ScriptEditorWindow(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index a1936050ff..80f7f1e006 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -78,6 +78,7 @@ class AttachmentsDialog; class BandwidthDialog; class LodToolsDialog; class MetavoxelEditor; +class MetavoxelNetworkSimulator; class ChatWindow; class OctreeStatsDialog; class MenuItemProperties; @@ -218,6 +219,7 @@ private slots: void cycleFrustumRenderMode(); void runTests(); void showMetavoxelEditor(); + void showMetavoxelNetworkSimulator(); void showScriptEditor(); void showChat(); void toggleConsole(); @@ -274,6 +276,7 @@ private: FrustumDrawMode _frustumDrawMode; ViewFrustumOffset _viewFrustumOffset; QPointer _MetavoxelEditor; + QPointer _metavoxelNetworkSimulator; QPointer _ScriptEditor; QPointer _chatWindow; QDialog* _jsConsole; @@ -430,6 +433,7 @@ namespace MenuOption { const QString MuteEnvironment = "Mute Environment"; const QString MyLocations = "My Locations..."; const QString NameLocation = "Name this location"; + const QString NetworkSimulator = "Network Simulator..."; const QString NewVoxelCullingMode = "New Voxel Culling Mode"; const QString ObeyEnvironmentalGravity = "Obey Environmental Gravity"; const QString OctreeStats = "Voxel and Entity Statistics"; diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e9cfde60f9..97385a8671 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -39,6 +39,13 @@ REGISTER_META_OBJECT(StaticModelRenderer) static int bufferPointVectorMetaTypeId = qRegisterMetaType(); +MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, int minimumDelay, int maximumDelay) : + dropRate(dropRate), + repeatRate(repeatRate), + minimumDelay(minimumDelay), + maximumDelay(maximumDelay) { +} + void MetavoxelSystem::init() { MetavoxelClientManager::init(); DefaultMetavoxelRendererImplementation::init(); @@ -61,6 +68,16 @@ MetavoxelLOD MetavoxelSystem::getLOD() { return _lod; } +void MetavoxelSystem::setNetworkSimulation(const NetworkSimulation& simulation) { + QWriteLocker locker(&_networkSimulationLock); + _networkSimulation = simulation; +} + +MetavoxelSystem::NetworkSimulation MetavoxelSystem::getNetworkSimulation() { + QReadLocker locker(&_networkSimulationLock); + return _networkSimulation; +} + class SimulateVisitor : public MetavoxelVisitor { public: @@ -692,10 +709,53 @@ MetavoxelData MetavoxelSystemClient::getAugmentedData() { return _augmentedData; } +class ReceiveDelayer : public QObject { +public: + + ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet); + +protected: + + virtual void timerEvent(QTimerEvent* event); + +private: + + SharedNodePointer _node; + QByteArray _packet; +}; + +ReceiveDelayer::ReceiveDelayer(const SharedNodePointer& node, const QByteArray& packet) : + _node(node), + _packet(packet) { +} + +void ReceiveDelayer::timerEvent(QTimerEvent* event) { + QMutexLocker locker(&_node->getMutex()); + MetavoxelClient* client = static_cast(_node->getLinkedData()); + if (client) { + QMetaObject::invokeMethod(&client->getSequencer(), "receivedDatagram", Q_ARG(const QByteArray&, _packet)); + } + deleteLater(); +} + int MetavoxelSystemClient::parseData(const QByteArray& packet) { // process through sequencer - QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); - Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + if (randFloat() < simulation.dropRate) { + return packet.size(); + } + int count = (randFloat() < simulation.repeatRate) ? 2 : 1; + for (int i = 0; i < count; i++) { + int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); + if (delay > 0) { + ReceiveDelayer* delayer = new ReceiveDelayer(_node, packet); + delayer->startTimer(delay); + + } else { + QMetaObject::invokeMethod(&_sequencer, "receivedDatagram", Q_ARG(const QByteArray&, packet)); + } + Application::getInstance()->getBandwidthMeter()->inputStream(BandwidthMeter::METAVOXELS).updateValue(packet.size()); + } return packet.size(); } @@ -774,9 +834,46 @@ void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { QThreadPool::globalInstance()->start(new Augmenter(_node, _data, getAugmentedData(), _remoteDataLOD)); } +class SendDelayer : public QObject { +public: + + SendDelayer(const SharedNodePointer& node, const QByteArray& data); + + virtual void timerEvent(QTimerEvent* event); + +private: + + SharedNodePointer _node; + QByteArray _data; +}; + +SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : + _node(node), + _data(data) { +} + +void SendDelayer::timerEvent(QTimerEvent* event) { + NodeList::getInstance()->writeDatagram(_data, _node); + deleteLater(); +} + void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { - NodeList::getInstance()->writeDatagram(data, _node); - Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + if (randFloat() < simulation.dropRate) { + return; + } + int count = (randFloat() < simulation.repeatRate) ? 2 : 1; + for (int i = 0; i < count; i++) { + int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); + if (delay > 0) { + SendDelayer* delayer = new SendDelayer(_node, data); + delayer->startTimer(delay); + + } else { + NodeList::getInstance()->writeDatagram(data, _node); + } + Application::getInstance()->getBandwidthMeter()->outputStream(BandwidthMeter::METAVOXELS).updateValue(data.size()); + } } BufferData::~BufferData() { diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index ac6a75c68b..af6f4c6c7a 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -31,12 +31,25 @@ class MetavoxelSystem : public MetavoxelClientManager { public: + class NetworkSimulation { + public: + float dropRate; + float repeatRate; + int minimumDelay; + int maximumDelay; + + NetworkSimulation(float dropRate = 0.0f, float repeatRate = 0.0f, int minimumDelay = 0, int maximumDelay = 0); + }; + virtual void init(); virtual MetavoxelLOD getLOD(); const Frustum& getFrustum() const { return _frustum; } + void setNetworkSimulation(const NetworkSimulation& simulation); + NetworkSimulation getNetworkSimulation(); + const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; } const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; } const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; } @@ -93,6 +106,9 @@ private: MetavoxelLOD _lod; QReadWriteLock _lodLock; Frustum _frustum; + + NetworkSimulation _networkSimulation; + QReadWriteLock _networkSimulationLock; }; /// Generic abstract base class for objects that handle a signal. diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index d6999196d8..7b0adb4cef 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -32,7 +32,7 @@ public: PacketRecord* baselineReceiveRecord = NULL); virtual ~Endpoint(); - const DatagramSequencer& getSequencer() const { return _sequencer; } + DatagramSequencer& getSequencer() { return _sequencer; } virtual void update(); From 02c6b5a8bfc09d9f81025d8a9dcadce11cc1f3ed Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Oct 2014 12:16:55 -0700 Subject: [PATCH 4/8] Fix for send delay, delay ordering. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 97385a8671..e0794725cb 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -849,7 +849,7 @@ private: SendDelayer::SendDelayer(const SharedNodePointer& node, const QByteArray& data) : _node(node), - _data(data) { + _data(data.constData(), data.size()) { } void SendDelayer::timerEvent(QTimerEvent* event) { From bd4738d2c2251b540913bdccda6918eb4717d67b Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Oct 2014 15:48:53 -0700 Subject: [PATCH 5/8] Reliable delta fix. --- .../src/metavoxels/MetavoxelServer.cpp | 13 +-- .../src/metavoxels/MetavoxelServer.h | 1 + libraries/metavoxels/src/Endpoint.cpp | 3 +- libraries/metavoxels/src/Endpoint.h | 10 ++- .../metavoxels/src/MetavoxelClientManager.cpp | 81 +++++++++++++++++-- .../metavoxels/src/MetavoxelClientManager.h | 12 +++ libraries/metavoxels/src/MetavoxelMessages.h | 3 +- tests/metavoxels/src/MetavoxelTests.cpp | 32 ++++---- 8 files changed, 120 insertions(+), 35 deletions(-) diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index dd35a23630..3cf01bdc9f 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -237,8 +237,9 @@ void MetavoxelSession::update() { // go back to the beginning with the current packet and note that there's a delta pending _sequencer.getOutputStream().getUnderlying().device()->seek(start); - MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID, _lod }; - out << QVariant::fromValue(msg); + MetavoxelDeltaPendingMessage msg = { ++_reliableDeltaID, sendRecord->getPacketNumber(), + _sequencer.getIncomingPacketNumber() }; + out << (_reliableDeltaMessage = QVariant::fromValue(msg)); _sequencer.endPacket(); } else { @@ -254,8 +255,9 @@ void MetavoxelSession::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* MetavoxelSession::maybeCreateSendRecord() const { - return _reliableDeltaChannel ? new PacketRecord(_reliableDeltaLOD, _reliableDeltaData) : - new PacketRecord(_lod, _sender->getData()); + return _reliableDeltaChannel ? new PacketRecord(_sequencer.getOutgoingPacketNumber(), + _reliableDeltaLOD, _reliableDeltaData) : new PacketRecord(_sequencer.getOutgoingPacketNumber(), + _lod, _sender->getData()); } void MetavoxelSession::handleMessage(const QVariant& message) { @@ -290,8 +292,7 @@ void MetavoxelSession::sendPacketGroup(int alreadySent) { for (int i = 0; i < additionalPackets; i++) { Bitstream& out = _sequencer.startPacket(); if (_reliableDeltaChannel) { - MetavoxelDeltaPendingMessage msg = { _reliableDeltaID, _reliableDeltaLOD }; - out << QVariant::fromValue(msg); + out << _reliableDeltaMessage; } else { out << QVariant(); } diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 70c49fad64..840041e0f0 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -134,6 +134,7 @@ private: MetavoxelLOD _reliableDeltaLOD; Bitstream::WriteMappings _reliableDeltaWriteMappings; int _reliableDeltaID; + QVariant _reliableDeltaMessage; }; /// Handles persistence in a separate thread. diff --git a/libraries/metavoxels/src/Endpoint.cpp b/libraries/metavoxels/src/Endpoint.cpp index 65e088c75e..5a4e74ce08 100644 --- a/libraries/metavoxels/src/Endpoint.cpp +++ b/libraries/metavoxels/src/Endpoint.cpp @@ -107,7 +107,8 @@ PacketRecord* Endpoint::maybeCreateReceiveRecord() const { return NULL; } -PacketRecord::PacketRecord(const MetavoxelLOD& lod, const MetavoxelData& data) : +PacketRecord::PacketRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data) : + _packetNumber(packetNumber), _lod(lod), _data(data) { } diff --git a/libraries/metavoxels/src/Endpoint.h b/libraries/metavoxels/src/Endpoint.h index 7b0adb4cef..816a1fe2a9 100644 --- a/libraries/metavoxels/src/Endpoint.h +++ b/libraries/metavoxels/src/Endpoint.h @@ -45,10 +45,10 @@ protected slots: virtual void handleMessage(const QVariant& message, Bitstream& in); void recordSend(); - void recordReceive(); + virtual void recordReceive(); - void clearSendRecordsBefore(int index); - void clearReceiveRecordsBefore(int index); + virtual void clearSendRecordsBefore(int index); + virtual void clearReceiveRecordsBefore(int index); protected: @@ -71,14 +71,16 @@ protected: class PacketRecord { public: - PacketRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData()); + PacketRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData()); virtual ~PacketRecord(); + int getPacketNumber() const { return _packetNumber; } const MetavoxelLOD& getLOD() const { return _lod; } const MetavoxelData& getData() const { return _data; } private: + int _packetNumber; MetavoxelLOD _lod; MetavoxelData _data; }; diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index 2e0146af3b..09164d72c4 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -216,12 +216,71 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) } } +PacketRecord* MetavoxelClient::getAcknowledgedSendRecord(int packetNumber) const { + PacketRecord* lastAcknowledged = getLastAcknowledgedSendRecord(); + if (lastAcknowledged->getPacketNumber() == packetNumber) { + return lastAcknowledged; + } + foreach (PacketRecord* record, _clearedSendRecords) { + if (record->getPacketNumber() == packetNumber) { + return record; + } + } + return NULL; +} + +PacketRecord* MetavoxelClient::getAcknowledgedReceiveRecord(int packetNumber) const { + PacketRecord* lastAcknowledged = getLastAcknowledgedReceiveRecord(); + if (lastAcknowledged->getPacketNumber() == packetNumber) { + return lastAcknowledged; + } + foreach (PacketRecord* record, _clearedReceiveRecords) { + if (record->getPacketNumber() == packetNumber) { + return record; + } + } + return NULL; +} + void MetavoxelClient::dataChanged(const MetavoxelData& oldData) { // make thread-safe copy QWriteLocker locker(&_dataCopyLock); _dataCopy = _data; } +void MetavoxelClient::recordReceive() { + Endpoint::recordReceive(); + + // clear the cleared lists + foreach (PacketRecord* record, _clearedSendRecords) { + delete record; + } + _clearedSendRecords.clear(); + + foreach (PacketRecord* record, _clearedReceiveRecords) { + delete record; + } + _clearedReceiveRecords.clear(); +} + +void MetavoxelClient::clearSendRecordsBefore(int index) { + // move to cleared list + QList::iterator end = _sendRecords.begin() + index + 1; + for (QList::const_iterator it = _sendRecords.begin(); it != end; it++) { + _clearedSendRecords.append(*it); + } + _sendRecords.erase(_sendRecords.begin(), end); +} + +void MetavoxelClient::clearReceiveRecordsBefore(int index) { + // move to cleared list + QList::iterator end = _receiveRecords.begin() + index + 1; + for (QList::const_iterator it = _receiveRecords.begin(); it != end; it++) { + _clearedReceiveRecords.append(*it); + } + _receiveRecords.erase(_receiveRecords.begin(), end); +} + void MetavoxelClient::writeUpdateMessage(Bitstream& out) { ClientStateMessage state = { _updater->getLOD() }; out << QVariant::fromValue(state); @@ -232,7 +291,9 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { if (userType == MetavoxelDeltaMessage::Type) { PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); if (_reliableDeltaChannel) { - _remoteData.readDelta(receiveRecord->getData(), receiveRecord->getLOD(), in, _remoteDataLOD = _reliableDeltaLOD); + MetavoxelData reference = _remoteData; + MetavoxelLOD referenceLOD = _remoteDataLOD; + _remoteData.readDelta(reference, referenceLOD, in, _remoteDataLOD = _reliableDeltaLOD); _sequencer.getInputStream().persistReadMappings(in.getAndResetReadMappings()); in.clearPersistentMappings(); _reliableDeltaChannel = NULL; @@ -260,8 +321,17 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { _reliableDeltaID = pending.id; _reliableDeltaChannel = _sequencer.getReliableInputChannel(RELIABLE_DELTA_CHANNEL_INDEX); _reliableDeltaChannel->getBitstream().copyPersistentMappings(_sequencer.getInputStream()); - _reliableDeltaLOD = pending.lod; - PacketRecord* receiveRecord = getLastAcknowledgedReceiveRecord(); + PacketRecord* sendRecord = getAcknowledgedSendRecord(pending.receivedPacketNumber); + if (!sendRecord) { + qWarning() << "Missing send record for delta" << pending.receivedPacketNumber; + return; + } + _reliableDeltaLOD = sendRecord->getLOD(); + PacketRecord* receiveRecord = getAcknowledgedReceiveRecord(pending.sentPacketNumber); + if (!receiveRecord) { + qWarning() << "Missing receive record for delta" << pending.sentPacketNumber; + return; + } _remoteDataLOD = receiveRecord->getLOD(); _remoteData = receiveRecord->getData(); } @@ -271,10 +341,11 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { - return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD()); + return new PacketRecord(_sequencer.getOutgoingPacketNumber(), + _reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD()); } PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { - return new PacketRecord(_remoteDataLOD, _remoteData); + return new PacketRecord(_sequencer.getIncomingPacketNumber(), _remoteDataLOD, _remoteData); } diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index 7ddee2d68d..0a32f2986d 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -116,8 +116,16 @@ public: protected: + PacketRecord* getAcknowledgedSendRecord(int packetNumber) const; + PacketRecord* getAcknowledgedReceiveRecord(int packetNumber) const; + virtual void dataChanged(const MetavoxelData& oldData); + virtual void recordReceive(); + + virtual void clearSendRecordsBefore(int index); + virtual void clearReceiveRecordsBefore(int index); + virtual void writeUpdateMessage(Bitstream& out); virtual void handleMessage(const QVariant& message, Bitstream& in); @@ -132,9 +140,13 @@ protected: ReliableChannel* _reliableDeltaChannel; MetavoxelLOD _reliableDeltaLOD; int _reliableDeltaID; + QVariant _reliableDeltaMessage; MetavoxelData _dataCopy; QReadWriteLock _dataCopyLock; + + QList _clearedSendRecords; + QList _clearedReceiveRecords; }; #endif // hifi_MetavoxelClientManager_h diff --git a/libraries/metavoxels/src/MetavoxelMessages.h b/libraries/metavoxels/src/MetavoxelMessages.h index b9a44a0e6b..06f2fc9b8d 100644 --- a/libraries/metavoxels/src/MetavoxelMessages.h +++ b/libraries/metavoxels/src/MetavoxelMessages.h @@ -68,7 +68,8 @@ class MetavoxelDeltaPendingMessage { public: STREAM int id; - STREAM MetavoxelLOD lod; + STREAM int sentPacketNumber; + STREAM int receivedPacketNumber; }; DECLARE_STREAMABLE_METATYPE(MetavoxelDeltaPendingMessage) diff --git a/tests/metavoxels/src/MetavoxelTests.cpp b/tests/metavoxels/src/MetavoxelTests.cpp index 0a6a5de96d..92f8d0568c 100644 --- a/tests/metavoxels/src/MetavoxelTests.cpp +++ b/tests/metavoxels/src/MetavoxelTests.cpp @@ -603,31 +603,27 @@ int RandomVisitor::visit(MetavoxelInfo& info) { class TestSendRecord : public PacketRecord { public: - TestSendRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), - const SharedObjectPointer& localState = SharedObjectPointer(), int packetNumber = 0); + TestSendRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), + const SharedObjectPointer& localState = SharedObjectPointer()); const SharedObjectPointer& getLocalState() const { return _localState; } - int getPacketNumber() const { return _packetNumber; } private: SharedObjectPointer _localState; - int _packetNumber; - }; -TestSendRecord::TestSendRecord(const MetavoxelLOD& lod, const MetavoxelData& data, - const SharedObjectPointer& localState, int packetNumber) : - PacketRecord(lod, data), - _localState(localState), - _packetNumber(packetNumber) { +TestSendRecord::TestSendRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data, + const SharedObjectPointer& localState) : + PacketRecord(packetNumber, lod, data), + _localState(localState) { } class TestReceiveRecord : public PacketRecord { public: - TestReceiveRecord(const MetavoxelLOD& lod = MetavoxelLOD(), const MetavoxelData& data = MetavoxelData(), - const SharedObjectPointer& remoteState = SharedObjectPointer()); + TestReceiveRecord(int packetNumber = 0, const MetavoxelLOD& lod = MetavoxelLOD(), + const MetavoxelData& data = MetavoxelData(), const SharedObjectPointer& remoteState = SharedObjectPointer()); const SharedObjectPointer& getRemoteState() const { return _remoteState; } @@ -636,9 +632,9 @@ private: SharedObjectPointer _remoteState; }; -TestReceiveRecord::TestReceiveRecord(const MetavoxelLOD& lod, +TestReceiveRecord::TestReceiveRecord(int packetNumber, const MetavoxelLOD& lod, const MetavoxelData& data, const SharedObjectPointer& remoteState) : - PacketRecord(lod, data), + PacketRecord(packetNumber, lod, data), _remoteState(remoteState) { } @@ -1110,14 +1106,14 @@ void TestEndpoint::handleMessage(const QVariant& message, Bitstream& in) { PacketRecord* TestEndpoint::maybeCreateSendRecord() const { if (_reliableDeltaChannel) { - return new TestSendRecord(_reliableDeltaLOD, _reliableDeltaData, _localState, _sequencer.getOutgoingPacketNumber()); + return new TestSendRecord(_sequencer.getOutgoingPacketNumber(), _reliableDeltaLOD, _reliableDeltaData, _localState); } - return new TestSendRecord(_lod, (_mode == METAVOXEL_CLIENT_MODE) ? MetavoxelData() : _data, - _localState, _sequencer.getOutgoingPacketNumber()); + return new TestSendRecord(_sequencer.getOutgoingPacketNumber(), _lod, + (_mode == METAVOXEL_CLIENT_MODE) ? MetavoxelData() : _data, _localState); } PacketRecord* TestEndpoint::maybeCreateReceiveRecord() const { - return new TestReceiveRecord(_remoteDataLOD, _remoteData, _remoteState); + return new TestReceiveRecord(_sequencer.getIncomingPacketNumber(), _remoteDataLOD, _remoteData, _remoteState); } void TestEndpoint::handleHighPriorityMessage(const QVariant& message) { From 0f58b42795a6831ed9e218eca573024eaa5b72c0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Oct 2014 16:27:26 -0700 Subject: [PATCH 6/8] Forgot these guys. --- .../src/ui/MetavoxelNetworkSimulator.cpp | 72 +++++++++++++++++++ interface/src/ui/MetavoxelNetworkSimulator.h | 40 +++++++++++ 2 files changed, 112 insertions(+) create mode 100644 interface/src/ui/MetavoxelNetworkSimulator.cpp create mode 100644 interface/src/ui/MetavoxelNetworkSimulator.h diff --git a/interface/src/ui/MetavoxelNetworkSimulator.cpp b/interface/src/ui/MetavoxelNetworkSimulator.cpp new file mode 100644 index 0000000000..4b48efd0ce --- /dev/null +++ b/interface/src/ui/MetavoxelNetworkSimulator.cpp @@ -0,0 +1,72 @@ +// +// MetavoxelNetworkSimulator.cpp +// interface/src/ui +// +// Created by Andrzej Kapolka on 10/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include + +#include "Application.h" +#include "MetavoxelNetworkSimulator.h" + +MetavoxelNetworkSimulator::MetavoxelNetworkSimulator() : + QWidget(Application::getInstance()->getGLWidget(), Qt::Dialog) { + + setWindowTitle("Metavoxel Network Simulator"); + setAttribute(Qt::WA_DeleteOnClose); + + QVBoxLayout* topLayout = new QVBoxLayout(); + setLayout(topLayout); + + QFormLayout* form = new QFormLayout(); + topLayout->addLayout(form); + + MetavoxelSystem::NetworkSimulation simulation = Application::getInstance()->getMetavoxels()->getNetworkSimulation(); + + form->addRow("Drop Rate:", _dropRate = new QDoubleSpinBox()); + _dropRate->setSuffix("%"); + _dropRate->setValue(simulation.dropRate * 100.0); + connect(_dropRate, static_cast(&QDoubleSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Repeat Rate:", _repeatRate = new QDoubleSpinBox()); + _repeatRate->setSuffix("%"); + _repeatRate->setValue(simulation.repeatRate * 100.0); + connect(_repeatRate, static_cast(&QDoubleSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Minimum Delay:", _minimumDelay = new QSpinBox()); + _minimumDelay->setMaximum(1000); + _minimumDelay->setSuffix("ms"); + _minimumDelay->setValue(simulation.minimumDelay); + connect(_minimumDelay, static_cast(&QSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + form->addRow("Maximum Delay:", _maximumDelay = new QSpinBox()); + _maximumDelay->setMaximum(1000); + _maximumDelay->setSuffix("ms"); + _maximumDelay->setValue(simulation.maximumDelay); + connect(_maximumDelay, static_cast(&QSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); + topLayout->addWidget(buttons); + connect(buttons, &QDialogButtonBox::accepted, this, &QWidget::close); + + show(); +} + +void MetavoxelNetworkSimulator::updateMetavoxelSystem() { + Application::getInstance()->getMetavoxels()->setNetworkSimulation(MetavoxelSystem::NetworkSimulation( + _dropRate->value() / 100.0, _repeatRate->value() / 100.0, qMin(_minimumDelay->value(), _maximumDelay->value()), + qMax(_minimumDelay->value(), _maximumDelay->value()))); +} diff --git a/interface/src/ui/MetavoxelNetworkSimulator.h b/interface/src/ui/MetavoxelNetworkSimulator.h new file mode 100644 index 0000000000..fcf4a53c04 --- /dev/null +++ b/interface/src/ui/MetavoxelNetworkSimulator.h @@ -0,0 +1,40 @@ +// +// MetavoxelNetworkSimulator.h +// interface/src/ui +// +// Created by Andrzej Kapolka on 10/20/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_MetavoxelNetworkSimulator_h +#define hifi_MetavoxelNetworkSimulator_h + +#include + +class QDoubleSpinBox; +class QSpinBox; + +/// Allows tweaking network simulation (packet drop percentage, etc.) settings for metavoxels. +class MetavoxelNetworkSimulator : public QWidget { + Q_OBJECT + +public: + + MetavoxelNetworkSimulator(); + +private slots: + + void updateMetavoxelSystem(); + +private: + + QDoubleSpinBox* _dropRate; + QDoubleSpinBox* _repeatRate; + QSpinBox* _minimumDelay; + QSpinBox* _maximumDelay; +}; + +#endif // hifi_MetavoxelNetworkSimulator_h From 311d1332f10451171b934e860a035454337f689a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Oct 2014 17:29:28 -0700 Subject: [PATCH 7/8] Include a simple bandwidth limit option. --- interface/src/MetavoxelSystem.cpp | 40 ++++++++++++++++++- interface/src/MetavoxelSystem.h | 29 +++++++++++++- .../src/ui/MetavoxelNetworkSimulator.cpp | 17 +++++++- interface/src/ui/MetavoxelNetworkSimulator.h | 1 + 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index e0794725cb..9cf621f2bd 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -39,11 +39,13 @@ REGISTER_META_OBJECT(StaticModelRenderer) static int bufferPointVectorMetaTypeId = qRegisterMetaType(); -MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, int minimumDelay, int maximumDelay) : +MetavoxelSystem::NetworkSimulation::NetworkSimulation(float dropRate, float repeatRate, + int minimumDelay, int maximumDelay, int bandwidthLimit) : dropRate(dropRate), repeatRate(repeatRate), minimumDelay(minimumDelay), - maximumDelay(maximumDelay) { + maximumDelay(maximumDelay), + bandwidthLimit(bandwidthLimit) { } void MetavoxelSystem::init() { @@ -695,6 +697,28 @@ void MetavoxelSystem::guideToAugmented(MetavoxelVisitor& visitor, bool render) { } } +Throttle::Throttle() : + _limit(INT_MAX), + _total(0) { +} + +bool Throttle::shouldThrottle(int bytes) { + // clear expired buckets + qint64 now = QDateTime::currentMSecsSinceEpoch(); + while (!_buckets.isEmpty() && _buckets.first().first >= now) { + _total -= _buckets.takeFirst().second; + } + + // if possible, add the new bucket + if (_total + bytes > _limit) { + return true; + } + const int BUCKET_DURATION = 1000; + _buckets.append(Bucket(now + BUCKET_DURATION, bytes)); + _total += bytes; + return false; +} + MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : MetavoxelClient(node, updater) { } @@ -746,6 +770,12 @@ int MetavoxelSystemClient::parseData(const QByteArray& packet) { } int count = (randFloat() < simulation.repeatRate) ? 2 : 1; for (int i = 0; i < count; i++) { + if (simulation.bandwidthLimit > 0) { + _receiveThrottle.setLimit(simulation.bandwidthLimit); + if (_receiveThrottle.shouldThrottle(packet.size())) { + continue; + } + } int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); if (delay > 0) { ReceiveDelayer* delayer = new ReceiveDelayer(_node, packet); @@ -864,6 +894,12 @@ void MetavoxelSystemClient::sendDatagram(const QByteArray& data) { } int count = (randFloat() < simulation.repeatRate) ? 2 : 1; for (int i = 0; i < count; i++) { + if (simulation.bandwidthLimit > 0) { + _sendThrottle.setLimit(simulation.bandwidthLimit); + if (_sendThrottle.shouldThrottle(data.size())) { + continue; + } + } int delay = randIntInRange(simulation.minimumDelay, simulation.maximumDelay); if (delay > 0) { SendDelayer* delayer = new SendDelayer(_node, data); diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index af6f4c6c7a..14a24eea59 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -37,8 +37,10 @@ public: float repeatRate; int minimumDelay; int maximumDelay; + int bandwidthLimit; - NetworkSimulation(float dropRate = 0.0f, float repeatRate = 0.0f, int minimumDelay = 0, int maximumDelay = 0); + NetworkSimulation(float dropRate = 0.0f, float repeatRate = 0.0f, int minimumDelay = 0, + int maximumDelay = 0, int bandwidthLimit = 0); }; virtual void init(); @@ -132,6 +134,28 @@ typedef QVector BufferPointVector; Q_DECLARE_METATYPE(BufferPointVector) +/// Simple throttle for limiting bandwidth on a per-second basis. +class Throttle { +public: + + Throttle(); + + /// Sets the per-second limit. + void setLimit(int limit) { _limit = limit; } + + /// Determines whether the message with the given size should be throttled (discarded). If not, registers the message + /// as having been processed (i.e., contributing to later throttling). + bool shouldThrottle(int bytes); + +private: + + int _limit; + int _total; + + typedef QPair Bucket; + QList _buckets; +}; + /// A client session associated with a single server. class MetavoxelSystemClient : public MetavoxelClient { Q_OBJECT @@ -161,6 +185,9 @@ private: MetavoxelData _augmentedData; MetavoxelData _renderedAugmentedData; QReadWriteLock _augmentedDataLock; + + Throttle _sendThrottle; + Throttle _receiveThrottle; }; /// Base class for cached static buffers. diff --git a/interface/src/ui/MetavoxelNetworkSimulator.cpp b/interface/src/ui/MetavoxelNetworkSimulator.cpp index 4b48efd0ce..aabee97636 100644 --- a/interface/src/ui/MetavoxelNetworkSimulator.cpp +++ b/interface/src/ui/MetavoxelNetworkSimulator.cpp @@ -18,6 +18,8 @@ #include "Application.h" #include "MetavoxelNetworkSimulator.h" +const int BYTES_PER_KILOBYTE = 1024; + MetavoxelNetworkSimulator::MetavoxelNetworkSimulator() : QWidget(Application::getInstance()->getGLWidget(), Qt::Dialog) { @@ -58,6 +60,13 @@ MetavoxelNetworkSimulator::MetavoxelNetworkSimulator() : connect(_maximumDelay, static_cast(&QSpinBox::valueChanged), this, &MetavoxelNetworkSimulator::updateMetavoxelSystem); + form->addRow("Bandwidth Limit:", _bandwidthLimit = new QSpinBox()); + _bandwidthLimit->setMaximum(1024 * 1024); + _bandwidthLimit->setSuffix("KB/s"); + _bandwidthLimit->setValue(simulation.bandwidthLimit / BYTES_PER_KILOBYTE); + connect(_bandwidthLimit, static_cast(&QSpinBox::valueChanged), this, + &MetavoxelNetworkSimulator::updateMetavoxelSystem); + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok, this); topLayout->addWidget(buttons); connect(buttons, &QDialogButtonBox::accepted, this, &QWidget::close); @@ -66,7 +75,13 @@ MetavoxelNetworkSimulator::MetavoxelNetworkSimulator() : } void MetavoxelNetworkSimulator::updateMetavoxelSystem() { + int bandwidthLimit = _bandwidthLimit->value() * BYTES_PER_KILOBYTE; + if (bandwidthLimit > 0) { + // make sure the limit is enough to let at least one packet through + const int MINIMUM_BANDWIDTH_LIMIT = 2048; + bandwidthLimit = qMax(bandwidthLimit, MINIMUM_BANDWIDTH_LIMIT); + } Application::getInstance()->getMetavoxels()->setNetworkSimulation(MetavoxelSystem::NetworkSimulation( _dropRate->value() / 100.0, _repeatRate->value() / 100.0, qMin(_minimumDelay->value(), _maximumDelay->value()), - qMax(_minimumDelay->value(), _maximumDelay->value()))); + qMax(_minimumDelay->value(), _maximumDelay->value()), bandwidthLimit)); } diff --git a/interface/src/ui/MetavoxelNetworkSimulator.h b/interface/src/ui/MetavoxelNetworkSimulator.h index fcf4a53c04..18d0857ae4 100644 --- a/interface/src/ui/MetavoxelNetworkSimulator.h +++ b/interface/src/ui/MetavoxelNetworkSimulator.h @@ -35,6 +35,7 @@ private: QDoubleSpinBox* _repeatRate; QSpinBox* _minimumDelay; QSpinBox* _maximumDelay; + QSpinBox* _bandwidthLimit; }; #endif // hifi_MetavoxelNetworkSimulator_h From 4840c44d7ca47cfc263ae859d25b54efbce81561 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 21 Oct 2014 17:41:41 -0700 Subject: [PATCH 8/8] Time calculation fix. --- interface/src/MetavoxelSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 9cf621f2bd..8166c3938c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -705,7 +705,7 @@ Throttle::Throttle() : bool Throttle::shouldThrottle(int bytes) { // clear expired buckets qint64 now = QDateTime::currentMSecsSinceEpoch(); - while (!_buckets.isEmpty() && _buckets.first().first >= now) { + while (!_buckets.isEmpty() && now >= _buckets.first().first) { _total -= _buckets.takeFirst().second; }