first cut at auto-LOD adjustment

This commit is contained in:
ZappoMan 2014-02-12 21:22:21 -08:00
parent d6b71f19a8
commit 20a6f4eea9
8 changed files with 108 additions and 28 deletions

View file

@ -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();
}
}

View file

@ -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() {

View file

@ -16,6 +16,18 @@
#include <AbstractMenuInterface.h>
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 {

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -28,6 +28,7 @@ public slots:
void sizeScaleValueChanged(int value);
void boundaryLevelValueChanged(int value);
void resetClicked(bool checked);
void reloadSliders();
protected:

View file

@ -45,6 +45,8 @@ public:
_alwaysDisplay(alwaysDisplay),
_runningTotal(runningTotal),
_totalCalls(totalCalls) { }
quint64 elapsed() const { return (usecTimestampNow() - _start); };
~PerformanceWarning();