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>
This commit is contained in:
Laszlo Agocs 2015-07-29 12:43:05 +02:00
parent 752362b0fa
commit 4d39da8734
2 changed files with 115 additions and 16 deletions

View File

@ -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);
if (stateSaver.vaoHelper->isValid())
stateSaver.vaoHelper->glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
m_program->enableAttributeArray(m_vertexCoordEntry);
m_program->enableAttributeArray(m_textureCoordEntry);
m_program->setAttributeArray(m_vertexCoordEntry, cursorCoordinates, 2);
m_program->setAttributeArray(m_textureCoordEntry, textureCoordinates, 2);
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);
m_program->disableAttributeArray(0);
m_program->disableAttributeArray(1);
m_program->release();
}

View File

@ -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;