zink: implement streamout and xfb handling in ntv
[mesa.git] / src / gallium / drivers / zink / nir_to_spirv / nir_to_spirv.c
index 4ab14b455e74e845dd2d8ffb517c8d82c48a799c..10ff16eaff2e726e362feffe72ffc98373e7b5c0 100644 (file)
 #include "nir.h"
 #include "pipe/p_state.h"
 #include "util/u_memory.h"
+#include "util/hash_table.h"
+
+/* this consistently maps slots to a zero-indexed value to avoid wasting slots */
+static unsigned slot_pack_map[] = {
+   /* Position is builtin */
+   [VARYING_SLOT_POS] = UINT_MAX,
+   [VARYING_SLOT_COL0] = 0, /* input/output */
+   [VARYING_SLOT_COL1] = 1, /* input/output */
+   [VARYING_SLOT_FOGC] = 2, /* input/output */
+   /* TEX0-7 are translated to VAR0-7 by nir, so we don't need to reserve */
+   [VARYING_SLOT_TEX0] = UINT_MAX, /* input/output */
+   [VARYING_SLOT_TEX1] = UINT_MAX,
+   [VARYING_SLOT_TEX2] = UINT_MAX,
+   [VARYING_SLOT_TEX3] = UINT_MAX,
+   [VARYING_SLOT_TEX4] = UINT_MAX,
+   [VARYING_SLOT_TEX5] = UINT_MAX,
+   [VARYING_SLOT_TEX6] = UINT_MAX,
+   [VARYING_SLOT_TEX7] = UINT_MAX,
+
+   /* PointSize is builtin */
+   [VARYING_SLOT_PSIZ] = UINT_MAX,
+
+   [VARYING_SLOT_BFC0] = 3, /* output only */
+   [VARYING_SLOT_BFC1] = 4, /* output only */
+   [VARYING_SLOT_EDGE] = 5, /* output only */
+   [VARYING_SLOT_CLIP_VERTEX] = 6, /* output only */
+
+   /* ClipDistance is builtin */
+   [VARYING_SLOT_CLIP_DIST0] = UINT_MAX,
+   [VARYING_SLOT_CLIP_DIST1] = UINT_MAX,
+
+   /* CullDistance is builtin */
+   [VARYING_SLOT_CULL_DIST0] = UINT_MAX, /* input/output */
+   [VARYING_SLOT_CULL_DIST1] = UINT_MAX, /* never actually used */
+
+   /* PrimitiveId is builtin */
+   [VARYING_SLOT_PRIMITIVE_ID] = UINT_MAX,
+
+   /* Layer is builtin */
+   [VARYING_SLOT_LAYER] = UINT_MAX, /* input/output */
+
+   /* ViewportIndex is builtin */
+   [VARYING_SLOT_VIEWPORT] =  UINT_MAX, /* input/output */
+
+   /* FrontFacing is builtin */
+   [VARYING_SLOT_FACE] = UINT_MAX,
+
+   /* PointCoord is builtin */
+   [VARYING_SLOT_PNTC] = UINT_MAX, /* input only */
+
+   /* TessLevelOuter is builtin */
+   [VARYING_SLOT_TESS_LEVEL_OUTER] = UINT_MAX,
+   /* TessLevelInner is builtin */
+   [VARYING_SLOT_TESS_LEVEL_INNER] = UINT_MAX,
+
+   [VARYING_SLOT_BOUNDING_BOX0] = 7, /* Only appears as TCS output. */
+   [VARYING_SLOT_BOUNDING_BOX1] = 8, /* Only appears as TCS output. */
+   [VARYING_SLOT_VIEW_INDEX] = 9, /* input/output */
+   [VARYING_SLOT_VIEWPORT_MASK] = 10, /* output only */
+};
+#define NTV_MIN_RESERVED_SLOTS 10
 
 struct ntv_context {
    struct spirv_builder builder;
@@ -34,36 +95,46 @@ struct ntv_context {
    SpvId GLSL_std_450;
 
    gl_shader_stage stage;
-   SpvId inputs[PIPE_MAX_SHADER_INPUTS][4];
-   SpvId input_types[PIPE_MAX_SHADER_INPUTS][4];
-   SpvId outputs[PIPE_MAX_SHADER_OUTPUTS][4];
-   SpvId output_types[PIPE_MAX_SHADER_OUTPUTS][4];
 
    SpvId ubos[128];
    size_t num_ubos;
+   SpvId image_types[PIPE_MAX_SAMPLERS];
    SpvId samplers[PIPE_MAX_SAMPLERS];
-   size_t num_samplers;
+   unsigned samplers_used : PIPE_MAX_SAMPLERS;
    SpvId entry_ifaces[PIPE_MAX_SHADER_INPUTS * 4 + PIPE_MAX_SHADER_OUTPUTS * 4];
    size_t num_entry_ifaces;
 
    SpvId *defs;
    size_t num_defs;
 
-   struct hash_table *vars;
+   SpvId *regs;
+   size_t num_regs;
+
+   struct hash_table *vars; /* nir_variable -> SpvId */
+   struct hash_table *so_outputs; /* pipe_stream_output -> SpvId */
+   unsigned outputs[VARYING_SLOT_MAX];
+   const struct glsl_type *so_output_gl_types[VARYING_SLOT_MAX];
+   SpvId so_output_types[VARYING_SLOT_MAX];
 
    const SpvId *block_ids;
    size_t num_blocks;
    bool block_started;
    SpvId loop_break, loop_cont;
+
+   SpvId front_face_var, instance_id_var, vertex_id_var;
 };
 
 static SpvId
-get_fvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
-                  const float values[]);
+get_fvec_constant(struct ntv_context *ctx, unsigned bit_size,
+                  unsigned num_components, float value);
 
 static SpvId
-get_uvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
-                  const uint32_t values[]);
+get_uvec_constant(struct ntv_context *ctx, unsigned bit_size,
+                  unsigned num_components, uint32_t value);
+
+static SpvId
+get_ivec_constant(struct ntv_context *ctx, unsigned bit_size,
+                  unsigned num_components, int32_t value);
 
 static SpvId
 emit_unop(struct ntv_context *ctx, SpvOp op, SpvId type, SpvId src);
@@ -95,6 +166,27 @@ block_label(struct ntv_context *ctx, nir_block *block)
    return ctx->block_ids[block->index];
 }
 
+static SpvId
+emit_float_const(struct ntv_context *ctx, int bit_size, float value)
+{
+   assert(bit_size == 32);
+   return spirv_builder_const_float(&ctx->builder, bit_size, value);
+}
+
+static SpvId
+emit_uint_const(struct ntv_context *ctx, int bit_size, uint32_t value)
+{
+   assert(bit_size == 32);
+   return spirv_builder_const_uint(&ctx->builder, bit_size, value);
+}
+
+static SpvId
+emit_int_const(struct ntv_context *ctx, int bit_size, int32_t value)
+{
+   assert(bit_size == 32);
+   return spirv_builder_const_int(&ctx->builder, bit_size, value);
+}
+
 static SpvId
 get_fvec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_components)
 {
@@ -140,14 +232,17 @@ get_uvec_type(struct ntv_context *ctx, unsigned bit_size, unsigned num_component
 static SpvId
 get_dest_uvec_type(struct ntv_context *ctx, nir_dest *dest)
 {
-   return get_uvec_type(ctx, nir_dest_bit_size(*dest),
-                             nir_dest_num_components(*dest));
+   unsigned bit_size = MAX2(nir_dest_bit_size(*dest), 32);
+   return get_uvec_type(ctx, bit_size, nir_dest_num_components(*dest));
 }
 
 static SpvId
 get_glsl_basetype(struct ntv_context *ctx, enum glsl_base_type type)
 {
    switch (type) {
+   case GLSL_TYPE_BOOL:
+      return spirv_builder_type_bool(&ctx->builder);
+
    case GLSL_TYPE_FLOAT:
       return spirv_builder_type_float(&ctx->builder, 32);
 
@@ -175,16 +270,33 @@ get_glsl_type(struct ntv_context *ctx, const struct glsl_type *type)
          get_glsl_basetype(ctx, glsl_get_base_type(type)),
          glsl_get_vector_elements(type));
 
+   if (glsl_type_is_array(type)) {
+      SpvId ret = spirv_builder_type_array(&ctx->builder,
+         get_glsl_type(ctx, glsl_get_array_element(type)),
+         emit_uint_const(ctx, 32, glsl_get_length(type)));
+      uint32_t stride = glsl_get_explicit_stride(type);
+      if (stride)
+         spirv_builder_emit_array_stride(&ctx->builder, ret, stride);
+      return ret;
+   }
+
+
    unreachable("we shouldn't get here, I think...");
 }
 
+#define HANDLE_EMIT_BUILTIN(SLOT, BUILTIN) \
+      case VARYING_SLOT_##SLOT: \
+         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltIn##BUILTIN); \
+         break
+
+
 static void
 emit_input(struct ntv_context *ctx, struct nir_variable *var)
 {
-   SpvId vec_type = get_glsl_type(ctx, var->type);
+   SpvId var_type = get_glsl_type(ctx, var->type);
    SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
                                                    SpvStorageClassInput,
-                                                   vec_type);
+                                                   var_type);
    SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
                                          SpvStorageClassInput);
 
@@ -192,19 +304,26 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
       spirv_builder_emit_name(&ctx->builder, var_id, var->name);
 
    if (ctx->stage == MESA_SHADER_FRAGMENT) {
-      switch (var->data.location) {
-      case VARYING_SLOT_POS:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragCoord);
-         break;
-
-      case VARYING_SLOT_PNTC:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointCoord);
-         break;
+      unsigned slot = var->data.location;
+      switch (slot) {
+      HANDLE_EMIT_BUILTIN(POS, FragCoord);
+      HANDLE_EMIT_BUILTIN(PNTC, PointCoord);
+      HANDLE_EMIT_BUILTIN(LAYER, Layer);
+      HANDLE_EMIT_BUILTIN(PRIMITIVE_ID, PrimitiveId);
+      HANDLE_EMIT_BUILTIN(CLIP_DIST0, ClipDistance);
+      HANDLE_EMIT_BUILTIN(CULL_DIST0, CullDistance);
+      HANDLE_EMIT_BUILTIN(VIEWPORT, ViewportIndex);
+      HANDLE_EMIT_BUILTIN(FACE, FrontFacing);
 
       default:
-         spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.driver_location);
-         break;
+         if (slot < VARYING_SLOT_VAR0) {
+            slot = slot_pack_map[slot];
+            if (slot == UINT_MAX)
+               debug_printf("unhandled varying slot: %s\n", gl_varying_slot_name(var->data.location));
+         } else
+            slot -= VARYING_SLOT_VAR0 - NTV_MIN_RESERVED_SLOTS;
+         assert(slot < VARYING_SLOT_VAR0);
+         spirv_builder_emit_location(&ctx->builder, var_id, slot);
       }
    } else {
       spirv_builder_emit_location(&ctx->builder, var_id,
@@ -218,11 +337,7 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
    if (var->data.interpolation == INTERP_MODE_FLAT)
       spirv_builder_emit_decoration(&ctx->builder, var_id, SpvDecorationFlat);
 
-   assert(var->data.driver_location < PIPE_MAX_SHADER_INPUTS);
-   assert(var->data.location_frac < 4);
-   assert(ctx->inputs[var->data.driver_location][var->data.location_frac] == 0);
-   ctx->inputs[var->data.driver_location][var->data.location_frac] = var_id;
-   ctx->input_types[var->data.driver_location][var->data.location_frac] = vec_type;
+   _mesa_hash_table_insert(ctx->vars, var, (void *)(intptr_t)var_id);
 
    assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
    ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
@@ -231,10 +346,10 @@ emit_input(struct ntv_context *ctx, struct nir_variable *var)
 static void
 emit_output(struct ntv_context *ctx, struct nir_variable *var)
 {
-   SpvId vec_type = get_glsl_type(ctx, var->type);
+   SpvId var_type = get_glsl_type(ctx, var->type);
    SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
                                                    SpvStorageClassOutput,
-                                                   vec_type);
+                                                   var_type);
    SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
                                          SpvStorageClassOutput);
    if (var->name)
@@ -242,28 +357,61 @@ emit_output(struct ntv_context *ctx, struct nir_variable *var)
 
 
    if (ctx->stage == MESA_SHADER_VERTEX) {
-      switch (var->data.location) {
-      case VARYING_SLOT_POS:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPosition);
-         break;
-
-      case VARYING_SLOT_PSIZ:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInPointSize);
+      unsigned slot = var->data.location;
+      switch (slot) {
+      HANDLE_EMIT_BUILTIN(POS, Position);
+      HANDLE_EMIT_BUILTIN(PSIZ, PointSize);
+      HANDLE_EMIT_BUILTIN(LAYER, Layer);
+      HANDLE_EMIT_BUILTIN(PRIMITIVE_ID, PrimitiveId);
+      HANDLE_EMIT_BUILTIN(CULL_DIST0, CullDistance);
+      HANDLE_EMIT_BUILTIN(VIEWPORT, ViewportIndex);
+      HANDLE_EMIT_BUILTIN(TESS_LEVEL_OUTER, TessLevelOuter);
+      HANDLE_EMIT_BUILTIN(TESS_LEVEL_INNER, TessLevelInner);
+
+      case VARYING_SLOT_CLIP_DIST0:
+         assert(glsl_type_is_array(var->type));
+         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInClipDistance);
+         /* this can be as large as 2x vec4, which requires 2 slots */
+         ctx->outputs[VARYING_SLOT_CLIP_DIST1] = var_id;
+         ctx->so_output_gl_types[VARYING_SLOT_CLIP_DIST1] = var->type;
+         ctx->so_output_types[VARYING_SLOT_CLIP_DIST1] = var_type;
          break;
 
       default:
-         spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.driver_location - 1);
+         if (slot < VARYING_SLOT_VAR0) {
+            slot = slot_pack_map[slot];
+            if (slot == UINT_MAX)
+               debug_printf("unhandled varying slot: %s\n", gl_varying_slot_name(var->data.location));
+         } else
+            slot -= VARYING_SLOT_VAR0 - NTV_MIN_RESERVED_SLOTS;
+         assert(slot < VARYING_SLOT_VAR0);
+         spirv_builder_emit_location(&ctx->builder, var_id, slot);
+         /* non-builtins get location incremented by VARYING_SLOT_VAR0 in vtn, so
+          * use driver_location for non-builtins with defined slots to avoid overlap
+          */
       }
+      ctx->outputs[var->data.location] = var_id;
+      ctx->so_output_gl_types[var->data.location] = var->type;
+      ctx->so_output_types[var->data.location] = var_type;
    } else if (ctx->stage == MESA_SHADER_FRAGMENT) {
-      switch (var->data.location) {
-      case FRAG_RESULT_DEPTH:
-         spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragDepth);
-         break;
-
-      default:
+      if (var->data.location >= FRAG_RESULT_DATA0)
          spirv_builder_emit_location(&ctx->builder, var_id,
-                                     var->data.driver_location);
+                                     var->data.location - FRAG_RESULT_DATA0);
+      else {
+         switch (var->data.location) {
+         case FRAG_RESULT_COLOR:
+            spirv_builder_emit_location(&ctx->builder, var_id, 0);
+            spirv_builder_emit_index(&ctx->builder, var_id, var->data.index);
+            break;
+
+         case FRAG_RESULT_DEPTH:
+            spirv_builder_emit_builtin(&ctx->builder, var_id, SpvBuiltInFragDepth);
+            break;
+
+         default:
+            spirv_builder_emit_location(&ctx->builder, var_id,
+                                        var->data.driver_location);
+         }
       }
    }
 
@@ -271,11 +419,24 @@ emit_output(struct ntv_context *ctx, struct nir_variable *var)
       spirv_builder_emit_component(&ctx->builder, var_id,
                                    var->data.location_frac);
 
-   assert(var->data.driver_location < PIPE_MAX_SHADER_INPUTS);
-   assert(var->data.location_frac < 4);
-   assert(ctx->outputs[var->data.driver_location][var->data.location_frac] == 0);
-   ctx->outputs[var->data.driver_location][var->data.location_frac] = var_id;
-   ctx->output_types[var->data.driver_location][var->data.location_frac] = vec_type;
+   switch (var->data.interpolation) {
+   case INTERP_MODE_NONE:
+   case INTERP_MODE_SMOOTH: /* XXX spirv doesn't seem to have anything for this */
+      break;
+   case INTERP_MODE_FLAT:
+      spirv_builder_emit_decoration(&ctx->builder, var_id, SpvDecorationFlat);
+      break;
+   case INTERP_MODE_EXPLICIT:
+      spirv_builder_emit_decoration(&ctx->builder, var_id, SpvDecorationExplicitInterpAMD);
+      break;
+   case INTERP_MODE_NOPERSPECTIVE:
+      spirv_builder_emit_decoration(&ctx->builder, var_id, SpvDecorationNoPerspective);
+      break;
+   default:
+      unreachable("unknown interpolation value");
+   }
+
+   _mesa_hash_table_insert(ctx->vars, var, (void *)(intptr_t)var_id);
 
    assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
    ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
@@ -290,12 +451,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 SpvDim2D;
+   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;
@@ -306,33 +471,98 @@ type_to_dim(enum glsl_sampler_dim gdim, bool *is_ms)
    return SpvDim2D;
 }
 
+uint32_t
+zink_binding(gl_shader_stage stage, VkDescriptorType type, int index)
+{
+   if (stage == MESA_SHADER_NONE ||
+       stage >= MESA_SHADER_COMPUTE) {
+      unreachable("not supported");
+   } else {
+      uint32_t stage_offset = (uint32_t)stage * (PIPE_MAX_CONSTANT_BUFFERS +
+                                                 PIPE_MAX_SHADER_SAMPLER_VIEWS);
+
+      switch (type) {
+      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+         assert(index < PIPE_MAX_CONSTANT_BUFFERS);
+         return stage_offset + index;
+
+      case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+         assert(index < PIPE_MAX_SHADER_SAMPLER_VIEWS);
+         return stage_offset + PIPE_MAX_CONSTANT_BUFFERS + index;
+
+      default:
+         unreachable("unexpected type");
+      }
+   }
+}
+
 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);
+         int index = var->data.binding + i;
+         assert(!(ctx->samplers_used & (1 << index)));
+         assert(!ctx->image_types[index]);
+         ctx->image_types[index] = image_type;
+         ctx->samplers[index] = var_id;
+         ctx->samplers_used |= 1 << index;
+
+         spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
+                                           var->data.descriptor_set);
+         int binding = zink_binding(ctx->stage,
+                                    VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+                                    var->data.binding + i);
+         spirv_builder_emit_binding(&ctx->builder, var_id, 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);
+
+      int index = var->data.binding;
+      assert(!(ctx->samplers_used & (1 << index)));
+      assert(!ctx->image_types[index]);
+      ctx->image_types[index] = image_type;
+      ctx->samplers[index] = var_id;
+      ctx->samplers_used |= 1 << index;
+
+      spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
+                                        var->data.descriptor_set);
+      int binding = zink_binding(ctx->stage,
+                                 VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+                                 var->data.binding);
+      spirv_builder_emit_binding(&ctx->builder, var_id, binding);
+   }
 }
 
 static void
@@ -340,7 +570,7 @@ emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
 {
    uint32_t size = glsl_count_attribute_slots(var->type, false);
    SpvId vec4_type = get_uvec_type(ctx, 32, 4);
-   SpvId array_length = spirv_builder_const_uint(&ctx->builder, 32, size);
+   SpvId array_length = emit_uint_const(ctx, 32, size);
    SpvId array_type = spirv_builder_type_array(&ctx->builder, vec4_type,
                                                array_length);
    spirv_builder_emit_array_stride(&ctx->builder, array_type, 16);
@@ -372,20 +602,26 @@ emit_ubo(struct ntv_context *ctx, struct nir_variable *var)
 
    spirv_builder_emit_descriptor_set(&ctx->builder, var_id,
                                      var->data.descriptor_set);
-   spirv_builder_emit_binding(&ctx->builder, var_id, var->data.binding);
+   int binding = zink_binding(ctx->stage,
+                              VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                              var->data.binding);
+   spirv_builder_emit_binding(&ctx->builder, var_id, binding);
 }
 
 static void
 emit_uniform(struct ntv_context *ctx, struct nir_variable *var)
 {
-   if (glsl_type_is_sampler(var->type))
-      emit_sampler(ctx, var);
-   else if (var->interface_type)
+   if (var->data.mode == nir_var_mem_ubo)
       emit_ubo(ctx, var);
+   else {
+      assert(var->data.mode == nir_var_uniform);
+      if (glsl_type_is_sampler(glsl_without_array(var->type)))
+         emit_sampler(ctx, var);
+   }
 }
 
 static SpvId
-get_src_uint_ssa(struct ntv_context *ctx, const nir_ssa_def *ssa)
+get_src_ssa(struct ntv_context *ctx, const nir_ssa_def *ssa)
 {
    assert(ssa->index < ctx->num_defs);
    assert(ctx->defs[ssa->index] != 0);
@@ -395,13 +631,13 @@ get_src_uint_ssa(struct ntv_context *ctx, const nir_ssa_def *ssa)
 static SpvId
 get_var_from_reg(struct ntv_context *ctx, nir_register *reg)
 {
-   struct hash_entry *he = _mesa_hash_table_search(ctx->vars, reg);
-   assert(he);
-   return (SpvId)(intptr_t)he->data;
+   assert(reg->index < ctx->num_regs);
+   assert(ctx->regs[reg->index] != 0);
+   return ctx->regs[reg->index];
 }
 
 static SpvId
-get_src_uint_reg(struct ntv_context *ctx, const nir_reg_src *reg)
+get_src_reg(struct ntv_context *ctx, const nir_reg_src *reg)
 {
    assert(reg->reg);
    assert(!reg->indirect);
@@ -413,21 +649,21 @@ get_src_uint_reg(struct ntv_context *ctx, const nir_reg_src *reg)
 }
 
 static SpvId
-get_src_uint(struct ntv_context *ctx, nir_src *src)
+get_src(struct ntv_context *ctx, nir_src *src)
 {
    if (src->is_ssa)
-      return get_src_uint_ssa(ctx, src->ssa);
+      return get_src_ssa(ctx, src->ssa);
    else
-      return get_src_uint_reg(ctx, &src->reg);
+      return get_src_reg(ctx, &src->reg);
 }
 
 static SpvId
-get_alu_src_uint(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
+get_alu_src_raw(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
 {
    assert(!alu->src[src].negate);
    assert(!alu->src[src].abs);
 
-   SpvId def = get_src_uint(ctx, &alu->src[src].src);
+   SpvId def = get_src(ctx, &alu->src[src].src);
 
    unsigned used_channels = 0;
    bool need_swizzle = false;
@@ -450,29 +686,35 @@ get_alu_src_uint(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
       return def;
 
    int bit_size = nir_src_bit_size(alu->src[src].src);
+   assert(bit_size == 1 || bit_size == 32);
+
+   SpvId raw_type = bit_size == 1 ? spirv_builder_type_bool(&ctx->builder) :
+                                    spirv_builder_type_uint(&ctx->builder, bit_size);
 
-   SpvId uint_type = spirv_builder_type_uint(&ctx->builder, bit_size);
    if (used_channels == 1) {
       uint32_t indices[] =  { alu->src[src].swizzle[0] };
-      return spirv_builder_emit_composite_extract(&ctx->builder, uint_type,
+      return spirv_builder_emit_composite_extract(&ctx->builder, raw_type,
                                                   def, indices,
                                                   ARRAY_SIZE(indices));
    } else if (live_channels == 1) {
-      SpvId uvec_type = spirv_builder_type_vector(&ctx->builder, uint_type,
-                                                  used_channels);
+      SpvId raw_vec_type = spirv_builder_type_vector(&ctx->builder,
+                                                     raw_type,
+                                                     used_channels);
 
-      SpvId constituents[NIR_MAX_VEC_COMPONENTS];
+      SpvId constituents[NIR_MAX_VEC_COMPONENTS] = {0};
       for (unsigned i = 0; i < used_channels; ++i)
         constituents[i] = def;
 
-      return spirv_builder_emit_composite_construct(&ctx->builder, uvec_type,
+      return spirv_builder_emit_composite_construct(&ctx->builder,
+                                                    raw_vec_type,
                                                     constituents,
                                                     used_channels);
    } else {
-      SpvId uvec_type = spirv_builder_type_vector(&ctx->builder, uint_type,
-                                                  used_channels);
+      SpvId raw_vec_type = spirv_builder_type_vector(&ctx->builder,
+                                                     raw_type,
+                                                     used_channels);
 
-      uint32_t components[NIR_MAX_VEC_COMPONENTS];
+      uint32_t components[NIR_MAX_VEC_COMPONENTS] = {0};
       size_t num_components = 0;
       for (unsigned i = 0; i < NIR_MAX_VEC_COMPONENTS; i++) {
          if (!nir_alu_instr_channel_used(alu, src, i))
@@ -481,13 +723,14 @@ get_alu_src_uint(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
          components[num_components++] = alu->src[src].swizzle[i];
       }
 
-      return spirv_builder_emit_vector_shuffle(&ctx->builder, uvec_type,
-                                        def, def, components, num_components);
+      return spirv_builder_emit_vector_shuffle(&ctx->builder, raw_vec_type,
+                                               def, def, components,
+                                               num_components);
    }
 }
 
 static void
-store_ssa_def_uint(struct ntv_context *ctx, nir_ssa_def *ssa, SpvId result)
+store_ssa_def(struct ntv_context *ctx, nir_ssa_def *ssa, SpvId result)
 {
    assert(result != 0);
    assert(ssa->index < ctx->num_defs);
@@ -495,33 +738,32 @@ store_ssa_def_uint(struct ntv_context *ctx, nir_ssa_def *ssa, SpvId result)
 }
 
 static SpvId
-bvec_to_uvec(struct ntv_context *ctx, SpvId value, unsigned num_components)
+emit_select(struct ntv_context *ctx, SpvId type, SpvId cond,
+            SpvId if_true, SpvId if_false)
 {
-   SpvId otype = get_uvec_type(ctx, 32, num_components);
-   uint32_t zeros[4] = { 0, 0, 0, 0 };
-   uint32_t ones[4] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff };
-   SpvId zero = get_uvec_constant(ctx, 32, num_components, zeros);
-   SpvId one = get_uvec_constant(ctx, 32, num_components, ones);
-   return emit_triop(ctx, SpvOpSelect, otype, value, one, zero);
+   return emit_triop(ctx, SpvOpSelect, type, cond, if_true, if_false);
 }
 
 static SpvId
 uvec_to_bvec(struct ntv_context *ctx, SpvId value, unsigned num_components)
 {
    SpvId type = get_bvec_type(ctx, num_components);
-
-   uint32_t zeros[NIR_MAX_VEC_COMPONENTS] = { 0 };
-   SpvId zero = get_uvec_constant(ctx, 32, num_components, zeros);
-
+   SpvId zero = get_uvec_constant(ctx, 32, num_components, 0);
    return emit_binop(ctx, SpvOpINotEqual, type, value, zero);
 }
 
+static SpvId
+emit_bitcast(struct ntv_context *ctx, SpvId type, SpvId value)
+{
+   return emit_unop(ctx, SpvOpBitcast, type, value);
+}
+
 static SpvId
 bitcast_to_uvec(struct ntv_context *ctx, SpvId value, unsigned bit_size,
                 unsigned num_components)
 {
    SpvId type = get_uvec_type(ctx, bit_size, num_components);
-   return emit_unop(ctx, SpvOpBitcast, type, value);
+   return emit_bitcast(ctx, type, value);
 }
 
 static SpvId
@@ -529,7 +771,7 @@ bitcast_to_ivec(struct ntv_context *ctx, SpvId value, unsigned bit_size,
                 unsigned num_components)
 {
    SpvId type = get_ivec_type(ctx, bit_size, num_components);
-   return emit_unop(ctx, SpvOpBitcast, type, value);
+   return emit_bitcast(ctx, type, value);
 }
 
 static SpvId
@@ -537,7 +779,7 @@ bitcast_to_fvec(struct ntv_context *ctx, SpvId value, unsigned bit_size,
                unsigned num_components)
 {
    SpvId type = get_fvec_type(ctx, bit_size, num_components);
-   return emit_unop(ctx, SpvOpBitcast, type, value);
+   return emit_bitcast(ctx, type, value);
 }
 
 static void
@@ -549,39 +791,40 @@ store_reg_def(struct ntv_context *ctx, nir_reg_dest *reg, SpvId result)
 }
 
 static void
-store_dest_uint(struct ntv_context *ctx, nir_dest *dest, SpvId result)
+store_dest_raw(struct ntv_context *ctx, nir_dest *dest, SpvId result)
 {
    if (dest->is_ssa)
-      store_ssa_def_uint(ctx, &dest->ssa, result);
+      store_ssa_def(ctx, &dest->ssa, result);
    else
       store_reg_def(ctx, &dest->reg, result);
 }
 
-static void
+static SpvId
 store_dest(struct ntv_context *ctx, nir_dest *dest, SpvId result, nir_alu_type type)
 {
    unsigned num_components = nir_dest_num_components(*dest);
    unsigned bit_size = nir_dest_bit_size(*dest);
 
-   switch (nir_alu_type_get_base_type(type)) {
-   case nir_type_bool:
-      assert(bit_size == 1);
-      result = bvec_to_uvec(ctx, result, num_components);
-      break;
+   if (bit_size != 1) {
+      switch (nir_alu_type_get_base_type(type)) {
+      case nir_type_bool:
+         assert("bool should have bit-size 1");
 
-   case nir_type_uint:
-      break; /* nothing to do! */
+      case nir_type_uint:
+         break; /* nothing to do! */
 
-   case nir_type_int:
-   case nir_type_float:
-      result = bitcast_to_uvec(ctx, result, bit_size, num_components);
-      break;
+      case nir_type_int:
+      case nir_type_float:
+         result = bitcast_to_uvec(ctx, result, bit_size, num_components);
+         break;
 
-   default:
-      unreachable("unsupported nir_alu_type");
+      default:
+         unreachable("unsupported nir_alu_type");
+      }
    }
 
-   store_dest_uint(ctx, dest, result);
+   store_dest_raw(ctx, dest, result);
+   return result;
 }
 
 static SpvId
@@ -590,6 +833,167 @@ emit_unop(struct ntv_context *ctx, SpvOp op, SpvId type, SpvId src)
    return spirv_builder_emit_unop(&ctx->builder, op, type, src);
 }
 
+/* return the intended xfb output vec type based on base type and vector size */
+static SpvId
+get_output_type(struct ntv_context *ctx, unsigned register_index, unsigned num_components)
+{
+   const struct glsl_type *out_type = ctx->so_output_gl_types[register_index];
+   enum glsl_base_type base_type = glsl_get_base_type(out_type);
+   if (base_type == GLSL_TYPE_ARRAY)
+      base_type = glsl_get_base_type(glsl_without_array(out_type));
+
+   switch (base_type) {
+   case GLSL_TYPE_BOOL:
+      return get_bvec_type(ctx, num_components);
+
+   case GLSL_TYPE_FLOAT:
+      return get_fvec_type(ctx, 32, num_components);
+
+   case GLSL_TYPE_INT:
+      return get_ivec_type(ctx, 32, num_components);
+
+   case GLSL_TYPE_UINT:
+      return get_uvec_type(ctx, 32, num_components);
+
+   default:
+      break;
+   }
+   unreachable("unknown type");
+   return 0;
+}
+
+/* for streamout create new outputs, as streamout can be done on individual components,
+   from complete outputs, so we just can't use the created packed outputs */
+static void
+emit_so_info(struct ntv_context *ctx, unsigned max_output_location,
+             const struct pipe_stream_output_info *so_info, struct pipe_stream_output_info *local_so_info)
+{
+   for (unsigned i = 0; i < local_so_info->num_outputs; i++) {
+      struct pipe_stream_output so_output = local_so_info->output[i];
+      SpvId out_type = get_output_type(ctx, so_output.register_index, so_output.num_components);
+      SpvId pointer_type = spirv_builder_type_pointer(&ctx->builder,
+                                                      SpvStorageClassOutput,
+                                                      out_type);
+      SpvId var_id = spirv_builder_emit_var(&ctx->builder, pointer_type,
+                                            SpvStorageClassOutput);
+      char name[10];
+
+      snprintf(name, 10, "xfb%d", i);
+      spirv_builder_emit_name(&ctx->builder, var_id, name);
+      spirv_builder_emit_offset(&ctx->builder, var_id, (so_output.dst_offset * 4));
+      spirv_builder_emit_xfb_buffer(&ctx->builder, var_id, so_output.output_buffer);
+      spirv_builder_emit_xfb_stride(&ctx->builder, var_id, so_info->stride[so_output.output_buffer] * 4);
+
+      /* output location is incremented by VARYING_SLOT_VAR0 for non-builtins in vtn,
+       * so we need to ensure that the new xfb location slot doesn't conflict with any previously-emitted
+       * outputs.
+       *
+       * if there's no previous outputs that take up user slots (VAR0+) then we can start right after the
+       * glsl builtin reserved slots, otherwise we start just after the adjusted user output slot
+       */
+      uint32_t location = NTV_MIN_RESERVED_SLOTS + i;
+      if (max_output_location >= VARYING_SLOT_VAR0)
+         location = max_output_location - VARYING_SLOT_VAR0 + 1 + i;
+      assert(location < VARYING_SLOT_VAR0);
+      spirv_builder_emit_location(&ctx->builder, var_id, location);
+
+      /* note: gl_ClipDistance[4] can the 0-indexed member of VARYING_SLOT_CLIP_DIST1 here,
+       * so this is still the 0 component
+       */
+      if (so_output.start_component)
+         spirv_builder_emit_component(&ctx->builder, var_id, so_output.start_component);
+
+      uint32_t *key = ralloc_size(NULL, sizeof(uint32_t));
+      *key = (uint32_t)so_output.register_index << 2 | so_output.start_component;
+      _mesa_hash_table_insert(ctx->so_outputs, key, (void *)(intptr_t)var_id);
+
+      assert(ctx->num_entry_ifaces < ARRAY_SIZE(ctx->entry_ifaces));
+      ctx->entry_ifaces[ctx->num_entry_ifaces++] = var_id;
+   }
+}
+
+static void
+emit_so_outputs(struct ntv_context *ctx,
+                const struct pipe_stream_output_info *so_info, struct pipe_stream_output_info *local_so_info)
+{
+   SpvId loaded_outputs[VARYING_SLOT_MAX] = {};
+   for (unsigned i = 0; i < local_so_info->num_outputs; i++) {
+      uint32_t components[NIR_MAX_VEC_COMPONENTS];
+      struct pipe_stream_output so_output = local_so_info->output[i];
+      uint32_t so_key = (uint32_t) so_output.register_index << 2 | so_output.start_component;
+      struct hash_entry *he = _mesa_hash_table_search(ctx->so_outputs, &so_key);
+      assert(he);
+      SpvId so_output_var_id = (SpvId)(intptr_t)he->data;
+
+      SpvId type = get_output_type(ctx, so_output.register_index, so_output.num_components);
+      SpvId output = ctx->outputs[so_output.register_index];
+      SpvId output_type = ctx->so_output_types[so_output.register_index];
+      const struct glsl_type *out_type = ctx->so_output_gl_types[so_output.register_index];
+
+      if (!loaded_outputs[so_output.register_index])
+         loaded_outputs[so_output.register_index] = spirv_builder_emit_load(&ctx->builder, output_type, output);
+      SpvId src = loaded_outputs[so_output.register_index];
+
+      SpvId result;
+
+      for (unsigned c = 0; c < so_output.num_components; c++) {
+         components[c] = so_output.start_component + c;
+         /* this is the second half of a 2 * vec4 array */
+         if (ctx->stage == MESA_SHADER_VERTEX && so_output.register_index == VARYING_SLOT_CLIP_DIST1)
+            components[c] += 4;
+      }
+
+      /* if we're emitting a scalar or the type we're emitting matches the output's original type and we're
+       * emitting the same number of components, then we can skip any sort of conversion here
+       */
+      if (glsl_type_is_scalar(out_type) || (type == output_type && glsl_get_length(out_type) == so_output.num_components))
+         result = src;
+      else {
+         if (ctx->stage == MESA_SHADER_VERTEX && so_output.register_index == VARYING_SLOT_POS) {
+            /* gl_Position was modified by nir_lower_clip_halfz, so we need to reverse that for streamout here:
+             * 
+             * opengl gl_Position.z = (vulkan gl_Position.z * 2.0) - vulkan gl_Position.w
+             *
+             * to do this, we extract the z and w components, perform the multiply and subtract ops, then reinsert
+             */
+            uint32_t z_component[] = {2};
+            uint32_t w_component[] = {3};
+            SpvId ftype = spirv_builder_type_float(&ctx->builder, 32);
+            SpvId z = spirv_builder_emit_composite_extract(&ctx->builder, ftype, src, z_component, 1);
+            SpvId w = spirv_builder_emit_composite_extract(&ctx->builder, ftype, src, w_component, 1);
+            SpvId new_z = emit_binop(ctx, SpvOpFMul, ftype, z, spirv_builder_const_float(&ctx->builder, 32, 2.0));
+            new_z = emit_binop(ctx, SpvOpFSub, ftype, new_z, w);
+            src = spirv_builder_emit_vector_insert(&ctx->builder, type, src, new_z, 2);
+         }
+         /* OpCompositeExtract can only extract scalars for our use here */
+         if (so_output.num_components == 1) {
+            result = spirv_builder_emit_composite_extract(&ctx->builder, type, src, components, so_output.num_components);
+         } else if (glsl_type_is_vector(out_type)) {
+            /* OpVectorShuffle can select vector members into a differently-sized vector */
+            result = spirv_builder_emit_vector_shuffle(&ctx->builder, type,
+                                                             src, src,
+                                                             components, so_output.num_components);
+            result = emit_unop(ctx, SpvOpBitcast, type, result);
+         } else {
+             /* for arrays, we need to manually extract each desired member
+              * and re-pack them into the desired output type
+              */
+             for (unsigned c = 0; c < so_output.num_components; c++) {
+                uint32_t member[] = { so_output.start_component + c };
+                SpvId base_type = get_glsl_type(ctx, glsl_without_array(out_type));
+
+                if (ctx->stage == MESA_SHADER_VERTEX && so_output.register_index == VARYING_SLOT_CLIP_DIST1)
+                   member[0] += 4;
+                components[c] = spirv_builder_emit_composite_extract(&ctx->builder, base_type, src, member, 1);
+             }
+             result = spirv_builder_emit_composite_construct(&ctx->builder, type, components, so_output.num_components);
+         }
+      }
+
+      spirv_builder_emit_store(&ctx->builder, so_output_var_id, result);
+   }
+}
+
 static SpvId
 emit_binop(struct ntv_context *ctx, SpvOp op, SpvId type,
            SpvId src0, SpvId src1)
@@ -623,45 +1027,72 @@ emit_builtin_binop(struct ntv_context *ctx, enum GLSLstd450 op, SpvId type,
 }
 
 static SpvId
-get_fvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
-                  const float values[])
+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)
 {
    assert(bit_size == 32);
 
-   if (num_components > 1) {
-      SpvId components[num_components];
-      for (int i = 0; i < num_components; i++)
-         components[i] = spirv_builder_const_float(&ctx->builder, bit_size,
-                                                   values[i]);
+   SpvId result = emit_float_const(ctx, bit_size, value);
+   if (num_components == 1)
+      return result;
 
-      SpvId type = get_fvec_type(ctx, bit_size, num_components);
-      return spirv_builder_const_composite(&ctx->builder, type, components,
-                                           num_components);
-   }
+   assert(num_components > 1);
+   SpvId components[num_components];
+   for (int i = 0; i < num_components; i++)
+      components[i] = result;
 
-   assert(num_components == 1);
-   return spirv_builder_const_float(&ctx->builder, bit_size, values[0]);
+   SpvId type = get_fvec_type(ctx, bit_size, num_components);
+   return spirv_builder_const_composite(&ctx->builder, type, components,
+                                        num_components);
 }
 
 static SpvId
-get_uvec_constant(struct ntv_context *ctx, int bit_size, int num_components,
-                  const uint32_t values[])
+get_uvec_constant(struct ntv_context *ctx, unsigned bit_size,
+                  unsigned num_components, uint32_t value)
 {
    assert(bit_size == 32);
 
-   if (num_components > 1) {
-      SpvId components[num_components];
-      for (int i = 0; i < num_components; i++)
-         components[i] = spirv_builder_const_uint(&ctx->builder, bit_size,
-                                                  values[i]);
+   SpvId result = emit_uint_const(ctx, bit_size, value);
+   if (num_components == 1)
+      return result;
 
-      SpvId type = get_uvec_type(ctx, bit_size, num_components);
-      return spirv_builder_const_composite(&ctx->builder, type, components,
-                                           num_components);
-   }
+   assert(num_components > 1);
+   SpvId components[num_components];
+   for (int i = 0; i < num_components; i++)
+      components[i] = result;
 
-   assert(num_components == 1);
-   return spirv_builder_const_uint(&ctx->builder, bit_size, values[0]);
+   SpvId type = get_uvec_type(ctx, bit_size, num_components);
+   return spirv_builder_const_composite(&ctx->builder, type, components,
+                                        num_components);
+}
+
+static SpvId
+get_ivec_constant(struct ntv_context *ctx, unsigned bit_size,
+                  unsigned num_components, int32_t value)
+{
+   assert(bit_size == 32);
+
+   SpvId result = emit_int_const(ctx, bit_size, value);
+   if (num_components == 1)
+      return result;
+
+   assert(num_components > 1);
+   SpvId components[num_components];
+   for (int i = 0; i < num_components; i++)
+      components[i] = result;
+
+   SpvId type = get_ivec_type(ctx, bit_size, num_components);
+   return spirv_builder_const_composite(&ctx->builder, type, components,
+                                        num_components);
 }
 
 static inline unsigned
@@ -679,36 +1110,40 @@ alu_instr_src_components(const nir_alu_instr *instr, unsigned src)
 static SpvId
 get_alu_src(struct ntv_context *ctx, nir_alu_instr *alu, unsigned src)
 {
-   SpvId uint_value = get_alu_src_uint(ctx, alu, src);
+   SpvId raw_value = get_alu_src_raw(ctx, alu, src);
 
    unsigned num_components = alu_instr_src_components(alu, src);
    unsigned bit_size = nir_src_bit_size(alu->src[src].src);
    nir_alu_type type = nir_op_infos[alu->op].input_types[src];
 
-   switch (nir_alu_type_get_base_type(type)) {
-   case nir_type_bool:
-      assert(bit_size == 1);
-      return uvec_to_bvec(ctx, uint_value, num_components);
+   if (bit_size == 1)
+      return raw_value;
+   else {
+      switch (nir_alu_type_get_base_type(type)) {
+      case nir_type_bool:
+         unreachable("bool should have bit-size 1");
 
-   case nir_type_int:
-      return bitcast_to_ivec(ctx, uint_value, bit_size, num_components);
+      case nir_type_int:
+         return bitcast_to_ivec(ctx, raw_value, bit_size, num_components);
 
-   case nir_type_uint:
-      return uint_value;
+      case nir_type_uint:
+         return raw_value;
 
-   case nir_type_float:
-      return bitcast_to_fvec(ctx, uint_value, bit_size, num_components);
+      case nir_type_float:
+         return bitcast_to_fvec(ctx, raw_value, bit_size, num_components);
 
-   default:
-      unreachable("unknown nir_alu_type");
+      default:
+         unreachable("unknown nir_alu_type");
+      }
    }
 }
 
-static void
+static SpvId
 store_alu_result(struct ntv_context *ctx, nir_alu_instr *alu, SpvId result)
 {
    assert(!alu->dest.saturate);
-   return store_dest(ctx, &alu->dest.dest, result, nir_op_infos[alu->op].output_type);
+   return store_dest(ctx, &alu->dest.dest, result,
+                     nir_op_infos[alu->op].output_type);
 }
 
 static SpvId
@@ -717,9 +1152,12 @@ get_dest_type(struct ntv_context *ctx, nir_dest *dest, nir_alu_type type)
    unsigned num_components = nir_dest_num_components(*dest);
    unsigned bit_size = nir_dest_bit_size(*dest);
 
+   if (bit_size == 1)
+      return get_bvec_type(ctx, num_components);
+
    switch (nir_alu_type_get_base_type(type)) {
    case nir_type_bool:
-      return get_bvec_type(ctx, num_components);
+      unreachable("bool should have bit-size 1");
 
    case nir_type_int:
       return get_ivec_type(ctx, bit_size, num_components);
@@ -739,8 +1177,11 @@ static void
 emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
 {
    SpvId src[nir_op_infos[alu->op].num_inputs];
-   for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++)
+   unsigned in_bit_sizes[nir_op_infos[alu->op].num_inputs];
+   for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++) {
       src[i] = get_alu_src(ctx, alu, i);
+      in_bit_sizes[i] = nir_src_bit_size(alu->src[i].src);
+   }
 
    SpvId dest_type = get_dest_type(ctx, &alu->dest.dest,
                                    nir_op_infos[alu->op].output_type);
@@ -760,16 +1201,48 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       result = emit_unop(ctx, spirv_op, dest_type, src[0]); \
       break;
 
+   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)
+   UNOP(nir_op_u2f32, SpvOpConvertUToF)
+#undef UNOP
+
+   case nir_op_inot:
+      if (bit_size == 1)
+         result = emit_unop(ctx, SpvOpLogicalNot, dest_type, src[0]);
+      else
+         result = emit_unop(ctx, SpvOpNot, dest_type, src[0]);
+      break;
+
+   case nir_op_b2i32:
+      assert(nir_op_infos[alu->op].num_inputs == 1);
+      result = emit_select(ctx, dest_type, src[0],
+                           get_ivec_constant(ctx, 32, num_components, 1),
+                           get_ivec_constant(ctx, 32, num_components, 0));
+      break;
+
+   case nir_op_b2f32:
+      assert(nir_op_infos[alu->op].num_inputs == 1);
+      result = emit_select(ctx, dest_type, src[0],
+                           get_fvec_constant(ctx, 32, num_components, 1),
+                           get_fvec_constant(ctx, 32, num_components, 0));
+      break;
+
 #define BUILTIN_UNOP(nir_op, spirv_op) \
    case nir_op: \
       assert(nir_op_infos[alu->op].num_inputs == 1); \
       result = emit_builtin_unop(ctx, spirv_op, dest_type, src[0]); \
       break;
 
-   UNOP(nir_op_fneg, SpvOpFNegate)
-   UNOP(nir_op_fddx, SpvOpDPdx)
-   UNOP(nir_op_fddy, SpvOpDPdy)
-
+   BUILTIN_UNOP(nir_op_iabs, GLSLstd450SAbs)
    BUILTIN_UNOP(nir_op_fabs, GLSLstd450FAbs)
    BUILTIN_UNOP(nir_op_fsqrt, GLSLstd450Sqrt)
    BUILTIN_UNOP(nir_op_frsq, GLSLstd450InverseSqrt)
@@ -783,18 +1256,30 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
    BUILTIN_UNOP(nir_op_fsign, GLSLstd450FSign)
    BUILTIN_UNOP(nir_op_fsin, GLSLstd450Sin)
    BUILTIN_UNOP(nir_op_fcos, GLSLstd450Cos)
+#undef BUILTIN_UNOP
 
-   case nir_op_frcp: {
+   case nir_op_frcp:
       assert(nir_op_infos[alu->op].num_inputs == 1);
-      float one[4] = { 1, 1, 1, 1 };
-      src[1] = src[0];
-      src[0] = get_fvec_constant(ctx, bit_size, num_components, one);
-      result = emit_binop(ctx, SpvOpFDiv, dest_type, src[0], src[1]);
-      }
+      result = emit_binop(ctx, SpvOpFDiv, dest_type,
+                          get_fvec_constant(ctx, bit_size, num_components, 1),
+                          src[0]);
+      break;
+
+   case nir_op_f2b1:
+      assert(nir_op_infos[alu->op].num_inputs == 1);
+      result = emit_binop(ctx, SpvOpFOrdNotEqual, dest_type, src[0],
+                          get_fvec_constant(ctx,
+                                            nir_src_bit_size(alu->src[0].src),
+                                            num_components, 0));
+      break;
+   case nir_op_i2b1:
+      assert(nir_op_infos[alu->op].num_inputs == 1);
+      result = emit_binop(ctx, SpvOpINotEqual, dest_type, src[0],
+                          get_ivec_constant(ctx,
+                                            nir_src_bit_size(alu->src[0].src),
+                                            num_components, 0));
       break;
 
-#undef UNOP
-#undef BUILTIN_UNOP
 
 #define BINOP(nir_op, spirv_op) \
    case nir_op: \
@@ -802,26 +1287,52 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       result = emit_binop(ctx, spirv_op, dest_type, src[0], src[1]); \
       break;
 
-#define BUILTIN_BINOP(nir_op, spirv_op) \
-   case nir_op: \
-      assert(nir_op_infos[alu->op].num_inputs == 2); \
-      result = emit_builtin_binop(ctx, spirv_op, dest_type, src[0], src[1]); \
-      break;
-
    BINOP(nir_op_iadd, SpvOpIAdd)
    BINOP(nir_op_isub, SpvOpISub)
    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)
+   BINOP(nir_op_fdiv, SpvOpFDiv)
    BINOP(nir_op_fmod, SpvOpFMod)
-   BINOP(nir_op_flt, SpvOpFUnordLessThan)
-   BINOP(nir_op_fge, SpvOpFUnordGreaterThanEqual)
+   BINOP(nir_op_ilt, SpvOpSLessThan)
+   BINOP(nir_op_ige, SpvOpSGreaterThanEqual)
+   BINOP(nir_op_uge, SpvOpUGreaterThanEqual)
+   BINOP(nir_op_flt, SpvOpFOrdLessThan)
+   BINOP(nir_op_fge, SpvOpFOrdGreaterThanEqual)
+   BINOP(nir_op_feq, SpvOpFOrdEqual)
+   BINOP(nir_op_fne, SpvOpFOrdNotEqual)
+   BINOP(nir_op_ishl, SpvOpShiftLeftLogical)
+   BINOP(nir_op_ishr, SpvOpShiftRightArithmetic)
+   BINOP(nir_op_ushr, SpvOpShiftRightLogical)
+#undef BINOP
+
+#define BINOP_LOG(nir_op, spv_op, spv_log_op) \
+   case nir_op: \
+      assert(nir_op_infos[alu->op].num_inputs == 2); \
+      if (nir_src_bit_size(alu->src[0].src) == 1) \
+         result = emit_binop(ctx, spv_log_op, dest_type, src[0], src[1]); \
+      else \
+         result = emit_binop(ctx, spv_op, dest_type, src[0], src[1]); \
+      break;
+
+   BINOP_LOG(nir_op_iand, SpvOpBitwiseAnd, SpvOpLogicalAnd)
+   BINOP_LOG(nir_op_ior, SpvOpBitwiseOr, SpvOpLogicalOr)
+   BINOP_LOG(nir_op_ieq, SpvOpIEqual, SpvOpLogicalEqual)
+   BINOP_LOG(nir_op_ine, SpvOpINotEqual, SpvOpLogicalNotEqual)
+#undef BINOP_LOG
+
+#define BUILTIN_BINOP(nir_op, spirv_op) \
+   case nir_op: \
+      assert(nir_op_infos[alu->op].num_inputs == 2); \
+      result = emit_builtin_binop(ctx, spirv_op, dest_type, src[0], src[1]); \
+      break;
 
    BUILTIN_BINOP(nir_op_fmin, GLSLstd450FMin)
    BUILTIN_BINOP(nir_op_fmax, GLSLstd450FMax)
-
-#undef BINOP
 #undef BUILTIN_BINOP
 
    case nir_op_fdot2:
@@ -831,6 +1342,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:
@@ -839,8 +1353,8 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       int num_components = nir_dest_num_components(alu->dest.dest);
       SpvId bool_type = get_bvec_type(ctx, num_components);
 
-      SpvId zero = spirv_builder_const_float(&ctx->builder, 32, 0.0f);
-      SpvId one = spirv_builder_const_float(&ctx->builder, 32, 1.0f);
+      SpvId zero = emit_float_const(ctx, bit_size, 0.0f);
+      SpvId one = emit_float_const(ctx, bit_size, 1.0f);
       if (num_components > 1) {
          SpvId zero_comps[num_components], one_comps[num_components];
          for (int i = 0; i < num_components; i++) {
@@ -864,24 +1378,95 @@ emit_alu(struct ntv_context *ctx, nir_alu_instr *alu)
       }
 
       result = emit_binop(ctx, op, bool_type, src[0], src[1]);
-      result = emit_triop(ctx, SpvOpSelect, dest_type, result, one, zero);
+      result = emit_select(ctx, dest_type, result, one, zero);
       }
       break;
 
-   case nir_op_fcsel: {
+   case nir_op_flrp:
       assert(nir_op_infos[alu->op].num_inputs == 3);
-      int num_components = nir_dest_num_components(alu->dest.dest);
-      SpvId bool_type = get_bvec_type(ctx, num_components);
+      result = emit_builtin_triop(ctx, GLSLstd450FMix, dest_type,
+                                  src[0], src[1], src[2]);
+      break;
 
-      float zero[4] = { 0, 0, 0, 0 };
-      SpvId cmp = get_fvec_constant(ctx, nir_src_bit_size(alu->src[0].src),
-                                         num_components, zero);
+   case nir_op_fcsel:
+      result = emit_binop(ctx, SpvOpFOrdGreaterThan,
+                          get_bvec_type(ctx, num_components),
+                          src[0],
+                          get_fvec_constant(ctx,
+                                            nir_src_bit_size(alu->src[0].src),
+                                            num_components, 0));
+      result = emit_select(ctx, dest_type, result, src[1], src[2]);
+      break;
 
-      result = emit_binop(ctx, SpvOpFOrdGreaterThan, bool_type, src[0], cmp);
-      result = emit_triop(ctx, SpvOpSelect, dest_type, result, src[1], src[2]);
-      }
+   case nir_op_bcsel:
+      assert(nir_op_infos[alu->op].num_inputs == 3);
+      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));
+      assert(in_bit_sizes[0] == in_bit_sizes[1]);
+      /* The type of Operand 1 and Operand 2 must be a scalar or vector of floating-point type. */
+      SpvOp op = in_bit_sizes[0] == 1 ? SpvOpLogicalNotEqual : SpvOpFOrdNotEqual;
+      result = emit_binop(ctx, op,
+                          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));
+      assert(in_bit_sizes[0] == in_bit_sizes[1]);
+      /* The type of Operand 1 and Operand 2 must be a scalar or vector of floating-point type. */
+      SpvOp op = in_bit_sizes[0] == 1 ? SpvOpLogicalEqual : SpvOpFOrdEqual;
+      result = emit_binop(ctx, op,
+                          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));
+      assert(in_bit_sizes[0] == in_bit_sizes[1]);
+      /* The type of Operand 1 and Operand 2 must be a scalar or vector of integer type. */
+      SpvOp op = in_bit_sizes[0] == 1 ? SpvOpLogicalNotEqual : SpvOpINotEqual;
+      result = emit_binop(ctx, op,
+                          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));
+      assert(in_bit_sizes[0] == in_bit_sizes[1]);
+      /* The type of Operand 1 and Operand 2 must be a scalar or vector of integer type. */
+      SpvOp op = in_bit_sizes[0] == 1 ? SpvOpLogicalEqual : SpvOpIEqual;
+      result = emit_binop(ctx, op,
+                          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: {
@@ -906,39 +1491,38 @@ 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;
-
-   SpvId constant = get_uvec_constant(ctx, load_const->def.bit_size,
-                                           load_const->def.num_components,
-                                           values);
-   store_ssa_def_uint(ctx, &load_const->def, constant);
-}
-
-static void
-emit_load_input(struct ntv_context *ctx, nir_intrinsic_instr *intr)
-{
-   nir_const_value *const_offset = nir_src_as_const_value(intr->src[0]);
-   if (const_offset) {
-      int driver_location = (int)nir_intrinsic_base(intr) + const_offset->u32;
-      assert(driver_location < PIPE_MAX_SHADER_INPUTS);
-      int location_frac = nir_intrinsic_component(intr);
-      assert(location_frac < 4);
-
-      SpvId ptr = ctx->inputs[driver_location][location_frac];
-      SpvId type = ctx->input_types[driver_location][location_frac];
-      assert(ptr && type);
-
-      SpvId result = spirv_builder_emit_load(&ctx->builder, type, ptr);
+   unsigned bit_size = load_const->def.bit_size;
+   unsigned num_components = load_const->def.num_components;
 
-      unsigned num_components = nir_dest_num_components(intr->dest);
-      unsigned bit_size = nir_dest_bit_size(intr->dest);
-      result = bitcast_to_uvec(ctx, result, bit_size, num_components);
+   SpvId constant;
+   if (num_components > 1) {
+      SpvId components[num_components];
+      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);
+
+         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);
+      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);
+   }
 
-      store_dest_uint(ctx, &intr->dest, result);
-   } else
-      unreachable("input-addressing not yet supported");
+   store_ssa_def(ctx, &load_const->def, constant);
 }
 
 static void
@@ -956,8 +1540,8 @@ emit_load_ubo(struct ntv_context *ctx, nir_intrinsic_instr *intr)
                                                       uvec4_type);
 
       unsigned idx = const_offset->u32;
-      SpvId member = spirv_builder_const_uint(&ctx->builder, 32, 0);
-      SpvId offset = spirv_builder_const_uint(&ctx->builder, 32, idx);
+      SpvId member = emit_uint_const(ctx, 32, 0);
+      SpvId offset = emit_uint_const(ctx, 32, idx);
       SpvId offsets[] = { member, offset };
       SpvId ptr = spirv_builder_emit_access_chain(&ctx->builder, pointer_type,
                                                   ctx->ubos[0], offsets,
@@ -987,30 +1571,12 @@ emit_load_ubo(struct ntv_context *ctx, nir_intrinsic_instr *intr)
                                                          num_components);
       }
 
-      store_dest_uint(ctx, &intr->dest, result);
-   } else
-      unreachable("uniform-addressing not yet supported");
-}
+      if (nir_dest_bit_size(intr->dest) == 1)
+         result = uvec_to_bvec(ctx, result, num_components);
 
-static void
-emit_store_output(struct ntv_context *ctx, nir_intrinsic_instr *intr)
-{
-   nir_const_value *const_offset = nir_src_as_const_value(intr->src[1]);
-   if (const_offset) {
-      int driver_location = (int)nir_intrinsic_base(intr) + const_offset->u32;
-      assert(driver_location < PIPE_MAX_SHADER_OUTPUTS);
-      int location_frac = nir_intrinsic_component(intr);
-      assert(location_frac < 4);
-
-      SpvId ptr = ctx->outputs[driver_location][location_frac];
-      assert(ptr > 0);
-
-      SpvId src = get_src_uint(ctx, &intr->src[0]);
-      SpvId spirv_type = ctx->output_types[driver_location][location_frac];
-      SpvId result = emit_unop(ctx, SpvOpBitcast, spirv_type, src);
-      spirv_builder_emit_store(&ctx->builder, ptr, result);
+      store_dest(ctx, &intr->dest, result, nir_type_uint);
    } else
-      unreachable("output-addressing not yet supported");
+      unreachable("uniform-addressing not yet supported");
 }
 
 static void
@@ -1023,26 +1589,131 @@ emit_discard(struct ntv_context *ctx, nir_intrinsic_instr *intr)
    spirv_builder_label(&ctx->builder, spirv_builder_new_id(&ctx->builder));
 }
 
+static void
+emit_load_deref(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+{
+   SpvId ptr = get_src(ctx, intr->src);
+
+   nir_variable *var = nir_intrinsic_get_var(intr, 0);
+   SpvId result = spirv_builder_emit_load(&ctx->builder,
+                                          get_glsl_type(ctx, var->type),
+                                          ptr);
+   unsigned num_components = nir_dest_num_components(intr->dest);
+   unsigned bit_size = nir_dest_bit_size(intr->dest);
+   result = bitcast_to_uvec(ctx, result, bit_size, num_components);
+   store_dest(ctx, &intr->dest, result, nir_type_uint);
+}
+
+static void
+emit_store_deref(struct ntv_context *ctx, nir_intrinsic_instr *intr)
+{
+   SpvId ptr = get_src(ctx, &intr->src[0]);
+   SpvId src = get_src(ctx, &intr->src[1]);
+
+   nir_variable *var = nir_intrinsic_get_var(intr, 0);
+   SpvId type = get_glsl_type(ctx, glsl_without_array(var->type));
+   SpvId result = emit_bitcast(ctx, type, src);
+   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 = 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);
+   assert(1 == nir_dest_num_components(intr->dest));
+   store_dest(ctx, &intr->dest, result, nir_type_bool);
+}
+
+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(ctx, &intr->dest, result, nir_type_uint);
+}
+
+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(ctx, &intr->dest, result, nir_type_uint);
+}
+
 static void
 emit_intrinsic(struct ntv_context *ctx, nir_intrinsic_instr *intr)
 {
    switch (intr->intrinsic) {
-   case nir_intrinsic_load_input:
-      emit_load_input(ctx, intr);
-      break;
-
    case nir_intrinsic_load_ubo:
       emit_load_ubo(ctx, intr);
       break;
 
-   case nir_intrinsic_store_output:
-      emit_store_output(ctx, intr);
-      break;
-
    case nir_intrinsic_discard:
       emit_discard(ctx, intr);
       break;
 
+   case nir_intrinsic_load_deref:
+      emit_load_deref(ctx, intr);
+      break;
+
+   case nir_intrinsic_store_deref:
+      emit_store_deref(ctx, intr);
+      break;
+
+   case nir_intrinsic_load_front_face:
+      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);
@@ -1056,46 +1727,100 @@ emit_undef(struct ntv_context *ctx, nir_ssa_undef_instr *undef)
    SpvId type = get_uvec_type(ctx, undef->def.bit_size,
                               undef->def.num_components);
 
-   store_ssa_def_uint(ctx, &undef->def,
-                      spirv_builder_emit_undef(&ctx->builder, type));
+   store_ssa_def(ctx, &undef->def,
+                 spirv_builder_emit_undef(&ctx->builder, type));
 }
 
 static SpvId
 get_src_float(struct ntv_context *ctx, nir_src *src)
 {
-   SpvId def = get_src_uint(ctx, src);
+   SpvId def = get_src(ctx, src);
    unsigned num_components = nir_src_num_components(*src);
    unsigned bit_size = nir_src_bit_size(*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(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);
-   assert(nir_alu_type_get_base_type(tex->dest_type) == nir_type_float);
+   assert(tex->op == nir_texop_tex ||
+          tex->op == nir_texop_txb ||
+          tex->op == nir_texop_txl ||
+          tex->op == nir_texop_txd ||
+          tex->op == nir_texop_txf ||
+          tex->op == nir_texop_txf_ms ||
+          tex->op == nir_texop_txs);
    assert(tex->texture_index == tex->sampler_index);
 
-   bool has_proj = false, has_lod = false;
-   SpvId coord = 0, proj, lod;
-   unsigned coord_components;
+   SpvId coord = 0, proj = 0, bias = 0, lod = 0, dref = 0, dx = 0, dy = 0,
+         offset = 0, sample = 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 ||
+             tex->op == nir_texop_txf_ms)
+            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;
 
       case nir_tex_src_projector:
-         has_proj = true;
-         proj = get_src_float(ctx, &tex->src[i].src);
          assert(nir_src_num_components(tex->src[i].src) == 1);
+         proj = get_src_float(ctx, &tex->src[i].src);
+         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);
+         assert(bias != 0);
          break;
 
       case nir_tex_src_lod:
-         has_lod = true;
-         lod = get_src_float(ctx, &tex->src[i].src);
          assert(nir_src_num_components(tex->src[i].src) == 1);
+         if (tex->op == nir_texop_txf ||
+             tex->op == nir_texop_txf_ms ||
+             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;
+
+      case nir_tex_src_ms_index:
+         assert(nir_src_num_components(tex->src[i].src) == 1);
+         sample = get_src_int(ctx, &tex->src[i].src);
+         break;
+
+      case nir_tex_src_comparator:
+         assert(nir_src_num_components(tex->src[i].src) == 1);
+         dref = get_src_float(ctx, &tex->src[i].src);
+         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:
@@ -1104,70 +1829,83 @@ emit_tex(struct ntv_context *ctx, nir_tex_instr *tex)
       }
    }
 
-   if (!has_lod && ctx->stage != MESA_SHADER_FRAGMENT) {
-      has_lod = true;
-      lod = spirv_builder_const_float(&ctx->builder, 32, 0);
+   if (lod == 0 && ctx->stage != MESA_SHADER_FRAGMENT) {
+      lod = emit_float_const(ctx, 32, 0.0f);
+      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);
 
-   assert(tex->texture_index < ctx->num_samplers);
+   assert(ctx->samplers_used & (1u << tex->texture_index));
    SpvId load = spirv_builder_emit_load(&ctx->builder, sampled_type,
                                         ctx->samplers[tex->texture_index]);
 
    SpvId dest_type = get_dest_type(ctx, &tex->dest, tex->dest_type);
 
-   SpvId result;
-   if (has_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];
-      SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
-      for (uint32_t i = 0; i < coord_components; ++i)
-         constituents[i] = spirv_builder_emit_composite_extract(&ctx->builder,
-                                              float_type,
-                                              coord,
-                                              &i, 1);
+      if (coord_components == 1)
+         constituents[0] = coord;
+      else {
+         assert(coord_components > 1);
+         SpvId float_type = spirv_builder_type_float(&ctx->builder, 32);
+         for (uint32_t i = 0; i < coord_components; ++i)
+            constituents[i] = spirv_builder_emit_composite_extract(&ctx->builder,
+                                                 float_type,
+                                                 coord,
+                                                 &i, 1);
+      }
 
       constituents[coord_components++] = proj;
 
       SpvId vec_type = get_fvec_type(ctx, 32, coord_components);
-      SpvId merged = spirv_builder_emit_composite_construct(&ctx->builder,
+      coord = spirv_builder_emit_composite_construct(&ctx->builder,
                                                             vec_type,
                                                             constituents,
                                                             coord_components);
+   }
 
-      if (has_lod)
-         result = spirv_builder_emit_image_sample_proj_explicit_lod(&ctx->builder,
-                                                                    dest_type,
-                                                                    load,
-                                                                    merged,
-                                                                    lod);
-      else
-         result = spirv_builder_emit_image_sample_proj_implicit_lod(&ctx->builder,
-                                                                    dest_type,
-                                                                    load,
-                                                                    merged);
+   SpvId actual_dest_type = dest_type;
+   if (dref)
+      actual_dest_type = spirv_builder_type_float(&ctx->builder, 32);
+
+   SpvId result;
+   if (tex->op == nir_texop_txf ||
+       tex->op == nir_texop_txf_ms) {
+      SpvId image = spirv_builder_emit_image(&ctx->builder, image_type, load);
+      result = spirv_builder_emit_image_fetch(&ctx->builder, dest_type,
+                                              image, coord, lod, sample);
    } else {
-      if (has_lod)
-         result = spirv_builder_emit_image_sample_explicit_lod(&ctx->builder,
-                                                               dest_type,
-                                                               load,
-                                                               coord, lod);
-      else
-         result = spirv_builder_emit_image_sample_implicit_lod(&ctx->builder,
-                                                               dest_type,
-                                                               load,
-                                                               coord);
+      result = spirv_builder_emit_image_sample(&ctx->builder,
+                                               actual_dest_type, load,
+                                               coord,
+                                               proj != 0,
+                                               lod, bias, dref, dx, dy,
+                                               offset);
    }
+
    spirv_builder_emit_decoration(&ctx->builder, result,
                                  SpvDecorationRelaxedPrecision);
 
+   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,
+                                                      components,
+                                                      4);
+   }
+
    store_dest(ctx, &tex->dest, result, tex->dest_type);
 }
 
@@ -1220,6 +1958,68 @@ emit_jump(struct ntv_context *ctx, nir_jump_instr *jump)
    }
 }
 
+static void
+emit_deref_var(struct ntv_context *ctx, nir_deref_instr *deref)
+{
+   assert(deref->deref_type == nir_deref_type_var);
+
+   struct hash_entry *he = _mesa_hash_table_search(ctx->vars, deref->var);
+   assert(he);
+   SpvId result = (SpvId)(intptr_t)he->data;
+   store_dest_raw(ctx, &deref->dest, result);
+}
+
+static void
+emit_deref_array(struct ntv_context *ctx, nir_deref_instr *deref)
+{
+   assert(deref->deref_type == nir_deref_type_array);
+   nir_variable *var = nir_deref_instr_get_variable(deref);
+
+   SpvStorageClass storage_class;
+   switch (var->data.mode) {
+   case nir_var_shader_in:
+      storage_class = SpvStorageClassInput;
+      break;
+
+   case nir_var_shader_out:
+      storage_class = SpvStorageClassOutput;
+      break;
+
+   default:
+      unreachable("Unsupported nir_variable_mode\n");
+   }
+
+   SpvId index = get_src(ctx, &deref->arr.index);
+
+   SpvId ptr_type = spirv_builder_type_pointer(&ctx->builder,
+                                               storage_class,
+                                               get_glsl_type(ctx, deref->type));
+
+   SpvId result = spirv_builder_emit_access_chain(&ctx->builder,
+                                                  ptr_type,
+                                                  get_src(ctx, &deref->parent),
+                                                  &index, 1);
+   /* uint is a bit of a lie here, it's really just an opaque type */
+   store_dest(ctx, &deref->dest, result, nir_type_uint);
+}
+
+static void
+emit_deref(struct ntv_context *ctx, nir_deref_instr *deref)
+{
+   switch (deref->deref_type) {
+   case nir_deref_type_var:
+      emit_deref_var(ctx, deref);
+      break;
+
+   case nir_deref_type_array:
+      emit_deref_array(ctx, deref);
+      break;
+
+   default:
+      unreachable("unexpected deref_type");
+   }
+}
+
 static void
 emit_block(struct ntv_context *ctx, struct nir_block *block)
 {
@@ -1254,7 +2054,7 @@ emit_block(struct ntv_context *ctx, struct nir_block *block)
          unreachable("nir_instr_type_parallel_copy not supported");
          break;
       case nir_instr_type_deref:
-         unreachable("nir_instr_type_deref not supported");
+         emit_deref(ctx, nir_instr_as_deref(instr));
          break;
       }
    }
@@ -1266,10 +2066,8 @@ emit_cf_list(struct ntv_context *ctx, struct exec_list *list);
 static SpvId
 get_src_bool(struct ntv_context *ctx, nir_src *src)
 {
-   SpvId def = get_src_uint(ctx, src);
-   assert(nir_src_bit_size(*src) == 32);
-   unsigned num_components = nir_src_num_components(*src);
-   return uvec_to_bvec(ctx, def, num_components);
+   assert(nir_src_bit_size(*src) == 1);
+   return get_src(ctx, src);
 }
 
 static void
@@ -1361,7 +2159,7 @@ emit_cf_list(struct ntv_context *ctx, struct exec_list *list)
 }
 
 struct spirv_shader *
-nir_to_spirv(struct nir_shader *s)
+nir_to_spirv(struct nir_shader *s, const struct pipe_stream_output_info *so_info, struct pipe_stream_output_info *local_so_info)
 {
    struct spirv_shader *ret = NULL;
 
@@ -1387,6 +2185,13 @@ nir_to_spirv(struct nir_shader *s)
       unreachable("invalid stage");
    }
 
+   // TODO: only enable when needed
+   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");
    spirv_builder_emit_source(&ctx.builder, SpvSourceLanguageGLSL, 450);
@@ -1424,22 +2229,36 @@ nir_to_spirv(struct nir_shader *s)
    SpvId entry_point = spirv_builder_new_id(&ctx.builder);
    spirv_builder_emit_name(&ctx.builder, entry_point, "main");
 
+   ctx.vars = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
+                                      _mesa_key_pointer_equal);
+
+   ctx.so_outputs = _mesa_hash_table_create(NULL, _mesa_hash_u32,
+                                            _mesa_key_u32_equal);
+
    nir_foreach_variable(var, &s->inputs)
       emit_input(&ctx, var);
 
    nir_foreach_variable(var, &s->outputs)
       emit_output(&ctx, var);
 
+   if (so_info)
+      emit_so_info(&ctx, util_last_bit64(s->info.outputs_written), so_info, local_so_info);
    nir_foreach_variable(var, &s->uniforms)
       emit_uniform(&ctx, var);
 
-   spirv_builder_emit_entry_point(&ctx.builder, exec_model, entry_point,
-                                  "main", ctx.entry_ifaces,
-                                  ctx.num_entry_ifaces);
-   if (s->info.stage == MESA_SHADER_FRAGMENT)
+   if (s->info.stage == MESA_SHADER_FRAGMENT) {
       spirv_builder_emit_exec_mode(&ctx.builder, entry_point,
                                    SpvExecutionModeOriginUpperLeft);
+      if (s->info.outputs_written & BITFIELD64_BIT(FRAG_RESULT_DEPTH))
+         spirv_builder_emit_exec_mode(&ctx.builder, entry_point,
+                                      SpvExecutionModeDepthReplacing);
+   }
 
+   if (so_info && so_info->num_outputs) {
+      spirv_builder_emit_cap(&ctx.builder, SpvCapabilityTransformFeedback);
+      spirv_builder_emit_exec_mode(&ctx.builder, entry_point,
+                                   SpvExecutionModeXfb);
+   }
 
    spirv_builder_function(&ctx.builder, entry_point, type_void,
                                             SpvFunctionControlMaskNone,
@@ -1453,10 +2272,11 @@ nir_to_spirv(struct nir_shader *s)
       goto fail;
    ctx.num_defs = entry->ssa_alloc;
 
-   ctx.vars = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
-                                            _mesa_key_pointer_equal);
-   if (!ctx.vars)
+   nir_index_local_regs(entry);
+   ctx.regs = malloc(sizeof(SpvId) * entry->reg_alloc);
+   if (!ctx.regs)
       goto fail;
+   ctx.num_regs = entry->reg_alloc;
 
    SpvId *block_ids = (SpvId *)malloc(sizeof(SpvId) * entry->num_blocks);
    if (!block_ids)
@@ -1478,17 +2298,23 @@ nir_to_spirv(struct nir_shader *s)
       SpvId var = spirv_builder_emit_var(&ctx.builder, pointer_type,
                                          SpvStorageClassFunction);
 
-      if (!_mesa_hash_table_insert(ctx.vars, reg, (void *)(intptr_t)var))
-         goto fail;
+      ctx.regs[reg->index] = var;
    }
 
    emit_cf_list(&ctx, &entry->body);
 
    free(ctx.defs);
 
+   if (so_info)
+      emit_so_outputs(&ctx, so_info, local_so_info);
+
    spirv_builder_return(&ctx.builder); // doesn't belong here, but whatevz
    spirv_builder_function_end(&ctx.builder);
 
+   spirv_builder_emit_entry_point(&ctx.builder, exec_model, entry_point,
+                                  "main", ctx.entry_ifaces,
+                                  ctx.num_entry_ifaces);
+
    size_t num_words = spirv_builder_get_num_words(&ctx.builder);
 
    ret = CALLOC_STRUCT(spirv_shader);
@@ -1509,6 +2335,12 @@ fail:
    if (ret)
       spirv_shader_delete(ret);
 
+   if (ctx.vars)
+      _mesa_hash_table_destroy(ctx.vars, NULL);
+
+   if (ctx.so_outputs)
+      _mesa_hash_table_destroy(ctx.so_outputs, NULL);
+
    return NULL;
 }