overte/libraries/voxels/src/VoxelPacket.cpp
2013-11-24 15:22:44 -08:00

301 lines
8.6 KiB
C++

//
// VoxelPacket.cpp
// hifi
//
// Created by Brad Hefta-Gaub on 11/19/2013.
// Copyright (c) 2013 HighFidelity, Inc. All rights reserved.
//
#include <PerfStat.h>
#include "VoxelPacket.h"
bool VoxelPacket::_debug = false;
VoxelPacket::VoxelPacket(bool enableCompression, int maxFinalizedSize) {
_enableCompression = enableCompression;
_maxFinalizedSize = maxFinalizedSize;
reset();
}
void VoxelPacket::reset() {
_bytesInUse = 0;
if (_enableCompression) {
_bytesAvailable = MAX_VOXEL_UNCOMRESSED_PACKET_SIZE;
} else {
_bytesAvailable = _maxFinalizedSize;
}
_subTreeAt = 0;
_compressedBytes = 0;
_bytesInUseLastCheck = 0;
_dirty = false;
}
VoxelPacket::~VoxelPacket() {
}
bool VoxelPacket::append(const unsigned char* data, int length) {
bool success = false;
if (length <= _bytesAvailable) {
memcpy(&_uncompressed[_bytesInUse], data, length);
_bytesInUse += length;
_bytesAvailable -= length;
success = true;
_dirty = true;
}
return success;
}
bool VoxelPacket::append(unsigned char byte) {
bool success = false;
if (_bytesAvailable > 0) {
_uncompressed[_bytesInUse] = byte;
_bytesInUse++;
_bytesAvailable--;
success = true;
_dirty = true;
}
return success;
}
bool VoxelPacket::updatePriorBitMask(int offset, unsigned char bitmask) {
bool success = false;
if (offset >= 0 && offset < _bytesInUse) {
_uncompressed[offset] = bitmask;
success = true;
_dirty = true;
}
return success;
}
bool VoxelPacket::updatePriorBytes(int offset, const unsigned char* replacementBytes, int length) {
bool success = false;
if (length >= 0 && offset >= 0 && ((offset + length) <= _bytesInUse)) {
memcpy(&_uncompressed[offset], replacementBytes, length); // copy new content
success = true;
_dirty = true;
}
return success;
}
bool VoxelPacket::startSubTree(const unsigned char* octcode) {
bool success = false;
int possibleStartAt = _bytesInUse;
if (octcode) {
int length = bytesRequiredForCodeLength(numberOfThreeBitSectionsInCode(octcode));
success = append(octcode, length); // handles checking compression
} else {
// NULL case, means root node, which is 0
unsigned char byte = 0;
success = append(byte); // handles checking compression
}
if (success) {
_subTreeAt = possibleStartAt;
}
return success;
}
const unsigned char* VoxelPacket::getFinalizedData() {
if (!_enableCompression) {
return &_uncompressed[0];
}
if (_dirty) {
if (_debug) {
printf("getFinalizedData() _compressedBytes=%d _bytesInUse=%d\n",_compressedBytes, _bytesInUse);
}
checkCompress();
}
return &_compressed[0];
}
int VoxelPacket::getFinalizedSize() {
if (!_enableCompression) {
return _bytesInUse;
}
if (_dirty) {
if (_debug) {
printf("getFinalizedSize() _compressedBytes=%d _bytesInUse=%d\n",_compressedBytes, _bytesInUse);
}
checkCompress();
}
return _compressedBytes;
}
void VoxelPacket::endSubTree() {
_subTreeAt = _bytesInUse;
}
void VoxelPacket::discardSubTree() {
int bytesInSubTree = _bytesInUse - _subTreeAt;
_bytesInUse -= bytesInSubTree;
_bytesAvailable += bytesInSubTree;
_subTreeAt = _bytesInUse; // should be the same actually...
_dirty = true;
}
int VoxelPacket::startLevel() {
int key = _bytesInUse;
return key;
}
void VoxelPacket::discardLevel(int key) {
int bytesInLevel = _bytesInUse - key;
if (_debug) {
printf("discardLevel() BEFORE _dirty=%s bytesInLevel=%d _compressedBytes=%d _bytesInUse=%d\n",
debug::valueOf(_dirty), bytesInLevel, _compressedBytes, _bytesInUse);
}
_bytesInUse -= bytesInLevel;
_bytesAvailable += bytesInLevel;
_dirty = true;
if (_debug) {
printf("discardLevel() AFTER _dirty=%s bytesInLevel=%d _compressedBytes=%d _bytesInUse=%d\n",
debug::valueOf(_dirty), bytesInLevel, _compressedBytes, _bytesInUse);
}
}
bool VoxelPacket::endLevel(int key) {
bool success = true;
// if we are dirty (something has changed) then try a compression test in the following cases...
// 1) If we've previously compressed and our _compressedBytes are "close enough to our limit" that we want to keep
// testing to make sure we don't overflow... VOXEL_PACKET_ALWAYS_TEST_COMPRESSION_THRESHOLD
// 2) If we've passed the uncompressed size where we believe we might pass the compressed threshold, and we've added
// a sufficient number of uncompressed bytes
if (_dirty && (
(_compressedBytes > VOXEL_PACKET_ALWAYS_TEST_COMPRESSED_THRESHOLD) ||
( (_bytesInUse > VOXEL_PACKET_TEST_UNCOMPRESSED_THRESHOLD) &&
(_bytesInUse - _bytesInUseLastCheck > VOXEL_PACKET_TEST_UNCOMPRESSED_CHANGE_THRESHOLD)
)
)) {
if (_debug) {
printf("endLevel() _dirty=%s _compressedBytes=%d _bytesInUse=%d\n",
debug::valueOf(_dirty), _compressedBytes, _bytesInUse);
}
success = checkCompress();
}
if (!success) {
discardLevel(key);
}
return success;
}
bool VoxelPacket::appendBitMask(unsigned char bitmask) {
return append(bitmask); // handles checking compression
}
bool VoxelPacket::appendColor(const nodeColor& color) {
// eventually we can make this use a dictionary...
bool success = false;
const int BYTES_PER_COLOR = 3;
if (_bytesAvailable > BYTES_PER_COLOR) {
// handles checking compression...
if (append(color[RED_INDEX])) {
if (append(color[GREEN_INDEX])) {
if (append(color[BLUE_INDEX])) {
success = true;
}
}
}
}
return success;
}
uint64_t VoxelPacket::_checkCompressTime = 0;
uint64_t VoxelPacket::_checkCompressCalls = 0;
bool VoxelPacket::checkCompress() {
PerformanceWarning warn(false,"VoxelPacket::checkCompress()",false,&_checkCompressTime,&_checkCompressCalls);
// without compression, we always pass...
if (!_enableCompression) {
return true;
}
_bytesInUseLastCheck = _bytesInUse;
bool success = false;
const int MAX_COMPRESSION = 2;
// we only want to compress the data payload, not the message header
QByteArray compressedData = qCompress(_uncompressed, _bytesInUse, MAX_COMPRESSION);
if (compressedData.size() < MAX_VOXEL_PACKET_SIZE) {
_compressedBytes = compressedData.size();
for (int i = 0; i < _compressedBytes; i++) {
_compressed[i] = compressedData[i];
}
_dirty = false;
success = true;
}
return success;
}
void VoxelPacket::loadFinalizedContent(const unsigned char* data, int length) {
reset(); // by definition we reset upon loading compressed content
if (length > 0) {
if (_enableCompression) {
QByteArray compressedData;
for (int i = 0; i < length; i++) {
compressedData[i] = data[i];
_compressed[i] = compressedData[i];
}
_compressedBytes = length;
QByteArray uncompressedData = qUncompress(compressedData);
if (uncompressedData.size() <= _bytesAvailable) {
_bytesInUse = uncompressedData.size();
_bytesAvailable -= uncompressedData.size();
for (int i = 0; i < _bytesInUse; i++) {
_uncompressed[i] = uncompressedData[i];
}
}
} else {
for (int i = 0; i < length; i++) {
_uncompressed[i] = _compressed[i] = data[i];
}
_bytesInUse = _compressedBytes = length;
}
} else {
if (_debug) printf("VoxelPacket::loadCompressedContent()... length = 0, nothing to do...\n");
}
}
void VoxelPacket::debugContent() {
printf("VoxelPacket::debugContent()... COMPRESSED DATA.... size=%d\n",_compressedBytes);
int perline=0;
for (int i = 0; i < _compressedBytes; i++) {
printf("%.2x ",_compressed[i]);
perline++;
if (perline >= 30) {
printf("\n");
perline=0;
}
}
printf("\n");
printf("VoxelPacket::debugContent()... UNCOMPRESSED DATA.... size=%d\n",_bytesInUse);
perline=0;
for (int i = 0; i < _bytesInUse; i++) {
printf("%.2x ",_uncompressed[i]);
perline++;
if (perline >= 30) {
printf("\n");
perline=0;
}
}
printf("\n");
}