QtBase  v6.3.1
qopengldebug.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtOpenGL module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QtCore/private/qobject_p.h>
41 #include <QtCore/qglobal.h>
42 #include <QtCore/qvarlengtharray.h>
43 #include <QtGui/qopengl.h>
44 #include <QtGui/qopenglfunctions.h>
45 #include <QtGui/qoffscreensurface.h>
46 
47 #include "qopengldebug.h"
48 
50 
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 
560 
561 #endif /* USE_MANUAL_DEFS */
562 
563 
567 static QOpenGLDebugMessage::Source qt_messageSourceFromGL(GLenum source)
568 {
569  switch (source) {
570  case GL_DEBUG_SOURCE_API:
582  }
583 
584  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source from GL");
586 }
587 
591 static GLenum qt_messageSourceToGL(QOpenGLDebugMessage::Source source)
592 {
593  switch (source) {
595  break;
597  return GL_DEBUG_SOURCE_API;
607  return GL_DEBUG_SOURCE_OTHER;
609  break;
610  }
611 
612  Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message source");
613  return GL_DEBUG_SOURCE_OTHER;
614 }
615 
619 static QString qt_messageSourceToString(QOpenGLDebugMessage::Source source)
620 {
621  switch (source) {
623  return QStringLiteral("InvalidSource");
625  return QStringLiteral("APISource");
627  return QStringLiteral("WindowSystemSource");
629  return QStringLiteral("ShaderCompilerSource");
631  return QStringLiteral("ThirdPartySource");
633  return QStringLiteral("ApplicationSource");
635  return QStringLiteral("OtherSource");
637  return QStringLiteral("AnySource");
638  }
639 
640  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message source");
641  return QString();
642 }
643 
647 static QOpenGLDebugMessage::Type qt_messageTypeFromGL(GLenum type)
648 {
649  switch (type) {
650  case GL_DEBUG_TYPE_ERROR:
660  case GL_DEBUG_TYPE_OTHER:
668  }
669 
670  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type from GL");
672 }
673 
677 static GLenum qt_messageTypeToGL(QOpenGLDebugMessage::Type type)
678 {
679  switch (type) {
681  break;
683  return GL_DEBUG_TYPE_ERROR;
693  return GL_DEBUG_TYPE_OTHER;
695  return GL_DEBUG_TYPE_MARKER;
701  break;
702  }
703 
704  Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message type");
705  return GL_DEBUG_TYPE_OTHER;
706 }
707 
711 static QString qt_messageTypeToString(QOpenGLDebugMessage::Type type)
712 {
713  switch (type) {
715  return QStringLiteral("InvalidType");
717  return QStringLiteral("ErrorType");
719  return QStringLiteral("DeprecatedBehaviorType");
721  return QStringLiteral("UndefinedBehaviorType");
723  return QStringLiteral("PortabilityType");
725  return QStringLiteral("PerformanceType");
727  return QStringLiteral("OtherType");
729  return QStringLiteral("MarkerType");
731  return QStringLiteral("GroupPushType");
733  return QStringLiteral("GroupPopType");
735  return QStringLiteral("AnyType");
736  }
737 
738  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message type");
739  return QString();
740 }
741 
745 static QOpenGLDebugMessage::Severity qt_messageSeverityFromGL(GLenum severity)
746 {
747  switch (severity) {
756  }
757 
758  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity from GL");
760 }
761 
765 static GLenum qt_messageSeverityToGL(QOpenGLDebugMessage::Severity severity)
766 {
767  switch (severity) {
769  break;
771  return GL_DEBUG_SEVERITY_HIGH;
775  return GL_DEBUG_SEVERITY_LOW;
779  break;
780  }
781 
782  Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid message severity");
784 }
785 
789 static QString qt_messageSeverityToString(QOpenGLDebugMessage::Severity severity)
790 {
791  switch (severity) {
793  return QStringLiteral("InvalidSeverity");
795  return QStringLiteral("HighSeverity");
797  return QStringLiteral("MediumSeverity");
799  return QStringLiteral("LowSeverity");
801  return QStringLiteral("NotificationSeverity");
803  return QStringLiteral("AnySeverity");
804  }
805 
806  Q_ASSERT_X(false, Q_FUNC_INFO, "Unknown message severity");
807  return QString();
808 }
809 
811 {
812 public:
814 
820 };
821 
826  : message(),
827  id(0),
828  source(QOpenGLDebugMessage::InvalidSource),
829  type(QOpenGLDebugMessage::InvalidType),
830  severity(QOpenGLDebugMessage::InvalidSeverity)
831 {
832 }
833 
834 
848 {
849 }
850 
857  : d(debugMessage.d)
858 {
859 }
860 
865 {
866 }
867 
873 {
874  d = debugMessage.d;
875  return *this;
876 }
877 
895 {
896  return d->source;
897 }
898 
903 {
904  return d->type;
905 }
906 
911 {
912  return d->severity;
913 }
914 
919 {
920  return d->id;
921 }
922 
927 {
928  return d->message;
929 }
930 
939  GLuint id,
942 {
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 
960  GLuint id,
963 {
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 
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 
999 #ifndef QT_NO_DEBUG_STREAM
1007 {
1008  QDebugStateSaver saver(debug);
1009  debug.nospace() << "QOpenGLDebugMessage::Source("
1010  << qt_messageSourceToString(source)
1011  << ')';
1012  return debug;
1013 }
1014 
1022 {
1023  QDebugStateSaver saver(debug);
1024  debug.nospace() << "QOpenGLDebugMessage::Type("
1025  << qt_messageTypeToString(type)
1026  << ')';
1027  return debug;
1028 }
1029 
1037 {
1038  QDebugStateSaver saver(debug);
1039  debug.nospace() << "QOpenGLDebugMessage::Severity("
1040  << qt_messageSeverityToString(severity)
1041  << ')';
1042  return debug;
1043 }
1044 
1052 {
1053  QDebugStateSaver saver(debug);
1054  debug.nospace() << "QOpenGLDebugMessage("
1055  << qt_messageSourceToString(message.source()) << ", "
1056  << message.id() << ", "
1057  << message.message() << ", "
1058  << qt_messageSeverityToString(message.severity()) << ", "
1059  << qt_messageTypeToString(message.type()) << ')';
1060  return debug;
1061 
1062 }
1063 #endif // QT_NO_DEBUG_STREAM
1064 
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);
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 
1074 {
1075  Q_DECLARE_PUBLIC(QOpenGLDebugLogger)
1076 public:
1078 
1079  void handleMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *rawMessage);
1080  void controlDebugMessages(QOpenGLDebugMessage::Sources sources,
1081  QOpenGLDebugMessage::Types types,
1082  QOpenGLDebugMessage::Severities severities, const QList<GLuint> &ids,
1083  const QByteArray &callerName, bool enable);
1085 
1086  qt_glDebugMessageControl_t glDebugMessageControl;
1087  qt_glDebugMessageInsert_t glDebugMessageInsert;
1088  qt_glDebugMessageCallback_t glDebugMessageCallback;
1089  qt_glGetDebugMessageLog_t glGetDebugMessageLog;
1090  qt_glPushDebugGroup_t glPushDebugGroup;
1091  qt_glPopDebugGroup_t glPopDebugGroup;
1092  qt_glGetPointerv_t glGetPointerv;
1093 
1099  bool initialized : 1;
1100  bool isLogging : 1;
1103 };
1104 
1109  : glDebugMessageControl(nullptr),
1110  glDebugMessageInsert(nullptr),
1111  glDebugMessageCallback(nullptr),
1112  glGetDebugMessageLog(nullptr),
1113  glPushDebugGroup(nullptr),
1114  glPopDebugGroup(nullptr),
1115  oldDebugCallbackFunction(nullptr),
1116  context(nullptr),
1117  maxMessageLength(0),
1118  loggingMode(QOpenGLDebugLogger::AsynchronousLogging),
1119  initialized(false),
1120  isLogging(false),
1121  debugWasEnabled(false),
1122  syncDebugWasEnabled(false)
1123 {
1124 }
1125 
1130  GLenum type,
1131  GLuint id,
1132  GLenum severity,
1133  GLsizei length,
1134  const GLchar *rawMessage)
1135 {
1138 
1140 
1141  QOpenGLDebugMessagePrivate *messagePrivate = message.d.data();
1142  messagePrivate->source = qt_messageSourceFromGL(source);
1143  messagePrivate->type = qt_messageTypeFromGL(type);
1144  messagePrivate->id = id;
1145  messagePrivate->severity = qt_messageSeverityFromGL(severity);
1146  // not passing the length to fromUtf8, as some bugged OpenGL drivers
1147  // do not handle the length correctly. Just rely on the message to be NUL terminated.
1148  messagePrivate->message = QString::fromUtf8(rawMessage);
1149 
1150  Q_Q(QOpenGLDebugLogger);
1151  emit q->messageLogged(message);
1152 }
1153 
1158  QOpenGLDebugMessage::Types types,
1159  QOpenGLDebugMessage::Severities severities,
1160  const QList<GLuint> &ids,
1161  const QByteArray &callerName, bool enable)
1162 {
1163  if (!initialized) {
1164  qWarning("QOpenGLDebugLogger::%s(): object must be initialized before enabling/disabling messages", callerName.constData());
1165  return;
1166  }
1168  qWarning("QOpenGLDebugLogger::%s(): invalid source specified", callerName.constData());
1169  return;
1170  }
1172  qWarning("QOpenGLDebugLogger::%s(): invalid type specified", callerName.constData());
1173  return;
1174  }
1176  qWarning("QOpenGLDebugLogger::%s(): invalid severity specified", callerName.constData());
1177  return;
1178  }
1179 
1180  QVarLengthArray<GLenum, 8> glSources;
1182  QVarLengthArray<GLenum, 8> glSeverities;
1183 
1184  if (ids.count() > 0) {
1186 
1187  // The GL_KHR_debug extension says:
1188  //
1189  // - If <count> is greater than zero, then <ids> is an array of <count>
1190  // message IDs for the specified combination of <source> and <type>. In
1191  // this case, if <source> or <type> is DONT_CARE, or <severity> is not
1192  // DONT_CARE, the error INVALID_OPERATION is generated. If <count> is
1193  // zero, the value if <ids> is ignored.
1194  //
1195  // This means we can't convert AnySource or AnyType into DONT_CARE, but we have to roll
1196  // them into individual sources/types.
1197 
1200  for (uint i = 1; i <= QOpenGLDebugMessage::LastSource; i = i << 1)
1202  }
1203 
1206  for (uint i = 1; i <= QOpenGLDebugMessage::LastType; i = i << 1)
1208  }
1209  }
1210 
1211 #define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target) \
1212  if (source == QOpenGLDebugMessage::Any ## type) { \
1213  target << GL_DONT_CARE; \
1214  } else { \
1215  for (uint i = 1; i <= QOpenGLDebugMessage::Last ## type; i = i << 1) \
1216  if (source.testFlag(QOpenGLDebugMessage:: type (i))) \
1217  target << qt_message ## type ## ToGL (QOpenGLDebugMessage:: type (i)); \
1218  }
1219 
1223 #undef CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS
1224 
1225  const GLsizei idCount = ids.count();
1226  // The GL_KHR_debug extension says that if idCount is 0, idPtr must be ignored.
1227  // Unfortunately, some bugged drivers do NOT ignore it, so pass NULL in case.
1228  const GLuint * const idPtr = idCount ? ids.constData() : nullptr;
1229 
1230  for (GLenum source : glSources)
1231  for (GLenum type : glTypes)
1232  for (GLenum severity : glSeverities)
1234 }
1235 
1240 {
1241  Q_ASSERT(context);
1242 
1243  // Re-make our context current somehow, otherwise stopLogging will fail.
1244 
1245  // Save the current context and its surface in case we need to set them back
1246  QOpenGLContext *currentContext = QOpenGLContext::currentContext();
1247  QSurface *currentSurface = nullptr;
1248 
1249  QScopedPointer<QOffscreenSurface> offscreenSurface;
1250 
1251  if (context != currentContext) {
1252  // Make our old context current on a temporary surface
1253  if (currentContext)
1254  currentSurface = currentContext->surface();
1255 
1256  offscreenSurface.reset(new QOffscreenSurface);
1257  offscreenSurface->setFormat(context->format());
1258  offscreenSurface->create();
1259  if (!context->makeCurrent(offscreenSurface.data()))
1260  qWarning("QOpenGLDebugLoggerPrivate::_q_contextAboutToBeDestroyed(): could not make the owning GL context current for cleanup");
1261  }
1262 
1263  Q_Q(QOpenGLDebugLogger);
1264  q->stopLogging();
1265 
1266  if (offscreenSurface) {
1267  // We did change the current context: set it back
1268  if (currentContext)
1269  currentContext->makeCurrent(currentSurface);
1270  else
1271  context->doneCurrent();
1272  }
1273 
1275  context = nullptr;
1276  initialized = false;
1277 }
1278 
1279 extern "C" {
1280 static void QOPENGLF_APIENTRY qt_opengl_debug_callback(GLenum source,
1281  GLenum type,
1282  GLuint id,
1283  GLenum severity,
1284  GLsizei length,
1285  const GLchar *rawMessage,
1286  const GLvoid *userParam)
1287 {
1288  QOpenGLDebugLoggerPrivate *loggerPrivate = static_cast<QOpenGLDebugLoggerPrivate *>(const_cast<GLvoid *>(userParam));
1289  loggerPrivate->handleMessage(source, type, id, severity, length, rawMessage);
1290 }
1291 }
1292 
1302 {
1303  // QOpenGLDebugMessage is going to be mostly used as an argument
1304  // of a cross thread connection, therefore let's ease the life for the users
1305  // and register the type for them.
1306  qRegisterMetaType<QOpenGLDebugMessage>();
1307 }
1308 
1313 {
1314  stopLogging();
1315 }
1316 
1333 {
1335  if (!context) {
1336  qWarning("QOpenGLDebugLogger::initialize(): no current OpenGL context found.");
1337  return false;
1338  }
1339 
1340  Q_D(QOpenGLDebugLogger);
1341  if (d->context == context) {
1342  // context is non-NULL, d->context is non NULL only on successful initialization.
1343  Q_ASSERT(d->initialized);
1344  return true;
1345  }
1346 
1347  if (d->isLogging) {
1348  qWarning("QOpenGLDebugLogger::initialize(): cannot initialize the object while logging. Please stop the logging first.");
1349  return false;
1350  }
1351 
1352  if (d->context)
1353  disconnect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed()));
1354 
1355  d->initialized = false;
1356  d->context = nullptr;
1357 
1358  if (!context->hasExtension(QByteArrayLiteral("GL_KHR_debug")))
1359  return false;
1360 
1361  d->context = context;
1362  connect(d->context, SIGNAL(aboutToBeDestroyed()), this, SLOT(_q_contextAboutToBeDestroyed()));
1363 
1364 #define GET_DEBUG_PROC_ADDRESS(procName) \
1365  d->procName = reinterpret_cast< qt_ ## procName ## _t >( \
1366  d->context->getProcAddress(d->context->isOpenGLES() ? (#procName "KHR") : (#procName)) \
1367  );
1368 
1369  GET_DEBUG_PROC_ADDRESS(glDebugMessageControl);
1370  GET_DEBUG_PROC_ADDRESS(glDebugMessageInsert);
1371  GET_DEBUG_PROC_ADDRESS(glDebugMessageCallback);
1372  GET_DEBUG_PROC_ADDRESS(glGetDebugMessageLog);
1373  GET_DEBUG_PROC_ADDRESS(glPushDebugGroup);
1374  GET_DEBUG_PROC_ADDRESS(glPopDebugGroup);
1375  GET_DEBUG_PROC_ADDRESS(glGetPointerv)
1376 
1377 #undef GET_DEBUG_PROC_ADDRESS
1378 
1380 
1381 #ifndef QT_NO_DEBUG
1382  if (!d->context->format().testOption(QSurfaceFormat::DebugContext)) {
1383  qWarning("QOpenGLDebugLogger::initialize(): the current context is not a debug context:\n"
1384  " this means that the GL may not generate any debug output at all.\n"
1385  " To avoid this warning, try creating the context with the\n"
1386  " QSurfaceFormat::DebugContext surface format option.");
1387  }
1388 #endif // QT_NO_DEBUG
1389 
1390  d->initialized = true;
1391  return true;
1392 }
1393 
1400 {
1401  Q_D(const QOpenGLDebugLogger);
1402  return d->isLogging;
1403 }
1404 
1428 {
1429  Q_D(QOpenGLDebugLogger);
1430  if (!d->initialized) {
1431  qWarning("QOpenGLDebugLogger::startLogging(): object must be initialized before logging can start");
1432  return;
1433  }
1434  if (d->isLogging) {
1435  qWarning("QOpenGLDebugLogger::startLogging(): this object is already logging");
1436  return;
1437  }
1438 
1439  d->isLogging = true;
1440  d->loggingMode = loggingMode;
1441 
1442  d->glGetPointerv(GL_DEBUG_CALLBACK_FUNCTION, reinterpret_cast<void **>(&d->oldDebugCallbackFunction));
1443  d->glGetPointerv(GL_DEBUG_CALLBACK_USER_PARAM, &d->oldDebugCallbackParameter);
1444 
1445  d->glDebugMessageCallback(&qt_opengl_debug_callback, d);
1446 
1448  d->debugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT);
1449  d->syncDebugWasEnabled = funcs->glIsEnabled(GL_DEBUG_OUTPUT_SYNCHRONOUS);
1450 
1451  if (d->loggingMode == SynchronousLogging)
1453  else
1455 
1456  funcs->glEnable(GL_DEBUG_OUTPUT);
1457 }
1458 
1465 {
1466  Q_D(const QOpenGLDebugLogger);
1467  return d->loggingMode;
1468 }
1469 
1476 {
1477  Q_D(QOpenGLDebugLogger);
1478  if (!d->isLogging)
1479  return;
1480 
1481  QOpenGLContext *currentContext = QOpenGLContext::currentContext();
1482  if (!currentContext || currentContext != d->context) {
1483  qWarning("QOpenGLDebugLogger::stopLogging(): attempting to stop logging with the wrong OpenGL context current");
1484  return;
1485  }
1486 
1487  d->isLogging = false;
1488 
1489  d->glDebugMessageCallback(d->oldDebugCallbackFunction, d->oldDebugCallbackParameter);
1490 
1492  if (!d->debugWasEnabled)
1493  funcs->glDisable(GL_DEBUG_OUTPUT);
1494 
1495  if (d->syncDebugWasEnabled)
1497  else
1499 }
1500 
1515 {
1516  Q_D(QOpenGLDebugLogger);
1517  if (!d->initialized) {
1518  qWarning("QOpenGLDebugLogger::logMessage(): object must be initialized before logging messages");
1519  return;
1520  }
1521  if (debugMessage.source() != QOpenGLDebugMessage::ApplicationSource
1522  && debugMessage.source() != QOpenGLDebugMessage::ThirdPartySource) {
1523  qWarning("QOpenGLDebugLogger::logMessage(): using a message source different from ApplicationSource\n"
1524  " or ThirdPartySource is not supported by GL_KHR_debug. The message will not be logged.");
1525  return;
1526  }
1527  if (debugMessage.type() == QOpenGLDebugMessage::InvalidType
1528  || debugMessage.type() == QOpenGLDebugMessage::AnyType
1529  || debugMessage.severity() == QOpenGLDebugMessage::InvalidSeverity
1530  || debugMessage.severity() == QOpenGLDebugMessage::AnySeverity) {
1531  qWarning("QOpenGLDebugLogger::logMessage(): the message has a non-valid type and/or severity. The message will not be logged.");
1532  return;
1533  }
1534 
1535  const GLenum source = qt_messageSourceToGL(debugMessage.source());
1536  const GLenum type = qt_messageTypeToGL(debugMessage.type());
1537  const GLenum severity = qt_messageSeverityToGL(debugMessage.severity());
1538  QByteArray rawMessage = debugMessage.message().toUtf8();
1539  rawMessage.append('\0');
1540 
1541  if (rawMessage.length() > d->maxMessageLength) {
1542  qWarning("QOpenGLDebugLogger::logMessage(): message too long, truncating it\n"
1543  " (%d bytes long, but the GL accepts up to %d bytes)", int(rawMessage.length()), d->maxMessageLength);
1544  rawMessage.resize(d->maxMessageLength - 1);
1545  rawMessage.append('\0');
1546  }
1547 
1548  // Don't pass rawMessage.length(), as unfortunately bugged
1549  // OpenGL drivers will eat the trailing NUL in the message. Just rely
1550  // on the message being NUL terminated.
1551  d->glDebugMessageInsert(source,
1552  type,
1553  debugMessage.id(),
1554  severity,
1555  -1,
1556  rawMessage.constData());
1557 }
1558 
1578 {
1579  Q_D(QOpenGLDebugLogger);
1580  if (!d->initialized) {
1581  qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before pushing a debug group");
1582  return;
1583  }
1586  qWarning("QOpenGLDebugLogger::pushGroup(): using a source different from ApplicationSource\n"
1587  " or ThirdPartySource is not supported by GL_KHR_debug. The group will not be pushed.");
1588  return;
1589  }
1590 
1591  QByteArray rawName = name.toUtf8();
1592  rawName.append('\0');
1593  if (rawName.length() > d->maxMessageLength) {
1594  qWarning("QOpenGLDebugLogger::pushGroup(): group name too long, truncating it\n"
1595  " (%d bytes long, but the GL accepts up to %d bytes)", int(rawName.length()), d->maxMessageLength);
1596  rawName.resize(d->maxMessageLength - 1);
1597  rawName.append('\0');
1598  }
1599 
1600  // Don't pass rawMessage.length(), as unfortunately bugged
1601  // OpenGL drivers will eat the trailing NUL in the name. Just rely
1602  // on the name being NUL terminated.
1603  d->glPushDebugGroup(qt_messageSourceToGL(source), id, -1, rawName.constData());
1604 }
1605 
1621 {
1622  Q_D(QOpenGLDebugLogger);
1623  if (!d->initialized) {
1624  qWarning("QOpenGLDebugLogger::pushGroup(): object must be initialized before popping a debug group");
1625  return;
1626  }
1627 
1628  d->glPopDebugGroup();
1629 }
1630 
1639 void QOpenGLDebugLogger::enableMessages(QOpenGLDebugMessage::Sources sources,
1640  QOpenGLDebugMessage::Types types,
1641  QOpenGLDebugMessage::Severities severities)
1642 {
1643  Q_D(QOpenGLDebugLogger);
1644  d->controlDebugMessages(sources, types, severities, QList<GLuint>(),
1645  QByteArrayLiteral("enableMessages"), true);
1646 }
1647 
1657  QOpenGLDebugMessage::Sources sources,
1658  QOpenGLDebugMessage::Types types)
1659 {
1660  Q_D(QOpenGLDebugLogger);
1661  d->controlDebugMessages(sources,
1662  types,
1664  ids,
1665  QByteArrayLiteral("enableMessages"),
1666  true);
1667 }
1668 
1677 void QOpenGLDebugLogger::disableMessages(QOpenGLDebugMessage::Sources sources,
1678  QOpenGLDebugMessage::Types types,
1679  QOpenGLDebugMessage::Severities severities)
1680 {
1681  Q_D(QOpenGLDebugLogger);
1682  d->controlDebugMessages(sources, types, severities, QList<GLuint>(),
1683  QByteArrayLiteral("disableMessages"), false);
1684 }
1685 
1695  QOpenGLDebugMessage::Sources sources,
1696  QOpenGLDebugMessage::Types types)
1697 {
1698  Q_D(QOpenGLDebugLogger);
1699  d->controlDebugMessages(sources,
1700  types,
1702  ids,
1703  QByteArrayLiteral("disableMessages"),
1704  false);
1705 }
1706 
1716 {
1717  Q_D(const QOpenGLDebugLogger);
1718  if (!d->initialized) {
1719  qWarning("QOpenGLDebugLogger::loggedMessages(): object must be initialized before reading logged messages");
1720  return QList<QOpenGLDebugMessage>();
1721  }
1722 
1723  static const GLuint maxMessageCount = 128;
1724  GLuint messagesRead;
1725  GLenum messageSources[maxMessageCount];
1726  GLenum messageTypes[maxMessageCount];
1727  GLuint messageIds[maxMessageCount];
1728  GLenum messageSeverities[maxMessageCount];
1729  GLsizei messageLengths[maxMessageCount];
1730 
1731  QByteArray messagesBuffer;
1732  messagesBuffer.resize(maxMessageCount * d->maxMessageLength);
1733 
1735  do {
1736  messagesRead = d->glGetDebugMessageLog(maxMessageCount,
1737  GLsizei(messagesBuffer.size()),
1738  messageSources,
1739  messageTypes,
1740  messageIds,
1741  messageSeverities,
1742  messageLengths,
1743  messagesBuffer.data());
1744 
1745  const char *messagesBufferPtr = messagesBuffer.constData();
1746  for (GLuint i = 0; i < messagesRead; ++i) {
1748 
1749  QOpenGLDebugMessagePrivate *messagePrivate = message.d.data();
1750  messagePrivate->source = qt_messageSourceFromGL(messageSources[i]);
1751  messagePrivate->type = qt_messageTypeFromGL(messageTypes[i]);
1752  messagePrivate->id = messageIds[i];
1753  messagePrivate->severity = qt_messageSeverityFromGL(messageSeverities[i]);
1754  messagePrivate->message = QString::fromUtf8(messagesBufferPtr, messageLengths[i] - 1);
1755 
1756  messagesBufferPtr += messageLengths[i];
1757  messages << message;
1758  }
1759  } while (messagesRead == maxMessageCount);
1760 
1761  return messages;
1762 }
1763 
1803 {
1804  Q_D(const QOpenGLDebugLogger);
1805  if (!d->initialized) {
1806  qWarning("QOpenGLDebugLogger::maximumMessageLength(): object must be initialized before reading the maximum message length");
1807  return -1;
1808  }
1809  return d->maxMessageLength;
1810 }
1811 
1812 
1814 
1815 #include "moc_qopengldebug.cpp"
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
char * data()
Definition: qbytearray.h:516
qsizetype size() const noexcept
Definition: qbytearray.h:470
const char * constData() const noexcept
Definition: qbytearray.h:144
qsizetype length() const noexcept
Definition: qbytearray.h:472
void resize(qsizetype size)
QByteArray & append(char c)
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
Convenience class for custom QDebug operators.
Definition: qdebug.h:176
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
Definition: qobject.cpp:2772
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
Definition: qobject.cpp:3048
The QOffscreenSurface class represents an offscreen surface in the underlying platform.
void setFormat(const QSurfaceFormat &format)
The QOpenGLContext class represents a native OpenGL context, enabling OpenGL rendering on a QSurface.
bool makeCurrent(QSurface *surface)
QSurfaceFormat format() const
static QOpenGLContext * currentContext()
QOpenGLFunctions * functions() const
QSurface * surface() const
The QOpenGLDebugLogger enables logging of OpenGL debugging messages. \inmodule QtOpenGL.
Definition: qopengldebug.h:156
LoggingMode loggingMode
the logging mode passed to startLogging().
Definition: qopengldebug.h:158
qint64 maximumMessageLength() const
bool isLogging() const
QOpenGLDebugLogger(QObject *parent=nullptr)
void disableMessages(QOpenGLDebugMessage::Sources sources=QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types=QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::Severities severities=QOpenGLDebugMessage::AnySeverity)
void logMessage(const QOpenGLDebugMessage &debugMessage)
void pushGroup(const QString &name, GLuint id=0, QOpenGLDebugMessage::Source source=QOpenGLDebugMessage::ApplicationSource)
void enableMessages(QOpenGLDebugMessage::Sources sources=QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types=QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::Severities severities=QOpenGLDebugMessage::AnySeverity)
void startLogging(LoggingMode loggingMode=AsynchronousLogging)
QList< QOpenGLDebugMessage > loggedMessages() const
qt_glDebugMessageCallback_t glDebugMessageCallback
qt_glPushDebugGroup_t glPushDebugGroup
void controlDebugMessages(QOpenGLDebugMessage::Sources sources, QOpenGLDebugMessage::Types types, QOpenGLDebugMessage::Severities severities, const QList< GLuint > &ids, const QByteArray &callerName, bool enable)
qt_glDebugMessageInsert_t glDebugMessageInsert
GLDEBUGPROC oldDebugCallbackFunction
qt_glGetPointerv_t glGetPointerv
QOpenGLDebugLogger::LoggingMode loggingMode
qt_glDebugMessageControl_t glDebugMessageControl
qt_glGetDebugMessageLog_t glGetDebugMessageLog
void handleMessage(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *rawMessage)
qt_glPopDebugGroup_t glPopDebugGroup
The QOpenGLDebugMessage class wraps an OpenGL debug message. \inmodule QtOpenGL \reentrant.
Definition: qopengldebug.h:66
QOpenGLDebugMessage & operator=(const QOpenGLDebugMessage &debugMessage)
static QOpenGLDebugMessage createThirdPartyMessage(const QString &text, GLuint id=0, Severity severity=NotificationSeverity, Type type=OtherType)
QDebug operator<<(QDebug debug, const QOpenGLDebugMessage &message)
QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Type type)
Source source() const
QString message() const
static QOpenGLDebugMessage createApplicationMessage(const QString &text, GLuint id=0, Severity severity=NotificationSeverity, Type type=OtherType)
GLuint id() const
Severity severity() const
QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Source source)
bool operator==(const QOpenGLDebugMessage &debugMessage) const
QDebug operator<<(QDebug debug, QOpenGLDebugMessage::Severity severity)
QOpenGLDebugMessage::Source source
QOpenGLDebugMessage::Type type
QOpenGLDebugMessage::Severity severity
The QOpenGLFunctions class provides cross-platform access to the OpenGL ES 2.0 API.
GLboolean glIsEnabled(GLenum cap)
void glDisable(GLenum cap)
void glEnable(GLenum cap)
void glGetIntegerv(GLenum pname, GLint *params)
T * data() const noexcept
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
The QSharedData class is a base class for shared data objects. \reentrant.
Definition: qshareddata.h:55
The QString class provides a Unicode character string.
Definition: qstring.h:388
static QString fromUtf8(QByteArrayView utf8)
Definition: qstring.cpp:5632
QByteArray toUtf8() const &
Definition: qstring.h:749
The QSurface class is an abstraction of renderable surfaces in Qt.
Definition: qsurface.h:57
QString text
[meta data]
#define QString()
Definition: parse-defines.h:51
#define QByteArrayLiteral(str)
Definition: qbytearray.h:80
#define Q_FUNC_INFO
unsigned int uint
Definition: qglobal.h:334
long long qint64
Definition: qglobal.h:298
@ text
#define qWarning
Definition: qlogging.h:179
#define SLOT(a)
Definition: qobjectdefs.h:87
#define SIGNAL(a)
Definition: qobjectdefs.h:88
#define QOPENGLF_APIENTRYP
Definition: qopengl.h:301
GLenum GLuint GLenum GLsizei length
Definition: qopengl.h:270
#define QOPENGLF_APIENTRY
Definition: qopengl.h:296
GLenum GLuint id
[6]
Definition: qopengl.h:270
GLenum GLuint GLenum GLsizei const GLchar const GLvoid * userParam
Definition: qopengl.h:270
GLenum type
Definition: qopengl.h:270
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: qopengl.h:270
GLenum GLuint GLenum severity
Definition: qopengl.h:270
char GLchar
Definition: qopengl.h:184
#define GL_DEBUG_TYPE_MARKER
#define GL_DEBUG_SEVERITY_LOW
GLsizei GLenum GLenum GLuint GLenum * severities
GLsizei GLenum GLenum GLuint GLenum GLsizei * lengths
#define GL_DEBUG_SOURCE_THIRD_PARTY
#define GL_DEBUG_TYPE_PORTABILITY
#define GL_DEBUG_TYPE_OTHER
GLsizei GLenum GLenum GLuint GLenum GLsizei GLchar * messageLog
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
#define GL_MAX_DEBUG_MESSAGE_LENGTH
GLsizei GLenum * sources
GLenum GLenum GLsizei const GLuint * ids
#define GL_DEBUG_TYPE_PUSH_GROUP
GLvoid ** params
GLenum GLuint GLenum GLsizei length
#define GL_DEBUG_SOURCE_APPLICATION
const void * userParam
#define GL_DEBUG_SOURCE_OTHER
GLenum GLuint id
GLenum GLenum GLsizei const GLuint GLboolean enabled
#define GL_DEBUG_SEVERITY_MEDIUM
#define GL_DEBUG_SOURCE_SHADER_COMPILER
typedef void(QOPENGLF_APIENTRYP qt_glDebugMessageControl_t)(GLenum source
GLenum type
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
#define GL_DEBUG_SEVERITY_NOTIFICATION
#define GL_DEBUG_TYPE_POP_GROUP
GLenum GLenum GLsizei count
#define GL_DEBUG_SOURCE_WINDOW_SYSTEM
#define GL_DEBUG_SOURCE_API
GLsizei bufsize
void(QOPENGLF_APIENTRY * GLDEBUGPROC)(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const GLvoid *userParam)
#define GL_DEBUG_CALLBACK_USER_PARAM
#define GL_DEBUG_TYPE_ERROR
#define CONVERT_TO_GL_DEBUG_MESSAGE_CONTROL_PARAMETERS(type, source, target)
#define GL_DEBUG_CALLBACK_FUNCTION
GLuint GLsizei const GLchar * message
#define GL_DEBUG_SEVERITY_HIGH
#define GL_DEBUG_OUTPUT_SYNCHRONOUS
#define GET_DEBUG_PROC_ADDRESS(procName)
typedef GLuint(QOPENGLF_APIENTRYP qt_glGetDebugMessageLog_t)(GLuint count
GLenum GLenum severity
GLsizei GLenum GLenum * types
#define GL_DEBUG_TYPE_PERFORMANCE
#define GL_DEBUG_OUTPUT
GLenum GLuint GLenum GLsizei const GLchar * buf
typedef GLint(GL_APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXEXTPROC)(GLuint program
GLsizei GLenum GLenum GLuint GLenum * severities
const GLfloat * m
GLenum GLenum GLsizei const GLuint * ids
GLsizei GLenum GLenum * types
GLenum pname
typedef GLsizei(GL_APIENTRYP PFNGLGETFRAMEBUFFERPIXELLOCALSTORAGESIZEEXTPROC)(GLuint target)
typedef GLenum(GL_APIENTRYP PFNGLGETGRAPHICSRESETSTATUSKHRPROC)(void)
GLboolean enable
typedef GLboolean(GL_APIENTRYP PFNGLISENABLEDIOESPROC)(GLenum target
GLuint name
GLsizei GLenum * sources
GLsizei GLsizei GLchar * source
GLdouble GLdouble GLdouble GLdouble q
Definition: qopenglext.h:259
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define Q_ASSERT_X(cond, x, msg)
Definition: qrandom.cpp:85
#define QStringLiteral(str)
#define emit
Definition: qtmetamacros.h:85
QObject::connect nullptr
Definition: moc.h:48
constexpr const auto messages
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent