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