Browse code

Add implementation

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

... ...
@@ -9,3 +9,24 @@ project(globject
9 9
     VERSION 1.0.0
10 10
     LANGUAGES CXX
11 11
 )
12
+
13
+## Main target
14
+add_library(${PROJECT_NAME})
15
+
16
+## Common
17
+include(common.cmake)
18
+common(
19
+    CXX_STANDARD 11
20
+    DISABLE_CPPCHECK # STR_JOIN
21
+    FETCHCONTENT
22
+        https://git.rcrnstn.net/rcrnstn/glbase
23
+        https://git.rcrnstn.net/rcrnstn/gltraits
24
+        https://git.rcrnstn.net/rcrnstn/cxx-str
25
+    DEPENDENCIES_PRIVATE
26
+        glbase
27
+        gltraits
28
+        cxx-str
29
+    DEPENDENCIES_TESTS
30
+        glbase
31
+        gltraits
32
+)
... ...
@@ -2,11 +2,85 @@
2 2
 
3 3
 A [C++11][]/[OpenGL][]>=[1.0][] [object][] library.
4 4
 
5
+This library provides a class that is intended to be the base class of several
6
+other classes that encapsulate different types of OpenGL objects, making it
7
+easy to avoid [common mistakes][].
8
+
5 9
 [`globject`]: https://git.rcrnstn.net/rcrnstn/globject
6 10
 [C++11]: https://en.wikipedia.org/wiki/C++11
7 11
 [OpenGL]: https://en.wikipedia.org/wiki/OpenGL
8 12
 [1.0]: https://en.wikipedia.org/wiki/OpenGL#Version_history
9 13
 [object]: https://www.khronos.org/opengl/wiki/OpenGL_Object
14
+[common mistakes]: https://www.khronos.org/opengl/wiki/Common_Mistakes#The_Object_Oriented_Language_Problem
15
+
16
+## Usage
17
+
18
+### Template arguments
19
+
20
+`GLObject` is template specialized the following values.
21
+
22
+-   `GL_TEXTURE`
23
+-   `GL_BUFFER`
24
+-   `GL_QUERY`
25
+-   `GL_PROGRAM`
26
+-   `GL_SHADER`
27
+-   `GL_VERTEX_ARRAY`
28
+-   `GL_FRAMEBUFFER`
29
+-   `GL_RENDERBUFFER`
30
+-   `GL_SAMPLER`
31
+-   `GL_TRANSFORM_FEEDBACK`
32
+-   `GL_PROGRAM_PIPELINE`
33
+
34
+The class handles the lifetime of an OpenGL object of the corresponding type.
35
+
36
+### Special member functions
37
+
38
+All [special member functions][] are `protected`. It is impossible to directly
39
+instantiate `GLObject`s, or to delete objects through `GLObject` pointers.
40
+Instead, use a derived class that encapsulates a specific type of OpenGL
41
+object.
42
+
43
+There are no (non-[`delete`][]d) default constructor, copy constructor, or copy
44
+assignment operators. This enforces the invariant that a successfully
45
+constructed instance (that has not been moved from) always (is the only
46
+instance that) corresponds to a valid OpenGL object.
47
+
48
+The move constructor/assignment operator's [`noexcept`][] specifiers are only
49
+guaranteed to be honored if [`debug()`][] is `0`.
50
+
51
+The constructor takes as arguments:
52
+
53
+-   `std::string const & label`. Used when printing debug messages, throwing
54
+    exceptions, and, if OpenGL>=[4.3][] or the [`KHR_debug`][] extension is
55
+    available, passed to [`glObjectLabel`][].
56
+
57
+[special member functions]: https://en.wikipedia.org/wiki/Special_member_functions
58
+[`delete`]: https://en.cppreference.com/w/cpp/language/function#Deleted_functions
59
+[`noexcept`]: https://en.cppreference.com/w/cpp/language/noexcept_spec
60
+[`debug()`]: https://git.rcrnstn.net/rcrnstn/glbase/#debug
61
+[4.3]: https://en.wikipedia.org/wiki/OpenGL#Version_history
62
+[`KHR_debug`]: https://registry.khronos.org/OpenGL/extensions/KHR/KHR_debug.txt
63
+[`glObjectLabel`]: https://registry.khronos.org/OpenGL-Refpages/gl4/html/glObjectLabel.xhtml
64
+
65
+### Object
66
+
67
+`GLuint object() const` returns the underlying OpenGL object. This is
68
+guaranteed to be valid, unless the constructor threw an exception or the
69
+`GLObject` has been moved from, in which case it is `0`.
70
+
71
+### Name
72
+
73
+`std::string name() const` returns a string representation of the object.
74
+
75
+### Debug
76
+
77
+`void debug_action_(std::string const & action)` calls outputs a debug message
78
+containing `action` and `name()`.
79
+
80
+### Fail
81
+
82
+`void fail_action_(std::string const & action) const` throws an exception with
83
+a message containing `action` and `name()`.
10 84
 
11 85
 ## Building
12 86
 
13 87
new file mode 100644
... ...
@@ -0,0 +1,164 @@
1
+/// Guards
2
+
3
+#ifndef GLOBJECT_HPP_
4
+#define GLOBJECT_HPP_
5
+
6
+
7
+/// Includes
8
+
9
+#include <string>
10
+
11
+#include <glbase.hpp>
12
+#include <gltraits.hpp>
13
+
14
+
15
+/// GLObject
16
+
17
+template<GLenum object_type>
18
+class GLObject;
19
+
20
+template<>
21
+class GLObject<0> : protected GLBase
22
+{
23
+
24
+protected:
25
+
26
+    //// Object
27
+
28
+    std::string static label_(
29
+        GLenum object_type,
30
+        GLuint object
31
+    );
32
+    void static label_(
33
+        GLenum              object_type,
34
+        GLuint              object,
35
+        std::string const & label
36
+    );
37
+
38
+    //// Name
39
+
40
+    std::string static name_(
41
+        GLenum              object_type,
42
+        GLuint              object,
43
+        std::string const & label = {}
44
+    );
45
+
46
+};
47
+
48
+template<GLenum object_type>
49
+class GLObject
50
+:
51
+    public    GLObject<0>,
52
+    protected GLTraits::Object<object_type>
53
+{
54
+
55
+public:
56
+
57
+    //// Special member functions
58
+
59
+    template<typename... Args>
60
+    explicit GLObject(std::string const & label, Args... args)
61
+    try
62
+    :
63
+        object_{0}
64
+    {
65
+        if (debug() >= 2)
66
+            this->debug_action_("construct", name_(object_type, 0, label));
67
+        this->gen_objects(1, &object_, args...);
68
+        try
69
+        {
70
+            // Strictly speaking `glObjectLabel` needs a *created* object.
71
+            // `glGen*s` does not create objects (`glCreate*` does though), it
72
+            // just reserves names for them. *Binding* that name for the first
73
+            // time creates the object. Therefore, the below is not guaranteed
74
+            // to work, but seems to on most drivers... Forcing our subclasses
75
+            // to bind the name and call us back to set the label is deemed too
76
+            // inconvenient so here we are.
77
+            label_(object_type, object_, label);
78
+            check_error_(glGetError());
79
+        }
80
+        catch (...)
81
+        {
82
+            this->delete_objects(1, &object_);
83
+            object_ = 0;
84
+            throw;
85
+        }
86
+    }
87
+    catch (...)
88
+    {
89
+        this->fail_action_("construct", name_(object_type, 0, label));
90
+    }
91
+
92
+    ~GLObject()
93
+    {
94
+        if (debug() >= 2)
95
+            debug_action_("destroy");
96
+        this->delete_objects(1, &object_);
97
+    }
98
+
99
+    GLObject(GLObject && other) noexcept
100
+    :
101
+        object_{other.object_}
102
+    {
103
+        if (debug() >= 2)
104
+            debug_action_("move construct");
105
+        other.object_ = 0;
106
+    }
107
+
108
+    GLObject & operator=(GLObject && other) noexcept
109
+    {
110
+        if (debug() >= 2)
111
+            debug_action_("move assign");
112
+        if (&other != this)
113
+        {
114
+            this->delete_objects(1, &object_);
115
+            object_ = other.object_;
116
+            other.object_ = 0;
117
+        }
118
+        return *this;
119
+    }
120
+
121
+    GLObject(GLObject const &) = delete;
122
+    GLObject & operator=(GLObject const &) = delete;
123
+
124
+    //// Object
125
+
126
+    GLBASE_GET(GLuint, object)
127
+
128
+    operator GLuint() const { return object(); }
129
+
130
+    //// Name
131
+
132
+    std::string name() const
133
+    {
134
+        return name_(object_type, object_);
135
+    }
136
+
137
+protected:
138
+
139
+    //// Debug
140
+
141
+    void debug_action_(std::string const & action) const
142
+    {
143
+        return this->debug_action_(action, name());
144
+    }
145
+
146
+    //// Fail
147
+
148
+    void fail_action_(std::string const & action) const
149
+    {
150
+        return this->fail_action_(action, name());
151
+    }
152
+
153
+private:
154
+
155
+    //// Object
156
+
157
+    GLuint object_;
158
+
159
+};
160
+
161
+
162
+/// Guards
163
+
164
+#endif
0 165
new file mode 100644
... ...
@@ -0,0 +1,85 @@
1
+/// Includes
2
+
3
+
4
+#include "globject.hpp"
5
+
6
+#include <algorithm>
7
+#include <cstddef>
8
+
9
+#include <glbase.hpp>
10
+
11
+#include <str.hpp>
12
+
13
+
14
+/// Object
15
+
16
+
17
+std::string GLObject<0>::label_(
18
+    GLenum object_type,
19
+    GLuint object
20
+)
21
+{
22
+    if (!supported({4, 3}, "GL_KHR_debug"))
23
+        return {};
24
+    auto length = GLint{};
25
+    glGetObjectLabel(
26
+        object_type,
27
+        object,
28
+        0,
29
+        &length,
30
+        nullptr
31
+    );
32
+    auto label = std::string((std::size_t)length, char{});
33
+    glGetObjectLabel(
34
+        object_type,
35
+        object,
36
+        length+1,
37
+        nullptr,
38
+        &label[0]
39
+    );
40
+    check_error_(glGetError());
41
+    return label;
42
+}
43
+
44
+
45
+void GLObject<0>::label_(
46
+    GLenum              object_type,
47
+    GLuint              object,
48
+    std::string const & label
49
+)
50
+{
51
+    if (!supported({4, 3}, "GL_KHR_debug"))
52
+        return;
53
+    auto thread_local label_length_max = integer(GL_MAX_LABEL_LENGTH);
54
+    auto label_length = std::min(
55
+        (GLsizei)label_length_max,
56
+        (GLsizei)label.length()
57
+    );
58
+    glObjectLabel(
59
+        object_type,
60
+        object,
61
+        label_length,
62
+        label.c_str()
63
+    );
64
+}
65
+
66
+
67
+/// Name
68
+
69
+
70
+std::string GLObject<0>::name_(
71
+    GLenum              object_type,
72
+    GLuint              object,
73
+    std::string const & label
74
+)
75
+{
76
+    return STR_JOIN(" ", it, it, {
77
+        str_object_type_(object_type),
78
+        object
79
+            ? STR(object)
80
+            : "",
81
+        !label.empty()
82
+            ? label
83
+            : label_(object_type, object),
84
+    });
85
+}
0 86
new file mode 100644
... ...
@@ -0,0 +1,25 @@
1
+#include <string>
2
+
3
+#include <glbase.hpp>
4
+#include <globject.hpp>
5
+
6
+
7
+struct GLObjectTest : protected GLObject<GL_TEXTURE>
8
+{
9
+    explicit GLObjectTest(std::string const & label)
10
+    :
11
+        GLObject(label)
12
+    {}
13
+};
14
+
15
+
16
+int main()
17
+{
18
+    static_assert(
19
+        sizeof(GLObjectTest) == sizeof(GLuint),
20
+        "GLObject must be the same size as GLuint"
21
+    );
22
+
23
+    GLBase::debug(2);
24
+    GLObjectTest("Test object");
25
+}