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