summaryrefslogtreecommitdiffstats
path: root/src/opengl
diff options
context:
space:
mode:
authorBjørn Erik Nilsen <bjorn.nilsen@nokia.com>2009-07-29 13:45:39 (GMT)
committerBjørn Erik Nilsen <bjorn.nilsen@nokia.com>2009-07-29 13:45:39 (GMT)
commit07c2b17276057a8b47c3be57ab7c2cf66dac0edd (patch)
tree48128509f60ff6d8a4bd129ccb3f29481529095a /src/opengl
parent80bd3a13ee6f42a39d3d3c2985a648e2229778f6 (diff)
parent928eb3dee17f43d54262d1f3dfc5212f183e6ce9 (diff)
downloadQt-07c2b17276057a8b47c3be57ab7c2cf66dac0edd.zip
Qt-07c2b17276057a8b47c3be57ab7c2cf66dac0edd.tar.gz
Qt-07c2b17276057a8b47c3be57ab7c2cf66dac0edd.tar.bz2
Merge branch 'kinetic-graphicseffect' of git@scm.dev.nokia.troll.no:qt/kinetic into graphicseffects
Conflicts: src/gui/graphicsview/qgraphicseffect.cpp src/gui/graphicsview/qgraphicseffect_p.h
Diffstat (limited to 'src/opengl')
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage.cpp123
-rw-r--r--src/opengl/gl2paintengineex/qglcustomshaderstage_p.h92
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp197
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager_p.h43
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h8
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache.cpp29
-rw-r--r--src/opengl/gl2paintengineex/qglgradientcache_p.h27
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp90
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h13
-rw-r--r--src/opengl/opengl.pro8
-rw-r--r--src/opengl/qgl.cpp478
-rw-r--r--src/opengl/qgl.h1
-rw-r--r--src/opengl/qgl_p.h171
-rw-r--r--src/opengl/qgl_x11.cpp140
-rw-r--r--src/opengl/qgl_x11egl.cpp11
-rw-r--r--src/opengl/qglextensions_p.h2
-rw-r--r--src/opengl/qglpixmapfilter.cpp279
-rw-r--r--src/opengl/qgraphicsshadereffect.cpp327
-rw-r--r--src/opengl/qgraphicsshadereffect.h89
-rw-r--r--src/opengl/qpixmapdata_gl.cpp183
-rw-r--r--src/opengl/qpixmapdata_gl_p.h19
21 files changed, 1945 insertions, 385 deletions
diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp
new file mode 100644
index 0000000..a82caa0
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qglcustomshaderstage_p.h"
+#include "qglengineshadermanager_p.h"
+#include "qpaintengineex_opengl2_p.h"
+#include <private/qpainter_p.h>
+
+class QGLCustomShaderStagePrivate
+{
+public:
+ QGLCustomShaderStagePrivate() :
+ m_manager(0) {}
+
+ QGLEngineShaderManager* m_manager;
+ QByteArray m_source;
+};
+
+
+
+
+QGLCustomShaderStage::QGLCustomShaderStage()
+ : d_ptr(new QGLCustomShaderStagePrivate)
+{
+}
+
+QGLCustomShaderStage::~QGLCustomShaderStage()
+{
+ Q_D(QGLCustomShaderStage);
+ if (d->m_manager)
+ d->m_manager->removeCustomStage(this);
+}
+
+void QGLCustomShaderStage::setUniformsDirty()
+{
+ Q_D(QGLCustomShaderStage);
+ if (d->m_manager)
+ d->m_manager->setDirty(); // ### Probably a bit overkill!
+}
+
+bool QGLCustomShaderStage::setOnPainter(QPainter* p)
+{
+ Q_D(QGLCustomShaderStage);
+ if (p->paintEngine()->type() != QPaintEngine::OpenGL2) {
+ qWarning("QGLCustomShaderStage::setOnPainter() - paint engine not OpenGL2");
+ return false;
+ }
+
+ // Might as well go through the paint engine to get to the context
+ const QGLContext* ctx = static_cast<QGL2PaintEngineEx*>(p->paintEngine())->context();
+ d->m_manager = QGLEngineShaderManager::managerForContext(ctx);
+ Q_ASSERT(d->m_manager);
+
+ d->m_manager->setCustomStage(this);
+ return true;
+}
+
+void QGLCustomShaderStage::removeFromPainter(QPainter* p)
+{
+ Q_D(QGLCustomShaderStage);
+ if (p->paintEngine()->type() != QPaintEngine::OpenGL2)
+ return;
+
+ // Might as well go through the paint engine to get to the context
+ const QGLContext* ctx = static_cast<QGL2PaintEngineEx*>(p->paintEngine())->context();
+ d->m_manager = QGLEngineShaderManager::managerForContext(ctx);
+ Q_ASSERT(d->m_manager);
+
+ // Just set the stage to null, don't call removeCustomStage().
+ // This should leave the program in a compiled/linked state
+ // if the next custom shader stage is this one again.
+ d->m_manager->setCustomStage(0);
+}
+
+const char* QGLCustomShaderStage::source() const
+{
+ Q_D(const QGLCustomShaderStage);
+ return d->m_source.constData();
+}
+
+void QGLCustomShaderStage::setSource(const QByteArray& s)
+{
+ Q_D(QGLCustomShaderStage);
+ d->m_source = s;
+}
diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
new file mode 100644
index 0000000..70e9ff0
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at http://www.qtsoftware.com/contact.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QGL_CUSTOM_SHADER_STAGE_H
+#define QGL_CUSTOM_SHADER_STAGE_H
+
+#include <QGLShaderProgram>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLCustomShaderStagePrivate;
+class Q_OPENGL_EXPORT QGLCustomShaderStage
+{
+ Q_DECLARE_PRIVATE(QGLCustomShaderStage);
+public:
+ QGLCustomShaderStage();
+ virtual ~QGLCustomShaderStage();
+ virtual void setUniforms(QGLShaderProgram*) = 0;
+
+ void setUniformsDirty();
+
+ bool setOnPainter(QPainter*);
+ void removeFromPainter(QPainter*);
+ const char* source() const;
+
+protected:
+ void setSource(const QByteArray&);
+
+private:
+ QGLCustomShaderStagePrivate* d_ptr;
+};
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+
+#endif
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index 27636f4..0692277 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -49,6 +49,28 @@
QT_BEGIN_NAMESPACE
+static void QGLEngineShaderManager_free(void *ptr)
+{
+ delete reinterpret_cast<QGLEngineShaderManager *>(ptr);
+}
+
+Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shader_managers, (QGLEngineShaderManager_free))
+
+QGLEngineShaderManager *QGLEngineShaderManager::managerForContext(const QGLContext *context)
+{
+ QGLEngineShaderManager *p = reinterpret_cast<QGLEngineShaderManager *>(qt_shader_managers()->value(context));
+ if (!p) {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (oldContext != context)
+ const_cast<QGLContext *>(context)->makeCurrent();
+ p = new QGLEngineShaderManager(const_cast<QGLContext *>(context));
+ qt_shader_managers()->insert(context, p);
+ if (oldContext && oldContext != context)
+ oldContext->makeCurrent();
+ }
+ return p;
+}
+
const char* QGLEngineShaderManager::qglEngineShaderSourceCode[] = {
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
@@ -64,6 +86,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
maskType(NoMask),
useTextureCoords(false),
compositionMode(QPainter::CompositionMode_SourceOver),
+ customSrcStage(0),
blitShaderProg(0),
simpleShaderProg(0),
currentShaderProg(0)
@@ -109,6 +132,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader;
code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader;
code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader;
+ code[CustomImageSrcFragmentShader] = ""; // Supplied by app.
code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader;
code[TextureBrushSrcFragmentShader] = qglslTextureBrushSrcFragmentShader;
code[TextureBrushSrcWithPatternFragmentShader] = qglslTextureBrushSrcWithPatternFragmentShader;
@@ -137,7 +161,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context)
#if defined(QT_DEBUG)
// Check that all the elements have been filled:
for (int i = 0; i < TotalShaderCount; ++i) {
- if (qglEngineShaderSourceCode[i] == 0) {
+ if (i != CustomImageSrcFragmentShader && qglEngineShaderSourceCode[i] == 0) {
int enumIndex = staticMetaObject.indexOfEnumerator("ShaderName");
QMetaEnum m = staticMetaObject.enumerator(enumIndex);
@@ -286,6 +310,44 @@ void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode)
shaderProgNeedsChanging = true; //###
}
+void QGLEngineShaderManager::setCustomStage(QGLCustomShaderStage* stage)
+{
+ // If the custom shader has changed, then destroy the previous compilation.
+ if (customSrcStage && stage && customSrcStage != stage)
+ removeCustomStage(customSrcStage);
+
+ customSrcStage = stage;
+ shaderProgNeedsChanging = true;
+}
+
+void QGLEngineShaderManager::shaderDestroyed(QObject *shader)
+{
+ // Remove any shader programs which has this as the srcPixel shader:
+ for (int i = 0; i < cachedPrograms.size(); ++i) {
+ if (cachedPrograms.at(i).srcPixelFragShader == shader) {
+ delete cachedPrograms.at(i).program;
+ cachedPrograms.removeAt(i--);
+ }
+ }
+
+ shaderProgNeedsChanging = true;
+}
+
+void QGLEngineShaderManager::removeCustomStage(QGLCustomShaderStage* stage)
+{
+ Q_UNUSED(stage); // Currently we only support one at a time...
+
+ QGLShader *compiledShader = compiledShaders[CustomImageSrcFragmentShader];
+
+ if (!compiledShader)
+ return;
+
+ compiledShaders[CustomImageSrcFragmentShader] = 0;
+ customSrcStage = 0;
+ shaderProgNeedsChanging = true;
+}
+
+
QGLShaderProgram* QGLEngineShaderManager::currentProgram()
{
return currentShaderProg->program;
@@ -310,6 +372,12 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
if (!shaderProgNeedsChanging)
return false;
+ bool useCustomSrc = customSrcStage != 0;
+ if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc) {
+ useCustomSrc = false;
+ qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src");
+ }
+
QGLEngineShaderProg requiredProgram;
requiredProgram.program = 0;
@@ -341,7 +409,11 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
qCritical("QGLEngineShaderManager::useCorrectShaderProg() - I'm scared, Qt::NoBrush style is set");
break;
case QGLEngineShaderManager::ImageSrc:
- srcPixelFragShaderName = ImageSrcFragmentShader;
+ srcPixelFragShaderName = useCustomSrc ? CustomImageSrcFragmentShader : ImageSrcFragmentShader;
+ positionVertexShaderName = PositionOnlyVertexShader;
+ break;
+ case QGLEngineShaderManager::NonPremultipliedImageSrc:
+ srcPixelFragShaderName = NonPremultipliedImageSrcFragmentShader;
positionVertexShaderName = PositionOnlyVertexShader;
break;
case QGLEngineShaderManager::PatternSrc:
@@ -353,10 +425,6 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
positionVertexShaderName = isAffine ? AffinePositionWithTextureBrushVertexShader
: PositionWithTextureBrushVertexShader;
break;
- case QGLEngineShaderManager::NonPremultipliedImageSrc:
- srcPixelFragShaderName = NonPremultipliedImageSrcFragmentShader;
- positionVertexShaderName = PositionOnlyVertexShader;
- break;
case Qt::SolidPattern:
srcPixelFragShaderName = SolidBrushSrcFragmentShader;
positionVertexShaderName = PositionOnlyVertexShader;
@@ -387,7 +455,6 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
requiredProgram.positionVertexShader = compiledShaders[positionVertexShaderName];
requiredProgram.srcPixelFragShader = compiledShaders[srcPixelFragShaderName];
-
const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus;
const bool hasMask = maskType != QGLEngineShaderManager::NoMask;
@@ -476,62 +543,63 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
else
requiredProgram.compositionFragShader = 0;
-
// At this point, requiredProgram is fully populated so try to find the program in the cache
+ bool foundProgramInCache = false;
for (int i = 0; i < cachedPrograms.size(); ++i) {
- QGLEngineShaderProg &prog = cachedPrograms[i];
- if ( (prog.mainVertexShader == requiredProgram.mainVertexShader)
- && (prog.positionVertexShader == requiredProgram.positionVertexShader)
- && (prog.mainFragShader == requiredProgram.mainFragShader)
- && (prog.srcPixelFragShader == requiredProgram.srcPixelFragShader)
- && (prog.compositionFragShader == requiredProgram.compositionFragShader) )
- {
- currentShaderProg = &prog;
- currentShaderProg->program->enable();
- shaderProgNeedsChanging = false;
- return true;
+ if (cachedPrograms[i] == requiredProgram) {
+ currentShaderProg = &cachedPrograms[i];
+ foundProgramInCache = true;
+ break;
}
}
- // Shader program not found in cache, create it now.
- requiredProgram.program = new QGLShaderProgram(ctx, this);
- requiredProgram.program->addShader(requiredProgram.mainVertexShader);
- requiredProgram.program->addShader(requiredProgram.positionVertexShader);
- requiredProgram.program->addShader(requiredProgram.mainFragShader);
- requiredProgram.program->addShader(requiredProgram.srcPixelFragShader);
- requiredProgram.program->addShader(requiredProgram.maskFragShader);
- requiredProgram.program->addShader(requiredProgram.compositionFragShader);
-
- // We have to bind the vertex attribute names before the program is linked:
- requiredProgram.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
- if (useTextureCoords)
- requiredProgram.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
-
- requiredProgram.program->link();
- if (!requiredProgram.program->isLinked()) {
- QString error;
- qWarning() << "Shader program failed to link,"
+ // If the shader program's not found in the cache, create it now.
+ if (!foundProgramInCache) {
+ requiredProgram.program = new QGLShaderProgram(ctx, this);
+ requiredProgram.program->addShader(requiredProgram.mainVertexShader);
+ requiredProgram.program->addShader(requiredProgram.positionVertexShader);
+ requiredProgram.program->addShader(requiredProgram.mainFragShader);
+ requiredProgram.program->addShader(requiredProgram.srcPixelFragShader);
+ requiredProgram.program->addShader(requiredProgram.maskFragShader);
+ requiredProgram.program->addShader(requiredProgram.compositionFragShader);
+
+ // We have to bind the vertex attribute names before the program is linked:
+ requiredProgram.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ if (useTextureCoords)
+ requiredProgram.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+
+ requiredProgram.program->link();
+ if (!requiredProgram.program->isLinked()) {
+ QString error;
+ qWarning() << "Shader program failed to link,"
#if defined(QT_DEBUG)
- << '\n'
- << " Shaders Used:" << '\n'
- << " mainVertexShader = " << requiredProgram.mainVertexShader->objectName() << '\n'
- << " positionVertexShader = " << requiredProgram.positionVertexShader->objectName() << '\n'
- << " mainFragShader = " << requiredProgram.mainFragShader->objectName() << '\n'
- << " srcPixelFragShader = " << requiredProgram.srcPixelFragShader->objectName() << '\n'
- << " maskFragShader = " << requiredProgram.maskFragShader->objectName() << '\n'
- << " compositionFragShader = "<< requiredProgram.compositionFragShader->objectName() << '\n'
+ << '\n'
+ << " Shaders Used:" << '\n'
+ << " mainVertexShader = " << requiredProgram.mainVertexShader->objectName() << '\n'
+ << " positionVertexShader = " << requiredProgram.positionVertexShader->objectName() << '\n'
+ << " mainFragShader = " << requiredProgram.mainFragShader->objectName() << '\n'
+ << " srcPixelFragShader = " << requiredProgram.srcPixelFragShader->objectName() << '\n'
+ << " maskFragShader = " << requiredProgram.maskFragShader->objectName() << '\n'
+ << " compositionFragShader = "<< requiredProgram.compositionFragShader->objectName() << '\n'
#endif
- << " Error Log:" << '\n'
- << " " << requiredProgram.program->log();
- qWarning() << error;
+ << " Error Log:" << '\n'
+ << " " << requiredProgram.program->log();
+ qWarning() << error;
+ delete requiredProgram.program;
+ } else {
+ cachedPrograms.append(requiredProgram);
+ // taking the address here is safe since
+ // cachePrograms isn't resized anywhere else
+ currentShaderProg = &cachedPrograms.last();
+ }
}
- else {
- cachedPrograms.append(requiredProgram);
- // taking the address here is safe since
- // cachePrograms isn't resized anywhere else
- currentShaderProg = &cachedPrograms.last();
+
+ if (currentShaderProg) {
currentShaderProg->program->enable();
+ if (useCustomSrc)
+ customSrcStage->setUniforms(currentShaderProg->program);
}
+
shaderProgNeedsChanging = false;
return true;
}
@@ -541,8 +609,27 @@ void QGLEngineShaderManager::compileNamedShader(QGLEngineShaderManager::ShaderNa
if (compiledShaders[name])
return;
- QGLShader *newShader = new QGLShader(type, ctx, this);
- newShader->compile(qglEngineShaderSourceCode[name]);
+ QGLShader *newShader;
+
+ QByteArray source;
+ if (name == CustomImageSrcFragmentShader) {
+ source = customSrcStage->source();
+ source += qglslCustomSrcFragmentShader;
+
+ newShader = customShaderCache.object(source);
+ if (!newShader) {
+ newShader = new QGLShader(type, ctx, this);
+ newShader->compile(source);
+ customShaderCache.insert(source, newShader);
+
+ connect(newShader, SIGNAL(destroyed(QObject *)),
+ this, SLOT(shaderDestroyed(QObject *)));
+ }
+ } else {
+ source = qglEngineShaderSourceCode[name];
+ newShader = new QGLShader(type, ctx, this);
+ newShader->compile(source);
+ }
#if defined(QT_DEBUG)
// Name the shader for easier debugging
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
index 442edfe..8122a08 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h
@@ -199,19 +199,23 @@
O = Global Opacity
- CUSTOM SHADER CODE (idea, depricated)
+ CUSTOM SHADER CODE
==================
The use of custom shader code is supported by the engine for drawImage and
drawPixmap calls. This is implemented via hooks in the fragment pipeline.
+
The custom shader is passed to the engine as a partial fragment shader
- (QGLCustomizedShader). The shader will implement a pre-defined method name
- which Qt's fragment pipeline will call. There are two different hooks which
- can be implemented as custom shader code:
+ (QGLCustomShaderStage). The shader will implement a pre-defined method name
+ which Qt's fragment pipeline will call:
+
+ lowp vec4 customShader(sampler2d src, vec2 srcCoords)
- mediump vec4 customShader(sampler2d src, vec2 srcCoords)
- mediump vec4 customShaderWithDest(sampler2d dest, sampler2d src, vec2 srcCoords)
+ The provided src and srcCoords parameters can be used to sample from the
+ source image.
+ Transformations, clipping, opacity, and composition modes set using QPainter
+ will be respected when using the custom shader hook.
*/
#ifndef QGLENGINE_SHADER_MANAGER_H
@@ -220,6 +224,8 @@
#include <QGLShader>
#include <QGLShaderProgram>
#include <QPainter>
+#include <private/qgl_p.h>
+#include <private/qglcustomshaderstage_p.h>
QT_BEGIN_HEADER
@@ -227,7 +233,6 @@ QT_BEGIN_NAMESPACE
QT_MODULE(OpenGL)
-
struct QGLEngineShaderProg
{
QGLShader* mainVertexShader;
@@ -239,6 +244,17 @@ struct QGLEngineShaderProg
QGLShaderProgram* program;
QVector<uint> uniformLocations;
+
+ bool operator==(const QGLEngineShaderProg& other) {
+ // We don't care about the program
+ return ( mainVertexShader == other.mainVertexShader &&
+ positionVertexShader == other.positionVertexShader &&
+ mainFragShader == other.mainFragShader &&
+ srcPixelFragShader == other.srcPixelFragShader &&
+ maskFragShader == other.maskFragShader &&
+ compositionFragShader == other.compositionFragShader
+ );
+ }
};
/*
@@ -259,7 +275,7 @@ struct QGLEngineCachedShaderProg
static const GLuint QT_VERTEX_COORDS_ATTR = 0;
static const GLuint QT_TEXTURE_COORDS_ATTR = 1;
-class QGLEngineShaderManager : public QObject
+class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject
{
Q_OBJECT
public:
@@ -304,6 +320,8 @@ public:
void setUseGlobalOpacity(bool);
void setMaskType(MaskType);
void setCompositionMode(QPainter::CompositionMode);
+ void setCustomStage(QGLCustomShaderStage* stage);
+ void removeCustomStage(QGLCustomShaderStage* stage);
uint getUniformLocation(Uniform id);
@@ -314,6 +332,8 @@ public:
QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers
QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer
+ static QGLEngineShaderManager *managerForContext(const QGLContext *context);
+
enum ShaderName {
MainVertexShader,
MainWithTexCoordsVertexShader,
@@ -341,8 +361,10 @@ public:
MainFragmentShader,
ImageSrcFragmentShader,
+ CustomSrcFragmentShader,
ImageSrcWithPatternFragmentShader,
NonPremultipliedImageSrcFragmentShader,
+ CustomImageSrcFragmentShader,
SolidBrushSrcFragmentShader,
TextureBrushSrcFragmentShader,
TextureBrushSrcWithPatternFragmentShader,
@@ -386,6 +408,8 @@ public:
Q_ENUMS(ShaderName)
#endif
+private slots:
+ void shaderDestroyed(QObject *shader);
private:
QGLContext* ctx;
@@ -398,11 +422,14 @@ private:
MaskType maskType;
bool useTextureCoords;
QPainter::CompositionMode compositionMode;
+ QGLCustomShaderStage* customSrcStage;
QGLShaderProgram* blitShaderProg;
QGLShaderProgram* simpleShaderProg;
QGLEngineShaderProg* currentShaderProg;
+ QCache<QByteArray, QGLShader> customShaderCache;
+
// TODO: Possibly convert to a LUT
QList<QGLEngineShaderProg> cachedPrograms;
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index 4e32f91..e379aa3 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -290,6 +290,14 @@ static const char* const qglslImageSrcFragmentShader = "\
return texture2D(imageTexture, textureCoords); \
}";
+static const char* const qglslCustomSrcFragmentShader = "\
+ varying highp vec2 textureCoords; \
+ uniform sampler2D imageTexture; \
+ lowp vec4 customShader(sampler2D texture, vec2 coords); \
+ lowp vec4 srcPixel() { \
+ return customShader(imageTexture, textureCoords); \
+ }";
+
static const char* const qglslImageSrcWithPatternFragmentShader = "\
varying highp vec2 textureCoords; \
uniform lowp vec4 patternColor; \
diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp
index 8c6b4f0..7c54bb9 100644
--- a/src/opengl/gl2paintengineex/qglgradientcache.cpp
+++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp
@@ -46,6 +46,28 @@
QT_BEGIN_NAMESPACE
+static void QGL2GradientCache_free(void *ptr)
+{
+ delete reinterpret_cast<QGL2GradientCache *>(ptr);
+}
+
+Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_gradient_caches, (QGL2GradientCache_free))
+
+QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context)
+{
+ QGL2GradientCache *p = reinterpret_cast<QGL2GradientCache *>(qt_gradient_caches()->value(context));
+ if (!p) {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (oldContext != context)
+ const_cast<QGLContext *>(context)->makeCurrent();
+ p = new QGL2GradientCache;
+ qt_gradient_caches()->insert(context, p);
+ if (oldContext && oldContext != context)
+ oldContext->makeCurrent();
+ }
+ return p;
+}
+
void QGL2GradientCache::cleanCache() {
QGLGradientColorTableHash::const_iterator it = cache.constBegin();
for (; it != cache.constEnd(); ++it) {
@@ -55,13 +77,8 @@ void QGL2GradientCache::cleanCache() {
cache.clear();
}
-GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity, const QGLContext *ctx)
+GLuint QGL2GradientCache::getBuffer(const QGradient &gradient, qreal opacity)
{
- if (buffer_ctx && !qgl_share_reg()->checkSharing(buffer_ctx, ctx))
- cleanCache();
-
- buffer_ctx = ctx;
-
quint64 hash_val = 0;
QGradientStops stops = gradient.stops();
diff --git a/src/opengl/gl2paintengineex/qglgradientcache_p.h b/src/opengl/gl2paintengineex/qglgradientcache_p.h
index 55c7b65..ba698bc 100644
--- a/src/opengl/gl2paintengineex/qglgradientcache_p.h
+++ b/src/opengl/gl2paintengineex/qglgradientcache_p.h
@@ -53,12 +53,12 @@
#include <QMultiHash>
#include <QObject>
#include <QtOpenGL/QtOpenGL>
+#include <private/qgl_p.h>
QT_BEGIN_NAMESPACE
-class QGL2GradientCache : public QObject
+class QGL2GradientCache
{
- Q_OBJECT
struct CacheInfo
{
inline CacheInfo(QGradientStops s, qreal op, QGradient::InterpolationMode mode) :
@@ -73,16 +73,12 @@ class QGL2GradientCache : public QObject
typedef QMultiHash<quint64, CacheInfo> QGLGradientColorTableHash;
public:
- QGL2GradientCache() : QObject(), buffer_ctx(0)
- {
-/*
- connect(QGLSignalProxy::instance(),
- SIGNAL(aboutToDestroyContext(const QGLContext *)),
- SLOT(cleanupGLContextRefs(const QGLContext *)));
-*/
- }
+ static QGL2GradientCache *cacheForContext(const QGLContext *context);
+
+ QGL2GradientCache() { }
+ ~QGL2GradientCache() {cleanCache();}
- GLuint getBuffer(const QGradient &gradient, qreal opacity, const QGLContext *ctx);
+ GLuint getBuffer(const QGradient &gradient, qreal opacity);
inline int paletteSize() const { return 1024; }
protected:
@@ -95,15 +91,6 @@ protected:
void cleanCache();
QGLGradientColorTableHash cache;
- const QGLContext *buffer_ctx;
-
-public slots:
- void cleanupGLContextRefs(const QGLContext *context) {
- if (context == buffer_ctx) {
- cleanCache();
- buffer_ctx = 0;
- }
- }
};
QT_END_NAMESPACE
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 2bfbf4a..db306a5 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -74,6 +74,7 @@
#include <private/qpainter_p.h>
#include <private/qfontengine_p.h>
#include <private/qtextureglyphcache_p.h>
+#include <private/qpixmapdata_gl_p.h>
#include "qglgradientcache_p.h"
#include "qglengineshadermanager_p.h"
@@ -115,6 +116,7 @@ public Q_SLOTS:
glDeleteFramebuffers(1, &m_fbo);
if (m_width || m_height)
glDeleteTextures(1, &m_texture);
+ ctx = 0;
} else {
// since the context holding the texture is shared, and
// about to be destroyed, we have to transfer ownership
@@ -151,10 +153,17 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyph
QGLTextureGlyphCache::~QGLTextureGlyphCache()
{
- glDeleteFramebuffers(1, &m_fbo);
-
- if (m_width || m_height)
- glDeleteTextures(1, &m_texture);
+ if (ctx) {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (oldContext != ctx)
+ ctx->makeCurrent();
+ glDeleteFramebuffers(1, &m_fbo);
+
+ if (m_width || m_height)
+ glDeleteTextures(1, &m_texture);
+ if (oldContext && oldContext != ctx)
+ oldContext->makeCurrent();
+ }
}
void QGLTextureGlyphCache::createTextureData(int width, int height)
@@ -266,10 +275,6 @@ extern QImage qt_imageForBrush(int brushStyle, bool invert);
QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate()
{
- if (shaderManager) {
- delete shaderManager;
- shaderManager = 0;
- }
}
void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id)
@@ -295,6 +300,7 @@ void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMod
QColor QGL2PaintEngineExPrivate::premultiplyColor(QColor c, GLfloat opacity)
{
qreal alpha = c.alphaF() * opacity;
+ c.setAlphaF(alpha);
c.setRedF(c.redF() * alpha);
c.setGreenF(c.greenF() * alpha);
c.setBlueF(c.blueF() * alpha);
@@ -338,9 +344,6 @@ void QGL2PaintEngineExPrivate::useSimpleShader()
}
}
-
-Q_GLOBAL_STATIC(QGL2GradientCache, qt_opengl_gradient_cache)
-
void QGL2PaintEngineExPrivate::updateBrushTexture()
{
// qDebug("QGL2PaintEngineExPrivate::updateBrushTexture()");
@@ -361,7 +364,10 @@ void QGL2PaintEngineExPrivate::updateBrushTexture()
// We apply global opacity in the fragment shaders, so we always pass 1.0
// for opacity to the cache.
- GLuint texId = qt_opengl_gradient_cache()->getBuffer(*g, 1.0, ctx);
+ GLuint texId = QGL2GradientCache::cacheForContext(ctx)->getBuffer(*g, 1.0);
+
+ glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
+ glBindTexture(GL_TEXTURE_2D, texId);
if (g->spread() == QGradient::RepeatSpread || g->type() == QGradient::ConicalGradient)
updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, true);
@@ -369,9 +375,6 @@ void QGL2PaintEngineExPrivate::updateBrushTexture()
updateTextureFilter(GL_TEXTURE_2D, GL_MIRRORED_REPEAT_IBM, true);
else
updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, true);
-
- glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT);
- glBindTexture(GL_TEXTURE_2D, texId);
}
else if (style == Qt::TexturePattern) {
const QPixmap& texPixmap = currentBrush->texture();
@@ -687,6 +690,12 @@ void QGL2PaintEngineEx::sync()
d->needsSync = true;
}
+const QGLContext *QGL2PaintEngineEx::context()
+{
+ Q_D(QGL2PaintEngineEx);
+ return d->ctx;
+}
+
void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode)
{
if (newMode == mode)
@@ -1052,14 +1061,18 @@ void QGL2PaintEngineEx::drawPixmap(const QRectF& dest, const QPixmap & pixmap, c
QGLContext *ctx = d->ctx;
glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
- GLuint id = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true);
+ QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, true, true);
+
+ GLfloat top = texture->yInverted ? (pixmap.height() - src.top()) : src.top();
+ GLfloat bottom = texture->yInverted ? (pixmap.height() - src.bottom()) : src.bottom();
+ QGLRect srcRect(src.left(), top, src.right(), bottom);
bool isBitmap = pixmap.isQBitmap();
bool isOpaque = !isBitmap && !pixmap.hasAlphaChannel();
d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT,
- state()->renderHints & QPainter::SmoothPixmapTransform, id);
- d->drawTexture(dest, src, pixmap.size(), isOpaque, isBitmap);
+ state()->renderHints & QPainter::SmoothPixmapTransform, texture->id);
+ d->drawTexture(dest, srcRect, pixmap.size(), isOpaque, isBitmap);
}
void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const QRectF& src,
@@ -1071,13 +1084,29 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const
QGLContext *ctx = d->ctx;
glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
- GLuint id = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true);
+ QGLTexture *texture = ctx->d_func()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, true);
+ GLuint id = texture->id;
d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT,
state()->renderHints & QPainter::SmoothPixmapTransform, id);
d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel());
}
+void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src)
+{
+ Q_D(QGL2PaintEngineEx);
+ ensureActive();
+ d->transferMode(ImageDrawingMode);
+
+ QGLContext *ctx = d->ctx;
+ glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT,
+ state()->renderHints & QPainter::SmoothPixmapTransform, textureId);
+ d->drawTexture(dest, src, size, false);
+}
+
void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem)
{
Q_D(QGL2PaintEngineEx);
@@ -1209,11 +1238,8 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev)
qt_resolve_version_2_0_functions(d->ctx);
#endif
- if (d->shaderManager) {
- d->shaderManager->setDirty();
- } else {
- d->shaderManager = new QGLEngineShaderManager(d->ctx);
- }
+ d->shaderManager = QGLEngineShaderManager::managerForContext(d->ctx);
+ d->shaderManager->setDirty();
glViewport(0, 0, d->width, d->height);
@@ -1288,6 +1314,14 @@ bool QGL2PaintEngineEx::end()
glUseProgram(0);
d->transferMode(BrushDrawingMode);
d->drawable.swapBuffers();
+#if defined(Q_WS_X11)
+ // On some (probably all) drivers, deleting an X pixmap which has been bound to a texture
+ // before calling glFinish/swapBuffers renders garbage. Presumably this is because X deletes
+ // the pixmap behind the driver's back before it's had a chance to use it. To fix this, we
+ // reference all QPixmaps which have been bound to stop them being deleted and only deref
+ // them here, after swapBuffers, where they can be safely deleted.
+ ctx->d_func()->boundPixmaps.clear();
+#endif
d->drawable.doneCurrent();
d->ctx->d_ptr->active_engine = 0;
@@ -1617,6 +1651,14 @@ QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState()
{
}
+QPixmapFilter *QGL2PaintEngineEx::createPixmapFilter(int type) const
+{
+ const QGLContext *ctx = QGLContext::currentContext();
+ if (ctx)
+ return ctx->d_func()->createPixmapFilter(type);
+ return 0;
+}
+
QT_END_NAMESPACE
#include "qpaintengineex_opengl2.moc"
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
index ec447a3..3ff2dca 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -88,8 +88,7 @@ public:
bool canRestoreClip;
};
-
-class QGL2PaintEngineEx : public QPaintEngineEx
+class Q_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx
{
Q_DECLARE_PRIVATE(QGL2PaintEngineEx)
public:
@@ -116,12 +115,13 @@ public:
virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr);
-
virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr,
Qt::ImageConversionFlags flags = Qt::AutoColor);
+ virtual void drawTexture(const QRectF &r, GLuint textureId, const QSize &size, const QRectF &sr);
+
virtual void drawTextItem(const QPointF &p, const QTextItem &textItem);
- Type type() const { return OpenGL; }
+ Type type() const { return OpenGL2; }
void setState(QPainterState *s);
QPainterState *createState(QPainterState *orig) const;
@@ -133,10 +133,15 @@ public:
}
virtual void sync();
+ const QGLContext* context();
+
+ QPixmapFilter *createPixmapFilter(int type) const;
+
private:
Q_DISABLE_COPY(QGL2PaintEngineEx)
};
+
class QGL2PaintEngineExPrivate : public QPaintEngineExPrivate
{
Q_DECLARE_PUBLIC(QGL2PaintEngineEx)
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
index c92b8cf..b217949 100644
--- a/src/opengl/opengl.pro
+++ b/src/opengl/opengl.pro
@@ -37,6 +37,7 @@ SOURCES += qgl.cpp \
!contains(QT_CONFIG, opengles1):!contains(QT_CONFIG, opengles1cl) {
HEADERS += qglshaderprogram.h \
qglpixmapfilter_p.h \
+ qgraphicsshadereffect.h \
qgraphicssystem_gl_p.h \
qwindowsurface_gl_p.h \
qpixmapdata_gl_p.h \
@@ -44,17 +45,20 @@ SOURCES += qgl.cpp \
gl2paintengineex/qglengineshadermanager_p.h \
gl2paintengineex/qgl2pexvertexarray_p.h \
gl2paintengineex/qpaintengineex_opengl2_p.h \
- gl2paintengineex/qglengineshadersource_p.h
+ gl2paintengineex/qglengineshadersource_p.h \
+ gl2paintengineex/qglcustomshaderstage_p.h
SOURCES += qglshaderprogram.cpp \
qglpixmapfilter.cpp \
+ qgraphicsshadereffect.cpp \
qgraphicssystem_gl.cpp \
qwindowsurface_gl.cpp \
qpixmapdata_gl.cpp \
gl2paintengineex/qglgradientcache.cpp \
gl2paintengineex/qglengineshadermanager.cpp \
gl2paintengineex/qgl2pexvertexarray.cpp \
- gl2paintengineex/qpaintengineex_opengl2.cpp
+ gl2paintengineex/qpaintengineex_opengl2.cpp \
+ gl2paintengineex/qglcustomshaderstage.cpp
}
diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp
index 0169ea2..edda6b6 100644
--- a/src/opengl/qgl.cpp
+++ b/src/opengl/qgl.cpp
@@ -87,7 +87,6 @@
#include <private/qglpixelbuffer_p.h>
#include <private/qwindowsurface_gl_p.h>
#include "qcolormap.h"
-#include "qcache.h"
#include "qfile.h"
#include "qlibrary.h"
@@ -1395,39 +1394,99 @@ int qt_next_power_of_two(int v)
return v;
}
-class QGLTexture {
-public:
- QGLTexture(const QGLContext *ctx, GLuint tx_id, GLenum tx_target, bool _clean = false)
- : context(ctx), id(tx_id), target(tx_target), clean(_clean) {}
- ~QGLTexture() {
- if (clean) {
- QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
- QGLContext *ctx = const_cast<QGLContext *>(context);
- bool switch_context = current && current != ctx && !qgl_share_reg()->checkSharing(current, ctx);
- if (switch_context)
- ctx->makeCurrent();
- glDeleteTextures(1, &id);
- if (switch_context)
- current->makeCurrent();
- }
- }
-
- const QGLContext *context;
- GLuint id;
- GLenum target;
- bool clean;
-};
-
-typedef QCache<qint64, QGLTexture> QGLTextureCache;
-static int qt_tex_cache_limit = 64*1024; // cache ~64 MB worth of textures - this is not accurate though
-static QGLTextureCache *qt_tex_cache = 0;
-
typedef void (*_qt_pixmap_cleanup_hook_64)(qint64);
typedef void (*_qt_image_cleanup_hook_64)(qint64);
extern Q_GUI_EXPORT _qt_pixmap_cleanup_hook_64 qt_pixmap_cleanup_hook_64;
extern Q_GUI_EXPORT _qt_image_cleanup_hook_64 qt_image_cleanup_hook_64;
+static QGLTextureCache *qt_gl_texture_cache = 0;
+
+QGLTextureCache::QGLTextureCache()
+ : m_cache(64*1024) // cache ~64 MB worth of textures - this is not accurate though
+{
+ Q_ASSERT(qt_gl_texture_cache == 0);
+ qt_gl_texture_cache = this;
+ qt_pixmap_cleanup_hook_64 = cleanupHook;
+ qt_image_cleanup_hook_64 = cleanupHook;
+}
+
+QGLTextureCache::~QGLTextureCache()
+{
+ qt_gl_texture_cache = 0;
+ qt_pixmap_cleanup_hook_64 = 0;
+ qt_image_cleanup_hook_64 = 0;
+}
+
+void QGLTextureCache::insert(QGLContext* ctx, qint64 key, QGLTexture* texture, int cost)
+{
+ if (m_cache.totalCost() + cost > m_cache.maxCost()) {
+ // the cache is full - make an attempt to remove something
+ const QList<qint64> keys = m_cache.keys();
+ int i = 0;
+ while (i < m_cache.count()
+ && (m_cache.totalCost() + cost > m_cache.maxCost())) {
+ QGLTexture *tex = m_cache.object(keys.at(i));
+ if (tex->context == ctx)
+ m_cache.remove(keys.at(i));
+ ++i;
+ }
+ }
+ m_cache.insert(key, texture, cost);
+}
+
+bool QGLTextureCache::remove(QGLContext* ctx, GLuint textureId)
+{
+ QList<qint64> keys = m_cache.keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ QGLTexture *tex = m_cache.object(keys.at(i));
+ if (tex->id == textureId && tex->context == ctx) {
+ tex->clean = true; // forces a glDeleteTextures() call
+ m_cache.remove(keys.at(i));
+ return true;
+ }
+ }
+ return false;
+}
+
+void QGLTextureCache::removeContextTextures(QGLContext* ctx)
+{
+ QList<qint64> keys = m_cache.keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ const qint64 &key = keys.at(i);
+ if (m_cache.object(key)->context == ctx)
+ m_cache.remove(key);
+ }
+}
+
+QGLTextureCache* QGLTextureCache::instance()
+{
+ if (!qt_gl_texture_cache)
+ qt_gl_texture_cache = new QGLTextureCache;
+
+ return qt_gl_texture_cache;
+}
+
+/*
+ a hook that removes textures from the cache when a pixmap/image
+ is deref'ed
+*/
+void QGLTextureCache::cleanupHook(qint64 cacheKey)
+{
+ // ### remove when the GL texture cache becomes thread-safe
+ if (qApp->thread() != QThread::currentThread())
+ return;
+ QGLTexture *texture = instance()->getTexture(cacheKey);
+ if (texture && texture->clean)
+ instance()->remove(cacheKey);
+}
+
+void QGLTextureCache::deleteIfEmpty()
+{
+ if (instance()->size() == 0)
+ delete instance();
+}
+
// DDS format structure
struct DDSFormat {
quint32 dwSize;
@@ -1556,21 +1615,8 @@ QGLContext::~QGLContext()
{
Q_D(QGLContext);
// remove any textures cached in this context
- if (qt_tex_cache) {
- QList<qint64> keys = qt_tex_cache->keys();
- for (int i = 0; i < keys.size(); ++i) {
- const qint64 &key = keys.at(i);
- if (qt_tex_cache->object(key)->context == this)
- qt_tex_cache->remove(key);
- }
- // ### thread safety
- if (qt_tex_cache->size() == 0) {
- qt_pixmap_cleanup_hook_64 = 0;
- qt_image_cleanup_hook_64 = 0;
- delete qt_tex_cache;
- qt_tex_cache = 0;
- }
- }
+ QGLTextureCache::instance()->removeContextTextures(this);
+ QGLTextureCache::deleteIfEmpty(); // ### thread safety
QGLSignalProxy::instance()->emitAboutToDestroyContext(this);
reset();
@@ -1701,21 +1747,6 @@ GLuint QGLContext::bindTexture(const QString &fileName)
return tx_id;
}
-/*
- a hook that removes textures from the cache when a pixmap/image
- is deref'ed
-*/
-static void qt_gl_clean_cache(qint64 cacheKey)
-{
- // ### remove when the GL texture cache becomes thread-safe
- if (qApp->thread() != QThread::currentThread())
- return;
- if (qt_tex_cache) {
- QGLTexture *texture = qt_tex_cache->object(cacheKey);
- if (texture && texture->clean)
- qt_tex_cache->remove(cacheKey);
- }
-}
static void convertToGLFormatHelper(QImage &dst, const QImage &img, GLenum texture_format)
{
@@ -1835,7 +1866,28 @@ QImage QGLContextPrivate::convertToGLFormat(const QImage &image, bool force_prem
return result;
}
-GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format,
+/*! \internal */
+QGLTexture *QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, bool clean)
+{
+ const qint64 key = image.cacheKey();
+ QGLTexture *texture = textureCacheLookup(key, target);
+ if (texture) {
+ glBindTexture(target, texture->id);
+ return texture;
+ }
+
+ if (!texture)
+ texture = bindTexture(image, target, format, key, clean);
+ // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
+ Q_ASSERT(texture);
+
+ if (texture->id > 0)
+ const_cast<QImage &>(image).data_ptr()->is_cached = true;
+
+ return texture;
+}
+
+QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format,
const qint64 key, bool clean)
{
Q_Q(QGLContext);
@@ -1853,11 +1905,6 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint
// the GL_BGRA format is only present in GL version >= 1.2
GLenum texture_format = (QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_1_2)
? GL_BGRA : GL_RGBA;
- if (!qt_tex_cache) {
- qt_tex_cache = new QGLTextureCache(qt_tex_cache_limit);
- qt_pixmap_cleanup_hook_64 = qt_gl_clean_cache;
- qt_image_cleanup_hook_64 = qt_gl_clean_cache;
- }
// Scale the pixmap if needed. GL textures needs to have the
// dimensions 2^n+2(border) x 2^m+2(border), unless we're using GL
@@ -1930,53 +1977,26 @@ GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint
// this assumes the size of a texture is always smaller than the max cache size
int cost = img.width()*img.height()*4/1024;
- if (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost()) {
- // the cache is full - make an attempt to remove something
- const QList<qint64> keys = qt_tex_cache->keys();
- int i = 0;
- while (i < qt_tex_cache->count()
- && (qt_tex_cache->totalCost() + cost > qt_tex_cache->maxCost())) {
- QGLTexture *tex = qt_tex_cache->object(keys.at(i));
- if (tex->context == q)
- qt_tex_cache->remove(keys.at(i));
- ++i;
- }
- }
- qt_tex_cache->insert(key, new QGLTexture(q, tx_id, target, clean), cost);
- return tx_id;
+ QGLTexture *texture = new QGLTexture(q, tx_id, target, clean, false);
+ QGLTextureCache::instance()->insert(q, key, texture, cost);
+ return texture;
}
-bool QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target, GLuint *id)
+QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum target)
{
Q_Q(QGLContext);
- if (qt_tex_cache) {
- QGLTexture *texture = qt_tex_cache->object(key);
- if (texture && texture->target == target
- && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context)))
- {
- *id = texture->id;
- return true;
- }
+ QGLTexture *texture = QGLTextureCache::instance()->getTexture(key);
+ if (texture && texture->target == target
+ && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context)))
+ {
+ return texture;
}
- return false;
+ return 0;
}
-/*! \internal */
-GLuint QGLContextPrivate::bindTexture(const QImage &image, GLenum target, GLint format, bool clean)
-{
- const qint64 key = image.cacheKey();
- GLuint id;
- if (textureCacheLookup(key, target, &id)) {
- glBindTexture(target, id);
- return id;
- }
- GLuint cached = bindTexture(image, target, format, key, clean);
- const_cast<QImage &>(image).data_ptr()->is_cached = (cached > 0);
- return cached;
-}
/*! \internal */
-GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean)
+QGLTexture *QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean, bool canInvert)
{
Q_Q(QGLContext);
QPixmapData *pd = pixmap.pixmapData();
@@ -1984,20 +2004,41 @@ GLuint QGLContextPrivate::bindTexture(const QPixmap &pixmap, GLenum target, GLin
if (target == GL_TEXTURE_2D && pd->classId() == QPixmapData::OpenGLClass) {
const QGLPixmapData *data = static_cast<const QGLPixmapData *>(pd);
- if (data->isValidContext(q))
- return data->bind();
+ if (data->isValidContext(q)) {
+ data->bind();
+ return data->texture();
+ }
}
#endif
const qint64 key = pixmap.cacheKey();
- GLuint id;
- if (textureCacheLookup(key, target, &id)) {
- glBindTexture(target, id);
- return id;
+ QGLTexture *texture = textureCacheLookup(key, target);
+ if (texture) {
+ glBindTexture(target, texture->id);
+ return texture;
}
- GLuint cached = bindTexture(pixmap.toImage(), target, format, key, clean);
- const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = (cached > 0);
- return cached;
+
+#if defined(Q_WS_X11)
+ // Try to use texture_from_pixmap
+ if (pd->classId() == QPixmapData::X11Class) {
+ QPixmap *thatPixmap = const_cast<QPixmap*>(&pixmap);
+ texture = bindTextureFromNativePixmap(thatPixmap, key, canInvert);
+ if (texture) {
+ texture->clean = clean;
+ boundPixmaps.insert(thatPixmap->data_ptr(), QPixmap(pixmap));
+ }
+ }
+#endif
+
+ if (!texture)
+ texture = bindTexture(pixmap.toImage(), target, format, key, clean);
+ // NOTE: bindTexture(const QImage&, GLenum, GLint, const qint64, bool) should never return null
+ Q_ASSERT(texture);
+
+ if (texture->id > 0)
+ const_cast<QPixmap &>(pixmap).data_ptr()->is_cached = true;
+
+ return texture;
}
/*! \internal */
@@ -2063,7 +2104,8 @@ int QGLContextPrivate::maxTextureSize()
GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
{
Q_D(QGLContext);
- return d->bindTexture(image, target, format, false);
+ QGLTexture *texture = d->bindTexture(image, target, format, false);
+ return texture->id;
}
#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
@@ -2071,7 +2113,8 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format)
GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMacCompatGLint format)
{
Q_D(QGLContext);
- return d->bindTexture(image, GLenum(target), GLint(format), false);
+ QGLTexture *texture = d->bindTexture(image, GLenum(target), GLint(format), false);
+ return texture->id;
}
#endif
@@ -2082,7 +2125,8 @@ GLuint QGLContext::bindTexture(const QImage &image, QMacCompatGLenum target, QMa
GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
{
Q_D(QGLContext);
- return d->bindTexture(pixmap, target, format, false);
+ QGLTexture *texture = d->bindTexture(pixmap, target, format, false, false);
+ return texture->id;
}
#ifdef Q_MAC_COMPAT_GL_FUNCTIONS
@@ -2090,7 +2134,8 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint forma
GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, QMacCompatGLint format)
{
Q_D(QGLContext);
- return d->bindTexture(pixmap, GLenum(target), GLint(format), false);
+ QGLTexture *texture = d->bindTexture(pixmap, GLenum(target), GLint(format), false, false);
+ return texture->id;
}
#endif
@@ -2103,17 +2148,8 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, QMacCompatGLenum target, Q
*/
void QGLContext::deleteTexture(GLuint id)
{
- if (qt_tex_cache) {
- QList<qint64> keys = qt_tex_cache->keys();
- for (int i = 0; i < keys.size(); ++i) {
- QGLTexture *tex = qt_tex_cache->object(keys.at(i));
- if (tex->id == id && tex->context == this) {
- tex->clean = true; // forces a glDeleteTextures() call
- qt_tex_cache->remove(keys.at(i));
- return;
- }
- }
- }
+ if (QGLTextureCache::instance()->remove(this, id))
+ return;
// check the DDS cache if the texture wasn't found in the pixmap/image
// cache
@@ -2307,9 +2343,7 @@ void QGLContext::drawTexture(const QPointF &point, QMacCompatGLuint textureId, Q
*/
void QGLContext::setTextureCacheLimit(int size)
{
- qt_tex_cache_limit = size;
- if (qt_tex_cache)
- qt_tex_cache->setMaxCost(qt_tex_cache_limit);
+ QGLTextureCache::instance()->setMaxCost(size);
}
/*!
@@ -2319,7 +2353,7 @@ void QGLContext::setTextureCacheLimit(int size)
*/
int QGLContext::textureCacheLimit()
{
- return qt_tex_cache_limit;
+ return QGLTextureCache::instance()->maxCost();
}
@@ -3363,8 +3397,13 @@ bool QGLWidget::event(QEvent *e)
#elif defined(Q_WS_WIN)
if (e->type() == QEvent::ParentChange) {
QGLContext *newContext = new QGLContext(d->glcx->requestedFormat(), this);
- qgl_share_reg()->replaceShare(d->glcx, newContext);
+ QList<const QGLContext *> shares = qgl_share_reg()->shares(d->glcx);
setContext(newContext);
+ for (int i = 0; i < shares.size(); ++i) {
+ if (newContext != shares.at(i))
+ qgl_share_reg()->addShare(newContext, shares.at(i));
+ }
+
// the overlay needs to be recreated as well
delete d->olcx;
if (isValid() && context()->format().hasOverlay()) {
@@ -4334,6 +4373,9 @@ void QGLExtensions::init_extensions()
if (extensions.contains(QLatin1String("EXT_framebuffer_blit")))
glExtensions |= FramebufferBlit;
+ if (extensions.contains(QLatin1String("GL_ARB_texture_non_power_of_two")))
+ glExtensions |= NPOTTextures;
+
QGLContext cx(QGLFormat::defaultFormat());
if (glExtensions & TextureCompression) {
qt_glCompressedTexImage2DARB = (pfn_glCompressedTexImage2DARB) cx.getProcAddress(QLatin1String("glCompressedTexImage2DARB"));
@@ -4541,32 +4583,34 @@ QGLFormat QGLDrawable::format() const
GLuint QGLDrawable::bindTexture(const QImage &image, GLenum target, GLint format)
{
+ QGLTexture *texture;
if (widget)
- return widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true);
+ texture = widget->d_func()->glcx->d_func()->bindTexture(image, target, format, true);
else if (buffer)
- return buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true);
+ texture = buffer->d_func()->qctx->d_func()->bindTexture(image, target, format, true);
else if (fbo && QGLContext::currentContext())
- return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true);
+ texture = const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(image, target, format, true);
#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL))
else if (wsurf)
- return wsurf->context()->d_func()->bindTexture(image, target, format, true);
+ texture = wsurf->context()->d_func()->bindTexture(image, target, format, true);
#endif
- return 0;
+ return texture->id;
}
GLuint QGLDrawable::bindTexture(const QPixmap &pixmap, GLenum target, GLint format)
{
+ QGLTexture *texture;
if (widget)
- return widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true);
+ texture = widget->d_func()->glcx->d_func()->bindTexture(pixmap, target, format, true, true);
else if (buffer)
- return buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true);
+ texture = buffer->d_func()->qctx->d_func()->bindTexture(pixmap, target, format, true, true);
else if (fbo && QGLContext::currentContext())
- return const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true);
+ texture = const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(pixmap, target, format, true, true);
#if defined(Q_WS_QWS) || (!defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL))
else if (wsurf)
- return wsurf->context()->d_func()->bindTexture(pixmap, target, format, true);
+ texture = wsurf->context()->d_func()->bindTexture(pixmap, target, format, true, true);
#endif
- return 0;
+ return texture->id;
}
QColor QGLDrawable::backgroundColor() const
@@ -4612,4 +4656,156 @@ bool QGLDrawable::autoFillBackground() const
return false;
}
+
+bool QGLShareRegister::checkSharing(const QGLContext *context1, const QGLContext *context2) {
+ bool sharing = (context1 && context2 && context1->d_ptr->groupResources == context2->d_ptr->groupResources);
+ return sharing;
+}
+
+void QGLShareRegister::addShare(const QGLContext *context, const QGLContext *share) {
+ Q_ASSERT(context && share);
+ if (context->d_ptr->groupResources == share->d_ptr->groupResources)
+ return;
+
+ // Make sure 'context' is not already shared with another group of contexts.
+ Q_ASSERT(reg.find(context->d_ptr->groupResources) == reg.end());
+ Q_ASSERT(context->d_ptr->groupResources->refs == 1);
+
+ // Free 'context' group resources and make it use the same resources as 'share'.
+ delete context->d_ptr->groupResources;
+ context->d_ptr->groupResources = share->d_ptr->groupResources;
+ context->d_ptr->groupResources->refs.ref();
+
+ // Maintain a list of all the contexts in each group of sharing contexts.
+ SharingHash::iterator it = reg.find(share->d_ptr->groupResources);
+ if (it == reg.end())
+ it = reg.insert(share->d_ptr->groupResources, ContextList() << share);
+ it.value() << context;
+}
+
+QList<const QGLContext *> QGLShareRegister::shares(const QGLContext *context) {
+ SharingHash::const_iterator it = reg.find(context->d_ptr->groupResources);
+ if (it == reg.end())
+ return ContextList();
+ return it.value();
+}
+
+void QGLShareRegister::removeShare(const QGLContext *context) {
+ SharingHash::iterator it = reg.find(context->d_ptr->groupResources);
+ if (it == reg.end())
+ return;
+
+ int count = it.value().removeAll(context);
+ Q_ASSERT(count == 1);
+
+ Q_ASSERT(it.value().size() != 0);
+ if (it.value().size() == 1)
+ reg.erase(it);
+}
+
+QGLContextResource::QGLContextResource(FreeFunc f, QObject *parent)
+ : QObject(parent), free(f)
+{
+ connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)), this, SLOT(aboutToDestroyContext(const QGLContext *)));
+}
+
+QGLContextResource::~QGLContextResource()
+{
+ while (!m_resources.empty())
+ remove(m_resources.begin().key());
+}
+
+void QGLContextResource::insert(const QGLContext *key, void *value)
+{
+ QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
+ if (shares.size() == 0)
+ shares.append(key);
+ void *oldValue = 0;
+ for (int i = 0; i < shares.size(); ++i) {
+ ResourceHash::iterator it = m_resources.find(shares.at(i));
+ if (it != m_resources.end()) {
+ Q_ASSERT(oldValue == 0 || oldValue == it.value());
+ oldValue = it.value();
+ it.value() = value;
+ } else {
+ m_resources.insert(shares.at(i), value);
+ }
+ }
+ if (oldValue != 0 && oldValue != value) {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (oldContext != key)
+ const_cast<QGLContext *>(key)->makeCurrent();
+ free(oldValue);
+ if (oldContext && oldContext != key)
+ oldContext->makeCurrent();
+ }
+}
+
+void *QGLContextResource::value(const QGLContext *key)
+{
+ ResourceHash::const_iterator it = m_resources.find(key);
+ // Check if there is a value associated with 'key'.
+ if (it != m_resources.end())
+ return it.value();
+ // Check if there is a value associated with sharing contexts.
+ QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
+ for (int i = 0; i < shares.size() && it == m_resources.end(); ++i)
+ it = m_resources.find(shares.at(i));
+ if (it == m_resources.end())
+ return 0; // Didn't find anything.
+
+ // Found something! Share this info with all the buddies.
+ for (int i = 0; i < shares.size(); ++i)
+ m_resources.insert(shares.at(i), it.value());
+ return it.value();
+}
+
+void QGLContextResource::remove(const QGLContext *key)
+{
+ QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
+ if (shares.size() == 0)
+ shares.append(key);
+ void *oldValue = 0;
+ for (int i = 0; i < shares.size(); ++i) {
+ ResourceHash::iterator it = m_resources.find(shares.at(i));
+ if (it != m_resources.end()) {
+ Q_ASSERT(oldValue == 0 || oldValue == it.value());
+ oldValue = it.value();
+ m_resources.erase(it);
+ }
+ }
+ if (oldValue != 0) {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (oldContext != key)
+ const_cast<QGLContext *>(key)->makeCurrent();
+ free(oldValue);
+ if (oldContext && oldContext != key)
+ oldContext->makeCurrent();
+ }
+}
+
+void QGLContextResource::aboutToDestroyContext(const QGLContext *key)
+{
+ ResourceHash::iterator it = m_resources.find(key);
+ if (it == m_resources.end())
+ return;
+
+ QList<const QGLContext *> shares = qgl_share_reg()->shares(key);
+ if (shares.size() > 1) {
+ Q_ASSERT(key->isSharing());
+ // At least one of the shared contexts must stay in the cache.
+ // Otherwise, the value pointer is lost.
+ for (int i = 0; i < 2/*shares.size()*/; ++i)
+ m_resources.insert(shares.at(i), it.value());
+ } else {
+ QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ if (oldContext != key)
+ const_cast<QGLContext *>(key)->makeCurrent();
+ free(it.value());
+ if (oldContext && oldContext != key)
+ oldContext->makeCurrent();
+ }
+ m_resources.erase(it);
+}
+
QT_END_NAMESPACE
diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h
index 86555da..31b9543 100644
--- a/src/opengl/qgl.h
+++ b/src/opengl/qgl.h
@@ -364,6 +364,7 @@ private:
friend class QGLPixmapData;
friend class QGLPixmapFilterBase;
friend class QGLTextureGlyphCache;
+ friend class QGLShareRegister;
friend QGLFormat::OpenGLVersionFlags QGLFormat::openGLVersionFlags();
#ifdef Q_WS_MAC
public:
diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h
index ac19d64..85dae0d 100644
--- a/src/opengl/qgl_p.h
+++ b/src/opengl/qgl_p.h
@@ -60,10 +60,7 @@
#include "QtCore/qthreadstorage.h"
#include "QtCore/qhash.h"
#include "private/qwidget_p.h"
-
-#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
-#include "private/qpixmapdata_gl_p.h"
-#endif
+#include "qcache.h"
#ifndef QT_OPENGL_ES_1_CL
#define q_vertexType float
@@ -196,17 +193,26 @@ public:
#endif
};
+struct QGLContextGroupResources
+{
+ QGLContextGroupResources() : refs(1) { }
+ QGLExtensionFuncs extensionFuncs;
+ QAtomicInt refs;
+};
+
+class QGLTexture;
+
class QGLContextPrivate
{
Q_DECLARE_PUBLIC(QGLContext)
public:
- explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {}
- ~QGLContextPrivate() {}
- GLuint bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key,
+ explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {groupResources = new QGLContextGroupResources;}
+ ~QGLContextPrivate() {if (!groupResources->refs.deref()) delete groupResources;}
+ QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, bool clean);
+ QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, const qint64 key,
bool clean = false);
- GLuint bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean);
- GLuint bindTexture(const QImage &image, GLenum target, GLint format, bool clean);
- bool textureCacheLookup(const qint64 key, GLenum target, GLuint *id);
+ QGLTexture *bindTexture(const QPixmap &pixmap, GLenum target, GLint format, bool clean, bool canInvert = false);
+ QGLTexture *textureCacheLookup(const qint64 key, GLenum target);
void init(QPaintDevice *dev, const QGLFormat &format);
QImage convertToGLFormat(const QImage &image, bool force_premul, GLenum texture_format);
int maxTextureSize();
@@ -234,6 +240,8 @@ public:
void* pbuf;
quint32 gpm;
int screen;
+ QHash<QPixmapData*, QPixmap> boundPixmaps;
+ QGLTexture *bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool internal);
#endif
#if defined(Q_WS_MAC)
bool update;
@@ -257,14 +265,14 @@ public:
QGLContext *q_ptr;
QGLFormat::OpenGLVersionFlags version_flags;
- QGLExtensionFuncs extensionFuncs;
+ QGLContextGroupResources *groupResources;
GLint max_texture_size;
GLuint current_fbo;
QPaintEngine *active_engine;
#ifdef Q_WS_WIN
- static inline QGLExtensionFuncs& qt_get_extension_funcs(const QGLContext *ctx) { return ctx->d_ptr->extensionFuncs; }
+ static inline QGLExtensionFuncs& qt_get_extension_funcs(const QGLContext *ctx) { return ctx->d_ptr->groupResources->extensionFuncs; }
#endif
#if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS)
@@ -293,6 +301,7 @@ class QGLPixelBuffer;
class QGLFramebufferObject;
class QWSGLWindowSurface;
class QGLWindowSurface;
+class QGLPixmapData;
class QGLDrawable {
public:
QGLDrawable() : widget(0), buffer(0), fbo(0)
@@ -353,7 +362,8 @@ public:
PackedDepthStencil = 0x00000200,
NVFloatBuffer = 0x00000400,
PixelBufferObject = 0x00000800,
- FramebufferBlit = 0x00001000
+ FramebufferBlit = 0x00001000,
+ NPOTTextures = 0x00002000
};
Q_DECLARE_FLAGS(Extensions, Extension)
@@ -371,65 +381,89 @@ struct QGLThreadContext {
};
extern QThreadStorage<QGLThreadContext *> qgl_context_storage;
-typedef QMultiHash<const QGLContext *, const QGLContext *> QGLSharingHash;
class QGLShareRegister
{
public:
QGLShareRegister() {}
~QGLShareRegister() { reg.clear(); }
- bool checkSharing(const QGLContext *context1, const QGLContext *context2, const QGLContext * skip=0) {
- if (context1 == context2)
- return true;
- QList<const QGLContext *> shares = reg.values(context1);
- for (int k=0; k<shares.size(); ++k) {
- const QGLContext *ctx = shares.at(k);
- if (ctx == skip) // avoid an indirect circular loop (infinite recursion)
- continue;
- if (ctx == context2)
- return true;
- if (checkSharing(ctx, context2, context1))
- return true;
- }
- return false;
- }
+ bool checkSharing(const QGLContext *context1, const QGLContext *context2);
+ void addShare(const QGLContext *context, const QGLContext *share);
+ QList<const QGLContext *> shares(const QGLContext *context);
+ void removeShare(const QGLContext *context);
+private:
+ // Use a context's 'groupResources' pointer to uniquely identify a group.
+ typedef QList<const QGLContext *> ContextList;
+ typedef QHash<const QGLContextGroupResources *, ContextList> SharingHash;
+ SharingHash reg;
+};
- void addShare(const QGLContext *context, const QGLContext *share) {
- reg.insert(context, share); // context sharing works both ways
- reg.insert(share, context);
- }
+extern Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg();
- void removeShare(const QGLContext *context) {
- QGLSharingHash::iterator it = reg.begin();
- while (it != reg.end()) {
- if (it.key() == context || it.value() == context)
- it = reg.erase(it);
- else
- ++it;
+class QGLTexture {
+public:
+ QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D,
+ bool _clean = false, bool _yInverted = false)
+ : context(ctx), id(tx_id), target(tx_target), clean(_clean), yInverted(_yInverted)
+#if defined(Q_WS_X11)
+ , boundPixmap(0)
+#endif
+ {}
+
+ ~QGLTexture() {
+ if (clean) {
+ QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext());
+ QGLContext *ctx = const_cast<QGLContext *>(context);
+ Q_ASSERT(ctx);
+ bool switch_context = current != ctx && !qgl_share_reg()->checkSharing(current, ctx);
+ if (switch_context)
+ ctx->makeCurrent();
+#if defined(Q_WS_X11)
+ // Although glXReleaseTexImage is a glX call, it must be called while there
+ // is a current context - the context the pixmap was bound to a texture in.
+ // Otherwise the release doesn't do anything and you get BadDrawable errors
+ // when you come to delete the context.
+ deleteBoundPixmap();
+#endif
+ glDeleteTextures(1, &id);
+ if (switch_context && current)
+ current->makeCurrent();
}
- }
+ }
- void replaceShare(const QGLContext *oldContext, const QGLContext *newContext) {
- QGLSharingHash::iterator it = reg.begin();
- while (it != reg.end()) {
- if (it.key() == oldContext)
- reg.insert(newContext, it.value());
- else if (it.value() == oldContext)
- reg.insert(it.key(), newContext);
- ++it;
- }
- removeShare(oldContext);
- }
+ QGLContext *context;
+ GLuint id;
+ GLenum target;
+ bool clean;
+ bool yInverted; // NOTE: Y-Inverted textures are for internal use only!
+#if defined(Q_WS_X11)
+ Qt::HANDLE boundPixmap;
+ void deleteBoundPixmap(); // in qgl_x11.cpp/qgl_x11egl.cpp
+#endif
+};
- QList<const QGLContext *> shares(const QGLContext *context) {
- return reg.values(context);
- }
+class QGLTextureCache {
+public:
+ QGLTextureCache();
+ ~QGLTextureCache();
+
+ void insert(QGLContext *ctx, qint64 key, QGLTexture *texture, int cost);
+ void remove(quint64 key) { m_cache.remove(key); }
+ bool remove(QGLContext *ctx, GLuint textureId);
+ void removeContextTextures(QGLContext *ctx);
+ int size() { return m_cache.size(); }
+ void setMaxCost(int newMax) { m_cache.setMaxCost(newMax); }
+ int maxCost() {return m_cache.maxCost(); }
+ QGLTexture* getTexture(quint64 key) { return m_cache.object(key); }
+
+ static QGLTextureCache *instance();
+ static void deleteIfEmpty();
+ static void cleanupHook(qint64 cacheKey);
private:
- QGLSharingHash reg;
+ QCache<qint64, QGLTexture> m_cache;
};
-extern Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg();
#ifdef Q_WS_QWS
extern QPaintEngine* qt_qgl_paint_engine();
@@ -464,6 +498,29 @@ inline GLenum qt_gl_preferredTextureTarget()
#endif
}
+// One resource per group of shared contexts.
+class QGLContextResource : public QObject
+{
+ Q_OBJECT
+public:
+ typedef void (*FreeFunc)(void *);
+ QGLContextResource(FreeFunc f, QObject *parent = 0);
+ ~QGLContextResource();
+ // Set resource 'value' for 'key' and all its shared contexts.
+ void insert(const QGLContext *key, void *value);
+ // Return resource for 'key' or a shared context.
+ void *value(const QGLContext *key);
+ // Free resource for 'key' and all its shared contexts.
+ void remove(const QGLContext *key);
+private slots:
+ // Remove entry 'key' from cache and delete resource if there are no shared contexts.
+ void aboutToDestroyContext(const QGLContext *key);
+private:
+ typedef QHash<const QGLContext *, void *> ResourceHash;
+ ResourceHash m_resources;
+ FreeFunc free;
+};
+
QT_END_NAMESPACE
#endif // QGL_P_H
diff --git a/src/opengl/qgl_x11.cpp b/src/opengl/qgl_x11.cpp
index 631625b..43bdec7 100644
--- a/src/opengl/qgl_x11.cpp
+++ b/src/opengl/qgl_x11.cpp
@@ -52,6 +52,7 @@
#include "qdebug.h"
#include <private/qfontengine_ft_p.h>
#include <private/qt_x11_p.h>
+#include <private/qpixmap_x11_p.h>
#ifdef Q_OS_HPUX
// for GLXPBuffer
#include <private/qglpixelbuffer_p.h>
@@ -81,6 +82,25 @@ extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
#define GLX_SAMPLES_ARB 100001
#endif
+#ifndef GLX_EXT_texture_from_pixmap
+#define GLX_TEXTURE_2D_BIT_EXT 0x00000002
+#define GLX_TEXTURE_RECTANGLE_BIT_EXT 0x00000004
+#define GLX_BIND_TO_TEXTURE_RGB_EXT 0x20D0
+#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1
+#define GLX_BIND_TO_MIPMAP_TEXTURE_EXT 0x20D2
+#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3
+#define GLX_Y_INVERTED_EXT 0x20D4
+#define GLX_TEXTURE_FORMAT_EXT 0x20D5
+#define GLX_TEXTURE_TARGET_EXT 0x20D6
+#define GLX_MIPMAP_TEXTURE_EXT 0x20D7
+#define GLX_TEXTURE_FORMAT_NONE_EXT 0x20D8
+#define GLX_TEXTURE_FORMAT_RGB_EXT 0x20D9
+#define GLX_TEXTURE_FORMAT_RGBA_EXT 0x20DA
+#define GLX_TEXTURE_2D_EXT 0x20DC
+#define GLX_TEXTURE_RECTANGLE_EXT 0x20DD
+#define GLX_FRONT_LEFT_EXT 0x20DE
+#endif
+
/*
The qt_gl_choose_cmap function is internal and used by QGLWidget::setContext()
and GLX (not Windows). If the application can't find any sharable
@@ -1516,4 +1536,124 @@ void QGLExtensions::init()
}
}
+
+typedef void (*qt_glXBindTexImageEXT)(Display*, GLXDrawable, int, const int*);
+typedef void (*qt_glXReleaseTexImageEXT)(Display*, GLXDrawable, int);
+static qt_glXBindTexImageEXT glXBindTexImageEXT = 0;
+static qt_glXReleaseTexImageEXT glXReleaseTexImageEXT = 0;
+static bool qt_resolved_texture_from_pixmap = false;
+
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool canInvert)
+{
+ Q_Q(QGLContext);
+
+ if (pm->data_ptr()->classId() != QPixmapData::X11Class)
+ return 0;
+ QX11PixmapData *pixmapData = static_cast<QX11PixmapData*>(pm->data_ptr());
+ const QX11Info *x11Info = qt_x11Info(pm);
+
+
+ // Check to see if we have NPOT texture support
+ // TODO: Use GLX_TEXTURE_RECTANGLE_EXT texture target on systems without npot textures
+ if ( !(QGLExtensions::glExtensions & QGLExtensions::NPOTTextures) &&
+ !(QGLFormat::openGLVersionFlags() & QGLFormat::OpenGL_Version_2_0))
+ return 0;
+
+ if (!qt_resolved_texture_from_pixmap) {
+ qt_resolved_texture_from_pixmap = true;
+
+ QString glxExt = QLatin1String(glXGetClientString(QX11Info::display(), GLX_EXTENSIONS));
+ if (glxExt.contains(QLatin1String("GLX_EXT_texture_from_pixmap"))) {
+#if defined(Q_OS_LINUX) || defined(Q_OS_BSD4)
+ void *handle = dlopen(NULL, RTLD_LAZY);
+ if (handle) {
+ glXBindTexImageEXT = (qt_glXBindTexImageEXT) dlsym(handle, "glXBindTexImageEXT");
+ glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) dlsym(handle, "glXReleaseTexImageEXT");
+ dlclose(handle);
+ }
+ if (!glXBindTexImageEXT)
+#endif
+ {
+ extern const QString qt_gl_library_name();
+ QLibrary lib(qt_gl_library_name());
+ glXBindTexImageEXT = (qt_glXBindTexImageEXT) lib.resolve("glXBindTexImageEXT");
+ glXReleaseTexImageEXT = (qt_glXReleaseTexImageEXT) lib.resolve("glXReleaseTexImageEXT");
+ }
+ }
+ }
+
+ if (!glXBindTexImageEXT)
+ return 0;
+
+#if !defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
+ return 0;
+#else
+ GLXFBConfig *configList = 0;
+ GLXFBConfig glxPixmapConfig;
+ int configCount = 0;
+ bool hasAlpha = pixmapData->hasAlphaChannel();
+
+ int configAttribs[] = {
+ hasAlpha ? GLX_BIND_TO_TEXTURE_RGBA_EXT : GLX_BIND_TO_TEXTURE_RGB_EXT, True,
+ GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
+ GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
+ // QGLContext::bindTexture() can't return an inverted texture, but QPainter::drawPixmap() can:
+ GLX_Y_INVERTED_EXT, canInvert ? GLX_DONT_CARE : False,
+ XNone
+// GLX_BIND_TO_MIPMAP_TEXTURE_EXT, False,
+// GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_1D_BIT_EXT or GLX_TEXTURE_2D_BIT_EXT or GLX_TEXTURE_RECTANGLE_BIT_EXT
+ };
+ configList = glXChooseFBConfig(x11Info->display(), x11Info->screen(), configAttribs, &configCount);
+ if (!configList)
+ return 0;
+ glxPixmapConfig = configList[0];
+ XFree(configList);
+
+ GLXPixmap glxPixmap;
+ int pixmapAttribs[] = {
+ GLX_TEXTURE_FORMAT_EXT, hasAlpha ? GLX_TEXTURE_FORMAT_RGBA_EXT : GLX_TEXTURE_FORMAT_RGB_EXT,
+ GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
+ GLX_MIPMAP_TEXTURE_EXT, False,
+ XNone
+// GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGBA_EXT or GLX_TEXTURE_FORMAT_RGB_EXT or GLX_TEXTURE_FORMAT_NONE_EXT,
+// GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT or GLX_TEXTURE_RECTANGLE_EXT,
+// GLX_MIPMAP_TEXTURE_EXT, True or False,
+ };
+
+ // Wrap the X Pixmap into a GLXPixmap:
+ glxPixmap = glXCreatePixmap(x11Info->display(), glxPixmapConfig, pixmapData->handle(), pixmapAttribs);
+
+ if (!glxPixmap)
+ return 0;
+
+ int yInverted;
+ glXGetFBConfigAttrib(x11Info->display(), glxPixmapConfig, GLX_Y_INVERTED_EXT, &yInverted);
+
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ glBindTexture(GL_TEXTURE_2D, textureId);
+ glXBindTexImageEXT(x11Info->display(), glxPixmap, GLX_FRONT_LEFT_EXT, 0);
+
+ glBindTexture(GL_TEXTURE_2D, textureId);
+
+ QGLTexture *texture = new QGLTexture(q, textureId, GL_TEXTURE_2D, canInvert, yInverted);
+ texture->boundPixmap = glxPixmap;
+
+ // We assume the cost of bound pixmaps is zero
+ QGLTextureCache::instance()->insert(q, key, texture, 0);
+
+ return texture;
+#endif //!defined(GLX_VERSION_1_3) || defined(Q_OS_HPUX)
+}
+
+void QGLTexture::deleteBoundPixmap()
+{
+ if (boundPixmap) {
+ glXReleaseTexImageEXT(QX11Info::display(), boundPixmap, GLX_FRONT_LEFT_EXT);
+ glXDestroyPixmap(QX11Info::display(), boundPixmap);
+ boundPixmap = 0;
+ }
+}
+
+
QT_END_NAMESPACE
diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp
index 9db3a30..99b026d 100644
--- a/src/opengl/qgl_x11egl.cpp
+++ b/src/opengl/qgl_x11egl.cpp
@@ -469,4 +469,15 @@ void QGLWidgetPrivate::recreateEglSurface(bool force)
}
}
+QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmap *pm, const qint64 key, bool canInvert)
+{
+ // TODO
+ return 0;
+}
+
+void QGLTexture::deleteBoundPixmap()
+{
+ //TODO
+}
+
QT_END_NAMESPACE
diff --git a/src/opengl/qglextensions_p.h b/src/opengl/qglextensions_p.h
index 3bb42c8..4f15197 100644
--- a/src/opengl/qglextensions_p.h
+++ b/src/opengl/qglextensions_p.h
@@ -535,7 +535,7 @@ struct QGLExtensionFuncs
#endif
#ifndef GL_MAX_SAMPLES_EXT
-#define GL_MAX_SAMPLES_EXT 0x8D5
+#define GL_MAX_SAMPLES_EXT 0x8D57
#endif
#ifndef GL_DRAW_FRAMEBUFFER_EXT
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp
index 7514743..1e72fd8 100644
--- a/src/opengl/qglpixmapfilter.cpp
+++ b/src/opengl/qglpixmapfilter.cpp
@@ -40,23 +40,27 @@
****************************************************************************/
#include "private/qpixmapfilter_p.h"
+#include "private/qpixmapdata_gl_p.h"
+#include "private/qpaintengineex_opengl2_p.h"
+#include "private/qglengineshadermanager_p.h"
#include "qglpixmapfilter_p.h"
#include "qgraphicssystem_gl_p.h"
#include "qpaintengine_opengl_p.h"
+#include "qcache.h"
-#include "qglpixelbuffer.h"
+#include "qglframebufferobject.h"
#include "qglshaderprogram.h"
#include "qgl_p.h"
#include "private/qapplication_p.h"
+#include "private/qmath_p.h"
QT_BEGIN_NAMESPACE
-
void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const
{
- const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true);
+ const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, true, false);
}
void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const
@@ -97,6 +101,27 @@ private:
mutable int m_kernelHeight;
};
+class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter>
+{
+public:
+ QGLPixmapBlurFilter();
+ ~QGLPixmapBlurFilter();
+
+ void setUniforms(QGLShaderProgram *program);
+
+protected:
+ bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
+
+private:
+ static QByteArray generateBlurShader(int radius, bool gaussianBlur);
+
+ mutable QGLShader *m_shader;
+
+ mutable QSize m_textureSize;
+
+ QGLShaderProgram *m_program;
+};
+
extern QGLWidget *qt_gl_share_widget();
QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const
@@ -105,6 +130,8 @@ QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const
case QPixmapFilter::ColorizeFilter:
return new QGLPixmapColorizeFilter;
+ case QPixmapFilter::BlurFilter:
+ return new QGLPixmapBlurFilter;
case QPixmapFilter::ConvolutionFilter:
return new QGLPixmapConvolutionFilter;
@@ -281,4 +308,250 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const
return true;
}
+QGLPixmapBlurFilter::QGLPixmapBlurFilter()
+{
+}
+
+QGLPixmapBlurFilter::~QGLPixmapBlurFilter()
+{
+}
+
+bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
+{
+ QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
+ filter->setSource(generateBlurShader(radius(), quality() == Qt::SmoothTransformation));
+
+ QGLFramebufferObjectFormat format;
+ format.setInternalFormat(src.hasAlphaChannel() ? GL_RGBA : GL_RGB);
+ QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format);
+
+ if (!fbo)
+ return false;
+
+ glBindTexture(GL_TEXTURE_2D, fbo->texture());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+
+ engine->syncState();
+ painter->save();
+
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+
+ // prepare for updateUniforms
+ m_textureSize = src.size();
+
+ // first pass, to fbo
+ fbo->bind();
+ if (src.hasAlphaChannel()) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ filter->setOnPainter(painter);
+
+ QTransform transform = engine->state()->matrix;
+ if (!transform.isIdentity()) {
+ engine->state()->matrix = QTransform();
+ engine->transformChanged();
+ }
+
+ engine->drawPixmap(src.rect().translated(0, painter->device()->height() - fbo->height()),
+ src, src.rect());
+
+ if (!transform.isIdentity()) {
+ engine->state()->matrix = transform;
+ engine->transformChanged();
+ }
+
+ fbo->release();
+
+ // second pass, to widget
+ m_program->setUniformValue("delta", 0.0, 1.0);
+ engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
+ filter->removeFromPainter(painter);
+
+ painter->restore();
+
+ qgl_fbo_pool()->release(fbo);
+
+ return true;
+}
+
+void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program)
+{
+ program->setUniformValue("invTextureSize", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height());
+ program->setUniformValue("delta", 1.0, 0.0);
+
+ m_program = program;
+}
+
+static inline qreal gaussian(qreal dx, qreal sigma)
+{
+ return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma);
+}
+
+QByteArray QGLPixmapBlurFilter::generateBlurShader(int radius, bool gaussianBlur)
+{
+ Q_ASSERT(radius >= 1);
+
+ QByteArray source;
+ source.reserve(1000);
+
+ source.append("uniform highp vec2 invTextureSize;\n");
+
+ bool separateXY = true;
+ bool clip = false;
+
+ if (separateXY) {
+ source.append("uniform highp vec2 delta;\n");
+
+ if (clip)
+ source.append("uniform highp vec2 clip;\n");
+ } else if (clip) {
+ source.append("uniform highp vec4 clip;\n");
+ }
+
+ source.append("mediump vec4 customShader(sampler2D src, vec2 srcCoords) {\n");
+
+ QVector<qreal> sampleOffsets;
+ QVector<qreal> weights;
+
+ if (gaussianBlur) {
+ QVector<qreal> gaussianComponents;
+
+ qreal sigma = radius / 1.65;
+
+ qreal sum = 0;
+ for (int i = -radius; i <= radius; ++i) {
+ float value = gaussian(i, sigma);
+ gaussianComponents << value;
+ sum += value;
+ }
+
+ // normalize
+ for (int i = 0; i < gaussianComponents.size(); ++i)
+ gaussianComponents[i] /= sum;
+
+ for (int i = 0; i < gaussianComponents.size() - 1; i += 2) {
+ qreal weight = gaussianComponents.at(i) + gaussianComponents.at(i + 1);
+ qreal offset = i - radius + gaussianComponents.at(i + 1) / weight;
+
+ sampleOffsets << offset;
+ weights << weight;
+ }
+
+ // odd size ?
+ if (gaussianComponents.size() & 1) {
+ sampleOffsets << radius;
+ weights << gaussianComponents.last();
+ }
+ } else {
+ for (int i = 0; i < radius; ++i) {
+ sampleOffsets << 2 * i - radius + 0.5;
+ weights << qreal(1);
+ }
+ sampleOffsets << radius;
+ weights << qreal(0.5);
+ }
+
+ int currentVariable = 1;
+ source.append(" mediump vec4 sample = vec4(0.0);\n");
+ source.append(" mediump vec2 coord;\n");
+
+ qreal weightSum = 0;
+ if (separateXY) {
+ source.append(" mediump float c;\n");
+ for (int i = 0; i < sampleOffsets.size(); ++i) {
+ qreal delta = sampleOffsets.at(i);
+
+ ++currentVariable;
+
+ QByteArray coordinate = "srcCoords";
+ if (delta != qreal(0)) {
+ coordinate.append(" + invTextureSize * delta * float(");
+ coordinate.append(QByteArray::number(delta));
+ coordinate.append(")");
+ }
+
+ source.append(" coord = ");
+ source.append(coordinate);
+ source.append(";\n");
+
+ if (clip) {
+ source.append(" c = dot(coord, delta);\n");
+ source.append(" if (c > clip.x && c < clip.y)\n ");
+ }
+
+ source.append(" sample += texture2D(src, coord)");
+
+ weightSum += weights.at(i);
+ if (weights.at(i) != qreal(1)) {
+ source.append(" * float(");
+ source.append(QByteArray::number(weights.at(i)));
+ source.append(");\n");
+ } else {
+ source.append(";\n");
+ }
+ }
+ } else {
+ for (int y = 0; y < sampleOffsets.size(); ++y) {
+ for (int x = 0; x < sampleOffsets.size(); ++x) {
+ QByteArray coordinate = "srcCoords";
+
+ qreal dx = sampleOffsets.at(x);
+ qreal dy = sampleOffsets.at(y);
+
+ if (dx != qreal(0) || dy != qreal(0)) {
+ coordinate.append(" + invTextureSize * vec2(float(");
+ coordinate.append(QByteArray::number(dx));
+ coordinate.append("), float(");
+ coordinate.append(QByteArray::number(dy));
+ coordinate.append("))");
+ }
+
+ source.append(" coord = ");
+ source.append(coordinate);
+ source.append(";\n");
+
+ if (clip)
+ source.append(" if (coord.x > clip.x && coord.x < clip.y && coord.y > clip.z && coord.y < clip.w)\n ");
+
+ source.append(" sample += texture2D(src, coord)");
+
+ ++currentVariable;
+
+ weightSum += weights.at(x) * weights.at(y);
+ if ((weights.at(x) != qreal(1) || weights.at(y) != qreal(1))) {
+ source.append(" * float(");
+ source.append(QByteArray::number(weights.at(x) * weights.at(y)));
+ source.append(");\n");
+ } else {
+ source.append(";\n");
+ }
+ }
+ }
+ }
+
+ source.append(" return ");
+ if (!gaussianBlur) {
+ source.append("float(");
+ if (separateXY)
+ source.append(QByteArray::number(1 / weightSum));
+ else
+ source.append(QByteArray::number(1 / weightSum));
+ source.append(") * ");
+ }
+ source.append("sample;\n");
+ source.append("}\n");
+
+ return source;
+}
+
QT_END_NAMESPACE
diff --git a/src/opengl/qgraphicsshadereffect.cpp b/src/opengl/qgraphicsshadereffect.cpp
new file mode 100644
index 0000000..d4f5fa0
--- /dev/null
+++ b/src/opengl/qgraphicsshadereffect.cpp
@@ -0,0 +1,327 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qgraphicsshadereffect.h"
+#if !defined(QT_OPENGL_ES_1) && !defined(QT_OPENGL_ES_1_CL)
+#include "qglshaderprogram.h"
+#include "gl2paintengineex/qglcustomshaderstage_p.h"
+#define QGL_HAVE_CUSTOM_SHADERS 1
+#endif
+#include <QtGui/qpainter.h>
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/private/qgraphicseffect_p.h>
+
+QT_BEGIN_NAMESPACE
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+/*!
+ \class QGraphicsShaderEffect
+ \brief The QGraphicsShaderEffect class is the base class for creating
+ custom GLSL shader effects in a QGraphicsScene.
+ \since 4.6
+ \ingroup multimedia
+ \ingroup graphicsview-api
+
+ The specific effect is defined by a fragment of GLSL source code
+ supplied to setPixelShaderFragment(). This source code must define a
+ function called \c{srcPixel()} that returns the source pixel value
+ to use in the paint engine's shader program. The shader fragment
+ is linked with the regular shader code used by the GL2 paint engine
+ to construct a complete QGLShaderProgram.
+
+ The following example shader converts the incoming pixmap to
+ grayscale and then applies a colorize operation using the
+ \c effectColor value:
+
+ \code
+ static char const colorizeShaderCode[] =
+ "varying highp vec2 textureCoords;\n"
+ "uniform sampler2D imageTexture;\n"
+ "uniform lowp vec4 effectColor;\n"
+ "lowp vec4 srcPixel() {\n"
+ " vec4 src = texture2D(imageTexture, textureCoords);\n"
+ " float gray = dot(src.rgb, vec3(0.212671, 0.715160, 0.072169));\n"
+ " vec4 colorize = 1.0-((1.0-gray)*(1.0-effectColor));\n"
+ " return vec4(colorize.rgb, src.a);\n"
+ "}";
+ \endcode
+
+ To use this shader code, it is necessary to define a subclass
+ of QGraphicsShaderEffect as follows:
+
+ \code
+ class ColorizeEffect : public QGraphicsShaderEffect
+ {
+ Q_OBJECT
+ public:
+ ColorizeEffect(QObject *parent = 0)
+ : QGraphicsShaderEffect(parent), color(Qt::black)
+ {
+ setPixelShaderFragment(colorizeShaderCode);
+ }
+
+ QColor effectColor() const { return color; }
+ void setEffectColor(const QColor& c)
+ {
+ color = c;
+ setUniformsDirty();
+ }
+
+ protected:
+ void setUniforms(QGLShaderProgram *program)
+ {
+ program->setUniformValue("effectColor", color);
+ }
+
+ private:
+ QColor color;
+ };
+ \endcode
+
+ The setUniforms() function is called when the effect is about
+ to be used for drawing to give the subclass the opportunity to
+ set effect-specific uniform variables.
+
+ QGraphicsShaderEffect is only supported when the GL2 paint engine
+ is in use. When any other paint engine is in use (GL1, raster, etc),
+ the drawItem() method will draw its item argument directly with
+ no effect applied.
+
+ \sa QGrapicsEffect
+*/
+
+static const char qglslDefaultImageFragmentShader[] = "\
+ varying highp vec2 textureCoords; \
+ uniform sampler2D imageTexture; \
+ lowp vec4 srcPixel() { \
+ return texture2D(imageTexture, textureCoords); \
+ }\n";
+
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+
+class QGLCustomShaderEffectStage : public QGLCustomShaderStage
+{
+public:
+ QGLCustomShaderEffectStage
+ (QGraphicsShaderEffect *e, const QByteArray& source)
+ : QGLCustomShaderStage(),
+ effect(e)
+ {
+ setSource(source);
+ }
+
+ void setUniforms(QGLShaderProgram *program);
+
+ QGraphicsShaderEffect *effect;
+};
+
+void QGLCustomShaderEffectStage::setUniforms(QGLShaderProgram *program)
+{
+ effect->setUniforms(program);
+}
+
+#endif
+
+class QGraphicsShaderEffectPrivate : public QGraphicsEffectPrivate
+{
+ Q_DECLARE_PUBLIC(QGraphicsShaderEffect)
+public:
+ QGraphicsShaderEffectPrivate()
+ : pixelShaderFragment(qglslDefaultImageFragmentShader)
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ , customShaderStage(0)
+#endif
+ {
+ }
+
+ QByteArray pixelShaderFragment;
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ QGLCustomShaderEffectStage *customShaderStage;
+#endif
+};
+
+/*!
+ Constructs a shader effect and attaches it to \a parent.
+*/
+QGraphicsShaderEffect::QGraphicsShaderEffect()
+ : QGraphicsEffect(*new QGraphicsShaderEffectPrivate())
+{
+}
+
+/*!
+ Destroys this shader effect.
+*/
+QGraphicsShaderEffect::~QGraphicsShaderEffect()
+{
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ Q_D(QGraphicsShaderEffect);
+ delete d->customShaderStage;
+#endif
+}
+
+/*!
+ Returns the source code for the pixel shader fragment for
+ this shader effect. The default is a shader that copies
+ its incoming pixmap directly to the output with no effect
+ applied.
+
+ \sa setPixelShaderFragment()
+*/
+QByteArray QGraphicsShaderEffect::pixelShaderFragment() const
+{
+ Q_D(const QGraphicsShaderEffect);
+ return d->pixelShaderFragment;
+}
+
+/*!
+ Sets the source code for the pixel shader fragment for
+ this shader effect to \a code.
+
+ The \a code must define a GLSL function called \c{srcPixel()}
+ that returns the source pixel value to use in the paint engine's
+ shader program. The following is the default pixel shader fragment,
+ which draws a pixmap with no effect applied:
+
+ \code
+ varying highp vec2 textureCoords;
+ uniform sampler2D imageTexture;
+ lowp vec4 srcPixel() {
+ return texture2D(imageTexture, textureCoords);
+ }
+ \endcode
+
+ \sa pixelShaderFragment(), setUniforms()
+*/
+void QGraphicsShaderEffect::setPixelShaderFragment(const QByteArray& code)
+{
+ Q_D(QGraphicsShaderEffect);
+ if (d->pixelShaderFragment != code) {
+ d->pixelShaderFragment = code;
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ delete d->customShaderStage;
+ d->customShaderStage = 0;
+#endif
+ }
+}
+
+/*!
+ \reimp
+*/
+void QGraphicsShaderEffect::draw(QPainter *painter)
+{
+ Q_D(QGraphicsShaderEffect);
+
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ // Find the item's bounds in device coordinates.
+ QTransform itemToPixmapTransform(painter->worldTransform());
+ QRectF deviceBounds = itemToPixmapTransform.mapRect(sourceBoundingRect());
+ QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
+ if (deviceRect.isEmpty())
+ return;
+
+ if (deviceRect.x() != 0 || deviceRect.y() != 0)
+ itemToPixmapTransform *= QTransform::fromTranslate(-deviceRect.x(), -deviceRect.y());
+
+ QPixmap pixmap(deviceRect.size());
+ if (!d->source->drawIntoPixmap(&pixmap, itemToPixmapTransform))
+ return;
+
+ // Set the custom shader on the paint engine. The setOnPainter()
+ // call may fail if the paint engine is not GL2. In that case,
+ // we fall through to drawing the pixmap normally.
+ if (!d->customShaderStage) {
+ d->customShaderStage = new QGLCustomShaderEffectStage
+ (this, d->pixelShaderFragment);
+ }
+ bool usingShader = d->customShaderStage->setOnPainter(painter);
+
+ // Draw using an untransformed painter.
+ QTransform restoreTransform = painter->worldTransform();
+ painter->setWorldTransform(QTransform());
+ painter->drawPixmap(deviceRect.topLeft(), pixmap);
+ painter->setWorldTransform(restoreTransform);
+
+ // Remove the custom shader to return to normal painting operations.
+ if (usingShader)
+ d->customShaderStage->removeFromPainter(painter);
+#else
+ drawSource(painter);
+#endif
+}
+
+/*!
+ Sets the custom uniform variables on this shader effect to
+ be dirty. The setUniforms() function will be called the next
+ time the shader program corresponding to this effect is used.
+
+ This function is typically called by subclasses when an
+ effect-specific parameter is changed by the application.
+
+ \sa setUniforms()
+*/
+void QGraphicsShaderEffect::setUniformsDirty()
+{
+#ifdef QGL_HAVE_CUSTOM_SHADERS
+ Q_D(QGraphicsShaderEffect);
+ if (d->customShaderStage)
+ d->customShaderStage->setUniformsDirty();
+#endif
+}
+
+/*!
+ Sets custom uniform variables on the current GL context when
+ \a program is about to be used by the paint engine.
+
+ This function should be overridden if the shader set with
+ setPixelShaderFragment() has additional parameters beyond
+ those that the paint engine normally sets itself.
+
+ \sa setUniformsDirty()
+*/
+void QGraphicsShaderEffect::setUniforms(QGLShaderProgram *program)
+{
+ Q_UNUSED(program);
+}
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
diff --git a/src/opengl/qgraphicsshadereffect.h b/src/opengl/qgraphicsshadereffect.h
new file mode 100644
index 0000000..032a233
--- /dev/null
+++ b/src/opengl/qgraphicsshadereffect.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGRAPHICSSHADEREFFECT_H
+#define QGRAPHICSSHADEREFFECT_H
+
+#include <QtGui/qgraphicseffect.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW
+
+class QGLShaderProgram;
+class QGLCustomShaderEffectStage;
+class QGraphicsShaderEffectPrivate;
+
+class Q_OPENGL_EXPORT QGraphicsShaderEffect : public QGraphicsEffect
+{
+ Q_OBJECT
+public:
+ QGraphicsShaderEffect();
+ virtual ~QGraphicsShaderEffect();
+
+ QByteArray pixelShaderFragment() const;
+ void setPixelShaderFragment(const QByteArray& code);
+
+protected:
+ void draw(QPainter *painter);
+ void setUniformsDirty();
+ virtual void setUniforms(QGLShaderProgram *program);
+
+private:
+ Q_DECLARE_PRIVATE(QGraphicsShaderEffect)
+ Q_DISABLE_COPY(QGraphicsShaderEffect)
+
+ friend class QGLCustomShaderEffectStage;
+};
+
+Q_DECLARE_METATYPE(QGraphicsShaderEffect *)
+
+#endif // QT_NO_GRAPHICSVIEW
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QGRAPHICSSHADEREFFECT_H
diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp
index f0c7e20..c0eed4d 100644
--- a/src/opengl/qpixmapdata_gl.cpp
+++ b/src/opengl/qpixmapdata_gl.cpp
@@ -55,6 +55,85 @@ QT_BEGIN_NAMESPACE
extern QGLWidget* qt_gl_share_widget();
+/*!
+ \class QGLFramebufferObjectPool
+ \since 4.6
+
+ \brief The QGLFramebufferObject class provides a pool of framebuffer
+ objects for offscreen rendering purposes.
+
+ When requesting an FBO of a given size and format, an FBO of the same
+ format and a size at least as big as the requested size will be returned.
+
+ \internal
+*/
+
+static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo)
+{
+ return qAbs(size.width() * size.height() - fbo->width() * fbo->height());
+}
+
+QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat)
+{
+ QGLFramebufferObject *chosen = 0;
+ QGLFramebufferObject *candidate = 0;
+ for (int i = 0; !chosen && i < m_fbos.size(); ++i) {
+ QGLFramebufferObject *fbo = m_fbos.at(i);
+
+ QGLFramebufferObjectFormat format = fbo->format();
+ if (format.samples() == requestFormat.samples()
+ && format.attachment() == requestFormat.attachment()
+ && format.textureTarget() == requestFormat.textureTarget()
+ && format.internalFormat() == requestFormat.internalFormat())
+ {
+ // choose the fbo with a matching format and the closest size
+ if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo))
+ candidate = fbo;
+ }
+
+ if (candidate) {
+ m_fbos.removeOne(candidate);
+
+ const QSize fboSize = candidate->size();
+ QSize sz = fboSize;
+
+ if (sz.width() < requestSize.width())
+ sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5)));
+ if (sz.height() < requestSize.height())
+ sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5)));
+
+ // wasting too much space?
+ if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 2.5)
+ sz = requestSize;
+
+ if (sz != fboSize) {
+ delete candidate;
+ qDebug() << "Resizing fbo in pool:" << sz;
+ candidate = new QGLFramebufferObject(sz, requestFormat);
+ }
+
+ chosen = candidate;
+ }
+ }
+
+ if (!chosen) {
+ qDebug() << "Creating new fbo in pool:" << requestSize;
+ chosen = new QGLFramebufferObject(requestSize, requestFormat);
+ }
+
+ if (!chosen->isValid()) {
+ delete chosen;
+ chosen = 0;
+ }
+
+ return chosen;
+}
+
+void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo)
+{
+ m_fbos << fbo;
+}
+
class QGLShareContextScope
{
public:
@@ -97,7 +176,6 @@ static int qt_gl_pixmap_serial = 0;
QGLPixmapData::QGLPixmapData(PixelType type)
: QPixmapData(type, OpenGLClass)
, m_renderFbo(0)
- , m_textureId(0)
, m_engine(0)
, m_ctx(0)
, m_dirty(false)
@@ -113,9 +191,9 @@ QGLPixmapData::~QGLPixmapData()
if (!shareWidget)
return;
- if (m_textureId) {
+ if (m_texture.id) {
QGLShareContextScope ctx(shareWidget->context());
- glDeleteTextures(1, &m_textureId);
+ glDeleteTextures(1, &m_texture.id);
}
}
@@ -148,10 +226,10 @@ void QGLPixmapData::resize(int width, int height)
is_null = (w <= 0 || h <= 0);
d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
- if (m_textureId) {
+ if (m_texture.id) {
QGLShareContextScope ctx(qt_gl_share_widget()->context());
- glDeleteTextures(1, &m_textureId);
- m_textureId = 0;
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
}
m_source = QImage();
@@ -172,9 +250,9 @@ void QGLPixmapData::ensureCreated() const
const GLenum format = qt_gl_preferredTextureFormat();
const GLenum target = GL_TEXTURE_2D;
- if (!m_textureId) {
- glGenTextures(1, &m_textureId);
- glBindTexture(target, m_textureId);
+ if (!m_texture.id) {
+ glGenTextures(1, &m_texture.id);
+ glBindTexture(target, m_texture.id);
GLenum format = m_hasAlpha ? GL_RGBA : GL_RGB;
glTexImage2D(target, 0, format, w, h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, 0);
@@ -185,13 +263,15 @@ void QGLPixmapData::ensureCreated() const
if (!m_source.isNull()) {
const QImage tx = ctx->d_func()->convertToGLFormat(m_source, true, format);
- glBindTexture(target, m_textureId);
+ glBindTexture(target, m_texture.id);
glTexSubImage2D(target, 0, 0, 0, w, h, format,
GL_UNSIGNED_BYTE, tx.bits());
if (useFramebufferObjects())
m_source = QImage();
}
+
+ m_texture.clean = false;
}
QGLFramebufferObject *QGLPixmapData::fbo() const
@@ -223,10 +303,10 @@ void QGLPixmapData::fromImage(const QImage &image,
is_null = (w <= 0 || h <= 0);
d = pixelType() == QPixmapData::PixmapType ? 32 : 1;
- if (m_textureId) {
+ if (m_texture.id) {
QGLShareContextScope ctx(qt_gl_share_widget()->context());
- glDeleteTextures(1, &m_textureId);
- m_textureId = 0;
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
}
}
@@ -256,9 +336,9 @@ void QGLPixmapData::fill(const QColor &color)
bool hasAlpha = color.alpha() != 255;
if (hasAlpha && !m_hasAlpha) {
- if (m_textureId) {
- glDeleteTextures(1, &m_textureId);
- m_textureId = 0;
+ if (m_texture.id) {
+ glDeleteTextures(1, &m_texture.id);
+ m_texture.id = 0;
m_dirty = true;
}
m_hasAlpha = color.alpha() != 255;
@@ -303,6 +383,8 @@ QImage QGLPixmapData::fillImage(const QColor &color) const
return img;
}
+extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
+
QImage QGLPixmapData::toImage() const
{
if (!isValid())
@@ -319,8 +401,7 @@ QImage QGLPixmapData::toImage() const
}
QGLShareContextScope ctx(qt_gl_share_widget()->context());
- extern QImage qt_gl_read_texture(const QSize &size, bool alpha_format, bool include_alpha);
- glBindTexture(GL_TEXTURE_2D, m_textureId);
+ glBindTexture(GL_TEXTURE_2D, m_texture.id);
return qt_gl_read_texture(QSize(w, h), true, true);
}
@@ -330,8 +411,11 @@ struct TextureBuffer
QGL2PaintEngineEx *engine;
};
-static QVector<TextureBuffer> textureBufferStack;
-static int currentTextureBuffer = 0;
+Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool)
+QGLFramebufferObjectPool* qgl_fbo_pool()
+{
+ return _qgl_fbo_pool();
+}
void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
{
@@ -350,7 +434,7 @@ void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const
glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
- GL_TEXTURE_2D, m_textureId, 0);
+ GL_TEXTURE_2D, m_texture.id, 0);
const int x0 = 0;
const int x1 = w;
@@ -378,7 +462,8 @@ void QGLPixmapData::swapBuffers()
copyBackFromRenderFbo(false);
m_renderFbo->release();
- --currentTextureBuffer;
+ qgl_fbo_pool()->release(m_renderFbo);
+ delete m_engine;
m_renderFbo = 0;
m_engine = 0;
@@ -396,19 +481,6 @@ void QGLPixmapData::doneCurrent()
m_renderFbo->release();
}
-static TextureBuffer createTextureBuffer(const QSize &size, QGL2PaintEngineEx *engine = 0)
-{
- TextureBuffer buffer;
- QGLFramebufferObjectFormat fmt;
- fmt.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
- fmt.setSamples(4);
-
- buffer.fbo = new QGLFramebufferObject(size, fmt);
- buffer.engine = engine ? engine : new QGL2PaintEngineEx;
-
- return buffer;
-}
-
bool QGLPixmapData::useFramebufferObjects()
{
return QGLFramebufferObject::hasOpenGLFramebufferObjects()
@@ -431,33 +503,15 @@ QPaintEngine* QGLPixmapData::paintEngine() const
qt_gl_share_widget()->makeCurrent();
QGLShareContextScope ctx(qt_gl_share_widget()->context());
- if (textureBufferStack.size() <= currentTextureBuffer) {
- textureBufferStack << createTextureBuffer(size());
- } else {
- QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size();
- if (sz.width() < w || sz.height() < h) {
- if (sz.width() < w)
- sz.setWidth(qMax(w, qRound(sz.width() * 1.5)));
- if (sz.height() < h)
- sz.setHeight(qMax(h, qRound(sz.height() * 1.5)));
-
- // wasting too much space?
- if (sz.width() * sz.height() > w * h * 2.5)
- sz = QSize(w, h);
-
- delete textureBufferStack.at(currentTextureBuffer).fbo;
- textureBufferStack[currentTextureBuffer] =
- createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine);
- qDebug() << "Creating new pixmap texture buffer:" << sz;
- }
- }
-
- if (textureBufferStack.at(currentTextureBuffer).fbo->isValid()) {
- m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo;
- m_engine = textureBufferStack.at(currentTextureBuffer).engine;
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(4);
+ format.setInternalFormat(m_hasAlpha ? GL_RGBA : GL_RGB);
- ++currentTextureBuffer;
+ m_renderFbo = qgl_fbo_pool()->acquire(size(), format);
+ if (m_renderFbo) {
+ m_engine = new QGL2PaintEngineEx;
return m_engine;
}
@@ -488,7 +542,7 @@ GLuint QGLPixmapData::bind(bool copyBack) const
ensureCreated();
}
- GLuint id = m_textureId;
+ GLuint id = m_texture.id;
glBindTexture(GL_TEXTURE_2D, id);
return id;
}
@@ -496,7 +550,12 @@ GLuint QGLPixmapData::bind(bool copyBack) const
GLuint QGLPixmapData::textureId() const
{
ensureCreated();
- return m_textureId;
+ return m_texture.id;
+}
+
+QGLTexture* QGLPixmapData::texture() const
+{
+ return &m_texture;
}
extern int qt_defaultDpiX();
diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h
index a6aa22d..14fb072 100644
--- a/src/opengl/qpixmapdata_gl_p.h
+++ b/src/opengl/qpixmapdata_gl_p.h
@@ -53,6 +53,7 @@
// We mean it.
//
+#include "qgl_p.h"
#include "qgl.h"
#include "private/qpixmapdata_p.h"
@@ -61,6 +62,19 @@ QT_BEGIN_NAMESPACE
class QPaintEngine;
class QGLFramebufferObject;
+class QGLFramebufferObjectFormat;
+
+class QGLFramebufferObjectPool
+{
+public:
+ QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format);
+ void release(QGLFramebufferObject *fbo);
+
+private:
+ QList<QGLFramebufferObject *> m_fbos;
+};
+
+QGLFramebufferObjectPool* qgl_fbo_pool();
class QGLPixmapData : public QPixmapData
{
@@ -80,10 +94,11 @@ public:
void fill(const QColor &color);
bool hasAlphaChannel() const;
QImage toImage() const;
- QPaintEngine* paintEngine() const;
+ QPaintEngine *paintEngine() const;
GLuint bind(bool copyBack = true) const;
GLuint textureId() const;
+ QGLTexture *texture() const;
bool isValidContext(const QGLContext *ctx) const;
@@ -116,10 +131,10 @@ private:
QImage fillImage(const QColor &color) const;
mutable QGLFramebufferObject *m_renderFbo;
- mutable GLuint m_textureId;
mutable QPaintEngine *m_engine;
mutable QGLContext *m_ctx;
mutable QImage m_source;
+ mutable QGLTexture m_texture;
// the texture is not in sync with the source image
mutable bool m_dirty;