summaryrefslogtreecommitdiffstats
path: root/src/adaptationlayers
diff options
context:
space:
mode:
Diffstat (limited to 'src/adaptationlayers')
-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
7 files changed, 305 insertions, 44 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;
};