README.md
b178b77b
 # [`glbase`][]
 
 A [C++11][]/[OpenGL][]>=[1.0][] basics library.
 
58cc126e
 This library wraps up some common basic OpenGL functionality for convenience,
 including checking for supported OpenGL [version][]s and [extension][]s, basic
 image loading and saving, debugging helpers, error checking, and string
 helpers.
 
b178b77b
 [`glbase`]: https://git.rcrnstn.net/rcrnstn/glbase
 [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
58cc126e
 [version]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [extension]: https://www.khronos.org/opengl/wiki/OpenGL_Extension
 
 ## Usage
 
 ### OpenGL loading library
 
 This library can be used with an arbitrary [OpenGL loading library][] by making
 sure `GLBASE_INCLUDE` is defined 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). Note that double quotes or angle brackets are
 required. The default if none is defined is `<GL/glew.h>`.
 
 If [CMake][] is used for building (see [Building][]), the `GLBASE_INCLUDE`
 CMake variable can be set to tell `glbase` to not use the default [GLEW][], and
 to set the corresponding define. In this case, the CMake variables
 `GLBASE_PACKAGES` and `GLBASE_TARGETS` can be used to set the packages and
 targets respectively to use for OpenGL and the loading library (either or both
 can be empty).
 
 [OpenGL loading library]: https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library
 [CMake]: https://cmake.org
 [Building]: #building
 [GLEW]: https://glew.sourceforge.net
 
 ### Thread safety
 
 "Global" state (usually used to mirror some OpenGL [context][] internal state
 for performance) is declared [`thread_local`][] and initialized lazily. This
 means that a given thread may create at most one OpenGL context, and the
 `GLBase`-derived objects created while that context is current may be used only
 from that thread. Driver vendors recommend to not share OpenGL contexts between
 threads, for performance.
 
 The underlying mechanism used to retrieve system error messages may be thread
 unsafe. (`GLBase`-derived classes are encouraged to use
 `std::generic_category().message(errno)`, which often is, but is not required
 to be, thread safe, instead of [`std::strerror`][]`(errno)`, which is thread
 unsafe on most platforms).
 
 [context]: https://www.khronos.org/opengl/wiki/OpenGL_Context
 [`thread_local`]: https://en.cppreference.com/w/cpp/keyword/thread_local
 [`std::strerror`]: https://en.cppreference.com/w/cpp/string/byte/strerror
 
 ### Conventions
 
 The following table summarizes the [naming convention][] used.
 
 | Category | Capitalization | Public | Protected  | Private    |
 | -------- | -------------- | ------ | ---------- | ---------- |
 | Type     | `PascalCase`   |        | `_` suffix | `_` suffix |
 | Function | `snake_case`   |        | `_` suffix | `_` suffix |
 | Variable | `snake_case`   | (none) | (none)     | `_` suffix |
 | Macro    | `MACRO_CASE`   | (few)  |            | `_` suffix |
 
 "Private" in this case includes things with [internal linkage][]. The [storage
 duration][] or whether members are bound to class instances or not does not
 affect naming. Parameters (including template parameters) are considered public
 even though their use obviously is constrained to a scope inaccessible to the
 outside user.
 
 As can be seen in the table, class member variables are always private and
 (either public or protected) access is provided by [Accessors][].
 `GLBase`-derived classes should prefer to use their own protected member
 functions over their private member variables.
 
 [naming convention]: https://en.wikipedia.org/wiki/Naming_convention_(programming)
 [internal linkage]: https://en.cppreference.com/w/cpp/language/storage_duration#internal_linkage
 [storage duration]: https://en.cppreference.com/w/cpp/language/storage_duration#storage_duration
 [Accessors]: #accessors
 
 ### Size
 
 `GLBase` is an [empty][] class and the [empty base optimization][] applies.
 
 [empty]: https://en.cppreference.com/w/cpp/types/is_empty
 [empty base optimization]: https://en.cppreference.com/w/cpp/language/ebo
 
 ### Special member functions
 
 Both the constructor and the destructor are [`protected`][]. End users should
 use a `GLBase`-derived class to instantiate objects.
 
 Note that the destructor is not [`virtual`][]. `GLBase`-derived objects must
 not be destroyed through `GLBase` pointers.
 
 [`protected`]: https://en.cppreference.com/w/cpp/keyword/protected
 [`virtual`]: https://en.cppreference.com/w/cpp/language/virtual
 
 ### Accessors
 
 Getter and setter functions 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 `const` reference). Setters take a [forwarding
 reference][] argument and return the old value (by value, after move).
 
 Macros that aid in the implementation of (potentially "global" 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`.
 
 `GLBASE_{GET,SET,ACCESS}{,_GLOBAL}(TYPE, NAME)`
 
 Additionally, "global" 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.
 
 `GLBASE_GLOBAL(NAME, INIT)`
 
 [forwarding reference]: https://en.cppreference.com/w/cpp/language/reference#Forwarding_references
 [`private`]: https://en.cppreference.com/w/cpp/keyword/private
 [translation unit]: https://en.wikipedia.org/wiki/Translation_unit_(programming)
 
 ### Base
 
 `Version` is an alias for `std::array<GLint, 2>`.
 
 `bool static supported(Version version_min, std::string const & extension =
 {})` returns `true` if the current OpenGL [context][] implements the given, or
 a later (but see `version_max(...)` below), version or the given extension. If
 no version check is desired, provide `{}` (or, equivalently, `{0, 0}`). If no
 extension check is desired, provide an empty extension string (the default). If
 a non-empty `extension` is provided, it must start with `"GL_"`. If no check at
 all is performed, `false` is returned. Being able to check for both version and
 extension 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. 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. A new method of determining
 supported extensions was introduced in OpenGL [3.0][] and the old method
 deprecated and removed in OpenGL [3.1][]. This function uses the method
 appropriate for the current OpenGL [context][]. Note that no other checks are
 performed, including checks for a compatibility [profile][] [context][] or
 support for the [`ARB_compatibility`][] extension.
 
 `Version version_max({Version version_max})` gets/sets the maximum version for
 which `supported(...)` described above will return `true`. To disable this
 functionality, provide `{}` (or, equivalently, `{0, 0}`), which is the default
 value. Setting it to any other value also disables extension checking in
 `supported(...)`. This is useful for testing fallback code paths, regardless of
 the version present on a development machine (many platforms will provide a
 [context][] with a version higher than the one requested).
 
 `char const * static string(GLenum name)` and `char const * static
 string(GLenum name, GLuint index)` call [`glGetString`][] and
 [`glGetStringi`][] respectively with the given arguments and return the result,
 which obviates the need to cast the result (the OpenGL functions return
 `GLubyte const *`).
 
 `GLint static integer(GLenum name)` calls [`glGetIntegerv`][] with the given
 argument and returns the result, which obviates the need to declare a variable
 beforehand. Make sure to only use values of `name` that get a single integer.
 
 [History of OpenGL]: https://www.khronos.org/opengl/wiki/History_of_OpenGL
 [`glGetIntegerv`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGet.xhtml
 [`glGetString`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetString.xhtml
 [`glGetStringi`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetString.xhtml
 [3.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [3.1]: https://en.wikipedia.org/wiki/OpenGL#Version_history
 [profile]: https://www.khronos.org/opengl/wiki/OpenGL_Context#OpenGL_3.2_and_Profiles
 [`ARB_compatibility`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_compatibility.txt
 
 ### Path
 
 `Path` is an alias for `std::string`.
 
 `Paths` is an alias for `std::vector<Path>`.
 
 `Path static path_prefix_(Path const & path, Path const & prefix)` returns
 `prefix` prepended to `path`, separated with a `/`, unless either
 
 1.  `prefix` is empty
 2.  `path` starts with a `/`
 
 in which case `path` is returned as is.
 
 ### TGA
 
 A nested `GLBase::TGA_` class is provided to handle [Truevision TGA][] images
 in left-to-right, bottom-to-top, uncompressed [BGRA][] format. TGA was selected
 because it is widely supported and has a trivial header and data layout. It is
 also convenient because it has a pixel ordering compatible with the default
 [pixel transfer parameters][] and `GL_BGRA` is often preferred by graphics
 cards.
 
 `GLBase::TGA_` contains the following `static constexpr` member variables.
 
 -   `char const name[]`: `"TGA"`
 -   `GLint columns`: `1`
 -   `GLint rows`: `4`
 -   `GLenum glsl`: `GL_FLOAT_VEC4`
 -   `GLenum format`: `GL_BGRA`
 -   `GLenum type`: `GL_UNSIGNED_BYTE`
 -   `GLenum internal_format`: `GL_RGBA8`
 -   `GLenum internal_format_srgb`: `GL_SRGB8_ALPHA8`
 -   `GLenum internal_format_compressed`: `GL_COMPRESSED_RGBA`
 -   `GLenum internal_format_compressed_srgb`: `GL_COMPRESSED_SRGB_ALPHA`
 -   `GLenum id`: `glsl`
 
 (These are the same `static constexpr` member variables defined by
 [`GLTraits::Value`][].)
 
 `GLBase::TGA_` contains the following type definitions.
 
 -   `Size`. An alias for `std::array<GLsizei, 2>`.
 -   `Data`. An alias for `std::vector<GLubyte>`.
 
 `GLBase::TGA_` contains the following member functions.
 
 -   `explicit TGA_(Size size, Data data, Path const & path = {})`. Instantiates
     a `TGA_` object with the given `size` and `data`. `path`, if given, is only
     used for error reporting.
 -   `TGA_ static read(std::string const & path)`. Reads the file `path` into a
     `TGA_` object.
 -   `void write(std::string const & path) const`. Writes the `TGA_` object to
     the file `path`.
 -   `Size size() const`. Gets the size of the TGA image.
 -   `Data const & data() const`. Gets the data of the TGA image.
 
 [Truevision TGA]: https://en.wikipedia.org/wiki/Truevision_TGA
 [BGRA]: https://en.wikipedia.org/wiki/RGBA_color_model
 [pixel transfer parameters]: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_transfer_parameters
 [`GLTraits::Value`]: https://git.rcrnstn.net/rcrnstn/gltraits/#value
 [GLSL]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language
 
 ### Exceptions
 
 All exceptions thrown are of the type `GLBase::Exception`, which inherits from
 [`std::runtime_error`][].
 
 [`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error
 
 ### Debug
 
 A dynamic debug facility is used instead of asserts mainly because it allows
 dynamic error messages and it is often desirable to turn on debugging only
 during a problematic part of an application run.
 
 `DebugCallback` is an alias for [`std::function`][]`<void (std::string const &
 message)>`. These functions may not [`throw`][] since they are called by
 OpenGL, which may not support throwing exceptions across its stack frames.
 
 `int static debug({int debug})` gets/sets the global debug level. When `>= 1`,
 potentially costly debug operations are performed as part of other operations.
 When `>= 2` notifications may be emitted. Defaults to `0`.
 
 `DebugCallback static debug_callback({DebugCallback debug_callback})` gets/sets
 a callback that may be called by `GLBase`-derived classes (typically through
 `debug_message(...)`, see below). Defaults to a function which outputs the
 `mesage` argument to [`std::cerr`][], appends a newline and flushes.
 
 `void static debug_message(std::string const & message)` calls the callback
 registered with `debug_callback(...)` with `message` as argument if the
 callback is not empty (i.e. set to `nullptr`).
 
 `void static debug_action_(std::string const & action, std::string const &
 name)` calls `debug_message(...)` with a string containing `action` and `name`.
 
 [`std::function`]: https://en.cppreference.com/w/cpp/utility/functional/function
 [`throw`]: https://en.cppreference.com/w/cpp/language/throw
 [`std::cerr`]: https://en.cppreference.com/w/cpp/io/cerr
 
 ### Check
 
 These functions throw an exception if some condition does not hold. It is
 recommended that `GLBase`-derived classes use similar helper functions, defined
 in an implementation file, to hide the string processing necessary to form a
 helpful exception message.
 
 `GLBase`-derived classes should treat error checking as free during
 construction and expensive data transfer operations. At all other times,
 non-critical error checking should only be performed if `debug() >= 1`.
 
 `void static check_supported(Version version_min, std::string const & extension
 = {})` checks that `supported(...)` returns `true` for the given arguments.
 
 `void static check_path_(std::string const & path)` checks that `path` is
 non-empty.
 
 `void static check_error_(GLenum error)` checks an error returned by
 [`glGetError`][].
 
 `void static check_type_(GLenum type, GLenum type_expected)` checks that `type`
 matches `type_expected`.
 
 `void static check_format_(GLenum format, GLenum format_expected)` checks that
 `format` matches `format_expected`.
 
 `void static check_internal_format_(GLenum internal_format)` checks that
 `internal_format` is supported.
 
 [`glGetError`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetError.xhtml
 
 ### Fail
 
 These functions throw an exception and should be marked [`[[noreturn]]`][]. It
 is recommended that `GLBase`-derived classes use similar helper functions,
 defined in an implementation file, to hide the string processing necessary to
 form a helpful exception message.
 
 `[[noreturn]] void static fail_action_(std::string const & action, std::string
 const & name)` calls [`std::terminate`][] if called outside a [`catch`][]
 block. Otherwise, throws an exception with a message that includes `action` and
 `name` followed by `":\n"` and the [`what()`][] of the current exception if it
 is a (derives from) [`std::exception`][], otherwise simply rethrows the current
 exception.
 
 [`[[noreturn]]`]: https://en.cppreference.com/w/cpp/language/attributes/noreturn
 [`std::terminate`]: https://en.cppreference.com/w/cpp/error/terminate
 [`catch`]: https://en.cppreference.com/w/cpp/language/try_catch
 [`what()`]: https://en.cppreference.com/w/cpp/error/exception/what
 [`std::exception`]: https://en.cppreference.com/w/cpp/error/exception
 
 ### String
 
 `std::string static str_path_(Path const & path)` returns `path` surrounded by
 double quotes.
 
 `std::string static str_paths_(Paths const & paths)` returns `paths` surrounded
 by double 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_object_type_(GLenum object_type)` returns the string
 representation of `object_type`.
 
 `std::string static str_glsl_(GLenum glsl)` returns the string representation
 of `glsl`.
 
 `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`.
 
 ## Texture compression formats
 
 This is here for reference only.
 
 Extension conventions are defined in [`GL_ARB_texture_compression`][]. Formats
 are described in the [Khronons Data Format Specification "Compressed Texture
 Image Formats"][] section.
 
 [Direct3D][] defines `BC*` texture "block" compression formats which
 correspond with some of the formats described below. See:
 
 -   [Direct3D 10 Texture Block Compression][]
 -   [Direct3D 11 Texture Block Compression][]
 
 A non-exhaustive list of compressed texture formats defined by OpenGL:
 
 -   **S3TC**
     -   Support: Widely supported  since OpenGL 1.3 (2001) (the non-sRGB
         version is considered an "[ubiquitous extension][]"). Not core because
         of patents, which expired 2017. [Mesa][] support since 17.3 (2017).
     -   Origin: Introduced by [S3 Graphics][]' [Savage 3D][] (1998) and
         supported by [Nvidia][]'s more popular [GeForce 256][] (also known
         as the "GeForce 1") (1999). (`DXT2` and `DXT4` are premultiplied alpha
         formats and not supported by OpenGL.)
     -   Formats:
         -   `{RGB{,A},SRGB{,_ALPHA}}_S3TC_DXT1_EXT` (`BC1`). 6:1 compression
             ratio. The alpha variants only have a single bit of alpha, and when
             `A` is `0` RGB must all be `0` as well. Therefore, it is best to
             use premultiplied alpha when using the alpha variants.
         -   `{RGBA,SRGB_ALPHA}_S3TC_DXT3_EXT` (`BC2`). 4:1 compression ratio.
             Stores *uncompressed* alpha separately. Not generally recommended.
         -   `{RGBA,SRGB_ALPHA}_S3TC_DXT5_EXT` (`BC3`). 4:1 compression ratio.
             Stores *compressed* alpha separately.
     -   References:
         -   [`GL_EXT_texture_compression_s3tc`][]
         -   [`GL_EXT_texture_compression_s3tc_srgb`][]
         -   [`GL_EXT_texture_sRGB`][]
         -   [`GL_NV_texture_compression_s3tc`][]
         -   [`GL_S3_s3tc`][]
         -   [OpenGL Wiki][S3TC OpenGL Wiki]
         -   [Wikipedia][S3TC Wikipedia]
 -   VTC
     -   Support: Not core. Only supported on Nvidia hardware.
     -   Origin: Extension of S3TC to 3D ("volume") textures.
     -   Formats: The same as S3TC, but interpreted differently for 3D textures.
     -   References:
         -   [`GL_NV_texture_compression_vtc`][]
 -   **RGTC**
     -   Support: Core since OpenGL 3.0 (2008).
     -   Origin: Uses a separate S3TC DXT5 alpha encoding for one or two
         component red/green. Published at the same time as LATC.
     -   Formats:
         -   `{,SIGNED_}RED_RGTC1` (`BC4`). 2:1 compression ratio.
         -   `{,SIGNED_}RG_RGTC2` (`BC5`). 2:1 compression ratio.
     -   References:
         -   [`GL_ARB_texture_compression_rgtc`][]
         -   [`GL_EXT_texture_compression_rgtc`][]
         -   [OpenGL Wiki][RGTC OpenGL Wiki]
         -   [Wikipedia][RGTC Wikipedia]
 -   LATC:
     -   Support: Not core, no ARB extension. Luminance/alpha formats in general
         have seen a decline since the introduction of the programmable
         pipeline, which can use [swizzle][]s to achieve the same effect.
     -   Origin: Uses a separate S3TC DXT5 alpha encoding for one or two
         component luminance/alpha. Published at the same time as RGTC.
     -   Formats:
         -   `{,SIGNED_}LUMINANCE_LATC1_EXT`. 2:1 compression ratio.
         -   `{,SIGNED_}LUMINANCE_ALPHA_LATC2_EXT`. 2:1 compression ratio.
     -   References
         -   [`GL_EXT_texture_compression_latc`][]
         -   [`GL_NV_texture_compression_latc`][]
 -   **BPTC**
     -   Support: Core since OpenGL 4.2 (2011).
     -   Origin: Block Partition texture compression.
     -   Formats:
         -   `RGB_BPTC_{UN,}SIGNED_FLOAT` (`BC6H`): 4:1 compression ratio.
             Unsigned/signed 16-bit ("high dynamic range") float RGB.
         -   `{RGBA,SRGB_ALPHA}_BPTC_UNORM` (`BC7`): 4:1 compression ratio.
             Unsigned normalized integer RGBA/sRGBA.
     -   References:
         -   [`GL_ARB_texture_compression_bptc`][]
         -   [`GL_EXT_texture_compression_bptc`][]
         -   [OpenGL Wiki][BPTC OpenGL Wiki]
         -   [Wikipedia][BPTC Wikipedia]
 -   **ETC**
     -   Support: Core since OpenGL 4.3 (2012). Desktop OpenGL does not support
         ETC1, only ETC2. Images encoded as ETC1 can be read by an ETC2 decoder.
     -   Origins: [Ericsson][]. EAC presumably stands for "Ericsson Alpha
         Compression" and is used to compress one or two channels (much like
         RGTC uses the alpha compression format of S3TC DX5).
     -   Formats:
         -   `{,S}RGB8{,_PUNCHTHROUGH_ALPHA1}_ETC2` 6:1 compression ratio.
         -   `{RGBA8,SRGB8_ALPHA8}_ETC2_EAC`. 4:1 compression ratio.
         -   `{,SIGNED_}R11_EAC` 2:1 compression ratio.
         -   `{,SIGNED_}RG11_EAC` 2:1 compression ratio.
     -   References:
         -   [`GL_ARB_ES3_compatibility`][]
         -   [`GL_OES_compressed_ETC1_RGB8_texture`][]
         -   [Wikipedia][ETC Wikipedia]
 -   ASTC:
     -   Support: Not core. The extension is written against OpenGL ES, and
         there is no wide desktop support, at least for the HDR version.
     -   Origin: [ARM][] and [AMD][]. Variable block ("adaptive scalable")
         texture compression.
     -   Formats:
         -   `{RGBA,SRGB8_ALPHA8}_ASTC_{4x4,5x{4,5},6x{5,6},8x{5,6,8},10x{5,6,8,10},12x{10,12}}_KHR`
             A large number of variables are encoded per block so e.g. the
             number of channels, dynamic range, and compression ratio vary.
     -   References:
         -   `GL_KHR_texture_compression_astc_ldr`, [`GL_KHR_texture_compression_astc_hdr`][]
         -   [`GL_KHR_texture_compression_astc_sliced_3d`][]
         -   [OpenGL Wiki][ASTC OpenGL Wiki]
         -   [Wikipedia][ASTC Wikipedia]
         -   [`ARM-software/astc-encoder`][]
 
 [`GL_ARB_texture_compression`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression.txt
 [Khronons Data Format Specification "Compressed Texture Image Formats"]: https://registry.khronos.org/DataFormat/specs/1.1/dataformat.1.1.html#_compressed_texture_image_formats
 [Direct3D]: https://en.wikipedia.org/wiki/Direct3D
 [Direct3D 10 Texture Block Compression]: https://learn.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
 [Direct3D 11 Texture Block Compression]: https://learn.microsoft.com/en-us/windows/win32/direct3d11/texture-block-compression-in-direct3d-11
 [ubiquitous extension]: https://www.khronos.org/opengl/wiki/Ubiquitous_Extension
 [Mesa]: https://en.wikipedia.org/wiki/Mesa_(computer_graphics)
 [S3 Graphics]: https://en.wikipedia.org/wiki/S3_Graphics
 [Savage 3D]: https://en.wikipedia.org/wiki/S3_Savage#Savage_3D
 [Nvidia]: https://en.wikipedia.org/wiki/Nvidia
 [GeForce 256]: https://en.wikipedia.org/wiki/GeForce_256
 [`GL_EXT_texture_compression_s3tc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt
 [`GL_EXT_texture_compression_s3tc_srgb`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc_srgb.txt
 [`GL_EXT_texture_sRGB`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_sRGB.txt
 [`GL_NV_texture_compression_s3tc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_s3tc.txt
 [`GL_S3_s3tc`]: https://registry.khronos.org/OpenGL/extensions/S3/S3_s3tc.txt
 [S3TC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/S3_Texture_Compression
 [S3TC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression
 [`GL_NV_texture_compression_vtc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_vtc.txt
 [`GL_ARB_texture_compression_rgtc`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression_rgtc.txt
 [`GL_EXT_texture_compression_rgtc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt
 [RGTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression
 [RGTC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression#BC4_and_BC5
 [swizzle]: https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Swizzling
 [`GL_EXT_texture_compression_latc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_latc.txt
 [`GL_NV_texture_compression_latc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_latc.txt
 [`GL_ARB_texture_compression_bptc`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
 [`GL_EXT_texture_compression_bptc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_bptc.txt
 [BPTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression
 [BPTC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression#BC6H_and_BC7
 [Ericsson]: https://en.wikipedia.org/wiki/Ericsson
 [`GL_ARB_ES3_compatibility`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_ES3_compatibility.txt
 [`GL_OES_compressed_ETC1_RGB8_texture`]: https://registry.khronos.org/OpenGL/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
 [ETC Wikipedia]: https://en.wikipedia.org/wiki/Ericsson_Texture_Compression
 [ARM]: https://en.wikipedia.org/wiki/Arm_(company)
 [AMD]: https://en.wikipedia.org/wiki/AMD
 [`GL_KHR_texture_compression_astc_hdr`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt
 [`GL_KHR_texture_compression_astc_sliced_3d`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_sliced_3d.txt
 [ASTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/ASTC_Texture_Compression
 [ASTC Wikipedia]: https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression
 [`ARM-software/astc-encoder`]: https://github.com/ARM-software/astc-encoder
7028155f
 
d143b7ee
 ## Building
 
 See [`BUILDING.md`][].
 
 [`BUILDING.md`]: BUILDING.md
 
7028155f
 ## License
 
 Licensed under the [ISC License][] unless otherwise noted, see the
 [`LICENSE`][] file.
 
 [ISC License]: https://choosealicense.com/licenses/isc
 [`LICENSE`]: LICENSE