diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 46f4d233c2..16674efcbf 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -95,12 +95,19 @@ void Agent::readPendingDatagrams() { // also give our local particle tree a chance to remap any internal locally created particles _particleViewer.getTree()->handleAddParticleResponse(receivedPacket); + // Make sure our Node and NodeList knows we've heard from this node. + SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket); + sourceNode->setLastHeardMicrostamp(usecTimestampNow()); + } else if (datagramPacketType == PacketTypeParticleData || datagramPacketType == PacketTypeParticleErase || datagramPacketType == PacketTypeOctreeStats || datagramPacketType == PacketTypeVoxelData ) { + // Make sure our Node and NodeList knows we've heard from this node. SharedNodePointer sourceNode = nodeList->sendingNodeForPacket(receivedPacket); + sourceNode->setLastHeardMicrostamp(usecTimestampNow()); + QByteArray mutablePacket = receivedPacket; ssize_t messageLength = mutablePacket.size(); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 978f090ec2..970b6518ec 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -200,13 +200,13 @@ void AssignmentClient::assignmentCompleted() { qDebug("Assignment finished or never started - waiting for new assignment."); NodeList* nodeList = NodeList::getInstance(); - + // have us handle incoming NodeList datagrams again disconnect(&nodeList->getNodeSocket(), 0, _currentAssignment, 0); connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, this, &AssignmentClient::readPendingDatagrams); _currentAssignment = NULL; - + // reset our NodeList by switching back to unassigned and clearing the list nodeList->setOwnerType(NodeType::Unassigned); nodeList->reset(); diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.h b/assignment-client/src/octree/OctreeInboundPacketProcessor.h index 237a22d954..e2ec1b5c50 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.h +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.h @@ -44,7 +44,7 @@ typedef std::map::iterator NodeToSenderStatsMapIterato /// Handles processing of incoming network packets for the voxel-server. As with other ReceivedPacketProcessor classes /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() class OctreeInboundPacketProcessor : public ReceivedPacketProcessor { - + Q_OBJECT public: OctreeInboundPacketProcessor(OctreeServer* myServer); diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index 3d5fbefc1f..a1922f980d 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -35,7 +35,8 @@ OctreeQueryNode::OctreeQueryNode() : _lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _lodChanged(false), _lodInitialized(false), - _sequenceNumber(0) + _sequenceNumber(0), + _lastRootTimestamp(0) { } diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index e48b7aaff4..20fa2c5858 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -23,6 +23,7 @@ class OctreeSendThread; class OctreeServer; class OctreeQueryNode : public OctreeQuery { + Q_OBJECT public: OctreeQueryNode(); virtual ~OctreeQueryNode(); @@ -85,6 +86,12 @@ public: void dumpOutOfView(); + quint64 getLastRootTimestamp() const { return _lastRootTimestamp; } + void setLastRootTimestamp(quint64 timestamp) { _lastRootTimestamp = timestamp; } + unsigned int getlastOctreePacketLength() const { return _lastOctreePacketLength; } + int getDuplicatePacketCount() const { return _duplicatePacketCount; } + + private: OctreeQueryNode(const OctreeQueryNode &); OctreeQueryNode& operator= (const OctreeQueryNode&); @@ -119,6 +126,7 @@ private: bool _lodInitialized; OCTREE_PACKET_SEQUENCE _sequenceNumber; + quint64 _lastRootTimestamp; }; #endif /* defined(__hifi__OctreeQueryNode__) */ diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 85839bc53f..caa729e340 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -23,18 +23,28 @@ OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer _packetData(), _nodeMissingCount(0) { - qDebug() << "client connected - starting sending thread"; + QString safeServerName("Octree"); + if (_myServer) { + safeServerName = _myServer->getMyServerName(); + } + qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client connected " + "- starting sending thread [" << this << "]"; + OctreeServer::clientConnected(); } OctreeSendThread::~OctreeSendThread() { - qDebug() << "client disconnected - ending sending thread"; + QString safeServerName("Octree"); + if (_myServer) { + safeServerName = _myServer->getMyServerName(); + } + qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " + "- ending sending thread [" << this << "]"; OctreeServer::clientDisconnected(); } bool OctreeSendThread::process() { - const int MAX_NODE_MISSING_CHECKS = 10; if (_nodeMissingCount > MAX_NODE_MISSING_CHECKS) { qDebug() << "our target node:" << _nodeUUID << "has been missing the last" << _nodeMissingCount @@ -57,18 +67,11 @@ bool OctreeSendThread::process() { // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - printf("nodeData->updateCurrentViewFrustum() changed=%s\n", debug::valueOf(viewFrustumChanged)); - } packetDistributor(node, nodeData, viewFrustumChanged); } } else { _nodeMissingCount++; } - } else { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("OctreeSendThread::process() waiting for isInitialLoadComplete()"); - } } // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap @@ -81,10 +84,6 @@ bool OctreeSendThread::process() { PerformanceWarning warn(false,"OctreeSendThread... usleep()",false,&_usleepTime,&_usleepCalls); usleep(usecToSleep); } else { - if (true || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug() << "Last send took too much time (" << (elapsed / USECS_PER_MSEC) - <<" msecs), barely sleeping 1 usec!\n"; - } const int MIN_USEC_TO_SLEEP = 1; usleep(MIN_USEC_TO_SLEEP); } @@ -114,7 +113,7 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer quint64 lockWaitEnd = usecTimestampNow(); float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); OctreeServer::trackNodeWaitTime(lockWaitElapsedUsec); - + const HifiSockAddr* nodeAddress = node->getActiveSocket(); if (!nodeAddress) { return packetsSent; // without sending... @@ -235,8 +234,6 @@ int OctreeSendThread::handlePacketSend(const SharedNodePointer& node, OctreeQuer /// Version of voxel distributor that sends the deepest LOD level at once int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged) { - bool forceDebugging = false; - int truePacketsSent = 0; int trueBytesSent = 0; int packetsSentThisInterval = 0; @@ -259,72 +256,22 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // then let's just send that waiting packet. if (!nodeData->getCurrentPacketFormatMatches()) { if (nodeData->isPacketWaiting()) { - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("about to call handlePacketSend() .... line: %d -- format change " - "wantColor=%s wantCompression=%s SENDING PARTIAL PACKET! currentPacketIsColor=%s " - "currentPacketIsCompressed=%s", - __LINE__, - debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); - } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } else { - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("wantColor=%s wantCompression=%s FIXING HEADER! currentPacketIsColor=%s currentPacketIsCompressed=%s", - debug::valueOf(wantColor), debug::valueOf(wantCompression), - debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(nodeData->getCurrentPacketIsCompressed()) ); - } nodeData->resetOctreePacket(); } int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (wantCompression) { targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d", __LINE__, - debug::valueOf(wantCompression), targetSize); - } - _packetData.changeSettings(wantCompression, targetSize); } - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("wantColor/isColor=%s/%s wantCompression/isCompressed=%s/%s viewFrustumChanged=%s, getWantLowResMoving()=%s", - debug::valueOf(wantColor), debug::valueOf(nodeData->getCurrentPacketIsColor()), - debug::valueOf(wantCompression), debug::valueOf(nodeData->getCurrentPacketIsCompressed()), - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->getWantLowResMoving())); - } - const ViewFrustum* lastViewFrustum = wantDelta ? &nodeData->getLastKnownViewFrustum() : NULL; - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("packetDistributor() viewFrustumChanged=%s, nodeBag.isEmpty=%s, viewSent=%s", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty()), - debug::valueOf(nodeData->getViewSent()) - ); - } - // If the current view frustum has changed OR we have nothing to send, then search against // the current view frustum for things to send. if (viewFrustumChanged || nodeData->nodeBag.isEmpty()) { - quint64 now = usecTimestampNow(); - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("(viewFrustumChanged=%s || nodeData->nodeBag.isEmpty() =%s)...", - debug::valueOf(viewFrustumChanged), debug::valueOf(nodeData->nodeBag.isEmpty())); - if (nodeData->getLastTimeBagEmpty() > 0) { - float elapsedSceneSend = (now - nodeData->getLastTimeBagEmpty()) / 1000000.0f; - if (viewFrustumChanged) { - qDebug("viewFrustumChanged resetting after elapsed time to send scene = %f seconds", elapsedSceneSend); - } else { - qDebug("elapsed time to send scene = %f seconds", elapsedSceneSend); - } - qDebug("[ occlusionCulling:%s, wantDelta:%s, wantColor:%s ]", - debug::valueOf(nodeData->getWantOcclusionCulling()), debug::valueOf(wantDelta), - debug::valueOf(wantColor)); - } - } // if our view has changed, we need to reset these things... if (viewFrustumChanged) { @@ -342,44 +289,25 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // track completed scenes and send out the stats packet accordingly nodeData->stats.sceneCompleted(); - ::endSceneSleepTime = _usleepTime; - unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; + nodeData->setLastRootTimestamp(_myServer->getOctree()->getRoot()->getLastChanged()); - unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); - unsigned long elapsedTime = nodeData->stats.getElapsedTime(); + // TODO: add these to stats page + //::endSceneSleepTime = _usleepTime; + //unsigned long sleepTime = ::endSceneSleepTime - ::startSceneSleepTime; + //unsigned long encodeTime = nodeData->stats.getTotalEncodeTime(); + //unsigned long elapsedTime = nodeData->stats.getElapsedTime(); - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("about to call handlePacketSend() .... line: %d -- completed scene", __LINE__ ); - } int packetsJustSent = handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval += packetsJustSent; - if (forceDebugging) { - qDebug("packetsJustSent=%d packetsSentThisInterval=%d", packetsJustSent, packetsSentThisInterval); - } - - if (forceDebugging || _myServer->wantsDebugSending()) { - qDebug() << "Scene completed at " << usecTimestampNow() - << "encodeTime:" << encodeTime - << " sleepTime:" << sleepTime - << " elapsed:" << elapsedTime - << " Packets:" << _totalPackets - << " Bytes:" << _totalBytes - << " Wasted:" << _totalWastedBytes; - } // If we're starting a full scene, then definitely we want to empty the nodeBag if (isFullScene) { nodeData->nodeBag.deleteAll(); } - if (forceDebugging || _myServer->wantsDebugSending()) { - qDebug() << "Scene started at " << usecTimestampNow() - << " Packets:" << _totalPackets - << " Bytes:" << _totalBytes - << " Wasted:" << _totalWastedBytes; - } - - ::startSceneSleepTime = _usleepTime; + // TODO: add these to stats page + //::startSceneSleepTime = _usleepTime; + // start tracking our stats nodeData->stats.sceneStarted(isFullScene, viewFrustumChanged, _myServer->getOctree()->getRoot(), _myServer->getJurisdiction()); @@ -398,60 +326,79 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue if (!nodeData->nodeBag.isEmpty()) { int bytesWritten = 0; quint64 start = usecTimestampNow(); - quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; - quint64 startCompressCalls = OctreePacketData::getCompressContentCalls(); + + // TODO: add these to stats page + //quint64 startCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; + //quint64 startCompressCalls = OctreePacketData::getCompressContentCalls(); int clientMaxPacketsPerInterval = std::max(1,(nodeData->getMaxOctreePacketsPerSecond() / INTERVALS_PER_SECOND)); int maxPacketsPerInterval = std::min(clientMaxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval()); - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } - int extraPackingAttempts = 0; bool completedScene = false; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval) { - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, _myServer->getPacketsPerClientPerInterval(), - nodeData->getMaxOctreePacketsPerSecond(), clientMaxPacketsPerInterval); - } + float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; + float encodeElapsedUsec = OctreeServer::SKIP_TIME; + float compressAndWriteElapsedUsec = OctreeServer::SKIP_TIME; + float packetSendingElapsedUsec = OctreeServer::SKIP_TIME; + + quint64 startInside = usecTimestampNow(); bool lastNodeDidntFit = false; // assume each node fits if (!nodeData->nodeBag.isEmpty()) { OctreeElement* subTree = nodeData->nodeBag.extract(); + + /* TODO: Looking for a way to prevent locking and encoding a tree that is not + // going to result in any packets being sent... + // + // If our node is root, and the root hasn't changed, and our view hasn't changed, + // and we've already seen at least one duplicate packet, then we probably don't need + // to lock the tree and encode, because the result should be that no bytes will be + // encoded, and this will be a duplicate packet from the last one we sent... + OctreeElement* root = _myServer->getOctree()->getRoot(); + bool skipEncode = false; + if ( + (subTree == root) + && (nodeData->getLastRootTimestamp() == root->getLastChanged()) + && !viewFrustumChanged + && (nodeData->getDuplicatePacketCount() > 0) + ) { + qDebug() << "is root, root not changed, view not changed, already seen a duplicate!" + << "Can we skip it?"; + skipEncode = true; + } + */ + bool wantOcclusionCulling = nodeData->getWantOcclusionCulling(); CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; - + float voxelSizeScale = nodeData->getOctreeSizeScale(); int boundaryLevelAdjustClient = nodeData->getBoundaryLevelAdjust(); - + int boundaryLevelAdjust = boundaryLevelAdjustClient + (viewFrustumChanged && nodeData->getWantLowResMoving() - ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); - + ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST); + EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, nodeData->getLastTimeBagEmpty(), isFullScene, &nodeData->stats, _myServer->getJurisdiction()); - + // TODO: should this include the lock time or not? This stat is sent down to the client, + // it seems like it may be a good idea to include the lock time as part of the encode time + // are reported to client. Since you can encode without the lock + nodeData->stats.encodeStarted(); + quint64 lockWaitStart = usecTimestampNow(); _myServer->getOctree()->lockForRead(); quint64 lockWaitEnd = usecTimestampNow(); - float lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); - OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec); - - nodeData->stats.encodeStarted(); + lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); quint64 encodeStart = usecTimestampNow(); bytesWritten = _myServer->getOctree()->encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); quint64 encodeEnd = usecTimestampNow(); - int encodeElapsedMsec = (encodeEnd - encodeStart)/USECS_PER_MSEC; - OctreeServer::trackEncodeTime(encodeElapsedMsec); - + encodeElapsedUsec = (float)(encodeEnd - encodeStart); + // If after calling encodeTreeBitstream() there are no nodes left to send, then we know we've // sent the entire scene. We want to know this below so we'll actually write this content into // the packet and send it @@ -490,6 +437,9 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // mean we should send the previous packet contents and reset it. if (completedScene || lastNodeDidntFit) { if (_packetData.hasContent()) { + + quint64 compressAndWriteStart = usecTimestampNow(); + // if for some reason the finalized size is greater than our available size, then probably the "compressed" // form actually inflated beyond our padding, and in this case we will send the current packet, then // write to out new packet... @@ -498,27 +448,19 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue if (writtenSize > nodeData->getAvailable()) { - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug("about to call handlePacketSend() .... line: %d -- " - "writtenSize[%d] > available[%d] too big, sending packet as is.", - __LINE__, writtenSize, nodeData->getAvailable()); - } packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); } - if (forceDebugging || (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug())) { - qDebug(">>>>>> calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%u", - nodeData->getAvailable(), _packetData.getFinalizedSize(), - _packetData.getUncompressedSize(), _packetData.getTargetSize()); - } nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); extraPackingAttempts = 0; + quint64 compressAndWriteEnd = usecTimestampNow(); + compressAndWriteElapsedUsec = (float)(compressAndWriteEnd - compressAndWriteStart); } // If we're not running compressed, then we know we can just send now. Or if we're running compressed, but // the packet doesn't have enough space to bother attempting to pack more... bool sendNow = true; - + if (nodeData->getCurrentPacketIsCompressed() && nodeData->getAvailable() >= MINIMUM_ATTEMPT_MORE_PACKING && extraPackingAttempts <= REASONABLE_NUMBER_OF_PACKING_ATTEMPTS) { @@ -527,10 +469,11 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int targetSize = MAX_OCTREE_PACKET_DATA_SIZE; if (sendNow) { - if (forceDebugging) { - qDebug("about to call handlePacketSend() .... line: %d -- sendNow = TRUE", __LINE__); - } + quint64 packetSendingStart = usecTimestampNow(); packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + quint64 packetSendingEnd = usecTimestampNow(); + packetSendingElapsedUsec = (float)(packetSendingEnd - packetSendingStart); + if (wantCompression) { targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE); } @@ -543,12 +486,17 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue // a larger compressed size then uncompressed size targetSize = nodeData->getAvailable() - sizeof(OCTREE_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; } - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d",__LINE__, - debug::valueOf(nodeData->getWantCompression()), targetSize); - } _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset + } + OctreeServer::trackTreeWaitTime(lockWaitElapsedUsec); + OctreeServer::trackEncodeTime(encodeElapsedUsec); + OctreeServer::trackCompressAndWriteTime(compressAndWriteElapsedUsec); + OctreeServer::trackPacketSendingTime(packetSendingElapsedUsec); + + quint64 endInside = usecTimestampNow(); + quint64 elapsedInsideUsecs = endInside - startInside; + OctreeServer::trackInsideTime((float)elapsedInsideUsecs); } @@ -566,53 +514,21 @@ int OctreeSendThread::packetDistributor(const SharedNodePointer& node, OctreeQue int elapsedmsec = (end - start)/USECS_PER_MSEC; OctreeServer::trackLoopTime(elapsedmsec); - quint64 endCompressCalls = OctreePacketData::getCompressContentCalls(); - int elapsedCompressCalls = endCompressCalls - startCompressCalls; - - quint64 endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; - int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; - - if (elapsedmsec > 100) { - if (elapsedmsec > 1000) { - int elapsedsec = (end - start)/1000000; - qDebug("WARNING! packetLoop() took %d seconds [%d milliseconds %d calls in compress] " - "to generate %d bytes in %d packets %d nodes still to send", - elapsedsec, elapsedCompressTimeMsecs, elapsedCompressCalls, - trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } else { - qDebug("WARNING! packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " - "to generate %d bytes in %d packets, %d nodes still to send", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, - trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } - } else if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("packetLoop() took %d milliseconds [%d milliseconds %d calls in compress] " - "to generate %d bytes in %d packets, %d nodes still to send", - elapsedmsec, elapsedCompressTimeMsecs, elapsedCompressCalls, - trueBytesSent, truePacketsSent, nodeData->nodeBag.count()); - } + // TODO: add these to stats page + //quint64 endCompressCalls = OctreePacketData::getCompressContentCalls(); + //int elapsedCompressCalls = endCompressCalls - startCompressCalls; + //quint64 endCompressTimeMsecs = OctreePacketData::getCompressContentTime() / 1000; + //int elapsedCompressTimeMsecs = endCompressTimeMsecs - startCompressTimeMsecs; // if after sending packets we've emptied our bag, then we want to remember that we've sent all // the voxels from the current view frustum if (nodeData->nodeBag.isEmpty()) { nodeData->updateLastKnownViewFrustum(); nodeData->setViewSent(true); - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - nodeData->map.printStats(); - } nodeData->map.erase(); // It would be nice if we could save this, and only reset it when the view frustum changes } - if (_myServer->wantsDebugSending() && _myServer->wantsVerboseDebug()) { - qDebug("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d " - "server PPI=%d nodePPS=%d nodePPI=%d", - truePacketsSent, packetsSentThisInterval, maxPacketsPerInterval, - _myServer->getPacketsPerClientPerInterval(), nodeData->getMaxOctreePacketsPerSecond(), - clientMaxPacketsPerInterval); - } - } // end if bag wasn't empty, and so we sent stuff... return truePacketsSent; } - diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 081e7f411f..39c27911b0 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -19,6 +19,7 @@ /// Threaded processor for sending voxel packets to a single client class OctreeSendThread : public GenericThread { + Q_OBJECT public: OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); virtual ~OctreeSendThread(); diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index c31beb7831..7e6ffe52da 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -20,10 +20,150 @@ OctreeServer* OctreeServer::_instance = NULL; int OctreeServer::_clientCount = 0; -SimpleMovingAverage OctreeServer::_averageLoopTime(10000); -SimpleMovingAverage OctreeServer::_averageEncodeTime(10000); -SimpleMovingAverage OctreeServer::_averageTreeWaitTime(10000); -SimpleMovingAverage OctreeServer::_averageNodeWaitTime(10000); +const int MOVING_AVERAGE_SAMPLE_COUNTS = 1000000; + +float OctreeServer::SKIP_TIME = -1.0f; // use this for trackXXXTime() calls for non-times + +SimpleMovingAverage OctreeServer::_averageLoopTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageInsideTime(MOVING_AVERAGE_SAMPLE_COUNTS); + +SimpleMovingAverage OctreeServer::_averageEncodeTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageShortEncodeTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageLongEncodeTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageExtraLongEncodeTime(MOVING_AVERAGE_SAMPLE_COUNTS); +int OctreeServer::_extraLongEncode = 0; +int OctreeServer::_longEncode = 0; +int OctreeServer::_shortEncode = 0; +int OctreeServer::_noEncode = 0; + +SimpleMovingAverage OctreeServer::_averageTreeWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageTreeShortWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageTreeLongWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageTreeExtraLongWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); +int OctreeServer::_extraLongTreeWait = 0; +int OctreeServer::_longTreeWait = 0; +int OctreeServer::_shortTreeWait = 0; +int OctreeServer::_noTreeWait = 0; + +SimpleMovingAverage OctreeServer::_averageNodeWaitTime(MOVING_AVERAGE_SAMPLE_COUNTS); + +SimpleMovingAverage OctreeServer::_averageCompressAndWriteTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageShortCompressTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageLongCompressTime(MOVING_AVERAGE_SAMPLE_COUNTS); +SimpleMovingAverage OctreeServer::_averageExtraLongCompressTime(MOVING_AVERAGE_SAMPLE_COUNTS); +int OctreeServer::_extraLongCompress = 0; +int OctreeServer::_longCompress = 0; +int OctreeServer::_shortCompress = 0; +int OctreeServer::_noCompress = 0; + +SimpleMovingAverage OctreeServer::_averagePacketSendingTime(MOVING_AVERAGE_SAMPLE_COUNTS); +int OctreeServer::_noSend = 0; + + +void OctreeServer::resetSendingStats() { + _averageLoopTime.reset(); + + _averageEncodeTime.reset(); + _averageShortEncodeTime.reset(); + _averageLongEncodeTime.reset(); + _averageExtraLongEncodeTime.reset(); + _extraLongEncode = 0; + _longEncode = 0; + _shortEncode = 0; + _noEncode = 0; + + _averageInsideTime.reset(); + _averageTreeWaitTime.reset(); + _averageTreeShortWaitTime.reset(); + _averageTreeLongWaitTime.reset(); + _averageTreeExtraLongWaitTime.reset(); + _extraLongTreeWait = 0; + _longTreeWait = 0; + _shortTreeWait = 0; + _noTreeWait = 0; + + _averageNodeWaitTime.reset(); + + _averageCompressAndWriteTime.reset(); + _averageShortCompressTime.reset(); + _averageLongCompressTime.reset(); + _averageExtraLongCompressTime.reset(); + _extraLongCompress = 0; + _longCompress = 0; + _shortCompress = 0; + _noCompress = 0; + + _averagePacketSendingTime.reset(); + _noSend = 0; +} + +void OctreeServer::trackEncodeTime(float time) { + const float MAX_SHORT_TIME = 10.0f; + const float MAX_LONG_TIME = 100.0f; + + if (time == SKIP_TIME) { + _noEncode++; + time = 0.0f; + } else if (time <= MAX_SHORT_TIME) { + _shortEncode++; + _averageShortEncodeTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longEncode++; + _averageLongEncodeTime.updateAverage(time); + } else { + _extraLongEncode++; + _averageExtraLongEncodeTime.updateAverage(time); + } + _averageEncodeTime.updateAverage(time); +} + +void OctreeServer::trackTreeWaitTime(float time) { + const float MAX_SHORT_TIME = 10.0f; + const float MAX_LONG_TIME = 100.0f; + if (time == SKIP_TIME) { + _noTreeWait++; + time = 0.0f; + } else if (time <= MAX_SHORT_TIME) { + _shortTreeWait++; + _averageTreeShortWaitTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longTreeWait++; + _averageTreeLongWaitTime.updateAverage(time); + } else { + _extraLongTreeWait++; + _averageTreeExtraLongWaitTime.updateAverage(time); + } + _averageTreeWaitTime.updateAverage(time); +} + +void OctreeServer::trackCompressAndWriteTime(float time) { + const float MAX_SHORT_TIME = 10.0f; + const float MAX_LONG_TIME = 100.0f; + if (time == SKIP_TIME) { + _noCompress++; + time = 0.0f; + } else if (time <= MAX_SHORT_TIME) { + _shortCompress++; + _averageShortCompressTime.updateAverage(time); + } else if (time <= MAX_LONG_TIME) { + _longCompress++; + _averageLongCompressTime.updateAverage(time); + } else { + _extraLongCompress++; + _averageExtraLongCompressTime.updateAverage(time); + } + _averageCompressAndWriteTime.updateAverage(time); +} + +void OctreeServer::trackPacketSendingTime(float time) { + if (time == SKIP_TIME) { + _noSend++; + time = 0.0f; + } + _averagePacketSendingTime.updateAverage(time); +} + + void OctreeServer::attachQueryNodeToNode(Node* newNode) { if (!newNode->getLinkedData()) { @@ -55,9 +195,11 @@ OctreeServer::OctreeServer(const QByteArray& packet) : { _instance = this; _averageLoopTime.updateAverage(0); + qDebug() << "Octree server starting... [" << this << "]"; } OctreeServer::~OctreeServer() { + qDebug() << qPrintable(_safeServerName) << "server shutting down... [" << this << "]"; if (_parsedArgV) { for (int i = 0; i < _argc; i++) { delete[] _parsedArgV[i]; @@ -82,7 +224,7 @@ OctreeServer::~OctreeServer() { delete _jurisdiction; _jurisdiction = NULL; - qDebug() << "OctreeServer::~OctreeServer()... DONE"; + qDebug() << qPrintable(_safeServerName) << "server DONE shutting down... [" << this << "]"; } void OctreeServer::initHTTPManager(int port) { @@ -121,6 +263,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& showStats = true; } else if (path == "/resetStats") { _octreeInboundPacketProcessor->resetStats(); + resetSendingStats(); showStats = true; } } @@ -255,7 +398,9 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& statsString += "\r\n"; // display outbound packet stats - statsString += QString("%1 Outbound Packet Statistics...\r\n").arg(getMyServerName()); + statsString += QString("%1 Outbound Packet Statistics... " + "[RESET]\r\n").arg(getMyServerName()); + quint64 totalOutboundPackets = OctreeSendThread::_totalPackets; quint64 totalOutboundBytes = OctreeSendThread::_totalBytes; quint64 totalWastedBytes = OctreeSendThread::_totalWastedBytes; @@ -263,7 +408,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& quint64 totalBytesOfBitMasks = OctreePacketData::getTotalBytesOfBitMasks(); quint64 totalBytesOfColor = OctreePacketData::getTotalBytesOfColor(); - const int COLUMN_WIDTH = 10; + const int COLUMN_WIDTH = 19; statsString += QString(" Configured Max PPS/Client: %1 pps/client\r\n") .arg(locale.toString((uint)getPacketsPerClientPerSecond()).rightJustified(COLUMN_WIDTH, ' ')); statsString += QString(" Configured Max PPS/Server: %1 pps/server\r\n\r\n") @@ -272,21 +417,130 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QString& .arg(locale.toString((uint)getCurrentClientCount()).rightJustified(COLUMN_WIDTH, ' ')); float averageLoopTime = getAverageLoopTime(); - statsString += QString().sprintf(" Average packetLoop() time: %5.2f msecs\r\n", averageLoopTime); - qDebug() << "averageLoopTime=" << averageLoopTime; + statsString += QString().sprintf(" Average packetLoop() time: %7.2f msecs\r\n", averageLoopTime); - float averageEncodeTime = getAverageEncodeTime(); - statsString += QString().sprintf(" Average encode time: %5.2f msecs\r\n", averageEncodeTime); - qDebug() << "averageEncodeTime=" << averageEncodeTime; + float averageInsideTime = getAverageInsideTime(); + statsString += QString().sprintf(" Average 'inside' time: %9.2f usecs\r\n\r\n", averageInsideTime); + int allWaitTimes = _extraLongTreeWait +_longTreeWait + _shortTreeWait + _noTreeWait; float averageTreeWaitTime = getAverageTreeWaitTime(); - statsString += QString().sprintf(" Average tree lock wait time: %7.2f usecs\r\n", averageTreeWaitTime); - qDebug() << "averageTreeWaitTime=" << averageTreeWaitTime; + statsString += QString().sprintf(" Average tree lock wait time:" + " %9.2f usecs samples: %12d \r\n", + averageTreeWaitTime, allWaitTimes); + float zeroVsTotal = (allWaitTimes > 0) ? ((float)_noTreeWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" No Lock Wait:" + " (%6.2f%%) samples: %12d \r\n", + zeroVsTotal * AS_PERCENT, _noTreeWait); + + float shortVsTotal = (allWaitTimes > 0) ? ((float)_shortTreeWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" Avg tree lock short wait time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageTreeShortWaitTime.getAverage(), + shortVsTotal * AS_PERCENT, _shortTreeWait); + + float longVsTotal = (allWaitTimes > 0) ? ((float)_longTreeWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" Avg tree lock long wait time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageTreeLongWaitTime.getAverage(), + longVsTotal * AS_PERCENT, _longTreeWait); + + float extraLongVsTotal = (allWaitTimes > 0) ? ((float)_extraLongTreeWait / (float)allWaitTimes) : 0.0f; + statsString += QString().sprintf(" Avg tree lock extra long wait time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n", + _averageTreeExtraLongWaitTime.getAverage(), + extraLongVsTotal * AS_PERCENT, _extraLongTreeWait); + + float averageEncodeTime = getAverageEncodeTime(); + statsString += QString().sprintf(" Average encode time: %9.2f usecs\r\n", averageEncodeTime); + + int allEncodeTimes = _noEncode + _shortEncode + _longEncode + _extraLongEncode; + + float zeroVsTotalEncode = (allEncodeTimes > 0) ? ((float)_noEncode / (float)allEncodeTimes) : 0.0f; + statsString += QString().sprintf(" No Encode:" + " (%6.2f%%) samples: %12d \r\n", + zeroVsTotalEncode * AS_PERCENT, _noEncode); + + float shortVsTotalEncode = (allEncodeTimes > 0) ? ((float)_shortEncode / (float)allEncodeTimes) : 0.0f; + statsString += QString().sprintf(" Avg short encode time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageShortEncodeTime.getAverage(), + shortVsTotalEncode * AS_PERCENT, _shortEncode); + + float longVsTotalEncode = (allEncodeTimes > 0) ? ((float)_longEncode / (float)allEncodeTimes) : 0.0f; + statsString += QString().sprintf(" Avg long encode time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageLongEncodeTime.getAverage(), + longVsTotalEncode * AS_PERCENT, _longEncode); + + float extraLongVsTotalEncode = (allEncodeTimes > 0) ? ((float)_extraLongEncode / (float)allEncodeTimes) : 0.0f; + statsString += QString().sprintf(" Avg extra long encode time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n", + _averageExtraLongEncodeTime.getAverage(), + extraLongVsTotalEncode * AS_PERCENT, _extraLongEncode); + + + float averageCompressAndWriteTime = getAverageCompressAndWriteTime(); + statsString += QString().sprintf(" Average compress and write time: %9.2f usecs\r\n", averageCompressAndWriteTime); + + int allCompressTimes = _noCompress + _shortCompress + _longCompress + _extraLongCompress; + + float zeroVsTotalCompress = (allCompressTimes > 0) ? ((float)_noCompress / (float)allCompressTimes) : 0.0f; + statsString += QString().sprintf(" No compression:" + " (%6.2f%%) samples: %12d \r\n", + zeroVsTotalCompress * AS_PERCENT, _noCompress); + + float shortVsTotalCompress = (allCompressTimes > 0) ? ((float)_shortCompress / (float)allCompressTimes) : 0.0f; + statsString += QString().sprintf(" Avg short compress time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageShortCompressTime.getAverage(), + shortVsTotalCompress * AS_PERCENT, _shortCompress); + + float longVsTotalCompress = (allCompressTimes > 0) ? ((float)_longCompress / (float)allCompressTimes) : 0.0f; + statsString += QString().sprintf(" Avg long compress time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n", + _averageLongCompressTime.getAverage(), + longVsTotalCompress * AS_PERCENT, _longCompress); + + float extraLongVsTotalCompress = (allCompressTimes > 0) ? ((float)_extraLongCompress / (float)allCompressTimes) : 0.0f; + statsString += QString().sprintf(" Avg extra long compress time:" + " %9.2f usecs (%6.2f%%) samples: %12d \r\n\r\n", + _averageExtraLongCompressTime.getAverage(), + extraLongVsTotalCompress * AS_PERCENT, _extraLongCompress); + + float averagePacketSendingTime = getAveragePacketSendingTime(); + statsString += QString().sprintf(" Average packet sending time: %9.2f usecs (includes node lock)\r\n", + averagePacketSendingTime); + + float noVsTotalSend = (_averagePacketSendingTime.getSampleCount() > 0) ? + ((float)_noSend / (float)_averagePacketSendingTime.getSampleCount()) : 0.0f; + statsString += QString().sprintf(" Not sending:" + " (%6.2f%%) samples: %12d \r\n", + noVsTotalSend * AS_PERCENT, _noSend); + float averageNodeWaitTime = getAverageNodeWaitTime(); - statsString += QString().sprintf(" Average node lock wait time: %7.2f usecs\r\n", averageNodeWaitTime); - qDebug() << "averageNodeWaitTime=" << averageNodeWaitTime; + statsString += QString().sprintf(" Average node lock wait time: %9.2f usecs\r\n", averageNodeWaitTime); + + statsString += QString().sprintf("--------------------------------------------------------------\r\n"); + + float encodeToInsidePercent = averageInsideTime == 0.0f ? 0.0f : (averageEncodeTime / averageInsideTime) * AS_PERCENT; + statsString += QString().sprintf(" encode ratio: %5.2f%%\r\n", + encodeToInsidePercent); + + float waitToInsidePercent = averageInsideTime == 0.0f ? 0.0f + : ((averageTreeWaitTime + averageNodeWaitTime) / averageInsideTime) * AS_PERCENT; + statsString += QString().sprintf(" waiting ratio: %5.2f%%\r\n", waitToInsidePercent); + + float compressAndWriteToInsidePercent = averageInsideTime == 0.0f ? 0.0f + : (averageCompressAndWriteTime / averageInsideTime) * AS_PERCENT; + statsString += QString().sprintf(" compress and write ratio: %5.2f%%\r\n", + compressAndWriteToInsidePercent); + + float sendingToInsidePercent = averageInsideTime == 0.0f ? 0.0f + : (averagePacketSendingTime / averageInsideTime) * AS_PERCENT; + statsString += QString().sprintf(" sending ratio: %5.2f%%\r\n", sendingToInsidePercent); + statsString += QString("\r\n"); @@ -556,6 +810,7 @@ void OctreeServer::readPendingDatagrams() { } void OctreeServer::run() { + _safeServerName = getMyServerName(); // Before we do anything else, create our tree... _tree = createTree(); @@ -611,6 +866,7 @@ void OctreeServer::run() { connect(nodeList, SIGNAL(nodeAdded(SharedNodePointer)), SLOT(nodeAdded(SharedNodePointer))); connect(nodeList, SIGNAL(nodeKilled(SharedNodePointer)),SLOT(nodeKilled(SharedNodePointer))); + // we need to ask the DS about agents so we can ping/reply with them nodeList->addNodeTypeToInterestSet(NodeType::Agent); @@ -732,17 +988,26 @@ void OctreeServer::run() { void OctreeServer::nodeAdded(SharedNodePointer node) { // we might choose to use this notifier to track clients in a pending state + qDebug() << qPrintable(_safeServerName) << "server added node:" << *node; } void OctreeServer::nodeKilled(SharedNodePointer node) { + qDebug() << qPrintable(_safeServerName) << "server killed node:" << *node; OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); if (nodeData) { - // Note: It should be safe to do this without locking the node, because if any other threads - // are using the SharedNodePointer, then they have a reference to the SharedNodePointer and the deleteLater() - // won't actually delete it until all threads have released their references to the pointer. - // But we can and should clear the linked data so that no one else tries to access it. + qDebug() << qPrintable(_safeServerName) << "server resetting Linked Data for node:" << *node; + node->setLinkedData(NULL); // set this first in case another thread comes through and tryes to acces this + qDebug() << qPrintable(_safeServerName) << "server deleting Linked Data for node:" << *node; nodeData->deleteLater(); - node->setLinkedData(NULL); } } +void OctreeServer::aboutToFinish() { + qDebug() << qPrintable(_safeServerName) << "server STARTING about to finish..."; + foreach (const SharedNodePointer& node, NodeList::getInstance()->getNodeHash()) { + qDebug() << qPrintable(_safeServerName) << "server about to finish while node still connected node:" << *node; + nodeKilled(node); + } + qDebug() << qPrintable(_safeServerName) << "server ENDING about to finish..."; +} + diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 94dcf18dc8..12091170d9 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -73,19 +73,34 @@ public: virtual int sendSpecialPacket(const SharedNodePointer& node) { return 0; } static void attachQueryNodeToNode(Node* newNode); + + static float SKIP_TIME; // use this for trackXXXTime() calls for non-times static void trackLoopTime(float time) { _averageLoopTime.updateAverage(time); } static float getAverageLoopTime() { return _averageLoopTime.getAverage(); } - static void trackEncodeTime(float time) { _averageEncodeTime.updateAverage(time); } + static void trackEncodeTime(float time); static float getAverageEncodeTime() { return _averageEncodeTime.getAverage(); } - static void trackTreeWaitTime(float time) { _averageTreeWaitTime.updateAverage(time); } + static void trackInsideTime(float time) { _averageInsideTime.updateAverage(time); } + static float getAverageInsideTime() { return _averageInsideTime.getAverage(); } + + static void trackTreeWaitTime(float time); static float getAverageTreeWaitTime() { return _averageTreeWaitTime.getAverage(); } + static void trackNodeWaitTime(float time) { _averageNodeWaitTime.updateAverage(time); } static float getAverageNodeWaitTime() { return _averageNodeWaitTime.getAverage(); } + static void trackCompressAndWriteTime(float time); + static float getAverageCompressAndWriteTime() { return _averageCompressAndWriteTime.getAverage(); } + + static void trackPacketSendingTime(float time); + static float getAveragePacketSendingTime() { return _averagePacketSendingTime.getAverage(); } + bool handleHTTPRequest(HTTPConnection* connection, const QString& path); + + virtual void aboutToFinish(); + public slots: /// runs the voxel server assignment void run(); @@ -96,6 +111,7 @@ public slots: protected: void parsePayload(); void initHTTPManager(int port); + void resetSendingStats(); int _argc; const char** _argv; @@ -120,12 +136,44 @@ protected: time_t _started; quint64 _startedUSecs; + QString _safeServerName; static int _clientCount; static SimpleMovingAverage _averageLoopTime; + static SimpleMovingAverage _averageEncodeTime; + static SimpleMovingAverage _averageShortEncodeTime; + static SimpleMovingAverage _averageLongEncodeTime; + static SimpleMovingAverage _averageExtraLongEncodeTime; + static int _extraLongEncode; + static int _longEncode; + static int _shortEncode; + static int _noEncode; + + static SimpleMovingAverage _averageInsideTime; static SimpleMovingAverage _averageTreeWaitTime; + static SimpleMovingAverage _averageTreeShortWaitTime; + static SimpleMovingAverage _averageTreeLongWaitTime; + static SimpleMovingAverage _averageTreeExtraLongWaitTime; + static int _extraLongTreeWait; + static int _longTreeWait; + static int _shortTreeWait; + static int _noTreeWait; + static SimpleMovingAverage _averageNodeWaitTime; + + static SimpleMovingAverage _averageCompressAndWriteTime; + static SimpleMovingAverage _averageShortCompressTime; + static SimpleMovingAverage _averageLongCompressTime; + static SimpleMovingAverage _averageExtraLongCompressTime; + static int _extraLongCompress; + static int _longCompress; + static int _shortCompress; + static int _noCompress; + + static SimpleMovingAverage _averagePacketSendingTime; + static int _noSend; + }; #endif // __octree_server__OctreeServer__ diff --git a/examples/seeingVoxelsExample.js b/examples/seeingVoxelsExample.js index 257e869b5b..0cfe54d89e 100644 --- a/examples/seeingVoxelsExample.js +++ b/examples/seeingVoxelsExample.js @@ -13,18 +13,23 @@ var yawDirection = -1; var yaw = 45; var yawMax = 70; var yawMin = 20; +var vantagePoint = {x: 5000, y: 500, z: 5000}; var isLocal = false; // set up our VoxelViewer with a position and orientation var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0); +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; +} + function init() { if (isLocal) { - MyAvatar.position = {x: 5000, y: 500, z: 5000}; + MyAvatar.position = vantagePoint; MyAvatar.orientation = orientation; } else { - VoxelViewer.setPosition({x: 5000, y: 500, z: 5000}); + VoxelViewer.setPosition(vantagePoint); VoxelViewer.setOrientation(orientation); VoxelViewer.queryOctree(); Agent.isAvatar = true; @@ -38,21 +43,26 @@ function keepLooking(deltaTime) { init(); } count++; - if (count % 10 == 0) { + if (count % getRandomInt(5, 15) == 0) { yaw += yawDirection; orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0); if (yaw > yawMax || yaw < yawMin) { yawDirection = yawDirection * -1; } - print("calling VoxelViewer.queryOctree()... count=" + count + " yaw=" + yaw); + if (count % 10000 == 0) { + print("calling VoxelViewer.queryOctree()... count=" + count + " yaw=" + yaw); + } if (isLocal) { MyAvatar.orientation = orientation; } else { VoxelViewer.setOrientation(orientation); VoxelViewer.queryOctree(); - print("VoxelViewer.getOctreeElementsCount()=" + VoxelViewer.getOctreeElementsCount()); + + if (count % 10000 == 0) { + print("VoxelViewer.getOctreeElementsCount()=" + VoxelViewer.getOctreeElementsCount()); + } } } diff --git a/interface/src/VoxelHideShowThread.h b/interface/src/VoxelHideShowThread.h index 2925888022..dc1f2062c1 100644 --- a/interface/src/VoxelHideShowThread.h +++ b/interface/src/VoxelHideShowThread.h @@ -16,6 +16,7 @@ /// Generalized threaded processor for handling received inbound packets. class VoxelHideShowThread : public GenericThread { + Q_OBJECT public: VoxelHideShowThread(VoxelSystem* theSystem); diff --git a/interface/src/VoxelPacketProcessor.h b/interface/src/VoxelPacketProcessor.h index 42040fe446..2acd347e99 100644 --- a/interface/src/VoxelPacketProcessor.h +++ b/interface/src/VoxelPacketProcessor.h @@ -16,6 +16,7 @@ /// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() class VoxelPacketProcessor : public ReceivedPacketProcessor { + Q_OBJECT protected: virtual void processPacket(const SharedNodePointer& sendingNode, const QByteArray& packet); }; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index fc867ec698..58cfd9b15d 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -355,15 +355,7 @@ void Octree::deleteOctalCodeFromTree(const unsigned char* codeBuffer, bool colla OctreeElement* node = _rootNode; - // We can't encode and delete nodes at the same time, so we guard against deleting any node that is actively - // being encoded. And we stick that code on our pendingDelete list. - if (isEncoding(codeBuffer)) { - queueForLaterDelete(codeBuffer); - } else { - startDeleting(codeBuffer); - deleteOctalCodeFromTreeRecursion(node, &args); - doneDeleting(codeBuffer); - } + deleteOctalCodeFromTreeRecursion(node, &args); } void Octree::deleteOctalCodeFromTreeRecursion(OctreeElement* node, void* extraData) { @@ -796,11 +788,8 @@ int Octree::encodeTreeBitstream(OctreeElement* node, return bytesWritten; } - startEncoding(node); - // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! if (params.viewFrustum && !node->isInView(*params.viewFrustum)) { - doneEncoding(node); params.stopReason = EncodeBitstreamParams::OUT_OF_VIEW; return bytesWritten; } @@ -825,7 +814,6 @@ int Octree::encodeTreeBitstream(OctreeElement* node, // If the octalcode couldn't fit, then we can return, because no nodes below us will fit... if (!roomForOctalCode) { - doneEncoding(node); bag.insert(node); // add the node back to the bag so it will eventually get included params.stopReason = EncodeBitstreamParams::DIDNT_FIT; return bytesWritten; @@ -841,7 +829,10 @@ int Octree::encodeTreeBitstream(OctreeElement* node, params.stats->traversed(node); } - int childBytesWritten = encodeTreeBitstreamRecursion(node, packetData, bag, params, currentEncodeLevel); + ViewFrustum::location parentLocationThisView = ViewFrustum::INTERSECT; // assume parent is in view, but not fully + + int childBytesWritten = encodeTreeBitstreamRecursion(node, packetData, bag, params, + currentEncodeLevel, parentLocationThisView); // if childBytesWritten == 1 then something went wrong... that's not possible assert(childBytesWritten != 1); @@ -868,14 +859,13 @@ int Octree::encodeTreeBitstream(OctreeElement* node, packetData->endSubTree(); } - doneEncoding(node); - return bytesWritten; } int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag, - EncodeBitstreamParams& params, int& currentEncodeLevel) const { + EncodeBitstreamParams& params, int& currentEncodeLevel, + const ViewFrustum::location& parentLocationThisView) const { // How many bytes have we written so far at this level; int bytesAtThisLevel = 0; @@ -906,6 +896,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, return bytesAtThisLevel; } } + + ViewFrustum::location nodeLocationThisView = ViewFrustum::INSIDE; // assume we're inside // caller can pass NULL as viewFrustum if they want everything if (params.viewFrustum) { @@ -922,10 +914,17 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, return bytesAtThisLevel; } + // if the parent isn't known to be INSIDE, then it must be INTERSECT, and we should double check to see + // if we are INSIDE, INTERSECT, or OUTSIDE + if (parentLocationThisView != ViewFrustum::INSIDE) { + assert(parentLocationThisView != ViewFrustum::OUTSIDE); // we shouldn't be here if our parent was OUTSIDE! + nodeLocationThisView = node->inFrustum(*params.viewFrustum); + } + // If we're at a node that is out of view, then we can return, because no nodes below us will be in view! // although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if // we're out of view - if (!node->isInView(*params.viewFrustum)) { + if (nodeLocationThisView == ViewFrustum::OUTSIDE) { if (params.stats) { params.stats->skippedOutOfView(node); } @@ -1079,7 +1078,11 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, OctreeElement* childNode = sortedChildren[i]; int originalIndex = indexOfChildren[i]; - bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum))); + bool childIsInView = (childNode && + ( !params.viewFrustum || // no view frustum was given, everything is assumed in view + (nodeLocationThisView == ViewFrustum::INSIDE) || // the parent was fully in view, we can assume ALL children are + (nodeLocationThisView == ViewFrustum::INTERSECT && childNode->isInView(*params.viewFrustum)) // the parent intersects and the child is in view + )); if (!childIsInView) { // must check childNode here, because it could be we got here because there was no childNode @@ -1306,7 +1309,8 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* node, // This only applies in the view frustum case, in other cases, like file save and copy/past where // no viewFrustum was requested, we still want to recurse the child tree. if (!params.viewFrustum || !oneAtBit(childrenColoredBits, originalIndex)) { - childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, packetData, bag, params, thisLevel); + childTreeBytesOut = encodeTreeBitstreamRecursion(childNode, packetData, bag, params, + thisLevel, nodeLocationThisView); } // remember this for reshuffling @@ -1621,56 +1625,6 @@ void dumpSetContents(const char* name, std::set set) { */ } -void Octree::startEncoding(OctreeElement* node) { - _encodeSetLock.lock(); - _codesBeingEncoded.insert(node->getOctalCode()); - _encodeSetLock.unlock(); -} - -void Octree::doneEncoding(OctreeElement* node) { - _encodeSetLock.lock(); - _codesBeingEncoded.erase(node->getOctalCode()); - _encodeSetLock.unlock(); - - // if we have any pending delete codes, then delete them now. - emptyDeleteQueue(); -} - -void Octree::startDeleting(const unsigned char* code) { - _deleteSetLock.lock(); - _codesBeingDeleted.insert(code); - _deleteSetLock.unlock(); -} - -void Octree::doneDeleting(const unsigned char* code) { - _deleteSetLock.lock(); - _codesBeingDeleted.erase(code); - _deleteSetLock.unlock(); -} - -bool Octree::isEncoding(const unsigned char* codeBuffer) { - _encodeSetLock.lock(); - bool isEncoding = (_codesBeingEncoded.find(codeBuffer) != _codesBeingEncoded.end()); - _encodeSetLock.unlock(); - return isEncoding; -} - -void Octree::queueForLaterDelete(const unsigned char* codeBuffer) { - _deletePendingSetLock.lock(); - _codesPendingDelete.insert(codeBuffer); - _deletePendingSetLock.unlock(); -} - -void Octree::emptyDeleteQueue() { - _deletePendingSetLock.lock(); - for (std::set::iterator i = _codesPendingDelete.begin(); i != _codesPendingDelete.end(); ++i) { - const unsigned char* codeToDelete = *i; - _codesBeingDeleted.erase(codeToDelete); - deleteOctalCodeFromTree(codeToDelete, COLLAPSE_EMPTY_TREE); - } - _deletePendingSetLock.unlock(); -} - void Octree::cancelImport() { _stopImport = true; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 4c237b5f56..f029431d87 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -287,7 +287,8 @@ protected: int encodeTreeBitstreamRecursion(OctreeElement* node, OctreePacketData* packetData, OctreeElementBag& bag, - EncodeBitstreamParams& params, int& currentEncodeLevel) const; + EncodeBitstreamParams& params, int& currentEncodeLevel, + const ViewFrustum::location& parentLocationThisView) const; static bool countOctreeElementsOperation(OctreeElement* node, void* extraData); @@ -302,40 +303,6 @@ protected: bool _shouldReaverage; bool _stopImport; - /// Octal Codes of any subtrees currently being encoded. While any of these codes is being encoded, ancestors and - /// descendants of them can not be deleted. - std::set _codesBeingEncoded; - /// mutex lock to protect the encoding set - QMutex _encodeSetLock; - - /// Called to indicate that a OctreeElement is in the process of being encoded. - void startEncoding(OctreeElement* node); - /// Called to indicate that a OctreeElement is done being encoded. - void doneEncoding(OctreeElement* node); - /// Is the Octal Code currently being deleted? - bool isEncoding(const unsigned char* codeBuffer); - - /// Octal Codes of any subtrees currently being deleted. While any of these codes is being deleted, ancestors and - /// descendants of them can not be encoded. - std::set _codesBeingDeleted; - /// mutex lock to protect the deleting set - QMutex _deleteSetLock; - - /// Called to indicate that an octal code is in the process of being deleted. - void startDeleting(const unsigned char* code); - /// Called to indicate that an octal code is done being deleted. - void doneDeleting(const unsigned char* code); - /// Octal Codes that were attempted to be deleted but couldn't be because they were actively being encoded, and were - /// instead queued for later delete - std::set _codesPendingDelete; - /// mutex lock to protect the deleting set - QMutex _deletePendingSetLock; - - /// Adds an Octal Code to the set of codes that needs to be deleted - void queueForLaterDelete(const unsigned char* codeBuffer); - /// flushes out any Octal Codes that had to be queued - void emptyDeleteQueue(); - QReadWriteLock _lock; /// This tree is receiving inbound viewer datagrams. diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index 72ac5b14d6..cdc4f419c0 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -1183,13 +1183,6 @@ float OctreeElement::getEnclosingRadius() const { return getScale() * sqrtf(3.0f) / 2.0f; } -bool OctreeElement::isInView(const ViewFrustum& viewFrustum) const { - AABox box = _box; // use temporary box so we can scale it - box.scale(TREE_SCALE); - bool inView = (ViewFrustum::OUTSIDE != viewFrustum.boxInFrustum(box)); - return inView; -} - ViewFrustum::location OctreeElement::inFrustum(const ViewFrustum& viewFrustum) const { AABox box = _box; // use temporary box so we can scale it box.scale(TREE_SCALE); @@ -1209,23 +1202,27 @@ bool OctreeElement::calculateShouldRender(const ViewFrustum* viewFrustum, float bool shouldRender = false; if (hasContent()) { float furthestDistance = furthestDistanceToCamera(*viewFrustum); - float boundary = boundaryDistanceForRenderLevel(getLevel() + boundaryLevelAdjust, voxelScaleSize); - float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); - bool inBoundary = (furthestDistance <= boundary); - bool inChildBoundary = (furthestDistance <= childBoundary); - shouldRender = (isLeaf() && inChildBoundary) || (inBoundary && !inChildBoundary); + float childBoundary = boundaryDistanceForRenderLevel(getLevel() + 1 + boundaryLevelAdjust, voxelScaleSize); + bool inChildBoundary = (furthestDistance <= childBoundary); + if (isLeaf() && inChildBoundary) { + shouldRender = true; + } else { + float boundary = childBoundary * 2.0f; // the boundary is always twice the distance of the child boundary + bool inBoundary = (furthestDistance <= boundary); + shouldRender = inBoundary && !inChildBoundary; + } } return shouldRender; } // Calculates the distance to the furthest point of the voxel to the camera +// does as much math as possible in voxel scale and then scales up to TREE_SCALE at end float OctreeElement::furthestDistanceToCamera(const ViewFrustum& viewFrustum) const { - AABox box = getAABox(); - box.scale(TREE_SCALE); - glm::vec3 furthestPoint = viewFrustum.getFurthestPointFromCamera(box); - glm::vec3 temp = viewFrustum.getPosition() - furthestPoint; - float distanceToVoxelCenter = sqrtf(glm::dot(temp, temp)); - return distanceToVoxelCenter; + glm::vec3 furthestPoint; + viewFrustum.getFurthestPointFromCameraVoxelScale(getAABox(), furthestPoint); + glm::vec3 temp = viewFrustum.getPositionVoxelScale() - furthestPoint; + float distanceToFurthestPoint = sqrtf(glm::dot(temp, temp)); + return distanceToFurthestPoint * (float)TREE_SCALE; } float OctreeElement::distanceToCamera(const ViewFrustum& viewFrustum) const { diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index 1785307696..ca2fa0b540 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -112,9 +112,7 @@ public: int getLevel() const { return numberOfThreeBitSectionsInCode(getOctalCode()) + 1; } float getEnclosingRadius() const; - - - bool isInView(const ViewFrustum& viewFrustum) const; + bool isInView(const ViewFrustum& viewFrustum) const { return inFrustum(viewFrustum) != ViewFrustum::OUTSIDE; } ViewFrustum::location inFrustum(const ViewFrustum& viewFrustum) const; float distanceToCamera(const ViewFrustum& viewFrustum) const; float furthestDistanceToCamera(const ViewFrustum& viewFrustum) const; @@ -126,7 +124,7 @@ public: float distanceSquareToPoint(const glm::vec3& point) const; // when you don't need the actual distance, use this. float distanceToPoint(const glm::vec3& point) const; - bool isLeaf() const { return getChildCount() == 0; } + bool isLeaf() const { return _childBitmask == 0; } int getChildCount() const { return numberOfOnes(_childBitmask); } void printDebugDetails(const char* label) const; bool isDirty() const { return _isDirty; } diff --git a/libraries/octree/src/OctreeElementBag.cpp b/libraries/octree/src/OctreeElementBag.cpp index c3ad44a604..3ecdfaf2e3 100644 --- a/libraries/octree/src/OctreeElementBag.cpp +++ b/libraries/octree/src/OctreeElementBag.cpp @@ -10,9 +10,8 @@ #include OctreeElementBag::OctreeElementBag() : - _bagElements(NULL), - _elementsInUse(0), - _sizeOfElementsArray(0) { + _bagElements() +{ OctreeElement::addDeleteHook(this); }; @@ -21,114 +20,35 @@ OctreeElementBag::~OctreeElementBag() { deleteAll(); } -void OctreeElementBag::deleteAll() { - if (_bagElements) { - delete[] _bagElements; - } - _bagElements = NULL; - _elementsInUse = 0; - _sizeOfElementsArray = 0; -} - - -const int GROW_BAG_BY = 100; - -// put a node into the bag -void OctreeElementBag::insert(OctreeElement* element) { - - // Search for where we should live in the bag (sorted) - // Note: change this to binary search... instead of linear! - int insertAt = _elementsInUse; - for (int i = 0; i < _elementsInUse; i++) { - // just compare the pointers... that's good enough - if (_bagElements[i] == element) { - return; // exit early!! - } - - if (_bagElements[i] > element) { - insertAt = i; - break; - } - } - // at this point, inserAt will be the location we want to insert at. - - // If we don't have room in our bag, then grow the bag - if (_sizeOfElementsArray < _elementsInUse + 1) { - OctreeElement** oldBag = _bagElements; - _bagElements = new OctreeElement*[_sizeOfElementsArray + GROW_BAG_BY]; - _sizeOfElementsArray += GROW_BAG_BY; - - // If we had an old bag... - if (oldBag) { - // copy old elements into the new bag, but leave a space where we need to - // insert the new node - memcpy(_bagElements, oldBag, insertAt * sizeof(OctreeElement*)); - memcpy(&_bagElements[insertAt + 1], &oldBag[insertAt], (_elementsInUse - insertAt) * sizeof(OctreeElement*)); - delete[] oldBag; - } - } else { - // move existing elements further back in the bag array, leave a space where we need to - // insert the new node - memmove(&_bagElements[insertAt + 1], &_bagElements[insertAt], (_elementsInUse - insertAt) * sizeof(OctreeElement*)); - } - _bagElements[insertAt] = element; - _elementsInUse++; -} - -// pull a node out of the bag (could come in any order) -OctreeElement* OctreeElementBag::extract() { - // pull the last node out, and shrink our list... - if (_elementsInUse) { - - // get the last element - OctreeElement* element = _bagElements[_elementsInUse - 1]; - - // reduce the count - _elementsInUse--; - - return element; - } - return NULL; -} - -bool OctreeElementBag::contains(OctreeElement* element) { - for (int i = 0; i < _elementsInUse; i++) { - // just compare the pointers... that's good enough - if (_bagElements[i] == element) { - return true; // exit early!! - } - // if we're past where it should be, then it's not here! - if (_bagElements[i] > element) { - return false; - } - } - // if we made it through the entire bag, it's not here! - return false; -} - -void OctreeElementBag::remove(OctreeElement* element) { - int foundAt = -1; - for (int i = 0; i < _elementsInUse; i++) { - // just compare the pointers... that's good enough - if (_bagElements[i] == element) { - foundAt = i; - break; - } - // if we're past where it should be, then it's not here! - if (_bagElements[i] > element) { - break; - } - } - // if we found it, then we need to remove it.... - if (foundAt != -1) { - memmove(&_bagElements[foundAt], &_bagElements[foundAt + 1], (_elementsInUse - foundAt) * sizeof(OctreeElement*)); - _elementsInUse--; - } -} - - void OctreeElementBag::elementDeleted(OctreeElement* element) { remove(element); // note: remove can safely handle nodes that aren't in it, so we don't need to check contains() } +void OctreeElementBag::deleteAll() { + _bagElements.clear(); +} + + +void OctreeElementBag::insert(OctreeElement* element) { + _bagElements.insert(element); +} + +OctreeElement* OctreeElementBag::extract() { + OctreeElement* result = NULL; + + if (_bagElements.size() > 0) { + QSet::iterator front = _bagElements.begin(); + result = *front; + _bagElements.erase(front); + } + return result; +} + +bool OctreeElementBag::contains(OctreeElement* element) { + return _bagElements.contains(element); +} + +void OctreeElementBag::remove(OctreeElement* element) { + _bagElements.remove(element); +} diff --git a/libraries/octree/src/OctreeElementBag.h b/libraries/octree/src/OctreeElementBag.h index a952b52095..ba74a05516 100644 --- a/libraries/octree/src/OctreeElementBag.h +++ b/libraries/octree/src/OctreeElementBag.h @@ -27,18 +27,14 @@ public: bool contains(OctreeElement* element); // is this element in the bag? void remove(OctreeElement* element); // remove a specific element from the bag - bool isEmpty() const { return (_elementsInUse == 0); } - int count() const { return _elementsInUse; } + bool isEmpty() const { return _bagElements.isEmpty(); } + int count() const { return _bagElements.size(); } void deleteAll(); virtual void elementDeleted(OctreeElement* element); private: - - OctreeElement** _bagElements; - int _elementsInUse; - int _sizeOfElementsArray; - //int _hookID; + QSet _bagElements; }; #endif /* defined(__hifi__OctreeElementBag__) */ diff --git a/libraries/octree/src/ViewFrustum.cpp b/libraries/octree/src/ViewFrustum.cpp index aeffcc4968..da03aad697 100644 --- a/libraries/octree/src/ViewFrustum.cpp +++ b/libraries/octree/src/ViewFrustum.cpp @@ -25,6 +25,7 @@ using namespace std; ViewFrustum::ViewFrustum() : _position(0,0,0), + _positionVoxelScale(0,0,0), _orientation(), _direction(IDENTITY_FRONT), _up(IDENTITY_UP), @@ -277,6 +278,10 @@ ViewFrustum::location ViewFrustum::boxInFrustum(const AABox& box) const { return keyholeResult; } + // TODO: These calculations are expensive, taking up 80% of our time in this function. + // This appears to be expensive because we have to test the distance to each plane. + // One suggested optimization is to first check against the approximated cone. We might + // also be able to test against the cone to the bounding sphere of the box. for(int i=0; i < 6; i++) { const glm::vec3& normal = _planes[i].getNormal(); const glm::vec3& boxVertexP = box.getVertexP(normal); @@ -691,39 +696,60 @@ OctreeProjectedPolygon ViewFrustum::getProjectedPolygon(const AABox& box) const return projectedPolygon; } - // Similar strategy to getProjectedPolygon() we use the knowledge of camera position relative to the // axis-aligned voxels to determine which of the voxels vertices must be the furthest. No need for // squares and square-roots. Just compares. -glm::vec3 ViewFrustum::getFurthestPointFromCamera(const AABox& box) const { +void ViewFrustum::getFurthestPointFromCamera(const AABox& box, glm::vec3& furthestPoint) const { const glm::vec3& bottomNearRight = box.getCorner(); - glm::vec3 center = box.calcCenter(); - glm::vec3 topFarLeft = box.calcTopFarLeft(); + float scale = box.getScale(); + float halfScale = scale * 0.5f; - glm::vec3 furthestPoint; - if (_position.x < center.x) { + if (_position.x < bottomNearRight.x + halfScale) { // we are to the right of the center, so the left edge is furthest - furthestPoint.x = topFarLeft.x; + furthestPoint.x = bottomNearRight.x + scale; } else { - // we are to the left of the center, so the right edge is furthest (at center ok too) furthestPoint.x = bottomNearRight.x; } - if (_position.y < center.y) { + if (_position.y < bottomNearRight.y + halfScale) { // we are below of the center, so the top edge is furthest - furthestPoint.y = topFarLeft.y; + furthestPoint.y = bottomNearRight.y + scale; } else { - // we are above the center, so the lower edge is furthest (at center ok too) furthestPoint.y = bottomNearRight.y; } - if (_position.z < center.z) { + if (_position.z < bottomNearRight.z + halfScale) { // we are to the near side of the center, so the far side edge is furthest - furthestPoint.z = topFarLeft.z; + furthestPoint.z = bottomNearRight.z + scale; } else { - // we are to the far side of the center, so the near side edge is furthest (at center ok too) furthestPoint.z = bottomNearRight.z; } - - return furthestPoint; } + +void ViewFrustum::getFurthestPointFromCameraVoxelScale(const AABox& box, glm::vec3& furthestPoint) const { + const glm::vec3& bottomNearRight = box.getCorner(); + float scale = box.getScale(); + float halfScale = scale * 0.5f; + + if (_positionVoxelScale.x < bottomNearRight.x + halfScale) { + // we are to the right of the center, so the left edge is furthest + furthestPoint.x = bottomNearRight.x + scale; + } else { + furthestPoint.x = bottomNearRight.x; + } + + if (_positionVoxelScale.y < bottomNearRight.y + halfScale) { + // we are below of the center, so the top edge is furthest + furthestPoint.y = bottomNearRight.y + scale; + } else { + furthestPoint.y = bottomNearRight.y; + } + + if (_positionVoxelScale.z < bottomNearRight.z + halfScale) { + // we are to the near side of the center, so the far side edge is furthest + furthestPoint.z = bottomNearRight.z + scale; + } else { + furthestPoint.z = bottomNearRight.z; + } +} + diff --git a/libraries/octree/src/ViewFrustum.h b/libraries/octree/src/ViewFrustum.h index da4a6997f1..a0b3a851aa 100644 --- a/libraries/octree/src/ViewFrustum.h +++ b/libraries/octree/src/ViewFrustum.h @@ -29,11 +29,12 @@ const float DEFAULT_FAR_CLIP = 50.0f * TREE_SCALE; class ViewFrustum { public: // setters for camera attributes - void setPosition(const glm::vec3& p) { _position = p; } + void setPosition(const glm::vec3& p) { _position = p; _positionVoxelScale = (p / (float)TREE_SCALE); } void setOrientation(const glm::quat& orientationAsQuaternion); // getters for camera attributes const glm::vec3& getPosition() const { return _position; } + const glm::vec3& getPositionVoxelScale() const { return _positionVoxelScale; } const glm::quat& getOrientation() const { return _orientation; } const glm::vec3& getDirection() const { return _direction; } const glm::vec3& getUp() const { return _up; } @@ -102,8 +103,11 @@ public: glm::vec2 projectPoint(glm::vec3 point, bool& pointInView) const; OctreeProjectedPolygon getProjectedPolygon(const AABox& box) const; - glm::vec3 getFurthestPointFromCamera(const AABox& box) const; - + void getFurthestPointFromCamera(const AABox& box, glm::vec3& furthestPoint) const; + + // assumes box is in voxel scale, not TREE_SCALE, will scale view frustum's position accordingly + void getFurthestPointFromCameraVoxelScale(const AABox& box, glm::vec3& furthestPoint) const; + private: // Used for keyhole calculations ViewFrustum::location pointInKeyhole(const glm::vec3& point) const; @@ -111,7 +115,8 @@ private: ViewFrustum::location boxInKeyhole(const AABox& box) const; // camera location/orientation attributes - glm::vec3 _position; + glm::vec3 _position; // the position in TREE_SCALE + glm::vec3 _positionVoxelScale; // the position in voxel scale glm::quat _orientation; // calculated for orientation diff --git a/libraries/shared/src/ReceivedPacketProcessor.h b/libraries/shared/src/ReceivedPacketProcessor.h index 7c99218753..f88512639b 100644 --- a/libraries/shared/src/ReceivedPacketProcessor.h +++ b/libraries/shared/src/ReceivedPacketProcessor.h @@ -18,6 +18,7 @@ /// Generalized threaded processor for handling received inbound packets. class ReceivedPacketProcessor : public GenericThread { + Q_OBJECT public: ReceivedPacketProcessor() { } diff --git a/libraries/shared/src/SimpleMovingAverage.cpp b/libraries/shared/src/SimpleMovingAverage.cpp index 5a55486216..676c85598e 100644 --- a/libraries/shared/src/SimpleMovingAverage.cpp +++ b/libraries/shared/src/SimpleMovingAverage.cpp @@ -42,13 +42,15 @@ int SimpleMovingAverage::updateAverage(float sample) { void SimpleMovingAverage::reset() { _numSamples = 0; + _average = 0; + _eventDeltaAverage = 0; } -float SimpleMovingAverage::getEventDeltaAverage() { +float SimpleMovingAverage::getEventDeltaAverage() const { return (ONE_MINUS_WEIGHTING * _eventDeltaAverage) + (WEIGHTING * ((usecTimestampNow() - _lastEventTimestamp) / 1000000.0f)); } -float SimpleMovingAverage::getAverageSampleValuePerSecond() { +float SimpleMovingAverage::getAverageSampleValuePerSecond() const { return _average * (1 / getEventDeltaAverage()); } \ No newline at end of file diff --git a/libraries/shared/src/SimpleMovingAverage.h b/libraries/shared/src/SimpleMovingAverage.h index 9788aafe58..f11cd16e58 100644 --- a/libraries/shared/src/SimpleMovingAverage.h +++ b/libraries/shared/src/SimpleMovingAverage.h @@ -20,10 +20,10 @@ public: int updateAverage(float sample); void reset(); - int getSampleCount() { return _numSamples; }; - float getAverage() { return _average; }; - float getEventDeltaAverage(); - float getAverageSampleValuePerSecond(); + int getSampleCount() const { return _numSamples; }; + float getAverage() const { return _average; }; + float getEventDeltaAverage() const; + float getAverageSampleValuePerSecond() const; private: int _numSamples; uint64_t _lastEventTimestamp; diff --git a/libraries/shared/src/ThreadedAssignment.cpp b/libraries/shared/src/ThreadedAssignment.cpp index e2d42883f6..c4282028ae 100644 --- a/libraries/shared/src/ThreadedAssignment.cpp +++ b/libraries/shared/src/ThreadedAssignment.cpp @@ -29,6 +29,7 @@ void ThreadedAssignment::setFinished(bool isFinished) { _isFinished = isFinished; if (_isFinished) { + aboutToFinish(); emit finished(); } } diff --git a/libraries/shared/src/ThreadedAssignment.h b/libraries/shared/src/ThreadedAssignment.h index 25489a8e25..d3502e9c4d 100644 --- a/libraries/shared/src/ThreadedAssignment.h +++ b/libraries/shared/src/ThreadedAssignment.h @@ -15,18 +15,17 @@ class ThreadedAssignment : public Assignment { Q_OBJECT public: ThreadedAssignment(const QByteArray& packet); - void setFinished(bool isFinished); + virtual void aboutToFinish() { }; + public slots: /// threaded run of assignment virtual void run() = 0; - virtual void deleteLater(); - virtual void readPendingDatagrams() = 0; + protected: bool readAvailableDatagram(QByteArray& destinationByteArray, HifiSockAddr& senderSockAddr); - void commonInit(const QString& targetName, NodeType_t nodeType); bool _isFinished; private slots: