From 3aa6af21b1f2fb385702816b71d8345f459b4943 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 18 Jul 2013 14:13:45 -0700 Subject: [PATCH] more voxel scene stats --- libraries/voxels/src/VoxelSceneStats.cpp | 203 +++++++++++++++++------ libraries/voxels/src/VoxelSceneStats.h | 70 ++++++-- libraries/voxels/src/VoxelTree.cpp | 84 +++++++++- libraries/voxels/src/VoxelTree.h | 16 +- voxel-server/src/main.cpp | 16 +- 5 files changed, 312 insertions(+), 77 deletions(-) diff --git a/libraries/voxels/src/VoxelSceneStats.cpp b/libraries/voxels/src/VoxelSceneStats.cpp index 26ec66c32b..44c10e2bf9 100644 --- a/libraries/voxels/src/VoxelSceneStats.cpp +++ b/libraries/voxels/src/VoxelSceneStats.cpp @@ -8,70 +8,179 @@ #include +#include "VoxelNode.h" #include "VoxelSceneStats.h" -VoxelSceneStats::VoxelSceneStats() : - start(0), - end(0), - elapsed(0), - total(0), - traversed(0), - internal(0), - internalOutOfView(0), - internalOccluded(0), - internalDirty(0), - leaves(0), - leavesOutOfView(0), - leavesOccluded(0), - leavesDirty(0), - packets(0), - bytes(0), - passes(0), - wasFinished(false), - wasMoving(false), - hadDeltaView(false), - hadOcclusionCulling(false) -{ +VoxelSceneStats::VoxelSceneStats() { + reset(); } VoxelSceneStats::~VoxelSceneStats() { } -void VoxelSceneStats::sceneStarted() { - start = usecTimestampNow(); +void VoxelSceneStats::sceneStarted(bool fullScene, bool moving) { + _start = usecTimestampNow(); + reset(); // resets packet and voxel stats + _fullSceneDraw = fullScene; + _moving = moving; } void VoxelSceneStats::sceneCompleted() { - end = usecTimestampNow(); - elapsed = end - start; + _end = usecTimestampNow(); + _elapsed = _end - _start; } void VoxelSceneStats::reset() { - start = 0; - end= 0; - elapsed= 0; + _packets = 0; + _bytes = 0; + _passes = 0; + _traversed = 0; + _internal = 0; + _leaves = 0; + + _skippedDistance = 0; + _internalSkippedDistance = 0; + _leavesSkippedDistance = 0; + + _skippedOutOfView = 0; + _internalSkippedOutOfView = 0; + _leavesSkippedOutOfView = 0; + + _skippedWasInView = 0; + _internalSkippedWasInView = 0; + _leavesSkippedWasInView = 0; + + _skippedNoChange = 0; + _internalSkippedNoChange = 0; + _leavesSkippedNoChange = 0; + + _skippedOccluded = 0; + _internalSkippedOccluded = 0; + _leavesSkippedOccluded = 0; + + _colorSent = 0; + _internalColorSent = 0; + _leavesColorSent = 0; + + _didntFit = 0; + _internalDidntFit = 0; + _leavesDidntFit = 0; - total = 0; - traversed = 0; - internal = 0; - internalOutOfView = 0; - internalOccluded = 0; - internalDirty = 0; - leaves = 0; - leavesOutOfView = 0; - leavesOccluded = 0; - leavesDirty = 0; - packets = 0; - bytes = 0; - passes = 0; - wasFinished = false; - wasMoving = false; - hadDeltaView = false; - hadOcclusionCulling = false; } +void VoxelSceneStats::packetSent(int bytes) { + _packets++; + _bytes += bytes; +} + +void VoxelSceneStats::traversed(const VoxelNode* node) { + _traversed++; + if (node->isLeaf()) { + _leaves++; + } else { + _internal++; + } +} + +void VoxelSceneStats::skippedDistance(const VoxelNode* node) { + _skippedDistance++; + if (node->isLeaf()) { + _leavesSkippedDistance++; + } else { + _internalSkippedDistance++; + } +} + +void VoxelSceneStats::skippedOutOfView(const VoxelNode* node) { + _skippedOutOfView++; + if (node->isLeaf()) { + _leavesSkippedOutOfView++; + } else { + _internalSkippedOutOfView++; + } +} + +void VoxelSceneStats::skippedWasInView(const VoxelNode* node) { + _skippedWasInView++; + if (node->isLeaf()) { + _leavesSkippedWasInView++; + } else { + _internalSkippedWasInView++; + } +} + +void VoxelSceneStats::skippedNoChange(const VoxelNode* node) { + _skippedNoChange++; + if (node->isLeaf()) { + _leavesSkippedNoChange++; + } else { + _internalSkippedNoChange++; + } +} + +void VoxelSceneStats::skippedOccluded(const VoxelNode* node) { + _skippedOccluded++; + if (node->isLeaf()) { + _leavesSkippedOccluded++; + } else { + _internalSkippedOccluded++; + } +} + +void VoxelSceneStats::colorSent(const VoxelNode* node) { + _colorSent++; + if (node->isLeaf()) { + _leavesColorSent++; + } else { + _internalColorSent++; + } +} + +void VoxelSceneStats::didntFit(const VoxelNode* node) { + _didntFit++; + if (node->isLeaf()) { + _leavesDidntFit++; + } else { + _internalDidntFit++; + } +} void VoxelSceneStats::printDebugDetails() { - qDebug("VoxelSceneStats: start: %llu, end: %llu, elapsed: %llu \n", start, end, elapsed); + qDebug("\n------------------------------\n"); + qDebug("VoxelSceneStats:\n"); + qDebug(" start : %llu \n", _start); + qDebug(" end : %llu \n", _end); + qDebug(" elapsed: %llu \n", _elapsed); + qDebug("\n"); + qDebug(" full scene: %s\n", debug::valueOf(_fullSceneDraw)); + qDebug(" moving: %s\n", debug::valueOf(_moving)); + qDebug("\n"); + qDebug(" packets: %d\n", _packets); + qDebug(" bytes : %d\n", _bytes); + qDebug("\n"); + qDebug(" traversed : %lu\n", _traversed ); + qDebug(" internal : %lu\n", _internal ); + qDebug(" leaves : %lu\n", _leaves ); + qDebug(" skipped distance : %lu\n", _skippedDistance ); + qDebug(" internal : %lu\n", _internalSkippedDistance ); + qDebug(" leaves : %lu\n", _leavesSkippedDistance ); + qDebug(" skipped out of view : %lu\n", _skippedOutOfView ); + qDebug(" internal : %lu\n", _internalSkippedOutOfView ); + qDebug(" leaves : %lu\n", _leavesSkippedOutOfView ); + qDebug(" skipped was in view : %lu\n", _skippedWasInView ); + qDebug(" internal : %lu\n", _internalSkippedWasInView ); + qDebug(" leaves : %lu\n", _leavesSkippedWasInView ); + qDebug(" skipped no change : %lu\n", _skippedNoChange ); + qDebug(" internal : %lu\n", _internalSkippedNoChange ); + qDebug(" leaves : %lu\n", _leavesSkippedNoChange ); + qDebug(" skipped occluded : %lu\n", _skippedOccluded ); + qDebug(" internal : %lu\n", _internalSkippedOccluded ); + qDebug(" leaves : %lu\n", _leavesSkippedOccluded ); + qDebug(" color sent : %lu\n", _colorSent ); + qDebug(" internal : %lu\n", _internalColorSent ); + qDebug(" leaves : %lu\n", _leavesColorSent ); + qDebug(" Didn't Fit : %lu\n", _didntFit ); + qDebug(" internal : %lu\n", _internalDidntFit ); + qDebug(" leaves : %lu\n", _leavesDidntFit ); } diff --git a/libraries/voxels/src/VoxelSceneStats.h b/libraries/voxels/src/VoxelSceneStats.h index a90de6e7a9..d41091ae80 100644 --- a/libraries/voxels/src/VoxelSceneStats.h +++ b/libraries/voxels/src/VoxelSceneStats.h @@ -11,43 +11,85 @@ #include +class VoxelNode; class VoxelSceneStats { public: VoxelSceneStats(); ~VoxelSceneStats(); void reset(); - void sceneStarted(); + void sceneStarted(bool fullScene, bool moving); void sceneCompleted(); void printDebugDetails(); + void packetSent(int bytes); + void traversed(const VoxelNode* node); + void skippedDistance(const VoxelNode* node); + void skippedOutOfView(const VoxelNode* node); + void skippedWasInView(const VoxelNode* node); + void skippedNoChange(const VoxelNode* node); + void skippedOccluded(const VoxelNode* node); + void colorSent(const VoxelNode* node); + void didntFit(const VoxelNode* node); + void colorBitsWritten(const VoxelNode* node); + void existsBitsWritten(const VoxelNode* node); + void existsInPacketBitsWritten(const VoxelNode* node); + +private: // scene timing data in usecs - uint64_t start; - uint64_t end; - uint64_t elapsed; + uint64_t _start; + uint64_t _end; + uint64_t _elapsed; // scene voxel related data + unsigned long _traversed; + unsigned long _internal; + unsigned long _leaves; + + unsigned long _skippedDistance; + unsigned long _internalSkippedDistance; + unsigned long _leavesSkippedDistance; + + unsigned long _skippedOutOfView; + unsigned long _internalSkippedOutOfView; + unsigned long _leavesSkippedOutOfView; + + unsigned long _skippedWasInView; + unsigned long _internalSkippedWasInView; + unsigned long _leavesSkippedWasInView; + + unsigned long _skippedNoChange; + unsigned long _internalSkippedNoChange; + unsigned long _leavesSkippedNoChange; + + unsigned long _skippedOccluded; + unsigned long _internalSkippedOccluded; + unsigned long _leavesSkippedOccluded; + + unsigned long _colorSent; + unsigned long _internalColorSent; + unsigned long _leavesColorSent; + + unsigned long _didntFit; + unsigned long _internalDidntFit; + unsigned long _leavesDidntFit; + unsigned long total; - unsigned long traversed; - unsigned long internal; unsigned long internalOutOfView; unsigned long internalOccluded; unsigned long internalDirty; - unsigned long leaves; unsigned long leavesOutOfView; unsigned long leavesOccluded; unsigned long leavesDirty; // scene network related data - unsigned int packets; - unsigned int bytes; - unsigned int passes; + unsigned int _packets; + unsigned int _bytes; + unsigned int _passes; // features related items - bool wasFinished; - bool wasMoving; - bool hadDeltaView; - bool hadOcclusionCulling; + bool _moving; + bool _fullSceneDraw; }; #endif /* defined(__hifi__VoxelSceneStats__) */ diff --git a/libraries/voxels/src/VoxelTree.cpp b/libraries/voxels/src/VoxelTree.cpp index 9cb6a85f93..13e55bea0f 100644 --- a/libraries/voxels/src/VoxelTree.cpp +++ b/libraries/voxels/src/VoxelTree.cpp @@ -1037,6 +1037,13 @@ int VoxelTree::encodeTreeBitstream(VoxelNode* node, unsigned char* outputBuffer, availableBytes -= codeLength; // keep track or remaining space int currentEncodeLevel = 0; + + // record some stats, this is the one node that we won't record below in the recursion function, so we need to + // track it here + if (params.stats) { + params.stats->traversed(node); + } + int childBytesWritten = encodeTreeBitstreamRecursion(node, outputBuffer, availableBytes, bag, params, currentEncodeLevel); // if childBytesWritten == 1 then something went wrong... that's not possible @@ -1081,6 +1088,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // If we're too far away for our render level, then just return if (distance >= boundaryDistance) { + if (params.stats) { + params.stats->skippedDistance(node); + } return bytesAtThisLevel; } @@ -1088,6 +1098,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // although technically, we really shouldn't ever be here, because our callers shouldn't be calling us if // we're out of view if (!node->isInView(*params.viewFrustum)) { + if (params.stats) { + params.stats->skippedOutOfView(node); + } return bytesAtThisLevel; } @@ -1110,6 +1123,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // if we're in deltaViewFrustum mode, and this node has changed since it was last sent, then we do // need to send it. if (wasInView && !(params.deltaViewFrustum && node->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE))) { + if (params.stats) { + params.stats->skippedWasInView(node); + } return bytesAtThisLevel; } @@ -1117,6 +1133,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp // then we can also bail early and save bits if (!params.forceSendScene && !params.deltaViewFrustum && !node->hasChangedSince(params.lastViewFrustumSent - CHANGE_FUDGE)) { + if (params.stats) { + params.stats->skippedNoChange(node); + } return bytesAtThisLevel; } @@ -1136,6 +1155,9 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp CoverageMapStorageResult result = params.map->checkMap(voxelPolygon, false); delete voxelPolygon; // cleanup if (result == OCCLUDED) { + if (params.stats) { + params.stats->skippedOccluded(node); + } return bytesAtThisLevel; } } else { @@ -1201,6 +1223,12 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp distancesToChildren[i] = 0.0f; currentCount++; } + + // track stats + if (params.stats && childNode) { + params.stats->traversed(childNode); + } + } // for each child node in Distance sorted order..., check to see if they exist, are colored, and in view, and if so @@ -1211,13 +1239,21 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp bool childIsInView = (childNode && (!params.viewFrustum || childNode->isInView(*params.viewFrustum))); - if (childIsInView) { + if (!childIsInView) { + if (params.stats) { + params.stats->skippedOutOfView(childNode); + } + } else { // Before we determine consider this further, let's see if it's in our LOD scope... float distance = distancesToChildren[i]; // params.viewFrustum ? childNode->distanceToCamera(*params.viewFrustum) : 0; float boundaryDistance = !params.viewFrustum ? 1 : boundaryDistanceForRenderLevel(childNode->getLevel() + params.boundaryLevelAdjust); - if (distance < boundaryDistance) { + if (!(distance < boundaryDistance)) { + if (params.stats) { + params.stats->skippedDistance(childNode); + } + } else { inViewCount++; // track children in view as existing and not a leaf, if they're a leaf, @@ -1261,7 +1297,18 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp } // wants occlusion culling & isLeaf() - bool shouldRender = !params.viewFrustum ? true : childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust); + bool shouldRender = !params.viewFrustum ? true : + childNode->calculateShouldRender(params.viewFrustum, params.boundaryLevelAdjust); + + // track some stats + if (params.stats) { + if (!shouldRender) { + params.stats->skippedDistance(childNode); + } + if (childIsOccluded) { + params.stats->skippedOccluded(childNode); + } + } // track children with actual color, only if the child wasn't previously in view! if (shouldRender && !childIsOccluded) { @@ -1288,7 +1335,14 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp inViewWithColorCount++; } else { // otherwise just track stats of the items we discarded - params.childWasInViewDiscarded++; + if (params.stats) { + if (childWasInView) { + params.stats->skippedWasInView(childNode); + } else { + params.stats->skippedNoChange(childNode); + } + } + } } } @@ -1297,14 +1351,23 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp *writeToThisLevelBuffer = childrenColoredBits; writeToThisLevelBuffer += sizeof(childrenColoredBits); // move the pointer bytesAtThisLevel += sizeof(childrenColoredBits); // keep track of byte count + if (params.stats) { + params.stats->colorBitsWritten(node); + } // write the color data... if (params.includeColor) { for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { if (oneAtBit(childrenColoredBits, i)) { - memcpy(writeToThisLevelBuffer, &node->getChildAtIndex(i)->getColor(), BYTES_PER_COLOR); + VoxelNode* childNode = node->getChildAtIndex(i); + memcpy(writeToThisLevelBuffer, &childNode->getColor(), BYTES_PER_COLOR); writeToThisLevelBuffer += BYTES_PER_COLOR; // move the pointer for color bytesAtThisLevel += BYTES_PER_COLOR; // keep track of byte count for color + + if (params.stats) { + params.stats->colorSent(childNode); + } + } } } @@ -1315,12 +1378,18 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp *writeToThisLevelBuffer = childrenExistInTreeBits; writeToThisLevelBuffer += sizeof(childrenExistInTreeBits); // move the pointer bytesAtThisLevel += sizeof(childrenExistInTreeBits); // keep track of byte count + if (params.stats) { + params.stats->existsBitsWritten(node); + } } // write the child exist bits *writeToThisLevelBuffer = childrenExistInPacketBits; writeToThisLevelBuffer += sizeof(childrenExistInPacketBits); // move the pointer bytesAtThisLevel += sizeof(childrenExistInPacketBits); // keep track of byte count + if (params.stats) { + params.stats->existsInPacketBitsWritten(node); + } // We only need to keep digging, if there is at least one child that is inView, and not a leaf. keepDiggingDeeper = (inViewNotLeafCount > 0); @@ -1333,6 +1402,11 @@ int VoxelTree::encodeTreeBitstreamRecursion(VoxelNode* node, unsigned char* outp availableBytes -= bytesAtThisLevel; } else { bag.insert(node); + + if (params.stats) { + params.stats->didntFit(node); + } + return 0; } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index e5db6526e9..199942605e 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -9,12 +9,14 @@ #ifndef __hifi__VoxelTree__ #define __hifi__VoxelTree__ -#include "SimpleMovingAverage.h" +#include +#include + +#include "CoverageMap.h" #include "ViewFrustum.h" #include "VoxelNode.h" #include "VoxelNodeBag.h" -#include "CoverageMap.h" -#include "PointerStack.h" +#include "VoxelSceneStats.h" // Callback function, for recuseTreeWithOperation typedef bool (*RecurseVoxelTreeOperation)(VoxelNode* node, void* extraData); @@ -36,6 +38,7 @@ typedef enum {GRADIENT, RANDOM, NATURAL} creationMode; #define NO_BOUNDARY_ADJUST 0 #define LOW_RES_MOVING_ADJUST 1 #define IGNORE_LAST_SENT 0 +#define IGNORE_SCENE_STATS NULL class EncodeBitstreamParams { public: @@ -48,10 +51,10 @@ public: bool deltaViewFrustum; const ViewFrustum* lastViewFrustum; bool wantOcclusionCulling; - long childWasInViewDiscarded; int boundaryLevelAdjust; uint64_t lastViewFrustumSent; bool forceSendScene; + VoxelSceneStats* stats; CoverageMap* map; EncodeBitstreamParams( @@ -66,7 +69,8 @@ public: CoverageMap* map = IGNORE_COVERAGE_MAP, int boundaryLevelAdjust = NO_BOUNDARY_ADJUST, uint64_t lastViewFrustumSent = IGNORE_LAST_SENT, - bool forceSendScene = true) : + bool forceSendScene = true, + VoxelSceneStats* stats = IGNORE_SCENE_STATS) : maxEncodeLevel (maxEncodeLevel), maxLevelReached (0), viewFrustum (viewFrustum), @@ -76,10 +80,10 @@ public: deltaViewFrustum (deltaViewFrustum), lastViewFrustum (lastViewFrustum), wantOcclusionCulling (wantOcclusionCulling), - childWasInViewDiscarded (0), boundaryLevelAdjust (boundaryLevelAdjust), lastViewFrustumSent (lastViewFrustumSent), forceSendScene (forceSendScene), + stats (stats), map (map) {} }; diff --git a/voxel-server/src/main.cpp b/voxel-server/src/main.cpp index 65b3f5c5c4..0c13d6620a 100644 --- a/voxel-server/src/main.cpp +++ b/voxel-server/src/main.cpp @@ -200,9 +200,6 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, // only set our last sent time if we weren't resetting due to frustum change uint64_t now = usecTimestampNow(); nodeData->setLastTimeBagEmpty(now); - if (::debugVoxelSending) { - printf("ENTIRE SCENE SENT! nodeData->setLastTimeBagEmpty(now=[%lld])\n", now); - } } nodeData->stats.sceneCompleted(); @@ -210,7 +207,10 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, // This is the start of "resending" the scene. nodeData->nodeBag.insert(serverTree.rootNode); - nodeData->stats.sceneStarted(); + + // start tracking our stats + bool fullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && nodeData->getViewFrustumJustStoppedChanging(); + nodeData->stats.sceneStarted(fullScene, viewFrustumChanged); } // If we have something in our nodeBag, then turn them into packets and send them out... @@ -243,12 +243,15 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, CoverageMap* coverageMap = wantOcclusionCulling ? &nodeData->map : IGNORE_COVERAGE_MAP; int boundaryLevelAdjust = viewFrustumChanged && nodeData->getWantLowResMoving() ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST; + + bool fullScene = (!viewFrustumChanged || !nodeData->getWantDelta()) && + nodeData->getViewFrustumJustStoppedChanging(); EncodeBitstreamParams params(INT_MAX, &nodeData->getCurrentViewFrustum(), wantColor, WANT_EXISTS_BITS, DONT_CHOP, wantDelta, lastViewFrustum, wantOcclusionCulling, coverageMap, boundaryLevelAdjust, nodeData->getLastTimeBagEmpty(), - nodeData->getViewFrustumJustStoppedChanging()); + fullScene, &nodeData->stats); bytesWritten = serverTree.encodeTreeBitstream(subTree, &tempOutputBuffer[0], MAX_VOXEL_PACKET_SIZE - 1, nodeData->nodeBag, params); @@ -258,6 +261,8 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, } else { nodeList->getNodeSocket()->send(node->getActiveSocket(), nodeData->getPacket(), nodeData->getPacketLength()); + + nodeData->stats.packetSent(nodeData->getPacketLength()); trueBytesSent += nodeData->getPacketLength(); truePacketsSent++; packetsSentThisInterval++; @@ -268,6 +273,7 @@ void deepestLevelVoxelDistributor(NodeList* nodeList, if (nodeData->isPacketWaiting()) { nodeList->getNodeSocket()->send(node->getActiveSocket(), nodeData->getPacket(), nodeData->getPacketLength()); + nodeData->stats.packetSent(nodeData->getPacketLength()); trueBytesSent += nodeData->getPacketLength(); truePacketsSent++; nodeData->resetVoxelPacket();