From 9541f826a4de72b620f4bd3aa7f74a82348bc91a Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 21 Aug 2014 13:57:33 +0200 Subject: Save and restore the state when doing decorations The idea that swapBuffers() randomly changes the state is ridiculous and breaks even Qt's own OpenGL examples. An application, that has full control over its context and rendering, is not necessarily prepared for random state changes in its context. To overcome this, save and restore all the interesting state. This is fragile and applications can break it in different ways by setting all sorts of exotic state, esp. when using modern OpenGL. For GLES2 level stuff it should be sufficient, though. Change-Id: I52e7ba4072b241eb92f5f04b81d707ad5bc68f6d Reviewed-by: Robin Burchell --- .../client/wayland-egl/qwaylandglcontext.cpp | 141 ++++++++++++++++++--- 1 file changed, 126 insertions(+), 15 deletions(-) (limited to 'src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp') diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp index ac1185f13..fe51dab22 100644 --- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp @@ -107,16 +107,21 @@ QWaylandGLContext::~QWaylandGLContext() bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface) { QWaylandEglWindow *window = static_cast(surface); + EGLSurface eglSurface = window->eglSurface(); + + if (eglSurface != EGL_NO_SURFACE && eglGetCurrentContext() == m_context && eglGetCurrentSurface(EGL_DRAW) == eglSurface) + return true; window->setCanResize(false); - EGLSurface eglSurface = window->eglSurface(); - if (!eglSurface) { + if (eglSurface == EGL_NO_SURFACE) { window->updateSurface(true); eglSurface = window->eglSurface(); } + if (!eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_context)) { qWarning("QWaylandGLContext::makeCurrent: eglError: %x, this: %p \n", eglGetError(), this); + window->setCanResize(true); return false; } @@ -130,6 +135,98 @@ void QWaylandGLContext::doneCurrent() eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } +#define STATE_GUARD_VERTEX_ATTRIB_COUNT 2 + +class StateGuard +{ +public: + StateGuard() { + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *) &m_program); + glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint *) &m_activeTextureUnit); + glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint *) &m_texture); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *) &m_fbo); + glGetIntegerv(GL_VIEWPORT, m_viewport); + glGetIntegerv(GL_DEPTH_WRITEMASK, &m_depthWriteMask); + glGetIntegerv(GL_COLOR_WRITEMASK, m_colorWriteMask); + m_blend = glIsEnabled(GL_BLEND); + m_depth = glIsEnabled(GL_DEPTH_TEST); + m_cull = glIsEnabled(GL_CULL_FACE); + m_scissor = glIsEnabled(GL_SCISSOR_TEST); + for (int i = 0; i < STATE_GUARD_VERTEX_ATTRIB_COUNT; ++i) { + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, (GLint *) &m_vertexAttribs[i].enabled); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, (GLint *) &m_vertexAttribs[i].arrayBuffer); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &m_vertexAttribs[i].size); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &m_vertexAttribs[i].stride); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint *) &m_vertexAttribs[i].type); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, (GLint *) &m_vertexAttribs[i].normalized); + glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &m_vertexAttribs[i].pointer); + } + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLint *) &m_minFilter); + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLint *) &m_magFilter); + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (GLint *) &m_wrapS); + glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (GLint *) &m_wrapT); + } + + ~StateGuard() { + glUseProgram(m_program); + glActiveTexture(m_activeTextureUnit); + glBindTexture(GL_TEXTURE_2D, m_texture); + glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); + glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3]); + glDepthMask(m_depthWriteMask); + glColorMask(m_colorWriteMask[0], m_colorWriteMask[1], m_colorWriteMask[2], m_colorWriteMask[3]); + if (m_blend) + glEnable(GL_BLEND); + if (m_depth) + glEnable(GL_DEPTH_TEST); + if (m_cull) + glEnable(GL_CULL_FACE); + if (m_scissor) + glEnable(GL_SCISSOR_TEST); + for (int i = 0; i < STATE_GUARD_VERTEX_ATTRIB_COUNT; ++i) { + if (m_vertexAttribs[i].enabled) + glEnableVertexAttribArray(i); + GLuint prevBuf; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint *) &prevBuf); + glBindBuffer(GL_ARRAY_BUFFER, m_vertexAttribs[i].arrayBuffer); + glVertexAttribPointer(i, m_vertexAttribs[i].size, m_vertexAttribs[i].type, + m_vertexAttribs[i].normalized, m_vertexAttribs[i].stride, + m_vertexAttribs[i].pointer); + glBindBuffer(GL_ARRAY_BUFFER, prevBuf); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT); + } + +private: + GLuint m_program; + GLenum m_activeTextureUnit; + GLuint m_texture; + GLuint m_fbo; + GLint m_depthWriteMask; + GLint m_colorWriteMask[4]; + GLboolean m_blend; + GLboolean m_depth; + GLboolean m_cull; + GLboolean m_scissor; + GLint m_viewport[4]; + struct VertexAttrib { + bool enabled; + GLuint arrayBuffer; + GLint size; + GLint stride; + GLenum type; + bool normalized; + void *pointer; + } m_vertexAttribs[STATE_GUARD_VERTEX_ATTRIB_COUNT]; + GLenum m_minFilter; + GLenum m_magFilter; + GLenum m_wrapS; + GLenum m_wrapT; +}; + void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) { QWaylandEglWindow *window = static_cast(surface); @@ -138,6 +235,11 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) if (window->decoration()) { makeCurrent(surface); + + // Must save & restore all state. Applications are usually not prepared + // for random context state changes in a swapBuffers() call. + StateGuard stateGuard; + if (!m_blitProgram) { initializeOpenGLFunctions(); m_blitProgram = new QOpenGLShaderProgram(); @@ -156,6 +258,9 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) gl_FragColor = texture2D(texture, outTexCoords);\n\ }"); + m_blitProgram->bindAttributeLocation("position", 0); + m_blitProgram->bindAttributeLocation("texCoords", 1); + if (!m_blitProgram->link()) { qDebug() << "Shader Program link failed."; qDebug() << m_blitProgram->log(); @@ -169,6 +274,11 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) glDisable(GL_DEPTH_TEST); glDisable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_SCISSOR_TEST); + glDepthMask(GL_FALSE); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + mUseNativeDefaultFbo = true; glBindFramebuffer(GL_FRAMEBUFFER, 0); mUseNativeDefaultFbo = false; @@ -194,39 +304,40 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) 1.0f, 1.0f, }; + glBindBuffer(GL_ARRAY_BUFFER, 0); m_blitProgram->bind(); - m_blitProgram->setUniformValue("texture", 0); - - m_blitProgram->enableAttributeArray("position"); - m_blitProgram->enableAttributeArray("texCoords"); - m_blitProgram->setAttributeArray("texCoords", textureVertices, 2); + m_blitProgram->enableAttributeArray(0); + m_blitProgram->enableAttributeArray(1); + m_blitProgram->setAttributeArray(1, textureVertices, 2); glActiveTexture(GL_TEXTURE0); //Draw Decoration - m_blitProgram->setAttributeArray("position", inverseSquareVertices, 2); + m_blitProgram->setAttributeArray(0, inverseSquareVertices, 2); QImage decorationImage = window->decoration()->contentImage(); cache->bindTexture(context(), decorationImage); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - if (!context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + if (context()->functions()->hasOpenGLFeature(QOpenGLFunctions::NPOTTextureRepeat)) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); //Draw Content - m_blitProgram->setAttributeArray("position", squareVertices, 2); + m_blitProgram->setAttributeArray(0, squareVertices, 2); glBindTexture(GL_TEXTURE_2D, window->contentTexture()); QRect r = window->contentsRect(); glViewport(r.x(), r.y(), r.width(), r.height()); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); //Cleanup - m_blitProgram->disableAttributeArray("position"); - m_blitProgram->disableAttributeArray("texCoords"); - m_blitProgram->release(); + m_blitProgram->disableAttributeArray(0); + m_blitProgram->disableAttributeArray(1); } eglSwapBuffers(m_eglDisplay, eglSurface); -- cgit v1.2.3