在学习OpenGL的过程中,往往因为某些测试目的或单纯太菜而写出错误的代码,此时只能面对漆黑的窗口怀疑人生。如何让OpenGL能够像编译器一样,在出错时打印错误信息到控制台呢?本文将介绍几种方法。
基础介绍
首先声明OpenGL的API库:glfw。这个库支持较新的glfw版本(如gl4),让我们可以使用更新的OpenGL特性。现目前glfw官网可以下载的版本是3.3
,注意这是glfw的版本,而不是开发环境所使用的OpenGL版本。
通过#include <GLFW/glfw3.h>
包含glfw头文件之后,我们就可以使用GLFW API 的所有常量、类型和函数原型。其中一部分是通过包含开发环境自身提供的OpenGL头文件实现的,但是Windows平台对OpenGL支持很少(大概是因为微软自己做了DirectX的原因),因此简单地包含glfw3.h只能让我们直接使用古老的OpenGL 1.2版本,新版本函数需要手动查询并绑定函数指针后才能使用,为了解决这一问题,一般使用extension loader library,如glad库来支持这些扩展。
OpenGL获取错误
OpenGL运行中产生的错误保存在一个队列中,调用函数
GLenum glGetError(void);
从队列中获取一个代表错误的枚举数,对应error的类型,种类如下
枚举值 | 枚举名 | 描述 |
---|---|---|
0 | GL_NO_ERROR | 错误队列为空,当前未记录任何错误 |
1280 | GL_INVALID_ENUM | 使用非法的枚举参数 |
1281 | GL_INVALID_VALUE | 参数越界(out of range) |
1282 | GL_INVALID_OPERATION | 当前状态下不允许执行此操作。 |
1283 | GL_STACK_OVERFLOW | 堆栈溢出 |
1284 | GL_STACK_UNDERFLOW | 堆栈下溢(出栈操作达到堆栈底部) |
1285 | GL_OUT_OF_MEMORY | 内存溢出,OpenGL此后的行为将是未定义的 |
1286 | GL_INVALID_FRAMEBUFFER_OPERATION | 使用未准备好的帧缓存 |
1287 | GL_CONTEXT_LOST | 由于显卡重置导致 OpenGL 上下文丢失。 |
由于错误可能会累积,或是在某个调用中产生多种错误,因此一般用while()
反复调用以获取全部错误。
以下是一个简单的Debug头文件,包含该头文件后,在某一行中调用checkGLError()
函数,即可确定该行之前产生了哪些错误。
在多处使用时,这个函数可以接收一个字符串参数,来标记不同错误的位置。
#pragma once
#include <iostream>
#include <string>
#include <map>
#include <GLFW/glfw3.h>
std::map<int, std::string> ErrorInfo{
{0, "NO_ERROR"},
{1280, "GL_INVALID_ENUM"},
{1281, "GL_INVALID_VALUE"},
{1282, "GL_INVALID_OPERATION"},
{1283, "GL_STACK_OVERFLOW"},
{1284, "GL_STACK_UNDERFLOW"},
{1285, "GL_OUT_OF_MEMORY"},
{1286, "GL_INVALID_FRAMEBUFFER_OPERATION"},
{1287, "GL_CONTEXT_LOST"}
};
void checkGLError(const std::string& flag = "NO_FLAG") {
int code = glGetError();
if (code != GL_NO_ERROR) {
if (flag != "NO_FLAG") {
std::cout << "OpenGL ERROR at flag \"" << flag << "\"\n";
}
while (code != GL_NO_ERROR) {
std::cerr << "ERROR CODE \"" << code << "\": " << ErrorInfo[code] << std::endl;
code = glGetError();
}
}
}
(或许)更好的一种方式是在每个OpenGL函数调用时都套上一个检测错误的宏,尽管这样做会降低运行效率,但在程序的Debug阶段是很有效的,具体做法见Cherno的OpenGL教程 p10。
新版本OpenGL支持的错误回调
设置错误回调后,即可在发生错误时自动处理,十分方便。
- 注意:要使用错误回调需要包含较新(4.3版本以后)的glad库。
错误回调的原型如下
void glDebugMessageCallback( DEBUGPROC , callback
void * userParam);
callback
是一个在出错时将被调用的函数的函数指针。userParam
是用户传入的,将对应传入callback
函数的指针。
glfw获取错误
- 注意:glfw所返回的错误是带有
glfw
前缀的函数所产生的错误,即glfw库运行时产生的错误,它们不会引起程序崩溃。只要glfw成功初始化,它都会保持安全地运行。
方法1
使用函数
int glfwGetError(const char ** description)
用法可参加这段代码
const char* description;
int code = glfwGetError(&description);
if (description) {
// print code and description
}
-
错误代码指示错误的常规类别。如果自上次调用以来未发生任何错误,则返回GLFW_NO_ERROR(0)。
-
注意,指针的内存由glfw自己管理,直接传入空指针即可,也不需要释放指针。
方法2
类似于OpenGL自己的回调函数,glfw也可以设置回调函数,每次发生错误时都会调用该回调。同样是定义一个特定原型的回调函数,并传入glfwSetErrorCallback
即可。
void error_callback(int code, const char* description) {
// print code and description
}
glfwSetErrorCallback(error_callback);
参考资料
GLFW: Introduction to the API
glGetError_PersonFly_小逗的博客-CSDN博客
GLFW:Getting started
glDebugMessageCallback - OpenGL 4 - docs.gl