From 7da87d6e156bd93802261d8941e9c1ec7126671b Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 26 Mar 2015 18:37:55 -0700 Subject: [PATCH] set up a way to request ResourceCache downloads from a non-networking thread. --- interface/src/Application.cpp | 12 ++++- interface/src/Application.h | 2 + libraries/networking/src/ResourceCache.cpp | 49 ++++++++++++-------- libraries/networking/src/ResourceCache.h | 12 ++++- libraries/render-utils/src/GeometryCache.cpp | 4 +- libraries/render-utils/src/GeometryCache.h | 3 +- libraries/render-utils/src/Model.cpp | 16 ++++++- libraries/render-utils/src/Model.h | 4 +- 8 files changed, 73 insertions(+), 29 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dc8fa08c4e..a69fc3659d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -344,6 +344,13 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // put the NodeList and datagram processing on the node thread nodeList->moveToThread(nodeThread); + // geometry background downloads need to happen on the Datagram Processor Thread. The idle loop will + // emit checkBackgroundDownloads to cause the GeometryCache to check it's queue for requested background + // downloads. + QSharedPointer geometryCacheP = DependencyManager::get(); + ResourceCache *geometryCache = geometryCacheP.data(); + connect(this, &Application::checkBackgroundDownloads, geometryCache, &ResourceCache::checkAsynchronousGets); + // connect the DataProcessor processDatagrams slot to the QUDPSocket readyRead() signal connect(&nodeList->getNodeSocket(), &QUdpSocket::readyRead, _datagramProcessor, &DatagramProcessor::processDatagrams); @@ -1569,6 +1576,9 @@ void Application::idle() { idleTimer->start(2); } } + + // check for any requested background downloads. + emit checkBackgroundDownloads(); } void Application::setFullscreen(bool fullscreen) { @@ -3140,7 +3150,7 @@ void Application::renderRearViewMirror(const QRect& region, bool billboard) { int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); - bool eyeRelativeCamera = false; + // bool eyeRelativeCamera = false; if (billboard) { _mirrorCamera.setFieldOfView(BILLBOARD_FIELD_OF_VIEW); // degees _mirrorCamera.setPosition(_myAvatar->getPosition() + diff --git a/interface/src/Application.h b/interface/src/Application.h index 7764f297d3..54dbf60f3f 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -333,6 +333,8 @@ signals: void svoImportRequested(const QString& url); + void checkBackgroundDownloads(); + public slots: void domainChanged(const QString& domainHostname); void updateWindowTitle(); diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index b574eb1aeb..f6dfb94903 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -18,6 +18,7 @@ #include #include +#include #include "NetworkAccessManager.h" #include "ResourceCache.h" @@ -48,32 +49,42 @@ void ResourceCache::refresh(const QUrl& url) { } } +void ResourceCache::getResourceAsynchronously(const QUrl& url) { + qDebug() << "ResourceCache::getResourceAsynchronously" << url.toString(); + _resourcesToBeGottenLock.lockForWrite(); + _resourcesToBeGotten.enqueue(QUrl(url)); + _resourcesToBeGottenLock.unlock(); +} + +void ResourceCache::checkAsynchronousGets() { + assert(QThread::currentThread() == thread()); + _resourcesToBeGottenLock.lockForRead(); + if (_resourcesToBeGotten.isEmpty()) { + _resourcesToBeGottenLock.unlock(); + return; + } + QUrl url = _resourcesToBeGotten.dequeue(); + _resourcesToBeGottenLock.unlock(); + getResource(url); +} + QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& fallback, - bool delayLoad, void* extra, bool block) { + bool delayLoad, void* extra) { + QSharedPointer resource = _resources.value(url); + if (!resource.isNull()) { + return resource; + } + if (QThread::currentThread() != thread()) { - // This will re-call this method in the main thread. If block is true and the main thread - // is waiting on a lock, we'll deadlock here. - if (block) { - QSharedPointer result; - QMetaObject::invokeMethod(this, "getResource", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(QSharedPointer, result), Q_ARG(const QUrl&, url), - Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra)); - return result; - } else { - // Queue the re-invocation of this method, but if the main thread is blocked, don't wait. The - // return value may be NULL -- it's expected that this will be called again later, in order - // to receive the actual Resource. - QMetaObject::invokeMethod(this, "getResource", Qt::QueuedConnection, - Q_ARG(const QUrl&, url), - Q_ARG(const QUrl&, fallback), Q_ARG(bool, delayLoad), Q_ARG(void*, extra)); - return _resources.value(url); - } + assert(delayLoad); + getResourceAsynchronously(url); + return QSharedPointer(); } if (!url.isValid() && !url.isEmpty() && fallback.isValid()) { return getResource(fallback, QUrl(), delayLoad); } - QSharedPointer resource = _resources.value(url); + if (resource.isNull()) { resource = createResource(url, fallback.isValid() ? getResource(fallback, QUrl(), true) : QSharedPointer(), delayLoad, extra); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d4aa9a7fd9..c7aceb2e1a 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include @@ -79,6 +81,9 @@ public: void refresh(const QUrl& url); +public slots: + void checkAsynchronousGets(); + protected: qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; qint64 _unusedResourcesSize = 0; @@ -89,7 +94,7 @@ protected: /// \param delayLoad if true, don't load the resource immediately; wait until load is first requested /// \param extra extra data to pass to the creator, if appropriate Q_INVOKABLE QSharedPointer getResource(const QUrl& url, const QUrl& fallback = QUrl(), - bool delayLoad = false, void* extra = NULL, bool block = true); + bool delayLoad = false, void* extra = NULL); /// Creates a new resource. virtual QSharedPointer createResource(const QUrl& url, @@ -109,6 +114,11 @@ private: int _lastLRUKey = 0; static int _requestLimit; + + void getResourceAsynchronously(const QUrl& url); + QReadWriteLock _resourcesToBeGottenLock; + QQueue _resourcesToBeGotten; + }; /// Base class for resources. diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 29f76291ea..c18dfa4d5f 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -1770,8 +1770,8 @@ void GeometryCache::renderLine(const glm::vec2& p1, const glm::vec2& p2, } -QSharedPointer GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad, bool block) { - return getResource(url, fallback, delayLoad, NULL, block).staticCast(); +QSharedPointer GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) { + return getResource(url, fallback, delayLoad, NULL).staticCast(); } QSharedPointer GeometryCache::createResource(const QUrl& url, diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index a64d041fc1..37156a6c71 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -203,8 +203,7 @@ public: /// Loads geometry from the specified URL. /// \param fallback a fallback URL to load if the desired one is unavailable /// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested - QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), - bool delayLoad = false, bool block = true); + QSharedPointer getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); protected: diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 873f458ccf..81c9f9448c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -325,6 +325,8 @@ void Model::init() { _skinTranslucentProgram = gpu::ShaderPointer(gpu::Shader::createProgram(skinModelVertex, modelTranslucentPixel)); makeResult = gpu::Shader::makeProgram(*_skinTranslucentProgram, slotBindings); initSkinProgram(_skinTranslucentProgram, _skinTranslucentLocations); + + (void) makeResult; // quiet compiler } } @@ -1032,12 +1034,22 @@ void Model::setURL(const QUrl& url, const QUrl& fallback, bool retainCurrent, bo } } -void Model::setCollisionModelURL(const QUrl& url, const QUrl& fallback, bool delayLoad) { + +const QSharedPointer Model::getCollisionGeometry(bool delayLoad) +{ + if (_collisionGeometry.isNull() && !_collisionUrl.isEmpty()) { + _collisionGeometry = DependencyManager::get()->getGeometry(_collisionUrl, QUrl(), delayLoad); + } + + return _collisionGeometry; +} + +void Model::setCollisionModelURL(const QUrl& url) { if (_collisionUrl == url) { return; } _collisionUrl = url; - _collisionGeometry = DependencyManager::get()->getGeometry(url, fallback, delayLoad); + _collisionGeometry = DependencyManager::get()->getGeometry(url, QUrl(), true); } bool Model::getJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 05db20b056..3b4cbdd450 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -109,7 +109,7 @@ public: const QUrl& getURL() const { return _url; } // Set the model to use for collisions - Q_INVOKABLE void setCollisionModelURL(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false); + Q_INVOKABLE void setCollisionModelURL(const QUrl& url); const QUrl& getCollisionURL() const { return _collisionUrl; } /// Sets the distance parameter used for LOD computations. @@ -134,7 +134,7 @@ public: const QSharedPointer& getGeometry() const { return _geometry; } /// Returns a reference to the shared collision geometry. - const QSharedPointer getCollisionGeometry() {return _collisionGeometry; } + const QSharedPointer getCollisionGeometry(bool delayLoad = true); /// Returns the number of joint states in the model. int getJointStateCount() const { return _jointStates.size(); }