From 2257afd390e59ece7ef5398906df44ad95a50158 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 26 Mar 2014 15:20:27 -0700 Subject: [PATCH 01/20] removed unneeded call to disconnect() --- interface/src/Audio.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index c2d5f535a7..1575375d4b 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -840,7 +840,6 @@ bool Audio::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo) // cleanup any previously initialized device if (_audioOutput) { _audioOutput->stop(); - disconnect(_outputDevice, 0, 0, 0); _outputDevice = NULL; delete _audioOutput; From 411ebf805a11148cf81fbcd90b8dfbf7b469f7b1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 26 Mar 2014 15:42:53 -0700 Subject: [PATCH 02/20] use InvokeMethod() for all menu scripting operations --- interface/src/Menu.h | 4 ++-- interface/src/scripting/MenuScriptingInterface.cpp | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9452ba220d..983264ea55 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -66,8 +66,6 @@ public: static Menu* getInstance(); ~Menu(); - bool isOptionChecked(const QString& menuOption); - void setIsOptionChecked(const QString& menuOption, bool isChecked); void triggerOption(const QString& menuOption); QAction* getActionForOption(const QString& menuOption); @@ -133,6 +131,8 @@ public slots: void removeSeparator(const QString& menuName, const QString& separatorName); void addMenuItem(const MenuItemProperties& properties); void removeMenuItem(const QString& menuName, const QString& menuitem); + bool isOptionChecked(const QString& menuOption); + void setIsOptionChecked(const QString& menuOption, bool isChecked); private slots: void aboutApp(); diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index ccb3fe5eb9..4f9003b288 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -65,9 +65,15 @@ void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& }; bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { - return Menu::getInstance()->isOptionChecked(menuOption); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menuOption)); + return result; } void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { - return Menu::getInstance()->setIsOptionChecked(menuOption, isChecked); + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); } From 52c8a865f1a815e6b8c78ca3c4933664a55da4d5 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 15:53:19 -0700 Subject: [PATCH 03/20] Provide a means of tracking resource download progress. Until we have a user-friendly progress meter/throbber, put the download progress in the extended stats. --- interface/interface_en.ts | 4 ++-- interface/src/Application.cpp | 15 +++++++++++++-- libraries/shared/src/ResourceCache.cpp | 11 +++++++---- libraries/shared/src/ResourceCache.h | 17 ++++++++++++++++- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index f5c7f225df..711af2ae2d 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 13c05fa702..edeb454f11 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2676,7 +2676,7 @@ void Application::displayStats() { glm::vec3 avatarPos = _myAvatar->getPosition(); - lines = _statsExpanded ? 4 : 3; + lines = _statsExpanded ? 5 : 3; displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - (mirrorEnabled ? 301 : 411) - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; @@ -2713,12 +2713,23 @@ void Application::displayStats() { verticalOffset += STATS_PELS_PER_LINE; drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, avatarMixerStats, WHITE_TEXT); + + stringstream downloadStats; + downloadStats << "Downloads: "; + foreach (Resource* resource, ResourceCache::getLoadingRequests()) { + const float MAXIMUM_PERCENTAGE = 100.0f; + downloadStats << roundf(resource->getProgress() * MAXIMUM_PERCENTAGE) << "% "; + } + downloadStats << "(" << ResourceCache::getPendingRequestCount() << " pending)"; + + verticalOffset += STATS_PELS_PER_LINE; + drawText(horizontalOffset, verticalOffset, 0.10f, 0.f, 2.f, downloadStats.str().c_str(), WHITE_TEXT); } verticalOffset = 0; horizontalOffset = _glWidget->width() - (mirrorEnabled ? 300 : 410); - lines = _statsExpanded ? 11 : 3; + lines = _statsExpanded ? 12 : 3; displayStatsBackground(backgroundColor, horizontalOffset, 0, _glWidget->width() - horizontalOffset, lines * STATS_PELS_PER_LINE + 10); horizontalOffset += 5; diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 6cca856b78..c7858e6e20 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -63,10 +63,12 @@ void ResourceCache::attemptRequest(Resource* resource) { return; } _requestLimit--; + _loadingRequests.append(resource); resource->makeRequest(); } -void ResourceCache::requestCompleted() { +void ResourceCache::requestCompleted(Resource* resource) { + _loadingRequests.removeOne(resource); _requestLimit++; // look for the highest priority pending request @@ -96,6 +98,7 @@ const int DEFAULT_REQUEST_LIMIT = 10; int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT; QList > ResourceCache::_pendingRequests; +QList ResourceCache::_loadingRequests; Resource::Resource(const QUrl& url, bool delayLoad) : _url(url), @@ -121,7 +124,7 @@ Resource::Resource(const QUrl& url, bool delayLoad) : Resource::~Resource() { if (_reply) { - ResourceCache::requestCompleted(); + ResourceCache::requestCompleted(this); delete _reply; } } @@ -215,7 +218,7 @@ void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { _replyTimer->disconnect(this); _replyTimer->deleteLater(); _replyTimer = NULL; - ResourceCache::requestCompleted(); + ResourceCache::requestCompleted(this); downloadFinished(reply); } @@ -250,7 +253,7 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug) _replyTimer->disconnect(this); _replyTimer->deleteLater(); _replyTimer = NULL; - ResourceCache::requestCompleted(); + ResourceCache::requestCompleted(this); // retry for certain types of failures switch (error) { diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index a544f43731..2fcda0bb98 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -37,6 +37,10 @@ public: static void setRequestLimit(int limit) { _requestLimit = limit; } static int getRequestLimit() { return _requestLimit; } + static const QList& getLoadingRequests() { return _loadingRequests; } + + static int getPendingRequestCount() { return _pendingRequests.size(); } + ResourceCache(QObject* parent = NULL); virtual ~ResourceCache(); @@ -58,7 +62,7 @@ protected: void addUnusedResource(const QSharedPointer& resource); static void attemptRequest(Resource* resource); - static void requestCompleted(); + static void requestCompleted(Resource* resource); private: @@ -70,6 +74,7 @@ private: static QNetworkAccessManager* _networkAccessManager; static int _requestLimit; static QList > _pendingRequests; + static QList _loadingRequests; }; /// Base class for resources. @@ -102,6 +107,15 @@ public: /// Checks whether the resource has loaded. bool isLoaded() const { return _loaded; } + /// For loading resources, returns the number of bytes received. + qint64 getBytesReceived() const { return _bytesReceived; } + + /// For loading resources, returns the number of total bytes (or zero if unknown). + qint64 getBytesTotal() const { return _bytesTotal; } + + /// For loading resources, returns the load progress. + float getProgress() const { return (_bytesTotal == 0) ? 0.0f : (float)_bytesReceived / _bytesTotal; } + void setSelf(const QWeakPointer& self) { _self = self; } void setCache(ResourceCache* cache) { _cache = cache; } @@ -152,6 +166,7 @@ private: int _lruKey; QNetworkReply* _reply; QTimer* _replyTimer; + int _index; qint64 _bytesReceived; qint64 _bytesTotal; int _attempts; From 1cae0dc94c89efca470543e42c6b2f4e3beb55cb Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 16:01:07 -0700 Subject: [PATCH 04/20] Adjust avatar LOD faster. Closes #2496. --- interface/interface_en.ts | 8 ++++---- interface/src/Menu.cpp | 14 +++++++++----- interface/src/Menu.h | 1 + 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index 711af2ae2d..da8827d89d 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 30be26ee96..e0da1c0318 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -62,7 +62,8 @@ Menu* Menu::getInstance() { const ViewFrustumOffset DEFAULT_FRUSTUM_OFFSET = {-135.0f, 0.0f, 0.0f, 25.0f, 0.0f}; const float DEFAULT_FACESHIFT_EYE_DEFLECTION = 0.25f; const float DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER = 1.0f; -const int FIVE_SECONDS_OF_FRAMES = 5 * 60; +const int ONE_SECOND_OF_FRAMES = 60; +const int FIVE_SECONDS_OF_FRAMES = 5 * ONE_SECOND_OF_FRAMES; Menu::Menu() : _actionHash(), @@ -82,6 +83,7 @@ Menu::Menu() : _lastAdjust(usecTimestampNow()), _lastAvatarDetailDrop(usecTimestampNow()), _fpsAverage(FIVE_SECONDS_OF_FRAMES), + _fastFPSAverage(ONE_SECOND_OF_FRAMES), _loginAction(NULL) { Application *appInstance = Application::getInstance(); @@ -1192,17 +1194,19 @@ void Menu::autoAdjustLOD(float currentFPS) { currentFPS = ASSUMED_FPS; } _fpsAverage.updateAverage(currentFPS); + _fastFPSAverage.updateAverage(currentFPS); quint64 now = usecTimestampNow(); - if (_fpsAverage.getAverage() < ADJUST_LOD_DOWN_FPS) { - if (now - _lastAvatarDetailDrop > ADJUST_LOD_DOWN_DELAY) { + const quint64 ADJUST_AVATAR_LOD_DOWN_DELAY = 1000 * 1000; + if (_fastFPSAverage.getAverage() < ADJUST_LOD_DOWN_FPS) { + if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) { // attempt to lower the detail in proportion to the fps difference float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f; - _avatarLODDistanceMultiplier *= (targetFps / _fpsAverage.getAverage()); + _avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage()); _lastAvatarDetailDrop = now; } - } else if (_fpsAverage.getAverage() > ADJUST_LOD_UP_FPS) { + } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { // let the detail level creep slowly upwards const float DISTANCE_DECREASE_RATE = 0.01f; const float MINIMUM_DISTANCE_MULTIPLIER = 0.1f; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 9452ba220d..23ddb8d3dc 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -211,6 +211,7 @@ private: quint64 _lastAdjust; quint64 _lastAvatarDetailDrop; SimpleMovingAverage _fpsAverage; + SimpleMovingAverage _fastFPSAverage; QAction* _loginAction; QAction* _chatAction; }; From 26da3bcefd86be8ee4096930a16e6d0669144b96 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Wed, 26 Mar 2014 16:28:03 -0700 Subject: [PATCH 05/20] Bump up the rate at which we increase detail. --- interface/src/Menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index e0da1c0318..79b0a23ce5 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1208,7 +1208,7 @@ void Menu::autoAdjustLOD(float currentFPS) { } } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { // let the detail level creep slowly upwards - const float DISTANCE_DECREASE_RATE = 0.01f; + const float DISTANCE_DECREASE_RATE = 0.02f; const float MINIMUM_DISTANCE_MULTIPLIER = 0.1f; _avatarLODDistanceMultiplier = qMax(MINIMUM_DISTANCE_MULTIPLIER, _avatarLODDistanceMultiplier - DISTANCE_DECREASE_RATE); From 11c089c2219e65945dc4c94216db0fc8ed7e17c4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Wed, 26 Mar 2014 16:48:57 -0700 Subject: [PATCH 06/20] add support for getting the actual default audio devices in windows by name --- interface/src/Audio.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 1575375d4b..75f624af7d 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -143,6 +143,28 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } } #endif +#ifdef WIN32 + QString deviceName; + if (mode == QAudio::AudioInput) { + WAVEINCAPS wic; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); + //Use the received manufacturer id to get the device's real name + waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); + qDebug() << "input device:" << wic.szPname; + deviceName = wic.szPname; + } else { + WAVEOUTCAPS woc; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); + //Use the received manufacturer id to get the device's real name + waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); + qDebug() << "output device:" << woc.szPname; + deviceName = woc.szPname; + } + return getNamedAudioDeviceForMode(mode, deviceName); +#endif + // fallback for failed lookup is the default device return (mode == QAudio::AudioInput) ? QAudioDeviceInfo::defaultInputDevice() : QAudioDeviceInfo::defaultOutputDevice(); From 0435b16668830623440b6e96695e038814a98edf Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 26 Mar 2014 16:52:24 -0700 Subject: [PATCH 07/20] fix formatting --- interface/src/Audio.cpp | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 75f624af7d..321a4ea2ad 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -144,25 +144,25 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } #endif #ifdef WIN32 - QString deviceName; - if (mode == QAudio::AudioInput) { - WAVEINCAPS wic; - // first use WAVE_MAPPER to get the default devices manufacturer ID - waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); - //Use the received manufacturer id to get the device's real name - waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); - qDebug() << "input device:" << wic.szPname; - deviceName = wic.szPname; - } else { - WAVEOUTCAPS woc; - // first use WAVE_MAPPER to get the default devices manufacturer ID - waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); - //Use the received manufacturer id to get the device's real name - waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); - qDebug() << "output device:" << woc.szPname; - deviceName = woc.szPname; - } - return getNamedAudioDeviceForMode(mode, deviceName); + QString deviceName; + if (mode == QAudio::AudioInput) { + WAVEINCAPS wic; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveInGetDevCaps(WAVE_MAPPER, &wic, sizeof(wic)); + //Use the received manufacturer id to get the device's real name + waveInGetDevCaps(wic.wMid, &wic, sizeof(wic)); + qDebug() << "input device:" << wic.szPname; + deviceName = wic.szPname; + } else { + WAVEOUTCAPS woc; + // first use WAVE_MAPPER to get the default devices manufacturer ID + waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(woc)); + //Use the received manufacturer id to get the device's real name + waveOutGetDevCaps(woc.wMid, &woc, sizeof(woc)); + qDebug() << "output device:" << woc.szPname; + deviceName = woc.szPname; + } + return getNamedAudioDeviceForMode(mode, deviceName); #endif From 7eab276122185e1af6e630062144a8ac0c7bcf54 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 26 Mar 2014 17:19:23 -0700 Subject: [PATCH 08/20] suppress debug in ScriptEngine, fix spatialization from FS head changes --- interface/src/Audio.cpp | 2 +- interface/src/avatar/Head.cpp | 3 --- libraries/script-engine/src/ScriptEngine.cpp | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index c2d5f535a7..c222e2ebd2 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -500,7 +500,7 @@ void Audio::handleAudioInput() { if (audioMixer && audioMixer->getActiveSocket()) { MyAvatar* interfaceAvatar = Application::getInstance()->getAvatar(); glm::vec3 headPosition = interfaceAvatar->getHead()->getPosition(); - glm::quat headOrientation = interfaceAvatar->getHead()->getOrientation(); + glm::quat headOrientation = interfaceAvatar->getHead()->getTweakedOrientation(); // we need the amount of bytes in the buffer + 1 for type // + 12 for 3 floats for position + float for bearing + 1 attenuation byte diff --git a/interface/src/avatar/Head.cpp b/interface/src/avatar/Head.cpp index 4a81df8b74..abbc408650 100644 --- a/interface/src/avatar/Head.cpp +++ b/interface/src/avatar/Head.cpp @@ -55,9 +55,6 @@ void Head::reset() { _faceModel.reset(); } - - - void Head::simulate(float deltaTime, bool isMine, bool billboard) { // Update audio trailing average for rendering facial animations diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c0834aad9d..1f1eab6baf 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -463,7 +463,6 @@ void ScriptEngine::timerFired() { if (!callingTimer->isActive()) { // this timer is done, we can kill it - qDebug() << "Deleting a single shot timer"; delete callingTimer; } } From c6c4b586992db4820dd4531df9bf9b6d660f44fb Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 02:31:39 -0700 Subject: [PATCH 09/20] adds reset of octree element population statistics --- libraries/octree/src/OctreeElement.cpp | 5 +++++ libraries/octree/src/OctreeElement.h | 1 + 2 files changed, 6 insertions(+) diff --git a/libraries/octree/src/OctreeElement.cpp b/libraries/octree/src/OctreeElement.cpp index cdc4f419c0..7697338a28 100644 --- a/libraries/octree/src/OctreeElement.cpp +++ b/libraries/octree/src/OctreeElement.cpp @@ -29,6 +29,11 @@ quint64 OctreeElement::_externalChildrenMemoryUsage = 0; quint64 OctreeElement::_voxelNodeCount = 0; quint64 OctreeElement::_voxelNodeLeafCount = 0; +void OctreeElement::resetPopulationStatistics() { + _voxelNodeCount = 0; + _voxelNodeLeafCount = 0; +} + OctreeElement::OctreeElement() { // Note: you must call init() from your subclass, otherwise the OctreeElement will not be properly // initialized. You will see DEADBEEF in your memory debugger if you have not properly called init() diff --git a/libraries/octree/src/OctreeElement.h b/libraries/octree/src/OctreeElement.h index ca2fa0b540..a37866bdbe 100644 --- a/libraries/octree/src/OctreeElement.h +++ b/libraries/octree/src/OctreeElement.h @@ -152,6 +152,7 @@ public: static void addUpdateHook(OctreeElementUpdateHook* hook); static void removeUpdateHook(OctreeElementUpdateHook* hook); + static void resetPopulationStatistics(); static unsigned long getNodeCount() { return _voxelNodeCount; } static unsigned long getInternalNodeCount() { return _voxelNodeCount - _voxelNodeLeafCount; } static unsigned long getLeafNodeCount() { return _voxelNodeLeafCount; } From 55b9dd1aee2da5deac9b69ebab1884b1303605d0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 02:32:19 -0700 Subject: [PATCH 10/20] remove non-locking version of nodeWithUUID() --- libraries/shared/src/NodeList.cpp | 15 +++------------ libraries/shared/src/NodeList.h | 3 +-- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index 6eb6eb77d8..dc5a419295 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -357,18 +357,9 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) { return 0; } -SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) { - SharedNodePointer node; - // if caller wants us to block and guarantee the correct answer, then honor that request - if (blockingLock) { - // this will block till we can get access - QMutexLocker locker(&_nodeHashMutex); - node = _nodeHash.value(nodeUUID); - } else if (_nodeHashMutex.tryLock()) { // some callers are willing to get wrong answers but not block - node = _nodeHash.value(nodeUUID); - _nodeHashMutex.unlock(); - } - return node; +SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) { + QMutexLocker locker(&_nodeHashMutex); + return _nodeHash.value(nodeUUID); } SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) { diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index 0a59537a46..f10e01f3f4 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -103,8 +103,7 @@ public: QByteArray constructPingReplyPacket(const QByteArray& pingPacket); void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); - /// passing false for blockingLock, will tryLock, and may return NULL when a node with the UUID actually does exist - SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true); + SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, From 9fdfa4a7a810b45ed7a41810fd61ce5a9d671b8b Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 02:33:32 -0700 Subject: [PATCH 11/20] switch to using SharedNodePointer instead of UUID in octree server send thread --- .../src/octree/OctreeQueryNode.cpp | 4 +-- .../src/octree/OctreeQueryNode.h | 2 +- .../src/octree/OctreeSendThread.cpp | 31 +++++-------------- .../src/octree/OctreeSendThread.h | 5 ++- assignment-client/src/octree/OctreeServer.cpp | 4 ++- 5 files changed, 15 insertions(+), 31 deletions(-) diff --git a/assignment-client/src/octree/OctreeQueryNode.cpp b/assignment-client/src/octree/OctreeQueryNode.cpp index e0ff29effd..984ce42631 100644 --- a/assignment-client/src/octree/OctreeQueryNode.cpp +++ b/assignment-client/src/octree/OctreeQueryNode.cpp @@ -76,9 +76,9 @@ void OctreeQueryNode::deleteLater() { } -void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID) { +void OctreeQueryNode::initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node) { // Create octree sending thread... - _octreeSendThread = new OctreeSendThread(nodeUUID, octreeServer); + _octreeSendThread = new OctreeSendThread(octreeServer, node); _octreeSendThread->initialize(true); } diff --git a/assignment-client/src/octree/OctreeQueryNode.h b/assignment-client/src/octree/OctreeQueryNode.h index b7e68e805d..17d6f6337c 100644 --- a/assignment-client/src/octree/OctreeQueryNode.h +++ b/assignment-client/src/octree/OctreeQueryNode.h @@ -83,7 +83,7 @@ public: OctreeSceneStats stats; - void initializeOctreeSendThread(OctreeServer* octreeServer, const QUuid& nodeUUID); + void initializeOctreeSendThread(OctreeServer* octreeServer, SharedNodePointer node); bool isOctreeSendThreadInitalized() { return _octreeSendThread; } void dumpOutOfView(); diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 2fa6397ac5..382d8aa528 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -19,11 +19,10 @@ quint64 startSceneSleepTime = 0; quint64 endSceneSleepTime = 0; -OctreeSendThread::OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer) : - _nodeUUID(nodeUUID), +OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : _myServer(myServer), + _node(node), _packetData(), - _nodeMissingCount(0), _processLock(), _isShuttingDown(false) { @@ -44,7 +43,8 @@ OctreeSendThread::~OctreeSendThread() { } qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; - OctreeServer::clientDisconnected(); + _node.clear(); + OctreeServer::clientDisconnected(); } void OctreeSendThread::setIsShuttingDown() { @@ -68,35 +68,18 @@ bool OctreeSendThread::process() { return false; // exit early if we're shutting down } - const int MAX_NODE_MISSING_CHECKS = 10; - if (_nodeMissingCount > MAX_NODE_MISSING_CHECKS) { - qDebug() << "our target node:" << _nodeUUID << "has been missing the last" << _nodeMissingCount - << "times we checked, we are going to stop attempting to send."; - return false; // stop processing and shutdown, our node no longer exists - } - quint64 start = usecTimestampNow(); // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - - // see if we can get access to our node, but don't wait on the lock, if the nodeList is busy - // it might not return a node that is known, but that's ok we can handle that case. - SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false); - - if (node) { - _nodeMissingCount = 0; - OctreeQueryNode* nodeData = NULL; - - nodeData = (OctreeQueryNode*) node->getLinkedData(); + if (!_node.isNull()) { + OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(node, nodeData, viewFrustumChanged); + packetDistributor(_node, nodeData, viewFrustumChanged); } - } else { - _nodeMissingCount++; } } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index ab88121ee8..4e18ee9b2a 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -21,7 +21,7 @@ class OctreeSendThread : public GenericThread { Q_OBJECT public: - OctreeSendThread(const QUuid& nodeUUID, OctreeServer* myServer); + OctreeSendThread(OctreeServer* myServer, SharedNodePointer node); virtual ~OctreeSendThread(); void setIsShuttingDown(); @@ -38,7 +38,7 @@ protected: virtual bool process(); private: - QUuid _nodeUUID; + SharedNodePointer _node; OctreeServer* _myServer; int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); @@ -46,7 +46,6 @@ private: OctreePacketData _packetData; - int _nodeMissingCount; QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing bool _isShuttingDown; }; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index fc01b1d04a..fa087cced2 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -835,7 +835,7 @@ void OctreeServer::readPendingDatagrams() { if (debug) { qDebug() << "calling initializeOctreeSendThread()... node:" << *matchingNode; } - nodeData->initializeOctreeSendThread(this, matchingNode->getUUID()); + nodeData->initializeOctreeSendThread(this, matchingNode); } } } else if (packetType == PacketTypeJurisdictionRequest) { @@ -852,7 +852,9 @@ void OctreeServer::readPendingDatagrams() { void OctreeServer::run() { _safeServerName = getMyServerName(); + // Before we do anything else, create our tree... + OctreeElement::resetPopulationStatistics(); _tree = createTree(); // use common init to setup common timers and logging From ca9f0c01c1ce1698bdc9d7f506b76fabedfd2b2a Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 11:08:55 -0700 Subject: [PATCH 12/20] Couple of quick fixes: reset the skeleton joints when space is pressed as a last resort to fix broken IK, don't render display names at the origin if we don't have a skeleton model. --- interface/src/avatar/Avatar.cpp | 10 +++++++--- interface/src/avatar/MyAvatar.cpp | 1 + interface/src/renderer/Model.cpp | 7 +++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index de9b33d9c7..94e1416e68 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -397,9 +397,13 @@ void Avatar::renderDisplayName() { glPushMatrix(); glm::vec3 textPosition; - getSkeletonModel().getNeckPosition(textPosition); - textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f; - + if (getSkeletonModel().getNeckPosition(textPosition)) { + textPosition += getBodyUpDirection() * getHeadHeight() * 1.1f; + } else { + const float HEAD_PROPORTION = 0.75f; + textPosition = _position + getBodyUpDirection() * (getBillboardSize() * HEAD_PROPORTION); + } + glTranslatef(textPosition.x, textPosition.y, textPosition.z); // we need "always facing camera": we must remove the camera rotation from the stack diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 618cda1199..350b4fb2be 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -79,6 +79,7 @@ void MyAvatar::reset() { // TODO? resurrect headMouse stuff? //_headMouseX = _glWidget->width() / 2; //_headMouseY = _glWidget->height() / 2; + _skeletonModel.reset(); getHead()->reset(); getHand()->reset(); diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index df5560a4b1..36ac6bec99 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -130,9 +130,16 @@ void Model::init() { } void Model::reset() { + if (_jointStates.isEmpty()) { + return; + } foreach (Model* attachment, _attachments) { attachment->reset(); } + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].rotation = geometry.joints.at(i).rotation; + } } void Model::clearShapes() { From 21eef6c15ee86dd6ce33f96308487724ddfab0c4 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 27 Mar 2014 13:10:03 -0700 Subject: [PATCH 13/20] make sure VoxelServer and ParticleServer are in Agent interest list --- assignment-client/src/Agent.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 25dcfcdd95..b5199e87e8 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -138,7 +138,11 @@ void Agent::run() { ThreadedAssignment::commonInit(AGENT_LOGGING_NAME, NodeType::Agent); NodeList* nodeList = NodeList::getInstance(); - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer); + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() + << NodeType::AudioMixer + << NodeType::AvatarMixer + << NodeType::VoxelServer + << NodeType::ParticleServer); // figure out the URL for the script for this agent assignment QString scriptURLString("http://%1:8080/assignment/%2"); From 7cc72307456b15e06691b4132be05e441a2a2058 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 13:18:45 -0700 Subject: [PATCH 14/20] Adjust the shadow shader to prevent seeing shadows on the opposite sides of objects. Closes #2544. --- interface/resources/shaders/shadow_map.frag | 5 ++-- interface/resources/shaders/shadow_map.vert | 30 +++++++++++++++++++++ interface/src/voxels/VoxelSystem.cpp | 15 ++++------- 3 files changed, 38 insertions(+), 12 deletions(-) create mode 100644 interface/resources/shaders/shadow_map.vert diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index 4bd8b8b768..810fc6a947 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -10,7 +10,8 @@ uniform sampler2DShadow shadowMap; +varying vec4 shadowColor; + void main(void) { - gl_FragColor = gl_Color * mix(vec4(0.8, 0.8, 0.8, 1.0), vec4(1.0, 1.0, 1.0, 1.0), - shadow2D(shadowMap, gl_TexCoord[0].stp)); + gl_FragColor = gl_Color * mix(shadowColor, vec4(1.0, 1.0, 1.0, 1.0), shadow2D(shadowMap, gl_TexCoord[0].stp)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert new file mode 100644 index 0000000000..a15cd070b9 --- /dev/null +++ b/interface/resources/shaders/shadow_map.vert @@ -0,0 +1,30 @@ +#version 120 + +// +// shadow_map.vert +// vertex shader +// +// Created by Andrzej Kapolka on 3/27/14. +// Copyright (c) 2014 High Fidelity, Inc. All rights reserved. +// + +varying vec4 shadowColor; + +void main(void) { + // the shadow color depends on the light product + vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + float lightProduct = dot(normal, gl_LightSource[0].position); + shadowColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.8, 0.8, 0.8, 1.0), step(0.0, lightProduct)); + + // standard diffuse lighting + gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient + + gl_LightSource[0].diffuse * max(0.0, lightProduct)); + + // generate the shadow texture coordinate using the eye position + vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; + gl_TexCoord[0] = vec4(dot(gl_EyePlaneS[0], eyePosition), dot(gl_EyePlaneT[0], eyePosition), + dot(gl_EyePlaneR[0], eyePosition), 1.0); + + // use the fixed function transform + gl_Position = ftransform(); +} diff --git a/interface/src/voxels/VoxelSystem.cpp b/interface/src/voxels/VoxelSystem.cpp index 4db5af3c04..5c68485436 100644 --- a/interface/src/voxels/VoxelSystem.cpp +++ b/interface/src/voxels/VoxelSystem.cpp @@ -506,8 +506,10 @@ void VoxelSystem::initVoxelMemory() { _perlinModulateProgram.setUniformValue("permutationNormalTexture", 0); _perlinModulateProgram.release(); - _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() - + "shaders/shadow_map.frag"); + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/shadow_map.vert"); + _shadowMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/shadow_map.frag"); _shadowMapProgram.link(); _shadowMapProgram.bind(); @@ -1471,10 +1473,6 @@ void VoxelSystem::applyScaleAndBindProgram(bool texture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.bind(); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getShadowDepthTextureID()); - glEnable(GL_TEXTURE_GEN_S); - glEnable(GL_TEXTURE_GEN_T); - glEnable(GL_TEXTURE_GEN_R); - glEnable(GL_TEXTURE_2D); glTexGenfv(GL_S, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[0]); glTexGenfv(GL_T, GL_EYE_PLANE, (const GLfloat*)&Application::getInstance()->getShadowMatrix()[1]); @@ -1496,10 +1494,7 @@ void VoxelSystem::removeScaleAndReleaseProgram(bool texture) { if (Menu::getInstance()->isOptionChecked(MenuOption::Shadows)) { _shadowMapProgram.release(); glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_GEN_S); - glDisable(GL_TEXTURE_GEN_T); - glDisable(GL_TEXTURE_GEN_R); - glDisable(GL_TEXTURE_2D); + } else if (texture) { _perlinModulateProgram.release(); From bda96ef935f00abc36ccbb89e3f0a134be61805c Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 13:35:22 -0700 Subject: [PATCH 15/20] added back use of nodeWithUUID() instead of sharedpointer --- .../src/octree/OctreeSendThread.cpp | 25 ++++++++-- .../src/octree/OctreeSendThread.h | 3 +- assignment-client/src/octree/OctreeServer.cpp | 46 +++++++++++++------ assignment-client/src/octree/OctreeServer.h | 4 ++ libraries/shared/src/NodeList.cpp | 18 ++++++-- libraries/shared/src/NodeList.h | 2 +- 6 files changed, 73 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 382d8aa528..023f3f3d2c 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -21,8 +21,9 @@ quint64 endSceneSleepTime = 0; OctreeSendThread::OctreeSendThread(OctreeServer* myServer, SharedNodePointer node) : _myServer(myServer), - _node(node), + _nodeUUID(node->getUUID()), _packetData(), + _nodeMissingCount(0), _processLock(), _isShuttingDown(false) { @@ -43,7 +44,6 @@ OctreeSendThread::~OctreeSendThread() { } qDebug() << qPrintable(safeServerName) << "server [" << _myServer << "]: client disconnected " "- ending sending thread [" << this << "]"; - _node.clear(); OctreeServer::clientDisconnected(); } @@ -72,13 +72,28 @@ bool OctreeSendThread::process() { // don't do any send processing until the initial load of the octree is complete... if (_myServer->isInitialLoadComplete()) { - if (!_node.isNull()) { - OctreeQueryNode* nodeData = static_cast(_node->getLinkedData()); + SharedNodePointer node = NodeList::getInstance()->nodeWithUUID(_nodeUUID, false); + if (node) { + _nodeMissingCount = 0; + OctreeQueryNode* nodeData = static_cast(node->getLinkedData()); // Sometimes the node data has not yet been linked, in which case we can't really do anything if (nodeData && !nodeData->isShuttingDown()) { bool viewFrustumChanged = nodeData->updateCurrentViewFrustum(); - packetDistributor(_node, nodeData, viewFrustumChanged); + packetDistributor(node, nodeData, viewFrustumChanged); + } + } else { + _nodeMissingCount++; + const int MANY_FAILED_LOCKS = 1; + if (_nodeMissingCount >= MANY_FAILED_LOCKS) { + + QString safeServerName("Octree"); + if (_myServer) { + safeServerName = _myServer->getMyServerName(); + } + + qDebug() << qPrintable(safeServerName) << "server: sending thread [" << this << "]" + << "failed to get nodeWithUUID() " << _nodeUUID <<". Failed:" << _nodeMissingCount << "times"; } } } diff --git a/assignment-client/src/octree/OctreeSendThread.h b/assignment-client/src/octree/OctreeSendThread.h index 4e18ee9b2a..4b1b6d8c92 100644 --- a/assignment-client/src/octree/OctreeSendThread.h +++ b/assignment-client/src/octree/OctreeSendThread.h @@ -38,14 +38,15 @@ protected: virtual bool process(); private: - SharedNodePointer _node; OctreeServer* _myServer; + QUuid _nodeUUID; int handlePacketSend(const SharedNodePointer& node, OctreeQueryNode* nodeData, int& trueBytesSent, int& truePacketsSent); int packetDistributor(const SharedNodePointer& node, OctreeQueryNode* nodeData, bool viewFrustumChanged); OctreePacketData _packetData; + int _nodeMissingCount; QMutex _processLock; // don't allow us to have our nodeData, or our thread to be deleted while we're processing bool _isShuttingDown; }; diff --git a/assignment-client/src/octree/OctreeServer.cpp b/assignment-client/src/octree/OctreeServer.cpp index fa087cced2..fd3f9e6cb7 100644 --- a/assignment-client/src/octree/OctreeServer.cpp +++ b/assignment-client/src/octree/OctreeServer.cpp @@ -1160,7 +1160,6 @@ QString OctreeServer::getStatusLink() { } void OctreeServer::sendStatsPacket() { - // TODO: we have too many stats to fit in a single MTU... so for now, we break it into multiple JSON objects and // send them separately. What we really should do is change the NodeList::sendStatsToDomainServer() to handle the // the following features: @@ -1241,59 +1240,78 @@ QMap OctreeServer::_threadsDidPacketDistributor; QMap OctreeServer::_threadsDidHandlePacketSend; QMap OctreeServer::_threadsDidCallWriteDatagram; +QMutex OctreeServer::_threadsDidProcessMutex; +QMutex OctreeServer::_threadsDidPacketDistributorMutex; +QMutex OctreeServer::_threadsDidHandlePacketSendMutex; +QMutex OctreeServer::_threadsDidCallWriteDatagramMutex; + void OctreeServer::didProcess(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidProcessMutex); _threadsDidProcess[thread] = usecTimestampNow(); } void OctreeServer::didPacketDistributor(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidPacketDistributorMutex); _threadsDidPacketDistributor[thread] = usecTimestampNow(); } void OctreeServer::didHandlePacketSend(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidHandlePacketSendMutex); _threadsDidHandlePacketSend[thread] = usecTimestampNow(); } void OctreeServer::didCallWriteDatagram(OctreeSendThread* thread) { + QMutexLocker locker(&_threadsDidCallWriteDatagramMutex); _threadsDidCallWriteDatagram[thread] = usecTimestampNow(); } void OctreeServer::stopTrackingThread(OctreeSendThread* thread) { + QMutexLocker lockerA(&_threadsDidProcessMutex); + QMutexLocker lockerB(&_threadsDidPacketDistributorMutex); + QMutexLocker lockerC(&_threadsDidHandlePacketSendMutex); + QMutexLocker lockerD(&_threadsDidCallWriteDatagramMutex); + _threadsDidProcess.remove(thread); _threadsDidPacketDistributor.remove(thread); _threadsDidHandlePacketSend.remove(thread); + _threadsDidCallWriteDatagram.remove(thread); } -int howManyThreadsDidSomething(QMap& something, quint64 since) { - if (since == 0) { - return something.size(); - } +int howManyThreadsDidSomething(QMutex& mutex, QMap& something, quint64 since) { int count = 0; - QMap::const_iterator i = something.constBegin(); - while (i != something.constEnd()) { - if (i.value() > since) { - count++; + if (mutex.tryLock()) { + if (since == 0) { + count = something.size(); + } else { + QMap::const_iterator i = something.constBegin(); + while (i != something.constEnd()) { + if (i.value() > since) { + count++; + } + ++i; + } } - ++i; + mutex.unlock(); } return count; } int OctreeServer::howManyThreadsDidProcess(quint64 since) { - return howManyThreadsDidSomething(_threadsDidProcess, since); + return howManyThreadsDidSomething(_threadsDidProcessMutex, _threadsDidProcess, since); } int OctreeServer::howManyThreadsDidPacketDistributor(quint64 since) { - return howManyThreadsDidSomething(_threadsDidPacketDistributor, since); + return howManyThreadsDidSomething(_threadsDidPacketDistributorMutex, _threadsDidPacketDistributor, since); } int OctreeServer::howManyThreadsDidHandlePacketSend(quint64 since) { - return howManyThreadsDidSomething(_threadsDidHandlePacketSend, since); + return howManyThreadsDidSomething(_threadsDidHandlePacketSendMutex, _threadsDidHandlePacketSend, since); } int OctreeServer::howManyThreadsDidCallWriteDatagram(quint64 since) { - return howManyThreadsDidSomething(_threadsDidCallWriteDatagram, since); + return howManyThreadsDidSomething(_threadsDidCallWriteDatagramMutex, _threadsDidCallWriteDatagram, since); } diff --git a/assignment-client/src/octree/OctreeServer.h b/assignment-client/src/octree/OctreeServer.h index 3dac42709f..63d43b6634 100644 --- a/assignment-client/src/octree/OctreeServer.h +++ b/assignment-client/src/octree/OctreeServer.h @@ -211,6 +211,10 @@ protected: static QMap _threadsDidHandlePacketSend; static QMap _threadsDidCallWriteDatagram; + static QMutex _threadsDidProcessMutex; + static QMutex _threadsDidPacketDistributorMutex; + static QMutex _threadsDidHandlePacketSendMutex; + static QMutex _threadsDidCallWriteDatagramMutex; }; #endif // __octree_server__OctreeServer__ diff --git a/libraries/shared/src/NodeList.cpp b/libraries/shared/src/NodeList.cpp index dc5a419295..761ea40d55 100644 --- a/libraries/shared/src/NodeList.cpp +++ b/libraries/shared/src/NodeList.cpp @@ -357,10 +357,20 @@ int NodeList::findNodeAndUpdateWithDataFromPacket(const QByteArray& packet) { return 0; } -SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID) { - QMutexLocker locker(&_nodeHashMutex); - return _nodeHash.value(nodeUUID); -} +SharedNodePointer NodeList::nodeWithUUID(const QUuid& nodeUUID, bool blockingLock) { + const int WAIT_TIME = 10; // wait up to 10ms in the try lock case + SharedNodePointer node; + // if caller wants us to block and guarantee the correct answer, then honor that request + if (blockingLock) { + // this will block till we can get access + QMutexLocker locker(&_nodeHashMutex); + node = _nodeHash.value(nodeUUID); + } else if (_nodeHashMutex.tryLock(WAIT_TIME)) { // some callers are willing to get wrong answers but not block + node = _nodeHash.value(nodeUUID); + _nodeHashMutex.unlock(); + } + return node; + } SharedNodePointer NodeList::sendingNodeForPacket(const QByteArray& packet) { QUuid nodeUUID = uuidFromPacketHeader(packet); diff --git a/libraries/shared/src/NodeList.h b/libraries/shared/src/NodeList.h index f10e01f3f4..d05d6a2fbc 100644 --- a/libraries/shared/src/NodeList.h +++ b/libraries/shared/src/NodeList.h @@ -103,7 +103,7 @@ public: QByteArray constructPingReplyPacket(const QByteArray& pingPacket); void pingPublicAndLocalSocketsForInactiveNode(const SharedNodePointer& node); - SharedNodePointer nodeWithUUID(const QUuid& nodeUUID); + SharedNodePointer nodeWithUUID(const QUuid& nodeUUID, bool blockingLock = true); SharedNodePointer sendingNodeForPacket(const QByteArray& packet); SharedNodePointer addOrUpdateNode(const QUuid& uuid, char nodeType, From 563725e7810259e49f47a5b6cca883a14bdea5d7 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 13:44:34 -0700 Subject: [PATCH 16/20] Use the "correct" shadow strategy: include only ambient term when in shadow. --- interface/resources/shaders/shadow_map.frag | 2 +- interface/resources/shaders/shadow_map.vert | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/interface/resources/shaders/shadow_map.frag b/interface/resources/shaders/shadow_map.frag index 810fc6a947..b683ed5af2 100644 --- a/interface/resources/shaders/shadow_map.frag +++ b/interface/resources/shaders/shadow_map.frag @@ -13,5 +13,5 @@ uniform sampler2DShadow shadowMap; varying vec4 shadowColor; void main(void) { - gl_FragColor = gl_Color * mix(shadowColor, vec4(1.0, 1.0, 1.0, 1.0), shadow2D(shadowMap, gl_TexCoord[0].stp)); + gl_FragColor = mix(shadowColor, gl_Color, shadow2D(shadowMap, gl_TexCoord[0].stp)); } diff --git a/interface/resources/shaders/shadow_map.vert b/interface/resources/shaders/shadow_map.vert index a15cd070b9..6809ca6e2b 100644 --- a/interface/resources/shaders/shadow_map.vert +++ b/interface/resources/shaders/shadow_map.vert @@ -11,14 +11,12 @@ varying vec4 shadowColor; void main(void) { - // the shadow color depends on the light product - vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); - float lightProduct = dot(normal, gl_LightSource[0].position); - shadowColor = mix(vec4(1.0, 1.0, 1.0, 1.0), vec4(0.8, 0.8, 0.8, 1.0), step(0.0, lightProduct)); + // the shadow color includes only the ambient terms + shadowColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient); - // standard diffuse lighting - gl_FrontColor = gl_Color * (gl_LightModel.ambient + gl_LightSource[0].ambient + - gl_LightSource[0].diffuse * max(0.0, lightProduct)); + // the normal color includes diffuse + vec4 normal = normalize(gl_ModelViewMatrix * vec4(gl_Normal, 0.0)); + gl_FrontColor = shadowColor + gl_Color * (gl_LightSource[0].diffuse * max(0.0, dot(normal, gl_LightSource[0].position))); // generate the shadow texture coordinate using the eye position vec4 eyePosition = gl_ModelViewMatrix * gl_Vertex; From 9078d9a0f245a4a7218936adc0dc421d1a32a770 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 14:14:31 -0700 Subject: [PATCH 17/20] Fix for avatar LOD's getting into a degenerate state. Don't persist the avatar LOD, since we adjust it more rapidly. --- interface/interface_en.ts | 8 ++++---- interface/src/Menu.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index da8827d89d..aa7b1951ea 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -113,18 +113,18 @@ Menu - + Open .ini config file - - + + Text files (*.ini) - + Save .ini config file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 79b0a23ce5..efb5f04c43 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -393,8 +393,6 @@ void Menu::loadSettings(QSettings* settings) { _maxVoxels = loadSetting(settings, "maxVoxels", DEFAULT_MAX_VOXELS_PER_SYSTEM); _maxVoxelPacketsPerSecond = loadSetting(settings, "maxVoxelsPPS", DEFAULT_MAX_VOXEL_PPS); _voxelSizeScale = loadSetting(settings, "voxelSizeScale", DEFAULT_OCTREE_SIZE_SCALE); - _avatarLODDistanceMultiplier = loadSetting(settings, "avatarLODDistanceMultiplier", - DEFAULT_AVATAR_LOD_DISTANCE_MULTIPLIER); _boundaryLevelAdjust = loadSetting(settings, "boundaryLevelAdjust", 0); settings->beginGroup("View Frustum Offset Camera"); @@ -434,7 +432,6 @@ void Menu::saveSettings(QSettings* settings) { settings->setValue("maxVoxels", _maxVoxels); settings->setValue("maxVoxelsPPS", _maxVoxelPacketsPerSecond); settings->setValue("voxelSizeScale", _voxelSizeScale); - settings->setValue("avatarLODDistanceMultiplier", _avatarLODDistanceMultiplier); settings->setValue("boundaryLevelAdjust", _boundaryLevelAdjust); settings->beginGroup("View Frustum Offset Camera"); settings->setValue("viewFrustumOffsetYaw", _viewFrustumOffset.yaw); @@ -1203,7 +1200,10 @@ void Menu::autoAdjustLOD(float currentFPS) { if (now - _lastAvatarDetailDrop > ADJUST_AVATAR_LOD_DOWN_DELAY) { // attempt to lower the detail in proportion to the fps difference float targetFps = (ADJUST_LOD_DOWN_FPS + ADJUST_LOD_UP_FPS) * 0.5f; - _avatarLODDistanceMultiplier *= (targetFps / _fastFPSAverage.getAverage()); + float averageFps = _fastFPSAverage.getAverage(); + const float MAXIMUM_MULTIPLIER_SCALE = 2.0f; + _avatarLODDistanceMultiplier *= (averageFps < EPSILON) ? MAXIMUM_MULTIPLIER_SCALE : + qMin(MAXIMUM_MULTIPLIER_SCALE, targetFps / averageFps); _lastAvatarDetailDrop = now; } } else if (_fastFPSAverage.getAverage() > ADJUST_LOD_UP_FPS) { From eab4b4bc5d945ed2ef73db745970ea599971c2b0 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 14:47:46 -0700 Subject: [PATCH 18/20] Make sure we process the model root nodes in alphabetic order. --- interface/src/renderer/FBXReader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index 53f4e04b0b..6846f79825 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -1259,7 +1259,13 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) remainingModels.insert(model.key()); } while (!remainingModels.isEmpty()) { - QString topID = getTopModelID(parentMap, models, *remainingModels.constBegin()); + QString first = *remainingModels.constBegin(); + foreach (const QString& id, remainingModels) { + if (id < first) { + first = id; + } + } + QString topID = getTopModelID(parentMap, models, first); appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs); } From 077df3906fbc491fd740e2bc99674772a2e26037 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Thu, 27 Mar 2014 15:02:26 -0700 Subject: [PATCH 19/20] Make sure we reset our LOD adjustments before we switch back from throttling the renderer. --- interface/interface_en.ts | 4 ++-- interface/src/Application.cpp | 2 ++ interface/src/Menu.cpp | 6 ++++++ interface/src/Menu.h | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/interface/interface_en.ts b/interface/interface_en.ts index aa7b1951ea..8ca6f7d269 100644 --- a/interface/interface_en.ts +++ b/interface/interface_en.ts @@ -14,12 +14,12 @@ - + Open Script - + JavaScript Files (*.js) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index edeb454f11..5b08997b7c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1636,6 +1636,8 @@ void Application::updateLOD() { // adjust it unless we were asked to disable this feature, or if we're currently in throttleRendering mode if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableAutoAdjustLOD) && !isThrottleRendering()) { Menu::getInstance()->autoAdjustLOD(_fps); + } else { + Menu::getInstance()->resetLODAdjust(); } } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index efb5f04c43..e1764374ea 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -1249,6 +1249,12 @@ void Menu::autoAdjustLOD(float currentFPS) { } } +void Menu::resetLODAdjust() { + _fpsAverage.reset(); + _fastFPSAverage.reset(); + _lastAvatarDetailDrop = _lastAdjust = usecTimestampNow(); +} + void Menu::setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; } diff --git a/interface/src/Menu.h b/interface/src/Menu.h index cab5645304..6b41430eaf 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -85,6 +85,7 @@ public: // User Tweakable LOD Items QString getLODFeedbackText(); void autoAdjustLOD(float currentFPS); + void resetLODAdjust(); void setVoxelSizeScale(float sizeScale); float getVoxelSizeScale() const { return _voxelSizeScale; } float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; } From c540427b9e25a6aac8185455550daab7052a08f3 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 27 Mar 2014 15:03:46 -0700 Subject: [PATCH 20/20] tweaks to _processLock to reduce time it's locked --- .../src/octree/OctreeSendThread.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index 023f3f3d2c..4105b21eb8 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -48,26 +48,28 @@ OctreeSendThread::~OctreeSendThread() { } void OctreeSendThread::setIsShuttingDown() { - QMutexLocker locker(&_processLock); // this will cause us to wait till the process loop is complete _isShuttingDown = true; OctreeServer::stopTrackingThread(this); + + // this will cause us to wait till the process loop is complete, we do this after we change _isShuttingDown + QMutexLocker locker(&_processLock); } bool OctreeSendThread::process() { + if (_isShuttingDown) { + return false; // exit early if we're shutting down + } + OctreeServer::didProcess(this); float lockWaitElapsedUsec = OctreeServer::SKIP_TIME; quint64 lockWaitStart = usecTimestampNow(); - QMutexLocker locker(&_processLock); + _processLock.lock(); quint64 lockWaitEnd = usecTimestampNow(); lockWaitElapsedUsec = (float)(lockWaitEnd - lockWaitStart); OctreeServer::trackProcessWaitTime(lockWaitElapsedUsec); - if (_isShuttingDown) { - return false; // exit early if we're shutting down - } - quint64 start = usecTimestampNow(); // don't do any send processing until the initial load of the octree is complete... @@ -98,6 +100,12 @@ bool OctreeSendThread::process() { } } + _processLock.unlock(); + + if (_isShuttingDown) { + return false; // exit early if we're shutting down + } + // Only sleep if we're still running and we got the lock last time we tried, otherwise try to get the lock asap if (isStillRunning()) { // dynamically sleep until we need to fire off the next set of octree elements