diff options
Diffstat (limited to 'mesalib/src/glsl/link_uniforms.cpp')
-rw-r--r-- | mesalib/src/glsl/link_uniforms.cpp | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/mesalib/src/glsl/link_uniforms.cpp b/mesalib/src/glsl/link_uniforms.cpp index 6dd1f5354..b9d5361b0 100644 --- a/mesalib/src/glsl/link_uniforms.cpp +++ b/mesalib/src/glsl/link_uniforms.cpp @@ -24,6 +24,7 @@ #include "main/core.h" #include "ir.h" #include "linker.h" +#include "ir_uniform.h" #include "glsl_symbol_table.h" #include "program/hash_table.h" @@ -34,6 +35,21 @@ * \author Ian Romanick <ian.d.romanick@intel.com> */ +/** + * Count the backing storage requirements for a type + */ +static unsigned +values_for_type(const glsl_type *type) +{ + if (type->is_sampler()) { + return 1; + } else if (type->is_array() && type->fields.array->is_sampler()) { + return type->array_size(); + } else { + return type->component_slots(); + } +} + void uniform_field_visitor::process(ir_variable *var) { @@ -83,3 +99,236 @@ uniform_field_visitor::recursion(const glsl_type *t, char **name, this->visit_field(t, *name); } } + +/** + * Class to help calculate the storage requirements for a set of uniforms + * + * As uniforms are added to the active set the number of active uniforms and + * the storage requirements for those uniforms are accumulated. The active + * uniforms are added the the hash table supplied to the constructor. + * + * If the same uniform is added multiple times (i.e., once for each shader + * target), it will only be accounted once. + */ +class count_uniform_size : public uniform_field_visitor { +public: + count_uniform_size(struct string_to_uint_map *map) + : num_active_uniforms(0), num_values(0), map(map) + { + /* empty */ + } + + /** + * Total number of active uniforms counted + */ + unsigned num_active_uniforms; + + /** + * Number of data values required to back the storage for the active uniforms + */ + unsigned num_values; + +private: + virtual void visit_field(const glsl_type *type, const char *name) + { + assert(!type->is_record()); + assert(!(type->is_array() && type->fields.array->is_record())); + + /* If the uniform is already in the map, there's nothing more to do. + */ + unsigned id; + if (this->map->get(id, name)) + return; + + char *key = strdup(name); + this->map->put(this->num_active_uniforms, key); + + /* Each leaf uniform occupies one entry in the list of active + * uniforms. + */ + this->num_active_uniforms++; + this->num_values += values_for_type(type); + } + + struct string_to_uint_map *map; +}; + +/** + * Class to help parcel out pieces of backing storage to uniforms + * + * Each uniform processed has some range of the \c gl_constant_value + * structures associated with it. The association is done by finding + * the uniform in the \c string_to_uint_map and using the value from + * the map to connect that slot in the \c gl_uniform_storage table + * with the next available slot in the \c gl_constant_value array. + * + * \warning + * This class assumes that every uniform that will be processed is + * already in the \c string_to_uint_map. In addition, it assumes that + * the \c gl_uniform_storage and \c gl_constant_value arrays are "big + * enough." + */ +class parcel_out_uniform_storage : public uniform_field_visitor { +public: + parcel_out_uniform_storage(struct string_to_uint_map *map, + struct gl_uniform_storage *uniforms, + union gl_constant_value *values) + : map(map), uniforms(uniforms), next_sampler(0), values(values) + { + /* empty */ + } + +private: + virtual void visit_field(const glsl_type *type, const char *name) + { + assert(!type->is_record()); + assert(!(type->is_array() && type->fields.array->is_record())); + + unsigned id; + bool found = this->map->get(id, name); + assert(found); + + if (!found) + return; + + /* If there is already storage associated with this uniform, it means + * that it was set while processing an earlier shader stage. For + * example, we may be processing the uniform in the fragment shader, but + * the uniform was already processed in the vertex shader. + */ + if (this->uniforms[id].storage != NULL) + return; + + const glsl_type *base_type; + if (type->is_array()) { + this->uniforms[id].array_elements = type->length; + base_type = type->fields.array; + } else { + this->uniforms[id].array_elements = 0; + base_type = type; + } + + if (base_type->is_sampler()) { + this->uniforms[id].sampler = this->next_sampler; + + /* Increment the sampler by 1 for non-arrays and by the number of + * array elements for arrays. + */ + this->next_sampler += MAX2(1, this->uniforms[id].array_elements); + } else { + this->uniforms[id].sampler = ~0; + } + + this->uniforms[id].name = strdup(name); + this->uniforms[id].type = base_type; + this->uniforms[id].initialized = 0; + this->uniforms[id].num_driver_storage = 0; + this->uniforms[id].driver_storage = NULL; + this->uniforms[id].storage = this->values; + + this->values += values_for_type(type); + } + + struct string_to_uint_map *map; + + struct gl_uniform_storage *uniforms; + unsigned next_sampler; + +public: + union gl_constant_value *values; +}; + +void +link_assign_uniform_locations(struct gl_shader_program *prog) +{ + ralloc_free(prog->UniformStorage); + prog->UniformStorage = NULL; + prog->NumUserUniformStorage = 0; + + if (prog->UniformHash != NULL) { + prog->UniformHash->clear(); + } else { + prog->UniformHash = new string_to_uint_map; + } + + for (unsigned i = 0; i < Elements(prog->SamplerUnits); i++) { + prog->SamplerUnits[i] = i; + } + + /* First pass: Count the uniform resources used by the user-defined + * uniforms. While this happens, each active uniform will have an index + * assigned to it. + * + * Note: this is *NOT* the index that is returned to the application by + * glGetUniformLocation. + */ + count_uniform_size uniform_size(prog->UniformHash); + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + foreach_list(node, prog->_LinkedShaders[i]->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_uniform)) + continue; + + /* FINISHME: Update code to process built-in uniforms! + */ + if (strncmp("gl_", var->name, 3) == 0) + continue; + + uniform_size.process(var); + } + } + + const unsigned num_user_uniforms = uniform_size.num_active_uniforms; + const unsigned num_data_slots = uniform_size.num_values; + + /* On the outside chance that there were no uniforms, bail out. + */ + if (num_user_uniforms == 0) + return; + + struct gl_uniform_storage *uniforms = + rzalloc_array(prog, struct gl_uniform_storage, num_user_uniforms); + union gl_constant_value *data = + rzalloc_array(uniforms, union gl_constant_value, num_data_slots); +#ifndef NDEBUG + union gl_constant_value *data_end = &data[num_data_slots]; +#endif + + parcel_out_uniform_storage parcel(prog->UniformHash, uniforms, data); + + for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) { + if (prog->_LinkedShaders[i] == NULL) + continue; + + foreach_list(node, prog->_LinkedShaders[i]->ir) { + ir_variable *const var = ((ir_instruction *) node)->as_variable(); + + if ((var == NULL) || (var->mode != ir_var_uniform)) + continue; + + /* FINISHME: Update code to process built-in uniforms! + */ + if (strncmp("gl_", var->name, 3) == 0) + continue; + + parcel.process(var); + } + } + +#ifndef NDEBUG + for (unsigned i = 0; i < num_user_uniforms; i++) { + assert(uniforms[i].storage != NULL); + } +#endif + + assert(parcel.values == data_end); + + prog->NumUserUniformStorage = num_user_uniforms; + prog->UniformStorage = uniforms; + + return; +} |