This commit is contained in:
Andrzej Kapolka 2014-03-19 16:52:46 -07:00
commit 418fc9f69c
25 changed files with 604 additions and 480 deletions

View file

@ -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();

View file

@ -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();

View file

@ -44,7 +44,7 @@ typedef std::map<QUuid, SingleSenderStats>::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);

View file

@ -35,7 +35,8 @@ OctreeQueryNode::OctreeQueryNode() :
_lastClientOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE),
_lodChanged(false),
_lodInitialized(false),
_sequenceNumber(0)
_sequenceNumber(0),
_lastRootTimestamp(0)
{
}

View file

@ -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__) */

View file

@ -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;
}

View file

@ -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();

View file

@ -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("<b>%1 Outbound Packet Statistics...</b>\r\n").arg(getMyServerName());
statsString += QString("<b>%1 Outbound Packet Statistics... "
"<a href='/resetStats'>[RESET]</a></b>\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<OctreeQueryNode*>(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...";
}

View file

@ -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__

View file

@ -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());
}
}
}

View file

@ -16,6 +16,7 @@
/// Generalized threaded processor for handling received inbound packets.
class VoxelHideShowThread : public GenericThread {
Q_OBJECT
public:
VoxelHideShowThread(VoxelSystem* theSystem);

View file

@ -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);
};

View file

@ -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<unsigned char*> 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<const unsigned char*>::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;
}

View file

@ -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<const unsigned char*> _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<const unsigned char*> _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<const unsigned char*> _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.

View file

@ -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 {

View file

@ -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; }

View file

@ -10,9 +10,8 @@
#include <OctalCode.h>
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<OctreeElement*>::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);
}

View file

@ -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<OctreeElement*> _bagElements;
};
#endif /* defined(__hifi__OctreeElementBag__) */

View file

@ -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;
}
}

View file

@ -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

View file

@ -18,6 +18,7 @@
/// Generalized threaded processor for handling received inbound packets.
class ReceivedPacketProcessor : public GenericThread {
Q_OBJECT
public:
ReceivedPacketProcessor() { }

View file

@ -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());
}

View file

@ -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;

View file

@ -29,6 +29,7 @@ void ThreadedAssignment::setFinished(bool isFinished) {
_isFinished = isFinished;
if (_isFinished) {
aboutToFinish();
emit finished();
}
}

View file

@ -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: