前言:调试输出是OpenGL4.3版本中增加的一种用于调试的扩展工具。它不仅比glGetError接口提供更多的错误信息,而且还允许通过debugger去定位错误源。
创建调试环境:环境的创建是与平台相关的任务,一般由各种封装库提供接口进行创建。常见的创建方式如下:
1.GLFW创建方式:需要在创建OpenGL环境之前进行调试环境的创建。创建代码如下所示:
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);
2.WGL创建方式:需要在创建OpenGL环境之后进行调试环境的创建。创建代码如下:
HGLRC CreateDebugContext(HDC hDC, HGLRC hShareContext, int major, int minor)
{
const int attriblist[] =
{
// 环境的主版本
WGL_CONTEXT_MAJOR_VERSION_ARB, major,
// 环境的次版本
WGL_CONTEXT_MINOR_VERSION_ARB, mior,
// 总是选择核心优化
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
// 打开调试环境
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
0
};
return wglCreateContextAttribsARB(hDC, hShareContext, attriblist);
}
3.GLX创建方式:无需先创建OpenGL环境就可以进行调试环境创建。创建代码如下:
GLXContext CreateDebugContext(Display* dpy, GLXFBConfig* config, GLXContext shareContext, int major, int minor)
{
const int attriblist[] =
{
// 环境的主版本
GLX_CONTEXT_MAJOR_VERSION_ARB, major,
// 环境的次版本
GLX_CONTEXT_MINOR_VERSION_ARB, mior,
// 总是选择核心优化
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
// 打开调试环境
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB,
0
};
return glXCreateContextAttribsARB(dpy, config, shareContext, attriblist);
}
检查调试环境:要检查是否成功地初始化了调试环境,我们可以对OpenGL进行查询。检查代码如下所示:
GLint flags;
glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
{
// TODO 初始化调试输出成功
}
开关调试消息:传递调试消息默认是启动的,当然我们也可以禁止。通常使用GL_DEBUG_OUTPUT或者GL_DEBUG_OUTPUT_SYNCHRONOUS来调用glEnable接口进行启用,或者调用glDisable接口来进行禁用。
传递调试消息:调试环境存在两种方式将获取的调试消息传递给开发者。第一种方式就是设置回调函数。第二种方式就是通过远程渲染方式利用调试环境获取调试消息日志。
常用接口如下:
1.void glDebugMessageCallback(DEBUGPROC callback, void* userParam)
:设置一个新的调试消息回调函数callback给调试环境。当调试消息生成的时候就会触发这个回调函数,并将调试消息以及用户自定义参数userParam传递给回调函数callback。
其中DEBUGPROC声明如下所示:
void APIENTRY DEBUGPROC(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, void *userParam)
:
.source表示消息来源。通常是GL_DEBUG_SOURCE_API(OpenGL API),GL_DEBUG_SOURCE_WINDOW_SYSTEM(窗口系统),GL_DEBUG_SOURCE_SHADER_COMPILER(着色器编译器),GL_DEBUG_SOURCE_THIRD_PARTY(第三方库),GL_DEBUG_SOURCE_APPLICATION(应用程序),GL_DEBUG_SOURCE_OTHER(其他)当中的一种。
.type表示消息类型。通常是GL_DEBUG_TYPE_ERROR(错误生成),GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR(过时功能),GL_DEBUG_TYPE_UNSINGNED_BEHAVIOR(未定义功能),GL_DEBUG_TYPE_PERFORMANCE(性能不优),GL_DEBUG_TYPE_PORTABILITY(不能移植),GL_DEBUG_TYPE_MARKER(注解),GL_DEBUG_TYPE_PUSH_GROUP(调用glPushDebugGroup),GL_DEBUG_TYPE_POP_GROUP(调用glPopDebugGroup),GL_DEBUG_TYPE_OTHER(其他)当中的一种。
.id表示消息标志。
.severity表示消息等级。通常是GL_DEBUG_SEVERITY_HIGH(致命消息。如:OpneGL错误,着色器编译失败等),GL_DEBUG_SEVERITY_MEDIUM(不致命消息。如:移植性能警告),GL_DEBUG_SEVERITY_LOW(没危害消息。如:冗余的状态切换),GL_DEBUG_SEVERITY_NOTIFICATION(普通通知消息)当中的一种。
.message表示调试消息。
.userParam表示用户自定义参数。与设置回调时传入的自定义参数相关联。
有了这一大堆的数据,我们可以创建一个非常有用的错误打印工具。代码如下所示:
void APIENTRY glDebugOutput(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
void *userParam)
{
// 忽略一些不重要的错误/警告代码
if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return;
std::cout << "---------------" << std::endl;
std::cout << "Debug message (" << id << "): " << message << std::endl;
switch (source)
{
case GL_DEBUG_SOURCE_API: std::cout << "Source: API"; break;
case GL_DEBUG_SOURCE_WINDOW_SYSTEM: std::cout << "Source: Window System"; break;
case GL_DEBUG_SOURCE_SHADER_COMPILER: std::cout << "Source: Shader Compiler"; break;
case GL_DEBUG_SOURCE_THIRD_PARTY: std::cout << "Source: Third Party"; break;
case GL_DEBUG_SOURCE_APPLICATION: std::cout << "Source: Application"; break;
case GL_DEBUG_SOURCE_OTHER: std::cout << "Source: Other"; break;
} std::cout << std::endl;
switch (type)
{
case GL_DEBUG_TYPE_ERROR: std::cout << "Type: Error"; break;
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: std::cout << "Type: Deprecated Behaviour"; break;
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: std::cout << "Type: Undefined Behaviour"; break;
case GL_DEBUG_TYPE_PORTABILITY: std::cout << "Type: Portability"; break;
case GL_DEBUG_TYPE_PERFORMANCE: std::cout << "Type: Performance"; break;
case GL_DEBUG_TYPE_MARKER: std::cout << "Type: Marker"; break;
case GL_DEBUG_TYPE_PUSH_GROUP: std::cout << "Type: Push Group"; break;
case GL_DEBUG_TYPE_POP_GROUP: std::cout << "Type: Pop Group"; break;
case GL_DEBUG_TYPE_OTHER: std::cout << "Type: Other"; break;
} std::cout << std::endl;
switch (severity)
{
case GL_DEBUG_SEVERITY_HIGH: std::cout << "Severity: high"; break;
case GL_DEBUG_SEVERITY_MEDIUM: std::cout << "Severity: medium"; break;
case GL_DEBUG_SEVERITY_LOW: std::cout << "Severity: low"; break;
case GL_DEBUG_SEVERITY_NOTIFICATION: std::cout << "Severity: notification"; break;
} std::cout << std::endl;
std::cout << std::endl;
}
过滤调试消息:可以潜在地过滤出需要的输出消息。使用接口如下所示:
void APIENTRY glDebugMessageControl(GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint * ids, GLboolean enabled)
:
.source表示消息来源。
.type表示消息类型。
.severity表示消息等级。