summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO31
-rw-r--r--qt-scene-graph.pro3
-rw-r--r--src/adaptationlayers/default/default_texturenode.cpp3
-rw-r--r--src/scenegraph/convenience/texturematerial.cpp18
-rw-r--r--src/scenegraph/convenience/texturematerial.h12
-rw-r--r--src/scenegraph/coreapi/qsgtexturemanager.cpp71
-rw-r--r--src/scenegraph/coreapi/qsgtexturemanager.h2
-rwxr-xr-xtests/auto/run_tests.sh18
-rw-r--r--tests/auto/texturemanager/texturemanager.pro17
-rw-r--r--tests/auto/texturemanager/tst_texturemanagertest.cpp239
10 files changed, 398 insertions, 16 deletions
diff --git a/TODO b/TODO
index 6aa4eb2..4814e57 100644
--- a/TODO
+++ b/TODO
@@ -12,7 +12,6 @@
declarative/
canvas/
view
- animation driver
items/
all the qxitems
scenegraph/
@@ -34,6 +33,9 @@
adaptation interfaces
default implementations
qmlrenderer
+ animation driver
+
+
- Make QxImage push normalized source coords into TextureNode so we don't have to renormalize for
every geometry change. It also makes a lot more sense, now that TextureNode doesn't know the
@@ -48,3 +50,30 @@
does a texture memcpy for the first glDrawElements() that uses the texture in the UI thread.
I guess that only leaves PBO uploads, but that won't remedy the problem, I recon...
EDIT: CLIENT_STORAGE + TEXTURE_STORAGE as SHARED + seems to kill ALL performance problems
+
+
+- Scene Graph
+ - Product Finalization:
+ - API Review and proper d-ptr implementations where required
+ - Documentation for all public classes
+ - Autotests for all public classes
+ - NodeInterfaces, Scene Graph structure, QSGContext, Texture classes, Renderer
+ - Solve the texture problem. Convenient base class, asynchronous uploads (threading?), atlasses?
+ - Multimedia, through Qt Mobility or directly to native API's like gstreamer
+ - We have phonon, but it doesn't work for devices
+ - Gstreamer, would work on meego / linux
+ - Lacking solution on symbian
+ - Upgrade to proper QML Items
+ - Examples for most common usecases usecases
+ - Solve the text problem
+ - Investigate mipmapping
+ - Investigate distance field
+ - Investigate curve rendering on gpu
+ - Scalable unhinted text layout
+ - text as geometry over a physical size
+ - Optimal backends for SGX, CTS79 and Mali 400.
+ - Pluggable vsync animation
+ - Wayland based
+ - eglSwapBuffers based
+ - signal based?
+
diff --git a/qt-scene-graph.pro b/qt-scene-graph.pro
index ab9a0bd..30287dd 100644
--- a/qt-scene-graph.pro
+++ b/qt-scene-graph.pro
@@ -1,3 +1,4 @@
TEMPLATE = subdirs
-SUBDIRS += src tools plugins examples
+SUBDIRS += src tools plugins examples \
+ tests/auto/texturemanager
CONFIG += ordered
diff --git a/src/adaptationlayers/default/default_texturenode.cpp b/src/adaptationlayers/default/default_texturenode.cpp
index 077f7de..85c6eeb 100644
--- a/src/adaptationlayers/default/default_texturenode.cpp
+++ b/src/adaptationlayers/default/default_texturenode.cpp
@@ -227,4 +227,7 @@ void DefaultTextureNode::updateTexture()
m_material.setTexture(m_texture, opaque);
m_materialO.setTexture(m_texture, opaque);
setMaterial(m_opacity == 1 ? &m_material : &m_materialO); // Indicate that the material state has changed.
+
+ m_material.setClampToEdge(m_clamp_to_edge);
+ m_materialO.setClampToEdge(m_clamp_to_edge);
}
diff --git a/src/scenegraph/convenience/texturematerial.cpp b/src/scenegraph/convenience/texturematerial.cpp
index 8e03884..2c431e7 100644
--- a/src/scenegraph/convenience/texturematerial.cpp
+++ b/src/scenegraph/convenience/texturematerial.cpp
@@ -128,6 +128,12 @@ void TextureMaterialData::updateEffectState(Renderer *renderer, AbstractEffect *
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
+
+ if (oldEffect == 0 || tx->clampToEdge() != oldTx->clampToEdge()) {
+ int wrapMode = tx->clampToEdge() ? GL_CLAMP_TO_EDGE : GL_REPEAT;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
+ }
}
@@ -226,20 +232,12 @@ AbstractEffectProgram *TextureMaterialWithOpacity::createProgram() const
void TextureMaterialWithOpacityData::updateEffectState(Renderer *renderer, AbstractEffect *newEffect, AbstractEffect *oldEffect)
{
+ TextureMaterialData::updateEffectState(renderer, newEffect, oldEffect);
+
Q_ASSERT(oldEffect == 0 || newEffect->type() == oldEffect->type());
TextureMaterialWithOpacity *tx = static_cast<TextureMaterialWithOpacity *>(newEffect);
TextureMaterialWithOpacity *oldTx = static_cast<TextureMaterialWithOpacity *>(oldEffect);
- if (oldEffect == 0 || tx->texture().texture() != oldTx->texture().texture()) {
- renderer->setTexture(0, tx->texture());
- oldEffect = 0; // Force filtering update.
- }
- if (oldEffect == 0 || tx->linearFiltering() != oldTx->linearFiltering()) {
- int filtering = tx->linearFiltering() ? GL_LINEAR : GL_NEAREST;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
- }
-
if (oldTx == 0 || tx->opacity() != oldTx->opacity())
m_program.setUniformValue(m_opacity_id, (GLfloat) tx->opacity());
}
diff --git a/src/scenegraph/convenience/texturematerial.h b/src/scenegraph/convenience/texturematerial.h
index 166bca1..6a43cb5 100644
--- a/src/scenegraph/convenience/texturematerial.h
+++ b/src/scenegraph/convenience/texturematerial.h
@@ -48,7 +48,13 @@
class QT_SCENEGRAPH_EXPORT TextureMaterial : public AbstractEffect
{
public:
- TextureMaterial() : m_texture(0), m_opaque(true), m_linear_filtering(false) { }
+ TextureMaterial()
+ : m_texture(0)
+ , m_opaque(true)
+ , m_linear_filtering(false)
+ , m_clamp_to_edge(true)
+ {
+ }
virtual AbstractEffectType *type() const;
virtual AbstractEffectProgram *createProgram() const;
@@ -61,12 +67,16 @@ public:
void setLinearFiltering(bool linearFiltering) { m_linear_filtering = linearFiltering; }
bool linearFiltering() const { return m_linear_filtering; }
+ void setClampToEdge(bool clamp) { m_clamp_to_edge = clamp; }
+ bool clampToEdge() const { return m_clamp_to_edge; }
+
static bool is(const AbstractEffect *effect);
protected:
QSGTextureRef m_texture;
bool m_opaque;
bool m_linear_filtering;
+ bool m_clamp_to_edge;
};
class TextureMaterialData : public AbstractShaderEffectProgram
diff --git a/src/scenegraph/coreapi/qsgtexturemanager.cpp b/src/scenegraph/coreapi/qsgtexturemanager.cpp
index d8c08f8..a26d353 100644
--- a/src/scenegraph/coreapi/qsgtexturemanager.cpp
+++ b/src/scenegraph/coreapi/qsgtexturemanager.cpp
@@ -120,13 +120,29 @@ public:
int uploadTimer;
QTime lastUpload;
+
+ int maxTextureSize;
};
+/*!
+ The QSGTextureManager class is responsible for converting QImages into texture ids
+ inside the QML scene graph.
+ The QSGTextureManager is created by the QSGContext class after a GL context is
+ available, so the QSGTextureManager and subclasses can safely assume that a
+ GL context is bound and make GL calls.
+ */
QSGTextureManager::QSGTextureManager()
: d(new QSGTextureManagerPrivate)
{
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &d->maxTextureSize);
+}
+
+
+int QSGTextureManager::maxTextureSize() const
+{
+ return d->maxTextureSize;
}
@@ -186,6 +202,9 @@ QSGTextureRef QSGTextureManager::upload(const QImage &image)
if (texture)
return QSGTextureRef(texture);
+ while (glGetError() != GL_NO_ERROR) {}
+
+
GLuint id;
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
@@ -198,6 +217,16 @@ QSGTextureRef QSGTextureManager::upload(const QImage &image)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
#endif
+ // Gracefully fail in case of an error...
+ GLuint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDeleteTextures(1, &id);
+ if (error != GL_OUT_OF_MEMORY)
+ qWarning("QSGTextureManager::upload failed, OpenGL error code: %x", error);
+ return QSGTextureRef();
+ }
+
texture = new QSGTexture;
texture->setTextureId(id);
texture->setTextureSize(image.size());
@@ -218,9 +247,29 @@ QSGTextureRef QSGTextureManager::requestUpload(const QImage &image,
const char *slot)
{
Q_ASSERT(!image.isNull());
+ QSGTexture *t = 0;
- QSGTexture *t = new QSGTexture();
- connect(t, SIGNAL(statusChanged(int)), listener, slot);
+
+ // Check if the image is already uploaded and thus part of the cache..
+ QSGTextureCacheKey key = { image.cacheKey() };
+ t = d->cache.value(key);
+ if (t)
+ return QSGTextureRef(t);
+
+
+ // Check if the image is already scheduled for asynchronous upload...
+ for (int i=0; i<d->asyncUploads.size(); ++i) {
+ const QSGTextureAsyncUpload &work = d->asyncUploads.at(i);
+ if (work.image.cacheKey() == image.cacheKey()) {
+ return QSGTextureRef(work.texture);
+ }
+ }
+
+
+ t = new QSGTexture();
+ t->setStatus(QSGTexture::Loading);
+ if (listener && slot)
+ connect(t, SIGNAL(statusChanged(int)), listener, slot);
connect(t, SIGNAL(destroyed(QObject*)), this, SLOT(textureDestroyed(QObject*)));
QSGTextureAsyncUpload work;
@@ -282,14 +331,30 @@ void QSGTextureManager::processAsyncTextures()
// Create or bind the texture...
if (upload.texture->textureId() == 0) {
+ while (glGetError() != GL_NO_ERROR) {}
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);
+
+ // Clean up
+ // Gracefully fail in case of an error...
+ GLuint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glDeleteTextures(1, &id);
+ if (error != GL_OUT_OF_MEMORY) {
+ qWarning("QSGTextureManager::async upload failed, OpenGL error code: %x", error);
+ t->setStatus(QSGTexture::Error);
+ } else {
+ t->setStatus(QSGTexture::Null);
+ }
+ return;
+ }
+
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());
diff --git a/src/scenegraph/coreapi/qsgtexturemanager.h b/src/scenegraph/coreapi/qsgtexturemanager.h
index 6eb25ad..2b065f3 100644
--- a/src/scenegraph/coreapi/qsgtexturemanager.h
+++ b/src/scenegraph/coreapi/qsgtexturemanager.h
@@ -177,6 +177,8 @@ public:
static void swizzleBGRAToRGBA(QImage *image);
+ int maxTextureSize() const;
+
protected:
void timerEvent(QTimerEvent *);
diff --git a/tests/auto/run_tests.sh b/tests/auto/run_tests.sh
new file mode 100755
index 0000000..87025c0
--- /dev/null
+++ b/tests/auto/run_tests.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+function execute_test()
+{
+ cd $1
+ qmake
+ make
+ ./tst_$1
+ cd ..
+}
+
+for val in *
+do
+ if [ -d "$val" ]
+ then
+ execute_test $val
+ fi
+done
diff --git a/tests/auto/texturemanager/texturemanager.pro b/tests/auto/texturemanager/texturemanager.pro
new file mode 100644
index 0000000..e203d26
--- /dev/null
+++ b/tests/auto/texturemanager/texturemanager.pro
@@ -0,0 +1,17 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2010-12-16T06:09:44
+#
+#-------------------------------------------------
+
+QT += opengl testlib
+
+TARGET = tst_texturemanager
+CONFIG += console
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+SOURCES += tst_texturemanagertest.cpp
+
+include(../../../src/scenegraph_include.pri)
diff --git a/tests/auto/texturemanager/tst_texturemanagertest.cpp b/tests/auto/texturemanager/tst_texturemanagertest.cpp
new file mode 100644
index 0000000..886dfa0
--- /dev/null
+++ b/tests/auto/texturemanager/tst_texturemanagertest.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt scene graph research project.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QString>
+#include <QtTest/QtTest>
+#include <QtCore/QCoreApplication>
+
+#include <qsgcontext.h>
+#include <qsgtexturemanager.h>
+
+class TextureManagerTest : public QObject
+{
+ Q_OBJECT
+
+public:
+ TextureManagerTest();
+
+private Q_SLOTS:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void upload();
+ void uploadSameImageTwice();
+
+ void requestUpload();
+ void requestUploadSameImageTwice();
+
+ void requestUploadAfterSyncUpload();
+
+ void gracefullyRunOutOfMemory();
+ void gracefullyFailOnTooLarge();
+
+ void maxTextureSize();
+
+private:
+ QSGContext *context;
+ QGLWidget *glWidget;
+ QSGTextureManager *tm;
+};
+
+
+TextureManagerTest::TextureManagerTest()
+{
+}
+
+
+void TextureManagerTest::initTestCase()
+{
+ glWidget = new QGLWidget();
+ glWidget->resize(300, 200);
+ glWidget->show();
+
+ context = new QSGContext();
+ context->initialize(const_cast<QGLContext *>(glWidget->context()));
+
+ tm = context->textureManager();
+}
+
+
+void TextureManagerTest::cleanupTestCase()
+{
+ delete context;
+ delete glWidget;
+}
+
+
+/*!
+ Verify that uploads work and are synchronous and that
+ size of returned texture must be at least image dims.
+ */
+void TextureManagerTest::upload()
+{
+ QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
+ QSGTextureRef t = tm->upload(image);
+
+ QVERIFY(!t.isNull());
+ QVERIFY(t.isReady());
+ QVERIFY(t->textureId() > 0);
+
+ QVERIFY(t->textureSize().width() >= image.width());
+ QVERIFY(t->textureSize().height() >= image.height());
+}
+
+
+/*!
+ Verify that two requests of the same image returns the same texture.
+ */
+void TextureManagerTest::uploadSameImageTwice()
+{
+ QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
+ QSGTextureRef t = tm->upload(image);
+
+ QImage shallowCopy = image;
+ QSGTextureRef t2 = tm->upload(shallowCopy);
+ QCOMPARE(t.texture(), t2.texture());
+}
+
+
+void TextureManagerTest::requestUpload()
+{
+ QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
+ QSGTextureRef t = tm->requestUpload(image, 0, 0);
+
+ QVERIFY(t->status() == QSGTexture::Ready || t->status() == QSGTexture::Loading);
+
+ int maxWait = 1000;
+ while (t->status() == QSGTexture::Loading) {
+ QTest::qWait(50);
+ QApplication::processEvents();
+ }
+
+ QVERIFY(t->status() == QSGTexture::Ready);
+ QVERIFY(t->textureId() > 0);
+ QVERIFY(t->textureSize().width() >= image.width());
+ QVERIFY(t->textureSize().height() >= image.height());
+}
+
+
+/*!
+ Test that we don't fail horribly when allocating large amounts of
+ texture memory. Since some drivers page graphics memory out to disk
+ and thus never run out, cap the test at 128Mb to not run forever
+ */
+void TextureManagerTest::gracefullyRunOutOfMemory()
+{
+ QImage image(512, 512, QImage::Format_ARGB32_Premultiplied);
+ QList<QSGTextureRef> refs;
+
+ int i = 128;
+ while (--i) {
+ QSGTextureRef t = tm->upload(image);
+ refs << t;
+ if (t.isNull())
+ break;
+ }
+
+ QVERIFY(true);
+}
+
+
+/*!
+ Test that we don't crash when we try to allocate a texture that is
+ too large for GL.
+ */
+void TextureManagerTest::gracefullyFailOnTooLarge()
+{
+ QImage image(32000, 2, QImage::Format_ARGB32_Premultiplied);
+ QSGTextureRef t = tm->upload(image);
+ QVERIFY(t.isNull());
+}
+
+
+void TextureManagerTest::maxTextureSize()
+{
+ int size = tm->maxTextureSize();
+
+ if (size < 1024) {
+ qWarning("Texture limit is only %d", size);
+ }
+
+ // 64 is the lowest allowed by the spec...
+ // Any lower number would indicate an error
+ QVERIFY(size >=64);
+}
+
+
+
+/*!
+ Verify that two consequitive requests return the same
+ texture instance so we don't upload images twice...
+ */
+void TextureManagerTest::requestUploadSameImageTwice()
+{
+ QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
+ QSGTextureRef t1 = tm->requestUpload(image, 0, 0);
+ QSGTextureRef t2 = tm->requestUpload(image, 0, 0);
+
+ QVERIFY(t1.texture() == t2.texture());
+}
+
+
+
+/*!
+ Verify that an async upload of a sync upload returns a ready
+ texture that is the same as the sync one.
+ */
+void TextureManagerTest::requestUploadAfterSyncUpload()
+{
+ QImage image(100, 100, QImage::Format_ARGB32_Premultiplied);
+ QSGTextureRef t1 = tm->upload(image);
+ QSGTextureRef t2 = tm->requestUpload(image, 0, 0);
+
+ QVERIFY(t1.texture() == t2.texture());
+ QVERIFY(t2.isReady());
+}
+
+
+
+QTEST_MAIN(TextureManagerTest);
+
+#include "tst_texturemanagertest.moc"