From d6b71f19a8dd075d45195ddfc05c617c7f40f8e4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Feb 2014 18:00:09 -0800 Subject: [PATCH 1/8] fix voxel texture to work better with larger TREE_SCALE --- interface/resources/shaders/perlin_modulate.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/shaders/perlin_modulate.frag b/interface/resources/shaders/perlin_modulate.frag index eea0da3671..8ead57c238 100644 --- a/interface/resources/shaders/perlin_modulate.frag +++ b/interface/resources/shaders/perlin_modulate.frag @@ -12,7 +12,7 @@ uniform sampler2D permutationNormalTexture; // the noise frequency -const float frequency = 1024.0; +const float frequency = 65536.0; // looks better with current TREE_SCALE, was 1024 when TREE_SCALE was either 512 or 128 // the noise amplitude const float amplitude = 0.1; From 20a6f4eea95e826a23d74ab17d275ac0d52f930d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 12 Feb 2014 21:22:21 -0800 Subject: [PATCH 2/8] first cut at auto-LOD adjustment --- interface/src/Application.cpp | 7 +++- interface/src/Menu.cpp | 31 +++++++++++++-- interface/src/Menu.h | 14 +++++++ interface/src/VoxelSystem.cpp | 61 ++++++++++++++++++++--------- interface/src/VoxelSystem.h | 14 ++++--- interface/src/ui/LodToolsDialog.cpp | 6 +++ interface/src/ui/LodToolsDialog.h | 1 + libraries/shared/src/PerfStat.h | 2 + 8 files changed, 108 insertions(+), 28 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 004b71074b..29d838e10b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2720,7 +2720,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "Application::displaySide() ... voxels..."); if (!Menu::getInstance()->isOptionChecked(MenuOption::DontRenderVoxels)) { - _voxels.render(Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures)); + _voxels.render(); + + // double check that our LOD doesn't need to be auto-adjusted + Menu::getInstance()->autoAdjustLOD(_fps); } } @@ -2811,7 +2814,7 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { _mouseVoxel.s, _mouseVoxel.s); - _sharedVoxelSystem.render(true); + _sharedVoxelSystem.render(); glPopMatrix(); } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 7eb5807c6f..89277c794c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -70,7 +70,8 @@ Menu::Menu() : _maxVoxels(DEFAULT_MAX_VOXELS_PER_SYSTEM), _voxelSizeScale(DEFAULT_OCTREE_SIZE_SCALE), _boundaryLevelAdjust(0), - _maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS) + _maxVoxelPacketsPerSecond(DEFAULT_MAX_VOXEL_PPS), + _lastAdjust(usecTimestampNow()) { Application *appInstance = Application::getInstance(); @@ -1096,14 +1097,38 @@ void Menu::voxelStatsDetailsClosed() { } } +void Menu::autoAdjustLOD(float currentFPS) { + bool changed = false; + quint64 now = usecTimestampNow(); + quint64 elapsed = now - _lastAdjust; + + if (elapsed > ADJUST_LOD_DOWN_DELAY && currentFPS < ADJUST_LOD_DOWN_FPS && _voxelSizeScale > ADJUST_LOD_MIN_SIZE_SCALE) { + _voxelSizeScale *= ADJUST_LOD_DOWN_BY; + changed = true; + _lastAdjust = now; + qDebug() << "adjusting LOD down... currentFPS=" << currentFPS << "_voxelSizeScale=" << _voxelSizeScale; + } + + if (elapsed > ADJUST_LOD_UP_DELAY && currentFPS > ADJUST_LOD_UP_FPS && _voxelSizeScale < ADJUST_LOD_MAX_SIZE_SCALE) { + _voxelSizeScale *= ADJUST_LOD_UP_BY; + changed = true; + _lastAdjust = now; + qDebug() << "adjusting LOD up... currentFPS=" << currentFPS << "_voxelSizeScale=" << _voxelSizeScale; + } + + if (changed) { + if (_lodToolsDialog) { + _lodToolsDialog->reloadSliders(); + } + } +} + void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; - Application::getInstance()->getVoxels()->redrawInViewVoxels(); } void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; - Application::getInstance()->getVoxels()->redrawInViewVoxels(); } void Menu::lodTools() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 742b9fc66f..fd37856b26 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -16,6 +16,18 @@ #include +const float ADJUST_LOD_DOWN_FPS = 40.0; +const float ADJUST_LOD_UP_FPS = 55.0; + +const quint64 ADJUST_LOD_DOWN_DELAY = 1000 * 1000 * 5; +const quint64 ADJUST_LOD_UP_DELAY = ADJUST_LOD_DOWN_DELAY * 2; + +const float ADJUST_LOD_DOWN_BY = 0.9f; +const float ADJUST_LOD_UP_BY = 1.1f; + +const float ADJUST_LOD_MIN_SIZE_SCALE = TREE_SCALE * 1.0f; +const float ADJUST_LOD_MAX_SIZE_SCALE = DEFAULT_OCTREE_SIZE_SCALE; + enum FrustumDrawMode { FRUSTUM_DRAW_MODE_ALL, FRUSTUM_DRAW_MODE_VECTORS, @@ -68,6 +80,7 @@ public: void handleViewFrustumOffsetKeyModifier(int key); // User Tweakable LOD Items + void autoAdjustLOD(float currentFPS); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } void setBoundaryLevelAdjust(int boundaryLevelAdjust); @@ -154,6 +167,7 @@ private: int _maxVoxelPacketsPerSecond; QMenu* _activeScriptsMenu; QString replaceLastOccurrence(QChar search, QChar replace, QString string); + quint64 _lastAdjust; }; namespace MenuOption { diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 1ffc6c3e5e..a4e47138d1 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -99,6 +99,9 @@ VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) _culledOnce = false; _inhideOutOfView = false; + + _lastKnownVoxelSizeScale = DEFAULT_OCTREE_SIZE_SCALE; + _lastKnownBoundaryLevelAdjust = 0; } void VoxelSystem::elementDeleted(OctreeElement* element) { @@ -249,6 +252,9 @@ VoxelSystem::~VoxelSystem() { delete _tree; } + +// This is called by the main application thread on both the initialization of the application and when +// the preferences dialog box is called/saved void VoxelSystem::setMaxVoxels(int maxVoxels) { if (maxVoxels == _maxVoxels) { return; @@ -267,6 +273,8 @@ void VoxelSystem::setMaxVoxels(int maxVoxels) { } } +// This is called by the main application thread on both the initialization of the application and when +// the use voxel shader menu item is chosen void VoxelSystem::setUseVoxelShader(bool useVoxelShader) { if (_useVoxelShader == useVoxelShader) { return; @@ -330,7 +338,7 @@ void VoxelSystem::setVoxelsAsPoints(bool voxelsAsPoints) { void VoxelSystem::cleanupVoxelMemory() { if (_initialized) { - _bufferWriteLock.lock(); + _readArraysLock.lockForWrite(); _initialized = false; // no longer initialized if (_useVoxelShader) { // these are used when in VoxelShader mode. @@ -368,7 +376,7 @@ void VoxelSystem::cleanupVoxelMemory() { delete[] _writeVoxelDirtyArray; delete[] _readVoxelDirtyArray; _writeVoxelDirtyArray = _readVoxelDirtyArray = NULL; - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); } } @@ -401,7 +409,7 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice } void VoxelSystem::initVoxelMemory() { - _bufferWriteLock.lock(); + _readArraysLock.lockForWrite(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them @@ -516,7 +524,7 @@ void VoxelSystem::initVoxelMemory() { _initialized = true; - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const { @@ -646,7 +654,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { } _inSetupNewVoxelsForDrawing = true; - + bool didWriteFullVBO = _writeRenderFullVBO; if (_tree->isDirty()) { static char buffer[64] = { 0 }; @@ -673,7 +681,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { } // lock on the buffer write lock so we can't modify the data when the GPU is reading it - _bufferWriteLock.lock(); + _readArraysLock.lockForWrite(); if (_voxelsUpdated) { _voxelsDirty=true; @@ -682,7 +690,7 @@ void VoxelSystem::setupNewVoxelsForDrawing() { // copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated copyWrittenDataToReadArrays(didWriteFullVBO); - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; @@ -713,8 +721,8 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { // lock on the buffer write lock so we can't modify the data when the GPU is reading it { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), - "setupNewVoxelsForDrawingSingleNode()... _bufferWriteLock.lock();" ); - _bufferWriteLock.lock(); + "setupNewVoxelsForDrawingSingleNode()... _readArraysLock.lockForWrite();" ); + _readArraysLock.lockForWrite(); } _voxelsDirty = true; // if we got this far, then we can assume some voxels are dirty @@ -725,7 +733,7 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { // after... _voxelsUpdated = 0; - _bufferWriteLock.unlock(); + _readArraysLock.unlock(); quint64 end = usecTimestampNow(); int elapsedmsec = (end - start) / 1000; @@ -733,8 +741,12 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; } -void VoxelSystem::checkForCulling() { +void VoxelSystem::recreateVoxelGeometryInView() { + // this is a temporary solution... we need a full implementation that actually does a full redraw into clean VBOs + hideOutOfView(true); +} +void VoxelSystem::checkForCulling() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()"); quint64 start = usecTimestampNow(); @@ -762,7 +774,20 @@ void VoxelSystem::checkForCulling() { _hasRecentlyChanged = false; } - hideOutOfView(forceFullFrustum); + // This would be a good place to do a special processing pass, for example, switching the LOD of the scene + bool fullRedraw = (_lastKnownVoxelSizeScale != Menu::getInstance()->getVoxelSizeScale() || + _lastKnownBoundaryLevelAdjust != Menu::getInstance()->getBoundaryLevelAdjust()); + + // track that these values + _lastKnownVoxelSizeScale = Menu::getInstance()->getVoxelSizeScale(); + _lastKnownBoundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); + + if (fullRedraw) { + // this will remove all old geometry and recreate the correct geometry for all in view voxels + recreateVoxelGeometryInView(); + } else { + hideOutOfView(forceFullFrustum); + } if (forceFullFrustum) { quint64 endViewCulling = usecTimestampNow(); @@ -1197,7 +1222,8 @@ void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex seg } } -void VoxelSystem::render(bool texture) { +void VoxelSystem::render() { + bool texture = Menu::getInstance()->isOptionChecked(MenuOption::VoxelTextures); bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "render()"); @@ -1404,11 +1430,7 @@ void VoxelSystem::killLocalVoxels() { setupNewVoxelsForDrawing(); } -void VoxelSystem::redrawInViewVoxels() { - hideOutOfView(true); -} - - +// only called on main thread bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void* extraData) { _nodeCount++; VoxelTreeElement* voxel = (VoxelTreeElement*)element; @@ -1416,12 +1438,15 @@ bool VoxelSystem::clearAllNodesBufferIndexOperation(OctreeElement* element, void return true; } +// only called on main thread, and also always followed by a call to cleanupVoxelMemory() +// you shouldn't be calling this on any other thread or without also cleaning up voxel memory void VoxelSystem::clearAllNodesBufferIndex() { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "VoxelSystem::clearAllNodesBufferIndex()"); _nodeCount = 0; _tree->lockForRead(); // we won't change the tree so it's ok to treat this as a read _tree->recurseTreeWithOperation(clearAllNodesBufferIndexOperation); + clearFreeBufferIndexes(); // this should be called too _tree->unlock(); if (Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings)) { qDebug("clearing buffer index of %d nodes", _nodeCount); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index d1404668bf..c2f60d6699 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -53,7 +53,7 @@ public: virtual void init(); void simulate(float deltaTime) { } - void render(bool texture); + void render(); void changeTree(VoxelTree* newTree); VoxelTree* getTree() const { return _tree; } @@ -79,7 +79,6 @@ public: unsigned long getVoxelMemoryUsageGPU(); void killLocalVoxels(); - void redrawInViewVoxels(); virtual void removeOutOfView(); virtual void hideOutOfView(bool forceFullFrustum = false); @@ -151,6 +150,7 @@ protected: static const bool DONT_BAIL_EARLY; // by default we will bail early, if you want to force not bailing, then use this void setupNewVoxelsForDrawingSingleNode(bool allowBailEarly = true); void checkForCulling(); + void recreateVoxelGeometryInView(); glm::vec3 computeVoxelVertex(const glm::vec3& startVertex, float voxelScale, int index) const; @@ -211,6 +211,10 @@ private: GLfloat* _readVerticesArray; GLubyte* _readColorsArray; + + QReadWriteLock _readArraysLock; + + GLfloat* _writeVerticesArray; GLubyte* _writeColorsArray; bool* _writeVoxelDirtyArray; @@ -253,9 +257,6 @@ private: GLuint _vboIndicesFront; GLuint _vboIndicesBack; - QMutex _bufferWriteLock; - QMutex _treeLock; - ViewFrustum _lastKnownViewFrustum; ViewFrustum _lastStableViewFrustum; ViewFrustum* _viewFrustum; @@ -299,6 +300,9 @@ private: bool _useFastVoxelPipeline; bool _inhideOutOfView; + + float _lastKnownVoxelSizeScale; + int _lastKnownBoundaryLevelAdjust; }; #endif diff --git a/interface/src/ui/LodToolsDialog.cpp b/interface/src/ui/LodToolsDialog.cpp index 788f7e5561..4cf4a29bf1 100644 --- a/interface/src/ui/LodToolsDialog.cpp +++ b/interface/src/ui/LodToolsDialog.cpp @@ -121,6 +121,12 @@ LodToolsDialog::~LodToolsDialog() { delete _boundaryLevelAdjust; } +void LodToolsDialog::reloadSliders() { + _lodSize->setValue(Menu::getInstance()->getVoxelSizeScale() / TREE_SCALE); + _boundaryLevelAdjust->setValue(Menu::getInstance()->getBoundaryLevelAdjust()); + _feedback->setText(getFeedbackText()); +} + void LodToolsDialog::sizeScaleValueChanged(int value) { float realValue = value * TREE_SCALE; Menu::getInstance()->setVoxelSizeScale(realValue); diff --git a/interface/src/ui/LodToolsDialog.h b/interface/src/ui/LodToolsDialog.h index ee14196188..ee96cffd7e 100644 --- a/interface/src/ui/LodToolsDialog.h +++ b/interface/src/ui/LodToolsDialog.h @@ -28,6 +28,7 @@ public slots: void sizeScaleValueChanged(int value); void boundaryLevelValueChanged(int value); void resetClicked(bool checked); + void reloadSliders(); protected: diff --git a/libraries/shared/src/PerfStat.h b/libraries/shared/src/PerfStat.h index fffb095021..a7a10b97b8 100644 --- a/libraries/shared/src/PerfStat.h +++ b/libraries/shared/src/PerfStat.h @@ -45,6 +45,8 @@ public: _alwaysDisplay(alwaysDisplay), _runningTotal(runningTotal), _totalCalls(totalCalls) { } + + quint64 elapsed() const { return (usecTimestampNow() - _start); }; ~PerformanceWarning(); From cac67d7baa0669f46864cce742b47e3099d6f4fd Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 13:06:45 -0800 Subject: [PATCH 3/8] add proper readWriteLocks around the VBO arrays to preven random triangles --- interface/src/VoxelSystem.cpp | 160 ++++++++++++++++++++++++++++++---- interface/src/VoxelSystem.h | 2 + 2 files changed, 144 insertions(+), 18 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index a4e47138d1..851c24c5ce 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -57,9 +57,12 @@ GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 }; VoxelSystem::VoxelSystem(float treeScale, int maxVoxels) : NodeData(), - _treeScale(treeScale), - _maxVoxels(maxVoxels), - _initialized(false) { + _treeScale(treeScale), + _maxVoxels(maxVoxels), + _initialized(false), + _writeArraysLock(QReadWriteLock::Recursive), + _readArraysLock(QReadWriteLock::Recursive) + { _voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0; _writeRenderFullVBO = true; @@ -124,8 +127,16 @@ void VoxelSystem::setDisableFastVoxelPipeline(bool disableFastVoxelPipeline) { void VoxelSystem::elementUpdated(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; +//qDebug() << "VoxelSystem::elementUpdated()..."; + // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { +/* +qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " + << "_writeRenderFullVBO="<< _writeRenderFullVBO + << "_inSetupNewVoxelsForDrawing="<< _inSetupNewVoxelsForDrawing + << "_useFastVoxelPipeline="<< _useFastVoxelPipeline; +*/ return; } @@ -136,6 +147,8 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); +//qDebug() << "VoxelSystem::elementUpdated()... recalcing should render!!"; + if (voxel->getShouldRender() != shouldRender) { voxel->setShouldRender(shouldRender); } @@ -163,11 +176,13 @@ void VoxelSystem::elementUpdated(OctreeElement* element) { const bool REUSE_INDEX = true; const bool DONT_FORCE_REDRAW = false; +//qDebug() << "VoxelSystem::elementUpdated()... calling updateNodeInArrays()!!!"; updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); _voxelsUpdated++; voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. +//qDebug() << "VoxelSystem::elementUpdated()... calling setupNewVoxelsForDrawingSingleNode()!!!"; setupNewVoxelsForDrawingSingleNode(); } } @@ -409,7 +424,8 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice } void VoxelSystem::initVoxelMemory() { - _readArraysLock.lockForWrite(); + //_readArraysLock.lockForWrite(); + //_writeArraysLock.lockForWrite(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them @@ -524,7 +540,8 @@ void VoxelSystem::initVoxelMemory() { _initialized = true; - _readArraysLock.unlock(); + //_writeArraysLock.unlock(); + //_readArraysLock.unlock(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const { @@ -741,9 +758,90 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) { _setupNewVoxelsForDrawingLastElapsed = elapsedmsec; } + + +class recreateVoxelGeometryInViewArgs { +public: + VoxelSystem* thisVoxelSystem; + ViewFrustum thisViewFrustum; + unsigned long nodesScanned; + float voxelSizeScale; + int boundaryLevelAdjust; + + recreateVoxelGeometryInViewArgs(VoxelSystem* voxelSystem) : + thisVoxelSystem(voxelSystem), + thisViewFrustum(*voxelSystem->getViewFrustum()), + nodesScanned(0), + voxelSizeScale(Menu::getInstance()->getVoxelSizeScale()), + boundaryLevelAdjust(Menu::getInstance()->getBoundaryLevelAdjust()) + { + } +}; + +// The goal of this operation is to remove any old references to old geometry, and if the voxel +// should be visible, create new geometry for it. +bool VoxelSystem::recreateVoxelGeometryInViewOperation(OctreeElement* element, void* extraData) { + VoxelTreeElement* voxel = (VoxelTreeElement*)element; + recreateVoxelGeometryInViewArgs* args = (recreateVoxelGeometryInViewArgs*)extraData; + + args->nodesScanned++; + + // reset the old geometry... + // note: this doesn't "mark the voxel as changed", so it only releases the old buffer index thereby forgetting the + // old geometry + voxel->setBufferIndex(GLBUFFER_INDEX_UNKNOWN); + + bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, args->voxelSizeScale, args->boundaryLevelAdjust); + bool inView = voxel->isInView(args->thisViewFrustum); + voxel->setShouldRender(inView && shouldRender); + + if (shouldRender) { + bool falseColorize = false; + if (falseColorize) { + voxel->setFalseColor(0,0,255); // false colorize + } + // These are both needed to force redraw... + voxel->setDirtyBit(); + qDebug() << "recreateVoxelGeometryInViewOperation()... calling voxel->markWithChangedTime()"; + voxel->markWithChangedTime(); // this will notifyUpdateHooks, which will result in our geometry being created + } + + return true; // keep recursing! +} + + +// TODO: does cleanupRemovedVoxels() ever get called? +// TODO: other than cleanupRemovedVoxels() is there anyplace we attempt to detect too many abandoned slots??? void VoxelSystem::recreateVoxelGeometryInView() { - // this is a temporary solution... we need a full implementation that actually does a full redraw into clean VBOs - hideOutOfView(true); + +qDebug() << "recreateVoxelGeometryInView()..."; + + recreateVoxelGeometryInViewArgs args(this); + _writeArraysLock.lockForWrite(); // don't let anyone read or write our write arrays until we're done + _tree->lockForRead(); // don't let anyone change our tree structure until we're run + + // reset our write arrays bookkeeping to think we've got no voxels in it + clearFreeBufferIndexes(); // does this do everything we need? + /** + // clear out all our abandoned indexes - they are all available + // TODO: is it possible freeBufferIndex() will get called??? + _voxelsInWriteArrays = 0; + _freeIndexLock.lock(); + _freeIndexes.clear(); + _freeIndexLock.unlock(); + **/ + + //_voxelsUpdated = 0; // ???? + //_writeRenderFullVBO = true; + + // do we need to reset out _writeVoxelDirtyArray arrays?? + memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); + + + + _tree->recurseTreeWithOperation(recreateVoxelGeometryInViewOperation,(void*)&args); + _tree->unlock(); + _writeArraysLock.unlock(); } void VoxelSystem::checkForCulling() { @@ -784,7 +882,8 @@ void VoxelSystem::checkForCulling() { if (fullRedraw) { // this will remove all old geometry and recreate the correct geometry for all in view voxels - recreateVoxelGeometryInView(); + //recreateVoxelGeometryInView(); + hideOutOfView(forceFullFrustum); } else { hideOutOfView(forceFullFrustum); } @@ -905,12 +1004,26 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) { PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "copyWrittenDataToReadArrays()"); - if (_voxelsDirty && _voxelsUpdated) { - if (fullVBOs) { - copyWrittenDataToReadArraysFullVBOs(); + // attempt to get the writeArraysLock for reading and the readArraysLock for writing + // so we can copy from the write to the read... if we fail, that's ok, we'll get it the next + // time around, the only side effect is the VBOs won't be updated this frame + const int WAIT_FOR_LOCK_IN_MS = 5; + if (_readArraysLock.tryLockForWrite(WAIT_FOR_LOCK_IN_MS)) { + if (_writeArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) { + if (_voxelsDirty && _voxelsUpdated) { + if (fullVBOs) { + copyWrittenDataToReadArraysFullVBOs(); + } else { + copyWrittenDataToReadArraysPartialVBOs(); + } + } + _writeArraysLock.unlock(); } else { - copyWrittenDataToReadArraysPartialVBOs(); + qDebug() << "couldn't get _writeArraysLock.LockForRead()..."; } + _readArraysLock.unlock(); + } else { + qDebug() << "couldn't get _readArraysLock.LockForWrite()..."; } } @@ -1105,7 +1218,8 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); - setupNewVoxelsForDrawing(); + // TODO: hmmmmm????? + //setupNewVoxelsForDrawing(); } void VoxelSystem::updateFullVBOs() { @@ -1166,17 +1280,27 @@ void VoxelSystem::updateVBOs() { // would like to include _callsToTreesToArrays PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer); if (_voxelsDirty) { - if (_readRenderFullVBO) { - updateFullVBOs(); + + // attempt to lock the read arrays, to for copying from them to the actual GPU VBOs. + // if we fail to get the lock, that's ok, our VBOs will update on the next frame... + const int WAIT_FOR_LOCK_IN_MS = 5; + if (_readArraysLock.tryLockForRead(WAIT_FOR_LOCK_IN_MS)) { + if (_readRenderFullVBO) { + updateFullVBOs(); + } else { + updatePartialVBOs(); + } + _voxelsDirty = false; + _readRenderFullVBO = false; + _readArraysLock.unlock(); } else { - updatePartialVBOs(); + qDebug() << "updateVBOs().... couldn't get _readArraysLock.tryLockForRead()"; } - _voxelsDirty = false; - _readRenderFullVBO = false; } _callsToTreesToArrays = 0; // clear it } +// this should only be called on the main application thread during render void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) { bool showWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarning, "updateVBOSegment()"); diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index c2f60d6699..121a7f86c4 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -194,6 +194,7 @@ private: static bool showAllSubTreeOperation(OctreeElement* element, void* extraData); static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData); static bool getVoxelEnclosingOperation(OctreeElement* element, void* extraData); + static bool recreateVoxelGeometryInViewOperation(OctreeElement* element, void* extraData); int updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, bool forceDraw); int forceRemoveNodeFromArrays(VoxelTreeElement* node); @@ -212,6 +213,7 @@ private: GLfloat* _readVerticesArray; GLubyte* _readColorsArray; + QReadWriteLock _writeArraysLock; QReadWriteLock _readArraysLock; From 3aba29b0154d97e0ad886f3f8eff5180e8626a63 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 13:26:31 -0800 Subject: [PATCH 4/8] first working cut at recreateVoxelGeometryInView() --- interface/src/VoxelSystem.cpp | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 851c24c5ce..37bb2b5db1 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -794,16 +794,9 @@ bool VoxelSystem::recreateVoxelGeometryInViewOperation(OctreeElement* element, v bool shouldRender = voxel->calculateShouldRender(&args->thisViewFrustum, args->voxelSizeScale, args->boundaryLevelAdjust); bool inView = voxel->isInView(args->thisViewFrustum); voxel->setShouldRender(inView && shouldRender); - - if (shouldRender) { - bool falseColorize = false; - if (falseColorize) { - voxel->setFalseColor(0,0,255); // false colorize - } - // These are both needed to force redraw... - voxel->setDirtyBit(); - qDebug() << "recreateVoxelGeometryInViewOperation()... calling voxel->markWithChangedTime()"; - voxel->markWithChangedTime(); // this will notifyUpdateHooks, which will result in our geometry being created + if (shouldRender && inView) { + // recreate the geometry + args->thisVoxelSystem->updateNodeInArrays(voxel, false, true); // DONT_REUSE_INDEX, FORCE_REDRAW } return true; // keep recursing! @@ -814,30 +807,17 @@ bool VoxelSystem::recreateVoxelGeometryInViewOperation(OctreeElement* element, v // TODO: other than cleanupRemovedVoxels() is there anyplace we attempt to detect too many abandoned slots??? void VoxelSystem::recreateVoxelGeometryInView() { -qDebug() << "recreateVoxelGeometryInView()..."; + qDebug() << "recreateVoxelGeometryInView()..."; recreateVoxelGeometryInViewArgs args(this); _writeArraysLock.lockForWrite(); // don't let anyone read or write our write arrays until we're done _tree->lockForRead(); // don't let anyone change our tree structure until we're run // reset our write arrays bookkeeping to think we've got no voxels in it - clearFreeBufferIndexes(); // does this do everything we need? - /** - // clear out all our abandoned indexes - they are all available - // TODO: is it possible freeBufferIndex() will get called??? - _voxelsInWriteArrays = 0; - _freeIndexLock.lock(); - _freeIndexes.clear(); - _freeIndexLock.unlock(); - **/ - - //_voxelsUpdated = 0; // ???? - //_writeRenderFullVBO = true; + clearFreeBufferIndexes(); // do we need to reset out _writeVoxelDirtyArray arrays?? memset(_writeVoxelDirtyArray, false, _maxVoxels * sizeof(bool)); - - _tree->recurseTreeWithOperation(recreateVoxelGeometryInViewOperation,(void*)&args); _tree->unlock(); @@ -882,8 +862,7 @@ void VoxelSystem::checkForCulling() { if (fullRedraw) { // this will remove all old geometry and recreate the correct geometry for all in view voxels - //recreateVoxelGeometryInView(); - hideOutOfView(forceFullFrustum); + recreateVoxelGeometryInView(); } else { hideOutOfView(forceFullFrustum); } From caab3afb69c65f7703ef9e90f76ccd7bd303b534 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:26:30 -0800 Subject: [PATCH 5/8] add menu item for auto adjust LOD --- interface/src/Application.cpp | 5 ++++- interface/src/Menu.cpp | 1 + interface/src/Menu.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fd4c52d9f2..ab511b62e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2709,7 +2709,10 @@ void Application::displaySide(Camera& whichCamera, bool selfAvatarOnly) { _voxels.render(); // double check that our LOD doesn't need to be auto-adjusted - Menu::getInstance()->autoAdjustLOD(_fps); + // only adjust if our option is set + if (Menu::getInstance()->isOptionChecked(MenuOption::AutoAdjustLOD)) { + Menu::getInstance()->autoAdjustLOD(_fps); + } } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 63717fdbb5..3eecdd1e96 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -317,6 +317,7 @@ Menu::Menu() : addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::VoxelTextures); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::DontFadeOnVoxelServerChanges); + addCheckableActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::AutoAdjustLOD); addActionToQMenuAndActionHash(voxelOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L, this, SLOT(lodTools())); QMenu* voxelProtoOptionsMenu = voxelOptionsMenu->addMenu("Voxel Server Protocol Options"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 130ce19475..fd9582229f 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -175,6 +175,7 @@ namespace MenuOption { const QString AmbientOcclusion = "Ambient Occlusion"; const QString Avatars = "Avatars"; const QString Atmosphere = "Atmosphere"; + const QString AutoAdjustLOD = "Automatically Adjust LOD"; const QString AutomaticallyAuditTree = "Automatically Audit Tree Stats"; const QString Bandwidth = "Bandwidth Display"; const QString BandwidthDetails = "Bandwidth Details"; From 4fb5e6842590feee2960211c448cc5367d03788a Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:33:23 -0800 Subject: [PATCH 6/8] removed some debug --- interface/src/VoxelSystem.cpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 37bb2b5db1..9cacd02b18 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -127,16 +127,9 @@ void VoxelSystem::setDisableFastVoxelPipeline(bool disableFastVoxelPipeline) { void VoxelSystem::elementUpdated(OctreeElement* element) { VoxelTreeElement* voxel = (VoxelTreeElement*)element; -//qDebug() << "VoxelSystem::elementUpdated()..."; // If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail.. if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) { -/* -qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " - << "_writeRenderFullVBO="<< _writeRenderFullVBO - << "_inSetupNewVoxelsForDrawing="<< _inSetupNewVoxelsForDrawing - << "_useFastVoxelPipeline="<< _useFastVoxelPipeline; -*/ return; } @@ -147,8 +140,6 @@ qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust(); shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust); -//qDebug() << "VoxelSystem::elementUpdated()... recalcing should render!!"; - if (voxel->getShouldRender() != shouldRender) { voxel->setShouldRender(shouldRender); } @@ -176,13 +167,11 @@ qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! " const bool REUSE_INDEX = true; const bool DONT_FORCE_REDRAW = false; -//qDebug() << "VoxelSystem::elementUpdated()... calling updateNodeInArrays()!!!"; updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW); _voxelsUpdated++; voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things. -//qDebug() << "VoxelSystem::elementUpdated()... calling setupNewVoxelsForDrawingSingleNode()!!!"; setupNewVoxelsForDrawingSingleNode(); } } From ed7fa6e311a368b3dbe5bf2e70f8aa7ea33d51b1 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:39:10 -0800 Subject: [PATCH 7/8] added back setupNewVoxelsForDrawing() on changeTree() --- interface/src/VoxelSystem.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 9cacd02b18..632c5e056e 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -1186,8 +1186,7 @@ void VoxelSystem::changeTree(VoxelTree* newTree) { connect(_tree, SIGNAL(importSize(float,float,float)), SIGNAL(importSize(float,float,float))); connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int))); - // TODO: hmmmmm????? - //setupNewVoxelsForDrawing(); + setupNewVoxelsForDrawing(); } void VoxelSystem::updateFullVBOs() { From 601c155303692956c5d1e6c94b7df4d3e337b584 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 13 Feb 2014 14:46:59 -0800 Subject: [PATCH 8/8] correctly lock arrays on init --- interface/src/VoxelSystem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 632c5e056e..a3352f36e7 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -413,8 +413,8 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice } void VoxelSystem::initVoxelMemory() { - //_readArraysLock.lockForWrite(); - //_writeArraysLock.lockForWrite(); + _readArraysLock.lockForWrite(); + _writeArraysLock.lockForWrite(); _memoryUsageRAM = 0; _memoryUsageVBO = 0; // our VBO allocations as we know them @@ -529,8 +529,8 @@ void VoxelSystem::initVoxelMemory() { _initialized = true; - //_writeArraysLock.unlock(); - //_readArraysLock.unlock(); + _writeArraysLock.unlock(); + _readArraysLock.unlock(); } void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const {