diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 673bba2408..116a1dcc84 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -349,6 +349,7 @@ int OctreeSendThread::packetDistributor(OctreeQueryNode* nodeData, bool viewFrus // track completed scenes and send out the stats packet accordingly nodeData->stats.sceneCompleted(); nodeData->setLastRootTimestamp(_myServer->getOctree()->getRoot()->getLastChanged()); + _myServer->getOctree()->releaseSceneEncodeData(&nodeData->extraEncodeData); // TODO: add these to stats page //::endSceneSleepTime = _usleepTime; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 3a6fb70769..5a0f130aa7 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -516,6 +516,14 @@ void EntityTree::removeNewlyCreatedHook(NewlyCreatedEntityHook* hook) { } +void EntityTree::releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { + foreach(void* extraData, *extraEncodeData) { + EntityTreeElementExtraEncodeData* thisExtraEncodeData = static_cast(extraData); + delete thisExtraEncodeData; + } + extraEncodeData->clear(); +} + void EntityTree::changeEntityState(EntityItem* const entity, EntityItem::SimulationState oldState, EntityItem::SimulationState newState) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5ded476ec0..f05eb69538 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -66,6 +66,7 @@ public: // the root at least needs to store the number of entities in the packet/buffer virtual int minimumRequiredRootDataBytes() const { return sizeof(uint16_t); } virtual bool suppressEmptySubtrees() const { return false; } + virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const; virtual bool versionHasSVOfileBreaks(PacketVersion thisVersion) const { return thisVersion >= VERSION_ENTITIES_HAS_FILE_BREAKS; } diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index ec7c470fb2..7a53c93ba3 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -94,7 +94,7 @@ void EntityTreeElement::initializeExtraEncodeData(EncodeBitstreamParams& params) } } -bool EntityTreeElement::shouldIncludeChild(int childIndex, EncodeBitstreamParams& params) const { +bool EntityTreeElement::shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const { OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes @@ -114,6 +114,30 @@ bool EntityTreeElement::shouldIncludeChild(int childIndex, EncodeBitstreamParams return false; } +bool EntityTreeElement::shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const { + EntityTreeElement* childElement = getChildAtIndex(childIndex); + if (childElement->alreadyFullyEncoded(params)) { + return false; + } + + return true; // if we don't know otherwise than recurse! +} + +bool EntityTreeElement::alreadyFullyEncoded(EncodeBitstreamParams& params) const { + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; + assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes + + if (extraEncodeData->contains(this)) { + EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData + = static_cast(extraEncodeData->value(this)); + + // If we know that ALL subtrees below us have already been recursed, then we don't + // need to recurse this child. + return entityTreeElementExtraEncodeData->subtreeCompleted; + } + return false; +} + void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const { OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes @@ -133,6 +157,12 @@ void EntityTreeElement::updateEncodedData(int childIndex, AppendState childAppen void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, OctreeElementBag* bag) const { + const bool wantDebug = false; + + if (wantDebug) { + qDebug() << "EntityTreeElement::elementEncodeComplete() element:" << getAACube(); + } + OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; assert(extraEncodeData); // EntityTrees always require extra encode data on their encoding passes assert(extraEncodeData->contains(this)); @@ -152,10 +182,10 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, Oct // 1) it's ok for our child trees to not yet be fully encoded/complete... // SO LONG AS... the our child's node is in the bag ready for encoding + bool someChildTreeNotComplete = false; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { EntityTreeElement* childElement = getChildAtIndex(i); if (childElement) { - bool isThisChildReallyComplete = thisExtraEncodeData->childCompleted[i]; // why would this ever fail??? // If we've encoding this element before... but we're coming back a second time in an attempt to @@ -163,20 +193,44 @@ void EntityTreeElement::elementEncodeComplete(EncodeBitstreamParams& params, Oct if (extraEncodeData->contains(childElement)) { EntityTreeElementExtraEncodeData* childExtraEncodeData = static_cast(extraEncodeData->value(childElement)); - - for (int ii = 0; ii < NUMBER_OF_CHILDREN; ii++) { - if (!childExtraEncodeData->childCompleted[ii]) { - isThisChildReallyComplete = false; + + if (wantDebug) { + qDebug() << "checking child: " << childElement->getAACube(); + qDebug() << " childElement->isLeaf():" << childElement->isLeaf(); + qDebug() << " childExtraEncodeData->elementCompleted:" << childExtraEncodeData->elementCompleted; + qDebug() << " childExtraEncodeData->subtreeCompleted:" << childExtraEncodeData->subtreeCompleted; + } + + if (childElement->isLeaf() && childExtraEncodeData->elementCompleted) { + if (wantDebug) { + qDebug() << " CHILD IS LEAF -- AND CHILD ELEMENT DATA COMPLETED!!!"; } + childExtraEncodeData->subtreeCompleted = true; } - if (isThisChildReallyComplete) { - extraEncodeData->remove(childElement); - delete childExtraEncodeData; + if (!childExtraEncodeData->elementCompleted || !childExtraEncodeData->subtreeCompleted) { + someChildTreeNotComplete = true; } } } } + + if (wantDebug) { + qDebug() << "for this element: " << getAACube(); + qDebug() << " WAS elementCompleted:" << thisExtraEncodeData->elementCompleted; + qDebug() << " WAS subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted; + } + + thisExtraEncodeData->subtreeCompleted = !someChildTreeNotComplete; + + if (wantDebug) { + qDebug() << " NOW elementCompleted:" << thisExtraEncodeData->elementCompleted; + qDebug() << " NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted; + + if (thisExtraEncodeData->subtreeCompleted) { + qDebug() << " YEAH!!!!! >>>>>>>>>>>>>> NOW subtreeCompleted:" << thisExtraEncodeData->subtreeCompleted; + } + } } OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData, diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index 1e72f356de..5790903411 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -40,10 +40,12 @@ class EntityTreeElementExtraEncodeData { public: EntityTreeElementExtraEncodeData() : elementCompleted(false), + subtreeCompleted(false), entities() { memset(childCompleted, 0, sizeof(childCompleted)); } bool elementCompleted; + bool subtreeCompleted; bool childCompleted[NUMBER_OF_CHILDREN]; QMap entities; }; @@ -51,6 +53,7 @@ public: inline QDebug operator<<(QDebug debug, const EntityTreeElementExtraEncodeData* data) { debug << "{"; debug << " elementCompleted: " << data->elementCompleted << ", "; + debug << " subtreeCompleted: " << data->subtreeCompleted << ", "; debug << " childCompleted[]: "; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { debug << " " << i << ":" << data->childCompleted[i] << ", "; @@ -109,10 +112,13 @@ public: virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const; virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) const; - virtual bool shouldIncludeChild(int childIndex, EncodeBitstreamParams& params) const; + virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const; + virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const; virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const; virtual void elementEncodeComplete(EncodeBitstreamParams& params, OctreeElementBag* bag) const; + bool alreadyFullyEncoded(EncodeBitstreamParams& params) const; + /// Override to serialize the state of this element. This is used for persistance and for transmission across the network. virtual OctreeElement::AppendState appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 3e712a8472..82857a886f 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1401,7 +1401,7 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // the childrenDataBits were set up by the in view/LOD logic, it may contain children that we've already // processed and sent the data bits for. Let our tree subclass determine if it really wants to send the // data for this child at this point - if (childElement && element->shouldIncludeChild(i, params)) { + if (childElement && element->shouldIncludeChildData(i, params)) { int bytesBeforeChild = packetData->getUncompressedSize(); @@ -1553,8 +1553,15 @@ int Octree::encodeTreeBitstreamRecursion(OctreeElement* element, // recursing, by returning TRUE in recurseChildrenWithData(). if (recurseChildrenWithData() || !params.viewFrustum || !oneAtBit(childrenDataBits, originalIndex)) { - childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params, - thisLevel, nodeLocationThisView); + + // Allow the datatype a chance to determine if it really wants to recurse this tree. Usually this + // will be true. But if the tree has already been encoded, we will skip this. + if (element->shouldRecurseChildTree(originalIndex, params)) { + childTreeBytesOut = encodeTreeBitstreamRecursion(childElement, packetData, bag, params, + thisLevel, nodeLocationThisView); + } else { + childTreeBytesOut = 0; + } } // remember this for reshuffling @@ -1964,6 +1971,8 @@ void Octree::writeToSVOFile(const char* fileName, OctreeElement* element) { } file.write((const char*)packetData.getFinalizedData(), packetData.getFinalizedSize()); } + + releaseSceneEncodeData(&extraEncodeData); } file.close(); } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 7629c4a05d..2471f502b2 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -235,6 +235,7 @@ public: virtual bool rootElementHasData() const { return false; } virtual int minimumRequiredRootDataBytes() const { return 0; } virtual bool suppressEmptySubtrees() const { return true; } + virtual void releaseSceneEncodeData(OctreeElementExtraEncodeData* extraEncodeData) const { } /// some versions of the SVO file will include breaks with buffer lengths between each buffer chunk in the SVO /// file. If the Octree subclass expects this for this particular version of the file, it should override this @@ -275,7 +276,7 @@ public: int encodeTreeBitstream(OctreeElement* element, OctreePacketData* packetData, OctreeElementBag& bag, EncodeBitstreamParams& params) ; - + bool isDirty() const { return _isDirty; } void clearDirtyBit() { _isDirty = false; } void setDirtyBit() { _isDirty = true; } diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index bd793e10e7..093a35720f 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -93,7 +93,8 @@ public: virtual void debugExtraEncodeData(EncodeBitstreamParams& params) const { } virtual void initializeExtraEncodeData(EncodeBitstreamParams& params) const { } - virtual bool shouldIncludeChild(int childIndex, EncodeBitstreamParams& params) const { return true; } + virtual bool shouldIncludeChildData(int childIndex, EncodeBitstreamParams& params) const { return true; } + virtual bool shouldRecurseChildTree(int childIndex, EncodeBitstreamParams& params) const { return true; } virtual void updateEncodedData(int childIndex, AppendState childAppendState, EncodeBitstreamParams& params) const { } virtual void elementEncodeComplete(EncodeBitstreamParams& params, OctreeElementBag* bag) const { }