From 4eb50fbbf414103fb2f9dfb8260f4db5d61190c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Niemel=C3=A4?= Date: Wed, 24 Aug 2011 15:29:17 +0300 Subject: 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 --- src/imports/shaders/shadereffectitem.cpp | 110 ++++++++++++++++++++++------- src/imports/shaders/shadereffectitem.h | 4 +- src/imports/shaders/shadereffectsource.cpp | 43 ++++++----- src/imports/shaders/shadereffectsource.h | 3 +- 4 files changed, 116 insertions(+), 44 deletions(-) mode change 100644 => 100755 src/imports/shaders/shadereffectitem.cpp diff --git a/src/imports/shaders/shadereffectitem.cpp b/src/imports/shaders/shadereffectitem.cpp old mode 100644 new mode 100755 index 056581c..04c81f5 --- 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::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 (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 +542,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(), 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(v)); + m_program->setUniformValue(name.constData(), qvariant_cast(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 aebe897..6c225a2 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 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; + + QGLFramebufferObject* m_contextObserver; }; QT_END_HEADER diff --git a/src/imports/shaders/shadereffectsource.cpp b/src/imports/shaders/shadereffectsource.cpp index 6210c41..21d814a 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 (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/imports/shaders/shadereffectsource.h b/src/imports/shaders/shadereffectsource.h index 0f03a6a..af8a815 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(); -- cgit v0.12