mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-09 04:58:08 +02:00
Merge pull request #2255 from ey6es/joints
Rather than unloading resources immediately when unused, use a simple LRU cache.
This commit is contained in:
commit
df4128a2dd
7 changed files with 121 additions and 26 deletions
|
@ -294,7 +294,8 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
|
||||||
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
|
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
|
||||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
|
|
||||||
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad));
|
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad),
|
||||||
|
&Resource::allReferencesCleared);
|
||||||
geometry->setLODParent(geometry);
|
geometry->setLODParent(geometry);
|
||||||
return geometry.staticCast<Resource>();
|
return geometry.staticCast<Resource>();
|
||||||
}
|
}
|
||||||
|
@ -536,6 +537,15 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
|
||||||
QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping));
|
QThreadPool::globalInstance()->start(new GeometryReader(_self, url, reply, _mapping));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkGeometry::reinsert() {
|
||||||
|
Resource::reinsert();
|
||||||
|
|
||||||
|
_lodParent = qWeakPointerCast<NetworkGeometry, Resource>(_self);
|
||||||
|
foreach (const QSharedPointer<NetworkGeometry>& lod, _lods) {
|
||||||
|
lod->setLODParent(_lodParent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
|
void NetworkGeometry::setGeometry(const FBXGeometry& geometry) {
|
||||||
_geometry = geometry;
|
_geometry = geometry;
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void downloadFinished(QNetworkReply* reply);
|
virtual void downloadFinished(QNetworkReply* reply);
|
||||||
|
virtual void reinsert();
|
||||||
|
|
||||||
Q_INVOKABLE void setGeometry(const FBXGeometry& geometry);
|
Q_INVOKABLE void setGeometry(const FBXGeometry& geometry);
|
||||||
|
|
||||||
|
|
|
@ -128,9 +128,12 @@ QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool no
|
||||||
}
|
}
|
||||||
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
|
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
|
||||||
if (texture.isNull()) {
|
if (texture.isNull()) {
|
||||||
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url));
|
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url), &Resource::allReferencesCleared);
|
||||||
texture->setSelf(texture);
|
texture->setSelf(texture);
|
||||||
|
texture->setCache(this);
|
||||||
_dilatableNetworkTextures.insert(url, texture);
|
_dilatableNetworkTextures.insert(url, texture);
|
||||||
|
} else {
|
||||||
|
_unusedResources.remove(texture->getLRUKey());
|
||||||
}
|
}
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
@ -229,7 +232,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
|
||||||
|
|
||||||
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
||||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
return QSharedPointer<Resource>(new NetworkTexture(url, *(const bool*)extra));
|
return QSharedPointer<Resource>(new NetworkTexture(url, *(const bool*)extra), &Resource::allReferencesCleared);
|
||||||
}
|
}
|
||||||
|
|
||||||
QOpenGLFramebufferObject* TextureCache::createFramebufferObject() {
|
QOpenGLFramebufferObject* TextureCache::createFramebufferObject() {
|
||||||
|
@ -352,26 +355,6 @@ DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url) :
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void DilatableNetworkTexture::imageLoaded(const QImage& image) {
|
|
||||||
_image = image;
|
|
||||||
|
|
||||||
// scan out from the center to find inner and outer radii
|
|
||||||
int halfWidth = image.width() / 2;
|
|
||||||
int halfHeight = image.height() / 2;
|
|
||||||
const int BLACK_THRESHOLD = 32;
|
|
||||||
while (_innerRadius < halfWidth && qGray(image.pixel(halfWidth + _innerRadius, halfHeight)) < BLACK_THRESHOLD) {
|
|
||||||
_innerRadius++;
|
|
||||||
}
|
|
||||||
_outerRadius = _innerRadius;
|
|
||||||
const int TRANSPARENT_THRESHOLD = 32;
|
|
||||||
while (_outerRadius < halfWidth && qAlpha(image.pixel(halfWidth + _outerRadius, halfHeight)) > TRANSPARENT_THRESHOLD) {
|
|
||||||
_outerRadius++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear out any textures we generated before loading
|
|
||||||
_dilatedTextures.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilation) {
|
QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilation) {
|
||||||
QSharedPointer<Texture> texture = _dilatedTextures.value(dilation);
|
QSharedPointer<Texture> texture = _dilatedTextures.value(dilation);
|
||||||
if (texture.isNull()) {
|
if (texture.isNull()) {
|
||||||
|
@ -400,3 +383,28 @@ QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilatio
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DilatableNetworkTexture::imageLoaded(const QImage& image) {
|
||||||
|
_image = image;
|
||||||
|
|
||||||
|
// scan out from the center to find inner and outer radii
|
||||||
|
int halfWidth = image.width() / 2;
|
||||||
|
int halfHeight = image.height() / 2;
|
||||||
|
const int BLACK_THRESHOLD = 32;
|
||||||
|
while (_innerRadius < halfWidth && qGray(image.pixel(halfWidth + _innerRadius, halfHeight)) < BLACK_THRESHOLD) {
|
||||||
|
_innerRadius++;
|
||||||
|
}
|
||||||
|
_outerRadius = _innerRadius;
|
||||||
|
const int TRANSPARENT_THRESHOLD = 32;
|
||||||
|
while (_outerRadius < halfWidth && qAlpha(image.pixel(halfWidth + _outerRadius, halfHeight)) > TRANSPARENT_THRESHOLD) {
|
||||||
|
_outerRadius++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear out any textures we generated before loading
|
||||||
|
_dilatedTextures.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DilatableNetworkTexture::reinsert() {
|
||||||
|
static_cast<TextureCache*>(_cache.data())->_dilatableNetworkTextures.insert(_url,
|
||||||
|
qWeakPointerCast<NetworkTexture, Resource>(_self));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,8 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
friend class DilatableNetworkTexture;
|
||||||
|
|
||||||
QOpenGLFramebufferObject* createFramebufferObject();
|
QOpenGLFramebufferObject* createFramebufferObject();
|
||||||
|
|
||||||
GLuint _permutationNormalTextureID;
|
GLuint _permutationNormalTextureID;
|
||||||
|
@ -151,6 +153,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void imageLoaded(const QImage& image);
|
virtual void imageLoaded(const QImage& image);
|
||||||
|
virtual void reinsert();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ QSharedPointer<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url)
|
||||||
|
|
||||||
QSharedPointer<Resource> ScriptCache::createResource(const QUrl& url,
|
QSharedPointer<Resource> ScriptCache::createResource(const QUrl& url,
|
||||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
return QSharedPointer<Resource>(new NetworkProgram(this, url));
|
return QSharedPointer<Resource>(new NetworkProgram(this, url), &Resource::allReferencesCleared);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
|
NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
|
||||||
|
|
|
@ -15,7 +15,15 @@
|
||||||
#include "ResourceCache.h"
|
#include "ResourceCache.h"
|
||||||
|
|
||||||
ResourceCache::ResourceCache(QObject* parent) :
|
ResourceCache::ResourceCache(QObject* parent) :
|
||||||
QObject(parent) {
|
QObject(parent),
|
||||||
|
_lastLRUKey(0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceCache::~ResourceCache() {
|
||||||
|
// make sure our unused resources know we're out of commission
|
||||||
|
foreach (const QSharedPointer<Resource>& resource, _unusedResources) {
|
||||||
|
resource->setCache(NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) {
|
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) {
|
||||||
|
@ -27,11 +35,27 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
|
||||||
resource = createResource(url, fallback.isValid() ?
|
resource = createResource(url, fallback.isValid() ?
|
||||||
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
|
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
|
||||||
resource->setSelf(resource);
|
resource->setSelf(resource);
|
||||||
|
resource->setCache(this);
|
||||||
_resources.insert(url, resource);
|
_resources.insert(url, resource);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
_unusedResources.remove(resource->getLRUKey());
|
||||||
}
|
}
|
||||||
return resource;
|
return resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
|
||||||
|
const int RETAINED_RESOURCE_COUNT = 50;
|
||||||
|
if (_unusedResources.size() > RETAINED_RESOURCE_COUNT) {
|
||||||
|
// unload the oldest resource
|
||||||
|
QMap<int, QSharedPointer<Resource> >::iterator it = _unusedResources.begin();
|
||||||
|
it.value()->setCache(NULL);
|
||||||
|
_unusedResources.erase(it);
|
||||||
|
}
|
||||||
|
resource->setLRUKey(++_lastLRUKey);
|
||||||
|
_unusedResources.insert(resource->getLRUKey(), resource);
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceCache::attemptRequest(Resource* resource) {
|
void ResourceCache::attemptRequest(Resource* resource) {
|
||||||
if (_requestLimit <= 0) {
|
if (_requestLimit <= 0) {
|
||||||
// wait until a slot becomes available
|
// wait until a slot becomes available
|
||||||
|
@ -74,10 +98,12 @@ int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
|
||||||
QList<QPointer<Resource> > ResourceCache::_pendingRequests;
|
QList<QPointer<Resource> > ResourceCache::_pendingRequests;
|
||||||
|
|
||||||
Resource::Resource(const QUrl& url, bool delayLoad) :
|
Resource::Resource(const QUrl& url, bool delayLoad) :
|
||||||
|
_url(url),
|
||||||
_request(url),
|
_request(url),
|
||||||
_startedLoading(false),
|
_startedLoading(false),
|
||||||
_failedToLoad(false),
|
_failedToLoad(false),
|
||||||
_loaded(false),
|
_loaded(false),
|
||||||
|
_lruKey(0),
|
||||||
_reply(NULL),
|
_reply(NULL),
|
||||||
_attempts(0) {
|
_attempts(0) {
|
||||||
|
|
||||||
|
@ -141,6 +167,21 @@ float Resource::getLoadPriority() {
|
||||||
return highestPriority;
|
return highestPriority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Resource::allReferencesCleared() {
|
||||||
|
if (_cache) {
|
||||||
|
// create and reinsert new shared pointer
|
||||||
|
QSharedPointer<Resource> self(this, &Resource::allReferencesCleared);
|
||||||
|
setSelf(self);
|
||||||
|
reinsert();
|
||||||
|
|
||||||
|
// add to the unused list
|
||||||
|
_cache->addUnusedResource(self);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Resource::attemptRequest() {
|
void Resource::attemptRequest() {
|
||||||
_startedLoading = true;
|
_startedLoading = true;
|
||||||
ResourceCache::attemptRequest(this);
|
ResourceCache::attemptRequest(this);
|
||||||
|
@ -155,6 +196,10 @@ void Resource::finishedLoading(bool success) {
|
||||||
_loadPriorities.clear();
|
_loadPriorities.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Resource::reinsert() {
|
||||||
|
_cache->_resources.insert(_url, _self);
|
||||||
|
}
|
||||||
|
|
||||||
const int REPLY_TIMEOUT_MS = 5000;
|
const int REPLY_TIMEOUT_MS = 5000;
|
||||||
|
|
||||||
void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
|
@ -189,6 +234,7 @@ void Resource::makeRequest() {
|
||||||
|
|
||||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
||||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
||||||
|
connect(_reply, SIGNAL(finished()), SLOT(handleReplyFinished()));
|
||||||
|
|
||||||
_replyTimer = new QTimer(this);
|
_replyTimer = new QTimer(this);
|
||||||
connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout()));
|
connect(_replyTimer, SIGNAL(timeout()), SLOT(handleReplyTimeout()));
|
||||||
|
@ -233,6 +279,11 @@ void Resource::handleReplyError(QNetworkReply::NetworkError error, QDebug debug)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Resource::handleReplyFinished() {
|
||||||
|
qDebug() << "Got finished without download progress/error?" << _url;
|
||||||
|
handleDownloadProgress(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
uint qHash(const QPointer<QObject>& value, uint seed) {
|
uint qHash(const QPointer<QObject>& value, uint seed) {
|
||||||
return qHash(value.data(), seed);
|
return qHash(value.data(), seed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,9 +38,12 @@ public:
|
||||||
static int getRequestLimit() { return _requestLimit; }
|
static int getRequestLimit() { return _requestLimit; }
|
||||||
|
|
||||||
ResourceCache(QObject* parent = NULL);
|
ResourceCache(QObject* parent = NULL);
|
||||||
|
virtual ~ResourceCache();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
QMap<int, QSharedPointer<Resource> > _unusedResources;
|
||||||
|
|
||||||
/// Loads a resource from the specified URL.
|
/// Loads a resource from the specified URL.
|
||||||
/// \param fallback a fallback URL to load if the desired one is unavailable
|
/// \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
|
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
|
||||||
|
@ -52,14 +55,17 @@ protected:
|
||||||
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||||
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) = 0;
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) = 0;
|
||||||
|
|
||||||
|
void addUnusedResource(const QSharedPointer<Resource>& resource);
|
||||||
|
|
||||||
static void attemptRequest(Resource* resource);
|
static void attemptRequest(Resource* resource);
|
||||||
static void requestCompleted();
|
static void requestCompleted();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
friend class Resource;
|
friend class Resource;
|
||||||
|
|
||||||
QHash<QUrl, QWeakPointer<Resource> > _resources;
|
QHash<QUrl, QWeakPointer<Resource> > _resources;
|
||||||
|
int _lastLRUKey;
|
||||||
|
|
||||||
static QNetworkAccessManager* _networkAccessManager;
|
static QNetworkAccessManager* _networkAccessManager;
|
||||||
static int _requestLimit;
|
static int _requestLimit;
|
||||||
|
@ -75,6 +81,9 @@ public:
|
||||||
Resource(const QUrl& url, bool delayLoad = false);
|
Resource(const QUrl& url, bool delayLoad = false);
|
||||||
~Resource();
|
~Resource();
|
||||||
|
|
||||||
|
/// Returns the key last used to identify this resource in the unused map.
|
||||||
|
int getLRUKey() const { return _lruKey; }
|
||||||
|
|
||||||
/// Makes sure that the resource has started loading.
|
/// Makes sure that the resource has started loading.
|
||||||
void ensureLoading();
|
void ensureLoading();
|
||||||
|
|
||||||
|
@ -95,6 +104,10 @@ public:
|
||||||
|
|
||||||
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
|
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
|
||||||
|
|
||||||
|
void setCache(ResourceCache* cache) { _cache = cache; }
|
||||||
|
|
||||||
|
void allReferencesCleared();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
||||||
void attemptRequest();
|
void attemptRequest();
|
||||||
|
@ -107,27 +120,36 @@ protected:
|
||||||
/// Should be called by subclasses when all the loading that will be done has been done.
|
/// Should be called by subclasses when all the loading that will be done has been done.
|
||||||
Q_INVOKABLE void finishedLoading(bool success);
|
Q_INVOKABLE void finishedLoading(bool success);
|
||||||
|
|
||||||
|
/// Reinserts this resource into the cache.
|
||||||
|
virtual void reinsert();
|
||||||
|
|
||||||
|
QUrl _url;
|
||||||
QNetworkRequest _request;
|
QNetworkRequest _request;
|
||||||
bool _startedLoading;
|
bool _startedLoading;
|
||||||
bool _failedToLoad;
|
bool _failedToLoad;
|
||||||
bool _loaded;
|
bool _loaded;
|
||||||
QHash<QPointer<QObject>, float> _loadPriorities;
|
QHash<QPointer<QObject>, float> _loadPriorities;
|
||||||
QWeakPointer<Resource> _self;
|
QWeakPointer<Resource> _self;
|
||||||
|
QPointer<ResourceCache> _cache;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
void handleReplyError();
|
void handleReplyError();
|
||||||
|
void handleReplyFinished();
|
||||||
void handleReplyTimeout();
|
void handleReplyTimeout();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void setLRUKey(int lruKey) { _lruKey = lruKey; }
|
||||||
|
|
||||||
void makeRequest();
|
void makeRequest();
|
||||||
|
|
||||||
void handleReplyError(QNetworkReply::NetworkError error, QDebug debug);
|
void handleReplyError(QNetworkReply::NetworkError error, QDebug debug);
|
||||||
|
|
||||||
friend class ResourceCache;
|
friend class ResourceCache;
|
||||||
|
|
||||||
|
int _lruKey;
|
||||||
QNetworkReply* _reply;
|
QNetworkReply* _reply;
|
||||||
QTimer* _replyTimer;
|
QTimer* _replyTimer;
|
||||||
qint64 _bytesReceived;
|
qint64 _bytesReceived;
|
||||||
|
|
Loading…
Reference in a new issue