summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2010-12-20 13:20:47 +0100
committerGunnar Sletta <gunnar.sletta@nokia.com>2010-12-20 13:20:47 +0100
commite9365b6bb7c73ba5e696581e13ff47a204502014 (patch)
tree32c9c42b0f67fb53dc03d2d175344fbb99e6a874
parent06fce604c9843f622838308b1e51233671ab65ce (diff)
Start refactoring texture manager to make way for partial uploader and threaded
Simple texture manager now works according to spec
-rw-r--r--TODO4
-rw-r--r--src/adaptationlayers/adaptationlayers.pri2
-rw-r--r--src/adaptationlayers/qsgpartialuploadtexturemanager.cpp110
-rw-r--r--src/adaptationlayers/qsgpartialuploadtexturemanager.h21
-rw-r--r--src/scenegraph/coreapi/qsgtexturemanager.cpp211
-rw-r--r--src/scenegraph/coreapi/qsgtexturemanager.h13
6 files changed, 161 insertions, 200 deletions
diff --git a/TODO b/TODO
index bfcecc9..23cb6dd 100644
--- a/TODO
+++ b/TODO
@@ -77,6 +77,8 @@
- Use source to grab static image of subtree for later use
- Feed arbitrary texture values into shader effects?
- Adaptation layer plugin infrastructure?
+ - Outstanding bugs:
+ - Vsync driver never pauses
- TextureManager:
- In base class:
@@ -91,4 +93,4 @@
- default: partial in-thread upload
- mac: fast all sync uploads
- dumb:
-
+ - add tests for that createTexture does indeed return something...
diff --git a/src/adaptationlayers/adaptationlayers.pri b/src/adaptationlayers/adaptationlayers.pri
index e28ddfd..7e96cc3 100644
--- a/src/adaptationlayers/adaptationlayers.pri
+++ b/src/adaptationlayers/adaptationlayers.pri
@@ -7,6 +7,7 @@ HEADERS += \
$$PWD/default/default_glyphnode.h \
$$PWD/default/default_glyphnode_p.h \
# $$PWD/threadedtexturemanager.h
+ adaptationlayers/qsgpartialuploadtexturemanager.h
SOURCES += \
$$PWD/adaptationlayer.cpp \
@@ -15,6 +16,7 @@ SOURCES += \
$$PWD/default/default_glyphnode.cpp \
$$PWD/default/default_glyphnode_p.cpp \
# $$PWD/threadedtexturemanager.cpp
+ adaptationlayers/qsgpartialuploadtexturemanager.cpp
#macx:{
# SOURCES += adaptationlayers/mactexturemanager.cpp
diff --git a/src/adaptationlayers/qsgpartialuploadtexturemanager.cpp b/src/adaptationlayers/qsgpartialuploadtexturemanager.cpp
new file mode 100644
index 0000000..dd8af85
--- /dev/null
+++ b/src/adaptationlayers/qsgpartialuploadtexturemanager.cpp
@@ -0,0 +1,110 @@
+#include "qsgpartialuploadtexturemanager.h"
+
+
+
+QSGPartialUploadTextureManager::QSGPartialUploadTextureManager()
+{
+}
+
+
+
+
+void QSGPartialUploadTextureManager::timerEvent(QTimerEvent *)
+{
+// // ### gunnar:
+// // In the future, I forsee us starting / stopping this timer based
+// // on wether the vsync animation driver is running or not.
+// // Then we can also skip the "time since last upload" logic which
+// // is currently kinda messy and unpredictable.
+// if (d->lastUpload.elapsed() > 50) {
+// processAsyncTextures();
+// }
+}
+
+
+
+void QSGPartialUploadTextureManager::processAsyncTextures()
+{
+// QTime time;
+// time.start();
+
+// while (!d->requests.isEmpty()) {
+
+// UploadRequest *request = d->requests.first();
+
+// int w = request->image.width();
+// int h = request->image.height();
+
+// int hChunkCount = (w + d->uploadChunkSize - 1) / d->uploadChunkSize;
+// int vChunkCount = (h + d->uploadChunkSize - 1) / d->uploadChunkSize;
+// int chunkCount = hChunkCount * vChunkCount;
+// QSGTexture *t = request->texture;
+
+//// printf("\nASYNC: texture: %p, id=%d, size=(%dx%d), progress: %d / %d (%dx%d)\n",
+//// t,
+//// t->textureId(),
+//// w, h,
+//// request->progress, chunkCount, hChunkCount, vChunkCount);
+
+// // Create or bind the texture...
+// if (request->texture->textureId() == 0) {
+// while (glGetError() != GL_NO_ERROR) {}
+// GLuint id;
+// glGenTextures(1, &id);
+// glBindTexture(GL_TEXTURE_2D, id);
+// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
+
+// // Clean up
+// // Gracefully fail in case of an error...
+// GLuint error = glGetError();
+// if (error != GL_NO_ERROR) {
+// glBindTexture(GL_TEXTURE_2D, 0);
+// glDeleteTextures(1, &id);
+// d->requests.dequeue();
+// t->setStatus(QSGTexture::Null);
+// delete request;
+// return;
+// }
+
+// t->setTextureId(id);
+// t->setTextureSize(QSize(w, h));
+// t->setAlphaChannel(request->image.hasAlphaChannel());
+//// printf("ASYNC: created texture %p with id=%d\n", t, id);
+// } else {
+// glBindTexture(GL_TEXTURE_2D, t->textureId());
+// }
+
+// if (time.elapsed() > d->maxUploadTime)
+// break;
+
+// while (request->progress < chunkCount && time.elapsed() < d->maxUploadTime) {
+// int x = (request->progress % hChunkCount) * d->uploadChunkSize;
+// int y = (request->progress / hChunkCount) * d->uploadChunkSize;
+
+// QRect area = QRect(x, y, d->uploadChunkSize, d->uploadChunkSize) & request->image.rect();
+// QImage subImage = request->image.copy(area);
+//// printf("ASYNC: - doing another batch: %d (x=%d, y=%d, w=%d, h=%d\n",
+//// request->progress,
+//// x, y, subImage.width(), subImage.height());
+
+// swizzleBGRAToRGBA(&subImage);
+// glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, subImage.width(), subImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
+
+// ++request->progress;
+// }
+
+// if (request->progress == chunkCount) {
+// t->setStatus(QSGTexture::Ready);
+// QSGTextureCacheKey key = { request->image.cacheKey() };
+// d->cache.insert(key, t);
+// d->requests.dequeue();
+// delete request;
+// if (d->requests.size() == 0) {
+// killTimer(d->uploadTimer);
+// d->uploadTimer = 0;
+// }
+// }
+// }
+
+// glBindTexture(GL_TEXTURE_2D, 0);
+}
diff --git a/src/adaptationlayers/qsgpartialuploadtexturemanager.h b/src/adaptationlayers/qsgpartialuploadtexturemanager.h
new file mode 100644
index 0000000..c9f7773
--- /dev/null
+++ b/src/adaptationlayers/qsgpartialuploadtexturemanager.h
@@ -0,0 +1,21 @@
+#ifndef QSGPARTIALUPLOADTEXTUREMANAGER_H
+#define QSGPARTIALUPLOADTEXTUREMANAGER_H
+
+#include "qsgtexturemanager.h"
+
+class QSGPartialUploadTextureManager : public QSGTextureManager
+{
+public:
+ QSGPartialUploadTextureManager();
+
+ void setContext(QSGContext *context);
+
+ QSGTextureRef requestUpload(const QImage &image, const QObject *listener, const char *slot);
+
+protected:
+ void timerEvent(QTimerEvent *);
+ void processAsyncTextures();
+
+};
+
+#endif // QSGPARTIALUPLOADTEXTUREMANAGER_H
diff --git a/src/scenegraph/coreapi/qsgtexturemanager.cpp b/src/scenegraph/coreapi/qsgtexturemanager.cpp
index 11228e5..9a4d1f0 100644
--- a/src/scenegraph/coreapi/qsgtexturemanager.cpp
+++ b/src/scenegraph/coreapi/qsgtexturemanager.cpp
@@ -88,13 +88,6 @@ struct QSGTextureCacheKey {
}
};
-struct QSGTextureAsyncUpload {
- QImage image;
- int progress;
- QSGTexture *texture;
-};
-
-
uint qHash(const QSGTextureCacheKey &key)
{
return (key.cacheKey >> 32) ^ uint(key.cacheKey);
@@ -105,23 +98,17 @@ class QSGTextureManagerPrivate
public:
QSGTextureManagerPrivate()
: context(0)
- , maxUploadTime(5)
- , uploadChunkSize(64)
- , uploadTimer(0)
{
}
+ QSGTextureRef upload(const QImage &image, const QObject *listener, const char *slot);
+
QSGContext *context;
QHash<QSGTextureCacheKey, QSGTexture *> cache;
- QQueue<QSGTextureAsyncUpload> asyncUploads;
- int maxUploadTime;
- int uploadChunkSize;
-
- int uploadTimer;
- QTime lastUpload;
-
int maxTextureSize;
+
+ QSGTextureManager *q;
};
@@ -136,6 +123,7 @@ public:
QSGTextureManager::QSGTextureManager()
: d(new QSGTextureManagerPrivate)
{
+ d->q = this;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &d->maxTextureSize);
}
@@ -149,9 +137,7 @@ int QSGTextureManager::maxTextureSize() const
void QSGTextureManager::setContext(QSGContext *context)
{
Q_ASSERT(!d->context);
-
d->context = context;
- connect(d->context, SIGNAL(aboutToRenderNextFrame()), this, SLOT(processAsyncTextures()));
}
QSGContext *QSGTextureManager::context() const
@@ -162,13 +148,6 @@ QSGContext *QSGTextureManager::context() const
void QSGTextureManager::textureDestroyed(QObject *destroyed)
{
- for (int i=0; i<d->asyncUploads.size(); ++i) {
- if (destroyed == d->asyncUploads[i].texture) {
- d->asyncUploads.removeAt(i);
- break;
- }
- }
-
for (QHash<QSGTextureCacheKey, QSGTexture *>::iterator it = d->cache.begin();
it != d->cache.end(); ++it) {
if (it.value() == destroyed) {
@@ -176,7 +155,6 @@ void QSGTextureManager::textureDestroyed(QObject *destroyed)
break;
}
}
-
}
@@ -192,30 +170,16 @@ void QSGTextureManager::swizzleBGRAToRGBA(QImage *image)
}
-
-QSGTextureRef QSGTextureManager::upload(const QImage &image)
+QSGTextureRef QSGTextureManagerPrivate::upload(const QImage &image, const QObject *listener, const char *slot)
{
Q_ASSERT(!image.isNull());
// Check if the image is already uploaded and cached
QSGTextureCacheKey key = { image.cacheKey() };
- QSGTexture *texture = d->cache.value(key);
+ QSGTexture *texture = cache.value(key);
if (texture)
return QSGTextureRef(texture);
- // Check if the image is already scheduled for asynchronous upload...
- // If so, kill the partial texture and upload in one go below using a new texture..
- for (int i=0; i<d->asyncUploads.size(); ++i) {
- const QSGTextureAsyncUpload &work = d->asyncUploads.at(i);
- if (work.image.cacheKey() == image.cacheKey()) {
- texture = work.texture;
- GLuint tid = texture->textureId();
- if (tid)
- glDeleteTextures(1, &tid);
- d->asyncUploads.removeAt(i);
- }
- }
-
// image not already scheduled, upload normally...
while (glGetError() != GL_NO_ERROR) {}
@@ -241,174 +205,37 @@ QSGTextureRef QSGTextureManager::upload(const QImage &image)
return QSGTextureRef();
}
- if (!texture)
- texture = new QSGTexture;
+ texture = new QSGTexture;
+ if (listener && slot)
+ QObject::connect(texture, SIGNAL(statusChanged(int)), listener, slot);
texture->setTextureId(id);
texture->setTextureSize(image.size());
texture->setAlphaChannel(image.hasAlphaChannel());
texture->setStatus(QSGTexture::Ready);
- connect(texture, SIGNAL(destroyed(QObject*)), this, SLOT(textureDestroyed(QObject*)));
+ QObject::connect(texture, SIGNAL(destroyed(QObject*)), q, SLOT(textureDestroyed(QObject*)));
- d->cache.insert(key, texture);
+ cache.insert(key, texture);
QSGTextureRef ref(texture);
return ref;
}
+QSGTextureRef QSGTextureManager::upload(const QImage &image)
+{
+ return d->upload(image, 0, 0);
+}
+
+
/*!
Schedules \a image to be uploaded.
- The function returns a texture reference which is
-
*/
QSGTextureRef QSGTextureManager::requestUpload(const QImage &image,
const QObject *listener,
const char *slot)
{
- Q_ASSERT(!image.isNull());
- QSGTexture *t = 0;
-
- // Check if the image is already uploaded and thus part of the cache..
- QSGTextureCacheKey key = { image.cacheKey() };
- t = d->cache.value(key);
- if (t)
- return QSGTextureRef(t);
-
- // Check if the image is already scheduled for asynchronous upload...
- for (int i=0; i<d->asyncUploads.size(); ++i) {
- const QSGTextureAsyncUpload &work = d->asyncUploads.at(i);
- if (work.image.cacheKey() == image.cacheKey()) {
- return QSGTextureRef(work.texture);
- }
- }
-
- // Not present in any caches, upload normally...
- t = new QSGTexture();
- t->setStatus(QSGTexture::Loading);
- if (listener && slot)
- connect(t, SIGNAL(statusChanged(int)), listener, slot);
- connect(t, SIGNAL(destroyed(QObject*)), this, SLOT(textureDestroyed(QObject*)));
-
- QSGTextureAsyncUpload work;
- work.image = image;
- work.progress = 0;
- work.texture = t;
-
- d->asyncUploads << work;
-
- if (d->uploadTimer == 0) {
- d->uploadTimer = startTimer(30);
- }
-
- return QSGTextureRef(t);
-}
-
-
-void QSGTextureManager::timerEvent(QTimerEvent *)
-{
- // ### gunnar:
- // In the future, I forsee us starting / stopping this timer based
- // on wether the vsync animation driver is running or not.
- // Then we can also skip the "time since last upload" logic which
- // is currently kinda messy and unpredictable.
- if (d->lastUpload.elapsed() > 50) {
- // Its been a while since the last frame tick, so we are pausing...
- // Upload a "big" chunk...
- int old = d->maxUploadTime;
- d->maxUploadTime = 50;
- processAsyncTextures();
- d->maxUploadTime = old;
- }
+ return d->upload(image, listener, slot);
}
-void QSGTextureManager::processAsyncTextures()
-{
- QTime time;
- time.start();
-
- d->lastUpload.restart();
- while (!d->asyncUploads.isEmpty()) {
-
- QSGTextureAsyncUpload &upload = d->asyncUploads.first();
-
- int w = upload.image.width();
- int h = upload.image.height();
-
- int hChunkCount = (w + d->uploadChunkSize - 1) / d->uploadChunkSize;
- int vChunkCount = (h + d->uploadChunkSize - 1) / d->uploadChunkSize;
- int chunkCount = hChunkCount * vChunkCount;
- QSGTexture *t = upload.texture;
-
-// printf("\nASYNC: texture: %p, id=%d, size=(%dx%d), progress: %d / %d (%dx%d)\n",
-// t,
-// t->textureId(),
-// w, h,
-// upload.progress, chunkCount, hChunkCount, vChunkCount);
-
- // Create or bind the texture...
- if (upload.texture->textureId() == 0) {
- while (glGetError() != GL_NO_ERROR) {}
- GLuint id;
- glGenTextures(1, &id);
- glBindTexture(GL_TEXTURE_2D, id);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
-
- // Clean up
- // Gracefully fail in case of an error...
- GLuint error = glGetError();
- if (error != GL_NO_ERROR) {
- glBindTexture(GL_TEXTURE_2D, 0);
- glDeleteTextures(1, &id);
- d->asyncUploads.dequeue();
- if (error != GL_OUT_OF_MEMORY) {
- qWarning("QSGTextureManager::async upload failed, OpenGL error code: %x", error);
- t->setStatus(QSGTexture::Error);
- } else {
- t->setStatus(QSGTexture::Null);
- }
- return;
- }
-
- t->setTextureId(id);
- t->setTextureSize(QSize(w, h));
- t->setAlphaChannel(upload.image.hasAlphaChannel());
-// printf("ASYNC: created texture %p with id=%d\n", t, id);
- } else {
- glBindTexture(GL_TEXTURE_2D, t->textureId());
- }
-
- if (time.elapsed() > d->maxUploadTime)
- break;
-
- while (upload.progress < chunkCount && time.elapsed() < d->maxUploadTime) {
- int x = (upload.progress % hChunkCount) * d->uploadChunkSize;
- int y = (upload.progress / hChunkCount) * d->uploadChunkSize;
-
- QRect area = QRect(x, y, d->uploadChunkSize, d->uploadChunkSize) & upload.image.rect();
- QImage subImage = upload.image.copy(area);
-// printf("ASYNC: - doing another batch: %d (x=%d, y=%d, w=%d, h=%d\n",
-// upload.progress,
-// x, y, subImage.width(), subImage.height());
-
- swizzleBGRAToRGBA(&subImage);
- glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, subImage.width(), subImage.height(), GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits());
-
- ++upload.progress;
- }
-
- if (upload.progress == chunkCount) {
- t->setStatus(QSGTexture::Ready);
- QSGTextureCacheKey key = { upload.image.cacheKey() };
- d->cache.insert(key, t);
- d->asyncUploads.dequeue();
- if (d->asyncUploads.size() == 0) {
- killTimer(d->uploadTimer);
- d->uploadTimer = 0;
- }
- }
- }
-
- glBindTexture(GL_TEXTURE_2D, 0);
-}
diff --git a/src/scenegraph/coreapi/qsgtexturemanager.h b/src/scenegraph/coreapi/qsgtexturemanager.h
index 2b065f3..45f0170 100644
--- a/src/scenegraph/coreapi/qsgtexturemanager.h
+++ b/src/scenegraph/coreapi/qsgtexturemanager.h
@@ -47,6 +47,7 @@
#include <QObject>
#include <QImage>
+#include <QQueue>
class QSGTextureManagerPrivate;
@@ -58,8 +59,7 @@ public:
enum Status {
Null,
Loading,
- Ready,
- Error
+ Ready
};
QSGTexture();
@@ -122,7 +122,8 @@ public:
: m_sub_rect(other.m_sub_rect)
{
m_texture = other.m_texture;
- ++m_texture->m_ref_count;
+ if (m_texture)
+ ++m_texture->m_ref_count;
}
~QSGTextureRef()
@@ -179,14 +180,12 @@ public:
int maxTextureSize() const;
-protected:
- void timerEvent(QTimerEvent *);
-
private slots:
- void processAsyncTextures();
void textureDestroyed(QObject *texture);
private:
+ friend class QSGTextureManagerPrivate;
+
QSGTextureManagerPrivate *d;
};