From 2f27d8ecd39fa69adb1ed61835c4684a1be43670 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Fri, 28 Feb 2014 16:14:02 -0800 Subject: [PATCH] Process geometry/textures in worker threads (via QThreadPool). Closes #2137. --- interface/src/renderer/FBXReader.cpp | 2 + interface/src/renderer/FBXReader.h | 3 ++ interface/src/renderer/GeometryCache.cpp | 57 +++++++++++++++++--- interface/src/renderer/GeometryCache.h | 2 + interface/src/renderer/TextureCache.cpp | 44 +++++++++++++-- interface/src/renderer/TextureCache.h | 2 + libraries/shared/src/RegisteredMetaTypes.cpp | 24 +++++++++ libraries/shared/src/RegisteredMetaTypes.h | 4 ++ libraries/shared/src/ResourceCache.cpp | 1 + libraries/shared/src/ResourceCache.h | 5 +- 10 files changed, 131 insertions(+), 13 deletions(-) diff --git a/interface/src/renderer/FBXReader.cpp b/interface/src/renderer/FBXReader.cpp index c9201f8f51..35d1dee9b0 100644 --- a/interface/src/renderer/FBXReader.cpp +++ b/interface/src/renderer/FBXReader.cpp @@ -28,6 +28,8 @@ using namespace std; +static int fbxGeometryMetaTypeId = qRegisterMetaType(); + template QVariant readBinaryArray(QDataStream& in) { quint32 arrayLength; quint32 encoding; diff --git a/interface/src/renderer/FBXReader.h b/interface/src/renderer/FBXReader.h index b89d0954b4..95622a2a8a 100644 --- a/interface/src/renderer/FBXReader.h +++ b/interface/src/renderer/FBXReader.h @@ -9,6 +9,7 @@ #ifndef __interface__FBXReader__ #define __interface__FBXReader__ +#include #include #include #include @@ -167,6 +168,8 @@ public: QVector attachments; }; +Q_DECLARE_METATYPE(FBXGeometry) + /// Reads an FST mapping from the supplied data. QVariantHash readMapping(const QByteArray& data); diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index cf59f9578a..0097c5ca20 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -8,6 +8,8 @@ #include #include +#include +#include #include "Application.h" #include "GeometryCache.h" @@ -443,6 +445,46 @@ void NetworkGeometry::clearLoadPriority(const QPointer& owner) { } } +/// Reads geometry in a worker thread. +class GeometryReader : public QRunnable { +public: + + GeometryReader(const QWeakPointer& geometry, const QUrl& url, + const QByteArray& data, const QVariantHash& mapping); + + virtual void run(); + +private: + + QWeakPointer _geometry; + QUrl _url; + QByteArray _data; + QVariantHash _mapping; +}; + +GeometryReader::GeometryReader(const QWeakPointer& geometry, const QUrl& url, + const QByteArray& data, const QVariantHash& mapping) : + _geometry(geometry), + _url(url), + _data(data), + _mapping(mapping) { +} + +void GeometryReader::run() { + QSharedPointer geometry = _geometry.toStrongRef(); + if (geometry.isNull()) { + return; + } + try { + QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, + _url.path().toLower().endsWith(".svo") ? readSVO(_data) : readFBX(_data, _mapping))); + + } catch (const QString& error) { + qDebug() << "Error reading " << _url << ": " << error; + QMetaObject::invokeMethod(geometry.data(), "finishedLoading", Q_ARG(bool, false)); + } +} + void NetworkGeometry::downloadFinished(QNetworkReply* reply) { QUrl url = reply->url(); QByteArray data = reply->readAll(); @@ -467,6 +509,7 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { for (QVariantHash::const_iterator it = lods.begin(); it != lods.end(); it++) { QSharedPointer geometry(new NetworkGeometry(url.resolved(it.key()), QSharedPointer(), true, _mapping, _textureBase)); + geometry->setSelf(geometry.staticCast()); geometry->setLODParent(_lodParent); _lods.insert(it.value().toFloat(), geometry); } @@ -481,14 +524,12 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) { return; } - try { - _geometry = url.path().toLower().endsWith(".svo") ? readSVO(data) : readFBX(data, _mapping); - - } catch (const QString& error) { - qDebug() << "Error reading " << url << ": " << error; - finishedLoading(false); - return; - } + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new GeometryReader(_self, url, data, _mapping)); +} + +void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { + _geometry = geometry; foreach (const FBXMesh& mesh, _geometry.meshes) { NetworkMesh networkMesh = { QOpenGLBuffer(QOpenGLBuffer::IndexBuffer), QOpenGLBuffer(QOpenGLBuffer::VertexBuffer) }; diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 2e6500b86a..8c59ee7dc5 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -90,6 +90,8 @@ protected: virtual void downloadFinished(QNetworkReply* reply); + Q_INVOKABLE void setGeometry(const FBXGeometry& geometry); + private: friend class GeometryCache; diff --git a/interface/src/renderer/TextureCache.cpp b/interface/src/renderer/TextureCache.cpp index 2b14ff8c18..22569f925a 100644 --- a/interface/src/renderer/TextureCache.cpp +++ b/interface/src/renderer/TextureCache.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include @@ -129,6 +131,7 @@ QSharedPointer TextureCache::getTexture(const QUrl& url, bool no QSharedPointer texture = _dilatableNetworkTextures.value(url); if (texture.isNull()) { texture = QSharedPointer(new DilatableNetworkTexture(url)); + texture->setSelf(texture); _dilatableNetworkTextures.insert(url, texture); } return texture; @@ -267,8 +270,30 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) : glBindTexture(GL_TEXTURE_2D, 0); } -void NetworkTexture::downloadFinished(QNetworkReply* reply) { - QImage image = QImage::fromData(reply->readAll()); +class ImageReader : public QRunnable { +public: + + ImageReader(const QWeakPointer& texture, const QByteArray& data); + + virtual void run(); + +private: + + QWeakPointer _texture; + QByteArray _data; +}; + +ImageReader::ImageReader(const QWeakPointer& texture, const QByteArray& data) : + _texture(texture), + _data(data) { +} + +void ImageReader::run() { + QSharedPointer texture = _texture.toStrongRef(); + if (texture.isNull()) { + return; + } + QImage image = QImage::fromData(_data); if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } @@ -292,8 +317,19 @@ void NetworkTexture::downloadFinished(QNetworkReply* reply) { } } int imageArea = image.width() * image.height(); - _averageColor = accumulated / (imageArea * EIGHT_BIT_MAXIMUM); - _translucent = (translucentPixels >= imageArea / 2); + QMetaObject::invokeMethod(texture.data(), "setImage", Q_ARG(const QImage&, image), + Q_ARG(const glm::vec4&, accumulated / (imageArea * EIGHT_BIT_MAXIMUM)), + Q_ARG(bool, translucentPixels >= imageArea / 2)); +} + +void NetworkTexture::downloadFinished(QNetworkReply* reply) { + // send the reader off to the thread pool + QThreadPool::globalInstance()->start(new ImageReader(_self, reply->readAll())); +} + +void NetworkTexture::setImage(const QImage& image, const glm::vec4& averageColor, bool translucent) { + _averageColor = averageColor; + _translucent = translucent; finishedLoading(true); imageLoaded(image); diff --git a/interface/src/renderer/TextureCache.h b/interface/src/renderer/TextureCache.h index 33b2834a9d..dba34a0d0e 100644 --- a/interface/src/renderer/TextureCache.h +++ b/interface/src/renderer/TextureCache.h @@ -129,6 +129,8 @@ protected: virtual void downloadFinished(QNetworkReply* reply); virtual void imageLoaded(const QImage& image); + Q_INVOKABLE void setImage(const QImage& image, const glm::vec4& averageColor, bool translucent); + private: glm::vec4 _averageColor; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 0358f169b8..d106977ae0 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -10,7 +10,15 @@ #include "RegisteredMetaTypes.h" +static int vec4MetaTypeId = qRegisterMetaType(); +static int vec3MetaTypeId = qRegisterMetaType(); +static int vec2MetaTypeId = qRegisterMetaType(); +static int quatMetaTypeId = qRegisterMetaType(); +static int xColorMetaTypeId = qRegisterMetaType(); +static int pickRayMetaTypeId = qRegisterMetaType(); + void registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, vec4toScriptValue, vec4FromScriptValue); qScriptRegisterMetaType(engine, vec3toScriptValue, vec3FromScriptValue); qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue); qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue); @@ -18,6 +26,22 @@ void registerMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, pickRayToScriptValue, pickRayFromScriptValue); } +QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", vec4.x); + obj.setProperty("y", vec4.y); + obj.setProperty("z", vec4.z); + obj.setProperty("w", vec4.w); + return obj; +} + +void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4) { + vec4.x = object.property("x").toVariant().toFloat(); + vec4.y = object.property("y").toVariant().toFloat(); + vec4.z = object.property("z").toVariant().toFloat(); + vec4.w = object.property("w").toVariant().toFloat(); +} + QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3) { QScriptValue obj = engine->newObject(); obj.setProperty("x", vec3.x); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 130a48cdeb..ea65e45c95 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -17,6 +17,7 @@ #include "SharedUtil.h" +Q_DECLARE_METATYPE(glm::vec4) Q_DECLARE_METATYPE(glm::vec3) Q_DECLARE_METATYPE(glm::vec2) Q_DECLARE_METATYPE(glm::quat) @@ -24,6 +25,9 @@ Q_DECLARE_METATYPE(xColor) void registerMetaTypes(QScriptEngine* engine); +QScriptValue vec4toScriptValue(QScriptEngine* engine, const glm::vec4& vec4); +void vec4FromScriptValue(const QScriptValue& object, glm::vec4& vec4); + QScriptValue vec3toScriptValue(QScriptEngine* engine, const glm::vec3 &vec3); void vec3FromScriptValue(const QScriptValue &object, glm::vec3 &vec3); diff --git a/libraries/shared/src/ResourceCache.cpp b/libraries/shared/src/ResourceCache.cpp index 86aca599ae..8dfdf1abb8 100644 --- a/libraries/shared/src/ResourceCache.cpp +++ b/libraries/shared/src/ResourceCache.cpp @@ -27,6 +27,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& if (resource.isNull()) { resource = createResource(url, fallback.isValid() ? getResource(fallback, QUrl(), true) : QSharedPointer(), delayLoad, extra); + resource->setSelf(resource); _resources.insert(url, resource); } return resource; diff --git a/libraries/shared/src/ResourceCache.h b/libraries/shared/src/ResourceCache.h index 1c3f94ab7d..eae5e752e2 100644 --- a/libraries/shared/src/ResourceCache.h +++ b/libraries/shared/src/ResourceCache.h @@ -91,6 +91,8 @@ public: /// Checks whether the resource has loaded. bool isLoaded() const { return _loaded; } + void setSelf(const QWeakPointer& self) { _self = self; } + protected slots: void attemptRequest(); @@ -100,13 +102,14 @@ protected: virtual void downloadFinished(QNetworkReply* reply) = 0; /// Should be called by subclasses when all the loading that will be done has been done. - void finishedLoading(bool success); + Q_INVOKABLE void finishedLoading(bool success); QNetworkRequest _request; bool _startedLoading; bool _failedToLoad; bool _loaded; QHash, float> _loadPriorities; + QWeakPointer _self; private slots: