20 OpenGL调试输出

本文详细介绍了OpenGL的调试输出功能,包括调试消息的生成、类型、级别、回调机制、日志管理以及如何控制和查询这些调试信息。着重讲解了调试消息的来源、类型、严重性级别以及如何通过回调函数处理和存储调试信息。
摘要由CSDN通过智能技术生成

调试输出 Debug Output

OpenGL为应用程序开发者提供了调试输出功能,以便在执行GL命令时获取错误、未定义行为、性能警告等重要信息。开发人员可以通过回调函数或查询消息日志来接收这些调试消息,并且可以控制哪些消息被显示或隐藏,甚至插入自定义的应用程序生成的消息。

调试消息的生成受上下文创建方式的影响。如果创建的是一个带有CONTEXT_FLAG_DEBUG_BIT标志的调试上下文,则系统会自动启用DEBUG_OUTPUT功能并确保生成完整的调试输出;若非调试上下文,默认情况下可能不会生成任何调试消息,即使后续手动启用DEBUG_OUTPUT,其具体输出级别也将由OpenGL实现决定,可能会完全没有调试信息。

因此,要确保获得全面的调试支持,应使用设置了调试标志的OpenGL上下文。


Debug Output Message SourceMessages Generated by
DEBUG_SOURCE_APIThe GL
DEBUG_SOURCE_SHADER_COMPILERThe GLSL shader compiler or compilers for other extension-provided languages
DEBUG_SOURCE_WINDOW_SYSTEMThe window system, such as WGL or GLX
DEBUG_SOURCE_THIRD_PARTYExternal debuggers or third-party middleware libraries
DEBUG_SOURCE_APPLICATIONThe application
DEBUG_SOURCE_OTHERSources that do not fit to any of the ones listed above

Table 20.1 : Sources of debug output messages


Debug Output Message TypeInforms about
DEBUG_TYPE_ERROREvents that generated an error
DEBUG_TYPE_DEPRECATED_BEHAVIORBehavior that has been marked for deprecation
DEBUG_TYPE_UNDEFINED_BEHAVIORBehavior that is undefined according to the specification
DEBUG_TYPE_PERFORMANCEImplementation-dependent performance warnings
DEBUG_TYPE_PORTABILITYUse of extensions or shaders in a way that is highly vendor-specific
DEBUG_TYPE_MARKERAnnotation of the command stream
DEBUG_TYPE_PUSH_GROUPEntering a debug group
DEBUG_TYPE_POP_GROUPLeaving a debug group
DEBUG_TYPE_OTHERTypes of events that do not fit any of the ones listed above

Table 20.2: Types of debug output messages


Severity Level TokenSuggested examples of messages
DEBUG_SEVERITY_HIGHAny GL error; dangerous undefined behavior; any shader compiler and linker errors;
DEBUG_SEVERITY_MEDIUMSevere performance warnings; GLSL or other shader compiler and linker warnings; use of currently deprecated behavior
DEBUG_SEVERITY_LOWPerformance warnings from redundant state changes; trivial undefined behavior
DEBUG_SEVERITY_NOTIFICATIONAny message which is not an error or performance concern

Table 20.3: Severity levels of messages


调试消息 Debug Messages

每条调试消息都具有以下独特标识属性:

  1. 消息源:从预定义的符号常量列表中选取,表示消息产生的具体模块或阶段。
  2. 消息类型:同样由一组符号常量定义,代表不同类别或类型的事件,如错误、警告等。
  3. ID:在每个消息源和类型对所构成的独立命名空间内分配的一个无符号整数,用于唯一标识该命名空间内的特定消息。

由于不同的消息源和类型可能有重叠的ID范围,因此要完全区分两条消息,必须同时考虑其消息源、类型和ID三者组合。

此外,每条消息还具备:

  • 严重级别:一个全局统一的轴上的级别分类,用以大致描述消息的重要性。开发者可以根据严重级别来过滤并控制输出的数量。
  • 字符串描述:每条消息都有一个终止符为null的文本说明,虽然格式随实现而异,但应包含触发消息的具体事件描述,并且即使同一类消息的不同实例,其字符串内容也应有足够的差异以便区分。

最后,所有消息(包括终止符)的长度不超过实现相关的最大长度常量MAX_DEBUG_MESSAGE_LENGTH,并且每条消息可以启用或禁用,初始状态下除了严重级别为DEBUG_SEVERITY_LOW的消息外,其他消息默认启用。DebugMessageControl命令可用于更改单个消息的状态(启用或禁用)。

调试消息回调 Debug Message Callback

应用程序可以通过调用以下命令来设置接收调试消息的回调函数:

  • void glDebugMessageCallback(DEBUGPROC callback, const void *userParam);

其中,callback 存储回调函数的地址。回调函数原型必须为:

  • void callback(enum source, enum type, uint id, enum severity, sizei length, const char *message, const void *userParam);

    • source:消息源,表示消息来自哪个部分,例如OpenGL库、第三方库等。
    • type:消息类型,表示消息的类型,如错误、警告、性能信息等。
    • id:消息ID,用于标识特定的消息。
    • severity:消息的严重程度,表示消息的重要性。
    • length:消息字符串的长度。
    • message:包含消息内容的字符串。
    • userParam:用户参数,可以是任意用户指定的数据,会在每次回调时传递给回调函数。

在GL调用回调函数执行代码时,应用程序需注意一些特殊情况,无论调试来源如何:

  • message指向的内存由GL拥有和管理,在回调函数调用期间才有效。
  • 从回调函数内部调用任何GL或窗口系统函数的行为未定义,可能会导致程序终止。
  • 在多线程GL实现中,对于异步调试输出的安全调试回调也需特别小心处理。第20.8节对此进行了更详细的描述。

如果DEBUG_OUTPUT被禁用,则GL不会调用回调函数。

调试消息日志 Debug Message Log

当OpenGL的DEBUG_CALLBACK_FUNCTION设置为NULL时,调试消息会被存储在每个上下文内部的一个有限容量的消息日志中,其大小上限由MAX_DEBUG_LOGGED_MESSAGES定义。如果日志已满,则新产生的消息会直接丢弃,不再添加到日志。应用程序可通过查询DEBUG_LOGGED_MESSAGESDEBUG_NEXT_LOGGED_MESSAGE_LENGTH来获取当前日志的状态,并使用GetDebugMessageLog函数从日志提取调试信息。

DEBUG_CALLBACK_FUNCTION被设置为非NULL值并且DEBUG_OUTPUT启用,调试消息将不会保存至日志,而是立即传递给回调函数进行实时处理,绕过日志机制。

控制调试消息 Controlling Debug Messages

  • void glDebugMessageControl( enum source, enum type, enum severity, sizei count, const uint *ids, boolean enabled );

控制活动调试组(参见第20.6节)的调试输出量。如果enabled参数为TRUE,则引用的消息子集将被启用;若为FALSE,则这些消息将被禁用。

该命令可通过以下方式引用不同消息子集:

  1. 如果sourcetypeseverity参数为DONT_CARE,则分别引用来自所有源、所有类型或所有严重级别的消息。
  2. 当指定了非DONT_CARE值时,匹配指定sourcetypeseverity的所有消息将被引用。
  3. count大于零,则ids是一个包含sourcetype组合下指定数量消息ID的数组。在这种情况下,sourcetype不能为DONT_CARE,而severity必须是DONT_CARE。在ids中未识别的消息ID将被忽略。如果count为零,则忽略ids的值。

尽管消息按照其来源和类型构成了一个隐含的层级结构,但并没有针对每种来源、每种类型或每种严重级别设置显式的启用状态。相反,每个消息都有独立存储的启用状态。一次性禁用某个来源的所有消息与使用类型和ID逐个禁用该来源的所有消息,在效果上并无区别。

如果DEBUG_OUTPUT被禁用,则等同于禁用了所有来源、类型或严重级别的消息。

外部生成的消息 Externally Generated Messages

为了支持应用程序和第三方库生成自己的消息,例如包含时间戳信息或有关特定渲染系统事件的信号的消息,可以调用以下函数

  • void glDebugMessageInsert( enum source, enum type, uint id, enum severity, int length, const char *buf );

id 的值指定消息的 ID,severity 指示调用方定义的严重性级别。 字符串 buf 包含消息的字符串表示形式。 参数length包含buf中的字符数。 如果 length 为负数,则暗示 buf 包含一个以 null 结尾的字符串。

调试组 Debug Groups

调试组提供了一种使用描述性文本对命令流进行注释,将一组离散的命令分组的方法。调试输出消息可以是由实现生成的,也可以是由应用程序使用 DebugMessageInsert 插入的,这些消息将被写入活动调试组(调试组堆栈的顶部)。调试组是严格分层的。它们的序列可以嵌套在其他调试组中,但不能重叠。如果应用程序没有推送任何调试组,则活动调试组为默认调试组。

  • void glPushDebugGroup( enum source, uint id, sizei length, const char *message );

    • 用于向命令流中推送一个调试组,该调试组由指定的消息描述。以下是参数及其工作方式的详细说明:
    • source:指定调试消息的来源,表示消息的原始来源。
    • id:指定调试组内生成的消息的ID。这对于在调试输出中识别特定消息很有用。
    • length:指示消息字符串中的字符数。如果长度为负数,则表示消息字符串以空字符结尾。
    • message:指向包含调试组描述文本的字符串的指针。
  • void glPopDebugGroup( void );

调用 PushDebugGroup 时,它会使用提供的消息字符串、来源和ID创建一个新的调试组。调试组的类型是 DEBUG_TYPE_PUSH_GROUP,其严重性是 DEBUG_SEVERITY_NOTIFICATION

OpenGL 实现维护着一个调试组堆栈。当推送新的调试组时,它成为活动调试组,并继承来自之前位于堆栈顶部的调试组的调试输出音量控制。调试输出音量的任何控制设置都适用于活动调试组及其上推的任何调试组。

由于调试组是分层的,调试输出音量的控制范围限于活动调试组及其子级。这种分层结构允许根据渲染命令的上下文对调试输出设置进行细粒度控制。

调试标签 Debug Labels

调试标签提供了一种用描述性文本标签注释任何对象(纹理、缓冲区、着色器等)的方法。然后,这些标签可以被调试输出(参见第20章)或外部工具(如调试器或分析器)使用,以描述标记的对象。

  • void glObjectLabel( enum identifier, uint name, sizei length, const char *label );

    • identifier 是一个枚举值,指示对象的类型,必须是下表中的一个标记。它表示了要附加标签的对象类型。
    • name 是要标记的对象的名称或标识符。
    • length 表示标签字符串的长度。如果是负数,那么标签字符串应当以空字符结尾。
    • label 是一个指向描述性标签字符串的指针。如果 label 为 NULL,则意味着从对象中移除任何调试标签。
IdentifierObject Type
BUFFERbuffer
FRAMEBUFFERframebuffer
PROGRAM_PIPELINEprogram pipeline
PROGRAMprogram
QUERYquery
RENDERBUFFERrenderbuffer
SAMPLERsampler
SHADERshader
TEXTUREtexture
TRANSFORM_FEEDBACKtransform feedback
VERTEX_ARRAYvertex array

Table 20.4: Object namespace identifiers and the corresponding object types.


  • void glObjectPtrLabel( void *ptr, sizei length, const char *label );

标记由ptr标识的同步对象。lengthlabel匹配ObjectLabel的相应参数。

标签是与之相关联的对象状态的一部分。对象标签的初始状态是空字符串。标签不需要唯一。

异步和同步调试输出 Asynchronous and Synchronous Debug Output

DEBUG_OUTPUT_SYNCHRONOUS 是一个状态,它影响 OpenGL 驱动程序生成调试消息的方式和时机。它有两种状态:启用和禁用。

  • DEBUG_OUTPUT_SYNCHRONOUS 被禁用时:

    • 驱动程序允许异步调用调试回调函数,可能来自多个线程,甚至是当前上下文未绑定到生成消息的线程。
    • 调试消息可能在生成消息的 GL 命令返回后异步地调用回调函数。
    • 应用程序需要确保调试回调线程安全,可以使用 userParam 帮助识别命令的来源。
  • DEBUG_OUTPUT_SYNCHRONOUS 被启用时:

    • 驱动程序保证回调函数由当前上下文同步调用。
    • 所有对回调函数的调用都由拥有当前上下文的线程执行,并且在生成调试消息的 GL 命令返回之前执行。
    • 启用同步调试输出简化了应用程序的线程安全性责任,但可能会导致驱动程序性能下降。

此外,启用 DEBUG_OUTPUT_SYNCHRONOUS 只保证了由当前上下文生成的消息的回调的上下文内同步,不保证跨多个上下文的同步。因此,如果应用程序使用多个上下文,则需要确保每个上下文的回调函数都是线程安全的,即使所有上下文都启用了 DEBUG_OUTPUT_SYNCHRONOUS

调试输出查询 Debug Output Queries

用调试输出命令设置的指针可以用通用的glGetPointerv命令查询。pnames DEBUG_CALLBACK_FUNCTIONDEBUG_CALLBACK_USER_PARAM分别查询当前回调函数和glDebugMessageCallback函数集的用户参数。

当没有设置调试回调时,调试消息存储在调试消息日志中,如第20.3节所述。可以通过调用从日志中查询消息

  • int glGetDebugMessageLog( uint count, sizei bufSize, enum *sources, enum *types, uint *ids, enum *severities, sizei *lengths, char *messageLog );

GetDebugMessageLog 函数从消息日志中获取最多 count 条消息,并返回成功获取的消息数量。

  • 消息将按照从最旧到最新的顺序进行获取,并从日志中移除已获取的消息。

  • 获取的消息的来源、类型、严重性、ID 和字符串长度将分别存储在由应用程序提供的数组 sourcestypesseveritiesidslengths 中。应用程序需要为每个数组分配足够的空间,以容纳最多 count 个元素。

  • 所有获取的消息的字符串表示将存储在 messageLog 数组中。如果获取了多条消息,则它们的字符串将连接到同一个 messageLog 数组中,并用单个空终止符进行分隔。数组中的最后一个字符串也将以空终止符结尾。

  • messageLog 的最大大小(包括所有空终止符使用的空间)由 bufSize 给出。

  • 如果消息的字符串(包括空终止符)无法完全适应 messageLog 数组中剩余的空间,则该消息及后续消息将不会被获取,并将保留在日志中。数组 lengths 中存储的字符串长度包括每个字符串的空终止符的空间。

  • sourcestypesidsseveritieslengthsmessageLog 数组中的任何一个或全部是 NULL 指针时,将丢弃这些数组的属性,但是这些消息仍将从日志中删除。因此,如果应用程序只想从消息日志中删除最多 count 条消息而忽略它们的属性,则可以将所有属性数组的指针传递给 GetDebugMessageLog 的参数,并将它们设为 NULL。

  • 如果上下文不是调试上下文,则 OpenGL 可能选择永远不向消息日志中添加消息,因此 GetDebugMessageLog 将始终返回零。

  • void glGetObjectLabel( enum identifier, uint name, sizei bufSize, sizei *length, char *label );

    • 命令用于获取对象的标签字符串。
    • identifiername 参数指定了对象的命名空间和名称,它们与 ObjectLabel 函数的相应参数相匹配。
    • label 参数中将返回对象的标签字符串。
    • 标签字符串将以空终止符结尾,并存储在 label 中。
    • 返回的标签字符串的实际字符数(不包括空终止符)将存储在 length 参数中。如果 length 是 NULL,则不返回长度信息。
    • 可以通过 bufSize 参数指定 label 可以容纳的最大字符数(包括空终止符)。
    • 如果对象没有指定调试标签,则 label 将包含一个空的、以空终止符结尾的字符串,并且 length 中返回 0。
    • 如果 label 是 NULL,但 length 不是 NULL,则不会返回字符串,但字符串的长度将返回到 length 中。
  • void glGetObjectPtrLabel( void *ptr, sizei bufSize, size *length, char *label );

    • 在label中返回标记由ptr标识的同步对象的字符串。bufSize、length和label与GetObjectLabel的相应参数相匹配。
  • 42
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值