spirv/nir: add support for ImageGatherExtended
authorIlia Mirkin <imirkin@alum.mit.edu>
Sun, 27 Nov 2016 21:09:55 +0000 (16:09 -0500)
committerIlia Mirkin <imirkin@alum.mit.edu>
Sat, 17 Dec 2016 01:27:37 +0000 (20:27 -0500)
The strategy is to do the same thing that the GLSL lower_offset_arrays
pass does - create 4 separate texture gather ops, one per offset, and
read in the results from each gather's w component to recreate the
desired result.

Signed-off-by: Ilia Mirkin <imirkin@alum.mit.edu>
Reviewed-by: Dave Airlie <airlied@redhat.com>
src/compiler/spirv/spirv_to_nir.c

index 7c35b8c614fb8a6e34713f59f45f7f6a530685e0..57d08865ff290b5b914a4af7ca275300bd5ef4ce 100644 (file)
@@ -1478,6 +1478,7 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode,
       (*p++) = vtn_tex_src(b, w[idx++], nir_tex_src_lod);
 
    /* Now we need to handle some number of optional arguments */
+   const struct vtn_ssa_value *gather_offsets = NULL;
    if (idx < count) {
       uint32_t operands = w[idx++];
 
@@ -1504,8 +1505,10 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode,
           operands & SpvImageOperandsConstOffsetMask)
          (*p++) = vtn_tex_src(b, w[idx++], nir_tex_src_offset);
 
-      if (operands & SpvImageOperandsConstOffsetsMask)
-         assert(!"Constant offsets to texture gather not yet implemented");
+      if (operands & SpvImageOperandsConstOffsetsMask) {
+         gather_offsets = vtn_ssa_value(b, w[idx++]);
+         (*p++) = (nir_tex_src){};
+      }
 
       if (operands & SpvImageOperandsSampleMask) {
          assert(texop == nir_texop_txf_ms);
@@ -1539,13 +1542,16 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode,
    }
 
    nir_deref_var *sampler = vtn_access_chain_to_deref(b, sampled.sampler);
+   nir_deref *texture;
    if (sampled.image) {
       nir_deref_var *image = vtn_access_chain_to_deref(b, sampled.image);
-      instr->texture = nir_deref_as_var(nir_copy_deref(instr, &image->deref));
+      texture = &image->deref;
    } else {
-      instr->texture = nir_deref_as_var(nir_copy_deref(instr, &sampler->deref));
+      texture = &sampler->deref;
    }
 
+   instr->texture = nir_deref_as_var(nir_copy_deref(instr, texture));
+
    switch (instr->op) {
    case nir_texop_tex:
    case nir_texop_txb:
@@ -1575,10 +1581,66 @@ vtn_handle_texture(struct vtn_builder *b, SpvOp opcode,
    assert(glsl_get_vector_elements(ret_type->type) ==
           nir_tex_instr_dest_size(instr));
 
+   nir_ssa_def *def;
+   nir_instr *instruction;
+   if (gather_offsets) {
+      assert(glsl_get_base_type(gather_offsets->type) == GLSL_TYPE_ARRAY);
+      assert(glsl_get_length(gather_offsets->type) == 4);
+      nir_tex_instr *instrs[4] = {instr, NULL, NULL, NULL};
+
+      /* Copy the current instruction 4x */
+      for (uint32_t i = 1; i < 4; i++) {
+         instrs[i] = nir_tex_instr_create(b->shader, instr->num_srcs);
+         instrs[i]->op = instr->op;
+         instrs[i]->coord_components = instr->coord_components;
+         instrs[i]->sampler_dim = instr->sampler_dim;
+         instrs[i]->is_array = instr->is_array;
+         instrs[i]->is_shadow = instr->is_shadow;
+         instrs[i]->is_new_style_shadow = instr->is_new_style_shadow;
+         instrs[i]->component = instr->component;
+         instrs[i]->dest_type = instr->dest_type;
+         instrs[i]->texture =
+            nir_deref_as_var(nir_copy_deref(instrs[i], texture));
+         instrs[i]->sampler = NULL;
+
+         memcpy(instrs[i]->src, srcs, instr->num_srcs * sizeof(*instr->src));
+
+         nir_ssa_dest_init(&instrs[i]->instr, &instrs[i]->dest,
+                           nir_tex_instr_dest_size(instr), 32, NULL);
+      }
+
+      /* Fill in the last argument with the offset from the passed in offsets
+       * and insert the instruction into the stream.
+       */
+      for (uint32_t i = 0; i < 4; i++) {
+         nir_tex_src src;
+         src.src = nir_src_for_ssa(gather_offsets->elems[i]->def);
+         src.src_type = nir_tex_src_offset;
+         instrs[i]->src[instrs[i]->num_srcs - 1] = src;
+         nir_builder_instr_insert(&b->nb, &instrs[i]->instr);
+      }
+
+      /* Combine the results of the 4 instructions by taking their .w
+       * components
+       */
+      nir_alu_instr *vec4 = nir_alu_instr_create(b->shader, nir_op_vec4);
+      nir_ssa_dest_init(&vec4->instr, &vec4->dest.dest, 4, 32, NULL);
+      vec4->dest.write_mask = 0xf;
+      for (uint32_t i = 0; i < 4; i++) {
+         vec4->src[i].src = nir_src_for_ssa(&instrs[i]->dest.ssa);
+         vec4->src[i].swizzle[0] = 3;
+      }
+      def = &vec4->dest.dest.ssa;
+      instruction = &vec4->instr;
+   } else {
+      def = &instr->dest.ssa;
+      instruction = &instr->instr;
+   }
+
    val->ssa = vtn_create_ssa_value(b, ret_type->type);
-   val->ssa->def = &instr->dest.ssa;
+   val->ssa->def = def;
 
-   nir_builder_instr_insert(&b->nb, &instr->instr);
+   nir_builder_instr_insert(&b->nb, instruction);
 }
 
 static void
@@ -2437,6 +2499,7 @@ vtn_handle_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
       case SpvCapabilityClipDistance:
       case SpvCapabilityCullDistance:
       case SpvCapabilityInputAttachment:
+      case SpvCapabilityImageGatherExtended:
          break;
 
       case SpvCapabilityGeometryStreams:
@@ -2451,7 +2514,6 @@ vtn_handle_preamble_instruction(struct vtn_builder *b, SpvOp opcode,
       case SpvCapabilityInt64Atomics:
       case SpvCapabilityAtomicStorage:
       case SpvCapabilityInt16:
-      case SpvCapabilityImageGatherExtended:
       case SpvCapabilityStorageImageMultisample:
       case SpvCapabilityImageCubeArray:
       case SpvCapabilityInt8: