mirror of
https://github.com/overte-org/overte.git
synced 2025-08-04 04:23:33 +02:00
add proper readWriteLocks around the VBO arrays to preven random triangles
This commit is contained in:
parent
e9259db944
commit
cac67d7baa
2 changed files with 144 additions and 18 deletions
|
@ -57,9 +57,12 @@ GLubyte identityIndicesBack[] = { 4, 5, 6, 4, 6, 7 };
|
||||||
|
|
||||||
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
|
VoxelSystem::VoxelSystem(float treeScale, int maxVoxels)
|
||||||
: NodeData(),
|
: NodeData(),
|
||||||
_treeScale(treeScale),
|
_treeScale(treeScale),
|
||||||
_maxVoxels(maxVoxels),
|
_maxVoxels(maxVoxels),
|
||||||
_initialized(false) {
|
_initialized(false),
|
||||||
|
_writeArraysLock(QReadWriteLock::Recursive),
|
||||||
|
_readArraysLock(QReadWriteLock::Recursive)
|
||||||
|
{
|
||||||
|
|
||||||
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
|
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
|
||||||
_writeRenderFullVBO = true;
|
_writeRenderFullVBO = true;
|
||||||
|
@ -124,8 +127,16 @@ void VoxelSystem::setDisableFastVoxelPipeline(bool disableFastVoxelPipeline) {
|
||||||
|
|
||||||
void VoxelSystem::elementUpdated(OctreeElement* element) {
|
void VoxelSystem::elementUpdated(OctreeElement* element) {
|
||||||
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
VoxelTreeElement* voxel = (VoxelTreeElement*)element;
|
||||||
|
//qDebug() << "VoxelSystem::elementUpdated()...";
|
||||||
|
|
||||||
// If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail..
|
// If we're in SetupNewVoxelsForDrawing() or _writeRenderFullVBO then bail..
|
||||||
if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) {
|
if (!_useFastVoxelPipeline || _inSetupNewVoxelsForDrawing || _writeRenderFullVBO) {
|
||||||
|
/*
|
||||||
|
qDebug() << "VoxelSystem::elementUpdated()... BAILING!!! "
|
||||||
|
<< "_writeRenderFullVBO="<< _writeRenderFullVBO
|
||||||
|
<< "_inSetupNewVoxelsForDrawing="<< _inSetupNewVoxelsForDrawing
|
||||||
|
<< "_useFastVoxelPipeline="<< _useFastVoxelPipeline;
|
||||||
|
*/
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +147,8 @@ void VoxelSystem::elementUpdated(OctreeElement* element) {
|
||||||
int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust();
|
int boundaryLevelAdjust = Menu::getInstance()->getBoundaryLevelAdjust();
|
||||||
shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust);
|
shouldRender = voxel->calculateShouldRender(_viewFrustum, voxelSizeScale, boundaryLevelAdjust);
|
||||||
|
|
||||||
|
//qDebug() << "VoxelSystem::elementUpdated()... recalcing should render!!";
|
||||||
|
|
||||||
if (voxel->getShouldRender() != shouldRender) {
|
if (voxel->getShouldRender() != shouldRender) {
|
||||||
voxel->setShouldRender(shouldRender);
|
voxel->setShouldRender(shouldRender);
|
||||||
}
|
}
|
||||||
|
@ -163,11 +176,13 @@ void VoxelSystem::elementUpdated(OctreeElement* element) {
|
||||||
|
|
||||||
const bool REUSE_INDEX = true;
|
const bool REUSE_INDEX = true;
|
||||||
const bool DONT_FORCE_REDRAW = false;
|
const bool DONT_FORCE_REDRAW = false;
|
||||||
|
//qDebug() << "VoxelSystem::elementUpdated()... calling updateNodeInArrays()!!!";
|
||||||
updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW);
|
updateNodeInArrays(voxel, REUSE_INDEX, DONT_FORCE_REDRAW);
|
||||||
_voxelsUpdated++;
|
_voxelsUpdated++;
|
||||||
|
|
||||||
voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
|
voxel->clearDirtyBit(); // clear the dirty bit, do this before we potentially delete things.
|
||||||
|
|
||||||
|
//qDebug() << "VoxelSystem::elementUpdated()... calling setupNewVoxelsForDrawingSingleNode()!!!";
|
||||||
setupNewVoxelsForDrawingSingleNode();
|
setupNewVoxelsForDrawingSingleNode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,7 +424,8 @@ void VoxelSystem::setupFaceIndices(GLuint& faceVBOID, GLubyte faceIdentityIndice
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelSystem::initVoxelMemory() {
|
void VoxelSystem::initVoxelMemory() {
|
||||||
_readArraysLock.lockForWrite();
|
//_readArraysLock.lockForWrite();
|
||||||
|
//_writeArraysLock.lockForWrite();
|
||||||
|
|
||||||
_memoryUsageRAM = 0;
|
_memoryUsageRAM = 0;
|
||||||
_memoryUsageVBO = 0; // our VBO allocations as we know them
|
_memoryUsageVBO = 0; // our VBO allocations as we know them
|
||||||
|
@ -524,7 +540,8 @@ void VoxelSystem::initVoxelMemory() {
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
_readArraysLock.unlock();
|
//_writeArraysLock.unlock();
|
||||||
|
//_readArraysLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const {
|
void VoxelSystem::writeToSVOFile(const char* filename, VoxelTreeElement* element) const {
|
||||||
|
@ -741,9 +758,90 @@ void VoxelSystem::setupNewVoxelsForDrawingSingleNode(bool allowBailEarly) {
|
||||||
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
|
_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() {
|
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() {
|
void VoxelSystem::checkForCulling() {
|
||||||
|
@ -784,7 +882,8 @@ void VoxelSystem::checkForCulling() {
|
||||||
|
|
||||||
if (fullRedraw) {
|
if (fullRedraw) {
|
||||||
// this will remove all old geometry and recreate the correct geometry for all in view voxels
|
// this will remove all old geometry and recreate the correct geometry for all in view voxels
|
||||||
recreateVoxelGeometryInView();
|
//recreateVoxelGeometryInView();
|
||||||
|
hideOutOfView(forceFullFrustum);
|
||||||
} else {
|
} else {
|
||||||
hideOutOfView(forceFullFrustum);
|
hideOutOfView(forceFullFrustum);
|
||||||
}
|
}
|
||||||
|
@ -905,12 +1004,26 @@ void VoxelSystem::copyWrittenDataToReadArrays(bool fullVBOs) {
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
|
||||||
"copyWrittenDataToReadArrays()");
|
"copyWrittenDataToReadArrays()");
|
||||||
|
|
||||||
if (_voxelsDirty && _voxelsUpdated) {
|
// attempt to get the writeArraysLock for reading and the readArraysLock for writing
|
||||||
if (fullVBOs) {
|
// so we can copy from the write to the read... if we fail, that's ok, we'll get it the next
|
||||||
copyWrittenDataToReadArraysFullVBOs();
|
// 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 {
|
} 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(importSize(float,float,float)), SIGNAL(importSize(float,float,float)));
|
||||||
connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int)));
|
connect(_tree, SIGNAL(importProgress(int)), SIGNAL(importProgress(int)));
|
||||||
|
|
||||||
setupNewVoxelsForDrawing();
|
// TODO: hmmmmm?????
|
||||||
|
//setupNewVoxelsForDrawing();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelSystem::updateFullVBOs() {
|
void VoxelSystem::updateFullVBOs() {
|
||||||
|
@ -1166,17 +1280,27 @@ void VoxelSystem::updateVBOs() {
|
||||||
// would like to include _callsToTreesToArrays
|
// would like to include _callsToTreesToArrays
|
||||||
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
|
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), buffer);
|
||||||
if (_voxelsDirty) {
|
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 {
|
} else {
|
||||||
updatePartialVBOs();
|
qDebug() << "updateVBOs().... couldn't get _readArraysLock.tryLockForRead()";
|
||||||
}
|
}
|
||||||
_voxelsDirty = false;
|
|
||||||
_readRenderFullVBO = false;
|
|
||||||
}
|
}
|
||||||
_callsToTreesToArrays = 0; // clear it
|
_callsToTreesToArrays = 0; // clear it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this should only be called on the main application thread during render
|
||||||
void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
void VoxelSystem::updateVBOSegment(glBufferIndex segmentStart, glBufferIndex segmentEnd) {
|
||||||
bool showWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
bool showWarning = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
|
||||||
PerformanceWarning warn(showWarning, "updateVBOSegment()");
|
PerformanceWarning warn(showWarning, "updateVBOSegment()");
|
||||||
|
|
|
@ -194,6 +194,7 @@ private:
|
||||||
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
|
static bool showAllSubTreeOperation(OctreeElement* element, void* extraData);
|
||||||
static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData);
|
static bool showAllLocalVoxelsOperation(OctreeElement* element, void* extraData);
|
||||||
static bool getVoxelEnclosingOperation(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 updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, bool forceDraw);
|
||||||
int forceRemoveNodeFromArrays(VoxelTreeElement* node);
|
int forceRemoveNodeFromArrays(VoxelTreeElement* node);
|
||||||
|
@ -212,6 +213,7 @@ private:
|
||||||
GLfloat* _readVerticesArray;
|
GLfloat* _readVerticesArray;
|
||||||
GLubyte* _readColorsArray;
|
GLubyte* _readColorsArray;
|
||||||
|
|
||||||
|
QReadWriteLock _writeArraysLock;
|
||||||
QReadWriteLock _readArraysLock;
|
QReadWriteLock _readArraysLock;
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue