From 56bf92c816687942670cfd599b76f00705392d2e Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 8 Dec 2010 09:04:06 +0100 Subject: Asynchronous texture loading on the rendering thread. Split the textures up into 64x64 chunks and only process textures for a few ms prior to each frame... --- .../default/default_texturenode.cpp | 2 +- src/canvas/qxgraphicsview.cpp | 4 +- src/graphicsitems/qximagebase.cpp | 7 +- src/scenegraph/coreapi/qsgcontext.cpp | 18 ++- src/scenegraph/coreapi/qsgcontext.h | 4 + src/scenegraph/coreapi/qsgtexturemanager.cpp | 126 +++++++++++++++++++-- src/scenegraph/coreapi/qsgtexturemanager.h | 13 ++- 7 files changed, 153 insertions(+), 21 deletions(-) diff --git a/src/adaptationlayers/default/default_texturenode.cpp b/src/adaptationlayers/default/default_texturenode.cpp index 5dcbba5..077f7de 100644 --- a/src/adaptationlayers/default/default_texturenode.cpp +++ b/src/adaptationlayers/default/default_texturenode.cpp @@ -213,7 +213,7 @@ void DefaultTextureNode::updateGeometry() v[0].tx = v[1].tx = src.left(); v[2].tx = v[3].tx = src.right(); v[0].ty = v[2].ty = src.top(); - v[1].ty = v[3].ty = src.bottom(); + v[1].ty = v[3].ty = src.bottom(); v += 4; } } diff --git a/src/canvas/qxgraphicsview.cpp b/src/canvas/qxgraphicsview.cpp index b3fe658..1057900 100644 --- a/src/canvas/qxgraphicsview.cpp +++ b/src/canvas/qxgraphicsview.cpp @@ -233,7 +233,8 @@ void QxGraphicsView::paintEvent(QPaintEvent *e) d->sg->renderer()->setDeviceRect(rect()); d->sg->renderer()->setProjectMatrixToDeviceRect(); - d->sg->renderer()->renderScene(); + + d->sg->renderNextFrame(); #ifndef Q_WS_QPA // printf("QxGraphicsView: Swapping...\n"); @@ -605,7 +606,6 @@ void QxGraphicsView::showEvent(QShowEvent *e) */ void QxGraphicsView::initializeSceneGraph() { - #ifdef Q_WS_QPA QPlatformWindow *platformWindow = window()->platformWindow(); QPlatformGLContext *platformContext = const_cast(platformWindow->glContext()); diff --git a/src/graphicsitems/qximagebase.cpp b/src/graphicsitems/qximagebase.cpp index 21bf956..185c291 100644 --- a/src/graphicsitems/qximagebase.cpp +++ b/src/graphicsitems/qximagebase.cpp @@ -215,12 +215,13 @@ void QxImageBase::textureStatusChanged(int status) if (status == QSGTexture::Ready) { d->status = Ready; emit statusChanged(d->status); - } - if (!d->texture.isNull()) { - pixmapChange(); + if (!d->texture.isNull()) { + pixmapChange(); + } } + } void QxImageBase::requestProgress(qint64 received, qint64 total) diff --git a/src/scenegraph/coreapi/qsgcontext.cpp b/src/scenegraph/coreapi/qsgcontext.cpp index a2fe816..152452c 100644 --- a/src/scenegraph/coreapi/qsgcontext.cpp +++ b/src/scenegraph/coreapi/qsgcontext.cpp @@ -130,6 +130,16 @@ bool QSGContext::isReady() const return d->gl; } + +void QSGContext::renderNextFrame() +{ + Q_D(QSGContext); + + emit aboutToRenderNextFrame(); + + d->renderer->renderScene(); +} + /*! Factory function for scene graph backends of the Rectangle element. */ @@ -172,9 +182,11 @@ QSGTextureManager *QSGContext::createTextureManager() { QStringList args = qApp->arguments(); + QSGTextureManager *manager; + // if (args.contains("--basic-texture-manager")) { // printf("QSGContext: Using basic texture manager\n"); - return new QSGTextureManager; + manager = new QSGTextureManager; // } else if (args.contains("--threaded-texture-manager")) { // printf("QSGContext: Using threaded texture manager\n"); // return new QSGThreadedTextureManager; @@ -191,5 +203,7 @@ QSGTextureManager *QSGContext::createTextureManager() // return new QSGEglFSThreadedTextureManager; //#endif -// return new TextureManager; + manager->setContext(this); + + return manager; } diff --git a/src/scenegraph/coreapi/qsgcontext.h b/src/scenegraph/coreapi/qsgcontext.h index 90f7451..afdb34b 100644 --- a/src/scenegraph/coreapi/qsgcontext.h +++ b/src/scenegraph/coreapi/qsgcontext.h @@ -38,6 +38,8 @@ public: bool isReady() const; + virtual void renderNextFrame(); + virtual RectangleNodeInterface *createRectangleNode(); virtual TextureNodeInterface *createTextureNode(); virtual GlyphNodeInterface *createGlyphNode(); @@ -46,6 +48,8 @@ public: signals: void ready(); + + void aboutToRenderNextFrame(); }; #endif // QSGCONTEXT_H diff --git a/src/scenegraph/coreapi/qsgtexturemanager.cpp b/src/scenegraph/coreapi/qsgtexturemanager.cpp index 89cbe19..5e468a0 100644 --- a/src/scenegraph/coreapi/qsgtexturemanager.cpp +++ b/src/scenegraph/coreapi/qsgtexturemanager.cpp @@ -45,6 +45,8 @@ #include #include +#include +#include QSGTexture::QSGTexture() : m_status(Null) @@ -74,7 +76,6 @@ void QSGTexture::setStatus(Status s) } - struct QSGTextureCacheKey { quint64 cacheKey; @@ -83,6 +84,12 @@ struct QSGTextureCacheKey { } }; +struct QSGTextureAsyncUpload { + QImage image; + int progress; + QSGTexture *texture; +}; + uint qHash(const QSGTextureCacheKey &key) { @@ -92,7 +99,19 @@ uint qHash(const QSGTextureCacheKey &key) class QSGTextureManagerPrivate { public: + QSGTextureManagerPrivate() + : context(0) + , maxUploadTime(5) + , uploadChunkSize(64) + { + } + + QSGContext *context; QHash cache; + + QQueue asyncUploads; + int maxUploadTime; + int uploadChunkSize; }; @@ -103,18 +122,29 @@ QSGTextureManager::QSGTextureManager() } +void QSGTextureManager::setContext(QSGContext *context) +{ + Q_ASSERT(!d->context); + + d->context = context; + connect(d->context, SIGNAL(aboutToRenderNextFrame()), this, SLOT(processAsyncTextures())); +} -QImage QSGTextureManager::swizzleBGGRAToRGBA(const QImage &image) +QSGContext *QSGTextureManager::context() const { - QImage img = image.copy(); - const int width = img.width(); - const int height = img.height(); + return d->context; +} + + +void QSGTextureManager::swizzleBGRAToRGBA(QImage *image) +{ + const int width = image->width(); + const int height = image->height(); for (int i = 0; i < height; ++i) { - uint *p = (uint *) img.scanLine(i); + uint *p = (uint *) image->scanLine(i); for (int x = 0; x < width; ++x) p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); } - return img; } @@ -131,7 +161,8 @@ QSGTextureRef QSGTextureManager::upload(const QImage &image) glBindTexture(GL_TEXTURE_2D, id); #ifdef QT_OPENGL_ES - QImage i = swizzleBGRAToRGBA(image); + QImage i = image; + swizzleBGRAToRGBA(&i); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, i.width(), i.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, i.constBits()); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); @@ -150,12 +181,85 @@ QSGTextureRef QSGTextureManager::upload(const QImage &image) } - QSGTextureRef QSGTextureManager::requestUpload(const QImage &image, const QObject *listener, const char *slot) { - QSGTextureRef texture = upload(image); - return texture; + QSGTexture *t = new QSGTexture(); + connect(t, SIGNAL(statusChanged(int)), listener, slot); + + QSGTextureAsyncUpload work; + work.image = image; + work.progress = 0; + work.texture = t; + + d->asyncUploads << work; + + return QSGTextureRef(t); } +void QSGTextureManager::processAsyncTextures() +{ + QTime time; + time.start(); + + 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("ASYNC: texture: %p, id=%d, size=(%dx%d), progress: %d / %d\n", +// t, +// t->textureId(), +// w, h, +// upload.progress, chunkCount); + + // Create or bind the texture... + if (upload.texture->textureId() == 0) { + 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); + t->setTextureId(id); + t->setTextureSize(QSize(w, h)); + t->setAlphaChannel(upload.image.hasAlphaChannel()); + t->setStatus(QSGTexture::Loading); +// printf("ASYNC: created texture %p with id=%d\n", t, id); + } else { + glBindTexture(GL_TEXTURE_2D, t->textureId()); + } + + if (time.elapsed() > d->maxUploadTime) + return; + + while (upload.progress < chunkCount && time.elapsed() < d->maxUploadTime) { + int x = (upload.progress % hChunkCount) * d->uploadChunkSize; + int y = (upload.progress / hChunkCount) * d->uploadChunkSize; + + QImage subImage = upload.image.copy(x, y, d->uploadChunkSize, d->uploadChunkSize); +// 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, d->uploadChunkSize, d->uploadChunkSize, GL_RGBA, GL_UNSIGNED_BYTE, subImage.constBits()); + + ++upload.progress; + } + + if (upload.progress == chunkCount) { + t->setStatus(QSGTexture::Ready); + d->asyncUploads.dequeue(); + } + } + + glBindTexture(GL_TEXTURE_2D, 0); +} diff --git a/src/scenegraph/coreapi/qsgtexturemanager.h b/src/scenegraph/coreapi/qsgtexturemanager.h index ef0c910..46979a2 100644 --- a/src/scenegraph/coreapi/qsgtexturemanager.h +++ b/src/scenegraph/coreapi/qsgtexturemanager.h @@ -43,6 +43,7 @@ #define QSGTEXTUREMANAGER_H #include "qmlscene_global.h" +#include "qsgcontext.h" #include #include @@ -159,15 +160,23 @@ private: }; -class QT_SCENEGRAPH_EXPORT QSGTextureManager +class QT_SCENEGRAPH_EXPORT QSGTextureManager : public QObject { + Q_OBJECT + public: QSGTextureManager(); + virtual void setContext(QSGContext *context); + QSGContext *context() const; + virtual QSGTextureRef upload(const QImage &image); virtual QSGTextureRef requestUpload(const QImage &image, const QObject *listener, const char *slot); - static QImage swizzleBGGRAToRGBA(const QImage &image); + static void swizzleBGRAToRGBA(QImage *image); + +private slots: + void processAsyncTextures(); private: QSGTextureManagerPrivate *d; -- cgit v1.2.3