From 64fa3ec88f0b8967275805e24a9c7c331742b154 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 7 Aug 2017 11:14:40 -0700 Subject: [PATCH] repeated and differential view traversals work --- .../src/entities/EntityTreeSendThread.cpp | 228 ++++++++++-------- .../src/entities/EntityTreeSendThread.h | 104 ++++---- 2 files changed, 190 insertions(+), 142 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index ba66c85be3..2ecd1c6d7b 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -17,38 +17,60 @@ #include "EntityServer.h" -const float INVALID_ENTITY_SEND_PRIORITY = -1.0e-6f; +const float DO_NOT_SEND = -1.0e-6f; -void PrioritizedEntity::updatePriority(const ViewFrustum& view) { +void TreeTraversalPath::ConicalView::set(const ViewFrustum& viewFrustum) { + // The ConicalView has two parts: a central sphere (same as ViewFrustm) and a circular cone that bounds the frustum part. + // Why? Because approximate intersection tests are much faster to compute for a cone than for a frustum. + _position = viewFrustum.getPosition(); + _direction = viewFrustum.getDirection(); + + // We cache the sin and cos of the half angle of the cone that bounds the frustum. + // (the math here is left as an exercise for the reader) + float A = viewFrustum.getAspectRatio(); + float t = tanf(0.5f * viewFrustum.getFieldOfView()); + _cosAngle = 1.0f / sqrtf(1.0f + (A * A + 1.0f) * (t * t)); + _sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle); + + _radius = viewFrustum.getCenterRadius(); +} + +float TreeTraversalPath::ConicalView::computePriority(const AACube& cube) const { + glm::vec3 p = cube.calcCenter() - _position; // position of bounding sphere in view-frame + float d = glm::length(p); // distance to center of bounding sphere + float r = 0.5f * cube.getScale(); // radius of bounding sphere + if (d < _radius + r) { + return r; + } + if (glm::dot(p, _direction) > sqrtf(d * d - r * r) * _cosAngle - r * _sinAngle) { + const float AVOID_DIVIDE_BY_ZERO = 0.001f; + return r / (d + AVOID_DIVIDE_BY_ZERO); + } + return DO_NOT_SEND; +} + + +// static +float TreeTraversalPath::ConicalView::computePriority(const EntityItemPointer& entity) const { + assert(entity); + bool success; + AACube cube = entity->getQueryAACube(success); + if (success) { + return computePriority(cube); + } else { + // when in doubt give it something positive + return 1.0f; + } +} + +float TreeTraversalPath::PrioritizedEntity::updatePriority(const TreeTraversalPath::ConicalView& conicalView) { EntityItemPointer entity = _weakEntity.lock(); if (entity) { - bool success; - AACube cube = entity->getQueryAACube(success); - if (success) { - glm::vec3 offset = cube.calcCenter() - view.getPosition(); - const float MIN_DISTANCE = 0.001f; - float distanceToCenter = glm::length(offset) + MIN_DISTANCE; - float distance = distanceToCenter; //- 0.5f * cube.getScale(); - if (distance < MIN_DISTANCE) { - // this object's bounding box overlaps the camera --> give it a big priority - _priority = cube.getScale(); - } else { - // NOTE: we assume view.aspectRatio < 1.0 (view width greater than height) - // so we only check against the larger (horizontal) view angle - float front = glm::dot(offset, view.getDirection()) / distanceToCenter; - if (front > cosf(glm::radians(view.getFieldOfView())) || distance < view.getCenterRadius()) { - _priority = cube.getScale() / distance; // + front; - } else { - _priority = INVALID_ENTITY_SEND_PRIORITY; - } - } - } else { - // when in doubt give it something positive - _priority = 1.0f; - } + _priority = conicalView.computePriority(entity); } else { - _priority = INVALID_ENTITY_SEND_PRIORITY; + _priority = DO_NOT_SEND; } + return _priority; } TreeTraversalPath::Fork::Fork(EntityTreeElementPointer& element) : _nextIndex(0) { @@ -56,7 +78,7 @@ TreeTraversalPath::Fork::Fork(EntityTreeElementPointer& element) : _nextIndex(0) _weakElement = element; } -EntityTreeElementPointer TreeTraversalPath::Fork::getNextElement(const ViewFrustum& view) { +EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementFirstTime(const ViewFrustum& view) { if (_nextIndex == -1) { // only get here for the TreeTraversalPath's root Fork at the very beginning of traversal // safe to assume this element is in view @@ -68,7 +90,7 @@ EntityTreeElementPointer TreeTraversalPath::Fork::getNextElement(const ViewFrust while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; - if (nextElement && ViewFrustum::OUTSIDE != nextElement->computeViewIntersection(view)) { + if (nextElement && view.cubeIntersectsKeyhole(nextElement->getAACube())) { return nextElement; } } @@ -77,7 +99,7 @@ EntityTreeElementPointer TreeTraversalPath::Fork::getNextElement(const ViewFrust return EntityTreeElementPointer(); } -EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementAgain(const ViewFrustum& view, uint64_t oldTime) { +EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementAgain(const ViewFrustum& view, uint64_t lastTime) { if (_nextIndex == -1) { // only get here for the TreeTraversalPath's root Fork at the very beginning of traversal // safe to assume this element is in view @@ -91,8 +113,8 @@ EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementAgain(const View while (_nextIndex < NUMBER_OF_CHILDREN) { EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; - if (nextElement && nextElement->getLastChanged() > oldTime && - ViewFrustum::OUTSIDE != nextElement->computeViewIntersection(view)) { + if (nextElement && nextElement->getLastChanged() > lastTime && + nextElement && view.cubeIntersectsKeyhole(nextElement->getAACube())) { return nextElement; } } @@ -101,10 +123,10 @@ EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementAgain(const View return EntityTreeElementPointer(); } -EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementDelta(const ViewFrustum& newView, const ViewFrustum& oldView, uint64_t oldTime) { +EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementDifferential(const ViewFrustum& view, const ViewFrustum& lastView, uint64_t lastTime) { if (_nextIndex == -1) { // only get here for the TreeTraversalPath's root Fork at the very beginning of traversal - // safe to assume this element is in newView + // safe to assume this element is in view ++_nextIndex; EntityTreeElementPointer element = _weakElement.lock(); assert(element); // should never lose root element @@ -116,9 +138,9 @@ EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementDelta(const View EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex); ++_nextIndex; if (nextElement && - !(nextElement->getLastChanged() < oldTime && - ViewFrustum::INSIDE == nextElement->computeViewIntersection(oldView)) && - ViewFrustum::OUTSIDE != nextElement->computeViewIntersection(newView)) { + (!(nextElement->getLastChanged() < lastTime && + ViewFrustum::INSIDE == lastView.calculateCubeKeyholeIntersection(nextElement->getAACube()))) && + ViewFrustum::OUTSIDE != view.calculateCubeKeyholeIntersection(nextElement->getAACube())) { return nextElement; } } @@ -130,45 +152,41 @@ EntityTreeElementPointer TreeTraversalPath::Fork::getNextElementDelta(const View TreeTraversalPath::TreeTraversalPath() { const int32_t MIN_PATH_DEPTH = 16; _forks.reserve(MIN_PATH_DEPTH); - _traversalCallback = std::bind(&TreeTraversalPath::traverseFirstTime, this); } -void TreeTraversalPath::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root) { - if (_startOfLastCompletedTraversal == 0) { - _traversalCallback = std::bind(&TreeTraversalPath::traverseFirstTime, this); - _currentView = view; - } else if (_currentView.isVerySimilar(view)) { - _traversalCallback = std::bind(&TreeTraversalPath::traverseAgain, this); +void TreeTraversalPath::startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root) { + if (_startOfCompletedTraversal == 0) { + _currentView = viewFrustum; + _getNextElementCallback = [&]() { + return _forks.back().getNextElementFirstTime(_currentView); + }; + + } else if (_currentView.isVerySimilar(viewFrustum)) { + _getNextElementCallback = [&]() { + return _forks.back().getNextElementAgain(_currentView, _startOfCompletedTraversal); + }; } else { - _currentView = view; - _traversalCallback = std::bind(&TreeTraversalPath::traverseDelta, this); + _currentView = viewFrustum; + + _getNextElementCallback = [&]() { + return _forks.back().getNextElementDifferential(_currentView, _completedView, _startOfCompletedTraversal); + }; } + _forks.clear(); - if (root) { - _forks.push_back(Fork(root)); - // set root fork's index such that root element returned at getNextElement() - _forks.back().initRootNextIndex(); - } + assert(root); + _forks.push_back(Fork(root)); + // set root fork's index such that root element returned at getNextElement() + _forks.back().initRootNextIndex(); + _startOfCurrentTraversal = usecTimestampNow(); } -EntityTreeElementPointer TreeTraversalPath::traverseFirstTime() { - return _forks.back().getNextElement(_currentView); -} - -EntityTreeElementPointer TreeTraversalPath::traverseAgain() { - return _forks.back().getNextElementAgain(_currentView, _startOfLastCompletedTraversal); -} - -EntityTreeElementPointer TreeTraversalPath::traverseDelta() { - return _forks.back().getNextElementDelta(_currentView, _lastCompletedView, _startOfLastCompletedTraversal); -} - EntityTreeElementPointer TreeTraversalPath::getNextElement() { - if (_forks.empty() || !_traversalCallback) { + if (_forks.empty()) { return EntityTreeElementPointer(); } - EntityTreeElementPointer nextElement = _traversalCallback(); + EntityTreeElementPointer nextElement = _getNextElementCallback(); if (nextElement) { int8_t nextIndex = _forks.back().getNextIndex(); if (nextIndex > 0) { @@ -182,12 +200,12 @@ EntityTreeElementPointer TreeTraversalPath::getNextElement() { _forks.pop_back(); if (_forks.empty()) { // we've traversed the entire tree - _lastCompletedView = _currentView; - _startOfLastCompletedTraversal = _startOfCurrentTraversal; + _completedView = _currentView; + _startOfCompletedTraversal = _startOfCurrentTraversal; return nextElement; } // keep looking for nextElement - nextElement = _traversalCallback(); + nextElement = _getNextElementCallback(); if (nextElement) { // we've descended one level so add it to the path _forks.push_back(Fork(nextElement)); @@ -199,7 +217,7 @@ EntityTreeElementPointer TreeTraversalPath::getNextElement() { void TreeTraversalPath::dump() const { for (size_t i = 0; i < _forks.size(); ++i) { - std::cout << (int)(_forks[i].getNextIndex()) << "-->"; + std::cout << (int32_t)(_forks[i].getNextIndex()) << "-->"; } } @@ -267,66 +285,74 @@ void EntityTreeSendThread::preDistributionProcessing() { } } -static size_t adebug = 0; - void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, bool viewFrustumChanged, bool isFullScene) { - static int foo=0;++foo;bool verbose=(0==(foo%800)); // adebug - if (viewFrustumChanged || verbose) { - ViewFrustum view; - nodeData->copyCurrentViewFrustum(view); - std::cout << "adebug reset view" << std::endl; // adebug - EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot()); - _path.startNewTraversal(view, root); - - adebug = 0; + if (nodeData->getUsesFrustum()) { + if (viewFrustumChanged) { + ViewFrustum viewFrustum; + nodeData->copyCurrentViewFrustum(viewFrustum); + EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot()); + _path.startNewTraversal(viewFrustum, root); + } } if (!_path.empty()) { int32_t numElements = 0; uint64_t t0 = usecTimestampNow(); uint64_t now = t0; - QVector entities; + uint32_t numEntities = 0; EntityTreeElementPointer nextElement = _path.getNextElement(); + const ViewFrustum& currentView = _path.getCurrentView(); + TreeTraversalPath::ConicalView conicalView(currentView); while (nextElement) { - nextElement->getEntities(_path.getView(), entities); + nextElement->forEachEntity([&](EntityItemPointer entity) { + ++numEntities; + bool success = false; + AACube cube = entity->getQueryAACube(success); + if (success) { + if (currentView.cubeIntersectsKeyhole(cube)) { + float priority = conicalView.computePriority(cube); + _sendQueue.push(TreeTraversalPath::PrioritizedEntity(entity, priority)); + std::cout << "adebug '" << entity->getName().toStdString() << "' send = " << (priority != DO_NOT_SEND) << std::endl; // adebug + } else { + std::cout << "adebug '" << entity->getName().toStdString() << "' out of view" << std::endl; // adebug + } + } else { + const float WHEN_IN_DOUBT_PRIORITY = 1.0f; + _sendQueue.push(TreeTraversalPath::PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); + } + }); ++numElements; now = usecTimestampNow(); - const uint64_t PARTIAL_TRAVERSAL_TIME_BUDGET = 80; // usec + const uint64_t PARTIAL_TRAVERSAL_TIME_BUDGET = 100000; // usec if (now - t0 > PARTIAL_TRAVERSAL_TIME_BUDGET) { break; } nextElement = _path.getNextElement(); } uint64_t dt1 = now - t0; - for (EntityItemPointer& entity : entities) { - PrioritizedEntity entry(entity); - entry.updatePriority(_path.getView()); - if (entry.getPriority() > INVALID_ENTITY_SEND_PRIORITY) { - _sendQueue.push(entry); - } - } - adebug += entities.size(); - std::cout << "adebug traverseTreeAndSendContents totalEntities = " << adebug - << " numElements = " << numElements - << " numEntities = " << entities.size() - << " queueSize = " << _sendQueue.size() - << " dt = " << dt1 << std::endl; // adebug - } else if (!_sendQueue.empty()) { + + //} else if (!_sendQueue.empty()) { + size_t sendQueueSize = _sendQueue.size(); while (!_sendQueue.empty()) { - PrioritizedEntity entry = _sendQueue.top(); + TreeTraversalPath::PrioritizedEntity entry = _sendQueue.top(); EntityItemPointer entity = entry.getEntity(); if (entity) { - std::cout << "adebug traverseTreeAndSendContents() " << entry.getPriority() - << " '" << entity->getName().toStdString() << "'" - << std::endl; // adebug + std::cout << "adebug '" << entity->getName().toStdString() << "'" + << " : " << entry.getPriority() << std::endl; // adebug } _sendQueue.pop(); } // std::priority_queue doesn't have a clear method, // so we "clear" _sendQueue by setting it equal to an empty queue _sendQueue = EntityPriorityQueue(); + std::cout << "adebug -end" + << " E = " << numElements + << " e = " << numEntities + << " Q = " << sendQueueSize + << " dt = " << dt1 << std::endl; // adebug + std::cout << "adebug" << std::endl; // adebug } OctreeSendThread::traverseTreeAndSendContents(node, nodeData, viewFrustumChanged, isFullScene); diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index dd072454b9..a7ddf7daa1 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.h +++ b/assignment-client/src/entities/EntityTreeSendThread.h @@ -16,36 +16,71 @@ #include "../octree/OctreeSendThread.h" +#include + #include "EntityTreeElement.h" +const float SQRT_TWO_OVER_TWO = 0.7071067811865; +const float DEFAULT_VIEW_RADIUS = 10.0f; + class EntityNodeData; class EntityItem; -class PrioritizedEntity { -public: - PrioritizedEntity(EntityItemPointer entity) : _weakEntity(entity) { } - void updatePriority(const ViewFrustum& view); - EntityItemPointer getEntity() const { return _weakEntity.lock(); } - float getPriority() const { return _priority; } - - class Compare { - public: - bool operator() (const PrioritizedEntity& A, const PrioritizedEntity& B) { return A._priority < B._priority; } - }; - - friend class Compare; - -private: - EntityItemWeakPointer _weakEntity; - float _priority { 0.0f }; -}; -using EntityPriorityQueue = std::priority_queue< PrioritizedEntity, std::vector, PrioritizedEntity::Compare >; class TreeTraversalPath { public: + class ConicalView { + public: + ConicalView() {} + ConicalView(const ViewFrustum& viewFrustum) { set(viewFrustum); } + void set(const ViewFrustum& viewFrustum); + float computePriority(const AACube& cube) const; + float computePriority(const EntityItemPointer& entity) const; + private: + glm::vec3 _position { 0.0f, 0.0f, 0.0f }; + glm::vec3 _direction { 0.0f, 0.0f, 1.0f }; + float _sinAngle { SQRT_TWO_OVER_TWO }; + float _cosAngle { SQRT_TWO_OVER_TWO }; + float _radius { DEFAULT_VIEW_RADIUS }; + }; + + class PrioritizedEntity { + public: + PrioritizedEntity(EntityItemPointer entity, float priority) : _weakEntity(entity), _priority(priority) { } + float updatePriority(const ConicalView& view); + EntityItemPointer getEntity() const { return _weakEntity.lock(); } + float getPriority() const { return _priority; } + + class Compare { + public: + bool operator() (const PrioritizedEntity& A, const PrioritizedEntity& B) { return A._priority < B._priority; } + }; + friend class Compare; + + private: + EntityItemWeakPointer _weakEntity; + float _priority; + }; + + class Fork { + public: + Fork(EntityTreeElementPointer& element); + + EntityTreeElementPointer getNextElementFirstTime(const ViewFrustum& view); + EntityTreeElementPointer getNextElementAgain(const ViewFrustum& view, uint64_t lastTime); + EntityTreeElementPointer getNextElementDifferential(const ViewFrustum& view, const ViewFrustum& lastView, uint64_t lastTime); + + int8_t getNextIndex() const { return _nextIndex; } + void initRootNextIndex() { _nextIndex = -1; } + + protected: + EntityTreeElementWeakPointer _weakElement; + int8_t _nextIndex; + }; + TreeTraversalPath(); - void startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root); + void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root); EntityTreeElementPointer getNextElement(); @@ -55,34 +90,21 @@ public: size_t size() const { return _forks.size(); } // adebug void dump() const; - class Fork { - public: - Fork(EntityTreeElementPointer& element); - - EntityTreeElementPointer getNextElement(const ViewFrustum& view); - EntityTreeElementPointer getNextElementAgain(const ViewFrustum& view, uint64_t oldTime); - EntityTreeElementPointer getNextElementDelta(const ViewFrustum& newView, const ViewFrustum& oldView, uint64_t oldTime); - int8_t getNextIndex() const { return _nextIndex; } - void initRootNextIndex() { _nextIndex = -1; } - - protected: - EntityTreeElementWeakPointer _weakElement; - int8_t _nextIndex; - }; + const ViewFrustum& getCurrentView() const { return _currentView; } + //float computePriority(EntityItemPointer& entity) const { return _computePriorityCallback(entity); } protected: - EntityTreeElementPointer traverseFirstTime(); - EntityTreeElementPointer traverseAgain(); - EntityTreeElementPointer traverseDelta(); - ViewFrustum _currentView; - ViewFrustum _lastCompletedView; + ViewFrustum _completedView; std::vector _forks; - std::function _traversalCallback { nullptr }; - uint64_t _startOfLastCompletedTraversal { 0 }; + std::function _getNextElementCallback { nullptr }; + //std::function _computePriorityCallback { nullptr }; + uint64_t _startOfCompletedTraversal { 0 }; uint64_t _startOfCurrentTraversal { 0 }; }; +using EntityPriorityQueue = std::priority_queue< TreeTraversalPath::PrioritizedEntity, std::vector, TreeTraversalPath::PrioritizedEntity::Compare >; + class EntityTreeSendThread : public OctreeSendThread {