glsl: Add link_uniform_blocks to calculate all UBO data at link-time
authorIan Romanick <ian.d.romanick@intel.com>
Tue, 22 Jan 2013 05:59:56 +0000 (00:59 -0500)
committerIan Romanick <ian.d.romanick@intel.com>
Fri, 25 Jan 2013 14:07:35 +0000 (09:07 -0500)
Calculate all of the block member offsets, the IndexNames, and
everything else to do with every UBO.

Signed-off-by: Ian Romanick <ian.d.romanick@intel.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
src/glsl/link_uniform_blocks.cpp
src/glsl/linker.h

index c9cbde95b6f6ae7455fec5d6cebe4410742ba147..74fe1e29f6d86c35a20e2f98fca25a4944fb91f4 100644 (file)
 #include "ir.h"
 #include "linker.h"
 #include "ir_uniform.h"
+#include "link_uniform_block_active_visitor.h"
+#include "main/hash_table.h"
 #include "program.h"
 
+class ubo_visitor : public uniform_field_visitor {
+public:
+   ubo_visitor(void *mem_ctx, gl_uniform_buffer_variable *variables,
+               unsigned num_variables)
+      : index(0), offset(0), buffer_size(0), variables(variables),
+        num_variables(num_variables), mem_ctx(mem_ctx), is_array_instance(false)
+   {
+      /* empty */
+   }
+
+   void process(const glsl_type *type, const char *name)
+   {
+      this->offset = 0;
+      this->buffer_size = 0;
+      this->is_array_instance = strchr(name, ']') != NULL;
+      this->uniform_field_visitor::process(type, name);
+   }
+
+   unsigned index;
+   unsigned offset;
+   unsigned buffer_size;
+   gl_uniform_buffer_variable *variables;
+   unsigned num_variables;
+   void *mem_ctx;
+   bool is_array_instance;
+
+private:
+   virtual void visit_field(const glsl_type *type, const char *name,
+                            bool row_major)
+   {
+      assert(this->index < this->num_variables);
+
+      gl_uniform_buffer_variable *v = &this->variables[this->index++];
+
+      v->Name = ralloc_strdup(mem_ctx, name);
+      v->Type = type;
+      v->RowMajor = row_major;
+
+      if (this->is_array_instance) {
+         v->IndexName = ralloc_strdup(mem_ctx, name);
+
+         char *open_bracket = strchr(v->IndexName, '[');
+         assert(open_bracket != NULL);
+
+         char *close_bracket = strchr(open_bracket, ']');
+         assert(close_bracket != NULL);
+
+         /* Length of the tail without the ']' but with the NUL.
+          */
+         unsigned len = strlen(close_bracket + 1) + 1;
+
+         memmove(open_bracket, close_bracket + 1, len);
+     } else {
+         v->IndexName = v->Name;
+      }
+
+      unsigned alignment = type->std140_base_alignment(v->RowMajor);
+      unsigned size = type->std140_size(v->RowMajor);
+
+      this->offset = glsl_align(this->offset, alignment);
+      v->Offset = this->offset;
+      this->offset += size;
+
+      /* From the GL_ARB_uniform_buffer_object spec:
+       *
+       *     "For uniform blocks laid out according to [std140] rules, the
+       *      minimum buffer object size returned by the
+       *      UNIFORM_BLOCK_DATA_SIZE query is derived by taking the offset of
+       *      the last basic machine unit consumed by the last uniform of the
+       *      uniform block (including any end-of-array or end-of-structure
+       *      padding), adding one, and rounding up to the next multiple of
+       *      the base alignment required for a vec4."
+       */
+      this->buffer_size = glsl_align(this->offset, 16);
+   }
+
+   virtual void visit_field(const glsl_struct_field *field)
+   {
+      this->offset = glsl_align(this->offset,
+                                field->type->std140_base_alignment(false));
+   }
+};
+
+class count_block_size : public uniform_field_visitor {
+public:
+   count_block_size() : num_active_uniforms(0)
+   {
+      /* empty */
+   }
+
+   unsigned num_active_uniforms;
+
+private:
+   virtual void visit_field(const glsl_type *type, const char *name,
+                            bool row_major)
+   {
+      (void) type;
+      (void) name;
+      (void) row_major;
+      this->num_active_uniforms++;
+   }
+};
+
+struct block {
+   const glsl_type *type;
+   bool has_instance_name;
+};
+
+int
+link_uniform_blocks(void *mem_ctx,
+                    struct gl_shader_program *prog,
+                    struct gl_shader **shader_list,
+                    unsigned num_shaders,
+                    struct gl_uniform_block **blocks_ret)
+{
+   /* This hash table will track all of the uniform blocks that have been
+    * encountered.  Since blocks with the same block-name must be the same,
+    * the hash is organized by block-name.
+    */
+   struct hash_table *block_hash =
+      _mesa_hash_table_create(mem_ctx, _mesa_key_string_equal);
+
+   /* Determine which uniform blocks are active.
+    */
+   link_uniform_block_active_visitor v(mem_ctx, block_hash, prog);
+   for (unsigned i = 0; i < num_shaders; i++) {
+      visit_list_elements(&v, shader_list[i]->ir);
+   }
+
+   /* Count the number of active uniform blocks.  Count the total number of
+    * active slots in those uniform blocks.
+    */
+   unsigned num_blocks = 0;
+   unsigned num_variables = 0;
+   count_block_size block_size;
+   struct hash_entry *entry;
+
+   hash_table_foreach (block_hash, entry) {
+      const struct link_uniform_block_active *const b =
+         (const struct link_uniform_block_active *) entry->data;
+
+      const glsl_type *const block_type =
+         b->type->is_array() ? b->type->fields.array : b->type;
+
+      assert((b->num_array_elements > 0) == b->type->is_array());
+
+      block_size.num_active_uniforms = 0;
+      block_size.process(block_type, "");
+
+      if (b->num_array_elements > 0) {
+         num_blocks += b->num_array_elements;
+         num_variables += b->num_array_elements
+            * block_size.num_active_uniforms;
+      } else {
+         num_blocks++;
+         num_variables += block_size.num_active_uniforms;
+      }
+
+   }
+
+   if (num_blocks == 0) {
+      assert(num_variables == 0);
+      _mesa_hash_table_destroy(block_hash, NULL);
+      return 0;
+   }
+
+   assert(num_variables != 0);
+
+   /* Allocate storage to hold all of the informatation related to uniform
+    * blocks that can be queried through the API.
+    */
+   gl_uniform_block *blocks =
+      ralloc_array(mem_ctx, gl_uniform_block, num_blocks);
+   gl_uniform_buffer_variable *variables =
+      ralloc_array(blocks, gl_uniform_buffer_variable, num_variables);
+
+   /* Add each variable from each uniform block to the API tracking
+    * structures.
+    */
+   unsigned i = 0;
+   ubo_visitor parcel(blocks, variables, num_variables);
+
+   STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_STD140)
+                 == unsigned(ubo_packing_std140));
+   STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_SHARED)
+                 == unsigned(ubo_packing_shared));
+   STATIC_ASSERT(unsigned(GLSL_INTERFACE_PACKING_PACKED)
+                 == unsigned(ubo_packing_packed));
+
+
+   hash_table_foreach (block_hash, entry) {
+      const struct link_uniform_block_active *const b =
+         (const struct link_uniform_block_active *) entry->data;
+      const glsl_type *block_type = b->type;
+
+      if (b->num_array_elements > 0) {
+         const char *const name = block_type->fields.array->name;
+
+         assert(b->has_instance_name);
+         for (unsigned j = 0; j < b->num_array_elements; j++) {
+            blocks[i].Name = ralloc_asprintf(blocks, "%s[%u]", name,
+                                             b->array_elements[j]);
+            blocks[i].Uniforms = &variables[parcel.index];
+            blocks[i].Binding = 0;
+            blocks[i].UniformBufferSize = 0;
+            blocks[i]._Packing =
+               gl_uniform_block_packing(block_type->interface_packing);
+
+            parcel.process(block_type->fields.array,
+                           blocks[i].Name);
+
+            blocks[i].UniformBufferSize = parcel.buffer_size;
+
+            blocks[i].NumUniforms =
+               (unsigned)(ptrdiff_t)(&variables[parcel.index] - blocks[i].Uniforms);
+
+            i++;
+         }
+      } else {
+         blocks[i].Name = ralloc_strdup(blocks, block_type->name);
+         blocks[i].Uniforms = &variables[parcel.index];
+         blocks[i].Binding = 0;
+         blocks[i].UniformBufferSize = 0;
+         blocks[i]._Packing =
+            gl_uniform_block_packing(block_type->interface_packing);
+
+         parcel.process(block_type,
+                        b->has_instance_name ? block_type->name : "");
+
+         blocks[i].UniformBufferSize = parcel.buffer_size;
+
+         blocks[i].NumUniforms =
+            (unsigned)(ptrdiff_t)(&variables[parcel.index] - blocks[i].Uniforms);
+
+         i++;
+      }
+   }
+
+   assert(parcel.index == num_variables);
+
+   _mesa_hash_table_destroy(block_hash, NULL);
+
+   *blocks_ret = blocks;
+   return num_blocks;
+}
+
 bool
 link_uniform_blocks_are_compatible(const gl_uniform_block *a,
                                   const gl_uniform_block *b)
index 5818f7ead9993bdf2da846587d44cf7bb3d5a6bd..14eb9c1cdc51ddbf65149d0de61fb9bcf64149b8 100644 (file)
@@ -53,6 +53,13 @@ extern bool
 link_uniform_blocks_are_compatible(const gl_uniform_block *a,
                                   const gl_uniform_block *b);
 
+extern int
+link_uniform_blocks(void *mem_ctx,
+                    struct gl_shader_program *prog,
+                    struct gl_shader **shader_list,
+                    unsigned num_shaders,
+                    struct gl_uniform_block **blocks_ret);
+
 /**
  * Class for processing all of the leaf fields of an uniform
  *