I believe the problem with voxel rendering is caused by the update thread's

writing the data while the render thread is attempting to read it.  This
should fix the issue.
This commit is contained in:
Andrzej Kapolka 2013-05-20 15:15:44 -07:00
parent 8c71741843
commit 8d522cd3ed
2 changed files with 50 additions and 35 deletions

View file

@ -46,7 +46,8 @@ GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- .
VoxelSystem::VoxelSystem() {
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
_renderFullVBO = true;
_writeRenderFullVBO = true;
_readRenderFullVBO = true;
_tree = new VoxelTree();
pthread_mutex_init(&_bufferWriteLock, NULL);
pthread_mutex_init(&_treeLock, NULL);
@ -57,7 +58,8 @@ VoxelSystem::~VoxelSystem() {
delete[] _writeVerticesArray;
delete[] _readColorsArray;
delete[] _writeColorsArray;
delete[] _voxelDirtyArray;
delete[] _writeVoxelDirtyArray;
delete[] _readVoxelDirtyArray;
delete _tree;
pthread_mutex_destroy(&_bufferWriteLock);
pthread_mutex_destroy(&_treeLock);
@ -183,11 +185,11 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
if (_tree->isDirty()) {
static char buffer[64] = { 0 };
if (_renderWarningsOn) {
sprintf(buffer, "newTreeToArrays() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no"));
sprintf(buffer, "newTreeToArrays() _writeRenderFullVBO=%s", (_writeRenderFullVBO ? "yes" : "no"));
};
PerformanceWarning warn(_renderWarningsOn, buffer);
_callsToTreesToArrays++;
if (_renderFullVBO) {
if (_writeRenderFullVBO) {
_voxelsInWriteArrays = 0; // reset our VBO
}
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
@ -195,10 +197,14 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
// since we called treeToArrays, we can assume that our VBO is in sync, and so partial updates to the VBOs are
// ok again, until/unless we call removeOutOfView()
_renderFullVBO = false;
_writeRenderFullVBO = false;
} else {
_voxelsUpdated = 0;
}
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
pthread_mutex_lock(&_bufferWriteLock);
if (_voxelsUpdated) {
_voxelsDirty=true;
}
@ -206,6 +212,8 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
// copy the newly written data to the arrays designated for reading, only does something if _voxelsDirty && _voxelsUpdated
copyWrittenDataToReadArrays();
pthread_mutex_unlock(&_bufferWriteLock);
double end = usecTimestampNow();
double elapsedmsec = (end - start) / 1000.0;
_setupNewVoxelsForDrawingLastFinished = end;
@ -218,30 +226,25 @@ void VoxelSystem::cleanupRemovedVoxels() {
while (!_removedVoxels.isEmpty()) {
delete _removedVoxels.extract();
}
_renderFullVBO = true; // if we remove voxels, we must update our full VBOs
_writeRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
}
}
void VoxelSystem::copyWrittenDataToReadArraysFullVBOs() {
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
pthread_mutex_lock(&_bufferWriteLock);
int bytesOfVertices = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLfloat);
int bytesOfColors = (_voxelsInWriteArrays * VERTEX_POINTS_PER_VOXEL) * sizeof(GLubyte);
memcpy(_readVerticesArray, _writeVerticesArray, bytesOfVertices);
memcpy(_readColorsArray, _writeColorsArray, bytesOfColors );
_voxelsInReadArrays = _voxelsInWriteArrays;
pthread_mutex_unlock(&_bufferWriteLock);
}
void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() {
// lock on the buffer write lock so we can't modify the data when the GPU is reading it
pthread_mutex_lock(&_bufferWriteLock);
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0;
bool inSegment = false;
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
bool thisVoxelDirty = _voxelDirtyArray[i];
bool thisVoxelDirty = _writeVoxelDirtyArray[i];
_readVoxelDirtyArray[i] |= thisVoxelDirty;
if (!inSegment) {
if (thisVoxelDirty) {
segmentStart = i;
@ -290,14 +293,15 @@ void VoxelSystem::copyWrittenDataToReadArraysPartialVBOs() {
// update our length
_voxelsInReadArrays = _voxelsInWriteArrays;
pthread_mutex_unlock(&_bufferWriteLock);
// clear our dirty flags
memset(_writeVoxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool));
}
void VoxelSystem::copyWrittenDataToReadArrays() {
PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()");
if (_voxelsDirty && _voxelsUpdated) {
if (_renderFullVBO) {
if (_readRenderFullVBO) {
copyWrittenDataToReadArraysFullVBOs();
} else {
copyWrittenDataToReadArraysPartialVBOs();
@ -327,7 +331,7 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
}
}
}
if (_renderFullVBO) {
if (_writeRenderFullVBO) {
voxelsUpdated += updateNodeInArraysAsFullVBO(node);
} else {
voxelsUpdated += updateNodeInArraysAsPartialVBO(node);
@ -365,7 +369,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
_writeVoxelDirtyArray[nodeIndex] = true; // just in case we switch to Partial mode
_voxelsInWriteArrays++; // our know vertices in the arrays
return 1; // rendered
}
@ -402,7 +406,7 @@ int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
node->setBufferIndex(nodeIndex);
_voxelsInWriteArrays++;
}
_voxelDirtyArray[nodeIndex] = true;
_writeVoxelDirtyArray[nodeIndex] = true;
// populate the array with points for the 8 vertices
// and RGB color for each added vertex
@ -431,9 +435,11 @@ void VoxelSystem::init() {
_voxelsInReadArrays = 0;
_unusedArraySpace = 0;
// we will track individual dirty sections with this array of bools
_voxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
memset(_voxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
// we will track individual dirty sections with these arrays of bools
_writeVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
memset(_writeVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
_readVoxelDirtyArray = new bool[MAX_VOXELS_PER_SYSTEM];
memset(_readVoxelDirtyArray, false, MAX_VOXELS_PER_SYSTEM * sizeof(bool));
// prep the data structures for incoming voxel data
_writeVerticesArray = new GLfloat[VERTEX_POINTS_PER_VOXEL * MAX_VOXELS_PER_SYSTEM];
@ -530,7 +536,7 @@ void VoxelSystem::init() {
void VoxelSystem::updateFullVBOs() {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = _voxelsInWriteArrays;
glBufferIndex segmentEnd = _voxelsInReadArrays;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
@ -544,21 +550,21 @@ void VoxelSystem::updateFullVBOs() {
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
// consider the _voxelDirtyArray[] clean!
memset(_voxelDirtyArray, false, _voxelsInWriteArrays * sizeof(bool));
// consider the _readVoxelDirtyArray[] clean!
memset(_readVoxelDirtyArray, false, _voxelsInReadArrays * sizeof(bool));
}
void VoxelSystem::updatePartialVBOs() {
glBufferIndex segmentStart = 0;
glBufferIndex segmentEnd = 0;
bool inSegment = false;
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
bool thisVoxelDirty = _voxelDirtyArray[i];
for (glBufferIndex i = 0; i < _voxelsInReadArrays; i++) {
bool thisVoxelDirty = _readVoxelDirtyArray[i];
if (!inSegment) {
if (thisVoxelDirty) {
segmentStart = i;
inSegment = true;
_voxelDirtyArray[i] = false; // consider us clean!
_readVoxelDirtyArray[i] = false; // consider us clean!
}
} else {
if (!thisVoxelDirty) {
@ -578,13 +584,13 @@ void VoxelSystem::updatePartialVBOs() {
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
}
_voxelDirtyArray[i] = false; // consider us clean!
_readVoxelDirtyArray[i] = false; // consider us clean!
}
}
// if we got to the end of the array, and we're in an active dirty segment...
if (inSegment) {
segmentEnd = _voxelsInWriteArrays - 1;
segmentEnd = _voxelsInReadArrays - 1;
inSegment = false;
int segmentLength = (segmentEnd - segmentStart) + 1;
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
@ -603,22 +609,27 @@ void VoxelSystem::updatePartialVBOs() {
void VoxelSystem::updateVBOs() {
static char buffer[40] = { 0 };
if (_renderWarningsOn) {
sprintf(buffer, "updateVBOs() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no"));
sprintf(buffer, "updateVBOs() _readRenderFullVBO=%s", (_readRenderFullVBO ? "yes" : "no"));
};
PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays
if (_voxelsDirty) {
if (_renderFullVBO) {
if (_readRenderFullVBO) {
updateFullVBOs();
} else {
updatePartialVBOs();
}
_voxelsDirty = false;
_readRenderFullVBO = false;
}
_callsToTreesToArrays = 0; // clear it
}
void VoxelSystem::render(bool texture) {
PerformanceWarning warn(_renderWarningsOn, "render()");
// get the lock so that the update thread won't change anything
pthread_mutex_lock(&_bufferWriteLock);
glPushMatrix();
updateVBOs();
// tell OpenGL where to find vertex and color information
@ -668,6 +679,8 @@ void VoxelSystem::render(bool texture) {
// scale back down to 1 so heads aren't massive
glPopMatrix();
pthread_mutex_unlock(&_bufferWriteLock);
}
int VoxelSystem::_nodeCount = 0;
@ -1016,7 +1029,7 @@ void VoxelSystem::collectStatsForTreesAndVBOs() {
glBufferIndex maxDirty = 0;
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
if (_voxelDirtyArray[i]) {
if (_writeVoxelDirtyArray[i]) {
minDirty = std::min(minDirty,i);
maxDirty = std::max(maxDirty,i);
}

View file

@ -118,13 +118,15 @@ private:
GLubyte* _readColorsArray;
GLfloat* _writeVerticesArray;
GLubyte* _writeColorsArray;
bool* _voxelDirtyArray;
bool* _writeVoxelDirtyArray;
bool* _readVoxelDirtyArray;
unsigned long _voxelsUpdated;
unsigned long _voxelsInWriteArrays;
unsigned long _voxelsInReadArrays;
unsigned long _unusedArraySpace;
bool _renderFullVBO;
bool _writeRenderFullVBO;
bool _readRenderFullVBO;
double _setupNewVoxelsForDrawingLastElapsed;
double _setupNewVoxelsForDrawingLastFinished;