Browse code

Add implementation

Robert Cranston authored on 10/01/2022 00:25:45
Showing 7 changed files

... ...
@@ -17,6 +17,7 @@ add_library(${PROJECT_NAME})
17 17
 include(common.cmake)
18 18
 common(
19 19
     CXX_STANDARD 11
20
+    DISABLE_CPPCHECK
20 21
     PACKAGES
21 22
         OpenGL
22 23
         GLEW
... ...
@@ -2,12 +2,376 @@
2 2
 
3 3
 A [C++11][]/[OpenGL][] [\>=1.0][] [object][] library.
4 4
 
5
+The provided `GLObject` class is intended to be the base class of several other
6
+classes that encapsulate different types of OpenGL objects.
7
+
5 8
 [`globject`]: https://git.rcrnstn.net/rcrnstn/globject
6 9
 [C++11]: https://en.wikipedia.org/wiki/C++11
7 10
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
8 11
 [\>=1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
9 12
 [object]: https://www.khronos.org/opengl/wiki/OpenGL_Object
10 13
 
14
+## Usage
15
+
16
+### OpenGL loading library
17
+
18
+This library can be used with an arbitrary [OpenGL loading library][] by making
19
+sure `GLOBJECT_LOADER` is `#define`d to the file to `#include`, e.g.
20
+`<glad/glad.h>` (either in the source or through a compiler switch, probably
21
+defined in the build system). The default if none is defined is `<GL/glew.h>`.
22
+
23
+[OpenGL loading library]: https://www.khronos.org/opengl/wiki/OpenGL_Loading_Library
24
+
25
+### [OpenGL Mathematics (GLM)][]
26
+
27
+If `glm/glm.hpp` is included (specifically, if `GLM_VERSION` is defined) before
28
+inclusion of `globject.hpp` [OpenGL Mathematics (GLM)][] support will be
29
+enabled. See [Data][].
30
+
31
+[OpenGL Mathematics (GLM)]: https://glm.g-truc.net
32
+[Data]: #data
33
+
34
+### Thread safety
35
+
36
+"Global" state (usually used to mirrors some OpenGL [context][] internal state
37
+for convenience or performance) is declared `thread_local`. This means that
38
+multithreading is supported under the assumption that a given OpenGL
39
+[context][] and the `GLObject`s created while it is current are only used from
40
+a single thread.
41
+
42
+This is not a huge limitation since driver vendors recommend to not share
43
+OpenGL [context][]s between threads, for performance.
44
+
45
+[context]: https://www.khronos.org/opengl/wiki/OpenGL_Context
46
+
47
+### Function documentation
48
+
49
+For brevity, argument and return types may omit `const &` and functions may
50
+omit `const` qualifiers in the remainder of this documentation when it has no
51
+implication for the user, other than performance. Consult the source for the
52
+exact function signatures.
53
+
54
+### `public` interface
55
+
56
+#### [Special member functions][]
57
+
58
+There is no `public` constructor. It is impossible to directly instantiate
59
+`GLObject`s. Instead, use a derived class that encapsulates a specific type of
60
+OpenGL object.
61
+
62
+There is no defined default constructor. The copy constructor and copy/move
63
+assignment operators are `delete`d. This enforces the invariant that a
64
+successfully constructed instance (that has not been moved from) always (is the
65
+only instance that) corresponds to a valid OpenGL object.
66
+
67
+The destructor is `virtual`, so references to instances of (different) derived
68
+classes can be held in (the same) containers.
69
+
70
+The move constructor's `noexcept` specifier is only honored if `debug` is
71
+`false`, see [Debug][].
72
+
73
+[special member functions]: https://en.wikipedia.org/wiki/Special_member_functions
74
+[Debug]: #debug
75
+
76
+#### Getters and setters
77
+
78
+Getters and setters share the same name and use function overloading to
79
+disambiguate. Getters (which can be run on `const` objects) take no argument
80
+and return the value (by constant reference). Setters take a [forwarding
81
+reference][] argument and return the old value (by value, after move). Getters
82
+and setters use a "gets"/"sets" shorthand below to indicate they conform to
83
+this behaviour.
84
+
85
+[forwarding reference]: https://en.cppreference.com/w/cpp/language/reference#Forwarding_references
86
+
87
+#### Core
88
+
89
+`bool static supported(Version version_min, std::string extension = {})`
90
+returns `true` if the current OpenGL context implements the given, or a later,
91
+version or the given extension. If no version check is desired, provide `{0,
92
+0}`. If no extension check is desired, provide an empty extension string (the
93
+default). If no check at all is performed, `false` is returned. This is
94
+convenient because extensions are often absorbed into newer versions of the
95
+OpenGL specification itself, and some platforms stop reporting support for the
96
+extension for those versions, so only checking if the extension is available
97
+fails. [History of OpenGL][] has a list of which versions of OpenGL absorbed
98
+which extensions. If a non-empty `extension` is provided, it must be on the
99
+form returned by [`glGetStringi`][]`(GL_EXTENSIONS, index)`, i.e. it must start
100
+with `"GL_"`. Since this library is agnostic to the used [OpenGL loading
101
+library][] (which is usually used to perform these kind of checks), the OpenGL
102
+API itself is queried. The old method of detecting the OpenGL version and
103
+extensions was deprecated in OpenGL 3.0 and removed in OpenGL 3.1. Both methods
104
+are dealt with correctly. `Version` is an alias for `std::array<GLint, 2>`.
105
+
106
+`GLint static get_integer(GLenum name)` calls [`glGetIntegerv`][] with the
107
+given argument and returns the result.
108
+
109
+`GLuint object()` gets the underlying OpenGL object. This is guaranteed to be
110
+valid, unless the `GLObject` has been moved from, in which case it is `0`.
111
+
112
+`operator GLuint()` (implicit conversion operator) returns `object()`.
113
+
114
+[History of OpenGL]: https://www.khronos.org/opengl/wiki/History_of_OpenGL
115
+[`glGetStringi`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetString.xhtml
116
+[`glGetIntegerv`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGet.xhtml
117
+
118
+#### Data
119
+
120
+`template<typename Data> struct DataTraits` is template specialized on `GL*`
121
+(and, if enabled, [OpenGL Mathematics (GLM)][]) types and provides the
122
+following `static constexpr` values.
123
+
124
+-   `char const[] name`
125
+-   `GLint        columns`
126
+-   `GLint        rows`
127
+-   `GLenum       format`
128
+-   `GLenum       type`
129
+-   `GLenum       internal_format`
130
+
131
+It also provides the following functions.
132
+
133
+-   `void static attrib(GLuint index, Data value)` which uploads `value` to the
134
+    [non-array attribute][] indicated by `index`. Note that [OpenGL Mathematics
135
+    (GLM)][] matrix types, if enabled, occupy several consecutive indices (one
136
+    per column), which are all uploaded automatically by this call.
137
+
138
+-   `void static uniform(GLint location, Data value)` which uploads `value` to
139
+    the [uniform][] indicated by `location` of the current [shader][] program.
140
+
141
+`GLOBJECT_DATA*` macros to ease the definition of new template specializations
142
+of `DataTraits` are defined. Consult the source for the definitions and usage
143
+examples.
144
+
145
+[non-array attribute]: https://www.khronos.org/opengl/wiki/Vertex_Specification#Non-array_attribute_values
146
+[uniform]: https://www.khronos.org/opengl/wiki/Uniform_(GLSL)
147
+[shader]: https://www.khronos.org/opengl/wiki/Shader
148
+
149
+#### Path
150
+
151
+`Path` is an alias for `std::string`.
152
+
153
+`Paths` is an alias for `std::vector<Path>`.
154
+
155
+#### Debug
156
+
157
+`bool static debug()` gets/sets the global `debug` flag. When `true`,
158
+potentially costly debug operations are performed as part of other operations.
159
+Defaults to `true`.
160
+
161
+`DebugCallback static debug_callback()` gets/sets a callback that may be called
162
+by `GLObject` and its derived classes when `debug` is `true`. The debug
163
+callback can also be called by client code if desired. `DebugCallback` is an
164
+alias for `std::function<void (std::string const & message)>`. Defaults to a
165
+function which outputs to `std::cerr`, appends a newline and flushes.
166
+
167
+`DebugObjects static debug_objects()` gets a list of all `GLObjects`
168
+constructed, and not subsequently destructed, during the time `debug` was
169
+`true`. `DebugObjects` is an alias for `std::vector<GLObject *>`.
170
+
171
+`std::string debug_name()` returns a short representation of the object.
172
+
173
+`std::string debug_info()` returns a potentially longer representation of the
174
+object.
175
+
176
+`std::string static debug_objects_name()` returns a short representation of all
177
+objects in `debug_objects`.
178
+
179
+`std::string static debug_objects_info()` returns a potentially longer
180
+representation of all objects in `debug_objects`.
181
+
182
+Code in `GLObject` (and properly behaved derived classes) uses the macro
183
+`GLOBJECT_DEBUG_IF(D)` which expands to `if (debug() >= 1)` by default. It can
184
+be made to expand to `if (false)` by defining `GLOBJECT_DEBUG 0`, with the
185
+result that an optimizing compiler will completely compile out the
186
+corresponding debug code.
187
+
188
+#### Exceptions
189
+
190
+All exceptions thrown are of the type `GLObject::Exception`, which inherits
191
+from `std::runtime_error`.
192
+
193
+### `protected` interface
194
+
195
+#### [Special member functions][]
196
+
197
+Derived classes should define a (usually `explicit` non-default) constructor. A
198
+`noexcept` move constructor and `virtual` destructor should only be defined if
199
+resources (other than `object`, which is handled by `GLObject`) need to be
200
+moved/released. Derived classes should not define or `delete` any of the
201
+non-defined/`delete`d special member functions detailed in the `public`
202
+interface documentation above.
203
+
204
+Two helper functions that act as adapters from the interface of the
205
+`glCreate*`/`glDelete*` family of functions (used by shader and program
206
+objects, termed "unconventional objects" by the OpenGL Wiki) to the interface
207
+of the `glGen*s`/`glDelete*s` family of functions are provided.
208
+
209
+-   `template<GLCreateObject gl_create_object> void static
210
+    gl_create_object_(GLsizei n, GLuint * objects)` takes a `glCreate*`
211
+    function as template argument, and conforms to the `glGen*s` interface.
212
+    `GLCreateObject` is an alias for `GLuint (*)()`.
213
+
214
+-   `template<GLDeleteObject gl_delete_object> void static
215
+    gl_delete_object_(GLsizei n, GLuint * objects)` takes a `glDelete*`
216
+    function as template argument, and conforms to the `glDelete*s` interface.
217
+    `GLDeleteObject` is an alias for `void (*)(GLuint)`.
218
+
219
+`GLObject` has a concept of pseudo-objects. They are classes that wish to make
220
+use of the `protected` interface of `GLObject` but does not encapsulate an
221
+OpenGL [object][].
222
+
223
+The `GLObject` constructor takes:
224
+
225
+-   `GLGenObjects gl_gen_objects` which is a function pointer that conforms to
226
+    the `glGen*s` interface. Specify `nullptr` for pseudo-objects.
227
+
228
+-   `GLDeleteObjects gl_delete_objects` which is a function pointer that
229
+    conforms to the `glDelete*s` interface. Specify `nullptr` for
230
+    pseudo-objects.
231
+
232
+-   `GLenum object_type` which is one of the following. Specify `0` for
233
+    pseudo-objects.
234
+
235
+    -   `GL_BUFFER`
236
+    -   `GL_SHADER`
237
+    -   `GL_PROGRAM`
238
+    -   `GL_VERTEX_ARRAY`
239
+    -   `GL_QUERY`
240
+    -   `GL_PROGRAM_PIPELINE`
241
+    -   `GL_TRANSFORM_FEEDBACK`
242
+    -   `GL_SAMPLER`
243
+    -   `GL_TEXTURE`
244
+    -   `GL_RENDERBUFFER`
245
+    -   `GL_FRAMEBUFFER`
246
+
247
+-   `std::string object_label` which will be used when throwing exceptions and,
248
+    if OpenGL [\>=4.3][] or the [`KHR_debug`][] extension is available, passed
249
+    to [`glObjectLabel`][].
250
+
251
+[\>=4.3]: https://en.wikipedia.org/wiki/OpenGL#Version_history
252
+[`KHR_debug`]: https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_debug.txt
253
+[`glObjectLabel`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glObjectLabel.xhtml
254
+
255
+#### Getters and setters
256
+
257
+Macros that aid in the implementation of (potentially static thread local)
258
+getters and setters are defined. These should be used in the class definition.
259
+They define functions named `NAME`, which operate on a variable named
260
+`NAME##_`. The underlying variable is not declared automatically, this must be
261
+done manually (probably `private`). The `ACCESS` variant defines both `GET` and
262
+`SET`.
263
+
264
+`GLOBJECT_{GET,SET,ACCESS}{,_THREAD}(TYPE, NAME)`
265
+
266
+Additionally, static thread local member variables need to be defined in
267
+exactly one translation unit. A macro to help with this is defined as well.
268
+Note that, since the definition is outside the class definition, the `NAME`
269
+needs to be qualified with the class name.
270
+
271
+`GLOBJECT_THREAD(NAME, INIT)`
272
+
273
+#### Path
274
+
275
+`Path static path_prefix_(Path path, Path prefix)` returns `prefix` prepended
276
+to `path`, separated with a `/`, unless
277
+
278
+1.  `prefix` is empty
279
+2.  `path` starts with a `/`
280
+
281
+in which case `path` is returned as is.
282
+
283
+#### TGA
284
+
285
+A `TGA` class is provided to handle [Truevision TGA][] images in uncompressed
286
+[BGRA][] (`GL_BGRA`) format. TGA was selected because it is widely supported
287
+and has trivial header and data layout.
288
+
289
+`Size` is an alias for `std::array<GLsizei, 2>`.
290
+
291
+`Data` is an alias for `std::vector<GLubyte>`.
292
+
293
+`explicit TGA(Size size, Data data)` instantiates a `TGA` object with the given
294
+`size` and `data`.
295
+
296
+`TGA static read(std::string path)` reads the file `path` into a `TGA` object.
297
+
298
+`void write(std::string const & path)` writes the `TGA` object to the file
299
+`path`.
300
+
301
+`Data data()` gets the data of the TGA image.
302
+
303
+`Size size()` gets the size of the TGA image.
304
+
305
+[Truevision TGA]: https://en.wikipedia.org/wiki/Truevision_TGA
306
+[BGRA]: https://en.wikipedia.org/wiki/RGBA_color_model
307
+
308
+#### Check
309
+
310
+These functions throw an exception if some condition does not hold. It is
311
+recommended that derived classes use similar helper functions, defined in an
312
+implementation file, to hide the string processing necessary to form a helpful
313
+exception message.
314
+
315
+Derived classes should treat error checking as free during:
316
+
317
+-   Construction and destruction, except move construction.
318
+-   Exceptions.
319
+-   Debug function calls.
320
+
321
+At all other times, non-critical error checking should only be performed if
322
+`debug` is `true`.
323
+
324
+`void static check_path_(std::string path)` checks that `path` is non-empty.
325
+
326
+`void static check_error_(GLenum error)` checks an error returned by
327
+[`glGetError`][].
328
+
329
+`void static check_supported_(Version version_min, std::string extension = {})`
330
+checks that `supported` returns `true` for the given arguments.
331
+
332
+`void static check_format_(GLenum format, GLenum format_expected)` checks that
333
+`format` matches `format_expected`.
334
+
335
+`void static check_type_(GLenum type, GLenum type_expected)` checks that `type`
336
+matches `type_expected`.
337
+
338
+`void static check_internal_format_(GLenum internal_format)` checks that
339
+`internal_format` is supported.
340
+
341
+[`glGetError`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetError.xhtml
342
+
343
+#### Fail
344
+
345
+These functions throw an exception and should be marked `[[noreturn]]`. It is
346
+recommended that derived classes use similar helper functions, defined in an
347
+implementation file, to hide the string processing necessary to form a helpful
348
+exception message.
349
+
350
+`void fail_action_(std::string action)` throws an exception with a message that
351
+includes `action` and `debug_name()`.
352
+
353
+#### String
354
+
355
+`std::string static str_path_(Path path)` returns `path` surrounded by quotes.
356
+
357
+`std::string static str_paths_(Paths paths)` returns `paths` surrounded by
358
+quotes and joined with commas.
359
+
360
+`std::string static str_enum_(GLenum name)` returns the hexadecimal string
361
+representation of `name` (often used as fallback).
362
+
363
+`std::string static str_error_(GLenum error)` returns the string representation
364
+of values returned by [`glGetError`][].
365
+
366
+`std::string static str_format_(GLenum format)` returns the string
367
+representation of `format`.
368
+
369
+`std::string static str_type_(GLenum type)` returns the string representation
370
+of `type`.
371
+
372
+`std::string static str_internal_format_(GLenum internal_format)` returns the
373
+string representation of `internal_format`.
374
+
11 375
 ## Build system
12 376
 
13 377
 This project supports [CMake][] and uses [`cmake-common`][]. There are several
14 378
new file mode 100755
... ...
@@ -0,0 +1,15 @@
1
+#!/bin/sh
2
+set -euC
3
+
4
+{
5
+  printf '%s\n' '#include <GL/glew.h>'
6
+  g++ -DGLM_VERSION -E '../include/globject.hpp' \
7
+  | grep -v '^#' \
8
+  | clang-format \
9
+  | awk '
10
+    x>0 &&                   /{/ {++x}
11
+    /GLObject::DataTraits<.*> {/ {++x}
12
+    x>0
13
+    x>0 &&                   /}/ {--x}
14
+  '
15
+} >| 'globject_data_traits.hpp'
0 16
new file mode 100644
... ...
@@ -0,0 +1,469 @@
1
+#include <GL/glew.h>
2
+template <> struct GLObject::DataTraits<GLfloat> {
3
+  auto static constexpr name = "GLfloat";
4
+  auto static constexpr columns = GLint{1};
5
+  auto static constexpr rows = GLint{1};
6
+  auto static constexpr format = GLenum{0x1903};
7
+  auto static constexpr type = GLenum{0x1406};
8
+  auto static constexpr internal_format = GLenum{0x822E};
9
+  void static attrib(GLuint index, GLfloat const &value) {
10
+    if (GLObject::debug())
11
+      check_supported_({2, 0});
12
+    __glewVertexAttrib1f(index, value);
13
+  }
14
+  void static uniform(GLint location, GLfloat const &value) {
15
+    if (GLObject::debug())
16
+      check_supported_({2, 0});
17
+    __glewUniform1f(location, value);
18
+  }
19
+};
20
+template <> struct GLObject::DataTraits<GLbyte> {
21
+  auto static constexpr name = "GLbyte";
22
+  auto static constexpr columns = GLint{1};
23
+  auto static constexpr rows = GLint{1};
24
+  auto static constexpr format = GLenum{0x1903};
25
+  auto static constexpr type = GLenum{0x1400};
26
+  auto static constexpr internal_format = GLenum{0x8231};
27
+  void static attrib(GLuint index, GLbyte const &value) {
28
+    if (GLObject::debug())
29
+      check_supported_({3, 0});
30
+    __glewVertexAttribI1i(index, value);
31
+  }
32
+  void static uniform(GLint location, GLbyte const &value) {
33
+    if (GLObject::debug())
34
+      check_supported_({2, 0});
35
+    __glewUniform1i(location, value);
36
+  }
37
+};
38
+template <> struct GLObject::DataTraits<GLshort> {
39
+  auto static constexpr name = "GLshort";
40
+  auto static constexpr columns = GLint{1};
41
+  auto static constexpr rows = GLint{1};
42
+  auto static constexpr format = GLenum{0x1903};
43
+  auto static constexpr type = GLenum{0x1402};
44
+  auto static constexpr internal_format = GLenum{0x8233};
45
+  void static attrib(GLuint index, GLshort const &value) {
46
+    if (GLObject::debug())
47
+      check_supported_({3, 0});
48
+    __glewVertexAttribI1i(index, value);
49
+  }
50
+  void static uniform(GLint location, GLshort const &value) {
51
+    if (GLObject::debug())
52
+      check_supported_({2, 0});
53
+    __glewUniform1i(location, value);
54
+  }
55
+};
56
+template <> struct GLObject::DataTraits<GLint> {
57
+  auto static constexpr name = "GLint";
58
+  auto static constexpr columns = GLint{1};
59
+  auto static constexpr rows = GLint{1};
60
+  auto static constexpr format = GLenum{0x1903};
61
+  auto static constexpr type = GLenum{0x1404};
62
+  auto static constexpr internal_format = GLenum{0x8235};
63
+  void static attrib(GLuint index, GLint const &value) {
64
+    if (GLObject::debug())
65
+      check_supported_({3, 0});
66
+    __glewVertexAttribI1i(index, value);
67
+  }
68
+  void static uniform(GLint location, GLint const &value) {
69
+    if (GLObject::debug())
70
+      check_supported_({2, 0});
71
+    __glewUniform1i(location, value);
72
+  }
73
+};
74
+template <> struct GLObject::DataTraits<GLubyte> {
75
+  auto static constexpr name = "GLubyte";
76
+  auto static constexpr columns = GLint{1};
77
+  auto static constexpr rows = GLint{1};
78
+  auto static constexpr format = GLenum{0x1903};
79
+  auto static constexpr type = GLenum{0x1401};
80
+  auto static constexpr internal_format = GLenum{0x8232};
81
+  void static attrib(GLuint index, GLubyte const &value) {
82
+    if (GLObject::debug())
83
+      check_supported_({3, 0});
84
+    __glewVertexAttribI1ui(index, value);
85
+  }
86
+  void static uniform(GLint location, GLubyte const &value) {
87
+    if (GLObject::debug())
88
+      check_supported_({3, 0});
89
+    __glewUniform1ui(location, value);
90
+  }
91
+};
92
+template <> struct GLObject::DataTraits<GLushort> {
93
+  auto static constexpr name = "GLushort";
94
+  auto static constexpr columns = GLint{1};
95
+  auto static constexpr rows = GLint{1};
96
+  auto static constexpr format = GLenum{0x1903};
97
+  auto static constexpr type = GLenum{0x1403};
98
+  auto static constexpr internal_format = GLenum{0x8234};
99
+  void static attrib(GLuint index, GLushort const &value) {
100
+    if (GLObject::debug())
101
+      check_supported_({3, 0});
102
+    __glewVertexAttribI1ui(index, value);
103
+  }
104
+  void static uniform(GLint location, GLushort const &value) {
105
+    if (GLObject::debug())
106
+      check_supported_({3, 0});
107
+    __glewUniform1ui(location, value);
108
+  }
109
+};
110
+template <> struct GLObject::DataTraits<GLuint> {
111
+  auto static constexpr name = "GLuint";
112
+  auto static constexpr columns = GLint{1};
113
+  auto static constexpr rows = GLint{1};
114
+  auto static constexpr format = GLenum{0x1903};
115
+  auto static constexpr type = GLenum{0x1405};
116
+  auto static constexpr internal_format = GLenum{0x8236};
117
+  void static attrib(GLuint index, GLuint const &value) {
118
+    if (GLObject::debug())
119
+      check_supported_({3, 0});
120
+    __glewVertexAttribI1ui(index, value);
121
+  }
122
+  void static uniform(GLint location, GLuint const &value) {
123
+    if (GLObject::debug())
124
+      check_supported_({3, 0});
125
+    __glewUniform1ui(location, value);
126
+  }
127
+};
128
+template <> struct GLObject::DataTraits<glm::vec2> {
129
+  auto static constexpr name = "glm::vec2";
130
+  auto static constexpr columns = GLint{1};
131
+  auto static constexpr rows = GLint{2};
132
+  auto static constexpr format = GLenum{0x8227};
133
+  auto static constexpr type = GLenum{0x1406};
134
+  auto static constexpr internal_format = GLenum{0x8230};
135
+  void static attrib(GLuint index, glm::vec2 const &value) {
136
+    if (GLObject::debug())
137
+      check_supported_({2, 0});
138
+    __glewVertexAttrib2fv(index, glm::value_ptr(value));
139
+  }
140
+  void static uniform(GLint location, glm::vec2 const &value) {
141
+    if (GLObject::debug())
142
+      check_supported_({2, 0});
143
+    __glewUniform2fv(location, 1, glm::value_ptr(value));
144
+  }
145
+};
146
+template <> struct GLObject::DataTraits<glm::vec3> {
147
+  auto static constexpr name = "glm::vec3";
148
+  auto static constexpr columns = GLint{1};
149
+  auto static constexpr rows = GLint{3};
150
+  auto static constexpr format = GLenum{0x1907};
151
+  auto static constexpr type = GLenum{0x1406};
152
+  auto static constexpr internal_format = GLenum{0x8815};
153
+  void static attrib(GLuint index, glm::vec3 const &value) {
154
+    if (GLObject::debug())
155
+      check_supported_({2, 0});
156
+    __glewVertexAttrib3fv(index, glm::value_ptr(value));
157
+  }
158
+  void static uniform(GLint location, glm::vec3 const &value) {
159
+    if (GLObject::debug())
160
+      check_supported_({2, 0});
161
+    __glewUniform3fv(location, 1, glm::value_ptr(value));
162
+  }
163
+};
164
+template <> struct GLObject::DataTraits<glm::vec4> {
165
+  auto static constexpr name = "glm::vec4";
166
+  auto static constexpr columns = GLint{1};
167
+  auto static constexpr rows = GLint{4};
168
+  auto static constexpr format = GLenum{0x1908};
169
+  auto static constexpr type = GLenum{0x1406};
170
+  auto static constexpr internal_format = GLenum{0x8814};
171
+  void static attrib(GLuint index, glm::vec4 const &value) {
172
+    if (GLObject::debug())
173
+      check_supported_({2, 0});
174
+    __glewVertexAttrib4fv(index, glm::value_ptr(value));
175
+  }
176
+  void static uniform(GLint location, glm::vec4 const &value) {
177
+    if (GLObject::debug())
178
+      check_supported_({2, 0});
179
+    __glewUniform4fv(location, 1, glm::value_ptr(value));
180
+  }
181
+};
182
+template <> struct GLObject::DataTraits<glm::ivec2> {
183
+  auto static constexpr name = "glm::ivec2";
184
+  auto static constexpr columns = GLint{1};
185
+  auto static constexpr rows = GLint{2};
186
+  auto static constexpr format = GLenum{0x8227};
187
+  auto static constexpr type = GLenum{0x1404};
188
+  auto static constexpr internal_format = GLenum{0x823B};
189
+  void static attrib(GLuint index, glm::ivec2 const &value) {
190
+    if (GLObject::debug())
191
+      check_supported_({3, 0});
192
+    __glewVertexAttribI2iv(index, glm::value_ptr(value));
193
+  }
194
+  void static uniform(GLint location, glm::ivec2 const &value) {
195
+    if (GLObject::debug())
196
+      check_supported_({2, 0});
197
+    __glewUniform2iv(location, 1, glm::value_ptr(value));
198
+  }
199
+};
200
+template <> struct GLObject::DataTraits<glm::ivec3> {
201
+  auto static constexpr name = "glm::ivec3";
202
+  auto static constexpr columns = GLint{1};
203
+  auto static constexpr rows = GLint{3};
204
+  auto static constexpr format = GLenum{0x1907};
205
+  auto static constexpr type = GLenum{0x1404};
206
+  auto static constexpr internal_format = GLenum{0x8D83};
207
+  void static attrib(GLuint index, glm::ivec3 const &value) {
208
+    if (GLObject::debug())
209
+      check_supported_({3, 0});
210
+    __glewVertexAttribI3iv(index, glm::value_ptr(value));
211
+  }
212
+  void static uniform(GLint location, glm::ivec3 const &value) {
213
+    if (GLObject::debug())
214
+      check_supported_({2, 0});
215
+    __glewUniform3iv(location, 1, glm::value_ptr(value));
216
+  }
217
+};
218
+template <> struct GLObject::DataTraits<glm::ivec4> {
219
+  auto static constexpr name = "glm::ivec4";
220
+  auto static constexpr columns = GLint{1};
221
+  auto static constexpr rows = GLint{4};
222
+  auto static constexpr format = GLenum{0x1908};
223
+  auto static constexpr type = GLenum{0x1404};
224
+  auto static constexpr internal_format = GLenum{0x8D82};
225
+  void static attrib(GLuint index, glm::ivec4 const &value) {
226
+    if (GLObject::debug())
227
+      check_supported_({3, 0});
228
+    __glewVertexAttribI4iv(index, glm::value_ptr(value));
229
+  }
230
+  void static uniform(GLint location, glm::ivec4 const &value) {
231
+    if (GLObject::debug())
232
+      check_supported_({2, 0});
233
+    __glewUniform4iv(location, 1, glm::value_ptr(value));
234
+  }
235
+};
236
+template <> struct GLObject::DataTraits<glm::uvec2> {
237
+  auto static constexpr name = "glm::uvec2";
238
+  auto static constexpr columns = GLint{1};
239
+  auto static constexpr rows = GLint{2};
240
+  auto static constexpr format = GLenum{0x8227};
241
+  auto static constexpr type = GLenum{0x1405};
242
+  auto static constexpr internal_format = GLenum{0x823C};
243
+  void static attrib(GLuint index, glm::uvec2 const &value) {
244
+    if (GLObject::debug())
245
+      check_supported_({3, 0});
246
+    __glewVertexAttribI2uiv(index, glm::value_ptr(value));
247
+  }
248
+  void static uniform(GLint location, glm::uvec2 const &value) {
249
+    if (GLObject::debug())
250
+      check_supported_({3, 0});
251
+    __glewUniform2uiv(location, 1, glm::value_ptr(value));
252
+  }
253
+};
254
+template <> struct GLObject::DataTraits<glm::uvec3> {
255
+  auto static constexpr name = "glm::uvec3";
256
+  auto static constexpr columns = GLint{1};
257
+  auto static constexpr rows = GLint{3};
258
+  auto static constexpr format = GLenum{0x1907};
259
+  auto static constexpr type = GLenum{0x1405};
260
+  auto static constexpr internal_format = GLenum{0x8D71};
261
+  void static attrib(GLuint index, glm::uvec3 const &value) {
262
+    if (GLObject::debug())
263
+      check_supported_({3, 0});
264
+    __glewVertexAttribI3uiv(index, glm::value_ptr(value));
265
+  }
266
+  void static uniform(GLint location, glm::uvec3 const &value) {
267
+    if (GLObject::debug())
268
+      check_supported_({3, 0});
269
+    __glewUniform3uiv(location, 1, glm::value_ptr(value));
270
+  }
271
+};
272
+template <> struct GLObject::DataTraits<glm::uvec4> {
273
+  auto static constexpr name = "glm::uvec4";
274
+  auto static constexpr columns = GLint{1};
275
+  auto static constexpr rows = GLint{4};
276
+  auto static constexpr format = GLenum{0x1908};
277
+  auto static constexpr type = GLenum{0x1405};
278
+  auto static constexpr internal_format = GLenum{0x8D70};
279
+  void static attrib(GLuint index, glm::uvec4 const &value) {
280
+    if (GLObject::debug())
281
+      check_supported_({3, 0});
282
+    __glewVertexAttribI4uiv(index, glm::value_ptr(value));
283
+  }
284
+  void static uniform(GLint location, glm::uvec4 const &value) {
285
+    if (GLObject::debug())
286
+      check_supported_({3, 0});
287
+    __glewUniform4uiv(location, 1, glm::value_ptr(value));
288
+  }
289
+};
290
+template <> struct GLObject::DataTraits<glm::mat2> {
291
+  auto static constexpr name = "glm::mat2";
292
+  auto static constexpr columns = GLint{2};
293
+  auto static constexpr rows = GLint{2};
294
+  auto static constexpr format = GLenum{0x8227};
295
+  auto static constexpr type = GLenum{0x1406};
296
+  auto static constexpr internal_format = GLenum{0x8230};
297
+  void static attrib(GLuint index, glm::mat2 const &value) {
298
+    if (GLObject::debug())
299
+      check_supported_({2, 0});
300
+    for (auto column = GLuint{0}; column < columns; ++column)
301
+      __glewVertexAttrib2fv(index + column,
302
+                            glm::value_ptr(value) + (column * rows));
303
+  }
304
+  void static uniform(GLint location, glm::mat2 const &value) {
305
+    if (GLObject::debug())
306
+      check_supported_({2, 0});
307
+    __glewUniformMatrix2fv(location, 1, 0, glm::value_ptr(value));
308
+  }
309
+};
310
+template <> struct GLObject::DataTraits<glm::mat2x3> {
311
+  auto static constexpr name = "glm::mat2x3";
312
+  auto static constexpr columns = GLint{2};
313
+  auto static constexpr rows = GLint{3};
314
+  auto static constexpr format = GLenum{0x1907};
315
+  auto static constexpr type = GLenum{0x1406};
316
+  auto static constexpr internal_format = GLenum{0x8815};
317
+  void static attrib(GLuint index, glm::mat2x3 const &value) {
318
+    if (GLObject::debug())
319
+      check_supported_({2, 0});
320
+    for (auto column = GLuint{0}; column < columns; ++column)
321
+      __glewVertexAttrib3fv(index + column,
322
+                            glm::value_ptr(value) + (column * rows));
323
+  }
324
+  void static uniform(GLint location, glm::mat2x3 const &value) {
325
+    if (GLObject::debug())
326
+      check_supported_({2, 1});
327
+    __glewUniformMatrix2x3fv(location, 1, 0, glm::value_ptr(value));
328
+  }
329
+};
330
+template <> struct GLObject::DataTraits<glm::mat2x4> {
331
+  auto static constexpr name = "glm::mat2x4";
332
+  auto static constexpr columns = GLint{2};
333
+  auto static constexpr rows = GLint{4};
334
+  auto static constexpr format = GLenum{0x1908};
335
+  auto static constexpr type = GLenum{0x1406};
336
+  auto static constexpr internal_format = GLenum{0x8814};
337
+  void static attrib(GLuint index, glm::mat2x4 const &value) {
338
+    if (GLObject::debug())
339
+      check_supported_({2, 0});
340
+    for (auto column = GLuint{0}; column < columns; ++column)
341
+      __glewVertexAttrib4fv(index + column,
342
+                            glm::value_ptr(value) + (column * rows));
343
+  }
344
+  void static uniform(GLint location, glm::mat2x4 const &value) {
345
+    if (GLObject::debug())
346
+      check_supported_({2, 1});
347
+    __glewUniformMatrix2x4fv(location, 1, 0, glm::value_ptr(value));
348
+  }
349
+};
350
+template <> struct GLObject::DataTraits<glm::mat3x2> {
351
+  auto static constexpr name = "glm::mat3x2";
352
+  auto static constexpr columns = GLint{3};
353
+  auto static constexpr rows = GLint{2};
354
+  auto static constexpr format = GLenum{0x8227};
355
+  auto static constexpr type = GLenum{0x1406};
356
+  auto static constexpr internal_format = GLenum{0x8230};
357
+  void static attrib(GLuint index, glm::mat3x2 const &value) {
358
+    if (GLObject::debug())
359
+      check_supported_({2, 0});
360
+    for (auto column = GLuint{0}; column < columns; ++column)
361
+      __glewVertexAttrib2fv(index + column,
362
+                            glm::value_ptr(value) + (column * rows));
363
+  }
364
+  void static uniform(GLint location, glm::mat3x2 const &value) {
365
+    if (GLObject::debug())
366
+      check_supported_({2, 1});
367
+    __glewUniformMatrix3x2fv(location, 1, 0, glm::value_ptr(value));
368
+  }
369
+};
370
+template <> struct GLObject::DataTraits<glm::mat3> {
371
+  auto static constexpr name = "glm::mat3";
372
+  auto static constexpr columns = GLint{3};
373
+  auto static constexpr rows = GLint{3};
374
+  auto static constexpr format = GLenum{0x1907};
375
+  auto static constexpr type = GLenum{0x1406};
376
+  auto static constexpr internal_format = GLenum{0x8815};
377
+  void static attrib(GLuint index, glm::mat3 const &value) {
378
+    if (GLObject::debug())
379
+      check_supported_({2, 0});
380
+    for (auto column = GLuint{0}; column < columns; ++column)
381
+      __glewVertexAttrib3fv(index + column,
382
+                            glm::value_ptr(value) + (column * rows));
383
+  }
384
+  void static uniform(GLint location, glm::mat3 const &value) {
385
+    if (GLObject::debug())
386
+      check_supported_({2, 0});
387
+    __glewUniformMatrix3fv(location, 1, 0, glm::value_ptr(value));
388
+  }
389
+};
390
+template <> struct GLObject::DataTraits<glm::mat3x4> {
391
+  auto static constexpr name = "glm::mat3x4";
392
+  auto static constexpr columns = GLint{3};
393
+  auto static constexpr rows = GLint{4};
394
+  auto static constexpr format = GLenum{0x1908};
395
+  auto static constexpr type = GLenum{0x1406};
396
+  auto static constexpr internal_format = GLenum{0x8814};
397
+  void static attrib(GLuint index, glm::mat3x4 const &value) {
398
+    if (GLObject::debug())
399
+      check_supported_({2, 0});
400
+    for (auto column = GLuint{0}; column < columns; ++column)
401
+      __glewVertexAttrib4fv(index + column,
402
+                            glm::value_ptr(value) + (column * rows));
403
+  }
404
+  void static uniform(GLint location, glm::mat3x4 const &value) {
405
+    if (GLObject::debug())
406
+      check_supported_({2, 1});
407
+    __glewUniformMatrix3x4fv(location, 1, 0, glm::value_ptr(value));
408
+  }
409
+};
410
+template <> struct GLObject::DataTraits<glm::mat4x2> {
411
+  auto static constexpr name = "glm::mat4x2";
412
+  auto static constexpr columns = GLint{4};
413
+  auto static constexpr rows = GLint{2};
414
+  auto static constexpr format = GLenum{0x8227};
415
+  auto static constexpr type = GLenum{0x1406};
416
+  auto static constexpr internal_format = GLenum{0x8230};
417
+  void static attrib(GLuint index, glm::mat4x2 const &value) {
418
+    if (GLObject::debug())
419
+      check_supported_({2, 0});
420
+    for (auto column = GLuint{0}; column < columns; ++column)
421
+      __glewVertexAttrib2fv(index + column,
422
+                            glm::value_ptr(value) + (column * rows));
423
+  }
424
+  void static uniform(GLint location, glm::mat4x2 const &value) {
425
+    if (GLObject::debug())
426
+      check_supported_({2, 1});
427
+    __glewUniformMatrix4x2fv(location, 1, 0, glm::value_ptr(value));
428
+  }
429
+};
430
+template <> struct GLObject::DataTraits<glm::mat4x3> {
431
+  auto static constexpr name = "glm::mat4x3";
432
+  auto static constexpr columns = GLint{4};
433
+  auto static constexpr rows = GLint{3};
434
+  auto static constexpr format = GLenum{0x1907};
435
+  auto static constexpr type = GLenum{0x1406};
436
+  auto static constexpr internal_format = GLenum{0x8815};
437
+  void static attrib(GLuint index, glm::mat4x3 const &value) {
438
+    if (GLObject::debug())
439
+      check_supported_({2, 0});
440
+    for (auto column = GLuint{0}; column < columns; ++column)
441
+      __glewVertexAttrib3fv(index + column,
442
+                            glm::value_ptr(value) + (column * rows));
443
+  }
444
+  void static uniform(GLint location, glm::mat4x3 const &value) {
445
+    if (GLObject::debug())
446
+      check_supported_({2, 1});
447
+    __glewUniformMatrix4x3fv(location, 1, 0, glm::value_ptr(value));
448
+  }
449
+};
450
+template <> struct GLObject::DataTraits<glm::mat4> {
451
+  auto static constexpr name = "glm::mat4";
452
+  auto static constexpr columns = GLint{4};
453
+  auto static constexpr rows = GLint{4};
454
+  auto static constexpr format = GLenum{0x1908};
455
+  auto static constexpr type = GLenum{0x1406};
456
+  auto static constexpr internal_format = GLenum{0x8814};
457
+  void static attrib(GLuint index, glm::mat4 const &value) {
458
+    if (GLObject::debug())
459
+      check_supported_({2, 0});
460
+    for (auto column = GLuint{0}; column < columns; ++column)
461
+      __glewVertexAttrib4fv(index + column,
462
+                            glm::value_ptr(value) + (column * rows));
463
+  }
464
+  void static uniform(GLint location, glm::mat4 const &value) {
465
+    if (GLObject::debug())
466
+      check_supported_({2, 0});
467
+    __glewUniformMatrix4fv(location, 1, 0, glm::value_ptr(value));
468
+  }
469
+};
0 470
new file mode 100644
... ...
@@ -0,0 +1,356 @@
1
+#ifndef GLOBJECT_HPP_
2
+#define GLOBJECT_HPP_
3
+
4
+
5
+#include <array>
6
+#include <functional>
7
+#include <stdexcept>
8
+#include <string>
9
+#include <utility>
10
+#include <vector>
11
+
12
+#ifndef GLOBJECT_LOADER
13
+#define GLOBJECT_LOADER <GL/glew.h>
14
+#endif
15
+// cppcheck-suppress preprocessorErrorDirective
16
+#include GLOBJECT_LOADER
17
+
18
+
19
+/// Getters and setters
20
+
21
+#define GLOBJECT_GET_(TYPE, NAME, STATIC, CONST) \
22
+    TYPE STATIC const & NAME() CONST \
23
+    { \
24
+        return NAME##_; \
25
+    }
26
+
27
+#define GLOBJECT_SET_(TYPE, NAME, STATIC, CONST) \
28
+    template<typename Type> \
29
+    TYPE STATIC NAME(Type && NAME) \
30
+    { \
31
+        auto NAME##_old = std::move         (NAME##_); \
32
+        NAME##_         = std::forward<Type>(NAME); \
33
+        return NAME##_old; \
34
+    }
35
+
36
+#define GLOBJECT_THREAD(NAME, INIT) \
37
+    decltype(NAME) thread_local NAME INIT;
38
+
39
+#define GLOBJECT_GET(       TYPE, NAME) GLOBJECT_GET_(TYPE, NAME,, const)
40
+#define GLOBJECT_SET(       TYPE, NAME) GLOBJECT_SET_(TYPE, NAME,, const)
41
+#define GLOBJECT_GET_THREAD(TYPE, NAME) GLOBJECT_GET_(TYPE, NAME, static,)
42
+#define GLOBJECT_SET_THREAD(TYPE, NAME) GLOBJECT_SET_(TYPE, NAME, static,)
43
+
44
+#define GLOBJECT_ACCESS(TYPE, NAME) \
45
+    GLOBJECT_GET(TYPE, NAME) \
46
+    GLOBJECT_SET(TYPE, NAME)
47
+
48
+#define GLOBJECT_ACCESS_THREAD(TYPE, NAME) \
49
+    GLOBJECT_GET_THREAD(TYPE, NAME) \
50
+    GLOBJECT_SET_THREAD(TYPE, NAME)
51
+
52
+/// Debug
53
+
54
+#ifndef GLOBJECT_DEBUG
55
+#define GLOBJECT_DEBUG 1
56
+#endif
57
+
58
+#if GLOBJECT_DEBUG
59
+#define GLOBJECT_DEBUG_IF(D) if (GLObject::debug() >= D)
60
+#else
61
+#define GLOBJECT_DEBUG_IF(D) if (false)
62
+#endif
63
+
64
+
65
+/// Class
66
+
67
+class GLObject
68
+{
69
+public:
70
+
71
+    /// Core
72
+
73
+    using Version = std::array<GLint, 2>;
74
+
75
+    bool static supported(
76
+        Version version_min,
77
+        std::string const & extension = {}
78
+    );
79
+
80
+    GLint static get_integer(GLenum name);
81
+
82
+    GLOBJECT_GET(GLuint, object)
83
+
84
+    operator GLuint() const { return object(); }
85
+
86
+    /// Data
87
+
88
+    template<typename Data>
89
+    struct DataTraits;
90
+
91
+    /// Path
92
+
93
+    using Path  = std::string;
94
+    using Paths = std::vector<Path>;
95
+
96
+    /// Debug
97
+
98
+    using DebugCallback = std::function<void (std::string const & message)>;
99
+    using DebugObjects  = std::vector<GLObject *>;
100
+
101
+    GLOBJECT_ACCESS_THREAD(int,           debug)
102
+    GLOBJECT_ACCESS_THREAD(DebugCallback, debug_callback)
103
+    GLOBJECT_GET_THREAD   (DebugObjects,  debug_objects)
104
+
105
+    std::string         debug_name() const;
106
+    std::string virtual debug_info() const;
107
+    std::string static  debug_objects_name();
108
+    std::string static  debug_objects_info();
109
+
110
+    /// Exceptions
111
+
112
+    struct Exception : std::runtime_error
113
+    {
114
+        using std::runtime_error::runtime_error;
115
+    };
116
+
117
+protected:
118
+
119
+    /// Special member functions
120
+
121
+    using GLGenObjects    = void (*)(GLsizei n, GLuint       * objects);
122
+    using GLDeleteObjects = void (*)(GLsizei n, GLuint const * objects);
123
+
124
+    using GLCreateObject = GLuint (*)();
125
+    using GLDeleteObject = void   (*)(GLuint);
126
+
127
+    template<GLCreateObject gl_create_object>
128
+    void static gl_create_object_(GLsizei n, GLuint * objects);
129
+
130
+    template<GLDeleteObject gl_delete_object>
131
+    void static gl_delete_object_(GLsizei n, GLuint * objects);
132
+
133
+    explicit GLObject(
134
+        GLGenObjects    gl_gen_objects,
135
+        GLDeleteObjects gl_delete_objects,
136
+        GLenum          object_type,
137
+        std::string     object_label
138
+    );
139
+    virtual ~GLObject();
140
+    GLObject(GLObject && other) noexcept;
141
+    GLObject(GLObject const &) = delete;
142
+    GLObject & operator=(GLObject &&) = delete;
143
+    GLObject & operator=(GLObject const &) = delete;
144
+
145
+    /// Path
146
+
147
+    Path static path_prefix_(
148
+        Path const & path,
149
+        Path const & prefix
150
+    );
151
+
152
+    /// TGA
153
+
154
+    class TGA
155
+    {
156
+    public:
157
+        using Size = std::array<GLsizei, 2>;
158
+        using Data = std::vector<GLubyte>;
159
+        explicit TGA(Size size, Data data);
160
+        TGA  static  read (Path const & path);
161
+        void         write(Path const & path) const;
162
+        Size         size() const;
163
+        Data const & data();
164
+    private:
165
+        std::string static str_size_(Size size);
166
+        void check_header_() const;
167
+        void check_data_() const;
168
+    private:
169
+        // NOLINTNEXTLINE
170
+        struct Header : std::array<GLubyte, 18>
171
+        {
172
+            explicit Header(Size size);
173
+        };
174
+        Header header_;
175
+        Data   data_;
176
+    };
177
+
178
+    /// Check
179
+
180
+    void static check_path_(Path const & path);
181
+    void static check_error_(GLenum error);
182
+    void static check_supported_(
183
+        Version version_min,
184
+        std::string const & extension = {}
185
+    );
186
+    void static check_format_(
187
+        GLenum format,
188
+        GLenum format_expected
189
+    );
190
+    void static check_type_(
191
+        GLenum type,
192
+        GLenum type_expected
193
+    );
194
+    void static check_internal_format_(
195
+        GLenum internal_format
196
+    );
197
+
198
+
199
+    /// Fail
200
+
201
+    [[noreturn]] void fail_action_(std::string const & action) const;
202
+
203
+    /// String
204
+
205
+    std::string static str_path_           (Path  const & path);
206
+    std::string static str_paths_          (Paths const & paths);
207
+    std::string static str_enum_           (GLenum name);
208
+    std::string static str_error_          (GLenum error);
209
+    std::string static str_format_         (GLenum format);
210
+    std::string static str_type_           (GLenum type);
211
+    std::string static str_internal_format_(GLenum internal_format);
212
+
213
+private:
214
+
215
+    /// Debug
216
+
217
+    void debug_label_() const;
218
+
219
+    void static debug_objects_push_back_(GLObject * debug_object);
220
+    void static debug_objects_erase_    (GLObject * debug_object);
221
+
222
+    /// String
223
+
224
+    std::string static str_object_type_(GLenum object_type);
225
+
226
+private:
227
+
228
+    /// Special member functions
229
+
230
+    GLDeleteObjects const gl_delete_objects_;
231
+
232
+    /// Core
233
+
234
+    auto static constexpr object_pseudo_ = (GLuint)-1;
235
+
236
+    GLenum      const object_type_;
237
+    std::string       object_label_;
238
+    GLuint            object_;
239
+
240
+    /// Debug
241
+
242
+    int           static thread_local debug_;
243
+    DebugCallback static thread_local debug_callback_;
244
+    DebugObjects  static thread_local debug_objects_;
245
+};
246
+
247
+
248
+/// Special member functions
249
+
250
+template<GLObject::GLCreateObject gl_create_object>
251
+void GLObject::gl_create_object_(GLsizei n, GLuint * objects)
252
+{
253
+    for (auto i = GLsizei{0}; i < n; ++i)
254
+        objects[i] = gl_create_object();
255
+}
256
+
257
+template<GLObject::GLDeleteObject gl_delete_object>
258
+void GLObject::gl_delete_object_(GLsizei n, GLuint * objects)
259
+{
260
+    for (auto i = GLsizei{0}; i < n; ++i)
261
+        gl_delete_object(objects[i]);
262
+}
263
+
264
+/// Data
265
+
266
+#define GLOBJECT_DATA( \
267
+    DATA, \
268
+    V1A, \
269
+    V2A, \
270
+    V1U, \
271
+    V2U, \
272
+    COLUMNS, \
273
+    ROWS, \
274
+    ATTRIB, \
275
+    UNIFORM, \
276
+    SUFFIX, \
277
+    FORMAT, \
278
+    TYPE, \
279
+    INTERNAL_FORMAT, \
280
+    VALUE, \
281
+    FOR_COLUMNS, \
282
+    PLUS_COLUMN, \
283
+    PLUS_COLUMN_TIMES_ROWS, \
284
+    ... \
285
+) \
286
+    template<> \
287
+    struct GLObject::DataTraits<DATA> \
288
+    { \
289
+        auto static constexpr name            =       #DATA; \
290
+        auto static constexpr columns         = GLint {COLUMNS}; \
291
+        auto static constexpr rows            = GLint {ROWS}; \
292
+        auto static constexpr format          = GLenum{FORMAT}; \
293
+        auto static constexpr type            = GLenum{TYPE}; \
294
+        auto static constexpr internal_format = GLenum{INTERNAL_FORMAT}; \
295
+        void static attrib(GLuint index, DATA const & value) \
296
+        { \
297
+            GLOBJECT_DEBUG_IF(1) \
298
+                check_supported_({V1A, V2A}); \
299
+            FOR_COLUMNS \
300
+                glVertexAttrib##ATTRIB##SUFFIX( \
301
+                    index PLUS_COLUMN, \
302
+                    VALUE PLUS_COLUMN_TIMES_ROWS \
303
+                ); \
304
+        } \
305
+        void static uniform(GLint location, DATA const & value) \
306
+        { \
307
+            GLOBJECT_DEBUG_IF(1) \
308
+                check_supported_({V1U, V2U}); \
309
+            glUniform##UNIFORM##SUFFIX( \
310
+                location, \
311
+                __VA_ARGS__ \
312
+                VALUE \
313
+            ); \
314
+        } \
315
+    };
316
+
317
+//                                                                                                                                                                GLOBJECT_DATA(DATA         , V1A, V2A, V1U, V2U, COLUMNS, ROWS, ATTRIB, UNIFORM     , SUFFIX      , FORMAT, TYPE, INTERNAL_FORMAT , VALUE, FOR_COLUMNS                                              , PLUS_COLUMN, PLUS_COLUMN_TIMES_ROWS, ...          )
318
+#define GLOBJECT_DATA_SCALAR(    DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE                                                                  ) GLOBJECT_DATA(DATA         , V1A, V2A, V1U, V2U, 1      , 1   , ATTRIB,             , 1##SUFFIX   , GL_RED, TYPE, GL_R  ##INTERNAL, value,                                                          ,            ,                       ,              )
319
+#define GLOBJECT_DATA_VECTOR_N(  DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                            FORMAT,      N   ) GLOBJECT_DATA(DATA##N      , V1A, V2A, V1U, V2U, 1      , N   , ATTRIB,             , N##SUFFIX##v, FORMAT, TYPE, FORMAT##INTERNAL, VALUE,                                                          ,            ,                       , 1,           )
320
+#define GLOBJECT_DATA_MATRIX_N(  DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, FORMAT,      N   ) GLOBJECT_DATA(DATA##N      , V1A, V2A, V1U, V2U, N      , N   , ATTRIB, Matrix      , N##SUFFIX##v, FORMAT, TYPE, FORMAT##INTERNAL, VALUE, for (auto column = GLuint{0}; column < columns; ++column), + column   , + (column * rows)     , 1, TRANSPOSE,)
321
+#define GLOBJECT_DATA_MATRIX_N_M(DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, FORMAT,      N, M) GLOBJECT_DATA(DATA##N##x##M, V1A, V2A, V1U, V2U, N      , M   , ATTRIB, Matrix##N##x, M##SUFFIX##v, FORMAT, TYPE, FORMAT##INTERNAL, VALUE, for (auto column = GLuint{0}; column < columns; ++column), + column   , + (column * rows)     , 1, TRANSPOSE,)
322
+
323
+#define GLOBJECT_DATA_VECTOR(    DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE                                              ) \
324
+    GLOBJECT_DATA_VECTOR_N(      DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                            GL_RG,       2   ) \
325
+    GLOBJECT_DATA_VECTOR_N(      DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                            GL_RGB,      3   ) \
326
+    GLOBJECT_DATA_VECTOR_N(      DATA,      V1A, V2A, V1U, V2U, ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                            GL_RGBA,     4   )
327
+
328
+#define GLOBJECT_DATA_MATRIX(    DATA,                          ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE                   ) \
329
+    GLOBJECT_DATA_MATRIX_N(      DATA,      2,   0,   2,   0,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RG,       2   ) \
330
+    GLOBJECT_DATA_MATRIX_N_M(    DATA,      2,   0,   2,   1,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RGB,      2, 3) \
331
+    GLOBJECT_DATA_MATRIX_N_M(    DATA,      2,   0,   2,   1,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RGBA,     2, 4) \
332
+    GLOBJECT_DATA_MATRIX_N_M(    DATA,      2,   0,   2,   1,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RG,       3, 2) \
333
+    GLOBJECT_DATA_MATRIX_N(      DATA,      2,   0,   2,   0,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RGB,      3   ) \
334
+    GLOBJECT_DATA_MATRIX_N_M(    DATA,      2,   0,   2,   1,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RGBA,     3, 4) \
335
+    GLOBJECT_DATA_MATRIX_N_M(    DATA,      2,   0,   2,   1,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RG,       4, 2) \
336
+    GLOBJECT_DATA_MATRIX_N_M(    DATA,      2,   0,   2,   1,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RGB,      4, 3) \
337
+    GLOBJECT_DATA_MATRIX_N(      DATA,      2,   0,   2,   0,   ATTRIB, SUFFIX, INTERNAL, TYPE,              VALUE,                 TRANSPOSE, GL_RGBA,     4   )
338
+
339
+GLOBJECT_DATA_SCALAR(            GLfloat,   2,   0,   2,   0,   ,       f,      32F,      GL_FLOAT                                                              )
340
+GLOBJECT_DATA_SCALAR(            GLbyte,    3,   0,   2,   0,   I,      i,      8I,       GL_BYTE                                                               )
341
+GLOBJECT_DATA_SCALAR(            GLshort,   3,   0,   2,   0,   I,      i,      16I,      GL_SHORT                                                              )
342
+GLOBJECT_DATA_SCALAR(            GLint,     3,   0,   2,   0,   I,      i,      32I,      GL_INT                                                                )
343
+GLOBJECT_DATA_SCALAR(            GLubyte,   3,   0,   3,   0,   I,      ui,     8UI,      GL_UNSIGNED_BYTE                                                      )
344
+GLOBJECT_DATA_SCALAR(            GLushort,  3,   0,   3,   0,   I,      ui,     16UI,     GL_UNSIGNED_SHORT                                                     )
345
+GLOBJECT_DATA_SCALAR(            GLuint,    3,   0,   3,   0,   I,      ui,     32UI,     GL_UNSIGNED_INT                                                       )
346
+
347
+#ifdef GLM_VERSION
348
+    #include <glm/gtc/type_ptr.hpp>
349
+    GLOBJECT_DATA_VECTOR(        glm::vec,  2,   0,   2,   0,   ,       f,      32F,      GL_FLOAT,          glm::value_ptr(value)                              )
350
+    GLOBJECT_DATA_VECTOR(        glm::ivec, 3,   0,   2,   0,   I,      i,      32I,      GL_INT,            glm::value_ptr(value)                              )
351
+    GLOBJECT_DATA_VECTOR(        glm::uvec, 3,   0,   3,   0,   I,      ui,     32UI,     GL_UNSIGNED_INT,   glm::value_ptr(value)                              )
352
+    GLOBJECT_DATA_MATRIX(        glm::mat,                      ,       f,      32F,      GL_FLOAT,          glm::value_ptr(value), GL_FALSE                    )
353
+#endif
354
+
355
+
356
+#endif // GLOBJECT_HPP_
0 357
new file mode 100644
... ...
@@ -0,0 +1,952 @@
1
+#include <globject.hpp>
2
+
3
+#include <algorithm>
4
+#include <array>
5
+#include <cerrno>
6
+#include <cstdio>
7
+#include <cstring>
8
+#include <fstream>
9
+#include <iostream>
10
+#include <iterator>
11
+#include <sstream>
12
+#include <string>
13
+#include <unordered_set>
14
+
15
+// NOLINTNEXTLINE
16
+#define STR_EXCEPTION GLObject::Exception
17
+#include <str.hpp>
18
+
19
+
20
+/// Special member functions
21
+
22
+
23
+GLObject::GLObject(
24
+    GLGenObjects    gl_gen_objects,
25
+    GLDeleteObjects gl_delete_objects,
26
+    GLenum          object_type,
27
+    // cppcheck-suppress passedByValue
28
+    std::string     object_label
29
+)
30
+:
31
+    gl_delete_objects_{gl_delete_objects},
32
+    object_type_      {object_type},
33
+    object_label_     {std::move(object_label)},
34
+    object_           {0}
35
+{
36
+    try
37
+    {
38
+        if (gl_gen_objects)
39
+            gl_gen_objects(1, &object_);
40
+        else
41
+            object_ = object_pseudo_;
42
+        check_error_(glGetError());
43
+        GLOBJECT_DEBUG_IF(1)
44
+        {
45
+            debug_label_();
46
+            debug_objects_push_back_(this);
47
+            debug_callback()("Create " + debug_name());
48
+        }
49
+    }
50
+    catch (...)
51
+    {
52
+        if (gl_delete_objects_)
53
+            gl_delete_objects_(1, &object_);
54
+        fail_action_("create");
55
+    }
56
+}
57
+
58
+
59
+GLObject::GLObject(GLObject && other) noexcept
60
+:
61
+    gl_delete_objects_{other.gl_delete_objects_},
62
+    object_type_      {other.object_type_},
63
+    object_label_     {std::move(other.object_label_)},
64
+    object_           {other.object_}
65
+{
66
+    other.object_ = 0;
67
+    GLOBJECT_DEBUG_IF(1)
68
+    {
69
+        debug_objects_erase_(&other);
70
+        debug_objects_push_back_(this);
71
+        debug_callback()("Move " + debug_name());
72
+    }
73
+}
74
+
75
+
76
+GLObject::~GLObject()
77
+{
78
+    if (gl_delete_objects_)
79
+        gl_delete_objects_(1, &object_);
80
+    GLOBJECT_DEBUG_IF(1)
81
+    {
82
+        debug_objects_erase_(this);
83
+        debug_callback()("Destroy " + debug_name());
84
+    }
85
+}
86
+
87
+
88
+/// Core
89
+
90
+
91
+bool GLObject::supported(
92
+    Version version_min,
93
+    std::string const & extension
94
+)
95
+{
96
+    auto static version    = Version{0, 0};
97
+    auto static extensions = std::unordered_set<std::string>{};
98
+    if (extension.size() && extension.rfind("GL_", 0) == extension.npos)
99
+        STR_THROW("Failed to parse extension \"" << extension << "\".");
100
+    if (version == Version{0, 0})
101
+    {
102
+        auto const * version_str = (char const *)glGetString(GL_VERSION);
103
+        if (!version_str)
104
+            return false;
105
+        // NOLINTNEXTLINE
106
+        if (2 != std::sscanf(version_str, "%d.%d", &version[0], &version[1]))
107
+            STR_THROW("Failed to parse version \"" << version_str << "\".");
108
+        if (version[0] >= 3)
109
+        {
110
+            auto extension_count = get_integer(GL_NUM_EXTENSIONS);
111
+            for (auto i = 0; i < extension_count; ++i)
112
+                extensions.insert(
113
+                    (char const *)glGetStringi(GL_EXTENSIONS, (GLuint)i)
114
+                );
115
+        }
116
+        else
117
+        {
118
+            auto istream = std::istringstream(
119
+                (char const *)glGetString(GL_EXTENSIONS)
120
+            );
121
+            std::copy(
122
+                std::istream_iterator<std::string>(istream),
123
+                std::istream_iterator<std::string>(),
124
+                std::inserter(extensions, extensions.end())
125
+            );
126
+        }
127
+    }
128
+    if
129
+    (
130
+        (version_min != Version{0, 0}) &&
131
+        (
132
+            (version[0] > version_min[0]) ||
133
+            (
134
+                version[0] == version_min[0] &&
135
+                version[1] >= version_min[1]
136
+            )
137
+        )
138
+    )
139
+        return true;
140
+    if
141
+    (
142
+        extension.size() &&
143
+        extensions.find(extension) != extensions.end()
144
+    )
145
+        return true;
146
+    return false;
147
+}
148
+
149
+
150
+GLint GLObject::get_integer(GLenum name)
151
+try
152
+{
153
+    auto value = GLint{};
154
+    glGetIntegerv(name, &value);
155
+    GLOBJECT_DEBUG_IF(1)
156
+        check_error_(glGetError());
157
+    return value;
158
+}
159
+catch (...)
160
+{
161
+    STR_THROW_NESTED("Failed to get integer " << str_enum_(name) << ":");
162
+}
163
+
164
+
165
+/// Path
166
+
167
+
168
+GLObject::Path GLObject::path_prefix_(
169
+    Path const & path,
170
+    Path const & prefix
171
+)
172
+{
173
+    if (prefix.empty() || path[0] == '/')
174
+        return path;
175
+    return STR(prefix << "/" << path);
176
+}
177
+
178
+
179
+/// TGA
180
+
181
+
182
+GLObject::TGA::TGA(Size size, Data data)
183
+try
184
+:
185
+    header_{size},
186
+    data_{std::move(data)}
187
+{
188
+    check_data_();
189
+}
190
+catch (...)
191
+{
192
+    STR_THROW_NESTED("Failed to create TGA " << str_size_(size) << ":");
193
+}
194
+
195
+
196
+GLObject::TGA GLObject::TGA::read(Path const & path)
197
+try
198
+{
199
+    auto tga = TGA({}, {});
200
+    auto istream = std::ifstream(path, std::ios::binary);
201
+    istream.read(
202
+        (char *)         tga.header_.data(),
203
+        (std::streamsize)tga.header_.size()
204
+    );
205
+    tga.check_header_();
206
+    tga.data_.resize((std::size_t)(4 * tga.size()[0] * tga.size()[1]));
207
+    istream.read(
208
+        (char *)         tga.data_.data(),
209
+        (std::streamsize)tga.data_.size()
210
+    );
211
+    istream.close();
212
+    if (!istream)
213
+        // NOLINTNEXTLINE
214
+        STR_THROW(std::strerror(errno));
215
+    return tga;
216
+}
217
+catch (...)
218
+{
219
+    STR_THROW_NESTED("Failed to read TGA " << str_path_(path) << ":");
220
+}
221
+
222
+
223
+void GLObject::TGA::write(Path const & path) const
224
+try
225
+{
226
+    check_data_();
227
+    auto ostream = std::ofstream(path, std::ios::binary);
228
+    ostream.write((char *)header_.data(), (std::streamsize)header_.size());
229
+    ostream.write((char *)data_  .data(), (std::streamsize)data_  .size());
230
+    ostream.close();
231
+    if (!ostream)
232
+        // NOLINTNEXTLINE
233
+        STR_THROW(std::strerror(errno));
234
+}
235
+catch (...)
236
+{
237
+    STR_THROW_NESTED("Failed to write TGA " << str_path_(path) << ":");
238
+}
239
+
240
+
241
+GLObject::TGA::Data const & GLObject::TGA::data()
242
+{
243
+    return data_;
244
+}
245
+
246
+
247
+GLObject::TGA::Size GLObject::TGA::size() const
248
+{
249
+    return {
250
+        (GLsizei)(header_[12]) << 0 | // NOLINT
251
+        (GLsizei)(header_[13]) << 8,  // NOLINT
252
+        (GLsizei)(header_[14]) << 0 | // NOLINT
253
+        (GLsizei)(header_[15]) << 8,  // NOLINT
254
+    };
255
+}
256
+
257
+
258
+GLObject::TGA::Header::Header(Size size)
259
+:
260
+    std::array<GLubyte, 18>{                // NOLINT
261
+        0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, // NOLINT
262
+        (GLubyte)(size[0] >> 0),            // NOLINT
263
+        (GLubyte)(size[0] >> 8),            // NOLINT
264
+        (GLubyte)(size[1] >> 0),            // NOLINT
265
+        (GLubyte)(size[1] >> 8),            // NOLINT
266
+        32, 8,                              // NOLINT
267
+    }
268
+{
269
+}
270
+
271
+
272
+std::string GLObject::TGA::str_size_(Size size)
273
+{
274
+    return STR("{" << STR_JOIN(", ", it, it, size) << "}");
275
+}
276
+
277
+
278
+void GLObject::TGA::check_header_() const
279
+{
280
+    auto header = Header(size());
281
+    if (header_ != header)
282
+        STR_THROW(
283
+            "Expected TGA header"                             << " "  <<
284
+            "[" << STR_JOIN(", ", byte, byte, header)  << "]" << ", " <<
285
+            "got"                                             << " "  <<
286
+            "[" << STR_JOIN(", ", byte, byte, header_) << "]" << "."
287
+        );
288
+}
289
+
290
+
291
+void GLObject::TGA::check_data_() const
292
+{
293
+    auto data_size = (std::size_t)(4 * size()[0] * size()[1]);
294
+    if (data_size != data_.size())
295
+        STR_THROW(
296
+            "Expected TGA data size " << data_size    << ", " <<
297
+            "got "                    << data_.size() << "."
298
+        );
299
+}
300
+
301
+
302
+/// Debug
303
+
304
+
305
+GLOBJECT_THREAD(GLObject::debug_,          {1})
306
+GLOBJECT_THREAD(GLObject::debug_objects_,  {})
307
+GLOBJECT_THREAD(GLObject::debug_callback_, {[](std::string const & debug)
308
+{
309
+    std::cerr << debug << std::endl;
310
+}})
311
+
312
+
313
+std::string GLObject::debug_name() const
314
+{
315
+    return STR_JOIN(" ", it, it, {
316
+        object_type_  == 0              ? "<GLObject>" :
317
+                                          str_object_type_(object_type_),
318
+        object_label_,
319
+        object_       == 0              ? "<INVALID>" :
320
+        object_       == object_pseudo_ ? "<PSEUDO>"  :
321
+                                          STR(object_),
322
+    });
323
+}
324
+
325
+
326
+void GLObject::debug_label_() const
327
+{
328
+    if
329
+    (
330
+        object_type_ &&
331
+        object_label_.size() &&
332
+        object_
333
+    )
334
+    {
335
+        if (supported({4, 3}, "GL_KHR_debug"))
336
+        {
337
+            auto static object_label_length_max = get_integer(
338
+                GL_MAX_LABEL_LENGTH
339
+            );
340
+            auto object_label_length = std::min(
341
+                (GLsizei)object_label_length_max,
342
+                (GLsizei)object_label_.length()
343
+            );
344
+            glObjectLabel(
345
+                object_type_,
346
+                object_,
347
+                object_label_length,
348
+                object_label_.c_str()
349
+            );
350
+        }
351
+    }
352
+}
353
+
354
+
355
+std::string static debug_objects_data_(
356
+    std::string (GLObject::*debug_data)() const
357
+)
358
+{
359
+    auto const & objects = GLObject::debug_objects();
360
+    auto header = STR("GLObjects: " << objects.size());
361
+    auto data   = STR_JOIN(
362
+        "\n",
363
+        object,
364
+        "  " << (object->*debug_data)(),
365
+        objects
366
+    );
367
+    return STR_JOIN("\n", it, it, {header, data});
368
+}
369
+
370
+
371
+std::string GLObject::debug_objects_name()
372
+{
373
+    return debug_objects_data_(&GLObject::debug_name);
374
+}
375
+
376
+
377
+std::string GLObject::debug_objects_info()
378
+{
379
+    return debug_objects_data_(&GLObject::debug_info);
380
+}
381
+
382
+
383
+std::string GLObject::debug_info() const
384
+{
385
+    return debug_name();
386
+}
387
+
388
+
389
+void GLObject::debug_objects_push_back_(GLObject * debug_object)
390
+{
391
+    debug_objects_.push_back(debug_object);
392
+}
393
+
394
+
395
+void GLObject::debug_objects_erase_(GLObject * debug_object)
396
+{
397
+    debug_objects_.erase(
398
+        std::remove(
399
+            debug_objects_.begin(),
400
+            debug_objects_.end(),
401
+            debug_object
402
+        ),
403
+        debug_objects_.end()
404
+    );
405
+}
406
+
407
+
408
+/// Check
409
+
410
+
411
+void GLObject::check_path_(std::string const & path)
412
+{
413
+    if (!path.size())
414
+        STR_THROW(
415
+            "Expected " << "non-empty path"     << ", " <<
416
+            "got "      << "\"" << path << "\"" << "."
417
+        );
418
+}
419
+
420
+
421
+void GLObject::check_error_(GLenum error)
422
+{
423
+    if (error != GL_NO_ERROR)
424
+        STR_THROW(
425
+            "Expected " << "no error"        << ", " <<
426
+            "got "      << str_error_(error) << "."
427
+        );
428
+}
429
+
430
+
431
+void GLObject::check_supported_(
432
+    Version version_min,
433
+    std::string const & extension
434
+)
435
+{
436
+    if (!supported(version_min, extension))
437
+        STR_THROW(
438
+            "Expected GL version " <<
439
+            STR_JOIN(".", it, it, version_min) <<
440
+            (extension.size() ? STR(" or extension " << extension) : "") << " "
441
+            "got " << glGetString(GL_VERSION)                            << "."
442
+        );
443
+}
444
+
445
+
446
+void GLObject::check_format_(
447
+    GLenum format,
448
+    GLenum format_expected
449
+)
450
+{
451
+    if (format != format_expected)
452
+        STR_THROW(
453
+            "Expected format " << str_format_(format_expected) << ", " <<
454
+            "got "             << str_format_(format)          << "."
455
+        );
456
+}
457
+
458
+
459
+void GLObject::check_type_(
460
+    GLenum type,
461
+    GLenum type_expected
462
+)
463
+{
464
+    if (type != type_expected)
465
+        STR_THROW(
466
+            "Expected type " << str_type_(type_expected) << ", " <<
467
+            "got "           << str_type_(type)          << "."
468
+        );
469
+}
470
+
471
+
472
+void GLObject::check_internal_format_(GLenum internal_format)
473
+{
474
+    switch (internal_format)
475
+    {
476
+        case GL_RED:
477
+        case GL_RGB:
478
+        case GL_RGBA:
479
+        case GL_DEPTH_COMPONENT:
480
+        case GL_STENCIL_INDEX:
481
+        case GL_R3_G3_B2:
482
+            check_supported_({1, 0});
483
+            return;
484
+        case GL_RGB4:
485
+        case GL_RGB5:
486
+        case GL_RGB8:
487
+        case GL_RGB10:
488
+        case GL_RGB12:
489
+        case GL_RGB16:
490
+        case GL_RGBA2:
491
+        case GL_RGBA4:
492
+        case GL_RGBA8:
493
+        case GL_RGBA12:
494
+        case GL_RGBA16:
495
+        case GL_RGB5_A1:
496
+        case GL_RGB10_A2:
497
+            check_supported_({1, 1}, "GL_EXT_texture");
498
+            return;
499
+        case GL_COMPRESSED_RGB:
500
+        case GL_COMPRESSED_RGBA:
501
+            check_supported_({1, 3}, "GL_ARB_texture_compression");
502
+            return;
503
+        case GL_DEPTH_COMPONENT16:
504
+        case GL_DEPTH_COMPONENT24:
505
+        case GL_DEPTH_COMPONENT32:
506
+            check_supported_({1, 4}, "GL_ARB_depth_texture");
507
+            return;
508
+        case GL_SRGB:
509
+        case GL_SRGB8:
510
+        case GL_SRGB_ALPHA:
511
+        case GL_SRGB8_ALPHA8:
512
+        case GL_COMPRESSED_SRGB:
513
+        case GL_COMPRESSED_SRGB_ALPHA:
514
+            check_supported_({2, 1}, "GL_EXT_texture_sRGB");
515
+            return;
516
+        case GL_SR8_EXT:
517
+            check_supported_({}, "GL_EXT_texture_sRGB_R8");
518
+            return;
519
+        case GL_SRG8_EXT:
520
+            check_supported_({}, "GL_EXT_texture_sRGB_RG8");
521
+            return;
522
+        case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
523
+        case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
524
+        case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
525
+        case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
526
+            check_supported_({}, "GL_EXT_texture_compression_s3tc");
527
+            return;
528
+        case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
529
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
530
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
531
+        case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
532
+            check_supported_({}, "GL_EXT_texture_sRGB");
533
+            check_supported_({}, "GL_EXT_texture_compression_s3tc");
534
+            return;
535
+        case GL_COMPRESSED_RED:
536
+        case GL_COMPRESSED_RG:
537
+            check_supported_({3, 0});
538
+            return;
539
+        case GL_COMPRESSED_RED_RGTC1:
540
+        case GL_COMPRESSED_SIGNED_RED_RGTC1:
541
+        case GL_COMPRESSED_RG_RGTC2:
542
+        case GL_COMPRESSED_SIGNED_RG_RGTC2:
543
+            check_supported_({3, 0}, "GL_ARB_texture_compression_rgtc");
544
+            return;
545
+        case GL_RGB16F:
546
+        case GL_RGB32F:
547
+        case GL_RGBA16F:
548
+        case GL_RGBA32F:
549
+            check_supported_({3, 0}, "GL_ARB_texture_float");
550
+            return;
551
+        case GL_RGB8I:
552
+        case GL_RGB16I:
553
+        case GL_RGB32I:
554
+        case GL_RGB8UI:
555
+        case GL_RGB16UI:
556
+        case GL_RGB32UI:
557
+        case GL_RGBA8I:
558
+        case GL_RGBA16I:
559
+        case GL_RGBA32I:
560
+        case GL_RGBA8UI:
561
+        case GL_RGBA16UI:
562
+        case GL_RGBA32UI:
563
+            check_supported_({3, 0}, "GL_EXT_texture_integer");
564
+            return;
565
+        case GL_R16F:
566
+        case GL_R32F:
567
+        case GL_RG16F:
568
+        case GL_RG32F:
569
+        case GL_R8:
570
+        case GL_R8I:
571
+        case GL_R8UI:
572
+        case GL_R16:
573
+        case GL_R16I:
574
+        case GL_R16UI:
575
+        case GL_R32I:
576
+        case GL_R32UI:
577
+        case GL_RG:
578
+        case GL_RG8:
579
+        case GL_RG8I:
580
+        case GL_RG8UI:
581
+        case GL_RG16:
582
+        case GL_RG16I:
583
+        case GL_RG16UI:
584
+        case GL_RG32I:
585
+        case GL_RG32UI:
586
+            check_supported_({3, 0}, "GL_ARB_texture_rg");
587
+            return;
588
+        case GL_R11F_G11F_B10F:
589
+            check_supported_({3, 0}, "GL_EXT_packed_float");
590
+            return;
591
+        case GL_RGB9_E5:
592
+            check_supported_({3, 0}, "GL_EXT_texture_shared_exponent");
593
+            return;
594
+        case GL_DEPTH_STENCIL:
595
+        case GL_DEPTH24_STENCIL8:
596
+            check_supported_({3, 0}, "GL_EXT_packed_depth_stencil");
597
+            return;
598
+        case GL_DEPTH32F_STENCIL8:
599
+        case GL_DEPTH_COMPONENT32F:
600
+            check_supported_({3, 0}, "GL_ARB_depth_buffer_float");
601
+            return;
602
+        case GL_STENCIL_INDEX8:
603
+            check_supported_({3, 0}, "GL_ARB_texture_stencil8");
604
+            return;
605
+        case GL_STENCIL_INDEX1:
606
+        case GL_STENCIL_INDEX4:
607
+        case GL_STENCIL_INDEX16:
608
+            check_supported_({3, 0}, "GL_ARB_framebuffer_object");
609
+            return;
610
+        case GL_R8_SNORM:
611
+        case GL_R16_SNORM:
612
+        case GL_RG8_SNORM:
613
+        case GL_RG16_SNORM:
614
+        case GL_RGB8_SNORM:
615
+        case GL_RGB16_SNORM:
616
+        case GL_RGBA8_SNORM:
617
+        case GL_RGBA16_SNORM:
618
+            check_supported_({3, 1}, "GL_EXT_texture_snorm");
619
+            return;
620
+        case GL_RGB10_A2UI:
621
+            check_supported_({3, 3}, "GL_ARB_texture_rgb10_a2ui");
622
+            return;
623
+        case GL_RGB565:
624
+            check_supported_({4, 1}, "GL_ARB_ES2_compatibility");
625
+            return;
626
+        case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
627
+        case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
628
+        case GL_COMPRESSED_RGBA_BPTC_UNORM:
629
+        case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
630
+            check_supported_({4, 2}, "GL_ARB_texture_compression_bptc");
631
+            return;
632
+        case GL_COMPRESSED_R11_EAC:
633
+        case GL_COMPRESSED_RG11_EAC:
634
+        case GL_COMPRESSED_SIGNED_R11_EAC:
635
+        case GL_COMPRESSED_SIGNED_RG11_EAC:
636
+        case GL_COMPRESSED_RGB8_ETC2:
637
+        case GL_COMPRESSED_SRGB8_ETC2:
638
+        case GL_COMPRESSED_RGBA8_ETC2_EAC:
639
+        case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
640
+        case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
641
+        case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
642
+            check_supported_({4, 2}, "GL_ARB_ES3_compatibility");
643
+            return;
644
+        case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
645
+        case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:
646
+        case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:
647
+        case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:
648
+        case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
649
+        case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:
650
+        case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:
651
+        case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
652
+        case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:
653
+        case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:
654
+        case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:
655
+        case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
656
+        case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:
657
+        case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
658
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
659
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
660
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
661
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
662
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
663
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
664
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
665
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
666
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
667
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
668
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
669
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
670
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
671
+        case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
672
+            check_supported_({4, 2}, "GL_KHR_texture_compression_astc_ldr");
673
+            return;
674
+        default:
675
+            STR_THROW(
676
+                "Expected " << "supported internal format"           << ", "
677
+                "got "      << str_internal_format_(internal_format) << "."
678
+            );
679
+    }
680
+}
681
+
682
+
683
+/// Fail
684
+
685
+
686
+void GLObject::fail_action_(std::string const & action) const
687
+{
688
+    STR_THROW_NESTED("Failed to " << action << " " << debug_name() << ":");
689
+}
690
+
691
+
692
+/// String
693
+
694
+
695
+std::string GLObject::str_path_(Path const & path)
696
+{
697
+    return STR("\"" << path << "\"");
698
+}
699
+
700
+
701
+std::string GLObject::str_paths_(Paths const & paths)
702
+{
703
+    return STR_JOIN(", ", path, str_path_(path), paths);
704
+}
705
+
706
+
707
+std::string GLObject::str_enum_(GLenum name)
708
+{
709
+    return STR(std::hex << std::showbase << name);
710
+}
711
+
712
+
713
+std::string GLObject::str_error_(GLenum error)
714
+{
715
+    switch (error)
716
+    {
717
+        STR_CASE(GL_NO_ERROR)
718
+        STR_CASE(GL_INVALID_ENUM)
719
+        STR_CASE(GL_INVALID_VALUE)
720
+        STR_CASE(GL_INVALID_OPERATION)
721
+        STR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION)
722
+        STR_CASE(GL_OUT_OF_MEMORY)
723
+        STR_CASE(GL_STACK_UNDERFLOW)
724
+        STR_CASE(GL_STACK_OVERFLOW)
725
+        default:
726
+            return str_enum_(error);
727
+    }
728
+}
729
+
730
+
731
+std::string GLObject::str_object_type_(GLenum object_type)
732
+{
733
+    switch (object_type)
734
+    {
735
+        STR_CASE(GL_BUFFER)
736
+        STR_CASE(GL_SHADER)
737
+        STR_CASE(GL_PROGRAM)
738
+        STR_CASE(GL_VERTEX_ARRAY)
739
+        STR_CASE(GL_QUERY)
740
+        STR_CASE(GL_PROGRAM_PIPELINE)
741
+        STR_CASE(GL_TRANSFORM_FEEDBACK)
742
+        STR_CASE(GL_SAMPLER)
743
+        STR_CASE(GL_TEXTURE)
744
+        STR_CASE(GL_RENDERBUFFER)
745
+        STR_CASE(GL_FRAMEBUFFER)
746
+        default:
747
+            return str_enum_(object_type);
748
+    }
749
+}
750
+
751
+
752
+std::string GLObject::str_format_(GLenum format)
753
+{
754
+    switch (format)
755
+    {
756
+        STR_CASE(GL_RED)
757
+        STR_CASE(GL_R)
758
+        STR_CASE(GL_RG)
759
+        STR_CASE(GL_RGB)
760
+        STR_CASE(GL_RGBA)
761
+        STR_CASE(GL_BGR)
762
+        STR_CASE(GL_BGRA)
763
+        STR_CASE(GL_DEPTH_COMPONENT)
764
+        STR_CASE(GL_STENCIL_INDEX)
765
+        default:
766
+            return str_enum_(format);
767
+    }
768
+}
769
+
770
+
771
+std::string GLObject::str_type_(GLenum type)
772
+{
773
+    switch (type)
774
+    {
775
+        STR_CASE(GL_FLOAT)
776
+        STR_CASE(GL_BYTE)
777
+        STR_CASE(GL_SHORT)
778
+        STR_CASE(GL_INT)
779
+        STR_CASE(GL_UNSIGNED_BYTE)
780
+        STR_CASE(GL_UNSIGNED_SHORT)
781
+        STR_CASE(GL_UNSIGNED_INT)
782
+        STR_CASE(GL_UNSIGNED_BYTE_3_3_2)
783
+        STR_CASE(GL_UNSIGNED_BYTE_2_3_3_REV)
784
+        STR_CASE(GL_UNSIGNED_SHORT_5_6_5)
785
+        STR_CASE(GL_UNSIGNED_SHORT_5_6_5_REV)
786
+        STR_CASE(GL_UNSIGNED_SHORT_4_4_4_4)
787
+        STR_CASE(GL_UNSIGNED_SHORT_4_4_4_4_REV)
788
+        STR_CASE(GL_UNSIGNED_SHORT_5_5_5_1)
789
+        STR_CASE(GL_UNSIGNED_SHORT_1_5_5_5_REV)
790
+        STR_CASE(GL_UNSIGNED_INT_8_8_8_8)
791
+        STR_CASE(GL_UNSIGNED_INT_8_8_8_8_REV)
792
+        STR_CASE(GL_UNSIGNED_INT_10_10_10_2)
793
+        STR_CASE(GL_UNSIGNED_INT_2_10_10_10_REV)
794
+        default:
795
+            return str_enum_(type);
796
+    }
797
+}
798
+
799
+
800
+std::string GLObject::str_internal_format_(GLenum internal_format)
801
+{
802
+    switch (internal_format)
803
+    {
804
+        STR_CASE(GL_RED)
805
+        STR_CASE(GL_RGB)
806
+        STR_CASE(GL_RGBA)
807
+        STR_CASE(GL_DEPTH_COMPONENT)
808
+        STR_CASE(GL_STENCIL_INDEX)
809
+        STR_CASE(GL_R3_G3_B2)
810
+        STR_CASE(GL_RGB4)
811
+        STR_CASE(GL_RGB5)
812
+        STR_CASE(GL_RGB8)
813
+        STR_CASE(GL_RGB10)
814
+        STR_CASE(GL_RGB12)
815
+        STR_CASE(GL_RGB16)
816
+        STR_CASE(GL_RGBA2)
817
+        STR_CASE(GL_RGBA4)
818
+        STR_CASE(GL_RGBA8)
819
+        STR_CASE(GL_RGBA12)
820
+        STR_CASE(GL_RGBA16)
821
+        STR_CASE(GL_RGB5_A1)
822
+        STR_CASE(GL_RGB10_A2)
823
+        STR_CASE(GL_COMPRESSED_RGB)
824
+        STR_CASE(GL_COMPRESSED_RGBA)
825
+        STR_CASE(GL_DEPTH_COMPONENT16)
826
+        STR_CASE(GL_DEPTH_COMPONENT24)
827
+        STR_CASE(GL_DEPTH_COMPONENT32)
828
+        STR_CASE(GL_SRGB)
829
+        STR_CASE(GL_SRGB8)
830
+        STR_CASE(GL_SRGB_ALPHA)
831
+        STR_CASE(GL_SRGB8_ALPHA8)
832
+        STR_CASE(GL_COMPRESSED_SRGB)
833
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA)
834
+        STR_CASE(GL_SR8_EXT)
835
+        STR_CASE(GL_SRG8_EXT)
836
+        STR_CASE(GL_COMPRESSED_RGB_S3TC_DXT1_EXT)
837
+        STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)
838
+        STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT)
839
+        STR_CASE(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
840
+        STR_CASE(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT)
841
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT)
842
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT)
843
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT)
844
+        STR_CASE(GL_COMPRESSED_RED)
845
+        STR_CASE(GL_COMPRESSED_RG)
846
+        STR_CASE(GL_COMPRESSED_RED_RGTC1)
847
+        STR_CASE(GL_COMPRESSED_SIGNED_RED_RGTC1)
848
+        STR_CASE(GL_COMPRESSED_RG_RGTC2)
849
+        STR_CASE(GL_COMPRESSED_SIGNED_RG_RGTC2)
850
+        STR_CASE(GL_RGB16F)
851
+        STR_CASE(GL_RGB32F)
852
+        STR_CASE(GL_RGBA16F)
853
+        STR_CASE(GL_RGBA32F)
854
+        STR_CASE(GL_RGB8I)
855
+        STR_CASE(GL_RGB16I)
856
+        STR_CASE(GL_RGB32I)
857
+        STR_CASE(GL_RGB8UI)
858
+        STR_CASE(GL_RGB16UI)
859
+        STR_CASE(GL_RGB32UI)
860
+        STR_CASE(GL_RGBA8I)
861
+        STR_CASE(GL_RGBA16I)
862
+        STR_CASE(GL_RGBA32I)
863
+        STR_CASE(GL_RGBA8UI)
864
+        STR_CASE(GL_RGBA16UI)
865
+        STR_CASE(GL_RGBA32UI)
866
+        STR_CASE(GL_R16F)
867
+        STR_CASE(GL_R32F)
868
+        STR_CASE(GL_RG16F)
869
+        STR_CASE(GL_RG32F)
870
+        STR_CASE(GL_R8)
871
+        STR_CASE(GL_R8I)
872
+        STR_CASE(GL_R8UI)
873
+        STR_CASE(GL_R16)
874
+        STR_CASE(GL_R16I)
875
+        STR_CASE(GL_R16UI)
876
+        STR_CASE(GL_R32I)
877
+        STR_CASE(GL_R32UI)
878
+        STR_CASE(GL_RG)
879
+        STR_CASE(GL_RG8)
880
+        STR_CASE(GL_RG8I)
881
+        STR_CASE(GL_RG8UI)
882
+        STR_CASE(GL_RG16)
883
+        STR_CASE(GL_RG16I)
884
+        STR_CASE(GL_RG16UI)
885
+        STR_CASE(GL_RG32I)
886
+        STR_CASE(GL_RG32UI)
887
+        STR_CASE(GL_R11F_G11F_B10F)
888
+        STR_CASE(GL_RGB9_E5)
889
+        STR_CASE(GL_DEPTH_STENCIL)
890
+        STR_CASE(GL_DEPTH24_STENCIL8)
891
+        STR_CASE(GL_DEPTH32F_STENCIL8)
892
+        STR_CASE(GL_DEPTH_COMPONENT32F)
893
+        STR_CASE(GL_STENCIL_INDEX8)
894
+        STR_CASE(GL_STENCIL_INDEX1)
895
+        STR_CASE(GL_STENCIL_INDEX4)
896
+        STR_CASE(GL_STENCIL_INDEX16)
897
+        STR_CASE(GL_R8_SNORM)
898
+        STR_CASE(GL_R16_SNORM)
899
+        STR_CASE(GL_RG8_SNORM)
900
+        STR_CASE(GL_RG16_SNORM)
901
+        STR_CASE(GL_RGB8_SNORM)
902
+        STR_CASE(GL_RGB16_SNORM)
903
+        STR_CASE(GL_RGBA8_SNORM)
904
+        STR_CASE(GL_RGBA16_SNORM)
905
+        STR_CASE(GL_RGB10_A2UI)
906
+        STR_CASE(GL_RGB565)
907
+        STR_CASE(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT)
908
+        STR_CASE(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT)
909
+        STR_CASE(GL_COMPRESSED_RGBA_BPTC_UNORM)
910
+        STR_CASE(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM)
911
+        STR_CASE(GL_COMPRESSED_R11_EAC)
912
+        STR_CASE(GL_COMPRESSED_RG11_EAC)
913
+        STR_CASE(GL_COMPRESSED_SIGNED_R11_EAC)
914
+        STR_CASE(GL_COMPRESSED_SIGNED_RG11_EAC)
915
+        STR_CASE(GL_COMPRESSED_RGB8_ETC2)
916
+        STR_CASE(GL_COMPRESSED_SRGB8_ETC2)
917
+        STR_CASE(GL_COMPRESSED_RGBA8_ETC2_EAC)
918
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)
919
+        STR_CASE(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2)
920
+        STR_CASE(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2)
921
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_4x4_KHR)
922
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_5x4_KHR)
923
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_5x5_KHR)
924
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_6x5_KHR)
925
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_6x6_KHR)
926
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x5_KHR)
927
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x6_KHR)
928
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_8x8_KHR)
929
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x5_KHR)
930
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x6_KHR)
931
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x8_KHR)
932
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_10x10_KHR)
933
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_12x10_KHR)
934
+        STR_CASE(GL_COMPRESSED_RGBA_ASTC_12x12_KHR)
935
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR)
936
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR)
937
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR)
938
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR)
939
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR)
940
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR)
941
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR)
942
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR)
943
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR)
944
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR)
945
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR)
946
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR)
947
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR)
948
+        STR_CASE(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR)
949
+        default:
950
+            return str_enum_(internal_format);
951
+    }
952
+}
0 953
new file mode 100644
... ...
@@ -0,0 +1,76 @@
1
+#include <iostream>
2
+#include <string>
3
+#include <utility>
4
+
5
+#include <glm/glm.hpp>
6
+
7
+#include <globject.hpp>
8
+
9
+
10
+class GLObjectTest : public GLObject
11
+{
12
+public:
13
+    explicit GLObjectTest()
14
+    :
15
+        GLObject(
16
+            nullptr,
17
+            nullptr,
18
+            0,
19
+            "GLObjectTest"
20
+        )
21
+    {
22
+        std::cout
23
+            << "glm::mat4x3:"                                                                << "\n"
24
+            << "  name:            "  <<           DataTraits<glm::mat4x3>::name             << "\n"
25
+            << "  columns:         "  <<           DataTraits<glm::mat4x3>::columns          << "\n"
26
+            << "  rows:            "  <<           DataTraits<glm::mat4x3>::rows             << "\n"
27
+            << "  format:          "  << str_enum_(DataTraits<glm::mat4x3>::format)          << "\n"
28
+            << "  type:            "  << str_enum_(DataTraits<glm::mat4x3>::type)            << "\n"
29
+            << "  internal_format: "  << str_enum_(DataTraits<glm::mat4x3>::internal_format) << "\n";
30
+
31
+        std::cout
32
+            << path_prefix_("path", "")       << "\n"
33
+            << path_prefix_("path", "prefix") << "\n";
34
+
35
+        // NOLINTNEXTLINE
36
+        TGA tga({1, 1}, {0, 0, 255, 127});
37
+        std::cout
38
+            << "TGA:"                                                    << "\n"
39
+            << "  size:      " << tga.size()[0] << ", " << tga.size()[1] << "\n"
40
+            << "  data size: " << tga.data().size()                      << "\n";
41
+
42
+        std::cout
43
+            << str_path_  ("path")             << "\n"
44
+            << str_paths_ ({"path1", "path2"}) << "\n"
45
+            << str_enum_  (0x01)               << "\n"
46
+            << str_error_ (GL_NO_ERROR)        << "\n";
47
+    }
48
+};
49
+
50
+
51
+int main()
52
+{
53
+    std::cout
54
+        << "Supported 1.0: "
55
+        << GLObject::supported({1, 0})
56
+        << "\n";
57
+    std::cout
58
+        << "GL_ACTIVE_TEXTURE: "
59
+        << GLObject::get_integer(GL_ACTIVE_TEXTURE)
60
+        << "\n";
61
+    GLObject::debug_callback()("Debug callback");
62
+
63
+    auto const print_debug_objects_info = []()
64
+    {
65
+        std::cout << GLObject::debug_objects_info() << "\n";
66
+    };
67
+    print_debug_objects_info();
68
+    {
69
+        auto object_test_moved = GLObjectTest{};
70
+        auto object_test = std::move(object_test_moved);
71
+        print_debug_objects_info();
72
+    }
73
+    print_debug_objects_info();
74
+
75
+    return 0;
76
+}