diff --git a/assignment-client/src/metavoxels/MetavoxelServer.cpp b/assignment-client/src/metavoxels/MetavoxelServer.cpp index cd80650638..3535a00573 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.cpp +++ b/assignment-client/src/metavoxels/MetavoxelServer.cpp @@ -21,8 +21,6 @@ #include "MetavoxelServer.h" -const int SEND_INTERVAL = 50; - MetavoxelServer::MetavoxelServer(const QByteArray& packet) : ThreadedAssignment(packet), _nextSender(0) { @@ -48,8 +46,8 @@ void MetavoxelServer::run() { NodeList* nodeList = NodeList::getInstance(); nodeList->addNodeTypeToInterestSet(NodeType::Agent); - connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachSession(const SharedNodePointer&))); - connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(maybeDeleteSession(const SharedNodePointer&))); + connect(nodeList, &NodeList::nodeAdded, this, &MetavoxelServer::maybeAttachSession); + connect(nodeList, &NodeList::nodeKilled, this, &MetavoxelServer::maybeDeleteSession); // initialize Bitstream before using it in multiple threads Bitstream::preThreadingInit(); @@ -65,7 +63,7 @@ void MetavoxelServer::run() { QThread* thread = new QThread(this); MetavoxelSender* sender = new MetavoxelSender(this); sender->moveToThread(thread); - sender->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); + connect(thread, &QThread::finished, sender, &QObject::deleteLater); thread->start(); QMetaObject::invokeMethod(sender, "start"); _senders.append(sender); @@ -75,7 +73,7 @@ void MetavoxelServer::run() { _persister = new MetavoxelPersister(this); QThread* persistenceThread = new QThread(this); _persister->moveToThread(persistenceThread); - _persister->connect(persistenceThread, SIGNAL(finished()), SLOT(deleteLater())); + connect(persistenceThread, &QThread::finished, _persister, &QObject::deleteLater); persistenceThread->start(); // queue up the load @@ -121,15 +119,16 @@ void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) { _nextSender = (_nextSender + 1) % _senders.size(); MetavoxelSession* session = new MetavoxelSession(node, sender); session->moveToThread(sender->thread()); + QMetaObject::invokeMethod(sender, "addSession", Q_ARG(QObject*, session)); node->setLinkedData(session); } } void MetavoxelServer::maybeDeleteSession(const SharedNodePointer& node) { if (node->getType() == NodeType::Agent) { - QMutexLocker locker(&node->getMutex()); + // we assume the node is already locked MetavoxelSession* session = static_cast(node->getLinkedData()); - if (session) { + if (session) { node->setLinkedData(NULL); session->deleteLater(); } @@ -141,26 +140,27 @@ MetavoxelSender::MetavoxelSender(MetavoxelServer* server) : _sendTimer(this) { _sendTimer.setSingleShot(true); - connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); + connect(&_sendTimer, &QTimer::timeout, this, &MetavoxelSender::sendDeltas); connect(_server, &MetavoxelServer::dataChanged, this, &MetavoxelSender::setData); } +const int SEND_INTERVAL = 50; + void MetavoxelSender::start() { _lastSend = QDateTime::currentMSecsSinceEpoch(); _sendTimer.start(SEND_INTERVAL); } +void MetavoxelSender::addSession(QObject* session) { + _sessions.insert(static_cast(session)); + connect(session, &QObject::destroyed, this, &MetavoxelSender::removeSession); +} + void MetavoxelSender::sendDeltas() { // send deltas for all sessions associated with our thread - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::Agent) { - QMutexLocker locker(&node->getMutex()); - MetavoxelSession* session = static_cast(node->getLinkedData()); - if (session && session->thread() == QThread::currentThread()) { - session->update(); - } - } + foreach (MetavoxelSession* session, _sessions) { + session->update(); } // restart the send timer @@ -171,6 +171,10 @@ void MetavoxelSender::sendDeltas() { _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); } +void MetavoxelSender::removeSession(QObject* session) { + _sessions.remove(static_cast(session)); +} + MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender) : Endpoint(node, new PacketRecord(), NULL), _sender(sender), diff --git a/assignment-client/src/metavoxels/MetavoxelServer.h b/assignment-client/src/metavoxels/MetavoxelServer.h index 45478a4946..6d62884633 100644 --- a/assignment-client/src/metavoxels/MetavoxelServer.h +++ b/assignment-client/src/metavoxels/MetavoxelServer.h @@ -77,14 +77,18 @@ public: Q_INVOKABLE void start(); + Q_INVOKABLE void addSession(QObject* session); + private slots: void setData(const MetavoxelData& data) { _data = data; } void sendDeltas(); + void removeSession(QObject* session); private: MetavoxelServer* _server; + QSet _sessions; QTimer _sendTimer; qint64 _lastSend; @@ -99,7 +103,7 @@ class MetavoxelSession : public Endpoint { public: MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender); - + virtual void update(); protected: diff --git a/interface/src/MetavoxelSystem.cpp b/interface/src/MetavoxelSystem.cpp index 2b3528b1b3..685a07e85c 100644 --- a/interface/src/MetavoxelSystem.cpp +++ b/interface/src/MetavoxelSystem.cpp @@ -10,6 +10,8 @@ // #include +#include +#include #include #include @@ -45,11 +47,9 @@ void MetavoxelSystem::init() { _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute()); } -MetavoxelLOD MetavoxelSystem::getLOD() const { - // the LOD threshold is temporarily tied to the avatar LOD parameter - const float BASE_LOD_THRESHOLD = 0.01f; - return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), - BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); +MetavoxelLOD MetavoxelSystem::getLOD() { + QReadLocker locker(&_lodLock); + return _lod; } class SpannerSimulateVisitor : public SpannerVisitor { @@ -77,9 +77,15 @@ bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimu } void MetavoxelSystem::simulate(float deltaTime) { - // update the clients - update(); - + // update the lod + { + // the LOD threshold is temporarily tied to the avatar LOD parameter + QWriteLocker locker(&_lodLock); + const float BASE_LOD_THRESHOLD = 0.01f; + _lod = MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(), + BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier()); + } + SpannerSimulateVisitor spannerSimulateVisitor(deltaTime); guide(spannerSimulateVisitor); } @@ -183,16 +189,20 @@ void MetavoxelSystem::render() { guide(spannerRenderVisitor); } +void MetavoxelSystem::setClientPoints(const SharedNodePointer& node, const BufferPointVector& points) { + QMutexLocker locker(&node->getMutex()); + MetavoxelSystemClient* client = static_cast(node->getLinkedData()); + if (client) { + client->setPoints(points); + } +} + MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { - return new MetavoxelSystemClient(node, this); + return new MetavoxelSystemClient(node, _updater); } -void MetavoxelSystem::updateClient(MetavoxelClient* client) { - MetavoxelClientManager::updateClient(client); -} - -MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system) : - MetavoxelClient(node, system), +MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : + MetavoxelClient(node, updater), _pointCount(0) { _buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); @@ -385,11 +395,14 @@ void PointBufferBuilder::run() { qint64 now = QDateTime::currentMSecsSinceEpoch(); PointCollector collector(_lod); _data.guide(collector); - QMetaObject::invokeMethod(node->getLinkedData(), "setPoints", Q_ARG(const BufferPointVector&, collector.points)); + QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels(), "setClientPoints", + Q_ARG(const SharedNodePointer&, node), Q_ARG(const BufferPointVector&, collector.points)); qDebug() << "collect" << (QDateTime::currentMSecsSinceEpoch() - now); } void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { + MetavoxelClient::dataChanged(oldData); + /* BufferBuilder builder(_remoteDataLOD); const AttributePointer& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute(); MetavoxelNode* oldRoot = oldData.getRoot(pointBufferAttribute); @@ -500,7 +513,7 @@ void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum return; } // slight performance optimization: don't render if clip bounds are entirely within sphere - Sphere* sphere = static_cast(parent()); + Sphere* sphere = static_cast(_spanner); Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); for (int i = 0; i < Box::VERTEX_COUNT; i++) { const float CLIP_PROPORTION = 0.95f; @@ -512,7 +525,7 @@ void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum } void SphereRenderer::renderUnclipped(float alpha, Mode mode) { - Sphere* sphere = static_cast(parent()); + Sphere* sphere = static_cast(_spanner); const QColor& color = sphere->getColor(); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha); @@ -533,6 +546,8 @@ StaticModelRenderer::StaticModelRenderer() : } void StaticModelRenderer::init(Spanner* spanner) { + SpannerRenderer::init(spanner); + _model->init(); StaticModel* staticModel = static_cast(spanner); @@ -554,7 +569,7 @@ void StaticModelRenderer::simulate(float deltaTime) { const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents; bounds = Box(extents.minimum, extents.maximum); } - static_cast(parent())->setBounds(glm::translate(_model->getTranslation()) * + static_cast(_spanner)->setBounds(glm::translate(_model->getTranslation()) * glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds); _model->simulate(deltaTime); } diff --git a/interface/src/MetavoxelSystem.h b/interface/src/MetavoxelSystem.h index afbfd9444f..71bc9dc231 100644 --- a/interface/src/MetavoxelSystem.h +++ b/interface/src/MetavoxelSystem.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -22,8 +23,12 @@ #include "renderer/ProgramObject.h" +class BufferPoint; class Model; +typedef QVector BufferPointVector; +typedef QPair BufferPointVectorPair; + /// Renders a metavoxel tree. class MetavoxelSystem : public MetavoxelClientManager { Q_OBJECT @@ -34,15 +39,16 @@ public: virtual void init(); - virtual MetavoxelLOD getLOD() const; + virtual MetavoxelLOD getLOD(); void simulate(float deltaTime); void render(); + Q_INVOKABLE void setClientPoints(const SharedNodePointer& node, const BufferPointVector& points); + protected: virtual MetavoxelClient* createClient(const SharedNodePointer& node); - virtual void updateClient(MetavoxelClient* client); private: @@ -50,6 +56,9 @@ private: static int _pointScaleLocation; AttributePointer _pointBufferAttribute; + + MetavoxelLOD _lod; + QReadWriteLock _lodLock; }; /// Describes contents of a point in a point buffer. @@ -60,9 +69,6 @@ public: quint8 normal[3]; }; -typedef QVector BufferPointVector; -typedef QPair BufferPointVectorPair; - Q_DECLARE_METATYPE(BufferPointVector) /// A client session associated with a single server. @@ -71,11 +77,11 @@ class MetavoxelSystemClient : public MetavoxelClient { public: - MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system); + MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater); void render(); - Q_INVOKABLE void setPoints(const BufferPointVector& points); + void setPoints(const BufferPointVector& points); virtual int parseData(const QByteArray& packet); diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index f82bc7ba17..728ed04e31 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -48,7 +48,13 @@ Stats::Stats(): _pingStatsWidth(STATS_PING_MIN_WIDTH), _geoStatsWidth(STATS_GEO_MIN_WIDTH), _voxelStatsWidth(STATS_VOXEL_MIN_WIDTH), - _lastHorizontalOffset(0) + _lastHorizontalOffset(0), + _metavoxelInternal(0), + _metavoxelLeaves(0), + _metavoxelSendProgress(0), + _metavoxelSendTotal(0), + _metavoxelReceiveProgress(0), + _metavoxelReceiveTotal(0) { QGLWidget* glWidget = Application::getInstance()->getGLWidget(); resetWidth(glWidget->width(), 0); @@ -487,36 +493,26 @@ void Stats::display( verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); - int internal = 0, leaves = 0; - int sendProgress = 0, sendTotal = 0; - int receiveProgress = 0, receiveTotal = 0; - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - client->getData().countNodes(internal, leaves, Application::getInstance()->getMetavoxels()->getLOD()); - client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal); - } - } - } + QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels()->getUpdater(), "getStats", + Q_ARG(QObject*, this), Q_ARG(const QByteArray&, "setMetavoxelStats")); + stringstream nodes; - nodes << "Metavoxels: " << (internal + leaves); + nodes << "Metavoxels: " << (_metavoxelInternal + _metavoxelLeaves); verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color); stringstream nodeTypes; - nodeTypes << "Internal: " << internal << " Leaves: " << leaves; + nodeTypes << "Internal: " << _metavoxelInternal << " Leaves: " << _metavoxelLeaves; verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color); - if (sendTotal > 0 || receiveTotal > 0) { + if (_metavoxelSendTotal > 0 || _metavoxelReceiveTotal > 0) { stringstream reliableStats; - if (sendTotal > 0) { - reliableStats << "Upload: " << (sendProgress * 100 / sendTotal) << "% "; + if (_metavoxelSendTotal > 0) { + reliableStats << "Upload: " << (_metavoxelSendProgress * 100 / _metavoxelSendTotal) << "% "; } - if (receiveTotal > 0) { - reliableStats << "Download: " << (receiveProgress * 100 / receiveTotal) << "%"; + if (_metavoxelReceiveTotal > 0) { + reliableStats << "Download: " << (_metavoxelReceiveProgress * 100 / _metavoxelReceiveTotal) << "%"; } verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color); @@ -849,3 +845,13 @@ void Stats::display( } + +void Stats::setMetavoxelStats(int internal, int leaves, int sendProgress, + int sendTotal, int receiveProgress, int receiveTotal) { + _metavoxelInternal = internal; + _metavoxelLeaves = leaves; + _metavoxelSendProgress = sendProgress; + _metavoxelSendTotal = sendTotal; + _metavoxelReceiveProgress = receiveProgress; + _metavoxelReceiveTotal = receiveTotal; +} diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 66d6ec99f0..5a38b5ed9f 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -31,6 +31,10 @@ public: void resetWidth(int width, int horizontalOffset); void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess); bool includeTimingRecord(const QString& name); + + Q_INVOKABLE void setMetavoxelStats(int internal, int leaves, int sendProgress, + int sendTotal, int receiveProgress, int receiveTotal); + private: static Stats* _sharedInstance; @@ -45,6 +49,13 @@ private: int _voxelStatsWidth; int _lastHorizontalOffset; + + int _metavoxelInternal; + int _metavoxelLeaves; + int _metavoxelSendProgress; + int _metavoxelSendTotal; + int _metavoxelReceiveProgress; + int _metavoxelReceiveTotal; }; #endif // hifi_Stats_h diff --git a/libraries/metavoxels/src/MetavoxelClientManager.cpp b/libraries/metavoxels/src/MetavoxelClientManager.cpp index af97551ade..a2d3410314 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.cpp +++ b/libraries/metavoxels/src/MetavoxelClientManager.cpp @@ -9,26 +9,31 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include #include +#include #include "MetavoxelClientManager.h" #include "MetavoxelMessages.h" -void MetavoxelClientManager::init() { - connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); - connect(NodeList::getInstance(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(maybeDeleteClient(const SharedNodePointer&))); +MetavoxelClientManager::MetavoxelClientManager() : + _updater(new MetavoxelUpdater(this)) { + QThread* thread = new QThread(this); + _updater->moveToThread(thread); + connect(thread, &QThread::finished, _updater, &QObject::deleteLater); + thread->start(); + QMetaObject::invokeMethod(_updater, "start"); } -void MetavoxelClientManager::update() { - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - updateClient(client); - } - } - } +MetavoxelClientManager::~MetavoxelClientManager() { + _updater->thread()->quit(); + _updater->thread()->wait(); +} + +void MetavoxelClientManager::init() { + connect(NodeList::getInstance(), &NodeList::nodeAdded, this, &MetavoxelClientManager::maybeAttachClient); + connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &MetavoxelClientManager::maybeDeleteClient); } SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin, @@ -41,7 +46,7 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { float clientDistance; - SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection( + SharedObjectPointer clientSpanner = client->getDataCopy().findFirstRaySpannerIntersection( origin, direction, attribute, clientDistance); if (clientSpanner && clientDistance < closestDistance) { closestSpanner = clientSpanner; @@ -70,35 +75,26 @@ void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool } void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); - return; - } - foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { - if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); - MetavoxelClient* client = static_cast(node->getLinkedData()); - if (client) { - client->applyEdit(edit, reliable); - } - } - } + QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable)); } -MetavoxelLOD MetavoxelClientManager::getLOD() const { +MetavoxelLOD MetavoxelClientManager::getLOD() { return MetavoxelLOD(); } void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) { if (node->getType() == NodeType::MetavoxelServer) { QMutexLocker locker(&node->getMutex()); - node->setLinkedData(createClient(node)); + MetavoxelClient* client = createClient(node); + client->moveToThread(_updater->thread()); + QMetaObject::invokeMethod(_updater, "addClient", Q_ARG(QObject*, client)); + node->setLinkedData(client); } } void MetavoxelClientManager::maybeDeleteClient(const SharedNodePointer& node) { if (node->getType() == NodeType::MetavoxelServer) { - QMutexLocker locker(&node->getMutex()); + // we assume the node is already locked MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { node->setLinkedData(NULL); @@ -108,11 +104,7 @@ void MetavoxelClientManager::maybeDeleteClient(const SharedNodePointer& node) { } MetavoxelClient* MetavoxelClientManager::createClient(const SharedNodePointer& node) { - return new MetavoxelClient(node, this); -} - -void MetavoxelClientManager::updateClient(MetavoxelClient* client) { - client->update(); + return new MetavoxelClient(node, _updater); } void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) { @@ -121,15 +113,75 @@ void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) { QMutexLocker locker(&node->getMutex()); MetavoxelClient* client = static_cast(node->getLinkedData()); if (client) { - client->guide(visitor); + client->getDataCopy().guide(visitor); } } } } -MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager) : +MetavoxelUpdater::MetavoxelUpdater(MetavoxelClientManager* clientManager) : + _clientManager(clientManager), + _sendTimer(this) { + + _sendTimer.setSingleShot(true); + connect(&_sendTimer, &QTimer::timeout, this, &MetavoxelUpdater::sendUpdates); +} + +const int SEND_INTERVAL = 33; + +void MetavoxelUpdater::start() { + _lastSend = QDateTime::currentMSecsSinceEpoch(); + _sendTimer.start(SEND_INTERVAL); +} + +void MetavoxelUpdater::addClient(QObject* client) { + _clients.insert(static_cast(client)); + connect(client, &QObject::destroyed, this, &MetavoxelUpdater::removeClient); +} + +void MetavoxelUpdater::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { + // apply to all clients + foreach (MetavoxelClient* client, _clients) { + client->applyEdit(edit, reliable); + } +} + +void MetavoxelUpdater::getStats(QObject* receiver, const QByteArray& method) { + int internal = 0, leaves = 0; + int sendProgress = 0, sendTotal = 0; + int receiveProgress = 0, receiveTotal = 0; + foreach (MetavoxelClient* client, _clients) { + client->getData().countNodes(internal, leaves, _lod); + client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal); + } + QMetaObject::invokeMethod(receiver, method.constData(), Q_ARG(int, internal), Q_ARG(int, leaves), Q_ARG(int, sendProgress), + Q_ARG(int, sendTotal), Q_ARG(int, receiveProgress), Q_ARG(int, receiveTotal)); +} + +void MetavoxelUpdater::sendUpdates() { + // get the latest LOD from the client manager + _lod = _clientManager->getLOD(); + + // send updates for all clients + foreach (MetavoxelClient* client, _clients) { + client->update(); + } + + // restart the send timer + qint64 now = QDateTime::currentMSecsSinceEpoch(); + int elapsed = now - _lastSend; + _lastSend = now; + + _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); +} + +void MetavoxelUpdater::removeClient(QObject* client) { + _clients.remove(static_cast(client)); +} + +MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater) : Endpoint(node, new PacketRecord(), new PacketRecord()), - _manager(manager), + _updater(updater), _reliableDeltaChannel(NULL), _reliableDeltaID(0) { @@ -137,9 +189,9 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientM SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); } -void MetavoxelClient::guide(MetavoxelVisitor& visitor) { - visitor.setLOD(_manager->getLOD()); - _data.guide(visitor); +MetavoxelData MetavoxelClient::getDataCopy() { + QReadLocker locker(&_dataCopyLock); + return _dataCopy; } void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { @@ -160,11 +212,13 @@ void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) } void MetavoxelClient::dataChanged(const MetavoxelData& oldData) { - // nothing by default + // make thread-safe copy + QWriteLocker locker(&_dataCopyLock); + _dataCopy = _data; } void MetavoxelClient::writeUpdateMessage(Bitstream& out) { - ClientStateMessage state = { _manager->getLOD() }; + ClientStateMessage state = { _updater->getLOD() }; out << QVariant::fromValue(state); } @@ -212,7 +266,7 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) { } PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { - return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _manager->getLOD()); + return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD()); } PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { diff --git a/libraries/metavoxels/src/MetavoxelClientManager.h b/libraries/metavoxels/src/MetavoxelClientManager.h index a915ba9c3c..333b709b9e 100644 --- a/libraries/metavoxels/src/MetavoxelClientManager.h +++ b/libraries/metavoxels/src/MetavoxelClientManager.h @@ -12,10 +12,14 @@ #ifndef hifi_MetavoxelClientManager_h #define hifi_MetavoxelClientManager_h +#include +#include + #include "Endpoint.h" class MetavoxelClient; class MetavoxelEditMessage; +class MetavoxelUpdater; /// Manages the set of connected metavoxel clients. class MetavoxelClientManager : public QObject { @@ -23,8 +27,12 @@ class MetavoxelClientManager : public QObject { public: + MetavoxelClientManager(); + virtual ~MetavoxelClientManager(); + virtual void init(); - void update(); + + MetavoxelUpdater* getUpdater() const { return _updater; } SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, const AttributePointer& attribute, float& distance); @@ -35,7 +43,8 @@ public: Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); - virtual MetavoxelLOD getLOD() const; + /// Returns the current LOD. This must be thread-safe, as it will be called from the updater thread. + virtual MetavoxelLOD getLOD(); private slots: @@ -45,9 +54,46 @@ private slots: protected: virtual MetavoxelClient* createClient(const SharedNodePointer& node); - virtual void updateClient(MetavoxelClient* client); void guide(MetavoxelVisitor& visitor); + + MetavoxelUpdater* _updater; +}; + +/// Handles updates in a dedicated thread. +class MetavoxelUpdater : public QObject { + Q_OBJECT + +public: + + MetavoxelUpdater(MetavoxelClientManager* clientManager); + + const MetavoxelLOD& getLOD() const { return _lod; } + + Q_INVOKABLE void start(); + + Q_INVOKABLE void addClient(QObject* client); + + Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable); + + /// Requests a set of statistics. The receiving method should take six integer arguments: internal node count, leaf count, + /// send progress, send total, receive progress, receive total. + Q_INVOKABLE void getStats(QObject* receiver, const QByteArray& method); + +private slots: + + void sendUpdates(); + void removeClient(QObject* client); + +private: + + MetavoxelClientManager* _clientManager; + QSet _clients; + + QTimer _sendTimer; + qint64 _lastSend; + + MetavoxelLOD _lod; }; /// Base class for metavoxel clients. @@ -56,12 +102,14 @@ class MetavoxelClient : public Endpoint { public: - MetavoxelClient(const SharedNodePointer& node, MetavoxelClientManager* manager); + MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater); - MetavoxelData& getData() { return _data; } + /// Returns a reference to the most recent data. This function is *not* thread-safe. + const MetavoxelData& getData() const { return _data; } + + /// Returns a copy of the most recent data. This function *is* thread-safe. + MetavoxelData getDataCopy(); - void guide(MetavoxelVisitor& visitor); - void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); protected: @@ -74,7 +122,7 @@ protected: virtual PacketRecord* maybeCreateSendRecord() const; virtual PacketRecord* maybeCreateReceiveRecord() const; - MetavoxelClientManager* _manager; + MetavoxelUpdater* _updater; MetavoxelData _data; MetavoxelData _remoteData; MetavoxelLOD _remoteDataLOD; @@ -82,6 +130,9 @@ protected: ReliableChannel* _reliableDeltaChannel; MetavoxelLOD _reliableDeltaLOD; int _reliableDeltaID; + + MetavoxelData _dataCopy; + QReadWriteLock _dataCopyLock; }; #endif // hifi_MetavoxelClientManager_h diff --git a/libraries/metavoxels/src/MetavoxelData.cpp b/libraries/metavoxels/src/MetavoxelData.cpp index 0f8773a896..400ae6bfdd 100644 --- a/libraries/metavoxels/src/MetavoxelData.cpp +++ b/libraries/metavoxels/src/MetavoxelData.cpp @@ -1899,7 +1899,7 @@ SpannerRenderer* Spanner::getRenderer() { metaObject = &SpannerRenderer::staticMetaObject; } _renderer = static_cast(metaObject->newInstance()); - _renderer->setParent(this); + connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater); _renderer->init(this); } return _renderer; @@ -1920,7 +1920,7 @@ SpannerRenderer::SpannerRenderer() { } void SpannerRenderer::init(Spanner* spanner) { - // nothing by default + _spanner = spanner; } void SpannerRenderer::simulate(float deltaTime) { diff --git a/libraries/metavoxels/src/MetavoxelData.h b/libraries/metavoxels/src/MetavoxelData.h index dfebbe4586..546f31c321 100644 --- a/libraries/metavoxels/src/MetavoxelData.h +++ b/libraries/metavoxels/src/MetavoxelData.h @@ -599,6 +599,10 @@ public: virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize); virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& clipMinimum, float clipSize, float& distance) const; + +protected: + + Spanner* _spanner; }; /// An object with a 3D transform.