summaryrefslogtreecommitdiffstats
path: root/src/adaptationlayers/qsgthreadedtexturemanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/adaptationlayers/qsgthreadedtexturemanager.cpp')
-rw-r--r--src/adaptationlayers/qsgthreadedtexturemanager.cpp236
1 files changed, 236 insertions, 0 deletions
diff --git a/src/adaptationlayers/qsgthreadedtexturemanager.cpp b/src/adaptationlayers/qsgthreadedtexturemanager.cpp
new file mode 100644
index 0000000..83e64db
--- /dev/null
+++ b/src/adaptationlayers/qsgthreadedtexturemanager.cpp
@@ -0,0 +1,236 @@
+#include "qsgthreadedtexturemanager.h"
+#include "qsgtexturemanager_p.h"
+
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+
+class QSGThreadedTexture : public QSGTexture
+{
+ Q_OBJECT
+public:
+ QSGThreadedTexture(QSGThreadedTextureManager *m)
+ : manager(m)
+ {
+ }
+ ~QSGThreadedTexture();
+
+ QSGThreadedTextureManager *manager;
+ QImage image;
+};
+
+
+class QSGThreadedTextureManagerThread : public QThread
+{
+public:
+ QSGThreadedTextureManagerThread(QSGThreadedTextureManager *m)
+ : manager(m)
+ , currentlyUploading(0)
+ {
+ manager->createThreadContext();
+ start();
+ }
+
+ void run() {
+
+ manager->makeThreadContextCurrent();
+
+ while (true) {
+
+ mutex.lock();
+ currentlyUploading = 0;
+ condition.wakeOne();
+
+ if (requests.isEmpty()) {
+ condition.wait(&mutex);
+ }
+
+ if (requests.isEmpty()) {
+ mutex.unlock();
+ continue;
+ }
+
+ currentlyUploading = requests.takeFirst();
+ mutex.unlock();
+
+ manager->uploadInThread(currentlyUploading->image, currentlyUploading);
+ }
+ }
+
+ QSGThreadedTextureManager *manager;
+ QSGThreadedTexture *currentlyUploading;
+
+ QWaitCondition condition;
+ QMutex mutex;
+
+ QList<QSGThreadedTexture *> requests;
+};
+
+
+class QSGThreadedTextureManagerPrivate : public QSGTextureManagerPrivate
+{
+public:
+ QSGThreadedTextureManagerThread *thread;
+
+ QGLWidget *widget;
+ QGLContext *context;
+};
+
+
+QSGThreadedTexture::~QSGThreadedTexture()
+{
+ QSGThreadedTextureManagerPrivate *d = manager->d_func();
+ d->thread->mutex.lock();
+ d->thread->requests.removeOne(this);
+ while (d->thread->currentlyUploading == this)
+ d->thread->condition.wait(&d->thread->mutex);
+ d->thread->mutex.unlock();
+
+ d->removeTextureFromCache(this);
+}
+
+
+QSGThreadedTextureManager::QSGThreadedTextureManager()
+ : QSGTextureManager(*(new QSGThreadedTextureManagerPrivate))
+{
+ Q_D(QSGThreadedTextureManager);
+ d->thread = 0;
+ d->widget = 0;
+ d->context = 0;
+}
+
+QSGTextureRef QSGThreadedTextureManager::upload(const QImage &image)
+{
+ Q_D(QSGThreadedTextureManager);
+
+ QSGTextureCacheKey key = { image.cacheKey() };
+ QSGTexture *texture = d->cache.value(key);
+
+ if (texture) {
+
+ QSGThreadedTexture *ttex = qobject_cast<QSGThreadedTexture *>(texture);
+ if ((ttex && ttex->status() == QSGTexture::Ready) || !ttex) {
+ // Already fully uploaded... Just return
+ return QSGTextureRef(texture);
+
+ } else {
+
+ // Lock and wait for the texture to uploaded...
+ d->thread->mutex.lock();
+ while (ttex->status() == QSGTexture::Loading) {
+ d->thread->condition.wait(&d->thread->mutex);
+ }
+ d->thread->mutex.unlock();
+
+ return QSGTextureRef(ttex);
+ }
+ }
+
+ return d->upload(image, 0, 0);
+}
+
+QSGTextureRef QSGThreadedTextureManager::requestUpload(const QImage &image, const QObject *listener, const char *slot)
+{
+ Q_D(QSGThreadedTextureManager);
+
+ QSGTextureCacheKey key = { image.cacheKey() };
+ QSGTexture *texture = d->cache.value(key);
+ if (texture)
+ return QSGTextureRef(texture);
+
+ QSGThreadedTexture *ttex = new QSGThreadedTexture(this);
+ ttex->image = image;
+ if (listener && slot)
+ connect(ttex, SIGNAL(statusChanged(int)), listener, slot);
+
+ ttex->setStatus(QSGTexture::Loading);
+
+ d->cache.insert(key, ttex);
+
+ if (!d->thread) {
+ d->thread = new QSGThreadedTextureManagerThread(this);
+ }
+
+ d->thread->mutex.lock();
+ d->thread->requests << ttex;
+ d->thread->condition.wakeOne();
+ d->thread->mutex.unlock();
+
+ return QSGTextureRef(ttex);
+}
+
+/*!
+ Called by the rendering thread to create a new context that can be used in the
+ upload thread.
+ */
+void QSGThreadedTextureManager::createThreadContext()
+{
+ Q_D(QSGThreadedTextureManager);
+
+ 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());
+ if (!d->context)
+ qFatal("QSGThreadedTextureManager: failed to create thread 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 createThreadContext
+ current.
+ */
+void QSGThreadedTextureManager::makeThreadContextCurrent()
+{
+ Q_D(QSGThreadedTextureManager);
+ 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(const QImage &image, QSGTexture *texture)
+{
+ GLuint id;
+ glGenTextures(1, &id);
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ QImage copy = image;
+// swizzleBGRAToRGBA(&copy);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
+
+ bool fail = glGetError() != GL_NO_ERROR;
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ if (fail) {
+ texture->setStatus(QSGTexture::Null);
+ glDeleteTextures(1, &id);
+ } else {
+ texture->setTextureId(id);
+ texture->setTextureSize(image.size());
+ texture->setAlphaChannel(image.hasAlphaChannel());
+ texture->setStatus(QSGTexture::Ready);
+ }
+}
+
+#include "qsgthreadedtexturemanager.moc"