diff --git a/src/shadereffectitem.cpp b/src/shadereffectitem.cpp index dbfe2a9..9dc864d 100644 --- a/src/shadereffectitem.cpp +++ b/src/shadereffectitem.cpp @@ -201,6 +201,7 @@ Rectangle { ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) : QDeclarativeItem(parent) + , m_program(0) , m_meshResolution(1, 1) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) , m_blending(true) @@ -214,10 +215,18 @@ ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) , m_hasShaderPrograms(false) , m_mirrored(false) , m_defaultVertexShader(true) + , m_lastContext(0) { setFlag(QGraphicsItem::ItemHasNoContents, false); connect(this, SIGNAL(visibleChanged()), this, SLOT(handleVisibilityChange())); m_active = isVisible(); + + // In Symbian OS QGLShaderProgram is not created here, but instead it is created + // later to minimize GPU memory usage. +#ifndef Q_OS_SYMBIAN + m_program = new QGLShaderProgram(this); + m_lastContext = const_cast (QGLContext::currentContext()); +#endif } ShaderEffectItem::~ShaderEffectItem() @@ -426,10 +435,24 @@ void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) if (!painter || !painter->device()) return; - if (!m_program.isLinked() || m_program_dirty) + QGLContext *context = const_cast (QGLContext::currentContext()); + if (!m_program || !m_lastContext || !QGLContext::areSharing(context, m_lastContext)) { + // Context has changed, re-create QGLShaderProgram + if (context) { + delete m_program; + m_program = 0; + m_program = new QGLShaderProgram(this); + m_lastContext = context; + } + } + + if (!m_program) + return; + + if (!m_program->isLinked() || m_program_dirty) updateShaderProgram(); - m_program.bind(); + m_program->bind(); QMatrix4x4 combinedMatrix; combinedMatrix.scale(2.0 / painter->device()->width(), -2.0 / painter->device()->height(), 1.0); @@ -438,7 +461,7 @@ void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) updateEffectState(combinedMatrix); for (int i = 0; i < m_attributeNames.size(); ++i) { - m_program.enableAttributeArray(m_geometry.attributes()[i].position); + m_program->enableAttributeArray(m_geometry.attributes()[i].position); } bindGeometry(); @@ -476,11 +499,14 @@ void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) glDisable(GL_DEPTH_TEST); for (int i = 0; i < m_attributeNames.size(); ++i) - m_program.disableAttributeArray(m_geometry.attributes()[i].position); + m_program->disableAttributeArray(m_geometry.attributes()[i].position); } void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) { + if (!m_program) + return; + for (int i = m_sources.size() - 1; i >= 0; --i) { const ShaderEffectItem::SourceData &source = m_sources.at(i); if (!source.source) @@ -491,10 +517,10 @@ void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) } if (m_respectsOpacity) - m_program.setUniformValue("qt_Opacity", static_cast (effectiveOpacity())); + m_program->setUniformValue("qt_Opacity", static_cast (effectiveOpacity())); if (m_respectsMatrix){ - m_program.setUniformValue("qt_ModelViewProjectionMatrix", matrix); + m_program->setUniformValue("qt_ModelViewProjectionMatrix", matrix); } QSet::const_iterator it; @@ -504,37 +530,37 @@ void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) switch (v.type()) { case QVariant::Color: - m_program.setUniformValue(name.constData(), qvariant_cast(v)); + m_program->setUniformValue(name.constData(), qvariant_cast(v)); break; case QVariant::Double: - m_program.setUniformValue(name.constData(), (float) qvariant_cast(v)); + m_program->setUniformValue(name.constData(), (float) qvariant_cast(v)); break; case QVariant::Transform: - m_program.setUniformValue(name.constData(), qvariant_cast(v)); + m_program->setUniformValue(name.constData(), qvariant_cast(v)); break; case QVariant::Int: - m_program.setUniformValue(name.constData(), v.toInt()); + m_program->setUniformValue(name.constData(), v.toInt()); break; case QVariant::Bool: - m_program.setUniformValue(name.constData(), GLint(v.toBool())); + m_program->setUniformValue(name.constData(), GLint(v.toBool())); break; case QVariant::Size: case QVariant::SizeF: - m_program.setUniformValue(name.constData(), v.toSizeF()); + m_program->setUniformValue(name.constData(), v.toSizeF()); break; case QVariant::Point: case QVariant::PointF: - m_program.setUniformValue(name.constData(), v.toPointF()); + m_program->setUniformValue(name.constData(), v.toPointF()); break; case QVariant::Rect: case QVariant::RectF: { QRectF r = v.toRectF(); - m_program.setUniformValue(name.constData(), r.x(), r.y(), r.width(), r.height()); + m_program->setUniformValue(name.constData(), r.x(), r.y(), r.width(), r.height()); } break; case QVariant::Vector3D: - m_program.setUniformValue(name.constData(), qvariant_cast(v)); + m_program->setUniformValue(name.constData(), qvariant_cast(v)); break; default: break; @@ -562,6 +588,9 @@ static inline int size_of_type(GLenum type) void ShaderEffectItem::bindGeometry() { + if (!m_program) + return; + char const *const *attrNames = m_attributeNames.constData(); int offset = 0; for (int j = 0; j < m_attributeNames.size(); ++j) { @@ -578,7 +607,7 @@ void ShaderEffectItem::bindGeometry() if (normalize) qWarning() << "ShaderEffectItem::bindGeometry() - non supported attribute type!"; - m_program.setAttributeArray(a.position, (GLfloat*) (((char*) m_geometry.vertexData()) + offset), a.tupleSize, m_geometry.stride()); + m_program->setAttributeArray(a.position, (GLfloat*) (((char*) m_geometry.vertexData()) + offset), a.tupleSize, m_geometry.stride()); //glVertexAttribPointer(a.position, a.tupleSize, a.type, normalize, m_geometry.stride(), (char *) m_geometry.vertexData() + offset); offset += a.tupleSize * size_of_type(a.type); } @@ -661,6 +690,15 @@ void ShaderEffectItem::setActive(bool enable) } } + // In Symbian OS QGLShaderProgram is deleted when not active (to minimize GPU memory usage). +#ifdef Q_OS_SYMBIAN + if (!m_active && m_program) { + delete m_program; + m_program = 0; + m_lastContext = 0; + } +#endif + emit activeChanged(); markDirty(); } @@ -780,7 +818,9 @@ void ShaderEffectItem::reset() { disconnectPropertySignals(); - m_program.removeAllShaders(); + if (m_program) + m_program->removeAllShaders(); + m_attributeNames.clear(); m_uniformNames.clear(); for (int i = 0; i < m_sources.size(); ++i) { @@ -825,6 +865,9 @@ void ShaderEffectItem::updateProperties() void ShaderEffectItem::updateShaderProgram() { + if (!m_program) + return; + QString vertexCode = m_vertex_code; QString fragmentCode = m_fragment_code; @@ -834,16 +877,16 @@ void ShaderEffectItem::updateShaderProgram() if (fragmentCode.isEmpty()) fragmentCode = QString::fromLatin1(qt_default_fragment_code); - m_program.addShaderFromSourceCode(QGLShader::Vertex, vertexCode); - m_program.addShaderFromSourceCode(QGLShader::Fragment, fragmentCode); + m_program->addShaderFromSourceCode(QGLShader::Vertex, vertexCode); + m_program->addShaderFromSourceCode(QGLShader::Fragment, fragmentCode); for (int i = 0; i < m_attributeNames.size(); ++i) { - m_program.bindAttributeLocation(m_attributeNames.at(i), m_geometry.attributes()[i].position); + m_program->bindAttributeLocation(m_attributeNames.at(i), m_geometry.attributes()[i].position); } - if (!m_program.link()) { + if (!m_program->link()) { qWarning("ShaderEffectItem: Shader compilation failed:"); - qWarning() << m_program.log(); + qWarning() << m_program->log(); } if (!m_attributeNames.contains(qt_postion_attribute_name)) @@ -853,10 +896,10 @@ void ShaderEffectItem::updateShaderProgram() if (!m_respectsMatrix) qWarning("ShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'."); - if (m_program.isLinked()) { - m_program.bind(); + if (m_program->isLinked()) { + m_program->bind(); for (int i = 0; i < m_sources.size(); ++i) - m_program.setUniformValue(m_sources.at(i).name.constData(), i); + m_program->setUniformValue(m_sources.at(i).name.constData(), i); } m_program_dirty = false; diff --git a/src/shadereffectitem.h b/src/shadereffectitem.h index 1d27543..7b70228 100644 --- a/src/shadereffectitem.h +++ b/src/shadereffectitem.h @@ -115,7 +115,7 @@ private: private: QString m_fragment_code; QString m_vertex_code; - QGLShaderProgram m_program; + QGLShaderProgram* m_program; QVector m_attributeNames; QSet m_uniformNames; QSize m_meshResolution; @@ -143,6 +143,8 @@ private: bool m_hasShaderPrograms : 1; bool m_mirrored : 1; bool m_defaultVertexShader : 1; + + QGLContext* m_lastContext; }; QT_END_HEADER diff --git a/src/shadereffectsource.cpp b/src/shadereffectsource.cpp index dec3bb0..2c25f2f 100644 --- a/src/shadereffectsource.cpp +++ b/src/shadereffectsource.cpp @@ -170,15 +170,11 @@ void ShaderEffectSource::setSourceRect(const QRectF &rect) return; m_sourceRect = rect; updateSizeAndTexture(); - updateBackbuffer(); emit sourceRectChanged(); emit repaintRequired(); - if (m_sourceItem) { - ShaderEffect* effect = qobject_cast (m_sourceItem->graphicsEffect()); - if (effect) - effect->m_changed = true; - } + m_dirtyTexture = true; + markSourceItemDirty(); } /*! @@ -207,11 +203,8 @@ void ShaderEffectSource::setTextureSize(const QSize &size) emit textureSizeChanged(); emit repaintRequired(); - if (m_sourceItem) { - ShaderEffect* effect = qobject_cast (m_sourceItem->graphicsEffect()); - if (effect) - effect->m_changed = true; - } + m_dirtyTexture = true; + markSourceItemDirty(); } /*! @@ -294,8 +287,10 @@ void ShaderEffectSource::setWrapMode(WrapMode mode) return; m_wrapMode = mode; - updateBackbuffer(); emit wrapModeChanged(); + + m_dirtyTexture = true; + markSourceItemDirty(); } /*! @@ -314,7 +309,7 @@ void ShaderEffectSource::grab() emit repaintRequired(); } -void ShaderEffectSource::bind() const +void ShaderEffectSource::bind() { GLint filtering = smooth() ? GL_LINEAR : GL_NEAREST; GLuint hwrap = (m_wrapMode == Repeat || m_wrapMode == RepeatHorizontally) ? GL_REPEAT : GL_CLAMP_TO_EDGE; @@ -323,9 +318,13 @@ void ShaderEffectSource::bind() const #if !defined(QT_OPENGL_ES_2) glEnable(GL_TEXTURE_2D); #endif - if (m_fbo) { + + if (m_fbo && m_fbo->isValid()) { glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); } else { + m_dirtyTexture = true; + emit repaintRequired(); + markSourceItemDirty(); glBindTexture(GL_TEXTURE_2D, 0); } @@ -354,7 +353,7 @@ void ShaderEffectSource::derefFromEffectItem() void ShaderEffectSource::updateBackbuffer() { - if (!m_sourceItem) + if (!m_sourceItem || !QGLContext::currentContext()) return; // Multisampling is not (for now) supported. @@ -370,7 +369,7 @@ void ShaderEffectSource::updateBackbuffer() if (!m_fbo) { m_fbo = new ShaderEffectBuffer(size, format); } else { - if (m_fbo->size() != size || m_fbo->format().internalTextureFormat() != GLenum(m_format)) { + if (!m_fbo->isValid() || m_fbo->size() != size || m_fbo->format().internalTextureFormat() != GLenum(m_format)) { delete m_fbo; m_fbo = 0; m_fbo = new ShaderEffectBuffer(size, format); @@ -397,6 +396,16 @@ void ShaderEffectSource::markSourceSizeDirty() emit repaintRequired(); } +void ShaderEffectSource::markSourceItemDirty() +{ + m_dirtyTexture = true; + if (m_sourceItem) { + ShaderEffect* effect = qobject_cast (m_sourceItem->graphicsEffect()); + if (effect) + effect->m_changed = true; + } +} + void ShaderEffectSource::updateSizeAndTexture() { if (m_sourceItem) { @@ -407,7 +416,7 @@ void ShaderEffectSource::updateSizeAndTexture() size.setWidth(1); if (size.height() < 1) size.setHeight(1); - if (m_fbo && m_fbo->size() != size) { + if (m_fbo && (m_fbo->size() != size || !m_fbo->isValid())) { delete m_fbo; m_fbo = 0; delete m_multisampledFbo; diff --git a/src/shadereffectsource.h b/src/shadereffectsource.h index 275e5b2..9c69548 100644 --- a/src/shadereffectsource.h +++ b/src/shadereffectsource.h @@ -99,7 +99,7 @@ public: void setWrapMode(WrapMode mode); bool isActive() const { return m_refs; } - void bind() const; + void bind(); void refFromEffectItem(); void derefFromEffectItem(); void updateBackbuffer(); @@ -124,6 +124,7 @@ Q_SIGNALS: public Q_SLOTS: void markSceneGraphDirty(); void markSourceSizeDirty(); + void markSourceItemDirty(); private: void updateSizeAndTexture(); diff --git a/tests/manual/qmlshadersplugintest/qml/qmlshadersplugintest/TestHideOriginal.qml b/tests/manual/qmlshadersplugintest/qml/qmlshadersplugintest/TestHideOriginal.qml index 1cd449f..fc7ddb0 100644 --- a/tests/manual/qmlshadersplugintest/qml/qmlshadersplugintest/TestHideOriginal.qml +++ b/tests/manual/qmlshadersplugintest/qml/qmlshadersplugintest/TestHideOriginal.qml @@ -75,7 +75,8 @@ Rectangle { varying highp vec2 qt_TexCoord0; uniform sampler2D source; void main() { - // Empty fragmentshader, we do not write any pixels via this effect item. We only observe hideoriginal functionality. + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); + // We do not write any visible pixels via this effect item. We only observe hideoriginal functionality. } " }