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 QtGui 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 | |
49 | QT_BEGIN_NAMESPACE |
50 | |
51 | /*! |
52 | \class QOpenGLDebugMessage |
53 | \brief The QOpenGLDebugMessage class wraps an OpenGL debug message. |
54 | \inmodule QtGui |
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 QtGui |
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 | |
559 | typedef 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 | */ |
567 | static 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 | */ |
591 | static 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 | */ |
619 | static 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 | */ |
647 | static 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 | */ |
677 | static 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 | */ |
711 | static 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 | */ |
745 | static 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 | */ |
765 | static 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 | */ |
789 | static 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 | |
810 | class QOpenGLDebugMessagePrivate : public QSharedData |
811 | { |
812 | public: |
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 | */ |
825 | QOpenGLDebugMessagePrivate::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 | */ |
846 | QOpenGLDebugMessage::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 | */ |
856 | QOpenGLDebugMessage::QOpenGLDebugMessage(const QOpenGLDebugMessage &debugMessage) |
857 | : d(debugMessage.d) |
858 | { |
859 | } |
860 | |
861 | /*! |
862 | Destroys this debug message. |
863 | */ |
864 | QOpenGLDebugMessage::~QOpenGLDebugMessage() |
865 | { |
866 | } |
867 | |
868 | /*! |
869 | Assigns the message \a debugMessage to this object, and returns a reference |
870 | to the copy. |
871 | */ |
872 | QOpenGLDebugMessage &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 | */ |
894 | QOpenGLDebugMessage::Source QOpenGLDebugMessage::source() const |
895 | { |
896 | return d->source; |
897 | } |
898 | |
899 | /*! |
900 | Returns the type of the debug message. |
901 | */ |
902 | QOpenGLDebugMessage::Type QOpenGLDebugMessage::type() const |
903 | { |
904 | return d->type; |
905 | } |
906 | |
907 | /*! |
908 | Returns the severity of the debug message. |
909 | */ |
910 | QOpenGLDebugMessage::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 | */ |
918 | GLuint QOpenGLDebugMessage::id() const |
919 | { |
920 | return d->id; |
921 | } |
922 | |
923 | /*! |
924 | Returns the textual message contained by this debug message. |
925 | */ |
926 | QString 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 | */ |
938 | QOpenGLDebugMessage 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 | */ |
959 | QOpenGLDebugMessage 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 | */ |
980 | bool 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 | */ |
1006 | QDebug 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 | */ |
1021 | QDebug 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 | */ |
1036 | QDebug 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 | */ |
1051 | QDebug operator<<(QDebug debug, const QOpenGLDebugMessage &message) |
1052 | { |
1053 | QDebugStateSaver saver(debug); |
1054 | debug.nospace() << "QOpenGLDebugMessage(" |
1055 | << qt_messageSourceToString(source: message.source()) << ", " |
1056 | << message.id() << ", " |
1057 | << message.message() << ", " |
1058 | << qt_messageSeverityToString(severity: message.severity()) << ", " |
1059 | << qt_messageTypeToString(type: message.type()) << ')'; |
1060 | return debug; |
1061 | |
1062 | } |
1063 | #endif // QT_NO_DEBUG_STREAM |
1064 | |
1065 | typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageControl_t)(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); |
1066 | typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageInsert_t)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); |
1067 | typedef void (QOPENGLF_APIENTRYP qt_glDebugMessageCallback_t)(GLDEBUGPROC callback, const void *userParam); |
1068 | typedef GLuint (QOPENGLF_APIENTRYP qt_glGetDebugMessageLog_t)(GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); |
1069 | typedef void (QOPENGLF_APIENTRYP qt_glPushDebugGroup_t)(GLenum source, GLuint id, GLsizei length, const GLchar *message); |
1070 | typedef void (QOPENGLF_APIENTRYP qt_glPopDebugGroup_t)(); |
1071 | typedef void (QOPENGLF_APIENTRYP qt_glGetPointerv_t)(GLenum pname, GLvoid **params); |
1072 | |
1073 | class QOpenGLDebugLoggerPrivate : public QObjectPrivate |
1074 | { |
1075 | Q_DECLARE_PUBLIC(QOpenGLDebugLogger) |
1076 | public: |
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, |
1083 | const QVector<GLuint> &ids, |
1084 | const QByteArray &callerName, |
1085 | bool enable); |
1086 | void _q_contextAboutToBeDestroyed(); |
1087 | |
1088 | qt_glDebugMessageControl_t glDebugMessageControl; |
1089 | qt_glDebugMessageInsert_t glDebugMessageInsert; |
1090 | qt_glDebugMessageCallback_t glDebugMessageCallback; |
1091 | qt_glGetDebugMessageLog_t glGetDebugMessageLog; |
1092 | qt_glPushDebugGroup_t glPushDebugGroup; |
1093 | qt_glPopDebugGroup_t glPopDebugGroup; |
1094 | qt_glGetPointerv_t glGetPointerv; |
1095 | |
1096 | GLDEBUGPROC oldDebugCallbackFunction; |
1097 | void *oldDebugCallbackParameter; |
1098 | QOpenGLContext *context; |
1099 | GLint maxMessageLength; |
1100 | QOpenGLDebugLogger::LoggingMode loggingMode; |
1101 | bool initialized : 1; |
1102 | bool isLogging : 1; |
1103 | bool debugWasEnabled : 1; |
1104 | bool syncDebugWasEnabled : 1; |
1105 | }; |
1106 | |
1107 | /*! |
1108 | \internal |
1109 | */ |
1110 | QOpenGLDebugLoggerPrivate::QOpenGLDebugLoggerPrivate() |
1111 | : glDebugMessageControl(nullptr), |
1112 | glDebugMessageInsert(nullptr), |
1113 | glDebugMessageCallback(nullptr), |
1114 | glGetDebugMessageLog(nullptr), |
1115 | glPushDebugGroup(nullptr), |
1116 | glPopDebugGroup(nullptr), |
1117 | oldDebugCallbackFunction(nullptr), |
1118 | context(nullptr), |
1119 | maxMessageLength(0), |
1120 | loggingMode(QOpenGLDebugLogger::AsynchronousLogging), |
1121 | initialized(false), |
1122 | isLogging(false), |
1123 | debugWasEnabled(false), |
1124 | syncDebugWasEnabled(false) |
1125 | { |
1126 | } |
1127 | |
1128 | /*! |
1129 | \internal |
1130 | */ |
1131 | void QOpenGLDebugLoggerPrivate::handleMessage(GLenum source, |
1132 | GLenum type, |
1133 | GLuint id, |
1134 | GLenum severity, |
1135 | GLsizei length, |
1136 | const GLchar *rawMessage) |
1137 | { |
1138 | if (oldDebugCallbackFunction) |
1139 | oldDebugCallbackFunction(source, type, id, severity, length, rawMessage, oldDebugCallbackParameter); |
1140 | |
1141 | QOpenGLDebugMessage message; |
1142 | |
1143 | QOpenGLDebugMessagePrivate *messagePrivate = message.d.data(); |
1144 | messagePrivate->source = qt_messageSourceFromGL(source); |
1145 | messagePrivate->type = qt_messageTypeFromGL(type); |
1146 | messagePrivate->id = id; |
1147 | messagePrivate->severity = qt_messageSeverityFromGL(severity); |
1148 | // not passing the length to fromUtf8, as some bugged OpenGL drivers |
1149 | // do not handle the length correctly. Just rely on the message to be NUL terminated. |
1150 | messagePrivate->message = QString::fromUtf8(str: rawMessage); |
1151 | |
1152 | Q_Q(QOpenGLDebugLogger); |
1153 | emit q->messageLogged(debugMessage: message); |
1154 | } |
1155 | |
1156 | /*! |
1157 | \internal |
1158 | */ |
1159 | void QOpenGLDebugLoggerPrivate::controlDebugMessages(QOpenGLDebugMessage::Sources sources, |
1160 | QOpenGLDebugMessage::Types types, |
1161 | QOpenGLDebugMessage::Severities severities, |
1162 | const QVector<GLuint> &ids, |
1163 | const QByteArray &callerName, |
1164 | bool enable) |
1165 | { |
1166 | if (!initialized) { |
1167 | qWarning(msg: "QOpenGLDebugLogger::%s(): object must be initialized before enabling/disabling messages" , callerName.constData()); |
1168 | return; |
1169 | } |
1170 | if (sources == QOpenGLDebugMessage::InvalidSource) { |
1171 | qWarning(msg: "QOpenGLDebugLogger::%s(): invalid source specified" , callerName.constData()); |
1172 | return; |
1173 | } |
1174 | if (types == QOpenGLDebugMessage::InvalidType) { |
1175 | qWarning(msg: "QOpenGLDebugLogger::%s(): invalid type specified" , callerName.constData()); |
1176 | return; |
1177 | } |
1178 | if (severities == QOpenGLDebugMessage::InvalidSeverity) { |
1179 | qWarning(msg: "QOpenGLDebugLogger::%s(): invalid severity specified" , callerName.constData()); |
1180 | return; |
1181 | } |
1182 | |
1183 | QVarLengthArray<GLenum, 8> glSources; |
1184 | QVarLengthArray<GLenum, 8> glTypes; |
1185 | QVarLengthArray<GLenum, 8> glSeverities; |
1186 | |
1187 | if (ids.count() > 0) { |
1188 | Q_ASSERT(severities == QOpenGLDebugMessage::AnySeverity); |
1189 | |
1190 | // The GL_KHR_debug extension says: |
1191 | // |
1192 | // - If <count> is greater than zero, then <ids> is an array of <count> |
1193 | // message IDs for the specified combination of <source> and <type>. In |
1194 | // this case, if <source> or <type> is DONT_CARE, or <severity> is not |
1195 | // DONT_CARE, the error INVALID_OPERATION is generated. If <count> is |
1196 | // zero, the value if <ids> is ignored. |
1197 | // |
1198 | // This means we can't convert AnySource or AnyType into DONT_CARE, but we have to roll |
1199 | // them into individual sources/types. |
1200 | |
1201 | if (sources == QOpenGLDebugMessage::AnySource) { |
1202 | sources = QOpenGLDebugMessage::InvalidSource; |
1203 | for (uint i = 1; i <= QOpenGLDebugMessage::LastSource; i = i << 1) |
1204 | sources |= QOpenGLDebugMessage::Source(i); |
1205 | } |
1206 | |
1207 | if (types == QOpenGLDebugMessage::AnyType) { |
1208 | types = QOpenGLDebugMessage::InvalidType; |
1209 | for (uint i = 1; i <= QOpenGLDebugMessage::LastType; i = i << 1) |
1210 | types |= QOpenGLDebugMessage::Type(i); |
1211 | } |
1212 | } |
1213 | |
1214 | #define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target) \ |
1215 | if (source == QOpenGLDebugMessage::Any ## type) { \ |
1216 | target << GL_DONT_CARE; \ |
1217 | } else { \ |
1218 | for (uint i = 1; i <= QOpenGLDebugMessage::Last ## type; i = i << 1) \ |
1219 | if (source.testFlag(QOpenGLDebugMessage:: type (i))) \ |
1220 | target << qt_message ## type ## ToGL (QOpenGLDebugMessage:: type (i)); \ |
1221 | } |
1222 | |
1223 | CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Source, sources, glSources) |
1224 | CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Type, types, glTypes) |
1225 | CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(Severity, severities, glSeverities) |
1226 | #undef CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS |
1227 | |
1228 | const GLsizei idCount = ids.count(); |
1229 | // The GL_KHR_debug extension says that if idCount is 0, idPtr must be ignored. |
1230 | // Unfortunately, some bugged drivers do NOT ignore it, so pass NULL in case. |
1231 | const GLuint * const idPtr = idCount ? ids.constData() : nullptr; |
1232 | |
1233 | for (GLenum source : glSources) |
1234 | for (GLenum type : glTypes) |
1235 | for (GLenum severity : glSeverities) |
1236 | glDebugMessageControl(source, type, severity, idCount, idPtr, GLboolean(enable)); |
1237 | } |
1238 | |
1239 | /*! |
1240 | \internal |
1241 | */ |
1242 | void QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed() |
1243 | { |
1244 | Q_ASSERT(context); |
1245 | |
1246 | // Re-make our context current somehow, otherwise stopLogging will fail. |
1247 | |
1248 | // Save the current context and its surface in case we need to set them back |
1249 | QOpenGLContext *currentContext = QOpenGLContext::currentContext(); |
1250 | QSurface *currentSurface = nullptr; |
1251 | |
1252 | QScopedPointer<QOffscreenSurface> offscreenSurface; |
1253 | |
1254 | if (context != currentContext) { |
1255 | // Make our old context current on a temporary surface |
1256 | if (currentContext) |
1257 | currentSurface = currentContext->surface(); |
1258 | |
1259 | offscreenSurface.reset(other: new QOffscreenSurface); |
1260 | offscreenSurface->setFormat(context->format()); |
1261 | offscreenSurface->create(); |
1262 | if (!context->makeCurrent(surface: offscreenSurface.data())) |
1263 | qWarning(msg: "QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed(): could not make the owning GL context current for cleanup" ); |
1264 | } |
1265 | |
1266 | Q_Q(QOpenGLDebugLogger); |
1267 | q->stopLogging(); |
1268 | |
1269 | if (offscreenSurface) { |
1270 | // We did change the current context: set it back |
1271 | if (currentContext) |
1272 | currentContext->makeCurrent(surface: currentSurface); |
1273 | else |
1274 | context->doneCurrent(); |
1275 | } |
1276 | |
1277 | QObject::disconnect(sender: context, SIGNAL(aboutToBeDestroyed()), receiver: q, SLOT(_q_contextAboutToBeDestroyed())); |
1278 | context = nullptr; |
1279 | initialized = false; |
1280 | } |
1281 | |
1282 | extern "C" { |
1283 | static void QOPENGLF_APIENTRY qt_opengl_debug_callback(GLenum source, |
1284 | GLenum type, |
1285 | GLuint id, |
1286 | GLenum severity, |
1287 | GLsizei length, |
1288 | const GLchar *rawMessage, |
1289 | const GLvoid *userParam) |
1290 | { |
1291 | QOpenGLDebugLoggerPrivate *loggerPrivate = static_cast<QOpenGLDebugLoggerPrivate *>(const_cast<GLvoid *>(userParam)); |
1292 | loggerPrivate->handleMessage(source, type, id, severity, length, rawMessage); |
1293 | } |
1294 | } |
1295 | |
1296 | /*! |
1297 | Constructs a new logger object with the given \a parent. |
1298 | |
1299 | \note The object must be initialized before logging can happen. |
1300 | |
1301 | \sa initialize() |
1302 | */ |
1303 | QOpenGLDebugLogger::QOpenGLDebugLogger(QObject *parent) |
1304 | : QObject(*new QOpenGLDebugLoggerPrivate, parent) |
1305 | { |
1306 | // QOpenGLDebugMessage is going to be mostly used as an argument |
1307 | // of a cross thread connection, therefore let's ease the life for the users |
1308 | // and register the type for them. |
1309 | qRegisterMetaType<QOpenGLDebugMessage>(); |
1310 | } |
1311 | |
1312 | /*! |
1313 | Destroys the logger object. |
1314 | */ |
1315 | QOpenGLDebugLogger::~QOpenGLDebugLogger() |
1316 | { |
1317 | stopLogging(); |
1318 | } |
1319 | |
1320 | /*! |
1321 | Initializes the object in the current OpenGL context. The context must |
1322 | support the \c{GL_KHR_debug} extension for the initialization to succeed. |
1323 | The object must be initialized before any logging can happen. |
1324 | |
1325 | It is safe to call this function multiple times from the same context. |
1326 | |
1327 | This function can also be used to change the context of a previously |
1328 | initialized object; note that in this case the object must not be logging |
1329 | when you call this function. |
1330 | |
1331 | Returns \c true if the logger is successfully initialized; false otherwise. |
1332 | |
1333 | \sa QOpenGLContext |
1334 | */ |
1335 | bool QOpenGLDebugLogger::initialize() |
1336 | { |
1337 | QOpenGLContext *context = QOpenGLContext::currentContext(); |
1338 | if (!context) { |
1339 | qWarning(msg: "QOpenGLDebugLogger::initialize(): no current OpenGL context found." ); |
1340 | return false; |
1341 | } |
1342 | |
1343 | Q_D(QOpenGLDebugLogger); |
1344 | if (d->context == context) { |
1345 | // context is non-NULL, d->context is non NULL only on successful initialization. |
1346 | Q_ASSERT(d->initialized); |
1347 | return true; |
1348 | } |
1349 | |
1350 | if (d->isLogging) { |
1351 | qWarning(msg: "QOpenGLDebugLogger::initialize(): cannot initialize the object while logging. Please stop the logging first." ); |
1352 | return false; |
1353 | } |
1354 | |
1355 | if (d->context) |
1356 | disconnect(sender: d->context, SIGNAL(aboutToBeDestroyed()), receiver: this, SLOT(_q_contextAboutToBeDestroyed())); |
1357 | |
1358 | d->initialized = false; |
1359 | d->context = nullptr; |
1360 | |
1361 | if (!context->hasExtension(QByteArrayLiteral("GL_KHR_debug" ))) |
1362 | return false; |
1363 | |
1364 | d->context = context; |
1365 | connect(sender: d->context, SIGNAL(aboutToBeDestroyed()), receiver: this, SLOT(_q_contextAboutToBeDestroyed())); |
1366 | |
1367 | #define GET_DEBUG_PROC_ADDRESS(procName) \ |
1368 | d->procName = reinterpret_cast< qt_ ## procName ## _t >( \ |
1369 | d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName)) \ |
1370 | ); |
1371 | |
1372 | GET_DEBUG_PROC_ADDRESS(glDebugMessageControl); |
1373 | GET_DEBUG_PROC_ADDRESS(glDebugMessageInsert); |
1374 | GET_DEBUG_PROC_ADDRESS(glDebugMessageCallback); |
1375 | GET_DEBUG_PROC_ADDRESS(glGetDebugMessageLog); |
1376 | GET_DEBUG_PROC_ADDRESS(glPushDebugGroup); |
1377 | GET_DEBUG_PROC_ADDRESS(glPopDebugGroup); |
1378 | GET_DEBUG_PROC_ADDRESS(glGetPointerv) |
1379 | |
1380 | #undef GET_DEBUG_PROC_ADDRESS |
1381 | |
1382 | QOpenGLContext::currentContext()->functions()->glGetIntegerv(GL_MAX_DEBUG_MESSAGE_LENGTH, params: &d->maxMessageLength); |
1383 | |
1384 | #ifndef QT_NO_DEBUG |
1385 | if (!d->context->format().testOption(option: QSurfaceFormat::DebugContext)) { |
1386 | qWarning(msg: "QOpenGLDebugLogger::initialize(): the current context is not a debug context:\n" |
1387 | " this means that the GL may not generate any debug output at all.\n" |
1388 | " To avoid this warning, try creating the context with the\n" |
1389 | " QSurfaceFormat::DebugContext surface format option." ); |
1390 | } |
1391 | #endif // QT_NO_DEBUG |
1392 | |
1393 | d->initialized = true; |
1394 | return true; |
1395 | } |
1396 | |
1397 | /*! |
1398 | Returns \c true if this object is currently logging, false otherwise. |
1399 | |
1400 | \sa startLogging() |
1401 | */ |
1402 | bool QOpenGLDebugLogger::isLogging() const |
1403 | { |
1404 | Q_D(const QOpenGLDebugLogger); |
1405 | return d->isLogging; |
1406 | } |
1407 | |
1408 | /*! |
1409 | Starts logging messages coming from the OpenGL server. When a new message |
1410 | is received, the signal messageLogged() is emitted, carrying the logged |
1411 | message as argument. |
1412 | |
1413 | \a loggingMode specifies whether the logging must be asynchronous (the default) |
1414 | or synchronous. |
1415 | |
1416 | QOpenGLDebugLogger will record the values of \c{GL_DEBUG_OUTPUT} and |
1417 | \c{GL_DEBUG_OUTPUT_SYNCHRONOUS} when logging is started, and set them back |
1418 | when logging is stopped. Moreover, any user-defined OpenGL debug callback |
1419 | installed when this function is invoked will be restored when logging is |
1420 | stopped; QOpenGLDebugLogger will ensure that the pre-existing callback will |
1421 | still be invoked when logging. |
1422 | |
1423 | \note It's not possible to change the logging mode without stopping and |
1424 | starting logging again. This might change in a future version of Qt. |
1425 | |
1426 | \note The object must be initialized before logging can happen. |
1427 | |
1428 | \sa stopLogging(), initialize() |
1429 | */ |
1430 | void QOpenGLDebugLogger::startLogging(QOpenGLDebugLogger::LoggingMode loggingMode) |
1431 | { |
1432 | Q_D(QOpenGLDebugLogger); |
1433 | if (!d->initialized) { |
1434 | qWarning(msg: "QOpenGLDebugLogger::startLogging(): object must be initialized before logging can start" ); |
1435 | return; |
1436 | } |
1437 | if (d->isLogging) { |
1438 | qWarning(msg: "QOpenGLDebugLogger::startLogging(): this object is already logging" ); |
1439 | return; |
1440 | } |
1441 | |
1442 | d->isLogging = true; |
1443 | d->loggingMode = loggingMode; |
1444 | |
1445 | d->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, reinterpret_cast<void **>(&d->oldDebugCallbackFunction)); |
1446 | d->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &d->oldDebugCallbackParameter); |
1447 | |
1448 | d->glDebugMessageCallback(&qt_opengl_debug_callback, d); |
1449 | |
1450 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
1451 | d->debugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT); |
1452 | d->syncDebugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
1453 | |
1454 | if (d->loggingMode == SynchronousLogging) |
1455 | funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
1456 | else |
1457 | funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
1458 | |
1459 | funcs->glEnable(GL_DEBUG_OUTPUT); |
1460 | } |
1461 | |
1462 | /*! |
1463 | Returns the logging mode of the object. |
1464 | |
1465 | \sa startLogging() |
1466 | */ |
1467 | QOpenGLDebugLogger::LoggingMode QOpenGLDebugLogger::loggingMode() const |
1468 | { |
1469 | Q_D(const QOpenGLDebugLogger); |
1470 | return d->loggingMode; |
1471 | } |
1472 | |
1473 | /*! |
1474 | Stops logging messages from the OpenGL server. |
1475 | |
1476 | \sa startLogging() |
1477 | */ |
1478 | void QOpenGLDebugLogger::stopLogging() |
1479 | { |
1480 | Q_D(QOpenGLDebugLogger); |
1481 | if (!d->isLogging) |
1482 | return; |
1483 | |
1484 | QOpenGLContext *currentContext = QOpenGLContext::currentContext(); |
1485 | if (!currentContext || currentContext != d->context) { |
1486 | qWarning(msg: "QOpenGLDebugLogger::stopLogging(): attempting to stop logging with the wrong OpenGL context current" ); |
1487 | return; |
1488 | } |
1489 | |
1490 | d->isLogging = false; |
1491 | |
1492 | d->glDebugMessageCallback(d->oldDebugCallbackFunction, d->oldDebugCallbackParameter); |
1493 | |
1494 | QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); |
1495 | if (!d->debugWasEnabled) |
1496 | funcs->glDisable(GL_DEBUG_OUTPUT); |
1497 | |
1498 | if (d->syncDebugWasEnabled) |
1499 | funcs->glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
1500 | else |
1501 | funcs->glDisable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
1502 | } |
1503 | |
1504 | /*! |
1505 | Inserts the message \a debugMessage into the OpenGL debug log. This provides |
1506 | a way for applications or libraries to insert custom messages that can |
1507 | ease the debugging of OpenGL applications. |
1508 | |
1509 | \note \a debugMessage must have QOpenGLDebugMessage::ApplicationSource or |
1510 | QOpenGLDebugMessage::ThirdPartySource as its source, and a valid |
1511 | type and severity, otherwise it will not be inserted into the log. |
1512 | |
1513 | \note The object must be initialized before logging can happen. |
1514 | |
1515 | \sa initialize() |
1516 | */ |
1517 | void QOpenGLDebugLogger::logMessage(const QOpenGLDebugMessage &debugMessage) |
1518 | { |
1519 | Q_D(QOpenGLDebugLogger); |
1520 | if (!d->initialized) { |
1521 | qWarning(msg: "QOpenGLDebugLogger::logMessage(): object must be initialized before logging messages" ); |
1522 | return; |
1523 | } |
1524 | if (debugMessage.source() != QOpenGLDebugMessage::ApplicationSource |
1525 | && debugMessage.source() != QOpenGLDebugMessage::ThirdPartySource) { |
1526 | qWarning(msg: "QOpenGLDebugLogger::logMessage(): using a message source different from ApplicationSource\n" |
1527 | " or ThirdPartySource is not supported by GL_KHR_debug. The message will not be logged." ); |
1528 | return; |
1529 | } |
1530 | if (debugMessage.type() == QOpenGLDebugMessage::InvalidType |
1531 | || debugMessage.type() == QOpenGLDebugMessage::AnyType |
1532 | || debugMessage.severity() == QOpenGLDebugMessage::InvalidSeverity |
1533 | || debugMessage.severity() == QOpenGLDebugMessage::AnySeverity) { |
1534 | qWarning(msg: "QOpenGLDebugLogger::logMessage(): the message has a non-valid type and/or severity. The message will not be logged." ); |
1535 | return; |
1536 | } |
1537 | |
1538 | const GLenum source = qt_messageSourceToGL(source: debugMessage.source()); |
1539 | const GLenum type = qt_messageTypeToGL(type: debugMessage.type()); |
1540 | const GLenum severity = qt_messageSeverityToGL(severity: debugMessage.severity()); |
1541 | QByteArray rawMessage = debugMessage.message().toUtf8(); |
1542 | rawMessage.append(c: '\0'); |
1543 | |
1544 | if (rawMessage.length() > d->maxMessageLength) { |
1545 | qWarning(msg: "QOpenGLDebugLogger::logMessage(): message too long, truncating it\n" |
1546 | " (%d bytes long, but the GL accepts up to %d bytes)" , rawMessage.length(), d->maxMessageLength); |
1547 | rawMessage.resize(size: d->maxMessageLength - 1); |
1548 | rawMessage.append(c: '\0'); |
1549 | } |
1550 | |
1551 | // Don't pass rawMessage.length(), as unfortunately bugged |
1552 | // OpenGL drivers will eat the trailing NUL in the message. Just rely |
1553 | // on the message being NUL terminated. |
1554 | d->glDebugMessageInsert(source, |
1555 | type, |
1556 | debugMessage.id(), |
1557 | severity, |
1558 | -1, |
1559 | rawMessage.constData()); |
1560 | } |
1561 | |
1562 | /*! |
1563 | Pushes a debug group with name \a name, id \a id, and source \a source onto |
1564 | the debug groups stack. If the group is successfully pushed, OpenGL will |
1565 | automatically log a message with message \a name, id \a id, source \a |
1566 | source, type QOpenGLDebugMessage::GroupPushType and severity |
1567 | QOpenGLDebugMessage::NotificationSeverity. |
1568 | |
1569 | The newly pushed group will inherit the same filtering settings of the |
1570 | group that was on the top of the stack; that is, the filtering will not be |
1571 | changed by pushing a new group. |
1572 | |
1573 | \note The \a source must either be QOpenGLDebugMessage::ApplicationSource or |
1574 | QOpenGLDebugMessage::ThirdPartySource, otherwise the group will not be pushed. |
1575 | |
1576 | \note The object must be initialized before managing debug groups. |
1577 | |
1578 | \sa popGroup(), enableMessages(), disableMessages() |
1579 | */ |
1580 | void QOpenGLDebugLogger::pushGroup(const QString &name, GLuint id, QOpenGLDebugMessage::Source source) |
1581 | { |
1582 | Q_D(QOpenGLDebugLogger); |
1583 | if (!d->initialized) { |
1584 | qWarning(msg: "QOpenGLDebugLogger::pushGroup(): object must be initialized before pushing a debug group" ); |
1585 | return; |
1586 | } |
1587 | if (source != QOpenGLDebugMessage::ApplicationSource |
1588 | && source != QOpenGLDebugMessage::ThirdPartySource) { |
1589 | qWarning(msg: "QOpenGLDebugLogger::pushGroup(): using a source different from ApplicationSource\n" |
1590 | " or ThirdPartySource is not supported by GL_KHR_debug. The group will not be pushed." ); |
1591 | return; |
1592 | } |
1593 | |
1594 | QByteArray rawName = name.toUtf8(); |
1595 | rawName.append(c: '\0'); |
1596 | if (rawName.length() > d->maxMessageLength) { |
1597 | qWarning(msg: "QOpenGLDebugLogger::pushGroup(): group name too long, truncating it\n" |
1598 | " (%d bytes long, but the GL accepts up to %d bytes)" , rawName.length(), d->maxMessageLength); |
1599 | rawName.resize(size: d->maxMessageLength - 1); |
1600 | rawName.append(c: '\0'); |
1601 | } |
1602 | |
1603 | // Don't pass rawMessage.length(), as unfortunately bugged |
1604 | // OpenGL drivers will eat the trailing NUL in the name. Just rely |
1605 | // on the name being NUL terminated. |
1606 | d->glPushDebugGroup(qt_messageSourceToGL(source), id, -1, rawName.constData()); |
1607 | } |
1608 | |
1609 | /*! |
1610 | Pops the topmost debug group from the debug groups stack. If the group is |
1611 | successfully popped, OpenGL will automatically log a message with message, |
1612 | id and source matching those of the popped group, type |
1613 | QOpenGLDebugMessage::GroupPopType and severity |
1614 | QOpenGLDebugMessage::NotificationSeverity. |
1615 | |
1616 | Popping a debug group will restore the message filtering settings of the |
1617 | group that becomes the top of the debug groups stack. |
1618 | |
1619 | \note The object must be initialized before managing debug groups. |
1620 | |
1621 | \sa pushGroup() |
1622 | */ |
1623 | void QOpenGLDebugLogger::popGroup() |
1624 | { |
1625 | Q_D(QOpenGLDebugLogger); |
1626 | if (!d->initialized) { |
1627 | qWarning(msg: "QOpenGLDebugLogger::pushGroup(): object must be initialized before popping a debug group" ); |
1628 | return; |
1629 | } |
1630 | |
1631 | d->glPopDebugGroup(); |
1632 | } |
1633 | |
1634 | /*! |
1635 | Enables the logging of messages from the given \a sources, of the given \a |
1636 | types and with the given \a severities and any message id. |
1637 | |
1638 | The logging will be enabled in the current control group. |
1639 | |
1640 | \sa disableMessages(), pushGroup(), popGroup() |
1641 | */ |
1642 | void QOpenGLDebugLogger::enableMessages(QOpenGLDebugMessage::Sources sources, |
1643 | QOpenGLDebugMessage::Types types, |
1644 | QOpenGLDebugMessage::Severities severities) |
1645 | { |
1646 | Q_D(QOpenGLDebugLogger); |
1647 | d->controlDebugMessages(sources, |
1648 | types, |
1649 | severities, |
1650 | ids: QVector<GLuint>(), |
1651 | QByteArrayLiteral("enableMessages" ), |
1652 | enable: true); |
1653 | } |
1654 | |
1655 | /*! |
1656 | Enables the logging of messages with the given \a ids, from the given \a |
1657 | sources and of the given \a types and any severity. |
1658 | |
1659 | The logging will be enabled in the current control group. |
1660 | |
1661 | \sa disableMessages(), pushGroup(), popGroup() |
1662 | */ |
1663 | void QOpenGLDebugLogger::enableMessages(const QVector<GLuint> &ids, |
1664 | QOpenGLDebugMessage::Sources sources, |
1665 | QOpenGLDebugMessage::Types types) |
1666 | { |
1667 | Q_D(QOpenGLDebugLogger); |
1668 | d->controlDebugMessages(sources, |
1669 | types, |
1670 | severities: QOpenGLDebugMessage::AnySeverity, |
1671 | ids, |
1672 | QByteArrayLiteral("enableMessages" ), |
1673 | enable: true); |
1674 | } |
1675 | |
1676 | /*! |
1677 | Disables the logging of messages with the given \a sources, of the given \a |
1678 | types and with the given \a severities and any message id. |
1679 | |
1680 | The logging will be disabled in the current control group. |
1681 | |
1682 | \sa enableMessages(), pushGroup(), popGroup() |
1683 | */ |
1684 | void QOpenGLDebugLogger::disableMessages(QOpenGLDebugMessage::Sources sources, |
1685 | QOpenGLDebugMessage::Types types, |
1686 | QOpenGLDebugMessage::Severities severities) |
1687 | { |
1688 | Q_D(QOpenGLDebugLogger); |
1689 | d->controlDebugMessages(sources, |
1690 | types, |
1691 | severities, |
1692 | ids: QVector<GLuint>(), |
1693 | QByteArrayLiteral("disableMessages" ), |
1694 | enable: false); |
1695 | } |
1696 | |
1697 | /*! |
1698 | Disables the logging of messages with the given \a ids, from the given \a |
1699 | sources and of the given \a types and any severity. |
1700 | |
1701 | The logging will be disabled in the current control group. |
1702 | |
1703 | \sa enableMessages(), pushGroup(), popGroup() |
1704 | */ |
1705 | void QOpenGLDebugLogger::disableMessages(const QVector<GLuint> &ids, |
1706 | QOpenGLDebugMessage::Sources sources, |
1707 | QOpenGLDebugMessage::Types types) |
1708 | { |
1709 | Q_D(QOpenGLDebugLogger); |
1710 | d->controlDebugMessages(sources, |
1711 | types, |
1712 | severities: QOpenGLDebugMessage::AnySeverity, |
1713 | ids, |
1714 | QByteArrayLiteral("disableMessages" ), |
1715 | enable: false); |
1716 | } |
1717 | |
1718 | /*! |
1719 | Reads all the available messages in the OpenGL internal debug log and |
1720 | returns them. Moreover, this function will clear the internal debug log, |
1721 | so that subsequent invocations will not return messages that were |
1722 | already returned. |
1723 | |
1724 | \sa startLogging() |
1725 | */ |
1726 | QList<QOpenGLDebugMessage> QOpenGLDebugLogger::loggedMessages() const |
1727 | { |
1728 | Q_D(const QOpenGLDebugLogger); |
1729 | if (!d->initialized) { |
1730 | qWarning(msg: "QOpenGLDebugLogger::loggedMessages(): object must be initialized before reading logged messages" ); |
1731 | return QList<QOpenGLDebugMessage>(); |
1732 | } |
1733 | |
1734 | static const GLuint maxMessageCount = 128; |
1735 | GLuint messagesRead; |
1736 | GLenum messageSources[maxMessageCount]; |
1737 | GLenum messageTypes[maxMessageCount]; |
1738 | GLuint messageIds[maxMessageCount]; |
1739 | GLenum messageSeverities[maxMessageCount]; |
1740 | GLsizei messageLengths[maxMessageCount]; |
1741 | |
1742 | QByteArray messagesBuffer; |
1743 | messagesBuffer.resize(size: maxMessageCount * d->maxMessageLength); |
1744 | |
1745 | QList<QOpenGLDebugMessage> messages; |
1746 | do { |
1747 | messagesRead = d->glGetDebugMessageLog(maxMessageCount, |
1748 | GLsizei(messagesBuffer.size()), |
1749 | messageSources, |
1750 | messageTypes, |
1751 | messageIds, |
1752 | messageSeverities, |
1753 | messageLengths, |
1754 | messagesBuffer.data()); |
1755 | |
1756 | const char *messagesBufferPtr = messagesBuffer.constData(); |
1757 | for (GLuint i = 0; i < messagesRead; ++i) { |
1758 | QOpenGLDebugMessage message; |
1759 | |
1760 | QOpenGLDebugMessagePrivate *messagePrivate = message.d.data(); |
1761 | messagePrivate->source = qt_messageSourceFromGL(source: messageSources[i]); |
1762 | messagePrivate->type = qt_messageTypeFromGL(type: messageTypes[i]); |
1763 | messagePrivate->id = messageIds[i]; |
1764 | messagePrivate->severity = qt_messageSeverityFromGL(severity: messageSeverities[i]); |
1765 | messagePrivate->message = QString::fromUtf8(str: messagesBufferPtr, size: messageLengths[i] - 1); |
1766 | |
1767 | messagesBufferPtr += messageLengths[i]; |
1768 | messages << message; |
1769 | } |
1770 | } while (messagesRead == maxMessageCount); |
1771 | |
1772 | return messages; |
1773 | } |
1774 | |
1775 | /*! |
1776 | \fn void QOpenGLDebugLogger::messageLogged(const QOpenGLDebugMessage &debugMessage) |
1777 | |
1778 | This signal is emitted when a debug message (wrapped by the \a debugMessage |
1779 | argument) is logged from the OpenGL server. |
1780 | |
1781 | Depending on the OpenGL implementation, this signal can be emitted |
1782 | from other threads than the one(s) the receiver(s) lives in, and even |
1783 | different from the thread the QOpenGLContext in which this object has |
1784 | been initialized lives in. Moreover, the signal could be emitted from |
1785 | multiple threads at the same time. This is normally not a problem, |
1786 | as Qt will utilize a queued connection for cross-thread signal emissions, |
1787 | but if you force the connection type to Direct then you must be aware of |
1788 | the potential races in the slots connected to this signal. |
1789 | |
1790 | If logging have been started in SynchronousLogging mode, OpenGL guarantees |
1791 | that this signal will be emitted from the same thread the QOpenGLContext |
1792 | has been bound to, and no concurrent invocations will ever happen. |
1793 | |
1794 | \note Logging must have been started, or this signal will not be emitted. |
1795 | |
1796 | \sa startLogging() |
1797 | */ |
1798 | |
1799 | /*! |
1800 | Returns the maximum supported length, in bytes, for the text of the messages |
1801 | passed to logMessage(). This is also the maximum length of a debug group |
1802 | name, as pushing or popping groups will automatically log a message with |
1803 | the debug group name as the message text. |
1804 | |
1805 | If a message text is too long, it will be automatically truncated by |
1806 | QOpenGLDebugLogger. |
1807 | |
1808 | \note Message texts are encoded in UTF-8 when they get passed to OpenGL, so |
1809 | their size in bytes does not usually match the amount of UTF-16 code units, |
1810 | as returned, for instance, by QString::length(). (It does if the message contains |
1811 | 7-bit ASCII only data, which is typical for debug messages.) |
1812 | */ |
1813 | qint64 QOpenGLDebugLogger::maximumMessageLength() const |
1814 | { |
1815 | Q_D(const QOpenGLDebugLogger); |
1816 | if (!d->initialized) { |
1817 | qWarning(msg: "QOpenGLDebugLogger::maximumMessageLength(): object must be initialized before reading the maximum message length" ); |
1818 | return -1; |
1819 | } |
1820 | return d->maxMessageLength; |
1821 | } |
1822 | |
1823 | |
1824 | QT_END_NAMESPACE |
1825 | |
1826 | #include "moc_qopengldebug.cpp" |
1827 | |