Browse code

Add input and output

Robert Cranston authored on 11/05/2021 12:27:29
Showing 6 changed files

... ...
@@ -20,6 +20,9 @@ Overview of usage:
20 20
 
21 21
 // Global data.
22 22
 constexpr auto light_count_max = 32;
23
+constexpr auto vert_position  = 0;
24
+constexpr auto vert_tex_coord = 1;
25
+constexpr auto frag_color = 0;
23 26
 
24 27
 
25 28
 int main()
... ...
@@ -29,6 +32,13 @@ int main()
29 32
     Shader::defines({
30 33
         {"light_count_max", std::to_string(light_count_max)},
31 34
     });
35
+    Shader::verts({
36
+        {"vert_position",  vert_position},
37
+        {"vert_tex_coord", vert_tex_coord},
38
+    });
39
+    Shader::frags({
40
+        {"frag_color", frag_color},
41
+    });
32 42
 
33 43
     // Create.
34 44
     auto player = Shader({
... ...
@@ -202,6 +212,37 @@ been seen before.
202 212
 [`glNamedStringARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt
203 213
 [`glCompileShaderIncludeARB`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_shading_language_include.txt
204 214
 
215
+### User-defined vertex inputs (attributes) and fragment outputs (data)
216
+
217
+Locations for user-defined [vertex inputs][] (attributes) and [fragment
218
+outputs][] (data, requires OpenGL >= 3.0) can be specified by calling
219
+`Shader::verts(Shader::Locations const & verts)` and
220
+`Shader::frags(Shader::Locations const & frags)` respectively before
221
+instantiating a `Shader` that uses them. `Shader::Locations` is an alias for
222
+`std::map<std::string, GLuint>`. Any `location` [layout qualifier][] overrides
223
+the values specified this way. Note that a `Shader` object retains the input
224
+and output locations that were in effect when it was instantiated. See
225
+[`glBindAttribLocation`][] and [`glBindFragDataLocation`][]. Note that fragment
226
+outputs are subject to [`glDrawBuffer`][] / [`glDrawBuffers`][].
227
+
228
+An error is thrown if a shader uses a non-specified input. No error is thrown
229
+for non-specified outputs (this is mostly a result of the inability to
230
+enumerate shader outputs before the introduction of the [program interface
231
+query][]). Shaders need not use all of the specified inputs/outputs.
232
+
233
+The intent is for the application to define global unchanging input/output
234
+locations that are used by all shaders, geometry and framebuffers and set them
235
+up once at application startup.
236
+
237
+[vertex inputs]: https://www.khronos.org/opengl/wiki/Vertex_Shader#Inputs
238
+[fragment outputs]: https://www.khronos.org/opengl/wiki/Fragment_Shader#Outputs
239
+[layout qualifier]: https://www.khronos.org/opengl/wiki/Layout_Qualifier_(GLSL)
240
+[`glBindAttribLocation`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindAttribLocation.xhtml
241
+[`glBindFragDataLocation`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBindFragDataLocation.xhtml
242
+[`glDrawBuffer`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffer.xhtml
243
+[`glDrawBuffers`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawBuffers.xhtml
244
+[program interface query]: https://www.khronos.org/opengl/wiki/Program_Introspection#Interface_query
245
+
205 246
 ## Dependencies
206 247
 
207 248
 Public (interface):
... ...
@@ -18,7 +18,10 @@
18 18
 #include <tests/all_subdir/all_recursive.h>
19 19
 
20 20
 
21
+attribute vec4 vert_position;
22
+
23
+
21 24
 void main()
22 25
 {
23
-    gl_Position = gl_Vertex;
26
+    gl_Position = gl_Vertex + vert_position;
24 27
 }
25 28
new file mode 100644
... ...
@@ -0,0 +1,10 @@
1
+# version 110
2
+
3
+
4
+attribute vec4 vert_unset;
5
+
6
+
7
+void main()
8
+{
9
+    gl_Position = gl_Vertex + vert_unset;
10
+}
... ...
@@ -23,9 +23,12 @@ public:
23 23
     Shader & operator=(Shader const &) = delete;
24 24
 
25 25
     using Defines = std::map<std::string, std::string>;
26
+    using Locations = std::map<std::string, GLuint>;
26 27
 
27 28
     static void root(std::string const & root);
28 29
     static void defines(Defines const & defines);
30
+    static void verts(Locations const & verts);
31
+    static void frags(Locations const & frags);
29 32
 
30 33
     GLuint program() const;
31 34
 
... ...
@@ -43,6 +46,8 @@ protected:
43 46
     std::string        program_name_;
44 47
     std::string static root_;
45 48
     Defines     static defines_;
49
+    Locations   static verts_;
50
+    Locations   static frags_;
46 51
 };
47 52
 
48 53
 
... ...
@@ -64,6 +69,8 @@ protected:
64 69
     }
65 70
 GLSHADER_SET_(std::string, root)
66 71
 GLSHADER_SET_(Defines, defines)
72
+GLSHADER_SET_(Locations, verts)
73
+GLSHADER_SET_(Locations, frags)
67 74
 
68 75
 inline GLuint Shader::program() const
69 76
 {
... ...
@@ -23,10 +23,15 @@
23 23
 using Here = std::tuple<std::string, int, std::string>;
24 24
 
25 25
 
26
+constexpr auto max_length_workaround = 4096;
27
+
28
+
26 29
 // NOLINTNEXTLINE
27 30
 #define GLSHADER_INIT_(NAME, INIT) decltype(NAME) NAME INIT;
28 31
 GLSHADER_INIT_(Shader::root_, {})
29 32
 GLSHADER_INIT_(Shader::defines_, {})
33
+GLSHADER_INIT_(Shader::verts_, {})
34
+GLSHADER_INIT_(Shader::frags_, {})
30 35
 
31 36
 
32 37
 template<typename Type>
... ...
@@ -340,6 +345,34 @@ static std::string source_(
340 345
 }
341 346
 
342 347
 
348
+template<typename Function>
349
+static void for_variable_(
350
+    GLuint program,
351
+    GLenum count_enum,
352
+    GLenum max_length_enum,
353
+    Function function
354
+)
355
+{
356
+    // Get count.
357
+    auto count = GLuint{};
358
+    glGetProgramiv(program, count_enum, (GLint *)&count);
359
+
360
+    // Get max length.
361
+    auto max_length = GLsizei{};
362
+    glGetProgramiv(program, max_length_enum, &max_length);
363
+
364
+    // Work around driver bugs.
365
+    if (max_length == 0 && count != 0)
366
+        max_length = max_length_workaround;
367
+
368
+    // Allocate and call function.
369
+    // NOLINTNEXTLINE
370
+    auto name = std::unique_ptr<GLchar[]>(new GLchar[max_length]);
371
+    for (auto index = GLuint{0}; index < count; ++index)
372
+        function(index, max_length, &name[0]);
373
+}
374
+
375
+
343 376
 Shader::Shader(Paths const & paths)
344 377
 :
345 378
     program_{0},
... ...
@@ -441,6 +474,19 @@ Shader::Shader(Paths const & paths)
441 474
             );
442 475
         }
443 476
 
477
+        // Set vertex input locations.
478
+        for (auto const & vert : verts_)
479
+            glBindAttribLocation(
480
+                program_, vert.second, vert.first.c_str()
481
+            );
482
+
483
+        // Set fragment output locations.
484
+        if (GLEW_VERSION_3_0)
485
+            for (auto const & frag : frags_)
486
+                glBindFragDataLocation(
487
+                    program_, frag.second, frag.first.c_str()
488
+                );
489
+
444 490
         // Link program.
445 491
         info_log_action_(
446 492
             STR("Failed to link " << program_name_),
... ...
@@ -451,6 +497,27 @@ Shader::Shader(Paths const & paths)
451 497
         // Detach shaders.
452 498
         for (auto const & shader : shaders)
453 499
             glDetachShader(program_, shader);
500
+
501
+        // Initialize vertex inputs.
502
+        for_variable_(
503
+            program_,
504
+            GL_ACTIVE_ATTRIBUTES, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH,
505
+            [&](GLuint index, GLsizei max_length, GLchar * name)
506
+            {
507
+                GLint  size{};
508
+                GLenum type{};
509
+                glGetActiveAttrib(
510
+                    program_, index, max_length,
511
+                    nullptr, &size, &type, name
512
+                );
513
+                auto location = glGetAttribLocation(program_, name);
514
+                if (location != -1 && verts_.find(name) == verts_.end())
515
+                    throw std::runtime_error{STR(
516
+                        "Failed to initialize vertex input '" << name <<
517
+                        "' of " << program_name_ << "."
518
+                    )};
519
+            }
520
+        );
454 521
     }
455 522
     catch (...)
456 523
     {
... ...
@@ -125,6 +125,18 @@ GLTEST(2, 0, 640, 480, glshader)
125 125
         "assets/shaders/tests/include_nested_extension.vert:7: #include \"include_nested_extension.h\""
126 126
     )
127 127
 
128
+    // Vertex inputs.
129
+    constexpr auto vert_position  = 0;
130
+    constexpr auto vert_tex_coord = 1;
131
+    Shader::verts({
132
+        {"vert_position",  vert_position},
133
+        {"vert_tex_coord", vert_tex_coord},
134
+    });
135
+    GLTEST_EXPECT_EXCEPTION(false,
136
+        Shader({"tests/vert_unset.vert"}),
137
+        "Failed to initialize vertex input 'vert_unset' of shader program 'tests/vert_unset.vert'."
138
+    );
139
+
128 140
     auto all = Shader({
129 141
         "tests/all.vert",
130 142
         "tests/all.frag",