Move Hide/Show local voxels to a dedicated thread so that it doesn't slow performance of voxel packet processing

This commit is contained in:
ZappoMan 2013-12-02 12:08:45 -08:00
parent 14129cd86d
commit 76b3bd4e6e
7 changed files with 132 additions and 70 deletions

View file

@ -140,6 +140,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
#endif
_stopNetworkReceiveThread(false),
_voxelProcessor(),
_voxelHideShowThread(&_voxels),
_voxelEditSender(this),
_packetCount(0),
_packetsPerSecond(0),
@ -329,6 +330,7 @@ void Application::initializeGL() {
// create thread for parsing of voxel data independent of the main network and rendering threads
_voxelProcessor.initialize(_enableProcessVoxelsThread);
_voxelEditSender.initialize(_enableProcessVoxelsThread);
_voxelHideShowThread.initialize(_enableProcessVoxelsThread);
if (_enableProcessVoxelsThread) {
qDebug("Voxel parsing thread created.\n");
}
@ -1401,6 +1403,7 @@ void Application::terminate() {
}
_voxelProcessor.terminate();
_voxelHideShowThread.terminate();
_voxelEditSender.terminate();
}
@ -2249,6 +2252,7 @@ void Application::updateThreads(float deltaTime) {
// parse voxel packets
if (!_enableProcessVoxelsThread) {
_voxelProcessor.threadRoutine();
_voxelHideShowThread.threadRoutine();
_voxelEditSender.threadRoutine();
}
}

View file

@ -41,6 +41,7 @@
#include "ViewFrustum.h"
#include "VoxelFade.h"
#include "VoxelEditPacketSender.h"
#include "VoxelHideShowThread.h"
#include "VoxelPacketProcessor.h"
#include "VoxelSystem.h"
#include "VoxelImporter.h"
@ -442,8 +443,9 @@ private:
bool _stopNetworkReceiveThread;
bool _enableProcessVoxelsThread;
VoxelPacketProcessor _voxelProcessor;
VoxelEditPacketSender _voxelEditSender;
VoxelPacketProcessor _voxelProcessor;
VoxelHideShowThread _voxelHideShowThread;
VoxelEditPacketSender _voxelEditSender;
unsigned char _incomingPacket[MAX_PACKET_SIZE];
int _packetCount;

View file

@ -0,0 +1,46 @@
//
// VoxelHideShowThread.cpp
// interface
//
// Created by Brad Hefta-Gaub on 12/1/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded interface thread for hiding and showing voxels in the local tree.
//
#include <QDebug>
#include <NodeList.h>
#include <PerfStat.h>
#include <SharedUtil.h>
#include "Menu.h"
#include "VoxelHideShowThread.h"
VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) :
_theSystem(theSystem) {
}
bool VoxelHideShowThread::process() {
const uint64_t MSECS_TO_USECS = 1000;
const uint64_t SECS_TO_USECS = 1000 * MSECS_TO_USECS;
const uint64_t FRAME_RATE = 60;
const uint64_t USECS_PER_FRAME = SECS_TO_USECS / FRAME_RATE; // every 60fps
uint64_t start = usecTimestampNow();
_theSystem->checkForCulling();
uint64_t end = usecTimestampNow();
uint64_t elapsed = end - start;
bool showExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
if (showExtraDebugging && elapsed > USECS_PER_FRAME) {
printf("VoxelHideShowThread::process()... checkForCulling took %llu\n", elapsed);
}
if (isStillRunning()) {
if (elapsed < USECS_PER_FRAME) {
uint64_t sleepFor = USECS_PER_FRAME - elapsed;
usleep(sleepFor);
}
}
return isStillRunning(); // keep running till they terminate us
}

View file

@ -0,0 +1,31 @@
//
// VoxelHideShowThread.h
// voxel-server
//
// Created by Brad Hefta-Gaub on 12/1/13
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
//
// Threaded or non-threaded voxel persistence
//
#ifndef __interface__VoxelHideShowThread__
#define __interface__VoxelHideShowThread__
#include <GenericThread.h>
#include "VoxelSystem.h"
/// Generalized threaded processor for handling received inbound packets.
class VoxelHideShowThread : public virtual GenericThread {
public:
VoxelHideShowThread(VoxelSystem* theSystem);
protected:
/// Implements generic processing behavior for this thread.
virtual bool process();
private:
VoxelSystem* _theSystem;
};
#endif // __interface__VoxelHideShowThread__

View file

@ -592,14 +592,15 @@ float VoxelSystem::getVoxelsBytesReadPerSecondAverage() {
}
int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
bool showTimingDetails = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings);
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData()",showTimingDetails);
unsigned char command = *sourceBuffer;
int numBytesPacketHeader = numBytesForPacketHeader(sourceBuffer);
switch(command) {
case PACKET_TYPE_VOXEL_DATA: {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings),
"readBitstreamToTree()");
PerformanceWarning warn(showTimingDetails, "VoxelSystem::parseData() PACKET_TYPE_VOXEL_DATA part...",showTimingDetails);
unsigned char* dataAt = sourceBuffer + numBytesPacketHeader;
VOXEL_PACKET_FLAGS flags = (*(VOXEL_PACKET_FLAGS*)(dataAt));
@ -641,7 +642,8 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
VoxelPacketData packetData(packetIsCompressed);
packetData.loadFinalizedContent(dataAt, sectionLength);
if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
qDebug("Got Packet color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
qDebug("VoxelSystem::parseData() ... Got Packet Section"
" color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
" subsection:%d sectionLength:%d uncompressed:%d\n",
debug::valueOf(packetIsColored), debug::valueOf(packetIsCompressed),
sequence, flightTime, numBytes, dataBytes, subsection, sectionLength, packetData.getUncompressedSize());
@ -657,12 +659,9 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
}
break;
}
if (!_useFastVoxelPipeline || _writeRenderFullVBO) {
setupNewVoxelsForDrawing();
} else {
checkForCulling();
setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
}
@ -689,8 +688,6 @@ void VoxelSystem::setupNewVoxelsForDrawing() {
_inSetupNewVoxelsForDrawing = true;
checkForCulling(); // check for out of view and deleted voxels...
bool didWriteFullVBO = _writeRenderFullVBO;
if (_tree->isDirty()) {
static char buffer[64] = { 0 };
@ -776,68 +773,44 @@ void VoxelSystem::checkForCulling() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "checkForCulling()");
uint64_t start = usecTimestampNow();
uint64_t sinceLastViewCulling = (start - _lastViewCulling) / 1000;
// These items used to be menu options, we are not defaulting to and only supporting these modes.
bool constantCulling = true;
bool performHideOutOfViewLogic = true;
bool performRemoveOutOfViewLogic = false;
// If the view frustum is no longer changing, but has changed, since last time, then remove nodes that are out of view
if (constantCulling || (
(sinceLastViewCulling >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS))
&& !isViewChanging()
)
) {
// 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).
if (performHideOutOfViewLogic) {
// track how long its been since we were last moving. If we have recently moved then only use delta frustums, if
// it's been a long time since we last moved, then go ahead and do a full frustum cull.
if (isViewChanging()) {
_lastViewIsChanging = start;
}
uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000;
bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS));
// These has changed events will occur before we stop. So we need to remember this for when we finally have stopped
// moving long enough to be enoughTime
if (hasViewChanged()) {
_hasRecentlyChanged = true;
}
// If we have recently changed, but it's been enough time since we last moved, then we will do a full frustum
// hide/show culling pass
bool forceFullFrustum = enoughTime && _hasRecentlyChanged;
// in hide mode, we only track the full frustum culls, because we don't care about the partials.
if (forceFullFrustum) {
_lastViewCulling = start;
_hasRecentlyChanged = false;
}
hideOutOfView(forceFullFrustum);
if (forceFullFrustum) {
uint64_t endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
}
} else if (performRemoveOutOfViewLogic) {
_lastViewCulling = start;
removeOutOfView();
uint64_t endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
}
// 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();
// track how long its been since we were last moving. If we have recently moved then only use delta frustums, if
// it's been a long time since we last moved, then go ahead and do a full frustum cull.
if (isViewChanging()) {
_lastViewIsChanging = start;
}
uint64_t sinceLastMoving = (start - _lastViewIsChanging) / 1000;
bool enoughTime = (sinceLastMoving >= std::max((float) _lastViewCullingElapsed, VIEW_CULLING_RATE_IN_MILLISECONDS));
// These has changed events will occur before we stop. So we need to remember this for when we finally have stopped
// moving long enough to be enoughTime
if (hasViewChanged()) {
_hasRecentlyChanged = true;
}
// If we have recently changed, but it's been enough time since we last moved, then we will do a full frustum
// hide/show culling pass
bool forceFullFrustum = enoughTime && _hasRecentlyChanged;
// in hide mode, we only track the full frustum culls, because we don't care about the partials.
if (forceFullFrustum) {
_lastViewCulling = start;
_hasRecentlyChanged = false;
}
hideOutOfView(forceFullFrustum);
if (forceFullFrustum) {
uint64_t endViewCulling = usecTimestampNow();
_lastViewCullingElapsed = (endViewCulling - start) / 1000;
}
// 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();
uint64_t sinceLastAudit = (start - _lastAudit) / 1000;

View file

@ -40,6 +40,9 @@ struct VoxelShaderVBOData
class VoxelSystem : public NodeData, public VoxelNodeDeleteHook, public VoxelNodeUpdateHook,
public NodeListHook, public DomainChangeListener {
Q_OBJECT
friend class VoxelHideShowThread;
public:
VoxelSystem(float treeScale = TREE_SCALE, int maxVoxels = DEFAULT_MAX_VOXELS_PER_SYSTEM);
~VoxelSystem();

View file

@ -63,6 +63,9 @@ int branchIndexWithDescendant(const unsigned char* ancestorOctalCode, const unsi
int parentSections = numberOfThreeBitSectionsInCode(ancestorOctalCode);
int branchStartBit = parentSections * 3;
// Note: this does not appear to be "multi-byte length code" safe. When octal codes are larger than 255 bytes
// long, the length code is stored in two bytes. The "1" below appears to assume that the length is always one
// byte long.
return sectionValue(descendantOctalCode + 1 + (branchStartBit / 8), branchStartBit % 8);
}