diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 43a5e784ed..fd20b968e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -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(); } } diff --git a/interface/src/Application.h b/interface/src/Application.h index e2d0820ab9..5034915bd6 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -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; diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp new file mode 100644 index 0000000000..bb64dd5b08 --- /dev/null +++ b/interface/src/VoxelHideShowThread.cpp @@ -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 +#include +#include +#include + +#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 +} diff --git a/interface/src/VoxelHideShowThread.h b/interface/src/VoxelHideShowThread.h new file mode 100644 index 0000000000..22df6c299c --- /dev/null +++ b/interface/src/VoxelHideShowThread.h @@ -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 +#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__ diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp index 324f293110..799db030aa 100644 --- a/interface/src/VoxelSystem.cpp +++ b/interface/src/VoxelSystem.cpp @@ -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; diff --git a/interface/src/VoxelSystem.h b/interface/src/VoxelSystem.h index d9c672fdf3..5006ec4973 100644 --- a/interface/src/VoxelSystem.h +++ b/interface/src/VoxelSystem.h @@ -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(); diff --git a/libraries/shared/src/OctalCode.cpp b/libraries/shared/src/OctalCode.cpp index 027a6a4d68..5ac27efd40 100644 --- a/libraries/shared/src/OctalCode.cpp +++ b/libraries/shared/src/OctalCode.cpp @@ -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); }