diff options
Diffstat (limited to 'src/opengl')
23 files changed, 1290 insertions, 778 deletions
diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp index 866d1a2..1fe3999 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp +++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray.cpp @@ -67,7 +67,30 @@ void QGL2PEXVertexArray::addRect(const QRectF &rect) << rect.bottomRight() << rect.bottomLeft() << rect.topLeft(); } -void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale) +void QGL2PEXVertexArray::addClosingLine(int index) +{ + if (QPointF(vertexArray.at(index)) != QPointF(vertexArray.last())) + vertexArray.add(vertexArray.at(index)); +} + +void QGL2PEXVertexArray::addCentroid(const QVectorPath &path, int subPathIndex) +{ + const QPointF *const points = reinterpret_cast<const QPointF *>(path.points()); + const QPainterPath::ElementType *const elements = path.elements(); + + QPointF sum = points[subPathIndex]; + int count = 1; + + for (int i = subPathIndex + 1; i < path.elementCount() && (!elements || elements[i] != QPainterPath::MoveToElement); ++i) { + sum += points[i]; + ++count; + } + + const QPointF centroid = sum / qreal(count); + vertexArray.add(centroid); +} + +void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline) { const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); const QPainterPath::ElementType* const elements = path.elements(); @@ -78,6 +101,10 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc boundingRectDirty = false; } + if (!outline) + addCentroid(path, 0); + + int lastMoveTo = vertexArray.size(); vertexArray.add(points[0]); // The first element is always a moveTo do { @@ -96,8 +123,14 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc const QPainterPath::ElementType elementType = elements[i]; switch (elementType) { case QPainterPath::MoveToElement: + if (!outline) + addClosingLine(lastMoveTo); // qDebug("element[%d] is a MoveToElement", i); vertexArrayStops.append(vertexArray.size()); + if (!outline) { + addCentroid(path, i); + lastMoveTo = vertexArray.size(); + } lineToArray(points[i].x(), points[i].y()); // Add the moveTo as a new vertex break; case QPainterPath::LineToElement: @@ -115,6 +148,8 @@ void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseSc } } while (0); + if (!outline) + addClosingLine(lastMoveTo); vertexArrayStops.append(vertexArray.size()); } diff --git a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h index 08ce234..719904f 100644 --- a/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h +++ b/src/opengl/gl2paintengineex/qgl2pexvertexarray_p.h @@ -104,7 +104,7 @@ public: boundingRectDirty(true) {} void addRect(const QRectF &rect); - void addPath(const QVectorPath &path, GLfloat curveInverseScale); + void addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline = true); void clear(); QGLPoint* data() {return vertexArray.data();} @@ -124,6 +124,9 @@ private: bool boundingRectDirty; inline void curveToArray(const QGLPoint &cp1, const QGLPoint &cp2, const QGLPoint &ep, GLfloat inverseScale); + + void addClosingLine(int index); + void addCentroid(const QVectorPath &path, int subPathIndex); }; QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h index 25f5c2f..f8c13c5 100644 --- a/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h +++ b/src/opengl/gl2paintengineex/qglcustomshaderstage_p.h @@ -64,7 +64,7 @@ QT_MODULE(OpenGL) class QGLCustomShaderStagePrivate; class Q_OPENGL_EXPORT QGLCustomShaderStage { - Q_DECLARE_PRIVATE(QGLCustomShaderStage); + Q_DECLARE_PRIVATE(QGLCustomShaderStage) public: QGLCustomShaderStage(); virtual ~QGLCustomShaderStage(); diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index eceed06..e22303d 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -60,12 +60,8 @@ QGLEngineSharedShaders *QGLEngineSharedShaders::shadersForContext(const QGLConte { QGLEngineSharedShaders *p = reinterpret_cast<QGLEngineSharedShaders *>(qt_shared_shaders()->value(context)); if (!p) { - QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (oldContext != context) - const_cast<QGLContext *>(context)->makeCurrent(); + QGLShareContextScope scope(context); qt_shared_shaders()->insert(context, p = new QGLEngineSharedShaders(context)); - if (oldContext && oldContext != context) - oldContext->makeCurrent(); } return p; } @@ -78,7 +74,7 @@ const char* QGLEngineSharedShaders::qglEngineShaderSourceCode[] = { }; QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) - : ctx(QGLContextPrivate::contextGroup(context)) + : ctxGuard(context) , blitShaderProg(0) , simpleShaderProg(0) { @@ -97,6 +93,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) code[MainVertexShader] = qglslMainVertexShader; code[MainWithTexCoordsVertexShader] = qglslMainWithTexCoordsVertexShader; + code[MainWithTexCoordsAndOpacityVertexShader] = qglslMainWithTexCoordsAndOpacityVertexShader; code[UntransformedPositionVertexShader] = qglslUntransformedPositionVertexShader; code[PositionOnlyVertexShader] = qglslPositionOnlyVertexShader; @@ -119,6 +116,7 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context) code[MainFragmentShader_C] = qglslMainFragmentShader_C; code[MainFragmentShader_O] = qglslMainFragmentShader_O; code[MainFragmentShader] = qglslMainFragmentShader; + code[MainFragmentShader_ImageArrays] = qglslMainFragmentShader_ImageArrays; code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader; code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader; @@ -223,7 +221,7 @@ QGLShader *QGLEngineSharedShaders::compileNamedShader(ShaderName name, QGLShader return compiledShaders[name]; QByteArray source = qglEngineShaderSourceCode[name]; - QGLShader *newShader = new QGLShader(type, ctx->context(), this); + QGLShader *newShader = new QGLShader(type, ctxGuard.context(), this); newShader->compile(source); #if defined(QT_DEBUG) @@ -245,7 +243,7 @@ QGLShader *QGLEngineSharedShaders::compileCustomShader(QGLCustomShaderStage *sta if (newShader) return newShader; - newShader = new QGLShader(type, ctx->context(), this); + newShader = new QGLShader(type, ctxGuard.context(), this); newShader->compile(source); customShaderCache.insert(source, newShader); @@ -273,7 +271,7 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS 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 = new QGLShaderProgram(ctxGuard.context(), this); cached.program->addShader(cached.mainVertexShader); cached.program->addShader(cached.positionVertexShader); cached.program->addShader(cached.mainFragShader); @@ -285,6 +283,8 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS cached.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); if (cached.useTextureCoords) cached.program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR); + if (cached.useOpacityAttribute) + cached.program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR); cached.program->link(); if (!cached.program->isLinked()) { @@ -331,7 +331,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) : ctx(context), shaderProgNeedsChanging(true), srcPixelType(Qt::NoBrush), - useGlobalOpacity(false), + opacityMode(NoOpacity), maskType(NoMask), compositionMode(QPainter::CompositionMode_SourceOver), customSrcStage(0), @@ -407,12 +407,12 @@ void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type) shaderProgNeedsChanging = true; //### } -void QGLEngineShaderManager::setUseGlobalOpacity(bool useOpacity) +void QGLEngineShaderManager::setOpacityMode(OpacityMode mode) { - if (useGlobalOpacity == useOpacity) + if (opacityMode == mode) return; - useGlobalOpacity = useOpacity; + opacityMode = mode; shaderProgNeedsChanging = true; //### } @@ -564,22 +564,28 @@ bool QGLEngineShaderManager::useCorrectShaderProg() // Choose fragment shader main function: QGLEngineSharedShaders::ShaderName mainFragShaderName; - if (hasCompose && hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO; - if (hasCompose && hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM; - if (!hasCompose && hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO; - if (!hasCompose && hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M; - if (hasCompose && !hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO; - if (hasCompose && !hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C; - if (!hasCompose && !hasMask && useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O; - if (!hasCompose && !hasMask && !useGlobalOpacity) - mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader; + if (opacityMode == AttributeOpacity) { + Q_ASSERT(!hasCompose && !hasMask); + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_ImageArrays; + } else { + bool useGlobalOpacity = (opacityMode == UniformOpacity); + if (hasCompose && hasMask && useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CMO; + if (hasCompose && hasMask && !useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CM; + if (!hasCompose && hasMask && useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_MO; + if (!hasCompose && hasMask && !useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_M; + if (hasCompose && !hasMask && useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_CO; + if (hasCompose && !hasMask && !useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_C; + if (!hasCompose && !hasMask && useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader_O; + if (!hasCompose && !hasMask && !useGlobalOpacity) + mainFragShaderName = QGLEngineSharedShaders::MainFragmentShader; + } requiredProgram.mainFragShader = sharedShaders->compileNamedShader(mainFragShaderName, QGLShader::PartialFragmentShader); @@ -652,12 +658,17 @@ bool QGLEngineShaderManager::useCorrectShaderProg() // Choose vertex shader main function QGLEngineSharedShaders::ShaderName mainVertexShaderName = QGLEngineSharedShaders::InvalidShaderName; - if (texCoords) + if (opacityMode == AttributeOpacity) { + Q_ASSERT(texCoords); + mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsAndOpacityVertexShader; + } else if (texCoords) { mainVertexShaderName = QGLEngineSharedShaders::MainWithTexCoordsVertexShader; - else + } else { mainVertexShaderName = QGLEngineSharedShaders::MainVertexShader; + } requiredProgram.mainVertexShader = sharedShaders->compileNamedShader(mainVertexShaderName, QGLShader::PartialVertexShader); requiredProgram.useTextureCoords = texCoords; + requiredProgram.useOpacityAttribute = (opacityMode == AttributeOpacity); // At this point, requiredProgram is fully populated so try to find the program in the cache diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 47d9a2a..291d24c 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -247,6 +247,7 @@ struct QGLEngineShaderProg QVector<uint> uniformLocations; bool useTextureCoords; + bool useOpacityAttribute; bool operator==(const QGLEngineShaderProg& other) { // We don't care about the program @@ -277,6 +278,7 @@ struct QGLEngineCachedShaderProg static const GLuint QT_VERTEX_COORDS_ATTR = 0; static const GLuint QT_TEXTURE_COORDS_ATTR = 1; +static const GLuint QT_OPACITY_ATTR = 2; class QGLEngineSharedShaders : public QObject { @@ -285,6 +287,7 @@ public: enum ShaderName { MainVertexShader, MainWithTexCoordsVertexShader, + MainWithTexCoordsAndOpacityVertexShader, UntransformedPositionVertexShader, PositionOnlyVertexShader, @@ -307,6 +310,7 @@ public: MainFragmentShader_C, MainFragmentShader_O, MainFragmentShader, + MainFragmentShader_ImageArrays, ImageSrcFragmentShader, ImageSrcWithPatternFragmentShader, @@ -361,7 +365,7 @@ private slots: void shaderDestroyed(QObject *shader); private: - QGLContextGroup *ctx; + QGLSharedResourceGuard ctxGuard; QGLShaderProgram *blitShaderProg; QGLShaderProgram *simpleShaderProg; QList<QGLEngineShaderProg> cachedPrograms; @@ -406,13 +410,19 @@ public: NumUniforms }; + enum OpacityMode { + NoOpacity, + UniformOpacity, + AttributeOpacity + }; + // 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 setUseGlobalOpacity(bool); + void setOpacityMode(OpacityMode); void setMaskType(MaskType); void setCompositionMode(QPainter::CompositionMode); void setCustomStage(QGLCustomShaderStage* stage); @@ -451,7 +461,7 @@ private: // Current state variables which influence the choice of shader: QTransform brushTransform; int srcPixelType; - bool useGlobalOpacity; + OpacityMode opacityMode; MaskType maskType; QPainter::CompositionMode compositionMode; QGLCustomShaderStage* customSrcStage; diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h index 8ae86d3..6712bf6 100644 --- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h @@ -84,6 +84,20 @@ static const char* const qglslMainWithTexCoordsVertexShader = "\ textureCoords = textureCoordArray; \ }"; +static const char* const qglslMainWithTexCoordsAndOpacityVertexShader = "\ + attribute highp vec2 textureCoordArray; \ + attribute lowp float opacityArray; \ + varying highp vec2 textureCoords; \ + varying lowp float opacity; \ + uniform highp float depth; \ + void setPosition(); \ + void main(void) \ + { \ + setPosition(); \ + gl_Position.z = depth * gl_Position.w; \ + textureCoords = textureCoordArray; \ + opacity = opacityArray; \ + }"; static const char* const qglslPositionOnlyVertexShader = "\ attribute highp vec4 vertexCoordsArray;\ @@ -331,6 +345,12 @@ static const char* const qglslShockingPinkSrcFragmentShader = "\ return vec4(0.98, 0.06, 0.75, 1.0); \ }"; +static const char* const qglslMainFragmentShader_ImageArrays = "\ + varying lowp float opacity; \ + lowp vec4 srcPixel(); \ + void main() { \ + gl_FragColor = srcPixel() * opacity; \ + }"; static const char* const qglslMainFragmentShader_CMO = "\ uniform lowp float globalOpacity; \ diff --git a/src/opengl/gl2paintengineex/qglgradientcache.cpp b/src/opengl/gl2paintengineex/qglgradientcache.cpp index 4b2b2a0..e06f15d 100644 --- a/src/opengl/gl2paintengineex/qglgradientcache.cpp +++ b/src/opengl/gl2paintengineex/qglgradientcache.cpp @@ -57,13 +57,9 @@ QGL2GradientCache *QGL2GradientCache::cacheForContext(const QGLContext *context) { QGL2GradientCache *p = reinterpret_cast<QGL2GradientCache *>(qt_gradient_caches()->value(context)); if (!p) { - QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (oldContext != context) - const_cast<QGLContext *>(context)->makeCurrent(); + QGLShareContextScope scope(context); p = new QGL2GradientCache; qt_gradient_caches()->insert(context, p); - if (oldContext && oldContext != context) - oldContext->makeCurrent(); } return p; } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 7e45fd9..8130151 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -87,6 +87,7 @@ QT_BEGIN_NAMESPACE //#define QT_GL_NO_SCISSOR_TEST +static const GLuint GL_STENCIL_HIGH_BIT = 0x80; static const GLuint QT_BRUSH_TEXTURE_UNIT = 0; static const GLuint QT_IMAGE_TEXTURE_UNIT = 0; //Can be the same as brush texture unit static const GLuint QT_MASK_TEXTURE_UNIT = 1; @@ -162,15 +163,11 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyph QGLTextureGlyphCache::~QGLTextureGlyphCache() { if (ctx) { - QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (oldContext != ctx) - ctx->makeCurrent(); + QGLShareContextScope scope(ctx); glDeleteFramebuffers(1, &m_fbo); if (m_width || m_height) glDeleteTextures(1, &m_texture); - if (oldContext && oldContext != ctx) - oldContext->makeCurrent(); } } @@ -231,6 +228,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) glPushAttrib(GL_ENABLE_BIT | GL_VIEWPORT_BIT | GL_SCISSOR_BIT); #endif + glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDisable(GL_BLEND); @@ -280,7 +278,7 @@ void QGLTextureGlyphCache::resizeTextureData(int width, int height) glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); glViewport(0, 0, pex->width, pex->height); - pex->updateDepthScissorTest(); + pex->updateClipScissorTest(); #ifndef QT_OPENGL_ES_2 if (pex->inRenderText) @@ -366,7 +364,7 @@ void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMod } -QColor QGL2PaintEngineExPrivate::premultiplyColor(QColor c, GLfloat opacity) +inline QColor qt_premultiplyColor(QColor c, GLfloat opacity) { qreal alpha = c.alphaF() * opacity; c.setAlphaF(alpha); @@ -393,7 +391,6 @@ void QGL2PaintEngineExPrivate::setBrush(const QBrush* brush) } -// Unless this gets used elsewhere, it's probably best to merge it into fillStencilWithVertexArray void QGL2PaintEngineExPrivate::useSimpleShader() { shaderManager->simpleProgram()->enable(); @@ -406,11 +403,6 @@ void QGL2PaintEngineExPrivate::useSimpleShader() shaderManager->simpleProgram()->setUniformValue("pmvMatrix", pmvMatrix); simpleShaderMatrixUniformDirty = false; } - - if (simpleShaderDepthUniformDirty) { - shaderManager->simpleProgram()->setUniformValue("depth", normalizedDeviceDepth(q->state()->currentDepth)); - simpleShaderDepthUniformDirty = false; - } } void QGL2PaintEngineExPrivate::updateBrushTexture() @@ -469,7 +461,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() QTransform brushQTransform = currentBrush->transform(); if (style == Qt::SolidPattern) { - QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); + QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::FragmentColor), col); } else { @@ -479,7 +471,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() if (style <= Qt::DiagCrossPattern) { translationPoint = q->state()->brushOrigin; - QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); + QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); @@ -541,7 +533,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms() const QPixmap& texPixmap = currentBrush->texture(); if (qHasPixmapTexture(*currentBrush) && currentBrush->texture().isQBitmap()) { - QColor col = premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); + QColor col = qt_premultiplyColor(currentBrush->color(), (GLfloat)q->state()->opacity); shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); } @@ -712,7 +704,7 @@ void QGL2PaintEngineExPrivate::drawTexture(const QGLRect& dest, const QGLRect& s shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); if (pattern) { - QColor col = premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); + QColor col = qt_premultiplyColor(q->state()->pen.color(), (GLfloat)q->state()->opacity); shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col); } @@ -766,6 +758,8 @@ void QGL2PaintEngineEx::beginNativePainting() d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); d->resetGLState(); + d->shaderManager->setDirty(); + d->needsSync = true; } @@ -773,9 +767,11 @@ void QGL2PaintEngineExPrivate::resetGLState() { glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE0); + glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); glDepthMask(true); + glDepthFunc(GL_LESS); glClearDepth(1); } @@ -796,9 +792,10 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) if (newMode == mode) return; - if (mode == TextDrawingMode || mode == ImageDrawingMode) { + if (mode == TextDrawingMode || mode == ImageDrawingMode || mode == ImageArrayDrawingMode) { glDisableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glDisableVertexAttribArray(QT_OPACITY_ATTR); lastTexture = GLuint(-1); } @@ -824,6 +821,16 @@ void QGL2PaintEngineExPrivate::transferMode(EngineMode newMode) glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, staticTextureCoordinateArray); } + if (newMode == ImageArrayDrawingMode) { + glEnableVertexAttribArray(QT_VERTEX_COORDS_ATTR); + glEnableVertexAttribArray(QT_TEXTURE_COORDS_ATTR); + glEnableVertexAttribArray(QT_OPACITY_ATTR); + + glVertexAttribPointer(QT_VERTEX_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, vertexCoordinateArray.data()); + glVertexAttribPointer(QT_TEXTURE_COORDS_ATTR, 2, GL_FLOAT, GL_FALSE, 0, textureCoordinateArray.data()); + glVertexAttribPointer(QT_OPACITY_ATTR, 1, GL_FLOAT, GL_FALSE, 0, opacityArray.data()); + } + // This needs to change when we implement high-quality anti-aliasing... if (newMode != TextDrawingMode) shaderManager->setMaskType(QGLEngineShaderManager::NoMask); @@ -868,37 +875,49 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) if (path.shape() == QVectorPath::RectangleHint) { QGLRect rect(points[0].x(), points[0].y(), points[2].x(), points[2].y()); prepareForDraw(currentBrush->isOpaque()); - composite(rect); - } - else if (path.shape() == QVectorPath::EllipseHint) { + } else if (path.shape() == QVectorPath::EllipseHint + || path.shape() == QVectorPath::ConvexPolygonHint) + { vertexCoordinateArray.clear(); - vertexCoordinateArray.addPath(path, inverseScale); + vertexCoordinateArray.addPath(path, inverseScale, false); prepareForDraw(currentBrush->isOpaque()); drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN); - } - else { + } else { // The path is too complicated & needs the stencil technique vertexCoordinateArray.clear(); - vertexCoordinateArray.addPath(path, inverseScale); + vertexCoordinateArray.addPath(path, inverseScale, false); fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); - // Stencil the brush onto the dest buffer - glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0 - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + glStencilMask(0xff); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + + if (q->state()->clipTestEnabled) { + // Pass when high bit is set, replace stencil value with current clip + glStencilFunc(GL_NOTEQUAL, q->state()->currentClip, GL_STENCIL_HIGH_BIT); + } else if (path.hasWindingFill()) { + // Pass when any bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, 0xff); + } else { + // Pass when high bit is set, replace stencil value with 0 + glStencilFunc(GL_NOTEQUAL, 0, GL_STENCIL_HIGH_BIT); + } - glEnable(GL_STENCIL_TEST); prepareForDraw(currentBrush->isOpaque()); -#ifndef QT_OPENGL_ES_2 if (inRenderText) - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText()); -#endif + prepareDepthRangeForRenderText(); + + // Stencil the brush onto the dest buffer composite(vertexCoordinateArray.boundingRect()); - glDisable(GL_STENCIL_TEST); + + if (inRenderText) + restoreDepthRangeForRenderText(); glStencilMask(0); + + updateClipScissorTest(); } } @@ -906,31 +925,28 @@ void QGL2PaintEngineExPrivate::fill(const QVectorPath& path) void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& vertexArray, bool useWindingFill) { // qDebug("QGL2PaintEngineExPrivate::fillStencilWithVertexArray()"); - glStencilMask(0xFFFF); // Enable stencil writes + glStencilMask(0xff); // Enable stencil writes if (dirtyStencilRegion.intersects(currentScissorBounds)) { - // Clear the stencil buffer to zeros - glDisable(GL_STENCIL_TEST); + QVector<QRect> clearRegion = dirtyStencilRegion.intersected(currentScissorBounds).rects(); glClearStencil(0); // Clear to zero - glClear(GL_STENCIL_BUFFER_BIT); + for (int i = 0; i < clearRegion.size(); ++i) { +#ifndef QT_GL_NO_SCISSOR_TEST + setScissor(clearRegion.at(i)); +#endif + glClear(GL_STENCIL_BUFFER_BIT); + } + dirtyStencilRegion -= currentScissorBounds; + +#ifndef QT_GL_NO_SCISSOR_TEST + updateClipScissorTest(); +#endif } glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // Disable color writes - glStencilFunc(GL_ALWAYS, 0, 0xFFFF); // Always pass the stencil test - - // Setup the stencil op: - if (useWindingFill) { - glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR_WRAP); // Inc. for front-facing triangle - glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR_WRAP); //Dec. for back-facing "holes" - } else - glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit - - // No point in using a fancy gradient shader for writing into the stencil buffer! useSimpleShader(); - glEnable(GL_STENCIL_TEST); // For some reason, this has to happen _after_ the simple shader is use()'d - glDisable(GL_BLEND); #ifndef QT_OPENGL_ES_2 if (inRenderText) { @@ -939,21 +955,94 @@ void QGL2PaintEngineExPrivate::fillStencilWithVertexArray(QGL2PEXVertexArray& ve } #endif - // Draw the vertecies into the stencil buffer: - drawVertexArrays(vertexArray, GL_TRIANGLE_FAN); + if (useWindingFill) { + if (q->state()->clipTestEnabled) { + // Flatten clip values higher than current clip, and set high bit to match current clip + glStencilFunc(GL_LEQUAL, GL_STENCIL_HIGH_BIT | q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + composite(vertexArray.boundingRect()); + + glStencilFunc(GL_EQUAL, GL_STENCIL_HIGH_BIT, GL_STENCIL_HIGH_BIT); + } else if (!stencilClean) { + // Clear stencil buffer within bounding rect + glStencilFunc(GL_ALWAYS, 0, 0xff); + glStencilOp(GL_ZERO, GL_ZERO, GL_ZERO); + composite(vertexArray.boundingRect()); + } + + // Inc. for front-facing triangle + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_INCR_WRAP); + // Dec. for back-facing "holes" + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_DECR_WRAP); + glStencilMask(~GL_STENCIL_HIGH_BIT); + drawVertexArrays(vertexArray, GL_TRIANGLE_FAN); + + if (q->state()->clipTestEnabled) { + // Clear high bit of stencil outside of path + glStencilFunc(GL_EQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + glStencilMask(GL_STENCIL_HIGH_BIT); + composite(vertexArray.boundingRect()); + } + } else { + glStencilMask(GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // Simply invert the stencil bit + drawVertexArrays(vertexArray, GL_TRIANGLE_FAN); + } + + // Enable color writes & disable stencil writes + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); #ifndef QT_OPENGL_ES_2 if (inRenderText) glPopAttrib(); #endif - // Enable color writes & disable stencil writes +} + +/* + If the maximum value in the stencil buffer is GL_STENCIL_HIGH_BIT - 1, + restore the stencil buffer to a pristine state. The current clip region + is set to 1, and the rest to 0. +*/ +void QGL2PaintEngineExPrivate::resetClipIfNeeded() +{ + if (maxClip != (GL_STENCIL_HIGH_BIT - 1)) + return; + + Q_Q(QGL2PaintEngineEx); + + useSimpleShader(); + glEnable(GL_STENCIL_TEST); + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + + QRectF bounds = q->state()->matrix.inverted().mapRect(QRectF(0, 0, width, height)); + QGLRect rect(bounds.left(), bounds.top(), bounds.right(), bounds.bottom()); + + // Set high bit on clip region + glStencilFunc(GL_LEQUAL, q->state()->currentClip, 0xff); + glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + glStencilMask(GL_STENCIL_HIGH_BIT); + composite(rect); + + // Reset clipping to 1 and everything else to zero + glStencilFunc(GL_NOTEQUAL, 0x01, GL_STENCIL_HIGH_BIT); + glStencilOp(GL_ZERO, GL_REPLACE, GL_REPLACE); + glStencilMask(0xff); + composite(rect); + + q->state()->currentClip = 1; + q->state()->canRestoreClip = false; + + maxClip = 1; + + glStencilMask(0x0); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); } bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) { - if (brushTextureDirty && mode != ImageDrawingMode) + if (brushTextureDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) updateBrushTexture(); if (compositionModeDirty) @@ -972,16 +1061,22 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) glEnable(GL_BLEND); } - bool useGlobalOpacityUniform = stateHasOpacity; - if (stateHasOpacity && (mode != ImageDrawingMode)) { - // Using a brush - bool brushIsPattern = (currentBrush->style() >= Qt::Dense1Pattern) && - (currentBrush->style() <= Qt::DiagCrossPattern); - - if ((currentBrush->style() == Qt::SolidPattern) || brushIsPattern) - useGlobalOpacityUniform = false; // Global opacity handled by srcPixel shader + QGLEngineShaderManager::OpacityMode opacityMode; + if (mode == ImageArrayDrawingMode) { + opacityMode = QGLEngineShaderManager::AttributeOpacity; + } else { + opacityMode = stateHasOpacity ? QGLEngineShaderManager::UniformOpacity + : QGLEngineShaderManager::NoOpacity; + if (stateHasOpacity && (mode != ImageDrawingMode)) { + // Using a brush + bool brushIsPattern = (currentBrush->style() >= Qt::Dense1Pattern) && + (currentBrush->style() <= Qt::DiagCrossPattern); + + if ((currentBrush->style() == Qt::SolidPattern) || brushIsPattern) + opacityMode = QGLEngineShaderManager::NoOpacity; // Global opacity handled by srcPixel shader + } } - shaderManager->setUseGlobalOpacity(useGlobalOpacityUniform); + shaderManager->setOpacityMode(opacityMode); bool changed = shaderManager->useCorrectShaderProg(); // If the shader program needs changing, we change it and mark all uniforms as dirty @@ -989,11 +1084,10 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) // The shader program has changed so mark all uniforms as dirty: brushUniformsDirty = true; shaderMatrixUniformDirty = true; - depthUniformDirty = true; opacityUniformDirty = true; } - if (brushUniformsDirty && mode != ImageDrawingMode) + if (brushUniformsDirty && mode != ImageDrawingMode && mode != ImageArrayDrawingMode) updateBrushUniforms(); if (shaderMatrixUniformDirty) { @@ -1001,12 +1095,7 @@ bool QGL2PaintEngineExPrivate::prepareForDraw(bool srcPixelsAreOpaque) shaderMatrixUniformDirty = false; } - if (depthUniformDirty) { - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), normalizedDeviceDepth(q->state()->currentDepth)); - depthUniformDirty = false; - } - - if (useGlobalOpacityUniform && opacityUniformDirty) { + if (opacityMode == QGLEngineShaderManager::UniformOpacity && opacityUniformDirty) { shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::GlobalOpacity), (GLfloat)q->state()->opacity); opacityUniformDirty = false; } @@ -1052,7 +1141,7 @@ void QGL2PaintEngineExPrivate::drawVertexArrays(QGL2PEXVertexArray& vertexArray, glDisableVertexAttribArray(QT_VERTEX_COORDS_ATTR); } -float QGL2PaintEngineExPrivate::zValueForRenderText() const +void QGL2PaintEngineExPrivate::prepareDepthRangeForRenderText() { #ifndef QT_OPENGL_ES_2 // Get the z translation value from the model view matrix and @@ -1060,9 +1149,19 @@ float QGL2PaintEngineExPrivate::zValueForRenderText() const // and z-far = 1, which is used in QGLWidget::renderText() GLdouble model[4][4]; glGetDoublev(GL_MODELVIEW_MATRIX, &model[0][0]); - return -2 * model[3][2] - 1; -#else - return 0; + float deviceZ = -2 * model[3][2] - 1; + + glGetFloatv(GL_DEPTH_RANGE, depthRange); + float windowZ = depthRange[0] + (deviceZ + 1) * 0.5 * (depthRange[1] - depthRange[0]); + + glDepthRange(windowZ, windowZ); +#endif +} + +void QGL2PaintEngineExPrivate::restoreDepthRangeForRenderText() +{ +#ifndef QT_OPENGL_ES_2 + glDepthRange(depthRange[0], depthRange[1]); #endif } @@ -1125,6 +1224,7 @@ void QGL2PaintEngineEx::opacityChanged() { // qDebug("QGL2PaintEngineEx::opacityChanged()"); Q_D(QGL2PaintEngineEx); + state()->opacityChanged = true; Q_ASSERT(d->shaderManager); d->brushUniformsDirty = true; @@ -1135,11 +1235,14 @@ void QGL2PaintEngineEx::compositionModeChanged() { // qDebug("QGL2PaintEngineEx::compositionModeChanged()"); Q_D(QGL2PaintEngineEx); + state()->compositionModeChanged = true; d->compositionModeDirty = true; } void QGL2PaintEngineEx::renderHintsChanged() { + state()->renderHintsChanged = true; + #if !defined(QT_OPENGL_ES_2) if ((state()->renderHints & QPainter::Antialiasing) || (state()->renderHints & QPainter::HighQualityAntialiasing)) @@ -1158,6 +1261,7 @@ void QGL2PaintEngineEx::transformChanged() { Q_D(QGL2PaintEngineEx); d->matrixDirty = true; + state()->matrixChanged = true; } @@ -1245,6 +1349,9 @@ void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem ? QFontEngineGlyphCache::Type(ti.fontEngine->glyphFormat) : d->glyphCacheType; + if (d->inRenderText) + glyphType = QFontEngineGlyphCache::Raster_A8; + if (glyphType == QFontEngineGlyphCache::Raster_RGBMask && state()->composition_mode != QPainter::CompositionMode_Source && state()->composition_mode != QPainter::CompositionMode_SourceOver) @@ -1317,6 +1424,9 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly QBrush pensBrush = q->state()->pen.brush(); setBrush(&pensBrush); + if (inRenderText) + prepareDepthRangeForRenderText(); + if (glyphType == QFontEngineGlyphCache::Raster_RGBMask) { // Subpixel antialiasing without gamma correction @@ -1332,7 +1442,7 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly QColor c = pensBrush.color(); qreal oldOpacity = q->state()->opacity; if (compMode == QPainter::CompositionMode_Source) { - c = premultiplyColor(c, q->state()->opacity); + c = qt_premultiplyColor(c, q->state()->opacity); q->state()->opacity = 1; opacityUniformDirty = true; } @@ -1369,10 +1479,6 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly glBindTexture(GL_TEXTURE_2D, cache->texture()); updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); -#ifndef QT_OPENGL_ES_2 - if (inRenderText) - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText()); -#endif shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT); glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size()); @@ -1403,12 +1509,101 @@ void QGL2PaintEngineExPrivate::drawCachedGlyphs(const QPointF &p, QFontEngineGly glBindTexture(GL_TEXTURE_2D, cache->texture()); updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, false); -#ifndef QT_OPENGL_ES_2 - if (inRenderText) - shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::Depth), zValueForRenderText()); -#endif shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::MaskTexture), QT_MASK_TEXTURE_UNIT); glDrawArrays(GL_TRIANGLES, 0, 6 * glyphs.size()); + + if (inRenderText) + restoreDepthRangeForRenderText(); +} + +void QGL2PaintEngineEx::drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints) +{ + // Use fallback for extended composition modes. + if (state()->composition_mode > QPainter::CompositionMode_Plus) { + QPaintEngineEx::drawPixmaps(drawingData, dataCount, pixmap, hints); + return; + } + + Q_D(QGL2PaintEngineEx); + + GLfloat dx = 1.0f / pixmap.size().width(); + GLfloat dy = 1.0f / pixmap.size().height(); + + d->vertexCoordinateArray.clear(); + d->textureCoordinateArray.clear(); + d->opacityArray.reset(); + + bool allOpaque = true; + + for (int i = 0; i < dataCount; ++i) { + qreal s = 0; + qreal c = 1; + if (drawingData[i].rotation != 0) { + s = qFastSin(drawingData[i].rotation * Q_PI / 180); + c = qFastCos(drawingData[i].rotation * Q_PI / 180); + } + + qreal right = 0.5 * drawingData[i].scaleX * drawingData[i].source.width(); + qreal bottom = 0.5 * drawingData[i].scaleY * drawingData[i].source.height(); + QGLPoint bottomRight(right * c - bottom * s, right * s + bottom * c); + QGLPoint bottomLeft(-right * c - bottom * s, -right * s + bottom * c); + + d->vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y()); + d->vertexCoordinateArray.lineToArray(-bottomLeft.x + drawingData[i].point.x(), -bottomLeft.y + drawingData[i].point.y()); + d->vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y()); + d->vertexCoordinateArray.lineToArray(-bottomRight.x + drawingData[i].point.x(), -bottomRight.y + drawingData[i].point.y()); + d->vertexCoordinateArray.lineToArray(bottomLeft.x + drawingData[i].point.x(), bottomLeft.y + drawingData[i].point.y()); + d->vertexCoordinateArray.lineToArray(bottomRight.x + drawingData[i].point.x(), bottomRight.y + drawingData[i].point.y()); + + QGLRect src(drawingData[i].source.left() * dx, drawingData[i].source.top() * dy, + drawingData[i].source.right() * dx, drawingData[i].source.bottom() * dy); + + d->textureCoordinateArray.lineToArray(src.right, src.bottom); + d->textureCoordinateArray.lineToArray(src.right, src.top); + d->textureCoordinateArray.lineToArray(src.left, src.top); + d->textureCoordinateArray.lineToArray(src.left, src.top); + d->textureCoordinateArray.lineToArray(src.left, src.bottom); + d->textureCoordinateArray.lineToArray(src.right, src.bottom); + + qreal opacity = drawingData[i].opacity * state()->opacity; + d->opacityArray << opacity << opacity << opacity << opacity << opacity << opacity; + allOpaque &= (opacity >= 0.99f); + } + + ensureActive(); + + QGLContext *ctx = d->ctx; + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + QGLTexture *texture = ctx->d_func()->bindTexture(pixmap, GL_TEXTURE_2D, GL_RGBA, + QGLContext::InternalBindOption + | QGLContext::CanFlipNativePixmapBindOption); + + if (texture->options & QGLContext::InvertedYBindOption) { + // Flip texture y-coordinate. + QGLPoint *data = d->textureCoordinateArray.data(); + for (int i = 0; i < 6 * dataCount; ++i) + data[i].y = 1 - data[i].y; + } + + d->transferMode(ImageArrayDrawingMode); + + bool isBitmap = pixmap.isQBitmap(); + bool isOpaque = !isBitmap && (!pixmap.hasAlphaChannel() || (hints & QDrawPixmaps::OpaqueHint)) && allOpaque; + + d->updateTextureFilter(GL_TEXTURE_2D, GL_CLAMP_TO_EDGE, + state()->renderHints & QPainter::SmoothPixmapTransform, texture->id); + + // Setup for texture drawing + d->shaderManager->setSrcPixelType(isBitmap ? QGLEngineShaderManager::PatternSrc : QGLEngineShaderManager::ImageSrc); + if (d->prepareForDraw(isOpaque)) + d->shaderManager->currentProgram()->setUniformValue(d->location(QGLEngineShaderManager::ImageTexture), QT_IMAGE_TEXTURE_UNIT); + + if (isBitmap) { + QColor col = qt_premultiplyColor(state()->pen.color(), (GLfloat)state()->opacity); + d->shaderManager->currentProgram()->setUniformValue(d->location(QGLEngineShaderManager::PatternColor), col); + } + + glDrawArrays(GL_TRIANGLES, 0, 6 * dataCount); } bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) @@ -1430,19 +1625,17 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) const QSize sz = d->device->size(); d->width = sz.width(); d->height = sz.height(); - d->last_created_state = 0; d->mode = BrushDrawingMode; d->brushTextureDirty = true; d->brushUniformsDirty = true; d->matrixDirty = true; d->compositionModeDirty = true; - d->simpleShaderDepthUniformDirty = true; - d->depthUniformDirty = true; d->opacityUniformDirty = true; d->needsSync = true; d->use_system_clip = !systemClip().isEmpty(); d->dirtyStencilRegion = QRect(0, 0, d->width, d->height); + d->stencilClean = true; // Calling begin paint should make the correct context current. So, any // code which calls into GL or otherwise needs a current context *must* @@ -1452,15 +1645,15 @@ bool QGL2PaintEngineEx::begin(QPaintDevice *pdev) #if !defined(QT_OPENGL_ES_2) bool success = qt_resolve_version_2_0_functions(d->ctx); Q_ASSERT(success); + Q_UNUSED(success); #endif d->shaderManager = new QGLEngineShaderManager(d->ctx); if (!d->inRenderText) { + glDisable(GL_STENCIL_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_SCISSOR_TEST); - glDepthFunc(GL_LESS); - glDepthMask(false); } #if !defined(QT_OPENGL_ES_2) @@ -1524,22 +1717,26 @@ void QGL2PaintEngineEx::ensureActive() if (d->needsSync) { d->transferMode(BrushDrawingMode); glViewport(0, 0, d->width, d->height); - glDepthMask(false); - glDepthFunc(GL_LESS); d->needsSync = false; + d->shaderManager->setDirty(); setState(state()); } } -void QGL2PaintEngineExPrivate::updateDepthScissorTest() +void QGL2PaintEngineExPrivate::updateClipScissorTest() { Q_Q(QGL2PaintEngineEx); - if (q->state()->depthTestEnabled) - glEnable(GL_DEPTH_TEST); - else - glDisable(GL_DEPTH_TEST); + if (q->state()->clipTestEnabled) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + } else { + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0xff); + } -#ifndef QT_GL_NO_SCISSOR_TEST +#ifdef QT_GL_NO_SCISSOR_TEST + currentScissorBounds = QRect(0, 0, width, height); +#else QRect bounds = q->state()->rectangleClip; if (!q->state()->clipEnabled) { if (use_system_clip) @@ -1578,69 +1775,93 @@ void QGL2PaintEngineEx::clipEnabledChanged() { Q_D(QGL2PaintEngineEx); - d->simpleShaderDepthUniformDirty = true; - d->depthUniformDirty = true; + state()->clipChanged = true; - if (painter()->hasClipping()) { - d->regenerateDepthClip(); - } else { - if (d->use_system_clip) { - state()->currentDepth = 0; - } else { - state()->depthTestEnabled = false; - } + if (painter()->hasClipping()) + d->regenerateClip(); + else + d->systemStateChanged(); +} - d->updateDepthScissorTest(); - } +void QGL2PaintEngineExPrivate::clearClip(uint value) +{ + dirtyStencilRegion -= currentScissorBounds; + + glStencilMask(0xff); + glClearStencil(value); + glClear(GL_STENCIL_BUFFER_BIT); + glStencilMask(0x0); + + q->state()->needsClipBufferClear = false; } -void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint depth) +void QGL2PaintEngineExPrivate::writeClip(const QVectorPath &path, uint value) { transferMode(BrushDrawingMode); if (matrixDirty) updateMatrix(); - if (q->state()->needsDepthBufferClear) { - glDepthMask(true); - glClearDepth(rawDepth(2)); - glClear(GL_DEPTH_BUFFER_BIT); - q->state()->needsDepthBufferClear = false; - glDepthMask(false); - } - if (path.isEmpty()) + stencilClean = false; + + const bool singlePass = !path.hasWindingFill() + && (((q->state()->currentClip == maxClip - 1) && q->state()->clipTestEnabled) + || q->state()->needsClipBufferClear); + const uint referenceClipValue = q->state()->needsClipBufferClear ? 1 : q->state()->currentClip; + + if (q->state()->needsClipBufferClear) + clearClip(1); + + if (path.isEmpty()) { + glEnable(GL_STENCIL_TEST); + glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); return; + } - glDisable(GL_BLEND); - glDepthMask(false); + if (q->state()->clipTestEnabled) + glStencilFunc(GL_LEQUAL, q->state()->currentClip, ~GL_STENCIL_HIGH_BIT); + else + glStencilFunc(GL_ALWAYS, 0, 0xff); vertexCoordinateArray.clear(); - vertexCoordinateArray.addPath(path, inverseScale); + vertexCoordinateArray.addPath(path, inverseScale, false); - glDepthMask(GL_FALSE); - fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); + if (!singlePass) + fillStencilWithVertexArray(vertexCoordinateArray, path.hasWindingFill()); - // Stencil the clip onto the clip buffer glColorMask(false, false, false, false); - glDepthMask(true); + glEnable(GL_STENCIL_TEST); + useSimpleShader(); - shaderManager->simpleProgram()->setUniformValue("depth", normalizedDeviceDepth(depth)); - simpleShaderDepthUniformDirty = true; + if (singlePass) { + // Under these conditions we can set the new stencil value in a single + // pass, by using the current value and the "new value" as the toggles - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); + glStencilFunc(GL_LEQUAL, referenceClipValue, ~GL_STENCIL_HIGH_BIT); + glStencilOp(GL_KEEP, GL_INVERT, GL_INVERT); + glStencilMask(value ^ referenceClipValue); - glStencilFunc(GL_NOTEQUAL, 0, 0xFFFF); // Pass if stencil buff value != 0 - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + drawVertexArrays(vertexCoordinateArray, GL_TRIANGLE_FAN); + } else { + glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); + glStencilMask(0xff); - glEnable(GL_STENCIL_TEST); - composite(vertexCoordinateArray.boundingRect()); - glDisable(GL_STENCIL_TEST); + if (!q->state()->clipTestEnabled && path.hasWindingFill()) { + // Pass when any clip bit is set, set high bit + glStencilFunc(GL_NOTEQUAL, GL_STENCIL_HIGH_BIT, ~GL_STENCIL_HIGH_BIT); + composite(vertexCoordinateArray.boundingRect()); + } + + // Pass when high bit is set, replace stencil value with new clip value + glStencilFunc(GL_NOTEQUAL, value, GL_STENCIL_HIGH_BIT); + + composite(vertexCoordinateArray.boundingRect()); + } + glStencilFunc(GL_LEQUAL, value, ~GL_STENCIL_HIGH_BIT); glStencilMask(0); glColorMask(true, true, true, true); - glDepthMask(false); } void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) @@ -1648,6 +1869,8 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) // qDebug("QGL2PaintEngineEx::clip()"); Q_D(QGL2PaintEngineEx); + state()->clipChanged = true; + ensureActive(); if (op == Qt::ReplaceClip) { @@ -1665,7 +1888,7 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) if (state()->matrix.type() <= QTransform::TxScale) { state()->rectangleClip = state()->rectangleClip.intersected(state()->matrix.mapRect(rect).toRect()); - d->updateDepthScissorTest(); + d->updateClipScissorTest(); return; } } @@ -1676,43 +1899,44 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) switch (op) { case Qt::NoClip: if (d->use_system_clip) { - state()->depthTestEnabled = true; - state()->currentDepth = 0; + state()->clipTestEnabled = true; + state()->currentClip = 1; } else { - state()->depthTestEnabled = false; + state()->clipTestEnabled = false; } state()->rectangleClip = QRect(0, 0, d->width, d->height); state()->canRestoreClip = false; - d->updateDepthScissorTest(); + d->updateClipScissorTest(); break; case Qt::IntersectClip: state()->rectangleClip = state()->rectangleClip.intersected(pathRect); - d->updateDepthScissorTest(); - ++state()->maxDepth; - d->writeClip(path, state()->maxDepth); - state()->currentDepth = state()->maxDepth - 1; - state()->depthTestEnabled = true; + d->updateClipScissorTest(); + d->resetClipIfNeeded(); + ++d->maxClip; + d->writeClip(path, d->maxClip); + state()->currentClip = d->maxClip; + state()->clipTestEnabled = true; break; case Qt::UniteClip: { -#ifndef QT_GL_NO_SCISSOR_TEST + d->resetClipIfNeeded(); + ++d->maxClip; if (state()->rectangleClip.isValid()) { - ++state()->maxDepth; - QPainterPath path; path.addRect(state()->rectangleClip); // flush the existing clip rectangle to the depth buffer - d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), state()->maxDepth); + d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(path)), d->maxClip); } + state()->clipTestEnabled = false; +#ifndef QT_GL_NO_SCISSOR_TEST QRect oldRectangleClip = state()->rectangleClip; state()->rectangleClip = state()->rectangleClip.united(pathRect); - d->updateDepthScissorTest(); + d->updateClipScissorTest(); QRegion extendRegion = QRegion(state()->rectangleClip) - oldRectangleClip; - glDepthFunc(GL_ALWAYS); if (!extendRegion.isEmpty()) { QPainterPath extendPath; extendPath.addRegion(extendRegion); @@ -1721,27 +1945,19 @@ void QGL2PaintEngineEx::clip(const QVectorPath &path, Qt::ClipOperation op) d->writeClip(qtVectorPathForPath(state()->matrix.inverted().map(extendPath)), 0); } #endif - glDepthFunc(GL_ALWAYS); // now write the clip path - d->writeClip(path, state()->maxDepth); + d->writeClip(path, d->maxClip); state()->canRestoreClip = false; - state()->currentDepth = state()->maxDepth - 1; - state()->depthTestEnabled = true; + state()->currentClip = d->maxClip; + state()->clipTestEnabled = true; break; } default: break; } - - glDepthFunc(GL_LESS); - if (state()->depthTestEnabled) { - glEnable(GL_DEPTH_TEST); - d->simpleShaderDepthUniformDirty = true; - d->depthUniformDirty = true; - } } -void QGL2PaintEngineExPrivate::regenerateDepthClip() +void QGL2PaintEngineExPrivate::regenerateClip() { systemStateChanged(); replayClipOperations(); @@ -1751,6 +1967,8 @@ void QGL2PaintEngineExPrivate::systemStateChanged() { Q_Q(QGL2PaintEngineEx); + q->state()->clipChanged = true; + if (systemClip.isEmpty()) { use_system_clip = false; } else { @@ -1762,46 +1980,34 @@ void QGL2PaintEngineExPrivate::systemStateChanged() } } - q->state()->depthTestEnabled = false; - q->state()->needsDepthBufferClear = true; + q->state()->clipTestEnabled = false; + q->state()->needsClipBufferClear = true; - q->state()->currentDepth = 1; - q->state()->maxDepth = 4; + q->state()->currentClip = 1; + maxClip = 1; q->state()->rectangleClip = use_system_clip ? systemClip.boundingRect() : QRect(0, 0, width, height); - updateDepthScissorTest(); + updateClipScissorTest(); - if (use_system_clip) { + if (systemClip.numRects() == 1) { + if (systemClip.boundingRect() == QRect(0, 0, width, height)) + use_system_clip = false; #ifndef QT_GL_NO_SCISSOR_TEST - if (systemClip.numRects() == 1) { - if (q->state()->rectangleClip == QRect(0, 0, width, height)) { - use_system_clip = false; - } else { - simpleShaderDepthUniformDirty = true; - depthUniformDirty = true; - } - return; - } + // scissoring takes care of the system clip + return; #endif - q->state()->needsDepthBufferClear = false; - - glDepthMask(true); + } - glClearDepth(0); - glClear(GL_DEPTH_BUFFER_BIT); + if (use_system_clip) { + clearClip(0); QPainterPath path; path.addRegion(systemClip); - glDepthFunc(GL_ALWAYS); - writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 2); - glDepthFunc(GL_LESS); - - glEnable(GL_DEPTH_TEST); - q->state()->depthTestEnabled = true; - - simpleShaderDepthUniformDirty = true; - depthUniformDirty = true; + q->state()->currentClip = 0; + writeClip(qtVectorPathForPath(q->state()->matrix.inverted().map(path)), 1); + q->state()->currentClip = 1; + q->state()->clipTestEnabled = true; } } @@ -1816,37 +2022,43 @@ void QGL2PaintEngineEx::setState(QPainterState *new_state) QPaintEngineEx::setState(s); - if (s == d->last_created_state) { - d->last_created_state = 0; + if (s->isNew) { + // Newly created state object. The call to setState() + // will either be followed by a call to begin(), or we are + // setting the state as part of a save(). + s->isNew = false; return; } - renderHintsChanged(); + // Setting the state as part of a restore(). - d->matrixDirty = true; - d->compositionModeDirty = true; - d->simpleShaderDepthUniformDirty = true; - d->depthUniformDirty = true; - d->simpleShaderMatrixUniformDirty = true; - d->shaderMatrixUniformDirty = true; - d->opacityUniformDirty = true; + if (old_state == s || old_state->renderHintsChanged) + renderHintsChanged(); - d->shaderManager->setDirty(); + if (old_state == s || old_state->matrixChanged) { + d->matrixDirty = true; + d->simpleShaderMatrixUniformDirty = true; + d->shaderMatrixUniformDirty = true; + } - if (old_state && old_state != s && old_state->canRestoreClip) { - d->updateDepthScissorTest(); - glDepthMask(false); - glDepthFunc(GL_LESS); - s->maxDepth = old_state->maxDepth; - } else { - d->regenerateDepthClip(); + if (old_state == s || old_state->compositionModeChanged) + d->compositionModeDirty = true; + + if (old_state == s || old_state->opacityChanged) + d->opacityUniformDirty = true; + + if (old_state == s || old_state->clipChanged) { + if (old_state && old_state != s && old_state->canRestoreClip) { + d->updateClipScissorTest(); + glDepthFunc(GL_LEQUAL); + } else { + d->regenerateClip(); + } } } QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const { - Q_D(const QGL2PaintEngineEx); - if (orig) const_cast<QGL2PaintEngineEx *>(this)->ensureActive(); @@ -1856,7 +2068,12 @@ QPainterState *QGL2PaintEngineEx::createState(QPainterState *orig) const else s = new QOpenGL2PaintEngineState(*static_cast<QOpenGL2PaintEngineState *>(orig)); - d->last_created_state = s; + s->matrixChanged = false; + s->compositionModeChanged = false; + s->opacityChanged = false; + s->renderHintsChanged = false; + s->clipChanged = false; + return s; } @@ -1869,21 +2086,19 @@ void QGL2PaintEngineEx::setRenderTextActive(bool active) QOpenGL2PaintEngineState::QOpenGL2PaintEngineState(QOpenGL2PaintEngineState &other) : QPainterState(other) { - needsDepthBufferClear = other.needsDepthBufferClear; - depthTestEnabled = other.depthTestEnabled; - scissorTestEnabled = other.scissorTestEnabled; - currentDepth = other.currentDepth; - maxDepth = other.maxDepth; + isNew = true; + needsClipBufferClear = other.needsClipBufferClear; + clipTestEnabled = other.clipTestEnabled; + currentClip = other.currentClip; canRestoreClip = other.canRestoreClip; rectangleClip = other.rectangleClip; } QOpenGL2PaintEngineState::QOpenGL2PaintEngineState() { - needsDepthBufferClear = true; - depthTestEnabled = false; - currentDepth = 1; - maxDepth = 4; + isNew = true; + needsClipBufferClear = true; + clipTestEnabled = false; canRestoreClip = true; } diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index f245945..5704a04 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -61,11 +61,13 @@ #include <private/qglpaintdevice_p.h> #include <private/qglpixmapfilter_p.h> #include <private/qfontengine_p.h> +#include <private/qdatabuffer_p.h> enum EngineMode { ImageDrawingMode, TextDrawingMode, - BrushDrawingMode + BrushDrawingMode, + ImageArrayDrawingMode }; QT_BEGIN_NAMESPACE @@ -80,17 +82,18 @@ public: QOpenGL2PaintEngineState(); ~QOpenGL2PaintEngineState(); - bool needsDepthBufferClear; - qreal depthBufferClearValue; + uint isNew : 1; + uint needsClipBufferClear : 1; + uint clipTestEnabled : 1; + uint canRestoreClip : 1; + uint matrixChanged : 1; + uint compositionModeChanged : 1; + uint opacityChanged : 1; + uint renderHintsChanged : 1; + uint clipChanged : 1; + uint currentClip : 8; - bool depthTestEnabled; - bool scissorTestEnabled; - uint maxDepth; - uint currentDepth; - - bool canRestoreClip; QRect rectangleClip; - bool hasRectangleClip; }; class Q_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx @@ -126,6 +129,8 @@ public: virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); + virtual void drawPixmaps(const QDrawPixmaps::Data *drawingData, int dataCount, const QPixmap &pixmap, QDrawPixmaps::DrawingHints hints); + Type type() const { return OpenGL2; } void setState(QPainterState *s); @@ -195,9 +200,9 @@ public: // ^ returns whether the current program changed or not inline void useSimpleShader(); - inline QColor premultiplyColor(QColor c, GLfloat opacity); - float zValueForRenderText() const; + void prepareDepthRangeForRenderText(); + void restoreDepthRangeForRenderText(); static QGLEngineShaderManager* shaderManagerForEngine(QGL2PaintEngineEx *engine) { return engine->d_func()->shaderManager; } @@ -208,8 +213,6 @@ public: EngineMode mode; QFontEngineGlyphCache::Type glyphCacheType; - mutable QOpenGL2PaintEngineState *last_created_state; - // Dirty flags bool matrixDirty; // Implies matrix uniforms are also dirty bool compositionModeDirty; @@ -217,12 +220,12 @@ public: bool brushUniformsDirty; bool simpleShaderMatrixUniformDirty; bool shaderMatrixUniformDirty; - bool depthUniformDirty; - bool simpleShaderDepthUniformDirty; bool opacityUniformDirty; + bool stencilClean; // Has the stencil not been used for clipping so far? QRegion dirtyStencilRegion; QRect currentScissorBounds; + uint maxClip; const QBrush* currentBrush; // May not be the state's brush! @@ -230,6 +233,7 @@ public: QGL2PEXVertexArray vertexCoordinateArray; QGL2PEXVertexArray textureCoordinateArray; + QDataBuffer<GLfloat> opacityArray; GLfloat staticVertexCoordinateArray[8]; GLfloat staticTextureCoordinateArray[8]; @@ -238,26 +242,16 @@ public: QGLEngineShaderManager* shaderManager; - void writeClip(const QVectorPath &path, uint depth); - void updateDepthScissorTest(); + void clearClip(uint value); + void writeClip(const QVectorPath &path, uint value); + void resetClipIfNeeded(); + + void updateClipScissorTest(); void setScissor(const QRect &rect); - void regenerateDepthClip(); + void regenerateClip(); void systemStateChanged(); uint use_system_clip : 1; - static inline GLfloat rawDepth(uint depth) - { - // assume at least 16 bits in the depth buffer, and - // use 2^15 depth levels to be safe with regard to - // rounding issues etc - return depth * (1.0f / GLfloat((1 << 15) - 1)); - } - - static inline GLfloat normalizedDeviceDepth(uint depth) - { - return 2.0f * rawDepth(depth) - 1.0f; - } - uint location(QGLEngineShaderManager::Uniform uniform) { return shaderManager->getUniformLocation(uniform); @@ -268,12 +262,16 @@ public: bool needsSync; bool inRenderText; + GLfloat depthRange[2]; + float textureInvertedY; QScopedPointer<QPixmapFilter> convolutionFilter; QScopedPointer<QPixmapFilter> colorizeFilter; QScopedPointer<QPixmapFilter> blurFilter; QScopedPointer<QPixmapFilter> fastBlurFilter; + QScopedPointer<QPixmapFilter> dropShadowFilter; + QScopedPointer<QPixmapFilter> fastDropShadowFilter; }; QT_END_NAMESPACE diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 1276443..8aef8b4 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -59,6 +59,8 @@ # include <private/qt_mac_p.h> #endif +#include <qdatetime.h> + #include <stdlib.h> // malloc #include "qpixmap.h" @@ -148,7 +150,25 @@ QGLSignalProxy *QGLSignalProxy::instance() class QGLEngineSelector { public: - QGLEngineSelector() : engineType(QPaintEngine::MaxUser) { } + QGLEngineSelector() : engineType(QPaintEngine::MaxUser) + { +#ifdef Q_WS_MAC + // The ATI X1600 driver for Mac OS X does not support return + // values from functions in GLSL. Since working around this in + // the GL2 engine would require a big, ugly rewrite, we're + // falling back to the GL 1 engine.. + QGLWidget *tmp = 0; + if (!QGLContext::currentContext()) { + tmp = new QGLWidget(); + tmp->makeCurrent(); + } + if (strstr((char *) glGetString(GL_RENDERER), "X1600")) + setPreferredPaintEngine(QPaintEngine::OpenGL); + if (tmp) + delete tmp; +#endif + + } void setPreferredPaintEngine(QPaintEngine::Type type) { if (type == QPaintEngine::OpenGL || type == QPaintEngine::OpenGL2) @@ -1391,11 +1411,39 @@ bool operator!=(const QGLFormat& a, const QGLFormat& b) /***************************************************************************** QGLContext implementation *****************************************************************************/ -QGLContextPrivate::~QGLContextPrivate() + +QGLContextGroup::~QGLContextGroup() +{ + // Clear any remaining QGLSharedResourceGuard objects on the group. + QGLSharedResourceGuard *guard = m_guards; + while (guard != 0) { + guard->m_group = 0; + guard->m_id = 0; + guard = guard->m_next; + } +} + +void QGLContextGroup::addGuard(QGLSharedResourceGuard *guard) +{ + if (m_guards) + m_guards->m_prev = guard; + guard->m_next = m_guards; + guard->m_prev = 0; + m_guards = guard; +} + +void QGLContextGroup::removeGuard(QGLSharedResourceGuard *guard) { - if (!reference->deref()) - delete reference; + if (guard->m_next) + guard->m_next->m_prev = guard->m_prev; + if (guard->m_prev) + guard->m_prev->m_next = guard->m_next; + else + m_guards = guard->m_next; +} +QGLContextPrivate::~QGLContextPrivate() +{ if (!group->m_refs.deref()) { Q_ASSERT(group->context() == q_ptr); delete group; @@ -1624,8 +1672,10 @@ void QGLTextureCache::pixmapCleanupHook(QPixmap* pixmap) } #if defined(Q_WS_X11) QPixmapData *pd = pixmap->data_ptr().data(); - Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken - QGLContextPrivate::destroyGlSurfaceForPixmap(pd); + if (pd->classId() == QPixmapData::X11Class) { + Q_ASSERT(pd->ref == 1); // Make sure reference counting isn't broken + QGLContextPrivate::destroyGlSurfaceForPixmap(pd); + } #endif } @@ -1718,7 +1768,7 @@ Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg() the top left corner. Inverting the texture implies a deep copy prior to upload. - \value MipmapBindOption Specifies that bindTexture should try + \value MipmapBindOption Specifies that bindTexture() should try to generate mipmaps. If the GL implementation supports the \c GL_SGIS_generate_mipmap extension, mipmaps will be automatically generated for the texture. Mipmap generation is only supported for @@ -1804,6 +1854,8 @@ QGLContext::~QGLContext() QGLTextureCache::instance()->removeContextTextures(this); QGLTextureCache::deleteIfEmpty(); // ### thread safety + d_ptr->group->cleanupResources(this); + QGLSignalProxy::instance()->emitAboutToDestroyContext(this); reset(); } @@ -2077,6 +2129,8 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G #ifdef QGL_BIND_TEXTURE_DEBUG printf("QGLContextPrivate::bindTexture(), imageSize=(%d,%d), internalFormat =0x%x, options=%x\n", image.width(), image.height(), internalFormat, int(options)); + QTime time; + time.start(); #endif // Scale the pixmap if needed. GL textures needs to have the @@ -2092,7 +2146,8 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G { img = img.scaled(tx_w, tx_h); #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - upscaled to %dx%d\n", tx_w, tx_h); + printf(" - upscaled to %dx%d (%d ms)\n", tx_w, tx_h, time.elapsed()); + #endif } @@ -2112,7 +2167,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G && options & QGLContext::MipmapBindOption) { #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - generating mipmaps\n"); + printf(" - generating mipmaps (%d ms)\n", time.elapsed()); #endif #if !defined(QT_OPENGL_ES_2) glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST); @@ -2148,7 +2203,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G if (premul) { img = img.convertToFormat(target_format = QImage::Format_ARGB32_Premultiplied); #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converting ARGB32 -> ARGB32_Premultiplied \n"); + printf(" - converting ARGB32 -> ARGB32_Premultiplied (%d ms) \n", time.elapsed()); #endif } break; @@ -2156,7 +2211,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G if (!premul) { img = img.convertToFormat(target_format = QImage::Format_ARGB32); #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converting ARGB32_Premultiplied -> ARGB32\n"); + printf(" - converting ARGB32_Premultiplied -> ARGB32 (%d ms)\n", time.elapsed()); #endif } break; @@ -2173,19 +2228,19 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G ? QImage::Format_ARGB32_Premultiplied : QImage::Format_ARGB32); #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converting to 32-bit alpha format\n"); + printf(" - converting to 32-bit alpha format (%d ms)\n", time.elapsed()); #endif } else { img = img.convertToFormat(QImage::Format_RGB32); #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - converting to 32-bit\n"); + printf(" - converting to 32-bit (%d ms)\n", time.elapsed()); #endif } } if (options & QGLContext::InvertedYBindOption) { #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - flipping bits over y\n"); + printf(" - flipping bits over y (%d ms)\n", time.elapsed()); #endif int ipl = img.bytesPerLine() / 4; int h = img.height(); @@ -2199,7 +2254,7 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G if (externalFormat == GL_RGBA) { #ifdef QGL_BIND_TEXTURE_DEBUG - printf(" - doing byte swapping\n"); + printf(" - doing byte swapping (%d ms)\n", time.elapsed()); #endif // The only case where we end up with a depth different from // 32 in the switch above is for the RGB16 case, where we set @@ -2242,6 +2297,13 @@ QGLTexture* QGLContextPrivate::bindTexture(const QImage &image, GLenum target, G } #endif +#ifdef QGL_BIND_TEXTURE_DEBUG + static int totalUploadTime = 0; + totalUploadTime += time.elapsed(); + printf(" - upload done in (%d ms) time=%d\n", time.elapsed(), totalUploadTime); +#endif + + // this assumes the size of a texture is always smaller than the max cache size int cost = img.width()*img.height()*4/1024; QGLTexture *texture = new QGLTexture(q, tx_id, target, options); @@ -2254,7 +2316,7 @@ QGLTexture *QGLContextPrivate::textureCacheLookup(const qint64 key, GLenum targe Q_Q(QGLContext); QGLTexture *texture = QGLTextureCache::instance()->getTexture(key); if (texture && texture->target == target - && (texture->context == q || qgl_share_reg()->checkSharing(q, texture->context))) + && (texture->context == q || QGLContext::areSharing(q, texture->context))) { return texture; } @@ -2361,6 +2423,8 @@ GLuint QGLContext::bindTexture(const QImage &image, GLenum target, GLint format) } /*! + \since 4.6 + Generates and binds a 2D GL texture to the current context, based on \a image. The generated texture id is returned and can be used in later \c glBindTexture() calls. @@ -2422,6 +2486,7 @@ GLuint QGLContext::bindTexture(const QPixmap &pixmap, GLenum target, GLint forma /*! \overload + \since 4.6 Generates and binds a 2D GL texture to the current context, based on \a pixmap. @@ -2755,6 +2820,20 @@ void QGLContext::setDevice(QPaintDevice *pDev) */ /*! + Returns true if \a context1 and \a context2 are sharing their + GL resources such as textures, shader programs, etc; + otherwise returns false. + + \since 4.6 +*/ +bool QGLContext::areSharing(const QGLContext *context1, const QGLContext *context2) +{ + if (!context1 || !context2) + return false; + return context1->d_ptr->group == context2->d_ptr->group; +} + +/*! \fn bool QGLContext::deviceIsPixmap() const Returns true if the paint device of this context is a pixmap; @@ -3722,6 +3801,11 @@ bool QGLWidget::event(QEvent *e) glFinish(); doneCurrent(); } else if (e->type() == QEvent::ParentChange) { + // if we've reparented a window that has the current context + // bound, we need to rebind that context to the new window id + if (d->glcx == QGLContext::currentContext()) + makeCurrent(); + if (d->glcx->d_func()->screen != d->xinfo.screen() || testAttribute(Qt::WA_TranslucentBackground)) { setContext(new QGLContext(d->glcx->requestedFormat(), this)); // ### recreating the overlay isn't supported atm @@ -4489,6 +4573,7 @@ GLuint QGLWidget::bindTexture(const QImage &image, GLenum target, GLint format) /*! \overload + \since 4.6 The binding \a options are a set of options used to decide how to bind the texture to the context. @@ -4530,6 +4615,7 @@ GLuint QGLWidget::bindTexture(const QPixmap &pixmap, GLenum target, GLint format /*! \overload + \since 4.6 Generates and binds a 2D GL texture to the current context, based on \a pixmap. The generated texture id is returned and can be used in @@ -4835,67 +4921,57 @@ Q_OPENGL_EXPORT const QString qt_gl_library_name() } #endif -bool QGLShareRegister::checkSharing(const QGLContext *context1, const QGLContext *context2) { - bool sharing = (context1 && context2 && context1->d_ptr->group == context2->d_ptr->group); - return sharing; -} - void QGLShareRegister::addShare(const QGLContext *context, const QGLContext *share) { Q_ASSERT(context && share); if (context->d_ptr->group == share->d_ptr->group) return; // Make sure 'context' is not already shared with another group of contexts. - Q_ASSERT(reg.find(context->d_ptr->group) == reg.end()); Q_ASSERT(context->d_ptr->group->m_refs == 1); // Free 'context' group resources and make it use the same resources as 'share'. + QGLContextGroup *group = share->d_ptr->group; delete context->d_ptr->group; - context->d_ptr->group = share->d_ptr->group; - context->d_ptr->group->m_refs.ref(); + context->d_ptr->group = group; + group->m_refs.ref(); // Maintain a list of all the contexts in each group of sharing contexts. - SharingHash::iterator it = reg.find(share->d_ptr->group); - if (it == reg.end()) - it = reg.insert(share->d_ptr->group, ContextList() << share); - it.value() << context; + // The list is empty if the "share" context wasn't sharing already. + if (group->m_shares.isEmpty()) + group->m_shares.append(share); + group->m_shares.append(context); } QList<const QGLContext *> QGLShareRegister::shares(const QGLContext *context) { - SharingHash::const_iterator it = reg.find(context->d_ptr->group); - if (it == reg.end()) - return ContextList(); - return it.value(); + return context->d_ptr->group->m_shares; } void QGLShareRegister::removeShare(const QGLContext *context) { - SharingHash::iterator it = reg.find(context->d_ptr->group); - if (it == reg.end()) + // Remove the context from the group. + QGLContextGroup *group = context->d_ptr->group; + if (group->m_shares.isEmpty()) return; - - int count = it.value().removeAll(context); - Q_ASSERT(count == 1); - Q_UNUSED(count); + group->m_shares.removeAll(context); // Update context group representative. - if (context->d_ptr->group->m_context == context) - context->d_ptr->group->m_context = it.value().first(); + Q_ASSERT(group->m_shares.size() != 0); + if (group->m_context == context) + group->m_context = group->m_shares[0]; - Q_ASSERT(it.value().size() != 0); - if (it.value().size() == 1) - reg.erase(it); + // If there is only one context left, then make the list empty. + if (group->m_shares.size() == 1) + group->m_shares.clear(); } -QGLContextResource::QGLContextResource(FreeFunc f, QObject *parent) - : QObject(parent), free(f) +QGLContextResource::QGLContextResource(FreeFunc f) + : free(f), active(0) { - connect(QGLSignalProxy::instance(), SIGNAL(aboutToDestroyContext(const QGLContext *)), this, SLOT(removeOne(const QGLContext *))); } QGLContextResource::~QGLContextResource() { #ifndef QT_NO_DEBUG - if (m_resources.size()) { + if (active != 0) { qWarning("QtOpenGL: Resources are still available at program shutdown.\n" " This is possibly caused by a leaked QGLWidget, \n" " QGLFramebufferObject or QGLPixelBuffer."); @@ -4905,121 +4981,53 @@ QGLContextResource::~QGLContextResource() void QGLContextResource::insert(const QGLContext *key, void *value) { - QList<const QGLContext *> shares = qgl_share_reg()->shares(key); - if (shares.size() == 0) - shares.append(key); - void *oldValue = 0; - for (int i = 0; i < shares.size(); ++i) { - ResourceHash::iterator it = m_resources.find(shares.at(i)); - if (it != m_resources.end()) { - Q_ASSERT(oldValue == 0 || oldValue == it.value()); - oldValue = it.value(); - it.value() = value; - } else { - m_resources.insert(shares.at(i), value); - } - } - if (oldValue != 0 && oldValue != value) { - QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (oldContext != key) - const_cast<QGLContext *>(key)->makeCurrent(); - free(oldValue); - if (oldContext && oldContext != key) - oldContext->makeCurrent(); - } + QGLContextGroup *group = QGLContextPrivate::contextGroup(key); + Q_ASSERT(!group->m_resources.contains(this)); + group->m_resources.insert(this, value); + active.ref(); } void *QGLContextResource::value(const QGLContext *key) { - ResourceHash::const_iterator it = m_resources.find(key); - // Check if there is a value associated with 'key'. - if (it != m_resources.end()) - return it.value(); - // Check if there is a value associated with sharing contexts. - QList<const QGLContext *> shares = qgl_share_reg()->shares(key); - for (int i = 0; i < shares.size() && it == m_resources.end(); ++i) - it = m_resources.find(shares.at(i)); - if (it == m_resources.end()) - return 0; // Didn't find anything. - - // Found something! Share this info with all the buddies. - for (int i = 0; i < shares.size(); ++i) - m_resources.insert(shares.at(i), it.value()); - return it.value(); -} - -void QGLContextResource::removeGroup(const QGLContext *key) -{ - QList<const QGLContext *> shares = qgl_share_reg()->shares(key); - if (shares.size() == 0) - shares.append(key); - void *oldValue = 0; - for (int i = 0; i < shares.size(); ++i) { - ResourceHash::iterator it = m_resources.find(shares.at(i)); - if (it != m_resources.end()) { - Q_ASSERT(oldValue == 0 || oldValue == it.value()); - oldValue = it.value(); - m_resources.erase(it); - } - } - if (oldValue != 0) { - QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (oldContext != key) - const_cast<QGLContext *>(key)->makeCurrent(); - free(oldValue); - if (oldContext && oldContext != key) - oldContext->makeCurrent(); - } + QGLContextGroup *group = QGLContextPrivate::contextGroup(key); + return group->m_resources.value(this, 0); +} + +void QGLContextResource::cleanup(const QGLContext *ctx, void *value) +{ + QGLShareContextScope scope(ctx); + free(value); + active.deref(); } -void QGLContextResource::removeOne(const QGLContext *key) +void QGLContextGroup::cleanupResources(const QGLContext *ctx) { - ResourceHash::iterator it = m_resources.find(key); - if (it == m_resources.end()) + // If there are still shares, then no cleanup to be done yet. + if (m_shares.size() > 1) return; - QList<const QGLContext *> shares = qgl_share_reg()->shares(key); - if (shares.size() > 1) { - Q_ASSERT(key->isSharing()); - // At least one of the shared contexts must stay in the cache. - // Otherwise, the value pointer is lost. - for (int i = 0; i < 2/*shares.size()*/; ++i) - m_resources.insert(shares.at(i), it.value()); - } else { - QGLContext *oldContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (oldContext != key) - const_cast<QGLContext *>(key)->makeCurrent(); - free(it.value()); - if (oldContext && oldContext != key) - oldContext->makeCurrent(); - } - m_resources.erase(it); + // Iterate over all resources and free each in turn. + QHash<QGLContextResource *, void *>::ConstIterator it; + for (it = m_resources.begin(); it != m_resources.end(); ++it) + it.key()->cleanup(ctx, it.value()); } -QGLContextReference::QGLContextReference(const QGLContext *ctx) - : m_ref(1), m_ctx(ctx) +QGLSharedResourceGuard::~QGLSharedResourceGuard() { - connect(QGLSignalProxy::instance(), - SIGNAL(aboutToDestroyContext(const QGLContext *)), - this, SLOT(aboutToDestroyContext(const QGLContext *))); + if (m_group) + m_group->removeGuard(this); } -void QGLContextReference::aboutToDestroyContext(const QGLContext *ctx) +void QGLSharedResourceGuard::setContext(const QGLContext *context) { - // Bail out if our context is not being destroyed. - if (ctx != m_ctx || !m_ctx) - return; - - // Find some other context that this one is shared with to take over. - QList<const QGLContext *> shares = qgl_share_reg()->shares(m_ctx); - shares.removeAll(m_ctx); - if (!shares.isEmpty()) { - m_ctx = shares[0]; - return; + if (m_group) + m_group->removeGuard(this); + if (context) { + m_group = QGLContextPrivate::contextGroup(context); + m_group->addGuard(this); + } else { + m_group = 0; } - - // No more contexts sharing with this one, so the reference is now invalid. - m_ctx = 0; } QT_END_NAMESPACE diff --git a/src/opengl/qgl.h b/src/opengl/qgl.h index 1776004..b1c1317 100644 --- a/src/opengl/qgl.h +++ b/src/opengl/qgl.h @@ -282,6 +282,8 @@ public: bool isSharing() const; void reset(); + static bool areSharing(const QGLContext *context1, const QGLContext *context2); + QGLFormat format() const; QGLFormat requestedFormat() const; void setFormat(const QGLFormat& format); diff --git a/src/opengl/qgl_egl.cpp b/src/opengl/qgl_egl.cpp index 6c93bea..fbf0349 100644 --- a/src/opengl/qgl_egl.cpp +++ b/src/opengl/qgl_egl.cpp @@ -75,7 +75,7 @@ void qt_egl_set_format(QEglProperties& props, int deviceType, const QGLFormat& f props.setValue(EGL_STENCIL_SIZE, f.stencilBufferSize() == -1 ? 1 : f.stencilBufferSize()); if (f.sampleBuffers()) { props.setValue(EGL_SAMPLE_BUFFERS, 1); - props.setValue(EGL_SAMPLES, f.samples()); + props.setValue(EGL_SAMPLES, f.samples() == -1 ? 1 : f.samples()); } else { props.setValue(EGL_SAMPLE_BUFFERS, 0); } @@ -142,8 +142,19 @@ void QGLContext::reset() d->cleanup(); doneCurrent(); if (d->eglContext) { - if (d->eglSurface != EGL_NO_SURFACE) - eglDestroySurface(d->eglContext->display(), d->eglSurface); + if (d->eglSurface != EGL_NO_SURFACE) { +#ifdef Q_WS_X11 + // Make sure we don't call eglDestroySurface on a surface which + // was created for a different winId: + if (d->paintDevice->devType() == QInternal::Widget) { + QGLWidget* w = static_cast<QGLWidget*>(d->paintDevice); + + if (w->d_func()->eglSurfaceWindowId == w->winId()) + eglDestroySurface(d->eglContext->display(), d->eglSurface); + } else +#endif + eglDestroySurface(d->eglContext->display(), d->eglSurface); + } delete d->eglContext; } d->eglContext = 0; diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 2e8ac88..8d4f673 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -219,6 +219,9 @@ public: #endif }; +class QGLContextResource; +class QGLSharedResourceGuard; + // QGLContextPrivate has the responsibility of creating context groups. // QGLContextPrivate and QGLShareRegister will both maintain the reference counter and destroy // context groups when needed. @@ -226,41 +229,29 @@ public: class QGLContextGroup { public: + ~QGLContextGroup(); + QGLExtensionFuncs &extensionFuncs() {return m_extensionFuncs;} const QGLContext *context() const {return m_context;} + + void addGuard(QGLSharedResourceGuard *guard); + void removeGuard(QGLSharedResourceGuard *guard); private: - QGLContextGroup(const QGLContext *context) : m_context(context), m_refs(1) { } + QGLContextGroup(const QGLContext *context) : m_context(context), m_guards(0), m_refs(1) { } QGLExtensionFuncs m_extensionFuncs; const QGLContext *m_context; // context group's representative + QList<const QGLContext *> m_shares; + QHash<QGLContextResource *, void *> m_resources; + QGLSharedResourceGuard *m_guards; // double-linked list of active guards. QAtomicInt m_refs; + void cleanupResources(const QGLContext *ctx); + friend class QGLShareRegister; + friend class QGLContext; friend class QGLContextPrivate; -}; - -// Reference to a QGLContext which automatically switches to another -// shared context when the main one is destroyed. If there is no -// shared context to switch to, the context pointer is set to null. -// Note: should be merged into QGLContextGroup at some point. -class QGLContextReference : public QObject -{ - Q_OBJECT -public: - QGLContextReference(const QGLContext *ctx); - ~QGLContextReference() {} - - const QGLContext *context() const { return m_ctx; } - - void ref() { m_ref.ref(); } - bool deref() { return m_ref.deref(); } - -private slots: - void aboutToDestroyContext(const QGLContext *ctx); - -private: - QAtomicInt m_ref; - const QGLContext *m_ctx; + friend class QGLContextResource; }; class QGLTexture; @@ -269,7 +260,7 @@ class QGLContextPrivate { Q_DECLARE_PUBLIC(QGLContext) public: - explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {reference = new QGLContextReference(context); group = new QGLContextGroup(context);} + explicit QGLContextPrivate(QGLContext *context) : internal_context(false), q_ptr(context) {group = new QGLContextGroup(context);} ~QGLContextPrivate(); QGLTexture *bindTexture(const QImage &image, GLenum target, GLint format, QGLContext::BindOptions options); @@ -332,7 +323,6 @@ public: QGLContext *q_ptr; QGLFormat::OpenGLVersionFlags version_flags; - QGLContextReference *reference; QGLContextGroup *group; GLint max_texture_size; @@ -343,13 +333,11 @@ public: #ifdef Q_WS_WIN static inline QGLExtensionFuncs& extensionFuncs(const QGLContext *ctx) { return ctx->d_ptr->group->extensionFuncs(); } - static inline QGLExtensionFuncs& extensionFuncs(QGLContextGroup *ctx) { return ctx->extensionFuncs(); } #endif #if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) static QGLExtensionFuncs qt_extensionFuncs; static inline QGLExtensionFuncs& extensionFuncs(const QGLContext *) { return qt_extensionFuncs; } - static inline QGLExtensionFuncs& extensionFuncs(QGLContextGroup *) { return qt_extensionFuncs; } #endif static void setCurrentContext(QGLContext *context); @@ -399,25 +387,59 @@ public: Q_DECLARE_OPERATORS_FOR_FLAGS(QGLExtensions::Extensions) -class QGLShareRegister +class Q_AUTOTEST_EXPORT QGLShareRegister { public: QGLShareRegister() {} - ~QGLShareRegister() { reg.clear(); } + ~QGLShareRegister() {} - bool checkSharing(const QGLContext *context1, const QGLContext *context2); void addShare(const QGLContext *context, const QGLContext *share); QList<const QGLContext *> shares(const QGLContext *context); void removeShare(const QGLContext *context); -private: - // Use a context's 'group' pointer to uniquely identify a group. - typedef QList<const QGLContext *> ContextList; - typedef QHash<const QGLContextGroup *, ContextList> SharingHash; - SharingHash reg; }; extern Q_OPENGL_EXPORT QGLShareRegister* qgl_share_reg(); +// Temporarily make a context current if not already current or +// shared with the current contex. The previous context is made +// current when the object goes out of scope. +class Q_OPENGL_EXPORT QGLShareContextScope +{ +public: + QGLShareContextScope(const QGLContext *ctx) + : m_oldContext(0) + { + QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); + if (currentContext != ctx && !QGLContext::areSharing(ctx, currentContext)) { + m_oldContext = currentContext; + m_ctx = const_cast<QGLContext *>(ctx); + m_ctx->makeCurrent(); + } else { + m_ctx = currentContext; + } + } + + operator QGLContext *() + { + return m_ctx; + } + + QGLContext *operator->() + { + return m_ctx; + } + + ~QGLShareContextScope() + { + if (m_oldContext) + m_oldContext->makeCurrent(); + } + +private: + QGLContext *m_oldContext; + QGLContext *m_ctx; +}; + class QGLTexture { public: QGLTexture(QGLContext *ctx = 0, GLuint tx_id = 0, GLenum tx_target = GL_TEXTURE_2D, @@ -433,12 +455,8 @@ public: ~QGLTexture() { if (options & QGLContext::MemoryManagedBindOption) { - QGLContext *current = const_cast<QGLContext *>(QGLContext::currentContext()); - QGLContext *ctx = const_cast<QGLContext *>(context); - Q_ASSERT(ctx); - bool switch_context = current != ctx && !qgl_share_reg()->checkSharing(current, ctx); - if (switch_context) - ctx->makeCurrent(); + Q_ASSERT(context); + QGLShareContextScope scope(context); #if defined(Q_WS_X11) // Although glXReleaseTexImage is a glX call, it must be called while there // is a current context - the context the pixmap was bound to a texture in. @@ -448,8 +466,6 @@ public: QGLContextPrivate::unbindPixmapFromTexture(boundPixmap); #endif glDeleteTextures(1, &id); - if (switch_context && current) - current->makeCurrent(); } } @@ -491,8 +507,6 @@ private: #ifdef Q_WS_QWS extern QPaintEngine* qt_qgl_paint_engine(); - -extern EGLDisplay qt_qgl_egl_display(); #endif bool qt_gl_preferGL2Engine(); @@ -515,66 +529,21 @@ inline GLenum qt_gl_preferredTextureTarget() } // One resource per group of shared contexts. -class QGLContextResource : public QObject +class Q_AUTOTEST_EXPORT QGLContextResource { - Q_OBJECT public: typedef void (*FreeFunc)(void *); - QGLContextResource(FreeFunc f, QObject *parent = 0); + QGLContextResource(FreeFunc f); ~QGLContextResource(); // Set resource 'value' for 'key' and all its shared contexts. void insert(const QGLContext *key, void *value); // Return resource for 'key' or a shared context. void *value(const QGLContext *key); - // Free resource for 'key' and all its shared contexts. - void removeGroup(const QGLContext *key); -private slots: - // Remove entry 'key' from cache and delete resource if there are no shared contexts. - void removeOne(const QGLContext *key); + // Cleanup 'value' in response to a context group being destroyed. + void cleanup(const QGLContext *ctx, void *value); private: - typedef QHash<const QGLContext *, void *> ResourceHash; - ResourceHash m_resources; FreeFunc free; -}; - -// Temporarily make a context current if not already current or -// shared with the current contex. The previous context is made -// current when the object goes out of scope. -class Q_OPENGL_EXPORT QGLShareContextScope -{ -public: - QGLShareContextScope(const QGLContext *ctx) - : m_oldContext(0) - { - QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); - if (currentContext != ctx && !qgl_share_reg()->checkSharing(ctx, currentContext)) { - m_oldContext = currentContext; - m_ctx = const_cast<QGLContext *>(ctx); - m_ctx->makeCurrent(); - } else { - m_ctx = currentContext; - } - } - - operator QGLContext *() - { - return m_ctx; - } - - QGLContext *operator->() - { - return m_ctx; - } - - ~QGLShareContextScope() - { - if (m_oldContext) - m_oldContext->makeCurrent(); - } - -private: - QGLContext *m_oldContext; - QGLContext *m_ctx; + QAtomicInt active; }; // Put a guard around a GL object identifier and its context. @@ -586,44 +555,27 @@ class Q_OPENGL_EXPORT QGLSharedResourceGuard { public: QGLSharedResourceGuard(const QGLContext *context) - : m_ctxref(0), m_id(0) + : m_group(0), m_id(0), m_next(0), m_prev(0) { setContext(context); } QGLSharedResourceGuard(const QGLContext *context, GLuint id) - : m_ctxref(0), m_id(id) + : m_group(0), m_id(id), m_next(0), m_prev(0) { setContext(context); } - ~QGLSharedResourceGuard() - { - if (m_ctxref && !m_ctxref->deref()) - delete m_ctxref; - } + ~QGLSharedResourceGuard(); const QGLContext *context() const { - return m_ctxref ? m_ctxref->context() : 0; + return m_group ? m_group->context() : 0; } - void setContext(const QGLContext *context) - { - if (m_ctxref && !m_ctxref->deref()) - delete m_ctxref; - if (context) { - m_ctxref = context->d_ptr->reference; - m_ctxref->ref(); - } else { - m_ctxref = 0; - } - } + void setContext(const QGLContext *context); GLuint id() const { - if (m_ctxref && m_ctxref->context()) - return m_id; - else - return 0; + return m_id; } void setId(GLuint id) @@ -632,8 +584,12 @@ public: } private: - QGLContextReference *m_ctxref; + QGLContextGroup *m_group; GLuint m_id; + QGLSharedResourceGuard *m_next; + QGLSharedResourceGuard *m_prev; + + friend class QGLContextGroup; }; QT_END_NAMESPACE diff --git a/src/opengl/qgl_x11egl.cpp b/src/opengl/qgl_x11egl.cpp index 67f391b..971a660 100644 --- a/src/opengl/qgl_x11egl.cpp +++ b/src/opengl/qgl_x11egl.cpp @@ -86,9 +86,19 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) qt_egl_add_platform_config(configProps, device()); configProps.setRenderableType(QEgl::OpenGL); + QEgl::PixelFormatMatch matchType = QEgl::BestPixelFormat; + if (device()->depth() == 16) { + configProps.setValue(EGL_RED_SIZE, 5); + configProps.setValue(EGL_GREEN_SIZE, 6); + configProps.setValue(EGL_BLUE_SIZE, 5); + configProps.setValue(EGL_ALPHA_SIZE, 0); + matchType = QEgl::ExactPixelFormat; + } + configProps.setRenderableType(QEgl::OpenGL); + // Search for a matching configuration, reducing the complexity // each time until we get something that matches. - if (!d->eglContext->chooseConfig(configProps, QEgl::BestPixelFormat)) { + if (!d->eglContext->chooseConfig(configProps, matchType)) { delete d->eglContext; d->eglContext = 0; return false; @@ -140,85 +150,36 @@ void QGLWidget::updateOverlayGL() //handle overlay } -void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext) +bool qt_egl_setup_x11_visual(XVisualInfo &vi, EGLDisplay display, EGLConfig config, const QX11Info &x11Info, bool useArgbVisual) { - Q_D(QGLWidget); - if (context == 0) { - qWarning("QGLWidget::setContext: Cannot set null context"); - return; - } - if (!context->deviceIsPixmap() && context->device() != this) { - qWarning("QGLWidget::setContext: Context must refer to this widget"); - return; - } - - if (d->glcx) - d->glcx->doneCurrent(); - QGLContext* oldcx = d->glcx; - d->glcx = context; - - if (parentWidget()) { - // force creation of delay-created widgets - parentWidget()->winId(); - if (parentWidget()->x11Info().screen() != x11Info().screen()) - d_func()->xinfo = parentWidget()->d_func()->xinfo; - } - - // If the application has set WA_TranslucentBackground and not explicitly set - // the alpha buffer size to zero, modify the format so it have an alpha channel - QGLFormat& fmt = d->glcx->d_func()->glFormat; - const bool useArgbVisual = testAttribute(Qt::WA_TranslucentBackground); - if (useArgbVisual && fmt.alphaBufferSize() == -1) - fmt.setAlphaBufferSize(1); - - bool createFailed = false; - if (!d->glcx->isValid()) { - if (!d->glcx->create(shareContext ? shareContext : oldcx)) - createFailed = true; - } - if (createFailed) { - if (deleteOldContext) - delete oldcx; - return; - } - - if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) { - if (deleteOldContext) - delete oldcx; - return; - } - - bool visible = isVisible(); - if (visible) - hide(); + bool foundVisualIsArgb = useArgbVisual; - XVisualInfo vi; memset(&vi, 0, sizeof(XVisualInfo)); // Check to see if EGL is suggesting an appropriate visual id: EGLint nativeVisualId; - QEglContext* qeglCtx = d->glcx->d_func()->eglContext; - qeglCtx->configAttrib(EGL_NATIVE_VISUAL_ID, &nativeVisualId); + eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &nativeVisualId); vi.visualid = nativeVisualId; if (vi.visualid) { // EGL has suggested a visual id, so get the rest of the visual info for that id: XVisualInfo *chosenVisualInfo; int matchingCount = 0; - chosenVisualInfo = XGetVisualInfo(x11Info().display(), VisualIDMask, &vi, &matchingCount); + chosenVisualInfo = XGetVisualInfo(x11Info.display(), VisualIDMask, &vi, &matchingCount); if (chosenVisualInfo) { #if !defined(QT_NO_XRENDER) if (useArgbVisual) { // Check to make sure the visual provided by EGL is ARGB XRenderPictFormat *format; - format = XRenderFindVisualFormat(x11Info().display(), chosenVisualInfo->visual); + format = XRenderFindVisualFormat(x11Info.display(), chosenVisualInfo->visual); if (format->type == PictTypeDirect && format->direct.alphaMask) { -// qDebug("Using opaque X Visual ID (%d) provided by EGL", (int)vi.visualid); +// qDebug("Using ARGB X Visual ID (%d) provided by EGL", (int)vi.visualid); + foundVisualIsArgb = true; vi = *chosenVisualInfo; } else { qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this is not ARGB", - nativeVisualId, (int)qeglCtx->config()); + nativeVisualId, (int)config); vi.visualid = 0; } } else @@ -231,32 +192,32 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, } else { qWarning("Warning: EGL suggested using X visual ID %d for config %d, but this seems to be invalid!", - nativeVisualId, (int)qeglCtx->config()); + nativeVisualId, (int)config); vi.visualid = 0; } } // If EGL does not know the visual ID, so try to select an appropriate one ourselves, first // using XRender if we're supposed to have an alpha, then falling back to XGetVisualInfo - - bool useArgb = context->format().alpha() && !context->deviceIsPixmap(); + #if !defined(QT_NO_XRENDER) - if (vi.visualid == 0 && useArgb) { + if (vi.visualid == 0 && useArgbVisual) { // Try to use XRender to find an ARGB visual we can use - vi.screen = x11Info().screen(); - vi.depth = 32; + vi.screen = x11Info.screen(); + vi.depth = 32; //### We might at some point (soon) get ARGB4444 vi.c_class = TrueColor; XVisualInfo *matchingVisuals; int matchingCount = 0; - matchingVisuals = XGetVisualInfo(x11Info().display(), + matchingVisuals = XGetVisualInfo(x11Info.display(), VisualScreenMask|VisualDepthMask|VisualClassMask, &vi, &matchingCount); for (int i = 0; i < matchingCount; ++i) { XRenderPictFormat *format; - format = XRenderFindVisualFormat(x11Info().display(), matchingVisuals[i].visual); + format = XRenderFindVisualFormat(x11Info.display(), matchingVisuals[i].visual); if (format->type == PictTypeDirect && format->direct.alphaMask) { vi = matchingVisuals[i]; + foundVisualIsArgb = true; // qDebug("Using X Visual ID (%d) for ARGB visual as provided by XRender", (int)vi.visualid); break; } @@ -267,17 +228,17 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, if (vi.visualid == 0) { EGLint depth; - qeglCtx->configAttrib(EGL_BUFFER_SIZE, &depth); + eglGetConfigAttrib(display, config, EGL_BUFFER_SIZE, &depth); int err; - err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), depth, TrueColor, &vi); + err = XMatchVisualInfo(x11Info.display(), x11Info.screen(), depth, TrueColor, &vi); if (err == 0) { qWarning("Warning: Can't find an X visual which matches the EGL config(%d)'s depth (%d)!", - (int)qeglCtx->config(), depth); - depth = x11Info().depth(); - err = XMatchVisualInfo(x11Info().display(), x11Info().screen(), depth, TrueColor, &vi); + (int)config, depth); + depth = x11Info.depth(); + err = XMatchVisualInfo(x11Info.display(), x11Info.screen(), depth, TrueColor, &vi); if (err == 0) { qWarning("Error: Couldn't get any matching X visual!"); - return; + return false; } else qWarning(" - Falling back to X11 suggested depth (%d)", depth); } @@ -285,8 +246,8 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, // qDebug("Using X Visual ID (%d) for EGL provided depth (%d)", (int)vi.visualid, depth); // Don't try to use ARGB now unless the visual is 32-bit - even then it might stil fail :-( - if (useArgb) - useArgb = vi.depth == 32; + if (useArgbVisual) + foundVisualIsArgb = vi.depth == 32; //### We might at some point (soon) get ARGB4444 } // qDebug("Visual Info:"); @@ -299,6 +260,65 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, // qDebug(" depth=%d", vi.depth); // qDebug(" screen=%d", vi.screen); // qDebug(" visualid=%d", vi.visualid); + return foundVisualIsArgb; +} + +void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, bool deleteOldContext) +{ + Q_D(QGLWidget); + if (context == 0) { + qWarning("QGLWidget::setContext: Cannot set null context"); + return; + } + if (!context->deviceIsPixmap() && context->device() != this) { + qWarning("QGLWidget::setContext: Context must refer to this widget"); + return; + } + + if (d->glcx) + d->glcx->doneCurrent(); + QGLContext* oldcx = d->glcx; + d->glcx = context; + + if (parentWidget()) { + // force creation of delay-created widgets + parentWidget()->winId(); + if (parentWidget()->x11Info().screen() != x11Info().screen()) + d_func()->xinfo = parentWidget()->d_func()->xinfo; + } + + // If the application has set WA_TranslucentBackground and not explicitly set + // the alpha buffer size to zero, modify the format so it have an alpha channel + QGLFormat& fmt = d->glcx->d_func()->glFormat; + const bool tryArgbVisual = testAttribute(Qt::WA_TranslucentBackground); + if (tryArgbVisual && fmt.alphaBufferSize() == -1) + fmt.setAlphaBufferSize(1); + + bool createFailed = false; + if (!d->glcx->isValid()) { + if (!d->glcx->create(shareContext ? shareContext : oldcx)) + createFailed = true; + } + if (createFailed) { + if (deleteOldContext) + delete oldcx; + return; + } + + if (d->glcx->windowCreated() || d->glcx->deviceIsPixmap()) { + if (deleteOldContext) + delete oldcx; + return; + } + + bool visible = isVisible(); + if (visible) + hide(); + + XVisualInfo vi; + QEglContext *eglContext = d->glcx->d_func()->eglContext; + bool usingArgbVisual = qt_egl_setup_x11_visual(vi, eglContext->display(), eglContext->config(), + x11Info(), tryArgbVisual); XSetWindowAttributes a; @@ -311,7 +331,7 @@ void QGLWidget::setContext(QGLContext *context, const QGLContext* shareContext, a.border_pixel = colmap.pixel(Qt::black); unsigned int valueMask = CWBackPixel|CWBorderPixel; - if(useArgb) { + if (usingArgbVisual) { a.colormap = XCreateColormap(x11Info().display(), p, vi.visual, AllocNone); valueMask |= CWColormap; } @@ -408,6 +428,103 @@ void QGLWidgetPrivate::recreateEglSurface(bool force) } } +// Selects which configs should be used +EGLConfig Q_OPENGL_EXPORT qt_chooseEGLConfigForPixmap(bool hasAlpha, bool readOnly) +{ + // Cache the configs we select as they wont change: + static EGLConfig roPixmapRGBConfig = 0; + static EGLConfig roPixmapRGBAConfig = 0; + static EGLConfig rwPixmapRGBConfig = 0; + static EGLConfig rwPixmapRGBAConfig = 0; + + EGLConfig* targetConfig; + + if (hasAlpha) { + if (readOnly) + targetConfig = &roPixmapRGBAConfig; + else + targetConfig = &rwPixmapRGBAConfig; + } + else { + if (readOnly) + targetConfig = &roPixmapRGBConfig; + else + targetConfig = &rwPixmapRGBConfig; + } + + if (*targetConfig == 0) { + QEglProperties configAttribs; + configAttribs.setValue(EGL_SURFACE_TYPE, EGL_PIXMAP_BIT); + configAttribs.setRenderableType(QEgl::OpenGL); + if (hasAlpha) + configAttribs.setValue(EGL_BIND_TO_TEXTURE_RGBA, EGL_TRUE); + else + configAttribs.setValue(EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE); + + // If this is going to be a render target, it needs to have a depth, stencil & sample buffer + if (!readOnly) { + configAttribs.setValue(EGL_DEPTH_SIZE, 1); + configAttribs.setValue(EGL_STENCIL_SIZE, 1); + configAttribs.setValue(EGL_SAMPLE_BUFFERS, 1); + } + + EGLint configCount = 0; + do { + eglChooseConfig(QEglContext::defaultDisplay(0), configAttribs.properties(), targetConfig, 1, &configCount); + if (configCount > 0) { + // Got one + qDebug() << "Found an" << (hasAlpha ? "ARGB" : "RGB") << (readOnly ? "readonly" : "target" ) + << "config (" << int(*targetConfig) << ") to create a pixmap surface:"; + +// QEglProperties configProps(*targetConfig); +// qDebug() << configProps.toString(); + break; + } + qWarning("choosePixmapConfig() - No suitible config found, reducing requirements"); + } while (configAttribs.reduceConfiguration()); + } + + if (*targetConfig == 0) + qWarning("choosePixmapConfig() - Couldn't find a suitable config"); + + return *targetConfig; +} + +bool Q_OPENGL_EXPORT qt_createEGLSurfaceForPixmap(QPixmapData* pmd, bool readOnly) +{ + Q_ASSERT(pmd->classId() == QPixmapData::X11Class); + QX11PixmapData* pixmapData = static_cast<QX11PixmapData*>(pmd); + + bool hasAlpha = pixmapData->hasAlphaChannel(); + + EGLConfig pixmapConfig = qt_chooseEGLConfigForPixmap(hasAlpha, readOnly); + + QEglProperties pixmapAttribs; + + // If the pixmap can't be bound to a texture, it's pretty useless + pixmapAttribs.setValue(EGL_TEXTURE_TARGET, EGL_TEXTURE_2D); + if (hasAlpha) + pixmapAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA); + else + pixmapAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB); + + EGLSurface pixmapSurface; + pixmapSurface = eglCreatePixmapSurface(QEglContext::defaultDisplay(0), + pixmapConfig, + (EGLNativePixmapType) pixmapData->handle(), + pixmapAttribs.properties()); + if (pixmapSurface == EGL_NO_SURFACE) { + qWarning("Failed to create a pixmap surface using config %d", (int)pixmapConfig); + return false; + } + + Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure! + pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; + pixmapData->is_cached = true; // Make sure the cleanup hook gets called + + return true; +} + QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, const qint64 key, QGLContext::BindOptions options) @@ -449,82 +566,14 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons destroyGlSurfaceForPixmap(pixmapData); } - EGLint pixmapAttribs[] = { - EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, - EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB, - EGL_NONE - }; - Q_ASSERT(sizeof(Qt::HANDLE) >= sizeof(EGLSurface)); // Just to make totally sure! - if (pixmapData->gl_surface == 0) - pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE; - EGLSurface pixmapSurface = (EGLSurface)pixmapData->gl_surface; - static EGLConfig pixmapRGBConfig = 0; - static EGLConfig pixmapRGBAConfig = 0; - - // Check to see if we need to find a config - if ((hasAlpha && !pixmapRGBAConfig) || (!hasAlpha && !pixmapRGBConfig) ) { - const EGLint configAttribs[] = { - EGL_SURFACE_TYPE, EGL_PIXMAP_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_DEPTH_SIZE, 0, - hasAlpha ? EGL_BIND_TO_TEXTURE_RGBA : EGL_BIND_TO_TEXTURE_RGB, EGL_TRUE, - EGL_NONE - }; - - EGLint configCount = 0; - eglChooseConfig(eglContext->display(), configAttribs, 0, 256, &configCount); - if (configCount == 0) { - haveTFP = false; - qWarning("bindTextureFromNativePixmap() - Couldn't find a suitable config"); - return 0; - } - - EGLConfig *configList = new EGLConfig[configCount]; - eglChooseConfig(eglContext->display(), configAttribs, configList, configCount, &configCount); - Q_ASSERT(configCount); - - // Try to create a pixmap surface for each config until one works - for (int i = 0; i < configCount; ++i) { - pixmapSurface = eglCreatePixmapSurface(eglContext->display(), configList[i], - (EGLNativePixmapType) pixmapData->handle(), - pixmapAttribs); - if (pixmapSurface != EGL_NO_SURFACE) { - // Got one! - qDebug() << "Found an" << (hasAlpha ? "ARGB" : "RGB") - << "config (" << int(configList[i]) << ") to create a pixmap surface"; - if (hasAlpha) - pixmapRGBAConfig = configList[i]; - else - pixmapRGBConfig = configList[i]; - pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; - break; - } - } - delete configList; - - if ((hasAlpha && !pixmapRGBAConfig) || (!hasAlpha && !pixmapRGBConfig) ) { - qDebug("Couldn't create a pixmap surface with any of the provided configs"); - haveTFP = false; - return 0; - } - } - - if (pixmapSurface == EGL_NO_SURFACE) { - pixmapSurface = eglCreatePixmapSurface(eglContext->display(), - hasAlpha? pixmapRGBAConfig : pixmapRGBConfig, - (EGLNativePixmapType) pixmapData->handle(), - pixmapAttribs); - if (pixmapSurface == EGL_NO_SURFACE) { - qWarning("Failed to create a pixmap surface using config %d", - (int)(hasAlpha? pixmapRGBAConfig : pixmapRGBConfig)); + if (pixmapData->gl_surface == 0) { + bool success = qt_createEGLSurfaceForPixmap(pixmapData, true); + if (!success) { haveTFP = false; return 0; } - pixmapData->gl_surface = (Qt::HANDLE)pixmapSurface; } - // Make sure the cleanup hook gets called so we can delete the glx pixmap - pixmapData->is_cached = true; Q_ASSERT(pixmapData->gl_surface); GLuint textureId; @@ -534,10 +583,10 @@ QGLTexture *QGLContextPrivate::bindTextureFromNativePixmap(QPixmapData* pd, cons // bind the egl pixmap surface to a texture EGLBoolean success; - success = eglBindTexImage(eglContext->display(), pixmapSurface, EGL_BACK_BUFFER); + success = eglBindTexImage(eglContext->display(), (EGLSurface)pixmapData->gl_surface, EGL_BACK_BUFFER); if (success == EGL_FALSE) { qWarning() << "eglBindTexImage() failed:" << eglContext->errorString(eglGetError()); - eglDestroySurface(eglContext->display(), pixmapSurface); + eglDestroySurface(eglContext->display(), (EGLSurface)pixmapData->gl_surface); pixmapData->gl_surface = (Qt::HANDLE)EGL_NO_SURFACE; haveTFP = false; return 0; diff --git a/src/opengl/qglframebufferobject.cpp b/src/opengl/qglframebufferobject.cpp index c728902..8fc95cf 100644 --- a/src/opengl/qglframebufferobject.cpp +++ b/src/opengl/qglframebufferobject.cpp @@ -64,7 +64,8 @@ QT_BEGIN_NAMESPACE extern QImage qt_gl_read_framebuffer(const QSize&, bool, bool); -#define QGL_FUNC_CONTEXT QGLContextGroup *ctx = d_ptr->ctx; +#define QGL_FUNC_CONTEXT const QGLContext *ctx = d_ptr->fbo_guard.context(); +#define QGL_FUNCP_CONTEXT const QGLContext *ctx = fbo_guard.context(); #ifndef QT_NO_DEBUG #define QT_RESET_GLERROR() \ @@ -317,7 +318,7 @@ void QGLFBOGLPaintDevice::setFBO(QGLFramebufferObject* f, QGLFramebufferObject::Attachment attachment) { fbo = f; - m_thisFBO = fbo->d_func()->fbo; // This shouldn't be needed + m_thisFBO = fbo->d_func()->fbo(); // This shouldn't be needed // The context that the fbo was created in may not have depth // and stencil buffers, but the fbo itself might. @@ -334,7 +335,7 @@ void QGLFBOGLPaintDevice::ensureActiveTarget() { QGLContext* ctx = const_cast<QGLContext*>(QGLContext::currentContext()); Q_ASSERT(ctx); - const GLuint fboId = fbo->d_func()->fbo; + const GLuint fboId = fbo->d_func()->fbo(); if (ctx->d_func()->current_fbo != fboId) { ctx->d_func()->current_fbo = fboId; glBindFramebuffer(GL_FRAMEBUFFER_EXT, fboId); @@ -359,6 +360,9 @@ void QGLFBOGLPaintDevice::endPaint() bool QGLFramebufferObjectPrivate::checkFramebufferStatus() const { + QGL_FUNCP_CONTEXT; + if (!ctx) + return false; // Context no longer exists. GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); switch(status) { case GL_NO_ERROR: @@ -405,11 +409,11 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, QGLFramebufferObject::Attachment attachment, GLenum texture_target, GLenum internal_format, GLint samples) { - QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); - ctx = QGLContextPrivate::contextGroup(currentContext); + QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); + fbo_guard.setContext(ctx); bool ext_detected = (QGLExtensions::glExtensions & QGLExtensions::FramebufferObject); - if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(currentContext))) + if (!ext_detected || (ext_detected && !qt_resolve_framebufferobject_extensions(ctx))) return; size = sz; @@ -417,8 +421,10 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, // texture dimensions QT_RESET_GLERROR(); // reset error state + GLuint fbo = 0; glGenFramebuffers(1, &fbo); glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo); + fbo_guard.setId(fbo); glDevice.setFBO(q, attachment); @@ -445,6 +451,7 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, QT_CHECK_GLERROR(); valid = checkFramebufferStatus(); + glBindTexture(target, 0); color_buffer = 0; } else { @@ -535,13 +542,14 @@ void QGLFramebufferObjectPrivate::init(QGLFramebufferObject *q, const QSize &sz, fbo_attachment = QGLFramebufferObject::NoAttachment; } - glBindFramebuffer(GL_FRAMEBUFFER_EXT, currentContext->d_ptr->current_fbo); + glBindFramebuffer(GL_FRAMEBUFFER_EXT, ctx->d_ptr->current_fbo); if (!valid) { if (color_buffer) glDeleteRenderbuffers(1, &color_buffer); else glDeleteTextures(1, &texture); glDeleteFramebuffers(1, &fbo); + fbo_guard.setId(0); } QT_CHECK_GLERROR(); @@ -810,19 +818,16 @@ QGLFramebufferObject::~QGLFramebufferObject() delete d->engine; - if (isValid()) { - const QGLContext *oldContext = QGLContext::currentContext(); - bool switchContext = !oldContext || QGLContextPrivate::contextGroup(oldContext) != ctx; - if (switchContext) - const_cast<QGLContext *>(ctx->context())->makeCurrent(); - glDeleteTextures(1, &d->texture); + if (isValid() && ctx) { + QGLShareContextScope scope(ctx); + if (d->texture) + glDeleteTextures(1, &d->texture); if (d->color_buffer) glDeleteRenderbuffers(1, &d->color_buffer); if (d->depth_stencil_buffer) glDeleteRenderbuffers(1, &d->depth_stencil_buffer); - glDeleteFramebuffers(1, &d->fbo); - if (oldContext && switchContext) - const_cast<QGLContext *>(oldContext)->makeCurrent(); + GLuint fbo = d->fbo(); + glDeleteFramebuffers(1, &fbo); } } @@ -838,11 +843,16 @@ QGLFramebufferObject::~QGLFramebufferObject() The non-power of two limitation does not apply if the OpenGL version is 2.0 or higher, or if the GL_ARB_texture_non_power_of_two extension is present. + + The framebuffer can also become invalid if the QGLContext that + the framebuffer was created within is destroyed and there are + no other shared contexts that can take over ownership of the + framebuffer. */ bool QGLFramebufferObject::isValid() const { Q_D(const QGLFramebufferObject); - return d->valid; + return d->valid && d->fbo_guard.context(); } /*! @@ -867,15 +877,17 @@ bool QGLFramebufferObject::bind() return false; Q_D(QGLFramebufferObject); QGL_FUNC_CONTEXT; - glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo); + if (!ctx) + return false; // Context no longer exists. + glBindFramebuffer(GL_FRAMEBUFFER_EXT, d->fbo()); d->valid = d->checkFramebufferStatus(); const QGLContext *context = QGLContext::currentContext(); if (d->valid && context) { - Q_ASSERT(QGLContextPrivate::contextGroup(context) == ctx); + Q_ASSERT(QGLContextPrivate::contextGroup(context) == QGLContextPrivate::contextGroup(ctx)); // Save the previous setting to automatically restore in release(). - if (context->d_ptr->current_fbo != d->fbo) { + if (context->d_ptr->current_fbo != d->fbo()) { d->previous_fbo = context->d_ptr->current_fbo; - context->d_ptr->current_fbo = d->fbo; + context->d_ptr->current_fbo = d->fbo(); } } return d->valid; @@ -900,10 +912,12 @@ bool QGLFramebufferObject::release() return false; Q_D(QGLFramebufferObject); QGL_FUNC_CONTEXT; + if (!ctx) + return false; // Context no longer exists. const QGLContext *context = QGLContext::currentContext(); if (context) { - Q_ASSERT(QGLContextPrivate::contextGroup(context) == ctx); + Q_ASSERT(QGLContextPrivate::contextGroup(context) == QGLContextPrivate::contextGroup(ctx)); // Restore the previous setting for stacked framebuffer objects. if (d->previous_fbo != context->d_ptr->current_fbo) { context->d_ptr->current_fbo = d->previous_fbo; @@ -976,7 +990,7 @@ QImage QGLFramebufferObject::toImage() const bool wasBound = isBound(); if (!wasBound) const_cast<QGLFramebufferObject *>(this)->bind(); - QImage image = qt_gl_read_framebuffer(d->size, format().textureTarget() != GL_RGB, true); + QImage image = qt_gl_read_framebuffer(d->size, format().internalTextureFormat() != GL_RGB, true); if (!wasBound) const_cast<QGLFramebufferObject *>(this)->release(); @@ -1144,7 +1158,7 @@ int QGLFramebufferObject::metric(PaintDeviceMetric metric) const GLuint QGLFramebufferObject::handle() const { Q_D(const QGLFramebufferObject); - return d->fbo; + return d->fbo(); } /*! \fn int QGLFramebufferObject::devType() const @@ -1175,7 +1189,7 @@ QGLFramebufferObject::Attachment QGLFramebufferObject::attachment() const bool QGLFramebufferObject::isBound() const { Q_D(const QGLFramebufferObject); - return QGLContext::currentContext()->d_ptr->current_fbo == d->fbo; + return QGLContext::currentContext()->d_ptr->current_fbo == d->fbo(); } /*! diff --git a/src/opengl/qglframebufferobject_p.h b/src/opengl/qglframebufferobject_p.h index f80209d..9fe80b8 100644 --- a/src/opengl/qglframebufferobject_p.h +++ b/src/opengl/qglframebufferobject_p.h @@ -127,15 +127,15 @@ private: class QGLFramebufferObjectPrivate { public: - QGLFramebufferObjectPrivate() : depth_stencil_buffer(0), valid(false), ctx(0), previous_fbo(0), engine(0) {} + QGLFramebufferObjectPrivate() : fbo_guard(0), texture(0), depth_stencil_buffer(0), color_buffer(0), valid(false), previous_fbo(0), engine(0) {} ~QGLFramebufferObjectPrivate() {} void init(QGLFramebufferObject *q, const QSize& sz, QGLFramebufferObject::Attachment attachment, GLenum internal_format, GLenum texture_target, GLint samples = 0); bool checkFramebufferStatus() const; + QGLSharedResourceGuard fbo_guard; GLuint texture; - GLuint fbo; GLuint depth_stencil_buffer; GLuint color_buffer; GLenum target; @@ -143,10 +143,11 @@ public: QGLFramebufferObjectFormat format; uint valid : 1; QGLFramebufferObject::Attachment fbo_attachment; - QGLContextGroup *ctx; // for Windows extension ptrs GLuint previous_fbo; mutable QPaintEngine *engine; QGLFBOGLPaintDevice glDevice; + + inline GLuint fbo() const { return fbo_guard.id(); } }; diff --git a/src/opengl/qglpixelbuffer_egl.cpp b/src/opengl/qglpixelbuffer_egl.cpp index 4cba1bb..744fbd4 100644 --- a/src/opengl/qglpixelbuffer_egl.cpp +++ b/src/opengl/qglpixelbuffer_egl.cpp @@ -151,7 +151,7 @@ bool QGLPixelBufferPrivate::init(const QSize &size, const QGLFormat &f, QGLWidge bool QGLPixelBufferPrivate::cleanup() { - eglDestroySurface(QEglContext::defaultDisplay(0), pbuf); + // No need to destroy "pbuf" here - it is done in QGLContext::reset(). return true; } @@ -202,13 +202,20 @@ GLuint QGLPixelBuffer::generateDynamicTexture() const bool QGLPixelBuffer::hasOpenGLPbuffers() { // See if we have at least 1 configuration that matches the default format. - QEglContext ctx; - if (!ctx.openDisplay(0)) + EGLDisplay dpy = QEglContext::defaultDisplay(0); + if (dpy == EGL_NO_DISPLAY) return false; QEglProperties configProps; qt_egl_set_format(configProps, QInternal::Pbuffer, QGLFormat::defaultFormat()); configProps.setRenderableType(QEgl::OpenGL); - return ctx.chooseConfig(configProps); + do { + EGLConfig cfg = 0; + EGLint matching = 0; + if (eglChooseConfig(dpy, configProps.properties(), + &cfg, 1, &matching) && matching > 0) + return true; + } while (configProps.reduceConfiguration()); + return false; } QT_END_NAMESPACE diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 15714c2..0603369 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -100,22 +100,42 @@ private: class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter> { public: - QGLPixmapBlurFilter(QPixmapBlurFilter::BlurHint hint); + QGLPixmapBlurFilter(Qt::RenderHint hint); void setUniforms(QGLShaderProgram *program); + static QByteArray generateGaussianShader(int radius, bool dropShadow = false); + protected: bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; private: - static QByteArray generateGaussianShader(int radius); mutable QSize m_textureSize; mutable bool m_horizontalBlur; mutable bool m_haveCached; mutable int m_cachedRadius; - mutable QPixmapBlurFilter::BlurHint m_hint; + mutable Qt::RenderHint m_hint; +}; + +class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter> +{ +public: + QGLPixmapDropShadowFilter(Qt::RenderHint hint); + + void setUniforms(QGLShaderProgram *program); + +protected: + bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; + +private: + mutable QSize m_textureSize; + mutable bool m_horizontalBlur; + + mutable bool m_haveCached; + mutable int m_cachedRadius; + mutable Qt::RenderHint m_hint; }; extern QGLWidget *qt_gl_share_widget(); @@ -131,16 +151,28 @@ QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *pr case QPixmapFilter::BlurFilter: { const QPixmapBlurFilter *proto = static_cast<const QPixmapBlurFilter *>(prototype); - if (proto->blurHint() == QPixmapBlurFilter::PerformanceHint || proto->radius() <= 5) { + if (proto->blurHint() == Qt::PerformanceHint || proto->radius() <= 5) { if (!d->fastBlurFilter) - d->fastBlurFilter.reset(new QGLPixmapBlurFilter(QPixmapBlurFilter::PerformanceHint)); + d->fastBlurFilter.reset(new QGLPixmapBlurFilter(Qt::PerformanceHint)); return d->fastBlurFilter.data(); } if (!d->blurFilter) - d->blurFilter.reset(new QGLPixmapBlurFilter(QPixmapBlurFilter::QualityHint)); + d->blurFilter.reset(new QGLPixmapBlurFilter(Qt::QualityHint)); return d->blurFilter.data(); } + case QPixmapFilter::DropShadowFilter: { + const QPixmapDropShadowFilter *proto = static_cast<const QPixmapDropShadowFilter *>(prototype); + if (proto->blurRadius() <= 5) { + if (!d->fastDropShadowFilter) + d->fastDropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::PerformanceHint)); + return d->fastDropShadowFilter.data(); + } + if (!d->dropShadowFilter) + d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter(Qt::QualityHint)); + return d->dropShadowFilter.data(); + } + case QPixmapFilter::ConvolutionFilter: if (!d->convolutionFilter) d->convolutionFilter.reset(new QGLPixmapConvolutionFilter); @@ -279,12 +311,26 @@ static const char *qt_gl_blur_filter_fast = " return color * (1.0 / float(samples));" "}"; -QGLPixmapBlurFilter::QGLPixmapBlurFilter(QPixmapBlurFilter::BlurHint hint) +static const char *qt_gl_drop_shadow_filter_fast = + "const int samples = 9;" + "uniform mediump vec2 delta;" + "uniform mediump vec4 shadowColor;" + "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {" + " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);" + " mediump float offset = (float(samples) - 1.0) / 2.0;" + " for (int i = 0; i < samples; i++) {" + " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;" + " color += texture2D(src, coord).a * shadowColor;" + " }" + " return color * (1.0 / float(samples));" + "}"; + +QGLPixmapBlurFilter::QGLPixmapBlurFilter(Qt::RenderHint hint) : m_haveCached(false) , m_cachedRadius(5) , m_hint(hint) { - if (hint == PerformanceHint) { + if (hint == Qt::PerformanceHint) { QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this); filter->setSource(qt_gl_blur_filter_fast); m_haveCached = true; @@ -296,7 +342,7 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this); int radius = this->radius(); - if (!m_haveCached || (m_hint == QualityHint && radius != m_cachedRadius)) { + if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) { // Only regenerate the shader from source if parameters have changed. m_haveCached = true; m_cachedRadius = radius; @@ -358,7 +404,7 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program) { - if (m_hint == QualityHint) { + if (m_hint == Qt::QualityHint) { if (m_horizontalBlur) program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0); else @@ -380,7 +426,7 @@ static inline qreal gaussian(qreal dx, qreal sigma) return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma); } -QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) +QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShadow) { Q_ASSERT(radius >= 1); @@ -388,6 +434,8 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) source.reserve(1000); source.append("uniform highp vec2 delta;\n"); + if (dropShadow) + source.append("uniform mediump vec4 shadowColor;\n"); source.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n"); QVector<qreal> sampleOffsets; @@ -444,7 +492,10 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) source.append(coordinate); source.append(";\n"); - source.append(" sample += texture2D(src, coord)"); + if (dropShadow) + source.append(" sample += texture2D(src, coord).a * shadowColor"); + else + source.append(" sample += texture2D(src, coord)"); weightSum += weights.at(i); if (weights.at(i) != qreal(1)) { @@ -463,4 +514,114 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius) return source; } +QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(Qt::RenderHint hint) + : m_haveCached(false) + , m_cachedRadius(5) + , m_hint(hint) +{ + if (hint == Qt::PerformanceHint) { + QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this); + filter->setSource(qt_gl_drop_shadow_filter_fast); + m_haveCached = true; + } +} + +bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const +{ + QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this); + + int radius = this->blurRadius(); + if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) { + // Only regenerate the shader from source if parameters have changed. + m_haveCached = true; + m_cachedRadius = radius; + filter->setSource(QGLPixmapBlurFilter::generateGaussianShader(radius, true)); + } + + QGLFramebufferObjectFormat format; + format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB)); + QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format); + + if (!fbo) + return false; + + glBindTexture(GL_TEXTURE_2D, fbo->texture()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D, 0); + + // prepare for updateUniforms + m_textureSize = src.size(); + + // horizontal pass, to pixmap + m_horizontalBlur = true; + + QPainter fboPainter(fbo); + + if (src.hasAlphaChannel()) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + } + + // ensure GL_LINEAR filtering is used + fboPainter.setRenderHint(QPainter::SmoothPixmapTransform); + filter->setOnPainter(&fboPainter); + fboPainter.drawPixmap(0, 0, src); + filter->removeFromPainter(&fboPainter); + fboPainter.end(); + + QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); + + // vertical pass, to painter + m_horizontalBlur = false; + + painter->save(); + // ensure GL_LINEAR filtering is used + painter->setRenderHint(QPainter::SmoothPixmapTransform); + filter->setOnPainter(painter); + QPointF ofs = offset(); + engine->drawTexture(src.rect().translated(pos.x() + ofs.x(), pos.y() + ofs.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height())); + filter->removeFromPainter(painter); + painter->restore(); + + qgl_fbo_pool()->release(fbo); + + // Now draw the actual pixmap over the top. + painter->drawPixmap(pos, src, srcRect); + + return true; +} + +void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program) +{ + QColor col = color(); + if (m_horizontalBlur) { + program->setUniformValue("shadowColor", 1.0f, 1.0f, 1.0f, 1.0f); + } else { + qreal alpha = col.alphaF(); + program->setUniformValue("shadowColor", col.redF() * alpha, + col.greenF() * alpha, + col.blueF() * alpha, + alpha); + } + if (m_hint == Qt::QualityHint) { + if (m_horizontalBlur) + program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0); + else + program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height()); + } else { + // 1.4 is chosen to most closely match the blurriness of the gaussian blur + // at low radii + qreal blur = blurRadius() / 1.4f; + + if (m_horizontalBlur) + program->setUniformValue("delta", blur / m_textureSize.width(), 0.0); + else + program->setUniformValue("delta", 0.0, blur / m_textureSize.height()); + } +} + QT_END_NAMESPACE diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp index ebcd723..dfa6c40 100644 --- a/src/opengl/qglshaderprogram.cpp +++ b/src/opengl/qglshaderprogram.cpp @@ -349,7 +349,7 @@ QGLShader::QGLShader(QGLShader::ShaderType type, const QGLContext *context, QObj context = QGLContext::currentContext(); d = new QGLShaderPrivate(context, type); #ifndef QT_NO_DEBUG - if (context && !qgl_share_reg()->checkSharing(context, QGLContext::currentContext())) { + if (context && !QGLContext::areSharing(context, QGLContext::currentContext())) { qWarning("QGLShader::QGLShader: \'context\' must be the currect context or sharing with it."); return; } @@ -374,7 +374,7 @@ QGLShader::QGLShader context = QGLContext::currentContext(); d = new QGLShaderPrivate(context, type); #ifndef QT_NO_DEBUG - if (context && !qgl_share_reg()->checkSharing(context, QGLContext::currentContext())) { + if (context && !QGLContext::areSharing(context, QGLContext::currentContext())) { qWarning("QGLShader::QGLShader: \'context\' must be currect context or sharing with it."); return; } @@ -806,8 +806,8 @@ bool QGLShaderProgram::addShader(QGLShader *shader) if (d->shaders.contains(shader)) return true; // Already added to this shader program. if (d->programGuard.id() && shader) { - if (!qgl_share_reg()->checkSharing(shader->d->shaderGuard.context(), - d->programGuard.context())) { + if (!QGLContext::areSharing(shader->d->shaderGuard.context(), + d->programGuard.context())) { qWarning("QGLShaderProgram::addShader: Program and shader are not associated with same context."); return false; } diff --git a/src/opengl/qpaintengine_opengl.cpp b/src/opengl/qpaintengine_opengl.cpp index a904064..3e4a8e7 100644 --- a/src/opengl/qpaintengine_opengl.cpp +++ b/src/opengl/qpaintengine_opengl.cpp @@ -338,7 +338,7 @@ void QGLOffscreen::initialize() int dim = qMax(2048, static_cast<int>(qt_next_power_of_two(qMax(device->size().width(), device->size().height())))); - bool shared_context = qgl_share_reg()->checkSharing(device->context(), ctx); + bool shared_context = QGLContext::areSharing(device->context(), ctx); bool would_fail = last_failed_size.isValid() && (device->size().width() >= last_failed_size.width() || device->size().height() >= last_failed_size.height()); @@ -555,7 +555,7 @@ public: QList<const QGLContext *> contexts = programs.uniqueKeys(); for (int i=0; i<contexts.size(); ++i) { const QGLContext *cx = contexts.at(i); - if (cx != ctx && qgl_share_reg()->checkSharing(cx, ctx)) { + if (cx != ctx && QGLContext::areSharing(cx, ctx)) { QList<GLProgram> progs = programs.values(cx); for (int k=0; k<progs.size(); ++k) { const GLProgram &prg = progs.at(k); @@ -1015,7 +1015,7 @@ public: } inline GLuint getBuffer(const QGradient &gradient, qreal opacity, QGLContext *ctx) { - if (buffer_ctx && !qgl_share_reg()->checkSharing(buffer_ctx, ctx)) + if (buffer_ctx && !QGLContext::areSharing(buffer_ctx, ctx)) cleanCache(); buffer_ctx = ctx; @@ -1365,7 +1365,7 @@ bool QOpenGLPaintEngine::begin(QPaintDevice *pdev) #ifdef QT_OPENGL_ES d->max_texture_size = ctx->d_func()->maxTextureSize(); #else - bool shared_ctx = qgl_share_reg()->checkSharing(d->device->context(), d->shader_ctx); + bool shared_ctx = QGLContext::areSharing(d->device->context(), d->shader_ctx); if (shared_ctx) { d->max_texture_size = d->shader_ctx->d_func()->maxTextureSize(); @@ -2311,7 +2311,7 @@ void QOpenGLPaintEnginePrivate::updateDepthClip() void QOpenGLPaintEnginePrivate::systemStateChanged() { Q_Q(QOpenGLPaintEngine); - if (q->state()->hasClipping) + if (q->painter()->hasClipping()) q->updateClipRegion(q->painter()->clipRegion(), Qt::ReplaceClip); else q->updateClipRegion(QRegion(), Qt::NoClip); @@ -4683,7 +4683,7 @@ void QGLGlyphCache::cacheGlyphs(QGLContext *context, const QTextItemInt &ti, QList<const QGLContext *> contexts = qt_context_cache.keys(); for (int i=0; i<contexts.size(); ++i) { const QGLContext *ctx = contexts.at(i); - if (ctx != context && qgl_share_reg()->checkSharing(context, ctx)) { + if (ctx != context && QGLContext::areSharing(context, ctx)) { context_key = ctx; dev_it = qt_context_cache.constFind(context_key); break; diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index 2331c6d..cbb310b 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -48,9 +48,12 @@ #include <private/qgl_p.h> #include <private/qdrawhelper_p.h> +#include <private/qimage_p.h> #include <private/qpaintengineex_opengl2_p.h> +#include <qdesktopwidget.h> + QT_BEGIN_NAMESPACE extern QGLWidget* qt_gl_share_widget(); @@ -248,7 +251,7 @@ bool QGLPixmapData::isValidContext(const QGLContext *ctx) const return true; const QGLContext *share_ctx = qt_gl_share_widget()->context(); - return ctx == share_ctx || qgl_share_reg()->checkSharing(ctx, share_ctx); + return ctx == share_ctx || QGLContext::areSharing(ctx, share_ctx); } void QGLPixmapData::resize(int width, int height) @@ -315,7 +318,7 @@ void QGLPixmapData::ensureCreated() const } void QGLPixmapData::fromImage(const QImage &image, - Qt::ImageConversionFlags) + Qt::ImageConversionFlags /*flags*/) { if (image.size() == QSize(w, h)) setSerialNumber(++qt_gl_pixmap_serial); @@ -323,20 +326,26 @@ void QGLPixmapData::fromImage(const QImage &image, if (pixelType() == BitmapType) { m_source = image.convertToFormat(QImage::Format_MonoLSB); + } else { - m_source = image.hasAlphaChannel() - ? image.convertToFormat(QImage::Format_ARGB32_Premultiplied) - : image.convertToFormat(QImage::Format_RGB32); + QImage::Format format = QImage::Format_RGB32; + if (qApp->desktop()->depth() == 16) + format = QImage::Format_RGB16; + + if (image.hasAlphaChannel() && const_cast<QImage &>(image).data_ptr()->checkForAlphaPixels()) + format = QImage::Format_ARGB32_Premultiplied;; + + m_source = image.convertToFormat(format); } m_dirty = true; m_hasFillColor = false; - m_hasAlpha = image.hasAlphaChannel(); + m_hasAlpha = m_source.hasAlphaChannel(); w = image.width(); h = image.height(); is_null = (w <= 0 || h <= 0); - d = pixelType() == QPixmapData::PixmapType ? 32 : 1; + d = m_source.depth(); if (m_texture.id) { QGLShareContextScope ctx(qt_gl_share_widget()->context()); diff --git a/src/opengl/qwindowsurface_gl.cpp b/src/opengl/qwindowsurface_gl.cpp index 3a348bc..7f8577a 100644 --- a/src/opengl/qwindowsurface_gl.cpp +++ b/src/opengl/qwindowsurface_gl.cpp @@ -306,8 +306,13 @@ QGLWindowSurface::QGLWindowSurface(QWidget *window) d_ptr->pb = 0; d_ptr->fbo = 0; d_ptr->ctx = 0; +#if defined (QT_OPENGL_ES_2) + d_ptr->tried_fbo = true; + d_ptr->tried_pb = true; +#else d_ptr->tried_fbo = false; d_ptr->tried_pb = false; +#endif d_ptr->destructive_swap_buffers = qgetenv("QT_GL_SWAPBUFFER_PRESERVE").isNull(); d_ptr->glDevice.d = d_ptr; d_ptr->q_ptr = this; @@ -438,6 +443,7 @@ void QGLWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint & QRect rect = br.translated(-offset - wOffset); const GLenum target = GL_TEXTURE_2D; + Q_UNUSED(target); if (context()) { context()->makeCurrent(); diff --git a/src/opengl/util/fragmentprograms_p.h b/src/opengl/util/fragmentprograms_p.h index 18da5c8..340023c 100644 --- a/src/opengl/util/fragmentprograms_p.h +++ b/src/opengl/util/fragmentprograms_p.h @@ -71,7 +71,7 @@ enum FragmentVariable { VAR_FMP2_M_RADIUS2, VAR_FMP, VAR_INV_MATRIX_M0, - VAR_ANGLE, + VAR_ANGLE }; enum FragmentBrushType { @@ -80,7 +80,7 @@ enum FragmentBrushType { FRAGMENT_PROGRAM_BRUSH_CONICAL, FRAGMENT_PROGRAM_BRUSH_LINEAR, FRAGMENT_PROGRAM_BRUSH_TEXTURE, - FRAGMENT_PROGRAM_BRUSH_PATTERN, + FRAGMENT_PROGRAM_BRUSH_PATTERN }; enum FragmentCompositionModeType { @@ -109,12 +109,12 @@ enum FragmentCompositionModeType { COMPOSITION_MODES_DIFFERENCE_NOMASK, COMPOSITION_MODES_EXCLUSION_NOMASK, COMPOSITION_MODE_BLEND_MODE_MASK, - COMPOSITION_MODE_BLEND_MODE_NOMASK, + COMPOSITION_MODE_BLEND_MODE_NOMASK }; enum FragmentMaskType { FRAGMENT_PROGRAM_MASK_TRAPEZOID_AA, - FRAGMENT_PROGRAM_MASK_ELLIPSE_AA, + FRAGMENT_PROGRAM_MASK_ELLIPSE_AA }; static const unsigned int num_fragment_variables = 19; |