diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index a52429542f..324f293110 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -619,26 +619,41 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { VOXEL_PACKET_INTERNAL_SECTION_SIZE sectionLength = 0; int dataBytes = numBytes - VOXEL_PACKET_HEADER_SIZE; - if (packetIsCompressed && dataBytes > sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE)) { - sectionLength = (*(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)dataAt); - dataAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - } else { - sectionLength = dataBytes; + int subsection = 1; + while (dataBytes > 0) { + if (packetIsCompressed) { + if (dataBytes > sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE)) { + sectionLength = (*(VOXEL_PACKET_INTERNAL_SECTION_SIZE*)dataAt); + dataAt += sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); + dataBytes -= sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); + } else { + sectionLength = 0; + dataBytes = 0; // stop looping something is wrong + } + } else { + sectionLength = dataBytes; + } + + if (sectionLength) { + // ask the VoxelTree to read the bitstream into the tree + ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); + lockTree(); + VoxelPacketData packetData(packetIsCompressed); + packetData.loadFinalizedContent(dataAt, sectionLength); + if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) { + qDebug("Got Packet color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d" + " subsection:%d sectionLength:%d uncompressed:%d\n", + debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), + sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize()); + } + _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); + unlockTree(); + + dataBytes -= sectionLength; + dataAt += sectionLength; + } } - unsigned char* voxelData = dataAt; - - // ask the VoxelTree to read the bitstream into the tree - ReadBitstreamToTreeParams args(packetIsColored ? WANT_COLOR : NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); - lockTree(); - VoxelPacketData packetData(packetIsCompressed); - packetData.loadFinalizedContent(voxelData, sectionLength); - if (true || Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) { - qDebug("Got Packet color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d sectionLength:%d uncompressed:%d\n", - debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed), - sequence, flightTime, numBytes, dataBytes, sectionLength, packetData.getUncompressedSize()); - } - _tree->readBitstreamToTree(packetData.getUncompressedData(), packetData.getUncompressedSize(), args); - unlockTree(); + subsection++; } break; } diff --git a/libraries/voxel-server-library/src/VoxelNodeData.cpp b/libraries/voxel-server-library/src/VoxelNodeData.cpp index 5509fa1200..6c18effa54 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.cpp +++ b/libraries/voxel-server-library/src/VoxelNodeData.cpp @@ -16,7 +16,7 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : VoxelQuery(owningNode), _viewSent(false), - _voxelPacketAvailableBytes(MAX_VOXEL_PACKET_SIZE), + _voxelPacketAvailableBytes(MAX_PACKET_SIZE), _maxSearchLevel(1), _maxLevelReachedInLastSearch(1), _lastTimeBagEmpty(0), @@ -30,9 +30,9 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) : _lodChanged(false), _lodInitialized(false) { - _voxelPacket = new unsigned char[_voxelPacketAvailableBytes]; + _voxelPacket = new unsigned char[MAX_PACKET_SIZE]; _voxelPacketAt = _voxelPacket; - _lastVoxelPacket = new unsigned char[_voxelPacketAvailableBytes]; + _lastVoxelPacket = new unsigned char[MAX_PACKET_SIZE]; _lastVoxelPacketLength = 0; _duplicatePacketCount = 0; _sequenceNumber = 0; @@ -108,18 +108,22 @@ void VoxelNodeData::resetVoxelPacket() { setAtBit(flags,PACKET_IS_COMPRESSED_BIT); } + _voxelPacketAvailableBytes = MAX_PACKET_SIZE; int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, PACKET_TYPE_VOXEL_DATA); _voxelPacketAt = _voxelPacket + numBytesPacketHeader; + _voxelPacketAvailableBytes -= numBytesPacketHeader; // pack in flags VOXEL_PACKET_FLAGS* flagsAt = (VOXEL_PACKET_FLAGS*)_voxelPacketAt; *flagsAt = flags; _voxelPacketAt += sizeof(VOXEL_PACKET_FLAGS); + _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_FLAGS); // pack in sequence number VOXEL_PACKET_SEQUENCE* sequenceAt = (VOXEL_PACKET_SEQUENCE*)_voxelPacketAt; *sequenceAt = _sequenceNumber; _voxelPacketAt += sizeof(VOXEL_PACKET_SEQUENCE); + _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SEQUENCE); _sequenceNumber++; // pack in timestamp @@ -127,8 +131,8 @@ void VoxelNodeData::resetVoxelPacket() { VOXEL_PACKET_SENT_TIME* timeAt = (VOXEL_PACKET_SENT_TIME*)_voxelPacketAt; *timeAt = now; _voxelPacketAt += sizeof(VOXEL_PACKET_SENT_TIME); + _voxelPacketAvailableBytes -= sizeof(VOXEL_PACKET_SENT_TIME); - _voxelPacketAvailableBytes = MAX_VOXEL_PACKET_DATA_SIZE; _voxelPacketWaiting = false; } @@ -144,6 +148,8 @@ void VoxelNodeData::writeToPacket(const unsigned char* buffer, int bytes) { _voxelPacketAvailableBytes -= bytes; _voxelPacketAt += bytes; _voxelPacketWaiting = true; + + assert(_voxelPacketAvailableBytes >= 0); } VoxelNodeData::~VoxelNodeData() { diff --git a/libraries/voxel-server-library/src/VoxelNodeData.h b/libraries/voxel-server-library/src/VoxelNodeData.h index bc6409b10c..fa0adc9e44 100644 --- a/libraries/voxel-server-library/src/VoxelNodeData.h +++ b/libraries/voxel-server-library/src/VoxelNodeData.h @@ -32,7 +32,7 @@ public: void writeToPacket(const unsigned char* buffer, int bytes); // writes to end of packet const unsigned char* getPacket() const { return _voxelPacket; } - int getPacketLength() const { return (MAX_VOXEL_PACKET_SIZE - _voxelPacketAvailableBytes); } + int getPacketLength() const { return (MAX_PACKET_SIZE - _voxelPacketAvailableBytes); } bool isPacketWaiting() const { return _voxelPacketWaiting; } bool packetIsDuplicate() const; diff --git a/libraries/voxel-server-library/src/VoxelSendThread.cpp b/libraries/voxel-server-library/src/VoxelSendThread.cpp index 93cf80905f..001e7005ac 100644 --- a/libraries/voxel-server-library/src/VoxelSendThread.cpp +++ b/libraries/voxel-server-library/src/VoxelSendThread.cpp @@ -251,9 +251,14 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod } nodeData->resetVoxelPacket(); } - int uncompressedSize = !wantCompression ? MAX_VOXEL_PACKET_DATA_SIZE - : MAX_VOXEL_PACKET_DATA_SIZE - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); - _packetData.changeSettings(wantCompression, uncompressedSize); + int targetSize = MAX_VOXEL_PACKET_DATA_SIZE; + if (wantCompression) { + targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); + } + printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + debug::valueOf(wantCompression), targetSize); + + _packetData.changeSettings(wantCompression, targetSize); } if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { @@ -315,7 +320,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod unsigned long elapsedTime = nodeData->stats.getElapsedTime(); packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - if (true || _myServer->wantsDebugVoxelSending()) { + if (_myServer->wantsDebugVoxelSending()) { qDebug("Scene completed at %llu encodeTime:%lu sleepTime:%lu elapsed:%lu Packets:%llu Bytes:%llu Wasted:%llu\n", usecTimestampNow(), encodeTime, sleepTime, elapsedTime, _totalPackets, _totalBytes, _totalWastedBytes); } @@ -373,6 +378,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod nodeData->getMaxVoxelPacketsPerSecond(), clientMaxPacketsPerInterval); } + int extraPackingAttempts = 0; while (somethingToSend && packetsSentThisInterval < maxPacketsPerInterval - (shouldSendEnvironments ? 1 : 0)) { if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { printf("truePacketsSent=%d packetsSentThisInterval=%d maxPacketsPerInterval=%d server PPI=%d nodePPS=%d nodePPI=%d\n", @@ -407,8 +413,20 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod nodeData->stats.encodeStarted(); bytesWritten = _myServer->getServerTree().encodeTreeBitstream(subTree, &_packetData, nodeData->nodeBag, params); - if (_packetData.hasContent() && bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { - lastNodeDidntFit = true; + // if we're trying to fill a full size packet, then we use this logic to determine if we have a DIDNT_FIT case. + if (_packetData.getTargetSize() == MAX_VOXEL_PACKET_DATA_SIZE) { + if (_packetData.hasContent() && bytesWritten == 0 && + params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { + lastNodeDidntFit = true; + } + } else { + // in compressed mode and we are trying to pack more... and we don't care if the _packetData has + // content or not... because in this case even if we were unable to pack any data, we want to drop + // below to our sendNow logic, but we do want to track that we attempted to pack extra + extraPackingAttempts++; + if (bytesWritten == 0 && params.stopReason == EncodeBitstreamParams::DIDNT_FIT) { + lastNodeDidntFit = true; + } } if (bytesWritten > 0) { @@ -422,18 +440,68 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod somethingToSend = false; // this will cause us to drop out of the loop... } + // If the last node didn't fit, but we're in compressed mode, then we actually want to see if we can fit a + // little bit more in this packet. To do this we + // We only consider sending anything if there is something in the _packetData to send... But // if bytesWritten == 0 it means either the subTree couldn't fit or we had an empty bag... Both cases // mean we should send the previous packet contents and reset it. - bool sendNow = lastNodeDidntFit; - if (_packetData.hasContent() && sendNow) { - if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { - printf("calling writeToPacket() compressedSize=%d uncompressedSize=%d\n", - _packetData.getFinalizedSize(), _packetData.getUncompressedSize()); + if (lastNodeDidntFit) { + if (_packetData.hasContent()) { + // 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... + int writtenSize = _packetData.getFinalizedSize() + + (nodeData->getCurrentPacketIsCompressed() ? sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) : 0); + + + if (writtenSize > nodeData->getAvailable()) { + if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { + printf("writtenSize[%d] > available[%d] too big, sending packet as is.\n", + writtenSize, nodeData->getAvailable()); + } + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + } + + if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { + printf("calling writeToPacket() available=%d compressedSize=%d uncompressedSize=%d target=%d\n", + nodeData->getAvailable(), _packetData.getFinalizedSize(), + _packetData.getUncompressedSize(), _packetData.getTargetSize()); + } + nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); + extraPackingAttempts = 0; } - nodeData->writeToPacket(_packetData.getFinalizedData(), _packetData.getFinalizedSize()); - packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); - _packetData.reset(); + + // If we're not running compressed, the 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) { + sendNow = false; // try to pack more + } + + int targetSize = MAX_VOXEL_PACKET_DATA_SIZE; + if (sendNow) { + packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); + if (wantCompression) { + targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE); + } + } else { + // If we're in compressed mode, then we want to see if we have room for more in this wire packet. + // but we've finalized the _packetData, so we want to start a new section, we will do that by + // resetting the packet settings with the max uncompressed size of our current available space + // in the wire packet. We also include room for our section header, and a little bit of padding + // to account for the fact that whenc compressing small amounts of data, we sometimes end up with + // a larger compressed size then uncompressed size + targetSize = nodeData->getAvailable() - sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) - COMPRESS_PADDING; + } + if (_myServer->wantsDebugVoxelSending() && _myServer->wantsVerboseDebug()) { + printf("line:%d _packetData.changeSettings() wantCompression=%s targetSize=%d\n",__LINE__, + debug::valueOf(nodeData->getWantCompression()), targetSize); + } + _packetData.changeSettings(nodeData->getWantCompression(), targetSize); // will do reset } } diff --git a/libraries/voxels/src/VoxelPacketData.cpp b/libraries/voxels/src/VoxelPacketData.cpp index dbbfa0da2e..d9efc648d8 100644 --- a/libraries/voxels/src/VoxelPacketData.cpp +++ b/libraries/voxels/src/VoxelPacketData.cpp @@ -28,11 +28,7 @@ void VoxelPacketData::changeSettings(bool enableCompression, int targetSize) { void VoxelPacketData::reset() { _bytesInUse = 0; - if (_enableCompression) { - _bytesAvailable = MAX_VOXEL_UNCOMRESSED_PACKET_SIZE; - } else { - _bytesAvailable = _targetSize; - } + _bytesAvailable = _targetSize; _subTreeAt = 0; _compressedBytes = 0; _bytesInUseLastCheck = 0; diff --git a/libraries/voxels/src/VoxelPacketData.h b/libraries/voxels/src/VoxelPacketData.h index 8dab8c2290..b443556de3 100644 --- a/libraries/voxels/src/VoxelPacketData.h +++ b/libraries/voxels/src/VoxelPacketData.h @@ -35,6 +35,10 @@ const int MAX_VOXEL_PACKET_DATA_SIZE = MAX_PACKET_SIZE - VOXEL_PACKET_HEADER_SIZ const int MAX_VOXEL_UNCOMRESSED_PACKET_SIZE = MAX_VOXEL_PACKET_DATA_SIZE; +const int MINIMUM_ATTEMPT_MORE_PACKING = sizeof(VOXEL_PACKET_INTERNAL_SECTION_SIZE) + 40; +const int COMPRESS_PADDING = 15; +const int REASONABLE_NUMBER_OF_PACKING_ATTEMPTS = 5; + const int PACKET_IS_COLOR_BIT = 0; const int PACKET_IS_COMPRESSED_BIT = 1; @@ -121,6 +125,7 @@ public: void loadFinalizedContent(const unsigned char* data, int length); bool isCompressed() const { return _enableCompression; } + int getTargetSize() const { return _targetSize; } void debugContent();