mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-08-17 08:36:41 +02:00
Voxel Render Optimization - using blended VBO update strategy
- Fixed a bug in updateNodeInArraysAsFullVBO() that caused blended strategy to not work properly. - Implement strategy to usually update only the portion of the VBOs that have changed, unless nodes have been removed, and then update the full VBO. This siginificantly improves treeToArrays() performance - Still to do implement partial strategy for copy buffers and update GPU
This commit is contained in:
parent
bb65137b7e
commit
655f14121d
3 changed files with 80 additions and 67 deletions
|
@ -43,7 +43,7 @@ GLubyte identityIndices[] = { 0,2,1, 0,3,2, // Z- .
|
|||
|
||||
VoxelSystem::VoxelSystem() {
|
||||
_voxelsInReadArrays = _voxelsInWriteArrays = _voxelsUpdated = 0;
|
||||
_alwaysRenderFullVBO = true;
|
||||
_renderFullVBO = true;
|
||||
_tree = new VoxelTree();
|
||||
pthread_mutex_init(&_bufferWriteLock, NULL);
|
||||
}
|
||||
|
@ -138,8 +138,9 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
PerformanceWarning warn(_renderWarningsOn, "setupNewVoxelsForDrawing()"); // would like to include _voxelsInArrays, _voxelsUpdated
|
||||
double start = usecTimestampNow();
|
||||
double sinceLastTime = (start - _setupNewVoxelsForDrawingLastFinished) / 1000.0;
|
||||
|
||||
if (sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
|
||||
|
||||
bool iAmDebugging = false; // if you're debugging set this to true, so you won't get skipped for slow debugging
|
||||
if (!iAmDebugging && sinceLastTime <= std::max(_setupNewVoxelsForDrawingLastElapsed, SIXTY_FPS_IN_MILLISECONDS)) {
|
||||
return; // bail early, it hasn't been long enough since the last time we ran
|
||||
}
|
||||
|
||||
|
@ -165,11 +166,11 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
|
|||
if (_tree->isDirty()) {
|
||||
static char buffer[64] = { 0 };
|
||||
if (_renderWarningsOn) {
|
||||
sprintf(buffer, "newTreeToArrays() _alwaysRenderFullVBO=%s", (_alwaysRenderFullVBO ? "yes" : "no"));
|
||||
sprintf(buffer, "newTreeToArrays() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no"));
|
||||
};
|
||||
PerformanceWarning warn(_renderWarningsOn, buffer);
|
||||
_callsToTreesToArrays++;
|
||||
if (_alwaysRenderFullVBO) {
|
||||
if (_renderFullVBO) {
|
||||
_voxelsInWriteArrays = 0; // reset our VBO
|
||||
}
|
||||
_voxelsUpdated = newTreeToArrays(_tree->rootNode);
|
||||
|
@ -177,7 +178,7 @@ 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()
|
||||
_alwaysRenderFullVBO = false;
|
||||
_renderFullVBO = false;
|
||||
} else {
|
||||
_voxelsUpdated = 0;
|
||||
}
|
||||
|
@ -200,7 +201,7 @@ void VoxelSystem::cleanupRemovedVoxels() {
|
|||
while (!_removedVoxels.isEmpty()) {
|
||||
delete _removedVoxels.extract();
|
||||
}
|
||||
_alwaysRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
|
||||
_renderFullVBO = true; // if we remove voxels, we must update our full VBOs
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,16 +239,16 @@ int VoxelSystem::newTreeToArrays(VoxelNode* node) {
|
|||
voxelsUpdated += newTreeToArrays(node->getChildAtIndex(i));
|
||||
}
|
||||
}
|
||||
if (_alwaysRenderFullVBO) {
|
||||
voxelsUpdated += newway__updateNodeInArray(node);
|
||||
if (_renderFullVBO) {
|
||||
voxelsUpdated += updateNodeInArraysAsFullVBO(node);
|
||||
} else {
|
||||
voxelsUpdated += oldway__updateNodeInArray(node);
|
||||
voxelsUpdated += updateNodeInArraysAsPartialVBO(node);
|
||||
}
|
||||
node->clearDirtyBit(); // always clear the dirty bit, even if it doesn't need to be rendered
|
||||
return voxelsUpdated;
|
||||
}
|
||||
|
||||
int VoxelSystem::newway__updateNodeInArray(VoxelNode* node) {
|
||||
int VoxelSystem::updateNodeInArraysAsFullVBO(VoxelNode* node) {
|
||||
// If we've run out of room, then just bail...
|
||||
if (_voxelsInWriteArrays >= MAX_VOXELS_PER_SYSTEM) {
|
||||
return 0;
|
||||
|
@ -266,18 +267,23 @@ int VoxelSystem::newway__updateNodeInArray(VoxelNode* node) {
|
|||
*(writeVerticesAt+j) = startVertex[j % 3] + (identityVertices[j] * voxelScale);
|
||||
*(writeColorsAt +j) = node->getColor()[j % 3];
|
||||
}
|
||||
_voxelsInWriteArrays++; // our know vertices in the arrays
|
||||
node->setBufferIndex(nodeIndex);
|
||||
_voxelsInWriteArrays++; // our know vertices in the arrays
|
||||
return 1; // rendered
|
||||
}
|
||||
return 0; // not-rendered
|
||||
}
|
||||
|
||||
int VoxelSystem::oldway__updateNodeInArray(VoxelNode* node) {
|
||||
int VoxelSystem::updateNodeInArraysAsPartialVBO(VoxelNode* node) {
|
||||
// If we've run out of room, then just bail...
|
||||
if (_voxelsInWriteArrays >= MAX_VOXELS_PER_SYSTEM) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Now, if we've changed any attributes (our renderness, our color, etc) then update the Arrays... for us
|
||||
if (node->isDirty() && (node->getShouldRender() || node->isKnownBufferIndex())) {
|
||||
glm::vec3 startVertex;
|
||||
float voxelScale = 0;
|
||||
|
||||
// If we're should render, use our legit location and scale,
|
||||
if (node->getShouldRender()) {
|
||||
startVertex = node->getCorner();
|
||||
|
@ -401,57 +407,66 @@ void VoxelSystem::init() {
|
|||
delete[] normalsArray;
|
||||
}
|
||||
|
||||
void VoxelSystem::updateFullVBOs() {
|
||||
glBufferIndex segmentStart = 0;
|
||||
glBufferIndex segmentEnd = _voxelsInWriteArrays;
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
|
||||
void VoxelSystem::updatePartialVBOs() {
|
||||
glBufferIndex segmentStart = 0;
|
||||
glBufferIndex segmentEnd = 0;
|
||||
bool inSegment = false;
|
||||
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
|
||||
if (!inSegment) {
|
||||
if (_voxelDirtyArray[i]) {
|
||||
segmentStart = i;
|
||||
inSegment = true;
|
||||
_voxelDirtyArray[i] = false; // consider us clean!
|
||||
}
|
||||
} else {
|
||||
if (!_voxelDirtyArray[i] || (i == (_voxelsInWriteArrays - 1)) ) {
|
||||
segmentEnd = i;
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VoxelSystem::updateVBOs() {
|
||||
static char buffer[40] = { 0 };
|
||||
if (_renderWarningsOn) {
|
||||
sprintf(buffer, "updateVBOs() _alwaysRenderFullVBO=%s", (_alwaysRenderFullVBO ? "yes" : "no"));
|
||||
sprintf(buffer, "updateVBOs() _renderFullVBO=%s", (_renderFullVBO ? "yes" : "no"));
|
||||
};
|
||||
PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays
|
||||
if (_voxelsDirty) {
|
||||
if (_alwaysRenderFullVBO) {
|
||||
glBufferIndex segmentStart = 0;
|
||||
glBufferIndex segmentEnd = _voxelsInWriteArrays;
|
||||
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
// updatePartialVBOs() is not yet working. For now, ALWAYS call updateFullVBOs()
|
||||
if (true || _renderFullVBO) {
|
||||
updateFullVBOs();
|
||||
} else {
|
||||
glBufferIndex segmentStart = 0;
|
||||
glBufferIndex segmentEnd = 0;
|
||||
bool inSegment = false;
|
||||
for (glBufferIndex i = 0; i < _voxelsInWriteArrays; i++) {
|
||||
if (!inSegment) {
|
||||
if (_voxelDirtyArray[i]) {
|
||||
segmentStart = i;
|
||||
inSegment = true;
|
||||
_voxelDirtyArray[i] = false; // consider us clean!
|
||||
}
|
||||
} else {
|
||||
if (!_voxelDirtyArray[i] || (i == (_voxelsInWriteArrays - 1)) ) {
|
||||
segmentEnd = i;
|
||||
inSegment = false;
|
||||
int segmentLength = (segmentEnd - segmentStart) + 1;
|
||||
GLintptr segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLsizeiptr segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLfloat);
|
||||
GLfloat* readVerticesFrom = _readVerticesArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboVerticesID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readVerticesFrom);
|
||||
segmentStartAt = segmentStart * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
segmentSizeBytes = segmentLength * VERTEX_POINTS_PER_VOXEL * sizeof(GLubyte);
|
||||
GLubyte* readColorsFrom = _readColorsArray + (segmentStart * VERTEX_POINTS_PER_VOXEL);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _vboColorsID);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, segmentStartAt, segmentSizeBytes, readColorsFrom);
|
||||
}
|
||||
}
|
||||
}
|
||||
updatePartialVBOs();
|
||||
}
|
||||
_voxelsDirty = false;
|
||||
}
|
||||
|
|
|
@ -79,8 +79,8 @@ private:
|
|||
static bool getDistanceFromViewRangeOperation(VoxelNode* node, void* extraData);
|
||||
static bool removeOutOfViewOperation(VoxelNode* node, void* extraData);
|
||||
|
||||
int newway__updateNodeInArray(VoxelNode* node);
|
||||
int oldway__updateNodeInArray(VoxelNode* node);
|
||||
int updateNodeInArraysAsFullVBO(VoxelNode* node);
|
||||
int updateNodeInArraysAsPartialVBO(VoxelNode* node);
|
||||
|
||||
// these are kinda hacks, used by getDistanceFromViewRangeOperation() probably shouldn't be here
|
||||
static float _maxDistance;
|
||||
|
@ -99,7 +99,7 @@ private:
|
|||
unsigned long _voxelsInReadArrays;
|
||||
unsigned long _unusedArraySpace;
|
||||
|
||||
bool _alwaysRenderFullVBO;
|
||||
bool _renderFullVBO;
|
||||
|
||||
double _setupNewVoxelsForDrawingLastElapsed;
|
||||
double _setupNewVoxelsForDrawingLastFinished;
|
||||
|
@ -119,6 +119,8 @@ private:
|
|||
void setupNewVoxelsForDrawing();
|
||||
void copyWrittenDataToReadArrays();
|
||||
void updateVBOs();
|
||||
void updateFullVBOs();
|
||||
void updatePartialVBOs();
|
||||
void cleanupRemovedVoxels();
|
||||
|
||||
bool _voxelsDirty;
|
||||
|
|
|
@ -150,9 +150,7 @@ void VoxelNode::setFalseColor(colorPart red, colorPart green, colorPart blue) {
|
|||
_currentColor[1] = green;
|
||||
_currentColor[2] = blue;
|
||||
_currentColor[3] = 1; // XXXBHG - False colors are always considered set
|
||||
//if (_shouldRender) {
|
||||
_isDirty = true;
|
||||
//}
|
||||
_isDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,9 +161,7 @@ void VoxelNode::setFalseColored(bool isFalseColored) {
|
|||
memcpy(&_currentColor,&_trueColor,sizeof(nodeColor));
|
||||
}
|
||||
_falseColored = isFalseColored;
|
||||
//if (_shouldRender) {
|
||||
_isDirty = true;
|
||||
//}
|
||||
_isDirty = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue