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

source code of qtbase/src/opengl/qopengldebug.cpp