glsl: don't lower builtins to mediump that don't allow it
[mesa.git] / src / compiler / glsl / gl_nir_linker.c
index 65d3e162a3c52db7382bde4ca42f0350bdbdf7ca..4a7a31629b825ea5e5fc402241dc68704533c82b 100644 (file)
 #include "main/shaderobj.h"
 #include "ir_uniform.h" /* for gl_uniform_storage */
 
-/* This file included general link methods, using NIR, instead of IR as
+/**
+ * This file included general link methods, using NIR, instead of IR as
  * the counter-part glsl/linker.cpp
- *
- * Also note that this is tailored for ARB_gl_spirv needs and particularities
  */
 
+static bool
+can_remove_uniform(nir_variable *var)
+{
+   /* Section 2.11.6 (Uniform Variables) of the OpenGL ES 3.0.3 spec
+    * says:
+    *
+    *     "All members of a named uniform block declared with a shared or
+    *     std140 layout qualifier are considered active, even if they are not
+    *     referenced in any shader in the program. The uniform block itself is
+    *     also considered active, even if no member of the block is
+    *     referenced."
+    *
+    * Although the spec doesn't state it std430 layouts are expect to behave
+    * the same way. If the variable is in a uniform block with one of those
+    * layouts, do not eliminate it.
+    */
+   if (nir_variable_is_in_block(var) &&
+       (glsl_get_ifc_packing(var->interface_type) !=
+        GLSL_INTERFACE_PACKING_PACKED))
+      return false;
+
+   if (glsl_get_base_type(glsl_without_array(var->type)) ==
+       GLSL_TYPE_SUBROUTINE)
+      return false;
+
+   /* Uniform initializers could get used by another stage */
+   if (var->constant_initializer)
+      return false;
+
+   return true;
+}
+
 /**
  * Built-in / reserved GL variables names start with "gl_"
  */
@@ -256,8 +287,8 @@ add_shader_variable(const struct gl_context *ctx,
          }
          return true;
       }
-      /* fallthrough */
    }
+   /* fallthrough */
 
    default: {
       /* The ARB_program_interface_query spec says:
@@ -570,6 +601,14 @@ bool
 gl_nir_link_spirv(struct gl_context *ctx, struct gl_shader_program *prog,
                   const struct gl_nir_linker_options *options)
 {
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+      struct gl_linked_shader *shader = prog->_LinkedShaders[i];
+      if (shader) {
+         nir_remove_dead_variables(shader->Program->nir, nir_var_uniform,
+                                   &can_remove_uniform);
+      }
+   }
+
    if (!gl_nir_link_uniform_blocks(ctx, prog))
       return false;
 
@@ -582,9 +621,62 @@ gl_nir_link_spirv(struct gl_context *ctx, struct gl_shader_program *prog,
    return true;
 }
 
+/**
+ * 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;
+   unsigned total_shader_storage_blocks = 0;
+
+   if (!ctx->Extensions.ARB_shader_image_load_store)
+      return;
+
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+      struct gl_linked_shader *sh = prog->_LinkedShaders[i];
+      if (!sh)
+         continue;
+
+      total_image_units += sh->Program->info.num_images;
+      total_shader_storage_blocks += sh->Program->info.num_ssbos;
+   }
+
+   if (total_image_units > ctx->Const.MaxCombinedImageUniforms)
+      linker_error(prog, "Too many combined image uniforms\n");
+
+   struct gl_linked_shader *frag_sh =
+      prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
+   if (frag_sh) {
+      uint64_t frag_outputs_written = frag_sh->Program->info.outputs_written;
+      fragment_outputs = util_bitcount64(frag_outputs_written);
+   }
+
+   if (total_image_units + fragment_outputs + total_shader_storage_blocks >
+       ctx->Const.MaxCombinedShaderOutputResources)
+      linker_error(prog, "Too many combined image uniforms, shader storage "
+                         " buffers and fragment outputs\n");
+}
+
 bool
 gl_nir_link_glsl(struct gl_context *ctx, struct gl_shader_program *prog)
 {
+   for (unsigned i = 0; i < MESA_SHADER_STAGES; i++) {
+      struct gl_linked_shader *shader = prog->_LinkedShaders[i];
+      if (shader) {
+         nir_remove_dead_variables(shader->Program->nir, nir_var_uniform,
+                                   &can_remove_uniform);
+      }
+   }
+
+   if (!gl_nir_link_uniforms(ctx, prog, true))
+      return false;
+
+   link_util_calculate_subroutine_compat(prog);
+   link_util_check_uniform_resources(ctx, prog);
+   link_util_check_subroutine_resources(prog);
+   check_image_resources(ctx, prog);
    gl_nir_link_assign_atomic_counter_resources(ctx, prog);
    gl_nir_link_check_atomic_counter_resources(ctx, prog);