- 类 QOpenGLDebugLogger#
QOpenGLDebugLogger
允许记录 OpenGL 调试消息。 更多信息…摘要#
属性#
loggingMode属性
- 开始记录()传递的记录模式
方法#
def
__init__()
def
enableMessages()
def
initialize()
定义
isLogging()
定义
popGroup()
定义
pushGroup()
槽#
定义
logMessage()
信号#
注意
本文档可能包含从 C++ 自动翻译到 Python 的代码段。我们始终欢迎对代码段的翻译做出贡献。如果您发现翻译存在问题,也可以通过在https:/bugreports.qt.io/projects/PYSIDE创建工单的方式告诉我们。
详细描述#
警告
本节包含从 C++ 自动翻译到 Python 的代码段,可能包含错误。
介绍#
OpenGL 编程可能会非常容易出现错误。大多数情况下,对一个 OpenGL API 的单一失败调用可能导致整个应用程序的一个部分停止工作,屏幕上什么都不会绘制。
要确保 OpenGL 实现未返回错误,唯一的办法是在每次 API 调用后都使用
glGetError
进行检查。此外,OpenGL 错误会累积,因此应该始终像这样在循环中使用 glGetErrorerror = 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 创建一个调试上下文,你必须将 QSurfaceFormat::DebugContext 格式选项设置在用于创建 QOpenGLContext 对象的 QSurfaceFormat 上
format = QSurfaceFormat() # 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) context = QOpenGLContext() context.setFormat(format) context.create()
请注意,这里请求的 3.2 OpenGL Core Profile 只是为了示例目的;此类与任何特定的 OpenGL 或 OpenGL ES 版本无关,因为它依赖于
GL_KHR_debug
扩展的有效性(如下文所述)。创建和初始化 QOpenGLDebugLogger#
QOpenGLDebugLogger
是一个简单的 QObject 派生类。就像所有 QObject 子类一样,你创建一个实例(可选择指定父对象),并像 Qt 中的其他 OpenGL 函数一样,在使用前必须通过调用initialize()
来初始化,只要存在当前的 OpenGL 上下文。ctx = QOpenGLContext.currentContext() logger = QOpenGLDebugLogger(self) 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()
函数检索存储在此日志中的消息。messages = logger.loggedMessages() for message in messages: print(message)
内部日志的大小有限;当它填满时,较旧的消息将被丢弃以为新传入的消息腾出空间。当你调用
loggedMessages()
时,内部日志也将被清空。如果您想确保不丢失任何调试信息,您必须使用实时日志记录而不是调用此函数。但是,即使在实时日志记录激活之前或禁用时,可能仍然会在创建上下文和激活实时日志记录之间(或通常情况下)生成调试信息。
消息的实时日志记录#
您还可以从实现中以创建时的方式实时接收OpenGL服务器的调试消息流。为此,您需要将合适的槽连接到
messageLogged()
信号,并通过调用startLogging()
来启动日志记录。logger.messageLogged.connect(receiver.handleLoggedMessage) logger.startLogging()
同样,您可以通过调用
stopLogging()
函数随时禁用日志记录。实时日志记录可以是异步的或同步的,具体取决于传递给
startLogging()
函数的参数。在异步模式下(默认值,因为它有非常小的开销),OpenGL实现可以在任何时间、或以与导致记录消息的OpenGL命令顺序不同的顺序生成消息。消息也可以从与当前绑定到上下文的线程不同的线程生成。这是因为在通常情况下,OpenGL实现都具有高度多线程和异步的特点,因此不保证调试消息的相对顺序和时序。另一方面,同步模式的日志记录开销很大,但OpenGL实现保证在命令返回之前,按顺序接收到特定命令引起的所有消息,并且来自与OpenGL上下文绑定的同一线程。
这意味着在同步模式下日志记录时,您将能够在调试器中运行OpenGL应用程序,将断点放在连接到
messageLogged()
信号的槽上,并在回溯中看到导致记录消息的精确调用。这对于调试OpenGL问题非常有用。请注意,如果在其他线程中进行OpenGL渲染,您必须将信号/槽连接类型强制为Qt::DirectConnection,才能看到实际的回溯。有关日志模式的其他信息,请参阅
LoggingMode
枚举的文档。注意
当启用实时日志记录时,调试信息将不会插入到内部OpenGL调试日志中;已存在于内部日志中的消息不会被删除,也不会通过
messageLogged()
信号发出。由于在实时日志记录开始之前可能会生成一些消息(因此这些消息将保留在内部OpenGL日志中),因此在调用startLogging()
后始终检查它是否包含消息是很重要的。在调试日志中插入消息#
应用程序和库可以在调试日志中插入自定义消息,例如标记一组相关的OpenGL命令,从而能够识别这些命令发出的潜在消息。
为了实现这一点,您可以通过调用
QOpenGLDebugMessage
对象创建方法:createApplicationMessage()
或createThirdPartyMessage()
来创建,然后通过调用logMessage()
方法将其插入日志。message = QOpenGLDebugMessage.createApplicationMessage("Custom message") logger.logMessage(message)
请注意,OpenGL的实现对可以插入调试日志的消息长度有供应商特定的限制。您可以通过调用
maximumMessageLength()
方法来检索此长度;超过限制的较长消息将自动被截断。控制调试输出链接到本节
QOpenGLDebugMessage
还可以对调试消息应用过滤器,从而限制记录的消息数量。您可以分别通过调用enableMessages()
和disableMessages()
来启用或禁用消息的记录。默认情况下,所有消息都进行记录。可以通过按照
来源、类型和严重性(包括在选定中包含所有ID)来启用或禁用消息;
ID、来源和类型(包括在选定中包含所有严重性)。
请注意,给定消息的“启用”状态是(ID,来源,类型,严重性)元组的属性;消息属性不能构成任何类型的层次结构。应在调用
enableMessages()
和disableMessages()
时小心,因为这将会改变哪些消息将被启用/禁用。无法按消息文本本身进行过滤;应用程序必须自己进行(在连接到
messageLogged()
信号的槽中,或者通过loggedMessages()
从内部调试日志中检索消息后进行)。为了简化启用/禁用状态的管理,
QOpenGLDebugMessage
还支持debug groups
的概念。一个调试组包含调试消息的启用/禁用配置。此外,调试组以栈的形式组织:可以通过调用pushGroup()
和popGroup()
分别推送和弹出组。 (在创建OpenGL上下文时,栈中已经有一个组。)enableMessages()
和disableMessages()
函数将修改当前调试组中的配置,即调试组栈顶的配置。当将新组推送到调试组堆栈时,它将继承堆栈顶部组(先前位于堆栈顶部的组)的配置。相反,弹出调试组将恢复成为新顶部的新组配置。
推送(分别弹出)调试组还将自动生成类型为
GroupPushType
(分别GroupPopType
)的调试消息。另请参阅
- class LoggingMode#
LoggingMode 枚举定义了记录器对象的记录模式。
常量
描述
QOpenGLDebugLogger.AsynchronousLogging
OpenGL服务器的消息异步记录。这意味着消息可以在导致它们的相应OpenGL操作之后记录,甚至可能会以非顺序的方式进行接收,具体取决于OpenGL实现。这种模式具有非常低的性能影响,因为OpenGL实现是线程化的并具有异步性质。
QOpenGLDebugLogger.SynchronousLogging
OpenGL服务器的消息同步且顺序地记录。这会严重影响性能,因为OpenGL实现本质上是异步的;但它对调试OpenGL问题非常有用,因为OpenGL保证OpenGL命令生成的消息将在相应命令执行返回之前记录。因此,您可以在
messageLogged()
信号上设置断点,以查看导致该信号的OpenGL命令;唯一的缺点是,如果您在多个线程中使用OpenGL,您可能需要在连接到messageLogged()
信号时强制直接连接。
注意
可以当使用
from __feature__ import true_property
时直接使用属性,或者否则使用访问器函数。- 属性 loggingMode: QOpenGLDebugLogger.LoggingMode#
此属性保存传递给
startLogging()
方法的日志模式...请注意,必须已启动日志记录,否则此属性值将无意义。
- 访问函数
使用指定的
parent
构造新的日志对象。- disableMessages([sources=QOpenGLDebugMessage.AnySource[, types=QOpenGLDebugMessage.AnyType[, severities=QOpenGLDebugMessage.AnySeverity]]])#
禁用具有给定
sources
、给定types
和给定severities
的消息记录以及任何消息 ID 的日志。将在当前控制组中禁用日志。
- disableMessages(ids[, sources=QOpenGLDebugMessage.AnySource[, types=QOpenGLDebugMessage.AnyType]])
- enableMessages([sources=QOpenGLDebugMessage.AnySource[, types=QOpenGLDebugMessage.AnyType[, severities=QOpenGLDebugMessage.AnySeverity]]])#
启用来自给定
sources
、指定types
和severities
以及任何消息 ID 的消息的日志记录。日志记录将在当前控制组中启用。
- enableMessages(ids[, sources=QOpenGLDebugMessage.AnySource[, types=QOpenGLDebugMessage.AnyType]])
- initialize()#
- 返回类型::
bool
初始化当前 OpenGL 上下文中的对象。初始化成功之前,上下文必须支持
GL_KHR_debug
扩展。对象必须在任何日志记录发生之前进行初始化。可以从同一上下文中多次安全地调用此函数。
此函数还可以用于更改先前已初始化的对象的上下文;注意,在这种情况下,在调用此函数时对象不能正在记录。
如果日志记录器成功初始化,则返回
true
;否则返回 false。另请参阅
- isLogging()#
- 返回类型::
bool
如果此对象目前正在进行日志记录,则返回
true
;否则返回 false。另请参阅
- logMessage(debugMessage)#
- 参数:
debugMessage –
QOpenGLDebugMessage
将消息
debugMessage
插入到 OpenGL 调试日志中。这为应用程序或库提供了插入自定义消息的方法,有助于简化 OpenGL 应用程序的调试。注意
debugMessage
必须具有其源为ApplicationSource
或ThirdPartySource
,并且具有有效的类型和严重性,否则它将不会被插入到日志中。- loggedMessages()#
- 返回类型::
读取OpenGL内部调试日志中所有可用的消息并返回。此外,该函数将清除内部调试日志,因此后续调用将不会返回已返回的消息。
另请参阅
返回对象的活动日志模式。
另请参阅
loggingMode
的获取器。- maximumMessageLength()#
- 返回类型::
int
返回传递给
logMessage
的文本的最大支持长度(以字节为单位)。这也是调试组名的最大长度,因为压入或弹出组将自动以调试组名作为消息文本记录消息。如果消息文本过长,则
QOpenGLDebugLogger
会自动截断。注意
消息文本在传递给OpenGL时以UTF-8编码,因此其字节大小通常与QString::length()返回的UTF-16代码单元数量不匹配。 (如果消息包含只有7位ASCII数据,则这些通常会返回,这是调试消息的典型情况。)
- messageLogged(debugMessage)#
- 参数:
debugMessage –
QOpenGLDebugMessage
当从OpenGL服务器记录调试消息(由
debugMessage
参数包装)时,发出此信号。根据OpenGL实现,此信号可以由除了接收者所在线程之外的线程发出,甚至可以由初始化此对象的QOpenGLContext所在的线程之外的线程发出。此外,信号可能由多个线程同时发出。这通常不是问题,因为Qt将利用队列连接利用跨线程信号发射,但如果您强制连接类型为直接,则必须了解此信号槽可能发生的潜在竞争条件。
如果已经在
同步日志
模式下开始了日志记录,OpenGL 保证这个信号将从 QOpenGLContext 绑定的同一线程发出,并且不会发生并发调用。从调试组堆栈中移除最顶层调试组。如果成功移除,OpenGL 将自动记录一个消息,该消息的消息、ID 和源与被移除组的消息匹配,类型为
GroupPopType
和严重性NotificationSeverity
。移除调试组将恢复成为调试组堆栈顶部的组的消息过滤设置。
将名称为
name
、ID 为id
和源为source
的调试组推入调试组堆栈。如果组成功推入,OpenGL 将自动记录一个消息,消息包含name
、IDid
、源source
、类型GroupPushType
和严重性NotificationSeverity
。新推入的组将继承位于栈顶的组的相同过滤设置;换句话说,通过推入新的组不会更改过滤。
- startLogging([loggingMode=QOpenGLDebugLogger.LoggingMode.AsynchronousLogging])#
- 参数:
loggingMode –
LoggingMode
开始记录OpenGL服务器发出的消息。当接收到新消息时,会发出信号
messageLogged()
,并将记录的消息作为参数传递。loggingMode
指定日志记录是异步(默认)还是同步。当请求开始日志记录时,
QOpenGLDebugLogger
将记录GL_DEBUG_OUTPUT
和GL_DEBUG_OUTPUT_SYNCHRONOUS
的值,并在停止日志记录时恢复它们。此外,当调用此函数时安装的任何用户定义的OpenGL调试回调在停止日志记录时将被恢复;QOpenGLDebugLogger
将确保在日志记录时仍然调用预存在的回调。注意
不停止再次开始日志记录,无法更改日志记录模式。这可能在Qt的将来版本中发生变化。
- stopLogging()#
停止记录OpenGL服务器发出的消息。
另请参阅