In the KWin/4 days our OpenGL implementation could be based on some assumptions. First of all only KWin creates an OpenGL context, none of the libraries, KWin depends on, uses OpenGL. From this it is possible to derive more assumptions, for example our compositing OpenGL context is always current. Which means that it’s possible to run any OpenGL code in any effect. No matter how the code path got triggered. Also we can link KWin against OpenGL or OpenGL ES. We don’t have to check with other libraries to prevent conflicts.
With KWin/5 these assumptions no longer hold. KWin uses QtQuick 2 and with that we pull in the Qt OpenGL module. One of the direct implications of this change is, that we are no longer able to provide kwin/OpenGL and kwin/OpenGLES at the same time. The compilation of KWin has to follow the compilation of the Qt OpenGL module. So compiling against OpenGLES is only possible if Qt is compiled for OpenGLES, which means that the option to run KWin on OpenGLES is probably non-existing on classic desktop computers and the option to run KWin on OpenGL is probably non-existing on ARM systems such as the Improv. Given that it’s no longer possible to compile both versions at the same time, the binary kwin_gles got removed. A kwin compiled against GLES is just called kwin.
With QtQuick also our assumption that only KWin creates an OpenGL context and makes it current doesn’t hold any more. In fact it could be that at any time Qt creates an OpenGL context and makes it current in the main GUI thread. Now people probably know that QtQuick 2 uses a rendering thread, so that should be fine, right? Well not quite. To decide whether QtQuick uses a rendering thread it creates a dummy context in the main gui thread and makes it current. So our assumption that our context is created once and then kept current, doesn’t hold any more. The first solution to this problem was to make our context current whenever we go into the repaint loop. That worked quite well, till I tested KWin/5 on my SandyBridge notebook.
The problem I stumbled upon is that Qt doesn’t use a rendering thread in all cases. For some hardware it prefers to use the main gui thread. One of them is SandyBridge Mobile, the hardware I am using on my notebook. This showed that the initial solution was not elaborated enough. With another context rendering in the same thread it showed that it’s quite likely that we hit conditions where our compositing context is not current. Resulting in e.g. not rendered window decorations, effects not able to load their textures, etc. etc.
These problems are hopefully solved, now. The effects API is extended by calls to make the context current and I tried to hunt down all effects which do OpenGL calls outside the repaint loop. Unfortunately given the large number of effects it’s still possible that some effects use it incorrectly. It will be difficult to track those down: so please test.
The case when QtQuick uses the main GUI thread for rendering illustrates that we in KWin are not the only ones having incorrect assumptions on OpenGL. QOpenGLContext assumes that every OpenGL context in a Qt application has been created through QOpenGLContext and that an OpenGL context is only made current on the current thread through the QOpenGLContext API. Especially if one uses glx or egl directly to make a context current QOpenGLContext doesn’t notice this and assumes that its own context is still current which can result in nastiness. This is circumvented in KWin now by making sure that QOpenGLContext has correct information once the compositing context is made current. Nevertheless we are still hitting a bug causing a crash. This one is also currently worked around in the development version by enforcing XRender based compositing on the hardware which uses the main GUI thread for rendering. On SandyBridge one can also use the environment variable QT_OPENGL_NO_SANITY_CHECK to force QtQuick to use a dedicated rendering thread as the problem why Qt uses the main gui thread is not relevant to KWin’s usage of QtQuick. KWin also checks for this environment variable and doesn’t force to XRender, if it is set.
Obviously one could question why we are not using QOpenGLContext given that this seems to conflict. We haven’t used Qt’s OpenGL module mostly for historic reasons. Of course I evaluated the option of using QOpenGLContext when investigating this issue and right now in Qt 5.2 it would not be an appropriate solution for the usage in KWin. It’s not possible to create a QOpenGLContext from a native context and even if it were possible our implementation is more flexible. KWin can be compiled against both egl and glx allowing to switch through an environment variable. Qt on the other hand supports either egl or glx, but not both at the same time. If I find time for this, I intend to improve the situation for Qt 5.3, so that we can consider the usage of QOpenGLContext once we depend on Qt 5.3. Switching to Qt’s OpenGL context wrapper would allow us to get rid of a considerable amount of code. I’m especially interested in the QOpenGLFunctions. Obviously that will only be a solution if KWin uses the same windowing system as Qt’s platform plugin. But that’s a problem for another day 😉
Just to nitpick, in Qt 5 world QtGui actually contains all the QOpenGL* classes, not QtOpenGL. (QtOpenGL these days only contains QGLWidget as a useful class, and I aim to move that to QWidgets for 5.3; for everything else you should be using the QtGui counterparts.)
ah thanks, yes I meant all the QOpenGL* classes and not QGLWidget and co.
Hi Martin,
thank you for this write up, very interesting. I too would like to see QOpenGLContext extended to cover adopting an existing OpenGL context created by other means. There is a JIRA for this at https://bugreports.qt-project.org/browse/QTBUG-31294 and an example patch (for OS X only at this stage) at https://codereview.qt-project.org/#change,56742
One query, why does KWin have to pull in QtOpenGL? It should be possible to use QtGui only now to get QOpenGLContext + QWindow. Or is there something in QtOpenGL that you use which still needs moving across to QtGui?
I’m interested to hear of any other OpenGL functionality that you think is missing from QtGui. I have plans for client-side texture data handling; texture samplers; occlusion queries; extending QOpenGLFramebufferObject to handle > ES 2 functionality; fixing mis-conceptions in QOpenGLBuffer’s API.
So much to do…
That was just bad writing on my side: it’s only QtGui. What I meant is more that it pulls in Qt’s OpenGL, which used to be the QtOpenGL module in Qt4 days.
I haven’t done a 1:1 comparison on what we have in KWin and what is in Qt, but I think since 5.2 everything we use and much more is already in Qt. There are some things which we do, but I don’t think belong into a library like Qt, e.g. controlling the swap behavior.
Please elaborate 🙂 there are patches about that too, f.i. https://codereview.qt-project.org/#change,70545
since 4.11 we have an adjusted behavior which is quite tailored for the needs of an OpenGL compositor and does more than glXSwapInterval. Using glXSwapInterval is just one of the options we have.
I guess Gentoo desktop users would still be able to pick OpenGL vs OpenGL ES.
right but you would need to compile Qt accordingly
What do you think, will it be possible for maintainers to provide both versions (gl and gles) as separate packages in binary distros? Will it be required to build different packages for something except just kwin-gl / kwin-gles?
Yes, Qt
Sorry, I’ve meant – something except kwin+qt. Also, does it mean recompiling all QT packages or just QOpenGL?
You wouldn’t need to recompile Qt applications AFAIK.
The QtOpenGl module isn’t very relevant anymore, see warning at the top of:
http://qt-project.org/doc/qt-5.1/qtopengl/qtopengl-index.html
You could technically avoid recompiling QtCore I guess. But I wouldn’t. 🙂
if the application is doing native OpenGL it might need to be recompiled, though. But I don’t know what would happen in reality. Quite likely that in mesa it would magically work 😉
Thumbs for the nice work, nice as usual.
Can’t you just
setenv("QT_OPENGL_NO_SANITY_CHECK", "1");
at KWin startup?no, as according to the comments in the Qt source code this would fail OpenGL initialization for QtQuick on VirtualBox.