Name Mode Size
doc 040000
include 040000
src 040000
tests 040000
.clang-tidy 100644 1 kb
.gitignore 100644 0 kb
CMakeLists.txt 100644 1 kb
LICENSE 100644 1 kb
README.md 100644 17 kb
common.cmake 100644 13 kb
README.md
# [`globject`][] A [C++11][]/[OpenGL][] [\>=1.0][] [object][] library. The provided `GLObject` class is intended to be the base class of several other classes that encapsulate different types of OpenGL objects. [`globject`]: https://git.rcrnstn.net/rcrnstn/globject [C++11]: https://en.wikipedia.org/wiki/C++11 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL [\>=1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history [object]: https://www.khronos.org/opengl/wiki/OpenGL_Object ## Usage ### OpenGL loading library This library can be used with an arbitrary [OpenGL loading library][] by making sure `GLOBJECT_LOADER` is `#define`d to the file to `#include`, e.g. `<glad/glad.h>` (either in the source or through a compiler switch, probably defined in the build system). The default if none is defined is `<GL/glew.h>`. [OpenGL loading library]: https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library ### [OpenGL Mathematics (GLM)][] If `glm/glm.hpp` is included (specifically, if `GLM_VERSION` is defined) before inclusion of `globject.hpp` [OpenGL Mathematics (GLM)][] support will be enabled. See [Data][]. [OpenGL Mathematics (GLM)]: https://glm.g-truc.net [Data]: #data ### Thread safety "Global" state (usually used to mirrors some OpenGL [context][] internal state for convenience or performance) is declared `thread_local`. This means that multithreading is supported under the assumption that a given OpenGL [context][] and the `GLObject`s created while it is current are only used from a single thread. This is not a huge limitation since driver vendors recommend to not share OpenGL [context][]s between threads, for performance. [context]: https://www.khronos.org/opengl/wiki/OpenGL_Context ### Function documentation For brevity, argument and return types may omit `const &` and functions may omit `const` qualifiers in the remainder of this documentation when it has no implication for the user, other than performance. Consult the source for the exact function signatures. ### `public` interface #### [Special member functions][] There is no `public` constructor. It is impossible to directly instantiate `GLObject`s. Instead, use a derived class that encapsulates a specific type of OpenGL object. There is no defined default constructor. The copy constructor and copy/move assignment operators are `delete`d. This enforces the invariant that a successfully constructed instance (that has not been moved from) always (is the only instance that) corresponds to a valid OpenGL object. The destructor is `virtual`, so references to instances of (different) derived classes can be held in (the same) containers. The move constructor's `noexcept` specifier is only honored if `debug` is `false`, see [Debug][]. [special member functions]: https://en.wikipedia.org/wiki/Special_member_functions [Debug]: #debug #### Getters and setters Getters and setters share the same name and use function overloading to disambiguate. Getters (which can be run on `const` objects) take no argument and return the value (by constant reference). Setters take a [forwarding reference][] argument and return the old value (by value, after move). Getters and setters use a "gets"/"sets" shorthand below to indicate they conform to this behaviour. [forwarding reference]: https://en.cppreference.com/w/cpp/language/reference#Forwarding_references #### Core `bool static supported(Version version_min, std::string extension = {})` returns `true` if the current OpenGL context implements the given, or a later, version or the given extension. If no version check is desired, provide `{0, 0}`. If no extension check is desired, provide an empty extension string (the default). If no check at all is performed, `false` is returned. This is convenient because extensions are often absorbed into newer versions of the OpenGL specification itself, and some platforms stop reporting support for the extension for those versions, so only checking if the extension is available fails. [History of OpenGL][] has a list of which versions of OpenGL absorbed which extensions. If a non-empty `extension` is provided, it must be on the form returned by [`glGetStringi`][]`(GL_EXTENSIONS, index)`, i.e. it must start with `"GL_"`. Since this library is agnostic to the used [OpenGL loading library][] (which is usually used to perform these kind of checks), the OpenGL API itself is queried. The old method of detecting the OpenGL version and extensions was deprecated in OpenGL 3.0 and removed in OpenGL 3.1. Both methods are dealt with correctly. `Version` is an alias for `std::array<GLint, 2>`. `GLint static get_integer(GLenum name)` calls [`glGetIntegerv`][] with the given argument and returns the result. `GLuint object()` gets the underlying OpenGL object. This is guaranteed to be valid, unless the `GLObject` has been moved from, in which case it is `0`. `operator GLuint()` (implicit conversion operator) returns `object()`. [History of OpenGL]: https://www.khronos.org/opengl/wiki/History_of_OpenGL [`glGetStringi`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetString.xhtml [`glGetIntegerv`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml #### Data `template<typename Data> struct DataTraits` is template specialized on `GL*` (and, if enabled, [OpenGL Mathematics (GLM)][]) types and provides the following `static constexpr` values. - `char const[] name` - `GLint columns` - `GLint rows` - `GLenum format` - `GLenum type` - `GLenum internal_format` It also provides the following functions. - `void static attrib(GLuint index, Data value)` which uploads `value` to the [non-array attribute][] indicated by `index`. Note that [OpenGL Mathematics (GLM)][] matrix types, if enabled, occupy several consecutive indices (one per column), which are all uploaded automatically by this call. - `void static uniform(GLint location, Data value)` which uploads `value` to the [uniform][] indicated by `location` of the current [shader][] program. `GLOBJECT_DATA*` macros to ease the definition of new template specializations of `DataTraits` are defined. Consult the source for the definitions and usage examples. [non-array attribute]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Non-array_attribute_values [uniform]: https://www.khronos.org/opengl/wiki/Uniform_(GLSL) [shader]: https://www.khronos.org/opengl/wiki/Shader #### Path `Path` is an alias for `std::string`. `Paths` is an alias for `std::vector<Path>`. #### Debug `bool static debug()` gets/sets the global `debug` flag. When `true`, potentially costly debug operations are performed as part of other operations. Defaults to `true`. `DebugCallback static debug_callback()` gets/sets a callback that may be called by `GLObject` and its derived classes when `debug` is `true`. The debug callback can also be called by client code if desired. `DebugCallback` is an alias for `std::function<void (std::string const & message)>`. Defaults to a function which outputs to `std::cerr`, appends a newline and flushes. `DebugObjects static debug_objects()` gets a list of all `GLObjects` constructed, and not subsequently destructed, during the time `debug` was `true`. `DebugObjects` is an alias for `std::vector<GLObject *>`. `std::string debug_name()` returns a short representation of the object. `std::string debug_info()` returns a potentially longer representation of the object. `std::string static debug_objects_name()` returns a short representation of all objects in `debug_objects`. `std::string static debug_objects_info()` returns a potentially longer representation of all objects in `debug_objects`. Code in `GLObject` (and properly behaved derived classes) uses the macro `GLOBJECT_DEBUG_IF(D)` which expands to `if (debug() >= 1)` by default. It can be made to expand to `if (false)` by defining `GLOBJECT_DEBUG 0`, with the result that an optimizing compiler will completely compile out the corresponding debug code. #### Exceptions All exceptions thrown are of the type `GLObject::Exception`, which inherits from `std::runtime_error`. ### `protected` interface #### [Special member functions][] Derived classes should define a (usually `explicit` non-default) constructor. A `noexcept` move constructor and `virtual` destructor should only be defined if resources (other than `object`, which is handled by `GLObject`) need to be moved/released. Derived classes should not define or `delete` any of the non-defined/`delete`d special member functions detailed in the `public` interface documentation above. Two helper functions that act as adapters from the interface of the `glCreate*`/`glDelete*` family of functions (used by shader and program objects, termed "unconventional objects" by the OpenGL Wiki) to the interface of the `glGen*s`/`glDelete*s` family of functions are provided. - `template<GLCreateObject gl_create_object> void static gl_create_object_(GLsizei n, GLuint * objects)` takes a `glCreate*` function as template argument, and conforms to the `glGen*s` interface. `GLCreateObject` is an alias for `GLuint (*)()`. - `template<GLDeleteObject gl_delete_object> void static gl_delete_object_(GLsizei n, GLuint * objects)` takes a `glDelete*` function as template argument, and conforms to the `glDelete*s` interface. `GLDeleteObject` is an alias for `void (*)(GLuint)`. `GLObject` has a concept of pseudo-objects. They are classes that wish to make use of the `protected` interface of `GLObject` but does not encapsulate an OpenGL [object][]. The `GLObject` constructor takes: - `GLGenObjects gl_gen_objects` which is a function pointer that conforms to the `glGen*s` interface. Specify `nullptr` for pseudo-objects. - `GLDeleteObjects gl_delete_objects` which is a function pointer that conforms to the `glDelete*s` interface. Specify `nullptr` for pseudo-objects. - `GLenum object_type` which is one of the following. Specify `0` for pseudo-objects. - `GL_BUFFER` - `GL_SHADER` - `GL_PROGRAM` - `GL_VERTEX_ARRAY` - `GL_QUERY` - `GL_PROGRAM_PIPELINE` - `GL_TRANSFORM_FEEDBACK` - `GL_SAMPLER` - `GL_TEXTURE` - `GL_RENDERBUFFER` - `GL_FRAMEBUFFER` - `std::string object_label` which will be used when throwing exceptions and, if OpenGL [\>=4.3][] or the [`KHR_debug`][] extension is available, passed to [`glObjectLabel`][]. [\>=4.3]: https://en.wikipedia.org/wiki/OpenGL#Version_history [`KHR_debug`]: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_debug.txt [`glObjectLabel`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glObjectLabel.xhtml #### Getters and setters Macros that aid in the implementation of (potentially static thread local) getters and setters are defined. These should be used in the class definition. They define functions named `NAME`, which operate on a variable named `NAME##_`. The underlying variable is not declared automatically, this must be done manually (probably `private`). The `ACCESS` variant defines both `GET` and `SET`. `GLOBJECT_{GET,SET,ACCESS}{,_THREAD}(TYPE, NAME)` Additionally, static thread local member variables need to be defined in exactly one translation unit. A macro to help with this is defined as well. Note that, since the definition is outside the class definition, the `NAME` needs to be qualified with the class name. `GLOBJECT_THREAD(NAME, INIT)` #### Path `Path static path_prefix_(Path path, Path prefix)` returns `prefix` prepended to `path`, separated with a `/`, unless 1. `prefix` is empty 2. `path` starts with a `/` in which case `path` is returned as is. #### TGA A `TGA` class is provided to handle [Truevision TGA][] images in uncompressed [BGRA][] (`GL_BGRA`) format. TGA was selected because it is widely supported and has trivial header and data layout. `Size` is an alias for `std::array<GLsizei, 2>`. `Data` is an alias for `std::vector<GLubyte>`. `explicit TGA(Size size, Data data)` instantiates a `TGA` object with the given `size` and `data`. `TGA static read(std::string path)` reads the file `path` into a `TGA` object. `void write(std::string const & path)` writes the `TGA` object to the file `path`. `Data data()` gets the data of the TGA image. `Size size()` gets the size of the TGA image. [Truevision TGA]: https://en.wikipedia.org/wiki/Truevision_TGA [BGRA]: https://en.wikipedia.org/wiki/RGBA_color_model #### Check These functions throw an exception if some condition does not hold. It is recommended that derived classes use similar helper functions, defined in an implementation file, to hide the string processing necessary to form a helpful exception message. Derived classes should treat error checking as free during: - Construction and destruction, except move construction. - Exceptions. - Debug function calls. At all other times, non-critical error checking should only be performed if `debug` is `true`. `void static check_path_(std::string path)` checks that `path` is non-empty. `void static check_error_(GLenum error)` checks an error returned by [`glGetError`][]. `void static check_supported_(Version version_min, std::string extension = {})` checks that `supported` returns `true` for the given arguments. `void static check_format_(GLenum format, GLenum format_expected)` checks that `format` matches `format_expected`. `void static check_type_(GLenum type, GLenum type_expected)` checks that `type` matches `type_expected`. `void static check_internal_format_(GLenum internal_format)` checks that `internal_format` is supported. [`glGetError`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetError.xhtml #### Fail These functions throw an exception and should be marked `[[noreturn]]`. It is recommended that derived classes use similar helper functions, defined in an implementation file, to hide the string processing necessary to form a helpful exception message. `void fail_action_(std::string action)` throws an exception with a message that includes `action` and `debug_name()`. #### String `std::string static str_path_(Path path)` returns `path` surrounded by quotes. `std::string static str_paths_(Paths paths)` returns `paths` surrounded by quotes and joined with commas. `std::string static str_enum_(GLenum name)` returns the hexadecimal string representation of `name` (often used as fallback). `std::string static str_error_(GLenum error)` returns the string representation of values returned by [`glGetError`][]. `std::string static str_format_(GLenum format)` returns the string representation of `format`. `std::string static str_type_(GLenum type)` returns the string representation of `type`. `std::string static str_internal_format_(GLenum internal_format)` returns the string representation of `internal_format`. ## Build system This project supports [CMake][] and uses [`cmake-common`][]. There are several ways to use it in other CMake-based projects: - With [`find_package`][]: ([Package][] and) [install][] it on the system. - With [`add_subdirectory`][]: Bundle it. - With [`FetchContent`][]: Download it as part of the CMake configure step. - With [`cmake-common`][]: Use any of the above methods through a simplified interface. As usual, use [`add_dependencies`][] or [`target_link_libraries`][] (or `cmake-common`'s `DEPENDENCIES_*`) to declare the dependency. [CMake]: https://cmake.org [`cmake-common`]: https://git.rcrnstn.net/rcrnstn/cmake-common [`FetchContent`]: https://cmake.org/cmake/help/v3.14/module/FetchContent.html [`add_subdirectory`]: https://cmake.org/cmake/help/v3.14/command/add_subdirectory.html [`find_package`]: https://cmake.org/cmake/help/v3.14/command/find_package.html [Package]: #Package [Install]: #Install [`add_dependencies`]: https://cmake.org/cmake/help/v3.14/command/add_dependencies.html [`target_link_libraries`]: https://cmake.org/cmake/help/v3.14/command/target_link_libraries.html ### Configure and generate To configure and generate a build tree, use `cmake`: ```sh cmake -B _build ``` To set the build type, pass e.g. `-D`[`CMAKE_BUILD_TYPE`][]`=Release`. [`cmake`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#generate-a-project-buildsystem [`CMAKE_BUILD_TYPE`]: https://cmake.org/cmake/help/v3.14/variable/CMAKE_BUILD_TYPE.html ### Build To build, use [`cmake --build`][]: ```sh cmake --build _build ``` To disable building tests, pass `-D`[`BUILD_TESTING`][]`=OFF`. [`cmake --build`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#build-a-project [`BUILD_TESTING`]: https://cmake.org/cmake/help/v3.14/module/CTest.html ### Test To run tests, use [`ctest`][]: ```sh (cd _build && ctest) ``` To show output from failing tests, pass `--output-on-failure`. To show output from all tests, pass `--verbose`. [`ctest`]: https://cmake.org/cmake/help/v3.14/manual/ctest.1.html ### Package To package, use [`cpack`][]: ```sh (cd _build && cpack) ``` [`cpack`]: https://cmake.org/cmake/help/v3.14/manual/cpack.1.html ### Install To install onto the current system, use [`cmake --install`][]: ```sh cmake --install _build ``` To set the prefix, pass e.g. `-D`[`CMAKE_INSTALL_PREFIX`][]`="$HOME/.local"`. [`cmake --install`]: https://cmake.org/cmake/help/v3.14/manual/cmake.1.html#install-a-project [`CMAKE_INSTALL_PREFIX`]: https://cmake.org/cmake/help/v3.14/variable/CMAKE_INSTALL_PREFIX.html ## License Licensed under the [ISC License][] unless otherwise noted, see the [`LICENSE`][] file. [ISC License]: https://choosealicense.com/licenses/isc/ [`LICENSE`]: LICENSE