[Mesa-dev] [PATCH 7/9] glsl : Linker support for UBO

Vincent Lejeune vljn at ovi.com
Thu Dec 1 07:35:36 PST 2011


   v2 :
   - Fix format issue thank to Brian Paul comments.
   - UBOs are now sent to program correctly.
---
 src/glsl/link_uniforms.cpp |    2 +-
 src/glsl/linker.cpp        |  310 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 309 insertions(+), 3 deletions(-)

diff --git a/src/glsl/link_uniforms.cpp b/src/glsl/link_uniforms.cpp
index c7de480..bacf2e9 100644
--- a/src/glsl/link_uniforms.cpp
+++ b/src/glsl/link_uniforms.cpp
@@ -308,7 +308,7 @@ link_assign_uniform_locations(struct gl_shader_program *prog)
       foreach_list(node, prog->_LinkedShaders[i]->ir) {
 	 ir_variable *const var = ((ir_instruction *) node)->as_variable();
 
-	 if ((var == NULL) || (var->mode != ir_var_uniform))
+       if ((var == NULL) || (var->mode != ir_var_uniform) || var->is_ubo_variable)
 	    continue;
 
 	 /* FINISHME: Update code to process built-in uniforms!
diff --git a/src/glsl/linker.cpp b/src/glsl/linker.cpp
index 3527088..786ebcf 100644
--- a/src/glsl/linker.cpp
+++ b/src/glsl/linker.cpp
@@ -64,7 +64,9 @@
  * \author Ian Romanick <ian.d.romanick at intel.com>
  */
 
+#include <stdint.h>
 #include "main/core.h"
+#include "main/hash.h"
 #include "glsl_symbol_table.h"
 #include "ir.h"
 #include "program.h"
@@ -847,6 +849,81 @@ get_main_function_signature(gl_shader *sh)
    return NULL;
 }
 
+/**
+ * This function duplicates content of a ubo source into shader ubo array.
+ */
+static struct gl_uniform_buffer_object*
+append_ubo (const struct gl_uniform_buffer_object& source, struct gl_shader* merged_shader)
+{
+   /* Use shorter alias */
+   unsigned& current_ubo_count = merged_shader->UBOCount;
+   struct gl_uniform_buffer_object*& ubo_array = merged_shader->UniformBufferObjects;
+
+   if (current_ubo_count == 0)
+      ubo_array = (gl_uniform_buffer_object*) ralloc_array_size(merged_shader,sizeof(gl_uniform_buffer_object),current_ubo_count + 1);
+   else
+      ubo_array = (gl_uniform_buffer_object*) reralloc_array_size(merged_shader,ubo_array,sizeof(gl_uniform_buffer_object),current_ubo_count + 1);
+   struct gl_uniform_buffer_object& dest = ubo_array[current_ubo_count];
+   dest = source; // copy everything, but need to duplicate ptr afterward
+   dest.Name = ralloc_strdup(merged_shader,source.Name);
+   dest.StorageLayout.Compact = (gl_shader_ubo_variable*) ralloc_array_size(merged_shader,sizeof(gl_shader_ubo_variable),source.NumberOfVariables);
+   for (unsigned i = 0; i < dest.NumberOfVariables; i++) {
+      dest.StorageLayout.Compact[i] = source.StorageLayout.Compact[i];
+      dest.StorageLayout.Compact[i].Name = ralloc_strdup(merged_shader,source.StorageLayout.Compact[i].Name);
+   }
+   current_ubo_count++;
+   return &dest;
+}
+
+/**
+ * TODO : write the function
+ * This function should check consistency between 2 UBO having same name
+ * from different shaders :
+ * - Same layout
+ * - Same variables (name and type) in same order
+ * - Same matrix layout (ie row/column major)
+ */
+static bool validate_separate_ubo(const gl_uniform_buffer_object& first, const gl_uniform_buffer_object& second)
+{
+   return true;
+}
+
+
+#ifdef FEATURE_ARB_uniform_buffer_object
+/**
+ * At intrastage, when several shaders of same type are merged in a single one,
+ * this function generates UBOs of the newly created shader from them and
+ * performs necessary check.
+ */
+static
+void merge_intrastage_ubo ( gl_shader_program* prog, struct gl_shader* merged_shader,
+                            struct gl_shader **shader_list, unsigned num_shaders)
+{
+   hash_table *ht = hash_table_ctor(0, hash_table_string_hash,
+                                    hash_table_string_compare);
+
+   for (unsigned shad_id=0; shad_id < num_shaders; shad_id++)
+   {
+      for(unsigned ubo_id=0; ubo_id < shader_list[shad_id]->UBOCount; ubo_id++)
+      {
+         struct gl_uniform_buffer_object* current_ubo = &(shader_list[shad_id]->UniformBufferObjects[ubo_id]);
+         struct gl_uniform_buffer_object* sh = (struct gl_uniform_buffer_object*) hash_table_find(ht,current_ubo->Name);
+         if(!sh)
+         {
+            append_ubo(*current_ubo,merged_shader);
+            hash_table_insert(ht,current_ubo,current_ubo->Name);
+         }
+         else
+         {
+            if(!validate_separate_ubo(*current_ubo,*sh))
+               linker_error(prog,"Uniform Buffer Object '%s definition mismatch",sh->Name);
+         }
+      }
+   }
+
+   hash_table_dtor(ht);
+}
+#endif
 
 /**
  * Combine a group of shaders for a single stage to generate a linked shader
@@ -1017,7 +1094,9 @@ link_intrastage_shaders(void *mem_ctx,
 
       v.run(linked->ir);
    }
-
+#if FEATURE_ARB_uniform_buffer_object
+   merge_intrastage_ubo(prog,linked,shader_list,num_shaders);
+#endif
    return linked;
 }
 
@@ -1048,7 +1127,8 @@ update_array_sizes(struct gl_shader_program *prog)
 
 	 if ((var == NULL) || (var->mode != ir_var_uniform &&
 			       var->mode != ir_var_in &&
-			       var->mode != ir_var_out) ||
+                         var->mode != ir_var_out &&
+                         !var->is_ubo_variable) ||
 	     !var->type->is_array())
 	    continue;
 
@@ -1093,6 +1173,227 @@ update_array_sizes(struct gl_shader_program *prog)
    }
 }
 
+#if FEATURE_ARB_uniform_buffer_object
+void assign_ubo_variables_index_recursive_walk(struct gl_shader_program* sh, struct gl_program_ubo_variable* ubo, unsigned& index)
+{
+   if (sh->IndexedVariableCount == 0)
+      sh->IndexedUBOVariables = (gl_program_ubo_variable**) malloc( (sh->IndexedVariableCount + 1) * sizeof(struct gl_program_ubo_variable*));
+   else
+      sh->IndexedUBOVariables = (gl_program_ubo_variable**) realloc(sh->IndexedUBOVariables, (sh->IndexedVariableCount + 1) * sizeof(struct gl_program_ubo_variable*));
+   sh->NamedAccessUBOVariables->put(index,ubo->Name);
+   sh->IndexedUBOVariables[sh->IndexedVariableCount] = ubo;
+   index++;
+   sh->IndexedVariableCount++;
+   if (ubo->Type == TYPE_ARRAY) {
+      unsigned num_element = ubo->Size / ubo->Stride;
+      for (unsigned i = 0; i < num_element; i++) {
+         assign_ubo_variables_index_recursive_walk(sh,&(ubo->ContainedVariables[i]),index);
+      }
+   }
+
+}
+#endif
+
+
+
+#if FEATURE_ARB_uniform_buffer_object
+static void expand_gl_ubo_recursive(struct gl_program_ubo_variable& ubovar,const struct glsl_type* type, struct gl_uniform_buffer_object* ubo)
+{
+   ubovar.IndexInUBO = -1;
+   ubovar.UBO = ubo;
+
+   if(type->is_array()) {
+      unsigned number_of_element = type->array_size();
+
+      ubovar.Type = TYPE_ARRAY;
+      ubovar.ContainedVariables = (gl_program_ubo_variable*) malloc(number_of_element * sizeof(gl_program_ubo_variable));
+      ubovar.ContainedVariablesCount = number_of_element;
+
+      for (unsigned i = 0; i < number_of_element; i++) {
+         struct gl_program_ubo_variable& inner_var_ubo = ubovar.ContainedVariables[i];
+         inner_var_ubo.Name = (char*) malloc((strlen(ubovar.Name)+8)*sizeof(char)); // 8 additionnal char, for safety
+         sprintf(inner_var_ubo.Name,"%s[%d]",ubovar.Name,i);
+
+         expand_gl_ubo_recursive(inner_var_ubo,type->fields.array,ubo);
+      }
+      ubovar.Stride = ubovar.ContainedVariables[0].Size;
+      ubovar.Size = number_of_element * ubovar.Stride;
+
+      return;
+   }
+
+   if(type->is_record()) {
+      // TODO
+      ubovar.Stride = -1;
+      return;
+   }
+
+   ubovar.ContainedVariablesCount = 0;
+
+   if(type->is_vector()) {
+      ubovar.Stride = -1;
+      ubovar.Size = 4 * type->vector_elements;
+      ubovar.Type = TYPE_VEC;
+      return;
+   }
+
+
+   if(type->is_scalar()) {
+      ubovar.Size = 4;
+      ubovar.Stride = -1;
+      ubovar.Type = TYPE_SCALAR;
+      return;
+   }
+
+}
+
+/**
+ * Generate structures for declared and implicit (eg, array element/struct field) variables in UBO.
+ */
+static void expand_gl_ubo(const struct gl_uniform_buffer_object* source,struct gl_uniform_buffer_object* dest)
+{
+   dest->Name = strdup(source->Name);
+   dest->MatrixLayout = source->MatrixLayout;
+   dest->Layout = source->Layout;
+   dest->NumberOfVariables = source->NumberOfVariables;
+   dest->StorageLayout.Full = (struct gl_program_ubo_variable*)
+         malloc(dest->NumberOfVariables * sizeof(gl_program_ubo_variable));
+   for (unsigned i = 0; i < dest->NumberOfVariables; i++) {
+      struct gl_shader_ubo_variable& sourcevar = source->StorageLayout.Compact[i];
+      struct gl_program_ubo_variable& destvar = dest->StorageLayout.Full[i];
+      destvar.Name = strdup(sourcevar.Name);
+      expand_gl_ubo_recursive(dest->StorageLayout.Full[i],sourcevar.Type,dest);
+      destvar.IndexInUBO = i;
+   }
+   dest->ReferencedByFS = dest->ReferencedByGS = dest->ReferencedByVS = 0;
+}
+
+/**
+ * This function sets offset of UBO elements according to
+ * standard packing rule from GL_ARB_Uniform_Buffer_Object spec.
+ * TODO : Have a recursive version to handle array and structures
+ */
+static void set_standard_uniform_block_layout(struct gl_uniform_buffer_object* prog_ubo)
+{
+   unsigned next_position = 0;
+
+   for(unsigned i=0;i<prog_ubo->NumberOfVariables;i++) {
+      struct gl_program_ubo_variable& var = prog_ubo->StorageLayout.Full[i];
+
+      if(var.Type == TYPE_ARRAY) {
+
+      }
+
+      if(var.Type == TYPE_SCALAR) {
+         var.Offset = next_position;
+      }
+      if(var.Type == TYPE_VEC) {
+         switch(var.Size / 4) {
+         case 4:
+         case 3:
+            var.Offset = (next_position + 15) / 16;
+            var.Offset *= 16;
+            break;
+         case 2:
+            var.Offset = (next_position + 7) /  8;
+            var.Offset *= 8;
+            break;
+         default:
+            break;
+         }
+      }
+      next_position = var.Offset + var.Size ;
+   }
+
+   prog_ubo->Size = (next_position + 1) + 15 / 16;
+
+   return;
+}
+
+/**
+ * At interstage this function extract UBOs from shaders to populate programs UBOs
+ * It also performs checks coherency between UBOs with same name.
+ */
+static void merge_interstage_ubo(gl_shader_program* prog)
+{
+   hash_table *ht = hash_table_ctor(0, hash_table_string_hash,
+                                    hash_table_string_compare);
+   unsigned& index = prog->UBOCount = 0;
+
+   for (unsigned k = 0; k < MESA_SHADER_TYPES; k++) {
+      gl_shader* shader = prog->_LinkedShaders[k];
+      if(shader==NULL)
+         continue;
+
+      for(unsigned ubo_idx = 0; ubo_idx < shader->UBOCount; ubo_idx++) {
+
+         struct gl_uniform_buffer_object* current_ubo = &(shader->UniformBufferObjects[ubo_idx]);
+         struct gl_uniform_buffer_object* ubo_model = (struct gl_uniform_buffer_object*) hash_table_find(ht,current_ubo->Name);
+         if(!ubo_model) {
+            if (index == 0)
+               prog->UniformBufferObject = (struct gl_uniform_buffer_object*) malloc((index + 1) * sizeof(struct gl_uniform_buffer_object));
+            else
+               prog->UniformBufferObject = (struct gl_uniform_buffer_object*) realloc(prog->UniformBufferObject, (index + 1) * sizeof(struct gl_uniform_buffer_object));
+            expand_gl_ubo(current_ubo,&(prog->UniformBufferObject[index]));
+            prog->UniformBufferObject[index].Index = index;
+
+
+
+
+            // Set variables info in current_ubo
+            // Todo : implementation dependant packing rules
+            set_standard_uniform_block_layout(&(prog->UniformBufferObject[index]));
+            hash_table_insert(ht,current_ubo,current_ubo->Name);
+            index++;
+         }
+         else {
+            if(!validate_separate_ubo(*current_ubo,*ubo_model))
+               linker_error(prog,"Uniform Buffer Object '%s definition mismatch",ubo_model->Name);
+         }
+         switch(shader->Type) {
+         case GL_FRAGMENT_SHADER:
+            prog->UniformBufferObject[index - 1].ReferencedByFS = 1;
+            break;
+         case GL_VERTEX_SHADER:
+            prog->UniformBufferObject[index - 1].ReferencedByVS = 1;
+            break;
+         case GL_GEOMETRY_SHADER_ARB:
+            prog->UniformBufferObject[index - 1].ReferencedByGS = 1;
+            break;
+         }
+      }
+   }
+
+   // UBO : mirror the setup to the variable and set program scope UBO variable
+   prog->NamedAccessUBOVariables = new string_to_uint_map();
+   unsigned total_uniforms = prog->NumUserUniformStorage;
+   unsigned idx = total_uniforms;
+
+   for(unsigned i = 0; i < prog->UBOCount; i++) {
+      const gl_uniform_buffer_object& current_ubo = prog->UniformBufferObject[i];
+      for (unsigned j = 0;j < current_ubo.NumberOfVariables; j++) {
+
+         for (unsigned k = 0; k < MESA_SHADER_TYPES; k++) {
+
+            gl_shader* shader = prog->_LinkedShaders[k];
+            if(shader==NULL)
+               continue;
+            ir_variable* var = shader->symbols->get_variable(current_ubo.StorageLayout.Full[j].Name);
+            if(var==NULL)
+               continue;
+            var->location = idx;
+
+         }
+         assign_ubo_variables_index_recursive_walk(prog, &(current_ubo.StorageLayout.Full[j]),idx);
+      }
+   }
+
+   hash_table_dtor(ht);
+}
+
+#endif
+
+
 /**
  * Find a contiguous set of available bits in a bitmask.
  *
@@ -2121,6 +2422,11 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 	 ;
    }
 
+
+#if FEATURE_ARB_uniform_buffer_object
+   merge_interstage_ubo(prog);
+#endif
+
    /* FINISHME: The value of the max_attribute_index parameter is
     * FINISHME: implementation dependent based on the value of
     * FINISHME: GL_MAX_VERTEX_ATTRIBS.  GL_MAX_VERTEX_ATTRIBS must be
-- 
1.7.7



More information about the mesa-dev mailing list