From 7261fcae4a8860a6afe5c570a1851db6caa2dddb Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 4 Oct 2016 14:35:23 -0700 Subject: [PATCH 01/22] possible fix for domain-server crash-on-exit --- libraries/shared/src/SettingInterface.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/shared/src/SettingInterface.cpp b/libraries/shared/src/SettingInterface.cpp index 95c6bc1efc..9db84055f7 100644 --- a/libraries/shared/src/SettingInterface.cpp +++ b/libraries/shared/src/SettingInterface.cpp @@ -34,7 +34,6 @@ namespace Setting { DependencyManager::destroy(); // - globalManager->deleteLater(); globalManager.reset(); // quit the settings manager thread and wait on it to make sure it's gone @@ -72,9 +71,9 @@ namespace Setting { globalManager = DependencyManager::set(); - QObject::connect(globalManager.data(), SIGNAL(destroyed()), thread, SLOT(quit())); QObject::connect(thread, SIGNAL(started()), globalManager.data(), SLOT(startTimer())); QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); + QObject::connect(thread, SIGNAL(finished()), globalManager.data(), SLOT(deleteLater())); globalManager->moveToThread(thread); thread->start(); qCDebug(shared) << "Settings thread started."; From 1fff480b73cd072cc7618f6e35c58963f5a5c337 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Sep 2016 13:23:02 -0700 Subject: [PATCH 02/22] Add timeout to AssetResourceRequest --- .../networking/src/AssetResourceRequest.cpp | 59 ++++++++++++++++++- .../networking/src/AssetResourceRequest.h | 6 ++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index a8311c6146..8faf7082cd 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -14,6 +14,7 @@ #include "AssetClient.h" #include "AssetUtils.h" #include "MappingRequest.h" +#include AssetResourceRequest::~AssetResourceRequest() { if (_assetMappingRequest) { @@ -23,6 +24,10 @@ AssetResourceRequest::~AssetResourceRequest() { if (_assetRequest) { _assetRequest->deleteLater(); } + + if (_sendTimer) { + cleanupTimer(); + } } bool AssetResourceRequest::urlIsAssetHash() const { @@ -32,6 +37,25 @@ bool AssetResourceRequest::urlIsAssetHash() const { return hashRegex.exactMatch(_url.toString()); } +void AssetResourceRequest::setupTimer() { + Q_ASSERT(!_sendTimer); + static const int TIMEOUT_MS = 2000; + + _sendTimer = new QTimer(); + connect(this, &QObject::destroyed, _sendTimer, &QTimer::deleteLater); + connect(_sendTimer, &QTimer::timeout, this, &AssetResourceRequest::onTimeout); + + _sendTimer->setSingleShot(true); + _sendTimer->start(TIMEOUT_MS); +} + +void AssetResourceRequest::cleanupTimer() { + Q_ASSERT(_sendTimer); + disconnect(_sendTimer, 0, this, 0); + _sendTimer->deleteLater(); + _sendTimer = nullptr; +} + void AssetResourceRequest::doSend() { // We'll either have a hash or an ATP path to a file (that maps to a hash) if (urlIsAssetHash()) { @@ -58,6 +82,8 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { Q_ASSERT(_state == InProgress); Q_ASSERT(request == _assetMappingRequest); + cleanupTimer(); + switch (request->getError()) { case MappingRequest::NoError: // we have no error, we should have a resulting hash - use that to send of a request for that asset @@ -93,6 +119,7 @@ void AssetResourceRequest::requestMappingForPath(const AssetPath& path) { _assetMappingRequest = nullptr; }); + setupTimer(); _assetMappingRequest->start(); } @@ -102,11 +129,15 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) { auto assetClient = DependencyManager::get(); _assetRequest = assetClient->createRequest(hash); - connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::progress); + connect(_assetRequest, &AssetRequest::progress, this, &AssetResourceRequest::onDownloadProgress); connect(_assetRequest, &AssetRequest::finished, this, [this](AssetRequest* req) { Q_ASSERT(_state == InProgress); Q_ASSERT(req == _assetRequest); Q_ASSERT(req->getState() == AssetRequest::Finished); + + cleanupTimer(); + + qDebug() << "Asset request finished, " << req->getHash() << "error: " << req->getError(); switch (req->getError()) { case AssetRequest::Error::NoError: @@ -134,9 +165,35 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) { _assetRequest = nullptr; }); + setupTimer(); _assetRequest->start(); } void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { + Q_ASSERT(_state == InProgress); + + // We've received data, so reset the timer + _sendTimer->start(); + emit progress(bytesReceived, bytesTotal); } + +void AssetResourceRequest::onTimeout() { + if (_state == InProgress) { + qWarning() << "Asset request timed out: " << _url; + if (_assetRequest) { + disconnect(_assetRequest, 0, this, 0); + _assetRequest->deleteLater(); + _assetRequest = nullptr; + } + if (_assetMappingRequest) { + disconnect(_assetMappingRequest, 0, this, 0); + _assetMappingRequest->deleteLater(); + _assetMappingRequest = nullptr; + } + _result = Timeout; + _state = Finished; + emit finished(); + } + cleanupTimer(); +} diff --git a/libraries/networking/src/AssetResourceRequest.h b/libraries/networking/src/AssetResourceRequest.h index 6839db0628..c462fbc3f8 100644 --- a/libraries/networking/src/AssetResourceRequest.h +++ b/libraries/networking/src/AssetResourceRequest.h @@ -28,13 +28,19 @@ protected: private slots: void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void onTimeout(); private: + void setupTimer(); + void cleanupTimer(); + bool urlIsAssetHash() const; void requestMappingForPath(const AssetPath& path); void requestHash(const AssetHash& hash); + QTimer* _sendTimer { nullptr }; + GetMappingRequest* _assetMappingRequest { nullptr }; AssetRequest* _assetRequest { nullptr }; }; From 3c374e8ee2ff4262ed85b9af4240a557b532413e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Sep 2016 16:21:41 -0700 Subject: [PATCH 03/22] Add retry logic to ScriptCache --- libraries/script-engine/src/ScriptCache.cpp | 66 ++++++++++++++++----- libraries/script-engine/src/ScriptCache.h | 11 +++- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index 91d7f36102..f8a2c02f10 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -25,6 +25,7 @@ #include "ScriptEngines.h" #include "ScriptEngineLogging.h" +#include ScriptCache::ScriptCache(QObject* parent) { // nothing to do here... @@ -133,8 +134,11 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable qCDebug(scriptengine) << "Found script in cache:" << url.toString(); contentAvailable(url.toString(), scriptContent, true, true); } else { - bool alreadyWaiting = _contentCallbacks.contains(url); - _contentCallbacks.insert(url, contentAvailable); + auto& scriptRequest = _activeScriptRequests[url]; + + bool alreadyWaiting = scriptRequest.scriptUsers.size() > 0; + scriptRequest.scriptUsers.push_back(contentAvailable); + lock.unlock(); if (alreadyWaiting) { @@ -152,6 +156,9 @@ void ScriptCache::getScriptContents(const QString& scriptOrURL, contentAvailable } } +static const int MAX_RETRIES = 5; +static int START_DELAY_BETWEEN_RETRIES = 200; + void ScriptCache::scriptContentAvailable() { #ifdef THREAD_DEBUGGING qCDebug(scriptengine) << "ScriptCache::scriptContentAvailable() on thread [" << QThread::currentThread() << "] expected thread [" << thread() << "]"; @@ -160,29 +167,60 @@ void ScriptCache::scriptContentAvailable() { QUrl url = req->getUrl(); QString scriptContent; - QList allCallbacks; + std::vector allCallbacks; + bool finished { false }; bool success { false }; { - Lock lock(_containerLock); - allCallbacks = _contentCallbacks.values(url); - _contentCallbacks.remove(url); Q_ASSERT(req->getState() == ResourceRequest::Finished); success = req->getResult() == ResourceRequest::Success; - if (success) { - _scriptCache[url] = scriptContent = req->getData(); - qCDebug(scriptengine) << "Done downloading script at:" << url.toString(); - } else { - // Dubious, but retained here because it matches the behavior before fixing the threading - scriptContent = _scriptCache[url]; - qCWarning(scriptengine) << "Error loading script from URL " << url; + Lock lock(_containerLock); + + if (_activeScriptRequests.contains(url)) { + auto& scriptRequest = _activeScriptRequests[url]; + + if (success) { + allCallbacks = scriptRequest.scriptUsers; + auto scriptContent = _scriptCache[url]; + + _activeScriptRequests.remove(url); + + _scriptCache[url] = scriptContent = req->getData(); + finished = true; + qCDebug(scriptengine) << "Done downloading script at:" << url.toString(); + } else { + if (scriptRequest.numRetries < MAX_RETRIES) { + ++scriptRequest.numRetries; + + qDebug() << "Script request failed: " << url; + + int timeout = exp(scriptRequest.numRetries) * START_DELAY_BETWEEN_RETRIES; + QTimer::singleShot(timeout, this, [this, url]() { + qDebug() << "Retrying script request: " << url; + + auto request = ResourceManager::createResourceRequest(nullptr, url); + Q_ASSERT(request); + + // We've already made a request, so the cache must be disabled or it wasn't there, so enabling + // it will do nothing. + request->setCacheEnabled(false); + connect(request, &ResourceRequest::finished, this, &ScriptCache::scriptContentAvailable); + request->send(); + }); + } else { + // Dubious, but retained here because it matches the behavior before fixing the threading + scriptContent = _scriptCache[url]; + finished = true; + qCWarning(scriptengine) << "Error loading script from URL " << url; + } + } } } req->deleteLater(); - if (!DependencyManager::get()->isStopped()) { + if (finished && !DependencyManager::get()->isStopped()) { foreach(contentAvailableCallback thisCallback, allCallbacks) { thisCallback(url.toString(), scriptContent, true, success); } diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 5c0c235bd1..42feaac2ad 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -15,13 +15,19 @@ #include #include +using contentAvailableCallback = std::function; + class ScriptUser { public: virtual void scriptContentsAvailable(const QUrl& url, const QString& scriptContents) = 0; virtual void errorInLoadingScript(const QUrl& url) = 0; }; -using contentAvailableCallback = std::function; +class ScriptRequest { +public: + std::vector scriptUsers { }; + int numRetries { 0 }; +}; /// Interface for loading scripts class ScriptCache : public QObject, public Dependency { @@ -51,7 +57,8 @@ private: ScriptCache(QObject* parent = NULL); Mutex _containerLock; - QMultiMap _contentCallbacks; + QMap _activeScriptRequests; + //QMultiMap _contentCallbacks; QHash _scriptCache; QMultiMap _scriptUsers; From c6485cc50cb60866495c92b07b6c5c28853a93f8 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Fri, 30 Sep 2016 16:56:08 -0700 Subject: [PATCH 04/22] Fix scriptcache only serving empty strings --- libraries/script-engine/src/ScriptCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index f8a2c02f10..d8d75bce64 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -182,7 +182,7 @@ void ScriptCache::scriptContentAvailable() { if (success) { allCallbacks = scriptRequest.scriptUsers; - auto scriptContent = _scriptCache[url]; + //auto scriptContent = _scriptCache[url]; _activeScriptRequests.remove(url); From ca4022814e3c81198c3d61d0b8a307b015494b01 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Sun, 2 Oct 2016 21:18:13 -0700 Subject: [PATCH 05/22] Add extra logging to asset client --- libraries/networking/src/AssetClient.cpp | 6 ++++++ libraries/networking/src/AssetRequest.cpp | 6 +++++- libraries/networking/src/AssetRequest.h | 1 + libraries/script-engine/src/AssetScriptingInterface.cpp | 1 - 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 9583d4735d..624d98df7a 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -394,12 +394,14 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message auto senderNode = node.toStrongRef(); if (!senderNode) { + qCDebug(asset_client) << "Got completed asset for node that no longer exists"; return; } // Check if we have any pending requests for this node auto messageMapIt = _pendingRequests.find(senderNode); if (messageMapIt == _pendingRequests.end()) { + qCDebug(asset_client) << "Got completed asset for a node that doesn't have any pending requests"; return; } @@ -409,6 +411,7 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message // Check if we have this pending request auto requestIt = messageCallbackMap.find(messageID); if (requestIt == messageCallbackMap.end()) { + qCDebug(asset_client) << "Got completed asset for a request that doesn't exist"; return; } @@ -416,13 +419,16 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message auto& message = callbacks.message; if (!message) { + qCDebug(asset_client) << "Got completed asset for a message that doesn't exist"; return; } if (message->failed()) { + qCDebug(asset_client) << "Got failed asset"; callbacks.completeCallback(false, AssetServerError::NoError, QByteArray()); } else { + qCDebug(asset_client) << "Got asset"; callbacks.completeCallback(true, AssetServerError::NoError, message->readAll()); } diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 4f0e812031..6f319df034 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -107,9 +107,11 @@ void AssetRequest::start() { auto assetClient = DependencyManager::get(); auto that = QPointer(this); // Used to track the request's lifetime + auto hash = _hash; _assetRequestID = assetClient->getAsset(_hash, start, end, - [this, that, start, end](bool responseReceived, AssetServerError serverError, const QByteArray& data) { + [this, that, hash, start, end](bool responseReceived, AssetServerError serverError, const QByteArray& data) { if (!that) { + qCWarning(asset_client) << "Got reply for dead asset request " << hash << "- error code" << _error; // If the request is dead, return return; } @@ -148,6 +150,8 @@ void AssetRequest::start() { if (_error != NoError) { qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; + } else { + qCDebug(asset_client) << "Received asset successfully: " << _hash; } _state = Finished; diff --git a/libraries/networking/src/AssetRequest.h b/libraries/networking/src/AssetRequest.h index c0bde9d8a8..0a6810b052 100644 --- a/libraries/networking/src/AssetRequest.h +++ b/libraries/networking/src/AssetRequest.h @@ -49,6 +49,7 @@ public: const State& getState() const { return _state; } const Error& getError() const { return _error; } QUrl getUrl() const { return ::getATPUrl(_hash); } + QString getHash() const { return _hash; } signals: void finished(AssetRequest* thisRequest); diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 80cd30a8b6..32b1b5e792 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -21,7 +21,6 @@ AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) : _engine(engine) { - } void AssetScriptingInterface::uploadData(QString data, QScriptValue callback) { From 82aa1bafaddc711a699eaf898b39ebf5ab15da88 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 09:31:09 -0700 Subject: [PATCH 06/22] Update BatchLoader to use ScriptCache --- libraries/script-engine/src/BatchLoader.cpp | 33 ++++++++++----------- libraries/script-engine/src/ScriptCache.cpp | 1 - 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/libraries/script-engine/src/BatchLoader.cpp b/libraries/script-engine/src/BatchLoader.cpp index f3e6242216..15b3b6c853 100644 --- a/libraries/script-engine/src/BatchLoader.cpp +++ b/libraries/script-engine/src/BatchLoader.cpp @@ -13,12 +13,14 @@ #include #include +#include #include "ScriptEngineLogging.h" #include "BatchLoader.h" #include #include #include "ResourceManager.h" #include "ScriptEngines.h" +#include "ScriptCache.h" BatchLoader::BatchLoader(const QList& urls) : QObject(), @@ -38,30 +40,25 @@ void BatchLoader::start() { for (const auto& rawURL : _urls) { QUrl url = expandScriptUrl(normalizeScriptURL(rawURL)); - auto request = ResourceManager::createResourceRequest(this, url); - if (!request) { - _data.insert(url, QString()); - qCDebug(scriptengine) << "Could not load" << url; - continue; - } - connect(request, &ResourceRequest::finished, this, [=]() { - if (request->getResult() == ResourceRequest::Success) { - _data.insert(url, request->getData()); + + qCDebug(scriptengine) << "Loading script at " << url; + + QPointer self = this; + DependencyManager::get()->getScriptContents(url.toString(), [this, self](const QString& url, const QString& contents, bool isURL, bool success) { + if (!self) { + return; + } + if (isURL && success) { + _data.insert(url, contents); + qCDebug(scriptengine) << "Loaded: " << url; } else { _data.insert(url, QString()); qCDebug(scriptengine) << "Could not load" << url; } - request->deleteLater(); checkFinished(); - }); - - // If we end up being destroyed before the reply finishes, clean it up - connect(this, &QObject::destroyed, request, &QObject::deleteLater); - - qCDebug(scriptengine) << "Loading script at " << url; - - request->send(); + }, false); } + checkFinished(); } diff --git a/libraries/script-engine/src/ScriptCache.cpp b/libraries/script-engine/src/ScriptCache.cpp index d8d75bce64..e9f20b5164 100644 --- a/libraries/script-engine/src/ScriptCache.cpp +++ b/libraries/script-engine/src/ScriptCache.cpp @@ -182,7 +182,6 @@ void ScriptCache::scriptContentAvailable() { if (success) { allCallbacks = scriptRequest.scriptUsers; - //auto scriptContent = _scriptCache[url]; _activeScriptRequests.remove(url); From ae507c4609f7c81d8722221421a0e6c58ad04fda Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Mon, 3 Oct 2016 22:26:10 -0700 Subject: [PATCH 07/22] Add ScriptCache to assignment clients --- assignment-client/src/AssignmentClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 800f00b352..3bea0ce31c 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -57,6 +57,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri auto scriptableAvatar = DependencyManager::set(); auto addressManager = DependencyManager::set(); auto scriptEngines = DependencyManager::set(); + DependencyManager::set(); // create a NodeList as an unassigned client, must be after addressManager auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); From 9dfa3e4a17b40fc2b2865c6470f895782f4172db Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Tue, 4 Oct 2016 08:21:40 -0700 Subject: [PATCH 08/22] Move ScriptCache from AssignmentClient to Agent --- assignment-client/src/Agent.cpp | 1 + assignment-client/src/AssignmentClient.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index c6a2a3d5e8..f4f5819361 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -62,6 +62,7 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index 3bea0ce31c..800f00b352 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -57,7 +57,6 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri auto scriptableAvatar = DependencyManager::set(); auto addressManager = DependencyManager::set(); auto scriptEngines = DependencyManager::set(); - DependencyManager::set(); // create a NodeList as an unassigned client, must be after addressManager auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); From e9196e8f62925d59b079f2a90c30ced02701c34e Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 13:41:15 -0700 Subject: [PATCH 09/22] testing client to check if domain-server and ACs are running --- tools/CMakeLists.txt | 3 + tools/ac-client/CMakeLists.txt | 3 + tools/ac-client/src/ACClientApp.cpp | 272 ++++++++++++++++++++++++++++ tools/ac-client/src/ACClientApp.h | 55 ++++++ tools/ac-client/src/main.cpp | 23 +++ 5 files changed, 356 insertions(+) create mode 100644 tools/ac-client/CMakeLists.txt create mode 100644 tools/ac-client/src/ACClientApp.cpp create mode 100644 tools/ac-client/src/ACClientApp.h create mode 100644 tools/ac-client/src/main.cpp diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index a077efc335..cf0e25a757 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -10,3 +10,6 @@ set_target_properties(vhacd-util PROPERTIES FOLDER "Tools") add_subdirectory(ice-client) set_target_properties(ice-client PROPERTIES FOLDER "Tools") + +add_subdirectory(ac-client) +set_target_properties(ac-client PROPERTIES FOLDER "Tools") diff --git a/tools/ac-client/CMakeLists.txt b/tools/ac-client/CMakeLists.txt new file mode 100644 index 0000000000..9e623b02e9 --- /dev/null +++ b/tools/ac-client/CMakeLists.txt @@ -0,0 +1,3 @@ +set(TARGET_NAME ac-client) +setup_hifi_project(Core Widgets) +link_hifi_libraries(shared networking) diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp new file mode 100644 index 0000000000..3ea2322ffd --- /dev/null +++ b/tools/ac-client/src/ACClientApp.cpp @@ -0,0 +1,272 @@ +// +// ACClientApp.cpp +// tools/ac-client/src +// +// Created by Seth Alves on 2016-10-5 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ACClientApp.h" + +ACClientApp::ACClientApp(int argc, char* argv[]) : + QCoreApplication(argc, argv) +{ + // parse command-line + QCommandLineParser parser; + parser.setApplicationDescription("High Fidelity AC client"); + parser.addHelpOption(); + + const QCommandLineOption helpOption = parser.addHelpOption(); + + const QCommandLineOption verboseOutput("v", "verbose output"); + parser.addOption(verboseOutput); + + const QCommandLineOption domainAddressOption("d", "domain-server address", "127.0.0.1"); + parser.addOption(domainAddressOption); + + const QCommandLineOption cacheSTUNOption("s", "cache stun-server response"); + parser.addOption(cacheSTUNOption); + + const QCommandLineOption listenPortOption("listenPort", "listen port", QString::number(INVALID_PORT)); + parser.addOption(listenPortOption); + + + if (!parser.parse(QCoreApplication::arguments())) { + qCritical() << parser.errorText() << endl; + parser.showHelp(); + Q_UNREACHABLE(); + } + + if (parser.isSet(helpOption)) { + parser.showHelp(); + Q_UNREACHABLE(); + } + + _verbose = parser.isSet(verboseOutput); + if (!_verbose) { + QLoggingCategory::setFilterRules("qt.network.ssl.warning=false"); + + const_cast(&networking())->setEnabled(QtDebugMsg, false); + const_cast(&networking())->setEnabled(QtInfoMsg, false); + const_cast(&networking())->setEnabled(QtWarningMsg, false); + + const_cast(&shared())->setEnabled(QtDebugMsg, false); + const_cast(&shared())->setEnabled(QtInfoMsg, false); + const_cast(&shared())->setEnabled(QtWarningMsg, false); + } + + // QString domainServerAddress = "127.0.0.1"; + // if (parser.isSet(domainAddressOption)) { + // // parse the IP and port combination for this target + // QString hostnamePortString = parser.value(domainAddressOption); + + // QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; + // quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; + // if (port == 0) { + // port = DEFAULT_DOMAIN_SERVER_PORT; + // } + + // if (address.isNull()) { + // qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" << + // "The parsed IP was" << address.toString() << "and the parsed port was" << port; + + // QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); + // } else { + // _iceServerAddr = HifiSockAddr(address, port); + // } + + // if (_verbose) { + // qDebug() << "domain-server Address is" << domainServerAddress << "port is" << port; + // } + // } + + QString domainServerAddress = "127.0.0.1:40103"; + if (parser.isSet(domainAddressOption)) { + domainServerAddress = parser.value(domainAddressOption); + } + + if (_verbose) { + qDebug() << "domain-server address is" << domainServerAddress; + } + + int listenPort = INVALID_PORT; + if (parser.isSet(listenPortOption)) { + listenPort = parser.value(listenPortOption).toInt(); + } + + Setting::preInit(); + DependencyManager::registerInheritance(); + // DependencyManager::registerInheritance(); + Setting::init(); + + DependencyManager::set([&]{ return QString("Mozilla/5.0 (HighFidelityACClient)"); }); + DependencyManager::set(); + DependencyManager::set(NodeType::Agent, listenPort); + + + auto nodeList = DependencyManager::get(); + + // start the nodeThread so its event loop is running + QThread* nodeThread = new QThread(this); + nodeThread->setObjectName("NodeList Thread"); + nodeThread->start(); + + // make sure the node thread is given highest priority + nodeThread->setPriority(QThread::TimeCriticalPriority); + + // setup a timer for domain-server check ins + QTimer* domainCheckInTimer = new QTimer(nodeList.data()); + connect(domainCheckInTimer, &QTimer::timeout, nodeList.data(), &NodeList::sendDomainServerCheckIn); + domainCheckInTimer->start(DOMAIN_SERVER_CHECK_IN_MSECS); + + // put the NodeList and datagram processing on the node thread + nodeList->moveToThread(nodeThread); + + + const DomainHandler& domainHandler = nodeList->getDomainHandler(); + + connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); + // connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); + // connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); + // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); + // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); + connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused); + + connect(nodeList.data(), &NodeList::nodeAdded, this, &ACClientApp::nodeAdded); + connect(nodeList.data(), &NodeList::nodeKilled, this, &ACClientApp::nodeKilled); + connect(nodeList.data(), &NodeList::nodeActivated, this, &ACClientApp::nodeActivated); + // connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); + // connect(nodeList.data(), &NodeList::uuidChanged, this, &ACClientApp::setSessionUUID); + // connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch); + + // // you might think we could just do this in NodeList but we only want this connection for Interface + // connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); + + // AccountManager _accountManager = new AccountManager(std::bind(&ACClientApp::getUserAgent, qApp)); + + // setState(lookUpStunServer); + + + nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer + << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); + + + // // send the identity packet for our avatar each second to our avatar mixer + // connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); + // identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); + + + // DependencyManager::get()->loadSettings(domainServerAddress); + + DependencyManager::get()->handleLookupString(domainServerAddress, false); + + QTimer* doTimer = new QTimer(this); + doTimer->setSingleShot(true); + connect(doTimer, &QTimer::timeout, this, &ACClientApp::timedOut); + doTimer->start(4000); +} + +ACClientApp::~ACClientApp() { +} + + +void ACClientApp::domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo) { + qDebug() << "domainConnectionRefused"; +} + +void ACClientApp::domainChanged(const QString& domainHostname) { + if (_verbose) { + qDebug() << "domainChanged"; + } +} + +void ACClientApp::nodeAdded(SharedNodePointer node) { + if (_verbose) { + qDebug() << "node added: " << node->getType(); + } +} + +void ACClientApp::nodeActivated(SharedNodePointer node) { + if (node->getType() == NodeType::EntityServer) { + if (_verbose) { + qDebug() << "saw EntityServer"; + } + _sawEntityServer = true; + } + else if (node->getType() == NodeType::AudioMixer) { + if (_verbose) { + qDebug() << "saw AudioMixer"; + } + _sawAudioMixer = true; + } + else if (node->getType() == NodeType::AvatarMixer) { + if (_verbose) { + qDebug() << "saw AvatarMixer"; + } + _sawAvatarMixer = true; + } + else if (node->getType() == NodeType::AssetServer) { + if (_verbose) { + qDebug() << "saw AssetServer"; + } + _sawAssetServer = true; + } + else if (node->getType() == NodeType::MessagesMixer) { + if (_verbose) { + qDebug() << "saw MessagesMixer"; + } + _sawMessagesMixer = true; + } + + if (_sawEntityServer && _sawAudioMixer && _sawAvatarMixer && _sawAssetServer && _sawMessagesMixer) { + if (_verbose) { + qDebug() << "success"; + } + finish(0); + } +} + +void ACClientApp::nodeKilled(SharedNodePointer node) { + qDebug() << "nodeKilled"; +} + +void ACClientApp::timedOut() { + if (_verbose) { + qDebug() << "timed out: " << _sawEntityServer << _sawAudioMixer << + _sawAvatarMixer << _sawAssetServer << _sawMessagesMixer; + } + finish(1); +} + +void ACClientApp::finish(int exitCode) { + auto nodeList = DependencyManager::get(); + + // send the domain a disconnect packet, force stoppage of domain-server check-ins + nodeList->getDomainHandler().disconnect(); + nodeList->setIsShuttingDown(true); + + // tell the packet receiver we're shutting down, so it can drop packets + nodeList->getPacketReceiver().setShouldDropPackets(true); + + QThread* nodeThread = DependencyManager::get()->thread(); + // remove the NodeList from the DependencyManager + DependencyManager::destroy(); + // ask the node thread to quit and wait until it is done + nodeThread->quit(); + nodeThread->wait(); + + QCoreApplication::exit(exitCode); +} diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h new file mode 100644 index 0000000000..36de592401 --- /dev/null +++ b/tools/ac-client/src/ACClientApp.h @@ -0,0 +1,55 @@ +// +// ACClientApp.h +// tools/ac-client/src +// +// Created by Seth Alves on 2016-10-5 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +#ifndef hifi_ACClientApp_h +#define hifi_ACClientApp_h + +#include +#include +#include +#include +#include +#include + + +class ACClientApp : public QCoreApplication { + Q_OBJECT +public: + ACClientApp(int argc, char* argv[]); + ~ACClientApp(); + + const int stunFailureExitStatus { 1 }; + const int iceFailureExitStatus { 2 }; + const int domainPingExitStatus { 3 }; + +private slots: + void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); + void domainChanged(const QString& domainHostname); + void nodeAdded(SharedNodePointer node); + void nodeActivated(SharedNodePointer node); + void nodeKilled(SharedNodePointer node); + +private: + NodeList* _nodeList; + void timedOut(); + void finish(int exitCode); + bool _verbose; + QTimer* _pingDomainTimer { nullptr }; + + bool _sawEntityServer { false }; + bool _sawAudioMixer { false }; + bool _sawAvatarMixer { false }; + bool _sawAssetServer { false }; + bool _sawMessagesMixer { false }; +}; + +#endif //hifi_ACClientApp_h diff --git a/tools/ac-client/src/main.cpp b/tools/ac-client/src/main.cpp new file mode 100644 index 0000000000..918df6413f --- /dev/null +++ b/tools/ac-client/src/main.cpp @@ -0,0 +1,23 @@ +// +// main.cpp +// tools/ice-client/src +// +// Created by Seth Alves on 2016-10-5 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +#include +#include +#include +#include + +#include "ACClientApp.h" + +using namespace std; + +int main(int argc, char * argv[]) { + ACClientApp app(argc, argv); + return app.exec(); +} From afbbd398dc380bfa9b9ba41b5e9cd7f322d94dbd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 5 Oct 2016 13:48:03 -0700 Subject: [PATCH 10/22] Remove QML threaded rendering --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 562 +++++--------------- libraries/gl/src/gl/OffscreenQmlSurface.h | 44 +- libraries/gl/src/gl/TextureRecycler.cpp | 83 +++ libraries/gl/src/gl/TextureRecycler.h | 47 ++ 4 files changed, 291 insertions(+), 445 deletions(-) create mode 100644 libraries/gl/src/gl/TextureRecycler.cpp create mode 100644 libraries/gl/src/gl/TextureRecycler.h diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index 797f297488..bcfaaffd08 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -32,11 +32,13 @@ #include #include #include +#include #include "OffscreenGLCanvas.h" #include "GLHelpers.h" #include "GLLogging.h" - +#include "TextureRecycler.h" +#include "Context.h" QString fixupHifiUrl(const QString& urlString) { static const QString ACCESS_TOKEN_PARAMETER = "access_token"; @@ -114,257 +116,23 @@ QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") -static const QEvent::Type INIT = QEvent::Type(QEvent::User + 1); -static const QEvent::Type RENDER = QEvent::Type(QEvent::User + 2); -static const QEvent::Type RESIZE = QEvent::Type(QEvent::User + 3); -static const QEvent::Type STOP = QEvent::Type(QEvent::User + 4); +#if 0 +QJsonObject getGLContextData(); +QJsonObject _glData; -class RawTextureRecycler { -public: - using TexturePtr = GLuint; - RawTextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {} - void setSize(const uvec2& size); - void clear(); - TexturePtr getNextTexture(); - void recycleTexture(GLuint texture); - -private: - - struct TexInfo { - TexturePtr _tex { 0 }; - uvec2 _size; - bool _active { false }; - - TexInfo() {} - TexInfo(TexturePtr tex, const uvec2& size) : _tex(tex), _size(size) {} - }; - - using Map = std::map; - using Queue = std::queue; - - Map _allTextures; - Queue _readyTextures; - uvec2 _size { 1920, 1080 }; - bool _useMipmaps; -}; - - -void RawTextureRecycler::setSize(const uvec2& size) { - if (size == _size) { - return; +QJsonObject OffscreenQmlSurface::getGLContextData() { + _glMutex.lock(); + if (_glData.isEmpty()) { + _glWait.wait(&_glMutex); } - _size = size; - while (!_readyTextures.empty()) { - _readyTextures.pop(); - } - std::set toDelete; - std::for_each(_allTextures.begin(), _allTextures.end(), [&](Map::const_reference item) { - if (!item.second._active && item.second._size != _size) { - toDelete.insert(item.first); - } - }); - std::for_each(toDelete.begin(), toDelete.end(), [&](Map::key_type key) { - _allTextures.erase(key); - }); -} - -void RawTextureRecycler::clear() { - while (!_readyTextures.empty()) { - _readyTextures.pop(); - } - _allTextures.clear(); -} - -RawTextureRecycler::TexturePtr RawTextureRecycler::getNextTexture() { - if (_readyTextures.empty()) { - TexturePtr newTexture; - glGenTextures(1, &newTexture); - - glBindTexture(GL_TEXTURE_2D, newTexture); - if (_useMipmaps) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _size.x, _size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - _allTextures[newTexture] = TexInfo { newTexture, _size }; - _readyTextures.push(newTexture); - } - - TexturePtr result = _readyTextures.front(); - _readyTextures.pop(); - auto& item = _allTextures[result]; - item._active = true; - return result; -} - -void RawTextureRecycler::recycleTexture(GLuint texture) { - Q_ASSERT(_allTextures.count(texture)); - auto& item = _allTextures[texture]; - Q_ASSERT(item._active); - item._active = false; - if (item._size != _size) { - // Buh-bye - _allTextures.erase(texture); - return; - } - - _readyTextures.push(item._tex); + _glMutex.unlock(); + return _glData; } +#endif -class OffscreenQmlRenderThread : public QThread { -public: - OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext); - virtual ~OffscreenQmlRenderThread() = default; - - virtual void run() override; - virtual bool event(QEvent *e) override; - -protected: - class Queue : private QQueue { - public: - void add(QEvent::Type type); - QEvent* take(); - - private: - QMutex _mutex; - QWaitCondition _waitCondition; - bool _isWaiting{ false }; - }; - - friend class OffscreenQmlSurface; - - QJsonObject getGLContextData(); - - Queue _queue; - QMutex _mutex; - QWaitCondition _waitCondition; - std::atomic _rendering { false }; - - QJsonObject _glData; - QMutex _glMutex; - QWaitCondition _glWait; - -private: - // Event-driven methods - void init(); - void render(); - void resize(); - void cleanup(); - - // Helper methods - void setupFbo(); - bool allowNewFrame(uint8_t fps); - bool fetchTexture(OffscreenQmlSurface::TextureAndFence& textureAndFence); - void releaseTexture(const OffscreenQmlSurface::TextureAndFence& textureAndFence); - - // Texture management - std::mutex _textureMutex; - GLuint _latestTexture { 0 }; - GLsync _latestTextureFence { 0 }; - std::list _returnedTextures; - - // Rendering members - OffscreenGLCanvas _canvas; - OffscreenQmlSurface* _surface{ nullptr }; - QQuickWindow* _quickWindow{ nullptr }; - QMyQuickRenderControl* _renderControl{ nullptr }; - GLuint _fbo { 0 }; - GLuint _depthStencil { 0 }; - RawTextureRecycler _textures { true }; - - uint64_t _lastRenderTime{ 0 }; - uvec2 _size{ 1920, 1080 }; - QSize _newSize; - bool _quit{ false }; -}; - -void OffscreenQmlRenderThread::Queue::add(QEvent::Type type) { - QMutexLocker locker(&_mutex); - enqueue(new QEvent(type)); - if (_isWaiting) { - _waitCondition.wakeOne(); - } -} - -QEvent* OffscreenQmlRenderThread::Queue::take() { - QMutexLocker locker(&_mutex); - while (isEmpty()) { - _isWaiting = true; - _waitCondition.wait(&_mutex); - _isWaiting = false; - } - QEvent* e = dequeue(); - return e; -} - -OffscreenQmlRenderThread::OffscreenQmlRenderThread(OffscreenQmlSurface* surface, QOpenGLContext* shareContext) : _surface(surface) { - _canvas.setObjectName("OffscreenQmlRenderCanvas"); - qCDebug(glLogging) << "Building QML Renderer"; - if (!_canvas.create(shareContext)) { - qWarning("Failed to create OffscreenGLCanvas"); - _quit = true; - return; - }; - - _renderControl = new QMyQuickRenderControl(); - QQuickWindow::setDefaultAlphaBuffer(true); - // Create a QQuickWindow that is associated with our render control. - // This window never gets created or shown, meaning that it will never get an underlying native (platform) window. - // NOTE: Must be created on the main thread so that OffscreenQmlSurface can send it events - // NOTE: Must be created on the rendering thread or it will refuse to render, - // so we wait until after its ctor to move object/context to this thread. - _quickWindow = new QQuickWindow(_renderControl); - _quickWindow->setColor(QColor(255, 255, 255, 0)); - _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); - - // We can prepare, but we must wait to start() the thread until after the ctor - _renderControl->prepareThread(this); - _canvas.getContextObject()->moveToThread(this); - moveToThread(this); - - _queue.add(INIT); -} - -void OffscreenQmlRenderThread::run() { - qCDebug(glLogging) << "Starting QML Renderer thread"; - - while (!_quit) { - QEvent* e = _queue.take(); - event(e); - delete e; - } -} - -bool OffscreenQmlRenderThread::event(QEvent *e) { - switch (int(e->type())) { - case INIT: - init(); - return true; - case RENDER: - render(); - return true; - case RESIZE: - resize(); - return true; - case STOP: - cleanup(); - return true; - default: - return QObject::event(e); - } -} - -void OffscreenQmlRenderThread::setupFbo() { +void OffscreenQmlSurface::setupFbo() { + _canvas->makeCurrent(); _textures.setSize(_size); if (_depthStencil) { glDeleteRenderbuffers(1, &_depthStencil); @@ -382,41 +150,12 @@ void OffscreenQmlRenderThread::setupFbo() { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthStencil); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + _canvas->doneCurrent(); } -QJsonObject OffscreenQmlRenderThread::getGLContextData() { - _glMutex.lock(); - if (_glData.isEmpty()) { - _glWait.wait(&_glMutex); - } - _glMutex.unlock(); - return _glData; -} - -void OffscreenQmlRenderThread::init() { - qCDebug(glLogging) << "Initializing QML Renderer"; - - if (!_canvas.makeCurrent()) { - qWarning("Failed to make context current on QML Renderer Thread"); - _quit = true; - return; - } - - _glMutex.lock(); - _glData = ::getGLContextData(); - _glMutex.unlock(); - _glWait.wakeAll(); - - connect(_renderControl, &QQuickRenderControl::renderRequested, _surface, &OffscreenQmlSurface::requestRender); - connect(_renderControl, &QQuickRenderControl::sceneChanged, _surface, &OffscreenQmlSurface::requestUpdate); - - _renderControl->initialize(_canvas.getContext()); - setupFbo(); -} - -void OffscreenQmlRenderThread::cleanup() { +void OffscreenQmlSurface::cleanup() { + _canvas->makeCurrent(); _renderControl->invalidate(); - if (_depthStencil) { glDeleteRenderbuffers(1, &_depthStencil); _depthStencil = 0; @@ -427,65 +166,17 @@ void OffscreenQmlRenderThread::cleanup() { } _textures.clear(); - - _canvas.doneCurrent(); - _canvas.getContextObject()->moveToThread(QCoreApplication::instance()->thread()); - - _quit = true; + _canvas->doneCurrent(); } -void OffscreenQmlRenderThread::resize() { - // Lock _newSize changes - { - QMutexLocker locker(&_mutex); - - // Update our members - if (_quickWindow) { - _quickWindow->setGeometry(QRect(QPoint(), _newSize)); - _quickWindow->contentItem()->setSize(_newSize); - } - - // Qt bug in 5.4 forces this check of pixel ratio, - // even though we're rendering offscreen. - qreal pixelRatio = 1.0; - if (_renderControl && _renderControl->_renderWindow) { - pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); - } - - uvec2 newOffscreenSize = toGlm(_newSize * pixelRatio); - if (newOffscreenSize == _size) { - return; - } - - qCDebug(glLogging) << "Offscreen UI resizing to " << _newSize.width() << "x" << _newSize.height() << " with pixel ratio " << pixelRatio; - _size = newOffscreenSize; - } - - _textures.setSize(_size); - setupFbo(); -} - -void OffscreenQmlRenderThread::render() { - // Ensure we always release the main thread - Finally releaseMainThread([this] { - _waitCondition.wakeOne(); - }); - - if (_surface->_paused) { +void OffscreenQmlSurface::render() { + if (_paused) { return; } - _rendering = true; - Finally unmarkRenderingFlag([this] { - _rendering = false; - }); - - { - QMutexLocker locker(&_mutex); - _renderControl->sync(); - releaseMainThread.trigger(); - } + _canvas->makeCurrent(); + _renderControl->sync(); _quickWindow->setRenderTarget(_fbo, QSize(_size.x, _size.y)); // Clear out any pending textures to be returned @@ -507,70 +198,61 @@ void OffscreenQmlRenderThread::render() { } } - try { - GLuint texture = _textures.getNextTexture(); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); - glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); - PROFILE_RANGE("qml_render->rendercontrol") - _renderControl->render(); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindTexture(GL_TEXTURE_2D, texture); - glGenerateMipmap(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, 0); + GLuint texture = _textures.getNextTexture(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _fbo); + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0); + PROFILE_RANGE("qml_render->rendercontrol") + _renderControl->render(); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, texture); + glGenerateMipmap(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); - { - std::unique_lock lock(_textureMutex); - // If the most recent texture was unused, we can directly recycle it - if (_latestTextureFence) { - } - if (_latestTexture) { - _textures.recycleTexture(_latestTexture); - glDeleteSync(_latestTextureFence); - _latestTexture = 0; - _latestTextureFence = 0; - } - - _latestTextureFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - _latestTexture = texture; - // Fence will be used in another thread / context, so a flush is required - glFlush(); + { + std::unique_lock lock(_textureMutex); + // If the most recent texture was unused, we can directly recycle it + if (_latestTextureAndFence.first) { + _textures.recycleTexture(_latestTextureAndFence.first); + glDeleteSync(static_cast(_latestTextureAndFence.second)); + _latestTextureAndFence = { 0, 0 }; } - _quickWindow->resetOpenGLState(); - _lastRenderTime = usecTimestampNow(); - } catch (std::runtime_error& error) { - qWarning() << "Failed to render QML: " << error.what(); + _latestTextureAndFence = { texture, glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0) }; + // Fence will be used in another thread / context, so a flush is required + glFlush(); } + + _quickWindow->resetOpenGLState(); + _lastRenderTime = usecTimestampNow(); + _canvas->doneCurrent(); } -bool OffscreenQmlRenderThread::fetchTexture(OffscreenQmlSurface::TextureAndFence& textureAndFence) { +bool OffscreenQmlSurface::fetchTexture(TextureAndFence& textureAndFence) { textureAndFence = { 0, 0 }; std::unique_lock lock(_textureMutex); - if (0 == _latestTexture) { + if (0 == _latestTextureAndFence.first) { return false; } // Ensure writes to the latest texture are complete before before returning it for reading - Q_ASSERT(0 != _latestTextureFence); - textureAndFence = { _latestTexture, _latestTextureFence }; - _latestTextureFence = 0; - _latestTexture = 0; + textureAndFence = _latestTextureAndFence; + _latestTextureAndFence = { 0, 0 }; return true; } -void OffscreenQmlRenderThread::releaseTexture(const OffscreenQmlSurface::TextureAndFence& textureAndFence) { +void OffscreenQmlSurface::releaseTexture(const TextureAndFence& textureAndFence) { std::unique_lock lock(_textureMutex); _returnedTextures.push_back(textureAndFence); } -bool OffscreenQmlRenderThread::allowNewFrame(uint8_t fps) { +bool OffscreenQmlSurface::allowNewFrame(uint8_t fps) { // If we already have a pending texture, don't render another one // i.e. don't render faster than the consumer context, since it wastes // GPU cycles on producing output that will never be seen { std::unique_lock lock(_textureMutex); - if (0 != _latestTexture) { + if (0 != _latestTextureAndFence.first) { return false; } } @@ -588,33 +270,46 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { QObject::disconnect(&_updateTimer); QObject::disconnect(qApp); - qCDebug(glLogging) << "Stopping QML Renderer Thread " << _renderer->currentThreadId(); - _renderer->_queue.add(STOP); - if (!_renderer->wait(MAX_SHUTDOWN_WAIT_SECS * USECS_PER_SECOND)) { - qWarning() << "Failed to shut down the QML Renderer Thread"; - } - delete _rootItem; - delete _renderer; - delete _qmlComponent; - delete _qmlEngine; + cleanup(); + + _canvas->deleteLater(); + _rootItem->deleteLater(); + _qmlComponent->deleteLater(); + _qmlEngine->deleteLater(); + _quickWindow->deleteLater(); } void OffscreenQmlSurface::onAboutToQuit() { + _paused = true; QObject::disconnect(&_updateTimer); } void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { qCDebug(glLogging) << "Building QML surface"; - _renderer = new OffscreenQmlRenderThread(this, shareContext); - _renderer->moveToThread(_renderer); - _renderer->setObjectName("QML Renderer Thread"); - _renderer->start(); + _renderControl = new QMyQuickRenderControl(); - _renderer->_renderControl->_renderWindow = _proxyWindow; + QQuickWindow::setDefaultAlphaBuffer(true); - connect(_renderer->_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged); + // Create a QQuickWindow that is associated with our render control. + // This window never gets created or shown, meaning that it will never get an underlying native (platform) window. + // NOTE: Must be created on the main thread so that OffscreenQmlSurface can send it events + // NOTE: Must be created on the rendering thread or it will refuse to render, + // so we wait until after its ctor to move object/context to this thread. + _quickWindow = new QQuickWindow(_renderControl); + _quickWindow->setColor(QColor(255, 255, 255, 0)); + _quickWindow->setFlags(_quickWindow->flags() | static_cast(Qt::WA_TranslucentBackground)); + + _renderControl->_renderWindow = _proxyWindow; + + _canvas = new OffscreenGLCanvas(); + if (!_canvas->create(shareContext)) { + qFatal("Failed to create OffscreenGLCanvas"); + return; + }; + + connect(_quickWindow, &QQuickWindow::focusObjectChanged, this, &OffscreenQmlSurface::onFocusObjectChanged); // Create a QML engine. _qmlEngine = new QQmlEngine; @@ -625,13 +320,25 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { importList.insert(importList.begin(), PathUtils::resourcesPath()); _qmlEngine->setImportPathList(importList); if (!_qmlEngine->incubationController()) { - _qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController()); + _qmlEngine->setIncubationController(_quickWindow->incubationController()); } - _qmlEngine->rootContext()->setContextProperty("GL", _renderer->getGLContextData()); + // FIXME +// _qmlEngine->rootContext()->setContextProperty("GL", _renderer->getGLContextData()); _qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); _qmlComponent = new QQmlComponent(_qmlEngine); + + connect(_renderControl, &QQuickRenderControl::renderRequested, [this] { _render = true; }); + connect(_renderControl, &QQuickRenderControl::sceneChanged, [this] { _render = _polish = true; }); + + if (!_canvas->makeCurrent()) { + qWarning("Failed to make context current for QML Renderer"); + return; + } + _renderControl->initialize(_canvas->getContext()); + setupFbo(); + // When Quick says there is a need to render, we will not render immediately. Instead, // a timer with a small interval is used to get better performance. QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); @@ -646,7 +353,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { - if (!_renderer || !_renderer->_quickWindow) { + if (!_quickWindow) { return; } @@ -662,7 +369,7 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { std::max(static_cast(scale * newSize.height()), 10)); } - QSize currentSize = _renderer->_quickWindow->geometry().size(); + QSize currentSize = _quickWindow->geometry().size(); if (newSize == currentSize && !forceResize) { return; } @@ -673,12 +380,26 @@ void OffscreenQmlSurface::resize(const QSize& newSize_, bool forceResize) { _rootItem->setSize(newSize); } - { - QMutexLocker locker(&(_renderer->_mutex)); - _renderer->_newSize = newSize; + // Update our members + _quickWindow->setGeometry(QRect(QPoint(), newSize)); + _quickWindow->contentItem()->setSize(newSize); + + // Qt bug in 5.4 forces this check of pixel ratio, + // even though we're rendering offscreen. + qreal pixelRatio = 1.0; + if (_renderControl && _renderControl->_renderWindow) { + pixelRatio = _renderControl->_renderWindow->devicePixelRatio(); } - _renderer->_queue.add(RESIZE); + uvec2 newOffscreenSize = toGlm(newSize * pixelRatio); + if (newOffscreenSize == _size) { + return; + } + + qCDebug(glLogging) << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio; + _size = newOffscreenSize; + _textures.setSize(_size); + setupFbo(); } QQuickItem* OffscreenQmlSurface::getRootItem() { @@ -710,15 +431,6 @@ void OffscreenQmlSurface::clearCache() { getRootContext()->engine()->clearComponentCache(); } -void OffscreenQmlSurface::requestUpdate() { - _polish = true; - _render = true; -} - -void OffscreenQmlSurface::requestRender() { - _render = true; -} - QObject* OffscreenQmlSurface::finishQmlLoad(std::function f) { disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0); if (_qmlComponent->isError()) { @@ -770,8 +482,8 @@ QObject* OffscreenQmlSurface::finishQmlLoad(std::functionsetParentItem(_renderer->_quickWindow->contentItem()); - _rootItem->setSize(_renderer->_quickWindow->renderTargetSize()); + _rootItem->setParentItem(_quickWindow->contentItem()); + _rootItem->setSize(_quickWindow->renderTargetSize()); return _rootItem; } @@ -781,34 +493,22 @@ void OffscreenQmlSurface::updateQuick() { // b) already rendering a frame // c) rendering too fast // then skip this - if (!_renderer || _renderer->_rendering || !_renderer->allowNewFrame(_maxFps)) { + if (!allowNewFrame(_maxFps)) { return; } if (_polish) { - _renderer->_renderControl->polishItems(); + _renderControl->polishItems(); _polish = false; } if (_render) { PROFILE_RANGE(__FUNCTION__); - // Lock the GUI size while syncing - QMutexLocker locker(&(_renderer->_mutex)); - _renderer->_queue.add(RENDER); - // FIXME need to find a better way to handle the render lockout than this locking of the main thread - _renderer->_waitCondition.wait(&(_renderer->_mutex)); + render(); _render = false; } } -bool OffscreenQmlSurface::fetchTexture(TextureAndFence& texture) { - return _renderer->fetchTexture(texture); -} - -void OffscreenQmlSurface::releaseTexture(const TextureAndFence& texture) { - _renderer->releaseTexture(texture); -} - QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject) { vec2 sourceSize; if (dynamic_cast(sourceObject)) { @@ -818,7 +518,7 @@ QPointF OffscreenQmlSurface::mapWindowToUi(const QPointF& sourcePosition, QObjec } vec2 offscreenPosition = toGlm(sourcePosition); offscreenPosition /= sourceSize; - offscreenPosition *= vec2(toGlm(_renderer->_quickWindow->size())); + offscreenPosition *= vec2(toGlm(_quickWindow->size())); return QPointF(offscreenPosition.x, offscreenPosition.y); } @@ -832,7 +532,7 @@ QPointF OffscreenQmlSurface::mapToVirtualScreen(const QPointF& originalPoint, QO // bool OffscreenQmlSurface::filterEnabled(QObject* originalDestination, QEvent* event) const { - if (_renderer->_quickWindow == originalDestination) { + if (_quickWindow == originalDestination) { return false; } // Only intercept events while we're in an active state @@ -850,7 +550,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even // Don't intercept our own events, or we enter an infinite recursion QObject* recurseTest = originalDestination; while (recurseTest) { - Q_ASSERT(recurseTest != _rootItem && recurseTest != _renderer->_quickWindow); + Q_ASSERT(recurseTest != _rootItem && recurseTest != _quickWindow); recurseTest = recurseTest->parent(); } #endif @@ -869,7 +569,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even case QEvent::KeyPress: case QEvent::KeyRelease: { event->ignore(); - if (QCoreApplication::sendEvent(_renderer->_quickWindow, event)) { + if (QCoreApplication::sendEvent(_quickWindow, event)) { return event->isAccepted(); } break; @@ -883,7 +583,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even wheelEvent->delta(), wheelEvent->buttons(), wheelEvent->modifiers(), wheelEvent->orientation()); mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_renderer->_quickWindow, &mappedEvent)) { + if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { return mappedEvent.isAccepted(); } break; @@ -904,7 +604,7 @@ bool OffscreenQmlSurface::eventFilter(QObject* originalDestination, QEvent* even _qmlEngine->rootContext()->setContextProperty("lastMousePosition", transformedPos); } mappedEvent.ignore(); - if (QCoreApplication::sendEvent(_renderer->_quickWindow, &mappedEvent)) { + if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { return mappedEvent.isAccepted(); } break; @@ -923,7 +623,7 @@ void OffscreenQmlSurface::pause() { void OffscreenQmlSurface::resume() { _paused = false; - requestRender(); + _render = true; } bool OffscreenQmlSurface::isPaused() const { @@ -932,8 +632,8 @@ bool OffscreenQmlSurface::isPaused() const { void OffscreenQmlSurface::setProxyWindow(QWindow* window) { _proxyWindow = window; - if (_renderer && _renderer->_renderControl) { - _renderer->_renderControl->_renderWindow = window; + if (_renderControl) { + _renderControl->_renderWindow = window; } } @@ -942,11 +642,11 @@ QObject* OffscreenQmlSurface::getEventHandler() { } QQuickWindow* OffscreenQmlSurface::getWindow() { - return _renderer->_quickWindow; + return _quickWindow; } QSize OffscreenQmlSurface::size() const { - return _renderer->_quickWindow->geometry().size(); + return _quickWindow->geometry().size(); } QQmlContext* OffscreenQmlSurface::getRootContext() { diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index fa2346dd2f..7232e410ea 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -16,9 +16,11 @@ #include #include +#include "TextureRecycler.h" class QWindow; class QMyQuickRenderControl; +class OffscreenGLCanvas; class QOpenGLContext; class QQmlEngine; class QQmlContext; @@ -26,8 +28,6 @@ class QQmlComponent; class QQuickWindow; class QQuickItem; -class OffscreenQmlRenderThread; - class OffscreenQmlSurface : public QObject { Q_OBJECT Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged) @@ -86,8 +86,6 @@ signals: void focusTextChanged(bool focusText); public slots: - void requestUpdate(); - void requestRender(); void onAboutToQuit(); protected: @@ -97,24 +95,42 @@ protected: private: QObject* finishQmlLoad(std::function f); QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject); + void setupFbo(); + bool allowNewFrame(uint8_t fps); + void render(); + void resize(); + void cleanup(); private slots: void updateQuick(); void onFocusObjectChanged(QObject* newFocus); private: - friend class OffscreenQmlRenderThread; - OffscreenQmlRenderThread* _renderer{ nullptr }; - QQmlEngine* _qmlEngine{ nullptr }; - QQmlComponent* _qmlComponent{ nullptr }; - QQuickItem* _rootItem{ nullptr }; + QQuickWindow* _quickWindow { nullptr }; + QMyQuickRenderControl* _renderControl{ nullptr }; + QQmlEngine* _qmlEngine { nullptr }; + QQmlComponent* _qmlComponent { nullptr }; + QQuickItem* _rootItem { nullptr }; + OffscreenGLCanvas* _canvas { nullptr }; QTimer _updateTimer; - bool _render{ false }; - bool _polish{ true }; - bool _paused{ true }; + uint32_t _fbo { 0 }; + uint32_t _depthStencil { 0 }; + uint64_t _lastRenderTime { 0 }; + uvec2 _size { 1920, 1080 }; + TextureRecycler _textures { true }; + + // Texture management + std::mutex _textureMutex; + TextureAndFence _latestTextureAndFence { 0, 0 }; + std::list _returnedTextures; + + + bool _render { false }; + bool _polish { true }; + bool _paused { true }; bool _focusText { false }; - uint8_t _maxFps{ 60 }; - MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p.toPoint(); } }; + uint8_t _maxFps { 60 }; + MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } }; QWindow* _proxyWindow { nullptr }; }; diff --git a/libraries/gl/src/gl/TextureRecycler.cpp b/libraries/gl/src/gl/TextureRecycler.cpp new file mode 100644 index 0000000000..4438e158c5 --- /dev/null +++ b/libraries/gl/src/gl/TextureRecycler.cpp @@ -0,0 +1,83 @@ +// +// Created by Bradley Austin Davis on 2016-10-05 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "TextureRecycler.h" +#include "Config.h" + +#include + + +void TextureRecycler::setSize(const uvec2& size) { + if (size == _size) { + return; + } + _size = size; + while (!_readyTextures.empty()) { + _readyTextures.pop(); + } + std::set toDelete; + std::for_each(_allTextures.begin(), _allTextures.end(), [&](Map::const_reference item) { + if (!item.second._active && item.second._size != _size) { + toDelete.insert(item.first); + } + }); + std::for_each(toDelete.begin(), toDelete.end(), [&](Map::key_type key) { + _allTextures.erase(key); + }); +} + +void TextureRecycler::clear() { + while (!_readyTextures.empty()) { + _readyTextures.pop(); + } + _allTextures.clear(); +} + +uint32_t TextureRecycler::getNextTexture() { + if (_readyTextures.empty()) { + uint32_t newTexture; + glGenTextures(1, &newTexture); + glBindTexture(GL_TEXTURE_2D, newTexture); + if (_useMipmaps) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -0.2f); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8.0f); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, _size.x, _size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + _allTextures.emplace(std::piecewise_construct, std::forward_as_tuple(newTexture), std::forward_as_tuple(newTexture, _size)); + _readyTextures.push(newTexture); + } + + uint32_t result = _readyTextures.front(); + _readyTextures.pop(); + auto& item = _allTextures[result]; + item._active = true; + return result; +} + +void TextureRecycler::recycleTexture(GLuint texture) { + Q_ASSERT(_allTextures.count(texture)); + auto& item = _allTextures[texture]; + Q_ASSERT(item._active); + item._active = false; + if (item._size != _size) { + // Buh-bye + _allTextures.erase(texture); + return; + } + + _readyTextures.push(item._tex); +} + diff --git a/libraries/gl/src/gl/TextureRecycler.h b/libraries/gl/src/gl/TextureRecycler.h new file mode 100644 index 0000000000..46cbcad219 --- /dev/null +++ b/libraries/gl/src/gl/TextureRecycler.h @@ -0,0 +1,47 @@ +// +// Created by Bradley Austin Davis on 2015-04-04 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once +#ifndef hifi_TextureRecycler_h +#define hifi_TextureRecycler_h + +#include +#include +#include + +#include + +class TextureRecycler { +public: + TextureRecycler(bool useMipmaps) : _useMipmaps(useMipmaps) {} + void setSize(const uvec2& size); + void clear(); + uint32_t getNextTexture(); + void recycleTexture(uint32_t texture); + +private: + + struct TexInfo { + const uint32_t _tex{ 0 }; + const uvec2 _size; + bool _active { false }; + + TexInfo() {} + TexInfo(uint32_t tex, const uvec2& size) : _tex(tex), _size(size) {} + TexInfo(const TexInfo& other) : _tex(other._tex), _size(other._size) {} + }; + + using Map = std::map; + using Queue = std::queue; + + Map _allTextures; + Queue _readyTextures; + uvec2 _size{ 1920, 1080 }; + bool _useMipmaps; +}; + +#endif From 35755ddcb1f96d6ca433b98fbb081048484a60c0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 13:48:54 -0700 Subject: [PATCH 11/22] cleanups --- tools/ac-client/src/ACClientApp.cpp | 43 ----------------------------- 1 file changed, 43 deletions(-) diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index 3ea2322ffd..0d952e8cc1 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -68,31 +68,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : const_cast(&shared())->setEnabled(QtWarningMsg, false); } - // QString domainServerAddress = "127.0.0.1"; - // if (parser.isSet(domainAddressOption)) { - // // parse the IP and port combination for this target - // QString hostnamePortString = parser.value(domainAddressOption); - - // QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) }; - // quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() }; - // if (port == 0) { - // port = DEFAULT_DOMAIN_SERVER_PORT; - // } - - // if (address.isNull()) { - // qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" << - // "The parsed IP was" << address.toString() << "and the parsed port was" << port; - - // QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection); - // } else { - // _iceServerAddr = HifiSockAddr(address, port); - // } - - // if (_verbose) { - // qDebug() << "domain-server Address is" << domainServerAddress << "port is" << port; - // } - // } - QString domainServerAddress = "127.0.0.1:40103"; if (parser.isSet(domainAddressOption)) { domainServerAddress = parser.value(domainAddressOption); @@ -109,7 +84,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : Setting::preInit(); DependencyManager::registerInheritance(); - // DependencyManager::registerInheritance(); Setting::init(); DependencyManager::set([&]{ return QString("Mozilla/5.0 (HighFidelityACClient)"); }); @@ -135,7 +109,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : // put the NodeList and datagram processing on the node thread nodeList->moveToThread(nodeThread); - const DomainHandler& domainHandler = nodeList->getDomainHandler(); connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); @@ -152,25 +125,9 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : // connect(nodeList.data(), &NodeList::uuidChanged, this, &ACClientApp::setSessionUUID); // connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch); - // // you might think we could just do this in NodeList but we only want this connection for Interface - // connect(nodeList.data(), &NodeList::limitOfSilentDomainCheckInsReached, nodeList.data(), &NodeList::reset); - - // AccountManager _accountManager = new AccountManager(std::bind(&ACClientApp::getUserAgent, qApp)); - - // setState(lookUpStunServer); - - nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); - - // // send the identity packet for our avatar each second to our avatar mixer - // connect(&identityPacketTimer, &QTimer::timeout, getMyAvatar(), &MyAvatar::sendIdentityPacket); - // identityPacketTimer.start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); - - - // DependencyManager::get()->loadSettings(domainServerAddress); - DependencyManager::get()->handleLookupString(domainServerAddress, false); QTimer* doTimer = new QTimer(this); From 1dc52d3bf579d6d621fc9f3109427c5a566ffed2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 13:54:09 -0700 Subject: [PATCH 12/22] cleanups, fail if packet-version mismatch --- tools/ac-client/src/ACClientApp.cpp | 11 ++++++++--- tools/ac-client/src/ACClientApp.h | 6 +----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index 0d952e8cc1..f174f50ab6 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -113,8 +113,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : connect(&domainHandler, SIGNAL(hostnameChanged(const QString&)), SLOT(domainChanged(const QString&))); // connect(&domainHandler, SIGNAL(resetting()), SLOT(resettingDomain())); - // connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle())); - // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle())); // connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails())); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ACClientApp::domainConnectionRefused); @@ -123,7 +121,7 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : connect(nodeList.data(), &NodeList::nodeActivated, this, &ACClientApp::nodeActivated); // connect(nodeList.data(), &NodeList::uuidChanged, getMyAvatar(), &MyAvatar::setSessionUUID); // connect(nodeList.data(), &NodeList::uuidChanged, this, &ACClientApp::setSessionUUID); - // connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch); + connect(nodeList.data(), &NodeList::packetVersionMismatch, this, &ACClientApp::notifyPacketVersionMismatch); nodeList->addSetOfNodeTypesToNodeInterestSet(NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer << NodeType::AssetServer << NodeType::MessagesMixer); @@ -208,6 +206,13 @@ void ACClientApp::timedOut() { finish(1); } +void ACClientApp::notifyPacketVersionMismatch() { + if (_verbose) { + qDebug() << "packet version mismatch"; + } + finish(1); +} + void ACClientApp::finish(int exitCode) { auto nodeList = DependencyManager::get(); diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h index 36de592401..f6c726dfbc 100644 --- a/tools/ac-client/src/ACClientApp.h +++ b/tools/ac-client/src/ACClientApp.h @@ -27,23 +27,19 @@ public: ACClientApp(int argc, char* argv[]); ~ACClientApp(); - const int stunFailureExitStatus { 1 }; - const int iceFailureExitStatus { 2 }; - const int domainPingExitStatus { 3 }; - private slots: void domainConnectionRefused(const QString& reasonMessage, int reasonCodeInt, const QString& extraInfo); void domainChanged(const QString& domainHostname); void nodeAdded(SharedNodePointer node); void nodeActivated(SharedNodePointer node); void nodeKilled(SharedNodePointer node); + void notifyPacketVersionMismatch(); private: NodeList* _nodeList; void timedOut(); void finish(int exitCode); bool _verbose; - QTimer* _pingDomainTimer { nullptr }; bool _sawEntityServer { false }; bool _sawAudioMixer { false }; From caf7d878416cce64f89dc3eae99fcdaddded74cd Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 5 Oct 2016 14:01:20 -0700 Subject: [PATCH 13/22] Restore GL context information to the QML context --- libraries/gl/src/gl/OffscreenQmlSurface.cpp | 18 ++---------------- libraries/gl/src/gl/OffscreenQmlSurface.h | 4 +++- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index bcfaaffd08..29296425e7 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -116,21 +116,6 @@ QNetworkAccessManager* QmlNetworkAccessManagerFactory::create(QObject* parent) { Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") -#if 0 -QJsonObject getGLContextData(); -QJsonObject _glData; - -QJsonObject OffscreenQmlSurface::getGLContextData() { - _glMutex.lock(); - if (_glData.isEmpty()) { - _glWait.wait(&_glMutex); - } - _glMutex.unlock(); - return _glData; -} -#endif - - void OffscreenQmlSurface::setupFbo() { _canvas->makeCurrent(); _textures.setSize(_size); @@ -324,7 +309,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { } // FIXME -// _qmlEngine->rootContext()->setContextProperty("GL", _renderer->getGLContextData()); + _qmlEngine->rootContext()->setContextProperty("GL", _glData); _qmlEngine->rootContext()->setContextProperty("offscreenWindow", QVariant::fromValue(getWindow())); _qmlComponent = new QQmlComponent(_qmlEngine); @@ -336,6 +321,7 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { qWarning("Failed to make context current for QML Renderer"); return; } + _glData = ::getGLContextData(); _renderControl->initialize(_canvas->getContext()); setupFbo(); diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index 7232e410ea..74e5f60b36 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -98,8 +98,8 @@ private: void setupFbo(); bool allowNewFrame(uint8_t fps); void render(); - void resize(); void cleanup(); + QJsonObject getGLContextData(); private slots: void updateQuick(); @@ -112,6 +112,8 @@ private: QQmlComponent* _qmlComponent { nullptr }; QQuickItem* _rootItem { nullptr }; OffscreenGLCanvas* _canvas { nullptr }; + QJsonObject _glData; + QTimer _updateTimer; uint32_t _fbo { 0 }; uint32_t _depthStencil { 0 }; From 6afe5360fe3d7f417769ff4d1368817493388b1c Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 5 Oct 2016 14:36:26 -0700 Subject: [PATCH 14/22] Fixing header --- libraries/gl/src/gl/OffscreenQmlSurface.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.h b/libraries/gl/src/gl/OffscreenQmlSurface.h index 74e5f60b36..30b9b2a58a 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.h +++ b/libraries/gl/src/gl/OffscreenQmlSurface.h @@ -9,11 +9,14 @@ #ifndef hifi_OffscreenQmlSurface_h #define hifi_OffscreenQmlSurface_h -#include -#include #include #include +#include +#include +#include + + #include #include #include "TextureRecycler.h" From 86a31903851c2ee9b82933aa6ba12d7ca124e54c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 14:45:32 -0700 Subject: [PATCH 15/22] on failure, print names of servers that didn't connect --- tools/ac-client/src/ACClientApp.cpp | 19 +++++++++++++++++++ tools/ac-client/src/ACClientApp.h | 1 + 2 files changed, 20 insertions(+) diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index f174f50ab6..df0f0c56ca 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -213,6 +213,24 @@ void ACClientApp::notifyPacketVersionMismatch() { finish(1); } +void ACClientApp::printFailedServers() { + if (!_sawEntityServer) { + qDebug() << "EntityServer"; + } + if (!_sawAudioMixer) { + qDebug() << "AudioMixer"; + } + if (!_sawAvatarMixer) { + qDebug() << "AvatarMixer"; + } + if (!_sawAssetServer) { + qDebug() << "AssetServer"; + } + if (!_sawMessagesMixer) { + qDebug() << "MessagesMixer"; + } +} + void ACClientApp::finish(int exitCode) { auto nodeList = DependencyManager::get(); @@ -230,5 +248,6 @@ void ACClientApp::finish(int exitCode) { nodeThread->quit(); nodeThread->wait(); + printFailedServers(); QCoreApplication::exit(exitCode); } diff --git a/tools/ac-client/src/ACClientApp.h b/tools/ac-client/src/ACClientApp.h index f6c726dfbc..29d571688e 100644 --- a/tools/ac-client/src/ACClientApp.h +++ b/tools/ac-client/src/ACClientApp.h @@ -38,6 +38,7 @@ private slots: private: NodeList* _nodeList; void timedOut(); + void printFailedServers(); void finish(int exitCode); bool _verbose; From 785d67b87a6377813d64b43c1946facac092c795 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 15:25:45 -0700 Subject: [PATCH 16/22] remove redundant addHelpOption --- tools/ac-client/src/ACClientApp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/ac-client/src/ACClientApp.cpp b/tools/ac-client/src/ACClientApp.cpp index df0f0c56ca..dad0a6d617 100644 --- a/tools/ac-client/src/ACClientApp.cpp +++ b/tools/ac-client/src/ACClientApp.cpp @@ -27,7 +27,6 @@ ACClientApp::ACClientApp(int argc, char* argv[]) : // parse command-line QCommandLineParser parser; parser.setApplicationDescription("High Fidelity AC client"); - parser.addHelpOption(); const QCommandLineOption helpOption = parser.addHelpOption(); From 0510411ac139fb963d6ad25900b11c88296c2b24 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 16:19:39 -0700 Subject: [PATCH 17/22] fix timer use --- tools/ice-client/src/ICEClientApp.cpp | 42 +++++++++++++-------------- tools/ice-client/src/ICEClientApp.h | 6 ++-- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index 992014ad7d..e0a08b7fcb 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -178,16 +178,9 @@ void ICEClientApp::doSomething() { qDebug() << "sending STUN request"; } _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); - _stunResponseTimerCanceled = false; - _stunResponseTimer.singleShot(stunResponseTimeoutMilliSeconds, this, [&] { - if (_stunResponseTimerCanceled) { - return; - } - if (_verbose) { - qDebug() << "timeout waiting for stun-server response"; - } - QCoreApplication::exit(stunFailureExitStatus); - }); + _stunResponseTimer.setSingleShot(true); + connect(&_iceResponseTimer, SIGNAL(timeout()), this, SLOT(stunResponseTimeout())); + _stunResponseTimer.start(stunResponseTimeoutMilliSeconds); setState(waitForStunResponse); } else { @@ -215,16 +208,9 @@ void ICEClientApp::doSomething() { } sendPacketToIceServer(PacketType::ICEServerQuery, _iceServerAddr, _sessionUUID, peerID); - _iceResponseTimerCanceled = false; - _iceResponseTimer.singleShot(iceResponseTimeoutMilliSeconds, this, [=] { - if (_iceResponseTimerCanceled) { - return; - } - if (_verbose) { - qDebug() << "timeout waiting for ice-server response"; - } - QCoreApplication::exit(iceFailureExitStatus); - }); + _iceResponseTimer.setSingleShot(true); + connect(&_iceResponseTimer, SIGNAL(timeout()), this, SLOT(iceResponseTimeout())); + _iceResponseTimer.start(iceResponseTimeoutMilliSeconds); } else if (_state == pause0) { setState(pause1); } else if (_state == pause1) { @@ -237,6 +223,20 @@ void ICEClientApp::doSomething() { } } +void ICEClientApp::iceResponseTimeout() { + if (_verbose) { + qDebug() << "timeout waiting for ice-server response"; + } + QCoreApplication::exit(iceFailureExitStatus); +} + +void ICEClientApp::stunResponseTimeout() { + if (_verbose) { + qDebug() << "timeout waiting for stun-server response"; + } + QCoreApplication::exit(stunFailureExitStatus); +} + void ICEClientApp::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID) { std::unique_ptr icePacket = NLPacket::create(packetType); @@ -298,7 +298,6 @@ void ICEClientApp::processSTUNResponse(std::unique_ptr packet) } _stunResponseTimer.stop(); - _stunResponseTimerCanceled = true; uint16_t newPublicPort; QHostAddress newPublicAddress; @@ -331,7 +330,6 @@ void ICEClientApp::processPacket(std::unique_ptr packet) { if (nlPacket->getType() == PacketType::ICEServerPeerInformation) { // cancel the timeout timer _iceResponseTimer.stop(); - _iceResponseTimerCanceled = true; QDataStream iceResponseStream(message->getMessage()); if (!_domainServerPeerSet) { diff --git a/tools/ice-client/src/ICEClientApp.h b/tools/ice-client/src/ICEClientApp.h index 3635bc07f4..de6b6abb14 100644 --- a/tools/ice-client/src/ICEClientApp.h +++ b/tools/ice-client/src/ICEClientApp.h @@ -33,6 +33,10 @@ public: const int stunResponseTimeoutMilliSeconds { 2000 }; const int iceResponseTimeoutMilliSeconds { 2000 }; +public slots: + void iceResponseTimeout(); + void stunResponseTimeout(); + private: enum State { lookUpStunServer, // 0 @@ -83,9 +87,7 @@ private: int _state { 0 }; QTimer _stunResponseTimer; - bool _stunResponseTimerCanceled { false }; QTimer _iceResponseTimer; - bool _iceResponseTimerCanceled { false }; int _domainPingCount { 0 }; }; From f338e38bcf7eaeeae290ae721aa5d49275163c42 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 5 Oct 2016 16:25:00 -0700 Subject: [PATCH 18/22] oops --- tools/ice-client/src/ICEClientApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ice-client/src/ICEClientApp.cpp b/tools/ice-client/src/ICEClientApp.cpp index e0a08b7fcb..f9e7a76142 100644 --- a/tools/ice-client/src/ICEClientApp.cpp +++ b/tools/ice-client/src/ICEClientApp.cpp @@ -179,7 +179,7 @@ void ICEClientApp::doSomething() { } _socket->writeDatagram(stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr); _stunResponseTimer.setSingleShot(true); - connect(&_iceResponseTimer, SIGNAL(timeout()), this, SLOT(stunResponseTimeout())); + connect(&_stunResponseTimer, SIGNAL(timeout()), this, SLOT(stunResponseTimeout())); _stunResponseTimer.start(stunResponseTimeoutMilliSeconds); setState(waitForStunResponse); From bba5a7563f511c46104859f23ed124dc7b975b27 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 5 Oct 2016 19:25:03 -0700 Subject: [PATCH 19/22] Remove extraneous logging --- libraries/networking/src/AssetClient.cpp | 10 ++++------ libraries/networking/src/AssetResourceRequest.cpp | 2 -- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/libraries/networking/src/AssetClient.cpp b/libraries/networking/src/AssetClient.cpp index 624d98df7a..f5e2caa60d 100644 --- a/libraries/networking/src/AssetClient.cpp +++ b/libraries/networking/src/AssetClient.cpp @@ -394,14 +394,14 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message auto senderNode = node.toStrongRef(); if (!senderNode) { - qCDebug(asset_client) << "Got completed asset for node that no longer exists"; + qCWarning(asset_client) << "Got completed asset for node that no longer exists"; return; } // Check if we have any pending requests for this node auto messageMapIt = _pendingRequests.find(senderNode); if (messageMapIt == _pendingRequests.end()) { - qCDebug(asset_client) << "Got completed asset for a node that doesn't have any pending requests"; + qCWarning(asset_client) << "Got completed asset for a node that doesn't have any pending requests"; return; } @@ -411,7 +411,7 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message // Check if we have this pending request auto requestIt = messageCallbackMap.find(messageID); if (requestIt == messageCallbackMap.end()) { - qCDebug(asset_client) << "Got completed asset for a request that doesn't exist"; + qCWarning(asset_client) << "Got completed asset for a request that doesn't exist"; return; } @@ -419,16 +419,14 @@ void AssetClient::handleCompleteCallback(const QWeakPointer& node, Message auto& message = callbacks.message; if (!message) { - qCDebug(asset_client) << "Got completed asset for a message that doesn't exist"; + qCWarning(asset_client) << "Got completed asset for a message that doesn't exist"; return; } if (message->failed()) { - qCDebug(asset_client) << "Got failed asset"; callbacks.completeCallback(false, AssetServerError::NoError, QByteArray()); } else { - qCDebug(asset_client) << "Got asset"; callbacks.completeCallback(true, AssetServerError::NoError, message->readAll()); } diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index 8faf7082cd..fa946d3f4f 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -136,8 +136,6 @@ void AssetResourceRequest::requestHash(const AssetHash& hash) { Q_ASSERT(req->getState() == AssetRequest::Finished); cleanupTimer(); - - qDebug() << "Asset request finished, " << req->getHash() << "error: " << req->getError(); switch (req->getError()) { case AssetRequest::Error::NoError: From 17a1f9b5ae35536a45ce7ace8ce9e83f1cdc1fbb Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 5 Oct 2016 19:25:17 -0700 Subject: [PATCH 20/22] Make timer child of QObject instead of attaching to destroyed --- libraries/networking/src/AssetResourceRequest.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/networking/src/AssetResourceRequest.cpp b/libraries/networking/src/AssetResourceRequest.cpp index fa946d3f4f..f320f00dbb 100644 --- a/libraries/networking/src/AssetResourceRequest.cpp +++ b/libraries/networking/src/AssetResourceRequest.cpp @@ -41,8 +41,7 @@ void AssetResourceRequest::setupTimer() { Q_ASSERT(!_sendTimer); static const int TIMEOUT_MS = 2000; - _sendTimer = new QTimer(); - connect(this, &QObject::destroyed, _sendTimer, &QTimer::deleteLater); + _sendTimer = new QTimer(this); connect(_sendTimer, &QTimer::timeout, this, &AssetResourceRequest::onTimeout); _sendTimer->setSingleShot(true); From 4901f386ca8a68e81a4f3b28def9667adc0e8d01 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 5 Oct 2016 19:25:41 -0700 Subject: [PATCH 21/22] Remove unused property in ScriptCache --- libraries/script-engine/src/ScriptCache.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptCache.h b/libraries/script-engine/src/ScriptCache.h index 42feaac2ad..17ba5c4b0a 100644 --- a/libraries/script-engine/src/ScriptCache.h +++ b/libraries/script-engine/src/ScriptCache.h @@ -58,11 +58,10 @@ private: Mutex _containerLock; QMap _activeScriptRequests; - //QMultiMap _contentCallbacks; QHash _scriptCache; QMultiMap _scriptUsers; QSet _badScripts; }; -#endif // hifi_ScriptCache_h \ No newline at end of file +#endif // hifi_ScriptCache_h From 4171fdd2e1372bb9465d3bae465769bd45b4467e Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 5 Oct 2016 19:27:24 -0700 Subject: [PATCH 22/22] Remove extraneous log line from AssetRequest --- libraries/networking/src/AssetRequest.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/networking/src/AssetRequest.cpp b/libraries/networking/src/AssetRequest.cpp index 6f319df034..4449531177 100644 --- a/libraries/networking/src/AssetRequest.cpp +++ b/libraries/networking/src/AssetRequest.cpp @@ -150,8 +150,6 @@ void AssetRequest::start() { if (_error != NoError) { qCWarning(asset_client) << "Got error retrieving asset" << _hash << "- error code" << _error; - } else { - qCDebug(asset_client) << "Received asset successfully: " << _hash; } _state = Finished;