From e3131d20989d3770791e5a67dd2fcbaeaff1061e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 19 Apr 2016 21:25:59 -0700 Subject: [PATCH 01/57] Rm prefetchAnimation from avatar/rig --- interface/src/avatar/MyAvatar.cpp | 8 -------- interface/src/avatar/MyAvatar.h | 3 --- libraries/animation/src/Rig.cpp | 8 -------- libraries/animation/src/Rig.h | 2 -- 4 files changed, 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ea713e2d96..06d863eaac 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -627,14 +627,6 @@ void MyAvatar::restoreRoleAnimation(const QString& role) { _rig->restoreRoleAnimation(role); } -void MyAvatar::prefetchAnimation(const QString& url) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "prefetchAnimation", Q_ARG(const QString&, url)); - return; - } - _rig->prefetchAnimation(url); -} - void MyAvatar::saveData() { Settings settings; settings.beginGroup("Avatar"); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 35caabe0f7..2de8aa9405 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -143,9 +143,6 @@ public: // remove an animation role override and return to the standard animation. Q_INVOKABLE void restoreRoleAnimation(const QString& role); - // prefetch animation - Q_INVOKABLE void prefetchAnimation(const QString& url); - // Adds handler(animStateDictionaryIn) => animStateDictionaryOut, which will be invoked just before each animGraph state update. // The handler will be called with an animStateDictionaryIn that has all those properties specified by the (possibly empty) // propertiesList argument. However for debugging, if the properties argument is null, all internal animGraph state is provided. diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index b3f5e30d40..a7115199a2 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -152,14 +152,6 @@ void Rig::restoreRoleAnimation(const QString& role) { } } -void Rig::prefetchAnimation(const QString& url) { - - // This will begin loading the NetworkGeometry for the given URL. - // which should speed us up if we request it later via overrideAnimation. - auto clipNode = std::make_shared("prefetch", url, 0, 0, 1.0, false, false); - _prefetchedAnimations.push_back(clipNode); -} - void Rig::destroyAnimGraph() { _animSkeleton.reset(); _animLoader.reset(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 897fa358e8..f50ea04b1d 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -94,7 +94,6 @@ public: QStringList getAnimationRoles() const; void overrideRoleAnimation(const QString& role, const QString& url, float fps, bool loop, float firstFrame, float lastFrame); void restoreRoleAnimation(const QString& role); - void prefetchAnimation(const QString& url); void initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset); void reset(const FBXGeometry& geometry); @@ -322,7 +321,6 @@ protected: SimpleMovingAverage _averageLateralSpeed { 10 }; std::map _origRoleAnimations; - std::vector _prefetchedAnimations; bool _lastEnableInverseKinematics { true }; bool _enableInverseKinematics { true }; From 74e1f817a54e013377cc1d41eca5bb4af22a0531 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 19 Apr 2016 21:26:11 -0700 Subject: [PATCH 02/57] Add prefetch to all resource caches --- libraries/networking/src/ResourceCache.cpp | 22 ++++++++++++++++++++++ libraries/networking/src/ResourceCache.h | 5 +++++ 2 files changed, 27 insertions(+) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 68d9a61226..84dd307792 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -169,6 +169,11 @@ void ResourceCache::clearATPAssets() { } void ResourceCache::refreshAll() { + // Remove refs to prefetching resources + _prefetchingResourcesLock.lock(); + _prefetchingResources.clear(); + _prefetchingResourcesLock.unlock(); + // Clear all unused resources so we don't have to reload them clearUnusedResource(); resetResourceCounters(); @@ -217,6 +222,23 @@ QVariantList ResourceCache::getResourceList() { return list; } +void ResourceCache::prefetch(const QUrl& url) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "prefetch", Q_ARG(QUrl, url)); + } else { + auto resource = getResource(url); + // If it is not loaded, hold a ref until it is + if (!resource->isLoaded()) { + QMutexLocker lock(&_prefetchingResourcesLock); + _prefetchingResources.insert(url, resource); + connect(resource.data(), &Resource::finishedLoading, [this, url]{ + QMutexLocker lock(&_prefetchingResourcesLock); + this->_prefetchingResources.remove(url); + }); + } + } +} + void ResourceCache::setRequestLimit(int limit) { _requestLimit = limit; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index ed3dbf69b6..e284e24860 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -96,6 +96,8 @@ public: Q_INVOKABLE QVariantList getResourceList(); + Q_INVOKABLE void prefetch(const QUrl& url); + static void setRequestLimit(int limit); static int getRequestLimit() { return _requestLimit; } @@ -175,6 +177,9 @@ private: qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive }; QMap> _unusedResources; + + QMutex _prefetchingResourcesLock{ QMutex::Recursive }; + QMap> _prefetchingResources; }; /// Base class for resources. From 3c7fc95d6cd901e768e0f4943ea4ad422ee28693 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 19 Apr 2016 23:24:48 -0700 Subject: [PATCH 03/57] Allow url-only cache queries for tex, model --- .../model-networking/src/model-networking/ModelCache.cpp | 7 ++++--- .../model-networking/src/model-networking/TextureCache.cpp | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 6dd1d97d7f..3e685e6397 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -237,13 +237,14 @@ ModelCache::ModelCache() { QSharedPointer ModelCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { - const GeometryExtra* geometryExtra = static_cast(extra); - Resource* resource = nullptr; if (url.path().toLower().endsWith(".fst")) { resource = new GeometryMappingResource(url); } else { - resource = new GeometryDefinitionResource(url, geometryExtra->mapping, geometryExtra->textureBaseUrl); + const GeometryExtra* geometryExtra = static_cast(extra); + auto mapping = geometryExtra ? geometryExtra->mapping : QVariantHash(); + auto textureBaseUrl = geometryExtra ? geometryExtra->textureBaseUrl : QUrl(); + resource = new GeometryDefinitionResource(url, mapping, textureBaseUrl); } return QSharedPointer(resource, &Resource::deleter); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 7d18151f2c..c931b8bd2a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -216,7 +216,9 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, TextureTy QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { const TextureExtra* textureExtra = static_cast(extra); - return QSharedPointer(new NetworkTexture(url, textureExtra->type, textureExtra->content), + auto type = textureExtra ? textureExtra->type : TextureType::DEFAULT_TEXTURE; + auto content = textureExtra ? textureExtra->content : QByteArray(); + return QSharedPointer(new NetworkTexture(url, type, content), &Resource::deleter); } From 8ad8b5d0d966c70a5aedc86bc6c40eee6110b17e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 19 Apr 2016 22:03:40 -0700 Subject: [PATCH 04/57] Return ScriptableResource from prefetch --- libraries/networking/src/ResourceCache.cpp | 75 ++++++++++++++------ libraries/networking/src/ResourceCache.h | 41 +++++++++-- libraries/script-engine/src/ScriptEngine.cpp | 16 +++++ 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 84dd307792..7feeff114e 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -119,6 +119,57 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { return highestResource; } +ScriptableResource::ScriptableResource(const QSharedPointer& resource) : + QObject(nullptr), + _resource(resource) {} + +void ScriptableResource::finished(bool success) { + if (_progressConnection) { + disconnect(_progressConnection); + } + if (_finishedConnection) { + disconnect(_finishedConnection); + } + + _isLoaded = true; + _isFailed = !success; + + if (_isFailed) { + emit failedChanged(_isFailed); + } + emit loadedChanged(_isLoaded); +} + +ScriptableResource* ResourceCache::prefetch(const QUrl& url) { + auto result = new ScriptableResource(); + + if (QThread::currentThread() != thread()) { + // Must be called in thread to ensure getResource returns a valid pointer + QMetaObject::invokeMethod(this, "prefetch", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(ScriptableResource*, result), Q_ARG(QUrl, url)); + return result; + } + + + auto resource = getResource(url); + result->_resource = resource; + result->setObjectName(url.toString()); + + result->_resource = resource; + if (resource->isLoaded()) { + result->finished(!resource->_failedToLoad); + } else { + result->_progressConnection = connect( + resource.data(), &Resource::handleDownloadProgress, + result, &ScriptableResource::progressChanged); + result->_finishedConnection = connect( + resource.data(), &Resource::finished, + result, &ScriptableResource::finished); + } + + return result; +} + ResourceCache::ResourceCache(QObject* parent) : QObject(parent) { auto& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::disconnectedFromDomain, @@ -169,11 +220,6 @@ void ResourceCache::clearATPAssets() { } void ResourceCache::refreshAll() { - // Remove refs to prefetching resources - _prefetchingResourcesLock.lock(); - _prefetchingResources.clear(); - _prefetchingResourcesLock.unlock(); - // Clear all unused resources so we don't have to reload them clearUnusedResource(); resetResourceCounters(); @@ -221,24 +267,7 @@ QVariantList ResourceCache::getResourceList() { return list; } - -void ResourceCache::prefetch(const QUrl& url) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "prefetch", Q_ARG(QUrl, url)); - } else { - auto resource = getResource(url); - // If it is not loaded, hold a ref until it is - if (!resource->isLoaded()) { - QMutexLocker lock(&_prefetchingResourcesLock); - _prefetchingResources.insert(url, resource); - connect(resource.data(), &Resource::finishedLoading, [this, url]{ - QMutexLocker lock(&_prefetchingResourcesLock); - this->_prefetchingResources.remove(url); - }); - } - } -} - + void ResourceCache::setRequestLimit(int limit) { _requestLimit = limit; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index e284e24860..3cd749ebc9 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -78,6 +78,40 @@ private: QList> _loadingRequests; }; +/// Wrapper to expose resources to JS/QML +class ScriptableResource : public QObject { + Q_OBJECT + Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged) + Q_PROPERTY(bool failed READ isFailed NOTIFY failedChanged) +public: + ScriptableResource(const QSharedPointer& resource = QSharedPointer()); + virtual ~ScriptableResource() = default; + + bool isLoaded() const { return _isLoaded; } + bool isFailed() const { return _isFailed; } + +signals: + void progressChanged(uint64_t bytesReceived, uint64_t bytesTotal); + void loadedChanged(bool loaded); // analogous to &Resource::finished + void failedChanged(bool failed); + +private slots: + void finished(bool success); + +private: + friend class ResourceCache; + + // Holds a ref to the resource to keep it in scope + QSharedPointer _resource; + + QMetaObject::Connection _progressConnection; + QMetaObject::Connection _finishedConnection; + + bool _isLoaded{ false }; + bool _isFailed{ false }; +}; + +Q_DECLARE_METATYPE(ScriptableResource*); /// Base class for resource caches. class ResourceCache : public QObject { @@ -96,7 +130,8 @@ public: Q_INVOKABLE QVariantList getResourceList(); - Q_INVOKABLE void prefetch(const QUrl& url); + // This must be exposed as a ptr so the ScriptEngine may take ownership + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url); static void setRequestLimit(int limit); static int getRequestLimit() { return _requestLimit; } @@ -177,9 +212,6 @@ private: qint64 _unusedResourcesMaxSize = DEFAULT_UNUSED_MAX_SIZE; QReadWriteLock _unusedResourcesLock { QReadWriteLock::Recursive }; QMap> _unusedResources; - - QMutex _prefetchingResourcesLock{ QMutex::Recursive }; - QMap> _prefetchingResources; }; /// Base class for resources. @@ -292,6 +324,7 @@ private: void reinsert(); friend class ResourceCache; + friend class ScriptableResource; ResourceRequest* _request = nullptr; int _lruKey = 0; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 15f3ebb985..4c6a988f36 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -270,6 +270,20 @@ static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantR assert(false); } +// Templated qScriptRegisterMetaType fails to compile with raw pointers +using ScriptableResourceRawPtr = ScriptableResource*; + +static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, const ScriptableResourceRawPtr& resource) { + auto object = engine->newQObject( + const_cast(resource), + QScriptEngine::ScriptOwnership); + return object; +} + +static void scriptableResourceFromScriptValue(const QScriptValue& value, ScriptableResourceRawPtr& resource) { + resource = static_cast(value.toQObject()); +} + void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once @@ -332,6 +346,8 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); + qScriptRegisterMetaType(this, scriptableResourceToScriptValue, scriptableResourceFromScriptValue); + // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); From 7d9790ecb57ef6f8a3059c265c9767c0a5fca89d Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 01:25:44 -0700 Subject: [PATCH 05/57] Add request for garbage collection Add garbage collection request in prod --- libraries/script-engine/src/ScriptEngine.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index e8ce00c66c..605f30ed6e 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -131,6 +131,8 @@ public: Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const MouseEvent& event); Q_INVOKABLE void callEntityScriptMethod(const EntityItemID& entityID, const QString& methodName, const EntityItemID& otherID, const Collision& collision); + Q_INVOKABLE void requestGarbageCollection() { collectGarbage(); } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts Q_INVOKABLE void stop(); From f8bdcd1ef2687c4651c687826f3019f6b2496fed Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 01:27:10 -0700 Subject: [PATCH 06/57] Update away.js to use prefetch --- examples/away.js | 4 ++-- examples/theBird.js | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/away.js b/examples/away.js index 9c5aed98fa..6fc7452910 100644 --- a/examples/away.js +++ b/examples/away.js @@ -46,8 +46,8 @@ var AWAY_INTRO = { endFrame: 83.0 }; -// prefetch the kneel animation so it's resident in memory when we need it. -MyAvatar.prefetchAnimation(AWAY_INTRO.url); +// prefetch the kneel animation and hold a ref so it's always resident in memory when we need it. +var _animation = AnimationCache.prefetch(AWAY_INTRO.url); function playAwayAnimation() { MyAvatar.overrideAnimation(AWAY_INTRO.url, AWAY_INTRO.playbackRate, AWAY_INTRO.loopFlag, AWAY_INTRO.startFrame, AWAY_INTRO.endFrame); diff --git a/examples/theBird.js b/examples/theBird.js index 02b2e7fc5d..4adc6e3968 100644 --- a/examples/theBird.js +++ b/examples/theBird.js @@ -20,8 +20,6 @@ for (i = 0; i < l; i++) { print(roles[i]); } -MyAvatar.prefetchAnimation(THE_BIRD_RIGHT_URL); - // replace point animations with the bird! MyAvatar.overrideRoleAnimation("rightHandPointIntro", THE_BIRD_RIGHT_URL, 30, false, 0, 12); MyAvatar.overrideRoleAnimation("rightHandPointHold", THE_BIRD_RIGHT_URL, 30, false, 12, 12); From a148a69a35750934fb1073520cdf46736974665a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 15:36:19 -0700 Subject: [PATCH 07/57] Move cache to bottom of texture header --- .../src/model-networking/TextureCache.cpp | 4 +- .../src/model-networking/TextureCache.h | 103 +++++++++--------- 2 files changed, 52 insertions(+), 55 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index c931b8bd2a..287025c84a 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -155,7 +155,7 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type } -TextureCache::TextureLoaderFunc getTextureLoaderForType(TextureType type) { +NetworkTexture::TextureLoaderFunc getTextureLoaderForType(TextureType type) { switch (type) { case ALBEDO_TEXTURE: { return model::TextureUsage::createAlbedoTextureFromImage; @@ -195,7 +195,7 @@ TextureCache::TextureLoaderFunc getTextureLoaderForType(TextureType type) { } case CUSTOM_TEXTURE: { Q_ASSERT(false); - return TextureCache::TextureLoaderFunc(); + return NetworkTexture::TextureLoaderFunc(); break; } case DEFAULT_TEXTURE: diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index c614a7ceb3..948792ae70 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -25,9 +25,6 @@ namespace gpu { class Batch; } -class NetworkTexture; - -typedef QSharedPointer NetworkTexturePointer; enum TextureType { DEFAULT_TEXTURE, @@ -45,6 +42,56 @@ enum TextureType { CUSTOM_TEXTURE }; +/// A simple object wrapper for an OpenGL texture. +class Texture { +public: + gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); } + gpu::TextureSourcePointer _textureSource; +}; + +/// A texture loaded from the network. + +class NetworkTexture : public Resource, public Texture { + Q_OBJECT + +public: + + typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); + using TextureLoaderFunc = std::function; + + NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); + NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); + + int getOriginalWidth() const { return _originalWidth; } + int getOriginalHeight() const { return _originalHeight; } + int getWidth() const { return _width; } + int getHeight() const { return _height; } + + TextureLoaderFunc getTextureLoader() const; + +signals: + void networkTextureCreated(const QWeakPointer& self); + +protected: + + virtual bool isCacheable() const override { return _loaded; } + + virtual void downloadFinished(const QByteArray& data) override; + + Q_INVOKABLE void loadContent(const QByteArray& content); + Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); + +private: + TextureType _type; + TextureLoaderFunc _textureLoader; + int _originalWidth { 0 }; + int _originalHeight { 0 }; + int _width { 0 }; + int _height { 0 }; +}; + +using NetworkTexturePointer = QSharedPointer; + /// Stores cached textures, including render-to-texture targets. class TextureCache : public ResourceCache, public Dependency { Q_OBJECT @@ -78,9 +125,6 @@ public: NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, const QByteArray& content = QByteArray()); - typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); - - typedef std::function TextureLoaderFunc; protected: virtual QSharedPointer createResource(const QUrl& url, @@ -99,51 +143,4 @@ private: gpu::TexturePointer _normalFittingTexture; }; -/// A simple object wrapper for an OpenGL texture. -class Texture { -public: - gpu::TexturePointer getGPUTexture() const { return _textureSource->getGPUTexture(); } - gpu::TextureSourcePointer _textureSource; -}; - -/// A texture loaded from the network. - -class NetworkTexture : public Resource, public Texture { - Q_OBJECT - -public: - - typedef TextureCache::TextureLoaderFunc TextureLoaderFunc; - - NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); - NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); - - int getOriginalWidth() const { return _originalWidth; } - int getOriginalHeight() const { return _originalHeight; } - int getWidth() const { return _width; } - int getHeight() const { return _height; } - - TextureLoaderFunc getTextureLoader() const; - -signals: - void networkTextureCreated(const QWeakPointer& self); - -protected: - - virtual bool isCacheable() const override { return _loaded; } - - virtual void downloadFinished(const QByteArray& data) override; - - Q_INVOKABLE void loadContent(const QByteArray& content); - Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); - -private: - TextureType _type; - TextureLoaderFunc _textureLoader; - int _originalWidth { 0 }; - int _originalHeight { 0 }; - int _width { 0 }; - int _height { 0 }; -}; - #endif // hifi_TextureCache_h From c096b0dfa1d5b5cfc627a62795cd342ec6133f2c Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 15:47:47 -0700 Subject: [PATCH 08/57] Add TextureType to NetworkTexture for reflection --- .../src/EntityTreeRenderer.cpp | 4 +- .../src/model-networking/ModelCache.cpp | 32 ++++++------- .../src/model-networking/ModelCache.h | 2 + .../src/model-networking/TextureCache.cpp | 36 ++++++++------- .../src/model-networking/TextureCache.h | 46 ++++++++++--------- 5 files changed, 63 insertions(+), 57 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 09f50c5de3..b565982fa3 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -380,7 +380,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetTexture(zone->getKeyLightProperties().getAmbientURL(), CUBE_TEXTURE); + _ambientTexture = textureCache->getTexture(zone->getKeyLightProperties().getAmbientURL(), NetworkTexture::CUBE_TEXTURE); _pendingAmbientTexture = true; if (_ambientTexture && _ambientTexture->isLoaded()) { @@ -410,7 +410,7 @@ void EntityTreeRenderer::applyZonePropertiesToScene(std::shared_ptrgetTexture(zone->getSkyboxProperties().getURL(), CUBE_TEXTURE); + _skyboxTexture = textureCache->getTexture(zone->getSkyboxProperties().getURL(), NetworkTexture::CUBE_TEXTURE); _pendingSkyboxTexture = true; if (_skyboxTexture && _skyboxTexture->isLoaded()) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 3e685e6397..e4fb5c97f8 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -425,7 +425,7 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur { _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.albedoTexture, NetworkTexture::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); _albedoTransform = material.albedoTexture.transform; map->setTextureTransform(_albedoTransform); @@ -442,39 +442,39 @@ NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textur if (!material.normalTexture.filename.isEmpty()) { - auto type = (material.normalTexture.isBumpmap ? BUMP_TEXTURE : NORMAL_TEXTURE); + auto type = (material.normalTexture.isBumpmap ? NetworkTexture::BUMP_TEXTURE : NetworkTexture::NORMAL_TEXTURE); auto map = fetchTextureMap(textureBaseUrl, material.normalTexture, type, MapChannel::NORMAL_MAP); setTextureMap(MapChannel::NORMAL_MAP, map); } if (!material.roughnessTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.roughnessTexture, NetworkTexture::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); setTextureMap(MapChannel::ROUGHNESS_MAP, map); } else if (!material.glossTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.glossTexture, NetworkTexture::GLOSS_TEXTURE, MapChannel::ROUGHNESS_MAP); setTextureMap(MapChannel::ROUGHNESS_MAP, map); } if (!material.metallicTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, METALLIC_TEXTURE, MapChannel::METALLIC_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.metallicTexture, NetworkTexture::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); setTextureMap(MapChannel::METALLIC_MAP, map); } else if (!material.specularTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, SPECULAR_TEXTURE, MapChannel::METALLIC_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.specularTexture, NetworkTexture::SPECULAR_TEXTURE, MapChannel::METALLIC_MAP); setTextureMap(MapChannel::METALLIC_MAP, map); } if (!material.occlusionTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.occlusionTexture, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); setTextureMap(MapChannel::OCCLUSION_MAP, map); } if (!material.emissiveTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.emissiveTexture, NetworkTexture::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); setTextureMap(MapChannel::EMISSIVE_MAP, map); } if (!material.lightmapTexture.filename.isEmpty()) { - auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); + auto map = fetchTextureMap(textureBaseUrl, material.lightmapTexture, NetworkTexture::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); _lightmapTransform = material.lightmapTexture.transform; _lightmapParams = material.lightmapParams; map->setTextureTransform(_lightmapTransform); @@ -496,7 +496,7 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!albedoName.isEmpty()) { auto url = textureMap.contains(albedoName) ? textureMap[albedoName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); + auto map = fetchTextureMap(url, NetworkTexture::ALBEDO_TEXTURE, MapChannel::ALBEDO_MAP); map->setTextureTransform(_albedoTransform); // when reassigning the albedo texture we also check for the alpha channel used as opacity map->setUseAlphaChannel(true); @@ -505,39 +505,39 @@ void NetworkMaterial::setTextures(const QVariantMap& textureMap) { if (!normalName.isEmpty()) { auto url = textureMap.contains(normalName) ? textureMap[normalName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, NORMAL_TEXTURE, MapChannel::NORMAL_MAP); + auto map = fetchTextureMap(url, NetworkTexture::NORMAL_TEXTURE, MapChannel::NORMAL_MAP); setTextureMap(MapChannel::NORMAL_MAP, map); } if (!roughnessName.isEmpty()) { auto url = textureMap.contains(roughnessName) ? textureMap[roughnessName].toUrl() : QUrl(); // FIXME: If passing a gloss map instead of a roughmap how do we know? - auto map = fetchTextureMap(url, ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); + auto map = fetchTextureMap(url, NetworkTexture::ROUGHNESS_TEXTURE, MapChannel::ROUGHNESS_MAP); setTextureMap(MapChannel::ROUGHNESS_MAP, map); } if (!metallicName.isEmpty()) { auto url = textureMap.contains(metallicName) ? textureMap[metallicName].toUrl() : QUrl(); // FIXME: If passing a specular map instead of a metallic how do we know? - auto map = fetchTextureMap(url, METALLIC_TEXTURE, MapChannel::METALLIC_MAP); + auto map = fetchTextureMap(url, NetworkTexture::METALLIC_TEXTURE, MapChannel::METALLIC_MAP); setTextureMap(MapChannel::METALLIC_MAP, map); } if (!occlusionName.isEmpty()) { auto url = textureMap.contains(occlusionName) ? textureMap[occlusionName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); + auto map = fetchTextureMap(url, NetworkTexture::OCCLUSION_TEXTURE, MapChannel::OCCLUSION_MAP); setTextureMap(MapChannel::OCCLUSION_MAP, map); } if (!emissiveName.isEmpty()) { auto url = textureMap.contains(emissiveName) ? textureMap[emissiveName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); + auto map = fetchTextureMap(url, NetworkTexture::EMISSIVE_TEXTURE, MapChannel::EMISSIVE_MAP); setTextureMap(MapChannel::EMISSIVE_MAP, map); } if (!lightmapName.isEmpty()) { auto url = textureMap.contains(lightmapName) ? textureMap[lightmapName].toUrl() : QUrl(); - auto map = fetchTextureMap(url, LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); + auto map = fetchTextureMap(url, NetworkTexture::LIGHTMAP_TEXTURE, MapChannel::LIGHTMAP_MAP); map->setTextureTransform(_lightmapTransform); map->setLightmapOffsetScale(_lightmapParams.x, _lightmapParams.y); setTextureMap(MapChannel::LIGHTMAP_MAP, map); diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index f479dc9ce2..464cb557ff 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -170,6 +170,8 @@ protected: const bool& isOriginal() const { return _isOriginal; } private: + using TextureType = NetworkTexture::Type; + // Helpers for the ctors QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); model::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 287025c84a..191f911120 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -145,60 +145,62 @@ const gpu::TexturePointer& TextureCache::getNormalFittingTexture() { /// Extra data for creating textures. class TextureExtra { public: - TextureType type; + NetworkTexture::Type type; const QByteArray& content; }; -NetworkTexturePointer TextureCache::getTexture(const QUrl& url, TextureType type, const QByteArray& content) { +NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const QByteArray& content) { TextureExtra extra = { type, content }; return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast(); } -NetworkTexture::TextureLoaderFunc getTextureLoaderForType(TextureType type) { +NetworkTexture::TextureLoaderFunc getTextureLoaderForType(NetworkTexture::Type type) { + using Type = NetworkTexture; + switch (type) { - case ALBEDO_TEXTURE: { + case Type::ALBEDO_TEXTURE: { return model::TextureUsage::createAlbedoTextureFromImage; break; } - case EMISSIVE_TEXTURE: { + case Type::EMISSIVE_TEXTURE: { return model::TextureUsage::createEmissiveTextureFromImage; break; } - case LIGHTMAP_TEXTURE: { + case Type::LIGHTMAP_TEXTURE: { return model::TextureUsage::createLightmapTextureFromImage; break; } - case CUBE_TEXTURE: { + case Type::CUBE_TEXTURE: { return model::TextureUsage::createCubeTextureFromImage; break; } - case BUMP_TEXTURE: { + case Type::BUMP_TEXTURE: { return model::TextureUsage::createNormalTextureFromBumpImage; break; } - case NORMAL_TEXTURE: { + case Type::NORMAL_TEXTURE: { return model::TextureUsage::createNormalTextureFromNormalImage; break; } - case ROUGHNESS_TEXTURE: { + case Type::ROUGHNESS_TEXTURE: { return model::TextureUsage::createRoughnessTextureFromImage; break; } - case GLOSS_TEXTURE: { + case Type::GLOSS_TEXTURE: { return model::TextureUsage::createRoughnessTextureFromGlossImage; break; } - case SPECULAR_TEXTURE: { + case Type::SPECULAR_TEXTURE: { return model::TextureUsage::createMetallicTextureFromImage; break; } - case CUSTOM_TEXTURE: { + case Type::CUSTOM_TEXTURE: { Q_ASSERT(false); return NetworkTexture::TextureLoaderFunc(); break; } - case DEFAULT_TEXTURE: + case Type::DEFAULT_TEXTURE: default: { return model::TextureUsage::create2DTextureFromImage; break; @@ -207,7 +209,7 @@ NetworkTexture::TextureLoaderFunc getTextureLoaderForType(TextureType type) { } /// Returns a texture version of an image file -gpu::TexturePointer TextureCache::getImageTexture(const QString& path, TextureType type) { +gpu::TexturePointer TextureCache::getImageTexture(const QString& path, Type type) { QImage image = QImage(path); auto loader = getTextureLoaderForType(type); return gpu::TexturePointer(loader(image, QUrl::fromLocalFile(path).fileName().toStdString())); @@ -216,13 +218,13 @@ gpu::TexturePointer TextureCache::getImageTexture(const QString& path, TextureTy QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra) { const TextureExtra* textureExtra = static_cast(extra); - auto type = textureExtra ? textureExtra->type : TextureType::DEFAULT_TEXTURE; + auto type = textureExtra ? textureExtra->type : Type::DEFAULT_TEXTURE; auto content = textureExtra ? textureExtra->content : QByteArray(); return QSharedPointer(new NetworkTexture(url, type, content), &Resource::deleter); } -NetworkTexture::NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content) : +NetworkTexture::NetworkTexture(const QUrl& url, Type type, const QByteArray& content) : Resource(url, !content.isEmpty()), _type(type) { diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 948792ae70..ebdc2002c5 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -26,22 +26,6 @@ namespace gpu { class Batch; } -enum TextureType { - DEFAULT_TEXTURE, - ALBEDO_TEXTURE, - NORMAL_TEXTURE, - BUMP_TEXTURE, - SPECULAR_TEXTURE, - METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey - ROUGHNESS_TEXTURE, - GLOSS_TEXTURE, - EMISSIVE_TEXTURE, - CUBE_TEXTURE, - OCCLUSION_TEXTURE, - LIGHTMAP_TEXTURE, - CUSTOM_TEXTURE -}; - /// A simple object wrapper for an OpenGL texture. class Texture { public: @@ -55,11 +39,27 @@ class NetworkTexture : public Resource, public Texture { Q_OBJECT public: - + enum Type { + DEFAULT_TEXTURE, + ALBEDO_TEXTURE, + NORMAL_TEXTURE, + BUMP_TEXTURE, + SPECULAR_TEXTURE, + METALLIC_TEXTURE = SPECULAR_TEXTURE, // for now spec and metallic texture are the same, converted to grey + ROUGHNESS_TEXTURE, + GLOSS_TEXTURE, + EMISSIVE_TEXTURE, + CUBE_TEXTURE, + OCCLUSION_TEXTURE, + LIGHTMAP_TEXTURE, + CUSTOM_TEXTURE + }; + Q_ENUM(Type) + typedef gpu::Texture* TextureLoader(const QImage& image, const std::string& srcImageName); using TextureLoaderFunc = std::function; - - NetworkTexture(const QUrl& url, TextureType type, const QByteArray& content); + + NetworkTexture(const QUrl& url, Type type, const QByteArray& content); NetworkTexture(const QUrl& url, const TextureLoaderFunc& textureLoader, const QByteArray& content); int getOriginalWidth() const { return _originalWidth; } @@ -82,7 +82,7 @@ protected: Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight); private: - TextureType _type; + Type _type; TextureLoaderFunc _textureLoader; int _originalWidth { 0 }; int _originalHeight { 0 }; @@ -96,6 +96,8 @@ using NetworkTexturePointer = QSharedPointer; class TextureCache : public ResourceCache, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY + + using Type = NetworkTexture::Type; public: /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture @@ -119,10 +121,10 @@ public: const gpu::TexturePointer& getNormalFittingTexture(); /// Returns a texture version of an image file - static gpu::TexturePointer getImageTexture(const QString& path, TextureType type = DEFAULT_TEXTURE); + static gpu::TexturePointer getImageTexture(const QString& path, Type type = Type::DEFAULT_TEXTURE); /// Loads a texture from the specified URL. - NetworkTexturePointer getTexture(const QUrl& url, TextureType type = DEFAULT_TEXTURE, + NetworkTexturePointer getTexture(const QUrl& url, Type type = Type::DEFAULT_TEXTURE, const QByteArray& content = QByteArray()); protected: From df4857947b154cadc1523f4aec5f298db352a00e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 17:16:59 -0700 Subject: [PATCH 09/57] Add prefetch overload for texture specificity --- .../src/model-networking/TextureCache.cpp | 12 ++++++++++++ .../src/model-networking/TextureCache.h | 7 +++++-- libraries/networking/src/ResourceCache.cpp | 4 ++-- libraries/networking/src/ResourceCache.h | 4 +++- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 191f911120..af84ecd0f3 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -35,6 +35,12 @@ TextureCache::TextureCache() { const qint64 TEXTURE_DEFAULT_UNUSED_MAX_SIZE = DEFAULT_UNUSED_MAX_SIZE; setUnusedResourceCacheSize(TEXTURE_DEFAULT_UNUSED_MAX_SIZE); setObjectName("TextureCache"); + + // Expose enum Type to JS/QML via properties + auto metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); ++i) { + setProperty(metaEnum.key(i), metaEnum.value(i)); + } } TextureCache::~TextureCache() { @@ -149,6 +155,12 @@ public: const QByteArray& content; }; +ScriptableResource* TextureCache::prefetch(const QUrl& url, int type) { + auto byteArray = QByteArray(); + TextureExtra extra = { (Type)type, byteArray }; + return ResourceCache::prefetch(url, &extra); +} + NetworkTexturePointer TextureCache::getTexture(const QUrl& url, Type type, const QByteArray& content) { TextureExtra extra = { type, content }; return ResourceCache::getResource(url, QUrl(), content.isEmpty(), &extra).staticCast(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index ebdc2002c5..fced2b3c5c 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -34,7 +35,6 @@ public: }; /// A texture loaded from the network. - class NetworkTexture : public Resource, public Texture { Q_OBJECT @@ -98,8 +98,11 @@ class TextureCache : public ResourceCache, public Dependency { SINGLETON_DEPENDENCY using Type = NetworkTexture::Type; - + public: + // Overload ResourceCache::prefetch to allow specifying texture type for loads + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type); + /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and /// the second, a set of random unit vectors to be used as noise gradients. diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 7feeff114e..f6b79e3acb 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -140,7 +140,7 @@ void ScriptableResource::finished(bool success) { emit loadedChanged(_isLoaded); } -ScriptableResource* ResourceCache::prefetch(const QUrl& url) { +ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { auto result = new ScriptableResource(); if (QThread::currentThread() != thread()) { @@ -151,7 +151,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url) { } - auto resource = getResource(url); + auto resource = getResource(url, QUrl(), false, extra); result->_resource = resource; result->setObjectName(url.toString()); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 3cd749ebc9..d678196ea3 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -131,7 +131,7 @@ public: Q_INVOKABLE QVariantList getResourceList(); // This must be exposed as a ptr so the ScriptEngine may take ownership - Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url); + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); } static void setRequestLimit(int limit); static int getRequestLimit() { return _requestLimit; } @@ -164,6 +164,8 @@ private slots: void clearATPAssets(); protected: + ScriptableResource* prefetch(const QUrl& url, void* extra); + /// Loads a resource 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 resource immediately; wait until load is first requested From 825431829deb932f457d6aa57b0bb8f8dd6f4071 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 17:22:15 -0700 Subject: [PATCH 10/57] Add url property to ScriptableResource --- libraries/networking/src/ResourceCache.cpp | 6 +++--- libraries/networking/src/ResourceCache.h | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index f6b79e3acb..4a80628da4 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -119,9 +119,9 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { return highestResource; } -ScriptableResource::ScriptableResource(const QSharedPointer& resource) : +ScriptableResource::ScriptableResource(const QUrl& url) : QObject(nullptr), - _resource(resource) {} + _url(url) {} void ScriptableResource::finished(bool success) { if (_progressConnection) { @@ -141,7 +141,7 @@ void ScriptableResource::finished(bool success) { } ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { - auto result = new ScriptableResource(); + auto result = new ScriptableResource(url); if (QThread::currentThread() != thread()) { // Must be called in thread to ensure getResource returns a valid pointer diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d678196ea3..35a0710aa6 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -81,12 +81,15 @@ private: /// Wrapper to expose resources to JS/QML class ScriptableResource : public QObject { Q_OBJECT + Q_PROPERTY(QUrl url READ getUrl) Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged) Q_PROPERTY(bool failed READ isFailed NOTIFY failedChanged) + public: - ScriptableResource(const QSharedPointer& resource = QSharedPointer()); + ScriptableResource(const QUrl& url); virtual ~ScriptableResource() = default; + const QUrl& getUrl() const { return _url; } bool isLoaded() const { return _isLoaded; } bool isFailed() const { return _isFailed; } @@ -107,6 +110,7 @@ private: QMetaObject::Connection _progressConnection; QMetaObject::Connection _finishedConnection; + QUrl _url; bool _isLoaded{ false }; bool _isFailed{ false }; }; From 5ec82fa43d89fcf32c99fd75390013802d804fab Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 17:27:00 -0700 Subject: [PATCH 11/57] Add release invokable to ScriptableResource --- libraries/networking/src/ResourceCache.cpp | 21 +++++++++++++++------ libraries/networking/src/ResourceCache.h | 4 ++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 4a80628da4..d1dabd5693 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -123,13 +123,13 @@ ScriptableResource::ScriptableResource(const QUrl& url) : QObject(nullptr), _url(url) {} +void ScriptableResource::release() { + disconnectHelper(); + _resource.reset(); +} + void ScriptableResource::finished(bool success) { - if (_progressConnection) { - disconnect(_progressConnection); - } - if (_finishedConnection) { - disconnect(_finishedConnection); - } + disconnectHelper(); _isLoaded = true; _isFailed = !success; @@ -140,6 +140,15 @@ void ScriptableResource::finished(bool success) { emit loadedChanged(_isLoaded); } +void ScriptableResource::disconnectHelper() { + if (_progressConnection) { + disconnect(_progressConnection); + } + if (_finishedConnection) { + disconnect(_finishedConnection); + } +} + ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { auto result = new ScriptableResource(url); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 35a0710aa6..997c9be37d 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -89,6 +89,8 @@ public: ScriptableResource(const QUrl& url); virtual ~ScriptableResource() = default; + Q_INVOKABLE void release(); + const QUrl& getUrl() const { return _url; } bool isLoaded() const { return _isLoaded; } bool isFailed() const { return _isFailed; } @@ -102,6 +104,8 @@ private slots: void finished(bool success); private: + void disconnectHelper(); + friend class ResourceCache; // Holds a ref to the resource to keep it in scope From 1a0a623d5f27a4ba12d2e574560e3b6e96d131f5 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 18:08:16 -0700 Subject: [PATCH 12/57] Report ScriptableResource memory cost to engine --- libraries/networking/src/ResourceCache.cpp | 14 ++++++++--- libraries/networking/src/ResourceCache.h | 26 ++++++++++++++------ libraries/script-engine/src/ScriptEngine.cpp | 7 ++++++ libraries/script-engine/src/ScriptEngine.h | 1 + 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index d1dabd5693..eb69f75279 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -128,6 +128,13 @@ void ScriptableResource::release() { _resource.reset(); } +void ScriptableResource::updateMemoryCost(const QObject* engine) { + if (_resource && !_resource->isInScript()) { + _resource->setInScript(true); + connect(_resource.data(), SIGNAL(updateSize(qint64)), engine, SLOT(updateMemoryCost(qint64))); + } +} + void ScriptableResource::finished(bool success) { disconnectHelper(); @@ -329,6 +336,7 @@ QSharedPointer ResourceCache::getResource(const QUrl& url, const QUrl& getResource(fallback, QUrl(), true) : QSharedPointer(), delayLoad, extra); resource->setSelf(resource); resource->setCache(this); + connect(resource.data(), &Resource::updateSize, this, &ResourceCache::updateTotalSize); { QWriteLocker locker(&_resourcesLock); _resources.insert(url, resource); @@ -414,8 +422,8 @@ void ResourceCache::removeResource(const QUrl& url, qint64 size) { _totalResourcesSize -= size; } -void ResourceCache::updateTotalSize(const qint64& oldSize, const qint64& newSize) { - _totalResourcesSize += (newSize - oldSize); +void ResourceCache::updateTotalSize(const qint64& deltaSize) { + _totalResourcesSize += deltaSize; emit dirty(); } @@ -592,7 +600,7 @@ void Resource::finishedLoading(bool success) { } void Resource::setSize(const qint64& bytes) { - QMetaObject::invokeMethod(_cache.data(), "updateTotalSize", Q_ARG(qint64, _bytes), Q_ARG(qint64, bytes)); + emit updateSize(bytes - _bytes); _bytes = bytes; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 997c9be37d..5b76cb48cd 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -95,6 +95,9 @@ public: bool isLoaded() const { return _isLoaded; } bool isFailed() const { return _isFailed; } + // Connects to a SLOT(updateMemoryCost(qint64) on the given engine + void updateMemoryCost(const QObject* engine); + signals: void progressChanged(uint64_t bytesReceived, uint64_t bytesTotal); void loadedChanged(bool loaded); // analogous to &Resource::finished @@ -166,7 +169,7 @@ public slots: void checkAsynchronousGets(); protected slots: - void updateTotalSize(const qint64& oldSize, const qint64& newSize); + void updateTotalSize(const qint64& deltaSize); private slots: void clearATPAssets(); @@ -291,6 +294,9 @@ signals: /// Fired when the resource is refreshed. void onRefresh(); + /// Fired when the size changes (through setSize). + void updateSize(qint64 deltaSize); + protected slots: void attemptRequest(); @@ -332,17 +338,21 @@ private: void makeRequest(); void retry(); void reinsert(); + + bool isInScript() const { return _isInScript; } + void setInScript(bool isInScript) { _isInScript = isInScript; } friend class ResourceCache; friend class ScriptableResource; - ResourceRequest* _request = nullptr; - int _lruKey = 0; - QTimer* _replyTimer = nullptr; - qint64 _bytesReceived = 0; - qint64 _bytesTotal = 0; - qint64 _bytes = 0; - int _attempts = 0; + ResourceRequest* _request{ nullptr }; + int _lruKey{ 0 }; + QTimer* _replyTimer{ nullptr }; + qint64 _bytesReceived{ 0 }; + qint64 _bytesTotal{ 0 }; + qint64 _bytes{ 0 }; + int _attempts{ 0 }; + bool _isInScript{ false }; }; uint qHash(const QPointer& value, uint seed = 0); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 4c6a988f36..29d592eec2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -274,6 +274,7 @@ static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantR using ScriptableResourceRawPtr = ScriptableResource*; static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, const ScriptableResourceRawPtr& resource) { + resource->updateMemoryCost(engine); auto object = engine->newQObject( const_cast(resource), QScriptEngine::ScriptOwnership); @@ -809,6 +810,12 @@ void ScriptEngine::callAnimationStateHandler(QScriptValue callback, AnimVariantM } } +void ScriptEngine::updateMemoryCost(const qint64& deltaSize) { + if (deltaSize > 0) { + reportAdditionalMemoryCost(deltaSize); + } +} + void ScriptEngine::timerFired() { QTimer* callingTimer = reinterpret_cast(sender()); CallbackData timerData = _timerFunctionMap.value(callingTimer); diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 605f30ed6e..175a3f1f1c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -158,6 +158,7 @@ public: public slots: void callAnimationStateHandler(QScriptValue callback, AnimVariantMap parameters, QStringList names, bool useNames, AnimVariantResultHandler resultHandler); + void updateMemoryCost(const qint64&); signals: void scriptLoaded(const QString& scriptFilename); From 92e372f385b202837e0f3ea0d4306ba78aa0ac57 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 18:18:35 -0700 Subject: [PATCH 13/57] Expose Resource progress as a signal --- libraries/networking/src/ResourceCache.cpp | 6 ++++-- libraries/networking/src/ResourceCache.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index eb69f75279..9d99f7c636 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -176,7 +176,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { result->finished(!resource->_failedToLoad); } else { result->_progressConnection = connect( - resource.data(), &Resource::handleDownloadProgress, + resource.data(), &Resource::onProgress, result, &ScriptableResource::progressChanged); result->_finishedConnection = connect( resource.data(), &Resource::finished, @@ -627,7 +627,9 @@ void Resource::makeRequest() { qCDebug(networking).noquote() << "Starting request for:" << _url.toDisplayString(); - connect(_request, &ResourceRequest::progress, this, &Resource::handleDownloadProgress); + connect(_request, &ResourceRequest::progress, this, &Resource::onProgress); + connect(this, &Resource::onProgress, this, &Resource::handleDownloadProgress); + connect(_request, &ResourceRequest::finished, this, &Resource::handleReplyFinished); _bytesReceived = _bytesTotal = _bytes = 0; diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 5b76cb48cd..fbbf64e837 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -294,6 +294,9 @@ signals: /// Fired when the resource is refreshed. void onRefresh(); + /// Fired on progress updates. + void onProgress(uint64_t bytesReceived, uint64_t bytesTotal); + /// Fired when the size changes (through setSize). void updateSize(qint64 deltaSize); From 713fc44ec5738789dbca9973ddc539b44ce0de82 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 20 Apr 2016 19:58:26 -0700 Subject: [PATCH 14/57] Add scriptableResourceTest.js --- examples/tests/scriptableResourceTest.js | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 examples/tests/scriptableResourceTest.js diff --git a/examples/tests/scriptableResourceTest.js b/examples/tests/scriptableResourceTest.js new file mode 100644 index 0000000000..48a03d9975 --- /dev/null +++ b/examples/tests/scriptableResourceTest.js @@ -0,0 +1,73 @@ +// +// scriptableResourceTest.js +// examples/tests +// +// Created by Zach Pomerantz on 4/20/16. +// Copyright 2016 High Fidelity, Inc. +// +// Preloads textures to play a simple movie, plays it, and frees those textures. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// A model exported from blender with a texture named 'Picture' on one face. +var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; +// A folder full of individual frames. +var MOVIE_URL = "http://hifi-content.s3.amazonaws.com/james/vidtest/"; + +var center = Vec3.sum( + Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), + Vec3.multiply(1, Quat.getFront(Camera.getOrientation())) +); + +// Left-pad num with 0s until it is size digits +function pad(num, size) { + var s = num + ""; + while (s.length < size) s = "0" + s; + return s; +} + +var pictureFrameProperties = { + name: 'scriptableResourceTest Picture Frame', + type: 'Model', + position: center, + modelURL: FRAME_URL, + dynamic: true, +} +var pictureFrame = Entities.addEntity(pictureFrameProperties); + +var frames = []; + +// Preload +var numLoading = 0; +for (var i = 0; i < 159; i++) { + var padded = pad(i, 3); + var filepath = MOVIE_URL + padded + '.jpg'; + var texture = TextureCache.prefetch(filepath); + frames.push(texture); + if (!texture.loaded) { + numLoading++; + texture.loadedChanged.connect(function() { + numLoading--; + if (!numLoading) play(); + }); + } +} + +function play() { + var frame = 0; + var movieInterval = Script.setInterval(function() { + Entities.editEntity(pictureFrame, { textures: JSON.stringify({ Picture: frames[frame].url }) }) + frame += 1; + if (frame == 159) { + Script.clearInterval(movieInterval); + Entities.deleteEntity(pictureFrame); + // free the textures at the next garbage collection + while (frames.length) frames.pop(); + // alternatively, the textures can be forcibly freed: + // frames.forEach(function(texture) { texture.release(); }); + Script.requestGarbageCollection(); + } + }, 33.3); +} From cba5bff89ef5403afb38a257895560abdfb82dde Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 21 Apr 2016 10:33:35 -0700 Subject: [PATCH 15/57] Make ResourceCache::prefetch private and comment --- .../src/model-networking/TextureCache.h | 5 ++--- libraries/networking/src/ResourceCache.h | 13 ++++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index fced2b3c5c..82f9edde8f 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -100,9 +100,6 @@ class TextureCache : public ResourceCache, public Dependency { using Type = NetworkTexture::Type; public: - // Overload ResourceCache::prefetch to allow specifying texture type for loads - Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type); - /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and /// the second, a set of random unit vectors to be used as noise gradients. @@ -131,6 +128,8 @@ public: const QByteArray& content = QByteArray()); protected: + // Overload ResourceCache::prefetch to allow specifying texture type for loads + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url, int type); virtual QSharedPointer createResource(const QUrl& url, const QSharedPointer& fallback, bool delayLoad, const void* extra); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index fbbf64e837..d5360fc5b3 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -141,9 +141,6 @@ public: Q_INVOKABLE QVariantList getResourceList(); - // This must be exposed as a ptr so the ScriptEngine may take ownership - Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); } - static void setRequestLimit(int limit); static int getRequestLimit() { return _requestLimit; } @@ -175,6 +172,16 @@ private slots: void clearATPAssets(); protected: + // Prefetches a resource to be held by the QScriptEngine. + // Pointers created through this method should be owned by the caller, + // which should be a QScriptEngine with ScriptableResource registered, so that + // the QScriptEngine will delete the pointer when it is garbage collected. + Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); } + + + // Prefetches a resource to be held by the QScriptEngine. + // Left as a protected member so subclasses can overload prefetch + // and delegate to it (see TextureCache::prefetch(const QUrl&, int). ScriptableResource* prefetch(const QUrl& url, void* extra); /// Loads a resource from the specified URL. From 2fd88d9fe1414b0825413a34ff560bae0ed59d42 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 21 Apr 2016 10:41:04 -0700 Subject: [PATCH 16/57] Add sanity check to ResourceCache::updateTotalSize --- libraries/networking/src/ResourceCache.cpp | 5 +++++ libraries/networking/src/ResourceCache.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 9d99f7c636..83fcc90a4d 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -424,6 +424,11 @@ void ResourceCache::removeResource(const QUrl& url, qint64 size) { void ResourceCache::updateTotalSize(const qint64& deltaSize) { _totalResourcesSize += deltaSize; + + // Sanity checks + assert(_totalResourcesSize >= 0); + assert(_totalResourcesSize < (1024 * BYTES_PER_GIGABYTES)); + emit dirty(); } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index d5360fc5b3..1461094cfb 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -50,7 +50,7 @@ static const qint64 DEFAULT_UNUSED_MAX_SIZE = 100 * BYTES_PER_MEGABYTES; static const qint64 DEFAULT_UNUSED_MAX_SIZE = 1024 * BYTES_PER_MEGABYTES; #endif static const qint64 MIN_UNUSED_MAX_SIZE = 0; -static const qint64 MAX_UNUSED_MAX_SIZE = 10 * BYTES_PER_GIGABYTES; +static const qint64 MAX_UNUSED_MAX_SIZE = MAXIMUM_CACHE_SIZE; // We need to make sure that these items are available for all instances of // ResourceCache derived classes. Since we can't count on the ordering of From 6e12493ed73abdb56b05968afda0b3efeba9623e Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 21 Apr 2016 10:54:03 -0700 Subject: [PATCH 17/57] Add default NetworkTexture::_textureLoader --- libraries/model-networking/src/model-networking/TextureCache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 82f9edde8f..8fd0b12369 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -83,7 +83,7 @@ protected: private: Type _type; - TextureLoaderFunc _textureLoader; + TextureLoaderFunc _textureLoader { [](const QImage&, const std::string&){ return nullptr; } }; int _originalWidth { 0 }; int _originalHeight { 0 }; int _width { 0 }; From 4df417e7568ed7a744435df2fcb63f6680a365fe Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 21 Apr 2016 11:47:48 -0700 Subject: [PATCH 18/57] Add CONSTs to scriptableResourceTest --- examples/tests/scriptableResourceTest.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/tests/scriptableResourceTest.js b/examples/tests/scriptableResourceTest.js index 48a03d9975..ad338f3b6b 100644 --- a/examples/tests/scriptableResourceTest.js +++ b/examples/tests/scriptableResourceTest.js @@ -16,6 +16,9 @@ var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/ // A folder full of individual frames. var MOVIE_URL = "http://hifi-content.s3.amazonaws.com/james/vidtest/"; +var NUM_FRAMES = 158; // 158 available +var FRAME_RATE = 30; // 30 default + var center = Vec3.sum( Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), Vec3.multiply(1, Quat.getFront(Camera.getOrientation())) @@ -41,7 +44,7 @@ var frames = []; // Preload var numLoading = 0; -for (var i = 0; i < 159; i++) { +for (var i = 1; i <= NUM_FRAMES + 1; i++) { var padded = pad(i, 3); var filepath = MOVIE_URL + padded + '.jpg'; var texture = TextureCache.prefetch(filepath); @@ -60,7 +63,7 @@ function play() { var movieInterval = Script.setInterval(function() { Entities.editEntity(pictureFrame, { textures: JSON.stringify({ Picture: frames[frame].url }) }) frame += 1; - if (frame == 159) { + if (frame > NUM_FRAMES) { Script.clearInterval(movieInterval); Entities.deleteEntity(pictureFrame); // free the textures at the next garbage collection @@ -69,5 +72,5 @@ function play() { // frames.forEach(function(texture) { texture.release(); }); Script.requestGarbageCollection(); } - }, 33.3); + }, 1000 / FRAME_RATE); } From 069065dc23a7e66f32aa896dc22b3a165e69c4b5 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 21 Apr 2016 17:15:11 -0700 Subject: [PATCH 19/57] Fix recursive invokes of ResourceCache::prefetch --- libraries/networking/src/ResourceCache.cpp | 3 ++- libraries/networking/src/ResourceCache.h | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 83fcc90a4d..2bd85ce5d2 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -162,7 +162,8 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { if (QThread::currentThread() != thread()) { // Must be called in thread to ensure getResource returns a valid pointer QMetaObject::invokeMethod(this, "prefetch", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(ScriptableResource*, result), Q_ARG(QUrl, url)); + Q_RETURN_ARG(ScriptableResource*, result), + Q_ARG(QUrl, url), Q_ARG(void*, extra)); return result; } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 1461094cfb..7d9e0f65fd 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -168,6 +168,11 @@ public slots: protected slots: void updateTotalSize(const qint64& deltaSize); + // Prefetches a resource to be held by the QScriptEngine. + // Left as a protected member so subclasses can overload prefetch + // and delegate to it (see TextureCache::prefetch(const QUrl&, int). + ScriptableResource* prefetch(const QUrl& url, void* extra); + private slots: void clearATPAssets(); @@ -178,12 +183,6 @@ protected: // the QScriptEngine will delete the pointer when it is garbage collected. Q_INVOKABLE ScriptableResource* prefetch(const QUrl& url) { return prefetch(url, nullptr); } - - // Prefetches a resource to be held by the QScriptEngine. - // Left as a protected member so subclasses can overload prefetch - // and delegate to it (see TextureCache::prefetch(const QUrl&, int). - ScriptableResource* prefetch(const QUrl& url, void* extra); - /// Loads a resource 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 resource immediately; wait until load is first requested From ed9c0dd74df796dfae60aaf0f9698f54b96fae2b Mon Sep 17 00:00:00 2001 From: Geenz Date: Mon, 25 Apr 2016 02:35:26 -0400 Subject: [PATCH 20/57] Apply the correct sRGB conversions. sRGB does not use a gamma curve of 2.2 - instead it uses a piece-wise gamma curve that 2.2 is approximate of. This will cause very subtle color differences between proper sRGB and "approximate" sRGB - differences that are noticeable nonetheless. --- libraries/gpu/src/gpu/Texture.cpp | 40 +++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 9f10f2e2b6..db4b444ce7 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -637,16 +637,48 @@ void SphericalHarmonics::assignPreset(int p) { } } +// This is based upon the conversions found in section 8.24 of the OpenGL 4.4 4.4 specification. +// glm::pow(color, 2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. +float sRGBValueToLinearValue(float& value) { + const float SRGB_ELBOW = 0.04045f; + float linearValue = 0.0f; + + // This should mirror the conversion table found in section 8.24: sRGB Texture Color Conversion + if (value <= SRGB_ELBOW) { + linearValue = value / 12.92f; + } else { + linearValue = powf(((value + 0.055f) / 1.055f), 2.4f); + } + + return linearValue; +} +// This is based upon the conversions found in section 17.3.9 of the OpenGL 4.4 specification. +// glm::pow(color, 1.0f/2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. +float LinearValueTosRGBValue(float& value) { + const float SRGB_ELBOW_INV = 0.0031308f; + float sRGBValue = 0.0f; + + // This should mirror the conversion table found in section 17.3.9: sRGB Conversion + if (value <= 0.0f) { + sRGBValue = 0.0f; + } else if (0 < value < SRGB_ELBOW_INV) { + sRGBValue = 12.92f * value; + } else if (SRGB_ELBOW_INV <= value < 1) { + sRGBValue = 1.055f * powf(value, 0.41666f - 0.055f); + } else { + sRGBValue = 1.0f; + } + + return sRGBValue; +} glm::vec3 sRGBToLinear(glm::vec3& color) { - const float GAMMA_CORRECTION = 2.2f; - return glm::pow(color, glm::vec3(GAMMA_CORRECTION)); + return glm::vec3(sRGBValueToLinearValue(color.x), sRGBValueToLinearValue(color.y), sRGBValueToLinearValue(color.z)); } glm::vec3 linearTosRGB(glm::vec3& color) { - const float GAMMA_CORRECTION_INV = 1.0f / 2.2f; - return glm::pow(color, glm::vec3(GAMMA_CORRECTION_INV)); + return glm::vec3(LinearValueTosRGBValue(color.x), LinearValueTosRGBValue(color.y), LinearValueTosRGBValue(color.z)); } // Originial code for the Spherical Harmonics taken from "Sun and Black Cat- Igor Dykhta (igor dykhta email) � 2007-2014 " From 97931d96b8609f4386002573d7ae87ff55b795b9 Mon Sep 17 00:00:00 2001 From: Geenz Date: Mon, 25 Apr 2016 12:59:16 -0400 Subject: [PATCH 21/57] Move sRGB calcs to ColorUtils And start consolidating any conversions with calls into ColorUtils. --- libraries/gpu/src/gpu/Texture.cpp | 48 ++---------------------- libraries/shared/src/ColorUtils.h | 62 ++++++++++++++++++++++++++++--- 2 files changed, 60 insertions(+), 50 deletions(-) diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index db4b444ce7..2362926c84 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -18,6 +18,8 @@ #include "GPULogging.h" #include "Context.h" +#include "ColorUtils.h" + using namespace gpu; static int TexturePointerMetaTypeId = qRegisterMetaType(); @@ -637,50 +639,6 @@ void SphericalHarmonics::assignPreset(int p) { } } -// This is based upon the conversions found in section 8.24 of the OpenGL 4.4 4.4 specification. -// glm::pow(color, 2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. -float sRGBValueToLinearValue(float& value) { - const float SRGB_ELBOW = 0.04045f; - float linearValue = 0.0f; - - // This should mirror the conversion table found in section 8.24: sRGB Texture Color Conversion - if (value <= SRGB_ELBOW) { - linearValue = value / 12.92f; - } else { - linearValue = powf(((value + 0.055f) / 1.055f), 2.4f); - } - - return linearValue; -} - -// This is based upon the conversions found in section 17.3.9 of the OpenGL 4.4 specification. -// glm::pow(color, 1.0f/2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. -float LinearValueTosRGBValue(float& value) { - const float SRGB_ELBOW_INV = 0.0031308f; - float sRGBValue = 0.0f; - - // This should mirror the conversion table found in section 17.3.9: sRGB Conversion - if (value <= 0.0f) { - sRGBValue = 0.0f; - } else if (0 < value < SRGB_ELBOW_INV) { - sRGBValue = 12.92f * value; - } else if (SRGB_ELBOW_INV <= value < 1) { - sRGBValue = 1.055f * powf(value, 0.41666f - 0.055f); - } else { - sRGBValue = 1.0f; - } - - return sRGBValue; -} - -glm::vec3 sRGBToLinear(glm::vec3& color) { - return glm::vec3(sRGBValueToLinearValue(color.x), sRGBValueToLinearValue(color.y), sRGBValueToLinearValue(color.z)); -} - -glm::vec3 linearTosRGB(glm::vec3& color) { - return glm::vec3(LinearValueTosRGBValue(color.x), LinearValueTosRGBValue(color.y), LinearValueTosRGBValue(color.z)); -} - // Originial code for the Spherical Harmonics taken from "Sun and Black Cat- Igor Dykhta (igor dykhta email) � 2007-2014 " void sphericalHarmonicsAdd(float * result, int order, const float * inputA, const float * inputB) { const int numCoeff = order * order; @@ -835,7 +793,7 @@ bool sphericalHarmonicsFromTexture(const gpu::Texture& cubeTexture, std::vector< float(data[pixOffsetIndex+2]) * UCHAR_TO_FLOAT); // Gamma correct - clr = sRGBToLinear(clr); + clr = ColorUtils::sRGBToLinearVec3(clr); // scale color and add to previously accumulated coefficients sphericalHarmonicsScale(shBuffB.data(), order, diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index b47e7c3a98..192f7f9da4 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -24,13 +24,16 @@ public: // Convert from gamma 2.2 space to linear inline static glm::vec3 toLinearVec3(const glm::vec3& srgb); inline static glm::vec3 toGamma22Vec3(const glm::vec3& linear); + + // Convert from sRGB gamma space to linear. + // This is pretty different from converting from 2.2. + inline static glm::vec3 sRGBToLinearVec3(const glm::vec3& srgb); + inline static glm::vec3 tosRGBVec3(const glm::vec3& srgb); + + inline static float sRGBToLinearFloat(const float& srgb); + inline static float tosRGBFloat(const float& linear); }; -inline glm::vec3 ColorUtils::toVec3(const xColor& color) { - const float ONE_OVER_255 = 1.0f / 255.0f; - return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); -} - inline glm::vec3 ColorUtils::toLinearVec3(const glm::vec3& srgb) { const float GAMMA_22 = 2.2f; // Couldn't find glm::pow(vec3, vec3) ? so did it myself... @@ -43,4 +46,53 @@ inline glm::vec3 ColorUtils::toGamma22Vec3(const glm::vec3& linear) { return glm::vec3(glm::pow(linear.x, INV_GAMMA_22), glm::pow(linear.y, INV_GAMMA_22), glm::pow(linear.z, INV_GAMMA_22)); } +inline glm::vec3 ColorUtils::toVec3(const xColor& color) { + const float ONE_OVER_255 = 1.0f / 255.0f; + return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); +} + +inline glm::vec3 ColorUtils::sRGBToLinearVec3(const glm::vec3& srgb) { + return glm::vec3(sRGBToLinearFloat(srgb.x), sRGBToLinearFloat(srgb.y), sRGBToLinearFloat(srgb.z)); +} + +inline glm::vec3 ColorUtils::tosRGBVec3(const glm::vec3& linear) { + return glm::vec3(tosRGBFloat(linear.x), tosRGBFloat(linear.y), tosRGBFloat(linear.z)); +} + +// This is based upon the conversions found in section 8.24 of the OpenGL 4.4 4.4 specification. +// glm::pow(color, 2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. +inline float ColorUtils::sRGBToLinearFloat(const float &srgb) { + const float SRGB_ELBOW = 0.04045f; + float linearValue = 0.0f; + + // This should mirror the conversion table found in section 8.24: sRGB Texture Color Conversion + if (srgb <= SRGB_ELBOW) { + linearValue = srgb / 12.92f; + } else { + linearValue = powf(((srgb + 0.055f) / 1.055f), 2.4f); + } + + return linearValue; +} + +// This is based upon the conversions found in section 17.3.9 of the OpenGL 4.4 specification. +// glm::pow(color, 1.0f/2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. +inline float ColorUtils::tosRGBFloat(const float &linear) { + const float SRGB_ELBOW_INV = 0.0031308f; + float sRGBValue = 0.0f; + + // This should mirror the conversion table found in section 17.3.9: sRGB Conversion + if (linear <= 0.0f) { + sRGBValue = 0.0f; + } else if (0 < linear < SRGB_ELBOW_INV) { + sRGBValue = 12.92f * linear; + } else if (SRGB_ELBOW_INV <= linear < 1) { + sRGBValue = 1.055f * powf(linear, 0.41666f - 0.055f); + } else { + sRGBValue = 1.0f; + } + + return sRGBValue; +} + #endif // hifi_ColorUtils_h \ No newline at end of file From ba5fa70b83378307f9f0ddcda7735285ece578c1 Mon Sep 17 00:00:00 2001 From: Geenz Date: Mon, 25 Apr 2016 13:31:50 -0400 Subject: [PATCH 22/57] Convert sRGB values for materials. --- libraries/model/src/model/Material.cpp | 6 +++--- libraries/model/src/model/Material.h | 6 +++--- libraries/shared/src/ColorUtils.h | 29 +++++++++++++++++++------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/libraries/model/src/model/Material.cpp b/libraries/model/src/model/Material.cpp index 802650df93..022242d1b5 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/model/src/model/Material.cpp @@ -55,7 +55,7 @@ Material::~Material() { void Material::setEmissive(const Color& emissive, bool isSRGB) { _key.setEmissive(glm::any(glm::greaterThan(emissive, Color(0.0f)))); _schemaBuffer.edit()._key = (uint32) _key._flags.to_ulong(); - _schemaBuffer.edit()._emissive = (isSRGB ? ColorUtils::toLinearVec3(emissive) : emissive); + _schemaBuffer.edit()._emissive = (isSRGB ? ColorUtils::sRGBToLinearVec3(emissive) : emissive); } void Material::setOpacity(float opacity) { @@ -67,7 +67,7 @@ void Material::setOpacity(float opacity) { void Material::setAlbedo(const Color& albedo, bool isSRGB) { _key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f)))); _schemaBuffer.edit()._key = (uint32)_key._flags.to_ulong(); - _schemaBuffer.edit()._albedo = (isSRGB ? ColorUtils::toLinearVec3(albedo) : albedo); + _schemaBuffer.edit()._albedo = (isSRGB ? ColorUtils::sRGBToLinearVec3(albedo) : albedo); } void Material::setRoughness(float roughness) { @@ -79,7 +79,7 @@ void Material::setRoughness(float roughness) { void Material::setFresnel(const Color& fresnel, bool isSRGB) { //_key.setAlbedo(glm::any(glm::greaterThan(albedo, Color(0.0f)))); - _schemaBuffer.edit()._fresnel = (isSRGB ? ColorUtils::toLinearVec3(fresnel) : fresnel); + _schemaBuffer.edit()._fresnel = (isSRGB ? ColorUtils::sRGBToLinearVec3(fresnel) : fresnel); } void Material::setMetallic(float metallic) { diff --git a/libraries/model/src/model/Material.h b/libraries/model/src/model/Material.h index c500e9ec10..59a3fad917 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/model/src/model/Material.h @@ -245,16 +245,16 @@ public: const MaterialKey& getKey() const { return _key; } void setEmissive(const Color& emissive, bool isSRGB = true); - Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._emissive) : _schemaBuffer.get()._emissive); } + Color getEmissive(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._emissive) : _schemaBuffer.get()._emissive); } void setOpacity(float opacity); float getOpacity() const { return _schemaBuffer.get()._opacity; } void setAlbedo(const Color& albedo, bool isSRGB = true); - Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._albedo) : _schemaBuffer.get()._albedo); } + Color getAlbedo(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._albedo) : _schemaBuffer.get()._albedo); } void setFresnel(const Color& fresnel, bool isSRGB = true); - Color getFresnel(bool SRGB = true) const { return (SRGB ? ColorUtils::toGamma22Vec3(_schemaBuffer.get()._fresnel) : _schemaBuffer.get()._fresnel); } + Color getFresnel(bool SRGB = true) const { return (SRGB ? ColorUtils::tosRGBVec3(_schemaBuffer.get()._fresnel) : _schemaBuffer.get()._fresnel); } void setMetallic(float metallic); float getMetallic() const { return _schemaBuffer.get()._metallic; } diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index 192f7f9da4..47e7520afa 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -30,10 +30,18 @@ public: inline static glm::vec3 sRGBToLinearVec3(const glm::vec3& srgb); inline static glm::vec3 tosRGBVec3(const glm::vec3& srgb); + inline static glm::vec4 sRGBToLinearVec4(const glm::vec4& srgb); + inline static glm::vec4 tosRGBVec4(const glm::vec4& srgb); + inline static float sRGBToLinearFloat(const float& srgb); inline static float tosRGBFloat(const float& linear); }; +inline glm::vec3 ColorUtils::toVec3(const xColor& color) { + const float ONE_OVER_255 = 1.0f / 255.0f; + return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); +} + inline glm::vec3 ColorUtils::toLinearVec3(const glm::vec3& srgb) { const float GAMMA_22 = 2.2f; // Couldn't find glm::pow(vec3, vec3) ? so did it myself... @@ -46,19 +54,26 @@ inline glm::vec3 ColorUtils::toGamma22Vec3(const glm::vec3& linear) { return glm::vec3(glm::pow(linear.x, INV_GAMMA_22), glm::pow(linear.y, INV_GAMMA_22), glm::pow(linear.z, INV_GAMMA_22)); } -inline glm::vec3 ColorUtils::toVec3(const xColor& color) { - const float ONE_OVER_255 = 1.0f / 255.0f; - return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); -} - +// Convert from sRGB color space to linear color space. inline glm::vec3 ColorUtils::sRGBToLinearVec3(const glm::vec3& srgb) { return glm::vec3(sRGBToLinearFloat(srgb.x), sRGBToLinearFloat(srgb.y), sRGBToLinearFloat(srgb.z)); } +// Convert from linear color space to sRGB color space. inline glm::vec3 ColorUtils::tosRGBVec3(const glm::vec3& linear) { return glm::vec3(tosRGBFloat(linear.x), tosRGBFloat(linear.y), tosRGBFloat(linear.z)); } +// Convert from sRGB color space with alpha to linear color space with alpha. +inline glm::vec4 ColorUtils::sRGBToLinearVec4(const glm::vec4& srgb) { + return glm::vec4(sRGBToLinearFloat(srgb.x), sRGBToLinearFloat(srgb.y), sRGBToLinearFloat(srgb.z), srgb.w); +} + +// Convert from linear color space with alpha to sRGB color space with alpha. +inline glm::vec4 ColorUtils::tosRGBVec4(const glm::vec4& linear) { + return glm::vec4(tosRGBFloat(linear.x), tosRGBFloat(linear.y), tosRGBFloat(linear.z), linear.w); +} + // This is based upon the conversions found in section 8.24 of the OpenGL 4.4 4.4 specification. // glm::pow(color, 2.2f) is approximate, and will cause subtle differences when used with sRGB framebuffers. inline float ColorUtils::sRGBToLinearFloat(const float &srgb) { @@ -84,9 +99,9 @@ inline float ColorUtils::tosRGBFloat(const float &linear) { // This should mirror the conversion table found in section 17.3.9: sRGB Conversion if (linear <= 0.0f) { sRGBValue = 0.0f; - } else if (0 < linear < SRGB_ELBOW_INV) { + } else if (0 < linear && linear < SRGB_ELBOW_INV) { sRGBValue = 12.92f * linear; - } else if (SRGB_ELBOW_INV <= linear < 1) { + } else if (SRGB_ELBOW_INV <= linear && linear < 1) { sRGBValue = 1.055f * powf(linear, 0.41666f - 0.055f); } else { sRGBValue = 1.0f; From d6ee569963d3e1ab005e6852f44e4175fd518b8e Mon Sep 17 00:00:00 2001 From: Geenz Date: Mon, 25 Apr 2016 23:52:33 -0400 Subject: [PATCH 23/57] Add sRGB conversions to shaders. --- libraries/gpu/src/gpu/Color.slh | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Color.slh b/libraries/gpu/src/gpu/Color.slh index d4d9ba7b81..4330b34b90 100644 --- a/libraries/gpu/src/gpu/Color.slh +++ b/libraries/gpu/src/gpu/Color.slh @@ -11,9 +11,14 @@ <@if not GPU_COLOR_SLH@> <@def GPU_COLOR_SLH@> +float sRGBFloatToLinear(float value) { + const float SRGB_ELBOW = 0.04045; + + return (value <= SRGB_ELBOW) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4); +} + vec3 colorToLinearRGB(vec3 srgb) { - const float GAMMA_22 = 2.2; - return pow(srgb, vec3(GAMMA_22)); + return vec3(sRGBFloatToLinear(srgb.r), sRGBFloatToLinear(srgb.g), sRGBFloatToLinear(srgb.b)); } vec4 colorToLinearRGBA(vec4 srgba) { From ae5ef6d94860d98061bbc68b5b42c19968ebba13 Mon Sep 17 00:00:00 2001 From: Geenz Date: Tue, 26 Apr 2016 17:00:59 -0400 Subject: [PATCH 24/57] Remove toLinearVec3 from ColorUtils toGamma22Vec3 should probably stay since that is a valid conversion from linear to a gamma space of 2.2. --- libraries/shared/src/ColorUtils.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/libraries/shared/src/ColorUtils.h b/libraries/shared/src/ColorUtils.h index 47e7520afa..5ee9254bc9 100644 --- a/libraries/shared/src/ColorUtils.h +++ b/libraries/shared/src/ColorUtils.h @@ -21,8 +21,7 @@ class ColorUtils { public: inline static glm::vec3 toVec3(const xColor& color); - // Convert from gamma 2.2 space to linear - inline static glm::vec3 toLinearVec3(const glm::vec3& srgb); + // Convert to gamma 2.2 space from linear inline static glm::vec3 toGamma22Vec3(const glm::vec3& linear); // Convert from sRGB gamma space to linear. @@ -42,12 +41,6 @@ inline glm::vec3 ColorUtils::toVec3(const xColor& color) { return glm::vec3(color.red * ONE_OVER_255, color.green * ONE_OVER_255, color.blue * ONE_OVER_255); } -inline glm::vec3 ColorUtils::toLinearVec3(const glm::vec3& srgb) { - const float GAMMA_22 = 2.2f; - // Couldn't find glm::pow(vec3, vec3) ? so did it myself... - return glm::vec3(glm::pow(srgb.x, GAMMA_22), glm::pow(srgb.y, GAMMA_22), glm::pow(srgb.z, GAMMA_22)); -} - inline glm::vec3 ColorUtils::toGamma22Vec3(const glm::vec3& linear) { const float INV_GAMMA_22 = 1.0f / 2.2f; // Couldn't find glm::pow(vec3, vec3) ? so did it myself... From ef7098b39aae5243ea19a1f7ea223e900da93292 Mon Sep 17 00:00:00 2001 From: Geenz Date: Tue, 26 Apr 2016 17:12:04 -0400 Subject: [PATCH 25/57] Missed a few. --- libraries/entities/src/ParticleEffectEntityItem.h | 8 ++++---- libraries/render-utils/src/text/Font.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index ba6f32b51f..28af5d5d59 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -49,7 +49,7 @@ public: const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } - glm::vec3 getColorRGB() const { return ColorUtils::toLinearVec3(toGlm(getXColor())); } + glm::vec3 getColorRGB() const { return ColorUtils::tosRGBVec3(toGlm(getXColor())); } static const xColor DEFAULT_COLOR; void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } @@ -62,17 +62,17 @@ public: bool _isColorStartInitialized = false; void setColorStart(const xColor& colorStart) { _colorStart = colorStart; _isColorStartInitialized = true; } xColor getColorStart() const { return _isColorStartInitialized ? _colorStart : getXColor(); } - glm::vec3 getColorStartRGB() const { return _isColorStartInitialized ? ColorUtils::toLinearVec3(toGlm(_colorStart)) : getColorRGB(); } + glm::vec3 getColorStartRGB() const { return _isColorStartInitialized ? ColorUtils::tosRGBVec3(toGlm(_colorStart)) : getColorRGB(); } bool _isColorFinishInitialized = false; void setColorFinish(const xColor& colorFinish) { _colorFinish = colorFinish; _isColorFinishInitialized = true; } xColor getColorFinish() const { return _isColorFinishInitialized ? _colorFinish : getXColor(); } - glm::vec3 getColorFinishRGB() const { return _isColorStartInitialized ? ColorUtils::toLinearVec3(toGlm(_colorFinish)) : getColorRGB(); } + glm::vec3 getColorFinishRGB() const { return _isColorStartInitialized ? ColorUtils::tosRGBVec3(toGlm(_colorFinish)) : getColorRGB(); } static const xColor DEFAULT_COLOR_SPREAD; void setColorSpread(const xColor& colorSpread) { _colorSpread = colorSpread; } xColor getColorSpread() const { return _colorSpread; } - glm::vec3 getColorSpreadRGB() const { return ColorUtils::toLinearVec3(toGlm(_colorSpread)); } + glm::vec3 getColorSpreadRGB() const { return ColorUtils::tosRGBVec3(toGlm(_colorSpread)); } static const float MAXIMUM_ALPHA; static const float MINIMUM_ALPHA; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index e7604544bd..532e5efcdd 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -363,7 +363,7 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); // need the gamma corrected color here - glm::vec4 lrgba = glm::vec4(ColorUtils::toLinearVec3(glm::vec3(*color)), color->a); + glm::vec4 lrgba = glm::vec4(ColorUtils::tosRGBVec3(glm::vec3(*color)), color->a); batch._glUniform4fv(_colorLoc, 1, (const float*)&lrgba); batch.setInputFormat(_format); From 83d6f7f1e34b6c3c2ad7c60fbcf189c4ff25346c Mon Sep 17 00:00:00 2001 From: Geenz Date: Tue, 26 Apr 2016 18:49:03 -0400 Subject: [PATCH 26/57] Quick fix for incorrect conversion. We want sRGB to linear, not linear to sRGB. --- libraries/entities/src/ParticleEffectEntityItem.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/ParticleEffectEntityItem.h b/libraries/entities/src/ParticleEffectEntityItem.h index 28af5d5d59..4538a1bb43 100644 --- a/libraries/entities/src/ParticleEffectEntityItem.h +++ b/libraries/entities/src/ParticleEffectEntityItem.h @@ -49,7 +49,7 @@ public: const rgbColor& getColor() const { return _color; } xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; } - glm::vec3 getColorRGB() const { return ColorUtils::tosRGBVec3(toGlm(getXColor())); } + glm::vec3 getColorRGB() const { return ColorUtils::sRGBToLinearVec3(toGlm(getXColor())); } static const xColor DEFAULT_COLOR; void setColor(const rgbColor& value) { memcpy(_color, value, sizeof(_color)); } @@ -62,17 +62,17 @@ public: bool _isColorStartInitialized = false; void setColorStart(const xColor& colorStart) { _colorStart = colorStart; _isColorStartInitialized = true; } xColor getColorStart() const { return _isColorStartInitialized ? _colorStart : getXColor(); } - glm::vec3 getColorStartRGB() const { return _isColorStartInitialized ? ColorUtils::tosRGBVec3(toGlm(_colorStart)) : getColorRGB(); } + glm::vec3 getColorStartRGB() const { return _isColorStartInitialized ? ColorUtils::sRGBToLinearVec3(toGlm(_colorStart)) : getColorRGB(); } bool _isColorFinishInitialized = false; void setColorFinish(const xColor& colorFinish) { _colorFinish = colorFinish; _isColorFinishInitialized = true; } xColor getColorFinish() const { return _isColorFinishInitialized ? _colorFinish : getXColor(); } - glm::vec3 getColorFinishRGB() const { return _isColorStartInitialized ? ColorUtils::tosRGBVec3(toGlm(_colorFinish)) : getColorRGB(); } + glm::vec3 getColorFinishRGB() const { return _isColorStartInitialized ? ColorUtils::sRGBToLinearVec3(toGlm(_colorFinish)) : getColorRGB(); } static const xColor DEFAULT_COLOR_SPREAD; void setColorSpread(const xColor& colorSpread) { _colorSpread = colorSpread; } xColor getColorSpread() const { return _colorSpread; } - glm::vec3 getColorSpreadRGB() const { return ColorUtils::tosRGBVec3(toGlm(_colorSpread)); } + glm::vec3 getColorSpreadRGB() const { return ColorUtils::sRGBToLinearVec3(toGlm(_colorSpread)); } static const float MAXIMUM_ALPHA; static const float MINIMUM_ALPHA; From bf31fc728aab18b37eed8c8e8633e93aa9275d28 Mon Sep 17 00:00:00 2001 From: Geenz Date: Tue, 26 Apr 2016 18:54:02 -0400 Subject: [PATCH 27/57] And another. --- libraries/render-utils/src/text/Font.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 532e5efcdd..3607aa5803 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -363,7 +363,7 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); // need the gamma corrected color here - glm::vec4 lrgba = glm::vec4(ColorUtils::tosRGBVec3(glm::vec3(*color)), color->a); + glm::vec4 lrgba = ColorUtils::sRGBToLinearVec4(*color); batch._glUniform4fv(_colorLoc, 1, (const float*)&lrgba); batch.setInputFormat(_format); From a21bc66fa3c6901304164d0b7a05e1eecc77a34a Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 26 Apr 2016 16:21:11 -0700 Subject: [PATCH 28/57] add support to abort evaluation of long running scripts on shutdown --- libraries/script-engine/src/ScriptEngine.cpp | 19 ++++++++++++++++++- libraries/script-engine/src/ScriptEngines.cpp | 16 ++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 15f3ebb985..c4ceba4365 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -199,11 +199,28 @@ void ScriptEngine::waitTillDoneRunning() { // NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts() // we want the application thread to continue to process events, because the scripts will likely need to - // marshall messages across to the main thread. For example if they access Settings or Meny in any of their + // marshall messages across to the main thread. For example if they access Settings or Menu in any of their // shutdown code. + auto startedWaiting = usecTimestampNow(); while (thread()->isRunning()) { // process events for the main application thread, allowing invokeMethod calls to pass between threads QCoreApplication::processEvents(); + auto stillWaiting = usecTimestampNow(); + auto elapsed = stillWaiting - startedWaiting; + + // if we've been waiting a second or more, then tell the script engine to stop evaluating + if (elapsed > USECS_PER_SECOND) { + qDebug() << __FUNCTION__ << "...giving up on evaluation elapsed:" << elapsed << "calling abortEvaluation()"; + abortEvaluation(); + } + + // if we've been waiting for more than 5 seconds then we should be more aggessive about stopping + static const auto WAITING_TOO_LONG = USECS_PER_SECOND * 5; + if (elapsed > WAITING_TOO_LONG) { + qDebug() << __FUNCTION__ << "...giving up on thread elapsed:" << elapsed << "calling thread->quit()"; + thread()->quit(); + break; + } } } } diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index eeca49ff84..624498c2b3 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -129,19 +129,31 @@ void ScriptEngines::shutdownScripting() { // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using // them. We don't need to stop scripts that aren't running. if (scriptEngine->isRunning()) { + qCDebug(scriptengine) << "about to shutdown script:" << scriptName; // If the script is running, but still evaluating then we need to wait for its evaluation step to // complete. After that we can handle the stop process appropriately if (scriptEngine->evaluatePending()) { + qCDebug(scriptengine) << "script still evaluating:" << scriptName; + auto startedWaiting = usecTimestampNow(); while (scriptEngine->evaluatePending()) { // This event loop allows any started, but not yet finished evaluate() calls to complete // we need to let these complete so that we can be guaranteed that the script engine isn't // in a partially setup state, which can confuse our shutdown unwinding. QEventLoop loop; - QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit); - loop.exec(); + static const int MAX_PROCESSING_TIME = 500; // in MS + loop.processEvents(QEventLoop::AllEvents, MAX_PROCESSING_TIME); + auto stillWaiting = usecTimestampNow(); + auto elapsed = stillWaiting - startedWaiting; + // if we've been waiting for more than 5 seconds, then tell the script engine to stop evaluating + static const auto WAITING_TOO_LONG = USECS_PER_SECOND * 5; + if (elapsed > WAITING_TOO_LONG) { + qCDebug(scriptengine) << "giving up on script evaluation elapsed:" << elapsed << "calling abortEvaluation() script:" << scriptName; + scriptEngine->abortEvaluation(); + } } + qCDebug(scriptengine) << "script DONE evaluating:" << scriptName; } // We disconnect any script engine signals from the application because we don't want to do any From 48e63ea8280003f1315a1c35421e6c4bb5cecd15 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 26 Apr 2016 16:24:53 -0700 Subject: [PATCH 29/57] add support to abort evaluation of long running scripts on shutdown --- libraries/script-engine/src/ScriptEngine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c4ceba4365..a30a786b32 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -201,6 +201,8 @@ void ScriptEngine::waitTillDoneRunning() { // we want the application thread to continue to process events, because the scripts will likely need to // marshall messages across to the main thread. For example if they access Settings or Menu in any of their // shutdown code. + QString scriptName = getFilename(); + auto startedWaiting = usecTimestampNow(); while (thread()->isRunning()) { // process events for the main application thread, allowing invokeMethod calls to pass between threads @@ -210,14 +212,14 @@ void ScriptEngine::waitTillDoneRunning() { // if we've been waiting a second or more, then tell the script engine to stop evaluating if (elapsed > USECS_PER_SECOND) { - qDebug() << __FUNCTION__ << "...giving up on evaluation elapsed:" << elapsed << "calling abortEvaluation()"; + qDebug() << "giving up on evaluation elapsed:" << elapsed << "calling abortEvaluation() script:" << scriptName; abortEvaluation(); } // if we've been waiting for more than 5 seconds then we should be more aggessive about stopping static const auto WAITING_TOO_LONG = USECS_PER_SECOND * 5; if (elapsed > WAITING_TOO_LONG) { - qDebug() << __FUNCTION__ << "...giving up on thread elapsed:" << elapsed << "calling thread->quit()"; + qDebug() << "giving up on thread elapsed:" << elapsed << "calling thread->quit() script:" << scriptName; thread()->quit(); break; } From 4202e60ac397e295ffef3a3284181a20b2f8c1e2 Mon Sep 17 00:00:00 2001 From: samcake Date: Tue, 26 Apr 2016 18:20:15 -0700 Subject: [PATCH 30/57] trying to add the skymap on translucents --- libraries/render-utils/src/DeferredGlobalLight.slh | 12 ++++++++++++ libraries/render-utils/src/RenderPipelines.cpp | 2 +- libraries/render-utils/src/model_translucent.slf | 5 +++-- libraries/render/src/render/ShapePipeline.cpp | 2 ++ libraries/render/src/render/ShapePipeline.h | 2 ++ 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index c87aa1cee2..5a429ff7c4 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -125,4 +125,16 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur } <@endfunc@> + + +<@func declareEvalGlobalLightColor()@> + +<$declareEvalSkyboxGlobalColor()$> + +vec3 evalGlobalLightColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness) { + return evalSkyboxGlobalColor(invViewMat, shadowAttenuation, obscurance, position, normal, albedo, metallic, emissive, roughness); +} +<@endfunc@> + + <@endif@> diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index aa0f9dc7d9..4452872108 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -98,7 +98,7 @@ void lightBatchSetter(const ShapePipeline& pipeline, gpu::Batch& batch) { if (pipeline.locations->lightBufferUnit >= 0) { DependencyManager::get()->setupKeyLightBatch(batch, pipeline.locations->lightBufferUnit, - -1); + pipeline.locations->lightAmbientMapUnit); } } diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 12a7b9299f..f70d922a67 100755 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -15,7 +15,8 @@ <@include model/Material.slh@> <@include DeferredGlobalLight.slh@> -<$declareEvalAmbientSphereGlobalColor()$> + +<$declareEvalGlobalLightColor()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -58,7 +59,7 @@ void main(void) { TransformCamera cam = getTransformCamera(); - _fragColor = vec4(evalAmbientSphereGlobalColor( + _fragColor = vec4(evalSkyboxGlobalColor( cam._viewInverse, 1.0, 1.0, diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 6974a7e385..766d899465 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -60,6 +60,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p slotBindings.insert(gpu::Shader::Binding(std::string("emissiveMap"), Slot::EMISSIVE_LIGHTMAP_MAP)); slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), Slot::OCCLUSION_MAP)); slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::LIGHT_BUFFER)); + slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::LIGHT_AMBIENT_MAP)); slotBindings.insert(gpu::Shader::Binding(std::string("normalFittingMap"), Slot::NORMAL_FITTING_MAP)); gpu::Shader::makeProgram(*program, slotBindings); @@ -77,6 +78,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p locations->skinClusterBufferUnit = program->getBuffers().findLocation("skinClusterBuffer"); locations->materialBufferUnit = program->getBuffers().findLocation("materialBuffer"); locations->lightBufferUnit = program->getBuffers().findLocation("lightBuffer"); + locations->lightAmbientMapUnit = program->getBuffers().findLocation("skyboxMap"); ShapeKey key{filter._flags}; auto gpuPipeline = gpu::Pipeline::create(program, state); diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 0f795aadde..876e056a9a 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -203,6 +203,7 @@ public: static const int OCCLUSION_MAP = 5; static const int LIGHT_BUFFER = 4; + static const int LIGHT_AMBIENT_MAP = 6; static const int NORMAL_FITTING_MAP = 10; }; @@ -220,6 +221,7 @@ public: int skinClusterBufferUnit; int materialBufferUnit; int lightBufferUnit; + int lightAmbientMapUnit; }; using LocationsPointer = std::shared_ptr; From 6b53c1e85d13e5cd6e80412ec859bc6600d38781 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 27 Apr 2016 11:55:02 -0700 Subject: [PATCH 31/57] Avoid leaking ScriptableResource --- libraries/networking/src/ResourceCache.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 2bd85ce5d2..48bc1dfc0c 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -157,7 +157,7 @@ void ScriptableResource::disconnectHelper() { } ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { - auto result = new ScriptableResource(url); + ScriptableResource* result = nullptr; if (QThread::currentThread() != thread()) { // Must be called in thread to ensure getResource returns a valid pointer @@ -167,6 +167,7 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { return result; } + result = new ScriptableResource(url); auto resource = getResource(url, QUrl(), false, extra); result->_resource = resource; From c2909946e6e9bd01db372458264e0c56029f1771 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 27 Apr 2016 13:59:27 -0700 Subject: [PATCH 32/57] Split test to prefetch, movie --- examples/tests/scriptableResourceTest.js | 76 --------------- .../developer/tests/scriptableResource/lib.js | 96 +++++++++++++++++++ .../tests/scriptableResource/movieTest.js | 42 ++++++++ .../tests/scriptableResource/prefetchTest.js | 33 +++++++ 4 files changed, 171 insertions(+), 76 deletions(-) delete mode 100644 examples/tests/scriptableResourceTest.js create mode 100644 scripts/developer/tests/scriptableResource/lib.js create mode 100644 scripts/developer/tests/scriptableResource/movieTest.js create mode 100644 scripts/developer/tests/scriptableResource/prefetchTest.js diff --git a/examples/tests/scriptableResourceTest.js b/examples/tests/scriptableResourceTest.js deleted file mode 100644 index ad338f3b6b..0000000000 --- a/examples/tests/scriptableResourceTest.js +++ /dev/null @@ -1,76 +0,0 @@ -// -// scriptableResourceTest.js -// examples/tests -// -// Created by Zach Pomerantz on 4/20/16. -// Copyright 2016 High Fidelity, Inc. -// -// Preloads textures to play a simple movie, plays it, and frees those textures. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -// A model exported from blender with a texture named 'Picture' on one face. -var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; -// A folder full of individual frames. -var MOVIE_URL = "http://hifi-content.s3.amazonaws.com/james/vidtest/"; - -var NUM_FRAMES = 158; // 158 available -var FRAME_RATE = 30; // 30 default - -var center = Vec3.sum( - Vec3.sum(MyAvatar.position, { x: 0, y: 0.5, z: 0 }), - Vec3.multiply(1, Quat.getFront(Camera.getOrientation())) -); - -// Left-pad num with 0s until it is size digits -function pad(num, size) { - var s = num + ""; - while (s.length < size) s = "0" + s; - return s; -} - -var pictureFrameProperties = { - name: 'scriptableResourceTest Picture Frame', - type: 'Model', - position: center, - modelURL: FRAME_URL, - dynamic: true, -} -var pictureFrame = Entities.addEntity(pictureFrameProperties); - -var frames = []; - -// Preload -var numLoading = 0; -for (var i = 1; i <= NUM_FRAMES + 1; i++) { - var padded = pad(i, 3); - var filepath = MOVIE_URL + padded + '.jpg'; - var texture = TextureCache.prefetch(filepath); - frames.push(texture); - if (!texture.loaded) { - numLoading++; - texture.loadedChanged.connect(function() { - numLoading--; - if (!numLoading) play(); - }); - } -} - -function play() { - var frame = 0; - var movieInterval = Script.setInterval(function() { - Entities.editEntity(pictureFrame, { textures: JSON.stringify({ Picture: frames[frame].url }) }) - frame += 1; - if (frame > NUM_FRAMES) { - Script.clearInterval(movieInterval); - Entities.deleteEntity(pictureFrame); - // free the textures at the next garbage collection - while (frames.length) frames.pop(); - // alternatively, the textures can be forcibly freed: - // frames.forEach(function(texture) { texture.release(); }); - Script.requestGarbageCollection(); - } - }, 1000 / FRAME_RATE); -} diff --git a/scripts/developer/tests/scriptableResource/lib.js b/scripts/developer/tests/scriptableResource/lib.js new file mode 100644 index 0000000000..ed3d746061 --- /dev/null +++ b/scripts/developer/tests/scriptableResource/lib.js @@ -0,0 +1,96 @@ +// +// lib.js +// scripts/developer/tests/scriptableResource +// +// Created by Zach Pomerantz on 4/20/16. +// Copyright 2016 High Fidelity, Inc. +// +// Preloads textures to play a simple movie, plays it, and frees those textures. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var NUM_FRAMES = 158; // 158 available +var FRAME_RATE = 30; // 30 default + +function getFrame(callback) { + // A model exported from blender with a texture named 'Picture' on one face. + var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; + + var model = ModelCache.prefetch(FRAME_URL); + if (model.loaded) { + makeFrame(true); + } else { + model.loadedChanged.connect(makeFrame); + } + + function makeFrame(success) { + if (!success) { throw "Failed to load frame"; } + + var pictureFrameProperties = { + name: 'scriptableResourceTest Picture Frame', + type: 'Model', + position: getPosition(), + modelURL: FRAME_URL, + dynamic: true, + }; + + callback(Entities.addEntity(pictureFrameProperties)); + } + + function getPosition() { + // Always put it 5 meters in front of you + var position = MyAvatar.position; + var yaw = MyAvatar.bodyYaw + MyAvatar.getHeadFinalYaw(); + var rads = (yaw / 180) * Math.PI; + + position.y += 0.5; + position.x += - 5 * Math.sin(rads); + position.z += - 5 * Math.cos(rads); + + print(JSON.stringify(position)); + return position; + } +} + +function prefetch(callback) { + // A folder full of individual frames. + var MOVIE_URL = "http://hifi-content.s3.amazonaws.com/james/vidtest/"; + + var frames = []; + + var numLoading = 0; + for (var i = 1; i <= NUM_FRAMES; ++i) { + var padded = pad(i, 3); + var filepath = MOVIE_URL + padded + '.jpg'; + var texture = TextureCache.prefetch(filepath); + frames.push(texture); + if (!texture.loaded) { + numLoading++; + texture.loadedChanged.connect(function() { + --numLoading; + if (!numLoading) { callback(frames); } + }); + } + } + if (!numLoading) { callback(frames); } + + function pad(num, size) { // left-pad num with zeros until it is size digits + var s = num.toString(); + while (s.length < size) { s = "0" + s; } + return s; + } +} + +function play(model, frames, callback) { + var frame = 0; + var movieInterval = Script.setInterval(function() { + Entities.editEntity(model, { textures: JSON.stringify({ Picture: frames[frame].url }) }); + if (++frame >= frames.length) { + Script.clearInterval(movieInterval); + callback(); + } + }, 1000 / FRAME_RATE); +} + diff --git a/scripts/developer/tests/scriptableResource/movieTest.js b/scripts/developer/tests/scriptableResource/movieTest.js new file mode 100644 index 0000000000..61b2bf7942 --- /dev/null +++ b/scripts/developer/tests/scriptableResource/movieTest.js @@ -0,0 +1,42 @@ +// +// testMovie.js +// scripts/developer/tests/scriptableResource +// +// Created by Zach Pomerantz on 4/27/16. +// Copyright 2016 High Fidelity, Inc. +// +// Preloads textures, plays them on a frame model, and unloads them. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var entity; + +Script.include([ + '../../../developer/utilities/cache/cacheStats.js', + 'lib.js', +], function() { + getFrame(function(frame) { + entity = frame; + prefetch(function(frames) { + play(frame, frames, function() { + // Delete each texture, so the next garbage collection cycle will release them. + + // Setting frames = null breaks the reference, + // but will not delete frames from the calling scope. + // Instead, we must mutate it in-place to free its elements for GC + // (assuming the elements are not held elsewhere). + while (frames.length) { frames.pop(); } + + // Alternatively, forcibly release each texture without relying on GC. + // frames.forEach(function(texture) { texture.release(); }); + + Entities.deleteEntity(entity); + Script.requestGarbageCollection(); + }); + }); + }); +}); + +Script.scriptEnding.connect(function() { entity && Entities.deleteEntity(entity); }); diff --git a/scripts/developer/tests/scriptableResource/prefetchTest.js b/scripts/developer/tests/scriptableResource/prefetchTest.js new file mode 100644 index 0000000000..cda805967e --- /dev/null +++ b/scripts/developer/tests/scriptableResource/prefetchTest.js @@ -0,0 +1,33 @@ +// +// testPrefetch.js +// scripts/developer/tests/scriptableResource +// +// Created by Zach Pomerantz on 4/27/16. +// Copyright 2016 High Fidelity, Inc. +// +// Preloads textures and unloads them. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +Script.include([ + '../../../developer/utilities/cache/cacheStats.js', + 'lib.js', +], function() { + prefetch(function(frames) { + // Delete each texture, so the next garbage collection cycle will release them. + + // Setting frames = null breaks the reference, + // but will not delete frames from the calling scope. + // Instead, we must mutate it in-place to free its elements for GC + // (assuming the elements are not held elsewhere). + while (frames.length) { frames.pop(); } + + // Alternatively, forcibly release each texture without relying on GC. + // frames.forEach(function(texture) { texture.release(); }); + + Script.requestGarbageCollection(); + }); +}); + From de8145ec7122dacace4c691a4673ec1213407db7 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 27 Apr 2016 16:47:18 -0700 Subject: [PATCH 33/57] Nest TextureType JS interface in Type --- .../model-networking/src/model-networking/TextureCache.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index af84ecd0f3..2aaddace88 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -37,9 +37,13 @@ TextureCache::TextureCache() { setObjectName("TextureCache"); // Expose enum Type to JS/QML via properties + // Despite being one-off, this should be fine, because TextureCache is a SINGLETON_DEPENDENCY + QObject* type = new QObject(this); + type->setObjectName("TextureType"); + setProperty("Type", QVariant::fromValue(type)); auto metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); ++i) { - setProperty(metaEnum.key(i), metaEnum.value(i)); + type->setProperty(metaEnum.key(i), metaEnum.value(i)); } } From 0c78d5bdd19044a4b353e9939d9fd4d3c0d7fe16 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 27 Apr 2016 17:12:03 -0700 Subject: [PATCH 34/57] Change ScriptableResource signal to state --- libraries/networking/src/ResourceCache.cpp | 41 +++++++++++++++---- libraries/networking/src/ResourceCache.h | 30 ++++++++++---- .../developer/tests/scriptableResource/lib.js | 15 ++++--- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 3e7e8dd345..aa42f94e8b 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -121,7 +121,17 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { ScriptableResource::ScriptableResource(const QUrl& url) : QObject(nullptr), - _url(url) {} + _url(url) { + + // Expose enum State to JS/QML via properties + QObject* state = new QObject(this); + state->setObjectName("ResourceState"); + setProperty("State", QVariant::fromValue(state)); + auto metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); ++i) { + state->setProperty(metaEnum.key(i), metaEnum.value(i)); + } +} void ScriptableResource::release() { disconnectHelper(); @@ -135,22 +145,30 @@ void ScriptableResource::updateMemoryCost(const QObject* engine) { } } +void ScriptableResource::loadingChanged() { + emit stateChanged(LOADING); +} + +void ScriptableResource::loadedChanged() { + emit stateChanged(LOADED); +} + void ScriptableResource::finished(bool success) { disconnectHelper(); - _isLoaded = true; - _isFailed = !success; - - if (_isFailed) { - emit failedChanged(_isFailed); - } - emit loadedChanged(_isLoaded); + emit stateChanged(success ? FINISHED : FAILED); } void ScriptableResource::disconnectHelper() { if (_progressConnection) { disconnect(_progressConnection); } + if (_loadingConnection) { + disconnect(_loadingConnection); + } + if (_loadedConnection) { + disconnect(_loadedConnection); + } if (_finishedConnection) { disconnect(_finishedConnection); } @@ -180,6 +198,12 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) { result->_progressConnection = connect( resource.data(), &Resource::onProgress, result, &ScriptableResource::progressChanged); + result->_loadingConnection = connect( + resource.data(), &Resource::loading, + result, &ScriptableResource::loadingChanged); + result->_loadedConnection = connect( + resource.data(), &Resource::loaded, + result, &ScriptableResource::loadedChanged); result->_finishedConnection = connect( resource.data(), &Resource::finished, result, &ScriptableResource::finished); @@ -644,6 +668,7 @@ void Resource::makeRequest() { } qCDebug(networking).noquote() << "Starting request for:" << _url.toDisplayString(); + emit loading(); connect(_request, &ResourceRequest::progress, this, &Resource::onProgress); connect(this, &Resource::onProgress, this, &Resource::handleDownloadProgress); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 7d9e0f65fd..904da4f097 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -82,28 +82,36 @@ private: class ScriptableResource : public QObject { Q_OBJECT Q_PROPERTY(QUrl url READ getUrl) - Q_PROPERTY(bool loaded READ isLoaded NOTIFY loadedChanged) - Q_PROPERTY(bool failed READ isFailed NOTIFY failedChanged) + Q_PROPERTY(int state READ getState NOTIFY stateChanged) public: + enum State { + QUEUED, + LOADING, + LOADED, + FINISHED, + FAILED, + }; + Q_ENUM(State) + ScriptableResource(const QUrl& url); virtual ~ScriptableResource() = default; Q_INVOKABLE void release(); const QUrl& getUrl() const { return _url; } - bool isLoaded() const { return _isLoaded; } - bool isFailed() const { return _isFailed; } + int getState() const { return (int)_state; } - // Connects to a SLOT(updateMemoryCost(qint64) on the given engine + // Connects to a SLOT(updateMemoryCost(qint64)) on the given engine void updateMemoryCost(const QObject* engine); signals: void progressChanged(uint64_t bytesReceived, uint64_t bytesTotal); - void loadedChanged(bool loaded); // analogous to &Resource::finished - void failedChanged(bool failed); + void stateChanged(int state); private slots: + void loadingChanged(); + void loadedChanged(); void finished(bool success); private: @@ -115,11 +123,12 @@ private: QSharedPointer _resource; QMetaObject::Connection _progressConnection; + QMetaObject::Connection _loadingConnection; + QMetaObject::Connection _loadedConnection; QMetaObject::Connection _finishedConnection; QUrl _url; - bool _isLoaded{ false }; - bool _isFailed{ false }; + State _state{ QUEUED }; }; Q_DECLARE_METATYPE(ScriptableResource*); @@ -287,6 +296,9 @@ public: const QUrl& getURL() const { return _url; } signals: + /// Fired when the resource begins downloading. + void loading(); + /// Fired when the resource has been downloaded. /// This can be used instead of downloadFinished to access data before it is processed. void loaded(const QByteArray request); diff --git a/scripts/developer/tests/scriptableResource/lib.js b/scripts/developer/tests/scriptableResource/lib.js index ed3d746061..21e2e64cae 100644 --- a/scripts/developer/tests/scriptableResource/lib.js +++ b/scripts/developer/tests/scriptableResource/lib.js @@ -22,11 +22,12 @@ function getFrame(callback) { if (model.loaded) { makeFrame(true); } else { - model.loadedChanged.connect(makeFrame); + model.stateChanged.connect(makeFrame); } - function makeFrame(success) { - if (!success) { throw "Failed to load frame"; } + function makeFrame(state) { + if (state == 4) { throw "Failed to load frame"; } + if (state != 3) { return; } var pictureFrameProperties = { name: 'scriptableResourceTest Picture Frame', @@ -68,9 +69,11 @@ function prefetch(callback) { frames.push(texture); if (!texture.loaded) { numLoading++; - texture.loadedChanged.connect(function() { - --numLoading; - if (!numLoading) { callback(frames); } + texture.stateChanged.connect(function(state) { + if (state == 3 || state == 4) { + --numLoading; + if (!numLoading) { callback(frames); } + } }); } } From 1e8d45aecb1c7d9056461ed93054eb611d127f3a Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 27 Apr 2016 18:27:41 -0700 Subject: [PATCH 35/57] Put ScriptableResource state enum in prototype --- libraries/networking/src/ResourceCache.cpp | 12 +--------- libraries/networking/src/ResourceCache.h | 3 +++ libraries/script-engine/src/ScriptEngine.cpp | 22 ++++++++++++++++++- .../developer/tests/scriptableResource/lib.js | 12 +++++----- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index aa42f94e8b..32b1c03766 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -121,17 +121,7 @@ QSharedPointer ResourceCacheSharedItems::getHighestPendingRequest() { ScriptableResource::ScriptableResource(const QUrl& url) : QObject(nullptr), - _url(url) { - - // Expose enum State to JS/QML via properties - QObject* state = new QObject(this); - state->setObjectName("ResourceState"); - setProperty("State", QVariant::fromValue(state)); - auto metaEnum = QMetaEnum::fromType(); - for (int i = 0; i < metaEnum.keyCount(); ++i) { - state->setProperty(metaEnum.key(i), metaEnum.value(i)); - } -} + _url(url) { } void ScriptableResource::release() { disconnectHelper(); diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 904da4f097..98473a58c3 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -24,9 +24,12 @@ #include #include #include + #include #include +#include + #include #include "ResourceManager.h" diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 29d592eec2..1cbd2a6127 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -285,6 +285,23 @@ static void scriptableResourceFromScriptValue(const QScriptValue& value, Scripta resource = static_cast(value.toQObject()); } +static QScriptValue createScriptableResourcePrototype(QScriptEngine* engine) { + auto prototype = engine->newObject(); + + // Expose enum State to JS/QML via properties + QObject* state = new QObject(engine); + state->setObjectName("ResourceState"); + auto metaEnum = QMetaEnum::fromType(); + for (int i = 0; i < metaEnum.keyCount(); ++i) { + state->setProperty(metaEnum.key(i), metaEnum.value(i)); + } + + auto prototypeState = engine->newQObject(state, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSlots | QScriptEngine::ExcludeSuperClassMethods); + prototype.setProperty("State", prototypeState); + + return prototype; +} + void ScriptEngine::init() { if (_isInitialized) { return; // only initialize once @@ -342,11 +359,14 @@ void ScriptEngine::init() { registerGlobalObject("Vec3", &_vec3Library); registerGlobalObject("Mat4", &_mat4Library); registerGlobalObject("Uuid", &_uuidLibrary); - registerGlobalObject("AnimationCache", DependencyManager::get().data()); registerGlobalObject("Messages", DependencyManager::get().data()); qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue); qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue); + // Scriptable cache access + auto resourcePrototype = createScriptableResourcePrototype(this); + globalObject().setProperty("Resource", resourcePrototype); + setDefaultPrototype(qMetaTypeId(), resourcePrototype); qScriptRegisterMetaType(this, scriptableResourceToScriptValue, scriptableResourceFromScriptValue); // constants diff --git a/scripts/developer/tests/scriptableResource/lib.js b/scripts/developer/tests/scriptableResource/lib.js index 21e2e64cae..053cd0bf44 100644 --- a/scripts/developer/tests/scriptableResource/lib.js +++ b/scripts/developer/tests/scriptableResource/lib.js @@ -19,15 +19,15 @@ function getFrame(callback) { var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; var model = ModelCache.prefetch(FRAME_URL); - if (model.loaded) { - makeFrame(true); + if (model.state = Resource.State.FINISHED) { + makeFrame(Resource.State.FINISHED); } else { model.stateChanged.connect(makeFrame); } function makeFrame(state) { - if (state == 4) { throw "Failed to load frame"; } - if (state != 3) { return; } + if (state == Resource.State.FAILED) { throw "Failed to load frame"; } + if (state != Resource.State.FINISHED) { return; } var pictureFrameProperties = { name: 'scriptableResourceTest Picture Frame', @@ -67,10 +67,10 @@ function prefetch(callback) { var filepath = MOVIE_URL + padded + '.jpg'; var texture = TextureCache.prefetch(filepath); frames.push(texture); - if (!texture.loaded) { + if (!texture.state == Resource.State.FINISHED) { numLoading++; texture.stateChanged.connect(function(state) { - if (state == 3 || state == 4) { + if (state == Resource.State.FAILED || state == Resource.State.FINISHED) { --numLoading; if (!numLoading) { callback(frames); } } From 854c0ea3e3ab36b0b527f900f5a1dc029a083835 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Wed, 27 Apr 2016 18:37:30 -0700 Subject: [PATCH 36/57] Clean out updateMemoryCost --- libraries/networking/src/ResourceCache.cpp | 11 +++++++---- libraries/networking/src/ResourceCache.h | 11 ++++++----- libraries/script-engine/src/ScriptEngine.cpp | 12 +++++++++++- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp index 32b1c03766..4cc8b1d4f0 100644 --- a/libraries/networking/src/ResourceCache.cpp +++ b/libraries/networking/src/ResourceCache.cpp @@ -128,10 +128,13 @@ void ScriptableResource::release() { _resource.reset(); } -void ScriptableResource::updateMemoryCost(const QObject* engine) { - if (_resource && !_resource->isInScript()) { - _resource->setInScript(true); - connect(_resource.data(), SIGNAL(updateSize(qint64)), engine, SLOT(updateMemoryCost(qint64))); +bool ScriptableResource::isInScript() const { + return _resource && _resource->isInScript(); +} + +void ScriptableResource::setInScript(bool isInScript) { + if (_resource) { + _resource->setInScript(isInScript); } } diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h index 98473a58c3..b81c69c079 100644 --- a/libraries/networking/src/ResourceCache.h +++ b/libraries/networking/src/ResourceCache.h @@ -104,9 +104,10 @@ public: const QUrl& getUrl() const { return _url; } int getState() const { return (int)_state; } + const QSharedPointer& getResource() const { return _resource; } - // Connects to a SLOT(updateMemoryCost(qint64)) on the given engine - void updateMemoryCost(const QObject* engine); + bool isInScript() const; + void setInScript(bool isInScript); signals: void progressChanged(uint64_t bytesReceived, uint64_t bytesTotal); @@ -357,6 +358,9 @@ private slots: void handleReplyFinished(); private: + friend class ResourceCache; + friend class ScriptableResource; + void setLRUKey(int lruKey) { _lruKey = lruKey; } void makeRequest(); @@ -366,9 +370,6 @@ private: bool isInScript() const { return _isInScript; } void setInScript(bool isInScript) { _isInScript = isInScript; } - friend class ResourceCache; - friend class ScriptableResource; - ResourceRequest* _request{ nullptr }; int _lruKey{ 0 }; QTimer* _replyTimer{ nullptr }; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 1cbd2a6127..fc8b581ffe 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -274,7 +274,17 @@ static void resultHandlerFromScriptValue(const QScriptValue& value, AnimVariantR using ScriptableResourceRawPtr = ScriptableResource*; static QScriptValue scriptableResourceToScriptValue(QScriptEngine* engine, const ScriptableResourceRawPtr& resource) { - resource->updateMemoryCost(engine); + // The first script to encounter this resource will track its memory. + // In this way, it will be more likely to GC. + // This fails in the case that the resource is used across many scripts, but + // in that case it would be too difficult to tell which one should track the memory, and + // this serves the common case (use in a single script). + auto data = resource->getResource(); + if (data && !resource->isInScript()) { + resource->setInScript(true); + QObject::connect(data.data(), SIGNAL(updateSize(qint64)), engine, SLOT(updateMemoryCost(qint64))); + } + auto object = engine->newQObject( const_cast(resource), QScriptEngine::ScriptOwnership); From 30e7b950a13c743addbfd2e9fe604d5344ed44d9 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Thu, 28 Apr 2016 11:41:36 -0700 Subject: [PATCH 37/57] Fix script engine deletion --- libraries/script-engine/src/ScriptEngine.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 15f3ebb985..23cc3a24a9 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -170,7 +170,7 @@ void ScriptEngine::runInThread() { } _isThreaded = true; - QThread* workerThread = new QThread(); // thread is not owned, so we need to manage the delete + QThread* workerThread = new QThread(this); // thread is not owned, so we need to manage the delete QString scriptEngineName = QString("Script Thread:") + getFilename(); workerThread->setObjectName(scriptEngineName); @@ -184,9 +184,6 @@ void ScriptEngine::runInThread() { // tell the thread to stop when the script engine is done connect(this, &ScriptEngine::doneRunning, workerThread, &QThread::quit); - // when the thread is finished, add thread to the deleteLater queue - connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater); - moveToThread(workerThread); // Starts an event loop, and emits workerThread->started() @@ -199,7 +196,7 @@ void ScriptEngine::waitTillDoneRunning() { // NOTE: waitTillDoneRunning() will be called on the main Application thread, inside of stopAllScripts() // we want the application thread to continue to process events, because the scripts will likely need to - // marshall messages across to the main thread. For example if they access Settings or Meny in any of their + // marshall messages across to the main thread. For example if they access Settings or Menu in any of their // shutdown code. while (thread()->isRunning()) { // process events for the main application thread, allowing invokeMethod calls to pass between threads From 8c278563f5dbf5e09f18f56a5042f987da87adc5 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 28 Apr 2016 13:40:55 -0700 Subject: [PATCH 38/57] allow scripts on the web to refer to interface-local javascript libraries --- libraries/script-engine/src/ScriptEngine.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 15f3ebb985..19246dd355 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -912,7 +912,13 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac // Do NOT use PreferLocalFile as its behavior is unpredictable (e.g., on defaultScriptsLocation()) const auto strippingFlags = QUrl::RemoveFilename | QUrl::RemoveQuery | QUrl::RemoveFragment; for (QString file : includeFiles) { - QUrl thisURL { resolvePath(file) }; + QUrl thisURL; + if (file.startsWith("/~/")) { + thisURL = expandScriptUrl(QUrl::fromLocalFile(file)); + } else { + thisURL = resolvePath(file); + } + if (!_includedURLs.contains(thisURL)) { if (!currentSandboxURL.isEmpty() && (thisURL.scheme() == "file") && ( From 648e6a74d7f159b588cce82de8590cf0b5421fe9 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 28 Apr 2016 15:43:58 -0700 Subject: [PATCH 39/57] don't let people access scripts outside of /~/ --- libraries/script-engine/src/ScriptEngines.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index eeca49ff84..84f2de0e56 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -82,6 +82,15 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { QStringList splitPath = url.path().split("/"); QUrl defaultScriptsLoc = defaultScriptsLocation(); url.setPath(defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/")); // 2 to skip the slashes in /~/ + + // stop something like Script.include(["/~/../Users/james/Desktop/naughty.js"]); from working + QFileInfo fileInfo(url.path()); + url.setPath(fileInfo.absoluteFilePath()); + + if (!url.path().startsWith(defaultScriptsLoc.path())) { + qCWarning(scriptengine) << "Script.include() ignoring file path" << normalizedScriptURL + << "outside of standard libraries"; + } return url; } return normalizedScriptURL; From 8c2b89b910b0575c799ecf297f73c42ac8283238 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 28 Apr 2016 17:08:38 -0700 Subject: [PATCH 40/57] trying to get /~/ from web sorted out --- libraries/script-engine/src/ScriptEngines.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 84f2de0e56..14b0b83acc 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -84,12 +84,17 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { url.setPath(defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/")); // 2 to skip the slashes in /~/ // stop something like Script.include(["/~/../Users/james/Desktop/naughty.js"]); from working - QFileInfo fileInfo(url.path()); - url.setPath(fileInfo.absoluteFilePath()); + qDebug() << "url: " << url.path(); + qDebug() << "BEFORE: " << url.path(); + QFileInfo fileInfo(url.toLocalFile()); + url = QUrl::fromLocalFile(fileInfo.absoluteFilePath()); + qDebug() << "AFTER: " << url.path(); + qDebug() << "default: " << defaultScriptsLoc.path(); - if (!url.path().startsWith(defaultScriptsLoc.path())) { + if (!defaultScriptsLoc.isParentOf(url)) { qCWarning(scriptengine) << "Script.include() ignoring file path" << normalizedScriptURL - << "outside of standard libraries"; + << "outside of standard libraries" << url.path() << defaultScriptsLoc.path(); + // return QUrl(""); } return url; } From ced18fe6be06f255f2ce7b11eba567ba7c3aa029 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 28 Apr 2016 18:29:08 -0700 Subject: [PATCH 41/57] trying some more to get /~/ to work right --- libraries/script-engine/src/ScriptEngines.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 14b0b83acc..80f2216e2b 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -83,18 +83,13 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { QUrl defaultScriptsLoc = defaultScriptsLocation(); url.setPath(defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/")); // 2 to skip the slashes in /~/ - // stop something like Script.include(["/~/../Users/james/Desktop/naughty.js"]); from working - qDebug() << "url: " << url.path(); - qDebug() << "BEFORE: " << url.path(); + // stop something like Script.include(["/~/../Desktop/naughty.js"]); from working QFileInfo fileInfo(url.toLocalFile()); - url = QUrl::fromLocalFile(fileInfo.absoluteFilePath()); - qDebug() << "AFTER: " << url.path(); - qDebug() << "default: " << defaultScriptsLoc.path(); - + url = QUrl::fromLocalFile(fileInfo.canonicalFilePath()); if (!defaultScriptsLoc.isParentOf(url)) { - qCWarning(scriptengine) << "Script.include() ignoring file path" << normalizedScriptURL - << "outside of standard libraries" << url.path() << defaultScriptsLoc.path(); - // return QUrl(""); + qCWarning(scriptengine) << "Script.include() ignoring file path" << rawScriptURL + << "-- outside of standard libraries: " << url.path() << defaultScriptsLoc.path(); + return QUrl(""); } return url; } From 257c8c94c3593c193debd63fc4862556816c64a4 Mon Sep 17 00:00:00 2001 From: samcake Date: Fri, 29 Apr 2016 10:58:55 -0700 Subject: [PATCH 42/57] clean up the lighting function and put the global specular lighting in a separate function --- libraries/model/src/model/Light.slh | 5 ++ .../render-utils/src/DeferredGlobalLight.slh | 82 +++++++++++-------- .../render-utils/src/model_translucent.slf | 4 +- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/libraries/model/src/model/Light.slh b/libraries/model/src/model/Light.slh index 80804e3cf5..7cc4691d63 100644 --- a/libraries/model/src/model/Light.slh +++ b/libraries/model/src/model/Light.slh @@ -109,10 +109,15 @@ SphericalHarmonics getLightAmbientSphere(Light l) { return l._ambientSphere; } +bool getLightHasAmbientMap(Light l) { + return l._control.x > 0; +} + float getLightAmbientMapNumMips(Light l) { return l._control.x; } + <@if GPU_FEATURE_PROFILE == GPU_CORE @> uniform lightBuffer { Light light; diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index 04c9df9bc5..7608c8ae08 100755 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -25,6 +25,43 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { } <@endfunc@> +<@func declareEvalGlobalSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> + +vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { + return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5); +} + +<@if supportAmbientMap@> + <$declareSkyboxMap()$> +<@endif@> + +vec3 evalGlobalSpecularIrradiance(Light light, vec3 fragEyeDir, vec3 fragNormal, float roughness, vec3 fresnel, float obscurance) { + vec3 direction = -reflect(fragEyeDir, fragNormal); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1 - roughness); + vec3 specularLight; + <@if supportIfAmbientMapElseAmbientSphere@> + if (getLightHasAmbientMap(light)) + <@endif@> + <@if supportAmbientMap@> + { + float levels = getLightAmbientMapNumMips(light); + float lod = min(floor((roughness) * levels), levels); + specularLight = evalSkyboxLight(direction, lod).xyz; + } + <@endif@> + <@if supportIfAmbientMapElseAmbientSphere@> + else + <@endif@> + <@if supportAmbientSphere@> + { + specularLight = evalSphericalLight(getLightAmbientSphere(light), direction).xyz; + } + <@endif@> + + return specularLight * ambientFresnel * getLightAmbientIntensity(light); +} +<@endfunc@> + <@func prepareGlobalLight()@> // prepareGlobalLight @@ -45,11 +82,6 @@ vec4 evalSkyboxLight(vec3 direction, float lod) { color += emissive; <@endfunc@> -<@func declareAmbientFresnel()@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { - return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5); -} -<@endfunc@> <@func declareEvalAmbientGlobalColor()@> vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness) { @@ -60,7 +92,8 @@ vec3 evalAmbientGlobalColor(mat4 invViewMat, float shadowAttenuation, float obsc <@endfunc@> <@func declareEvalAmbientSphereGlobalColor()@> -<$declareAmbientFresnel()$> +<$declareEvalGlobalSpecularIrradiance(1, 0, 0)$> + vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness) { <$prepareGlobalLight()$> @@ -69,18 +102,15 @@ vec3 evalAmbientSphereGlobalColor(mat4 invViewMat, float shadowAttenuation, floa color += (1 - metallic) * albedo * evalSphericalLight(getLightAmbientSphere(light), fragNormal).xyz * obscurance * getLightAmbientIntensity(light); // Specular highlight from ambient - vec3 direction = -reflect(fragEyeDir, fragNormal); - vec3 skyboxLight = evalSphericalLight(getLightAmbientSphere(light), direction).xyz; - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1 - roughness); - color += ambientFresnel * skyboxLight.rgb * obscurance * getLightAmbientIntensity(light); + vec3 specularLighting = evalGlobalSpecularIrradiance(light, fragEyeDir, fragNormal, roughness, fresnel, obscurance); + color += specularLighting; return color; } <@endfunc@> <@func declareEvalSkyboxGlobalColor()@> -<$declareSkyboxMap()$> -<$declareAmbientFresnel()$> +<$declareEvalGlobalSpecularIrradiance(0, 1, 0)$> vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness) { <$prepareGlobalLight()$> @@ -89,13 +119,8 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu color += (1 - metallic) * albedo * evalSphericalLight(getLightAmbientSphere(light), fragNormal).xyz * obscurance * getLightAmbientIntensity(light); // Specular highlight from ambient - vec3 direction = -reflect(fragEyeDir, fragNormal); - - float levels = getLightAmbientMapNumMips(light); - float lod = min(floor((roughness) * levels), levels); - vec4 skyboxLight = evalSkyboxLight(direction, lod); - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1 - roughness); - color += ambientFresnel * skyboxLight.rgb * obscurance * getLightAmbientIntensity(light); + vec3 specularLighting = evalGlobalSpecularIrradiance(light, fragEyeDir, fragNormal, roughness, fresnel, obscurance); + color += specularLighting; return color; } @@ -127,32 +152,23 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur -<@func declareEvalGlobalLightColor()@> +<@func declareEvalGlobalLightingAlphaBlended()@> -<$declareSkyboxMap()$> -<$declareAmbientFresnel()$> +<$declareEvalGlobalSpecularIrradiance(1, 1, 1)$> -vec3 evalSkyboxGlobalColorAlpha(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness, float opacity) { +vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> // Diffuse from ambient color += (1 - metallic) * albedo * evalSphericalLight(getLightAmbientSphere(light), fragNormal).xyz * obscurance * getLightAmbientIntensity(light); // Specular highlight from ambient - vec3 direction = -reflect(fragEyeDir, fragNormal); - - float levels = getLightAmbientMapNumMips(light); - float lod = min(floor((roughness) * levels), levels); - vec4 skyboxLight = evalSkyboxLight(direction, lod); - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, fragEyeDir, fragNormal, 1 - roughness); - color += ambientFresnel * skyboxLight.rgb * obscurance * getLightAmbientIntensity(light) / opacity; + vec3 specularLighting = evalGlobalSpecularIrradiance(light, fragEyeDir, fragNormal, roughness, fresnel, obscurance); + color += specularLighting / opacity; return color; } -vec3 evalGlobalLightColor(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, float metallic, vec3 emissive, float roughness, float opacity) { - return evalSkyboxGlobalColorAlpha(invViewMat, shadowAttenuation, obscurance, position, normal, albedo, metallic, emissive, roughness, opacity); -} <@endfunc@> diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 2bd49345ee..1a807c5703 100755 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -16,7 +16,7 @@ <@include DeferredGlobalLight.slh@> -<$declareEvalGlobalLightColor()$> +<$declareEvalGlobalLightingAlphaBlended()$> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -59,7 +59,7 @@ void main(void) { TransformCamera cam = getTransformCamera(); - _fragColor = vec4(evalGlobalLightColor( + _fragColor = vec4(evalGlobalLightingAlphaBlended( cam._viewInverse, 1.0, 1.0, From f03130ff5ab857ae0e59e0d060b1b8693b9a7f11 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 29 Apr 2016 11:03:14 -0700 Subject: [PATCH 43/57] more work toward keeping /~/../.. from working --- libraries/script-engine/src/ScriptEngine.cpp | 7 +++++- libraries/script-engine/src/ScriptEngines.cpp | 24 +++++++++++++++---- libraries/script-engine/src/ScriptEngines.h | 1 + 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 19246dd355..78a0ad2d19 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -914,7 +914,12 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac for (QString file : includeFiles) { QUrl thisURL; if (file.startsWith("/~/")) { - thisURL = expandScriptUrl(QUrl::fromLocalFile(file)); + thisURL = expandScriptUrl(QUrl::fromLocalFile(expandScriptPath(file))); + QUrl defaultScriptsLoc = defaultScriptsLocation(); + if (!defaultScriptsLoc.isParentOf(thisURL)) { + qDebug() << "ScriptEngine::include -- skipping" << file << "-- outside of standard libraries"; + continue; + } } else { thisURL = resolvePath(file); } diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 80f2216e2b..aed2d15a1a 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -70,6 +70,12 @@ QUrl normalizeScriptURL(const QUrl& rawScriptURL) { } } +QString expandScriptPath(const QString& rawPath) { + QStringList splitPath = rawPath.split("/"); + QUrl defaultScriptsLoc = defaultScriptsLocation(); + return defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/"); // 2 to skip the slashes in /~/ +} + QUrl expandScriptUrl(const QUrl& rawScriptURL) { QUrl normalizedScriptURL = normalizeScriptURL(rawScriptURL); if (normalizedScriptURL.scheme() == "http" || @@ -79,17 +85,25 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { } else if (normalizedScriptURL.scheme() == "file") { if (normalizedScriptURL.path().startsWith("/~/")) { QUrl url = normalizedScriptURL; - QStringList splitPath = url.path().split("/"); - QUrl defaultScriptsLoc = defaultScriptsLocation(); - url.setPath(defaultScriptsLoc.path() + "/" + splitPath.mid(2).join("/")); // 2 to skip the slashes in /~/ + url.setPath(expandScriptPath(url.path())); // stop something like Script.include(["/~/../Desktop/naughty.js"]); from working QFileInfo fileInfo(url.toLocalFile()); + #if defined(Q_OS_WIN) + url = QUrl::fromLocalFile(fileInfo.canonicalFilePath().toLower()); + #elif defined(Q_OS_OSX) + url = QUrl::fromLocalFile(fileInfo.canonicalFilePath().toLower()); + #else url = QUrl::fromLocalFile(fileInfo.canonicalFilePath()); + #endif + + QUrl defaultScriptsLoc = defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(url)) { qCWarning(scriptengine) << "Script.include() ignoring file path" << rawScriptURL - << "-- outside of standard libraries: " << url.path() << defaultScriptsLoc.path(); - return QUrl(""); + << "-- outside of standard libraries: " + << url.path() + << defaultScriptsLoc.path(); + return rawScriptURL; } return url; } diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 5de71663e9..0963b21600 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -101,6 +101,7 @@ protected: }; QUrl normalizeScriptURL(const QUrl& rawScriptURL); +QString expandScriptPath(const QString& rawPath); QUrl expandScriptUrl(const QUrl& rawScriptURL); #endif // hifi_ScriptEngine_h From b28cfd27ec1cfdab9df4d66e2b18aeeb51972933 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 29 Apr 2016 13:17:28 -0700 Subject: [PATCH 44/57] fix problem where canonicalFilePath will strip a trailing slash --- libraries/script-engine/src/ScriptEngine.cpp | 1 + libraries/script-engine/src/ScriptEngines.cpp | 9 +++------ libraries/shared/src/PathUtils.cpp | 15 +++++++++------ 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 78a0ad2d19..960b3c6d90 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -874,6 +874,7 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { } // at this point we should have a legitimate fully qualified URL for our parent + qDebug() << "ScriptEngine::resolvePath" << parentURL << url; url = expandScriptUrl(parentURL.resolved(url)); return url; } diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index aed2d15a1a..3f2e0aa908 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -89,13 +89,7 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { // stop something like Script.include(["/~/../Desktop/naughty.js"]); from working QFileInfo fileInfo(url.toLocalFile()); - #if defined(Q_OS_WIN) - url = QUrl::fromLocalFile(fileInfo.canonicalFilePath().toLower()); - #elif defined(Q_OS_OSX) - url = QUrl::fromLocalFile(fileInfo.canonicalFilePath().toLower()); - #else url = QUrl::fromLocalFile(fileInfo.canonicalFilePath()); - #endif QUrl defaultScriptsLoc = defaultScriptsLocation(); if (!defaultScriptsLoc.isParentOf(url)) { @@ -105,6 +99,9 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { << defaultScriptsLoc.path(); return rawScriptURL; } + if (rawScriptURL.path().endsWith("/") && !url.path().endsWith("/")) { + url.setPath(url.path() + "/"); + } return url; } return normalizedScriptURL; diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 025768908c..954ed2d75a 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -56,12 +56,15 @@ QString findMostRecentFileExtension(const QString& originalFileName, QVector Date: Fri, 29 Apr 2016 13:23:01 -0700 Subject: [PATCH 45/57] remove debug print --- libraries/script-engine/src/ScriptEngine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 960b3c6d90..78a0ad2d19 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -874,7 +874,6 @@ QUrl ScriptEngine::resolvePath(const QString& include) const { } // at this point we should have a legitimate fully qualified URL for our parent - qDebug() << "ScriptEngine::resolvePath" << parentURL << url; url = expandScriptUrl(parentURL.resolved(url)); return url; } From 043fe3508a950225603adbcdb66aa8f41679ea7f Mon Sep 17 00:00:00 2001 From: Geenz Date: Sat, 30 Apr 2016 15:47:42 -0400 Subject: [PATCH 46/57] Add SRGBA color format. --- libraries/gpu/src/gpu/Format.cpp | 1 + libraries/gpu/src/gpu/Format.h | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 7c603919fd..b7a8380f78 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -11,6 +11,7 @@ using namespace gpu; const Element Element::COLOR_RGBA_32{ VEC4, NUINT8, RGBA }; +const Element Element::COLOR_SRGBA_32{ VEC4, NUINT8, SRGBA }; const Element Element::VEC4F_COLOR_RGBA{ VEC4, FLOAT, RGBA }; const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; const Element Element::VEC2F_XY{ VEC2, FLOAT, XY }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 9c08e45bec..6b2bc4b93e 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -245,6 +245,7 @@ public: } static const Element COLOR_RGBA_32; + static const Element COLOR_SRGBA_32; static const Element VEC4F_COLOR_RGBA; static const Element VEC2F_UV; static const Element VEC2F_XY; From 73ff0308a9cdcf4a2f0b72c0d2c41e98b10dabb9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 30 Apr 2016 15:23:50 -0700 Subject: [PATCH 47/57] some CR feedback --- libraries/script-engine/src/ScriptEngine.cpp | 12 +++++++++++- libraries/script-engine/src/ScriptEngine.h | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index a30a786b32..fd5df60415 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -140,6 +140,8 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam connect(this, &QScriptEngine::signalHandlerException, this, [this](const QScriptValue& exception) { hadUncaughtExceptions(*this, _fileNameString); }); + + setProcessEventsInterval(MSECS_PER_SECOND); } ScriptEngine::~ScriptEngine() { @@ -193,6 +195,14 @@ void ScriptEngine::runInThread() { workerThread->start(); } +void ScriptEngine::threadSafeAbortEvaluation() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "threadSafeAbortEvaluation"); + return; + } + abortEvaluation(); +} + void ScriptEngine::waitTillDoneRunning() { // If the script never started running or finished running before we got here, we don't need to wait for it if (_isRunning && _isThreaded) { @@ -213,7 +223,7 @@ void ScriptEngine::waitTillDoneRunning() { // if we've been waiting a second or more, then tell the script engine to stop evaluating if (elapsed > USECS_PER_SECOND) { qDebug() << "giving up on evaluation elapsed:" << elapsed << "calling abortEvaluation() script:" << scriptName; - abortEvaluation(); + threadSafeAbortEvaluation(); } // if we've been waiting for more than 5 seconds then we should be more aggessive about stopping diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index e8ce00c66c..f1bd7463d2 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -134,6 +134,7 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts Q_INVOKABLE void stop(); + Q_INVOKABLE void threadSafeAbortEvaluation(); bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget bool isRunning() const { return _isRunning; } // used by ScriptWidget From ee4748fee965c649a6d7a48183abb7c001da73be Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 30 Apr 2016 15:31:09 -0700 Subject: [PATCH 48/57] CR Feedback --- libraries/script-engine/src/ScriptEngine.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fd5df60415..fc09c21929 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -222,14 +222,14 @@ void ScriptEngine::waitTillDoneRunning() { // if we've been waiting a second or more, then tell the script engine to stop evaluating if (elapsed > USECS_PER_SECOND) { - qDebug() << "giving up on evaluation elapsed:" << elapsed << "calling abortEvaluation() script:" << scriptName; + qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsed << "] aborting evaluation."; threadSafeAbortEvaluation(); } // if we've been waiting for more than 5 seconds then we should be more aggessive about stopping static const auto WAITING_TOO_LONG = USECS_PER_SECOND * 5; if (elapsed > WAITING_TOO_LONG) { - qDebug() << "giving up on thread elapsed:" << elapsed << "calling thread->quit() script:" << scriptName; + qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsed << "] quitting."; thread()->quit(); break; } @@ -396,7 +396,6 @@ void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) { if (partsToGo > 0) { //QObject *object = new QObject; QScriptValue partValue = newArray(); //newQObject(object, QScriptEngine::ScriptOwnership); - qDebug() << "partValue[" << pathPart<<"].isArray() :" << partValue.isArray(); partObject.setProperty(pathPart, partValue); } else { partObject.setProperty(pathPart, value); @@ -1089,11 +1088,6 @@ void ScriptEngine::loadEntityScript(QWeakPointer theEngine, const << QThread::currentThread() << "] expected thread [" << strongEngine->thread() << "]"; #endif strongEngine->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success); - } else { - // FIXME - I'm leaving this in for testing, so that QA can confirm that sometimes the script contents - // returns after the ScriptEngine has been deleted, we can remove this after QA verifies the - // repro case. - qDebug() << "ScriptCache::getScriptContents() returned after our ScriptEngine was deleted... script:" << scriptOrURL; } }, forceRedownload); } From 00838d1e1ec4e89b11cac10dd614c15753a8eb1d Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 30 Apr 2016 15:38:37 -0700 Subject: [PATCH 49/57] more CR feedback removed redundant abort code --- libraries/script-engine/src/ScriptEngines.cpp | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 624498c2b3..8fea7e4653 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -131,31 +131,6 @@ void ScriptEngines::shutdownScripting() { if (scriptEngine->isRunning()) { qCDebug(scriptengine) << "about to shutdown script:" << scriptName; - // If the script is running, but still evaluating then we need to wait for its evaluation step to - // complete. After that we can handle the stop process appropriately - if (scriptEngine->evaluatePending()) { - qCDebug(scriptengine) << "script still evaluating:" << scriptName; - auto startedWaiting = usecTimestampNow(); - while (scriptEngine->evaluatePending()) { - - // This event loop allows any started, but not yet finished evaluate() calls to complete - // we need to let these complete so that we can be guaranteed that the script engine isn't - // in a partially setup state, which can confuse our shutdown unwinding. - QEventLoop loop; - static const int MAX_PROCESSING_TIME = 500; // in MS - loop.processEvents(QEventLoop::AllEvents, MAX_PROCESSING_TIME); - auto stillWaiting = usecTimestampNow(); - auto elapsed = stillWaiting - startedWaiting; - // if we've been waiting for more than 5 seconds, then tell the script engine to stop evaluating - static const auto WAITING_TOO_LONG = USECS_PER_SECOND * 5; - if (elapsed > WAITING_TOO_LONG) { - qCDebug(scriptengine) << "giving up on script evaluation elapsed:" << elapsed << "calling abortEvaluation() script:" << scriptName; - scriptEngine->abortEvaluation(); - } - } - qCDebug(scriptengine) << "script DONE evaluating:" << scriptName; - } - // We disconnect any script engine signals from the application because we don't want to do any // extra stopScript/loadScript processing that the Application normally does when scripts start // and stop. We can safely short circuit this because we know we're in the "quitting" process From 6026e055aaa173978e300ffcf7bfc00fe6592ae4 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Sat, 30 Apr 2016 15:45:42 -0700 Subject: [PATCH 50/57] more CR feedback removed redundant abort code --- libraries/script-engine/src/ScriptEngine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index fc09c21929..3b8b4925ad 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -221,7 +221,8 @@ void ScriptEngine::waitTillDoneRunning() { auto elapsed = stillWaiting - startedWaiting; // if we've been waiting a second or more, then tell the script engine to stop evaluating - if (elapsed > USECS_PER_SECOND) { + static const auto MAX_SCRIPT_EVALUATION_TIME = USECS_PER_SECOND; + if (elapsed > MAX_SCRIPT_EVALUATION_TIME) { qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsed << "] aborting evaluation."; threadSafeAbortEvaluation(); } From 29e0744b89bb52652f084f243c92b3959428f7c0 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Mon, 2 May 2016 09:40:08 -0700 Subject: [PATCH 51/57] Fix equality in scriptableResource/lib --- scripts/developer/tests/scriptableResource/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/developer/tests/scriptableResource/lib.js b/scripts/developer/tests/scriptableResource/lib.js index 053cd0bf44..5241d0968e 100644 --- a/scripts/developer/tests/scriptableResource/lib.js +++ b/scripts/developer/tests/scriptableResource/lib.js @@ -19,7 +19,7 @@ function getFrame(callback) { var FRAME_URL = "http://hifi-production.s3.amazonaws.com/tutorials/pictureFrame/finalFrame.fbx"; var model = ModelCache.prefetch(FRAME_URL); - if (model.state = Resource.State.FINISHED) { + if (model.state === Resource.State.FINISHED) { makeFrame(Resource.State.FINISHED); } else { model.stateChanged.connect(makeFrame); From 4569032314fa3a1955c116e0ff9a444cbf1383d1 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 May 2016 12:44:07 -0700 Subject: [PATCH 52/57] more CR feedback --- libraries/script-engine/src/ScriptEngine.cpp | 23 ++++++-------------- libraries/script-engine/src/ScriptEngine.h | 1 - 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 3b8b4925ad..d4548aa3df 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -195,14 +195,6 @@ void ScriptEngine::runInThread() { workerThread->start(); } -void ScriptEngine::threadSafeAbortEvaluation() { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "threadSafeAbortEvaluation"); - return; - } - abortEvaluation(); -} - void ScriptEngine::waitTillDoneRunning() { // If the script never started running or finished running before we got here, we don't need to wait for it if (_isRunning && _isThreaded) { @@ -218,21 +210,20 @@ void ScriptEngine::waitTillDoneRunning() { // process events for the main application thread, allowing invokeMethod calls to pass between threads QCoreApplication::processEvents(); auto stillWaiting = usecTimestampNow(); - auto elapsed = stillWaiting - startedWaiting; + auto elapsedUsecs = stillWaiting - startedWaiting; // if we've been waiting a second or more, then tell the script engine to stop evaluating static const auto MAX_SCRIPT_EVALUATION_TIME = USECS_PER_SECOND; - if (elapsed > MAX_SCRIPT_EVALUATION_TIME) { - qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsed << "] aborting evaluation."; - threadSafeAbortEvaluation(); - } + static const auto WAITING_TOO_LONG = MAX_SCRIPT_EVALUATION_TIME * 5; // if we've been waiting for more than 5 seconds then we should be more aggessive about stopping - static const auto WAITING_TOO_LONG = USECS_PER_SECOND * 5; - if (elapsed > WAITING_TOO_LONG) { - qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsed << "] quitting."; + if (elapsedUsecs > WAITING_TOO_LONG) { + qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsedUsecs << "] quitting."; thread()->quit(); break; + } else if (elapsedUsecs > MAX_SCRIPT_EVALUATION_TIME) { + qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsedUsecs << "] aborting evaluation."; + QMetaObject::invokeMethod(this, "abortEvaluation"); } } } diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index f1bd7463d2..e8ce00c66c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -134,7 +134,6 @@ public: //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // NOTE - this is intended to be a public interface for Agent scripts, and local scripts, but not for EntityScripts Q_INVOKABLE void stop(); - Q_INVOKABLE void threadSafeAbortEvaluation(); bool isFinished() const { return _isFinished; } // used by Application and ScriptWidget bool isRunning() const { return _isRunning; } // used by ScriptWidget From ae8f5028086002f3ed226f1e9de486d08724e0a0 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 May 2016 14:23:36 -0700 Subject: [PATCH 53/57] keep track of actions we are editing and ignore updates from server for these actions --- libraries/entities/src/EntityActionInterface.h | 4 ++++ libraries/entities/src/EntityItem.cpp | 5 ++++- libraries/entities/src/EntityScriptingInterface.cpp | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityActionInterface.h b/libraries/entities/src/EntityActionInterface.h index ba59d66cf4..c634326a31 100644 --- a/libraries/entities/src/EntityActionInterface.h +++ b/libraries/entities/src/EntityActionInterface.h @@ -54,6 +54,9 @@ public: virtual bool lifetimeIsOver() { return false; } virtual quint64 getExpires() { return 0; } + virtual bool isMine() { return _isMine; } + virtual void setIsMine(bool value) { _isMine = value; } + bool locallyAddedButNotYetReceived = false; virtual bool shouldSuppressLocationEdits() { return false; } @@ -89,6 +92,7 @@ protected: QUuid _id; EntityActionType _type; bool _active { false }; + bool _isMine { false }; // did this interface create / edit this action? }; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 2d1bbf2f88..fc3bc38e14 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1702,6 +1702,7 @@ bool EntityItem::updateAction(EntitySimulation* simulation, const QUuid& actionI success = action->updateArguments(arguments); if (success) { + action->setIsMine(true); serializeActions(success, _allActionsDataCache); _dirtyFlags |= Simulation::DIRTY_PHYSICS_ACTIVATION; } else { @@ -1808,7 +1809,9 @@ void EntityItem::deserializeActionsInternal() { EntityActionPointer action = _objectActions[actionID]; // TODO: make sure types match? there isn't currently a way to // change the type of an existing action. - action->deserialize(serializedAction); + if (!action->isMine()) { + action->deserialize(serializedAction); + } action->locallyAddedButNotYetReceived = false; updated << actionID; } else { diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 8213316b7b..9a4539ea9f 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -830,6 +830,7 @@ QUuid EntityScriptingInterface::addAction(const QString& actionTypeString, if (!action) { return false; } + action->setIsMine(true); success = entity->addAction(simulation, action); entity->grabSimulationOwnership(); return false; // Physics will cause a packet to be sent, so don't send from here. From b98419e537e420b8ae415e0f2b2e700278dde9b7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Mon, 2 May 2016 14:58:38 -0700 Subject: [PATCH 54/57] add units to debug message --- libraries/script-engine/src/ScriptEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index d4548aa3df..9bced25611 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -218,11 +218,11 @@ void ScriptEngine::waitTillDoneRunning() { // if we've been waiting for more than 5 seconds then we should be more aggessive about stopping if (elapsedUsecs > WAITING_TOO_LONG) { - qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsedUsecs << "] quitting."; + qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsedUsecs << " usecs] quitting."; thread()->quit(); break; } else if (elapsedUsecs > MAX_SCRIPT_EVALUATION_TIME) { - qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsedUsecs << "] aborting evaluation."; + qCDebug(scriptengine) << "Script " << scriptName << " has been running too long [" << elapsedUsecs << " usecs] aborting evaluation."; QMetaObject::invokeMethod(this, "abortEvaluation"); } } From 1da041de9b6ef8d63e2fe160dd5ba692cd0d6d60 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 2 May 2016 15:09:08 -0700 Subject: [PATCH 55/57] don't consume the physics related property changes from the packet that informs us that we are the simulationOwner of an entity --- libraries/entities/src/EntityItem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index fc3bc38e14..dc017f81e6 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -658,6 +658,9 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef if (_simulationOwner.set(newSimOwner)) { _dirtyFlags |= Simulation::DIRTY_SIMULATOR_ID; somethingChanged = true; + // recompute weOwnSimulation so that if this is the packet that tells use we are the owner, + // we ignore the physics changes from this packet. + weOwnSimulation = _simulationOwner.matchesValidID(myNodeID); } } { // When we own the simulation we don't accept updates to the entity's transform/velocities From e971da7dcbf706c75c379ecb004bc85e0e54689b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 2 May 2016 16:54:32 -0700 Subject: [PATCH 56/57] move dice GUI --- scripts/tutorials/createDice.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scripts/tutorials/createDice.js b/scripts/tutorials/createDice.js index 527735da57..8d6e6df816 100644 --- a/scripts/tutorials/createDice.js +++ b/scripts/tutorials/createDice.js @@ -13,6 +13,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // + + var isDice = false; var NUMBER_OF_DICE = 4; var LIFETIME = 10000; // Dice will live for about 3 hours @@ -29,14 +31,14 @@ var screenSize = Controller.getViewportDimensions(); var BUTTON_SIZE = 32; var PADDING = 3; - +var BOTTOM_PADDING = 50; //a helper library for creating toolbars Script.include("http://hifi-production.s3.amazonaws.com/tutorials/dice/toolBars.js"); -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.dice.toolbar", function(screenSize) { +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.toolbars.dice7", function(screenSize) { return { - x: (screenSize.x / 2 - BUTTON_SIZE * 2 + PADDING), - y: (screenSize.y - (BUTTON_SIZE + PADDING)) + x: (screenSize.x - BUTTON_SIZE*3), + y: (screenSize.y - 100) }; }); @@ -54,7 +56,7 @@ var offButton = toolBar.addOverlay("image", { var deleteButton = toolBar.addOverlay("image", { x: screenSize.x / 2 - BUTTON_SIZE, - y: screenSize.y - (BUTTON_SIZE + PADDING), + y: screenSize.y - (BUTTON_SIZE + PADDING)+BOTTOM_PADDING, width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: "http://hifi-production.s3.amazonaws.com/tutorials/dice/delete.png", @@ -69,7 +71,7 @@ var deleteButton = toolBar.addOverlay("image", { var diceIconURL = "http://hifi-production.s3.amazonaws.com/tutorials/dice/dice.png" var diceButton = toolBar.addOverlay("image", { x: screenSize.x / 2 + PADDING, - y: screenSize.y - (BUTTON_SIZE + PADDING), + y: screenSize.y - (BUTTON_SIZE + PADDING)+BOTTOM_PADDING, width: BUTTON_SIZE, height: BUTTON_SIZE, imageURL: diceIconURL, From a9ab531e273d8ddf54b91bec3f90c3ad58ef381b Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Mon, 2 May 2016 17:01:44 -0700 Subject: [PATCH 57/57] clear up toolbar name --- scripts/tutorials/createDice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tutorials/createDice.js b/scripts/tutorials/createDice.js index 8d6e6df816..00ec1184bc 100644 --- a/scripts/tutorials/createDice.js +++ b/scripts/tutorials/createDice.js @@ -35,7 +35,7 @@ var BOTTOM_PADDING = 50; //a helper library for creating toolbars Script.include("http://hifi-production.s3.amazonaws.com/tutorials/dice/toolBars.js"); -var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.toolbars.dice7", function(screenSize) { +var toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL, "highfidelity.toolbars-dice", function(screenSize) { return { x: (screenSize.x - BUTTON_SIZE*3), y: (screenSize.y - 100)