commit 6decb3db61ff3f319b9dffd8f3288bf6f96696d2 Author: Jani Hautakangas Date: Thu Aug 4 11:16:09 2011 +0300 Fix for ShaderEffectItem GPU resource handling On Symbian shader items should release their GPU resources when application goes to background. When application goes to background it destroys all GL resources. ShaderEffectItem can hook up into QGLContextResource cleanup and release shaders and FBOs when attached context gets destroyed. Also ShaderEffectItem should lazily load FBOs on the first paint to prevent GPU mem consumption when item is not shown. Task-number: QTBUG-20724 QTBUG-20736 Reviewed-by: pending diff --git a/src/imports/shaders/shadereffectitem.cpp b/src/imports/shaders/shadereffectitem.cpp index 056581c..736e98f 100644 --- a/src/imports/shaders/shadereffectitem.cpp +++ b/src/imports/shaders/shadereffectitem.cpp @@ -46,6 +46,58 @@ #include #include +#ifdef Q_OS_SYMBIAN +#include + +class ShaderEffectItemCleanup +{ +public: + ShaderEffectItemCleanup() {} + + ~ShaderEffectItemCleanup() + { + while(m_list.size() > 0) { + ShaderEffectItem *item = m_list.takeAt(0); + if (item) + item->uninitialize(); + } + } + + static ShaderEffectItemCleanup *cleanupForContext(const QGLContext *context); + + void insert(ShaderEffectItem *item) + { + m_list.append(item); + } + +private: + + QList m_list; +}; + +#if QT_VERSION >= 0x040800 +Q_GLOBAL_STATIC(QGLContextGroupResource, qt_shader_effect_item_cleanup) +#else +static void qt_shader_effect_item_cleanup_free(void *data) +{ + delete reinterpret_cast(data); +} +Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_shader_effect_item_cleanup, (qt_shader_effect_item_cleanup_free)) +#endif + +ShaderEffectItemCleanup *ShaderEffectItemCleanup::cleanupForContext(const QGLContext *context) +{ + ShaderEffectItemCleanup *p = reinterpret_cast(qt_shader_effect_item_cleanup()->value(context)); +#if QT_VERSION < 0x040800 + if (!p) { + QGLShareContextScope scope(context); + qt_shader_effect_item_cleanup()->insert(context, p = new ShaderEffectItemCleanup); + } +#endif + return p; +} +#endif // Q_OS_SYMBIAN + static const char qt_default_vertex_code[] = "uniform highp mat4 qt_ModelViewProjectionMatrix;\n" "attribute highp vec4 qt_Vertex;\n" @@ -69,7 +121,6 @@ static const char qt_postion_attribute_name[] = "qt_Vertex"; static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0"; static const char qt_emptyAttributeName[] = ""; - /*! \qmlclass ShaderEffectItem ShaderEffectItem \ingroup qml-shader-elements @@ -214,7 +265,11 @@ ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) , m_hasShaderPrograms(false) , m_mirrored(false) , m_defaultVertexShader(true) +#ifdef Q_OS_SYMBIAN + , m_initialized(false) +#endif { + m_program = new QGLShaderProgram(this); setFlag(QGraphicsItem::ItemHasNoContents, false); connect(this, SIGNAL(visibleChanged()), this, SLOT(handleVisibilityChange())); m_active = isVisible(); @@ -422,10 +477,17 @@ void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) if (!painter || !painter->device()) return; - if (!m_program.isLinked() || m_program_dirty) + if (!m_program->isLinked() || m_program_dirty) { +#ifdef Q_OS_SYMBIAN + if (!m_initialized) { + ShaderEffectItemCleanup::cleanupForContext(QGLContext::currentContext())->insert(this); + initialize(); + } +#endif updateShaderProgram(); + } - m_program.bind(); + m_program->bind(); QMatrix4x4 combinedMatrix; combinedMatrix.scale(2.0 / painter->device()->width(), -2.0 / painter->device()->height(), 1.0); @@ -434,7 +496,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(); @@ -472,7 +534,7 @@ 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) @@ -487,10 +549,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; @@ -500,37 +562,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; @@ -574,7 +636,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); } @@ -641,7 +703,12 @@ void ShaderEffectItem::setActive(bool enable) if (!source) continue; disconnect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); +#ifdef Q_OS_SYMBIAN + if (m_initialized) + source->derefFromEffectItem(); +#else source->derefFromEffectItem(); +#endif } } @@ -652,7 +719,12 @@ void ShaderEffectItem::setActive(bool enable) ShaderEffectSource *source = m_sources.at(i).source; if (!source) continue; +#ifdef Q_OS_SYMBIAN + if (m_initialized) + source->refFromEffectItem(); +#else source->refFromEffectItem(); +#endif connect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); } } @@ -661,6 +733,62 @@ void ShaderEffectItem::setActive(bool enable) markDirty(); } +#ifdef Q_OS_SYMBIAN +void ShaderEffectItem::initialize() +{ + if (m_initialized) + return; + + if (m_active) { + for (int i = 0; i < m_sources.size(); ++i) { + QVariant var = property(m_sources.at(i).name); + if (var.isNull()) { + continue; + } else if (!qVariantCanConvert(var)) { + continue; + } + + ShaderEffectSource *source = m_sources.at(i).source; + if (!source) + continue; + + source->refFromEffectItem(); + } + } + + m_initialized = true; +} + +void ShaderEffectItem::uninitialize() +{ + if (!m_initialized) + return; + + if (m_active) { + for (int i = 0; i < m_sources.size(); ++i) { + QVariant var = property(m_sources.at(i).name); + if (var.isNull()) { + continue; + } else if (!qVariantCanConvert(var)) { + continue; + } + + ShaderEffectSource *source = m_sources.at(i).source; + if (!source) + continue; + + source->derefFromEffectItem(); + } + } + + m_program->removeAllShaders(); + delete m_program; + m_program = new QGLShaderProgram(this); + m_program_dirty = true; + m_initialized = false; +} +#endif // Q_OS_SYMBIAN + void ShaderEffectItem::preprocess() { for (int i = 0; i < m_sources.size(); ++i) { @@ -725,7 +853,12 @@ void ShaderEffectItem::setSource(const QVariant &var, int index) // Unlike in scenegraph, ref counting is used to optimize memory consumption. Sources themself may free fbos when not referenced. if (m_active && source.source) { +#ifdef Q_OS_SYMBIAN + if (m_initialized) + source.source->refFromEffectItem(); +#else source.source->refFromEffectItem(); +#endif connect(source.source, SIGNAL(repaintRequired()), this, SLOT(markDirty())); } } @@ -776,7 +909,7 @@ void ShaderEffectItem::reset() { disconnectPropertySignals(); - m_program.removeAllShaders(); + m_program->removeAllShaders(); m_attributeNames.clear(); m_uniformNames.clear(); for (int i = 0; i < m_sources.size(); ++i) { @@ -830,16 +963,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)) @@ -849,10 +982,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/imports/shaders/shadereffectitem.h b/src/imports/shaders/shadereffectitem.h index aebe897..eea1ff5 100644 --- a/src/imports/shaders/shadereffectitem.h +++ b/src/imports/shaders/shadereffectitem.h @@ -51,6 +51,10 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE +#ifdef Q_OS_SYMBIAN +class ShaderEffectItemCleanup; +#endif + class ShaderEffectItem : public QDeclarativeItem { Q_OBJECT @@ -112,10 +116,15 @@ private: bool active() const { return m_active; } void setActive(bool enable); +#ifdef Q_OS_SYMBIAN + void initialize(); + void uninitialize(); +#endif + 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 +152,10 @@ private: bool m_hasShaderPrograms : 1; bool m_mirrored : 1; bool m_defaultVertexShader : 1; +#ifdef Q_OS_SYMBIAN + bool m_initialized : 1; + friend class ShaderEffectItemCleanup; +#endif }; QT_END_HEADER