Name Mode Size
assets 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 9 kb
common.cmake 100644 13 kb
README.md
# [`glshader`][] A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. [`glshader`]: https://git.rcrnstn.net/rcrnstn/glshader [C++11]: https://en.wikipedia.org/wiki/C++11 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL [2.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history [shader]: https://www.khronos.org/opengl/wiki/Shader ## Usage Overview of usage: ```cpp #include <GL/glew.h> #include <glshader.hpp> // Global data. constexpr auto light_count_max = 32; int main() { // Global settings. Shader::root("assets/shaders"); Shader::defines({ {"light_count_max", std::to_string(light_count_max)}, }); // Create. auto player = Shader({ "player.vert", "player.frag", "lighting.frag", }); // Use. player .use() .validate(); } ``` All public (non-static) member functions return a reference to the current object, allowing [method chaining][]. [method chaining]: https://en.wikipedia.org/wiki/Method_chaining ### Errors Errors are handled by throwing [`std::runtime_error`][] with a [`what()`][] that returns helpful context, including operating system messages and the OpenGL info log where appropriate. If OpenGL \>=4.3 or the extension [`GL_KHR_debug`][] is available, [`glObjectLabel`][] is used to improve debug messages provided to any callback registered with [`glDebugMessageCallback`][]. Se [`tests/glshader.cpp`][] for an overview of some of the handled errors. (Note that the tests are written for compatibility and convenience and do not necessarily reflect current best practices.) [`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error [`what()`]: https://en.cppreference.com/w/cpp/error/exception/what [`GL_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 [`glDebugMessageCallback`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDebugMessageCallback.xhtml [`tests/glshader.cpp`]: tests/glshader.cpp ### Lifetime No default constructor is defined. This, together with a throwing constructor, means that a (non-moved-from) `Shader` object always corresponds to a valid OpenGL shader object. At destruction, the underlying OpenGL shader program is deleted, but not explicitly unbound. Note that the behavior of [deletion unbinding][] depends on the OpenGL version. It is recommended to always consider the underlying OpenGL shader program to be invalid after destruction. Copying (creating a new identical underlying OpenGL shader object) or move assigning (deleting the underlying OpenGL shader object of the moved-into `Shader` object) would probably be a (performance) bug and is disabled. I.e. `Shader` is a move(-constructible)-only type. [deletion unbinding]: https://www.khronos.org/opengl/wiki/OpenGL_Object#Deletion_unbinding ### Loading files An arbitrary number of files can be loaded by giving them as arguments to the `Shader(Shader::Paths const & paths)` constructor. `Shader::Paths` is an alias for `std::set<std::string>`. `std::set` is used over e.g. `std::vector` to facilitate deduplicating shaders based on paths, even when the paths are given in different orders. `std::set` is used over `std::unordered_set` to retain the same ordering in error messages as in the creation for easy identification. The [shader stage][] is automatically detected by the file extension using the same rules as the [OpenGL / OpenGL ES Reference Compiler][]: - `.vert`: vertex - `.tesc`: tessellation control - `.tese`: tessellation evaluation - `.geom`: geometry - `.frag`: fragment - `.comp`: compute If multiple files are provided for the same stage they are compiled separately and linked into the final shader program. The value passed to `Shader::root(std::string const & root)` is prepended, with a separating `"/"` if non-empty, to all paths (defaults to `""`). [shader stage]: https://www.khronos.org/opengl/wiki/Shader#Stages [OpenGL / OpenGL ES Reference Compiler]: https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ ### Validation By calling `validate()` the shader can be [validate][]d against the current OpenGL state to make sure that a subsequent draw call would succeed. An error is thrown if a problem is detected. Additional issues that are not typically detected by [`glValidateProgram`][] are checked for: - Shader program not [current](#use). If the macro [`NDEBUG`][] is defined, the check is optimised out. Therefore, it is recommended to always call `validate()` before issuing draw calls. [validate]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glValidateProgram.xhtml [`glValidateProgram`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glValidateProgram.xhtml [`NDEBUG`]: https://en.cppreference.com/w/c/error/assert ### Use The shader needs to be [use][]d by calling `use()` before performing certain other actions on it. If the macro [`NDEBUG`][] is not defined, an error is thrown if this is not the case. [use]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glUseProgram.xhtml ### Preprocessing A simple preprocessor is run before shader sources are passed to OpenGL. At present the preprocessor does not interpret line continuations or multi-line comments correctly on lines it acts upon. Other lines are left alone so they can be used there without issue. #### `#version` Each supplied shader file is checked for the existence of one, and only one, [`#version`][]. An error is thrown if this is not the case. (The OpenGL default is to use `#version 110` if none is provided.) [`#version`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Version #### `#define`s [`#define`][]s specified with `Shader::defines(Shader::Defines const & defines)` are injected after the `#version`. `Shader::Defines` is an alias for `std::map<std::string, std::string>`. [`#define`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives ## Dependencies Public (interface): - [OpenGL][], system (e.g. [`libgl1-mesa-dev`][]). - [OpenGL Extension Wrangler (GLEW)][], system (e.g. [`libglew-dev`][]). Private (build): - [`str`][], downloaded as part of the CMake configure step. Private (tests): - [GLFW][], system (e.g. [`libglfw3-dev`][]). - [`gltest`][], downloaded as part of the CMake configure step. [OpenGL Extension Wrangler (GLEW)]: http://glew.sourceforge.net [GLFW]: https://www.glfw.org [`libgl1-mesa-dev`]: https://packages.debian.org/search?keywords=libgl1-mesa-dev [`libglew-dev`]: https://packages.debian.org/search?keywords=libglew-dev [`libglfw3-dev`]: https://packages.debian.org/search?keywords=libglfw3-dev [`str`]: https://git.rcrnstn.net/rcrnstn/str [`gltest`]: https://git.rcrnstn.net/rcrnstn/gltest ## 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