本页内容

QOpenGLDebugLogger Class

QOpenGLDebugLogger 支持记录 OpenGL 调试消息。更多内容...

头文件: #include <QOpenGLDebugLogger>
CMake: find_package(Qt6 REQUIRED COMPONENTS OpenGL)
target_link_libraries(mytarget PRIVATE Qt6::OpenGL)
qmake: QT += opengl
继承自: QObject

公共类型

enum LoggingMode { AsynchronousLogging, SynchronousLogging }

属性

公共函数

QOpenGLDebugLogger(QObject *parent = nullptr)
virtual ~QOpenGLDebugLogger()
void disableMessages(QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::Severities severities = QOpenGLDebugMessage::AnySeverity)
void disableMessages(const QList<GLuint> &ids, QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType)
void enableMessages(QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::Severities severities = QOpenGLDebugMessage::AnySeverity)
void enableMessages(const QList<GLuint> &ids, QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType)
bool initialize()
bool isLogging() const
QList<QOpenGLDebugMessage> loggedMessages() const
QOpenGLDebugLogger::LoggingMode loggingMode() const
qint64 maximumMessageLength() const
void popGroup()
void pushGroup(const QString &name, GLuint id = 0, QOpenGLDebugMessage::Source source = QOpenGLDebugMessage::ApplicationSource)

公共槽位

void logMessage(const QOpenGLDebugMessage &debugMessage)
void startLogging(QOpenGLDebugLogger::LoggingMode loggingMode = AsynchronousLogging)
void stopLogging()

信号

void messageLogged(const QOpenGLDebugMessage &debugMessage)

详细说明

简介

OpenGL 编程很容易出错。大多数情况下,一次 OpenGL 调用的失败就可能导致应用程序的整个部分停止工作,屏幕上也无法显示任何内容。

确保 OpenGL 实现未返回任何错误的唯一方法,是在每次 API 调用后都通过 `glGetError ` 进行检查。此外,由于 OpenGL 错误会累积,因此应始终像这样在循环中使用 `glGetError`:

GLenum error = GL_NO_ERROR;
do {
    error = glGetError();
    if (error != GL_NO_ERROR) {
        // handle the error
    }
} while (error != GL_NO_ERROR);

如果您尝试清空错误堆栈,请确保不仅要继续执行直到返回 GL_NO_ERROR,还要在遇到 GL_CONTEXT_LOST 时中断程序,因为该错误值会不断重复出现。

此外,作为应用程序开发者,我们还关注许多其他信息,例如性能问题,或是关于使用已弃用 API 的警告。此类消息不会通过普通的 OpenGL 错误报告机制进行报告。

QOpenGLDebugLogger 旨在通过提供对OpenGL 调试日志的访问权限来解决这些问题。如果您的 OpenGL 实现支持该功能(通过暴露GL_KHR_debug 扩展),则来自 OpenGL 服务器的消息要么会被记录到内部 OpenGL 日志中,要么会在生成时“实时”传递给监听器。

QOpenGLDebugLogger 同时支持这两种工作模式。请参阅以下各节,了解它们之间的区别。

创建 OpenGL 调试上下文

出于效率考虑,除非 OpenGL 上下文是调试上下文,否则 OpenGL 实现可以完全不生成任何调试输出。若要在 Qt 中创建调试上下文,必须在用于创建 `QOpenGLContext ` 对象的 `QSurfaceFormat ` 上设置 `QSurfaceFormat::DebugContext ` 格式选项:

QSurfaceFormat format;
// asks for a OpenGL 3.2 debug context using the Core profile
format.setMajorVersion(3);
format.setMinorVersion(2);
format.setProfile(QSurfaceFormat::CoreProfile);
format.setOption(QSurfaceFormat::DebugContext);

QOpenGLContext *context = new QOpenGLContext;
context->setFormat(format);
context->create();

请注意,请求 3.2 OpenGL 核心配置文件仅是为了示例目的;该类并不绑定于任何特定的 OpenGL 或 OpenGL ES 版本,因为它依赖于GL_KHR_debug 扩展的可用性(见下文)。

创建并初始化 QOpenGLDebugLogger

QOpenGLDebugLogger 是一个简单的、从QObject 派生的类。与所有QObject 子类一样,您需要创建一个实例(并可选地指定父对象),并且与 Qt OpenGL 中的其他函数一样,您必须在使用前通过在当前 OpenGL 上下文中调用initialize() 对其进行初始化:

QOpenGLContext *ctx = QOpenGLContext::currentContext();
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this);

logger->initialize(); // initializes in the current context, i.e. ctx

请注意,要访问 OpenGL 记录的消息,上下文中必须支持GL_KHR_debug 扩展。您可以通过调用以下代码来检查该扩展是否存在:

ctx->hasExtension(QByteArrayLiteral("GL_KHR_debug"));

其中ctx 是一个有效的QOpenGLContext 。如果该扩展不可用,initialize() 将返回 false。

读取 OpenGL 内部调试日志

OpenGL 实现会保存一个用于记录调试消息的内部日志。可以通过调用loggedMessages() 函数来检索存储在此日志中的消息:

constQList<QOpenGLDebugMessage>messages= logger->loggedMessages();
for(constQOpenGLDebugMessage&message: messages)
    qDebug() << message;

内部日志的大小是有限的;当其填满时,较旧的消息会被丢弃,以腾出空间接收新到的消息。调用 `loggedMessages()` 时,内部日志也会被清空。

若要确保不丢失任何调试消息,必须使用实时日志记录,而非调用此函数。不过,在上下文创建与实时日志记录激活之间的这段时间内(或者一般而言,当实时日志记录被禁用时),仍可能会生成调试消息。

消息的实时日志记录

还可以接收来自 OpenGL 服务器的调试消息流,这些消息由实现生成。要实现这一点,您需要将一个合适的槽连接到messageLogged() 信号,并通过调用startLogging() 来启动日志记录:

connect(logger, &QOpenGLDebugLogger::messageLogged, receiver, &LogHandler::handleLoggedMessage);
logger->startLogging();

同样,可随时通过调用stopLogging()函数禁用日志记录。

实时日志记录可以是异步的,也可以是同步的,这取决于传递给 `startLogging()` 的参数。 在异步模式下进行日志记录时(这是默认模式,因为其开销非常小),OpenGL 实现可以在任何时候生成消息,且/或其生成顺序可能与导致这些消息被记录的 OpenGL 命令的执行顺序不同。 这些消息还可能由一个与上下文当前绑定的线程不同的线程生成。这是因为 OpenGL 实现通常是高度多线程和异步的,因此无法保证调试消息的相对顺序和时间点。

另一方面,同步模式下的日志记录虽然开销较大,但 OpenGL 实现保证由某个特定命令引发的所有消息都会在该命令返回之前按顺序接收,并且来自与 OpenGL 上下文绑定的同一线程。

这意味着,在同步模式下进行日志记录时,您可以将 OpenGL 应用程序在调试器中运行,在连接到 `messageLogged()` 信号的槽上设置断点,并在回溯中看到引发该日志消息的确切调用。这对调试 OpenGL 问题非常有用。 请注意,如果 OpenGL 渲染在另一个线程中进行,您必须将信号/插槽连接类型强制设置为“Qt::DirectConnection ”,才能看到实际的回溯信息。

有关日志记录模式的更多信息,请参阅LoggingMode 枚举的文档。

注意: 启用实时日志记录后, 调试消息将不再插入到内部 OpenGL 调试日志中;内部日志中已存在的消息既不会被删除,也不会通过messageLogged() 信号输出。 由于某些消息可能在实时日志记录开始之前就已生成(因此会被保留在内部 OpenGL 日志中),因此在调用startLogging() 之后,务必检查该日志中是否包含任何消息。

在调试日志中插入消息

应用程序和库可以将自定义消息插入调试日志中,例如用于标记一组相关的 OpenGL 命令,从而能够识别最终来自这些命令的消息。

要实现这一点,您可以通过调用createApplicationMessage()或createThirdPartyMessage()创建一个QOpenGLDebugMessage 对象,然后通过调用logMessage()将其插入日志:

QOpenGLDebugMessage message =
    QOpenGLDebugMessage::createApplicationMessage(QStringLiteral("Custom message"));

logger->logMessage(message);

请注意,OpenGL 实现中,可插入调试日志的消息长度存在供应商特有的限制。您可以通过调用maximumMessageLength() 方法获取该长度;超过限制的消息将自动被截断。

控制调试输出

QOpenGLDebugMessage 还能够对调试消息应用过滤器,从而限制日志中记录的消息数量。您可以通过分别调用enableMessages() 和disableMessages() 来启用或禁用消息日志记录。默认情况下,所有消息都会被记录。

可以通过以下方式选择消息来启用或禁用它们:

  • 来源、类型和严重性(且选择中包含所有 ID);
  • ID、来源和类型(且选择中包含所有严重性级别)。

请注意,特定消息的“启用”状态是 (id, source, type, severity) 元组的属性;消息属性之间不构成任何形式的层级关系。您应谨慎处理对enableMessages() 和disableMessages() 的调用顺序,因为这会改变哪些消息被启用或禁用。

无法直接根据消息正文进行过滤;应用程序必须自行实现(在连接到messageLogged() 信号的插槽中,或在通过loggedMessages() 从内部调试日志中获取消息后)。

为了简化启用/禁用状态的管理,QOpenGLDebugMessage 还支持debug groups 这一概念。一个调试组包含一组启用/禁用的调试消息配置。 此外,调试组以栈的形式组织:可以通过分别调用pushGroup() 和popGroup() 来压入和弹出组。(创建 OpenGL 上下文时,栈中已存在一个组)。

enableMessages() 和disableMessages() 函数将修改当前调试组的配置,即调试组栈顶的那个组。

当一个新组被压入调试组栈时,它将继承之前位于栈顶的组的配置。反之,弹出一个调试组将恢复成为新栈顶的调试组的配置。

压入(或弹出)调试组时,系统还会自动生成类型为 `QOpenGLDebugMessage::GroupPushType `(或 `GroupPopType`)的调试消息。

另请参阅 QOpenGLDebugMessage 。

成员类型文档

enum QOpenGLDebugLogger::LoggingMode

LoggingMode 枚举定义了日志记录器对象的日志记录模式。

常量值描述
QOpenGLDebugLogger::AsynchronousLogging0来自 OpenGL 服务器的消息以异步方式记录。这意味着,消息可能会在引发它们的相应 OpenGL 操作发生一段时间后才被记录,甚至可能会以乱序的方式接收,这取决于 OpenGL 的实现。 由于 OpenGL 实现本质上具有高度的线程化和异步性,因此此模式对性能的影响非常小。
QOpenGLDebugLogger::SynchronousLogging1来自 OpenGL 服务器的消息以同步且顺序的方式进行日志记录。这会严重影响性能,因为 OpenGL 实现本质上非常异步;但这对于调试 OpenGL 问题非常有用,因为 OpenGL 保证由 OpenGL 命令生成的消息会在相应命令执行返回之前被记录到日志中。 因此,您可以在messageLogged()信号上设置断点,并在回溯中查看是哪个OpenGL命令触发了该信号;唯一需要注意的是,如果您在多个线程中使用OpenGL,在连接到messageLogged()信号时,可能需要强制直接连接。

属性文档

[read-only] loggingMode : LoggingMode

该属性保存传递给 `startLogging()` 的日志记录模式。

请注意,必须已启动日志记录,否则该属性的值将毫无意义。

访问函数:

QOpenGLDebugLogger::LoggingMode loggingMode() const

另请参阅 startLogging() 和isLogging()。

成员函数文档

[explicit] QOpenGLDebugLogger::QOpenGLDebugLogger(QObject *parent = nullptr)

使用给定的parent 构建一个新的日志记录器对象。

注意: 必须先初始化该 对象,才能开始记录日志。

另请参阅 initialize()。

[virtual noexcept] QOpenGLDebugLogger::~QOpenGLDebugLogger()

销毁日志记录器对象。

void QOpenGLDebugLogger::disableMessages(QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::Severities severities = QOpenGLDebugMessage::AnySeverity)

禁用包含指定sources 、指定types 以及指定severities 的消息的日志记录,无论其消息ID为何。

将在当前控制组中禁用日志记录。

另请参阅 enableMessages()、pushGroup() 和popGroup()。

void QOpenGLDebugLogger::disableMessages(const QList<GLuint> &ids, QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType)

禁用对具有指定ids 的消息的日志记录,这些消息来自指定sources ,且具有指定types ,无论其严重性如何。

当前控制组中的日志记录将被禁用。

另请参阅 enableMessages()、pushGroup() 和popGroup()。

void QOpenGLDebugLogger::enableMessages(QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType, QOpenGLDebugMessage::Severities severities = QOpenGLDebugMessage::AnySeverity)

启用对来自指定sources 、指定types 、指定severities 以及任何消息ID的消息的日志记录。

将在当前控制组中启用日志记录。

另请参阅 disableMessages()、pushGroup() 和popGroup()。

void QOpenGLDebugLogger::enableMessages(const QList<GLuint> &ids, QOpenGLDebugMessage::Sources sources = QOpenGLDebugMessage::AnySource, QOpenGLDebugMessage::Types types = QOpenGLDebugMessage::AnyType)

启用对具有指定ids 的消息的日志记录,这些消息来自指定的sources ,并包含指定的types ,且不限严重性级别。

将在当前控制组中启用日志记录。

另请参阅 disableMessages()、pushGroup() 和popGroup()。

bool QOpenGLDebugLogger::initialize()

在当前的 OpenGL 上下文中初始化该对象。要使初始化成功,该上下文必须支持GL_KHR_debug 扩展。在进行任何日志记录之前,必须先初始化该对象。

在同一上下文中多次调用此函数是安全的。

此函数也可用于更改先前已初始化对象的上下文;请注意,在此情况下,调用此函数时该对象不得正在进行日志记录。

如果日志器初始化成功,则返回true ;否则返回false。

另请参阅 QOpenGLContext 。

bool QOpenGLDebugLogger::isLogging() const

如果该对象当前正在记录日志,则返回 `true `;否则返回 `false`。

另请参阅 startLogging()。

[slot] void QOpenGLDebugLogger::logMessage(const QOpenGLDebugMessage &debugMessage)

将消息debugMessage 插入到 OpenGL 调试日志中。这为应用程序或库提供了一种插入自定义消息的方式,有助于简化 OpenGL 应用程序的调试工作。

注意: debugMessage 的来源必须是QOpenGLDebugMessage::ApplicationSource 或QOpenGLDebugMessage::ThirdPartySource ,并且必须具有有效的类型和严重程度,否则不会被插入日志。

注意: 必须先初始化该 对象,才能进行日志记录。

另请参阅 initialize()。

QList<QOpenGLDebugMessage> QOpenGLDebugLogger::loggedMessages() const

读取 OpenGL 内部调试日志中所有可用的消息并返回它们。此外,该函数会清空内部调试日志,以确保后续调用不会返回已经返回过的消息。

另请参阅 startLogging()。

QOpenGLDebugLogger::LoggingMode QOpenGLDebugLogger::loggingMode() const

返回该对象的日志记录模式。

注意: 这是属性 loggingMode 的获取器 函数。

另请参阅 startLogging()。

qint64 QOpenGLDebugLogger::maximumMessageLength() const

返回传递给logMessage() 的消息正文所支持的最大长度(以字节为单位)。这也是调试组名的最大长度,因为在添加或移除组时,系统会自动记录一条消息,其消息正文即为该调试组名。

如果消息文本过长,QOpenGLDebugLogger 会自动对其进行截断。

注意:消息 文本在传递给 OpenGL 时采用 UTF-8 编码,因此其字节大小通常与 UTF-16 码单元数量不一致,例如QString::length() 返回的数值。(如果消息仅包含 7 位 ASCII 数据——这在调试消息中很常见——则两者大小一致。)

[signal] void QOpenGLDebugLogger::messageLogged(const QOpenGLDebugMessage &debugMessage)

当从 OpenGL 服务器记录一条调试消息(由 `debugMessage ` 参数封装)时,会发出此信号。

根据 OpenGL 的实现情况,该信号可能由接收者所在线程以外的其他线程发出,甚至可能与初始化该对象的QOpenGLContext 所在的线程不同。 此外,该信号可能由多个线程同时发出。这通常不会造成问题,因为 Qt 会使用队列连接来处理跨线程信号发送,但如果你将连接类型强制设为 Direct,则必须注意连接到该信号的槽中可能出现的竞争条件。

如果已在SynchronousLogging 模式下启动日志记录,OpenGL保证该信号将从QOpenGLContext 所绑定的同一线程发出,且绝不会发生并发调用。

注意: 必须已启动日志记录 ,否则该信号不会被发出。

另请参阅 startLogging()。

void QOpenGLDebugLogger::popGroup()

从调试组栈中弹出最顶层的调试组。如果成功弹出该组,OpenGL 将自动记录一条日志消息,其中 message、id 和 source 与被弹出的组对应,type 为QOpenGLDebugMessage::GroupPopType ,severity 为QOpenGLDebugMessage::NotificationSeverity 。

弹出一个调试组将恢复该调试组的消息过滤设置,该组将成为调试组栈的顶部。

注意: 在管理调试组之前,必须先初始化该 对象。

另请参阅 pushGroup()。

void QOpenGLDebugLogger::pushGroup(const QString &name, GLuint id = 0, QOpenGLDebugMessage::Source source = QOpenGLDebugMessage::ApplicationSource)

将名称为name 、ID为id 、来源为source 的调试组压入调试组栈。如果该组成功压入,OpenGL将自动记录一条日志,其消息内容为name ,ID为id ,来源为source ,类型为QOpenGLDebugMessage::GroupPushType ,严重性为QOpenGLDebugMessage::NotificationSeverity 。

新压入的组将继承栈顶组的相同过滤设置;也就是说,压入新组不会改变过滤设置。

注意: source 必须为QOpenGLDebugMessage::ApplicationSource 或QOpenGLDebugMessage::ThirdPartySource ,否则该组将不会被压入堆栈。

注意: 在管理调试组之前,必须先初始化该 对象。

另请参阅 popGroup()、enableMessages() 和disableMessages()。

[slot] void QOpenGLDebugLogger::startLogging(QOpenGLDebugLogger::LoggingMode loggingMode = AsynchronousLogging)

开始记录来自 OpenGL 服务器的消息。当接收到新消息时,会发出messageLogged() 信号,并将记录的消息作为参数传递。

loggingMode 指定日志记录应为异步(默认)还是同步。

QOpenGLDebugLogger 将在日志记录开始时记录GL_DEBUG_OUTPUT 和GL_DEBUG_OUTPUT_SYNCHRONOUS 的值,并在日志记录停止时将其恢复。此外,调用此函数时安装的任何用户定义的 OpenGL 调试回调,将在日志记录停止时恢复;QOpenGLDebugLogger 将确保在日志记录期间仍会调用原有的回调。

注意:无法 在不停止并重新启动日志记录的情况下更改日志记录模式。此行为可能会在 Qt 的未来版本中发生改变。

注意: 必须先初始化该 对象,才能进行日志记录。

另请参阅 stopLogging() 和initialize()。

[slot] void QOpenGLDebugLogger::stopLogging()

停止记录来自 OpenGL 服务器的消息。

另请参阅 startLogging()。

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.