diff options
Diffstat (limited to 'src/opengl/gl2paintengineex')
9 files changed, 505 insertions, 117 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) |