From 2ecad38adbeaa652daaab8f2a6d112921b7e74eb Mon Sep 17 00:00:00 2001
From: marha <marha@users.sourceforge.net>
Date: Thu, 4 Jul 2013 13:38:47 +0200
Subject: fontconfig mesa libxcb git update 4 jul 2013

libxcb           commit 8b6bb1a71977116d382f45eef803aedd3e313d37
libxcb/xcb-proto commit e6a246e50e62cbcba33d0e1d2371e69e6e089383
fontconfig       commit d420e1df983871ab18b0f07976596fdf0ce78847
mesa             commit bbd1e60198548a12be3405fc32dd39a87e8968ab
---
 mesalib/src/glsl/Makefile.sources              |   1 +
 mesalib/src/glsl/ir_optimization.h             |   4 +
 mesalib/src/glsl/link_varyings.cpp             |  67 ++--
 mesalib/src/glsl/link_varyings.h               |   9 +
 mesalib/src/glsl/linker.cpp                    | 135 ++++---
 mesalib/src/glsl/opt_dead_builtin_varyings.cpp | 477 +++++++++++++++++++++++++
 6 files changed, 601 insertions(+), 92 deletions(-)
 create mode 100644 mesalib/src/glsl/opt_dead_builtin_varyings.cpp

(limited to 'mesalib/src/glsl')

diff --git a/mesalib/src/glsl/Makefile.sources b/mesalib/src/glsl/Makefile.sources
index acd19d1ff..979c4165f 100644
--- a/mesalib/src/glsl/Makefile.sources
+++ b/mesalib/src/glsl/Makefile.sources
@@ -82,6 +82,7 @@ LIBGLSL_FILES = \
 	$(GLSL_SRCDIR)/opt_constant_variable.cpp \
 	$(GLSL_SRCDIR)/opt_copy_propagation.cpp \
 	$(GLSL_SRCDIR)/opt_copy_propagation_elements.cpp \
+	$(GLSL_SRCDIR)/opt_dead_builtin_varyings.cpp \
 	$(GLSL_SRCDIR)/opt_dead_code.cpp \
 	$(GLSL_SRCDIR)/opt_dead_code_local.cpp \
 	$(GLSL_SRCDIR)/opt_dead_functions.cpp \
diff --git a/mesalib/src/glsl/ir_optimization.h b/mesalib/src/glsl/ir_optimization.h
index d38d5e303..fad6f1bfe 100644
--- a/mesalib/src/glsl/ir_optimization.h
+++ b/mesalib/src/glsl/ir_optimization.h
@@ -76,6 +76,10 @@ bool do_constant_variable_unlinked(exec_list *instructions);
 bool do_copy_propagation(exec_list *instructions);
 bool do_copy_propagation_elements(exec_list *instructions);
 bool do_constant_propagation(exec_list *instructions);
+void do_dead_builtin_varyings(struct gl_context *ctx,
+                              exec_list *producer, exec_list *consumer,
+                              unsigned num_tfeedback_decls,
+                              class tfeedback_decl *tfeedback_decls);
 bool do_dead_code(exec_list *instructions, bool uniform_locations_assigned);
 bool do_dead_code_local(exec_list *instructions);
 bool do_dead_code_unlinked(exec_list *instructions);
diff --git a/mesalib/src/glsl/link_varyings.cpp b/mesalib/src/glsl/link_varyings.cpp
index 4fdbdc199..51cbdaa0e 100644
--- a/mesalib/src/glsl/link_varyings.cpp
+++ b/mesalib/src/glsl/link_varyings.cpp
@@ -1113,16 +1113,12 @@ assign_varying_locations(struct gl_context *ctx,
       }
    }
 
-   unsigned varying_vectors = 0;
-
    if (consumer) {
       foreach_list(node, consumer->ir) {
          ir_variable *const var = ((ir_instruction *) node)->as_variable();
 
-         if ((var == NULL) || (var->mode != ir_var_shader_in))
-            continue;
-
-         if (var->is_unmatched_generic_inout) {
+         if (var && var->mode == ir_var_shader_in &&
+             var->is_unmatched_generic_inout) {
             if (prog->Version <= 120) {
                /* On page 25 (page 31 of the PDF) of the GLSL 1.20 spec:
                 *
@@ -1148,45 +1144,46 @@ assign_varying_locations(struct gl_context *ctx,
              * value is written by the previous stage.
              */
             var->mode = ir_var_auto;
-         } else if (is_varying_var(consumer->Type, var)) {
-            /* The packing rules are used for vertex shader inputs are also
-             * used for fragment shader inputs.
-             */
-            varying_vectors += count_attribute_slots(var->type);
          }
       }
    }
 
+   return true;
+}
+
+bool
+check_against_varying_limit(struct gl_context *ctx,
+                            struct gl_shader_program *prog,
+                            gl_shader *consumer)
+{
+   unsigned varying_vectors = 0;
+
+   foreach_list(node, consumer->ir) {
+      ir_variable *const var = ((ir_instruction *) node)->as_variable();
+
+      if (var && var->mode == ir_var_shader_in &&
+          is_varying_var(consumer->Type, var)) {
+         /* The packing rules used for vertex shader inputs are also
+          * used for fragment shader inputs.
+          */
+         varying_vectors += count_attribute_slots(var->type);
+      }
+   }
+
    if (ctx->API == API_OPENGLES2 || prog->IsES) {
       if (varying_vectors > ctx->Const.MaxVarying) {
-         if (ctx->Const.GLSLSkipStrictMaxVaryingLimitCheck) {
-            linker_warning(prog, "shader uses too many varying vectors "
-                           "(%u > %u), but the driver will try to optimize "
-                           "them out; this is non-portable out-of-spec "
-                           "behavior\n",
-                           varying_vectors, ctx->Const.MaxVarying);
-         } else {
-            linker_error(prog, "shader uses too many varying vectors "
-                         "(%u > %u)\n",
-                         varying_vectors, ctx->Const.MaxVarying);
-            return false;
-         }
+         linker_error(prog, "shader uses too many varying vectors "
+                      "(%u > %u)\n",
+                      varying_vectors, ctx->Const.MaxVarying);
+         return false;
       }
    } else {
       const unsigned float_components = varying_vectors * 4;
       if (float_components > ctx->Const.MaxVarying * 4) {
-         if (ctx->Const.GLSLSkipStrictMaxVaryingLimitCheck) {
-            linker_warning(prog, "shader uses too many varying components "
-                           "(%u > %u), but the driver will try to optimize "
-                           "them out; this is non-portable out-of-spec "
-                           "behavior\n",
-                           float_components, ctx->Const.MaxVarying * 4);
-         } else {
-            linker_error(prog, "shader uses too many varying components "
-                         "(%u > %u)\n",
-                         float_components, ctx->Const.MaxVarying * 4);
-            return false;
-         }
+         linker_error(prog, "shader uses too many varying components "
+                      "(%u > %u)\n",
+                      float_components, ctx->Const.MaxVarying * 4);
+         return false;
       }
    }
 
diff --git a/mesalib/src/glsl/link_varyings.h b/mesalib/src/glsl/link_varyings.h
index ee1010a7b..7f7be353b 100644
--- a/mesalib/src/glsl/link_varyings.h
+++ b/mesalib/src/glsl/link_varyings.h
@@ -125,6 +125,10 @@ public:
          return this->vector_elements * this->matrix_columns * this->size;
    }
 
+   unsigned get_location() const {
+      return this->location;
+   }
+
 private:
    /**
     * The name that was supplied to glTransformFeedbackVaryings.  Used for
@@ -232,4 +236,9 @@ assign_varying_locations(struct gl_context *ctx,
                          unsigned num_tfeedback_decls,
                          tfeedback_decl *tfeedback_decls);
 
+bool
+check_against_varying_limit(struct gl_context *ctx,
+                            struct gl_shader_program *prog,
+                            gl_shader *consumer);
+
 #endif /* GLSL_LINK_VARYINGS_H */
diff --git a/mesalib/src/glsl/linker.cpp b/mesalib/src/glsl/linker.cpp
index c168e47e0..ba97ade25 100644
--- a/mesalib/src/glsl/linker.cpp
+++ b/mesalib/src/glsl/linker.cpp
@@ -1514,31 +1514,31 @@ static bool
 check_resources(struct gl_context *ctx, struct gl_shader_program *prog)
 {
    static const char *const shader_names[MESA_SHADER_TYPES] = {
-      "vertex", "fragment", "geometry"
+      "vertex", "geometry", "fragment"
    };
 
    const unsigned max_samplers[MESA_SHADER_TYPES] = {
       ctx->Const.VertexProgram.MaxTextureImageUnits,
-      ctx->Const.FragmentProgram.MaxTextureImageUnits,
-      ctx->Const.GeometryProgram.MaxTextureImageUnits
+      ctx->Const.GeometryProgram.MaxTextureImageUnits,
+      ctx->Const.FragmentProgram.MaxTextureImageUnits
    };
 
    const unsigned max_default_uniform_components[MESA_SHADER_TYPES] = {
       ctx->Const.VertexProgram.MaxUniformComponents,
-      ctx->Const.FragmentProgram.MaxUniformComponents,
-      ctx->Const.GeometryProgram.MaxUniformComponents
+      ctx->Const.GeometryProgram.MaxUniformComponents,
+      ctx->Const.FragmentProgram.MaxUniformComponents
    };
 
    const unsigned max_combined_uniform_components[MESA_SHADER_TYPES] = {
       ctx->Const.VertexProgram.MaxCombinedUniformComponents,
-      ctx->Const.FragmentProgram.MaxCombinedUniformComponents,
-      ctx->Const.GeometryProgram.MaxCombinedUniformComponents
+      ctx->Const.GeometryProgram.MaxCombinedUniformComponents,
+      ctx->Const.FragmentProgram.MaxCombinedUniformComponents
    };
 
    const unsigned max_uniform_blocks[MESA_SHADER_TYPES] = {
       ctx->Const.VertexProgram.MaxUniformBlocks,
-      ctx->Const.FragmentProgram.MaxUniformBlocks,
       ctx->Const.GeometryProgram.MaxUniformBlocks,
+      ctx->Const.FragmentProgram.MaxUniformBlocks
    };
 
    for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
@@ -1836,9 +1836,9 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
       goto done;
    }
 
-   unsigned prev;
-   for (prev = 0; prev < MESA_SHADER_TYPES; prev++) {
-      if (prog->_LinkedShaders[prev] != NULL)
+   unsigned first;
+   for (first = 0; first < MESA_SHADER_TYPES; first++) {
+      if (prog->_LinkedShaders[first] != NULL)
 	 break;
    }
 
@@ -1850,7 +1850,7 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
        *     non-zero, but the program object has no vertex or geometry
        *     shader;
        */
-      if (prev >= MESA_SHADER_FRAGMENT) {
+      if (first >= MESA_SHADER_FRAGMENT) {
          linker_error(prog, "Transform feedback varyings specified, but "
                       "no vertex or geometry shader is present.");
          goto done;
@@ -1864,69 +1864,90 @@ link_shaders(struct gl_context *ctx, struct gl_shader_program *prog)
          goto done;
    }
 
-   for (unsigned i = prev + 1; i < MESA_SHADER_TYPES; i++) {
-      if (prog->_LinkedShaders[i] == NULL)
-	 continue;
-
-      if (!assign_varying_locations(
-				    ctx, mem_ctx, prog, prog->_LinkedShaders[prev], prog->_LinkedShaders[i],
-             i == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
-             tfeedback_decls))
-	 goto done;
-
-      prev = i;
+   /* Linking the stages in the opposite order (from fragment to vertex)
+    * ensures that inter-shader outputs written to in an earlier stage are
+    * eliminated if they are (transitively) not used in a later stage.
+    */
+   int last, next;
+   for (last = MESA_SHADER_TYPES-1; last >= 0; last--) {
+      if (prog->_LinkedShaders[last] != NULL)
+         break;
    }
 
-   if (prev != MESA_SHADER_FRAGMENT && num_tfeedback_decls != 0) {
-      /* There was no fragment shader, but we still have to assign varying
-       * locations for use by transform feedback.
-       */
-      if (!assign_varying_locations(
-				    ctx, mem_ctx, prog, prog->_LinkedShaders[prev], NULL, num_tfeedback_decls,
-             tfeedback_decls))
-         goto done;
-   }
+   if (last >= 0 && last < MESA_SHADER_FRAGMENT) {
+      gl_shader *const sh = prog->_LinkedShaders[last];
 
-   if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls))
-      goto done;
+      if (num_tfeedback_decls != 0) {
+         /* There was no fragment shader, but we still have to assign varying
+          * locations for use by transform feedback.
+          */
+         if (!assign_varying_locations(ctx, mem_ctx, prog,
+                                       sh, NULL,
+                                       num_tfeedback_decls, tfeedback_decls))
+            goto done;
+      }
 
-   if (prog->_LinkedShaders[MESA_SHADER_VERTEX] != NULL) {
-      demote_shader_inputs_and_outputs(prog->_LinkedShaders[MESA_SHADER_VERTEX],
-				       ir_var_shader_out);
+      do_dead_builtin_varyings(ctx, sh->ir, NULL,
+                               num_tfeedback_decls, tfeedback_decls);
 
-      /* Eliminate code that is now dead due to unused vertex outputs being
-       * demoted.
+      demote_shader_inputs_and_outputs(sh, ir_var_shader_out);
+
+      /* Eliminate code that is now dead due to unused outputs being demoted.
        */
-      while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_VERTEX]->ir, false))
-	 ;
+      while (do_dead_code(sh->ir, false))
+         ;
    }
+   else if (first == MESA_SHADER_FRAGMENT) {
+      /* If the program only contains a fragment shader...
+       */
+      gl_shader *const sh = prog->_LinkedShaders[first];
 
-   if (prog->_LinkedShaders[MESA_SHADER_GEOMETRY] != NULL) {
-      gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_GEOMETRY];
+      do_dead_builtin_varyings(ctx, NULL, sh->ir,
+                               num_tfeedback_decls, tfeedback_decls);
 
       demote_shader_inputs_and_outputs(sh, ir_var_shader_in);
-      demote_shader_inputs_and_outputs(sh, ir_var_shader_out);
 
-      /* Eliminate code that is now dead due to unused geometry outputs being
-       * demoted.
-       */
-      while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_GEOMETRY]->ir, false))
-	 ;
+      while (do_dead_code(sh->ir, false))
+         ;
    }
 
-   if (prog->_LinkedShaders[MESA_SHADER_FRAGMENT] != NULL) {
-      gl_shader *const sh = prog->_LinkedShaders[MESA_SHADER_FRAGMENT];
+   next = last;
+   for (int i = next - 1; i >= 0; i--) {
+      if (prog->_LinkedShaders[i] == NULL)
+         continue;
 
-      demote_shader_inputs_and_outputs(sh, ir_var_shader_in);
+      gl_shader *const sh_i = prog->_LinkedShaders[i];
+      gl_shader *const sh_next = prog->_LinkedShaders[next];
+
+      if (!assign_varying_locations(ctx, mem_ctx, prog, sh_i, sh_next,
+                next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
+                tfeedback_decls))
+         goto done;
 
-      /* Eliminate code that is now dead due to unused fragment inputs being
-       * demoted.  This shouldn't actually do anything other than remove
-       * declarations of the (now unused) global variables.
+      do_dead_builtin_varyings(ctx, sh_i->ir, sh_next->ir,
+                next == MESA_SHADER_FRAGMENT ? num_tfeedback_decls : 0,
+                tfeedback_decls);
+
+      demote_shader_inputs_and_outputs(sh_i, ir_var_shader_out);
+      demote_shader_inputs_and_outputs(sh_next, ir_var_shader_in);
+
+      /* Eliminate code that is now dead due to unused outputs being demoted.
        */
-      while (do_dead_code(prog->_LinkedShaders[MESA_SHADER_FRAGMENT]->ir, false))
-	 ;
+      while (do_dead_code(sh_i->ir, false))
+         ;
+      while (do_dead_code(sh_next->ir, false))
+         ;
+
+      /* This must be done after all dead varyings are eliminated. */
+      if (!check_against_varying_limit(ctx, prog, sh_next))
+         goto done;
+
+      next = i;
    }
 
+   if (!store_tfeedback_info(ctx, prog, num_tfeedback_decls, tfeedback_decls))
+      goto done;
+
    update_array_sizes(prog);
    link_assign_uniform_locations(prog);
    store_fragdepth_layout(prog);
diff --git a/mesalib/src/glsl/opt_dead_builtin_varyings.cpp b/mesalib/src/glsl/opt_dead_builtin_varyings.cpp
new file mode 100644
index 000000000..2e813d24e
--- /dev/null
+++ b/mesalib/src/glsl/opt_dead_builtin_varyings.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright © 2013 Marek Olšák <maraeo@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * \file opt_dead_builtin_varyings.cpp
+ *
+ * This eliminates the built-in shader outputs which are either not written
+ * at all or not used by the next stage. It also eliminates unused elements
+ * of gl_TexCoord inputs, which reduces the overall varying usage.
+ * The varyings handled here are the primary and secondary color, the fog,
+ * and the texture coordinates (gl_TexCoord).
+ *
+ * This pass is necessary, because the Mesa GLSL linker cannot eliminate
+ * built-in varyings like it eliminates user-defined varyings, because
+ * the built-in varyings have pre-assigned locations. Also, the elimination
+ * of unused gl_TexCoord elements requires its own lowering pass anyway.
+ *
+ * It's implemented by replacing all occurences of dead varyings with
+ * temporary variables, which creates dead code. It is recommended to run
+ * a dead-code elimination pass after this.
+ *
+ * If any texture coordinate slots can be eliminated, the gl_TexCoord array is
+ * broken down into separate vec4 variables with locations equal to
+ * VARYING_SLOT_TEX0 + i.
+ */
+
+#include "main/imports.h" /* for snprintf */
+#include "ir.h"
+#include "ir_rvalue_visitor.h"
+#include "ir_optimization.h"
+#include "ir_print_visitor.h"
+#include "glsl_types.h"
+#include "link_varyings.h"
+
+
+/**
+ * This obtains detailed information about built-in varyings from shader code.
+ */
+class varying_info_visitor : public ir_hierarchical_visitor {
+public:
+   /* "mode" can be either ir_var_shader_in or ir_var_shader_out */
+   varying_info_visitor(ir_variable_mode mode)
+      : lower_texcoord_array(true),
+        texcoord_array(NULL),
+        texcoord_usage(0),
+        color_usage(0),
+        tfeedback_color_usage(0),
+        fog(NULL),
+        has_fog(false),
+        tfeedback_has_fog(false),
+        mode(mode)
+   {
+      memset(color, 0, sizeof(color));
+      memset(backcolor, 0, sizeof(backcolor));
+   }
+
+   virtual ir_visitor_status visit_enter(ir_dereference_array *ir)
+   {
+      ir_variable *var = ir->variable_referenced();
+
+      if (var && var->mode == this->mode &&
+          var->location == VARYING_SLOT_TEX0) {
+         this->texcoord_array = var;
+
+         ir_constant *index = ir->array_index->as_constant();
+         if (index == NULL) {
+            /* There is variable indexing, we can't lower the texcoord array.
+             */
+            this->texcoord_usage |= (1 << var->type->array_size()) - 1;
+            this->lower_texcoord_array = false;
+         }
+         else {
+            this->texcoord_usage |= 1 << index->get_uint_component(0);
+         }
+
+         /* Don't visit the leaves of ir_dereference_array. */
+         return visit_continue_with_parent;
+      }
+
+      return visit_continue;
+   }
+
+   virtual ir_visitor_status visit(ir_dereference_variable *ir)
+   {
+      ir_variable *var = ir->variable_referenced();
+
+      if (var->mode == this->mode && var->type->is_array() &&
+          var->location == VARYING_SLOT_TEX0) {
+         /* This is a whole array dereference like "gl_TexCoord = x;",
+          * there's probably no point in lowering that.
+          */
+         this->texcoord_usage |= (1 << var->type->array_size()) - 1;
+         this->lower_texcoord_array = false;
+      }
+      return visit_continue;
+   }
+
+   virtual ir_visitor_status visit(ir_variable *var)
+   {
+      if (var->mode != this->mode)
+         return visit_continue;
+
+      /* Handle colors and fog. */
+      switch (var->location) {
+      case VARYING_SLOT_COL0:
+         this->color[0] = var;
+         this->color_usage |= 1;
+         break;
+      case VARYING_SLOT_COL1:
+         this->color[1] = var;
+         this->color_usage |= 2;
+         break;
+      case VARYING_SLOT_BFC0:
+         this->backcolor[0] = var;
+         this->color_usage |= 1;
+         break;
+      case VARYING_SLOT_BFC1:
+         this->backcolor[1] = var;
+         this->color_usage |= 2;
+         break;
+      case VARYING_SLOT_FOGC:
+         this->fog = var;
+         this->has_fog = true;
+         break;
+      }
+
+      return visit_continue;
+   }
+
+   void get(exec_list *ir,
+            unsigned num_tfeedback_decls,
+            tfeedback_decl *tfeedback_decls)
+   {
+      /* Handle the transform feedback varyings. */
+      for (unsigned i = 0; i < num_tfeedback_decls; i++) {
+         if (!tfeedback_decls[i].is_varying())
+            continue;
+
+         unsigned location = tfeedback_decls[i].get_location();
+
+         switch (location) {
+         case VARYING_SLOT_COL0:
+         case VARYING_SLOT_BFC0:
+            this->tfeedback_color_usage |= 1;
+            break;
+         case VARYING_SLOT_COL1:
+         case VARYING_SLOT_BFC1:
+            this->tfeedback_color_usage |= 2;
+            break;
+         case VARYING_SLOT_FOGC:
+            this->tfeedback_has_fog = true;
+            break;
+         default:
+            if (location >= VARYING_SLOT_TEX0 &&
+                location <= VARYING_SLOT_TEX7) {
+               this->lower_texcoord_array = false;
+            }
+         }
+      }
+
+      /* Process the shader. */
+      visit_list_elements(this, ir);
+
+      if (!this->texcoord_array) {
+         this->lower_texcoord_array = false;
+      }
+   }
+
+   bool lower_texcoord_array;
+   ir_variable *texcoord_array;
+   unsigned texcoord_usage; /* bitmask */
+
+   ir_variable *color[2];
+   ir_variable *backcolor[2];
+   unsigned color_usage; /* bitmask */
+   unsigned tfeedback_color_usage; /* bitmask */
+
+   ir_variable *fog;
+   bool has_fog;
+   bool tfeedback_has_fog;
+
+   ir_variable_mode mode;
+};
+
+
+/**
+ * This replaces unused varyings with temporary variables.
+ *
+ * If "ir" is the producer, the "external" usage should come from
+ * the consumer. It also works the other way around. If either one is
+ * missing, set the "external" usage to a full mask.
+ */
+class replace_varyings_visitor : public ir_rvalue_visitor {
+public:
+   replace_varyings_visitor(exec_list *ir,
+                            const varying_info_visitor *info,
+                            unsigned external_texcoord_usage,
+                            unsigned external_color_usage,
+                            bool external_has_fog)
+      : info(info), new_fog(NULL)
+   {
+      void *const ctx = ir;
+
+      memset(this->new_texcoord, 0, sizeof(this->new_texcoord));
+      memset(this->new_color, 0, sizeof(this->new_color));
+      memset(this->new_backcolor, 0, sizeof(this->new_backcolor));
+
+      const char *mode_str =
+         info->mode == ir_var_shader_in ? "in" : "out";
+
+      /* Handle texcoord outputs.
+       *
+       * We're going to break down the gl_TexCoord array into separate
+       * variables. First, add declarations of the new variables all
+       * occurences of gl_TexCoord will be replaced with.
+       */
+      if (info->lower_texcoord_array) {
+         for (int i = MAX_TEXTURE_COORD_UNITS-1; i >= 0; i--) {
+            if (info->texcoord_usage & (1 << i)) {
+               char name[32];
+
+               if (!(external_texcoord_usage & (1 << i))) {
+                  /* This varying is unused in the next stage. Declare
+                   * a temporary instead of an output. */
+                  snprintf(name, 32, "gl_%s_TexCoord%i_dummy", mode_str, i);
+                  this->new_texcoord[i] =
+                     new (ctx) ir_variable(glsl_type::vec4_type, name,
+                                           ir_var_temporary);
+               }
+               else {
+                  snprintf(name, 32, "gl_%s_TexCoord%i", mode_str, i);
+                  this->new_texcoord[i] =
+                     new(ctx) ir_variable(glsl_type::vec4_type, name,
+                                          info->mode);
+                  this->new_texcoord[i]->location = VARYING_SLOT_TEX0 + i;
+                  this->new_texcoord[i]->explicit_location = true;
+                  this->new_texcoord[i]->explicit_index = 0;
+               }
+
+               ir->head->insert_before(new_texcoord[i]);
+            }
+         }
+      }
+
+      /* Create dummy variables which will replace set-but-unused color and
+       * fog outputs.
+       */
+      external_color_usage |= info->tfeedback_color_usage;
+
+      for (int i = 0; i < 2; i++) {
+         char name[32];
+
+         if (!(external_color_usage & (1 << i))) {
+            if (info->color[i]) {
+               snprintf(name, 32, "gl_%s_FrontColor%i_dummy", mode_str, i);
+               this->new_color[i] =
+                  new (ctx) ir_variable(glsl_type::vec4_type, name,
+                                        ir_var_temporary);
+            }
+
+            if (info->backcolor[i]) {
+               snprintf(name, 32, "gl_%s_BackColor%i_dummy", mode_str, i);
+               this->new_backcolor[i] =
+                  new (ctx) ir_variable(glsl_type::vec4_type, name,
+                                        ir_var_temporary);
+            }
+         }
+      }
+
+      if (!external_has_fog && !info->tfeedback_has_fog &&
+          info->fog) {
+         char name[32];
+
+         snprintf(name, 32, "gl_%s_FogFragCoord_dummy", mode_str);
+         this->new_fog = new (ctx) ir_variable(glsl_type::float_type, name,
+                                               ir_var_temporary);
+      }
+
+      /* Now do the replacing. */
+      visit_list_elements(this, ir);
+   }
+
+   virtual ir_visitor_status visit(ir_variable *var)
+   {
+      /* Remove the gl_TexCoord array. */
+      if (this->info->lower_texcoord_array &&
+          var == this->info->texcoord_array) {
+         var->remove();
+      }
+
+      /* Replace set-but-unused color and fog outputs with dummy variables. */
+      for (int i = 0; i < 2; i++) {
+         if (var == this->info->color[i] && this->new_color[i]) {
+            var->replace_with(this->new_color[i]);
+         }
+         if (var == this->info->backcolor[i] &&
+             this->new_backcolor[i]) {
+            var->replace_with(this->new_backcolor[i]);
+         }
+      }
+
+      if (var == this->info->fog && this->new_fog) {
+         var->replace_with(this->new_fog);
+      }
+
+      return visit_continue;
+   }
+
+   virtual void handle_rvalue(ir_rvalue **rvalue)
+   {
+      if (!*rvalue)
+         return;
+
+      void *ctx = ralloc_parent(*rvalue);
+
+      /* Replace an array dereference gl_TexCoord[i] with a single
+       * variable dereference representing gl_TexCoord[i].
+       */
+      if (this->info->lower_texcoord_array) {
+         /* gl_TexCoord[i] occurence */
+         ir_dereference_array *const da = (*rvalue)->as_dereference_array();
+
+         if (da && da->variable_referenced() ==
+             this->info->texcoord_array) {
+            unsigned i = da->array_index->as_constant()->get_uint_component(0);
+
+            *rvalue = new(ctx) ir_dereference_variable(this->new_texcoord[i]);
+            return;
+         }
+      }
+
+      /* Replace set-but-unused color and fog outputs with dummy variables. */
+      ir_dereference_variable *const dv = (*rvalue)->as_dereference_variable();
+      if (!dv)
+         return;
+
+      ir_variable *var = dv->variable_referenced();
+
+      for (int i = 0; i < 2; i++) {
+         if (var == this->info->color[i] && this->new_color[i]) {
+            *rvalue = new(ctx) ir_dereference_variable(this->new_color[i]);
+            return;
+         }
+         if (var == this->info->backcolor[i] &&
+             this->new_backcolor[i]) {
+            *rvalue = new(ctx) ir_dereference_variable(this->new_backcolor[i]);
+            return;
+         }
+      }
+
+      if (var == this->info->fog && this->new_fog) {
+         *rvalue = new(ctx) ir_dereference_variable(this->new_fog);
+      }
+   }
+
+   virtual ir_visitor_status visit_leave(ir_assignment *ir)
+   {
+      handle_rvalue(&ir->rhs);
+      handle_rvalue(&ir->condition);
+
+      /* We have to use set_lhs when changing the LHS of an assignment. */
+      ir_rvalue *lhs = ir->lhs;
+
+      handle_rvalue(&lhs);
+      if (lhs != ir->lhs) {
+         ir->set_lhs(lhs);
+      }
+
+      return visit_continue;
+   }
+
+private:
+   const varying_info_visitor *info;
+   struct ir_variable *new_texcoord[MAX_TEXTURE_COORD_UNITS];
+   struct ir_variable *new_color[2];
+   struct ir_variable *new_backcolor[2];
+   struct ir_variable *new_fog;
+};
+
+
+static void
+lower_texcoord_array(exec_list *ir, const varying_info_visitor *info)
+{
+   replace_varyings_visitor(ir, info,
+                            (1 << MAX_TEXTURE_COORD_UNITS) - 1,
+                            1 | 2, true);
+}
+
+
+void
+do_dead_builtin_varyings(struct gl_context *ctx,
+                         exec_list *producer, exec_list *consumer,
+                         unsigned num_tfeedback_decls,
+                         tfeedback_decl *tfeedback_decls)
+{
+   /* This optimization has no effect with the core context and GLES2, because
+    * the built-in varyings we're eliminating here are not available there.
+    *
+    * EXT_separate_shader_objects doesn't allow this optimization,
+    * because a program object can be bound partially (e.g. only one
+    * stage of a program object can be bound).
+    */
+   if (ctx->API == API_OPENGL_CORE ||
+       ctx->API == API_OPENGLES2 ||
+       ctx->Extensions.EXT_separate_shader_objects) {
+      return;
+   }
+
+   /* Information about built-in varyings. */
+   varying_info_visitor producer_info(ir_var_shader_out);
+   varying_info_visitor consumer_info(ir_var_shader_in);
+
+   if (producer) {
+      producer_info.get(producer, num_tfeedback_decls, tfeedback_decls);
+
+      if (!consumer) {
+         /* At least eliminate unused gl_TexCoord elements. */
+         if (producer_info.lower_texcoord_array) {
+            lower_texcoord_array(producer, &producer_info);
+         }
+         return;
+      }
+   }
+
+   if (consumer) {
+      consumer_info.get(consumer, 0, NULL);
+
+      if (!producer) {
+         /* At least eliminate unused gl_TexCoord elements. */
+         if (consumer_info.lower_texcoord_array) {
+            lower_texcoord_array(consumer, &consumer_info);
+         }
+         return;
+      }
+   }
+
+   /* Eliminate the varyings unused by the other shader. */
+   if (producer_info.lower_texcoord_array ||
+       producer_info.color_usage ||
+       producer_info.has_fog) {
+      replace_varyings_visitor(producer,
+                               &producer_info,
+                               consumer_info.texcoord_usage,
+                               consumer_info.color_usage,
+                               consumer_info.has_fog);
+   }
+
+   if (consumer_info.lower_texcoord_array ||
+       consumer_info.color_usage ||
+       consumer_info.has_fog) {
+      replace_varyings_visitor(consumer,
+                               &consumer_info,
+                               producer_info.texcoord_usage,
+                               producer_info.color_usage,
+                               producer_info.has_fog);
+   }
+}
-- 
cgit v1.2.3