通过 GL_KHR_debug 在 ARM Mali GPU 上更轻松地进行 OpenGL ES 调试-2
- UID
- 1066743
|
通过 GL_KHR_debug 在 ARM Mali GPU 上更轻松地进行 OpenGL ES 调试-2
另一种替代方法是通过将它们包裹在宏中对每个 API 调用进行错误检查:
#ifdef _DEBUG
#define CALL_GL(a) a; check_gl_error_and_print()
#else
#define CALL_GL(a) a;
#endif
CALL_GL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data));
CALL_GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
CALL_GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
CALL_GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
CALL_GL(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
有关哪个行和文件产生该错误的信息可以轻松添加到这些宏中。图形调试工具也拥有自动插入这些检查的选项。
但这些方法都不完美。
一方面,很少或经常检查错误之间存在权衡。如果很少检查错误,就需要进行更多的工作来跟踪其原因和位置。如果经常检查错误,则代码的可读性可能会变差,因为调试代码会随处可见。更重要的是,这些方法无法解决缺乏详细信息这一根本问题。它们最多提供有关错误产生时间的信息,但无法提供有关其产生原因的任何其他信息。为此,我们需要扩展 API 的错误处理能力,而这正是 扩展的目的。
通过 GL_KHR_debug 获取更好的错误消息
扩展通过允许检测到的所有错误报告携带详细的错误消息解决了上述问题。默认情况下,调试通知存储在应用程序可以查询的驱动程序内的日志里。但是,应用程序也可以注册驱动程序在具有新通知时随时调用的回调函数。这是使用该扩展的最简单方式,因此现在我们将重点讲述。
请思考以下示例:
glGetIntegerv(GL_CURRENT_PROGRAM, &prog);
pos = glGetAttribLocation(prog, "position");
glEnableVertexAttribArray(pos);
glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, 0, triangle_corners);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, triangle_indices);
error = glGetError(); // GL_INVALID_OPERATION
在该列表中,尚不清楚产生 GL_INVALID_OPERATION 错误的原因和时间。通过,该驱动程序向我们提供了以下信息:
Error:glGetAttribLocation::<program> could not be made part of current state. <program> is not linked
Error:glEnableVertexAttribArray::<index> is greater than or equal to GL_MAX_VERTEX_ATTRIBS
Error:glVertexAttribPointer::<index> is greater than or equal to GL_MAX_VERTEX_ATTRIBS
好多了!
如何使用 GL_KHR_debug
那么,我们如何启用该功能?正如我们将看到的,这相当简单。只需对我们的 和 OpenGL ES 初始化代码进行一些更改即可。
调试信息只保证在所谓的调试上下文中提供,因此,我们需要创建其中之一。我们可以使用 EGL_KHR_create_context 扩展做到这一点:
EGLint ctx_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR,
EGL_NONE};
EGLContext *ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, ctx_attribs);
对正常上下文创建的唯一更改是增加 EGL_CONTEXT_FLAGS_KHR属性及其相关联参数。
接下来,我们需要定义供 OpenGL ES 调用的回调函数:
static void on_gl_error(enum source, enum type, uint id, enum severity,
sizei length, const char* message, void *userParam)
{
printf("%s\n", message);
}
我们需要略加注意我们在回调函数内所进行的操作,因为其从 OpenGL ES 驱动程序内进行调用。特别是,我们无法调用任何 OpenGL ES 函数,因为这可能会产生严重问题 - 包括应用程序崩溃。
最后,我们需要告诉 OpenGL ES 我们的回调函数:
static void enable_debug_callbacks(void)
{
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR, GL_TRUE);
glDebugMessageCallbackKHR(on_gl_error, NULL);
}1
就是这样。现在,驱动程序将在其生成调试消息时调用回调函数。该驱动程序需要为其检测到的每个 API 错误生成调试消息。我们不再需要在我们的代码中的任何其他地方检查错误!此外,通过在回调函数中设置断点,很容易找出不正确命令的调用来源。
注:当然,正如任何 OpenGL ES 扩展一样,您仅应在检查该扩展受支持并通过 eglGetProcAddress 检索扩展函数指针后进行该操作。对于 GL_KHR_debug 扩展特别需要注意的是将“KHR”后缀添加到所有新函数名称中的 OpenGL ES 版规范的后期更改。例如,“glDebugMessageCallback”更改为“glDebugMessageCallbackKHR”。由于这是非常迟的更改,因此某些实施可能仍使用无后缀版。为避免可移植性问题,应用程序应该同时查询有后缀和无后缀函数指针,以确定哪一个存在于任何给定实施上。
您可能想知道有关上个示例中对 glEnable
的额外调用的问题。这是必需的,因为某些 OpenGL ES 实施可能为内部多线程形式,因此可以同时检测和报告多个错误。通过启用同步输出,我们保证同一时间回调函数仅从一个线程进行调用。此外,我们还保证在返回 API 函数之前调用该回调函数。如果我们不启用该行为,则我们将必须确保我们的回调函数是线程安全的。 |
|
|
|
|
|