... | ... |
@@ -13,6 +13,8 @@ A [C++11][] [OpenGL][] \>=[2.0][] [shader][] library. |
13 | 13 |
Overview of usage: |
14 | 14 |
|
15 | 15 |
```cpp |
16 |
+#include <array> |
|
17 |
+ |
|
16 | 18 |
#include <GL/glew.h> |
17 | 19 |
#include <glm/glm.hpp> |
18 | 20 |
|
... | ... |
@@ -32,6 +34,11 @@ int main() |
32 | 34 |
// Local data. |
33 | 35 |
auto light_count = 3; |
34 | 36 |
auto color = glm::vec4{1, 0, 0, 1}; |
37 |
+ struct |
|
38 |
+ { |
|
39 |
+ std::array<float, 4> value1; |
|
40 |
+ std::array<float, 4> value2; |
|
41 |
+ } values = {}; |
|
35 | 42 |
|
36 | 43 |
// Global settings. |
37 | 44 |
Shader::root("assets/shaders"); |
... | ... |
@@ -57,7 +64,9 @@ int main() |
57 | 64 |
.use() |
58 | 65 |
.uniform("light_count", light_count) |
59 | 66 |
.uniform("color", color) |
67 |
+ .uniform("values", values) |
|
60 | 68 |
.validate(); |
69 |
+ Shader::uniform_buffer("values", values); |
|
61 | 70 |
} |
62 | 71 |
``` |
63 | 72 |
|
... | ... |
@@ -294,6 +303,22 @@ the supplied variables `TYPE const & value` and `GLint const & location`, |
294 | 303 |
probably as parameters to `glUniform*`. `GLSHADER_UNIFORM_DELETE(TYPE)` can be |
295 | 304 |
used to [`delete`][] the specialization for a given type. |
296 | 305 |
|
306 |
+#### Uniform blocks / buffers |
|
307 |
+ |
|
308 |
+If no specialization is provided, [uniform block][]s are assumed. This requires |
|
309 |
+OpenGL \>=3.1 or the [`ARB_uniform_buffer_object`][] extension, an error is |
|
310 |
+thrown if it is not available. Shared, by name, [Uniform Buffer Object |
|
311 |
+(UBO)][]s are automatically allocated. Beware of alignment, it is recommended |
|
312 |
+to use the `std140` [memory layout][] and to [avoid `vec3`/`mat3`][]. |
|
313 |
+ |
|
314 |
+Bare arrays are not supported, wrap them in uniform blocks. |
|
315 |
+ |
|
316 |
+[Uniform block]: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Uniform_blocks |
|
317 |
+[`ARB_uniform_buffer_object`]: https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt |
|
318 |
+[Uniform Buffer Object (UBO)]: https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object |
|
319 |
+[memory layout]: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout |
|
320 |
+[avoid `vec3`/`mat3`]: https://stackoverflow.com/questions/38172696/should-i-ever-use-a-vec3-inside-of-a-uniform-buffer-or-shader-storage-buffer-object |
|
321 |
+ |
|
297 | 322 |
## Dependencies |
298 | 323 |
|
299 | 324 |
Public (interface): |
... | ... |
@@ -2,6 +2,7 @@ |
2 | 2 |
|
3 | 3 |
|
4 | 4 |
#extension GL_ARB_shading_language_include : require |
5 |
+#extension GL_ARB_uniform_buffer_object : require |
|
5 | 6 |
|
6 | 7 |
|
7 | 8 |
#include "all_green.h" |
... | ... |
@@ -9,6 +10,10 @@ |
9 | 10 |
|
10 | 11 |
uniform float blue; |
11 | 12 |
uniform vec4 color; |
13 |
+layout(std140) uniform small |
|
14 |
+{ |
|
15 |
+ vec4 value1; |
|
16 |
+}; |
|
12 | 17 |
|
13 | 18 |
|
14 | 19 |
void main() |
... | ... |
@@ -41,6 +41,25 @@ public: |
41 | 41 |
std::string const & name, |
42 | 42 |
Value const & value, |
43 | 43 |
bool required = true |
44 |
+ ); |
|
45 |
+ template<typename Value> |
|
46 |
+ Shader & uniform( |
|
47 |
+ std::string const & name, |
|
48 |
+ Value * value, |
|
49 |
+ bool required = true |
|
50 |
+ ) = delete; |
|
51 |
+ |
|
52 |
+ template<typename Value> |
|
53 |
+ static void uniform_buffer( |
|
54 |
+ std::string const & name, |
|
55 |
+ Value const & value, |
|
56 |
+ bool required = true |
|
57 |
+ ); |
|
58 |
+ template<typename Value> |
|
59 |
+ static void uniform_buffer( |
|
60 |
+ std::string const & name, |
|
61 |
+ Value * value, |
|
62 |
+ bool required = true |
|
44 | 63 |
) = delete; |
45 | 64 |
|
46 | 65 |
protected: |
... | ... |
@@ -51,6 +70,19 @@ protected: |
51 | 70 |
bool set; |
52 | 71 |
}; |
53 | 72 |
|
73 |
+ struct UniformBuffer |
|
74 |
+ { |
|
75 |
+ GLuint buffer; |
|
76 |
+ GLsizeiptr size; |
|
77 |
+ GLuint binding; |
|
78 |
+ bool set; |
|
79 |
+ }; |
|
80 |
+ |
|
81 |
+ struct UniformBlock |
|
82 |
+ { |
|
83 |
+ UniformBuffer & buffer; |
|
84 |
+ }; |
|
85 |
+ |
|
54 | 86 |
void validate_() const; |
55 | 87 |
void current_( |
56 | 88 |
std::string const & error |
... | ... |
@@ -66,17 +98,31 @@ protected: |
66 | 98 |
std::string const & name, |
67 | 99 |
bool required |
68 | 100 |
); |
101 |
+ UniformBlock * uniform_block_( |
|
102 |
+ std::string const & error, |
|
103 |
+ std::string const & name, |
|
104 |
+ bool required, |
|
105 |
+ GLsizeiptr size |
|
106 |
+ ); |
|
107 |
+ static UniformBuffer * uniform_buffer_( |
|
108 |
+ std::string const & error, |
|
109 |
+ std::string const & name, |
|
110 |
+ bool required, |
|
111 |
+ GLsizeiptr size |
|
112 |
+ ); |
|
69 | 113 |
|
70 | 114 |
template<typename Value> |
71 | 115 |
using ByName = std::unordered_map<std::string, Value>; |
72 | 116 |
|
73 |
- GLuint program_; |
|
74 |
- std::string program_name_; |
|
75 |
- std::string static root_; |
|
76 |
- Defines static defines_; |
|
77 |
- Locations static verts_; |
|
78 |
- Locations static frags_; |
|
79 |
- ByName<Uniform> uniforms_; |
|
117 |
+ GLuint program_; |
|
118 |
+ std::string program_name_; |
|
119 |
+ std::string static root_; |
|
120 |
+ Defines static defines_; |
|
121 |
+ Locations static verts_; |
|
122 |
+ Locations static frags_; |
|
123 |
+ ByName<Uniform> uniforms_; |
|
124 |
+ ByName<UniformBlock> uniform_blocks_; |
|
125 |
+ ByName<UniformBuffer> static uniform_buffers_; |
|
80 | 126 |
}; |
81 | 127 |
|
82 | 128 |
|
... | ... |
@@ -123,6 +169,49 @@ inline Shader & Shader::use() |
123 | 169 |
} |
124 | 170 |
|
125 | 171 |
|
172 |
+// Uniform template definitions. |
|
173 |
+ |
|
174 |
+#define GLSHADER_UNIFORM_BUFFER_(BLOCK_OR_BUFFER, BUFFER, SET) \ |
|
175 |
+ if (auto block_or_buffer = BLOCK_OR_BUFFER( \ |
|
176 |
+ error, name, required, sizeof(value) \ |
|
177 |
+ )) \ |
|
178 |
+ { \ |
|
179 |
+ glBindBuffer(GL_UNIFORM_BUFFER, block_or_buffer->BUFFER); \ |
|
180 |
+ GLSHADER_DEBUG_(error_(error, "unprocessed previous error");) \ |
|
181 |
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(value), &value); \ |
|
182 |
+ GLSHADER_DEBUG_(error_(error);) \ |
|
183 |
+ GLSHADER_DEBUG_(block_or_buffer->SET = true;) \ |
|
184 |
+ } |
|
185 |
+ |
|
186 |
+template<typename Value> |
|
187 |
+inline void Shader::uniform_buffer( |
|
188 |
+ std::string const & name, |
|
189 |
+ Value const & value, |
|
190 |
+ bool required |
|
191 |
+) |
|
192 |
+{ |
|
193 |
+ GLSHADER_DEBUG_ERROR_( |
|
194 |
+ "Failed to set uniform buffer '" + name + "'" |
|
195 |
+ ) |
|
196 |
+ GLSHADER_UNIFORM_BUFFER_(uniform_buffer_, buffer, set) |
|
197 |
+} |
|
198 |
+ |
|
199 |
+template<typename Value> |
|
200 |
+inline Shader & Shader::uniform( |
|
201 |
+ std::string const & name, |
|
202 |
+ Value const & value, |
|
203 |
+ bool required |
|
204 |
+) |
|
205 |
+{ |
|
206 |
+ GLSHADER_DEBUG_ERROR_( |
|
207 |
+ "Failed to set uniform block '" + name + "' of " + program_name_ |
|
208 |
+ ) |
|
209 |
+ GLSHADER_DEBUG_(current_(error);) |
|
210 |
+ GLSHADER_UNIFORM_BUFFER_(uniform_block_, buffer.buffer, buffer.set) |
|
211 |
+ return *this; |
|
212 |
+} |
|
213 |
+ |
|
214 |
+ |
|
126 | 215 |
// Uniform template specializations. |
127 | 216 |
|
128 | 217 |
#define GLSHADER_UNIFORM_SIGNATURE_(TYPE) \ |
... | ... |
@@ -33,6 +33,7 @@ GLSHADER_INIT_(Shader::root_, {}) |
33 | 33 |
GLSHADER_INIT_(Shader::defines_, {}) |
34 | 34 |
GLSHADER_INIT_(Shader::verts_, {}) |
35 | 35 |
GLSHADER_INIT_(Shader::frags_, {}) |
36 |
+GLSHADER_INIT_(Shader::uniform_buffers_, {}) |
|
36 | 37 |
|
37 | 38 |
|
38 | 39 |
template<typename Type> |
... | ... |
@@ -402,7 +403,8 @@ Shader::Shader(Paths const & paths) |
402 | 403 |
program_name_{STR( |
403 | 404 |
"shader program " << STR_JOIN(", ", it, "'" << it << "'", paths) |
404 | 405 |
)}, |
405 |
- uniforms_{} |
|
406 |
+ uniforms_{}, |
|
407 |
+ uniform_blocks_{} |
|
406 | 408 |
{ |
407 | 409 |
// Get label limits. |
408 | 410 |
static auto const max_label_length = get_integer_<GLsizei>( |
... | ... |
@@ -560,6 +562,39 @@ Shader::Shader(Paths const & paths) |
560 | 562 |
uniforms_.emplace(name, Uniform{location, false}); |
561 | 563 |
} |
562 | 564 |
); |
565 |
+ |
|
566 |
+ // Initialize uniform blocks. |
|
567 |
+ for_variable_( |
|
568 |
+ program_, |
|
569 |
+ GL_ACTIVE_UNIFORM_BLOCKS, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, |
|
570 |
+ [&](GLuint index, GLsizei max_length, GLchar * name) |
|
571 |
+ { |
|
572 |
+ if (!(GLEW_VERSION_3_1 || GLEW_ARB_uniform_buffer_object)) |
|
573 |
+ throw std::runtime_error{STR( |
|
574 |
+ "Failed to initialize uniform block; " << |
|
575 |
+ "ARB_uniform_buffer_object not available." |
|
576 |
+ )}; |
|
577 |
+ glGetActiveUniformBlockName( |
|
578 |
+ program_, index, max_length, |
|
579 |
+ nullptr, name |
|
580 |
+ ); |
|
581 |
+ auto error = STR( |
|
582 |
+ "Failed to initialize uniform block '" << name << "' of " |
|
583 |
+ << program_name_ |
|
584 |
+ ); |
|
585 |
+ auto size = GLsizei{}; |
|
586 |
+ glGetActiveUniformBlockiv( |
|
587 |
+ program_, index, GL_UNIFORM_BLOCK_DATA_SIZE, (GLint *)&size |
|
588 |
+ ); |
|
589 |
+ auto & uniform_buffer = *uniform_buffer_( |
|
590 |
+ error, name, false, size |
|
591 |
+ ); |
|
592 |
+ glUniformBlockBinding(program_, index, uniform_buffer.binding); |
|
593 |
+ uniform_blocks_.emplace( |
|
594 |
+ name, UniformBlock{uniform_buffer} |
|
595 |
+ ); |
|
596 |
+ } |
|
597 |
+ ); |
|
563 | 598 |
} |
564 | 599 |
catch (...) |
565 | 600 |
{ |
... | ... |
@@ -573,9 +608,10 @@ Shader::Shader(Paths const & paths) |
573 | 608 |
|
574 | 609 |
Shader::Shader(Shader && other) noexcept |
575 | 610 |
: |
576 |
- program_ {other.program_}, |
|
577 |
- program_name_{std::move(other.program_name_)}, |
|
578 |
- uniforms_ {std::move(other.uniforms_)} |
|
611 |
+ program_ {other.program_}, |
|
612 |
+ program_name_ {std::move(other.program_name_)}, |
|
613 |
+ uniforms_ {std::move(other.uniforms_)}, |
|
614 |
+ uniform_blocks_{std::move(other.uniform_blocks_)} |
|
579 | 615 |
{ |
580 | 616 |
other.program_ = 0; |
581 | 617 |
} |
... | ... |
@@ -635,6 +671,14 @@ void Shader::validate_() const |
635 | 671 |
return uniform.set; |
636 | 672 |
} |
637 | 673 |
); |
674 |
+ |
|
675 |
+ // Validate uniform blocks. |
|
676 |
+ uniforms_validate_(validate_error, "uniform block", uniform_blocks_, |
|
677 |
+ [](UniformBlock const & uniform_block) |
|
678 |
+ { |
|
679 |
+ return uniform_block.buffer.set; |
|
680 |
+ } |
|
681 |
+ ); |
|
638 | 682 |
} |
639 | 683 |
|
640 | 684 |
|
... | ... |
@@ -665,11 +709,160 @@ Shader::Uniform * Shader::uniform_( |
665 | 709 |
|
666 | 710 |
// Error if required. |
667 | 711 |
if (required) |
712 |
+ { |
|
713 |
+ auto error_hint = std::string{}; |
|
714 |
+ if (uniform_blocks_.find(name) != uniform_blocks_.end()) |
|
715 |
+ error_hint = " (did you mean the uniform block?)"; |
|
716 |
+ throw std::runtime_error{STR( |
|
717 |
+ error << "; " << |
|
718 |
+ "uniform required but not found" << error_hint << "." |
|
719 |
+ )}; |
|
720 |
+ } |
|
721 |
+ |
|
722 |
+ // Return. |
|
723 |
+ return nullptr; |
|
724 |
+} |
|
725 |
+ |
|
726 |
+ |
|
727 |
+Shader::UniformBlock * Shader::uniform_block_( |
|
728 |
+ std::string const & error, |
|
729 |
+ std::string const & name, |
|
730 |
+ bool required, |
|
731 |
+ GLsizeiptr size |
|
732 |
+) |
|
733 |
+{ |
|
734 |
+ // Return if found. |
|
735 |
+ auto it = uniform_blocks_.find(name); |
|
736 |
+ if (it != uniform_blocks_.end()) |
|
737 |
+ { |
|
738 |
+ auto & uniform_block = it->second; |
|
739 |
+ if (size != uniform_block.buffer.size) |
|
740 |
+ throw std::runtime_error{STR( |
|
741 |
+ error << "; " << |
|
742 |
+ "expected size " << uniform_block.buffer.size << " but got " << |
|
743 |
+ size << "." |
|
744 |
+ )}; |
|
745 |
+ return &uniform_block; |
|
746 |
+ } |
|
747 |
+ |
|
748 |
+ // Error if required. |
|
749 |
+ if (required) |
|
750 |
+ { |
|
751 |
+ auto error_hint = std::string{}; |
|
752 |
+ if (uniforms_.find(name) != uniforms_.end()) |
|
753 |
+ error_hint = " (did you mean the uniform?)"; |
|
668 | 754 |
throw std::runtime_error{STR( |
669 | 755 |
error << "; " << |
670 |
- "uniform required but not found." |
|
756 |
+ "uniform block required but not found" << error_hint << "." |
|
671 | 757 |
)}; |
758 |
+ } |
|
672 | 759 |
|
673 | 760 |
// Return. |
674 | 761 |
return nullptr; |
675 | 762 |
} |
763 |
+ |
|
764 |
+ |
|
765 |
+Shader::UniformBuffer * Shader::uniform_buffer_( |
|
766 |
+ std::string const & error, |
|
767 |
+ std::string const & name, |
|
768 |
+ bool required, |
|
769 |
+ GLsizeiptr size |
|
770 |
+) |
|
771 |
+{ |
|
772 |
+ // Get uniform block / buffer limits. |
|
773 |
+ static auto const max_uniform_block_size = get_integer_<GLuint>( |
|
774 |
+ GL_MAX_UNIFORM_BLOCK_SIZE |
|
775 |
+ ); |
|
776 |
+ static auto const max_uniform_buffer_bindings = get_integer_<GLuint>( |
|
777 |
+ GL_MAX_UNIFORM_BUFFER_BINDINGS |
|
778 |
+ ); |
|
779 |
+ |
|
780 |
+ // Define next binding. |
|
781 |
+ static auto next_uniform_buffer_binding_ = GLuint{0}; |
|
782 |
+ |
|
783 |
+ // Return if found. |
|
784 |
+ auto it = uniform_buffers_.find(name); |
|
785 |
+ if (it != uniform_buffers_.end()) |
|
786 |
+ { |
|
787 |
+ auto & uniform_buffer = it->second; |
|
788 |
+ if (size != uniform_buffer.size) |
|
789 |
+ throw std::runtime_error{STR( |
|
790 |
+ error << "; " << |
|
791 |
+ "expected size " << uniform_buffer.size << " but got " << size |
|
792 |
+ << "." |
|
793 |
+ )}; |
|
794 |
+ return &uniform_buffer; |
|
795 |
+ } |
|
796 |
+ |
|
797 |
+ // Error if required. |
|
798 |
+ if (required) |
|
799 |
+ throw std::runtime_error{STR( |
|
800 |
+ error << "; " << |
|
801 |
+ "uniform buffer required but not found." |
|
802 |
+ )}; |
|
803 |
+ |
|
804 |
+ // Set create error. |
|
805 |
+ auto create_error = STR( |
|
806 |
+ error << ":\n" << |
|
807 |
+ "Failed to create uniform buffer '" << name << "'" |
|
808 |
+ ); |
|
809 |
+ |
|
810 |
+ // Check availability. |
|
811 |
+ if (!(GLEW_VERSION_3_1 || GLEW_ARB_uniform_buffer_object)) |
|
812 |
+ throw std::runtime_error{STR( |
|
813 |
+ create_error << "; " << |
|
814 |
+ "ARB_uniform_buffer_object not available." |
|
815 |
+ )}; |
|
816 |
+ |
|
817 |
+ // Create storage. |
|
818 |
+ auto emplace = uniform_buffers_.emplace(name, UniformBuffer{}); |
|
819 |
+ if (!emplace.second) |
|
820 |
+ throw std::runtime_error{STR( |
|
821 |
+ create_error << "; " << |
|
822 |
+ "already exists." |
|
823 |
+ )}; |
|
824 |
+ auto & uniform_buffer = emplace.first->second; |
|
825 |
+ |
|
826 |
+ // Check for errors. |
|
827 |
+ if (size > max_uniform_block_size) |
|
828 |
+ throw std::runtime_error{STR( |
|
829 |
+ create_error << "; " << |
|
830 |
+ "buffer has size " << size << " but max size is " << |
|
831 |
+ max_uniform_block_size << "." |
|
832 |
+ )}; |
|
833 |
+ if (next_uniform_buffer_binding_ >= max_uniform_buffer_bindings) |
|
834 |
+ throw std::runtime_error{STR( |
|
835 |
+ create_error << "; " << |
|
836 |
+ "buffer would have binding " << next_uniform_buffer_binding_ << |
|
837 |
+ " but max bindings is " << max_uniform_buffer_bindings << "." |
|
838 |
+ )}; |
|
839 |
+ |
|
840 |
+ // Generate and bind. |
|
841 |
+ glGenBuffers(1, &uniform_buffer.buffer); |
|
842 |
+ glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer.buffer), |
|
843 |
+ |
|
844 |
+ // Allocate size. |
|
845 |
+ uniform_buffer.size = size; |
|
846 |
+ error_(create_error, "unprocessed previous error"); |
|
847 |
+ glBufferData( |
|
848 |
+ GL_UNIFORM_BUFFER, |
|
849 |
+ uniform_buffer.size, |
|
850 |
+ nullptr, |
|
851 |
+ GL_DYNAMIC_DRAW |
|
852 |
+ ); |
|
853 |
+ error_(create_error); |
|
854 |
+ |
|
855 |
+ // Allocate binding and bind. |
|
856 |
+ uniform_buffer.binding = next_uniform_buffer_binding_++; |
|
857 |
+ glBindBufferBase( |
|
858 |
+ GL_UNIFORM_BUFFER, |
|
859 |
+ uniform_buffer.binding, |
|
860 |
+ uniform_buffer.buffer |
|
861 |
+ ); |
|
862 |
+ |
|
863 |
+ // Mark as unset. |
|
864 |
+ uniform_buffer.set = false; |
|
865 |
+ |
|
866 |
+ // Return |
|
867 |
+ return &uniform_buffer; |
|
868 |
+} |
... | ... |
@@ -1,3 +1,4 @@ |
1 |
+#include <array> |
|
1 | 2 |
#include <string> |
2 | 3 |
|
3 | 4 |
#include <GL/glew.h> |
... | ... |
@@ -166,6 +167,47 @@ GLTEST(2, 0, 640, 480, glshader) |
166 | 167 |
// Uniform OpenGL Mathematics (GLM). |
167 | 168 |
glm::vec4 color{1.0F}; |
168 | 169 |
|
170 |
+ // Uniform buffer. |
|
171 |
+ struct |
|
172 |
+ { |
|
173 |
+ std::array<float, 4> value1; |
|
174 |
+ } small = {}; |
|
175 |
+ struct |
|
176 |
+ { |
|
177 |
+ std::array<float, 4> value1; |
|
178 |
+ std::array<float, 4> value2; |
|
179 |
+ } big = {}; |
|
180 |
+ GLTEST_EXPECT_EXCEPTION(false, |
|
181 |
+ Shader({"tests/uniform_buffer_small.vert"}).use().validate(), |
|
182 |
+ "Failed to validate shader program 'tests/uniform_buffer_small.vert'; " |
|
183 |
+ "uniform block 'small' not set." |
|
184 |
+ ) |
|
185 |
+ GLTEST_EXPECT_EXCEPTION(false, |
|
186 |
+ Shader::uniform_buffer("small", big), |
|
187 |
+ "Failed to set uniform buffer 'small'; " |
|
188 |
+ "expected size 16 but got 32." |
|
189 |
+ ) |
|
190 |
+ GLTEST_EXPECT_EXCEPTION(false, |
|
191 |
+ Shader({"tests/uniform_buffer_small.vert"}).use().uniform("small", big), |
|
192 |
+ "Failed to set uniform block 'small' of shader program 'tests/uniform_buffer_small.vert'; " |
|
193 |
+ "expected size 16 but got 32." |
|
194 |
+ ) |
|
195 |
+ GLTEST_EXPECT_EXCEPTION(false, |
|
196 |
+ Shader({"tests/uniform_buffer_bad_size.vert"}), |
|
197 |
+ "Failed to initialize uniform block 'small' of shader program 'tests/uniform_buffer_bad_size.vert'; " |
|
198 |
+ "expected size 16 but got 32." |
|
199 |
+ ) |
|
200 |
+ GLTEST_EXPECT_EXCEPTION(false, |
|
201 |
+ Shader({"tests/uniform_buffer_small.vert"}).use().uniform("small", 1.0F), |
|
202 |
+ "Failed to set uniform 'small' of shader program 'tests/uniform_buffer_small.vert'; " |
|
203 |
+ "uniform required but not found (did you mean the uniform block?)." |
|
204 |
+ ) |
|
205 |
+ GLTEST_EXPECT_EXCEPTION(false, |
|
206 |
+ Shader({"tests/uniform_blue.frag"}).use().uniform("blue", small), |
|
207 |
+ "Failed to set uniform block 'blue' of shader program 'tests/uniform_blue.frag'; " |
|
208 |
+ "uniform block required but not found (did you mean the uniform?)." |
|
209 |
+ ) |
|
210 |
+ |
|
169 | 211 |
auto all = Shader({ |
170 | 212 |
"tests/all.vert", |
171 | 213 |
"tests/all.frag", |
... | ... |
@@ -175,7 +217,9 @@ GLTEST(2, 0, 640, 480, glshader) |
175 | 217 |
.use() |
176 | 218 |
.uniform("blue", blue) |
177 | 219 |
.uniform("color", color) |
220 |
+ .uniform("small", small) |
|
178 | 221 |
.validate(); |
222 |
+ Shader::uniform_buffer("small", small); |
|
179 | 223 |
|
180 | 224 |
constexpr auto size = 0.5F; |
181 | 225 |
glBegin(GL_TRIANGLE_STRIP); |