diff options
author | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2015-07-29 12:43:05 +0200 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2015-08-06 07:30:50 +0000 |
commit | 4d39da87349d4930a4fd97af3afaacc1fd838c9f (patch) | |
tree | 2609063517063ab38d3a7caf95d917f35423f1e6 | |
parent | 752362b0fa48ae9be15757aff7eb3dce6665b41a (diff) |
eglfs: save state in the cursor
Running an example like hellogles3 with eglfs and the mouse cursor visible
breaks due to the cursor implementation not being aware of vertex array
objects. The cursor drawing code uses client side pointers and having a
VAO bound breaks this. Instead, the VAO should be reset to 0.
In addition, the same applies to all touched state: naive applications
doing custom OpenGL rendering expect the state to be unchanged between
frames and this is not unreasonable. So introduce saving and restoring
for all touched state, with the exception of Quick where there is no
need to waste time with this.
Change-Id: Ib79039e80993ab5fc8368357fc1147ee7863d055
Reviewed-by: Andy Nichols <andy.nichols@theqtcompany.com>
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfscursor.cpp | 129 | ||||
-rw-r--r-- | src/plugins/platforms/eglfs/qeglfscursor.h | 2 |
2 files changed, 115 insertions, 16 deletions
diff --git a/src/plugins/platforms/eglfs/qeglfscursor.cpp b/src/plugins/platforms/eglfs/qeglfscursor.cpp index d81aa2eaf2..eea130a754 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.cpp +++ b/src/plugins/platforms/eglfs/qeglfscursor.cpp @@ -43,6 +43,11 @@ #include <QtCore/QJsonObject> #include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qopenglvertexarrayobject_p.h> + +#ifndef GL_VERTEX_ARRAY_BINDING +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#endif QT_BEGIN_NAMESPACE @@ -50,8 +55,6 @@ QEglFSCursor::QEglFSCursor(QPlatformScreen *screen) : m_visible(true), m_screen(static_cast<QEglFSScreen *>(screen)), m_program(0), - m_vertexCoordEntry(0), - m_textureCoordEntry(0), m_textureEntry(0), m_deviceListener(0), m_updateRequested(false) @@ -134,10 +137,10 @@ void QEglFSCursor::createShaderPrograms() m_program = new QOpenGLShaderProgram; m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, textureVertexProgram); m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, textureFragmentProgram); + m_program->bindAttributeLocation("vertexCoordEntry", 0); + m_program->bindAttributeLocation("textureCoordEntry", 1); m_program->link(); - m_vertexCoordEntry = m_program->attributeLocation("vertexCoordEntry"); - m_textureCoordEntry = m_program->attributeLocation("textureCoordEntry"); m_textureEntry = m_program->uniformLocation("texture"); } @@ -322,8 +325,105 @@ void QEglFSCursor::paintOnScreen() draw(r); } +// In order to prevent breaking code doing custom OpenGL rendering while +// expecting the state in the context unchanged, save and restore all the state +// we touch. The exception is Qt Quick where the scenegraph is known to be able +// to deal with the changes we make. +struct StateSaver +{ + StateSaver() { + f = QOpenGLContext::currentContext()->functions(); + vaoHelper = new QOpenGLVertexArrayObjectHelper(QOpenGLContext::currentContext()); + + static bool windowsChecked = false; + static bool shouldSave = true; + if (!windowsChecked) { + windowsChecked = true; + QWindowList windows = QGuiApplication::allWindows(); + if (!windows.isEmpty() && windows[0]->inherits("QQuickWindow")) + shouldSave = false; + } + saved = shouldSave; + if (!shouldSave) + return; + + f->glGetIntegerv(GL_CURRENT_PROGRAM, &program); + f->glGetIntegerv(GL_TEXTURE_BINDING_2D, &texture); + f->glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); + f->glGetIntegerv(GL_FRONT_FACE, &frontFace); + cull = f->glIsEnabled(GL_CULL_FACE); + depthTest = f->glIsEnabled(GL_DEPTH_TEST); + blend = f->glIsEnabled(GL_BLEND); + f->glGetIntegerv(GL_BLEND_SRC_RGB, blendFunc); + f->glGetIntegerv(GL_BLEND_SRC_ALPHA, blendFunc + 1); + f->glGetIntegerv(GL_BLEND_DST_RGB, blendFunc + 2); + f->glGetIntegerv(GL_BLEND_DST_ALPHA, blendFunc + 3); + f->glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &arrayBuf); + if (vaoHelper->isValid()) + f->glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vao); + for (int i = 0; i < 2; ++i) { + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, &va[i].enabled); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, &va[i].size); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, &va[i].type); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &va[i].normalized); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, &va[i].stride); + f->glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, &va[i].buffer); + f->glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &va[i].pointer); + } + } + ~StateSaver() { + if (saved) { + f->glUseProgram(program); + f->glBindTexture(GL_TEXTURE_2D, texture); + f->glActiveTexture(activeTexture); + f->glFrontFace(frontFace); + if (cull) + f->glEnable(GL_CULL_FACE); + else + f->glDisable(GL_CULL_FACE); + if (depthTest) + f->glEnable(GL_DEPTH_TEST); + else + f->glDisable(GL_DEPTH_TEST); + if (blend) + f->glEnable(GL_BLEND); + else + f->glDisable(GL_BLEND); + f->glBlendFuncSeparate(blendFunc[0], blendFunc[1], blendFunc[2], blendFunc[3]); + f->glBindBuffer(GL_ARRAY_BUFFER, arrayBuf); + if (vaoHelper->isValid()) + vaoHelper->glBindVertexArray(vao); + for (int i = 0; i < 2; ++i) { + if (va[i].enabled) + f->glEnableVertexAttribArray(i); + else + f->glDisableVertexAttribArray(i); + f->glBindBuffer(GL_ARRAY_BUFFER, va[i].buffer); + f->glVertexAttribPointer(i, va[i].size, va[i].type, va[i].normalized, va[i].stride, va[i].pointer); + } + } + delete vaoHelper; + } + QOpenGLFunctions *f; + QOpenGLVertexArrayObjectHelper *vaoHelper; + bool saved; + GLint program; + GLint texture; + GLint activeTexture; + GLint frontFace; + bool cull; + bool depthTest; + bool blend; + GLint blendFunc[4]; + GLint vao; + GLint arrayBuf; + struct { GLint enabled, type, size, normalized, stride, buffer; GLvoid *pointer; } va[2]; +}; + void QEglFSCursor::draw(const QRectF &r) { + StateSaver stateSaver; + if (!m_program) { // one time initialization initializeOpenGLFunctions(); @@ -373,13 +473,16 @@ void QEglFSCursor::draw(const QRectF &r) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_cursor.texture); - glBindBuffer(GL_ARRAY_BUFFER, 0); - m_program->enableAttributeArray(m_vertexCoordEntry); - m_program->enableAttributeArray(m_textureCoordEntry); + if (stateSaver.vaoHelper->isValid()) + stateSaver.vaoHelper->glBindVertexArray(0); - m_program->setAttributeArray(m_vertexCoordEntry, cursorCoordinates, 2); - m_program->setAttributeArray(m_textureCoordEntry, textureCoordinates, 2); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + m_program->enableAttributeArray(0); + m_program->enableAttributeArray(1); + m_program->setAttributeArray(0, cursorCoordinates, 2); + m_program->setAttributeArray(1, textureCoordinates, 2); m_program->setUniformValue(m_textureEntry, 0); @@ -388,13 +491,11 @@ void QEglFSCursor::draw(const QRectF &r) glEnable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); // disable depth testing to make sure cursor is always on top - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - glDisable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, 0); - m_program->disableAttributeArray(m_textureCoordEntry); - m_program->disableAttributeArray(m_vertexCoordEntry); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + m_program->disableAttributeArray(0); + m_program->disableAttributeArray(1); m_program->release(); } diff --git a/src/plugins/platforms/eglfs/qeglfscursor.h b/src/plugins/platforms/eglfs/qeglfscursor.h index 048f276137..be5db41b37 100644 --- a/src/plugins/platforms/eglfs/qeglfscursor.h +++ b/src/plugins/platforms/eglfs/qeglfscursor.h @@ -131,8 +131,6 @@ private: bool m_visible; QEglFSScreen *m_screen; QOpenGLShaderProgram *m_program; - int m_vertexCoordEntry; - int m_textureCoordEntry; int m_textureEntry; QEglFSCursorDeviceListener *m_deviceListener; bool m_updateRequested; |