| ... | ... |
@@ -5,9 +5,14 @@ A C++11 helper for [OpenGL][] >=2.0 [shaders][Shader]. |
| 5 | 5 |
`glshader` can load an arbitrary number of [shaders][Shader] with the type |
| 6 | 6 |
autodetected by the file extension (using the same rules as the [OpenGL / |
| 7 | 7 |
OpenGL ES Reference Compiler][]). [Uniforms][Uniform] are set with a unified |
| 8 |
-interface (using template specialization). Data is provided by [OpenGL |
|
| 8 |
+interface (using template specialization). Data can be provided by [OpenGL |
|
| 9 | 9 |
Mathematics (GLM)][] objects (which uses [OpenGL Shading Language (GLSL)][] |
| 10 |
-naming conventions). For example: |
|
| 10 |
+naming conventions). For other types of data [uniform blocks][Uniform block] |
|
| 11 |
+are used with (shared, per name) [Uniform Buffer Object (UBO)][] backing |
|
| 12 |
+(beware of alignment). UBOs can also be accessed directly and bound to a |
|
| 13 |
+uniform block by name. The expected usage pattern (as used by |
|
| 14 |
+[`glBufferData`][]) can optionally be specified (defaults to |
|
| 15 |
+`GL_DYNAMIC_DRAW`). For example: |
|
| 11 | 16 |
|
| 12 | 17 |
```cpp |
| 13 | 18 |
Shader shader({
|
| ... | ... |
@@ -18,6 +23,16 @@ shader.use(); |
| 18 | 23 |
|
| 19 | 24 |
glm::vec4 color(1, 0, 0, 1); |
| 20 | 25 |
shader.uniform("color", color);
|
| 26 |
+ |
|
| 27 |
+struct Matrices {
|
|
| 28 |
+ glm::mat4 view; |
|
| 29 |
+ glm::mat4 projection; |
|
| 30 |
+} matrices; |
|
| 31 |
+// Either: |
|
| 32 |
+shader.uniform("matrices", matrices);
|
|
| 33 |
+// or: |
|
| 34 |
+Shader::ubo("matrices", matrices);
|
|
| 35 |
+shader.uniform("matrices", "matrices");
|
|
| 21 | 36 |
``` |
| 22 | 37 |
|
| 23 | 38 |
Errors are handled by throwing [`std::runtime_error`][] with a [`what()`][] |
| ... | ... |
@@ -28,6 +43,9 @@ messages. |
| 28 | 43 |
[OpenGL]: https://www.opengl.org |
| 29 | 44 |
[Shader]: https://www.khronos.org/opengl/wiki/Shader |
| 30 | 45 |
[Uniform]: https://www.khronos.org/opengl/wiki/Uniform |
| 46 |
+[Uniform block]: https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Uniform_blocks |
|
| 47 |
+[Uniform Buffer Object (UBO)]: https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object |
|
| 48 |
+[`glBufferData`]: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferData.xhtml |
|
| 31 | 49 |
[OpenGL / OpenGL ES Reference Compiler]: https://www.khronos.org/opengles/sdk/tools/Reference-Compiler/ |
| 32 | 50 |
[OpenGL Mathematics (GLM)]: https://glm.g-truc.net |
| 33 | 51 |
[OpenGL Shading Language (GLSL)]: https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language |
| ... | ... |
@@ -11,6 +11,13 @@ |
| 11 | 11 |
#include "str.hpp" |
| 12 | 12 |
|
| 13 | 13 |
|
| 14 |
+Shader::StringCache<Shader::UniformBuffer> |
|
| 15 |
+Shader::uniform_buffer_cache_{};
|
|
| 16 |
+ |
|
| 17 |
+GLuint |
|
| 18 |
+Shader::uniform_buffer_binding_next_{0};
|
|
| 19 |
+ |
|
| 20 |
+ |
|
| 14 | 21 |
static void checked_action( |
| 15 | 22 |
void (glAction)( |
| 16 | 23 |
GLuint object |
| ... | ... |
@@ -65,6 +72,25 @@ static GLenum shader_type(std::string type_str) |
| 65 | 72 |
} |
| 66 | 73 |
|
| 67 | 74 |
|
| 75 |
+static std::string uniform_buffer_usage_str(GLenum usage) |
|
| 76 |
+{
|
|
| 77 |
+ switch(usage) |
|
| 78 |
+ {
|
|
| 79 |
+ STR_CASE(GL_STREAM_DRAW) |
|
| 80 |
+ STR_CASE(GL_STREAM_READ) |
|
| 81 |
+ STR_CASE(GL_STREAM_COPY) |
|
| 82 |
+ STR_CASE(GL_STATIC_DRAW) |
|
| 83 |
+ STR_CASE(GL_STATIC_READ) |
|
| 84 |
+ STR_CASE(GL_STATIC_COPY) |
|
| 85 |
+ STR_CASE(GL_DYNAMIC_DRAW) |
|
| 86 |
+ STR_CASE(GL_DYNAMIC_READ) |
|
| 87 |
+ STR_CASE(GL_DYNAMIC_COPY) |
|
| 88 |
+ default: |
|
| 89 |
+ return STR(std::hex << std::showbase << usage); |
|
| 90 |
+ } |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+ |
|
| 68 | 94 |
Shader::Shader(std::vector<std::string> paths, std::string name) |
| 69 | 95 |
: |
| 70 | 96 |
program_{0},
|
| ... | ... |
@@ -73,7 +99,8 @@ Shader::Shader(std::vector<std::string> paths, std::string name) |
| 73 | 99 |
? std::move(name) |
| 74 | 100 |
: STR_JOIN(", ", "'" << it << "'", paths_)
|
| 75 | 101 |
}, |
| 76 |
- uniform_location_cache_{}
|
|
| 102 |
+ uniform_location_cache_{},
|
|
| 103 |
+ uniform_block_index_cache_{}
|
|
| 77 | 104 |
{
|
| 78 | 105 |
new_(); |
| 79 | 106 |
} |
| ... | ... |
@@ -81,10 +108,11 @@ Shader::Shader(std::vector<std::string> paths, std::string name) |
| 81 | 108 |
|
| 82 | 109 |
Shader::Shader(Shader const & other) |
| 83 | 110 |
: |
| 84 |
- program_ {0},
|
|
| 85 |
- paths_ {other.paths_},
|
|
| 86 |
- name_ {other.name_},
|
|
| 87 |
- uniform_location_cache_{other.uniform_location_cache_}
|
|
| 111 |
+ program_ {0},
|
|
| 112 |
+ paths_ {other.paths_},
|
|
| 113 |
+ name_ {other.name_},
|
|
| 114 |
+ uniform_location_cache_ {other.uniform_location_cache_},
|
|
| 115 |
+ uniform_block_index_cache_{other.uniform_block_index_cache_}
|
|
| 88 | 116 |
{
|
| 89 | 117 |
new_(); |
| 90 | 118 |
} |
| ... | ... |
@@ -93,10 +121,11 @@ Shader::Shader(Shader const & other) |
| 93 | 121 |
Shader & Shader::operator=(Shader const & other) |
| 94 | 122 |
{
|
| 95 | 123 |
delete_(); |
| 96 |
- program_ = 0; |
|
| 97 |
- paths_ = other.paths_; |
|
| 98 |
- name_ = other.name_; |
|
| 99 |
- uniform_location_cache_ = other.uniform_location_cache_; |
|
| 124 |
+ program_ = 0; |
|
| 125 |
+ paths_ = other.paths_; |
|
| 126 |
+ name_ = other.name_; |
|
| 127 |
+ uniform_location_cache_ = other.uniform_location_cache_; |
|
| 128 |
+ uniform_block_index_cache_ = other.uniform_block_index_cache_; |
|
| 100 | 129 |
new_(); |
| 101 | 130 |
return *this; |
| 102 | 131 |
} |
| ... | ... |
@@ -104,10 +133,11 @@ Shader & Shader::operator=(Shader const & other) |
| 104 | 133 |
|
| 105 | 134 |
Shader::Shader(Shader && other) |
| 106 | 135 |
: |
| 107 |
- program_ {std::move(other.program_)},
|
|
| 108 |
- paths_ {std::move(other.paths_)},
|
|
| 109 |
- name_ {std::move(other.name_)},
|
|
| 110 |
- uniform_location_cache_{std::move(other.uniform_location_cache_)}
|
|
| 136 |
+ program_ {std::move(other.program_)},
|
|
| 137 |
+ paths_ {std::move(other.paths_)},
|
|
| 138 |
+ name_ {std::move(other.name_)},
|
|
| 139 |
+ uniform_location_cache_ {std::move(other.uniform_location_cache_)},
|
|
| 140 |
+ uniform_block_index_cache_{std::move(other.uniform_block_index_cache_)}
|
|
| 111 | 141 |
{
|
| 112 | 142 |
other.program_ = 0; |
| 113 | 143 |
} |
| ... | ... |
@@ -116,10 +146,11 @@ Shader::Shader(Shader && other) |
| 116 | 146 |
Shader & Shader::operator=(Shader && other) |
| 117 | 147 |
{
|
| 118 | 148 |
delete_(); |
| 119 |
- program_ = std::move(other.program_); |
|
| 120 |
- paths_ = std::move(other.paths_); |
|
| 121 |
- name_ = std::move(other.name_); |
|
| 122 |
- uniform_location_cache_ = std::move(other.uniform_location_cache_); |
|
| 149 |
+ program_ = std::move(other.program_); |
|
| 150 |
+ paths_ = std::move(other.paths_); |
|
| 151 |
+ name_ = std::move(other.name_); |
|
| 152 |
+ uniform_location_cache_ = std::move(other.uniform_location_cache_); |
|
| 153 |
+ uniform_block_index_cache_ = std::move(other.uniform_block_index_cache_); |
|
| 123 | 154 |
other.program_ = 0; |
| 124 | 155 |
return *this; |
| 125 | 156 |
} |
| ... | ... |
@@ -240,13 +271,51 @@ void Shader::new_() |
| 240 | 271 |
} |
| 241 | 272 |
|
| 242 | 273 |
|
| 243 |
-void Shader::delete_() |
|
| 244 |
-{
|
|
| 274 |
+void Shader::delete_() {
|
|
| 245 | 275 |
// Delete program (and detach and delete shaders). |
| 246 | 276 |
glDeleteProgram(program_); |
| 247 | 277 |
} |
| 248 | 278 |
|
| 249 | 279 |
|
| 280 |
+Shader & Shader::uniform( |
|
| 281 |
+ std::string const & name, std::string const & buffer_name |
|
| 282 |
+) {
|
|
| 283 |
+ // Find uniform buffer in cache. |
|
| 284 |
+ auto cache_entry = uniform_buffer_cache_.find(buffer_name); |
|
| 285 |
+ if (cache_entry == uniform_buffer_cache_.end()) |
|
| 286 |
+ throw std::runtime_error{STR(
|
|
| 287 |
+ "Failed to set uniform block '" << name << "' of shader program " |
|
| 288 |
+ << name << "; no uniform buffer '" << buffer_name << "'." |
|
| 289 |
+ )}; |
|
| 290 |
+ auto const & uniform_buffer = cache_entry->second; |
|
| 291 |
+ |
|
| 292 |
+ // Bind. |
|
| 293 |
+ glUniformBlockBinding( |
|
| 294 |
+ program_, uniform_block_index_(name), uniform_buffer.binding |
|
| 295 |
+ ); |
|
| 296 |
+ |
|
| 297 |
+ // Return. |
|
| 298 |
+ return *this; |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+ |
|
| 302 |
+void Shader::uniform_buffer_delete(std::string const & name) {
|
|
| 303 |
+ // Find uniform buffer in cache. |
|
| 304 |
+ auto cache_entry = uniform_buffer_cache_.find(name); |
|
| 305 |
+ if (cache_entry == uniform_buffer_cache_.end()) |
|
| 306 |
+ throw std::runtime_error{STR(
|
|
| 307 |
+ "Failed to delete uniform buffer '" << name << "'; does not exist." |
|
| 308 |
+ )}; |
|
| 309 |
+ auto const & uniform_buffer = cache_entry->second; |
|
| 310 |
+ |
|
| 311 |
+ // Delete buffer. |
|
| 312 |
+ glDeleteBuffers(1, &uniform_buffer.buffer); |
|
| 313 |
+ |
|
| 314 |
+ // Erase cache entries. |
|
| 315 |
+ uniform_buffer_cache_.erase(cache_entry); |
|
| 316 |
+} |
|
| 317 |
+ |
|
| 318 |
+ |
|
| 250 | 319 |
void Shader::ensure_current_( |
| 251 | 320 |
std::string const & operation, |
| 252 | 321 |
std::string const & name |
| ... | ... |
@@ -285,3 +354,104 @@ GLint Shader::uniform_location_(std::string const & name) |
| 285 | 354 |
uniform_location_cache_.emplace(name, location); |
| 286 | 355 |
return location; |
| 287 | 356 |
} |
| 357 |
+ |
|
| 358 |
+ |
|
| 359 |
+GLuint Shader::uniform_block_index_(std::string const & name) {
|
|
| 360 |
+ // Try cache. |
|
| 361 |
+ auto & cache = uniform_block_index_cache_; |
|
| 362 |
+ auto cache_entry = cache.find(name); |
|
| 363 |
+ if (cache_entry != cache.end()) |
|
| 364 |
+ return cache_entry->second; |
|
| 365 |
+ |
|
| 366 |
+ // Query OpenGL. |
|
| 367 |
+ auto index = glGetUniformBlockIndex(program_, name.c_str()); |
|
| 368 |
+ if (index == GL_INVALID_INDEX) |
|
| 369 |
+ throw std::runtime_error{STR(
|
|
| 370 |
+ "Failed to get index of uniform block '" << name << |
|
| 371 |
+ "' in shader program " << name_ << "." |
|
| 372 |
+ )}; |
|
| 373 |
+ |
|
| 374 |
+ // Save in cache and return. |
|
| 375 |
+ cache.emplace(name, index); |
|
| 376 |
+ return index; |
|
| 377 |
+} |
|
| 378 |
+ |
|
| 379 |
+ |
|
| 380 |
+GLuint Shader::uniform_buffer_( |
|
| 381 |
+ std::string const & name, GLsizeiptr size, GLenum usage |
|
| 382 |
+) {
|
|
| 383 |
+ // Get usage string. |
|
| 384 |
+ auto const & usage_str = uniform_buffer_usage_str(usage); |
|
| 385 |
+ |
|
| 386 |
+ // Try cache. |
|
| 387 |
+ auto cache_entry = uniform_buffer_cache_.find(name); |
|
| 388 |
+ if (cache_entry != uniform_buffer_cache_.end()) {
|
|
| 389 |
+ // Get uniform buffer. |
|
| 390 |
+ auto const & uniform_buffer = cache_entry->second; |
|
| 391 |
+ |
|
| 392 |
+ // Check size and usage. |
|
| 393 |
+ if (size != uniform_buffer.size) |
|
| 394 |
+ throw std::runtime_error{STR(
|
|
| 395 |
+ "Failed to set data of uniform buffer '" << name << |
|
| 396 |
+ "'; data has size " << size << " but buffer has size " << |
|
| 397 |
+ uniform_buffer.size << "." |
|
| 398 |
+ )}; |
|
| 399 |
+ if (usage != uniform_buffer.usage) |
|
| 400 |
+ throw std::runtime_error{STR(
|
|
| 401 |
+ "Failed to set data of uniform buffer '" << name << |
|
| 402 |
+ "'; data has usage " << usage_str << " but buffer has usage " |
|
| 403 |
+ << uniform_buffer_usage_str(uniform_buffer.usage) << "." |
|
| 404 |
+ )}; |
|
| 405 |
+ |
|
| 406 |
+ // Return. |
|
| 407 |
+ return uniform_buffer.buffer; |
|
| 408 |
+ } |
|
| 409 |
+ |
|
| 410 |
+ // Validate size and usage. |
|
| 411 |
+ if (size <= 0) |
|
| 412 |
+ throw std::runtime_error{STR(
|
|
| 413 |
+ "Failed to create uniform buffer '" << name << "', invalid size " |
|
| 414 |
+ << size << "." |
|
| 415 |
+ )}; |
|
| 416 |
+ if (usage_str.rfind("GL_", 0) != 0)
|
|
| 417 |
+ throw std::runtime_error{STR(
|
|
| 418 |
+ "Failed to create uniform buffer '" << name << "', invalid usage " |
|
| 419 |
+ << usage_str << "." |
|
| 420 |
+ )}; |
|
| 421 |
+ |
|
| 422 |
+ // Check max uniform buffer bindings. |
|
| 423 |
+ GLuint max_uniform_buffer_bindings; |
|
| 424 |
+ glGetIntegerv( |
|
| 425 |
+ GL_MAX_UNIFORM_BUFFER_BINDINGS, |
|
| 426 |
+ (GLint *)&max_uniform_buffer_bindings |
|
| 427 |
+ ); |
|
| 428 |
+ if (uniform_buffer_binding_next_ >= max_uniform_buffer_bindings) |
|
| 429 |
+ throw std::runtime_error{STR(
|
|
| 430 |
+ "Failed to bind uniform buffer '" << name << "'; max bindings of " |
|
| 431 |
+ << max_uniform_buffer_bindings << " exceeded." |
|
| 432 |
+ )}; |
|
| 433 |
+ |
|
| 434 |
+ // Create uniform buffer. |
|
| 435 |
+ UniformBuffer uniform_buffer; |
|
| 436 |
+ |
|
| 437 |
+ // Generate and bind. |
|
| 438 |
+ glGenBuffers(1, &uniform_buffer.buffer); |
|
| 439 |
+ glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer.buffer), |
|
| 440 |
+ |
|
| 441 |
+ // Set size and usage. |
|
| 442 |
+ uniform_buffer.size = size; |
|
| 443 |
+ uniform_buffer.usage = usage; |
|
| 444 |
+ glBufferData( |
|
| 445 |
+ GL_UNIFORM_BUFFER, uniform_buffer.size, nullptr, uniform_buffer.usage |
|
| 446 |
+ ); |
|
| 447 |
+ |
|
| 448 |
+ // Allocate binding and bind. |
|
| 449 |
+ uniform_buffer.binding = uniform_buffer_binding_next_++; |
|
| 450 |
+ glBindBufferBase( |
|
| 451 |
+ GL_UNIFORM_BUFFER, uniform_buffer.binding, uniform_buffer.buffer |
|
| 452 |
+ ); |
|
| 453 |
+ |
|
| 454 |
+ // Save in cache and return. |
|
| 455 |
+ uniform_buffer_cache_.emplace(name, uniform_buffer); |
|
| 456 |
+ return uniform_buffer.buffer; |
|
| 457 |
+} |
| ... | ... |
@@ -24,13 +24,28 @@ public: |
| 24 | 24 |
Shader & use(); |
| 25 | 25 |
Shader & validate(); |
| 26 | 26 |
template<typename Value> |
| 27 |
- Shader & uniform(std::string const & name, Value const & value) = delete; |
|
| 27 |
+ Shader & uniform(std::string const & name, Value const & value); |
|
| 28 |
+ Shader & uniform(std::string const & name, std::string const & buffer_name); |
|
| 29 |
+ template<typename Value> |
|
| 30 |
+ static void uniform_buffer( |
|
| 31 |
+ std::string const & name, |
|
| 32 |
+ Value const & value, |
|
| 33 |
+ GLenum usage = GL_DYNAMIC_DRAW |
|
| 34 |
+ ); |
|
| 35 |
+ static void uniform_buffer_delete(std::string const & name); |
|
| 28 | 36 |
|
| 29 | 37 |
private: |
| 30 | 38 |
|
| 31 | 39 |
template<typename Value> |
| 32 | 40 |
using StringCache = std::unordered_map<std::string, Value>; |
| 33 | 41 |
|
| 42 |
+ struct UniformBuffer {
|
|
| 43 |
+ GLuint buffer; |
|
| 44 |
+ GLsizeiptr size; |
|
| 45 |
+ GLenum usage; |
|
| 46 |
+ GLuint binding; |
|
| 47 |
+ }; |
|
| 48 |
+ |
|
| 34 | 49 |
void new_(); |
| 35 | 50 |
void delete_(); |
| 36 | 51 |
void ensure_current_( |
| ... | ... |
@@ -38,11 +53,18 @@ private: |
| 38 | 53 |
std::string const & name = "" |
| 39 | 54 |
); |
| 40 | 55 |
GLint uniform_location_(std::string const & name); |
| 56 |
+ GLuint uniform_block_index_(std::string const & name); |
|
| 57 |
+ static GLuint uniform_buffer_( |
|
| 58 |
+ std::string const & name, GLsizeiptr size, GLenum usage |
|
| 59 |
+ ); |
|
| 41 | 60 |
|
| 42 | 61 |
GLuint program_; |
| 43 | 62 |
std::vector<std::string> paths_; |
| 44 | 63 |
std::string name_; |
| 45 | 64 |
StringCache<GLint> uniform_location_cache_; |
| 65 |
+ StringCache<GLuint> uniform_block_index_cache_; |
|
| 66 |
+ static StringCache<UniformBuffer> uniform_buffer_cache_; |
|
| 67 |
+ static GLuint uniform_buffer_binding_next_; |
|
| 46 | 68 |
}; |
| 47 | 69 |
|
| 48 | 70 |
|
| ... | ... |
@@ -54,6 +76,29 @@ private: |
| 54 | 76 |
#endif |
| 55 | 77 |
|
| 56 | 78 |
|
| 79 |
+template<typename Value> |
|
| 80 |
+inline Shader & Shader::uniform( |
|
| 81 |
+ std::string const & name, Value const & value |
|
| 82 |
+) {
|
|
| 83 |
+ GLSHADER_ENSURE_CURRENT("set uniform block", name);
|
|
| 84 |
+ uniform_block_index_(name); |
|
| 85 |
+ uniform_buffer(name, value); |
|
| 86 |
+ uniform(name, name); |
|
| 87 |
+ return *this; |
|
| 88 |
+} |
|
| 89 |
+ |
|
| 90 |
+ |
|
| 91 |
+template<typename Value> |
|
| 92 |
+inline void Shader::uniform_buffer( |
|
| 93 |
+ std::string const & name, Value const & value, GLenum usage |
|
| 94 |
+) {
|
|
| 95 |
+ glBindBuffer( |
|
| 96 |
+ GL_UNIFORM_BUFFER, uniform_buffer_(name, sizeof(value), usage) |
|
| 97 |
+ ); |
|
| 98 |
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(value), &value); |
|
| 99 |
+} |
|
| 100 |
+ |
|
| 101 |
+ |
|
| 57 | 102 |
#define GLSHADER_UNIFORM(VALUE_TYPE, CODE) \ |
| 58 | 103 |
template<> \ |
| 59 | 104 |
inline Shader & Shader::uniform( \ |
| ... | ... |
@@ -93,6 +93,38 @@ void test() {
|
| 93 | 93 |
"Failed to get location of uniform 'nonexistent' of shader program 'good.vert', 'good.frag'." |
| 94 | 94 |
) |
| 95 | 95 |
shader.uniform("color", color);
|
| 96 |
+ |
|
| 97 |
+ struct Matrices {
|
|
| 98 |
+ glm::mat4 view; |
|
| 99 |
+ glm::mat4 projection; |
|
| 100 |
+ } matrices; |
|
| 101 |
+ EXPECT_EXCEPTION( |
|
| 102 |
+ Shader({ "good.vert", "good.frag" }).uniform("noncurrent", matrices),
|
|
| 103 |
+ "Failed to set uniform block 'noncurrent' of shader program 'good.vert', 'good.frag'; program is not current." |
|
| 104 |
+ ) |
|
| 105 |
+ EXPECT_EXCEPTION( |
|
| 106 |
+ shader.uniform("nonexistent", matrices),
|
|
| 107 |
+ "Failed to get index of uniform block 'nonexistent' in shader program 'good.vert', 'good.frag'." |
|
| 108 |
+ ) |
|
| 109 |
+ // (Allocate), set data and bind UBO implicitly. |
|
| 110 |
+ // shader.uniform("matrices", matrices);
|
|
| 111 |
+ // (Allocate), set data and bind UBO explicitly. |
|
| 112 |
+ Shader::uniform_buffer("matrices", matrices);
|
|
| 113 |
+ // shader.uniform("matrices", "matrices");
|
|
| 114 |
+ EXPECT_EXCEPTION( |
|
| 115 |
+ Shader::uniform_buffer("matrices", matrices, GL_STATIC_DRAW),
|
|
| 116 |
+ "Failed to set data of uniform buffer 'matrices'; data has usage GL_STATIC_DRAW but buffer has usage GL_DYNAMIC_DRAW." |
|
| 117 |
+ ) |
|
| 118 |
+ EXPECT_EXCEPTION( |
|
| 119 |
+ Shader::uniform_buffer("matrices", color),
|
|
| 120 |
+ "Failed to set data of uniform buffer 'matrices'; data has size 16 but buffer has size 128." |
|
| 121 |
+ ) |
|
| 122 |
+ EXPECT_EXCEPTION( |
|
| 123 |
+ Shader::uniform_buffer_delete("nonexistent"),
|
|
| 124 |
+ "Failed to delete uniform buffer 'nonexistent'; does not exist." |
|
| 125 |
+ ) |
|
| 126 |
+ // Delete UBO. |
|
| 127 |
+ // Shader::uniform_buffer_delete("matrices");
|
|
| 96 | 128 |
} |
| 97 | 129 |
|
| 98 | 130 |
|