From abef77a3fa1e4d2f6a44b323672db8ccbbd2b03f Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Wed, 2 Sep 2009 14:18:10 +0200 Subject: Split QGLEngineShaderManager into a shared and a per engine part. Both the shaders and the engine states were shared between OpenGL contexts, but the states should be only apply to one context, not a group of contexts. This commit separates the shaders and the states. Task-number: 257254 Reviewed-by: Samuel --- .../gl2paintengineex/qglcustomshaderstage.cpp | 10 +- .../gl2paintengineex/qglengineshadermanager.cpp | 447 +++++++++++---------- .../gl2paintengineex/qglengineshadermanager_p.h | 159 ++++---- .../gl2paintengineex/qpaintengineex_opengl2.cpp | 8 +- .../gl2paintengineex/qpaintengineex_opengl2_p.h | 2 + src/opengl/qgl.cpp | 8 +- src/opengl/qgl_p.h | 4 +- 7 files changed, 337 insertions(+), 301 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp index e025736..0b93320 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage.cpp @@ -86,9 +86,8 @@ bool QGLCustomShaderStage::setOnPainter(QPainter* p) return false; } - // Might as well go through the paint engine to get to the context - const QGLContext* ctx = static_cast(p->paintEngine())->context(); - d->m_manager = QGLEngineShaderManager::managerForContext(ctx); + QGL2PaintEngineEx *engine = static_cast(p->paintEngine()); + d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine); Q_ASSERT(d->m_manager); d->m_manager->setCustomStage(this); @@ -101,9 +100,8 @@ void QGLCustomShaderStage::removeFromPainter(QPainter* p) 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(p->paintEngine())->context(); - d->m_manager = QGLEngineShaderManager::managerForContext(ctx); + QGL2PaintEngineEx *engine = static_cast(p->paintEngine()); + d->m_manager = QGL2PaintEngineExPrivate::shaderManagerForEngine(engine); Q_ASSERT(d->m_manager); // Just set the stage to null, don't call removeCustomStage(). diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index d48a7b6..e7c11fd 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -49,47 +49,38 @@ QT_BEGIN_NAMESPACE -static void QGLEngineShaderManager_free(void *ptr) +static void qt_shared_shaders_free(void *data) { - delete reinterpret_cast(ptr); + delete reinterpret_cast(data); } -Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shader_managers, (QGLEngineShaderManager_free)) +Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shared_shaders, (qt_shared_shaders_free)) -QGLEngineShaderManager *QGLEngineShaderManager::managerForContext(const QGLContext *context) +QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLContext *context) { - QGLEngineShaderManager *p = reinterpret_cast(qt_shader_managers()->value(context)); + QGLEngineSharedShaders *p = reinterpret_cast(qt_shared_shaders()->value(context)); if (!p) { QGLContext *oldContext = const_cast(QGLContext::currentContext()); if (oldContext != context) const_cast(context)->makeCurrent(); - p = new QGLEngineShaderManager(const_cast(context)); - qt_shader_managers()->insert(context, p); + qt_shared_shaders()->insert(context, p = new QGLEngineSharedShaders(context)); if (oldContext && oldContext != context) oldContext->makeCurrent(); } return p; } -const char* QGLEngineShaderManager::qglEngineShaderSourceCode[] = { +const char* QGLEngineSharedShaders::qglEngineShaderSourceCode[] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0 }; -QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) - : ctx(context), - shaderProgNeedsChanging(true), - srcPixelType(Qt::NoBrush), - useGlobalOpacity(false), - maskType(NoMask), - useTextureCoords(false), - compositionMode(QPainter::CompositionMode_SourceOver), - customSrcStage(0), - blitShaderProg(0), - simpleShaderProg(0), - currentShaderProg(0) +QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) + : ctx(QGLContextPrivate::contextGroup(context)) + , blitShaderProg(0) + , simpleShaderProg(0) { memset(compiledShaders, 0, sizeof(compiledShaders)); @@ -174,7 +165,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) } // Compile up the simple shader: - simpleShaderProg = new QGLShaderProgram(ctx, this); + simpleShaderProg = new QGLShaderProgram(context, this); compileNamedShader(MainVertexShader, QGLShader::PartialVertexShader); compileNamedShader(PositionOnlyVertexShader, QGLShader::PartialVertexShader); compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader); @@ -191,7 +182,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) } // Compile the blit shader: - blitShaderProg = new QGLShaderProgram(ctx, this); + blitShaderProg = new QGLShaderProgram(context, this); compileNamedShader(MainWithTexCoordsVertexShader, QGLShader::PartialVertexShader); compileNamedShader(UntransformedPositionVertexShader, QGLShader::PartialVertexShader); compileNamedShader(MainFragmentShader, QGLShader::PartialFragmentShader); @@ -209,6 +200,145 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) } } +void QGLEngineSharedShaders::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--); + } + } + + emit shaderProgNeedsChanging(); +} + +QGLShader *QGLEngineSharedShaders::compileNamedShader(ShaderName name, QGLShader::ShaderType type) +{ + Q_ASSERT(name != CustomImageSrcFragmentShader); + if (compiledShaders[name]) + return compiledShaders[name]; + + QByteArray source = qglEngineShaderSourceCode[name]; + QGLShader *newShader = new QGLShader(type, ctx->context(), this); + newShader->compile(source); + +#if defined(QT_DEBUG) + // Name the shader for easier debugging + QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName")); + newShader->setObjectName(QLatin1String(m.valueToKey(name))); +#endif + + compiledShaders[name] = newShader; + return newShader; +} + +QGLShader *QGLEngineSharedShaders::compileCustomShader(QGLCustomShaderStage *stage, QGLShader::ShaderType type) +{ + QByteArray source = stage->source(); + source += qglslCustomSrcFragmentShader; + + QGLShader *newShader = customShaderCache.object(source); + if (newShader) + return newShader; + + newShader = new QGLShader(type, ctx->context(), this); + newShader->compile(source); + customShaderCache.insert(source, newShader); + + connect(newShader, SIGNAL(destroyed(QObject *)), + this, SLOT(shaderDestroyed(QObject *))); + +#if defined(QT_DEBUG) + // Name the shader for easier debugging + QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName")); + newShader->setObjectName(QLatin1String(m.valueToKey(CustomImageSrcFragmentShader))); +#endif + + return newShader; +} + +// The address returned here will only be valid until next time this function is called. +QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineShaderProg &prog) +{ + for (int i = 0; i < cachedPrograms.size(); ++i) { + if (cachedPrograms[i] == prog) + return &cachedPrograms[i]; + } + + cachedPrograms.append(prog); + QGLEngineShaderProg &cached = cachedPrograms.last(); + + // If the shader program's not found in the cache, create it now. + cached.program = new QGLShaderProgram(ctx->context(), this); + cached.program->addShader(cached.mainVertexShader); + cached.program->addShader(cached.positionVertexShader); + cached.program->addShader(cached.mainFragShader); + cached.program->addShader(cached.srcPixelFragShader); + cached.program->addShader(cached.maskFragShader); + cached.program->addShader(cached.compositionFragShader); + + // We have to bind the vertex attribute names before the program is linked: + cached.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); + cached.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + + cached.program->link(); + if (!cached.program->isLinked()) { + QLatin1String none("none"); + QLatin1String br("\n"); + QString error; + error = QLatin1String("Shader program failed to link,") +#if defined(QT_DEBUG) + + br + + QLatin1String(" Shaders Used:\n") + + QLatin1String(" mainVertexShader = ") + + (cached.mainVertexShader ? + cached.mainVertexShader->objectName() : none) + br + + QLatin1String(" positionVertexShader = ") + + (cached.positionVertexShader ? + cached.positionVertexShader->objectName() : none) + br + + QLatin1String(" mainFragShader = ") + + (cached.mainFragShader ? + cached.mainFragShader->objectName() : none) + br + + QLatin1String(" srcPixelFragShader = ") + + (cached.srcPixelFragShader ? + cached.srcPixelFragShader->objectName() : none) + br + + QLatin1String(" maskFragShader = ") + + (cached.maskFragShader ? + cached.maskFragShader->objectName() : none) + br + + QLatin1String(" compositionFragShader = ") + + (cached.compositionFragShader ? + cached.compositionFragShader->objectName() : none) + br +#endif + + QLatin1String(" Error Log:\n") + + QLatin1String(" ") + cached.program->log(); + qWarning() << error; + delete cached.program; + cachedPrograms.removeLast(); + return 0; + } else { + // taking the address here is safe since + // cachePrograms isn't resized anywhere else + return &cached; + } +} + +QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) + : ctx(context), + shaderProgNeedsChanging(true), + srcPixelType(Qt::NoBrush), + useGlobalOpacity(false), + maskType(NoMask), + useTextureCoords(false), + compositionMode(QPainter::CompositionMode_SourceOver), + customSrcStage(0), + currentShaderProg(0), + customShader(0) +{ + sharedShaders = QGLEngineSharedShaders::shadersForContext(context); + connect(sharedShaders, SIGNAL(shaderProgNeedsChanging()), this, SLOT(shaderProgNeedsChangingSlot())); +} + QGLEngineShaderManager::~QGLEngineShaderManager() { //### @@ -312,24 +442,8 @@ void QGLEngineShaderManager::setCompositionMode(QPainter::CompositionMode mode) 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--); - } - } - + customShader = 0; // Will be compiled from 'customSrcStage' later. shaderProgNeedsChanging = true; } @@ -337,13 +451,8 @@ 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; + customShader = 0; shaderProgNeedsChanging = true; } @@ -355,12 +464,12 @@ QGLShaderProgram* QGLEngineShaderManager::currentProgram() QGLShaderProgram* QGLEngineShaderManager::simpleProgram() { - return simpleShaderProg; + return sharedShaders->simpleProgram(); } QGLShaderProgram* QGLEngineShaderManager::blitProgram() { - return blitShaderProg; + return sharedShaders->blitProgram(); } @@ -382,26 +491,25 @@ bool QGLEngineShaderManager::useCorrectShaderProg() requiredProgram.program = 0; // Choose vertex shader main function - QGLEngineShaderManager::ShaderName mainVertexShaderName = InvalidShaderName; + QGLEngineSharedShaders::ShaderName mainVertexShaderName = QGLEngineSharedShaders::InvalidShaderName; if (useTextureCoords) - mainVertexShaderName = MainWithTexCoordsVertexShader; + mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsVertexShader; else - mainVertexShaderName = MainVertexShader; - compileNamedShader(mainVertexShaderName, QGLShader::PartialVertexShader); - requiredProgram.mainVertexShader = compiledShaders[mainVertexShaderName]; + mainVertexShaderName = QGLEngineSharedShaders::MainVertexShader; + requiredProgram.mainVertexShader = sharedShaders->compileNamedShader(mainVertexShaderName, QGLShader::PartialVertexShader); // Choose vertex shader shader position function (which typically also sets // varyings) and the source pixel (srcPixel) fragment shader function: - QGLEngineShaderManager::ShaderName positionVertexShaderName = InvalidShaderName; - QGLEngineShaderManager::ShaderName srcPixelFragShaderName = InvalidShaderName; + QGLEngineSharedShaders::ShaderName positionVertexShaderName = QGLEngineSharedShaders::InvalidShaderName; + QGLEngineSharedShaders::ShaderName srcPixelFragShaderName = QGLEngineSharedShaders::InvalidShaderName; bool isAffine = brushTransform.isAffine(); if ( (srcPixelType >= Qt::Dense1Pattern) && (srcPixelType <= Qt::DiagCrossPattern) ) { if (isAffine) - positionVertexShaderName = AffinePositionWithPatternBrushVertexShader; + positionVertexShaderName = QGLEngineSharedShaders::AffinePositionWithPatternBrushVertexShader; else - positionVertexShaderName = PositionWithPatternBrushVertexShader; + positionVertexShaderName = QGLEngineSharedShaders::PositionWithPatternBrushVertexShader; - srcPixelFragShaderName = PatternBrushSrcFragmentShader; + srcPixelFragShaderName = QGLEngineSharedShaders::PatternBrushSrcFragmentShader; } else switch (srcPixelType) { default: @@ -409,204 +517,143 @@ bool QGLEngineShaderManager::useCorrectShaderProg() qCritical("QGLEngineShaderManager::useCorrectShaderProg() - I'm scared, Qt::NoBrush style is set"); break; case QGLEngineShaderManager::ImageSrc: - srcPixelFragShaderName = useCustomSrc ? CustomImageSrcFragmentShader : ImageSrcFragmentShader; - positionVertexShaderName = PositionOnlyVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::ImageSrcFragmentShader; + positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; break; case QGLEngineShaderManager::NonPremultipliedImageSrc: - srcPixelFragShaderName = NonPremultipliedImageSrcFragmentShader; - positionVertexShaderName = PositionOnlyVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::NonPremultipliedImageSrcFragmentShader; + positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; break; case QGLEngineShaderManager::PatternSrc: - srcPixelFragShaderName = ImageSrcWithPatternFragmentShader; - positionVertexShaderName = PositionOnlyVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::ImageSrcWithPatternFragmentShader; + positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; break; case QGLEngineShaderManager::TextureSrcWithPattern: - srcPixelFragShaderName = TextureBrushSrcWithPatternFragmentShader; - positionVertexShaderName = isAffine ? AffinePositionWithTextureBrushVertexShader - : PositionWithTextureBrushVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::TextureBrushSrcWithPatternFragmentShader; + positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader; break; case Qt::SolidPattern: - srcPixelFragShaderName = SolidBrushSrcFragmentShader; - positionVertexShaderName = PositionOnlyVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::SolidBrushSrcFragmentShader; + positionVertexShaderName = QGLEngineSharedShaders::PositionOnlyVertexShader; break; case Qt::LinearGradientPattern: - srcPixelFragShaderName = LinearGradientBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? AffinePositionWithLinearGradientBrushVertexShader - : PositionWithLinearGradientBrushVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::LinearGradientBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithLinearGradientBrushVertexShader + : QGLEngineSharedShaders::PositionWithLinearGradientBrushVertexShader; break; case Qt::ConicalGradientPattern: - srcPixelFragShaderName = ConicalGradientBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? AffinePositionWithConicalGradientBrushVertexShader - : PositionWithConicalGradientBrushVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::ConicalGradientBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithConicalGradientBrushVertexShader + : QGLEngineSharedShaders::PositionWithConicalGradientBrushVertexShader; break; case Qt::RadialGradientPattern: - srcPixelFragShaderName = RadialGradientBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? AffinePositionWithRadialGradientBrushVertexShader - : PositionWithRadialGradientBrushVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::RadialGradientBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithRadialGradientBrushVertexShader + : QGLEngineSharedShaders::PositionWithRadialGradientBrushVertexShader; break; case Qt::TexturePattern: - srcPixelFragShaderName = TextureBrushSrcFragmentShader; - positionVertexShaderName = isAffine ? AffinePositionWithTextureBrushVertexShader - : PositionWithTextureBrushVertexShader; + srcPixelFragShaderName = QGLEngineSharedShaders::TextureBrushSrcFragmentShader; + positionVertexShaderName = isAffine ? QGLEngineSharedShaders::AffinePositionWithTextureBrushVertexShader + : QGLEngineSharedShaders::PositionWithTextureBrushVertexShader; break; }; - compileNamedShader(positionVertexShaderName, QGLShader::PartialVertexShader); - compileNamedShader(srcPixelFragShaderName, QGLShader::PartialFragmentShader); - requiredProgram.positionVertexShader = compiledShaders[positionVertexShaderName]; - requiredProgram.srcPixelFragShader = compiledShaders[srcPixelFragShaderName]; + requiredProgram.positionVertexShader = sharedShaders->compileNamedShader(positionVertexShaderName, QGLShader::PartialVertexShader); + if (useCustomSrc) { + if (!customShader) + customShader = sharedShaders->compileCustomShader(customSrcStage, QGLShader::PartialFragmentShader); + requiredProgram.srcPixelFragShader = customShader; + } else { + requiredProgram.srcPixelFragShader = sharedShaders->compileNamedShader(srcPixelFragShaderName, QGLShader::PartialFragmentShader); + } const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; const bool hasMask = maskType != QGLEngineShaderManager::NoMask; // Choose fragment shader main function: - QGLEngineShaderManager::ShaderName mainFragShaderName; + QGLEngineSharedShaders::ShaderName mainFragShaderName; if (hasCompose && hasMask && useGlobalOpacity) - mainFragShaderName = MainFragmentShader_CMO; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO; if (hasCompose && hasMask && !useGlobalOpacity) - mainFragShaderName = MainFragmentShader_CM; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM; if (!hasCompose && hasMask && useGlobalOpacity) - mainFragShaderName = MainFragmentShader_MO; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO; if (!hasCompose && hasMask && !useGlobalOpacity) - mainFragShaderName = MainFragmentShader_M; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M; if (hasCompose && !hasMask && useGlobalOpacity) - mainFragShaderName = MainFragmentShader_CO; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO; if (hasCompose && !hasMask && !useGlobalOpacity) - mainFragShaderName = MainFragmentShader_C; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C; if (!hasCompose && !hasMask && useGlobalOpacity) - mainFragShaderName = MainFragmentShader_O; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O; if (!hasCompose && !hasMask && !useGlobalOpacity) - mainFragShaderName = MainFragmentShader; + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader; - compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader); - requiredProgram.mainFragShader = compiledShaders[mainFragShaderName]; + requiredProgram.mainFragShader = sharedShaders->compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader); if (hasMask) { - QGLEngineShaderManager::ShaderName maskShaderName = QGLEngineShaderManager::InvalidShaderName; + QGLEngineSharedShaders::ShaderName maskShaderName = QGLEngineSharedShaders::InvalidShaderName; if (maskType == PixelMask) - maskShaderName = MaskFragmentShader; + maskShaderName = QGLEngineSharedShaders::MaskFragmentShader; else if (maskType == SubPixelMask) - maskShaderName = RgbMaskFragmentShader; + maskShaderName = QGLEngineSharedShaders::RgbMaskFragmentShader; else if (maskType == SubPixelWithGammaMask) - maskShaderName = RgbMaskWithGammaFragmentShader; + maskShaderName = QGLEngineSharedShaders::RgbMaskWithGammaFragmentShader; else qCritical("QGLEngineShaderManager::useCorrectShaderProg() - Unknown mask type"); - compileNamedShader(maskShaderName, QGLShader::PartialFragmentShader); - requiredProgram.maskFragShader = compiledShaders[maskShaderName]; - } - else + requiredProgram.maskFragShader = sharedShaders->compileNamedShader(maskShaderName, QGLShader::PartialFragmentShader); + } else { requiredProgram.maskFragShader = 0; + } if (hasCompose) { - QGLEngineShaderManager::ShaderName compositionShaderName = QGLEngineShaderManager::InvalidShaderName; + QGLEngineSharedShaders::ShaderName compositionShaderName = QGLEngineSharedShaders::InvalidShaderName; switch (compositionMode) { case QPainter::CompositionMode_Multiply: - compositionShaderName = MultiplyCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::MultiplyCompositionModeFragmentShader; break; case QPainter::CompositionMode_Screen: - compositionShaderName = ScreenCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::ScreenCompositionModeFragmentShader; break; case QPainter::CompositionMode_Overlay: - compositionShaderName = OverlayCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::OverlayCompositionModeFragmentShader; break; case QPainter::CompositionMode_Darken: - compositionShaderName = DarkenCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::DarkenCompositionModeFragmentShader; break; case QPainter::CompositionMode_Lighten: - compositionShaderName = LightenCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::LightenCompositionModeFragmentShader; break; case QPainter::CompositionMode_ColorDodge: - compositionShaderName = ColorDodgeCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::ColorDodgeCompositionModeFragmentShader; break; case QPainter::CompositionMode_ColorBurn: - compositionShaderName = ColorBurnCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::ColorBurnCompositionModeFragmentShader; break; case QPainter::CompositionMode_HardLight: - compositionShaderName = HardLightCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::HardLightCompositionModeFragmentShader; break; case QPainter::CompositionMode_SoftLight: - compositionShaderName = SoftLightCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::SoftLightCompositionModeFragmentShader; break; case QPainter::CompositionMode_Difference: - compositionShaderName = DifferenceCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::DifferenceCompositionModeFragmentShader; break; case QPainter::CompositionMode_Exclusion: - compositionShaderName = ExclusionCompositionModeFragmentShader; + compositionShaderName = QGLEngineSharedShaders::ExclusionCompositionModeFragmentShader; break; default: qWarning("QGLEngineShaderManager::useCorrectShaderProg() - Unsupported composition mode"); } - compileNamedShader(compositionShaderName, QGLShader::PartialFragmentShader); - requiredProgram.compositionFragShader = compiledShaders[compositionShaderName]; - } - else + requiredProgram.compositionFragShader = sharedShaders->compileNamedShader(compositionShaderName, QGLShader::PartialFragmentShader); + } 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) { - if (cachedPrograms[i] == requiredProgram) { - currentShaderProg = &cachedPrograms[i]; - foundProgramInCache = true; - break; - } } - // 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()) { - QLatin1String none("none"); - QLatin1String br("\n"); - QString error; - error = QLatin1String("Shader program failed to link,") -#if defined(QT_DEBUG) - + br - + QLatin1String(" Shaders Used:\n") - + QLatin1String(" mainVertexShader = ") - + (requiredProgram.mainVertexShader ? - requiredProgram.mainVertexShader->objectName() : none) + br - + QLatin1String(" positionVertexShader = ") - + (requiredProgram.positionVertexShader ? - requiredProgram.positionVertexShader->objectName() : none) + br - + QLatin1String(" mainFragShader = ") - + (requiredProgram.mainFragShader ? - requiredProgram.mainFragShader->objectName() : none) + br - + QLatin1String(" srcPixelFragShader = ") - + (requiredProgram.srcPixelFragShader ? - requiredProgram.srcPixelFragShader->objectName() : none) + br - + QLatin1String(" maskFragShader = ") - + (requiredProgram.maskFragShader ? - requiredProgram.maskFragShader->objectName() : none) + br - + QLatin1String(" compositionFragShader = ") - + (requiredProgram.compositionFragShader ? - requiredProgram.compositionFragShader->objectName() : none) + br -#endif - + QLatin1String(" Error Log:\n") - + QLatin1String(" ") + 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(); - } - } + // At this point, requiredProgram is fully populated so try to find the program in the cache + currentShaderProg = sharedShaders->findProgramInCache(requiredProgram); if (currentShaderProg) { currentShaderProg->program->enable(); @@ -618,40 +665,4 @@ bool QGLEngineShaderManager::useCorrectShaderProg() return true; } -void QGLEngineShaderManager::compileNamedShader(QGLEngineShaderManager::ShaderName name, QGLShader::ShaderType type) -{ - if (compiledShaders[name]) - return; - - 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 - QMetaEnum m = staticMetaObject.enumerator(staticMetaObject.indexOfEnumerator("ShaderName")); - newShader->setObjectName(QLatin1String(m.valueToKey(name))); -#endif - - compiledShaders[name] = newShader; -} - QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 99cd82e..0cfdcf1 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -275,65 +275,10 @@ struct QGLEngineCachedShaderProg static const GLuint QT_VERTEX_COORDS_ATTR = 0; static const GLuint QT_TEXTURE_COORDS_ATTR = 1; -class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject +class QGLEngineSharedShaders : public QObject { Q_OBJECT public: - QGLEngineShaderManager(QGLContext* context); - ~QGLEngineShaderManager(); - - enum MaskType {NoMask, PixelMask, SubPixelMask, SubPixelWithGammaMask}; - enum PixelSrcType { - ImageSrc = Qt::TexturePattern+1, - NonPremultipliedImageSrc = Qt::TexturePattern+2, - PatternSrc = Qt::TexturePattern+3, - TextureSrcWithPattern = Qt::TexturePattern+4 - }; - - enum Uniform { - ImageTexture, - PatternColor, - GlobalOpacity, - Depth, - PmvMatrix, - MaskTexture, - FragmentColor, - LinearData, - Angle, - HalfViewportSize, - Fmp, - Fmp2MRadius2, - Inverse2Fmp2MRadius2, - InvertedTextureSize, - BrushTransform, - BrushTexture, - NumUniforms - }; - - // There are optimisations we can do, depending on the brush transform: - // 1) May not have to apply perspective-correction - // 2) Can use lower precision for matrix - void optimiseForBrushTransform(const QTransform &transform); - void setSrcPixelType(Qt::BrushStyle); - void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images - void setTextureCoordsEnabled(bool); // For images & text glyphs - void setUseGlobalOpacity(bool); - void setMaskType(MaskType); - void setCompositionMode(QPainter::CompositionMode); - void setCustomStage(QGLCustomShaderStage* stage); - void removeCustomStage(QGLCustomShaderStage* stage); - - uint getUniformLocation(Uniform id); - - void setDirty(); // someone has manually changed the current shader program - bool useCorrectShaderProg(); // returns true if the shader program needed to be changed - - QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen - 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, @@ -392,6 +337,92 @@ public: TotalShaderCount, InvalidShaderName }; + QGLEngineSharedShaders(const QGLContext *context); + + QGLShader *compileNamedShader(ShaderName name, QGLShader::ShaderType type); + + QGLShaderProgram *simpleProgram() { return simpleShaderProg; } + QGLShaderProgram *blitProgram() { return blitShaderProg; } + // Compile the program if it's not already in the cache, return the item in the cache. + QGLEngineShaderProg *findProgramInCache(const QGLEngineShaderProg &prog); + // Compile the custom shader if it's not already in the cache, return the item in the cache. + QGLShader *compileCustomShader(QGLCustomShaderStage *stage, QGLShader::ShaderType type); + + static QGLEngineSharedShaders *shadersForContext(const QGLContext *context); + +signals: + void shaderProgNeedsChanging(); + +private slots: + void shaderDestroyed(QObject *shader); + +private: + QGLContextGroup *ctx; + QGLShaderProgram *blitShaderProg; + QGLShaderProgram *simpleShaderProg; + QList cachedPrograms; + QCache customShaderCache; + QGLShader* compiledShaders[TotalShaderCount]; + + static const char* qglEngineShaderSourceCode[TotalShaderCount]; +}; + +class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject +{ + Q_OBJECT +public: + QGLEngineShaderManager(QGLContext* context); + ~QGLEngineShaderManager(); + + enum MaskType {NoMask, PixelMask, SubPixelMask, SubPixelWithGammaMask}; + enum PixelSrcType { + ImageSrc = Qt::TexturePattern+1, + NonPremultipliedImageSrc = Qt::TexturePattern+2, + PatternSrc = Qt::TexturePattern+3, + TextureSrcWithPattern = Qt::TexturePattern+4 + }; + + enum Uniform { + ImageTexture, + PatternColor, + GlobalOpacity, + Depth, + PmvMatrix, + MaskTexture, + FragmentColor, + LinearData, + Angle, + HalfViewportSize, + Fmp, + Fmp2MRadius2, + Inverse2Fmp2MRadius2, + InvertedTextureSize, + BrushTransform, + BrushTexture, + NumUniforms + }; + + // There are optimisations we can do, depending on the brush transform: + // 1) May not have to apply perspective-correction + // 2) Can use lower precision for matrix + void optimiseForBrushTransform(const QTransform &transform); + void setSrcPixelType(Qt::BrushStyle); + void setSrcPixelType(PixelSrcType); // For non-brush sources, like pixmaps & images + void setTextureCoordsEnabled(bool); // For images & text glyphs + void setUseGlobalOpacity(bool); + void setMaskType(MaskType); + void setCompositionMode(QPainter::CompositionMode); + void setCustomStage(QGLCustomShaderStage* stage); + void removeCustomStage(QGLCustomShaderStage* stage); + + uint getUniformLocation(Uniform id); + + void setDirty(); // someone has manually changed the current shader program + bool useCorrectShaderProg(); // returns true if the shader program needed to be changed + + QGLShaderProgram* currentProgram(); // Returns pointer to the shader the manager has chosen + QGLShaderProgram* simpleProgram(); // Used to draw into e.g. stencil buffers + QGLShaderProgram* blitProgram(); // Used to blit a texture into the framebuffer /* // These allow the ShaderName enum to be used as a cache key @@ -408,7 +439,7 @@ public: #endif private slots: - void shaderDestroyed(QObject *shader); + void shaderProgNeedsChangingSlot() { shaderProgNeedsChanging = true; } private: QGLContext* ctx; @@ -423,19 +454,9 @@ private: QPainter::CompositionMode compositionMode; QGLCustomShaderStage* customSrcStage; - QGLShaderProgram* blitShaderProg; - QGLShaderProgram* simpleShaderProg; QGLEngineShaderProg* currentShaderProg; - - // TODO: Possibly convert to a LUT - QList cachedPrograms; - QCache customShaderCache; - - QGLShader* compiledShaders[TotalShaderCount]; - - void compileNamedShader(QGLEngineShaderManager::ShaderName name, QGLShader::ShaderType type); - - static const char* qglEngineShaderSourceCode[TotalShaderCount]; + QGLEngineSharedShaders *sharedShaders; + QGLShader *customShader; }; QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 0c01263..7271740 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -316,6 +316,7 @@ extern QImage qt_imageForBrush(int brushStyle, bool invert); QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate() { + delete shaderManager; } void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id) @@ -1330,7 +1331,8 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) qt_resolve_version_2_0_functions(d->ctx); #endif - d->shaderManager = QGLEngineShaderManager::managerForContext(d->ctx); + if (!d->shaderManager) + d->shaderManager = new QGLEngineShaderManager(d->ctx); d->shaderManager->setDirty(); glViewport(0, 0, d->width, d->height); @@ -1430,11 +1432,13 @@ void QGL2PaintEngineEx::ensureActive() p->transferMode(BrushDrawingMode); p->drawable.doneCurrent(); } + d->drawable.context()->makeCurrent(); d->drawable.makeCurrent(); ctx->d_ptr->active_engine = this; - d->needsSync = true; + } else { + d->drawable.context()->makeCurrent(); } if (d->needsSync) { diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 552e390..cb23b11 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -196,6 +196,8 @@ public: float zValueForRenderText() const; + static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; } + QGL2PaintEngineEx* q; QGLDrawable drawable; int width, height; diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 8b7674e..1d765f8 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -4922,13 +4922,13 @@ void QGLShareRegister::removeShare(const QGLContext *context) { QGLContextResource::QGLContextResource(FreeFunc f, QObject *parent) : QObject(parent), free(f) { - connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)), this, SLOT(aboutToDestroyContext(const QGLContext *))); + connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)), this, SLOT(removeOne(const QGLContext *))); } QGLContextResource::~QGLContextResource() { while (!m_resources.empty()) - remove(m_resources.begin().key()); + removeGroup(m_resources.begin().key()); } void QGLContextResource::insert(const QGLContext *key, void *value) @@ -4976,7 +4976,7 @@ void *QGLContextResource::value(const QGLContext *key) return it.value(); } -void QGLContextResource::remove(const QGLContext *key) +void QGLContextResource::removeGroup(const QGLContext *key) { QList shares = qgl_share_reg()->shares(key); if (shares.size() == 0) @@ -5000,7 +5000,7 @@ void QGLContextResource::remove(const QGLContext *key) } } -void QGLContextResource::aboutToDestroyContext(const QGLContext *key) +void QGLContextResource::removeOne(const QGLContext *key) { ResourceHash::iterator it = m_resources.find(key); if (it == m_resources.end()) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index a39c52c..72ec35e 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -540,10 +540,10 @@ public: // 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); + void removeGroup(const QGLContext *key); private slots: // Remove entry 'key' from cache and delete resource if there are no shared contexts. - void aboutToDestroyContext(const QGLContext *key); + void removeOne(const QGLContext *key); private: typedef QHash ResourceHash; ResourceHash m_resources; -- cgit v0.12