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=47422674383658218989What 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.pngHere 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?
-
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.