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:
parent
752362b0fa
commit
4d39da8734
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user