summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2010-12-06 13:54:07 +0100
committerGunnar Sletta <gunnar.sletta@nokia.com>2010-12-06 13:54:07 +0100
commitba1c7be72879f9954029aa54023ac30a6e66710d (patch)
tree9b542ef6f9fc29756b630397fe911ab8438e87f0
parentab405ff9fcb670367454ceb18c02175b9d74de7e (diff)
threaded texture uploader for lighthouse's EglFStexturemanager
-rw-r--r--src/adaptationlayers/adaptationlayer.cpp7
-rw-r--r--src/adaptationlayers/adaptationlayer.h8
-rw-r--r--src/adaptationlayers/adaptationlayers.pri2
-rw-r--r--src/adaptationlayers/qsgeglfsthreadedtexturemanager.cpp169
-rw-r--r--src/adaptationlayers/qsgeglfsthreadedtexturemanager.h23
-rw-r--r--src/adaptationlayers/threadedtexturemanager.cpp136
-rw-r--r--src/adaptationlayers/threadedtexturemanager.h4
-rw-r--r--src/canvas/qxgraphicsview.cpp36
-rw-r--r--src/canvas/qxgraphicsview.h5
-rw-r--r--src/scenegraph/coreapi/qsgcontext.cpp6
-rw-r--r--tools/qmlscene/main.cpp3
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();