diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 2ca6384a8d..4848181ebc 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -268,6 +268,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) { *(writeColorsAt +j) = node->getColor()[j % 3]; } node->setBufferIndex(nodeIndex); + _voxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode _voxelsInWriteArrays++; // our know vertices in the arrays return 1; // rendered } @@ -280,8 +281,8 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) { return 0; } - // Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays... for us - if (node->isDirty() && (node->getShouldRender() || node->isKnownBufferIndex())) { + // Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays... + if (node->isDirty()) { glm::vec3 startVertex; float voxelScale = 0; // If we're should render, use our legit location and scale, @@ -301,8 +302,9 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) { nodeIndex = node->getBufferIndex(); } else { nodeIndex = _voxelsInWriteArrays; + node->setBufferIndex(nodeIndex); + _voxelsInWriteArrays++; } - _voxelDirtyArray[nodeIndex] = true; // populate the array with points for the 8 vertices @@ -313,10 +315,6 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) { *(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale); *(writeColorsAt +j) = node->getColor()[j % 3]; } - if (!node->isKnownBufferIndex()) { - node->setBufferIndex(nodeIndex); - _voxelsInWriteArrays++; // our know vertices in the arrays - } return 1; // updated! } return 0; // not-updated @@ -424,11 +422,10 @@ void VoxelSystem::updateFullVBOs() { glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); // consider the _voxelDirtyArray[] clean! - memset(_voxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); + memset(_voxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool)); } void VoxelSystem::updatePartialVBOs() { - int segmentCount = 0; glBufferIndex segmentStart = 0; glBufferIndex segmentEnd = 0; bool inSegment = false; @@ -457,11 +454,6 @@ void VoxelSystem::updatePartialVBOs() { GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); - - // debug - segmentCount++; - printLog("updatePartialVBOs() start=%ld, end=%ld, length=%ld, segmentCount=%d \n", - segmentStart, segmentEnd, segmentLength, segmentCount); } _voxelDirtyArray[i] = false; // consider us clean! } @@ -482,45 +474,9 @@ void VoxelSystem::updatePartialVBOs() { GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); - - // debug - segmentCount++; - printLog("updatePartialVBOs() start=%ld, end=%ld, length=%ld, segmentCount=%d \n", - segmentStart, segmentEnd, segmentLength, segmentCount); } } -void debugOpenGLError(const char* label) { - GLenum error = glGetError(); - const char* errorMessage; - switch (error) { - case GL_NO_ERROR: - return; - case GL_INVALID_ENUM: - errorMessage = "GL_INVALID_ENUM"; - break; - case GL_INVALID_VALUE: - errorMessage = "GL_INVALID_VALUE"; - break; - case GL_INVALID_OPERATION: - errorMessage = "GL_INVALID_OPERATION"; - break; - case GL_STACK_OVERFLOW: - errorMessage = "GL_STACK_OVERFLOW"; - break; - case GL_STACK_UNDERFLOW: - errorMessage = "GL_STACK_UNDERFLOW"; - break; - case GL_OUT_OF_MEMORY: - errorMessage = "GL_OUT_OF_MEMORY"; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - errorMessage = "GL_INVALID_FRAMEBUFFER_OPERATION"; - break; - } - printLog("%s generated %s", label, errorMessage); -} - void VoxelSystem::updateJustEnoughVBOs() { bool somethingDirty = false; glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN; @@ -538,31 +494,17 @@ void VoxelSystem::updateJustEnoughVBOs() { if (somethingDirty) { glBufferIndex segmentStart = minDirty; glBufferIndex segmentEnd = maxDirty; - -glGetError(); // clear errors - int segmentLength = (segmentEnd - segmentStart) + 1; GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat); GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID); -debugOpenGLError("glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID)"); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom); -debugOpenGLError("glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom)"); - segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte); GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL); - glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID); -debugOpenGLError("glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID)"); - glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom); -debugOpenGLError("glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom)"); - - // debug - printLog("updateJustEnoughVBOs() start=%ld, end=%ld, length=%ld, \n", segmentStart, segmentEnd, segmentLength); } } @@ -574,8 +516,7 @@ void VoxelSystem::updateVBOs() { PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays if (_voxelsDirty) { // updatePartialVBOs() is not yet working. For now, ALWAYS call updateFullVBOs() - bool alwaysRenderFullVBO = true; - if (alwaysRenderFullVBO || _renderFullVBO) { + if (_renderFullVBO) { updateFullVBOs(); } else { updateJustEnoughVBOs(); @@ -832,6 +773,9 @@ void VoxelSystem::removeOutOfView() { removeOutOfViewArgs args(this); _tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)&args); + if (args.nodesRemoved) { + _tree->setDirtyBit(); + } bool showRemoveDebugDetails = false; if (showRemoveDebugDetails) { printLog("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n", @@ -841,6 +785,22 @@ void VoxelSystem::removeOutOfView() { } } +bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + VoxelDetail& detail, float& distance, BoxFace& face) { + VoxelNode* node; + if (!_tree->findRayIntersection(origin, direction, node, distance, face)) { + return false; + } + detail.x = node->getCorner().x; + detail.y = node->getCorner().y; + detail.z = node->getCorner().z; + detail.s = node->getScale(); + detail.red = node->getColor()[0]; + detail.green = node->getColor()[1]; + detail.blue = node->getColor()[2]; + return true; +} + class falseColorizeRandomEveryOtherArgs { public: falseColorizeRandomEveryOtherArgs() : totalNodes(0), colorableNodes(0), coloredNodes(0), colorThis(true) {}; @@ -872,19 +832,101 @@ void VoxelSystem::falseColorizeRandomEveryOther() { setupNewVoxelsForDrawing(); } -bool VoxelSystem::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - VoxelDetail& detail, float& distance, BoxFace& face) { - VoxelNode* node; - if (!_tree->findRayIntersection(origin, direction, node, distance, face)) { - return false; +class collectStatsForTreesAndVBOsArgs { +public: + collectStatsForTreesAndVBOsArgs() : + totalNodes(0), + dirtyNodes(0), + shouldRenderNodes(0), + coloredNodes(0), + nodesInVBO(0), + nodesInVBOOverExpectedMax(0), + duplicateVBOIndex(0) + { + memset(hasIndexFound, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool)); + }; + + unsigned long totalNodes; + unsigned long dirtyNodes; + unsigned long shouldRenderNodes; + unsigned long coloredNodes; + unsigned long nodesInVBO; + unsigned long nodesInVBOOverExpectedMax; + unsigned long duplicateVBOIndex; + unsigned long expectedMax; + + bool colorThis; + bool hasIndexFound[MAX_VOXELS_PER_SYSTEM]; +}; + +bool VoxelSystem::collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData) { + collectStatsForTreesAndVBOsArgs* args = (collectStatsForTreesAndVBOsArgs*)extraData; + args->totalNodes++; + + if (node->isColored()) { + args->coloredNodes++; } - detail.x = node->getCorner().x; - detail.y = node->getCorner().y; - detail.z = node->getCorner().z; - detail.s = node->getScale(); - detail.red = node->getColor()[0]; - detail.green = node->getColor()[1]; - detail.blue = node->getColor()[2]; - return true; + + if (node->getShouldRender()) { + args->shouldRenderNodes++; + } + + if (node->isDirty()) { + args->dirtyNodes++; + } + + if (node->isKnownBufferIndex()) { + args->nodesInVBO++; + unsigned long nodeIndex = node->getBufferIndex(); + if (args->hasIndexFound[nodeIndex]) { + args->duplicateVBOIndex++; + printLog("duplicateVBO found... index=%ld, isDirty=%s, shouldRender=%s \n", nodeIndex, + node->isDirty() ? "yes" : "no" , node->getShouldRender() ? "yes" : "no" ); + } else { + args->hasIndexFound[nodeIndex] = true; + } + if (nodeIndex > args->expectedMax) { + args->nodesInVBOOverExpectedMax++; + } + } + + return true; // keep going! } +void VoxelSystem::collectStatsForTreesAndVBOs() { + + glBufferIndex minDirty = GLBUFFER_INDEX_UNKNOWN; + glBufferIndex maxDirty = 0; + + for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) { + if (_voxelDirtyArray[i]) { + minDirty = std::min(minDirty,i); + maxDirty = std::max(maxDirty,i); + } + } + + collectStatsForTreesAndVBOsArgs args; + args.expectedMax = _voxelsInWriteArrays; + _tree->recurseTreeWithOperation(collectStatsForTreesAndVBOsOperation,&args); + + printLog("_voxelsDirty=%s _voxelsInWriteArrays=%ld minDirty=%ld maxDirty=%ld \n", (_voxelsDirty ? "yes" : "no"), + _voxelsInWriteArrays, minDirty, maxDirty); + + printLog("stats: total %ld, dirty %ld, colored %ld, shouldRender %ld, inVBO %ld, nodesInVBOOverExpectedMax %ld, duplicateVBOIndex %ld\n", + args.totalNodes, args.dirtyNodes, args.coloredNodes, args.shouldRenderNodes, + args.nodesInVBO, args.nodesInVBOOverExpectedMax, args.duplicateVBOIndex); + + glBufferIndex minInVBO = GLBUFFER_INDEX_UNKNOWN; + glBufferIndex maxInVBO = 0; + + for (glBufferIndex i = 0; i < MAX_VOXELS_PER_SYSTEM; i++) { + if (args.hasIndexFound[i]) { + minInVBO = std::min(minInVBO,i); + maxInVBO = std::max(maxInVBO,i); + } + } + + printLog("minInVBO=%ld maxInVBO=%ld _voxelsInWriteArrays=%ld _voxelsInReadArrays=%ld\n", + minInVBO, maxInVBO, _voxelsInWriteArrays, _voxelsInReadArrays); + +} diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index 08d1257069..8492474109 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -68,6 +68,8 @@ public: bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, VoxelDetail& detail, float& distance, BoxFace& face); + + void collectStatsForTreesAndVBOs(); private: int _callsToTreesToArrays; @@ -84,6 +86,7 @@ private: static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData); static bool removeOutOfViewOperation(VoxelNode* node, void* extraData); static bool falseColorizeRandomEveryOtherOperation(VoxelNode* node, void* extraData); + static bool collectStatsForTreesAndVBOsOperation(VoxelNode* node, void* extraData); int updateNodeInArraysAsFullVBO(VoxelNode* node); int updateNodeInArraysAsPartialVBO(VoxelNode* node); @@ -127,12 +130,14 @@ private: void setupNewVoxelsForDrawing(); void copyWrittenDataToReadArrays(); + bool _voxelsDirty; + +public: void updateVBOs(); void updateFullVBOs(); // all voxels in the VBO void updatePartialVBOs(); // multiple segments, only dirty voxels void updateJustEnoughVBOs(); // single segment from first dirty, to last dirty, may include clean voxels - bool _voxelsDirty; }; #endif diff --git a/interface/src/main.cpp b/interface/src/main.cpp index f5a19507c9..e4010ad96b 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -1337,6 +1337,13 @@ int doFalseRandomizeEveryOtherVoxelColors(int state) { return state; } +int doTreeStats(int state) { + if (state == MENU_ROW_PICKED) { + ::voxels.collectStatsForTreesAndVBOs(); + } + return state; +} + int doFalseRandomizeVoxelColors(int state) { if (state == MENU_ROW_PICKED) { ::voxels.falseColorizeRandom(); @@ -1437,6 +1444,7 @@ void initMenu() { menuColumnDebug->addRow("FALSE Color Voxels by Distance", doFalseColorizeByDistance); menuColumnDebug->addRow("FALSE Color Voxel Out of View", doFalseColorizeInView); menuColumnDebug->addRow("Show TRUE Colors", doTrueVoxelColors); + menuColumnDebug->addRow("Calculate Tree Stats", doTreeStats); } void testPointToVoxel() { @@ -1636,7 +1644,12 @@ void key(unsigned char k, int x, int y) { } // Process keypresses - if (k == 'q' || k == 'Q') ::terminate(); + + if (k == 'S') { + ::voxels.collectStatsForTreesAndVBOs(); + } + + if (k == 'q' || k == 'Q') ::terminate(); if (k == '/') ::renderStatsOn = !::renderStatsOn; // toggle stats if (k == '*') ::renderStarsOn = !::renderStarsOn; // toggle stars if (k == 'V' || k == 'v') ::renderVoxels = !::renderVoxels; // toggle voxels diff --git a/libraries/voxels/src/VoxelNode.cpp b/libraries/voxels/src/VoxelNode.cpp index 80c31bc877..5af7f09ea2 100644 --- a/libraries/voxels/src/VoxelNode.cpp +++ b/libraries/voxels/src/VoxelNode.cpp @@ -94,6 +94,7 @@ VoxelNode* VoxelNode::removeChildAtIndex(int childIndex) { VoxelNode* returnedChild = _children[childIndex]; if (_children[childIndex]) { _children[childIndex] = NULL; + _isDirty = true; } return returnedChild; } diff --git a/libraries/voxels/src/VoxelTree.h b/libraries/voxels/src/VoxelTree.h index 5574ca7602..8cecff035d 100644 --- a/libraries/voxels/src/VoxelTree.h +++ b/libraries/voxels/src/VoxelTree.h @@ -61,6 +61,7 @@ public: bool isDirty() const { return _isDirty; }; void clearDirtyBit() { _isDirty = false; }; + void setDirtyBit() { _isDirty = true; }; unsigned long int getNodesChangedFromBitstream() const { return _nodesChangedFromBitstream; }; bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction,