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