| ... | ... |
@@ -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); |