diff options
author | Gunnar Sletta <gunnar.sletta@nokia.com> | 2010-12-06 13:54:07 +0100 |
---|---|---|
committer | Gunnar Sletta <gunnar.sletta@nokia.com> | 2010-12-06 13:54:07 +0100 |
commit | ba1c7be72879f9954029aa54023ac30a6e66710d (patch) | |
tree | 9b542ef6f9fc29756b630397fe911ab8438e87f0 | |
parent | ab405ff9fcb670367454ceb18c02175b9d74de7e (diff) |
threaded texture uploader for lighthouse's EglFStexturemanager
-rw-r--r-- | src/adaptationlayers/adaptationlayer.cpp | 7 | ||||
-rw-r--r-- | src/adaptationlayers/adaptationlayer.h | 8 | ||||
-rw-r--r-- | src/adaptationlayers/adaptationlayers.pri | 2 | ||||
-rw-r--r-- | src/adaptationlayers/qsgeglfsthreadedtexturemanager.cpp | 169 | ||||
-rw-r--r-- | src/adaptationlayers/qsgeglfsthreadedtexturemanager.h | 23 | ||||
-rw-r--r-- | src/adaptationlayers/threadedtexturemanager.cpp | 136 | ||||
-rw-r--r-- | src/adaptationlayers/threadedtexturemanager.h | 4 | ||||
-rw-r--r-- | src/canvas/qxgraphicsview.cpp | 36 | ||||
-rw-r--r-- | src/canvas/qxgraphicsview.h | 5 | ||||
-rw-r--r-- | src/scenegraph/coreapi/qsgcontext.cpp | 6 | ||||
-rw-r--r-- | tools/qmlscene/main.cpp | 3 |
11 files changed, 340 insertions, 59 deletions
diff --git a/src/adaptationlayers/adaptationlayer.cpp b/src/adaptationlayers/adaptationlayer.cpp index 0fdddd9..fe31289 100644 --- a/src/adaptationlayers/adaptationlayer.cpp +++ b/src/adaptationlayers/adaptationlayer.cpp @@ -41,6 +41,8 @@ #include "adaptationlayer.h" +#include <qdatetime.h> + /*! Constructs a new texture reference with status set to Null */ @@ -77,6 +79,9 @@ void TextureReference::setStatus(Status s) */ const TextureReference *TextureManager::requestUploadedTexture(const QImage &image, UploadHints hints, QObject *listener, const char *slot) { + QTime time; + time.start(); + QGLContext *context = const_cast<QGLContext *>(QGLContext::currentContext()); QGLContext::BindOptions options = QGLContext::PremultipliedAlphaBindOption; @@ -88,6 +93,8 @@ const TextureReference *TextureManager::requestUploadedTexture(const QImage &ima image.hasAlphaChannel() ? GL_RGBA : GL_RGB, options); + printf("Texture uploaded in: %d\n", time.elapsed()); + TextureReference *ref = new TextureReference(); if (listener && slot) diff --git a/src/adaptationlayers/adaptationlayer.h b/src/adaptationlayers/adaptationlayer.h index abe4d1a..e43571b 100644 --- a/src/adaptationlayers/adaptationlayer.h +++ b/src/adaptationlayers/adaptationlayer.h @@ -159,6 +159,14 @@ public: void setMipmaps(bool mipmapped) { m_mipmap = mipmapped; } bool hasMipmaps() const { return m_mipmap; } + /* + ### gunnar: the texture reference is potentially written to in a thread + while uploading, so ANY access to the other accessors is strictly not + allowed until status returns Uploaded. + To support fetching and writing status from multiple threads, which I'm + unsure if will happen, we should change status to be an QAtomicInt + */ + void setStatus(Status s); Status status() const { return m_status; } diff --git a/src/adaptationlayers/adaptationlayers.pri b/src/adaptationlayers/adaptationlayers.pri index bbba835..c93d8b8 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 \ + $$PWD/qsgeglfsthreadedtexturemanager.h SOURCES += \ $$PWD/adaptationlayer.cpp \ @@ -15,6 +16,7 @@ SOURCES += \ $$PWD/default/default_glyphnode.cpp \ $$PWD/default/default_glyphnode_p.cpp \ $$PWD/threadedtexturemanager.cpp \ + $$PWD/qsgeglfsthreadedtexturemanager.cpp macx:{ SOURCES += adaptationlayers/mactexturemanager.cpp diff --git a/src/adaptationlayers/qsgeglfsthreadedtexturemanager.cpp b/src/adaptationlayers/qsgeglfsthreadedtexturemanager.cpp new file mode 100644 index 0000000..b868979 --- /dev/null +++ b/src/adaptationlayers/qsgeglfsthreadedtexturemanager.cpp @@ -0,0 +1,169 @@ +#include "qsgeglfsthreadedtexturemanager.h" + +#include <QThread> +#include <qdatetime.h> + +#include <EGL/egl.h> +#include <GLES2/gl2.h> + + +class QSGEglFSThreadedTextureManagerPrivate +{ +public: + EGLDisplay display; + EGLSurface surface; + EGLContext context; + + bool uploadsScanlines; +}; + + +QSGEglFSThreadedTextureManager::QSGEglFSThreadedTextureManager() + : d(new QSGEglFSThreadedTextureManagerPrivate) +{ + d->uploadsScanlines = false; +} + + +void QSGEglFSThreadedTextureManager::setUploadsScanlines(bool b) +{ + d->uploadsScanlines = b; +} + + +bool QSGEglFSThreadedTextureManager::uploadsScanlines() const +{ + return d->uploadsScanlines; +} + + +void QSGEglFSThreadedTextureManager::initializeThreadContext() +{ + d->display = eglGetCurrentDisplay(); + + EGLint attribs[] = { + EGL_WIDTH, 8, + EGL_HEIGHT, 8, + + EGL_NONE + }; + + EGLConfig configs[256]; + EGLint count; + + d->surface = 0; + EGLConfig *config = 0; + if (eglGetConfigs(d->display, configs, 256, &count)) { + for (int i=0; i<count; ++i) { + int type; + eglGetConfigAttrib(d->display, configs[i], EGL_RENDERABLE_TYPE, &type); + if (!(type & EGL_OPENGL_ES2_BIT)) + continue; + + int r, g, b, a; + eglGetConfigAttrib(d->display, configs[i], EGL_RED_SIZE, &r); + eglGetConfigAttrib(d->display, configs[i], EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(d->display, configs[i], EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(d->display, configs[i], EGL_ALPHA_SIZE, &a); + + if ((r & g & b & a) == 8) { + d->surface = eglCreatePbufferSurface(d->display, configs[i], attribs); + config = configs + i; + break; + } + } + } + d->context = eglCreateContext(d->display, *config, eglGetCurrentContext(), 0); +} + + +void QSGEglFSThreadedTextureManager::makeThreadContextCurrent() +{ + eglMakeCurrent(d->display, d->surface, d->surface, d->context); +} + +class ThreadAccess : public QThread +{ +public: + static void doSleep(int us) { + usleep(us); + } +}; + + +static inline void qgl_byteSwapImage(QImage &img) +{ + const int width = img.width(); + const int height = img.height(); + + for (int i = 0; i < height; ++i) { + uint *p = (uint *) img.scanLine(i); + for (int x = 0; x < width; ++x) + p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); + } +} + +#define DO_TIMING + +void QSGEglFSThreadedTextureManager::uploadInThread(TextureReference *texture, const QImage &im, UploadHints hints) +{ + while (glGetError() != GL_NO_ERROR) printf("bust...\n"); + +#ifdef DO_TIMING + QTime time; + time.start(); +#endif + + QImage image = im; + qgl_byteSwapImage(image); + +#ifdef DO_TIMING + int time_byteswap = time.elapsed(); +#endif + + int w = image.width(); + int h = image.height(); + int bpl = image.bytesPerLine(); + const uchar *bits = image.constBits(); + + // This should not be neccesary... + if (d->uploadsScanlines) + makeThreadContextCurrent(); + + GLuint id; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + + if (d->uploadsScanlines) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, id); // Should also not be required, but it fails without... driver bug, for sure.. + for (int y=0; y<h; ++y) { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, w, 1, GL_RGBA, GL_UNSIGNED_BYTE, y * bpl + bits); + if (glGetError() != GL_NO_ERROR) { + qWarning("ERROR: failed to upload chunk...\n"); + break; + } + ThreadAccess::doSleep(2); + } + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, bits); + } + + glBindTexture(GL_TEXTURE_2D, 0); + +#ifdef DO_TIMING + int time_upload = time.elapsed(); + printf("EGLFS Threaded upload: byteSwap: %d ms, upload: %d ms\n", + time_byteswap, + time_upload); +#endif + + + texture->setOwnsTexture(true); + texture->setTextureId(id); + texture->setTextureSize(image.size()); + texture->setMipmaps(hints & TextureManager::GenerateMipmapUploadHint); + texture->setStatus(TextureReference::Uploaded); +} + + diff --git a/src/adaptationlayers/qsgeglfsthreadedtexturemanager.h b/src/adaptationlayers/qsgeglfsthreadedtexturemanager.h new file mode 100644 index 0000000..61caeb6 --- /dev/null +++ b/src/adaptationlayers/qsgeglfsthreadedtexturemanager.h @@ -0,0 +1,23 @@ +#ifndef QSGEGLFSTHREADEDTEXTUREMANAGER_H +#define QSGEGLFSTHREADEDTEXTUREMANAGER_H + +#include "threadedtexturemanager.h" + +class QSGEglFSThreadedTextureManagerPrivate; + +class QSGEglFSThreadedTextureManager : public QSGThreadedTextureManager +{ +public: + QSGEglFSThreadedTextureManager(); + + void initializeThreadContext(); + void makeThreadContextCurrent(); + void uploadInThread(TextureReference *texture, const QImage &image, UploadHints hints); + + void setUploadsScanlines(bool); + bool uploadsScanlines() const; + +private: + QSGEglFSThreadedTextureManagerPrivate *d; +}; +#endif // QSGEGLFSTHREADEDTEXTUREMANAGER_H diff --git a/src/adaptationlayers/threadedtexturemanager.cpp b/src/adaptationlayers/threadedtexturemanager.cpp index 616f0ec..665dbd9 100644 --- a/src/adaptationlayers/threadedtexturemanager.cpp +++ b/src/adaptationlayers/threadedtexturemanager.cpp @@ -24,29 +24,15 @@ struct TextureToUpload class QSGTTMUploadThread : public QThread { public: - QSGTTMUploadThread() { - QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); - // Getting the share widget from the current context is rather nasty and - // will not work for lighthouse based - Q_ASSERT(ctx->device() && ctx->device()->devType() == QInternal::Widget); - - QGLWidget *share = static_cast<QGLWidget *>(ctx->device()); - - widget = new QGLWidget(0, share); - widget->resize(8, 8); - - context = const_cast<QGLContext *>(widget->context()); - - widget->doneCurrent(); - - ctx->makeCurrent(); - + QSGTTMUploadThread(QSGThreadedTextureManager *m) + : manager(m) + { start(); } void run() { - context->makeCurrent(); + manager->makeThreadContextCurrent(); while (true) { @@ -66,49 +52,42 @@ public: if (work.image.isNull()) continue; -#ifdef QT_OPENGL_ES - qWarning("ThreadedTextureManager: Chances are that BGRA does not work on GLES"); -#endif - - GLuint id; - glGenTextures(1, &id); - glBindTexture(GL_TEXTURE_2D, id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, work.image.width(), work.image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, work.image.constBits()); - - if (work.hints & TextureManager::GenerateMipmapUploadHint) - glGenerateMipmap(GL_TEXTURE_2D); - - glBindTexture(GL_TEXTURE_2D, 0); - - work.texture->setTextureId(id); - work.texture->setTextureSize(work.image.size()); - work.texture->setMipmaps(work.hints & TextureManager::GenerateMipmapUploadHint); - work.texture->setStatus(TextureReference::Uploaded); + manager->uploadInThread(work.texture, work.image, work.hints); } } - QGLWidget *widget; - QGLContext *context; + QSGThreadedTextureManager *manager; QWaitCondition condition; QMutex mutex; - QQueue<TextureToUpload> requests; + + }; class QSGThreadedTextureManagerPrivate { public: - QSGTTMUploadThread thread; + QSGTTMUploadThread *thread; + QGLContext *context; + QGLWidget *widget; }; QSGThreadedTextureManager::QSGThreadedTextureManager() { d = new QSGThreadedTextureManagerPrivate; + d->thread = 0; + d->context = 0; + d->widget = 0; } const TextureReference *QSGThreadedTextureManager::requestUploadedTexture(const QImage &image, UploadHints hints, QObject *listener, const char *slot) { + if (!d->thread) { + d->thread = new QSGTTMUploadThread(this); + initializeThreadContext(); + } + Q_ASSERT(!image.isNull()); if (hints & SynchronousUploadHint) { @@ -122,15 +101,84 @@ const TextureReference *QSGThreadedTextureManager::requestUploadedTexture(const QObject::connect(work.texture, SIGNAL(statusChanged(int)), listener, slot); - d->thread.mutex.lock(); + d->thread->mutex.lock(); - d->thread.requests << work; - d->thread.condition.wakeOne(); - d->thread.mutex.unlock(); + d->thread->requests << work; + d->thread->condition.wakeOne(); + d->thread->mutex.unlock(); return work.texture; } +/*! + Reimplement this function to initialize a non-standard threaded context. + + This function is called from the same thread as the scene graph is running + in. The default implementation will construct a QGLWidget, extract its + context and use this context in the background thread. + */ +void QSGThreadedTextureManager::initializeThreadContext() +{ + QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); + // Getting the share widget from the current context is rather nasty and + // will not work for lighthouse based + Q_ASSERT(ctx->device() && ctx->device()->devType() == QInternal::Widget); + + QGLWidget *share = static_cast<QGLWidget *>(ctx->device()); + + d->widget = new QGLWidget(0, share); + d->widget->resize(8, 8); + + d->context = const_cast<QGLContext *>(d->widget->context()); + + d->widget->doneCurrent(); + + ctx->makeCurrent(); +} + + + +/*! + Reimplement this function to make the threaded context current. + + This function is called from the background thread. The default + implementation makes the context created in initializeThreadContext + current. + */ +void QSGThreadedTextureManager::makeThreadContextCurrent() +{ + d->context->makeCurrent(); +} + +/*! + Reimplement this function to upload images in the background thread. + + This function is called from the background thread. The default + implementation does this using a single glTexImage2D call. + */ + +void QSGThreadedTextureManager::uploadInThread(TextureReference *texture, const QImage &image, UploadHints hints) +{ +#ifdef QT_OPENGL_ES + qWarning("ThreadedTextureManager: Chances are that BGRA does not work on GLES"); +#endif + + GLuint id; + glGenTextures(1, &id); + glBindTexture(GL_TEXTURE_2D, id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); + + if (hints & TextureManager::GenerateMipmapUploadHint) + glGenerateMipmap(GL_TEXTURE_2D); + + glBindTexture(GL_TEXTURE_2D, 0); + + texture->setOwnsTexture(true); + texture->setTextureId(id); + texture->setTextureSize(image.size()); + texture->setMipmaps(hints & TextureManager::GenerateMipmapUploadHint); + texture->setStatus(TextureReference::Uploaded); +} diff --git a/src/adaptationlayers/threadedtexturemanager.h b/src/adaptationlayers/threadedtexturemanager.h index 26abaf1..633f81c 100644 --- a/src/adaptationlayers/threadedtexturemanager.h +++ b/src/adaptationlayers/threadedtexturemanager.h @@ -12,6 +12,10 @@ public: const TextureReference *requestUploadedTexture(const QImage &image, UploadHints hints, QObject *listener, const char *slot); + virtual void initializeThreadContext(); + virtual void makeThreadContextCurrent(); + virtual void uploadInThread(TextureReference *texture, const QImage &image, UploadHints hints); + private: QSGThreadedTextureManagerPrivate *d; }; diff --git a/src/canvas/qxgraphicsview.cpp b/src/canvas/qxgraphicsview.cpp index 1d302c1..caf7ca3 100644 --- a/src/canvas/qxgraphicsview.cpp +++ b/src/canvas/qxgraphicsview.cpp @@ -162,10 +162,6 @@ QxGraphicsView::QxGraphicsView(QWidget *parent) d->animationDriver.install(); else printf("Not using VSync Animation Driver\n"); - -#ifdef Q_WS_QPA - initializeSceneGraph(); -#endif } QxGraphicsView::~QxGraphicsView() @@ -555,13 +551,6 @@ QSize QxGraphicsView::sizeHint() const return d->rootObjectSize(); } -#ifndef Q_WS_QPA -void QxGraphicsView::initializeGL() -{ - initializeSceneGraph(); -} -#endif - /*! Returns the scene graph context used by this view. @@ -595,6 +584,19 @@ void QxGraphicsView::setSceneGraphContext(QSGContext *context) d->sg = context; } +void QxGraphicsView::showEvent(QShowEvent *e) +{ + printf("showEvent\n"); + initializeSceneGraph(); + +#ifdef Q_WS_QPA + QWidget::showEvent(e); +#else + QGLWidget::showEvent(e); +#endif + +} + /*! Called when there is a GL context ready to initialize the scene graph context and bind together the various things. @@ -605,6 +607,18 @@ void QxGraphicsView::setSceneGraphContext(QSGContext *context) */ void QxGraphicsView::initializeSceneGraph() { + +#ifdef Q_WS_QPA + QPlatformWindow *platformWindow = window()->platformWindow(); + QPlatformGLContext *platformContext = const_cast<QPlatformGLContext *>(platformWindow->glContext()); + if (!platformContext) { + qFatal("QxGraphicsView::initializeSceneGraph: No platform GL context"); + } + platformContext->makeCurrent(); +#endif + + + // If the user didn't override with a custom context, use the default one. if (!d->sg) d->sg = new QSGContext(); diff --git a/src/canvas/qxgraphicsview.h b/src/canvas/qxgraphicsview.h index 212b4af..ae22e4a 100644 --- a/src/canvas/qxgraphicsview.h +++ b/src/canvas/qxgraphicsview.h @@ -104,10 +104,7 @@ protected: virtual void mouseReleaseEvent(QMouseEvent *); virtual void keyPressEvent(QKeyEvent *); virtual void keyReleaseEvent(QKeyEvent *); - -#ifndef Q_WS_QPA - virtual void initializeGL(); -#endif + virtual void showEvent(QShowEvent *); private: void initializeSceneGraph(); diff --git a/src/scenegraph/coreapi/qsgcontext.cpp b/src/scenegraph/coreapi/qsgcontext.cpp index 05eb477..0437d91 100644 --- a/src/scenegraph/coreapi/qsgcontext.cpp +++ b/src/scenegraph/coreapi/qsgcontext.cpp @@ -12,6 +12,10 @@ #include "mactexturemanager.h" #endif +#ifdef Q_WS_QPA +#include "qsgeglfsthreadedtexturemanager.h" +#endif + #include <private/qobject_p.h> class QSGContextPrivate : public QObjectPrivate @@ -167,6 +171,8 @@ TextureManager *QSGContext::createTextureManager() return new QSGMacTextureManager; #elif defined (Q_WS_WIN) return new QSGThreadedTextureManager; +#elif defined (Q_WS_QPA) + return new QSGEglFSThreadedTextureManager; #else return new TextureManager; #endif diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index fe363d6..ed5899d 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -478,7 +478,10 @@ int main(int argc, char ** argv) view->showMaximized(); else view->show(); + +#ifdef Q_WS_MAC view->raise(); +#endif exitCode = app.exec(); |