Project goals¶
OGLplus has been developed with the following goals in mind:
Type safety¶
OpenGL defines its symbolic constants as plain numbers of the GLenum
type.
It is pretty easy to to confuse similar-looking constant names like
GL_LINE
and GL_LINES
and use them in the wrong place. For example:
glPolygonMode(GL_FRONT, GL_LINES);
The second argument in the call above is wrong; correctly it should have been:
glPolygonMode(GL_FRONT, GL_LINE);
Such mistakes are made easily, but can be hard to find.
OGLplus defines typed wrappers around GL constants and uses the C++ type system to check for such mismatches. If a constant is used in a wrong place it results in a compilation error, instead of a run-time error.
Additionally, OpenGL and OpenAL, etc. identify objects of various kinds like programs, shaders, textures, buffers, audio sources, etc. by plain integer values which can again be easily confused:
GLuint shader = glCreateShader(GL_VERTEX_SHADER);
GLuint program = glCreateProgram();
glShaderSource(shader, ...);
glCompileShader(shader); // so far so good...
glAttachShader(shader, program); // run-time error (swapped arguments)
In OGLplus various references to various object kinds are strongly-typed and mismatching them results in compile-time error:
auto& [gl, GL] = ...
owned_shader_name shader;
gl.create_shader(GL.vertex_shader) >> shader;
owned_program_name;
gl.create_program() >> program;
gl.shader_source(shader, ...);
gl.compile_shader(shader);
gl.attach_shader(shader, program); // compile error
No preprocessor ifdefs in client code¶
The OpenGL API is heavily versioned and there are many extensions that are
implementation-specific. In such cases users are often checking whether macros
like GL_VERSION_3_0
or GL_ARB_compatibility
are defined and use the C++
preprocessor to switch between implementations using new features or extensions
and fallback implementations using an older GL API:
void myFunction() {
#ifdef GL_VERSION_4_5
// use new features here
#else
// use legacy API instead
#endif
}
This makes the application code ugly and sometimes hard to reason about.
In OGLplus the GL functions, etc. are wrapped in objects that provide additional functionality besides just calling the wrapped GL operation. For example they can be converted to boolean value which says whether a function or constant can be used. This boolean value can be a compile-time constant, so in many cases you do not pay for the check at run-time and the unused code is completely removed from the compiled binary. Also you can directly check for the availability of the function, not for the version:
void myFunction(texture_name tex) {
auto& [gl, GL] = ...
if(gl.texture_sub_image2d) {
// yay, we can use DSA
gl.texture_sub_image(tex, ...);
} else {
// we have to bind
gl.active_texture(0);
gl.bind_texture(GL.texture_2d, tex);
gl.tex_sub_image2d(GL.texture_2d, ...);
}
}
TODO