Bug in Qt >= 5.4 when drawing QPixmap onto QOpenGLPaintDevice



  • Hello,
    I came across a strange behavior when drawing QPixmap-s onto QOpenGLPaintDevice in Qt 5.4+
    Here is the Qt project which can reproduce this behavior: http://s000.tinyupload.com/?file_id=47422674383658218989

    What I do is I render scene into two textures, first is rendered with OpenGL (simple triangle), the second one is rendered with QPainter using QOpenGLPaintDevice. These two textures are then sent into a shader program where I combine them.
    Correct result in Qt 5.3.2: http://pasteboard.co/FCKxxWJ.png
    Incorrect result in Qt 5.6.0: http://pasteboard.co/FCM2FIu.png

    Here is what I do:

    // Render triangle into the first texture
    mLayer1FBO->bind();
    glViewport(0, 0, mLayer1FBO->width(), mLayer1FBO->height());
    glClearColor(0, 1, 1, 1);
    glClear(GL_COLOR_BUFFER_BIT);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1, 1, -1, 1, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
    glBegin(GL_TRIANGLES);
    glColor4f(0, 0, 1, 1);
    glVertex2f(-1, -1);
    glVertex2f( 0,  1);
    glVertex2f( 1, -1);
    glEnd();
    mLayer1FBO->release();
    
    // Render translucent line and a rectangle into the second texture
    mLayer2FBO->bind();
    glViewport(0, 0, mLayer2FBO->width(), mLayer2FBO->height());
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);
    {
    	QOpenGLPaintDevice openglPaintDevice(mLayer2FBO->size());
    	QPainter openglPainter(&openglPaintDevice);
    
    	/*
    	 * Abnormality #1
    	 * Rendering strokes with opacity fills the bounding box with pen's color
    	 */
    	openglPainter.setPen(QColor(255, 255, 0, 200));
    	openglPainter.drawLine(0, 0, 120, 100);
    
    	{
    		mLayer2Pixmap->fill(Qt::transparent);
    		QPainter pixmapPainter(mLayer2Pixmap);
    		pixmapPainter.fillRect(50, 50, 100, 100, QColor(255, 0, 0, 255));
    	}
    	/*
    	 * Abnormality #2
    	 * Rendering to a QPixmap then drawing the pixmap with QPainter onto OpenGL surface
    	 * will lead to an invalid OpenGL state (third part of this example which takes two
    	 * textures and blends them together in a simple fragment shader stops working).
    	 * Using QImage over QPixmap does not help
    	 */
    	openglPainter.drawPixmap(0, 0, *mLayer2Pixmap);
    }
    mLayer2FBO->release();
    
    // Blend two textures together into the default render target
    #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0)
    glBindFramebuffer(GL_FRAMEBUFFER, context()->contextHandle()->defaultFramebufferObject());
    #else
    glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebufferObject());
    #endif
    mBlender->setBackgroundColor(Color::Black);
    mBlender->setTextureCount(2);
    mBlender->setTexture(0, mLayer1FBO->texture(), 1.0f);
    mBlender->setTexture(1, mLayer2FBO->texture(), 1.0f);
    mBlender->render(CompositionEffect::InvalidSourceTexture, mLayer1FBO->width(), mLayer2FBO->height());
    

    If I comment out this line:

    openglPainter.drawPixmap(0, 0, *mLayer2Pixmap);
    

    then everything works fine (except the second texture does not contain the red rectangle, obviously).
    My guess is that the drawPixmap method somehow modifies the OpenGL state.

    This is the code used to blend together the textures (see TextureCombineCompositionEffect.cpp and ShaderCompositionEffect.cpp in the attached ZIP):

    // Bind shader program
    mShaderProgram.bind();
    
    // Bind vertex buffer object and set attribute pointer
    mOpenGLFunctions.glBindBuffer(GL_ARRAY_BUFFER, mVBO);
    mOpenGLFunctions.glEnableVertexAttribArray(mAttribPos);
    mOpenGLFunctions.glVertexAttribPointer(mAttribPos, 2, GL_FLOAT, GL_FALSE, 0, 0);
    
    // Bind textures
    const int count = static_cast<int>(mTextureCount);
    for (int i = 0; i < count; i++)
    {
    	mOpenGLFunctions.glActiveTexture(GL_TEXTURE0 + i);
    	glEnable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, mTextures[i]);
    	mShaderProgram.setUniformValue(mAttribSamplers + i, i);
    }
    
    // Set attributes
    mShaderProgram.setUniformValue(mAttribCount, count);
    mShaderProgram.setUniformValueArray(mAttribFactors, mBlendFactors, count, 1);
    mShaderProgram.setUniformValue(mAttribBackground, QVector4D(mBackgroundColor.r, mBackgroundColor.g,
    															mBackgroundColor.b, mBackgroundColor.a));
    
    // Render screen quad
    mOpenGLFunctions.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    // Unbind textures
    const int textureCount = static_cast<int>(mTextureCount);
    for (int i = 0; i < textureCount; ++i)
    {
    	mOpenGLFunctions.glActiveTexture(GL_TEXTURE0 + i);
    	glDisable(GL_TEXTURE_2D);
    	glBindTexture(GL_TEXTURE_2D, 0);
    }
    
    // Unbind vertex buffer object and shader program
    mOpenGLFunctions.glDisableVertexAttribArray(mAttribPos);
    mOpenGLFunctions.glBindBuffer(GL_ARRAY_BUFFER, 0);
    mShaderProgram.release();
    


  • I have found out that if I call:

    glActiveTexture(GL_TEXTURE0);
    

    before calling

    openglPainter.drawPixmap(0, 0, *mLayer2Pixmap);
    

    then it works. I have no idea why, because the QGL2PaintEngineEx::drawPixmap(const QRectF&, const QPixmap&, const QRectF&) method activates the texture unit like this:

    d->glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT);
    

    What is going on, Qt devs?


  • Lifetime Qt Champion

    Hi,

    What OS are you running ? Your shader crashes on my old OS X.

    In any case, you should take a look at the bug report system to see if it's something know.

    As for the Qt developers/maintainers, you can reach them on the interest mailing list. This forum is more user oriented.



  • @SGaist Hi, I'm running Windows 10, OpenGL 4.5.0



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.