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 | #include "EntityTreeSendThread.h" | 12 | #include "EntityTreeSendThread.h" |
| 13 | #include <iostream> // adebug | ||
| 14 | 13 | ||
| 15 | #include <EntityNodeData.h> | 14 | #include <EntityNodeData.h> |
| 16 | #include <EntityTypes.h> | 15 | #include <EntityTypes.h> |
| 17 | 16 | ||
| 18 | #include "EntityServer.h" | 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 | void EntityTreeSendThread::preDistributionProcessing() { | 19 | void EntityTreeSendThread::preDistributionProcessing() { |
| 27 | auto node = _node.toStrongRef(); | 20 | auto node = _node.toStrongRef(); |
| 28 | auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData()); | 21 | auto nodeData = static_cast<EntityNodeData*>(node->getLinkedData()); |
| @@ -90,14 +83,14 @@ void EntityTreeSendThread::preDistributionProcessing() { · 61 unmodified lines | |||
| 90 | void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, | 83 | void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData, |
| 91 | bool viewFrustumChanged, bool isFullScene) { | 84 | bool viewFrustumChanged, bool isFullScene) { |
| 92 | // BEGIN EXPERIMENTAL DIFFERENTIAL TRAVERSAL | 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 | if (nodeData->getUsesFrustum()) { | 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 | if (viewFrustumChanged) { | 94 | if (viewFrustumChanged) { |
| 102 | ViewFrustum viewFrustum; | 95 | ViewFrustum viewFrustum; |
| 103 | nodeData->copyCurrentViewFrustum(viewFrustum); | 96 | nodeData->copyCurrentViewFrustum(viewFrustum); |
| @@ -105,28 +98,14 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O · 1 unmodified line | |||
| 105 | startNewTraversal(viewFrustum, root); | 98 | startNewTraversal(viewFrustum, root); |
| 106 | } | 99 | } |
| 107 | } | 100 | } |
| 108 | if (!_traversalPath.empty()) { | 101 | if (!_traversal.finished()) { |
| 109 | uint64_t startTime = usecTimestampNow(); | 102 | uint64_t startTime = usecTimestampNow(); |
| 110 | uint64_t now = startTime; | ||
| 111 | 103 | ||
| 112 | VisibleElement next; | 104 | const uint64_t TIME_BUDGET = 100000; // usec |
| 113 | getNextVisibleElement(next); | 105 | _traversal.traverse(TIME_BUDGET); |
| 114 | while (next.element) { | ||
| 115 | if (next.element->hasContent()) { | ||
| 116 | _scanNextElementCallback(next); | ||
| 117 | } | ||
| 118 | 106 | ||
| 119 | // TODO: pick a reasonable budget for each partial traversal | 107 | uint64_t dt = usecTimestampNow() - startTime; |
| 120 | const uint64_t PARTIAL_TRAVERSAL_TIME_BUDGET = 100000; // usec | 108 | std::cout << "adebug traversal complete " << " Q.size = " << _sendQueue.size() << " dt = " << dt << std::endl; // adebug |
| 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 | ||
| 130 | } | 109 | } |
| 131 | if (!_sendQueue.empty()) { | 110 | if (!_sendQueue.empty()) { |
| 132 | // print what needs to be sent | 111 | // print what needs to be sent |
| @@ -134,11 +113,9 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O · 1 unmodified line | |||
| 134 | PrioritizedEntity entry = _sendQueue.top(); | 113 | PrioritizedEntity entry = _sendQueue.top(); |
| 135 | EntityItemPointer entity = entry.getEntity(); | 114 | EntityItemPointer entity = entry.getEntity(); |
| 136 | if (entity) { | 115 | if (entity) { |
| 137 | std::cout << "adebug '" << entity->getName().toStdString() << "'" | 116 | std::cout << "adebug send '" << entity->getName().toStdString() << "'" << " : " << entry.getPriority() << std::endl; // adebug |
| 138 | << " : " << entry.getPriority() << std::endl; // adebug | ||
| 139 | } | 117 | } |
| 140 | _sendQueue.pop(); | 118 | _sendQueue.pop(); |
| 141 | std::cout << "adebug" << std::endl; // adebug | ||
| 142 | } | 119 | } |
| 143 | } | 120 | } |
| 144 | // END EXPERIMENTAL DIFFERENTIAL TRAVERSAL | 121 | // END EXPERIMENTAL DIFFERENTIAL TRAVERSAL |
| @@ -195,39 +172,29 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil · 50 unmodified lines | |||
| 195 | return hasNewChild || hasNewDescendants; | 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 | // there are three types of traversal: | 177 | // there are three types of traversal: |
| 200 | // | 178 | // |
| 201 | // (1) FirstTime = at login --> find everything in view | 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 | // (3) Differential = view has changed --> find what has changed or in new view but not old | 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: | 183 | // The "scanCallback" we provide to the traversal depends on the type: |
| 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 | ||
| 212 | // | 184 | // |
| 213 | // The _conicalView is updated here as a cached view approximation used by the lambdas for efficient | 185 | // The _conicalView is updated here as a cached view approximation used by the lambdas for efficient |
| 214 | // computation of entity sorting priorities. | 186 | // computation of entity sorting priorities. |
| 215 | // | 187 | // |
| 216 | if (_startOfCompletedTraversal == 0) { | 188 | _conicalView.set(_traversal.getCurrentView()); |
| 217 | // first time | ||
| 218 | _currentView = viewFrustum; | ||
| 219 | _conicalView.set(_currentView); | ||
| 220 | |||
| 221 | _getNextVisibleElementCallback = [&](VisibleElement& next) { | ||
| 222 | _traversalPath.back().getNextVisibleElementFirstTime(next, _currentView); | ||
| 223 | }; | ||
| 224 | 189 | ||
| 225 | _scanNextElementCallback = [&](VisibleElement& next) { | 190 | switch (type) { |
| 191 | case DiffTraversal::First: | ||
| 192 | _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { | ||
| 226 | next.element->forEachEntity([&](EntityItemPointer entity) { | 193 | next.element->forEachEntity([&](EntityItemPointer entity) { |
| 227 | bool success = false; | 194 | bool success = false; |
| 228 | AACube cube = entity->getQueryAACube(success); | 195 | AACube cube = entity->getQueryAACube(success); |
| 229 | if (success) { | 196 | if (success) { |
| 230 | if (_currentView.cubeIntersectsKeyhole(cube)) { | 197 | if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { |
| 231 | float priority = _conicalView.computePriority(cube); | 198 | float priority = _conicalView.computePriority(cube); |
| 232 | _sendQueue.push(PrioritizedEntity(entity, priority)); | 199 | _sendQueue.push(PrioritizedEntity(entity, priority)); |
| 233 | } | 200 | } |
| @@ -236,21 +203,18 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& viewFrustum, Ent · 2 unmodified lines | |||
| 236 | _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); | 203 | _sendQueue.push(PrioritizedEntity(entity, WHEN_IN_DOUBT_PRIORITY)); |
| 237 | } | 204 | } |
| 238 | }); | 205 | }); |
| 239 | }; | 206 | }); |
| 240 | } else if (_currentView.isVerySimilar(viewFrustum)) { | 207 | break; |
| 241 | // again | 208 | case DiffTraversal::Repeat: |
| 242 | _getNextVisibleElementCallback = [&](VisibleElement& next) { | 209 | _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { |
| 243 | _traversalPath.back().getNextVisibleElementAgain(next, _currentView, _startOfCompletedTraversal); | 210 | if (next.element->getLastChangedContent() > _traversal.getStartOfCompletedTraversal()) { |
| 244 | }; | 211 | uint64_t timestamp = _traversal.getStartOfCompletedTraversal(); |
| 245 | |||
| 246 | _scanNextElementCallback = [&](VisibleElement& next) { | ||
| 247 | if (next.element->getLastChangedContent() > _startOfCompletedTraversal) { | ||
| 248 | next.element->forEachEntity([&](EntityItemPointer entity) { | 212 | next.element->forEachEntity([&](EntityItemPointer entity) { |
| 249 | if (entity->getLastEdited() > _startOfCompletedTraversal) { | 213 | if (entity->getLastEdited() > timestamp) { |
| 250 | bool success = false; | 214 | bool success = false; |
| 251 | AACube cube = entity->getQueryAACube(success); | 215 | AACube cube = entity->getQueryAACube(success); |
| 252 | if (success) { | 216 | if (success) { |
| 253 | if (next.intersection == ViewFrustum::INSIDE || _currentView.cubeIntersectsKeyhole(cube)) { | 217 | if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) { |
| 254 | float priority = _conicalView.computePriority(cube); | 218 | float priority = _conicalView.computePriority(cube); |
| 255 | _sendQueue.push(PrioritizedEntity(entity, priority)); | 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 | }; | 228 | }); |
| 265 | } else { | 229 | break; |
| 266 | // differential | 230 | case DiffTraversal::Differential: |
| 267 | _currentView = viewFrustum; | 231 | _traversal.setScanCallback([&] (DiffTraversal::VisibleElement& next) { |
| 268 | _conicalView.set(_currentView); | 232 | // NOTE: for Differential case: next.intersection is against completedView not currentView |
| 269 | 233 | uint64_t timestamp = _traversal.getStartOfCompletedTraversal(); | |
| 270 | _getNextVisibleElementCallback = [&](VisibleElement& next) { | 234 | if (next.element->getLastChangedContent() > timestamp || next.intersection != ViewFrustum::INSIDE) { |
| 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) { | ||
| 277 | next.element->forEachEntity([&](EntityItemPointer entity) { | 235 | next.element->forEachEntity([&](EntityItemPointer entity) { |
| 278 | bool success = false; | 236 | bool success = false; |
| 279 | AACube cube = entity->getQueryAACube(success); | 237 | AACube cube = entity->getQueryAACube(success); |
| 280 | if (success) { | 238 | if (success) { |
| 281 | if (_currentView.cubeIntersectsKeyhole(cube) && | 239 | if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube) && |
| 282 | (entity->getLastEdited() > _startOfCompletedTraversal || | 240 | (entity->getLastEdited() > timestamp || |
| 283 | !_completedView.cubeIntersectsKeyhole(cube))) { | 241 | !_traversal.getCompletedView().cubeIntersectsKeyhole(cube))) { |
| 284 | float priority = _conicalView.computePriority(cube); | 242 | float priority = _conicalView.computePriority(cube); |
| 285 | _sendQueue.push(PrioritizedEntity(entity, priority)); | 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 | }; | 251 | }); |
| 294 | } | 252 | break; |
| 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 | } | ||
| 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 | } | ||