Optimized performance of newTreeToArrays() by implementing blended VBO strategy

- Introduced concept of sometimes only updating the new/changed part of the VBO
  while other times updating the full VBO. This allows us to get the speed
  advantage of only partial VBO updates when nodes haven't been removed
- Some debugging output related to _alwaysRenderFullVBO
- added cleanupRemovedVoxels() which actually deletes the nodes that were
  previously removed (fixes a memory leak!!)
This commit is contained in:
ZappoMan 2013-05-08 20:33:41 -07:00
parent cadc07c63d
commit bb65137b7e
2 changed files with 37 additions and 4 deletions

View file

@ -147,19 +147,37 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
// If the view frustum has changed, since last time, then remove nodes that are out of view // If the view frustum has changed, since last time, then remove nodes that are out of view
if ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) && hasViewChanged()) { if ((sinceLastViewCulling >= std::max(_lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS)) && hasViewChanged()) {
_lastViewCulling = start; _lastViewCulling = start;
// When we call removeOutOfView() voxels, we don't actually remove the voxels from the VBOs, but we do remove
// them from tree, this makes our tree caclulations faster, but doesn't require us to fully rebuild the VBOs (which
// can be expensive).
removeOutOfView(); removeOutOfView();
// Once we call cleanupRemovedVoxels() we do need to rebuild our VBOs (if anything was actually removed). So,
// we should consider putting this someplace else... as this might be able to occur less frequently, and save us on
// VBO reubuilding. Possibly we should do this only if our actual VBO usage crosses some lower boundary.
cleanupRemovedVoxels();
double endViewCulling = usecTimestampNow(); double endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000.0; _lastViewCullingElapsed = (endViewCulling - start) / 1000.0;
} }
if (_tree->isDirty()) { if (_tree->isDirty()) {
PerformanceWarning warn(_renderWarningsOn, "calling... newTreeToArrays()"); static char buffer[64] = { 0 };
if (_renderWarningsOn) {
sprintf(buffer, "newTreeToArrays() _alwaysRenderFullVBO=%s", (_alwaysRenderFullVBO ? "yes" : "no"));
};
PerformanceWarning warn(_renderWarningsOn, buffer);
_callsToTreesToArrays++; _callsToTreesToArrays++;
if (_alwaysRenderFullVBO) { if (_alwaysRenderFullVBO) {
_voxelsInWriteArrays = 0; // reset our VBO _voxelsInWriteArrays = 0; // reset our VBO
} }
_voxelsUpdated = newTreeToArrays(_tree->rootNode); _voxelsUpdated = newTreeToArrays(_tree->rootNode);
_tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean _tree->clearDirtyBit(); // after we pull the trees into the array, we can consider the tree clean
// 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;
} else { } else {
_voxelsUpdated = 0; _voxelsUpdated = 0;
} }
@ -176,6 +194,16 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
_setupNewVoxelsForDrawingLastElapsed = elapsedmsec; _setupNewVoxelsForDrawingLastElapsed = elapsedmsec;
} }
void VoxelSystem::cleanupRemovedVoxels() {
PerformanceWarning warn(_renderWarningsOn, "cleanupRemovedVoxels()");
if (!_removedVoxels.isEmpty()) {
while (!_removedVoxels.isEmpty()) {
delete _removedVoxels.extract();
}
_alwaysRenderFullVBO = true; // if we remove voxels, we must update our full VBOs
}
}
void VoxelSystem::copyWrittenDataToReadArrays() { void VoxelSystem::copyWrittenDataToReadArrays() {
PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()"); // would like to include _voxelsInArrays, _voxelsUpdated PerformanceWarning warn(_renderWarningsOn, "copyWrittenDataToReadArrays()"); // would like to include _voxelsInArrays, _voxelsUpdated
if (_voxelsDirty && _voxelsUpdated) { if (_voxelsDirty && _voxelsUpdated) {
@ -374,7 +402,11 @@ void VoxelSystem::init() {
} }
void VoxelSystem::updateVBOs() { void VoxelSystem::updateVBOs() {
PerformanceWarning warn(_renderWarningsOn, "updateVBOs()"); // would like to include _callsToTreesToArrays static char buffer[40] = { 0 };
if (_renderWarningsOn) {
sprintf(buffer, "updateVBOs() _alwaysRenderFullVBO=%s", (_alwaysRenderFullVBO ? "yes" : "no"));
};
PerformanceWarning warn(_renderWarningsOn, buffer); // would like to include _callsToTreesToArrays
if (_voxelsDirty) { if (_voxelsDirty) {
if (_alwaysRenderFullVBO) { if (_alwaysRenderFullVBO) {
glBufferIndex segmentStart = 0; glBufferIndex segmentStart = 0;
@ -673,9 +705,9 @@ void VoxelSystem::removeOutOfView() {
_tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)&args); _tree->recurseTreeWithOperation(removeOutOfViewOperation,(void*)&args);
if (_renderWarningsOn) { if (_renderWarningsOn) {
printLog("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld bag.count()=%d \n", printLog("removeOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld _removedVoxels.count()=%d \n",
args.nodesScanned, args.nodesRemoved, args.nodesInside, args.nodesScanned, args.nodesRemoved, args.nodesInside,
args.nodesIntersect, args.nodesOutside, args.dontRecurseBag.count() args.nodesIntersect, args.nodesOutside, _removedVoxels.count()
); );
} }
} }

View file

@ -119,6 +119,7 @@ private:
void setupNewVoxelsForDrawing(); void setupNewVoxelsForDrawing();
void copyWrittenDataToReadArrays(); void copyWrittenDataToReadArrays();
void updateVBOs(); void updateVBOs();
void cleanupRemovedVoxels();
bool _voxelsDirty; bool _voxelsDirty;
}; };