From: Steinar H. Gunderson Date: Tue, 23 Feb 2016 21:33:18 +0000 (+0100) Subject: Use UBOs instead of glUniform. Work in progress; no clear wins seen yet. X-Git-Url: https://git.sesse.net/?p=movit;a=commitdiff_plain;h=abd6d031918db487e0724218dc604d4a51e5f650 Use UBOs instead of glUniform. Work in progress; no clear wins seen yet. --- diff --git a/effect.h b/effect.h index 67e6423..d812db0 100644 --- a/effect.h +++ b/effect.h @@ -59,7 +59,9 @@ struct Uniform { const T *value; // Owner by the effect. size_t num_values; // Number of elements; for arrays only. _Not_ the vector length. std::string prefix; // Filled in only after phases have been constructed. - GLint location; // Filled in only after phases have been constructed. -1 if no location. + GLuint location; // Filled in only after phases have been constructed. GL_INVALID_INDEX if no location, or if using UBOs. + GLint ubo_offset; // Same. -1 if no location or if not using UBOs. + GLint ubo_num_elem; // Same. 0 if no location or if not using UBOs. }; class Effect { diff --git a/effect_chain.cpp b/effect_chain.cpp index fa7340d..f800d1e 100644 --- a/effect_chain.cpp +++ b/effect_chain.cpp @@ -275,6 +275,7 @@ template void extract_uniform_declarations(const vector > &effect_uniforms, const string &type_specifier, const string &effect_id, + bool in_ubo_block, vector > *phase_uniforms, string *glsl_string) { @@ -282,8 +283,10 @@ void extract_uniform_declarations(const vector > &effect_uniforms, phase_uniforms->push_back(effect_uniforms[i]); phase_uniforms->back().prefix = effect_id; - *glsl_string += string("uniform ") + type_specifier + " " + effect_id - + "_" + effect_uniforms[i].name + ";\n"; + if (!in_ubo_block) { + *glsl_string += "uniform "; + } + *glsl_string += type_specifier + " " + effect_id + "_" + effect_uniforms[i].name + ";\n"; } } @@ -291,6 +294,7 @@ template void extract_uniform_array_declarations(const vector > &effect_uniforms, const string &type_specifier, const string &effect_id, + bool in_ubo_block, vector > *phase_uniforms, string *glsl_string) { @@ -298,8 +302,12 @@ void extract_uniform_array_declarations(const vector > &effect_unifor phase_uniforms->push_back(effect_uniforms[i]); phase_uniforms->back().prefix = effect_id; + if (!in_ubo_block) { + *glsl_string += "uniform "; + } + char buf[256]; - snprintf(buf, sizeof(buf), "uniform %s %s_%s[%d];\n", + snprintf(buf, sizeof(buf), "%s %s_%s[%d];\n", type_specifier.c_str(), effect_id.c_str(), effect_uniforms[i].name.c_str(), int(effect_uniforms[i].num_values)); @@ -313,6 +321,7 @@ void collect_uniform_locations(GLuint glsl_program_num, vector > *pha for (unsigned i = 0; i < phase_uniforms->size(); ++i) { Uniform &uniform = (*phase_uniforms)[i]; uniform.location = get_uniform_location(glsl_program_num, uniform.prefix, uniform.name); + get_uniform_offset_and_size(glsl_program_num, uniform.prefix, uniform.name, &uniform.ubo_offset, &uniform.ubo_num_elem); } } @@ -422,24 +431,34 @@ void EffectChain::compile_glsl_program(Phase *phase) // before in the output source, since output_fragment_shader() is allowed // to register new uniforms (e.g. arrays that are of unknown length until // finalization time). - // TODO: Make a uniform block for platforms that support it. string frag_shader_uniforms = ""; for (unsigned i = 0; i < phase->effects.size(); ++i) { + const bool in_ubo_block = true; // TODO: Check for the extension. Node *node = phase->effects[i]; Effect *effect = node->effect; const string effect_id = phase->effect_ids[node]; - extract_uniform_declarations(effect->uniforms_sampler2d, "sampler2D", effect_id, &phase->uniforms_sampler2d, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_bool, "bool", effect_id, &phase->uniforms_bool, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_int, "int", effect_id, &phase->uniforms_int, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_float, "float", effect_id, &phase->uniforms_float, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_vec2, "vec2", effect_id, &phase->uniforms_vec2, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_vec3, "vec3", effect_id, &phase->uniforms_vec3, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_vec4, "vec4", effect_id, &phase->uniforms_vec4, &frag_shader_uniforms); - extract_uniform_array_declarations(effect->uniforms_float_array, "float", effect_id, &phase->uniforms_float, &frag_shader_uniforms); - extract_uniform_array_declarations(effect->uniforms_vec2_array, "vec2", effect_id, &phase->uniforms_vec2, &frag_shader_uniforms); - extract_uniform_array_declarations(effect->uniforms_vec3_array, "vec3", effect_id, &phase->uniforms_vec3, &frag_shader_uniforms); - extract_uniform_array_declarations(effect->uniforms_vec4_array, "vec4", effect_id, &phase->uniforms_vec4, &frag_shader_uniforms); - extract_uniform_declarations(effect->uniforms_mat3, "mat3", effect_id, &phase->uniforms_mat3, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_bool, "bool", effect_id, in_ubo_block, &phase->uniforms_bool, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_int, "int", effect_id, in_ubo_block, &phase->uniforms_int, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_float, "float", effect_id, in_ubo_block, &phase->uniforms_float, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_vec2, "vec2", effect_id, in_ubo_block, &phase->uniforms_vec2, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_vec3, "vec3", effect_id, in_ubo_block, &phase->uniforms_vec3, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_vec4, "vec4", effect_id, in_ubo_block, &phase->uniforms_vec4, &frag_shader_uniforms); + extract_uniform_array_declarations(effect->uniforms_float_array, "float", effect_id, in_ubo_block, &phase->uniforms_float, &frag_shader_uniforms); + extract_uniform_array_declarations(effect->uniforms_vec2_array, "vec2", effect_id, in_ubo_block, &phase->uniforms_vec2, &frag_shader_uniforms); + extract_uniform_array_declarations(effect->uniforms_vec3_array, "vec3", effect_id, in_ubo_block, &phase->uniforms_vec3, &frag_shader_uniforms); + extract_uniform_array_declarations(effect->uniforms_vec4_array, "vec4", effect_id, in_ubo_block, &phase->uniforms_vec4, &frag_shader_uniforms); + extract_uniform_declarations(effect->uniforms_mat3, "mat3", effect_id, in_ubo_block, &phase->uniforms_mat3, &frag_shader_uniforms); + } + if (!frag_shader_uniforms.empty()) { + frag_shader_uniforms = "layout(packed) uniform MovitUniforms {\n" + frag_shader_uniforms + "};\n"; + } + + // Samplers must be outside the UBO block. + for (unsigned i = 0; i < phase->effects.size(); ++i) { + Node *node = phase->effects[i]; + Effect *effect = node->effect; + const string effect_id = phase->effect_ids[node]; + extract_uniform_declarations(effect->uniforms_sampler2d, "sampler2D", effect_id, /*in_ubo_block=*/false, &phase->uniforms_sampler2d, &frag_shader_uniforms); } frag_shader = frag_shader_header + frag_shader_uniforms + frag_shader; @@ -466,6 +485,34 @@ void EffectChain::compile_glsl_program(Phase *phase) phase->attribute_indexes.insert(texcoord_attribute_index); } + // Create an UBO for holding the uniforms. This UBO will be updated each frame. + // TODO: Delete the block on destruction. + phase->uniform_block_index = glGetUniformBlockIndex(phase->glsl_program_num, "MovitUniforms"); + if (phase->uniform_block_index != GL_INVALID_INDEX) { + glGenBuffers(1, &phase->ubo); + check_error(); + GLsizei block_size; + glGetActiveUniformBlockiv( + phase->glsl_program_num, phase->uniform_block_index, + GL_UNIFORM_BLOCK_DATA_SIZE, &block_size); + check_error(); + phase->ubo_data.resize(block_size); + + glBindBuffer(GL_UNIFORM_BUFFER, phase->ubo); + check_error(); + glBufferData(GL_UNIFORM_BUFFER, block_size, NULL, GL_DYNAMIC_DRAW); + check_error(); + + // Associate the uniform block with binding point 0, + // and attach the UBO to that binding point. + glUniformBlockBinding(phase->glsl_program_num, phase->uniform_block_index, 0); + check_error(); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, phase->ubo); + check_error(); + } else { + phase->ubo = GL_INVALID_INDEX; + } + // Collect the resulting location numbers for each uniform. collect_uniform_locations(phase->glsl_program_num, &phase->uniforms_sampler2d); collect_uniform_locations(phase->glsl_program_num, &phase->uniforms_bool); @@ -475,6 +522,9 @@ void EffectChain::compile_glsl_program(Phase *phase) collect_uniform_locations(phase->glsl_program_num, &phase->uniforms_vec3); collect_uniform_locations(phase->glsl_program_num, &phase->uniforms_vec4); collect_uniform_locations(phase->glsl_program_num, &phase->uniforms_mat3); + + glBindBuffer(GL_UNIFORM_BUFFER, 0); + check_error(); } // Construct GLSL programs, starting at the given effect and following @@ -1946,54 +1996,75 @@ void EffectChain::execute_phase(Phase *phase, bool last_phase, void EffectChain::setup_uniforms(Phase *phase) { - // TODO: Use UBO blocks. + char *ubo_data = phase->ubo_data.empty() ? NULL : &phase->ubo_data[0]; + for (size_t i = 0; i < phase->uniforms_sampler2d.size(); ++i) { const Uniform &uniform = phase->uniforms_sampler2d[i]; - if (uniform.location != -1) { + if (uniform.location != GL_INVALID_INDEX) { glUniform1iv(uniform.location, uniform.num_values, uniform.value); } + assert(uniform.ubo_offset == -1); // Samplers don't go into UBOs. } for (size_t i = 0; i < phase->uniforms_bool.size(); ++i) { const Uniform &uniform = phase->uniforms_bool[i]; assert(uniform.num_values == 1); - if (uniform.location != -1) { + if (uniform.location != GL_INVALID_INDEX) { glUniform1i(uniform.location, *uniform.value); } + if (uniform.ubo_offset != -1) { + GLint int_val = *uniform.value; + memcpy(ubo_data + uniform.ubo_offset, &int_val, sizeof(int_val)); + } } for (size_t i = 0; i < phase->uniforms_int.size(); ++i) { const Uniform &uniform = phase->uniforms_int[i]; - if (uniform.location != -1) { + if (uniform.location != GL_INVALID_INDEX) { glUniform1iv(uniform.location, uniform.num_values, uniform.value); } + if (uniform.ubo_offset != -1) { + memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * sizeof(*uniform.value)); + } } for (size_t i = 0; i < phase->uniforms_float.size(); ++i) { const Uniform &uniform = phase->uniforms_float[i]; - if (uniform.location != -1) { + if (uniform.location != GL_INVALID_INDEX) { glUniform1fv(uniform.location, uniform.num_values, uniform.value); } + if (uniform.ubo_offset != -1) { + memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * sizeof(*uniform.value)); + } } for (size_t i = 0; i < phase->uniforms_vec2.size(); ++i) { const Uniform &uniform = phase->uniforms_vec2[i]; - if (uniform.location != -1) { - glUniform2fv(uniform.location, uniform.num_values, uniform.value); + if (uniform.location != GL_INVALID_INDEX) { + glUniform2fv(uniform.location, uniform.ubo_num_elem, uniform.value); + } + if (uniform.ubo_offset != -1) { + memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * 2 * sizeof(*uniform.value)); } } for (size_t i = 0; i < phase->uniforms_vec3.size(); ++i) { const Uniform &uniform = phase->uniforms_vec3[i]; - if (uniform.location != -1) { - glUniform3fv(uniform.location, uniform.num_values, uniform.value); + if (uniform.location != GL_INVALID_INDEX) { + glUniform3fv(uniform.location, uniform.ubo_num_elem, uniform.value); + } + if (uniform.ubo_offset != -1) { + memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * 3 * sizeof(*uniform.value)); } } for (size_t i = 0; i < phase->uniforms_vec4.size(); ++i) { const Uniform &uniform = phase->uniforms_vec4[i]; - if (uniform.location != -1) { - glUniform4fv(uniform.location, uniform.num_values, uniform.value); + if (uniform.location != GL_INVALID_INDEX) { + glUniform4fv(uniform.location, uniform.ubo_num_elem, uniform.value); + } + if (uniform.ubo_offset != -1) { + memcpy(ubo_data + uniform.ubo_offset, uniform.value, uniform.ubo_num_elem * 4 * sizeof(*uniform.value)); } } for (size_t i = 0; i < phase->uniforms_mat3.size(); ++i) { const Uniform &uniform = phase->uniforms_mat3[i]; - assert(uniform.num_values == 1); - if (uniform.location != -1) { + assert(uniform.ubo_num_elem == 1); + if (uniform.location != GL_INVALID_INDEX) { // Convert to float (GLSL has no double matrices). float matrixf[9]; for (unsigned y = 0; y < 3; ++y) { @@ -2003,6 +2074,16 @@ void EffectChain::setup_uniforms(Phase *phase) } glUniformMatrix3fv(uniform.location, 1, GL_FALSE, matrixf); } + if (uniform.ubo_offset != -1) { + // TODO + assert(false); + } + } + + if (phase->ubo != GL_INVALID_INDEX) { + // TODO: Do we want to demand DSA for this? + glNamedBufferSubData(phase->ubo, 0, phase->ubo_data.size(), ubo_data); + return; } } diff --git a/effect_chain.h b/effect_chain.h index 718d5ff..de50512 100644 --- a/effect_chain.h +++ b/effect_chain.h @@ -179,6 +179,10 @@ struct Phase { std::vector > uniforms_vec4; std::vector > uniforms_mat3; + GLuint ubo; // GL_INVALID_INDEX if not using UBOs. + GLuint uniform_block_index; + std::vector ubo_data; + // For measurement of GPU time used. std::list timer_query_objects_running; std::list timer_query_objects_free; diff --git a/effect_util.cpp b/effect_util.cpp index dbeb48b..4036caf 100644 --- a/effect_util.cpp +++ b/effect_util.cpp @@ -14,6 +14,24 @@ GLint get_uniform_location(GLuint glsl_program_num, const string &prefix, const return glGetUniformLocation(glsl_program_num, name.c_str()); } +void get_uniform_offset_and_size(GLuint glsl_program_num, const string &prefix, const string &key, GLint *offset, GLint *size) +{ + string name = prefix + "_" + key; + GLuint index; + const GLchar *name_cstr = name.c_str(); + glGetUniformIndices(glsl_program_num, 1, &name_cstr, &index); + check_error(); + if (index == GL_INVALID_INDEX) { + *offset = -1; + *size = 0; + return; + } + glGetActiveUniformsiv(glsl_program_num, 1, &index, GL_UNIFORM_OFFSET, offset); + check_error(); + glGetActiveUniformsiv(glsl_program_num, 1, &index, GL_UNIFORM_SIZE, size); + check_error(); +} + void set_uniform_int(GLuint glsl_program_num, const string &prefix, const string &key, int value) { GLint location = get_uniform_location(glsl_program_num, prefix, key); diff --git a/effect_util.h b/effect_util.h index 1a8f04a..e472d23 100644 --- a/effect_util.h +++ b/effect_util.h @@ -23,6 +23,7 @@ class Node; // Note that using EffectChain::register_uniform_*() is more efficient // than calling these from set_gl_state(). GLint get_uniform_location(GLuint glsl_program_num, const std::string &prefix, const std::string &key); +void get_uniform_offset_and_size(GLuint glsl_program_num, const std::string &prefix, const std::string &key, GLint *offset, GLint *size); void set_uniform_int(GLuint glsl_program_num, const std::string &prefix, const std::string &key, int value); void set_uniform_float(GLuint glsl_program_num, const std::string &prefix, const std::string &key, float value); void set_uniform_vec2(GLuint glsl_program_num, const std::string &prefix, const std::string &key, const float *values); diff --git a/header.130.frag b/header.130.frag index 129f493..10c8014 100644 --- a/header.130.frag +++ b/header.130.frag @@ -1,5 +1,9 @@ #version 130 +// TODO: Make dependent on whether we actually use the feature, +// and also put in all the headers. +#extension GL_ARB_uniform_buffer_object : enable + in vec2 tc; vec4 tex2D(sampler2D s, vec2 coord)