Qt5 OpenGL Part 5: Debug Logging


This part – like the last one – is entirely optional. The functionality we will add in this section will not produce visual results, but it will take advantage of some functionality (through extensions) which is core in OpenGL 4.3. If you find that this code does not work, you simply won’t be able to log debug messages. But I imagine that any GPU capable of producing a 4.3 OpenGL Context, would also be able to provide the GPU extensions to allow for glDebugMessageCallback – which is a requirement for this part.

We will be logging messages from the GPU and printing them to our output for debug information! So first we debug the return values from the API, then we debug our calls into the API. What we’ll handle in the next parts will be debugging the actual performance of the GPU – an exciting idea borrowed from the pages of OpenGL Insights.

QOpenGLDebugLogger

Another method of handling errors will be by using the QOpenGLDebugLogger – a fairly new class which may not be supported on all graphics cards.

Even though this functionality isn’t directly supported in OpenGL 3.3, many extensions exist to allow it. Support for OpenGL has been doing pretty well lately, and even my 4-year old laptop can manage to create a 4.3 Core OpenGL context – so I think there’s a good chance this will work on many machines. (Minimum support for such functionality officially is a GPU which supports 4.3 OpenGL, but may be able to pull off less than that through extensions).

QOpenGLDebugLogger allows us to connect a signal for receiving an event to a slot for handling such event. There are two primary modes for the QOpenGLDebugLogger; Asynchronous, and Synchronous. You can imagine what these modes entail.

Normally, you will want to work in Asynchronous mode. Synchronous mode will simply allow you to work with a valid callstack which contains the point where the code breaks, whereas Asynchronous makes no such promise. (Because the log is not processed immediately when the error occurs). So the idea is if we start getting notices that we need to directly debug – put it into Synchronous mode! Otherwise, Asynchronous is just fine.


Debug Logging

This is going to be a short tutorial. In Qt5.4 most of the heavy lifting is done for us, we only need to use the class abstraction that wraps the functionality of OpenGL’s glDebugMessageCallback. It’s nice not to have to think about this kind of setup. :)

1. Add QOpenGLDebugLogger to our Window header.

First part is pretty straight-forward, we have to forward declare QOpenGLDebugMessage and QOpenGLDebugLogger. We will include them in the source.

Then, we need to create a slot to handle debug messages, and a pointer to a QOpenGLDebugLogger.

That’s all for the header, pretty simple.

2. Add the implementation to our Window source.

The source is a little trickier in Qt5.4 – because of an API design flaw. When working with QopenGL* classes, such as Widgets or Windows, the classes have to go through and destroy their children. The problem is that QObject handles this destruction, and QOpenGLDebugLogger – when active – requires a valid OpenGL context to be in the current thread when destructing. This can cause some problems because the destructor for QOpenGLWindow and QOpenGLWidget don’t call makeCurrent(),  thus standard usage of QOpenGLDebugLogger will cause crashes to occur. Luckily we have resolved to call makeCurrent() in our own destructor, so for us this is not a problem for us. (Just be aware of it in case you experience similar issues.)

The next thing we need to do is initialize our m_debugLogger variable to NULL. We’re going to do this via Q_NULLPTR.

And then in initializeGL, we’re going to initialize the logger.

And finally, we need to print our messages. If you want, you can just print our debug messages directly – but I opted to prettify it a little bit.

QObject will take care of cleaning up the memory and freeing our debug logger for us.

Oh, and as always – don’t forget to include our QOpenGLDebugLogger and QOpenGLDebugMessage classes!

3. Create a Debug Context

One final change needs to happen before the context is created. We have to express to OpenGL that we want a debug context. We can do this through QSurfaceFormat::setOption(), or QSurfaceFormat::setOptions().

Now if we run it (depending on our GPU manufacturer) we will receive some information based on what we’re doing with OpenGL.

Note that this is different than catching errors like when we use the OpenGL API improperly and can’t say – compile a shader. These messages take place of checking glGetError after every single OpenGL call. It saves us a lot of time.

Summary

This is what we learned about in this tutorial.

  • We learned that OpenGL provides a mechanism for reporting errors via a callback asynchronously.
  • Since glDebugMessageCallback isn’t core until 4.3, Qt provided a class for wrapping it which includes extensions.
  • Using the QOpenGLDebugLogger, we subscribe to GPU messages that tell us important information.
  • We can request debug information by creating a Debug Context with OpenGL.

View Code on GitHub

Cheers!

Leave a comment

Your email address will not be published. Required fields are marked *