From 33494d7477c63968a23402f96cf9fb983b451544 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 12 Jul 2019 10:30:52 -0700 Subject: [PATCH 01/17] Bump minimum threadpool size to 2 --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9aa2bdbcdb..63e73ca453 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -288,7 +288,7 @@ static const uint32_t MAX_CONCURRENT_RESOURCE_DOWNLOADS = 4; // For processing on QThreadPool, we target a number of threads after reserving some // based on how many are being consumed by the application and the display plugin. However, // we will never drop below the 'min' value -static const int MIN_PROCESSING_THREAD_POOL_SIZE = 1; +static const int MIN_PROCESSING_THREAD_POOL_SIZE = 2; static const QString SNAPSHOT_EXTENSION = ".jpg"; static const QString JPG_EXTENSION = ".jpg"; From a991b6d6195cd2c68e1463eda571093987b74ac8 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 31 Jul 2019 16:59:21 -0700 Subject: [PATCH 02/17] BUGZ-1046 - send entity packets reliably for initial query --- assignment-client/src/entities/EntityTreeSendThread.cpp | 5 +++++ assignment-client/src/octree/OctreeSendThread.cpp | 9 +++++---- libraries/networking/src/udt/Packet.h | 1 + libraries/octree/src/OctreeQueryNode.cpp | 2 +- libraries/octree/src/OctreeSceneStats.h | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index 2b964ae674..f00813b006 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -160,6 +160,11 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O if (sendComplete && nodeData->wantReportInitialCompletion() && _traversal.finished()) { // Dealt with all nearby entities. nodeData->setReportInitialCompletion(false); + // initial stats and entity packets are reliable until the initial query is complete + // to guarantee all entity data is available for safe landing/physics start. Afterwards + // the packets are unreliable for performance. + nodeData->stats.getStatsMessage().setReliable(false); + nodeData->getPacket().setReliable(false); // Send EntityQueryInitialResultsComplete reliable packet ... auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp index af36df1eba..cf09a10e98 100644 --- a/assignment-client/src/octree/OctreeSendThread.cpp +++ b/assignment-client/src/octree/OctreeSendThread.cpp @@ -194,13 +194,13 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // actually send it OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(statsPacket, *node); + DependencyManager::get()->sendPacket(NLPacket::createCopy(statsPacket), *node); } else { // not enough room in the packet, send two packets // first packet OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(statsPacket, *node); + DependencyManager::get()->sendPacket(NLPacket::createCopy(statsPacket), *node); int numBytes = statsPacket.getDataSize(); _totalBytes += numBytes; @@ -230,7 +230,7 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // second packet OctreeServer::didCallWriteDatagram(this); - DependencyManager::get()->sendUnreliablePacket(sentPacket, *node); + DependencyManager::get()->sendPacket(NLPacket::createCopy(sentPacket), *node); numBytes = sentPacket.getDataSize(); _totalBytes += numBytes; @@ -263,7 +263,8 @@ int OctreeSendThread::handlePacketSend(SharedNodePointer node, OctreeQueryNode* // just send the octree packet OctreeServer::didCallWriteDatagram(this); NLPacket& sentPacket = nodeData->getPacket(); - DependencyManager::get()->sendUnreliablePacket(sentPacket, *node); + + DependencyManager::get()->sendPacket(NLPacket::createCopy(sentPacket), *node); int numBytes = sentPacket.getDataSize(); _totalBytes += numBytes; diff --git a/libraries/networking/src/udt/Packet.h b/libraries/networking/src/udt/Packet.h index cad5cccb0e..36098235f9 100644 --- a/libraries/networking/src/udt/Packet.h +++ b/libraries/networking/src/udt/Packet.h @@ -83,6 +83,7 @@ public: bool isPartOfMessage() const { return _isPartOfMessage; } bool isReliable() const { return _isReliable; } + void setReliable(bool reliable) { _isReliable = reliable; } ObfuscationLevel getObfuscationLevel() const { return _obfuscationLevel; } SequenceNumber getSequenceNumber() const { return _sequenceNumber; } diff --git a/libraries/octree/src/OctreeQueryNode.cpp b/libraries/octree/src/OctreeQueryNode.cpp index 0c1c108f68..5bce547a34 100644 --- a/libraries/octree/src/OctreeQueryNode.cpp +++ b/libraries/octree/src/OctreeQueryNode.cpp @@ -82,7 +82,7 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() { void OctreeQueryNode::init() { _myPacketType = getMyPacketType(); - _octreePacket = NLPacket::create(getMyPacketType()); + _octreePacket = NLPacket::create(getMyPacketType(), -1, true); resetOctreePacket(); // don't bump sequence } diff --git a/libraries/octree/src/OctreeSceneStats.h b/libraries/octree/src/OctreeSceneStats.h index e1228609a6..f78e51081b 100644 --- a/libraries/octree/src/OctreeSceneStats.h +++ b/libraries/octree/src/OctreeSceneStats.h @@ -169,7 +169,7 @@ private: bool _isReadyToSend; - std::unique_ptr _statsPacket = NLPacket::create(PacketType::OctreeStats); + std::unique_ptr _statsPacket = NLPacket::create(PacketType::OctreeStats, -1, true); // scene timing data in usecs bool _isStarted; From ccfe6bda63eae782e602a9d9881ff886940450d9 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Mon, 5 Aug 2019 10:34:03 -0700 Subject: [PATCH 03/17] Detect clipping prior to echo cancellation (overload of the uncancelled raw input will cause echo leakage!) --- libraries/audio-client/src/AudioClient.cpp | 52 +++++++++++----------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index c16e297c28..99add0f797 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -170,27 +170,21 @@ static void channelDownmix(int16_t* source, int16_t* dest, int numSamples) { } } -static float computeLoudness(int16_t* samples, int numSamples, int numChannels, bool& isClipping) { +static bool detectClipping(int16_t* samples, int numSamples, int numChannels) { const int32_t CLIPPING_THRESHOLD = 32392; // -0.1 dBFS - const int32_t CLIPPING_DETECTION = 3; // consecutive samples over threshold + const int CLIPPING_DETECTION = 3; // consecutive samples over threshold - float scale = numSamples ? 1.0f / numSamples : 0.0f; - - int32_t loudness = 0; - isClipping = false; + bool isClipping = false; if (numChannels == 2) { - int32_t oversLeft = 0; - int32_t oversRight = 0; + int oversLeft = 0; + int oversRight = 0; for (int i = 0; i < numSamples/2; i++) { int32_t left = std::abs((int32_t)samples[2*i+0]); int32_t right = std::abs((int32_t)samples[2*i+1]); - loudness += left; - loudness += right; - if (left > CLIPPING_THRESHOLD) { isClipping |= (++oversLeft >= CLIPPING_DETECTION); } else { @@ -203,13 +197,11 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels, } } } else { - int32_t overs = 0; + int overs = 0; for (int i = 0; i < numSamples; i++) { int32_t sample = std::abs((int32_t)samples[i]); - loudness += sample; - if (sample > CLIPPING_THRESHOLD) { isClipping |= (++overs >= CLIPPING_DETECTION); } else { @@ -218,6 +210,17 @@ static float computeLoudness(int16_t* samples, int numSamples, int numChannels, } } + return isClipping; +} + +static float computeLoudness(int16_t* samples, int numSamples) { + + float scale = numSamples ? 1.0f / numSamples : 0.0f; + + int32_t loudness = 0; + for (int i = 0; i < numSamples; i++) { + loudness += std::abs((int32_t)samples[i]); + } return (float)loudness * scale; } @@ -1393,6 +1396,15 @@ void AudioClient::handleMicAudioInput() { _inputRingBuffer.readSamples(inputAudioSamples.get(), inputSamplesRequired); + // detect clipping on the raw input + bool isClipping = detectClipping(inputAudioSamples.get(), inputSamplesRequired, _inputFormat.channelCount()); + if (isClipping) { + _timeSinceLastClip = 0.0f; + } else if (_timeSinceLastClip >= 0.0f) { + _timeSinceLastClip += AudioConstants::NETWORK_FRAME_SECS; + } + isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time + #if defined(WEBRTC_ENABLED) if (_isAECEnabled) { processWebrtcNearEnd(inputAudioSamples.get(), inputSamplesRequired / _inputFormat.channelCount(), @@ -1400,9 +1412,7 @@ void AudioClient::handleMicAudioInput() { } #endif - // detect loudness and clipping on the raw input - bool isClipping = false; - float loudness = computeLoudness(inputAudioSamples.get(), inputSamplesRequired, _inputFormat.channelCount(), isClipping); + float loudness = computeLoudness(inputAudioSamples.get(), inputSamplesRequired); _lastRawInputLoudness = loudness; // envelope detection @@ -1410,14 +1420,6 @@ void AudioClient::handleMicAudioInput() { loudness += tc * (_lastSmoothedRawInputLoudness - loudness); _lastSmoothedRawInputLoudness = loudness; - // clipping indicator - if (isClipping) { - _timeSinceLastClip = 0.0f; - } else if (_timeSinceLastClip >= 0.0f) { - _timeSinceLastClip += AudioConstants::NETWORK_FRAME_SECS; - } - isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time - emit inputLoudnessChanged(_lastSmoothedRawInputLoudness, isClipping); if (!_muted) { From ee3b91f465994a684a98fc1b2f0d8de3990047fb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 5 Aug 2019 20:02:58 -0700 Subject: [PATCH 04/17] Fix vcpkg pre-requisites build for devs using VS2019 --- hifi_vcpkg.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py index 764a6270bd..821d9ae0b7 100644 --- a/hifi_vcpkg.py +++ b/hifi_vcpkg.py @@ -71,16 +71,19 @@ endif() if 'Windows' == system: self.exe = os.path.join(self.path, 'vcpkg.exe') + self.bootstrapCmd = 'bootstrap-vcpkg.bat' self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-win32.tar.gz?versionId=YZYkDejDRk7L_hrK_WVFthWvisAhbDzZ' self.vcpkgHash = '3e0ff829a74956491d57666109b3e6b5ce4ed0735c24093884317102387b2cb1b2cd1ff38af9ed9173501f6e32ffa05cc6fe6d470b77a71ca1ffc3e0aa46ab9e' self.hostTriplet = 'x64-windows' elif 'Darwin' == system: self.exe = os.path.join(self.path, 'vcpkg') + self.bootstrapCmd = 'bootstrap-vcpkg.sh' self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-osx.tar.gz?versionId=_fhqSxjfrtDJBvEsQ8L_ODcdUjlpX9cc' self.vcpkgHash = '519d666d02ef22b87c793f016ca412e70f92e1d55953c8f9bd4ee40f6d9f78c1df01a6ee293907718f3bbf24075cc35492fb216326dfc50712a95858e9cbcb4d' self.hostTriplet = 'x64-osx' else: self.exe = os.path.join(self.path, 'vcpkg') + self.bootstrapCmd = 'bootstrap-vcpkg.sh' self.vcpkgUrl = 'https://hifi-public.s3.amazonaws.com/dependencies/vcpkg/vcpkg-linux.tar.gz?versionId=97Nazh24etEVKWz33XwgLY0bvxEfZgMU' self.vcpkgHash = '6a1ce47ef6621e699a4627e8821ad32528c82fce62a6939d35b205da2d299aaa405b5f392df4a9e5343dd6a296516e341105fbb2dd8b48864781d129d7fba10d' self.hostTriplet = 'x64-linux' @@ -141,8 +144,14 @@ endif() downloadVcpkg = True if downloadVcpkg: - print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path)) - hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash) + if "HIFI_VCPKG_BOOTSTRAP" in os.environ: + print("Cloning vcpkg from github to {}".format(self.path)) + hifi_utils.executeSubprocess(['git', 'clone', 'git@github.com:microsoft/vcpkg.git', self.path]) + print("Bootstrapping vcpkg") + hifi_utils.executeSubprocess([self.bootstrapCmd], folder=self.path) + else: + print("Fetching vcpkg from {} to {}".format(self.vcpkgUrl, self.path)) + hifi_utils.downloadAndExtract(self.vcpkgUrl, self.path, self.vcpkgHash) print("Replacing port files") portsPath = os.path.join(self.path, 'ports') From 0f6ad4353117cb7e53a9aaa82828e0db873a0ec2 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 6 Aug 2019 15:34:13 -0700 Subject: [PATCH 05/17] Set desktop reserved threads to 1; HMD reserved threads to 2 --- .../display-plugins/src/display-plugins/OpenGLDisplayPlugin.h | 3 ++- plugins/oculus/src/OculusBaseDisplayPlugin.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index 5eebd92fba..a8d7847c09 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -78,7 +78,8 @@ public: void setVsyncEnabled(bool vsyncEnabled) { _vsyncEnabled = vsyncEnabled; } bool isVsyncEnabled() const { return _vsyncEnabled; } // Three threads, one for rendering, one for texture transfers, one reserved for the GL driver - int getRequiredThreadCount() const override { return 3; } + // Drop to one reserved for better other-task performance in desktop + int getRequiredThreadCount() const override { return 1; } virtual std::function getHUDOperator() override; void copyTextureToQuickFramebuffer(NetworkTexturePointer source, diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index d442c365ae..0805623b76 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -38,6 +38,8 @@ public: virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; + // Attempt to reserve two threads. + int getRequiredThreadCount() const override { return 2; } protected: void customizeContext() override; From 609b533cf272585ebb8df11ac9dbf0948a5513c3 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 6 Aug 2019 17:22:30 -0700 Subject: [PATCH 06/17] Fix create duplication key conflicting with camera orbit --- scripts/system/libraries/entityCameraTool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/libraries/entityCameraTool.js b/scripts/system/libraries/entityCameraTool.js index 4410f19a5e..2968b8e903 100644 --- a/scripts/system/libraries/entityCameraTool.js +++ b/scripts/system/libraries/entityCameraTool.js @@ -355,7 +355,7 @@ CameraManager = function() { return; } - if (event.isRightButton || (event.isLeftButton && event.isControl && !event.isShifted)) { + if (event.isRightButton || (event.isLeftButton && event.isAlt && !event.isShifted)) { that.mode = MODE_ORBIT; } else if (event.isMiddleButton || (event.isLeftButton && event.isControl && event.isShifted)) { that.mode = MODE_PAN; From 0b72e4dc6061669d0e84973dfd09486d92e0ff8f Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 7 Aug 2019 13:18:39 -0700 Subject: [PATCH 07/17] Make XML HTTP requests script owned This was causing them to leak every time --- libraries/script-engine/src/XMLHttpRequestClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index a74d185c6a..19294710a4 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -57,7 +57,7 @@ XMLHttpRequestClass::~XMLHttpRequestClass() { } QScriptValue XMLHttpRequestClass::constructor(QScriptContext* context, QScriptEngine* engine) { - return engine->newQObject(new XMLHttpRequestClass(engine)); + return engine->newQObject(new XMLHttpRequestClass(engine), QScriptEngine::ScriptOwnership); } QScriptValue XMLHttpRequestClass::getStatus() const { From ec9d13700db7511f5df9843fdd1bc39e55f9ac32 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 7 Aug 2019 13:19:53 -0700 Subject: [PATCH 08/17] Break JS circular ref to help garbage collect --- scripts/modules/request.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/modules/request.js b/scripts/modules/request.js index 37f3ac0d7b..31a7a4a1f8 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -40,6 +40,9 @@ module.exports = { response = { statusCode: httpRequest.status }; } callback(error, response, optionalCallbackParameter); + + // Break circular reference to httpRequest so the engine can garbage collect it. + httpRequest.onreadystatechange = null; } }; if (typeof options === 'string') { From b2704da9efa6a4cae8d01157995d16293d70f831 Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 7 Aug 2019 15:03:01 -0700 Subject: [PATCH 09/17] Don't manage _sendData's memory --- .../script-engine/src/XMLHttpRequestClass.cpp | 33 ++++++------------- .../script-engine/src/XMLHttpRequestClass.h | 20 +++++------ 2 files changed, 20 insertions(+), 33 deletions(-) diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index 19294710a4..51e3ef0759 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -30,30 +30,14 @@ Q_DECLARE_METATYPE(QByteArray*) XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : _engine(engine), - _async(true), - _url(), - _method(""), - _responseType(""), - _request(), - _reply(NULL), - _sendData(NULL), - _rawResponseData(), - _responseData(""), - _onTimeout(QScriptValue::NullValue), - _onReadyStateChange(QScriptValue::NullValue), - _readyState(XMLHttpRequestClass::UNSENT), - _errorCode(QNetworkReply::NoError), - _timeout(0), - _timer(this), - _numRedirects(0) { + _timer(this) { _request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); _timer.setSingleShot(true); } XMLHttpRequestClass::~XMLHttpRequestClass() { - if (_reply) { delete _reply; } - if (_sendData) { delete _sendData; } + if (_reply) { _reply->deleteLater(); } } QScriptValue XMLHttpRequestClass::constructor(QScriptContext* context, QScriptEngine* engine) { @@ -169,13 +153,12 @@ void XMLHttpRequestClass::send() { void XMLHttpRequestClass::send(const QScriptValue& data) { if (_readyState == OPENED && !_reply) { + if (!data.isNull()) { - _sendData = new QBuffer(this); if (data.isObject()) { - QByteArray ba = qscriptvalue_cast(data); - _sendData->setData(ba); + _sendData = qscriptvalue_cast(data); } else { - _sendData->setData(data.toString().toUtf8()); + _sendData = data.toString().toUtf8(); } } @@ -235,6 +218,10 @@ void XMLHttpRequestClass::requestFinished() { } } + disconnectFromReply(_reply); + _reply->deleteLater(); + _reply = nullptr; + setReadyState(DONE); emit requestComplete(); } @@ -246,7 +233,7 @@ void XMLHttpRequestClass::abortRequest() { disconnectFromReply(_reply); _reply->abort(); _reply->deleteLater(); - _reply = NULL; + _reply = nullptr; } } diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index c79859e895..d7f3c2e059 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -98,23 +98,23 @@ private: void disconnectFromReply(QNetworkReply* reply); void abortRequest(); - QScriptEngine* _engine; - bool _async; + QScriptEngine* _engine { nullptr }; + bool _async { true }; QUrl _url; QString _method; QString _responseType; QNetworkRequest _request; - QNetworkReply* _reply; - QBuffer* _sendData; + QNetworkReply* _reply { nullptr }; + QByteArray _sendData; QByteArray _rawResponseData; QScriptValue _responseData; - QScriptValue _onTimeout; - QScriptValue _onReadyStateChange; - ReadyState _readyState; - QNetworkReply::NetworkError _errorCode; - int _timeout; + QScriptValue _onTimeout { QScriptValue::NullValue }; + QScriptValue _onReadyStateChange { QScriptValue::NullValue }; + ReadyState _readyState { XMLHttpRequestClass::UNSENT }; + QNetworkReply::NetworkError _errorCode { QNetworkReply::NoError }; + int _timeout { 0 }; QTimer _timer; - int _numRedirects; + int _numRedirects { 0 }; private slots: void requestFinished(); From 74eacf23460a3cef407863327bbe9fd7ac6b3d43 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 7 Aug 2019 15:08:44 -0700 Subject: [PATCH 10/17] Make sure random switch does not repeat previous state --- libraries/animation/src/AnimNodeLoader.cpp | 3 -- libraries/animation/src/AnimRandomSwitch.cpp | 34 ++++++++++++++------ libraries/animation/src/AnimRandomSwitch.h | 3 +- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/libraries/animation/src/AnimNodeLoader.cpp b/libraries/animation/src/AnimNodeLoader.cpp index 6828ed1c7e..d43351fff9 100644 --- a/libraries/animation/src/AnimNodeLoader.cpp +++ b/libraries/animation/src/AnimNodeLoader.cpp @@ -943,9 +943,6 @@ bool processRandomSwitchStateMachineNode(AnimNode::Pointer node, const QJsonObje } auto randomStatePtr = std::make_shared(id, iter->second, interpTarget, interpDuration, interpTypeEnum, easingTypeEnum, priority, resume); - if (priority > 0.0f) { - smNode->addToPrioritySum(priority); - } assert(randomStatePtr); if (!interpTargetVar.isEmpty()) { diff --git a/libraries/animation/src/AnimRandomSwitch.cpp b/libraries/animation/src/AnimRandomSwitch.cpp index edc8c8dd96..92d6edb36f 100644 --- a/libraries/animation/src/AnimRandomSwitch.cpp +++ b/libraries/animation/src/AnimRandomSwitch.cpp @@ -27,22 +27,34 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co AnimRandomSwitch::RandomSwitchState::Pointer desiredState = _currentState; if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1 || animVars.lookup(_triggerRandomSwitchVar, false)) { - // get a random number and decide which motion to choose. + // filter states different to the last random state and with priorities. bool currentStateHasPriority = false; - float dice = randFloatInRange(0.0f, 1.0f); - float lowerBound = 0.0f; - for (const RandomSwitchState::Pointer& randState : _randomStates) { + std::vector randomStatesToConsider; + float totalPriorities = 0.0f; + for (int i = 0; i < _randomStates.size(); i++) { + auto randState = _randomStates[i]; if (randState->getPriority() > 0.0f) { - float upperBound = lowerBound + (randState->getPriority() / _totalPriorities); - if ((dice > lowerBound) && (dice < upperBound)) { - desiredState = randState; + bool isRepeatingClip = _children[randState->getChildIndex()]->getID() == _lastPlayedState; + if (!isRepeatingClip) { + randomStatesToConsider.push_back(randState); + totalPriorities += randState->getPriority(); } - lowerBound = upperBound; - // this indicates if the curent state is one that can be selected randomly, or is one that was transitioned to by the random duration timer. currentStateHasPriority = currentStateHasPriority || (_currentState == randState); } } + // get a random number and decide which motion to choose. + float dice = randFloatInRange(0.0f, 1.0f); + float lowerBound = 0.0f; + for (int i = 0; i < randomStatesToConsider.size(); i++) { + auto randState = randomStatesToConsider[i]; + float upperBound = lowerBound + (randState->getPriority() / totalPriorities); + if ((dice > lowerBound) && (dice < upperBound)) { + desiredState = randState; + break; + } + lowerBound = upperBound; + } if (abs(_randomSwitchEvaluationCount - context.getEvaluationCount()) > 1) { _duringInterp = false; switchRandomState(animVars, context, desiredState, _duringInterp); @@ -155,8 +167,10 @@ void AnimRandomSwitch::addState(RandomSwitchState::Pointer randomState) { } void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const AnimContext& context, RandomSwitchState::Pointer desiredState, bool shouldInterp) { - auto nextStateNode = _children[desiredState->getChildIndex()]; + if (nextStateNode->getType() == AnimNodeType::Clip) { + _lastPlayedState = nextStateNode->getID(); + } if (shouldInterp) { const float FRAMES_PER_SECOND = 30.0f; diff --git a/libraries/animation/src/AnimRandomSwitch.h b/libraries/animation/src/AnimRandomSwitch.h index 7c185dd7cb..888ed1f6b5 100644 --- a/libraries/animation/src/AnimRandomSwitch.h +++ b/libraries/animation/src/AnimRandomSwitch.h @@ -143,7 +143,6 @@ protected: void setTransitionVar(const QString& transitionVar) { _transitionVar = transitionVar; } void setTriggerTimeMin(float triggerTimeMin) { _triggerTimeMin = triggerTimeMin; } void setTriggerTimeMax(float triggerTimeMax) { _triggerTimeMax = triggerTimeMax; } - void addToPrioritySum(float priority) { _totalPriorities += priority; } void addState(RandomSwitchState::Pointer randomState); @@ -164,7 +163,6 @@ protected: float _alpha = 0.0f; AnimPoseVec _prevPoses; AnimPoseVec _nextPoses; - float _totalPriorities { 0.0f }; RandomSwitchState::Pointer _currentState; RandomSwitchState::Pointer _previousState; @@ -179,6 +177,7 @@ protected: float _randomSwitchTimeMin { 10.0f }; float _randomSwitchTimeMax { 20.0f }; float _randomSwitchTime { 0.0f }; + QString _lastPlayedState; private: // no copies From cff4f9da19ac5d1160c85e38d6000e6da897d921 Mon Sep 17 00:00:00 2001 From: Matt Hardcastle Date: Sat, 3 Aug 2019 14:07:25 -0700 Subject: [PATCH 11/17] Use execve to "launch" Interface on macOS Co-authored-by: dante ruiz --- launchers/darwin/CMakeLists.txt | 2 + launchers/darwin/src/Launcher.m | 34 +++++---- .../darwin/src/NSTask+NSTaskExecveAdditions.h | 9 +++ .../darwin/src/NSTask+NSTaskExecveAdditions.m | 73 +++++++++++++++++++ 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 launchers/darwin/src/NSTask+NSTaskExecveAdditions.h create mode 100644 launchers/darwin/src/NSTask+NSTaskExecveAdditions.m diff --git a/launchers/darwin/CMakeLists.txt b/launchers/darwin/CMakeLists.txt index fe7f4298ce..f71976960e 100644 --- a/launchers/darwin/CMakeLists.txt +++ b/launchers/darwin/CMakeLists.txt @@ -43,6 +43,8 @@ set(src_files src/LaunchInterface.h src/CustomUI.h src/CustomUI.m + src/NSTask+NSTaskExecveAdditions.h + src/NSTask+NSTaskExecveAdditions.m src/main.mm nib/Window.xib nib/SplashScreen.xib diff --git a/launchers/darwin/src/Launcher.m b/launchers/darwin/src/Launcher.m index 38027d6fd3..8fb501db55 100644 --- a/launchers/darwin/src/Launcher.m +++ b/launchers/darwin/src/Launcher.m @@ -7,6 +7,7 @@ #import "ProcessScreen.h" #import "ErrorViewController.h" #import "Settings.h" +#import "NSTask+NSTaskExecveAdditions.h" @interface Launcher () @@ -456,8 +457,6 @@ static BOOL const DELETE_ZIP_FILES = TRUE; NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; NSURL *url = [NSURL fileURLWithPath:[workspace fullPathForApplication:[[self getAppPath] stringByAppendingString:@"interface.app/Contents/MacOS/interface"]]]; - NSError *error = nil; - NSString* contentPath = [[self getDownloadPathForContentAndScripts] stringByAppendingString:@"content"]; NSString* displayName = [ self displayName]; NSString* scriptsPath = [[self getAppPath] stringByAppendingString:@"interface.app/Contents/Resources/scripts/simplifiedUIBootstrapper.js"]; @@ -484,13 +483,11 @@ static BOOL const DELETE_ZIP_FILES = TRUE; @"--no-updater", @"--no-launcher", nil]; } - [workspace launchApplicationAtURL:url options:NSWorkspaceLaunchNewInstance configuration:[NSDictionary dictionaryWithObject:arguments forKey:NSWorkspaceLaunchConfigurationArguments] error:&error]; - [NSTimer scheduledTimerWithTimeInterval: 3.0 - target: self - selector: @selector(exitLauncher:) - userInfo:nil - repeats: NO]; + NSTask *task = [[NSTask alloc] init]; + task.launchPath = [url path]; + task.arguments = arguments; + [task replaceThisProcess]; } - (ProcessState) currentProccessState @@ -500,15 +497,20 @@ static BOOL const DELETE_ZIP_FILES = TRUE; - (void) callLaunchInterface:(NSTimer*) timer { + NSWindow* mainWindow = [[[NSApplication sharedApplication] windows] objectAtIndex:0]; + ProcessScreen* processScreen = [[ProcessScreen alloc] initWithNibName:@"ProcessScreen" bundle:nil]; - [[[[NSApplication sharedApplication] windows] objectAtIndex:0] setContentViewController: processScreen]; - [self launchInterface]; -} - - -- (void) exitLauncher:(NSTimer*) timer -{ - [NSApp terminate:self]; + [mainWindow setContentViewController: processScreen]; + @try + { + [self launchInterface]; + } + @catch (NSException *exception) + { + NSLog(@"Caught exception: Name: %@, Reason: %@", exception.name, exception.reason); + ErrorViewController* errorViewController = [[ErrorViewController alloc] initWithNibName:@"ErrorScreen" bundle:nil]; + [mainWindow setContentViewController: errorViewController]; + } } @end diff --git a/launchers/darwin/src/NSTask+NSTaskExecveAdditions.h b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.h new file mode 100644 index 0000000000..f26a4021de --- /dev/null +++ b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.h @@ -0,0 +1,9 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSTask (NSTaskExecveAdditions) +- (void) replaceThisProcess; +@end + +NS_ASSUME_NONNULL_END diff --git a/launchers/darwin/src/NSTask+NSTaskExecveAdditions.m b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.m new file mode 100644 index 0000000000..f204d065fa --- /dev/null +++ b/launchers/darwin/src/NSTask+NSTaskExecveAdditions.m @@ -0,0 +1,73 @@ +#import "NSTask+NSTaskExecveAdditions.h" + +#import + +char ** +toCArray(NSArray *array) +{ + // Add one to count to accommodate the NULL that terminates the array. + char **cArray = (char **) calloc([array count] + 1, sizeof(char *)); + if (cArray == NULL) { + NSException *exception = [NSException + exceptionWithName:@"MemoryException" + reason:@"malloc failed" + userInfo:nil]; + @throw exception; + } + char *str; + for (int i = 0; i < [array count]; i++) { + str = (char *) [array[i] UTF8String]; + if (str == NULL) { + NSException *exception = [NSException + exceptionWithName:@"NULLStringException" + reason:@"UTF8String was NULL" + userInfo:nil]; + @throw exception; + } + if (asprintf(&cArray[i], "%s", str) == -1) { + for (int j = 0; j < i; j++) { + free(cArray[j]); + } + free(cArray); + NSException *exception = [NSException + exceptionWithName:@"MemoryException" + reason:@"malloc failed" + userInfo:nil]; + @throw exception; + } + } + return cArray; +} + +@implementation NSTask (NSTaskExecveAdditions) + +- (void) replaceThisProcess { + char **args = toCArray([@[[self launchPath]] arrayByAddingObjectsFromArray:[self arguments]]); + + NSMutableArray *env = [[NSMutableArray alloc] init]; + NSDictionary* environvment = [[NSProcessInfo processInfo] environment]; + for (NSString* key in environvment) { + NSString* environmentVariable = [[key stringByAppendingString:@"="] stringByAppendingString:environvment[key]]; + [env addObject:environmentVariable]; + } + + char** envp = toCArray(env); + // `execve` replaces the current process with `path`. + // It will only return if it fails to replace the current process. + chdir(dirname(args[0])); + execve(args[0], (char * const *)args, envp); + + // If we're here `execve` failed. :( + for (int i = 0; i < [[self arguments] count]; i++) { + free((void *) args[i]); + } + free((void *) args); + + NSException *exception = [NSException + exceptionWithName:@"ExecveException" + reason:[NSString stringWithFormat:@"couldn't execve: %s", strerror(errno)] + userInfo:nil]; + @throw exception; +} + +@end From 473af995d6d371941d4d564696aebe5fb71f1e80 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 7 Aug 2019 16:53:08 -0700 Subject: [PATCH 12/17] Fix warnings --- libraries/animation/src/AnimRandomSwitch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimRandomSwitch.cpp b/libraries/animation/src/AnimRandomSwitch.cpp index 92d6edb36f..45e6ae0050 100644 --- a/libraries/animation/src/AnimRandomSwitch.cpp +++ b/libraries/animation/src/AnimRandomSwitch.cpp @@ -31,7 +31,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co bool currentStateHasPriority = false; std::vector randomStatesToConsider; float totalPriorities = 0.0f; - for (int i = 0; i < _randomStates.size(); i++) { + for (size_t i = 0; i < _randomStates.size(); i++) { auto randState = _randomStates[i]; if (randState->getPriority() > 0.0f) { bool isRepeatingClip = _children[randState->getChildIndex()]->getID() == _lastPlayedState; @@ -46,7 +46,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co // get a random number and decide which motion to choose. float dice = randFloatInRange(0.0f, 1.0f); float lowerBound = 0.0f; - for (int i = 0; i < randomStatesToConsider.size(); i++) { + for (size_t i = 0; i < randomStatesToConsider.size(); i++) { auto randState = randomStatesToConsider[i]; float upperBound = lowerBound + (randState->getPriority() / totalPriorities); if ((dice > lowerBound) && (dice < upperBound)) { From 3a22db0a13bcb6f22249960d8d4dba09eb722ccb Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 8 Aug 2019 06:25:04 -0700 Subject: [PATCH 13/17] Last state for all node types --- libraries/animation/src/AnimRandomSwitch.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/animation/src/AnimRandomSwitch.cpp b/libraries/animation/src/AnimRandomSwitch.cpp index 45e6ae0050..8b99568f07 100644 --- a/libraries/animation/src/AnimRandomSwitch.cpp +++ b/libraries/animation/src/AnimRandomSwitch.cpp @@ -168,9 +168,7 @@ void AnimRandomSwitch::addState(RandomSwitchState::Pointer randomState) { void AnimRandomSwitch::switchRandomState(const AnimVariantMap& animVars, const AnimContext& context, RandomSwitchState::Pointer desiredState, bool shouldInterp) { auto nextStateNode = _children[desiredState->getChildIndex()]; - if (nextStateNode->getType() == AnimNodeType::Clip) { - _lastPlayedState = nextStateNode->getID(); - } + _lastPlayedState = nextStateNode->getID(); if (shouldInterp) { const float FRAMES_PER_SECOND = 30.0f; From 597cff335adfcc00a161f5f8db26647d033beaaa Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 8 Aug 2019 08:40:00 -0700 Subject: [PATCH 14/17] Reserve vector size --- libraries/animation/src/AnimRandomSwitch.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/animation/src/AnimRandomSwitch.cpp b/libraries/animation/src/AnimRandomSwitch.cpp index 8b99568f07..3cf402cc14 100644 --- a/libraries/animation/src/AnimRandomSwitch.cpp +++ b/libraries/animation/src/AnimRandomSwitch.cpp @@ -30,6 +30,7 @@ const AnimPoseVec& AnimRandomSwitch::evaluate(const AnimVariantMap& animVars, co // filter states different to the last random state and with priorities. bool currentStateHasPriority = false; std::vector randomStatesToConsider; + randomStatesToConsider.reserve(_randomStates.size()); float totalPriorities = 0.0f; for (size_t i = 0; i < _randomStates.size(); i++) { auto randState = _randomStates[i]; From 03a4a58697aace8f65c5e1984579ce7a32f6a9b0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 8 Aug 2019 10:07:40 -0700 Subject: [PATCH 15/17] PR feedback --- BUILD_WIN.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 2faa9809bb..bc6e5c8386 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -5,7 +5,7 @@ If you are upgrading from previous versions, do a clean uninstall of those versi Note: The prerequisites will require about 10 GB of space on your drive. You will also need a system with at least 8GB of main memory. -### Step 1. Visual Studio & Python +### Step 1. Visual Studio & Python 3.x If you don’t have Community or Professional edition of Visual Studio, download [Visual Studio Community 2019](https://visualstudio.microsoft.com/vs/). If you have Visual Studio 2017, you are not required to download Visual Studio 2019. @@ -21,7 +21,7 @@ When selecting components, check "Desktop development with C++". On the right on * MSVC v141 - VS 2017 C++ x64/x86 build tools * MSVC v140 - VS 2015 C++ build tools (v14.00) -If you do not already have a Python development environment installed, also check "Python Development" in this screen. +If you do not already have a Python 3.x development environment installed, also check "Python Development" in this screen. If you already have Visual Studio installed and need to add Python, open the "Add or remove programs" control panel and find the "Microsoft Visual Studio Installer". Select it and click "Modify". In the installer, select "Modify" again, then check "Python Development" and allow the installer to apply the changes. @@ -31,9 +31,10 @@ If you do not wish to use the Python installation bundled with Visual Studio, yo ### Step 2. Installing CMake -Download and install the latest version of CMake 3.14. +Download and install the latest version of CMake 3.15. + * Note that earlier versions of CMake will work, but there is a specific bug related to the interaction of Visual Studio 2019 and CMake versions prior to 3.15 that will cause Visual Studio to rebuild far more than it needs to on every build -Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.14 Version page](https://cmake.org/files/v3.14/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted. +Download the file named win64-x64 Installer from the [CMake Website](https://cmake.org/download/). You can access the installer on this [3.15 Version page](https://cmake.org/files/v3.15/). During installation, make sure to check "Add CMake to system PATH for all users" when prompted. ### Step 3. Create VCPKG environment variable In the next step, you will use CMake to build High Fidelity. By default, the CMake process builds dependency files in Windows' `%TEMP%` directory, which is periodically cleared by the operating system. To prevent you from having to re-build the dependencies in the event that Windows clears that directory, we recommend that you create a `HIFI_VCPKG_BASE` environment variable linked to a directory somewhere on your machine. That directory will contain all dependency files until you manually remove them. @@ -42,9 +43,18 @@ To create this variable: * Naviagte to 'Edit the System Environment Variables' Through the start menu. * Click on 'Environment Variables' * Select 'New' -* Set "Variable name" to HIFI_VCPKG_BASE +* Set "Variable name" to `HIFI_VCPKG_BASE` * Set "Variable value" to any directory that you have control over. +Additionally, if you have Visual Studio 2019 installed and _only_ Visual Studio 2019 (i.e. you do not have Visual Studio 2017 installed) you must add an additional environment variable `HIFI_VCPKG_BOOTSTRAP` that will fix a bug in our `vcpkg` pre-build step. + +To create this variable: +* Naviagte to 'Edit the System Environment Variables' Through the start menu. +* Click on 'Environment Variables' +* Select 'New' +* Set "Variable name" to `HIFI_VCPKG_BOOTSTRAP` +* Set "Variable value" to `1` + ### Step 4. Running CMake to Generate Build Files Run Command Prompt from Start and run the following commands: From 99e1178d9c583293117c7feff4c4131a7f7d678b Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 8 Aug 2019 11:14:02 -0700 Subject: [PATCH 16/17] Move getRequiredThreadCount override from OculusBaseDisplayPlugin to HmdDisplayPlugin --- .../display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h | 2 ++ plugins/oculus/src/OculusBaseDisplayPlugin.h | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index 5317ec54da..36810681ad 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -53,6 +53,8 @@ public: void updateVisionSqueezeParameters(float visionSqueezeX, float visionSqueezeY, float visionSqueezeTransition, int visionSqueezePerEye, float visionSqueezeGroundPlaneY, float visionSqueezeSpotlightSize); + // Attempt to reserve two threads. + int getRequiredThreadCount() const override { return 2; } signals: void hmdMountedChanged(); diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index 0805623b76..d442c365ae 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -38,8 +38,6 @@ public: virtual StencilMaskMode getStencilMaskMode() const override { return StencilMaskMode::MESH; } virtual StencilMaskMeshOperator getStencilMaskMeshOperator() override; - // Attempt to reserve two threads. - int getRequiredThreadCount() const override { return 2; } protected: void customizeContext() override; From 29d017bb65357544d0814384fc846466d5b8b060 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 8 Aug 2019 13:49:15 -0700 Subject: [PATCH 17/17] CR --- scripts/modules/request.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/modules/request.js b/scripts/modules/request.js index 31a7a4a1f8..48c9913bd6 100644 --- a/scripts/modules/request.js +++ b/scripts/modules/request.js @@ -39,10 +39,11 @@ module.exports = { if (error) { response = { statusCode: httpRequest.status }; } - callback(error, response, optionalCallbackParameter); // Break circular reference to httpRequest so the engine can garbage collect it. httpRequest.onreadystatechange = null; + + callback(error, response, optionalCallbackParameter); } }; if (typeof options === 'string') {