Browse code

Add implementation

Robert Cranston authored on 14/10/2022 20:41:19
Showing 5 changed files

... ...
@@ -9,3 +9,30 @@ project(glbase
9 9
     VERSION 1.0.0
10 10
     LANGUAGES CXX
11 11
 )
12
+
13
+## Main target
14
+add_library(${PROJECT_NAME})
15
+
16
+## Variables
17
+if(NOT GLBASE_INCLUDE)
18
+    set(GLBASE_PACKAGES GLEW       OpenGL)
19
+    set(GLBASE_TARGETS  GLEW::GLEW OpenGL::GL)
20
+    set(GLBASE_INCLUDE  <GL/glew.h>)
21
+endif()
22
+
23
+## Common
24
+include(common.cmake)
25
+common(
26
+    CXX_STANDARD 11
27
+    DISABLE_CPPCHECK # function try block
28
+    PACKAGES
29
+        ${GLBASE_PACKAGES}
30
+    FETCHCONTENT
31
+        https://git.rcrnstn.net/rcrnstn/cxx-str
32
+    DEPENDENCIES_PUBLIC
33
+        ${GLBASE_TARGETS}
34
+    DEPENDENCIES_PRIVATE
35
+        cxx-str
36
+    DEFINITIONS
37
+        GLBASE_INCLUDE=${GLBASE_INCLUDE}
38
+)
... ...
@@ -2,10 +2,518 @@
2 2
 
3 3
 A [C++11][]/[OpenGL][]>=[1.0][] basics library.
4 4
 
5
+This library wraps up some common basic OpenGL functionality for convenience,
6
+including checking for supported OpenGL [version][]s and [extension][]s, basic
7
+image loading and saving, debugging helpers, error checking, and string
8
+helpers.
9
+
5 10
 [`glbase`]: https://git.rcrnstn.net/rcrnstn/glbase
6 11
 [C++11]: https://en.wikipedia.org/wiki/C++11
7 12
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
8 13
 [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
14
+[version]: https://en.wikipedia.org/wiki/OpenGL#Version_history
15
+[extension]: https://www.khronos.org/opengl/wiki/OpenGL_Extension
16
+
17
+## Usage
18
+
19
+### OpenGL loading library
20
+
21
+This library can be used with an arbitrary [OpenGL loading library][] by making
22
+sure `GLBASE_INCLUDE` is defined to the file to `#include`, e.g.
23
+`<glad/glad.h>` (either in the source or through a compiler switch, probably
24
+defined in the build system). Note that double quotes or angle brackets are
25
+required. The default if none is defined is `<GL/glew.h>`.
26
+
27
+If [CMake][] is used for building (see [Building][]), the `GLBASE_INCLUDE`
28
+CMake variable can be set to tell `glbase` to not use the default [GLEW][], and
29
+to set the corresponding define. In this case, the CMake variables
30
+`GLBASE_PACKAGES` and `GLBASE_TARGETS` can be used to set the packages and
31
+targets respectively to use for OpenGL and the loading library (either or both
32
+can be empty).
33
+
34
+[OpenGL loading library]: https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library
35
+[CMake]: https://cmake.org
36
+[Building]: #building
37
+[GLEW]: https://glew.sourceforge.net
38
+
39
+### Thread safety
40
+
41
+"Global" state (usually used to mirror some OpenGL [context][] internal state
42
+for performance) is declared [`thread_local`][] and initialized lazily. This
43
+means that a given thread may create at most one OpenGL context, and the
44
+`GLBase`-derived objects created while that context is current may be used only
45
+from that thread. Driver vendors recommend to not share OpenGL contexts between
46
+threads, for performance.
47
+
48
+The underlying mechanism used to retrieve system error messages may be thread
49
+unsafe. (`GLBase`-derived classes are encouraged to use
50
+`std::generic_category().message(errno)`, which often is, but is not required
51
+to be, thread safe, instead of [`std::strerror`][]`(errno)`, which is thread
52
+unsafe on most platforms).
53
+
54
+[context]: https://www.khronos.org/opengl/wiki/OpenGL_Context
55
+[`thread_local`]: https://en.cppreference.com/w/cpp/keyword/thread_local
56
+[`std::strerror`]: https://en.cppreference.com/w/cpp/string/byte/strerror
57
+
58
+### Conventions
59
+
60
+The following table summarizes the [naming convention][] used.
61
+
62
+| Category | Capitalization | Public | Protected  | Private    |
63
+| -------- | -------------- | ------ | ---------- | ---------- |
64
+| Type     | `PascalCase`   |        | `_` suffix | `_` suffix |
65
+| Function | `snake_case`   |        | `_` suffix | `_` suffix |
66
+| Variable | `snake_case`   | (none) | (none)     | `_` suffix |
67
+| Macro    | `MACRO_CASE`   | (few)  |            | `_` suffix |
68
+
69
+"Private" in this case includes things with [internal linkage][]. The [storage
70
+duration][] or whether members are bound to class instances or not does not
71
+affect naming. Parameters (including template parameters) are considered public
72
+even though their use obviously is constrained to a scope inaccessible to the
73
+outside user.
74
+
75
+As can be seen in the table, class member variables are always private and
76
+(either public or protected) access is provided by [Accessors][].
77
+`GLBase`-derived classes should prefer to use their own protected member
78
+functions over their private member variables.
79
+
80
+[naming convention]: https://en.wikipedia.org/wiki/Naming_convention_(programming)
81
+[internal linkage]: https://en.cppreference.com/w/cpp/language/storage_duration#internal_linkage
82
+[storage duration]: https://en.cppreference.com/w/cpp/language/storage_duration#storage_duration
83
+[Accessors]: #accessors
84
+
85
+### Size
86
+
87
+`GLBase` is an [empty][] class and the [empty base optimization][] applies.
88
+
89
+[empty]: https://en.cppreference.com/w/cpp/types/is_empty
90
+[empty base optimization]: https://en.cppreference.com/w/cpp/language/ebo
91
+
92
+### Special member functions
93
+
94
+Both the constructor and the destructor are [`protected`][]. End users should
95
+use a `GLBase`-derived class to instantiate objects.
96
+
97
+Note that the destructor is not [`virtual`][]. `GLBase`-derived objects must
98
+not be destroyed through `GLBase` pointers.
99
+
100
+[`protected`]: https://en.cppreference.com/w/cpp/keyword/protected
101
+[`virtual`]: https://en.cppreference.com/w/cpp/language/virtual
102
+
103
+### Accessors
104
+
105
+Getter and setter functions share the same name and use function overloading to
106
+disambiguate. Getters (which can be run on `const` objects) take no argument
107
+and return the value (by `const` reference). Setters take a [forwarding
108
+reference][] argument and return the old value (by value, after move).
109
+
110
+Macros that aid in the implementation of (potentially "global" static thread
111
+local) getters and setters are defined. These should be used in the class
112
+definition. They define functions named `NAME`, which operate on a variable
113
+named `NAME##_`. The underlying variable is not declared automatically, this
114
+must be done manually (probably [`private`][]). The `ACCESS` variant defines
115
+both `GET` and `SET`.
116
+
117
+`GLBASE_{GET,SET,ACCESS}{,_GLOBAL}(TYPE, NAME)`
118
+
119
+Additionally, "global" static thread local member variables need to be defined
120
+in exactly one [translation unit][]. A macro to help with this is defined as
121
+well. Note that, since the definition is outside the class definition, the
122
+`NAME` needs to be qualified with the class name.
123
+
124
+`GLBASE_GLOBAL(NAME, INIT)`
125
+
126
+[forwarding reference]: https://en.cppreference.com/w/cpp/language/reference#Forwarding_references
127
+[`private`]: https://en.cppreference.com/w/cpp/keyword/private
128
+[translation unit]: https://en.wikipedia.org/wiki/Translation_unit_(programming)
129
+
130
+### Base
131
+
132
+`Version` is an alias for `std::array<GLint, 2>`.
133
+
134
+`bool static supported(Version version_min, std::string const & extension =
135
+{})` returns `true` if the current OpenGL [context][] implements the given, or
136
+a later (but see `version_max(...)` below), version or the given extension. If
137
+no version check is desired, provide `{}` (or, equivalently, `{0, 0}`). If no
138
+extension check is desired, provide an empty extension string (the default). If
139
+a non-empty `extension` is provided, it must start with `"GL_"`. If no check at
140
+all is performed, `false` is returned. Being able to check for both version and
141
+extension is convenient because extensions are often absorbed into newer
142
+versions of the OpenGL specification itself, and some platforms stop reporting
143
+support for the extension for those versions, so only checking if the extension
144
+is available fails. [History of OpenGL][] has a list of which versions of
145
+OpenGL absorbed which extensions. Since this library is agnostic to the used
146
+[OpenGL loading library][] (which is usually used to perform these kind of
147
+checks), the OpenGL API itself is queried. A new method of determining
148
+supported extensions was introduced in OpenGL [3.0][] and the old method
149
+deprecated and removed in OpenGL [3.1][]. This function uses the method
150
+appropriate for the current OpenGL [context][]. Note that no other checks are
151
+performed, including checks for a compatibility [profile][] [context][] or
152
+support for the [`ARB_compatibility`][] extension.
153
+
154
+`Version version_max({Version version_max})` gets/sets the maximum version for
155
+which `supported(...)` described above will return `true`. To disable this
156
+functionality, provide `{}` (or, equivalently, `{0, 0}`), which is the default
157
+value. Setting it to any other value also disables extension checking in
158
+`supported(...)`. This is useful for testing fallback code paths, regardless of
159
+the version present on a development machine (many platforms will provide a
160
+[context][] with a version higher than the one requested).
161
+
162
+`char const * static string(GLenum name)` and `char const * static
163
+string(GLenum name, GLuint index)` call [`glGetString`][] and
164
+[`glGetStringi`][] respectively with the given arguments and return the result,
165
+which obviates the need to cast the result (the OpenGL functions return
166
+`GLubyte const *`).
167
+
168
+`GLint static integer(GLenum name)` calls [`glGetIntegerv`][] with the given
169
+argument and returns the result, which obviates the need to declare a variable
170
+beforehand. Make sure to only use values of `name` that get a single integer.
171
+
172
+[History of OpenGL]: https://www.khronos.org/opengl/wiki/History_of_OpenGL
173
+[`glGetIntegerv`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGet.xhtml
174
+[`glGetString`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetString.xhtml
175
+[`glGetStringi`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetString.xhtml
176
+[3.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
177
+[3.1]: https://en.wikipedia.org/wiki/OpenGL#Version_history
178
+[profile]: https://www.khronos.org/opengl/wiki/OpenGL_Context#OpenGL_3.2_and_Profiles
179
+[`ARB_compatibility`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_compatibility.txt
180
+
181
+### Path
182
+
183
+`Path` is an alias for `std::string`.
184
+
185
+`Paths` is an alias for `std::vector<Path>`.
186
+
187
+`Path static path_prefix_(Path const & path, Path const & prefix)` returns
188
+`prefix` prepended to `path`, separated with a `/`, unless either
189
+
190
+1.  `prefix` is empty
191
+2.  `path` starts with a `/`
192
+
193
+in which case `path` is returned as is.
194
+
195
+### TGA
196
+
197
+A nested `GLBase::TGA_` class is provided to handle [Truevision TGA][] images
198
+in left-to-right, bottom-to-top, uncompressed [BGRA][] format. TGA was selected
199
+because it is widely supported and has a trivial header and data layout. It is
200
+also convenient because it has a pixel ordering compatible with the default
201
+[pixel transfer parameters][] and `GL_BGRA` is often preferred by graphics
202
+cards.
203
+
204
+`GLBase::TGA_` contains the following `static constexpr` member variables.
205
+
206
+-   `char const name[]`: `"TGA"`
207
+-   `GLint columns`: `1`
208
+-   `GLint rows`: `4`
209
+-   `GLenum glsl`: `GL_FLOAT_VEC4`
210
+-   `GLenum format`: `GL_BGRA`
211
+-   `GLenum type`: `GL_UNSIGNED_BYTE`
212
+-   `GLenum internal_format`: `GL_RGBA8`
213
+-   `GLenum internal_format_srgb`: `GL_SRGB8_ALPHA8`
214
+-   `GLenum internal_format_compressed`: `GL_COMPRESSED_RGBA`
215
+-   `GLenum internal_format_compressed_srgb`: `GL_COMPRESSED_SRGB_ALPHA`
216
+-   `GLenum id`: `glsl`
217
+
218
+(These are the same `static constexpr` member variables defined by
219
+[`GLTraits::Value`][].)
220
+
221
+`GLBase::TGA_` contains the following type definitions.
222
+
223
+-   `Size`. An alias for `std::array<GLsizei, 2>`.
224
+-   `Data`. An alias for `std::vector<GLubyte>`.
225
+
226
+`GLBase::TGA_` contains the following member functions.
227
+
228
+-   `explicit TGA_(Size size, Data data, Path const & path = {})`. Instantiates
229
+    a `TGA_` object with the given `size` and `data`. `path`, if given, is only
230
+    used for error reporting.
231
+-   `TGA_ static read(std::string const & path)`. Reads the file `path` into a
232
+    `TGA_` object.
233
+-   `void write(std::string const & path) const`. Writes the `TGA_` object to
234
+    the file `path`.
235
+-   `Size size() const`. Gets the size of the TGA image.
236
+-   `Data const & data() const`. Gets the data of the TGA image.
237
+
238
+[Truevision TGA]: https://en.wikipedia.org/wiki/Truevision_TGA
239
+[BGRA]: https://en.wikipedia.org/wiki/RGBA_color_model
240
+[pixel transfer parameters]: https://www.khronos.org/opengl/wiki/Pixel_Transfer#Pixel_transfer_parameters
241
+[`GLTraits::Value`]: https://git.rcrnstn.net/rcrnstn/gltraits/#value
242
+[GLSL]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language
243
+
244
+### Exceptions
245
+
246
+All exceptions thrown are of the type `GLBase::Exception`, which inherits from
247
+[`std::runtime_error`][].
248
+
249
+[`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error
250
+
251
+### Debug
252
+
253
+A dynamic debug facility is used instead of asserts mainly because it allows
254
+dynamic error messages and it is often desirable to turn on debugging only
255
+during a problematic part of an application run.
256
+
257
+`DebugCallback` is an alias for [`std::function`][]`<void (std::string const &
258
+message)>`. These functions may not [`throw`][] since they are called by
259
+OpenGL, which may not support throwing exceptions across its stack frames.
260
+
261
+`int static debug({int debug})` gets/sets the global debug level. When `>= 1`,
262
+potentially costly debug operations are performed as part of other operations.
263
+When `>= 2` notifications may be emitted. Defaults to `0`.
264
+
265
+`DebugCallback static debug_callback({DebugCallback debug_callback})` gets/sets
266
+a callback that may be called by `GLBase`-derived classes (typically through
267
+`debug_message(...)`, see below). Defaults to a function which outputs the
268
+`mesage` argument to [`std::cerr`][], appends a newline and flushes.
269
+
270
+`void static debug_message(std::string const & message)` calls the callback
271
+registered with `debug_callback(...)` with `message` as argument if the
272
+callback is not empty (i.e. set to `nullptr`).
273
+
274
+`void static debug_action_(std::string const & action, std::string const &
275
+name)` calls `debug_message(...)` with a string containing `action` and `name`.
276
+
277
+[`std::function`]: https://en.cppreference.com/w/cpp/utility/functional/function
278
+[`throw`]: https://en.cppreference.com/w/cpp/language/throw
279
+[`std::cerr`]: https://en.cppreference.com/w/cpp/io/cerr
280
+
281
+### Check
282
+
283
+These functions throw an exception if some condition does not hold. It is
284
+recommended that `GLBase`-derived classes use similar helper functions, defined
285
+in an implementation file, to hide the string processing necessary to form a
286
+helpful exception message.
287
+
288
+`GLBase`-derived classes should treat error checking as free during
289
+construction and expensive data transfer operations. At all other times,
290
+non-critical error checking should only be performed if `debug() >= 1`.
291
+
292
+`void static check_supported(Version version_min, std::string const & extension
293
+= {})` checks that `supported(...)` returns `true` for the given arguments.
294
+
295
+`void static check_path_(std::string const & path)` checks that `path` is
296
+non-empty.
297
+
298
+`void static check_error_(GLenum error)` checks an error returned by
299
+[`glGetError`][].
300
+
301
+`void static check_type_(GLenum type, GLenum type_expected)` checks that `type`
302
+matches `type_expected`.
303
+
304
+`void static check_format_(GLenum format, GLenum format_expected)` checks that
305
+`format` matches `format_expected`.
306
+
307
+`void static check_internal_format_(GLenum internal_format)` checks that
308
+`internal_format` is supported.
309
+
310
+[`glGetError`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glGetError.xhtml
311
+
312
+### Fail
313
+
314
+These functions throw an exception and should be marked [`[[noreturn]]`][]. It
315
+is recommended that `GLBase`-derived classes use similar helper functions,
316
+defined in an implementation file, to hide the string processing necessary to
317
+form a helpful exception message.
318
+
319
+`[[noreturn]] void static fail_action_(std::string const & action, std::string
320
+const & name)` calls [`std::terminate`][] if called outside a [`catch`][]
321
+block. Otherwise, throws an exception with a message that includes `action` and
322
+`name` followed by `":\n"` and the [`what()`][] of the current exception if it
323
+is a (derives from) [`std::exception`][], otherwise simply rethrows the current
324
+exception.
325
+
326
+[`[[noreturn]]`]: https://en.cppreference.com/w/cpp/language/attributes/noreturn
327
+[`std::terminate`]: https://en.cppreference.com/w/cpp/error/terminate
328
+[`catch`]: https://en.cppreference.com/w/cpp/language/try_catch
329
+[`what()`]: https://en.cppreference.com/w/cpp/error/exception/what
330
+[`std::exception`]: https://en.cppreference.com/w/cpp/error/exception
331
+
332
+### String
333
+
334
+`std::string static str_path_(Path const & path)` returns `path` surrounded by
335
+double quotes.
336
+
337
+`std::string static str_paths_(Paths const & paths)` returns `paths` surrounded
338
+by double quotes and joined with commas.
339
+
340
+`std::string static str_enum_(GLenum name)` returns the hexadecimal string
341
+representation of `name` (often used as fallback).
342
+
343
+`std::string static str_error_(GLenum error)` returns the string representation
344
+of values returned by [`glGetError`][].
345
+
346
+`std::string static str_object_type_(GLenum object_type)` returns the string
347
+representation of `object_type`.
348
+
349
+`std::string static str_glsl_(GLenum glsl)` returns the string representation
350
+of `glsl`.
351
+
352
+`std::string static str_format_(GLenum format)` returns the string
353
+representation of `format`.
354
+
355
+`std::string static str_type_(GLenum type)` returns the string representation
356
+of `type`.
357
+
358
+`std::string static str_internal_format_(GLenum internal_format)` returns the
359
+string representation of `internal_format`.
360
+
361
+## Texture compression formats
362
+
363
+This is here for reference only.
364
+
365
+Extension conventions are defined in [`GL_ARB_texture_compression`][]. Formats
366
+are described in the [Khronons Data Format Specification "Compressed Texture
367
+Image Formats"][] section.
368
+
369
+[Direct3D][] defines `BC*` texture "block" compression formats which
370
+correspond with some of the formats described below. See:
371
+
372
+-   [Direct3D 10 Texture Block Compression][]
373
+-   [Direct3D 11 Texture Block Compression][]
374
+
375
+A non-exhaustive list of compressed texture formats defined by OpenGL:
376
+
377
+-   **S3TC**
378
+    -   Support: Widely supported  since OpenGL 1.3 (2001) (the non-sRGB
379
+        version is considered an "[ubiquitous extension][]"). Not core because
380
+        of patents, which expired 2017. [Mesa][] support since 17.3 (2017).
381
+    -   Origin: Introduced by [S3 Graphics][]' [Savage 3D][] (1998) and
382
+        supported by [Nvidia][]'s more popular [GeForce 256][] (also known
383
+        as the "GeForce 1") (1999). (`DXT2` and `DXT4` are premultiplied alpha
384
+        formats and not supported by OpenGL.)
385
+    -   Formats:
386
+        -   `{RGB{,A},SRGB{,_ALPHA}}_S3TC_DXT1_EXT` (`BC1`). 6:1 compression
387
+            ratio. The alpha variants only have a single bit of alpha, and when
388
+            `A` is `0` RGB must all be `0` as well. Therefore, it is best to
389
+            use premultiplied alpha when using the alpha variants.
390
+        -   `{RGBA,SRGB_ALPHA}_S3TC_DXT3_EXT` (`BC2`). 4:1 compression ratio.
391
+            Stores *uncompressed* alpha separately. Not generally recommended.
392
+        -   `{RGBA,SRGB_ALPHA}_S3TC_DXT5_EXT` (`BC3`). 4:1 compression ratio.
393
+            Stores *compressed* alpha separately.
394
+    -   References:
395
+        -   [`GL_EXT_texture_compression_s3tc`][]
396
+        -   [`GL_EXT_texture_compression_s3tc_srgb`][]
397
+        -   [`GL_EXT_texture_sRGB`][]
398
+        -   [`GL_NV_texture_compression_s3tc`][]
399
+        -   [`GL_S3_s3tc`][]
400
+        -   [OpenGL Wiki][S3TC OpenGL Wiki]
401
+        -   [Wikipedia][S3TC Wikipedia]
402
+-   VTC
403
+    -   Support: Not core. Only supported on Nvidia hardware.
404
+    -   Origin: Extension of S3TC to 3D ("volume") textures.
405
+    -   Formats: The same as S3TC, but interpreted differently for 3D textures.
406
+    -   References:
407
+        -   [`GL_NV_texture_compression_vtc`][]
408
+-   **RGTC**
409
+    -   Support: Core since OpenGL 3.0 (2008).
410
+    -   Origin: Uses a separate S3TC DXT5 alpha encoding for one or two
411
+        component red/green. Published at the same time as LATC.
412
+    -   Formats:
413
+        -   `{,SIGNED_}RED_RGTC1` (`BC4`). 2:1 compression ratio.
414
+        -   `{,SIGNED_}RG_RGTC2` (`BC5`). 2:1 compression ratio.
415
+    -   References:
416
+        -   [`GL_ARB_texture_compression_rgtc`][]
417
+        -   [`GL_EXT_texture_compression_rgtc`][]
418
+        -   [OpenGL Wiki][RGTC OpenGL Wiki]
419
+        -   [Wikipedia][RGTC Wikipedia]
420
+-   LATC:
421
+    -   Support: Not core, no ARB extension. Luminance/alpha formats in general
422
+        have seen a decline since the introduction of the programmable
423
+        pipeline, which can use [swizzle][]s to achieve the same effect.
424
+    -   Origin: Uses a separate S3TC DXT5 alpha encoding for one or two
425
+        component luminance/alpha. Published at the same time as RGTC.
426
+    -   Formats:
427
+        -   `{,SIGNED_}LUMINANCE_LATC1_EXT`. 2:1 compression ratio.
428
+        -   `{,SIGNED_}LUMINANCE_ALPHA_LATC2_EXT`. 2:1 compression ratio.
429
+    -   References
430
+        -   [`GL_EXT_texture_compression_latc`][]
431
+        -   [`GL_NV_texture_compression_latc`][]
432
+-   **BPTC**
433
+    -   Support: Core since OpenGL 4.2 (2011).
434
+    -   Origin: Block Partition texture compression.
435
+    -   Formats:
436
+        -   `RGB_BPTC_{UN,}SIGNED_FLOAT` (`BC6H`): 4:1 compression ratio.
437
+            Unsigned/signed 16-bit ("high dynamic range") float RGB.
438
+        -   `{RGBA,SRGB_ALPHA}_BPTC_UNORM` (`BC7`): 4:1 compression ratio.
439
+            Unsigned normalized integer RGBA/sRGBA.
440
+    -   References:
441
+        -   [`GL_ARB_texture_compression_bptc`][]
442
+        -   [`GL_EXT_texture_compression_bptc`][]
443
+        -   [OpenGL Wiki][BPTC OpenGL Wiki]
444
+        -   [Wikipedia][BPTC Wikipedia]
445
+-   **ETC**
446
+    -   Support: Core since OpenGL 4.3 (2012). Desktop OpenGL does not support
447
+        ETC1, only ETC2. Images encoded as ETC1 can be read by an ETC2 decoder.
448
+    -   Origins: [Ericsson][]. EAC presumably stands for "Ericsson Alpha
449
+        Compression" and is used to compress one or two channels (much like
450
+        RGTC uses the alpha compression format of S3TC DX5).
451
+    -   Formats:
452
+        -   `{,S}RGB8{,_PUNCHTHROUGH_ALPHA1}_ETC2` 6:1 compression ratio.
453
+        -   `{RGBA8,SRGB8_ALPHA8}_ETC2_EAC`. 4:1 compression ratio.
454
+        -   `{,SIGNED_}R11_EAC` 2:1 compression ratio.
455
+        -   `{,SIGNED_}RG11_EAC` 2:1 compression ratio.
456
+    -   References:
457
+        -   [`GL_ARB_ES3_compatibility`][]
458
+        -   [`GL_OES_compressed_ETC1_RGB8_texture`][]
459
+        -   [Wikipedia][ETC Wikipedia]
460
+-   ASTC:
461
+    -   Support: Not core. The extension is written against OpenGL ES, and
462
+        there is no wide desktop support, at least for the HDR version.
463
+    -   Origin: [ARM][] and [AMD][]. Variable block ("adaptive scalable")
464
+        texture compression.
465
+    -   Formats:
466
+        -   `{RGBA,SRGB8_ALPHA8}_ASTC_{4x4,5x{4,5},6x{5,6},8x{5,6,8},10x{5,6,8,10},12x{10,12}}_KHR`
467
+            A large number of variables are encoded per block so e.g. the
468
+            number of channels, dynamic range, and compression ratio vary.
469
+    -   References:
470
+        -   `GL_KHR_texture_compression_astc_ldr`, [`GL_KHR_texture_compression_astc_hdr`][]
471
+        -   [`GL_KHR_texture_compression_astc_sliced_3d`][]
472
+        -   [OpenGL Wiki][ASTC OpenGL Wiki]
473
+        -   [Wikipedia][ASTC Wikipedia]
474
+        -   [`ARM-software/astc-encoder`][]
475
+
476
+[`GL_ARB_texture_compression`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression.txt
477
+[Khronons Data Format Specification "Compressed Texture Image Formats"]: https://registry.khronos.org/DataFormat/specs/1.1/dataformat.1.1.html#_compressed_texture_image_formats
478
+[Direct3D]: https://en.wikipedia.org/wiki/Direct3D
479
+[Direct3D 10 Texture Block Compression]: https://learn.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression
480
+[Direct3D 11 Texture Block Compression]: https://learn.microsoft.com/en-us/windows/win32/direct3d11/texture-block-compression-in-direct3d-11
481
+[ubiquitous extension]: https://www.khronos.org/opengl/wiki/Ubiquitous_Extension
482
+[Mesa]: https://en.wikipedia.org/wiki/Mesa_(computer_graphics)
483
+[S3 Graphics]: https://en.wikipedia.org/wiki/S3_Graphics
484
+[Savage 3D]: https://en.wikipedia.org/wiki/S3_Savage#Savage_3D
485
+[Nvidia]: https://en.wikipedia.org/wiki/Nvidia
486
+[GeForce 256]: https://en.wikipedia.org/wiki/GeForce_256
487
+[`GL_EXT_texture_compression_s3tc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc.txt
488
+[`GL_EXT_texture_compression_s3tc_srgb`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_s3tc_srgb.txt
489
+[`GL_EXT_texture_sRGB`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_sRGB.txt
490
+[`GL_NV_texture_compression_s3tc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_s3tc.txt
491
+[`GL_S3_s3tc`]: https://registry.khronos.org/OpenGL/extensions/S3/S3_s3tc.txt
492
+[S3TC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/S3_Texture_Compression
493
+[S3TC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression
494
+[`GL_NV_texture_compression_vtc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_vtc.txt
495
+[`GL_ARB_texture_compression_rgtc`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression_rgtc.txt
496
+[`GL_EXT_texture_compression_rgtc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_rgtc.txt
497
+[RGTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/Red_Green_Texture_Compression
498
+[RGTC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression#BC4_and_BC5
499
+[swizzle]: https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Swizzling
500
+[`GL_EXT_texture_compression_latc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_latc.txt
501
+[`GL_NV_texture_compression_latc`]: https://registry.khronos.org/OpenGL/extensions/NV/NV_texture_compression_latc.txt
502
+[`GL_ARB_texture_compression_bptc`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_texture_compression_bptc.txt
503
+[`GL_EXT_texture_compression_bptc`]: https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_compression_bptc.txt
504
+[BPTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/BPTC_Texture_Compression
505
+[BPTC Wikipedia]: https://en.wikipedia.org/wiki/S3_Texture_Compression#BC6H_and_BC7
506
+[Ericsson]: https://en.wikipedia.org/wiki/Ericsson
507
+[`GL_ARB_ES3_compatibility`]: https://registry.khronos.org/OpenGL/extensions/ARB/ARB_ES3_compatibility.txt
508
+[`GL_OES_compressed_ETC1_RGB8_texture`]: https://registry.khronos.org/OpenGL/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
509
+[ETC Wikipedia]: https://en.wikipedia.org/wiki/Ericsson_Texture_Compression
510
+[ARM]: https://en.wikipedia.org/wiki/Arm_(company)
511
+[AMD]: https://en.wikipedia.org/wiki/AMD
512
+[`GL_KHR_texture_compression_astc_hdr`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_hdr.txt
513
+[`GL_KHR_texture_compression_astc_sliced_3d`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_texture_compression_astc_sliced_3d.txt
514
+[ASTC OpenGL Wiki]: https://www.khronos.org/opengl/wiki/ASTC_Texture_Compression
515
+[ASTC Wikipedia]: https://en.wikipedia.org/wiki/Adaptive_scalable_texture_compression
516
+[`ARM-software/astc-encoder`]: https://github.com/ARM-software/astc-encoder
9 517
 
10 518
 ## Building
11 519
 
12 520
new file mode 100644
... ...
@@ -0,0 +1,211 @@
1
+/// Guards
2
+
3
+#ifndef GLBASE_HPP_
4
+#define GLBASE_HPP_
5
+
6
+
7
+/// Includes
8
+
9
+#include <array>
10
+#include <functional>
11
+#include <stdexcept>
12
+#include <string>
13
+#include <utility>
14
+#include <vector>
15
+
16
+#ifndef GLBASE_INCLUDE
17
+#define GLBASE_INCLUDE <GL/glew.h>
18
+#endif
19
+#include GLBASE_INCLUDE // IWYU pragma: export
20
+
21
+
22
+/// Accessors
23
+
24
+#define GLBASE_GET_(TYPE, NAME, STATIC, CONST) \
25
+    TYPE STATIC const & NAME() CONST \
26
+    { \
27
+        return NAME##_; \
28
+    }
29
+
30
+#define GLBASE_SET_(TYPE, NAME, STATIC, CONST) \
31
+    template<typename Type> \
32
+    TYPE STATIC NAME(Type && NAME) \
33
+    { \
34
+        auto NAME##_old = std::move   (NAME##_); \
35
+        NAME##_         = std::forward(NAME); \
36
+        return NAME##_old; \
37
+    }
38
+
39
+#define GLBASE_GET(       TYPE, NAME) GLBASE_GET_(TYPE, NAME,, const)
40
+#define GLBASE_SET(       TYPE, NAME) GLBASE_SET_(TYPE, NAME,, const)
41
+#define GLBASE_GET_GLOBAL(TYPE, NAME) GLBASE_GET_(TYPE, NAME, static,)
42
+#define GLBASE_SET_GLOBAL(TYPE, NAME) GLBASE_SET_(TYPE, NAME, static,)
43
+
44
+#define GLBASE_ACCESS(TYPE, NAME) \
45
+    GLBASE_GET(TYPE, NAME) \
46
+    GLBASE_SET(TYPE, NAME)
47
+
48
+#define GLBASE_ACCESS_GLOBAL(TYPE, NAME) \
49
+    GLBASE_GET_GLOBAL(TYPE, NAME) \
50
+    GLBASE_SET_GLOBAL(TYPE, NAME)
51
+
52
+#define GLBASE_GLOBAL(NAME, INIT) \
53
+    decltype(NAME) thread_local NAME INIT;
54
+
55
+
56
+/// GLBase
57
+
58
+class GLBase
59
+{
60
+
61
+public:
62
+
63
+    //// Base
64
+
65
+    using Version = std::array<GLint, 2>;
66
+
67
+    GLBASE_ACCESS_GLOBAL(Version, version_max)
68
+
69
+    bool static supported(
70
+        Version             version_min,
71
+        std::string const & extension = {}
72
+    );
73
+
74
+    char  static const * string (GLenum name);
75
+    char  static const * string (GLenum name, GLuint index);
76
+    GLint static         integer(GLenum name);
77
+
78
+    //// Path
79
+
80
+    using Path  = std::string;
81
+    using Paths = std::vector<Path>;
82
+
83
+    //// Exceptions
84
+
85
+    struct Exception : std::runtime_error
86
+    {
87
+        using std::runtime_error::runtime_error;
88
+    };
89
+
90
+    //// Debug
91
+
92
+    using DebugCallback = std::function<void (std::string const & message)>;
93
+
94
+    GLBASE_GET_GLOBAL(int, debug)
95
+    int static debug(int debug);
96
+
97
+    GLBASE_ACCESS_GLOBAL(DebugCallback, debug_callback)
98
+
99
+    void static debug_message(std::string const & message)
100
+    {
101
+        if (debug_callback())
102
+            debug_callback()(message);
103
+    }
104
+
105
+    //// Check
106
+
107
+    void static check_supported(
108
+        Version             version_min,
109
+        std::string const & extension = {}
110
+    );
111
+
112
+protected:
113
+
114
+    //// Special member functions
115
+
116
+    explicit GLBase() = default;
117
+    ~GLBase() = default;
118
+
119
+    //// Path
120
+
121
+    Path static path_prefix_(
122
+        Path const & path,
123
+        Path const & prefix
124
+    );
125
+
126
+    //// TGA
127
+
128
+    class TGA_
129
+    {
130
+    public:
131
+        using Size = std::array<GLsizei, 2>;
132
+        using Data = std::vector<GLubyte>;
133
+        explicit TGA_(Size size, Data data, Path const & path = {});
134
+        TGA_ static read  (Path const & path);
135
+        void        write (Path const & path) const;
136
+        GLBASE_GET(Size, size);
137
+        GLBASE_GET(Data, data);
138
+    protected:
139
+        struct Header_ : std::array<GLubyte, 18>
140
+        {
141
+            explicit Header_(Size size);
142
+            Size tga_size() const;
143
+        };
144
+        std::string static name_(Path const & path);
145
+        void static check_header_(Header_ const & header);
146
+        void        check_data_size_() const;
147
+    private:
148
+        Size size_;
149
+        Data data_;
150
+    };
151
+
152
+    //// Debug
153
+
154
+    void static debug_action_(
155
+        std::string const & action,
156
+        std::string const & name
157
+    );
158
+
159
+    //// Check
160
+
161
+    void static check_path_(Path const & path);
162
+    void static check_error_(GLenum error);
163
+    void static check_type_(
164
+        GLenum type,
165
+        GLenum type_expected
166
+    );
167
+    void static check_format_(
168
+        GLenum format,
169
+        GLenum format_expected
170
+    );
171
+    void static check_internal_format_(
172
+        GLenum internal_format
173
+    );
174
+
175
+    //// Fail
176
+
177
+    [[noreturn]]
178
+    void static fail_action_(
179
+        std::string const & action,
180
+        std::string const & name
181
+    );
182
+
183
+    //// String
184
+
185
+    std::string static str_path_           (Path  const & path);
186
+    std::string static str_paths_          (Paths const & paths);
187
+    std::string static str_enum_           (GLenum name);
188
+    std::string static str_error_          (GLenum error);
189
+    std::string static str_object_type_    (GLenum object_type);
190
+    std::string static str_glsl_           (GLenum glsl);
191
+    std::string static str_format_         (GLenum format);
192
+    std::string static str_type_           (GLenum type);
193
+    std::string static str_internal_format_(GLenum internal_format);
194
+
195
+private:
196
+
197
+    //// Base
198
+
199
+    Version static thread_local version_max_;
200
+
201
+    //// Debug
202
+
203
+    int           static thread_local debug_;
204
+    DebugCallback static thread_local debug_callback_;
205
+
206
+};
207
+
208
+
209
+/// Guards
210
+
211
+#endif
0 212
new file mode 100644
... ...
@@ -0,0 +1,1034 @@
1
+/// Includes
2
+
3
+
4
+#include <glbase.hpp>
5
+
6
+#include <algorithm>
7
+#include <array>
8
+#include <cstdio>
9
+#include <fstream>
10
+#include <iostream>
11
+#include <iterator>
12
+#include <sstream>
13
+#include <string>
14
+#include <unordered_set>
15
+
16
+#define STR_EXCEPTION GLBase::Exception
17
+#include <str.hpp>
18
+
19
+
20
+/// Base
21
+
22
+
23
+GLBASE_GLOBAL(GLBase::version_max_, {})
24
+
25
+
26
+bool GLBase::supported(
27
+    Version             version_min,
28
+    std::string const & extension
29
+)
30
+{
31
+    if (!extension.empty() && extension.rfind("GL_", 0) == std::string::npos)
32
+        STR_THROW("Failed to parse extension \"" << extension << "\".");
33
+    auto static thread_local version    = Version{};
34
+    auto static thread_local extensions = std::unordered_set<std::string>{};
35
+    if (version == Version{})
36
+    {
37
+        auto const * version_str = string(GL_VERSION);
38
+        if (!version_str)
39
+            return false;
40
+        // NOLINTNEXTLINE
41
+        if (std::sscanf(version_str, "%d.%d", &version[0], &version[1]) != 2)
42
+            STR_THROW("Failed to parse version \"" << version_str << "\".");
43
+        if (version[0] >= 3)
44
+        {
45
+            auto count = (GLuint)integer(GL_NUM_EXTENSIONS);
46
+            for (auto index = GLuint{0}; index < count; ++index)
47
+            {
48
+                auto const * extension_str = string(GL_EXTENSIONS, index);
49
+                if (extension_str)
50
+                    extensions.insert(extension_str);
51
+            }
52
+        }
53
+        else
54
+        {
55
+            auto const * extensions_str = string(GL_EXTENSIONS);
56
+            if (!extensions_str)
57
+                return false;
58
+            auto istream = std::istringstream(extensions_str);
59
+            using iterator = std::istream_iterator<std::string>;
60
+            std::copy(
61
+                iterator(istream),
62
+                iterator(),
63
+                std::inserter(extensions, extensions.end())
64
+            );
65
+        }
66
+    }
67
+    if (version_max() != Version{})
68
+        if
69
+        (
70
+            version_min[0] > version_max()[0] ||
71
+            (
72
+                version_min[0] == version_max()[0] &&
73
+                version_min[1] >  version_max()[1]
74
+            )
75
+        )
76
+            return false;
77
+    if (version_min != Version{})
78
+        if
79
+        (
80
+            version[0] > version_min[0] ||
81
+            (
82
+                version[0] == version_min[0] &&
83
+                version[1] >= version_min[1]
84
+            )
85
+        )
86
+            return true;
87
+    if (!extension.empty())
88
+        if (extensions.find(extension) != extensions.end())
89
+            return true;
90
+    return false;
91
+}
92
+
93
+
94
+char const * GLBase::string(GLenum name)
95
+try
96
+{
97
+    auto const * string = (char const *)glGetString(name);
98
+    check_error_(glGetError());
99
+    return string;
100
+}
101
+catch (...)
102
+{
103
+    fail_action_("get string", str_enum_(name));
104
+}
105
+
106
+
107
+char const * GLBase::string(GLenum name, GLuint index)
108
+try
109
+{
110
+    // if (debug() >= 1)
111
+    //     check_supported({3, 0});
112
+    auto const * string = (char const *)glGetStringi(name, index);
113
+    check_error_(glGetError());
114
+    return string;
115
+}
116
+catch (...)
117
+{
118
+    fail_action_("get string", STR(str_enum_(name) << "[" << index << "]"));
119
+}
120
+
121
+
122
+GLint GLBase::integer(GLenum name)
123
+try
124
+{
125
+    auto integer = GLint{};
126
+    glGetIntegerv(name, &integer);
127
+    check_error_(glGetError());
128
+    return integer;
129
+}
130
+catch (...)
131
+{
132
+    fail_action_("get integer", str_enum_(name));
133
+}
134
+
135
+
136
+/// Path
137
+
138
+
139
+GLBase::Path GLBase::path_prefix_(
140
+    Path const & path,
141
+    Path const & prefix
142
+)
143
+{
144
+    check_path_(path);
145
+    if (prefix.empty() || path[0] == '/')
146
+        return path;
147
+    return STR(prefix << "/" << path);
148
+}
149
+
150
+
151
+/// TGA
152
+
153
+
154
+GLBase::TGA_::TGA_(Size size, Data data, Path const & path)
155
+try
156
+:
157
+    size_{size},
158
+    data_{std::move(data)}
159
+{
160
+    check_data_size_();
161
+}
162
+catch (...)
163
+{
164
+    fail_action_("create", name_(path));
165
+}
166
+
167
+
168
+GLBase::TGA_ GLBase::TGA_::read(Path const & path)
169
+try
170
+{
171
+    auto istream = std::ifstream(path, std::ios::binary);
172
+    auto header  = Header_({});
173
+    istream.read((char *)header.data(), (std::streamsize)header.size());
174
+    check_header_(header);
175
+    auto size = header.tga_size();
176
+    auto data = Data(4 * (std::size_t)size[0] * (std::size_t)size[1]);
177
+    istream.read((char *)data.data(), (std::streamsize)data.size());
178
+    if (!istream)
179
+        STR_THROW_ERRNO();
180
+    if (!istream.eof())
181
+        STR_THROW("Garbage at end of file.");
182
+    return TGA_(size, std::move(data), path);;
183
+}
184
+catch (...)
185
+{
186
+    fail_action_("read", name_(path));
187
+}
188
+
189
+
190
+void GLBase::TGA_::write(Path const & path) const
191
+try
192
+{
193
+    auto header  = Header_(size());
194
+    auto ostream = std::ofstream(path, std::ios::binary);
195
+    ostream.write((char *)header.data(), (std::streamsize)header.size());
196
+    ostream.write((char *)data_ .data(), (std::streamsize)data_ .size());
197
+    if (!ostream)
198
+        STR_THROW_ERRNO();
199
+}
200
+catch (...)
201
+{
202
+    fail_action_("write", name_(path));
203
+}
204
+
205
+
206
+GLBase::TGA_::Header_::Header_(Size size)
207
+:
208
+    std::array<GLubyte, 18>{                // NOLINT
209
+        0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // NOLINT
210
+        (GLubyte)(size[0] >> 0),            // NOLINT
211
+        (GLubyte)(size[0] >> 8),            // NOLINT
212
+        (GLubyte)(size[1] >> 0),            // NOLINT
213
+        (GLubyte)(size[1] >> 8),            // NOLINT
214
+        32, 0,                              // NOLINT
215
+    }
216
+{
217
+}
218
+
219
+
220
+GLBase::TGA_::Size GLBase::TGA_::Header_::tga_size() const
221
+{
222
+    return {
223
+        (GLsizei)((*this)[12]) << 0 | // NOLINT
224
+        (GLsizei)((*this)[13]) << 8,  // NOLINT
225
+        (GLsizei)((*this)[14]) << 0 | // NOLINT
226
+        (GLsizei)((*this)[15]) << 8,  // NOLINT
227
+    };
228
+}
229
+
230
+
231
+std::string GLBase::TGA_::name_(Path const & path)
232
+{
233
+    return STR("TGA" << " " << str_path_(path));
234
+}
235
+
236
+
237
+void GLBase::TGA_::check_header_(Header_ const & header)
238
+{
239
+    auto header_ = Header_(header.tga_size());
240
+    if (header != header_)
241
+        STR_THROW(
242
+            "Expected TGA header"                      << " " <<
243
+            "[" << STR_JOIN(", ", byte, byte, header_) << "]" << ", " <<
244
+            "got"                                      << " " <<
245
+            "[" << STR_JOIN(", ", byte, byte, header)  << "]" << "."
246
+        );
247
+}
248
+
249
+
250
+void GLBase::TGA_::check_data_size_() const
251
+{
252
+    auto size = this->size();
253
+    auto data_size = (std::size_t)(4 * size[0] * size[1]);
254
+    if (data_size != data_.size())
255
+        STR_THROW(
256
+            "Expected TGA data size " << data_size    << ", " <<
257
+            "got "                    << data_.size() << "."
258
+        );
259
+}
260
+
261
+
262
+//// Debug
263
+
264
+
265
+GLBASE_GLOBAL(GLBase::debug_,          {0})
266
+GLBASE_GLOBAL(GLBase::debug_callback_, {[](std::string const & message) {
267
+    std::cerr << message << std::endl;
268
+}})
269
+
270
+
271
+int GLBase::debug(int debug)
272
+{
273
+    auto debug_old = debug_;
274
+    debug_         = debug;
275
+    if (supported({4, 3}, "GL_KHR_debug"))
276
+    {
277
+        if (debug_old && !debug)
278
+            glDisable(GL_DEBUG_OUTPUT);
279
+        if (!debug_old && debug)
280
+        {
281
+            glEnable(GL_DEBUG_OUTPUT);
282
+            glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
283
+            glDebugMessageControl(
284
+                GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE,
285
+                0, nullptr,
286
+                GL_TRUE
287
+            );
288
+        }
289
+        if (debug_old >= 2 && debug < 2)
290
+            glDebugMessageControl(
291
+                GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION,
292
+                0, nullptr,
293
+                GL_FALSE
294
+            );
295
+        if (debug_old < 2 && debug >= 2)
296
+            glDebugMessageControl(
297
+                GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION,
298
+                0, nullptr,
299
+                GL_TRUE
300
+            );
301
+    }
302
+    return debug_old;
303
+}
304
+
305
+
306
+void GLBase::debug_action_(
307
+    std::string const & action,
308
+    std::string const & name
309
+)
310
+{
311
+    debug_message(STR("Trying to " << action << " " << name << "."));
312
+}
313
+
314
+
315
+/// Check
316
+
317
+
318
+void GLBase::check_path_(Path const & path)
319
+{
320
+    if (path.empty())
321
+        STR_THROW(
322
+            "Expected " << "non-empty path" << ", " <<
323
+            "got "      << str_path_(path)  << "."
324
+        );
325
+}
326
+
327
+
328
+void GLBase::check_error_(GLenum error)
329
+{
330
+    if (error != GL_NO_ERROR)
331
+        STR_THROW(
332
+            "Expected " << "no error"        << ", " <<
333
+            "got "      << str_error_(error) << "."
334
+        );
335
+}
336
+
337
+
338
+void GLBase::check_supported(
339
+    Version             version_min,
340
+    std::string const & extension
341
+)
342
+{
343
+    if (!supported(version_min, extension))
344
+    {
345
+        auto const * version_str = string(GL_VERSION);
346
+        STR_THROW(
347
+            "Expected OpenGL version >=" <<
348
+                STR_JOIN(".", it, it, version_min) <<
349
+                (
350
+                    !extension.empty()
351
+                        ? STR(" or extension " << extension)
352
+                        : ""
353
+                ) <<
354
+            ", " <<
355
+            "got " <<
356
+                (
357
+                    version_str
358
+                        ? version_str
359
+                        : "none (no current context?)"
360
+                ) <<
361
+            "."
362
+        );
363
+    }
364
+}
365
+
366
+
367
+void GLBase::check_type_(
368
+    GLenum type,
369
+    GLenum type_expected
370
+)
371
+{
372
+    if (type != type_expected)
373
+        STR_THROW(
374
+            "Expected type " << str_type_(type_expected) << ", " <<
375
+            "got "           << str_type_(type)          << "."
376
+        );
377
+}
378
+
379
+
380
+void GLBase::check_format_(
381
+    GLenum format,
382
+    GLenum format_expected
383
+)
384
+{
385
+    if (format != format_expected)
386
+        STR_THROW(
387
+            "Expected format " << str_format_(format_expected) << ", " <<
388
+            "got "             << str_format_(format)          << "."
389
+        );
390
+}
391
+
392
+
393
+void GLBase::check_internal_format_(GLenum internal_format)
394
+{
395
+    switch (internal_format)
396
+    {
397
+        case GL_RED:
398
+        case GL_RGB:
399
+        case GL_RGBA:
400
+        case GL_DEPTH_COMPONENT:
401
+        case GL_STENCIL_INDEX:
402
+            check_supported({1, 0});
403
+            return;
404
+        case GL_R3_G3_B2:
405
+            check_supported({1, 1});
406
+            return;
407
+        case GL_RGB4:
408
+        case GL_RGB5:
409
+        case GL_RGB8:
410
+        case GL_RGB10:
411
+        case GL_RGB12:
412
+        case GL_RGB16:
413
+        case GL_RGB5_A1:
414
+        case GL_RGB10_A2:
415
+        case GL_RGBA2:
416
+        case GL_RGBA4:
417
+        case GL_RGBA8:
418
+        case GL_RGBA12:
419
+        case GL_RGBA16:
420
+            check_supported({1, 1}, "GL_EXT_texture");
421
+            return;
422
+        case GL_RGB2_EXT:
423
+            check_supported({}, "GL_EXT_texture");
424
+            return;
425
+        case GL_COMPRESSED_RGB:
426
+        case GL_COMPRESSED_RGBA:
427
+            check_supported({1, 3}, "GL_ARB_texture_compression");
428
+            return;
429
+        case GL_DEPTH_COMPONENT16:
430
+        case GL_DEPTH_COMPONENT24:
431
+        case GL_DEPTH_COMPONENT32:
432
+            check_supported({1, 4}, "GL_ARB_depth_texture");
433
+            return;
434
+        case GL_SRGB:
435
+        case GL_SRGB8:
436
+        case GL_SRGB_ALPHA:
437
+        case GL_SRGB8_ALPHA8:
438
+        case GL_COMPRESSED_SRGB:
439
+        case GL_COMPRESSED_SRGB_ALPHA:
440
+            check_supported({2, 1}, "GL_EXT_texture_sRGB");
441
+            return;
442
+        case GL_SR8_EXT:
443
+            check_supported({}, "GL_EXT_texture_sRGB_R8");
444
+            return;
445
+        case GL_SRG8_EXT:
446
+            check_supported({}, "GL_EXT_texture_sRGB_RG8");
447
+            return;
448
+        case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
449
+        case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
450
+        case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
451
+        case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
452
+            check_supported({}, "GL_EXT_texture_compression_s3tc");
453
+            return;
454
+        case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
455
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
456
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
457
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
458
+            check_supported({}, "GL_EXT_texture_sRGB");
459
+            check_supported({}, "GL_EXT_texture_compression_s3tc");
460
+            return;
461
+        case GL_COMPRESSED_RED:
462
+        case GL_COMPRESSED_RG:
463
+            check_supported({3, 0});
464
+            return;
465
+        case GL_COMPRESSED_RED_RGTC1:
466
+        case GL_COMPRESSED_RG_RGTC2:
467
+        case GL_COMPRESSED_SIGNED_RED_RGTC1:
468
+        case GL_COMPRESSED_SIGNED_RG_RGTC2:
469
+            check_supported({3, 0}, "GL_ARB_texture_compression_rgtc");
470
+            return;
471
+        case GL_RGB16F:
472
+        case GL_RGB32F:
473
+        case GL_RGBA16F:
474
+        case GL_RGBA32F:
475
+            check_supported({3, 0}, "GL_ARB_texture_float");
476
+            return;
477
+        case GL_RGB8I:
478
+        case GL_RGB8UI:
479
+        case GL_RGB16I:
480
+        case GL_RGB16UI:
481
+        case GL_RGB32I:
482
+        case GL_RGB32UI:
483
+        case GL_RGBA8I:
484
+        case GL_RGBA8UI:
485
+        case GL_RGBA16I:
486
+        case GL_RGBA16UI:
487
+        case GL_RGBA32I:
488
+        case GL_RGBA32UI:
489
+            check_supported({3, 0}, "GL_EXT_texture_integer");
490
+            return;
491
+        case GL_R8:
492
+        case GL_R8I:
493
+        case GL_R8UI:
494
+        case GL_R16:
495
+        case GL_R16I:
496
+        case GL_R16UI:
497
+        case GL_R32I:
498
+        case GL_R32UI:
499
+        case GL_R16F:
500
+        case GL_R32F:
501
+        case GL_RG:
502
+        case GL_RG8:
503
+        case GL_RG8I:
504
+        case GL_RG8UI:
505
+        case GL_RG16:
506
+        case GL_RG16I:
507
+        case GL_RG16UI:
508
+        case GL_RG32I:
509
+        case GL_RG32UI:
510
+        case GL_RG16F:
511
+        case GL_RG32F:
512
+            check_supported({3, 0}, "GL_ARB_texture_rg");
513
+            return;
514
+        case GL_R11F_G11F_B10F:
515
+            check_supported({3, 0}, "GL_EXT_packed_float");
516
+            return;
517
+        case GL_RGB9_E5:
518
+            check_supported({3, 0}, "GL_EXT_texture_shared_exponent");
519
+            return;
520
+        case GL_DEPTH_STENCIL:
521
+        case GL_DEPTH24_STENCIL8:
522
+            check_supported({3, 0}, "GL_EXT_packed_depth_stencil");
523
+            return;
524
+        case GL_DEPTH32F_STENCIL8:
525
+        case GL_DEPTH_COMPONENT32F:
526
+            check_supported({3, 0}, "GL_ARB_depth_buffer_float");
527
+            return;
528
+        case GL_STENCIL_INDEX1:
529
+        case GL_STENCIL_INDEX4:
530
+        case GL_STENCIL_INDEX8:
531
+        case GL_STENCIL_INDEX16:
532
+            check_supported({3, 0}, "GL_ARB_framebuffer_object");
533
+            return;
534
+        case GL_R8_SNORM:
535
+        case GL_R16_SNORM:
536
+        case GL_RG8_SNORM:
537
+        case GL_RG16_SNORM:
538
+        case GL_RGB8_SNORM:
539
+        case GL_RGB16_SNORM:
540
+        case GL_RGBA8_SNORM:
541
+        case GL_RGBA16_SNORM:
542
+            check_supported({3, 1}, "GL_EXT_texture_snorm");
543
+            return;
544
+        case GL_RGB10_A2UI:
545
+            check_supported({3, 3}, "GL_ARB_texture_rgb10_a2ui");
546
+            return;
547
+        case GL_RGB565:
548
+            check_supported({4, 1}, "GL_ARB_ES2_compatibility");
549
+            return;
550
+        case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
551
+        case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
552
+        case GL_COMPRESSED_RGBA_BPTC_UNORM:
553
+        case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
554
+            check_supported({4, 2}, "GL_ARB_texture_compression_bptc");
555
+            return;
556
+        case GL_COMPRESSED_RGB8_ETC2:
557
+        case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
558
+        case GL_COMPRESSED_SRGB8_ETC2:
559
+        case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
560
+        case GL_COMPRESSED_RGBA8_ETC2_EAC:
561
+        case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
562
+        case GL_COMPRESSED_R11_EAC:
563
+        case GL_COMPRESSED_RG11_EAC:
564
+        case GL_COMPRESSED_SIGNED_R11_EAC:
565
+        case GL_COMPRESSED_SIGNED_RG11_EAC:
566
+            check_supported({4, 3}, "GL_ARB_ES3_compatibility");
567
+            return;
568
+        case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
569
+        case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
570
+        case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
571
+        case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
572
+        case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
573
+        case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
574
+        case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
575
+        case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
576
+        case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
577
+        case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
578
+        case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
579
+        case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
580
+        case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
581
+        case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
582
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
583
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
584
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
585
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
586
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
587
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
588
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
589
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
590
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
591
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
592
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
593
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
594
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
595
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
596
+            check_supported({}, "GL_KHR_texture_compression_astc_ldr");
597
+            return;
598
+        default:
599
+            STR_THROW(
600
+                "Expected " << "internal format"                     << ", " <<
601
+                "got "      << str_internal_format_(internal_format) << "."
602
+            );
603
+    }
604
+}
605
+
606
+
607
+/// Fail
608
+
609
+
610
+void GLBase::fail_action_(
611
+    std::string const & action,
612
+    std::string const & name
613
+)
614
+{
615
+    STR_RETHROW("Failed to " << action << " " << name << ":\n");
616
+}
617
+
618
+
619
+/// String
620
+
621
+
622
+std::string GLBase::str_path_(Path const & path)
623
+{
624
+    return STR("\"" << path << "\"");
625
+}
626
+
627
+
628
+std::string GLBase::str_paths_(Paths const & paths)
629
+{
630
+    return STR_JOIN(", ", path, str_path_(path), paths);
631
+}
632
+
633
+
634
+std::string GLBase::str_enum_(GLenum name)
635
+{
636
+    return STR(std::hex << std::showbase << std::uppercase << name);
637
+}
638
+
639
+
640
+std::string GLBase::str_error_(GLenum error)
641
+{
642
+    switch (error)
643
+    {
644
+        STR_CASE(GL_NO_ERROR)
645
+        STR_CASE(GL_INVALID_ENUM)
646
+        STR_CASE(GL_INVALID_VALUE)
647
+        STR_CASE(GL_INVALID_OPERATION)
648
+        STR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
649
+        STR_CASE(GL_OUT_OF_MEMORY)
650
+        STR_CASE(GL_STACK_OVERFLOW)
651
+        STR_CASE(GL_STACK_UNDERFLOW)
652
+        STR_CASE(GL_CONTEXT_LOST) // GL_KHR_robustness
653
+        default:
654
+            return str_enum_(error);
655
+    }
656
+}
657
+
658
+
659
+std::string GLBase::str_object_type_(GLenum object_type)
660
+{
661
+    switch (object_type)
662
+    {
663
+        STR_CASE(GL_TEXTURE)
664
+        STR_CASE(GL_BUFFER)
665
+        STR_CASE(GL_SHADER)
666
+        STR_CASE(GL_PROGRAM)
667
+        STR_CASE(GL_PROGRAM_PIPELINE)
668
+        STR_CASE(GL_FRAMEBUFFER)
669
+        STR_CASE(GL_RENDERBUFFER)
670
+        STR_CASE(GL_VERTEX_ARRAY)
671
+        STR_CASE(GL_TRANSFORM_FEEDBACK)
672
+        STR_CASE(GL_SAMPLER)
673
+        STR_CASE(GL_QUERY)
674
+        default:
675
+            return str_enum_(object_type);
676
+    }
677
+}
678
+
679
+
680
+std::string GLBase::str_glsl_(GLenum glsl)
681
+{
682
+    switch (glsl)
683
+    {
684
+        STR_CASE(GL_FLOAT)
685
+        STR_CASE(GL_FLOAT_VEC2)
686
+        STR_CASE(GL_FLOAT_VEC3)
687
+        STR_CASE(GL_FLOAT_VEC4)
688
+        STR_CASE(GL_FLOAT_MAT2)
689
+        STR_CASE(GL_FLOAT_MAT2x3)
690
+        STR_CASE(GL_FLOAT_MAT2x4)
691
+        STR_CASE(GL_FLOAT_MAT3x2)
692
+        STR_CASE(GL_FLOAT_MAT3)
693
+        STR_CASE(GL_FLOAT_MAT3x4)
694
+        STR_CASE(GL_FLOAT_MAT4x2)
695
+        STR_CASE(GL_FLOAT_MAT4x3)
696
+        STR_CASE(GL_FLOAT_MAT4)
697
+        STR_CASE(GL_INT)
698
+        STR_CASE(GL_INT_VEC2)
699
+        STR_CASE(GL_INT_VEC3)
700
+        STR_CASE(GL_INT_VEC4)
701
+        STR_CASE(GL_UNSIGNED_INT)
702
+        STR_CASE(GL_UNSIGNED_INT_VEC2)
703
+        STR_CASE(GL_UNSIGNED_INT_VEC3)
704
+        STR_CASE(GL_UNSIGNED_INT_VEC4)
705
+        STR_CASE(GL_DOUBLE)
706
+        STR_CASE(GL_DOUBLE_VEC2)
707
+        STR_CASE(GL_DOUBLE_VEC3)
708
+        STR_CASE(GL_DOUBLE_VEC4)
709
+        STR_CASE(GL_DOUBLE_MAT2)
710
+        STR_CASE(GL_DOUBLE_MAT2x3)
711
+        STR_CASE(GL_DOUBLE_MAT2x4)
712
+        STR_CASE(GL_DOUBLE_MAT3x2)
713
+        STR_CASE(GL_DOUBLE_MAT3)
714
+        STR_CASE(GL_DOUBLE_MAT3x4)
715
+        STR_CASE(GL_DOUBLE_MAT4x2)
716
+        STR_CASE(GL_DOUBLE_MAT4x3)
717
+        STR_CASE(GL_DOUBLE_MAT4)
718
+        STR_CASE(GL_BOOL)
719
+        STR_CASE(GL_BOOL_VEC2)
720
+        STR_CASE(GL_BOOL_VEC3)
721
+        STR_CASE(GL_BOOL_VEC4)
722
+        STR_CASE(GL_SAMPLER_1D)
723
+        STR_CASE(GL_SAMPLER_1D_SHADOW)
724
+        STR_CASE(GL_SAMPLER_1D_ARRAY)
725
+        STR_CASE(GL_SAMPLER_1D_ARRAY_SHADOW)
726
+        STR_CASE(GL_SAMPLER_2D)
727
+        STR_CASE(GL_SAMPLER_2D_SHADOW)
728
+        STR_CASE(GL_SAMPLER_2D_ARRAY)
729
+        STR_CASE(GL_SAMPLER_2D_ARRAY_SHADOW)
730
+        STR_CASE(GL_SAMPLER_2D_RECT)
731
+        STR_CASE(GL_SAMPLER_2D_RECT_SHADOW)
732
+        STR_CASE(GL_SAMPLER_2D_MULTISAMPLE)
733
+        STR_CASE(GL_SAMPLER_2D_MULTISAMPLE_ARRAY)
734
+        STR_CASE(GL_SAMPLER_3D)
735
+        STR_CASE(GL_SAMPLER_CUBE)
736
+        STR_CASE(GL_SAMPLER_CUBE_SHADOW)
737
+        STR_CASE(GL_SAMPLER_CUBE_MAP_ARRAY)
738
+        STR_CASE(GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW)
739
+        STR_CASE(GL_SAMPLER_BUFFER)
740
+        STR_CASE(GL_INT_SAMPLER_1D)
741
+        STR_CASE(GL_INT_SAMPLER_1D_ARRAY)
742
+        STR_CASE(GL_INT_SAMPLER_2D)
743
+        STR_CASE(GL_INT_SAMPLER_2D_ARRAY)
744
+        STR_CASE(GL_INT_SAMPLER_2D_RECT)
745
+        STR_CASE(GL_INT_SAMPLER_2D_MULTISAMPLE)
746
+        STR_CASE(GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
747
+        STR_CASE(GL_INT_SAMPLER_3D)
748
+        STR_CASE(GL_INT_SAMPLER_CUBE)
749
+        STR_CASE(GL_INT_SAMPLER_CUBE_MAP_ARRAY)
750
+        STR_CASE(GL_INT_SAMPLER_BUFFER)
751
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_1D)
752
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_1D_ARRAY)
753
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D)
754
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_ARRAY)
755
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_RECT)
756
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE)
757
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY)
758
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_3D)
759
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_CUBE)
760
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY)
761
+        STR_CASE(GL_UNSIGNED_INT_SAMPLER_BUFFER)
762
+        STR_CASE(GL_IMAGE_1D)
763
+        STR_CASE(GL_IMAGE_1D_ARRAY)
764
+        STR_CASE(GL_IMAGE_2D)
765
+        STR_CASE(GL_IMAGE_2D_ARRAY)
766
+        STR_CASE(GL_IMAGE_2D_RECT)
767
+        STR_CASE(GL_IMAGE_2D_MULTISAMPLE)
768
+        STR_CASE(GL_IMAGE_2D_MULTISAMPLE_ARRAY)
769
+        STR_CASE(GL_IMAGE_3D)
770
+        STR_CASE(GL_IMAGE_CUBE)
771
+        STR_CASE(GL_IMAGE_CUBE_MAP_ARRAY)
772
+        STR_CASE(GL_IMAGE_BUFFER)
773
+        STR_CASE(GL_INT_IMAGE_1D)
774
+        STR_CASE(GL_INT_IMAGE_1D_ARRAY)
775
+        STR_CASE(GL_INT_IMAGE_2D)
776
+        STR_CASE(GL_INT_IMAGE_2D_ARRAY)
777
+        STR_CASE(GL_INT_IMAGE_2D_RECT)
778
+        STR_CASE(GL_INT_IMAGE_2D_MULTISAMPLE)
779
+        STR_CASE(GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY)
780
+        STR_CASE(GL_INT_IMAGE_3D)
781
+        STR_CASE(GL_INT_IMAGE_CUBE)
782
+        STR_CASE(GL_INT_IMAGE_CUBE_MAP_ARRAY)
783
+        STR_CASE(GL_INT_IMAGE_BUFFER)
784
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_1D)
785
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_1D_ARRAY)
786
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_2D)
787
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_ARRAY)
788
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_RECT)
789
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE)
790
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY)
791
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_3D)
792
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_CUBE)
793
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY)
794
+        STR_CASE(GL_UNSIGNED_INT_IMAGE_BUFFER)
795
+        // GL_ARB_gpu_shader_int64
796
+        STR_CASE(GL_INT64_ARB)
797
+        STR_CASE(GL_INT64_VEC2_ARB)
798
+        STR_CASE(GL_INT64_VEC3_ARB)
799
+        STR_CASE(GL_INT64_VEC4_ARB)
800
+        STR_CASE(GL_UNSIGNED_INT64_ARB) // GL_ARB_bindless_texture
801
+        STR_CASE(GL_UNSIGNED_INT64_VEC2_ARB)
802
+        STR_CASE(GL_UNSIGNED_INT64_VEC3_ARB)
803
+        STR_CASE(GL_UNSIGNED_INT64_VEC4_ARB)
804
+        default:
805
+            return str_enum_(glsl);
806
+    }
807
+}
808
+
809
+
810
+std::string GLBase::str_format_(GLenum format)
811
+{
812
+    switch (format)
813
+    {
814
+        STR_CASE(GL_RED)
815
+        STR_CASE(GL_RED_INTEGER)
816
+        STR_CASE(GL_GREEN)
817
+        STR_CASE(GL_GREEN_INTEGER)
818
+        STR_CASE(GL_BLUE)
819
+        STR_CASE(GL_BLUE_INTEGER)
820
+        STR_CASE(GL_ALPHA)
821
+        STR_CASE(GL_ALPHA_INTEGER)
822
+        STR_CASE(GL_RG)
823
+        STR_CASE(GL_RG_INTEGER)
824
+        STR_CASE(GL_RGB)
825
+        STR_CASE(GL_RGB_INTEGER)
826
+        STR_CASE(GL_RGBA)
827
+        STR_CASE(GL_RGBA_INTEGER)
828
+        STR_CASE(GL_BGR)
829
+        STR_CASE(GL_BGR_INTEGER)
830
+        STR_CASE(GL_BGRA)
831
+        STR_CASE(GL_BGRA_INTEGER)
832
+        STR_CASE(GL_ABGR_EXT) // GL_EXT_abgr
833
+        STR_CASE(GL_DEPTH_STENCIL)
834
+        STR_CASE(GL_DEPTH_COMPONENT)
835
+        STR_CASE(GL_STENCIL_INDEX)
836
+        default:
837
+            return str_enum_(format);
838
+    }
839
+}
840
+
841
+
842
+std::string GLBase::str_type_(GLenum type)
843
+{
844
+    switch (type)
845
+    {
846
+        STR_CASE(GL_FLOAT)
847
+        STR_CASE(GL_BYTE)
848
+        STR_CASE(GL_SHORT)
849
+        STR_CASE(GL_INT)
850
+        STR_CASE(GL_UNSIGNED_BYTE)
851
+        STR_CASE(GL_UNSIGNED_SHORT)
852
+        STR_CASE(GL_UNSIGNED_INT)
853
+        STR_CASE(GL_UNSIGNED_BYTE_3_3_2)
854
+        STR_CASE(GL_UNSIGNED_BYTE_2_3_3_REV)
855
+        STR_CASE(GL_UNSIGNED_SHORT_5_6_5)
856
+        STR_CASE(GL_UNSIGNED_SHORT_5_6_5_REV)
857
+        STR_CASE(GL_UNSIGNED_SHORT_4_4_4_4)
858
+        STR_CASE(GL_UNSIGNED_SHORT_4_4_4_4_REV)
859
+        STR_CASE(GL_UNSIGNED_SHORT_5_5_5_1)
860
+        STR_CASE(GL_UNSIGNED_SHORT_1_5_5_5_REV)
861
+        STR_CASE(GL_UNSIGNED_INT_8_8_8_8)
862
+        STR_CASE(GL_UNSIGNED_INT_8_8_8_8_REV)
863
+        STR_CASE(GL_UNSIGNED_INT_10_10_10_2)
864
+        STR_CASE(GL_UNSIGNED_INT_2_10_10_10_REV)
865
+        STR_CASE(GL_UNSIGNED_INT_10F_11F_11F_REV)
866
+        STR_CASE(GL_UNSIGNED_INT_5_9_9_9_REV)
867
+        STR_CASE(GL_UNSIGNED_INT_24_8)
868
+        STR_CASE(GL_FLOAT_32_UNSIGNED_INT_24_8_REV)
869
+        STR_CASE(GL_DOUBLE)
870
+        STR_CASE(GL_HALF_FLOAT)
871
+        STR_CASE(GL_FIXED)
872
+        // GL_ARB_gpu_shader_int64
873
+        STR_CASE(GL_INT64_ARB)
874
+        STR_CASE(GL_UNSIGNED_INT64_ARB) // GL_ARB_bindless_texture
875
+        default:
876
+            return str_enum_(type);
877
+    }
878
+}
879
+
880
+
881
+std::string GLBase::str_internal_format_(GLenum internal_format)
882
+{
883
+    switch (internal_format)
884
+    {
885
+        STR_CASE(GL_RED)
886
+        STR_CASE(GL_R8)
887
+        STR_CASE(GL_R8I)
888
+        STR_CASE(GL_R8UI)
889
+        STR_CASE(GL_R16)
890
+        STR_CASE(GL_R16I)
891
+        STR_CASE(GL_R16UI)
892
+        STR_CASE(GL_R32I)
893
+        STR_CASE(GL_R32UI)
894
+        STR_CASE(GL_R16F)
895
+        STR_CASE(GL_R32F)
896
+        STR_CASE(GL_RG)
897
+        STR_CASE(GL_RG8)
898
+        STR_CASE(GL_RG8I)
899
+        STR_CASE(GL_RG8UI)
900
+        STR_CASE(GL_RG16)
901
+        STR_CASE(GL_RG16I)
902
+        STR_CASE(GL_RG16UI)
903
+        STR_CASE(GL_RG32I)
904
+        STR_CASE(GL_RG32UI)
905
+        STR_CASE(GL_RG16F)
906
+        STR_CASE(GL_RG32F)
907
+        STR_CASE(GL_RGB)
908
+        STR_CASE(GL_RGB2_EXT) // GL_EXT_texture
909
+        STR_CASE(GL_R3_G3_B2)
910
+        STR_CASE(GL_RGB4)
911
+        STR_CASE(GL_RGB5)
912
+        STR_CASE(GL_RGB565)
913
+        STR_CASE(GL_RGB8)
914
+        STR_CASE(GL_RGB8I)
915
+        STR_CASE(GL_RGB8UI)
916
+        STR_CASE(GL_RGB10)
917
+        STR_CASE(GL_RGB12)
918
+        STR_CASE(GL_RGB16)
919
+        STR_CASE(GL_RGB16I)
920
+        STR_CASE(GL_RGB16UI)
921
+        STR_CASE(GL_RGB32I)
922
+        STR_CASE(GL_RGB32UI)
923
+        STR_CASE(GL_RGB16F)
924
+        STR_CASE(GL_RGB32F)
925
+        STR_CASE(GL_R11F_G11F_B10F)
926
+        STR_CASE(GL_RGB9_E5)
927
+        STR_CASE(GL_RGBA)
928
+        STR_CASE(GL_RGBA2)
929
+        STR_CASE(GL_RGBA4)
930
+        STR_CASE(GL_RGBA8)
931
+        STR_CASE(GL_RGBA8I)
932
+        STR_CASE(GL_RGBA8UI)
933
+        STR_CASE(GL_RGBA12)
934
+        STR_CASE(GL_RGBA16)
935
+        STR_CASE(GL_RGBA16I)
936
+        STR_CASE(GL_RGBA16UI)
937
+        STR_CASE(GL_RGBA32I)
938
+        STR_CASE(GL_RGBA32UI)
939
+        STR_CASE(GL_RGB5_A1)
940
+        STR_CASE(GL_RGB10_A2)
941
+        STR_CASE(GL_RGB10_A2UI)
942
+        STR_CASE(GL_RGBA16F)
943
+        STR_CASE(GL_RGBA32F)
944
+        STR_CASE(GL_R8_SNORM)
945
+        STR_CASE(GL_R16_SNORM)
946
+        STR_CASE(GL_RG8_SNORM)
947
+        STR_CASE(GL_RG16_SNORM)
948
+        STR_CASE(GL_RGB8_SNORM)
949
+        STR_CASE(GL_RGB16_SNORM)
950
+        STR_CASE(GL_RGBA8_SNORM)
951
+        STR_CASE(GL_RGBA16_SNORM)
952
+        STR_CASE(GL_SR8_EXT)  // GL_EXT_texture_sRGB_R8
953
+        STR_CASE(GL_SRG8_EXT) // GL_EXT_texture_sRGB_RG8
954
+        STR_CASE(GL_SRGB)
955
+        STR_CASE(GL_SRGB8)
956
+        STR_CASE(GL_SRGB_ALPHA)
957
+        STR_CASE(GL_SRGB8_ALPHA8)
958
+        STR_CASE(GL_DEPTH_STENCIL)
959
+        STR_CASE(GL_DEPTH24_STENCIL8)
960
+        STR_CASE(GL_DEPTH32F_STENCIL8)
961
+        STR_CASE(GL_DEPTH_COMPONENT)
962
+        STR_CASE(GL_DEPTH_COMPONENT16)
963
+        STR_CASE(GL_DEPTH_COMPONENT24)
964
+        STR_CASE(GL_DEPTH_COMPONENT32)
965
+        STR_CASE(GL_DEPTH_COMPONENT32F)
966
+        STR_CASE(GL_STENCIL_INDEX)
967
+        STR_CASE(GL_STENCIL_INDEX1)
968
+        STR_CASE(GL_STENCIL_INDEX4)
969
+        STR_CASE(GL_STENCIL_INDEX8)
970
+        STR_CASE(GL_STENCIL_INDEX16)
971
+        STR_CASE(GL_COMPRESSED_RED)
972
+        STR_CASE(GL_COMPRESSED_RG)
973
+        STR_CASE(GL_COMPRESSED_RGB)
974
+        STR_CASE(GL_COMPRESSED_RGBA)
975
+        STR_CASE(GL_COMPRESSED_SRGB)
976
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA)
977
+        STR_CASE(GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
978
+        STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
979
+        STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
980
+        STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
981
+        STR_CASE(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT)
982
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT)
983
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT)
984
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT)
985
+        STR_CASE(GL_COMPRESSED_RED_RGTC1)
986
+        STR_CASE(GL_COMPRESSED_RG_RGTC2)
987
+        STR_CASE(GL_COMPRESSED_SIGNED_RED_RGTC1)
988
+        STR_CASE(GL_COMPRESSED_SIGNED_RG_RGTC2)
989
+        STR_CASE(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT)
990
+        STR_CASE(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT)
991
+        STR_CASE(GL_COMPRESSED_RGBA_BPTC_UNORM)
992
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)
993
+        STR_CASE(GL_COMPRESSED_RGB8_ETC2)
994
+        STR_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2)
995
+        STR_CASE(GL_COMPRESSED_SRGB8_ETC2)
996
+        STR_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2)
997
+        STR_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC)
998
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
999
+        STR_CASE(GL_COMPRESSED_R11_EAC)
1000
+        STR_CASE(GL_COMPRESSED_RG11_EAC)
1001
+        STR_CASE(GL_COMPRESSED_SIGNED_R11_EAC)
1002
+        STR_CASE(GL_COMPRESSED_SIGNED_RG11_EAC)
1003
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_4x4_KHR)
1004
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_5x4_KHR)
1005
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_5x5_KHR)
1006
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_6x5_KHR)
1007
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_6x6_KHR)
1008
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x5_KHR)
1009
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x6_KHR)
1010
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x8_KHR)
1011
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x5_KHR)
1012
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x6_KHR)
1013
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x8_KHR)
1014
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x10_KHR)
1015
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_12x10_KHR)
1016
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_12x12_KHR)
1017
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR)
1018
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR)
1019
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR)
1020
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR)
1021
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR)
1022
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR)
1023
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR)
1024
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR)
1025
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR)
1026
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR)
1027
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR)
1028
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR)
1029
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR)
1030
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR)
1031
+        default:
1032
+            return str_enum_(internal_format);
1033
+    }
1034
+}
0 1035
new file mode 100644
... ...
@@ -0,0 +1,65 @@
1
+#include <exception>
2
+#include <iostream>
3
+#include <string>
4
+#include <type_traits>
5
+
6
+#include <glbase.hpp>
7
+
8
+
9
+struct GLBaseTest : protected GLBase
10
+{
11
+    explicit GLBaseTest()
12
+    :
13
+        GLBase{}
14
+    {
15
+        std::cout
16
+            << path_prefix_("path", "")       << "\n"
17
+            << path_prefix_("path", "prefix") << std::endl;
18
+
19
+        try
20
+        {
21
+            TGA_::read("nonexistent");
22
+        }
23
+        catch (std::exception const & exception)
24
+        {
25
+            std::cout << exception.what() << std::endl;
26
+        }
27
+
28
+        // NOLINTNEXTLINE
29
+        TGA_ tga({1, 1}, {0, 0, 255, 127});
30
+        std::cout
31
+            << "TGA:"                                                    << "\n"
32
+            << "  size:      " << tga.size()[0] << ", " << tga.size()[1] << "\n"
33
+            << "  data size: " << tga.data().size()                      << std::endl;
34
+
35
+        std::cout
36
+            << str_path_           ("path")             << "\n"
37
+            << str_paths_          ({"path1", "path2"}) << "\n"
38
+            << str_enum_           (0x01)               << "\n"
39
+            << str_error_          (GL_NO_ERROR)        << "\n"
40
+            << str_object_type_    (GL_TEXTURE)         << "\n"
41
+            << str_glsl_           (GL_FLOAT_VEC3)      << "\n"
42
+            << str_format_         (GL_RGBA)            << "\n"
43
+            << str_type_           (GL_FLOAT)           << "\n"
44
+            << str_internal_format_(GL_RGBA32F)         << std::endl;
45
+    }
46
+};
47
+
48
+
49
+int main()
50
+{
51
+    static_assert(std::is_empty<GLBase>::value, "GLBase must be empty");
52
+
53
+    std::cout
54
+        << "Supported 1.0: "
55
+        << GLBase::supported({1, 0})
56
+        << std::endl;
57
+    std::cout
58
+        << "GL_ACTIVE_TEXTURE: "
59
+        << GLBase::integer(GL_ACTIVE_TEXTURE)
60
+        << std::endl;
61
+    GLBase::debug(1);
62
+    GLBase::debug_message("Debug message");
63
+
64
+    GLBaseTest{};
65
+}