diff options
author | Marko Niemelä <marko.a.niemela@nokia.com> | 2011-08-24 15:29:17 +0300 |
---|---|---|
committer | Marko Niemelä <marko.a.niemela@nokia.com> | 2011-08-24 15:29:17 +0300 |
commit | 4eb50fbbf414103fb2f9dfb8260f4db5d61190c4 (patch) | |
tree | a0054520bc685defb1b028953db08132022c37bf /src/imports | |
parent | 0b19ee5ecabfc516256f0a6db0f2e7bc37e703a0 (diff) | |
download | qt4-tools-4eb50fbbf414103fb2f9dfb8260f4db5d61190c4.tar.gz |
Fix for qml1shaderplugin GPU resource handling on Symbian
qml1shaderplugin now automatically recovers from situations when GL context is
destroyed and related FBOs and shaderprograms become invalid. Additionally the
shaderprograms are created lazily so that no GPU memory is used if items are
not visible.
Task-number: QTBUG-20724 QTBUG-20736
Reviewed-by: Jani Hautakangas
Diffstat (limited to 'src/imports')
-rwxr-xr-x[-rw-r--r--] | src/imports/shaders/shadereffectitem.cpp | 110 | ||||
-rw-r--r-- | src/imports/shaders/shadereffectitem.h | 4 | ||||
-rw-r--r-- | src/imports/shaders/shadereffectsource.cpp | 43 | ||||
-rw-r--r-- | src/imports/shaders/shadereffectsource.h | 3 |
4 files changed, 116 insertions, 44 deletions
diff --git a/src/imports/shaders/shadereffectitem.cpp b/src/imports/shaders/shadereffectitem.cpp index 056581c654..04c81f5cec 100644..100755 --- a/src/imports/shaders/shadereffectitem.cpp +++ b/src/imports/shaders/shadereffectitem.cpp @@ -199,8 +199,13 @@ Rectangle { */ +#ifdef Q_OS_SYMBIAN +#define OBSERVE_GL_CONTEXT_LOSS 1 +#endif + ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) : QDeclarativeItem(parent) + , m_program(0) , m_meshResolution(1, 1) , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4) , m_blending(true) @@ -214,15 +219,21 @@ ShaderEffectItem::ShaderEffectItem(QDeclarativeItem *parent) , m_hasShaderPrograms(false) , m_mirrored(false) , m_defaultVertexShader(true) + , m_contextObserver(0) { setFlag(QGraphicsItem::ItemHasNoContents, false); connect(this, SIGNAL(visibleChanged()), this, SLOT(handleVisibilityChange())); m_active = isVisible(); + +#ifndef OBSERVE_GL_CONTEXT_LOSS + m_program = new QGLShaderProgram(this); +#endif } ShaderEffectItem::~ShaderEffectItem() { reset(); + delete m_contextObserver; } @@ -422,10 +433,38 @@ void ShaderEffectItem::renderEffect(QPainter *painter, const QMatrix4x4 &matrix) if (!painter || !painter->device()) return; - if (!m_program.isLinked() || m_program_dirty) +#ifdef OBSERVE_GL_CONTEXT_LOSS + QGLContext *context = const_cast <QGLContext*> (QGLContext::currentContext()); + if (!m_program || !m_contextObserver || !m_contextObserver->isValid()) { + // Context has changed, re-create QGLShaderProgram + if (context) { + delete m_program; + m_program = 0; + + delete m_contextObserver; + m_contextObserver = 0; + + m_program = new QGLShaderProgram(this); + m_contextObserver = new QGLFramebufferObject(QSize(2,2)); + + if (!m_contextObserver || !m_program) { + delete m_program; + m_program = 0; + delete m_contextObserver; + m_contextObserver = 0; + qWarning() << "ShaderEffectItem::renderEffect - Creating QGLShaderProgram or QGLFrameBufferObject failed!"; + } + } + } +#endif + + 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); @@ -434,7 +473,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,11 +511,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) @@ -487,10 +529,10 @@ void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) } if (m_respectsOpacity) - m_program.setUniformValue("qt_Opacity", static_cast<float> (effectiveOpacity())); + m_program->setUniformValue("qt_Opacity", static_cast<float> (effectiveOpacity())); if (m_respectsMatrix){ - m_program.setUniformValue("qt_ModelViewProjectionMatrix", matrix); + m_program->setUniformValue("qt_ModelViewProjectionMatrix", matrix); } QSet<QByteArray>::const_iterator it; @@ -500,37 +542,37 @@ void ShaderEffectItem::updateEffectState(const QMatrix4x4 &matrix) switch (v.type()) { case QVariant::Color: - m_program.setUniformValue(name.constData(), qvariant_cast<QColor>(v)); + m_program->setUniformValue(name.constData(), qvariant_cast<QColor>(v)); break; case QVariant::Double: - m_program.setUniformValue(name.constData(), (float) qvariant_cast<double>(v)); + m_program->setUniformValue(name.constData(), (float) qvariant_cast<double>(v)); break; case QVariant::Transform: - m_program.setUniformValue(name.constData(), qvariant_cast<QTransform>(v)); + m_program->setUniformValue(name.constData(), qvariant_cast<QTransform>(v)); break; case QVariant::Int: - m_program.setUniformValue(name.constData(), v.toInt()); + m_program->setUniformValue(name.constData(), GLint(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<QVector3D>(v)); + m_program->setUniformValue(name.constData(), qvariant_cast<QVector3D>(v)); break; default: break; @@ -558,6 +600,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) { @@ -574,7 +619,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); } @@ -657,6 +702,16 @@ void ShaderEffectItem::setActive(bool enable) } } + // QGLShaderProgram is deleted when not active (to minimize GPU memory usage). +#ifdef OBSERVE_GL_CONTEXT_LOSS + if (!m_active && m_program) { + delete m_program; + m_program = 0; + delete m_contextObserver; + m_contextObserver = 0; + } +#endif + emit activeChanged(); markDirty(); } @@ -776,7 +831,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) { @@ -821,6 +878,9 @@ void ShaderEffectItem::updateProperties() void ShaderEffectItem::updateShaderProgram() { + if (!m_program) + return; + QString vertexCode = m_vertex_code; QString fragmentCode = m_fragment_code; @@ -830,16 +890,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 +909,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(), (GLint) i); } m_program_dirty = false; diff --git a/src/imports/shaders/shadereffectitem.h b/src/imports/shaders/shadereffectitem.h index aebe897a35..6c225a299c 100644 --- a/src/imports/shaders/shadereffectitem.h +++ b/src/imports/shaders/shadereffectitem.h @@ -115,7 +115,7 @@ private: private: QString m_fragment_code; QString m_vertex_code; - QGLShaderProgram m_program; + QGLShaderProgram* m_program; QVector<const char *> m_attributeNames; QSet<QByteArray> m_uniformNames; QSize m_meshResolution; @@ -143,6 +143,8 @@ private: bool m_hasShaderPrograms : 1; bool m_mirrored : 1; bool m_defaultVertexShader : 1; + + QGLFramebufferObject* m_contextObserver; }; QT_END_HEADER diff --git a/src/imports/shaders/shadereffectsource.cpp b/src/imports/shaders/shadereffectsource.cpp index 6210c413b7..21d814a871 100644 --- a/src/imports/shaders/shadereffectsource.cpp +++ b/src/imports/shaders/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<ShaderEffect*> (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<ShaderEffect*> (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<ShaderEffect*> (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/imports/shaders/shadereffectsource.h b/src/imports/shaders/shadereffectsource.h index 0f03a6a39d..af8a8157ac 100644 --- a/src/imports/shaders/shadereffectsource.h +++ b/src/imports/shaders/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(); |