Showing the changes to interface/src/VoxelSystem.cpp (1 of 8 files). Show all
Unified · Split
| @@ -70,10 +70,12 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) · 69 unmodified lines | |||
| 70 | _writeRenderFullVBO = true; | 70 | _writeRenderFullVBO = true; |
| 71 | _readRenderFullVBO = true; | 71 | _readRenderFullVBO = true; |
| 72 | _tree = new VoxelTree(); | 72 | _tree = new VoxelTree(); |
| 73 | _tree->rootNode->setVoxelSystem(this); | ||
| 73 | pthread_mutex_init(&_bufferWriteLock, NULL); | 74 | pthread_mutex_init(&_bufferWriteLock, NULL); |
| 74 | pthread_mutex_init(&_treeLock, NULL); | 75 | pthread_mutex_init(&_treeLock, NULL); |
| 75 | 76 | ||
| 76 | VoxelNode::addDeleteHook(this); | 77 | VoxelNode::addDeleteHook(this); |
| 78 | VoxelNode::addUpdateHook(this); | ||
| 77 | _abandonedVBOSlots = 0; | 79 | _abandonedVBOSlots = 0; |
| 78 | _falseColorizeBySource = false; | 80 | _falseColorizeBySource = false; |
| 79 | _dataSourceID = UNKNOWN_NODE_ID; | 81 | _dataSourceID = UNKNOWN_NODE_ID; |
| @@ -96,6 +98,8 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) · 16 unmodified lines | |||
| 96 | _writeVoxelDirtyArray = NULL; | 98 | _writeVoxelDirtyArray = NULL; |
| 97 | _readVoxelDirtyArray = NULL; | 99 | _readVoxelDirtyArray = NULL; |
| 98 | 100 | ||
| 101 | _inSetupNewVoxelsForDrawing = false; | ||
| 102 | _useFastVoxelPipeline = false; | ||
| 99 | } | 103 | } |
| 100 | 104 | ||
| 101 | void VoxelSystem::voxelDeleted(VoxelNode* node) { | 105 | void VoxelSystem::voxelDeleted(VoxelNode* node) { |
| @@ -104,6 +108,61 @@ void VoxelSystem::voxelDeleted(VoxelNode* node) { · 2 unmodified lines | |||
| 104 | } | 108 | } |
| 105 | } | 109 | } |
| 106 | 110 | ||
| 111 | void VoxelSystem::setUseFastVoxelPipeline(bool useFastVoxelPipeline) { | ||
| 112 | _useFastVoxelPipeline = useFastVoxelPipeline; | ||
| 113 | printf("setUseFastVoxelPipeline() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline)); | ||
| 114 | } | ||
| 115 | |||
| 116 | void VoxelSystem::voxelUpdated(VoxelNode* node) { | ||
| 117 | |||
| 118 | //printf("VoxelSystem::voxelUpdated() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline)); | ||
| 119 | |||
| 120 | // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. | ||
| 121 | if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | |||
| 125 | if (node->getVoxelSystem() == this) { | ||
| 126 | //printf("VoxelSystem::voxelUpdated()... node->getVoxelSystem() == this\n"); | ||
| 127 | |||
| 128 | bool shouldRender = false; // assume we don't need to render it | ||
| 129 | // if it's colored, we might need to render it! | ||
| 130 | shouldRender = node->calculateShouldRender(_viewFrustum); | ||
| 131 | |||
| 132 | node->setShouldRender(shouldRender); | ||
| 133 | |||
| 134 | //printf("VoxelSystem::voxelUpdated() shouldRender=%s\n", debug::valueOf(shouldRender)); | ||
| 135 | |||
| 136 | if (!node->isLeaf()) { | ||
| 137 | |||
| 138 | // As we check our children, see if any of them went from shouldRender to NOT shouldRender | ||
| 139 | // then we probably dropped LOD and if we don't have color, we want to average our children | ||
| 140 | // for a new color. | ||
| 141 | int childrenGotHiddenCount = 0; | ||
| 142 | for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { | ||
| 143 | VoxelNode* childNode = node->getChildAtIndex(i); | ||
| 144 | if (childNode) { | ||
| 145 | bool wasShouldRender = childNode->getShouldRender(); | ||
| 146 | bool isShouldRender = childNode->calculateShouldRender(_viewFrustum); | ||
| 147 | if (wasShouldRender && !isShouldRender) { | ||
| 148 | childrenGotHiddenCount++; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | if (childrenGotHiddenCount > 0) { | ||
| 153 | node->setColorFromAverageOfChildren(); | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | updateNodeInArraysAsPartialVBO(node); | ||
| 158 | _voxelsUpdated++; | ||
| 159 | |||
| 160 | node->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. | ||
| 161 | |||
| 162 | setupNewVoxelsForDrawingSingleNode(); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 107 | // returns an available index, starts by reusing a previously freed index, but if there isn't one available | 166 | // returns an available index, starts by reusing a previously freed index, but if there isn't one available |
| 108 | // it will use the end of the VBO array and grow our accounting of that array. | 167 | // it will use the end of the VBO array and grow our accounting of that array. |
| 109 | // and makes the index available for some other node to use | 168 | // and makes the index available for some other node to use |
| @@ -147,6 +206,7 @@ VoxelSystem::~VoxelSystem() { · 37 unmodified lines | |||
| 147 | pthread_mutex_destroy(&_treeLock); | 206 | pthread_mutex_destroy(&_treeLock); |
| 148 | 207 | ||
| 149 | VoxelNode::removeDeleteHook(this); | 208 | VoxelNode::removeDeleteHook(this); |
| 209 | VoxelNode::removeUpdateHook(this); | ||
| 150 | } | 210 | } |
| 151 | 211 | ||
| 152 | void VoxelSystem::setMaxVoxels(int maxVoxels) { | 212 | void VoxelSystem::setMaxVoxels(int maxVoxels) { |
| @@ -462,7 +522,11 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { · 309 unmodified lines | |||
| 462 | break; | 522 | break; |
| 463 | } | 523 | } |
| 464 | 524 | ||
| 465 | setupNewVoxelsForDrawing(); | 525 | //printf("VoxelSystem::parseData() _useFastVoxelPipeline=%s\n", debug::valueOf(_useFastVoxelPipeline)); |
| 526 | |||
| 527 | if (!_useFastVoxelPipeline || _writeRenderFullVBO) { | ||
| 528 | setupNewVoxelsForDrawing(); | ||
| 529 | } | ||
| 466 | 530 | ||
| 467 | pthread_mutex_unlock(&_treeLock); | 531 | pthread_mutex_unlock(&_treeLock); |
| 468 | 532 | ||
| @@ -474,6 +538,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) { · 5 unmodified lines | |||
| 474 | void VoxelSystem::setupNewVoxelsForDrawing() { | 538 | void VoxelSystem::setupNewVoxelsForDrawing() { |
| 475 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), | 539 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), |
| 476 | "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated | 540 | "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated |
| 541 | _inSetupNewVoxelsForDrawing = true; | ||
| 477 | uint64_t start = usecTimestampNow(); | 542 | uint64_t start = usecTimestampNow(); |
| 478 | uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; | 543 | uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; |
| 479 | 544 | ||
| @@ -547,12 +612,92 @@ void VoxelSystem::setupNewVoxelsForDrawing() { · 67 unmodified lines | |||
| 547 | int elapsedmsec = (end - start) / 1000; | 612 | int elapsedmsec = (end - start) / 1000; |
| 548 | _setupNewVoxelsForDrawingLastFinished = end; | 613 | _setupNewVoxelsForDrawingLastFinished = end; |
| 549 | _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; | 614 | _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; |
| 615 | _inSetupNewVoxelsForDrawing = false; | ||
| 550 | } | 616 | } |
| 551 | 617 | ||
| 618 | void VoxelSystem::setupNewVoxelsForDrawingSingleNode() { | ||
| 619 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), | ||
| 620 | "setupNewVoxelsForDrawingSingleNode()"); // would like to include _voxelsInArrays, _voxelsUpdated | ||
| 621 | uint64_t start = usecTimestampNow(); | ||
| 622 | uint64_t sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000; | ||
| 623 | |||
| 624 | // clear up the VBOs for any nodes that have been recently deleted. | ||
| 625 | clearFreeBufferIndexes(); | ||
| 626 | |||
| 627 | bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging | ||
| 628 | if (!iAmDebugging && sinceLastTime <= std::max((float) _setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) { | ||
| 629 | return; // bail early, it hasn't been long enough since the last time we ran | ||
| 630 | } | ||
| 631 | |||
| 632 | uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000; | ||
| 633 | // If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view | ||
| 634 | if ((sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) | ||
| 635 | && !isViewChanging()) { | ||
| 636 | _lastViewCulling = start; | ||
| 637 | |||
| 638 | // When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove | ||
| 639 | // them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which | ||
| 640 | // can be expensive). | ||
| 641 | removeOutOfView(); | ||
| 642 | |||
| 643 | // Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So, | ||
| 644 | // we should consider putting this someplace else... as this might be able to occur less frequently, and save us on | ||
| 645 | // VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary. | ||
| 646 | cleanupRemovedVoxels(); | ||
| 647 | |||
| 648 | uint64_t endViewCulling = usecTimestampNow(); | ||
| 649 | _lastViewCullingElapsed = (endViewCulling - start) / 1000; | ||
| 650 | } | ||
| 651 | |||
| 652 | bool didWriteFullVBO = _writeRenderFullVBO; | ||
| 653 | |||
| 654 | if (_tree->isDirty()) { | ||
| 655 | static char buffer[64] = { 0 }; | ||
| 656 | if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { | ||
| 657 | sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", debug::valueOf(_writeRenderFullVBO)); | ||
| 658 | }; | ||
| 659 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); | ||
| 660 | _callsToTreesToArrays++; | ||
| 661 | if (_writeRenderFullVBO) { | ||
| 662 | _voxelsInWriteArrays = 0; // reset our VBO | ||
| 663 | _freeIndexes.clear(); // reset our free indexes | ||
| 664 | _voxelsUpdated = newTreeToArrays(_tree->rootNode); | ||
| 665 | _tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean | ||
| 666 | _abandonedVBOSlots = 0; // reset the count of our abandoned slots, why is this here and not earlier???? | ||
| 667 | } | ||
| 668 | |||
| 669 | // since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are | ||
| 670 | // ok again, until/unless we call removeOutOfView() | ||
| 671 | _writeRenderFullVBO = false; | ||
| 672 | } else { | ||
| 673 | _voxelsUpdated = 0; | ||
| 674 | } | ||
| 675 | |||
| 676 | // lock on the buffer write lock so we can't modify the data when the GPU is reading it | ||
| 677 | pthread_mutex_lock(&_bufferWriteLock); | ||
| 678 | |||
| 679 | if (_voxelsUpdated) { | ||
| 680 | _voxelsDirty=true; | ||
| 681 | } | ||
| 682 | |||
| 683 | // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated | ||
| 684 | copyWrittenDataToReadArrays(didWriteFullVBO); | ||
| 685 | |||
| 686 | // after... | ||
| 687 | _voxelsUpdated = 0; | ||
| 688 | |||
| 689 | pthread_mutex_unlock(&_bufferWriteLock); | ||
| 690 | |||
| 691 | uint64_t end = usecTimestampNow(); | ||
| 692 | int elapsedmsec = (end - start) / 1000; | ||
| 693 | _setupNewVoxelsForDrawingLastFinished = end; | ||
| 694 | _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; | ||
| 695 | } | ||
| 552 | void VoxelSystem::cleanupRemovedVoxels() { | 696 | void VoxelSystem::cleanupRemovedVoxels() { |
| 553 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "cleanupRemovedVoxels()"); | 697 | PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "cleanupRemovedVoxels()"); |
| 554 | // This handles cleanup of voxels that were culled as part of our regular out of view culling operation | 698 | // This handles cleanup of voxels that were culled as part of our regular out of view culling operation |
| 555 | if (!_removedVoxels.isEmpty()) { | 699 | if (!_removedVoxels.isEmpty()) { |
| 700 | qDebug() << "cleanupRemovedVoxels().. _removedVoxels=" << _removedVoxels.count() << "\n"; | ||
| 556 | while (!_removedVoxels.isEmpty()) { | 701 | while (!_removedVoxels.isEmpty()) { |
| 557 | delete _removedVoxels.extract(); | 702 | delete _removedVoxels.extract(); |
| 558 | } | 703 | } |
| @@ -560,8 +705,9 @@ void VoxelSystem::cleanupRemovedVoxels() { · 1 unmodified line | |||
| 560 | } | 705 | } |
| 561 | // we also might have VBO slots that have been abandoned, if too many of our VBO slots | 706 | // we also might have VBO slots that have been abandoned, if too many of our VBO slots |
| 562 | // are abandonded we want to rerender our full VBOs | 707 | // are abandonded we want to rerender our full VBOs |
| 563 | const float TOO_MANY_ABANDONED_RATIO = 0.25f; | 708 | const float TOO_MANY_ABANDONED_RATIO = 0.5f; |
| 564 | if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) { | 709 | if (!_writeRenderFullVBO && (_abandonedVBOSlots > (_voxelsInWriteArrays * TOO_MANY_ABANDONED_RATIO))) { |
| 710 | qDebug() << "cleanupRemovedVoxels().. _abandonedVBOSlots ["<< _abandonedVBOSlots << "] > TOO_MANY_ABANDONED_RATIO \n"; | ||
| 565 | _writeRenderFullVBO = true; | 711 | _writeRenderFullVBO = true; |
| 566 | } | 712 | } |
| 567 | } | 713 | } |
| @@ -704,7 +850,7 @@ int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) { · 136 unmodified lines | |||
| 704 | return 1; // rendered | 850 | return 1; // rendered |
| 705 | } else { | 851 | } else { |
| 706 | node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); | 852 | node->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); |
| 707 | node->setVoxelSystem(NULL); | 853 | //node->setVoxelSystem(NULL); |
| 708 | } | 854 | } |
| 709 | 855 | ||
| 710 | return 0; // not-rendered | 856 | return 0; // not-rendered |
| @@ -805,6 +951,9 @@ void VoxelSystem::init() { · 94 unmodified lines | |||
| 805 | // VBO for the verticesArray | 951 | // VBO for the verticesArray |
| 806 | initVoxelMemory(); | 952 | initVoxelMemory(); |
| 807 | _initialized = true; | 953 | _initialized = true; |
| 954 | |||
| 955 | // our own _removedVoxels doesn't need to be notified of voxel deletes | ||
| 956 | VoxelNode::removeDeleteHook(&_removedVoxels); | ||
| 808 | } | 957 | } |
| 809 | 958 | ||
| 810 | void VoxelSystem::changeTree(VoxelTree* newTree) { | 959 | void VoxelSystem::changeTree(VoxelTree* newTree) { |
| @@ -812,6 +961,7 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { · 1 unmodified line | |||
| 812 | 961 | ||
| 813 | _tree = newTree; | 962 | _tree = newTree; |
| 814 | _tree->setDirtyBit(); | 963 | _tree->setDirtyBit(); |
| 964 | _tree->rootNode->setVoxelSystem(this); | ||
| 815 | 965 | ||
| 816 | connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); | 966 | connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); |
| 817 | connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); | 967 | connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); |