Showing the changes to assignment-client/src/entities/EntityTreeSendThread.cpp (1 of 6 files). Show all
Unified · Split
| @@ -10,19 +10,12 @@ · 9 unmodified lines | ||
| 10 | 10 | // |
| 11 | 11 | |
| 12 | 12 | #include "EntityTreeSendThread.h" |
| 13 | −#include <iostream> // adebug | |
| 14 | 13 | |
| 15 | 14 | #include <EntityNodeData.h> |
| 16 | 15 | #include <EntityTypes.h> |
| 17 | 16 | |
| 18 | 17 | #include "EntityServer.h" |
| 19 | 18 | |
| 20 | −EntityTreeSendThread::EntityTreeSendThread(OctreeServer* myServer, const SharedNodePointer& node) | |
| 21 | − : OctreeSendThread(myServer, node) { | |
| 22 | − const int32_t MIN_PATH_DEPTH = 16; | |
| 23 | − _traversalPath.reserve(MIN_PATH_DEPTH); | |
| 24 | −} | |
| 25 | − | |
| 26 | 19 | void EntityTreeSendThread::preDistributionProcessing() { |
| 27 | 20 | auto node = _node.toStrongRef(); |
| 28 | 21 | auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData()); |
| @@ -90,14 +83,14 @@ void EntityTreeSendThread::preDistributionProcessing() { · 61 unmodified lines | ||
| 90 | 83 | void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, |
| 91 | 84 | bool viewFrustumChanged, bool isFullScene) { |
| 92 | 85 | // BEGIN EXPERIMENTAL DIFFERENTIAL TRAVERSAL |
| 93 | − { | |
| 94 | − // DEBUG HACK: trigger traversal (Again) every so often | |
| 95 | − const uint64_t TRAVERSE_AGAIN_PERIOD = 2 * USECS_PER_SECOND; | |
| 96 | − if (!viewFrustumChanged && usecTimestampNow() > _startOfCompletedTraversal + TRAVERSE_AGAIN_PERIOD) { | |
| 97 | − viewFrustumChanged = true; | |
| 98 | − } | |
| 99 | − } | |
| 100 | 86 | if (nodeData->getUsesFrustum()) { |
| 87 | + { | |
| 88 | + // DEBUG HACK: trigger traversal (Again) every so often | |
| 89 | + const uint64_t TRAVERSE_AGAIN_PERIOD = 4 * USECS_PER_SECOND; | |
| 90 | + if (!viewFrustumChanged && usecTimestampNow() > _traversal.getStartOfCompletedTraversal() + TRAVERSE_AGAIN_PERIOD) { | |
| 91 | + viewFrustumChanged = true; | |
| 92 | + } | |
| 93 | + } | |
| 101 | 94 | if (viewFrustumChanged) { |
| 102 | 95 | ViewFrustum viewFrustum; |
| 103 | 96 | nodeData->copyCurrentViewFrustum(viewFrustum); |
| @@ -105,28 +98,14 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O · 1 unmodified line | ||
| 105 | 98 | startNewTraversal(viewFrustum, root); |
| 106 | 99 | } |
| 107 | 100 | } |
| 108 | − if (!_traversalPath.empty()) { | |
| 101 | + if (!_traversal.finished()) { | |
| 109 | 102 | uint64_t startTime = usecTimestampNow(); |
| 110 | − uint64_t now = startTime; | |
| 111 | 103 | |
| 112 | − VisibleElement next; | |
| 113 | − getNextVisibleElement(next); | |
| 114 | − while (next.element) { | |
| 115 | − if (next.element->hasContent()) { | |
| 116 | − _scanNextElementCallback(next); | |
| 117 | − } | |
| 104 | + const uint64_t TIME_BUDGET = 100000; // usec | |
| 105 | + _traversal.traverse(TIME_BUDGET); | |
| 118 | 106 | |
| 119 | − // TODO: pick a reasonable budget for each partial traversal | |
| 120 | − const uint64_t PARTIAL_TRAVERSAL_TIME_BUDGET = 100000; // usec | |
| 121 | − now = usecTimestampNow(); | |
| 122 | − if (now - startTime > PARTIAL_TRAVERSAL_TIME_BUDGET) { | |
| 123 | − break; | |
| 124 | − } | |
| 125 | − getNextVisibleElement(next); | |
| 126 | − } | |
| 127 | − | |
| 128 | − uint64_t dt = now - startTime; | |
| 129 | − std::cout << "adebug traversal complete " << " Q.size = " << _sendQueue.size() << " dt = " << dt << std::endl; // adebug | |
| 107 | + uint64_t dt = usecTimestampNow() - startTime; | |
| 108 | + std::cout << "adebug traversal complete " << " Q.size = " << _sendQueue.size() << " dt = " << dt << std::endl; // adebug | |
| 130 | 109 | } |
| 131 | 110 | if (!_sendQueue.empty()) { |
| 132 | 111 | // print what needs to be sent |
| @@ -134,11 +113,9 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O · 1 unmodified line | ||
| 134 | 113 | PrioritizedEntity entry = _sendQueue.top(); |
| 135 | 114 | EntityItemPointer entity = entry.getEntity(); |
| 136 | 115 | if (entity) { |
| 137 | − std::cout << "adebug '" << entity->getName().toStdString() << "'" | |
| 138 | − << " : " << entry.getPriority() << std::endl; // adebug | |
| 116 | + std::cout << "adebug send '" << entity->getName().toStdString() << "'" << " : " << entry.getPriority() << std::endl; // adebug | |
| 139 | 117 | } |
| 140 | 118 | _sendQueue.pop(); |
| 141 | − std::cout << "adebug" << std::endl; // adebug | |
| 142 | 119 | } |
| 143 | 120 | } |
| 144 | 121 | // END EXPERIMENTAL DIFFERENTIAL TRAVERSAL |
| @@ -195,39 +172,29 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil · 50 unmodified lines | ||
| 195 | 172 | return hasNewChild || hasNewDescendants; |
| 196 | 173 | } |
| 197 | 174 | |
| 198 | −void EntityTreeSendThread::startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root) { | |
| 175 | +void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root) { | |
| 176 | + DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root); | |
| 199 | 177 | // there are three types of traversal: |
| 200 | 178 | // |
| 201 | 179 | // (1) FirstTime = at login --> find everything in view |
| 202 | − // (2) Again = view hasn't changed --> find what has changed since last complete traversal | |
| 180 | + // (2) Repeat = view hasn't changed --> find what has changed since last complete traversal | |
| 203 | 181 | // (3) Differential = view has changed --> find what has changed or in new view but not old |
| 204 | 182 | // |
| 205 | − // For each traversal type we define two callback lambdas: | |
| 206 | − // | |
| 207 | − // _getNextVisibleElementCallback = identifies elements that need to be traversed,i | |
| 208 | − // updates VisibleElement ref argument with pointer-to-element and view-intersection | |
| 209 | − // (INSIDE, INTERSECT, or OUTSIDE) | |
| 210 | − // | |
| 211 | − // _scanNextElementCallback = identifies entities that need to be appended to _sendQueue | |
| 183 | + // The "scanCallback" we provide to the traversal depends on the type: | |
| 212 | 184 | // |
| 213 | 185 | // The _conicalView is updated here as a cached view approximation used by the lambdas for efficient |
| 214 | 186 | // computation of entity sorting priorities. |
| 215 | 187 | // |
| 216 | − if (_startOfCompletedTraversal == 0) { | |
| 217 | − // first time | |
| 218 | − _currentView = viewFrustum; | |
| 219 | − _conicalView.set(_currentView); | |
| 220 | − | |
| 221 | − _getNextVisibleElementCallback = [&](VisibleElement& next) { | |
| 222 | − _traversalPath.back().getNextVisibleElementFirstTime(next, _currentView); | |
| 223 | − }; | |
| 188 | + _conicalView.set(_traversal.getCurrentView()); | |
| 224 | 189 | |
| 225 | − _scanNextElementCallback = [&](VisibleElement& next) { | |
| 190 | + switch (type) { | |
| 191 | + case DiffTraversal::First: | |
| 192 | + _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { | |
| 226 | 193 | next.element->forEachEntity([&](EntityItemPointer entity) { |
| 227 | 194 | bool success = false; |
| 228 | 195 | AACube cube = entity->getQueryAACube(success); |
| 229 | 196 | if (success) { |
| 230 | − if (_currentView.cubeIntersectsKeyhole(cube)) { | |
| 197 | + if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { | |
| 231 | 198 | float priority = _conicalView.computePriority(cube); |
| 232 | 199 | _sendQueue.push(PrioritizedEntity(entity, priority)); |
| 233 | 200 | } |
| @@ -236,21 +203,18 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& viewFrustum, Ent · 2 unmodified lines | ||
| 236 | 203 | _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); |
| 237 | 204 | } |
| 238 | 205 | }); |
| 239 | − }; | |
| 240 | − } else if (_currentView.isVerySimilar(viewFrustum)) { | |
| 241 | − // again | |
| 242 | − _getNextVisibleElementCallback = [&](VisibleElement& next) { | |
| 243 | − _traversalPath.back().getNextVisibleElementAgain(next, _currentView, _startOfCompletedTraversal); | |
| 244 | − }; | |
| 245 | − | |
| 246 | − _scanNextElementCallback = [&](VisibleElement& next) { | |
| 247 | − if (next.element->getLastChangedContent() > _startOfCompletedTraversal) { | |
| 206 | + }); | |
| 207 | + break; | |
| 208 | + case DiffTraversal::Repeat: | |
| 209 | + _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { | |
| 210 | + if (next.element->getLastChangedContent() > _traversal.getStartOfCompletedTraversal()) { | |
| 211 | + uint64_t timestamp = _traversal.getStartOfCompletedTraversal(); | |
| 248 | 212 | next.element->forEachEntity([&](EntityItemPointer entity) { |
| 249 | − if (entity->getLastEdited() > _startOfCompletedTraversal) { | |
| 213 | + if (entity->getLastEdited() > timestamp) { | |
| 250 | 214 | bool success = false; |
| 251 | 215 | AACube cube = entity->getQueryAACube(success); |
| 252 | 216 | if (success) { |
| 253 | − if (next.intersection == ViewFrustum::INSIDE || _currentView.cubeIntersectsKeyhole(cube)) { | |
| 217 | + if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { | |
| 254 | 218 | float priority = _conicalView.computePriority(cube); |
| 255 | 219 | _sendQueue.push(PrioritizedEntity(entity, priority)); |
| 256 | 220 | } |
| @@ -261,26 +225,20 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& viewFrustum, Ent · 4 unmodified lines | ||
| 261 | 225 | } |
| 262 | 226 | }); |
| 263 | 227 | } |
| 264 | − }; | |
| 265 | − } else { | |
| 266 | − // differential | |
| 267 | − _currentView = viewFrustum; | |
| 268 | − _conicalView.set(_currentView); | |
| 269 | − | |
| 270 | − _getNextVisibleElementCallback = [&](VisibleElement& next) { | |
| 271 | − _traversalPath.back().getNextVisibleElementDifferential(next, _currentView, _completedView, _startOfCompletedTraversal); | |
| 272 | − }; | |
| 273 | − | |
| 274 | − _scanNextElementCallback = [&](VisibleElement& next) { | |
| 275 | − // NOTE: for differential case next.intersection is against _completedView not _currentView | |
| 276 | − if (next.element->getLastChangedContent() > _startOfCompletedTraversal || next.intersection != ViewFrustum::INSIDE) { | |
| 228 | + }); | |
| 229 | + break; | |
| 230 | + case DiffTraversal::Differential: | |
| 231 | + _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { | |
| 232 | + // NOTE: for Differential case: next.intersection is against completedView not currentView | |
| 233 | + uint64_t timestamp = _traversal.getStartOfCompletedTraversal(); | |
| 234 | + if (next.element->getLastChangedContent() > timestamp || next.intersection != ViewFrustum::INSIDE) { | |
| 277 | 235 | next.element->forEachEntity([&](EntityItemPointer entity) { |
| 278 | 236 | bool success = false; |
| 279 | 237 | AACube cube = entity->getQueryAACube(success); |
| 280 | 238 | if (success) { |
| 281 | − if (_currentView.cubeIntersectsKeyhole(cube) && | |
| 282 | − (entity->getLastEdited() > _startOfCompletedTraversal || | |
| 283 | − !_completedView.cubeIntersectsKeyhole(cube))) { | |
| 239 | + if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube) && | |
| 240 | + (entity->getLastEdited() > timestamp || | |
| 241 | + !_traversal.getCompletedView().cubeIntersectsKeyhole(cube))) { | |
| 284 | 242 | float priority = _conicalView.computePriority(cube); |
| 285 | 243 | _sendQueue.push(PrioritizedEntity(entity, priority)); |
| 286 | 244 | } |
| @@ -290,55 +248,8 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& viewFrustum, Ent · 3 unmodified lines | ||
| 290 | 248 | } |
| 291 | 249 | }); |
| 292 | 250 | } |
| 293 | − }; | |
| 294 | − } | |
| 295 | − | |
| 296 | − _traversalPath.clear(); | |
| 297 | − assert(root); | |
| 298 | − _traversalPath.push_back(TraversalWaypoint(root)); | |
| 299 | − // set root fork's index such that root element returned at getNextElement() | |
| 300 | − _traversalPath.back().initRootNextIndex(); | |
| 301 | − | |
| 302 | − _startOfCurrentTraversal = usecTimestampNow(); | |
| 303 | −} | |
| 304 | − | |
| 305 | −void EntityTreeSendThread::getNextVisibleElement(VisibleElement& next) { | |
| 306 | − if (_traversalPath.empty()) { | |
| 307 | − next.element.reset(); | |
| 308 | − next.intersection = ViewFrustum::OUTSIDE; | |
| 309 | − return; | |
| 310 | − } | |
| 311 | − _getNextVisibleElementCallback(next); | |
| 312 | − if (next.element) { | |
| 313 | − int8_t nextIndex = _traversalPath.back().getNextIndex(); | |
| 314 | − if (nextIndex > 0) { | |
| 315 | − // next.element needs to be added to the path | |
| 316 | − _traversalPath.push_back(TraversalWaypoint(next.element)); | |
| 317 | − } | |
| 318 | − } else { | |
| 319 | − // we're done at this level | |
| 320 | − while (!next.element) { | |
| 321 | − // pop one level | |
| 322 | − _traversalPath.pop_back(); | |
| 323 | − if (_traversalPath.empty()) { | |
| 324 | − // we've traversed the entire tree | |
| 325 | − _completedView = _currentView; | |
| 326 | − _startOfCompletedTraversal = _startOfCurrentTraversal; | |
| 327 | − return; | |
| 328 | − } | |
| 329 | − // keep looking for next | |
| 330 | − _getNextVisibleElementCallback(next); | |
| 331 | − if (next.element) { | |
| 332 | − // we've descended one level so add it to the path | |
| 333 | − _traversalPath.push_back(TraversalWaypoint(next.element)); | |
| 334 | − } | |
| 335 | − } | |
| 251 | + }); | |
| 252 | + break; | |
| 336 | 253 | } |
| 337 | 254 | } |
| 338 | 255 | |
| 339 | −// DEBUG method: delete later | |
| 340 | −void EntityTreeSendThread::dump() const { | |
| 341 | − for (size_t i = 0; i < _traversalPath.size(); ++i) { | |
| 342 | − std::cout << (int32_t)(_traversalPath[i].getNextIndex()) << "-->"; | |
| 343 | − } | |
| 344 | −} | |