Along with my recent push to have a more active developer blog – I’ve also decided to start doing some personal posts which look back on the week and what I’ve done or learned. This is meant more as a personal exercise than anything else. I call it “Week In Development” just because it sounds better than “Week (or two or three) In Development” or “Developer Review” or anything like that. Because realistically, these will probably be bi-weekly, or maybe monthly. It just depends on how busy I get.
Work Done
Most of the work this week was to account for the fact that I have to make a graphics framework for practicing shader techniques in OpenGL. I opted to go for using Qt5.4 because of the recent addition of some major OpenGL support classes. I can say that I haven’t been disappointed in this decision! Qt5.4 is pretty great, and adds some wonderfully convenient QOpenGL classes! It’s also been the first week of actual graphics programming – aside from a few minor things here and there in the past. I don’t consider myself having learned graphics from previous classes, not well enough at least. I never had the time to devote it to memory. (Though to be fair, I had some pretty excellent instructors.)
Qt5.4 QOpenGLWindow Bug
A part of the weekend was spent dealing with a bug which only occurred under Windows versions of Qt5.4 – When building on *nix of any flavor, the bug was not present. What would happen is when animating a window via connecting the SIGNAL(frameSwapped()) to the SLOT(update()) , QOpenGLWindow would stutter when I tried to move it around via the title bar. On *nix (tested OS X and Ubuntu x64) this bug was not present, and the window moved around smoothly.
After a small amount of debugging (and some help from GeekInABox and acf_ on irc.freenode.net #qt channel) the cause seemed to be related to QExposeEvents being sent out when a window was moved. This triggered two paintGL() calls – one from my connection to animate the window from SIGNAL(frameSwapped()) , and one from QExposeEvent telling the window that the whole surface area was invalidated and needed to be repainted.
This would cause the window to wait 2 vsyncs before actually moving the underlying window. When we wait for a single vsync, this “lag” is not noticeable – or at least greatly reduced. But instead what we get is a window whose content is refreshed at 60hz, but positioning is refreshed at 30hz. This is not ideal.
So what should be done about this?
I will not recommend that you should attempt to handle this problem in your own code. I don’t believe that it’s something the end-user should handle. Instead I believe that somewhere down the line, QOpenGLWindow, QPainterDevice, or QWindow should handle the invalid QExposeEvents. But I cannot say that with 100% certainty that they are something we always want to filter. Under every version of Windows, would duplicate QExposeEvents being raised from moving a window be unneeded? More research needs done before I can say that.
However, there is a workaround.
If we cache our QRegion of validated surface area from the QExposeEvents, and only handle the QExposeEvent when the region has changed – in terms of animation this is completely valid because we’re calling paintGL() every vsync anyways.
So we can add a few lines to our window.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
#ifndef WINDOW_H #define WINDOW_H #include <QOpenGLWindow> #include <QOpenGLFunctions> #include <QOpenGLBuffer> #include <QOpenGLVertexArrayObject> #include <QMatrix4x4> #include "transform3d.h" class QExposeEvent; class QOpenGLShaderProgram; class Window : public QOpenGLWindow, protected QOpenGLFunctions { Q_OBJECT // OpenGL Events public: Window(); void initializeGL(); void resizeGL(int width, int height); void paintGL(); protected slots: void teardownGL(); void update(); protected: void exposeEvent(QExposeEvent *ev); private: // OpenGL State Information QOpenGLBuffer m_vertex; QOpenGLVertexArrayObject m_object; QOpenGLShaderProgram *m_program; // Shader Information int u_modelToWorld; int u_worldToView; QMatrix4x4 m_projection; Transform3D m_transform; // Fix for Windows QRegion m_cachedRegion; // Private Helpers void printVersionInformation(); }; #endif // WINDOW_H |
And a few lines to our window.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <QExposeEvent> // ... void Window::exposeEvent(QExposeEvent *ev) { if (ev->region() != m_cachedRegion) { m_cachedRegion = ev->region(); QOpenGLWindow::exposeEvent(ev); } else { ev->ignore(); } } // ... |
And… Window stuttering is gone!
Note: I want to re-emphasize that I don’t think the end-user should have to deal with this problem. It’s well documented that the recommended pattern for animating a QOpenGLWindow is to simply connect the SIGNAL(frameSwapped()) to the SLOT(update()) . Adding anything onto this feels like you’re fixing a problem you shouldn’t even be encountering.
Okay, then what’s the real fix?
The real fix is that someone patches Qt5.4 such that the next release fixes this problem. I was going to attempt to handle the patch myself, but the JIRA I need to request access to for contributing to qt-project has a bug where I cannot view the captcha on the sign-up page. This is a terrible problem because it means new users cannot even submit bugs because they cannot create an account! I’ve sent an email just today to the JIRA Maintainer. Stuff like this breaks all the time and goes unnoticed (because how often do you check the Create Account screen?), so after the captcha is restored I can become a contributor and take a look at the problem formally.
Note: Since part of the build process involves a valid JIRA username, I’d rather avoid even starting the debugging process until I have a valid JIRA account.
I’ll have more this when JIRA captcha is fixed and I can sign up with the service. (May take a while depending on how busy the maintainer is, and how severe they consider not being able to create a JIRA account is. [Probably pretty severe])
A New Direction
The plan with the Qt5.4 OpenGL Tutorials was really to understand how OpenGL and Qt framework could combine to form rich applications that have dynamic, powerful graphics. Another part of it involves seeing what the overhead of Qt is (if it’s significant or measurable) for developers who want to learn OpenGL. But I’m starting to have fun with the real strength of Qt, which is being able to compile the same code on multiple machines, to different devices. So I think the tutorials will take a turn more towards that.
I had originally settled on OpenGL Desktop 3.3 – but the more I play with OpenGL ES 3.0, the more I wish I would have went down that path. It hits the most devices, with the most similar-to desktop shader language 330 of any GLES version. I plan on redirecting my tutorials to focus more on deploying to these different devices, and as such being able to run OpenGL ES 3.0 or OpenGL Desktop 3.3 will become important. The idea is that after we get our framework together, we will move it to different devices (some of this can be done already with very minor adjustments to the code).
Some of the devices planned:
- Desktop (Windows, Linux, Mac)
- iPhone 6 / iPhone 6+
- Android Nexus 9 tablet
- Oculus Rift
I’m unsure if I’ll have time for all of these, but we will at least do the Android Nexus 9 tablet.
Schedule
So looking towards the new tutorials, what’s coming up? Well, below you can see the tutorials I’ve already covered contain links to their posts, and everything after is what I currently have planned. I have “Camera Control” written, I just need to check my math to make sure everything is in order. I probably wont be posting until Thursday of next week, but I will have a stream of tutorials ready to go.
- Creating a Window
- Basic Rendering
- 3D Rendering
- Fly-Through Camera
- Efficient Input Managers
- Camera Control
- Debugging OpenGL
- Error Handling
- GPU Profiling
- Debug Drawing
- Obj Loader
- Obj Parser
- Half-Edge Mesh
I’m trying to cover a minimum of 5 tutorials a week. I came up a little short on that because I haven’t posted the “Camera Controls” tutorial. This was mostly due to the bug above – I had made a claim that it seemed like a bug in “3D Rendering” when I first observed it, but I really needed to know. Turns out it’s just going to require more work to find out for sure. Heck, I might build the current dev branch and find that someone has already fixed it! (Wishful thinking)
Cheers!