Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qopengldebug.cpp
Go to the documentation of this file.
1// Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtCore/private/qobject_p.h>
5#include <QtCore/qglobal.h>
6#include <QtCore/qvarlengtharray.h>
7#include <QtGui/qopengl.h>
8#include <QtGui/qopenglfunctions.h>
9#include <QtGui/qoffscreensurface.h>
10
11#include "qopengldebug.h"
12
13QT_BEGIN_NAMESPACE
14
15QT_IMPL_METATYPE_EXTERN(QOpenGLDebugMessage)
16
17/*!
18 \class QOpenGLDebugMessage
19 \brief The QOpenGLDebugMessage class wraps an OpenGL debug message.
20 \inmodule QtOpenGL
21 \reentrant
22 \since 5.1
23 \ingroup shared
24 \ingroup painting-3D
25
26 Debug messages are usually created by the OpenGL server and then read by
27 OpenGL clients (either from the OpenGL internal debug log, or logged in real-time).
28 A debug message has a textual representation, a vendor-specific numeric id,
29 a source, a type and a severity.
30
31 It's also possible for applications or third-party libraries and toolkits
32 to create and insert messages in the debug log. In order to do so, you can use
33 the createApplicationMessage() or the createThirdPartyMessage() static functions.
34
35 \sa QOpenGLDebugLogger
36*/
37
38/*!
39 \class QOpenGLDebugLogger
40 \brief The QOpenGLDebugLogger enables logging of OpenGL debugging messages.
41 \inmodule QtOpenGL
42 \since 5.1
43 \ingroup painting-3D
44
45 \section1 Introduction
46
47 OpenGL programming can be very error prone. Most of the time, a single
48 failing call to OpenGL can cause an entire portion of an application to
49 stop working, with nothing being drawn on the screen.
50
51 The only way to be sure that no errors are being returned from the OpenGL
52 implementation is checking with \c{glGetError} after each and every API
53 call. Moreover, OpenGL errors stack up, therefore glGetError should always
54 be used in a loop like this:
55
56 \snippet code/src_gui_opengl_qopengldebug.cpp 0
57
58 If you try to clear the error stack, make sure not just keep going until
59 GL_NO_ERROR is returned but also break on GL_CONTEXT_LOST as that error
60 value will keep repeating.
61
62 There are also many other information we are interested in (as application
63 developers), for instance performance issues, or warnings about using
64 deprecated APIs. Those kind of messages are not reported through the
65 ordinary OpenGL error reporting mechanisms.
66
67 QOpenGLDebugLogger aims at addressing these issues by providing access to
68 the \e{OpenGL debug log}. If your OpenGL implementation supports it (by
69 exposing the \c{GL_KHR_debug} extension), messages from the OpenGL server
70 will be either logged in an internal OpenGL log, or passed in "real-time"
71 to listeners as they're generated from OpenGL.
72
73 QOpenGLDebugLogger supports both these modes of operation. Refer to the
74 following sections to find out the differences between them.
75
76 \section1 Creating an OpenGL Debug Context
77
78 For efficiency reasons, OpenGL implementations are allowed not to create
79 any debug output at all, unless the OpenGL context is a debug context. In order
80 to create a debug context from Qt, you must set the QSurfaceFormat::DebugContext
81 format option on the QSurfaceFormat used to create the QOpenGLContext object:
82
83 \snippet code/src_gui_opengl_qopengldebug.cpp 1
84
85 Note that requesting a 3.2 OpenGL Core Profile is just for the example's
86 purposes; this class is not tied to any specific OpenGL or OpenGL ES
87 version, as it relies on the availability of the \c{GL_KHR_debug} extension
88 (see below).
89
90 \section1 Creating and Initializing a QOpenGLDebugLogger
91
92 QOpenGLDebugLogger is a simple QObject-derived class. Just like all QObject
93 subclasses, you create an instance (and optionally specify a parent
94 object), and like the other OpenGL functions in Qt you \e{must} initialize
95 it before usage by calling initialize() whilst there is a current OpenGL context:
96
97 \snippet code/src_gui_opengl_qopengldebug.cpp 2
98
99 Note that the \c{GL_KHR_debug} extension \e{must} be available in the context
100 in order to access the messages logged by OpenGL. You can check the
101 presence of this extension by calling:
102
103 \snippet code/src_gui_opengl_qopengldebug.cpp 3
104
105 where \c{ctx} is a valid QOpenGLContext. If the extension is not available,
106 initialize() will return false.
107
108 \section1 Reading the Internal OpenGL Debug Log
109
110 OpenGL implementations keep an internal log of debug messages. Messages
111 stored in this log can be retrieved by using the loggedMessages() function:
112
113 \snippet code/src_gui_opengl_qopengldebug.cpp 4
114
115 The internal log has a limited size; when it fills up, older messages will
116 get discarded to make room for the new incoming messages. When you call
117 loggedMessages(), the internal log will be emptied as well.
118
119 If you want to be sure not to lose any debug message, you must use real-time
120 logging instead of calling this function. However, debug messages might
121 still be generated in the timespan between context creation and activation
122 of real-time logging (or, in general, when the real-time logging is disabled).
123
124 \section1 Real-time logging of messages
125
126 It is also possible to receive a stream of debug messages from the OpenGL
127 server \e{as they are generated} by the implementation. In order to do so,
128 you need to connect a suitable slot to the messageLogged() signal, and
129 start logging by calling startLogging():
130
131 \snippet code/src_gui_opengl_qopengldebug.cpp 5
132
133 Similarly, logging can be disabled at any time by calling the stopLogging()
134 function.
135
136 Real-time logging can be either asynchronous or synchronous, depending on
137 the parameter passed to startLogging(). When logging in asynchronous mode
138 (the default, as it has a very small overhead), the OpenGL implementation
139 can generate messages at any time, and/or in an order which is different from the
140 order of the OpenGL commands which caused those messages to be logged.
141 The messages could also be generated from a thread that it's different from
142 the thread the context is currently bound to. This is because OpenGL
143 implementations are usually highly threaded and asynchronous, and therefore
144 no warranties are made about the relative order and the timings of the
145 debug messages.
146
147 On the other hand, logging in synchronous mode has a high overhead, but
148 the OpenGL implementation guarantees that all the messages caused by a
149 certain command are received in order, before the command returns,
150 and from the same thread the OpenGL context is bound to.
151
152 This means that when logging in synchronous mode you will be able to run
153 your OpenGL application in a debugger, put a breakpoint on a slot connected
154 to the messageLogged() signal, and see in the backtrace the exact call
155 that caused the logged message. This can be extremely useful to debug
156 an OpenGL problem. Note that if OpenGL rendering is happening in another
157 thread, you must force the signal/slot connection type to Qt::DirectConnection
158 in order to be able to see the actual backtrace.
159
160 Refer to the LoggingMode enum documentation for more information about
161 logging modes.
162
163 \note When real-time logging is enabled, debug messages will \e{not} be
164 inserted in the internal OpenGL debug log any more; messages already
165 present in the internal log will not be deleted, nor they will be emitted
166 through the messageLogged() signal. Since some messages might be generated
167 before real-time logging is started (and therefore be kept in the internal
168 OpenGL log), it is important to always check if it contains any message
169 after calling startLogging().
170
171 \section1 Inserting Messages in the Debug Log
172
173 It is possible for applications and libraries to insert custom messages in
174 the debug log, for instance for marking a group of related OpenGL commands
175 and therefore being then able to identify eventual messages coming from them.
176
177 In order to do so, you can create a QOpenGLDebugMessage object by calling
178 \l{QOpenGLDebugMessage::}{createApplicationMessage()} or
179 \l{QOpenGLDebugMessage::}{createThirdPartyMessage()}, and then inserting it
180 into the log by calling logMessage():
181
182 \snippet code/src_gui_opengl_qopengldebug.cpp 6
183
184 Note that OpenGL implementations have a vendor-specific limit to the length
185 of the messages that can be inserted in the debug log. You can retrieve
186 this length by calling the maximumMessageLength() method; messages longer
187 than the limit will automatically get truncated.
188
189 \section1 Controlling the Debug Output
190
191 QOpenGLDebugMessage is also able to apply filters to the debug messages, and
192 therefore limit the amount of messages logged. You can enable or disable
193 logging of messages by calling enableMessages() and disableMessages()
194 respectively. By default, all messages are logged.
195
196 It is possible to enable or disable messages by selecting them by:
197
198 \list
199 \li source, type and severity (and including all ids in the selection);
200 \li id, source and type (and including all severities in the selection).
201 \endlist
202
203 Note that the "enabled" status for a given message is a property of the
204 (id, source, type, severity) tuple; the message attributes \e{do not} form
205 a hierarchy of any kind. You should be careful about the order of the calls
206 to enableMessages() and disableMessages(), as it will change which
207 messages will are enabled / disabled.
208
209 It's not possible to filter by the message text itself; applications
210 have to do that on their own (in slots connected to the messageLogged()
211 signal, or after fetching the messages in the internal debug log
212 through loggedMessages()).
213
214 In order to simplify the management of the enabled / disabled statuses,
215 QOpenGLDebugMessage also supports the concept of \c{debug groups}. A debug
216 group contains the group of enabled / disabled configurations of debug
217 messages. Moreover, debug groups are organized in a stack: it is possible
218 to push and pop groups by calling pushGroup() and popGroup() respectively.
219 (When an OpenGL context is created, there is already a group in the stack).
220
221 The enableMessages() and disableMessages() functions will modify the
222 configuration in the current debug group, that is, the one at the top of
223 the debug groups stack.
224
225 When a new group is pushed onto the debug groups stack, it will inherit
226 the configuration of the group that was previously on the top of the stack.
227 Vice versa, popping a debug group will restore the configuration of
228 the debug group that becomes the new top.
229
230 Pushing (respectively popping) debug groups will also automatically generate
231 a debug message of type QOpenGLDebugMessage::GroupPushType (respectively
232 \l{QOpenGLDebugMessage::}{GroupPopType}).
233
234 \sa QOpenGLDebugMessage
235*/
236
237/*!
238 \enum QOpenGLDebugMessage::Source
239
240 The Source enum defines the source of the debug message.
241
242 \value InvalidSource
243 The source of the message is invalid; this is the source of a
244 default-constructed QOpenGLDebugMessage object.
245
246 \value APISource
247 The message was generated in response to OpenGL API calls.
248
249 \value WindowSystemSource
250 The message was generated by the window system.
251
252 \value ShaderCompilerSource
253 The message was generated by the shader compiler.
254
255 \value ThirdPartySource
256 The message was generated by a third party, for instance an OpenGL
257 framework a or debugging toolkit.
258
259 \value ApplicationSource
260 The message was generated by the application itself.
261
262 \value OtherSource
263 The message was generated by a source not included in this
264 enumeration.
265
266 \omitvalue LastSource
267
268 \value AnySource
269 This value corresponds to a mask of all possible message sources.
270*/
271
272/*!
273 \enum QOpenGLDebugMessage::Type
274
275 The Type enum defines the type of the debug message.
276
277 \value InvalidType
278 The type of the message is invalid; this is the type of a
279 default-constructed QOpenGLDebugMessage object.
280
281 \value ErrorType
282 The message represents an error.
283
284 \value DeprecatedBehaviorType
285 The message represents an usage of deprecated behavior.
286
287 \value UndefinedBehaviorType
288 The message represents an usage of undefined behavior.
289
290 \value PortabilityType
291 The message represents an usage of vendor-specific behavior,
292 that might pose portability concerns.
293
294 \value PerformanceType
295 The message represents a performance issue.
296
297 \value OtherType
298 The message represents a type not included in this
299 enumeration.
300
301 \value MarkerType
302 The message represents a marker in the debug log.
303
304 \value GroupPushType
305 The message represents a debug group push operation.
306
307 \value GroupPopType
308 The message represents a debug group pop operation.
309
310 \omitvalue LastType
311
312 \value AnyType
313 This value corresponds to a mask of all possible message types.
314*/
315
316/*!
317 \enum QOpenGLDebugMessage::Severity
318
319 The Severity enum defines the severity of the debug message.
320
321 \value InvalidSeverity
322 The severity of the message is invalid; this is the severity of a
323 default-constructed QOpenGLDebugMessage object.
324
325 \value HighSeverity
326 The message has a high severity.
327
328 \value MediumSeverity
329 The message has a medium severity.
330
331 \value LowSeverity
332 The message has a low severity.
333
334 \value NotificationSeverity
335 The message is a notification.
336
337 \omitvalue LastSeverity
338
339 \value AnySeverity
340 This value corresponds to a mask of all possible message severities.
341*/
342
343/*!
344 \property QOpenGLDebugLogger::loggingMode
345
346 \brief the logging mode passed to startLogging().
347
348 Note that logging must have been started or the value of this property
349 will be meaningless.
350
351 \sa startLogging(), isLogging()
352*/
353/*!
354 \enum QOpenGLDebugLogger::LoggingMode
355
356 The LoggingMode enum defines the logging mode of the logger object.
357
358 \value AsynchronousLogging
359 Messages from the OpenGL server are logged asynchronously. This means
360 that messages can be logged some time after the corresponding OpenGL
361 actions that caused them, and even be received in an out-of-order
362 fashion, depending on the OpenGL implementation. This mode has a very low
363 performance penalty, as OpenGL implementations are heavily threaded
364 and asynchronous by nature.
365
366 \value SynchronousLogging
367 Messages from the OpenGL server are logged synchronously and
368 sequentially. This has a severe performance hit, as OpenGL
369 implementations are very asynchronous by nature; but it's very useful
370 to debug OpenGL problems, as OpenGL guarantees that the messages
371 generated by a OpenGL command will be logged before the corresponding
372 command execution has returned. Therefore, you can install a breakpoint
373 on the messageLogged() signal and see in the backtrace which OpenGL
374 command caused it; the only caveat is that if you are using OpenGL from
375 multiple threads you may need to force direct connection when
376 connecting to the messageLogged() signal.
377*/
378
379// When using OpenGL ES 2.0, all the necessary GL_KHR_debug constants are
380// provided in qopengles2ext.h. Unfortunately, newer versions of that file
381// suffix everything with _KHR which causes extra headache when the goal is
382// to have a single piece of code that builds in all our target
383// environments. Therefore, try to detect this and use our custom defines
384// instead, which we anyway need for OS X.
385
386#if defined(GL_KHR_debug) && defined(GL_DEBUG_SOURCE_API_KHR)
387#define USE_MANUAL_DEFS
388#endif
389
390// Under OSX (at least up to 10.8) we cannot include our copy of glext.h,
391// but we use the system-wide one, which unfortunately lacks all the needed
392// defines/typedefs. In order to make the code compile, we just add here
393// the GL_KHR_debug defines.
394
395#ifndef GL_KHR_debug
396#define GL_KHR_debug 1
397#define USE_MANUAL_DEFS
398#endif
399
400#ifdef USE_MANUAL_DEFS
401
402#ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
403#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
404#endif
405#ifndef GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH
406#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243
407#endif
408#ifndef GL_DEBUG_CALLBACK_FUNCTION
409#define GL_DEBUG_CALLBACK_FUNCTION 0x8244
410#endif
411#ifndef GL_DEBUG_CALLBACK_USER_PARAM
412#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245
413#endif
414#ifndef GL_DEBUG_SOURCE_API
415#define GL_DEBUG_SOURCE_API 0x8246
416#endif
417#ifndef GL_DEBUG_SOURCE_WINDOW_SYSTEM
418#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247
419#endif
420#ifndef GL_DEBUG_SOURCE_SHADER_COMPILER
421#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248
422#endif
423#ifndef GL_DEBUG_SOURCE_THIRD_PARTY
424#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249
425#endif
426#ifndef GL_DEBUG_SOURCE_APPLICATION
427#define GL_DEBUG_SOURCE_APPLICATION 0x824A
428#endif
429#ifndef GL_DEBUG_SOURCE_OTHER
430#define GL_DEBUG_SOURCE_OTHER 0x824B
431#endif
432#ifndef GL_DEBUG_TYPE_ERROR
433#define GL_DEBUG_TYPE_ERROR 0x824C
434#endif
435#ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
436#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
437#endif
438#ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
439#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
440#endif
441#ifndef GL_DEBUG_TYPE_PORTABILITY
442#define GL_DEBUG_TYPE_PORTABILITY 0x824F
443#endif
444#ifndef GL_DEBUG_TYPE_PERFORMANCE
445#define GL_DEBUG_TYPE_PERFORMANCE 0x8250
446#endif
447#ifndef GL_DEBUG_TYPE_OTHER
448#define GL_DEBUG_TYPE_OTHER 0x8251
449#endif
450#ifndef GL_DEBUG_TYPE_MARKER
451#define GL_DEBUG_TYPE_MARKER 0x8268
452#endif
453#ifndef GL_DEBUG_TYPE_PUSH_GROUP
454#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269
455#endif
456#ifndef GL_DEBUG_TYPE_POP_GROUP
457#define GL_DEBUG_TYPE_POP_GROUP 0x826A
458#endif
459#ifndef GL_DEBUG_SEVERITY_NOTIFICATION
460#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
461#endif
462#ifndef GL_MAX_DEBUG_GROUP_STACK_DEPTH
463#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C
464#endif
465#ifndef GL_DEBUG_GROUP_STACK_DEPTH
466#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D
467#endif
468#ifndef GL_BUFFER
469#define GL_BUFFER 0x82E0
470#endif
471#ifndef GL_SHADER
472#define GL_SHADER 0x82E1
473#endif
474#ifndef GL_PROGRAM
475#define GL_PROGRAM 0x82E2
476#endif
477#ifndef GL_QUERY
478#define GL_QUERY 0x82E3
479#endif
480#ifndef GL_PROGRAM_PIPELINE
481#define GL_PROGRAM_PIPELINE 0x82E4
482#endif
483#ifndef GL_SAMPLER
484#define GL_SAMPLER 0x82E6
485#endif
486#ifndef GL_DISPLAY_LIST
487#define GL_DISPLAY_LIST 0x82E7
488#endif
489#ifndef GL_MAX_LABEL_LENGTH
490#define GL_MAX_LABEL_LENGTH 0x82E8
491#endif
492#ifndef GL_MAX_DEBUG_MESSAGE_LENGTH
493#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143
494#endif
495#ifndef GL_MAX_DEBUG_LOGGED_MESSAGES
496#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144
497#endif
498#ifndef GL_DEBUG_LOGGED_MESSAGES
499#define GL_DEBUG_LOGGED_MESSAGES 0x9145
500#endif
501#ifndef GL_DEBUG_SEVERITY_HIGH
502#define GL_DEBUG_SEVERITY_HIGH 0x9146
503#endif
504#ifndef GL_DEBUG_SEVERITY_MEDIUM
505#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
506#endif
507#ifndef GL_DEBUG_SEVERITY_LOW
508#define GL_DEBUG_SEVERITY_LOW 0x9148
509#endif
510#ifndef GL_DEBUG_OUTPUT
511#define GL_DEBUG_OUTPUT 0x92E0
512#endif
513#ifndef GL_CONTEXT_FLAG_DEBUG_BIT
514#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
515#endif
516#ifndef GL_STACK_OVERFLOW
517#define GL_STACK_OVERFLOW 0x0503
518#endif
519#ifndef GL_STACK_UNDERFLOW
520#define GL_STACK_UNDERFLOW 0x0504
521#endif
522
523typedef void (QOPENGLF_APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const GLvoid *userParam);
524
525#endif /* USE_MANUAL_DEFS */
526
527
528/*!
529 \internal
530*/
532{
533 switch (source) {
535 return QOpenGLDebugMessage::APISource;
537 return QOpenGLDebugMessage::WindowSystemSource;
539 return QOpenGLDebugMessage::ShaderCompilerSource;
541 return QOpenGLDebugMessage::ThirdPartySource;
543 return QOpenGLDebugMessage::ApplicationSource;
545 return QOpenGLDebugMessage::OtherSource;
546 }
547
548 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source from GL");
549 return QOpenGLDebugMessage::OtherSource;
550}
551
552/*!
553 \internal
554*/
555static GLenum qt_messageSourceToGL(QOpenGLDebugMessage::Source source)
556{
557 switch (source) {
558 case QOpenGLDebugMessage::InvalidSource:
559 break;
560 case QOpenGLDebugMessage::APISource:
561 return GL_DEBUG_SOURCE_API;
562 case QOpenGLDebugMessage::WindowSystemSource:
564 case QOpenGLDebugMessage::ShaderCompilerSource:
566 case QOpenGLDebugMessage::ThirdPartySource:
568 case QOpenGLDebugMessage::ApplicationSource:
570 case QOpenGLDebugMessage::OtherSource:
572 case QOpenGLDebugMessage::AnySource:
573 break;
574 }
575
576 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message source");
578}
579
580/*!
581 \internal
582*/
583static QString qt_messageSourceToString(QOpenGLDebugMessage::Source source)
584{
585 switch (source) {
586 case QOpenGLDebugMessage::InvalidSource:
587 return QStringLiteral("InvalidSource");
588 case QOpenGLDebugMessage::APISource:
589 return QStringLiteral("APISource");
590 case QOpenGLDebugMessage::WindowSystemSource:
591 return QStringLiteral("WindowSystemSource");
592 case QOpenGLDebugMessage::ShaderCompilerSource:
593 return QStringLiteral("ShaderCompilerSource");
594 case QOpenGLDebugMessage::ThirdPartySource:
595 return QStringLiteral("ThirdPartySource");
596 case QOpenGLDebugMessage::ApplicationSource:
597 return QStringLiteral("ApplicationSource");
598 case QOpenGLDebugMessage::OtherSource:
599 return QStringLiteral("OtherSource");
600 case QOpenGLDebugMessage::AnySource:
601 return QStringLiteral("AnySource");
602 }
603
604 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source");
605 return QString();
606}
607
608/*!
609 \internal
610*/
612{
613 switch (type) {
615 return QOpenGLDebugMessage::ErrorType;
617 return QOpenGLDebugMessage::DeprecatedBehaviorType;
619 return QOpenGLDebugMessage::UndefinedBehaviorType;
621 return QOpenGLDebugMessage::PortabilityType;
623 return QOpenGLDebugMessage::PerformanceType;
625 return QOpenGLDebugMessage::OtherType;
627 return QOpenGLDebugMessage::MarkerType;
629 return QOpenGLDebugMessage::GroupPushType;
631 return QOpenGLDebugMessage::GroupPopType;
632 }
633
634 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type from GL");
635 return QOpenGLDebugMessage::OtherType;
636}
637
638/*!
639 \internal
640*/
641static GLenum qt_messageTypeToGL(QOpenGLDebugMessage::Type type)
642{
643 switch (type) {
644 case QOpenGLDebugMessage::InvalidType:
645 break;
646 case QOpenGLDebugMessage::ErrorType:
647 return GL_DEBUG_TYPE_ERROR;
648 case QOpenGLDebugMessage::DeprecatedBehaviorType:
650 case QOpenGLDebugMessage::UndefinedBehaviorType:
652 case QOpenGLDebugMessage::PortabilityType:
654 case QOpenGLDebugMessage::PerformanceType:
656 case QOpenGLDebugMessage::OtherType:
657 return GL_DEBUG_TYPE_OTHER;
658 case QOpenGLDebugMessage::MarkerType:
660 case QOpenGLDebugMessage::GroupPushType:
662 case QOpenGLDebugMessage::GroupPopType:
664 case QOpenGLDebugMessage::AnyType:
665 break;
666 }
667
668 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type");
669 return GL_DEBUG_TYPE_OTHER;
670}
671
672/*!
673 \internal
674*/
675static QString qt_messageTypeToString(QOpenGLDebugMessage::Type type)
676{
677 switch (type) {
678 case QOpenGLDebugMessage::InvalidType:
679 return QStringLiteral("InvalidType");
680 case QOpenGLDebugMessage::ErrorType:
681 return QStringLiteral("ErrorType");
682 case QOpenGLDebugMessage::DeprecatedBehaviorType:
683 return QStringLiteral("DeprecatedBehaviorType");
684 case QOpenGLDebugMessage::UndefinedBehaviorType:
685 return QStringLiteral("UndefinedBehaviorType");
686 case QOpenGLDebugMessage::PortabilityType:
687 return QStringLiteral("PortabilityType");
688 case QOpenGLDebugMessage::PerformanceType:
689 return QStringLiteral("PerformanceType");
690 case QOpenGLDebugMessage::OtherType:
691 return QStringLiteral("OtherType");
692 case QOpenGLDebugMessage::MarkerType:
693 return QStringLiteral("MarkerType");
694 case QOpenGLDebugMessage::GroupPushType:
695 return QStringLiteral("GroupPushType");
696 case QOpenGLDebugMessage::GroupPopType:
697 return QStringLiteral("GroupPopType");
698 case QOpenGLDebugMessage::AnyType:
699 return QStringLiteral("AnyType");
700 }
701
702 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type");
703 return QString();
704}
705
706/*!
707 \internal
708*/
710{
711 switch (severity) {
713 return QOpenGLDebugMessage::HighSeverity;
715 return QOpenGLDebugMessage::MediumSeverity;
717 return QOpenGLDebugMessage::LowSeverity;
719 return QOpenGLDebugMessage::NotificationSeverity;
720 }
721
722 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity from GL");
723 return QOpenGLDebugMessage::NotificationSeverity;
724}
725
726/*!
727 \internal
728*/
729static GLenum qt_messageSeverityToGL(QOpenGLDebugMessage::Severity severity)
730{
731 switch (severity) {
732 case QOpenGLDebugMessage::InvalidSeverity:
733 break;
734 case QOpenGLDebugMessage::HighSeverity:
736 case QOpenGLDebugMessage::MediumSeverity:
738 case QOpenGLDebugMessage::LowSeverity:
740 case QOpenGLDebugMessage::NotificationSeverity:
742 case QOpenGLDebugMessage::AnySeverity:
743 break;
744 }
745
746 Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message severity");
748}
749
750/*!
751 \internal
752*/
753static QString qt_messageSeverityToString(QOpenGLDebugMessage::Severity severity)
754{
755 switch (severity) {
756 case QOpenGLDebugMessage::InvalidSeverity:
757 return QStringLiteral("InvalidSeverity");
758 case QOpenGLDebugMessage::HighSeverity:
759 return QStringLiteral("HighSeverity");
760 case QOpenGLDebugMessage::MediumSeverity:
761 return QStringLiteral("MediumSeverity");
762 case QOpenGLDebugMessage::LowSeverity:
763 return QStringLiteral("LowSeverity");
764 case QOpenGLDebugMessage::NotificationSeverity:
765 return QStringLiteral("NotificationSeverity");
766 case QOpenGLDebugMessage::AnySeverity:
767 return QStringLiteral("AnySeverity");
768 }
769
770 Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity");
771 return QString();
772}
773
785
786/*!
787 \internal
788*/
797
798
799/*!
800 Constructs a debug message with an empty message string, id set to 0,
801 source set to InvalidSource, type set to InvalidType, and severity set to
802 InvalidSeverity.
803
804 \note This constructor should not be used to create a debug message;
805 instead, use the createApplicationMessage() or the createThirdPartyMessage()
806 static functions.
807
808 \sa createApplicationMessage(), createThirdPartyMessage()
809*/
810QOpenGLDebugMessage::QOpenGLDebugMessage()
811 : d(new QOpenGLDebugMessagePrivate)
812{
813}
814
815/*!
816 Constructs a debug message as a copy of \a debugMessage.
817
818 \sa operator=()
819*/
820QOpenGLDebugMessage::QOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage)
821 : d(debugMessage.d)
822{
823}
824
825/*!
826 Destroys this debug message.
827*/
828QOpenGLDebugMessage::~QOpenGLDebugMessage()
829{
830}
831
832/*!
833 Assigns the message \a debugMessage to this object, and returns a reference
834 to the copy.
835*/
836QOpenGLDebugMessage &QOpenGLDebugMessage::operator=(const QOpenGLDebugMessage &debugMessage)
837{
838 d = debugMessage.d;
839 return *this;
840}
841
842/*!
843 \fn QOpenGLDebugMessage &QOpenGLDebugMessage::operator=(QOpenGLDebugMessage &&debugMessage)
844
845 Move-assigns \a debugMessage to this object.
846*/
847
848/*!
849 \fn void QOpenGLDebugMessage::swap(QOpenGLDebugMessage &other)
850 \memberswap{message]
851*/
852
853/*!
854 Returns the source of the debug message.
855*/
856QOpenGLDebugMessage::Source QOpenGLDebugMessage::source() const
857{
858 return d->source;
859}
860
861/*!
862 Returns the type of the debug message.
863*/
864QOpenGLDebugMessage::Type QOpenGLDebugMessage::type() const
865{
866 return d->type;
867}
868
869/*!
870 Returns the severity of the debug message.
871*/
872QOpenGLDebugMessage::Severity QOpenGLDebugMessage::severity() const
873{
874 return d->severity;
875}
876
877/*!
878 Returns the id of the debug message. Ids are generally vendor-specific.
879*/
880GLuint QOpenGLDebugMessage::id() const
881{
882 return d->id;
883}
884
885/*!
886 Returns the textual message contained by this debug message.
887*/
888QString QOpenGLDebugMessage::message() const
889{
890 return d->message;
891}
892
893/*!
894 Constructs and returns a debug message with \a text as its text, \a id
895 as id, \a severity as severity, and \a type as type. The message source
896 will be set to ApplicationSource.
897
898 \sa QOpenGLDebugLogger::logMessage(), createThirdPartyMessage()
899*/
900QOpenGLDebugMessage QOpenGLDebugMessage::createApplicationMessage(const QString &text,
901 GLuint id,
902 QOpenGLDebugMessage::Severity severity,
903 QOpenGLDebugMessage::Type type)
904{
905 QOpenGLDebugMessage m;
906 m.d->message = text;
907 m.d->id = id;
908 m.d->severity = severity;
909 m.d->type = type;
910 m.d->source = ApplicationSource;
911 return m;
912}
913
914/*!
915 Constructs and returns a debug message with \a text as its text, \a id
916 as id, \a severity as severity, and \a type as type. The message source
917 will be set to ThirdPartySource.
918
919 \sa QOpenGLDebugLogger::logMessage(), createApplicationMessage()
920*/
921QOpenGLDebugMessage QOpenGLDebugMessage::createThirdPartyMessage(const QString &text,
922 GLuint id,
923 QOpenGLDebugMessage::Severity severity,
924 QOpenGLDebugMessage::Type type)
925{
926 QOpenGLDebugMessage m;
927 m.d->message = text;
928 m.d->id = id;
929 m.d->severity = severity;
930 m.d->type = type;
931 m.d->source = ThirdPartySource;
932 return m;
933}
934
935/*!
936 Returns \c true if this debug message is equal to \a debugMessage, or false
937 otherwise. Two debugging messages are equal if they have the same textual
938 message, the same id, the same source, the same type and the same severity.
939
940 \sa operator!=()
941*/
942bool QOpenGLDebugMessage::operator==(const QOpenGLDebugMessage &debugMessage) const
943{
944 return (d == debugMessage.d)
945 || (d->id == debugMessage.d->id
946 && d->source == debugMessage.d->source
947 && d->type == debugMessage.d->type
948 && d->severity == debugMessage.d->severity
949 && d->message == debugMessage.d->message);
950}
951
952/*!
953 \fn bool QOpenGLDebugMessage::operator!=(const QOpenGLDebugMessage &debugMessage) const
954
955 Returns \c true if this message is different from \a debugMessage, or false
956 otherwise.
957
958 \sa operator==()
959*/
960
961#ifndef QT_NO_DEBUG_STREAM
962/*!
963 \relates QOpenGLDebugMessage
964
965 Writes the source \a source into the debug object \a debug for debugging
966 purposes.
967*/
968QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Source source)
969{
970 QDebugStateSaver saver(debug);
971 debug.nospace() << "QOpenGLDebugMessage::Source("
972 << qt_messageSourceToString(source)
973 << ')';
974 return debug;
975}
976
977/*!
978 \relates QOpenGLDebugMessage
979
980 Writes the type \a type into the debug object \a debug for debugging
981 purposes.
982*/
983QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Type type)
984{
985 QDebugStateSaver saver(debug);
986 debug.nospace() << "QOpenGLDebugMessage::Type("
987 << qt_messageTypeToString(type)
988 << ')';
989 return debug;
990}
991
992/*!
993 \relates QOpenGLDebugMessage
994
995 Writes the severity \a severity into the debug object \a debug for debugging
996 purposes.
997*/
998QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Severity severity)
999{
1000 QDebugStateSaver saver(debug);
1001 debug.nospace() << "QOpenGLDebugMessage::Severity("
1002 << qt_messageSeverityToString(severity)
1003 << ')';
1004 return debug;
1005}
1006
1007/*!
1008 \relates QOpenGLDebugMessage
1009
1010 Writes the message \a message into the debug object \a debug for debugging
1011 purposes.
1012*/
1013QDebug operator<<(QDebug debug, const QOpenGLDebugMessage &message)
1014{
1015 QDebugStateSaver saver(debug);
1016 debug.nospace() << "QOpenGLDebugMessage("
1017 << qt_messageSourceToString(message.source()) << ", "
1018 << message.id() << ", "
1019 << message.message() << ", "
1020 << qt_messageSeverityToString(message.severity()) << ", "
1021 << qt_messageTypeToString(message.type()) << ')';
1022 return debug;
1023
1024}
1025#endif // QT_NO_DEBUG_STREAM
1026
1027typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageControl_t)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
1028typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageInsert_t)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf);
1029typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageCallback_t)(GLDEBUGPROC callback, const void *userParam);
1030typedef GLuint (QOPENGLF_APIENTRYP qt_glGetDebugMessageLog_t)(GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog);
1031typedef void (QOPENGLF_APIENTRYP qt_glPushDebugGroup_t)(GLenum source, GLuint id, GLsizei length, const GLchar *message);
1032typedef void (QOPENGLF_APIENTRYP qt_glPopDebugGroup_t)();
1033typedef void (QOPENGLF_APIENTRYP qt_glGetPointerv_t)(GLenum pname, GLvoid **params);
1034
1035class QOpenGLDebugLoggerPrivate : public QObjectPrivate
1036{
1037 Q_DECLARE_PUBLIC(QOpenGLDebugLogger)
1038public:
1039 QOpenGLDebugLoggerPrivate();
1040
1041 void handleMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *rawMessage);
1042 void controlDebugMessages(QOpenGLDebugMessage::Sources sources,
1043 QOpenGLDebugMessage::Types types,
1044 QOpenGLDebugMessage::Severities severities, const QList<GLuint> &ids,
1045 const QByteArray &callerName, bool enable);
1046 void _q_contextAboutToBeDestroyed();
1047
1048 qt_glDebugMessageControl_t glDebugMessageControl;
1049 qt_glDebugMessageInsert_t glDebugMessageInsert;
1050 qt_glDebugMessageCallback_t glDebugMessageCallback;
1051 qt_glGetDebugMessageLog_t glGetDebugMessageLog;
1052 qt_glPushDebugGroup_t glPushDebugGroup;
1053 qt_glPopDebugGroup_t glPopDebugGroup;
1054 qt_glGetPointerv_t glGetPointerv;
1055
1056 GLDEBUGPROC oldDebugCallbackFunction;
1057 void *oldDebugCallbackParameter;
1058 QOpenGLContext *context;
1059 GLint maxMessageLength;
1060 QOpenGLDebugLogger::LoggingMode loggingMode;
1061 bool initialized : 1;
1062 bool isLogging : 1;
1063 bool debugWasEnabled : 1;
1064 bool syncDebugWasEnabled : 1;
1065};
1066
1067/*!
1068 \internal
1069*/
1070QOpenGLDebugLoggerPrivate::QOpenGLDebugLoggerPrivate()
1071 : glDebugMessageControl(nullptr),
1072 glDebugMessageInsert(nullptr),
1073 glDebugMessageCallback(nullptr),
1074 glGetDebugMessageLog(nullptr),
1075 glPushDebugGroup(nullptr),
1076 glPopDebugGroup(nullptr),
1077 oldDebugCallbackFunction(nullptr),
1078 context(nullptr),
1079 maxMessageLength(0),
1080 loggingMode(QOpenGLDebugLogger::AsynchronousLogging),
1081 initialized(false),
1082 isLogging(false),
1083 debugWasEnabled(false),
1084 syncDebugWasEnabled(false)
1085{
1086}
1087
1088/*!
1089 \internal
1090*/
1091void QOpenGLDebugLoggerPrivate::handleMessage(GLenum source,
1092 GLenum type,
1093 GLuint id,
1094 GLenum severity,
1095 GLsizei length,
1096 const GLchar *rawMessage)
1097{
1098 if (oldDebugCallbackFunction)
1099 oldDebugCallbackFunction(source, type, id, severity, length, rawMessage, oldDebugCallbackParameter);
1100
1101 QOpenGLDebugMessage message;
1102
1103 QOpenGLDebugMessagePrivate *messagePrivate = message.d.data();
1104 messagePrivate->source = qt_messageSourceFromGL(source);
1105 messagePrivate->type = qt_messageTypeFromGL(type);
1106 messagePrivate->id = id;
1107 messagePrivate->severity = qt_messageSeverityFromGL(severity);
1108 // not passing the length to fromUtf8, as some bugged OpenGL drivers
1109 // do not handle the length correctly. Just rely on the message to be NUL terminated.
1110 messagePrivate->message = QString::fromUtf8(rawMessage);
1111
1112 Q_Q(QOpenGLDebugLogger);
1113 emit q->messageLogged(message);
1114}
1115
1116/*!
1117 \internal
1118*/
1119void QOpenGLDebugLoggerPrivate::controlDebugMessages(QOpenGLDebugMessage::Sources sources,
1120 QOpenGLDebugMessage::Types types,
1121 QOpenGLDebugMessage::Severities severities,
1122 const QList<GLuint> &ids,
1123 const QByteArray &callerName, bool enable)
1124{
1125 if (!initialized) {
1126 qWarning("QOpenGLDebugLogger::%s(): object must be initialized before enabling/disabling messages", callerName.constData());
1127 return;
1128 }
1129 if (sources == QOpenGLDebugMessage::InvalidSource) {
1130 qWarning("QOpenGLDebugLogger::%s(): invalid source specified", callerName.constData());
1131 return;
1132 }
1133 if (types == QOpenGLDebugMessage::InvalidType) {
1134 qWarning("QOpenGLDebugLogger::%s(): invalid type specified", callerName.constData());
1135 return;
1136 }
1137 if (severities == QOpenGLDebugMessage::InvalidSeverity) {
1138 qWarning("QOpenGLDebugLogger::%s(): invalid severity specified", callerName.constData());
1139 return;
1140 }
1141
1142 QVarLengthArray<GLenum, 8> glSources;
1143 QVarLengthArray<GLenum, 8> glTypes;
1144 QVarLengthArray<GLenum, 8> glSeverities;
1145
1146 if (ids.size() > 0) {
1147 Q_ASSERT(severities == QOpenGLDebugMessage::AnySeverity);
1148
1149 // The GL_KHR_debug extension says:
1150 //
1151 // - If <count> is greater than zero, then <ids> is an array of <count>
1152 // message IDs for the specified combination of <source> and <type>. In
1153 // this case, if <source> or <type> is DONT_CARE, or <severity> is not
1154 // DONT_CARE, the error INVALID_OPERATION is generated. If <count> is
1155 // zero, the value if <ids> is ignored.
1156 //
1157 // This means we can't convert AnySource or AnyType into DONT_CARE, but we have to roll
1158 // them into individual sources/types.
1159
1160 if (sources == QOpenGLDebugMessage::AnySource) {
1161 sources = QOpenGLDebugMessage::InvalidSource;
1162 for (uint i = 1; i <= QOpenGLDebugMessage::LastSource; i = i << 1)
1163 sources |= QOpenGLDebugMessage::Source(i);
1164 }
1165
1166 if (types == QOpenGLDebugMessage::AnyType) {
1167 types = QOpenGLDebugMessage::InvalidType;
1168 for (uint i = 1; i <= QOpenGLDebugMessage::LastType; i = i << 1)
1169 types |= QOpenGLDebugMessage::Type(i);
1170 }
1171 }
1172
1173#define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target)
1174 if (source == QOpenGLDebugMessage::Any ## type) {
1175 target << GL_DONT_CARE;
1176 } else {
1177 for (uint i = 1; i <= QOpenGLDebugMessage::Last ## type; i = i << 1)
1178 if (source.testFlag(QOpenGLDebugMessage:: type (i)))
1179 target << qt_message ## type ## ToGL (QOpenGLDebugMessage:: type (i));
1180 }
1181
1182 CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Source, sources, glSources)
1184 CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Severity, severities, glSeverities)
1185#undef CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS
1186
1187 const GLsizei idCount = ids.size();
1188 // The GL_KHR_debug extension says that if idCount is 0, idPtr must be ignored.
1189 // Unfortunately, some bugged drivers do NOT ignore it, so pass NULL in case.
1190 const GLuint * const idPtr = idCount ? ids.constData() : nullptr;
1191
1192 for (GLenum source : glSources)
1193 for (GLenum type : glTypes)
1194 for (GLenum severity : glSeverities)
1195 glDebugMessageControl(source, type, severity, idCount, idPtr, GLboolean(enable));
1196}
1197
1198/*!
1199 \internal
1200*/
1201void QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed()
1202{
1203 Q_ASSERT(context);
1204
1205 // Re-make our context current somehow, otherwise stopLogging will fail.
1206
1207 // Save the current context and its surface in case we need to set them back
1208 QOpenGLContext *currentContext = QOpenGLContext::currentContext();
1209 QSurface *currentSurface = nullptr;
1210
1211 QScopedPointer<QOffscreenSurface> offscreenSurface;
1212
1213 if (context != currentContext) {
1214 // Make our old context current on a temporary surface
1215 if (currentContext)
1216 currentSurface = currentContext->surface();
1217
1218 offscreenSurface.reset(new QOffscreenSurface);
1219 offscreenSurface->setFormat(context->format());
1220 offscreenSurface->create();
1221 if (!context->makeCurrent(offscreenSurface.data()))
1222 qWarning("QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed(): could not make the owning GL context current for cleanup");
1223 }
1224
1225 Q_Q(QOpenGLDebugLogger);
1226 q->stopLogging();
1227
1228 if (offscreenSurface) {
1229 // We did change the current context: set it back
1230 if (currentContext)
1231 currentContext->makeCurrent(currentSurface);
1232 else
1233 context->doneCurrent();
1234 }
1235
1236 QObject::disconnect(context, SIGNAL(aboutToBeDestroyed()), q, SLOT(_q_contextAboutToBeDestroyed()));
1237 context = nullptr;
1238 initialized = false;
1239}
1240
1241extern "C" {
1242static void QOPENGLF_APIENTRY qt_opengl_debug_callback(GLenum source,
1243 GLenum type,
1244 GLuint id,
1245 GLenum severity,
1246 GLsizei length,
1247 const GLchar *rawMessage,
1248 const GLvoid *userParam)
1249{
1250 QOpenGLDebugLoggerPrivate *loggerPrivate = static_cast<QOpenGLDebugLoggerPrivate *>(const_cast<GLvoid *>(userParam));
1251 loggerPrivate->handleMessage(source, type, id, severity, length, rawMessage);
1252}
1253}
1254
1255/*!
1256 Constructs a new logger object with the given \a parent.
1257
1258 \note The object must be initialized before logging can happen.
1259
1260 \sa initialize()
1261*/
1262QOpenGLDebugLogger::QOpenGLDebugLogger(QObject *parent)
1263 : QObject(*new QOpenGLDebugLoggerPrivate, parent)
1264{
1265 // QOpenGLDebugMessage is going to be mostly used as an argument
1266 // of a cross thread connection, therefore let's ease the life for the users
1267 // and register the type for them.
1268 qRegisterMetaType<QOpenGLDebugMessage>();
1269}
1270
1271/*!
1272 Destroys the logger object.
1273*/
1274QOpenGLDebugLogger::~QOpenGLDebugLogger()
1275{
1276 stopLogging();
1277}
1278
1279/*!
1280 Initializes the object in the current OpenGL context. The context must
1281 support the \c{GL_KHR_debug} extension for the initialization to succeed.
1282 The object must be initialized before any logging can happen.
1283
1284 It is safe to call this function multiple times from the same context.
1285
1286 This function can also be used to change the context of a previously
1287 initialized object; note that in this case the object must not be logging
1288 when you call this function.
1289
1290 Returns \c true if the logger is successfully initialized; false otherwise.
1291
1292 \sa QOpenGLContext
1293*/
1294bool QOpenGLDebugLogger::initialize()
1295{
1296 QOpenGLContext *context = QOpenGLContext::currentContext();
1297 if (!context) {
1298 qWarning("QOpenGLDebugLogger::initialize(): no current OpenGL context found.");
1299 return false;
1300 }
1301
1302 Q_D(QOpenGLDebugLogger);
1303 if (d->context == context) {
1304 // context is non-NULL, d->context is non NULL only on successful initialization.
1305 Q_ASSERT(d->initialized);
1306 return true;
1307 }
1308
1309 if (d->isLogging) {
1310 qWarning("QOpenGLDebugLogger::initialize(): cannot initialize the object while logging. Please stop the logging first.");
1311 return false;
1312 }
1313
1314 if (d->context)
1315 disconnect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed()));
1316
1317 d->initialized = false;
1318 d->context = nullptr;
1319
1320 if (!context->hasExtension(QByteArrayLiteral("GL_KHR_debug")))
1321 return false;
1322
1323 d->context = context;
1324 connect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed()));
1325
1326#define GET_DEBUG_PROC_ADDRESS(procName)
1327 d->procName = reinterpret_cast< qt_ ## procName ## _t >(
1328 d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName))
1329 );
1330
1331 GET_DEBUG_PROC_ADDRESS(glDebugMessageControl);
1332 GET_DEBUG_PROC_ADDRESS(glDebugMessageInsert);
1333 GET_DEBUG_PROC_ADDRESS(glDebugMessageCallback);
1334 GET_DEBUG_PROC_ADDRESS(glGetDebugMessageLog);
1335 GET_DEBUG_PROC_ADDRESS(glPushDebugGroup);
1336 GET_DEBUG_PROC_ADDRESS(glPopDebugGroup);
1337 GET_DEBUG_PROC_ADDRESS(glGetPointerv)
1338
1339#undef GET_DEBUG_PROC_ADDRESS
1340
1341 QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, &d->maxMessageLength);
1342
1343#ifndef QT_NO_DEBUG
1344 if (!d->context->format().testOption(QSurfaceFormat::DebugContext)) {
1345 qWarning("QOpenGLDebugLogger::initialize(): the current context is not a debug context:\n"
1346 " this means that the GL may not generate any debug output at all.\n"
1347 " To avoid this warning, try creating the context with the\n"
1348 " QSurfaceFormat::DebugContext surface format option.");
1349 }
1350#endif // QT_NO_DEBUG
1351
1352 d->initialized = true;
1353 return true;
1354}
1355
1356/*!
1357 Returns \c true if this object is currently logging, false otherwise.
1358
1359 \sa startLogging()
1360*/
1361bool QOpenGLDebugLogger::isLogging() const
1362{
1363 Q_D(const QOpenGLDebugLogger);
1364 return d->isLogging;
1365}
1366
1367/*!
1368 Starts logging messages coming from the OpenGL server. When a new message
1369 is received, the signal messageLogged() is emitted, carrying the logged
1370 message as argument.
1371
1372 \a loggingMode specifies whether the logging must be asynchronous (the default)
1373 or synchronous.
1374
1375 QOpenGLDebugLogger will record the values of \c{GL_DEBUG_OUTPUT} and
1376 \c{GL_DEBUG_OUTPUT_SYNCHRONOUS} when logging is started, and set them back
1377 when logging is stopped. Moreover, any user-defined OpenGL debug callback
1378 installed when this function is invoked will be restored when logging is
1379 stopped; QOpenGLDebugLogger will ensure that the pre-existing callback will
1380 still be invoked when logging.
1381
1382 \note It's not possible to change the logging mode without stopping and
1383 starting logging again. This might change in a future version of Qt.
1384
1385 \note The object must be initialized before logging can happen.
1386
1387 \sa stopLogging(), initialize()
1388*/
1389void QOpenGLDebugLogger::startLogging(QOpenGLDebugLogger::LoggingMode loggingMode)
1390{
1391 Q_D(QOpenGLDebugLogger);
1392 if (!d->initialized) {
1393 qWarning("QOpenGLDebugLogger::startLogging(): object must be initialized before logging can start");
1394 return;
1395 }
1396 if (d->isLogging) {
1397 qWarning("QOpenGLDebugLogger::startLogging(): this object is already logging");
1398 return;
1399 }
1400
1401 d->isLogging = true;
1402 d->loggingMode = loggingMode;
1403
1404 d->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, reinterpret_cast<void **>(&d->oldDebugCallbackFunction));
1405 d->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &d->oldDebugCallbackParameter);
1406
1407 d->glDebugMessageCallback(&qt_opengl_debug_callback, d);
1408
1409 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
1410 d->debugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT);
1411 d->syncDebugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1412
1413 if (d->loggingMode == SynchronousLogging)
1414 funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1415 else
1416 funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1417
1418 funcs->glEnable(GL_DEBUG_OUTPUT);
1419}
1420
1421/*!
1422 Returns the logging mode of the object.
1423
1424 \sa startLogging()
1425*/
1426QOpenGLDebugLogger::LoggingMode QOpenGLDebugLogger::loggingMode() const
1427{
1428 Q_D(const QOpenGLDebugLogger);
1429 return d->loggingMode;
1430}
1431
1432/*!
1433 Stops logging messages from the OpenGL server.
1434
1435 \sa startLogging()
1436*/
1437void QOpenGLDebugLogger::stopLogging()
1438{
1439 Q_D(QOpenGLDebugLogger);
1440 if (!d->isLogging)
1441 return;
1442
1443 QOpenGLContext *currentContext = QOpenGLContext::currentContext();
1444 if (!currentContext || currentContext != d->context) {
1445 qWarning("QOpenGLDebugLogger::stopLogging(): attempting to stop logging with the wrong OpenGL context current");
1446 return;
1447 }
1448
1449 d->isLogging = false;
1450
1451 d->glDebugMessageCallback(d->oldDebugCallbackFunction, d->oldDebugCallbackParameter);
1452
1453 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
1454 if (!d->debugWasEnabled)
1455 funcs->glDisable(GL_DEBUG_OUTPUT);
1456
1457 if (d->syncDebugWasEnabled)
1458 funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1459 else
1460 funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1461}
1462
1463/*!
1464 Inserts the message \a debugMessage into the OpenGL debug log. This provides
1465 a way for applications or libraries to insert custom messages that can
1466 ease the debugging of OpenGL applications.
1467
1468 \note \a debugMessage must have QOpenGLDebugMessage::ApplicationSource or
1469 QOpenGLDebugMessage::ThirdPartySource as its source, and a valid
1470 type and severity, otherwise it will not be inserted into the log.
1471
1472 \note The object must be initialized before logging can happen.
1473
1474 \sa initialize()
1475*/
1476void QOpenGLDebugLogger::logMessage(const QOpenGLDebugMessage &debugMessage)
1477{
1478 Q_D(QOpenGLDebugLogger);
1479 if (!d->initialized) {
1480 qWarning("QOpenGLDebugLogger::logMessage(): object must be initialized before logging messages");
1481 return;
1482 }
1483 if (debugMessage.source() != QOpenGLDebugMessage::ApplicationSource
1484 && debugMessage.source() != QOpenGLDebugMessage::ThirdPartySource) {
1485 qWarning("QOpenGLDebugLogger::logMessage(): using a message source different from ApplicationSource\n"
1486 " or ThirdPartySource is not supported by GL_KHR_debug. The message will not be logged.");
1487 return;
1488 }
1489 if (debugMessage.type() == QOpenGLDebugMessage::InvalidType
1490 || debugMessage.type() == QOpenGLDebugMessage::AnyType
1491 || debugMessage.severity() == QOpenGLDebugMessage::InvalidSeverity
1492 || debugMessage.severity() == QOpenGLDebugMessage::AnySeverity) {
1493 qWarning("QOpenGLDebugLogger::logMessage(): the message has a non-valid type and/or severity. The message will not be logged.");
1494 return;
1495 }
1496
1497 const GLenum source = qt_messageSourceToGL(debugMessage.source());
1498 const GLenum type = qt_messageTypeToGL(debugMessage.type());
1499 const GLenum severity = qt_messageSeverityToGL(debugMessage.severity());
1500 QByteArray rawMessage = debugMessage.message().toUtf8();
1501 rawMessage.append('\0');
1502
1503 if (rawMessage.size() > d->maxMessageLength) {
1504 qWarning("QOpenGLDebugLogger::logMessage(): message too long, truncating it\n"
1505 " (%d bytes long, but the GL accepts up to %d bytes)", int(rawMessage.size()), d->maxMessageLength);
1506 rawMessage.resize(d->maxMessageLength - 1);
1507 rawMessage.append('\0');
1508 }
1509
1510 // Don't pass rawMessage.length(), as unfortunately bugged
1511 // OpenGL drivers will eat the trailing NUL in the message. Just rely
1512 // on the message being NUL terminated.
1513 d->glDebugMessageInsert(source,
1514 type,
1515 debugMessage.id(),
1516 severity,
1517 -1,
1518 rawMessage.constData());
1519}
1520
1521/*!
1522 Pushes a debug group with name \a name, id \a id, and source \a source onto
1523 the debug groups stack. If the group is successfully pushed, OpenGL will
1524 automatically log a message with message \a name, id \a id, source \a
1525 source, type QOpenGLDebugMessage::GroupPushType and severity
1526 QOpenGLDebugMessage::NotificationSeverity.
1527
1528 The newly pushed group will inherit the same filtering settings of the
1529 group that was on the top of the stack; that is, the filtering will not be
1530 changed by pushing a new group.
1531
1532 \note The \a source must either be QOpenGLDebugMessage::ApplicationSource or
1533 QOpenGLDebugMessage::ThirdPartySource, otherwise the group will not be pushed.
1534
1535 \note The object must be initialized before managing debug groups.
1536
1537 \sa popGroup(), enableMessages(), disableMessages()
1538*/
1539void QOpenGLDebugLogger::pushGroup(const QString &name, GLuint id, QOpenGLDebugMessage::Source source)
1540{
1541 Q_D(QOpenGLDebugLogger);
1542 if (!d->initialized) {
1543 qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before pushing a debug group");
1544 return;
1545 }
1546 if (source != QOpenGLDebugMessage::ApplicationSource
1547 && source != QOpenGLDebugMessage::ThirdPartySource) {
1548 qWarning("QOpenGLDebugLogger::pushGroup(): using a source different from ApplicationSource\n"
1549 " or ThirdPartySource is not supported by GL_KHR_debug. The group will not be pushed.");
1550 return;
1551 }
1552
1553 QByteArray rawName = name.toUtf8();
1554 rawName.append('\0');
1555 if (rawName.size() > d->maxMessageLength) {
1556 qWarning("QOpenGLDebugLogger::pushGroup(): group name too long, truncating it\n"
1557 " (%d bytes long, but the GL accepts up to %d bytes)", int(rawName.size()), d->maxMessageLength);
1558 rawName.resize(d->maxMessageLength - 1);
1559 rawName.append('\0');
1560 }
1561
1562 // Don't pass rawMessage.length(), as unfortunately bugged
1563 // OpenGL drivers will eat the trailing NUL in the name. Just rely
1564 // on the name being NUL terminated.
1565 d->glPushDebugGroup(qt_messageSourceToGL(source), id, -1, rawName.constData());
1566}
1567
1568/*!
1569 Pops the topmost debug group from the debug groups stack. If the group is
1570 successfully popped, OpenGL will automatically log a message with message,
1571 id and source matching those of the popped group, type
1572 QOpenGLDebugMessage::GroupPopType and severity
1573 QOpenGLDebugMessage::NotificationSeverity.
1574
1575 Popping a debug group will restore the message filtering settings of the
1576 group that becomes the top of the debug groups stack.
1577
1578 \note The object must be initialized before managing debug groups.
1579
1580 \sa pushGroup()
1581*/
1582void QOpenGLDebugLogger::popGroup()
1583{
1584 Q_D(QOpenGLDebugLogger);
1585 if (!d->initialized) {
1586 qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before popping a debug group");
1587 return;
1588 }
1589
1590 d->glPopDebugGroup();
1591}
1592
1593/*!
1594 Enables the logging of messages from the given \a sources, of the given \a
1595 types and with the given \a severities and any message id.
1596
1597 The logging will be enabled in the current control group.
1598
1599 \sa disableMessages(), pushGroup(), popGroup()
1600*/
1601void QOpenGLDebugLogger::enableMessages(QOpenGLDebugMessage::Sources sources,
1602 QOpenGLDebugMessage::Types types,
1603 QOpenGLDebugMessage::Severities severities)
1604{
1605 Q_D(QOpenGLDebugLogger);
1606 d->controlDebugMessages(sources, types, severities, QList<GLuint>(),
1607 QByteArrayLiteral("enableMessages"), true);
1608}
1609
1610/*!
1611 Enables the logging of messages with the given \a ids, from the given \a
1612 sources and of the given \a types and any severity.
1613
1614 The logging will be enabled in the current control group.
1615
1616 \sa disableMessages(), pushGroup(), popGroup()
1617*/
1618void QOpenGLDebugLogger::enableMessages(const QList<GLuint> &ids,
1619 QOpenGLDebugMessage::Sources sources,
1620 QOpenGLDebugMessage::Types types)
1621{
1622 Q_D(QOpenGLDebugLogger);
1623 d->controlDebugMessages(sources,
1624 types,
1625 QOpenGLDebugMessage::AnySeverity,
1626 ids,
1627 QByteArrayLiteral("enableMessages"),
1628 true);
1629}
1630
1631/*!
1632 Disables the logging of messages with the given \a sources, of the given \a
1633 types and with the given \a severities and any message id.
1634
1635 The logging will be disabled in the current control group.
1636
1637 \sa enableMessages(), pushGroup(), popGroup()
1638*/
1639void QOpenGLDebugLogger::disableMessages(QOpenGLDebugMessage::Sources sources,
1640 QOpenGLDebugMessage::Types types,
1641 QOpenGLDebugMessage::Severities severities)
1642{
1643 Q_D(QOpenGLDebugLogger);
1644 d->controlDebugMessages(sources, types, severities, QList<GLuint>(),
1645 QByteArrayLiteral("disableMessages"), false);
1646}
1647
1648/*!
1649 Disables the logging of messages with the given \a ids, from the given \a
1650 sources and of the given \a types and any severity.
1651
1652 The logging will be disabled in the current control group.
1653
1654 \sa enableMessages(), pushGroup(), popGroup()
1655*/
1656void QOpenGLDebugLogger::disableMessages(const QList<GLuint> &ids,
1657 QOpenGLDebugMessage::Sources sources,
1658 QOpenGLDebugMessage::Types types)
1659{
1660 Q_D(QOpenGLDebugLogger);
1661 d->controlDebugMessages(sources,
1662 types,
1663 QOpenGLDebugMessage::AnySeverity,
1664 ids,
1665 QByteArrayLiteral("disableMessages"),
1666 false);
1667}
1668
1669/*!
1670 Reads all the available messages in the OpenGL internal debug log and
1671 returns them. Moreover, this function will clear the internal debug log,
1672 so that subsequent invocations will not return messages that were
1673 already returned.
1674
1675 \sa startLogging()
1676*/
1677QList<QOpenGLDebugMessage> QOpenGLDebugLogger::loggedMessages() const
1678{
1679 Q_D(const QOpenGLDebugLogger);
1680 if (!d->initialized) {
1681 qWarning("QOpenGLDebugLogger::loggedMessages(): object must be initialized before reading logged messages");
1682 return QList<QOpenGLDebugMessage>();
1683 }
1684
1685 static const GLuint maxMessageCount = 128;
1686 GLuint messagesRead;
1687 GLenum messageSources[maxMessageCount];
1688 GLenum messageTypes[maxMessageCount];
1689 GLuint messageIds[maxMessageCount];
1690 GLenum messageSeverities[maxMessageCount];
1691 GLsizei messageLengths[maxMessageCount];
1692
1693 QByteArray messagesBuffer;
1694 messagesBuffer.resize(maxMessageCount * d->maxMessageLength);
1695
1696 QList<QOpenGLDebugMessage> messages;
1697 do {
1698 messagesRead = d->glGetDebugMessageLog(maxMessageCount,
1699 GLsizei(messagesBuffer.size()),
1700 messageSources,
1701 messageTypes,
1702 messageIds,
1703 messageSeverities,
1704 messageLengths,
1705 messagesBuffer.data());
1706
1707 const char *messagesBufferPtr = messagesBuffer.constData();
1708 for (GLuint i = 0; i < messagesRead; ++i) {
1709 QOpenGLDebugMessage message;
1710
1711 QOpenGLDebugMessagePrivate *messagePrivate = message.d.data();
1712 messagePrivate->source = qt_messageSourceFromGL(messageSources[i]);
1713 messagePrivate->type = qt_messageTypeFromGL(messageTypes[i]);
1714 messagePrivate->id = messageIds[i];
1715 messagePrivate->severity = qt_messageSeverityFromGL(messageSeverities[i]);
1716 messagePrivate->message = QString::fromUtf8(messagesBufferPtr, messageLengths[i] - 1);
1717
1718 messagesBufferPtr += messageLengths[i];
1719 messages << message;
1720 }
1721 } while (messagesRead == maxMessageCount);
1722
1723 return messages;
1724}
1725
1726/*!
1727 \fn void QOpenGLDebugLogger::messageLogged(const QOpenGLDebugMessage &debugMessage)
1728
1729 This signal is emitted when a debug message (wrapped by the \a debugMessage
1730 argument) is logged from the OpenGL server.
1731
1732 Depending on the OpenGL implementation, this signal can be emitted
1733 from other threads than the one(s) the receiver(s) lives in, and even
1734 different from the thread the QOpenGLContext in which this object has
1735 been initialized lives in. Moreover, the signal could be emitted from
1736 multiple threads at the same time. This is normally not a problem,
1737 as Qt will utilize a queued connection for cross-thread signal emissions,
1738 but if you force the connection type to Direct then you must be aware of
1739 the potential races in the slots connected to this signal.
1740
1741 If logging have been started in SynchronousLogging mode, OpenGL guarantees
1742 that this signal will be emitted from the same thread the QOpenGLContext
1743 has been bound to, and no concurrent invocations will ever happen.
1744
1745 \note Logging must have been started, or this signal will not be emitted.
1746
1747 \sa startLogging()
1748*/
1749
1750/*!
1751 Returns the maximum supported length, in bytes, for the text of the messages
1752 passed to logMessage(). This is also the maximum length of a debug group
1753 name, as pushing or popping groups will automatically log a message with
1754 the debug group name as the message text.
1755
1756 If a message text is too long, it will be automatically truncated by
1757 QOpenGLDebugLogger.
1758
1759 \note Message texts are encoded in UTF-8 when they get passed to OpenGL, so
1760 their size in bytes does not usually match the amount of UTF-16 code units,
1761 as returned, for instance, by QString::length(). (It does if the message contains
1762 7-bit ASCII only data, which is typical for debug messages.)
1763*/
1764qint64 QOpenGLDebugLogger::maximumMessageLength() const
1765{
1766 Q_D(const QOpenGLDebugLogger);
1767 if (!d->initialized) {
1768 qWarning("QOpenGLDebugLogger::maximumMessageLength(): object must be initialized before reading the maximum message length");
1769 return -1;
1770 }
1771 return d->maxMessageLength;
1772}
1773
1774
1775QT_END_NAMESPACE
1776
1777#include "moc_qopengldebug.cpp"
The QOpenGLDebugMessage class wraps an OpenGL debug message.
#define USE_MANUAL_DEFS
static GLenum qt_messageTypeToGL(QOpenGLDebugMessage::Type type)
static QOpenGLDebugMessage::Severity qt_messageSeverityFromGL(GLenum severity)
static QOpenGLDebugMessage::Type qt_messageTypeFromGL(GLenum type)
static GLenum qt_messageSourceToGL(QOpenGLDebugMessage::Source source)
static GLenum qt_messageSeverityToGL(QOpenGLDebugMessage::Severity severity)
static QString qt_messageTypeToString(QOpenGLDebugMessage::Type type)
#define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target)
#define GET_DEBUG_PROC_ADDRESS(procName)
static QString qt_messageSourceToString(QOpenGLDebugMessage::Source source)
static QOpenGLDebugMessage::Source qt_messageSourceFromGL(GLenum source)
static QString qt_messageSeverityToString(QOpenGLDebugMessage::Severity severity)
#define GL_DEBUG_TYPE_MARKER
#define GL_DEBUG_SEVERITY_LOW
#define GL_DEBUG_SOURCE_THIRD_PARTY
#define GL_DEBUG_TYPE_PORTABILITY
#define GL_DEBUG_TYPE_OTHER
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
#define GL_MAX_DEBUG_MESSAGE_LENGTH
#define GL_DEBUG_TYPE_PUSH_GROUP
#define GL_DEBUG_SOURCE_APPLICATION
#define GL_DEBUG_SOURCE_OTHER
#define GL_DEBUG_SEVERITY_MEDIUM
#define GL_DEBUG_SOURCE_SHADER_COMPILER
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
#define GL_DEBUG_SEVERITY_NOTIFICATION
#define GL_DEBUG_TYPE_POP_GROUP
#define GL_DEBUG_SOURCE_WINDOW_SYSTEM
#define GL_DEBUG_SOURCE_API
#define GL_DEBUG_CALLBACK_USER_PARAM
#define GL_DEBUG_TYPE_ERROR
#define GL_DEBUG_CALLBACK_FUNCTION
#define GL_DEBUG_SEVERITY_HIGH
#define GL_DEBUG_OUTPUT_SYNCHRONOUS
#define GL_DEBUG_TYPE_PERFORMANCE
#define GL_DEBUG_OUTPUT