nir: save IO semantics in lowered IO intrinsics
[mesa.git] / src / compiler / nir / nir_lower_samplers.c
index 7dfa96cc98ac0c7c92c6db38d036550b4b80bf27..3eb69bcd5de0ee27310d6bdb87e01ddb3516386b 100644 (file)
  * DEALINGS IN THE SOFTWARE.
  */
 
-#include "nir.h"
+#include "nir/nir.h"
 #include "nir_builder.h"
-#include "program/hash_table.h"
-#include "compiler/glsl/ir_uniform.h"
 
-#include "main/compiler.h"
-#include "main/mtypes.h"
-#include "program/prog_parameter.h"
-#include "program/program.h"
-
-/* Calculate the sampler index based on array indicies and also
- * calculate the base uniform location for struct members.
- */
 static void
-calc_sampler_offsets(nir_deref *tail, nir_tex_instr *instr,
-                     unsigned *array_elements, nir_ssa_def **indirect,
-                     nir_builder *b, unsigned *location)
+lower_tex_src_to_offset(nir_builder *b,
+                        nir_tex_instr *instr, unsigned src_idx)
 {
-   if (tail->child == NULL)
-      return;
+   nir_ssa_def *index = NULL;
+   unsigned base_index = 0;
+   unsigned array_elements = 1;
+   nir_tex_src *src = &instr->src[src_idx];
+   bool is_sampler = src->src_type == nir_tex_src_sampler_deref;
+
+   /* We compute first the offsets */
+   nir_deref_instr *deref = nir_instr_as_deref(src->src.ssa->parent_instr);
+   while (deref->deref_type != nir_deref_type_var) {
+      assert(deref->parent.is_ssa);
+      nir_deref_instr *parent =
+         nir_instr_as_deref(deref->parent.ssa->parent_instr);
+
+      assert(deref->deref_type == nir_deref_type_array);
+
+      if (nir_src_is_const(deref->arr.index) && index == NULL) {
+         /* We're still building a direct index */
+         base_index += nir_src_as_uint(deref->arr.index) * array_elements;
+      } else {
+         if (index == NULL) {
+            /* We used to be direct but not anymore */
+            index = nir_imm_int(b, base_index);
+            base_index = 0;
+         }
 
-   switch (tail->child->deref_type) {
-   case nir_deref_type_array: {
-      nir_deref_array *deref_array = nir_deref_as_array(tail->child);
+         index = nir_iadd(b, index,
+                          nir_imul(b, nir_imm_int(b, array_elements),
+                                   nir_ssa_for_src(b, deref->arr.index, 1)));
+      }
 
-      assert(deref_array->deref_array_type != nir_deref_array_type_wildcard);
+      array_elements *= glsl_get_length(parent->type);
 
-      calc_sampler_offsets(tail->child, instr, array_elements,
-                           indirect, b, location);
-      instr->texture_index += deref_array->base_offset * *array_elements;
+      deref = parent;
+   }
 
-      if (deref_array->deref_array_type == nir_deref_array_type_indirect) {
-         nir_ssa_def *mul =
-            nir_imul(b, nir_imm_int(b, *array_elements),
-                     nir_ssa_for_src(b, deref_array->indirect, 1));
+   if (index)
+      index = nir_umin(b, index, nir_imm_int(b, array_elements - 1));
 
-         nir_instr_rewrite_src(&instr->instr, &deref_array->indirect,
-                               NIR_SRC_INIT);
+   /* We hit the deref_var.  This is the end of the line */
+   assert(deref->deref_type == nir_deref_type_var);
 
-         if (*indirect) {
-            *indirect = nir_iadd(b, *indirect, mul);
-         } else {
-            *indirect = mul;
-         }
-      }
+   base_index += deref->var->data.binding;
 
-      *array_elements *= glsl_get_length(tail->type);
-       break;
-   }
+   /* We have the offsets, we apply them, rewriting the source or removing
+    * instr if needed
+    */
+   if (index) {
+      nir_instr_rewrite_src(&instr->instr, &src->src,
+                            nir_src_for_ssa(index));
 
-   case nir_deref_type_struct: {
-      nir_deref_struct *deref_struct = nir_deref_as_struct(tail->child);
-      *location += glsl_get_record_location_offset(tail->type, deref_struct->index);
-      calc_sampler_offsets(tail->child, instr, array_elements,
-                           indirect, b, location);
-      break;
+      src->src_type = is_sampler ?
+         nir_tex_src_sampler_offset :
+         nir_tex_src_texture_offset;
+   } else {
+      nir_tex_instr_remove_src(instr, src_idx);
    }
 
-   default:
-      unreachable("Invalid deref type");
-      break;
+   if (is_sampler) {
+      instr->sampler_index = base_index;
+   } else {
+      instr->texture_index = base_index;
    }
 }
 
-static void
-lower_sampler(nir_tex_instr *instr, const struct gl_shader_program *shader_program,
-              gl_shader_stage stage, nir_builder *builder)
+static bool
+lower_sampler(nir_builder *b, nir_tex_instr *instr)
 {
-   if (instr->texture == NULL)
-      return;
-
-   /* In GLSL, we only fill out the texture field.  The sampler is inferred */
-   assert(instr->sampler == NULL);
+   int texture_idx =
+      nir_tex_instr_src_index(instr, nir_tex_src_texture_deref);
 
-   instr->texture_index = 0;
-   unsigned location = instr->texture->var->data.location;
-   unsigned array_elements = 1;
-   nir_ssa_def *indirect = NULL;
-
-   builder->cursor = nir_before_instr(&instr->instr);
-   calc_sampler_offsets(&instr->texture->deref, instr, &array_elements,
-                        &indirect, builder, &location);
+   if (texture_idx >= 0) {
+      b->cursor = nir_before_instr(&instr->instr);
 
-   if (indirect) {
-      /* First, we have to resize the array of texture sources */
-      nir_tex_src *new_srcs = rzalloc_array(instr, nir_tex_src,
-                                            instr->num_srcs + 2);
-
-      for (unsigned i = 0; i < instr->num_srcs; i++) {
-         new_srcs[i].src_type = instr->src[i].src_type;
-         nir_instr_move_src(&instr->instr, &new_srcs[i].src,
-                            &instr->src[i].src);
-      }
-
-      ralloc_free(instr->src);
-      instr->src = new_srcs;
-
-      /* Now we can go ahead and move the source over to being a
-       * first-class texture source.
-       */
-      instr->src[instr->num_srcs].src_type = nir_tex_src_texture_offset;
-      instr->num_srcs++;
-      nir_instr_rewrite_src(&instr->instr,
-                            &instr->src[instr->num_srcs - 1].src,
-                            nir_src_for_ssa(indirect));
-
-      instr->src[instr->num_srcs].src_type = nir_tex_src_sampler_offset;
-      instr->num_srcs++;
-      nir_instr_rewrite_src(&instr->instr,
-                            &instr->src[instr->num_srcs - 1].src,
-                            nir_src_for_ssa(indirect));
-
-      instr->texture_array_size = array_elements;
+      lower_tex_src_to_offset(b, instr, texture_idx);
    }
 
-   if (location > shader_program->NumUniformStorage - 1 ||
-       !shader_program->UniformStorage[location].opaque[stage].active) {
-      assert(!"cannot return a sampler");
-      return;
-   }
+   int sampler_idx =
+      nir_tex_instr_src_index(instr, nir_tex_src_sampler_deref);
 
-   instr->texture_index +=
-      shader_program->UniformStorage[location].opaque[stage].index;
+   if (sampler_idx >= 0) {
+      lower_tex_src_to_offset(b, instr, sampler_idx);
+   }
 
-   instr->sampler_index = instr->texture_index;
+   if (texture_idx < 0 && sampler_idx < 0)
+      return false;
 
-   instr->texture = NULL;
+   return true;
 }
 
-typedef struct {
-   nir_builder builder;
-   const struct gl_shader_program *shader_program;
-   gl_shader_stage stage;
-} lower_state;
-
 static bool
-lower_block_cb(nir_block *block, void *_state)
+lower_impl(nir_function_impl *impl)
 {
-   lower_state *state = (lower_state *) _state;
-
-   nir_foreach_instr(block, instr) {
-      if (instr->type == nir_instr_type_tex) {
-         nir_tex_instr *tex_instr = nir_instr_as_tex(instr);
-         lower_sampler(tex_instr, state->shader_program, state->stage,
-                       &state->builder);
+   nir_builder b;
+   nir_builder_init(&b, impl);
+   bool progress = false;
+
+   nir_foreach_block(block, impl) {
+      nir_foreach_instr(instr, block) {
+         if (instr->type == nir_instr_type_tex)
+            progress |= lower_sampler(&b, nir_instr_as_tex(instr));
       }
    }
 
-   return true;
-}
-
-static void
-lower_impl(nir_function_impl *impl, const struct gl_shader_program *shader_program,
-           gl_shader_stage stage)
-{
-   lower_state state;
-
-   nir_builder_init(&state.builder, impl);
-   state.shader_program = shader_program;
-   state.stage = stage;
+   if (progress) {
+      nir_metadata_preserve(impl, nir_metadata_block_index |
+                                  nir_metadata_dominance);
+   } else {
+      nir_metadata_preserve(impl, nir_metadata_all);
+   }
 
-   nir_foreach_block_call(impl, lower_block_cb, &state);
+   return progress;
 }
 
-void
-nir_lower_samplers(nir_shader *shader,
-                   const struct gl_shader_program *shader_program)
+bool
+nir_lower_samplers(nir_shader *shader)
 {
-   nir_foreach_function(shader, function) {
+   bool progress = false;
+
+   /* Next, lower derefs to offsets. */
+   nir_foreach_function(function, shader) {
       if (function->impl)
-         lower_impl(function->impl, shader_program, shader->stage);
+         progress |= lower_impl(function->impl);
    }
+
+   return progress;
 }