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

View file

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