glsl: Add linker support for explicit attribute locations
[mesa.git] / src / glsl / linker.cpp
index c2c662152e2191f65b959e168ecde4f856f17501..c612fe546637f02b460a0872a80e5ccbff298897 100644 (file)
@@ -191,7 +191,7 @@ invalidate_variable_locations(gl_shader *sh, enum ir_variable_mode mode,
 
       /* Only assign locations for generic attributes / varyings / etc.
        */
-      if (var->location >= generic_base)
+      if ((var->location >= generic_base) && !var->explicit_location)
          var->location = -1;
    }
 }
@@ -365,6 +365,19 @@ cross_validate_globals(struct gl_shader_program *prog,
               }
            }
 
+           if (var->explicit_location) {
+              if (existing->explicit_location
+                  && (var->location != existing->location)) {
+                    linker_error_printf(prog, "explicit locations for %s "
+                                        "`%s' have differing values\n",
+                                        mode_string(var), var->name);
+                    return false;
+              }
+
+              existing->location = var->location;
+              existing->explicit_location = true;
+           }
+
            /* FINISHME: Handle non-constant initializers.
             */
            if (var->constant_value != NULL) {
@@ -457,7 +470,7 @@ cross_validate_outputs_to_inputs(struct gl_shader_program *prog,
          */
         if (input->type != output->type) {
            linker_error_printf(prog,
-                               "%s shader output `%s' delcared as "
+                               "%s shader output `%s' declared as "
                                "type `%s', but %s shader input declared "
                                "as type `%s'\n",
                                producer_stage, output->name,
@@ -857,13 +870,15 @@ struct uniform_node {
 
  */
 static void
-update_uniform_array_sizes(struct gl_shader_program *prog)
+update_array_sizes(struct gl_shader_program *prog)
 {
    for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
       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->mode != ir_var_in &&
+                              var->mode != ir_var_out) ||
             !var->type->is_array())
            continue;
 
@@ -880,6 +895,7 @@ update_uniform_array_sizes(struct gl_shader_program *prog)
               }
            }
         }
+
         if (size + 1 != var->type->fields.array->length) {
            var->type = glsl_type::get_array_instance(var->type->fields.array,
                                                      size + 1);
@@ -979,8 +995,6 @@ assign_uniform_locations(struct gl_shader_program *prog)
                                    hash_table_string_compare);
    void *mem_ctx = talloc_new(NULL);
 
-   update_uniform_array_sizes(prog);
-
    for (unsigned i = 0; i < prog->_NumLinkedShaders; i++) {
       unsigned next_position = 0;
 
@@ -1185,6 +1199,24 @@ assign_attribute_locations(gl_shader_program *prog, unsigned max_attribute_index
       if ((var == NULL) || (var->mode != ir_var_in))
         continue;
 
+      if (var->explicit_location) {
+        const unsigned slots = count_attribute_slots(var->type);
+        const unsigned use_mask = (1 << slots) - 1;
+        const int attr = var->location - VERT_ATTRIB_GENERIC0;
+
+        if ((var->location >= (int)(max_attribute_index + VERT_ATTRIB_GENERIC0))
+            || (var->location < 0)) {
+           linker_error_printf(prog,
+                               "invalid explicit location %d specified for "
+                               "`%s'\n",
+                               (var->location < 0) ? var->location : attr,
+                               var->name);
+           return false;
+        } else if (var->location >= VERT_ATTRIB_GENERIC0) {
+           used_locations |= (use_mask << attr);
+        }
+      }
+
       /* The location was explicitly assigned, nothing to do here.
        */
       if (var->location != -1)
@@ -1475,6 +1507,8 @@ link_shaders(GLcontext *ctx, struct gl_shader_program *prog)
         ;
    }
 
+   update_array_sizes(prog);
+
    assign_uniform_locations(prog);
 
    if (prog->_NumLinkedShaders && prog->_LinkedShaders[0]->Type == GL_VERTEX_SHADER) {
@@ -1483,8 +1517,10 @@ link_shaders(GLcontext *ctx, struct gl_shader_program *prog)
        * FINISHME: GL_MAX_VERTEX_ATTRIBS.  GL_MAX_VERTEX_ATTRIBS must be
        * FINISHME: at least 16, so hardcode 16 for now.
        */
-      if (!assign_attribute_locations(prog, 16))
+      if (!assign_attribute_locations(prog, 16)) {
+        prog->LinkStatus = false;
         goto done;
+      }
 
       if (prog->_NumLinkedShaders == 1)
         demote_unread_shader_outputs(prog->_LinkedShaders[0]);