Progress on threading.

This commit is contained in:
Andrzej Kapolka 2014-07-23 18:20:00 -07:00
parent 4ea8e7de5d
commit 27f88e9f80
10 changed files with 274 additions and 119 deletions

View file

@ -21,8 +21,6 @@
#include "MetavoxelServer.h" #include "MetavoxelServer.h"
const int SEND_INTERVAL = 50;
MetavoxelServer::MetavoxelServer(const QByteArray& packet) : MetavoxelServer::MetavoxelServer(const QByteArray& packet) :
ThreadedAssignment(packet), ThreadedAssignment(packet),
_nextSender(0) { _nextSender(0) {
@ -48,8 +46,8 @@ void MetavoxelServer::run() {
NodeList* nodeList = NodeList::getInstance(); NodeList* nodeList = NodeList::getInstance();
nodeList->addNodeTypeToInterestSet(NodeType::Agent); nodeList->addNodeTypeToInterestSet(NodeType::Agent);
connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachSession(const SharedNodePointer&))); connect(nodeList, &NodeList::nodeAdded, this, &MetavoxelServer::maybeAttachSession);
connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)), SLOT(maybeDeleteSession(const SharedNodePointer&))); connect(nodeList, &NodeList::nodeKilled, this, &MetavoxelServer::maybeDeleteSession);
// initialize Bitstream before using it in multiple threads // initialize Bitstream before using it in multiple threads
Bitstream::preThreadingInit(); Bitstream::preThreadingInit();
@ -65,7 +63,7 @@ void MetavoxelServer::run() {
QThread* thread = new QThread(this); QThread* thread = new QThread(this);
MetavoxelSender* sender = new MetavoxelSender(this); MetavoxelSender* sender = new MetavoxelSender(this);
sender->moveToThread(thread); sender->moveToThread(thread);
sender->connect(thread, SIGNAL(finished()), SLOT(deleteLater())); connect(thread, &QThread::finished, sender, &QObject::deleteLater);
thread->start(); thread->start();
QMetaObject::invokeMethod(sender, "start"); QMetaObject::invokeMethod(sender, "start");
_senders.append(sender); _senders.append(sender);
@ -75,7 +73,7 @@ void MetavoxelServer::run() {
_persister = new MetavoxelPersister(this); _persister = new MetavoxelPersister(this);
QThread* persistenceThread = new QThread(this); QThread* persistenceThread = new QThread(this);
_persister->moveToThread(persistenceThread); _persister->moveToThread(persistenceThread);
_persister->connect(persistenceThread, SIGNAL(finished()), SLOT(deleteLater())); connect(persistenceThread, &QThread::finished, _persister, &QObject::deleteLater);
persistenceThread->start(); persistenceThread->start();
// queue up the load // queue up the load
@ -121,13 +119,14 @@ void MetavoxelServer::maybeAttachSession(const SharedNodePointer& node) {
_nextSender = (_nextSender + 1) % _senders.size(); _nextSender = (_nextSender + 1) % _senders.size();
MetavoxelSession* session = new MetavoxelSession(node, sender); MetavoxelSession* session = new MetavoxelSession(node, sender);
session->moveToThread(sender->thread()); session->moveToThread(sender->thread());
QMetaObject::invokeMethod(sender, "addSession", Q_ARG(QObject*, session));
node->setLinkedData(session); node->setLinkedData(session);
} }
} }
void MetavoxelServer::maybeDeleteSession(const SharedNodePointer& node) { void MetavoxelServer::maybeDeleteSession(const SharedNodePointer& node) {
if (node->getType() == NodeType::Agent) { if (node->getType() == NodeType::Agent) {
QMutexLocker locker(&node->getMutex()); // we assume the node is already locked
MetavoxelSession* session = static_cast<MetavoxelSession*>(node->getLinkedData()); MetavoxelSession* session = static_cast<MetavoxelSession*>(node->getLinkedData());
if (session) { if (session) {
node->setLinkedData(NULL); node->setLinkedData(NULL);
@ -141,27 +140,28 @@ MetavoxelSender::MetavoxelSender(MetavoxelServer* server) :
_sendTimer(this) { _sendTimer(this) {
_sendTimer.setSingleShot(true); _sendTimer.setSingleShot(true);
connect(&_sendTimer, SIGNAL(timeout()), SLOT(sendDeltas())); connect(&_sendTimer, &QTimer::timeout, this, &MetavoxelSender::sendDeltas);
connect(_server, &MetavoxelServer::dataChanged, this, &MetavoxelSender::setData); connect(_server, &MetavoxelServer::dataChanged, this, &MetavoxelSender::setData);
} }
const int SEND_INTERVAL = 50;
void MetavoxelSender::start() { void MetavoxelSender::start() {
_lastSend = QDateTime::currentMSecsSinceEpoch(); _lastSend = QDateTime::currentMSecsSinceEpoch();
_sendTimer.start(SEND_INTERVAL); _sendTimer.start(SEND_INTERVAL);
} }
void MetavoxelSender::addSession(QObject* session) {
_sessions.insert(static_cast<MetavoxelSession*>(session));
connect(session, &QObject::destroyed, this, &MetavoxelSender::removeSession);
}
void MetavoxelSender::sendDeltas() { void MetavoxelSender::sendDeltas() {
// send deltas for all sessions associated with our thread // send deltas for all sessions associated with our thread
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { foreach (MetavoxelSession* session, _sessions) {
if (node->getType() == NodeType::Agent) {
QMutexLocker locker(&node->getMutex());
MetavoxelSession* session = static_cast<MetavoxelSession*>(node->getLinkedData());
if (session && session->thread() == QThread::currentThread()) {
session->update(); session->update();
} }
}
}
// restart the send timer // restart the send timer
qint64 now = QDateTime::currentMSecsSinceEpoch(); qint64 now = QDateTime::currentMSecsSinceEpoch();
@ -171,6 +171,10 @@ void MetavoxelSender::sendDeltas() {
_sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL))); _sendTimer.start(qMax(0, 2 * SEND_INTERVAL - qMax(elapsed, SEND_INTERVAL)));
} }
void MetavoxelSender::removeSession(QObject* session) {
_sessions.remove(static_cast<MetavoxelSession*>(session));
}
MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender) : MetavoxelSession::MetavoxelSession(const SharedNodePointer& node, MetavoxelSender* sender) :
Endpoint(node, new PacketRecord(), NULL), Endpoint(node, new PacketRecord(), NULL),
_sender(sender), _sender(sender),

View file

@ -77,14 +77,18 @@ public:
Q_INVOKABLE void start(); Q_INVOKABLE void start();
Q_INVOKABLE void addSession(QObject* session);
private slots: private slots:
void setData(const MetavoxelData& data) { _data = data; } void setData(const MetavoxelData& data) { _data = data; }
void sendDeltas(); void sendDeltas();
void removeSession(QObject* session);
private: private:
MetavoxelServer* _server; MetavoxelServer* _server;
QSet<MetavoxelSession*> _sessions;
QTimer _sendTimer; QTimer _sendTimer;
qint64 _lastSend; qint64 _lastSend;

View file

@ -10,6 +10,8 @@
// //
#include <QMutexLocker> #include <QMutexLocker>
#include <QReadLocker>
#include <QWriteLocker>
#include <QtDebug> #include <QtDebug>
#include <glm/gtx/transform.hpp> #include <glm/gtx/transform.hpp>
@ -45,11 +47,9 @@ void MetavoxelSystem::init() {
_pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute()); _pointBufferAttribute = AttributeRegistry::getInstance()->registerAttribute(new PointBufferAttribute());
} }
MetavoxelLOD MetavoxelSystem::getLOD() const { MetavoxelLOD MetavoxelSystem::getLOD() {
// the LOD threshold is temporarily tied to the avatar LOD parameter QReadLocker locker(&_lodLock);
const float BASE_LOD_THRESHOLD = 0.01f; return _lod;
return MetavoxelLOD(Application::getInstance()->getCamera()->getPosition(),
BASE_LOD_THRESHOLD * Menu::getInstance()->getAvatarLODDistanceMultiplier());
} }
class SpannerSimulateVisitor : public SpannerVisitor { class SpannerSimulateVisitor : public SpannerVisitor {
@ -77,8 +77,14 @@ bool SpannerSimulateVisitor::visit(Spanner* spanner, const glm::vec3& clipMinimu
} }
void MetavoxelSystem::simulate(float deltaTime) { void MetavoxelSystem::simulate(float deltaTime) {
// update the clients // update the lod
update(); {
// 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); SpannerSimulateVisitor spannerSimulateVisitor(deltaTime);
guide(spannerSimulateVisitor); guide(spannerSimulateVisitor);
@ -183,16 +189,20 @@ void MetavoxelSystem::render() {
guide(spannerRenderVisitor); guide(spannerRenderVisitor);
} }
void MetavoxelSystem::setClientPoints(const SharedNodePointer& node, const BufferPointVector& points) {
QMutexLocker locker(&node->getMutex());
MetavoxelSystemClient* client = static_cast<MetavoxelSystemClient*>(node->getLinkedData());
if (client) {
client->setPoints(points);
}
}
MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) { MetavoxelClient* MetavoxelSystem::createClient(const SharedNodePointer& node) {
return new MetavoxelSystemClient(node, this); return new MetavoxelSystemClient(node, _updater);
} }
void MetavoxelSystem::updateClient(MetavoxelClient* client) { MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater) :
MetavoxelClientManager::updateClient(client); MetavoxelClient(node, updater),
}
MetavoxelSystemClient::MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system) :
MetavoxelClient(node, system),
_pointCount(0) { _pointCount(0) {
_buffer.setUsagePattern(QOpenGLBuffer::StaticDraw); _buffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
@ -385,11 +395,14 @@ void PointBufferBuilder::run() {
qint64 now = QDateTime::currentMSecsSinceEpoch(); qint64 now = QDateTime::currentMSecsSinceEpoch();
PointCollector collector(_lod); PointCollector collector(_lod);
_data.guide(collector); _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); qDebug() << "collect" << (QDateTime::currentMSecsSinceEpoch() - now);
} }
void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) { void MetavoxelSystemClient::dataChanged(const MetavoxelData& oldData) {
MetavoxelClient::dataChanged(oldData);
/* BufferBuilder builder(_remoteDataLOD); /* BufferBuilder builder(_remoteDataLOD);
const AttributePointer& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute(); const AttributePointer& pointBufferAttribute = Application::getInstance()->getMetavoxels()->getPointBufferAttribute();
MetavoxelNode* oldRoot = oldData.getRoot(pointBufferAttribute); MetavoxelNode* oldRoot = oldData.getRoot(pointBufferAttribute);
@ -500,7 +513,7 @@ void SphereRenderer::render(float alpha, Mode mode, const glm::vec3& clipMinimum
return; return;
} }
// slight performance optimization: don't render if clip bounds are entirely within sphere // slight performance optimization: don't render if clip bounds are entirely within sphere
Sphere* sphere = static_cast<Sphere*>(parent()); Sphere* sphere = static_cast<Sphere*>(_spanner);
Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize)); Box clipBox(clipMinimum, clipMinimum + glm::vec3(clipSize, clipSize, clipSize));
for (int i = 0; i < Box::VERTEX_COUNT; i++) { for (int i = 0; i < Box::VERTEX_COUNT; i++) {
const float CLIP_PROPORTION = 0.95f; 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) { void SphereRenderer::renderUnclipped(float alpha, Mode mode) {
Sphere* sphere = static_cast<Sphere*>(parent()); Sphere* sphere = static_cast<Sphere*>(_spanner);
const QColor& color = sphere->getColor(); const QColor& color = sphere->getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha); glColor4f(color.redF(), color.greenF(), color.blueF(), color.alphaF() * alpha);
@ -533,6 +546,8 @@ StaticModelRenderer::StaticModelRenderer() :
} }
void StaticModelRenderer::init(Spanner* spanner) { void StaticModelRenderer::init(Spanner* spanner) {
SpannerRenderer::init(spanner);
_model->init(); _model->init();
StaticModel* staticModel = static_cast<StaticModel*>(spanner); StaticModel* staticModel = static_cast<StaticModel*>(spanner);
@ -554,7 +569,7 @@ void StaticModelRenderer::simulate(float deltaTime) {
const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents; const Extents& extents = _model->getGeometry()->getFBXGeometry().meshExtents;
bounds = Box(extents.minimum, extents.maximum); bounds = Box(extents.minimum, extents.maximum);
} }
static_cast<StaticModel*>(parent())->setBounds(glm::translate(_model->getTranslation()) * static_cast<StaticModel*>(_spanner)->setBounds(glm::translate(_model->getTranslation()) *
glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds); glm::mat4_cast(_model->getRotation()) * glm::scale(_model->getScale()) * bounds);
_model->simulate(deltaTime); _model->simulate(deltaTime);
} }

View file

@ -14,6 +14,7 @@
#include <QList> #include <QList>
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
#include <QReadWriteLock>
#include <QVector> #include <QVector>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -22,8 +23,12 @@
#include "renderer/ProgramObject.h" #include "renderer/ProgramObject.h"
class BufferPoint;
class Model; class Model;
typedef QVector<BufferPoint> BufferPointVector;
typedef QPair<BufferPointVector, BufferPointVector> BufferPointVectorPair;
/// Renders a metavoxel tree. /// Renders a metavoxel tree.
class MetavoxelSystem : public MetavoxelClientManager { class MetavoxelSystem : public MetavoxelClientManager {
Q_OBJECT Q_OBJECT
@ -34,15 +39,16 @@ public:
virtual void init(); virtual void init();
virtual MetavoxelLOD getLOD() const; virtual MetavoxelLOD getLOD();
void simulate(float deltaTime); void simulate(float deltaTime);
void render(); void render();
Q_INVOKABLE void setClientPoints(const SharedNodePointer& node, const BufferPointVector& points);
protected: protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node); virtual MetavoxelClient* createClient(const SharedNodePointer& node);
virtual void updateClient(MetavoxelClient* client);
private: private:
@ -50,6 +56,9 @@ private:
static int _pointScaleLocation; static int _pointScaleLocation;
AttributePointer _pointBufferAttribute; AttributePointer _pointBufferAttribute;
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
}; };
/// Describes contents of a point in a point buffer. /// Describes contents of a point in a point buffer.
@ -60,9 +69,6 @@ public:
quint8 normal[3]; quint8 normal[3];
}; };
typedef QVector<BufferPoint> BufferPointVector;
typedef QPair<BufferPointVector, BufferPointVector> BufferPointVectorPair;
Q_DECLARE_METATYPE(BufferPointVector) Q_DECLARE_METATYPE(BufferPointVector)
/// A client session associated with a single server. /// A client session associated with a single server.
@ -71,11 +77,11 @@ class MetavoxelSystemClient : public MetavoxelClient {
public: public:
MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelSystem* system); MetavoxelSystemClient(const SharedNodePointer& node, MetavoxelUpdater* updater);
void render(); void render();
Q_INVOKABLE void setPoints(const BufferPointVector& points); void setPoints(const BufferPointVector& points);
virtual int parseData(const QByteArray& packet); virtual int parseData(const QByteArray& packet);

View file

@ -48,7 +48,13 @@ Stats::Stats():
_pingStatsWidth(STATS_PING_MIN_WIDTH), _pingStatsWidth(STATS_PING_MIN_WIDTH),
_geoStatsWidth(STATS_GEO_MIN_WIDTH), _geoStatsWidth(STATS_GEO_MIN_WIDTH),
_voxelStatsWidth(STATS_VOXEL_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(); QGLWidget* glWidget = Application::getInstance()->getGLWidget();
resetWidth(glWidget->width(), 0); resetWidth(glWidget->width(), 0);
@ -487,36 +493,26 @@ void Stats::display(
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, downloads.str().c_str(), color);
int internal = 0, leaves = 0; QMetaObject::invokeMethod(Application::getInstance()->getMetavoxels()->getUpdater(), "getStats",
int sendProgress = 0, sendTotal = 0; Q_ARG(QObject*, this), Q_ARG(const QByteArray&, "setMetavoxelStats"));
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<MetavoxelSystemClient*>(node->getLinkedData());
if (client) {
client->getData().countNodes(internal, leaves, Application::getInstance()->getMetavoxels()->getLOD());
client->getSequencer().addReliableChannelStats(sendProgress, sendTotal, receiveProgress, receiveTotal);
}
}
}
stringstream nodes; stringstream nodes;
nodes << "Metavoxels: " << (internal + leaves); nodes << "Metavoxels: " << (_metavoxelInternal + _metavoxelLeaves);
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodes.str().c_str(), color);
stringstream nodeTypes; stringstream nodeTypes;
nodeTypes << "Internal: " << internal << " Leaves: " << leaves; nodeTypes << "Internal: " << _metavoxelInternal << " Leaves: " << _metavoxelLeaves;
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color); drawText(horizontalOffset, verticalOffset, scale, rotation, font, nodeTypes.str().c_str(), color);
if (sendTotal > 0 || receiveTotal > 0) { if (_metavoxelSendTotal > 0 || _metavoxelReceiveTotal > 0) {
stringstream reliableStats; stringstream reliableStats;
if (sendTotal > 0) { if (_metavoxelSendTotal > 0) {
reliableStats << "Upload: " << (sendProgress * 100 / sendTotal) << "% "; reliableStats << "Upload: " << (_metavoxelSendProgress * 100 / _metavoxelSendTotal) << "% ";
} }
if (receiveTotal > 0) { if (_metavoxelReceiveTotal > 0) {
reliableStats << "Download: " << (receiveProgress * 100 / receiveTotal) << "%"; reliableStats << "Download: " << (_metavoxelReceiveProgress * 100 / _metavoxelReceiveTotal) << "%";
} }
verticalOffset += STATS_PELS_PER_LINE; verticalOffset += STATS_PELS_PER_LINE;
drawText(horizontalOffset, verticalOffset, scale, rotation, font, reliableStats.str().c_str(), color); 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;
}

View file

@ -31,6 +31,10 @@ public:
void resetWidth(int width, int horizontalOffset); void resetWidth(int width, int horizontalOffset);
void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess); void display(const float* color, int horizontalOffset, float fps, int packetsPerSecond, int bytesPerSecond, int voxelPacketsToProcess);
bool includeTimingRecord(const QString& name); bool includeTimingRecord(const QString& name);
Q_INVOKABLE void setMetavoxelStats(int internal, int leaves, int sendProgress,
int sendTotal, int receiveProgress, int receiveTotal);
private: private:
static Stats* _sharedInstance; static Stats* _sharedInstance;
@ -45,6 +49,13 @@ private:
int _voxelStatsWidth; int _voxelStatsWidth;
int _lastHorizontalOffset; int _lastHorizontalOffset;
int _metavoxelInternal;
int _metavoxelLeaves;
int _metavoxelSendProgress;
int _metavoxelSendTotal;
int _metavoxelReceiveProgress;
int _metavoxelReceiveTotal;
}; };
#endif // hifi_Stats_h #endif // hifi_Stats_h

View file

@ -9,26 +9,31 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QDateTime>
#include <QReadLocker>
#include <QThread> #include <QThread>
#include <QWriteLocker>
#include "MetavoxelClientManager.h" #include "MetavoxelClientManager.h"
#include "MetavoxelMessages.h" #include "MetavoxelMessages.h"
void MetavoxelClientManager::init() { MetavoxelClientManager::MetavoxelClientManager() :
connect(NodeList::getInstance(), SIGNAL(nodeAdded(SharedNodePointer)), SLOT(maybeAttachClient(const SharedNodePointer&))); _updater(new MetavoxelUpdater(this)) {
connect(NodeList::getInstance(), SIGNAL(nodeKilled(SharedNodePointer)), SLOT(maybeDeleteClient(const SharedNodePointer&))); QThread* thread = new QThread(this);
_updater->moveToThread(thread);
connect(thread, &QThread::finished, _updater, &QObject::deleteLater);
thread->start();
QMetaObject::invokeMethod(_updater, "start");
} }
void MetavoxelClientManager::update() { MetavoxelClientManager::~MetavoxelClientManager() {
foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { _updater->thread()->quit();
if (node->getType() == NodeType::MetavoxelServer) { _updater->thread()->wait();
QMutexLocker locker(&node->getMutex()); }
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) { void MetavoxelClientManager::init() {
updateClient(client); connect(NodeList::getInstance(), &NodeList::nodeAdded, this, &MetavoxelClientManager::maybeAttachClient);
} connect(NodeList::getInstance(), &NodeList::nodeKilled, this, &MetavoxelClientManager::maybeDeleteClient);
}
}
} }
SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin, SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(const glm::vec3& origin,
@ -41,7 +46,7 @@ SharedObjectPointer MetavoxelClientManager::findFirstRaySpannerIntersection(cons
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData()); MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) { if (client) {
float clientDistance; float clientDistance;
SharedObjectPointer clientSpanner = client->getData().findFirstRaySpannerIntersection( SharedObjectPointer clientSpanner = client->getDataCopy().findFirstRaySpannerIntersection(
origin, direction, attribute, clientDistance); origin, direction, attribute, clientDistance);
if (clientSpanner && clientDistance < closestDistance) { if (clientSpanner && clientDistance < closestDistance) {
closestSpanner = clientSpanner; closestSpanner = clientSpanner;
@ -70,35 +75,26 @@ void MetavoxelClientManager::setSpanner(const SharedObjectPointer& object, bool
} }
void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { void MetavoxelClientManager::applyEdit(const MetavoxelEditMessage& edit, bool reliable) {
if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(_updater, "applyEdit", Q_ARG(const MetavoxelEditMessage&, edit), Q_ARG(bool, reliable));
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<MetavoxelClient*>(node->getLinkedData());
if (client) {
client->applyEdit(edit, reliable);
}
}
}
} }
MetavoxelLOD MetavoxelClientManager::getLOD() const { MetavoxelLOD MetavoxelClientManager::getLOD() {
return MetavoxelLOD(); return MetavoxelLOD();
} }
void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) { void MetavoxelClientManager::maybeAttachClient(const SharedNodePointer& node) {
if (node->getType() == NodeType::MetavoxelServer) { if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex()); 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) { void MetavoxelClientManager::maybeDeleteClient(const SharedNodePointer& node) {
if (node->getType() == NodeType::MetavoxelServer) { if (node->getType() == NodeType::MetavoxelServer) {
QMutexLocker locker(&node->getMutex()); // we assume the node is already locked
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData()); MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) { if (client) {
node->setLinkedData(NULL); node->setLinkedData(NULL);
@ -108,11 +104,7 @@ void MetavoxelClientManager::maybeDeleteClient(const SharedNodePointer& node) {
} }
MetavoxelClient* MetavoxelClientManager::createClient(const SharedNodePointer& node) { MetavoxelClient* MetavoxelClientManager::createClient(const SharedNodePointer& node) {
return new MetavoxelClient(node, this); return new MetavoxelClient(node, _updater);
}
void MetavoxelClientManager::updateClient(MetavoxelClient* client) {
client->update();
} }
void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) { void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) {
@ -121,15 +113,75 @@ void MetavoxelClientManager::guide(MetavoxelVisitor& visitor) {
QMutexLocker locker(&node->getMutex()); QMutexLocker locker(&node->getMutex());
MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData()); MetavoxelClient* client = static_cast<MetavoxelClient*>(node->getLinkedData());
if (client) { 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<MetavoxelClient*>(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<MetavoxelClient*>(client));
}
MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelUpdater* updater) :
Endpoint(node, new PacketRecord(), new PacketRecord()), Endpoint(node, new PacketRecord(), new PacketRecord()),
_manager(manager), _updater(updater),
_reliableDeltaChannel(NULL), _reliableDeltaChannel(NULL),
_reliableDeltaID(0) { _reliableDeltaID(0) {
@ -137,9 +189,9 @@ MetavoxelClient::MetavoxelClient(const SharedNodePointer& node, MetavoxelClientM
SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&))); SIGNAL(receivedMessage(const QVariant&, Bitstream&)), SLOT(handleMessage(const QVariant&, Bitstream&)));
} }
void MetavoxelClient::guide(MetavoxelVisitor& visitor) { MetavoxelData MetavoxelClient::getDataCopy() {
visitor.setLOD(_manager->getLOD()); QReadLocker locker(&_dataCopyLock);
_data.guide(visitor); return _dataCopy;
} }
void MetavoxelClient::applyEdit(const MetavoxelEditMessage& edit, bool reliable) { 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) { void MetavoxelClient::dataChanged(const MetavoxelData& oldData) {
// nothing by default // make thread-safe copy
QWriteLocker locker(&_dataCopyLock);
_dataCopy = _data;
} }
void MetavoxelClient::writeUpdateMessage(Bitstream& out) { void MetavoxelClient::writeUpdateMessage(Bitstream& out) {
ClientStateMessage state = { _manager->getLOD() }; ClientStateMessage state = { _updater->getLOD() };
out << QVariant::fromValue(state); out << QVariant::fromValue(state);
} }
@ -212,7 +266,7 @@ void MetavoxelClient::handleMessage(const QVariant& message, Bitstream& in) {
} }
PacketRecord* MetavoxelClient::maybeCreateSendRecord() const { PacketRecord* MetavoxelClient::maybeCreateSendRecord() const {
return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _manager->getLOD()); return new PacketRecord(_reliableDeltaChannel ? _reliableDeltaLOD : _updater->getLOD());
} }
PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const { PacketRecord* MetavoxelClient::maybeCreateReceiveRecord() const {

View file

@ -12,10 +12,14 @@
#ifndef hifi_MetavoxelClientManager_h #ifndef hifi_MetavoxelClientManager_h
#define hifi_MetavoxelClientManager_h #define hifi_MetavoxelClientManager_h
#include <QReadWriteLock>
#include <QTimer>
#include "Endpoint.h" #include "Endpoint.h"
class MetavoxelClient; class MetavoxelClient;
class MetavoxelEditMessage; class MetavoxelEditMessage;
class MetavoxelUpdater;
/// Manages the set of connected metavoxel clients. /// Manages the set of connected metavoxel clients.
class MetavoxelClientManager : public QObject { class MetavoxelClientManager : public QObject {
@ -23,8 +27,12 @@ class MetavoxelClientManager : public QObject {
public: public:
MetavoxelClientManager();
virtual ~MetavoxelClientManager();
virtual void init(); virtual void init();
void update();
MetavoxelUpdater* getUpdater() const { return _updater; }
SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction, SharedObjectPointer findFirstRaySpannerIntersection(const glm::vec3& origin, const glm::vec3& direction,
const AttributePointer& attribute, float& distance); const AttributePointer& attribute, float& distance);
@ -35,7 +43,8 @@ public:
Q_INVOKABLE void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); 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: private slots:
@ -45,9 +54,46 @@ private slots:
protected: protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node); virtual MetavoxelClient* createClient(const SharedNodePointer& node);
virtual void updateClient(MetavoxelClient* client);
void guide(MetavoxelVisitor& visitor); 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<MetavoxelClient*> _clients;
QTimer _sendTimer;
qint64 _lastSend;
MetavoxelLOD _lod;
}; };
/// Base class for metavoxel clients. /// Base class for metavoxel clients.
@ -56,11 +102,13 @@ class MetavoxelClient : public Endpoint {
public: 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; }
void guide(MetavoxelVisitor& visitor); /// Returns a copy of the most recent data. This function *is* thread-safe.
MetavoxelData getDataCopy();
void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false); void applyEdit(const MetavoxelEditMessage& edit, bool reliable = false);
@ -74,7 +122,7 @@ protected:
virtual PacketRecord* maybeCreateSendRecord() const; virtual PacketRecord* maybeCreateSendRecord() const;
virtual PacketRecord* maybeCreateReceiveRecord() const; virtual PacketRecord* maybeCreateReceiveRecord() const;
MetavoxelClientManager* _manager; MetavoxelUpdater* _updater;
MetavoxelData _data; MetavoxelData _data;
MetavoxelData _remoteData; MetavoxelData _remoteData;
MetavoxelLOD _remoteDataLOD; MetavoxelLOD _remoteDataLOD;
@ -82,6 +130,9 @@ protected:
ReliableChannel* _reliableDeltaChannel; ReliableChannel* _reliableDeltaChannel;
MetavoxelLOD _reliableDeltaLOD; MetavoxelLOD _reliableDeltaLOD;
int _reliableDeltaID; int _reliableDeltaID;
MetavoxelData _dataCopy;
QReadWriteLock _dataCopyLock;
}; };
#endif // hifi_MetavoxelClientManager_h #endif // hifi_MetavoxelClientManager_h

View file

@ -1899,7 +1899,7 @@ SpannerRenderer* Spanner::getRenderer() {
metaObject = &SpannerRenderer::staticMetaObject; metaObject = &SpannerRenderer::staticMetaObject;
} }
_renderer = static_cast<SpannerRenderer*>(metaObject->newInstance()); _renderer = static_cast<SpannerRenderer*>(metaObject->newInstance());
_renderer->setParent(this); connect(this, &QObject::destroyed, _renderer, &QObject::deleteLater);
_renderer->init(this); _renderer->init(this);
} }
return _renderer; return _renderer;
@ -1920,7 +1920,7 @@ SpannerRenderer::SpannerRenderer() {
} }
void SpannerRenderer::init(Spanner* spanner) { void SpannerRenderer::init(Spanner* spanner) {
// nothing by default _spanner = spanner;
} }
void SpannerRenderer::simulate(float deltaTime) { void SpannerRenderer::simulate(float deltaTime) {

View file

@ -599,6 +599,10 @@ public:
virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize); virtual void render(float alpha, Mode mode, const glm::vec3& clipMinimum, float clipSize);
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
const glm::vec3& clipMinimum, float clipSize, float& distance) const; const glm::vec3& clipMinimum, float clipSize, float& distance) const;
protected:
Spanner* _spanner;
}; };
/// An object with a 3D transform. /// An object with a 3D transform.