Working on LRU cache for resources.

This commit is contained in:
Andrzej Kapolka 2014-03-10 10:49:38 -07:00
parent 9d2758ee14
commit d2f947aee1
7 changed files with 103 additions and 25 deletions

View file

@ -294,7 +294,8 @@ QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, cons
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
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);
return geometry.staticCast<Resource>();
}
@ -536,6 +537,15 @@ void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
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) {
_geometry = geometry;

View file

@ -89,6 +89,7 @@ public:
protected:
virtual void downloadFinished(QNetworkReply* reply);
virtual void reinsert() const;
Q_INVOKABLE void setGeometry(const FBXGeometry& geometry);

View file

@ -128,9 +128,12 @@ QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool no
}
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
if (texture.isNull()) {
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url));
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url), &Resource::allReferencesCleared);
texture->setSelf(texture);
texture->setCache(this);
_dilatableNetworkTextures.insert(url, texture);
} else {
_unusedResources.removeOne(texture);
}
return texture;
}
@ -229,7 +232,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
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() {
@ -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> texture = _dilatedTextures.value(dilation);
if (texture.isNull()) {
@ -400,3 +383,28 @@ QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilatio
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));
}

View file

@ -76,6 +76,8 @@ protected:
private:
friend class DilatableNetworkTexture;
QOpenGLFramebufferObject* createFramebufferObject();
GLuint _permutationNormalTextureID;
@ -151,6 +153,7 @@ public:
protected:
virtual void imageLoaded(const QImage& image);
virtual void reinsert();
private:

View file

@ -51,7 +51,7 @@ QSharedPointer<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url)
QSharedPointer<Resource> ScriptCache::createResource(const QUrl& url,
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) :

View file

@ -18,6 +18,13 @@ ResourceCache::ResourceCache(QObject* parent) :
QObject(parent) {
}
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) {
if (!url.isValid() && fallback.isValid()) {
return getResource(fallback, QUrl(), delayLoad);
@ -27,11 +34,25 @@ QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl&
resource = createResource(url, fallback.isValid() ?
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
resource->setSelf(resource);
resource->setCache(this);
_resources.insert(url, resource);
} else {
_unusedResources.removeOne(resource);
}
return resource;
}
void ResourceCache::addUnusedResource(const QSharedPointer<Resource>& resource) {
const int RETAINED_RESOURCE_COUNT = 1;
if (_unusedResources.size() > RETAINED_RESOURCE_COUNT) {
// unload the oldest resource
QSharedPointer<Resource> oldResource = _unusedResources.takeFirst();
oldResource->setCache(NULL);
}
_unusedResources.append(resource);
}
void ResourceCache::attemptRequest(Resource* resource) {
if (_requestLimit <= 0) {
// wait until a slot becomes available
@ -74,6 +95,7 @@ int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
QList<QPointer<Resource> > ResourceCache::_pendingRequests;
Resource::Resource(const QUrl& url, bool delayLoad) :
_url(url),
_request(url),
_startedLoading(false),
_failedToLoad(false),
@ -141,6 +163,22 @@ float Resource::getLoadPriority() {
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->_unusedResources.append(self);
} else {
qDebug() << "deleting" << _url;
delete this;
}
}
void Resource::attemptRequest() {
_startedLoading = true;
ResourceCache::attemptRequest(this);
@ -155,6 +193,10 @@ void Resource::finishedLoading(bool success) {
_loadPriorities.clear();
}
void Resource::reinsert() {
_cache->_resources.insert(_url, _self);
}
const int REPLY_TIMEOUT_MS = 5000;
void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {

View file

@ -38,9 +38,12 @@ public:
static int getRequestLimit() { return _requestLimit; }
ResourceCache(QObject* parent = NULL);
virtual ~ResourceCache();
protected:
QList<QSharedPointer<Resource> > _unusedResources;
/// 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
@ -52,13 +55,15 @@ protected:
virtual QSharedPointer<Resource> createResource(const QUrl& url,
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) = 0;
void addUnusedResource(const QSharedPointer<Resource>& resource);
static void attemptRequest(Resource* resource);
static void requestCompleted();
private:
friend class Resource;
QHash<QUrl, QWeakPointer<Resource> > _resources;
static QNetworkAccessManager* _networkAccessManager;
@ -95,6 +100,10 @@ public:
void setSelf(const QWeakPointer<Resource>& self) { _self = self; }
void setCache(ResourceCache* cache) { _cache = cache; }
void allReferencesCleared();
protected slots:
void attemptRequest();
@ -107,12 +116,17 @@ protected:
/// Should be called by subclasses when all the loading that will be done has been done.
Q_INVOKABLE void finishedLoading(bool success);
/// Reinserts this resource into the cache.
virtual void reinsert();
QUrl _url;
QNetworkRequest _request;
bool _startedLoading;
bool _failedToLoad;
bool _loaded;
QHash<QPointer<QObject>, float> _loadPriorities;
QWeakPointer<Resource> _self;
QPointer<ResourceCache> _cache;
private slots: