mesa: Move declarations before code.
[mesa.git] / src / glsl / linker.cpp
index 38a6560d1b508d2eae47a9b8d987b1ff8289ec8d..f6b26618552dd87c7054c457b00bac0f68b3cc78 100644 (file)
@@ -1206,6 +1206,7 @@ link_gs_inout_layout_qualifiers(struct gl_shader_program *prog,
                                unsigned num_shaders)
 {
    linked_shader->Geom.VerticesOut = 0;
+   linked_shader->Geom.Invocations = 0;
    linked_shader->Geom.InputType = PRIM_UNKNOWN;
    linked_shader->Geom.OutputType = PRIM_UNKNOWN;
 
@@ -1259,6 +1260,18 @@ link_gs_inout_layout_qualifiers(struct gl_shader_program *prog,
         }
         linked_shader->Geom.VerticesOut = shader->Geom.VerticesOut;
       }
+
+      if (shader->Geom.Invocations != 0) {
+        if (linked_shader->Geom.Invocations != 0 &&
+            linked_shader->Geom.Invocations != shader->Geom.Invocations) {
+           linker_error(prog, "geometry shader defined with conflicting "
+                        "invocation count (%d and %d)\n",
+                        linked_shader->Geom.Invocations,
+                        shader->Geom.Invocations);
+           return;
+        }
+        linked_shader->Geom.Invocations = shader->Geom.Invocations;
+      }
    }
 
    /* Just do the intrastage -> interstage propagation right now,
@@ -1285,8 +1298,76 @@ link_gs_inout_layout_qualifiers(struct gl_shader_program *prog,
       return;
    }
    prog->Geom.VerticesOut = linked_shader->Geom.VerticesOut;
+
+   if (linked_shader->Geom.Invocations == 0)
+      linked_shader->Geom.Invocations = 1;
+
+   prog->Geom.Invocations = linked_shader->Geom.Invocations;
 }
 
+
+/**
+ * Perform cross-validation of compute shader local_size_{x,y,z} layout
+ * qualifiers for the attached compute shaders, and propagate them to the
+ * linked CS and linked shader program.
+ */
+static void
+link_cs_input_layout_qualifiers(struct gl_shader_program *prog,
+                                struct gl_shader *linked_shader,
+                                struct gl_shader **shader_list,
+                                unsigned num_shaders)
+{
+   for (int i = 0; i < 3; i++)
+      linked_shader->Comp.LocalSize[i] = 0;
+
+   /* This function is called for all shader stages, but it only has an effect
+    * for compute shaders.
+    */
+   if (linked_shader->Stage != MESA_SHADER_COMPUTE)
+      return;
+
+   /* From the ARB_compute_shader spec, in the section describing local size
+    * declarations:
+    *
+    *     If multiple compute shaders attached to a single program object
+    *     declare local work-group size, the declarations must be identical;
+    *     otherwise a link-time error results. Furthermore, if a program
+    *     object contains any compute shaders, at least one must contain an
+    *     input layout qualifier specifying the local work sizes of the
+    *     program, or a link-time error will occur.
+    */
+   for (unsigned sh = 0; sh < num_shaders; sh++) {
+      struct gl_shader *shader = shader_list[sh];
+
+      if (shader->Comp.LocalSize[0] != 0) {
+         if (linked_shader->Comp.LocalSize[0] != 0) {
+            for (int i = 0; i < 3; i++) {
+               if (linked_shader->Comp.LocalSize[i] !=
+                   shader->Comp.LocalSize[i]) {
+                  linker_error(prog, "compute shader defined with conflicting "
+                               "local sizes\n");
+                  return;
+               }
+            }
+         }
+         for (int i = 0; i < 3; i++)
+            linked_shader->Comp.LocalSize[i] = shader->Comp.LocalSize[i];
+      }
+   }
+
+   /* Just do the intrastage -> interstage propagation right now,
+    * since we already know we're in the right type of shader program
+    * for doing it.
+    */
+   if (linked_shader->Comp.LocalSize[0] == 0) {
+      linker_error(prog, "compute shader didn't declare local size\n");
+      return;
+   }
+   for (int i = 0; i < 3; i++)
+      prog->Comp.LocalSize[i] = linked_shader->Comp.LocalSize[i];
+}
+
+
 /**
  * Combine a group of shaders for a single stage to generate a linked shader
  *
@@ -1391,6 +1472,7 @@ link_intrastage_shaders(void *mem_ctx,
    ralloc_steal(linked, linked->UniformBlocks);
 
    link_gs_inout_layout_qualifiers(prog, linked, shader_list, num_shaders);
+   link_cs_input_layout_qualifiers(prog, linked, shader_list, num_shaders);
 
    populate_symbol_table(linked);
 
@@ -1967,6 +2049,46 @@ check_resources(struct gl_context *ctx, struct gl_shader_program *prog)
    }
 }
 
+/**
+ * Validate shader image resources.
+ */
+static void
+check_image_resources(struct gl_context *ctx, struct gl_shader_program *prog)
+{
+   unsigned total_image_units = 0;
+   unsigned fragment_outputs = 0;
+
+   if (!ctx->Extensions.ARB_shader_image_load_store)
+      return;
+
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+      struct gl_shader *sh = prog->_LinkedShaders[i];
+
+      if (sh) {
+         if (sh->NumImages > ctx->Const.Program[i].MaxImageUniforms)
+            linker_error(prog, "Too many %s shader image uniforms",
+                         _mesa_shader_stage_to_string(i));
+
+         total_image_units += sh->NumImages;
+
+         if (i == MESA_SHADER_FRAGMENT) {
+            foreach_list(node, sh->ir) {
+               ir_variable *var = ((ir_instruction *)node)->as_variable();
+               if (var && var->data.mode == ir_var_shader_out)
+                  fragment_outputs += var->type->count_attribute_slots();
+            }
+         }
+      }
+   }
+
+   if (total_image_units > ctx->Const.MaxCombinedImageUniforms)
+      linker_error(prog, "Too many combined image uniforms");
+
+   if (total_image_units + fragment_outputs >
+       ctx->Const.MaxCombinedImageUnitsAndFragmentOutputs)
+      linker_error(prog, "Too many combined image uniforms and fragment outputs");
+}
+
 void
 link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 {
@@ -1996,19 +2118,14 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 
    /* Separate the shaders into groups based on their type.
     */
-   struct gl_shader **vert_shader_list;
-   unsigned num_vert_shaders = 0;
-   struct gl_shader **frag_shader_list;
-   unsigned num_frag_shaders = 0;
-   struct gl_shader **geom_shader_list;
-   unsigned num_geom_shaders = 0;
-
-   vert_shader_list = (struct gl_shader **)
-      calloc(prog->NumShaders, sizeof(struct gl_shader *));
-   frag_shader_list = (struct gl_shader **)
-      calloc(prog->NumShaders, sizeof(struct gl_shader *));
-   geom_shader_list = (struct gl_shader **)
-      calloc(prog->NumShaders, sizeof(struct gl_shader *));
+   struct gl_shader **shader_list[MESA_SHADER_STAGES];
+   unsigned num_shaders[MESA_SHADER_STAGES];
+
+   for (int i = 0; i < MESA_SHADER_STAGES; i++) {
+      shader_list[i] = (struct gl_shader **)
+         calloc(prog->NumShaders, sizeof(struct gl_shader *));
+      num_shaders[i] = 0;
+   }
 
    unsigned min_version = UINT_MAX;
    unsigned max_version = 0;
@@ -2024,20 +2141,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
         goto done;
       }
 
-      switch (prog->Shaders[i]->Stage) {
-      case MESA_SHADER_VERTEX:
-        vert_shader_list[num_vert_shaders] = prog->Shaders[i];
-        num_vert_shaders++;
-        break;
-      case MESA_SHADER_FRAGMENT:
-        frag_shader_list[num_frag_shaders] = prog->Shaders[i];
-        num_frag_shaders++;
-        break;
-      case MESA_SHADER_GEOMETRY:
-        geom_shader_list[num_geom_shaders] = prog->Shaders[i];
-        num_geom_shaders++;
-        break;
-      }
+      gl_shader_stage shader_type = prog->Shaders[i]->Stage;
+      shader_list[shader_type][num_shaders[shader_type]] = prog->Shaders[i];
+      num_shaders[shader_type]++;
    }
 
    /* In desktop GLSL, different shader versions may be linked together.  In
@@ -2054,12 +2160,20 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 
    /* Geometry shaders have to be linked with vertex shaders.
     */
-   if (num_geom_shaders > 0 && num_vert_shaders == 0) {
+   if (num_shaders[MESA_SHADER_GEOMETRY] > 0 &&
+       num_shaders[MESA_SHADER_VERTEX] == 0) {
       linker_error(prog, "Geometry shader must be linked with "
                   "vertex shader\n");
       goto done;
    }
 
+   /* Compute shaders have additional restrictions. */
+   if (num_shaders[MESA_SHADER_COMPUTE] > 0 &&
+       num_shaders[MESA_SHADER_COMPUTE] != prog->NumShaders) {
+      linker_error(prog, "Compute shaders may not be linked with any other "
+                   "type of shader\n");
+   }
+
    for (unsigned int i = 0; i < MESA_SHADER_STAGES; i++) {
       if (prog->_LinkedShaders[i] != NULL)
         ctx->Driver.DeleteShader(ctx, prog->_LinkedShaders[i]);
@@ -2069,55 +2183,39 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 
    /* Link all shaders for a particular stage and validate the result.
     */
-   if (num_vert_shaders > 0) {
-      gl_shader *const sh =
-        link_intrastage_shaders(mem_ctx, ctx, prog, vert_shader_list,
-                                num_vert_shaders);
-
-      if (!prog->LinkStatus)
-        goto done;
-
-      validate_vertex_shader_executable(prog, sh);
-      if (!prog->LinkStatus)
-        goto done;
-      prog->LastClipDistanceArraySize = prog->Vert.ClipDistanceArraySize;
-
-      _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_VERTEX],
-                            sh);
-   }
-
-   if (num_frag_shaders > 0) {
-      gl_shader *const sh =
-        link_intrastage_shaders(mem_ctx, ctx, prog, frag_shader_list,
-                                num_frag_shaders);
+   for (int stage = 0; stage < MESA_SHADER_STAGES; stage++) {
+      if (num_shaders[stage] > 0) {
+         gl_shader *const sh =
+            link_intrastage_shaders(mem_ctx, ctx, prog, shader_list[stage],
+                                    num_shaders[stage]);
 
-      if (!prog->LinkStatus)
-        goto done;
+         if (!prog->LinkStatus)
+            goto done;
 
-      validate_fragment_shader_executable(prog, sh);
-      if (!prog->LinkStatus)
-        goto done;
+         switch (stage) {
+         case MESA_SHADER_VERTEX:
+            validate_vertex_shader_executable(prog, sh);
+            break;
+         case MESA_SHADER_GEOMETRY:
+            validate_geometry_shader_executable(prog, sh);
+            break;
+         case MESA_SHADER_FRAGMENT:
+            validate_fragment_shader_executable(prog, sh);
+            break;
+         }
+         if (!prog->LinkStatus)
+            goto done;
 
-      _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_FRAGMENT],
-                            sh);
+         _mesa_reference_shader(ctx, &prog->_LinkedShaders[stage], sh);
+      }
    }
 
-   if (num_geom_shaders > 0) {
-      gl_shader *const sh =
-        link_intrastage_shaders(mem_ctx, ctx, prog, geom_shader_list,
-                                num_geom_shaders);
-
-      if (!prog->LinkStatus)
-        goto done;
-
-      validate_geometry_shader_executable(prog, sh);
-      if (!prog->LinkStatus)
-        goto done;
+   if (num_shaders[MESA_SHADER_GEOMETRY] > 0)
       prog->LastClipDistanceArraySize = prog->Geom.ClipDistanceArraySize;
-
-      _mesa_reference_shader(ctx, &prog->_LinkedShaders[MESA_SHADER_GEOMETRY],
-                            sh);
-   }
+   else if (num_shaders[MESA_SHADER_VERTEX] > 0)
+      prog->LastClipDistanceArraySize = prog->Vert.ClipDistanceArraySize;
+   else
+      prog->LastClipDistanceArraySize = 0; /* Not used */
 
    /* Here begins the inter-stage linking phase.  Some initial validation is
     * performed, then locations are assigned for uniforms, attributes, and
@@ -2129,7 +2227,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
 
    unsigned prev;
 
-   for (prev = 0; prev < MESA_SHADER_STAGES; prev++) {
+   for (prev = 0; prev <= MESA_SHADER_FRAGMENT; prev++) {
       if (prog->_LinkedShaders[prev] != NULL)
          break;
    }
@@ -2137,7 +2235,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
    /* Validate the inputs of each stage with the output of the preceding
     * stage.
     */
-   for (unsigned i = prev + 1; i < MESA_SHADER_STAGES; i++) {
+   for (unsigned i = prev + 1; i <= MESA_SHADER_FRAGMENT; i++) {
       if (prog->_LinkedShaders[i] == NULL)
          continue;
 
@@ -2232,7 +2330,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
    }
 
    unsigned first;
-   for (first = 0; first < MESA_SHADER_STAGES; first++) {
+   for (first = 0; first <= MESA_SHADER_FRAGMENT; first++) {
       if (prog->_LinkedShaders[first] != NULL)
         break;
    }
@@ -2264,7 +2362,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
     * eliminated if they are (transitively) not used in a later stage.
     */
    int last, next;
-   for (last = MESA_SHADER_STAGES-1; last >= 0; last--) {
+   for (last = MESA_SHADER_FRAGMENT; last >= 0; last--) {
       if (prog->_LinkedShaders[last] != NULL)
          break;
    }
@@ -2354,17 +2452,19 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
    store_fragdepth_layout(prog);
 
    check_resources(ctx, prog);
+   check_image_resources(ctx, prog);
    link_check_atomic_counter_resources(ctx, prog);
 
    if (!prog->LinkStatus)
       goto done;
 
    /* OpenGL ES requires that a vertex shader and a fragment shader both be
-    * present in a linked program.  By checking prog->IsES, we also
-    * catch the GL_ARB_ES2_compatibility case.
+    * present in a linked program. GL_ARB_ES2_compatibility doesn't say
+    * anything about shader linking when one of the shaders (vertex or
+    * fragment shader) is absent. So, the extension shouldn't change the
+    * behavior specified in GLSL specification.
     */
-   if (!prog->InternalSeparateShader &&
-       (ctx->API == API_OPENGLES2 || prog->IsES)) {
+   if (!prog->InternalSeparateShader && ctx->API == API_OPENGLES2) {
       if (prog->_LinkedShaders[MESA_SHADER_VERTEX] == NULL) {
         linker_error(prog, "program lacks a vertex shader\n");
       } else if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] == NULL) {
@@ -2375,11 +2475,8 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
    /* FINISHME: Assign fragment shader output locations. */
 
 done:
-   free(vert_shader_list);
-   free(frag_shader_list);
-   free(geom_shader_list);
-
    for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+      free(shader_list[i]);
       if (prog->_LinkedShaders[i] == NULL)
         continue;