zink: lower away fdph
[mesa.git] / src / gallium / drivers / zink / nir_to_spirv / nir_to_spirv.c
index 96f1f5433213952e364f0c56bbb72529d72c52b3..043b210caf9116225fde307e8b8e07c92708ff25 100644 (file)
@@ -38,6 +38,7 @@ struct ntv_context {
 
    SpvId ubos[128];
    size_t num_ubos;
+   SpvId image_types[PIPE_MAX_SAMPLERS];
    SpvId samplers[PIPE_MAX_SAMPLERS];
    size_t num_samplers;
    SpvId entry_ifaces[PIPE_MAX_SHADER_INPUTS * 4 + PIPE_MAX_SHADER_OUTPUTS * 4];
@@ -56,7 +57,7 @@ struct ntv_context {
    bool block_started;
    SpvId loop_break, loop_cont;
 
-   SpvId front_face_var;
+   SpvId front_face_var, instance_id_var, vertex_id_var;
 };
 
 static SpvId
@@ -235,7 +236,9 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
    if (ctx->stage == MESA_SHADER_FRAGMENT) {
       if (var->data.location >= VARYING_SLOT_VAR0)
          spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.location - VARYING_SLOT_VAR0);
+                                     var->data.location -
+                                     VARYING_SLOT_VAR0 +
+                                     VARYING_SLOT_TEX0);
       else if ((var->data.location >= VARYING_SLOT_COL0 &&
                 var->data.location <= VARYING_SLOT_TEX7) ||
                var->data.location == VARYING_SLOT_BFC0 ||
@@ -291,7 +294,9 @@ emit_output(struct ntv_context *ctx, struct nir_variable *var)
    if (ctx->stage == MESA_SHADER_VERTEX) {
       if (var->data.location >= VARYING_SLOT_VAR0)
          spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.location - VARYING_SLOT_VAR0);
+                                     var->data.location -
+                                     VARYING_SLOT_VAR0 +
+                                     VARYING_SLOT_TEX0);
       else if ((var->data.location >= VARYING_SLOT_COL0 &&
                 var->data.location <= VARYING_SLOT_TEX7) ||
                var->data.location == VARYING_SLOT_BFC0 ||
@@ -358,12 +363,16 @@ type_to_dim(enum glsl_sampler_dim gdim, bool *is_ms)
       return SpvDim1D;
    case GLSL_SAMPLER_DIM_2D:
       return SpvDim2D;
-   case GLSL_SAMPLER_DIM_RECT:
-      return SpvDimRect;
-   case GLSL_SAMPLER_DIM_CUBE:
-      return SpvDimCube;
    case GLSL_SAMPLER_DIM_3D:
       return SpvDim3D;
+   case GLSL_SAMPLER_DIM_CUBE:
+      return SpvDimCube;
+   case GLSL_SAMPLER_DIM_RECT:
+      return SpvDimRect;
+   case GLSL_SAMPLER_DIM_BUF:
+      return SpvDimBuffer;
+   case GLSL_SAMPLER_DIM_EXTERNAL:
+      return SpvDim2D; /* seems dodgy... */
    case GLSL_SAMPLER_DIM_MS:
       *is_ms = true;
       return SpvDim2D;
@@ -377,30 +386,62 @@ type_to_dim(enum glsl_sampler_dim gdim, bool *is_ms)
 static void
 emit_sampler(struct ntv_context *ctx, struct nir_variable *var)
 {
+   const struct glsl_type *type = glsl_without_array(var->type);
+
    bool is_ms;
-   SpvDim dimension = type_to_dim(glsl_get_sampler_dim(var->type), &is_ms);
-   SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
-   SpvId image_type = spirv_builder_type_image(&ctx->builder, float_type,
-                            dimension, false, glsl_sampler_type_is_array(var->type), is_ms, 1,
-                            SpvImageFormatUnknown);
+   SpvDim dimension = type_to_dim(glsl_get_sampler_dim(type), &is_ms);
+
+   SpvId result_type = get_glsl_basetype(ctx, glsl_get_sampler_result_type(type));
+   SpvId image_type = spirv_builder_type_image(&ctx->builder, result_type,
+                                               dimension, false,
+                                               glsl_sampler_type_is_array(type),
+                                               is_ms, 1,
+                                               SpvImageFormatUnknown);
 
    SpvId sampled_type = spirv_builder_type_sampled_image(&ctx->builder,
                                                          image_type);
    SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
                                                    SpvStorageClassUniformConstant,
                                                    sampled_type);
-   SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
-                                         SpvStorageClassUniformConstant);
 
-   if (var->name)
-      spirv_builder_emit_name(&ctx->builder, var_id, var->name);
+   if (glsl_type_is_array(var->type)) {
+      for (int i = 0; i < glsl_get_length(var->type); ++i) {
+         SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
+                                               SpvStorageClassUniformConstant);
 
-   assert(ctx->num_samplers < ARRAY_SIZE(ctx->samplers));
-   ctx->samplers[ctx->num_samplers++] = var_id;
+         if (var->name) {
+            char element_name[100];
+            snprintf(element_name, sizeof(element_name), "%s_%d", var->name, i);
+            spirv_builder_emit_name(&ctx->builder, var_id, var->name);
+         }
 
-   spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
-                                     var->data.descriptor_set);
-   spirv_builder_emit_binding(&ctx->builder, var_id, var->data.binding);
+         assert(ctx->num_samplers < ARRAY_SIZE(ctx->image_types));
+         ctx->image_types[ctx->num_samplers] = image_type;
+
+         assert(ctx->num_samplers < ARRAY_SIZE(ctx->samplers));
+         ctx->samplers[ctx->num_samplers++] = var_id;
+
+         spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
+                                           var->data.descriptor_set);
+         spirv_builder_emit_binding(&ctx->builder, var_id, var->data.binding);
+      }
+   } else {
+      SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
+                                            SpvStorageClassUniformConstant);
+
+      if (var->name)
+         spirv_builder_emit_name(&ctx->builder, var_id, var->name);
+
+      assert(ctx->num_samplers < ARRAY_SIZE(ctx->image_types));
+      ctx->image_types[ctx->num_samplers] = image_type;
+
+      assert(ctx->num_samplers < ARRAY_SIZE(ctx->samplers));
+      ctx->samplers[ctx->num_samplers++] = var_id;
+
+      spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
+                                        var->data.descriptor_set);
+      spirv_builder_emit_binding(&ctx->builder, var_id, var->data.binding);
+   }
 }
 
 static void
@@ -450,7 +491,7 @@ emit_uniform(struct ntv_context *ctx, struct nir_variable *var)
       emit_ubo(ctx, var);
    else {
       assert(var->data.mode == nir_var_uniform);
-      if (glsl_type_is_sampler(var->type))
+      if (glsl_type_is_sampler(glsl_without_array(var->type)))
          emit_sampler(ctx, var);
    }
 }
@@ -702,6 +743,15 @@ emit_builtin_binop(struct ntv_context *ctx, enum GLSLstd450 op, SpvId type,
                                       op, args, ARRAY_SIZE(args));
 }
 
+static SpvId
+emit_builtin_triop(struct ntv_context *ctx, enum GLSLstd450 op, SpvId type,
+                   SpvId src0, SpvId src1, SpvId src2)
+{
+   SpvId args[] = { src0, src1, src2 };
+   return spirv_builder_emit_ext_inst(&ctx->builder, type, ctx->GLSL_std_450,
+                                      op, args, ARRAY_SIZE(args));
+}
+
 static SpvId
 get_fvec_constant(struct ntv_context *ctx, unsigned bit_size,
                   unsigned num_components, float value)
@@ -861,7 +911,11 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
    UNOP(nir_op_ineg, SpvOpSNegate)
    UNOP(nir_op_fneg, SpvOpFNegate)
    UNOP(nir_op_fddx, SpvOpDPdx)
+   UNOP(nir_op_fddx_coarse, SpvOpDPdxCoarse)
+   UNOP(nir_op_fddx_fine, SpvOpDPdxFine)
    UNOP(nir_op_fddy, SpvOpDPdy)
+   UNOP(nir_op_fddy_coarse, SpvOpDPdyCoarse)
+   UNOP(nir_op_fddy_fine, SpvOpDPdyFine)
    UNOP(nir_op_f2i32, SpvOpConvertFToS)
    UNOP(nir_op_f2u32, SpvOpConvertFToU)
    UNOP(nir_op_i2f32, SpvOpConvertSToF)
@@ -889,6 +943,7 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       result = emit_builtin_unop(ctx, spirv_op, dest_type, src[0]); \
       break;
 
+   BUILTIN_UNOP(nir_op_iabs, GLSLstd450SAbs)
    BUILTIN_UNOP(nir_op_fabs, GLSLstd450FAbs)
    BUILTIN_UNOP(nir_op_fsqrt, GLSLstd450Sqrt)
    BUILTIN_UNOP(nir_op_frsq, GLSLstd450InverseSqrt)
@@ -931,6 +986,7 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
    BINOP(nir_op_imul, SpvOpIMul)
    BINOP(nir_op_idiv, SpvOpSDiv)
    BINOP(nir_op_udiv, SpvOpUDiv)
+   BINOP(nir_op_umod, SpvOpUMod)
    BINOP(nir_op_fadd, SpvOpFAdd)
    BINOP(nir_op_fsub, SpvOpFSub)
    BINOP(nir_op_fmul, SpvOpFMul)
@@ -940,6 +996,7 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
    BINOP(nir_op_ige, SpvOpSGreaterThanEqual)
    BINOP(nir_op_ieq, SpvOpIEqual)
    BINOP(nir_op_ine, SpvOpINotEqual)
+   BINOP(nir_op_uge, SpvOpUGreaterThanEqual)
    BINOP(nir_op_flt, SpvOpFOrdLessThan)
    BINOP(nir_op_fge, SpvOpFOrdGreaterThanEqual)
    BINOP(nir_op_feq, SpvOpFOrdEqual)
@@ -968,6 +1025,9 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       result = emit_binop(ctx, SpvOpDot, dest_type, src[0], src[1]);
       break;
 
+   case nir_op_fdph:
+      unreachable("should already be lowered away");
+
    case nir_op_seq:
    case nir_op_sne:
    case nir_op_slt:
@@ -1005,6 +1065,12 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       }
       break;
 
+   case nir_op_flrp:
+      assert(nir_op_infos[alu->op].num_inputs == 3);
+      result = emit_builtin_triop(ctx, GLSLstd450FMix, dest_type,
+                                  src[0], src[1], src[2]);
+      break;
+
    case nir_op_fcsel:
       result = emit_binop(ctx, SpvOpFOrdGreaterThan,
                           get_bvec_type(ctx, num_components),
@@ -1020,6 +1086,54 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       result = emit_select(ctx, dest_type, src[0], src[1], src[2]);
       break;
 
+   case nir_op_bany_fnequal2:
+   case nir_op_bany_fnequal3:
+   case nir_op_bany_fnequal4:
+      assert(nir_op_infos[alu->op].num_inputs == 2);
+      assert(alu_instr_src_components(alu, 0) ==
+             alu_instr_src_components(alu, 1));
+      result = emit_binop(ctx, SpvOpFOrdNotEqual,
+                          get_bvec_type(ctx, alu_instr_src_components(alu, 0)),
+                          src[0], src[1]);
+      result = emit_unop(ctx, SpvOpAny, dest_type, result);
+      break;
+
+   case nir_op_ball_fequal2:
+   case nir_op_ball_fequal3:
+   case nir_op_ball_fequal4:
+      assert(nir_op_infos[alu->op].num_inputs == 2);
+      assert(alu_instr_src_components(alu, 0) ==
+             alu_instr_src_components(alu, 1));
+      result = emit_binop(ctx, SpvOpFOrdEqual,
+                          get_bvec_type(ctx, alu_instr_src_components(alu, 0)),
+                          src[0], src[1]);
+      result = emit_unop(ctx, SpvOpAll, dest_type, result);
+      break;
+
+   case nir_op_bany_inequal2:
+   case nir_op_bany_inequal3:
+   case nir_op_bany_inequal4:
+      assert(nir_op_infos[alu->op].num_inputs == 2);
+      assert(alu_instr_src_components(alu, 0) ==
+             alu_instr_src_components(alu, 1));
+      result = emit_binop(ctx, SpvOpINotEqual,
+                          get_bvec_type(ctx, alu_instr_src_components(alu, 0)),
+                          src[0], src[1]);
+      result = emit_unop(ctx, SpvOpAny, dest_type, result);
+      break;
+
+   case nir_op_ball_iequal2:
+   case nir_op_ball_iequal3:
+   case nir_op_ball_iequal4:
+      assert(nir_op_infos[alu->op].num_inputs == 2);
+      assert(alu_instr_src_components(alu, 0) ==
+             alu_instr_src_components(alu, 1));
+      result = emit_binop(ctx, SpvOpIEqual,
+                          get_bvec_type(ctx, alu_instr_src_components(alu, 0)),
+                          src[0], src[1]);
+      result = emit_unop(ctx, SpvOpAll, dest_type, result);
+      break;
+
    case nir_op_vec2:
    case nir_op_vec3:
    case nir_op_vec4: {
@@ -1044,27 +1158,40 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
 static void
 emit_load_const(struct ntv_context *ctx, nir_load_const_instr *load_const)
 {
-   uint32_t values[NIR_MAX_VEC_COMPONENTS];
-   for (int i = 0; i < load_const->def.num_components; ++i)
-      values[i] = load_const->value[i].u32;
-
    unsigned bit_size = load_const->def.bit_size;
    unsigned num_components = load_const->def.num_components;
 
    SpvId constant;
    if (num_components > 1) {
       SpvId components[num_components];
-      for (int i = 0; i < num_components; i++)
-         components[i] = emit_uint_const(ctx, bit_size, values[i]);
+      SpvId type;
+      if (bit_size == 1) {
+         for (int i = 0; i < num_components; i++)
+            components[i] = spirv_builder_const_bool(&ctx->builder,
+                                                     load_const->value[i].b);
+
+         type = get_bvec_type(ctx, num_components);
+      } else {
+         for (int i = 0; i < num_components; i++)
+            components[i] = emit_uint_const(ctx, bit_size,
+                                            load_const->value[i].u32);
 
-      SpvId type = get_uvec_type(ctx, bit_size, num_components);
+         type = get_uvec_type(ctx, bit_size, num_components);
+      }
       constant = spirv_builder_const_composite(&ctx->builder, type,
                                                components, num_components);
    } else {
       assert(num_components == 1);
-      constant = emit_uint_const(ctx, bit_size, values[0]);
+      if (bit_size == 1)
+         constant = spirv_builder_const_bool(&ctx->builder,
+                                             load_const->value[0].b);
+      else
+         constant = emit_uint_const(ctx, bit_size, load_const->value[0].u32);
    }
 
+   if (bit_size == 1)
+      constant = bvec_to_uvec(ctx, constant, num_components);
+
    store_ssa_def_uint(ctx, &load_const->def, constant);
 }
 
@@ -1158,25 +1285,33 @@ emit_store_deref(struct ntv_context *ctx, nir_intrinsic_instr *intr)
    spirv_builder_emit_store(&ctx->builder, ptr, result);
 }
 
+static SpvId
+create_builtin_var(struct ntv_context *ctx, SpvId var_type,
+                   SpvStorageClass storage_class,
+                   const char *name, SpvBuiltIn builtin)
+{
+   SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
+                                                   storage_class,
+                                                   var_type);
+   SpvId var = spirv_builder_emit_var(&ctx->builder, pointer_type,
+                                      storage_class);
+   spirv_builder_emit_name(&ctx->builder, var, name);
+   spirv_builder_emit_builtin(&ctx->builder, var, builtin);
+
+   assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
+   ctx->entry_ifaces[ctx->num_entry_ifaces++] = var;
+   return var;
+}
+
 static void
 emit_load_front_face(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 {
-   SpvId var_type = get_glsl_type(ctx, glsl_bool_type());
-   if (!ctx->front_face_var) {
-      SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
-                                                      SpvStorageClassInput,
-                                                      var_type);
-      ctx->front_face_var = spirv_builder_emit_var(&ctx->builder,
-                                                   pointer_type,
-                                                   SpvStorageClassInput);
-      spirv_builder_emit_name(&ctx->builder, ctx->front_face_var,
-                              "gl_FrontFacing");
-      spirv_builder_emit_builtin(&ctx->builder, ctx->front_face_var,
-                                 SpvBuiltInFrontFacing);
-
-      assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
-      ctx->entry_ifaces[ctx->num_entry_ifaces++] = ctx->front_face_var;
-   }
+   SpvId var_type = spirv_builder_type_bool(&ctx->builder);
+   if (!ctx->front_face_var)
+      ctx->front_face_var = create_builtin_var(ctx, var_type,
+                                               SpvStorageClassInput,
+                                               "gl_FrontFacing",
+                                               SpvBuiltInFrontFacing);
 
    SpvId result = spirv_builder_emit_load(&ctx->builder, var_type,
                                           ctx->front_face_var);
@@ -1185,6 +1320,38 @@ emit_load_front_face(struct ntv_context *ctx, nir_intrinsic_instr *intr)
    store_dest_uint(ctx, &intr->dest, result);
 }
 
+static void
+emit_load_instance_id(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+{
+   SpvId var_type = spirv_builder_type_uint(&ctx->builder, 32);
+   if (!ctx->instance_id_var)
+      ctx->instance_id_var = create_builtin_var(ctx, var_type,
+                                               SpvStorageClassInput,
+                                               "gl_InstanceId",
+                                               SpvBuiltInInstanceIndex);
+
+   SpvId result = spirv_builder_emit_load(&ctx->builder, var_type,
+                                          ctx->instance_id_var);
+   assert(1 == nir_dest_num_components(intr->dest));
+   store_dest_uint(ctx, &intr->dest, result);
+}
+
+static void
+emit_load_vertex_id(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+{
+   SpvId var_type = spirv_builder_type_uint(&ctx->builder, 32);
+   if (!ctx->vertex_id_var)
+      ctx->vertex_id_var = create_builtin_var(ctx, var_type,
+                                               SpvStorageClassInput,
+                                               "gl_VertexID",
+                                               SpvBuiltInVertexIndex);
+
+   SpvId result = spirv_builder_emit_load(&ctx->builder, var_type,
+                                          ctx->vertex_id_var);
+   assert(1 == nir_dest_num_components(intr->dest));
+   store_dest_uint(ctx, &intr->dest, result);
+}
+
 static void
 emit_intrinsic(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 {
@@ -1209,6 +1376,14 @@ emit_intrinsic(struct ntv_context *ctx, nir_intrinsic_instr *intr)
       emit_load_front_face(ctx, intr);
       break;
 
+   case nir_intrinsic_load_instance_id:
+      emit_load_instance_id(ctx, intr);
+      break;
+
+   case nir_intrinsic_load_vertex_id:
+      emit_load_vertex_id(ctx, intr);
+      break;
+
    default:
       fprintf(stderr, "emit_intrinsic: not implemented (%s)\n",
               nir_intrinsic_infos[intr->intrinsic].name);
@@ -1235,21 +1410,36 @@ get_src_float(struct ntv_context *ctx, nir_src *src)
    return bitcast_to_fvec(ctx, def, bit_size, num_components);
 }
 
+static SpvId
+get_src_int(struct ntv_context *ctx, nir_src *src)
+{
+   SpvId def = get_src_uint(ctx, src);
+   unsigned num_components = nir_src_num_components(*src);
+   unsigned bit_size = nir_src_bit_size(*src);
+   return bitcast_to_ivec(ctx, def, bit_size, num_components);
+}
+
 static void
 emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
 {
    assert(tex->op == nir_texop_tex ||
           tex->op == nir_texop_txb ||
-          tex->op == nir_texop_txl);
-   assert(nir_alu_type_get_base_type(tex->dest_type) == nir_type_float);
+          tex->op == nir_texop_txl ||
+          tex->op == nir_texop_txd ||
+          tex->op == nir_texop_txf ||
+          tex->op == nir_texop_txs);
    assert(tex->texture_index == tex->sampler_index);
 
-   SpvId coord = 0, proj = 0, bias = 0, lod = 0, dref = 0;
-   unsigned coord_components;
+   SpvId coord = 0, proj = 0, bias = 0, lod = 0, dref = 0, dx = 0, dy = 0,
+         offset = 0;
+   unsigned coord_components = 0;
    for (unsigned i = 0; i < tex->num_srcs; i++) {
       switch (tex->src[i].src_type) {
       case nir_tex_src_coord:
-         coord = get_src_float(ctx, &tex->src[i].src);
+         if (tex->op == nir_texop_txf)
+            coord = get_src_int(ctx, &tex->src[i].src);
+         else
+            coord = get_src_float(ctx, &tex->src[i].src);
          coord_components = nir_src_num_components(tex->src[i].src);
          break;
 
@@ -1259,6 +1449,10 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
          assert(proj != 0);
          break;
 
+      case nir_tex_src_offset:
+         offset = get_src_int(ctx, &tex->src[i].src);
+         break;
+
       case nir_tex_src_bias:
          assert(tex->op == nir_texop_txb);
          bias = get_src_float(ctx, &tex->src[i].src);
@@ -1267,7 +1461,11 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
 
       case nir_tex_src_lod:
          assert(nir_src_num_components(tex->src[i].src) == 1);
-         lod = get_src_float(ctx, &tex->src[i].src);
+         if (tex->op == nir_texop_txf ||
+             tex->op == nir_texop_txs)
+            lod = get_src_int(ctx, &tex->src[i].src);
+         else
+            lod = get_src_float(ctx, &tex->src[i].src);
          assert(lod != 0);
          break;
 
@@ -1277,6 +1475,16 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
          assert(dref != 0);
          break;
 
+      case nir_tex_src_ddx:
+         dx = get_src_float(ctx, &tex->src[i].src);
+         assert(dx != 0);
+         break;
+
+      case nir_tex_src_ddy:
+         dy = get_src_float(ctx, &tex->src[i].src);
+         assert(dy != 0);
+         break;
+
       default:
          fprintf(stderr, "texture source: %d\n", tex->src[i].src_type);
          unreachable("unknown texture source");
@@ -1288,12 +1496,7 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
       assert(lod != 0);
    }
 
-   bool is_ms;
-   SpvDim dimension = type_to_dim(tex->sampler_dim, &is_ms);
-   SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
-   SpvId image_type = spirv_builder_type_image(&ctx->builder, float_type,
-                            dimension, false, tex->is_array, is_ms, 1,
-                            SpvImageFormatUnknown);
+   SpvId image_type = ctx->image_types[tex->texture_index];
    SpvId sampled_type = spirv_builder_type_sampled_image(&ctx->builder,
                                                          image_type);
 
@@ -1303,7 +1506,16 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
 
    SpvId dest_type = get_dest_type(ctx, &tex->dest, tex->dest_type);
 
-   if (proj) {
+   if (tex->op == nir_texop_txs) {
+      SpvId image = spirv_builder_emit_image(&ctx->builder, image_type, load);
+      SpvId result = spirv_builder_emit_image_query_size(&ctx->builder,
+                                                         dest_type, image,
+                                                         lod);
+      store_dest(ctx, &tex->dest, result, tex->dest_type);
+      return;
+   }
+
+   if (proj && coord_components > 0) {
       SpvId constituents[coord_components + 1];
       if (coord_components == 1)
          constituents[0] = coord;
@@ -1328,17 +1540,26 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
 
    SpvId actual_dest_type = dest_type;
    if (dref)
-      actual_dest_type = float_type;
+      actual_dest_type = spirv_builder_type_float(&ctx->builder, 32);
+
+   SpvId result;
+   if (tex->op == nir_texop_txf) {
+      SpvId image = spirv_builder_emit_image(&ctx->builder, image_type, load);
+      result = spirv_builder_emit_image_fetch(&ctx->builder, dest_type,
+                                              image, coord, lod);
+   } else {
+      result = spirv_builder_emit_image_sample(&ctx->builder,
+                                               actual_dest_type, load,
+                                               coord,
+                                               proj != 0,
+                                               lod, bias, dref, dx, dy,
+                                               offset);
+   }
 
-   SpvId result = spirv_builder_emit_image_sample(&ctx->builder,
-                                                  actual_dest_type, load,
-                                                  coord,
-                                                  proj != 0,
-                                                  lod, bias, dref);
    spirv_builder_emit_decoration(&ctx->builder, result,
                                  SpvDecorationRelaxedPrecision);
 
-   if (dref) {
+   if (dref && nir_dest_num_components(tex->dest) > 1) {
       SpvId components[4] = { result, result, result, result };
       result = spirv_builder_emit_composite_construct(&ctx->builder,
                                                       dest_type,
@@ -1629,8 +1850,11 @@ nir_to_spirv(struct nir_shader *s)
    }
 
    // TODO: only enable when needed
-   if (s->info.stage == MESA_SHADER_FRAGMENT)
+   if (s->info.stage == MESA_SHADER_FRAGMENT) {
       spirv_builder_emit_cap(&ctx.builder, SpvCapabilitySampled1D);
+      spirv_builder_emit_cap(&ctx.builder, SpvCapabilityImageQuery);
+      spirv_builder_emit_cap(&ctx.builder, SpvCapabilityDerivativeControl);
+   }
 
    ctx.stage = s->info.stage;
    ctx.GLSL_std_450 = spirv_builder_import(&ctx.builder, "GLSL.std.450");