README.md
c7bdb13d
 # [`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
80c358cb
 
fa2758de
 ## Usage
 
 Overview of usage:
 
 ```cpp
 #include <GL/glew.h>
 
 #include <glshader.hpp>
 
 
772e8338
 // Global data.
 constexpr auto light_count_max = 32;
30b3651e
 constexpr auto vert_position  = 0;
 constexpr auto vert_tex_coord = 1;
 constexpr auto frag_color = 0;
772e8338
 
 
fa2758de
 int main()
 {
     // Global settings.
     Shader::root("assets/shaders");
772e8338
     Shader::defines({
         {"light_count_max", std::to_string(light_count_max)},
     });
30b3651e
     Shader::verts({
         {"vert_position",  vert_position},
         {"vert_tex_coord", vert_tex_coord},
     });
     Shader::frags({
         {"frag_color", frag_color},
     });
fa2758de
 
     // Create.
     auto player = Shader({
         "player.vert",
         "player.frag", "lighting.frag",
     });
55f9c41b
 
     // Use.
     player
9a0c30b8
         .use()
55f9c41b
         .validate();
fa2758de
 }
 ```
 
55f9c41b
 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
 
fa2758de
 ### 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/
 
55f9c41b
 ### 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`][]
9a0c30b8
 are checked for:
 
 -   Shader program not [current](#use).
7583d2da
 -   Uniforms not set with a call to [`uniform`](#uniforms), uniform blocks not
     set with a call to `uniform` or `uniform_buffer`. (OpenGL defaults them to
     zero unless initialized in the shader.)
55f9c41b
 
 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
 
9a0c30b8
 ### Use
 
 The shader needs to be [use][]d by calling `use()` before performing certain
7583d2da
 other actions on it, such as calling [`uniform`](#uniforms).
9a0c30b8
 
 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
 
5c248477
 ### 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
 
772e8338
 #### `#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
 
bbde7908
 #### `#include`s
 
 Support for [`#include`][]s is provided, with the same syntax as the
 [`ARB_shading_language_include`][] extension. An `#include`d path must be
 surrounded by `<` `>`, in which case it is treated as relative to
 [`root`](#loading-files), or by `"` `"`, in which case it is treated as
 relative to the including file.
 
 Even though the extension is not used for including files, an `#extension
 GL_ARB_shading_language_include : require` line is required for portability,
 otherwise an error is thrown. The line is silently dropped before the source is
 fed to OpenGL so as to not upset the shader compiler if the extension is not
 available.
 
 If the [`ARB_shading_language_include`][] extension is available, `#line`
 statements that include the file path are injected before every line (after the
 `#version`) to improve error messages from the shader compiler.
 
 Note that the `#include` statements are expanded before the source is fed to
 OpenGL, and therefore before the [GLSL preprocessor][] is run which means
 things like [include guard][]s do not work. (For portability, it is recommended
 that include guards be used anyway.) Circular includes are broken by skipping
 `#include`s where the *including file, line number, included file* tuple has
 been seen before.
 
 [`#include`]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives
 [`glCompileShader`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glCompileShader.xhtml
 [GLSL preprocessor]: https://www.khronos.org/opengl/wiki/Core_Language_(GLSL)#Preprocessor_directives
 [include guard]: https://en.wikipedia.org/wiki/Include_guard
 [`ARB_shading_language_include`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt
 [`glNamedStringARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt
 [`glCompileShaderIncludeARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt
 
30b3651e
 ### User-defined vertex inputs (attributes) and fragment outputs (data)
 
 Locations for user-defined [vertex inputs][] (attributes) and [fragment
 outputs][] (data, requires OpenGL >= 3.0) can be specified by calling
 `Shader::verts(Shader::Locations const & verts)` and
 `Shader::frags(Shader::Locations const & frags)` respectively before
 instantiating a `Shader` that uses them. `Shader::Locations` is an alias for
 `std::map<std::string, GLuint>`. Any `location` [layout qualifier][] overrides
 the values specified this way. Note that a `Shader` object retains the input
 and output locations that were in effect when it was instantiated. See
 [`glBindAttribLocation`][] and [`glBindFragDataLocation`][]. Note that fragment
 outputs are subject to [`glDrawBuffer`][] / [`glDrawBuffers`][].
 
 An error is thrown if a shader uses a non-specified input. No error is thrown
 for non-specified outputs (this is mostly a result of the inability to
 enumerate shader outputs before the introduction of the [program interface
 query][]). Shaders need not use all of the specified inputs/outputs.
 
 The intent is for the application to define global unchanging input/output
 locations that are used by all shaders, geometry and framebuffers and set them
 up once at application startup.
 
 [vertex inputs]: https://www.khronos.org/opengl/wiki/Vertex_Shader#Inputs
 [fragment outputs]: https://www.khronos.org/opengl/wiki/Fragment_Shader#Outputs
 [layout qualifier]: https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)
 [`glBindAttribLocation`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindAttribLocation.xhtml
 [`glBindFragDataLocation`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindFragDataLocation.xhtml
 [`glDrawBuffer`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffer.xhtml
 [`glDrawBuffers`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffers.xhtml
 [program interface query]: https://www.khronos.org/opengl/wiki/Program_Introspection#Interface_query
 
7583d2da
 ### Uniforms
 
 [Uniform][]s are set with the template specialized `uniform(std::string const &
 name, T const & value, bool required = true)`. If `required` is `true` an error
 is thrown if the shader does not contain an active uniform with the given
 `name`. If the macro [`NDEBUG`][] is not defined an error is thrown if the
 value is of the wrong type. [`use`](#use) must be called before calling
 `uniform`.
 
 [uniform]: https://www.khronos.org/opengl/wiki/Uniform
 
 #### User-defined uniforms
 
 Support for e.g. third party mathematics libraries can be added by providing
 the relevant template specializations of `uniform`. The macro
 `GLSHADER_UNIFORM(TYPE, CODE)` may be used to ease this task. `TYPE` is the
 type for which to provide the specialization and `CODE` is code that may use
 the supplied variables `TYPE const & value` and `GLint const & location`,
 probably as parameters to `glUniform*`. `GLSHADER_UNIFORM_DELETE(TYPE)` can be
 used to [`delete`][] the specialization for a given type.
 
 [`delete`]: https://en.cppreference.com/w/cpp/language/function#Deleted_functions
 
fa2758de
 ## 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
 
3bca6231
 ## 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
 
80c358cb
 ## License
 
 Licensed under the [ISC License][] unless otherwise noted, see the
 [`LICENSE`][] file.
 
 [ISC License]: https://choosealicense.com/licenses/isc
 [`LICENSE`]: LICENSE