Return ScriptableResource from prefetch

This commit is contained in:
Zach Pomerantz 2016-04-19 22:03:40 -07:00
parent 3c7fc95d6c
commit 8ad8b5d0d9
3 changed files with 105 additions and 27 deletions

View file

@ -119,6 +119,57 @@ QSharedPointer<Resource> ResourceCacheSharedItems::getHighestPendingRequest() {
return highestResource;
}
ScriptableResource::ScriptableResource(const QSharedPointer<Resource>& 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<NodeList>()->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;

View file

@ -78,6 +78,40 @@ private:
QList<QWeakPointer<Resource>> _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>& resource = QSharedPointer<Resource>());
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> _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<int, QSharedPointer<Resource>> _unusedResources;
QMutex _prefetchingResourcesLock{ QMutex::Recursive };
QMap<QUrl, QSharedPointer<Resource>> _prefetchingResources;
};
/// Base class for resources.
@ -292,6 +324,7 @@ private:
void reinsert();
friend class ResourceCache;
friend class ScriptableResource;
ResourceRequest* _request = nullptr;
int _lruKey = 0;

View file

@ -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<ScriptableResourceRawPtr>(resource),
QScriptEngine::ScriptOwnership);
return object;
}
static void scriptableResourceFromScriptValue(const QScriptValue& value, ScriptableResourceRawPtr& resource) {
resource = static_cast<ScriptableResourceRawPtr>(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)));