В моих проектах OpenGL я оборачиваю каждый вызов в OpenGL API с проверкой ошибок. Мой подход выглядит примерно так:
std::string error_description(GLenum err) {
switch(err) {
case GL_NO_ERROR:
return "GL_NO_ERROR: No error has been recorded. The value of this symbolic constant is guaranteed to be 0. ";
case GL_INVALID_ENUM:
return "GL_INVALID_ENUM: An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag. ";
case GL_INVALID_VALUE:
return "GL_INVALID_VALUE: A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag. ";
case GL_INVALID_OPERATION:
return "GL_INVALID_OPERATION: The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag. ";
case GL_INVALID_FRAMEBUFFER_OPERATION:
return
"GL_INVALID_FRAMEBUFFER_OPERATION: The framebuffer object is not complete."
"The offending command is ignored and has no other side effect than to set the error flag.";
case GL_OUT_OF_MEMORY:
return "GL_OUT_OF_MEMORY: There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded. . ";
case GL_STACK_UNDERFLOW:
return "GL_STACK_UNDERFLOW: An attempt has been made to perform an operation that would cause an internal stack to underflow. ";
case GL_STACK_OVERFLOW:
return "GL_STACK_OVERFLOW: An attempt has been made to perform an operation that would cause an internal stack to overflow. ";
default:
return "No Description";
}
}
namespace detail {
void check() {
const auto err = glGetError();
if(err != GL_NO_ERROR) {
throw std::runtime_error(error_description(err));
}
}
}
template <class result_t, class... gl_args_t, class... args_t>
result_t call(result_t (*fun)(gl_args_t...), args_t... args) {
if constexpr(!std::is_same_v<result_t, void>) {
auto result = fun(std::forward<args_t>(args)...);
#ifndef NDEBUG
detail::check();
#endif
return result;
} else {
fun(std::forward<args_t>(args)...);
#ifndef NDEBUG
detail::check();
#endif
}
}
В приложении я вызываю API следующим образом:
call(<api_call>, <arguments...>>);
call(glGenBuffers, 1, &buffer);
Причины, почему я предпочитаю проверку ошибок, а не обратный вызов ошибок:
- Необработанные исключения прервут вашу программу и упростят идентификацию точной строки кода, которая не работает
- Если вы последовательны и переносите все вызовы OpenGL, то вы знаете, что ваш конечный автомат всегда находится в определенном состоянии