first cut at compresses voxel packets

This commit is contained in:
ZappoMan 2013-11-18 09:46:32 -08:00
parent 893eadcb2f
commit c2cb6df61c
5 changed files with 123 additions and 16 deletions

View file

@ -8,6 +8,8 @@
// Threaded or non-threaded voxel packet receiver for the Application // Threaded or non-threaded voxel packet receiver for the Application
// //
#include <QByteArray>
#include <PerfStat.h> #include <PerfStat.h>
#include "Application.h" #include "Application.h"
@ -56,7 +58,17 @@ void VoxelPacketProcessor::processPacket(sockaddr& senderAddress, unsigned char*
app->_environment.parseData(&senderAddress, packetData, messageLength); app->_environment.parseData(&senderAddress, packetData, messageLength);
} else { } else {
app->_voxels.setDataSourceUUID(voxelServer->getUUID()); app->_voxels.setDataSourceUUID(voxelServer->getUUID());
app->_voxels.parseData(packetData, messageLength);
// thse packets are commpressed...
int numBytesPacketHeader = numBytesForPacketHeader(packetData);
QByteArray compressedData((const char*)packetData + numBytesPacketHeader,
messageLength - numBytesPacketHeader);
QByteArray uncompressedData = qUncompress(compressedData);
QByteArray uncompressedPacket((const char*)packetData, numBytesPacketHeader);
uncompressedPacket.append(uncompressedData);
app->_voxels.parseData((unsigned char*)uncompressedPacket.data(), uncompressedPacket.size());
app->_voxels.setDataSourceUUID(QUuid()); app->_voxels.setDataSourceUUID(QUuid());
} }
} }

View file

@ -595,7 +595,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"readBitstreamToTree()"); "readBitstreamToTree()");
// ask the VoxelTree to read the bitstream into the tree // ask the VoxelTree to read the bitstream into the tree
ReadBitstreamToTreeParams args(WANT_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); ReadBitstreamToTreeParams args(WANT_COLOR, NO_EXISTS_BITS /*WANT_EXISTS_BITS*/, NULL, getDataSourceUUID());
lockTree(); lockTree();
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);
unlockTree(); unlockTree();
@ -605,7 +605,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"readBitstreamToTree()"); "readBitstreamToTree()");
// ask the VoxelTree to read the MONOCHROME bitstream into the tree // ask the VoxelTree to read the MONOCHROME bitstream into the tree
ReadBitstreamToTreeParams args(NO_COLOR, WANT_EXISTS_BITS, NULL, getDataSourceUUID()); ReadBitstreamToTreeParams args(NO_COLOR, NO_EXISTS_BITS /*WANT_EXISTS_BITS*/, NULL, getDataSourceUUID());
lockTree(); lockTree();
_tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args); _tree->readBitstreamToTree(voxelData, numBytes - numBytesPacketHeader, args);
unlockTree(); unlockTree();

View file

@ -13,10 +13,12 @@
#include <cstdio> #include <cstdio>
#include "VoxelSendThread.h" #include "VoxelSendThread.h"
const int UNCOMPRESSED_SIZE_MULTIPLE = 20;
VoxelNodeData::VoxelNodeData(Node* owningNode) : VoxelNodeData::VoxelNodeData(Node* owningNode) :
VoxelQuery(owningNode), VoxelQuery(owningNode),
_viewSent(false), _viewSent(false),
_voxelPacketAvailableBytes(MAX_VOXEL_PACKET_SIZE), _voxelPacketAvailableBytes(MAX_VOXEL_PACKET_SIZE * UNCOMPRESSED_SIZE_MULTIPLE),
_maxSearchLevel(1), _maxSearchLevel(1),
_maxLevelReachedInLastSearch(1), _maxLevelReachedInLastSearch(1),
_lastTimeBagEmpty(0), _lastTimeBagEmpty(0),
@ -29,9 +31,9 @@ VoxelNodeData::VoxelNodeData(Node* owningNode) :
_lodChanged(false), _lodChanged(false),
_lodInitialized(false) _lodInitialized(false)
{ {
_voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; _voxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE * UNCOMPRESSED_SIZE_MULTIPLE];
_voxelPacketAt = _voxelPacket; _voxelPacketAt = _voxelPacket;
_lastVoxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE]; _lastVoxelPacket = new unsigned char[MAX_VOXEL_PACKET_SIZE * UNCOMPRESSED_SIZE_MULTIPLE];
_lastVoxelPacketLength = 0; _lastVoxelPacketLength = 0;
_duplicatePacketCount = 0; _duplicatePacketCount = 0;
resetVoxelPacket(); resetVoxelPacket();
@ -44,9 +46,9 @@ void VoxelNodeData::initializeVoxelSendThread(VoxelServer* voxelServer) {
_voxelSendThread->initialize(true); _voxelSendThread->initialize(true);
} }
bool VoxelNodeData::packetIsDuplicate() const { bool VoxelNodeData::packetIsDuplicate() {
if (_lastVoxelPacketLength == getPacketLength()) { if (_lastVoxelPacketLength == getPacketLength()) {
return memcmp(_lastVoxelPacket, _voxelPacket, getPacketLength()) == 0; return memcmp(_lastVoxelPacket, getPacket(), getPacketLength()) == 0;
} }
return false; return false;
} }
@ -91,7 +93,7 @@ void VoxelNodeData::resetVoxelPacket() {
// scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing // scene information, (e.g. the root node packet of a static scene), we can use this as a strategy for reducing
// packet send rate. // packet send rate.
_lastVoxelPacketLength = getPacketLength(); _lastVoxelPacketLength = getPacketLength();
memcpy(_lastVoxelPacket, _voxelPacket, _lastVoxelPacketLength); memcpy(_lastVoxelPacket, getPacket(), _lastVoxelPacketLength);
// If we're moving, and the client asked for low res, then we force monochrome, otherwise, use // If we're moving, and the client asked for low res, then we force monochrome, otherwise, use
// the clients requested color state. // the clients requested color state.
@ -100,15 +102,63 @@ void VoxelNodeData::resetVoxelPacket() {
int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, voxelPacketType); int numBytesPacketHeader = populateTypeAndVersion(_voxelPacket, voxelPacketType);
_voxelPacketAt = _voxelPacket + numBytesPacketHeader; _voxelPacketAt = _voxelPacket + numBytesPacketHeader;
_voxelPacketAvailableBytes = MAX_VOXEL_PACKET_SIZE - numBytesPacketHeader; _voxelPacketAvailableBytes = (MAX_VOXEL_PACKET_SIZE * UNCOMPRESSED_SIZE_MULTIPLE) - numBytesPacketHeader;
compressPacket();
_voxelPacketWaiting = false; _voxelPacketWaiting = false;
} }
bool VoxelNodeData::willFit(unsigned char* buffer, int bytes) {
int uncompressedLength = getPacketLengthUncompressed();
const int MAX_COMPRESSION = 9;
// we only want to compress the data payload, not the message header
int numBytesPacketHeader = numBytesForPacketHeader(_voxelPacket);
// start with the current uncompressed data
QByteArray uncompressedTestData((const char*)_voxelPacket + numBytesPacketHeader,
uncompressedLength - numBytesPacketHeader);
// append this next buffer...
uncompressedTestData.append((const char*)buffer, bytes);
// test compress it
QByteArray compressedData = qCompress(uncompressedTestData, MAX_COMPRESSION);
bool wouldFit = (compressedData.size() + numBytesPacketHeader) <= MAX_VOXEL_PACKET_SIZE;
/*
if (!wouldFit) {
printf("would not fit... previous size: %d, buffer size: %d, would be:%d MAX_VOXEL_PACKET_SIZE: %d\n",
getPacketLength(), bytes, (compressedData.size() + numBytesPacketHeader), MAX_VOXEL_PACKET_SIZE);
} else {
printf("would fit... previous size: %d, buffer size: %d, would be:%d MAX_VOXEL_PACKET_SIZE: %d\n",
getPacketLength(), bytes, (compressedData.size() + numBytesPacketHeader), MAX_VOXEL_PACKET_SIZE);
}
*/
return wouldFit;
}
void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) { void VoxelNodeData::writeToPacket(unsigned char* buffer, int bytes) {
memcpy(_voxelPacketAt, buffer, bytes); memcpy(_voxelPacketAt, buffer, bytes);
_voxelPacketAvailableBytes -= bytes; _voxelPacketAvailableBytes -= bytes;
_voxelPacketAt += bytes; _voxelPacketAt += bytes;
_voxelPacketWaiting = true; _voxelPacketWaiting = true;
compressPacket();
}
int VoxelNodeData::getPacketLengthUncompressed() const {
return (MAX_VOXEL_PACKET_SIZE * UNCOMPRESSED_SIZE_MULTIPLE) - _voxelPacketAvailableBytes;
}
void VoxelNodeData::compressPacket() {
int uncompressedLength = getPacketLengthUncompressed();
const int MAX_COMPRESSION = 9;
// we only want to compress the data payload, not the message header
int numBytesPacketHeader = numBytesForPacketHeader(_voxelPacket);
QByteArray compressedData = qCompress(_voxelPacket+numBytesPacketHeader,
uncompressedLength-numBytesPacketHeader, MAX_COMPRESSION);
_compressedPacket.clear();
_compressedPacket.append((const char*)_voxelPacket, numBytesPacketHeader);
_compressedPacket.append(compressedData);
} }
VoxelNodeData::~VoxelNodeData() { VoxelNodeData::~VoxelNodeData() {

View file

@ -29,15 +29,18 @@ public:
void resetVoxelPacket(); // resets voxel packet to after "V" header void resetVoxelPacket(); // resets voxel packet to after "V" header
void writeToPacket(unsigned char* buffer, int bytes); // writes to end of packet void writeToPacket(unsigned char* buffer, int bytes); // writes to end of packet
bool willFit(unsigned char* buffer, int bytes); // tests to see if the bytes will fit
const unsigned char* getPacket() const { return (const unsigned char*)_compressedPacket.constData(); }
int getPacketLength() const { return _compressedPacket.size(); }
int getPacketLengthUncompressed() const;
const unsigned char* getPacket() const { return _voxelPacket; }
int getPacketLength() const { return (MAX_VOXEL_PACKET_SIZE - _voxelPacketAvailableBytes); }
bool isPacketWaiting() const { return _voxelPacketWaiting; } bool isPacketWaiting() const { return _voxelPacketWaiting; }
bool packetIsDuplicate() const; bool packetIsDuplicate();
bool shouldSuppressDuplicatePacket(); bool shouldSuppressDuplicatePacket();
int getAvailable() const { return _voxelPacketAvailableBytes; } int getAvailable() const { return MAX_VOXEL_PACKET_SIZE - getPacketLength(); }
int getMaxSearchLevel() const { return _maxSearchLevel; }; int getMaxSearchLevel() const { return _maxSearchLevel; };
void resetMaxSearchLevel() { _maxSearchLevel = 1; }; void resetMaxSearchLevel() { _maxSearchLevel = 1; };
void incrementMaxSearchLevel() { _maxSearchLevel++; }; void incrementMaxSearchLevel() { _maxSearchLevel++; };
@ -109,6 +112,9 @@ private:
float _lastClientVoxelSizeScale; float _lastClientVoxelSizeScale;
bool _lodChanged; bool _lodChanged;
bool _lodInitialized; bool _lodInitialized;
QByteArray _compressedPacket;
void compressPacket();
}; };
#endif /* defined(__hifi__VoxelNodeData__) */ #endif /* defined(__hifi__VoxelNodeData__) */

View file

@ -78,6 +78,11 @@ bool VoxelSendThread::process() {
} }
int totalUncompressed = 0;
int totalCompressed = 0;
int totalWastedBytes = 0;
int totalPackets = 0;
int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) { int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int& trueBytesSent, int& truePacketsSent) {
int packetsSent = 0; int packetsSent = 0;
@ -102,6 +107,17 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength()); memcpy(statsMessage + statsMessageLength, nodeData->getPacket(), nodeData->getPacketLength());
statsMessageLength += nodeData->getPacketLength(); statsMessageLength += nodeData->getPacketLength();
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
::totalWastedBytes += thisWastedBytes;
::totalUncompressed += nodeData->getPacketLengthUncompressed();
::totalCompressed += nodeData->getPacketLength();
::totalPackets++;
qDebug("Adding stats to packet [%d]: uncompress size:%d [%d], compressed size:%d [%d] thisWastedBytes:%d [%d]\n",
totalPackets,
nodeData->getPacketLengthUncompressed(), ::totalUncompressed,
nodeData->getPacketLength(), ::totalCompressed,
thisWastedBytes, ::totalWastedBytes);
// actually send it // actually send it
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength); NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), statsMessage, statsMessageLength);
} else { } else {
@ -113,12 +129,34 @@ int VoxelSendThread::handlePacketSend(Node* node, VoxelNodeData* nodeData, int&
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength()); nodeData->getPacket(), nodeData->getPacketLength());
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
::totalWastedBytes += thisWastedBytes;
::totalUncompressed += nodeData->getPacketLengthUncompressed();
::totalCompressed += nodeData->getPacketLength();
::totalPackets++;
qDebug("Sending packet [%d]: uncompress size:%d [%d], compressed size:%d [%d] thisWastedBytes:%d [%d]\n",
totalPackets,
nodeData->getPacketLengthUncompressed(), ::totalUncompressed,
nodeData->getPacketLength(), ::totalCompressed,
thisWastedBytes, ::totalWastedBytes);
} }
nodeData->stats.markAsSent(); nodeData->stats.markAsSent();
} else { } else {
// just send the voxel packet // just send the voxel packet
NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(), NodeList::getInstance()->getNodeSocket()->send(node->getActiveSocket(),
nodeData->getPacket(), nodeData->getPacketLength()); nodeData->getPacket(), nodeData->getPacketLength());
int thisWastedBytes = MAX_PACKET_SIZE - nodeData->getPacketLength();
::totalWastedBytes += thisWastedBytes;
::totalUncompressed += nodeData->getPacketLengthUncompressed();
::totalCompressed += nodeData->getPacketLength();
::totalPackets++;
qDebug("Sending packet [%d]: uncompress size:%d [%d], compressed size:%d [%d] thisWastedBytes:%d [%d]\n",
totalPackets,
nodeData->getPacketLengthUncompressed(), ::totalUncompressed,
nodeData->getPacketLength(), ::totalCompressed,
thisWastedBytes, ::totalWastedBytes);
} }
// remember to track our stats // remember to track our stats
nodeData->stats.packetSent(nodeData->getPacketLength()); nodeData->stats.packetSent(nodeData->getPacketLength());
@ -297,7 +335,7 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged(); nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged();
EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor,
WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, NO_EXISTS_BITS /*WANT_EXISTS_BITS*/, DONT_CHOP, wantDelta, lastViewFrustum,
wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, voxelSizeScale,
nodeData->getLastTimeBagEmpty(), nodeData->getLastTimeBagEmpty(),
isFullScene, &nodeData->stats, _myServer->getJurisdiction()); isFullScene, &nodeData->stats, _myServer->getJurisdiction());
@ -310,7 +348,8 @@ int VoxelSendThread::deepestLevelVoxelDistributor(Node* node, VoxelNodeData* nod
nodeData->stats.encodeStopped(); nodeData->stats.encodeStopped();
_myServer->getServerTree().unlock(); _myServer->getServerTree().unlock();
if (nodeData->getAvailable() >= bytesWritten) { // NOTE: could be better, the bytesWritten might be compressable...
if (nodeData->willFit(_tempOutputBuffer, bytesWritten)) {
nodeData->writeToPacket(_tempOutputBuffer, bytesWritten); nodeData->writeToPacket(_tempOutputBuffer, bytesWritten);
} else { } else {
packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent); packetsSentThisInterval += handlePacketSend(node, nodeData, trueBytesSent, truePacketsSent);