/*
 * Mesa 3-D graphics library
 *
 * Copyright (C) 2012-2013 LunarG, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Chia-I Wu <olv@lunarg.com>
 */

#include "genhw/genhw.h" /* for SBE setup */
#include "tgsi/tgsi_parse.h"
#include "intel_winsys.h"

#include "shader/ilo_shader_internal.h"
#include "ilo_builder.h"
#include "ilo_state.h"
#include "ilo_state_3d.h"
#include "ilo_shader.h"

struct ilo_shader_cache {
   struct list_head shaders;
   struct list_head changed;
};

/**
 * Create a shader cache.  A shader cache can manage shaders and upload them
 * to a bo as a whole.
 */
struct ilo_shader_cache *
ilo_shader_cache_create(void)
{
   struct ilo_shader_cache *shc;

   shc = CALLOC_STRUCT(ilo_shader_cache);
   if (!shc)
      return NULL;

   list_inithead(&shc->shaders);
   list_inithead(&shc->changed);

   return shc;
}

/**
 * Destroy a shader cache.
 */
void
ilo_shader_cache_destroy(struct ilo_shader_cache *shc)
{
   FREE(shc);
}

/**
 * Add a shader to the cache.
 */
void
ilo_shader_cache_add(struct ilo_shader_cache *shc,
                     struct ilo_shader_state *shader)
{
   struct ilo_shader *sh;

   shader->cache = shc;
   LIST_FOR_EACH_ENTRY(sh, &shader->variants, list)
      sh->uploaded = false;

   list_add(&shader->list, &shc->changed);
}

/**
 * Remove a shader from the cache.
 */
void
ilo_shader_cache_remove(struct ilo_shader_cache *shc,
                        struct ilo_shader_state *shader)
{
   list_del(&shader->list);
   shader->cache = NULL;
}

/**
 * Notify the cache that a managed shader has changed.
 */
static void
ilo_shader_cache_notify_change(struct ilo_shader_cache *shc,
                               struct ilo_shader_state *shader)
{
   if (shader->cache == shc) {
      list_del(&shader->list);
      list_add(&shader->list, &shc->changed);
   }
}

/**
 * Upload managed shaders to the bo.  Only shaders that are changed or added
 * after the last upload are uploaded.
 */
void
ilo_shader_cache_upload(struct ilo_shader_cache *shc,
                        struct ilo_builder *builder)
{
   struct ilo_shader_state *shader, *next;

   LIST_FOR_EACH_ENTRY_SAFE(shader, next, &shc->changed, list) {
      struct ilo_shader *sh;

      LIST_FOR_EACH_ENTRY(sh, &shader->variants, list) {
         if (sh->uploaded)
            continue;

         sh->cache_offset = ilo_builder_instruction_write(builder,
               sh->kernel_size, sh->kernel);

         sh->uploaded = true;
      }

      list_del(&shader->list);
      list_add(&shader->list, &shc->shaders);
   }
}

/**
 * Invalidate all shaders so that they get uploaded in next
 * ilo_shader_cache_upload().
 */
void
ilo_shader_cache_invalidate(struct ilo_shader_cache *shc)
{
   struct ilo_shader_state *shader, *next;

   LIST_FOR_EACH_ENTRY_SAFE(shader, next, &shc->shaders, list) {
      list_del(&shader->list);
      list_add(&shader->list, &shc->changed);
   }

   LIST_FOR_EACH_ENTRY(shader, &shc->changed, list) {
      struct ilo_shader *sh;

      LIST_FOR_EACH_ENTRY(sh, &shader->variants, list)
         sh->uploaded = false;
   }
}

/**
 * Initialize a shader variant.
 */
void
ilo_shader_variant_init(struct ilo_shader_variant *variant,
                        const struct ilo_shader_info *info,
                        const struct ilo_state_vector *vec)
{
   int num_views, i;

   memset(variant, 0, sizeof(*variant));

   switch (info->type) {
   case PIPE_SHADER_VERTEX:
      variant->u.vs.rasterizer_discard =
         vec->rasterizer->state.rasterizer_discard;
      variant->u.vs.num_ucps =
         util_last_bit(vec->rasterizer->state.clip_plane_enable);
      break;
   case PIPE_SHADER_GEOMETRY:
      variant->u.gs.rasterizer_discard =
         vec->rasterizer->state.rasterizer_discard;
      variant->u.gs.num_inputs = vec->vs->shader->out.count;
      for (i = 0; i < vec->vs->shader->out.count; i++) {
         variant->u.gs.semantic_names[i] =
            vec->vs->shader->out.semantic_names[i];
         variant->u.gs.semantic_indices[i] =
            vec->vs->shader->out.semantic_indices[i];
      }
      break;
   case PIPE_SHADER_FRAGMENT:
      variant->u.fs.flatshade =
         (info->has_color_interp && vec->rasterizer->state.flatshade);
      variant->u.fs.fb_height = (info->has_pos) ?
         vec->fb.state.height : 1;
      variant->u.fs.num_cbufs = vec->fb.state.nr_cbufs;
      break;
   default:
      assert(!"unknown shader type");
      break;
   }

   /* use PCB unless constant buffer 0 is not in user buffer  */
   if ((vec->cbuf[info->type].enabled_mask & 0x1) &&
       !vec->cbuf[info->type].cso[0].user_buffer)
      variant->use_pcb = false;
   else
      variant->use_pcb = true;

   num_views = vec->view[info->type].count;
   assert(info->num_samplers <= num_views);

   variant->num_sampler_views = info->num_samplers;
   for (i = 0; i < info->num_samplers; i++) {
      const struct pipe_sampler_view *view = vec->view[info->type].states[i];
      const struct ilo_sampler_cso *sampler = vec->sampler[info->type].cso[i];

      if (view) {
         variant->sampler_view_swizzles[i].r = view->swizzle_r;
         variant->sampler_view_swizzles[i].g = view->swizzle_g;
         variant->sampler_view_swizzles[i].b = view->swizzle_b;
         variant->sampler_view_swizzles[i].a = view->swizzle_a;
      }
      else if (info->shadow_samplers & (1 << i)) {
         variant->sampler_view_swizzles[i].r = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].g = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].b = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].a = PIPE_SWIZZLE_ONE;
      }
      else {
         variant->sampler_view_swizzles[i].r = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].g = PIPE_SWIZZLE_GREEN;
         variant->sampler_view_swizzles[i].b = PIPE_SWIZZLE_BLUE;
         variant->sampler_view_swizzles[i].a = PIPE_SWIZZLE_ALPHA;
      }

      /*
       * When non-nearest filter and PIPE_TEX_WRAP_CLAMP wrap mode is used,
       * the HW wrap mode is set to GEN6_TEXCOORDMODE_CLAMP_BORDER, and we
       * need to manually saturate the texture coordinates.
       */
      if (sampler) {
         variant->saturate_tex_coords[0] |= sampler->saturate_s << i;
         variant->saturate_tex_coords[1] |= sampler->saturate_t << i;
         variant->saturate_tex_coords[2] |= sampler->saturate_r << i;
      }
   }
}

/**
 * Guess the shader variant, knowing that the context may still change.
 */
static void
ilo_shader_variant_guess(struct ilo_shader_variant *variant,
                         const struct ilo_shader_info *info,
                         const struct ilo_state_vector *vec)
{
   int i;

   memset(variant, 0, sizeof(*variant));

   switch (info->type) {
   case PIPE_SHADER_VERTEX:
      break;
   case PIPE_SHADER_GEOMETRY:
      break;
   case PIPE_SHADER_FRAGMENT:
      variant->u.fs.flatshade = false;
      variant->u.fs.fb_height = (info->has_pos) ?
         vec->fb.state.height : 1;
      variant->u.fs.num_cbufs = 1;
      break;
   default:
      assert(!"unknown shader type");
      break;
   }

   variant->use_pcb = true;

   variant->num_sampler_views = info->num_samplers;
   for (i = 0; i < info->num_samplers; i++) {
      if (info->shadow_samplers & (1 << i)) {
         variant->sampler_view_swizzles[i].r = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].g = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].b = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].a = PIPE_SWIZZLE_ONE;
      }
      else {
         variant->sampler_view_swizzles[i].r = PIPE_SWIZZLE_RED;
         variant->sampler_view_swizzles[i].g = PIPE_SWIZZLE_GREEN;
         variant->sampler_view_swizzles[i].b = PIPE_SWIZZLE_BLUE;
         variant->sampler_view_swizzles[i].a = PIPE_SWIZZLE_ALPHA;
      }
   }
}


/**
 * Parse a TGSI instruction for the shader info.
 */
static void
ilo_shader_info_parse_inst(struct ilo_shader_info *info,
                           const struct tgsi_full_instruction *inst)
{
   int i;

   /* look for edgeflag passthrough */
   if (info->edgeflag_out >= 0 &&
       inst->Instruction.Opcode == TGSI_OPCODE_MOV &&
       inst->Dst[0].Register.File == TGSI_FILE_OUTPUT &&
       inst->Dst[0].Register.Index == info->edgeflag_out) {

      assert(inst->Src[0].Register.File == TGSI_FILE_INPUT);
      info->edgeflag_in = inst->Src[0].Register.Index;
   }

   if (inst->Instruction.Texture) {
      bool shadow;

      switch (inst->Texture.Texture) {
      case TGSI_TEXTURE_SHADOW1D:
      case TGSI_TEXTURE_SHADOW2D:
      case TGSI_TEXTURE_SHADOWRECT:
      case TGSI_TEXTURE_SHADOW1D_ARRAY:
      case TGSI_TEXTURE_SHADOW2D_ARRAY:
      case TGSI_TEXTURE_SHADOWCUBE:
      case TGSI_TEXTURE_SHADOWCUBE_ARRAY:
         shadow = true;
         break;
      default:
         shadow = false;
         break;
      }

      for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
         const struct tgsi_full_src_register *src = &inst->Src[i];

         if (src->Register.File == TGSI_FILE_SAMPLER) {
            const int idx = src->Register.Index;

            if (idx >= info->num_samplers)
               info->num_samplers = idx + 1;

            if (shadow)
               info->shadow_samplers |= 1 << idx;
         }
      }
   }
}

/**
 * Parse a TGSI property for the shader info.
 */
static void
ilo_shader_info_parse_prop(struct ilo_shader_info *info,
                           const struct tgsi_full_property *prop)
{
   switch (prop->Property.PropertyName) {
   case TGSI_PROPERTY_FS_COLOR0_WRITES_ALL_CBUFS:
      info->fs_color0_writes_all_cbufs = prop->u[0].Data;
      break;
   default:
      break;
   }
}

/**
 * Parse a TGSI declaration for the shader info.
 */
static void
ilo_shader_info_parse_decl(struct ilo_shader_info *info,
                           const struct tgsi_full_declaration *decl)
{
   switch (decl->Declaration.File) {
   case TGSI_FILE_INPUT:
      if (decl->Declaration.Interpolate &&
          decl->Interp.Interpolate == TGSI_INTERPOLATE_COLOR)
         info->has_color_interp = true;
      if (decl->Declaration.Semantic &&
          decl->Semantic.Name == TGSI_SEMANTIC_POSITION)
         info->has_pos = true;
      break;
   case TGSI_FILE_OUTPUT:
      if (decl->Declaration.Semantic &&
          decl->Semantic.Name == TGSI_SEMANTIC_EDGEFLAG)
         info->edgeflag_out = decl->Range.First;
      break;
   case TGSI_FILE_CONSTANT:
      {
         const int idx = (decl->Declaration.Dimension) ?
            decl->Dim.Index2D : 0;
         if (info->constant_buffer_count <= idx)
            info->constant_buffer_count = idx + 1;
      }
      break;
   case TGSI_FILE_SYSTEM_VALUE:
      if (decl->Declaration.Semantic &&
          decl->Semantic.Name == TGSI_SEMANTIC_INSTANCEID)
         info->has_instanceid = true;
      if (decl->Declaration.Semantic &&
          decl->Semantic.Name == TGSI_SEMANTIC_VERTEXID)
         info->has_vertexid = true;
      break;
   default:
      break;
   }
}

static void
ilo_shader_info_parse_tokens(struct ilo_shader_info *info)
{
   struct tgsi_parse_context parse;

   info->edgeflag_in = -1;
   info->edgeflag_out = -1;

   tgsi_parse_init(&parse, info->tokens);
   while (!tgsi_parse_end_of_tokens(&parse)) {
      const union tgsi_full_token *token;

      tgsi_parse_token(&parse);
      token = &parse.FullToken;

      switch (token->Token.Type) {
      case TGSI_TOKEN_TYPE_DECLARATION:
         ilo_shader_info_parse_decl(info, &token->FullDeclaration);
         break;
      case TGSI_TOKEN_TYPE_INSTRUCTION:
         ilo_shader_info_parse_inst(info, &token->FullInstruction);
         break;
      case TGSI_TOKEN_TYPE_PROPERTY:
         ilo_shader_info_parse_prop(info, &token->FullProperty);
         break;
      default:
         break;
      }
   }
   tgsi_parse_free(&parse);
}

/**
 * Create a shader state.
 */
static struct ilo_shader_state *
ilo_shader_state_create(const struct ilo_dev_info *dev,
                        const struct ilo_state_vector *vec,
                        int type, const void *templ)
{
   struct ilo_shader_state *state;
   struct ilo_shader_variant variant;

   state = CALLOC_STRUCT(ilo_shader_state);
   if (!state)
      return NULL;

   state->info.dev = dev;
   state->info.type = type;

   if (type == PIPE_SHADER_COMPUTE) {
      const struct pipe_compute_state *c =
         (const struct pipe_compute_state *) templ;

      state->info.tokens = tgsi_dup_tokens(c->prog);
      state->info.compute.req_local_mem = c->req_local_mem;
      state->info.compute.req_private_mem = c->req_private_mem;
      state->info.compute.req_input_mem = c->req_input_mem;
   }
   else {
      const struct pipe_shader_state *s =
         (const struct pipe_shader_state *) templ;

      state->info.tokens = tgsi_dup_tokens(s->tokens);
      state->info.stream_output = s->stream_output;
   }

   list_inithead(&state->variants);

   ilo_shader_info_parse_tokens(&state->info);

   /* guess and compile now */
   ilo_shader_variant_guess(&variant, &state->info, vec);
   if (!ilo_shader_state_use_variant(state, &variant)) {
      ilo_shader_destroy(state);
      return NULL;
   }

   return state;
}

/**
 * Add a compiled shader to the shader state.
 */
static void
ilo_shader_state_add_shader(struct ilo_shader_state *state,
                            struct ilo_shader *sh)
{
   list_add(&sh->list, &state->variants);
   state->num_variants++;
   state->total_size += sh->kernel_size;

   if (state->cache)
      ilo_shader_cache_notify_change(state->cache, state);
}

/**
 * Remove a compiled shader from the shader state.
 */
static void
ilo_shader_state_remove_shader(struct ilo_shader_state *state,
                               struct ilo_shader *sh)
{
   list_del(&sh->list);
   state->num_variants--;
   state->total_size -= sh->kernel_size;
}

/**
 * Garbage collect shader variants in the shader state.
 */
static void
ilo_shader_state_gc(struct ilo_shader_state *state)
{
   /* activate when the variants take up more than 4KiB of space */
   const int limit = 4 * 1024;
   struct ilo_shader *sh, *next;

   if (state->total_size < limit)
      return;

   /* remove from the tail as the most recently ones are at the head */
   LIST_FOR_EACH_ENTRY_SAFE_REV(sh, next, &state->variants, list) {
      ilo_shader_state_remove_shader(state, sh);
      ilo_shader_destroy_kernel(sh);

      if (state->total_size <= limit / 2)
         break;
   }
}

/**
 * Search for a shader variant.
 */
static struct ilo_shader *
ilo_shader_state_search_variant(struct ilo_shader_state *state,
                                const struct ilo_shader_variant *variant)
{
   struct ilo_shader *sh = NULL, *tmp;

   LIST_FOR_EACH_ENTRY(tmp, &state->variants, list) {
      if (memcmp(&tmp->variant, variant, sizeof(*variant)) == 0) {
         sh = tmp;
         break;
      }
   }

   return sh;
}

static void
copy_so_info(struct ilo_shader *sh,
             const struct pipe_stream_output_info *so_info)
{
   unsigned i, attr;

   if (!so_info->num_outputs)
      return;

   sh->so_info = *so_info;

   for (i = 0; i < so_info->num_outputs; i++) {
      /* figure out which attribute is sourced */
      for (attr = 0; attr < sh->out.count; attr++) {
         const int reg_idx = sh->out.register_indices[attr];
         if (reg_idx == so_info->output[i].register_index)
            break;
      }

      if (attr < sh->out.count) {
         sh->so_info.output[i].register_index = attr;
      }
      else {
         assert(!"stream output an undefined register");
         sh->so_info.output[i].register_index = 0;
      }

      /* PSIZE is at W channel */
      if (sh->out.semantic_names[attr] == TGSI_SEMANTIC_PSIZE) {
         assert(so_info->output[i].start_component == 0);
         assert(so_info->output[i].num_components == 1);
         sh->so_info.output[i].start_component = 3;
      }
   }
}

/**
 * Add a shader variant to the shader state.
 */
static struct ilo_shader *
ilo_shader_state_add_variant(struct ilo_shader_state *state,
                             const struct ilo_shader_variant *variant)
{
   struct ilo_shader *sh;

   switch (state->info.type) {
   case PIPE_SHADER_VERTEX:
      sh = ilo_shader_compile_vs(state, variant);
      break;
   case PIPE_SHADER_FRAGMENT:
      sh = ilo_shader_compile_fs(state, variant);
      break;
   case PIPE_SHADER_GEOMETRY:
      sh = ilo_shader_compile_gs(state, variant);
      break;
   case PIPE_SHADER_COMPUTE:
      sh = ilo_shader_compile_cs(state, variant);
      break;
   default:
      sh = NULL;
      break;
   }
   if (!sh) {
      assert(!"failed to compile shader");
      return NULL;
   }

   sh->variant = *variant;

   copy_so_info(sh, &state->info.stream_output);

   ilo_shader_state_add_shader(state, sh);

   return sh;
}

/**
 * Update state->shader to point to a variant.  If the variant does not exist,
 * it will be added first.
 */
bool
ilo_shader_state_use_variant(struct ilo_shader_state *state,
                             const struct ilo_shader_variant *variant)
{
   struct ilo_shader *sh;
   bool construct_cso = false;

   sh = ilo_shader_state_search_variant(state, variant);
   if (!sh) {
      ilo_shader_state_gc(state);

      sh = ilo_shader_state_add_variant(state, variant);
      if (!sh)
         return false;

      construct_cso = true;
   }

   /* move to head */
   if (state->variants.next != &sh->list) {
      list_del(&sh->list);
      list_add(&sh->list, &state->variants);
   }

   state->shader = sh;

   if (construct_cso) {
      switch (state->info.type) {
      case PIPE_SHADER_VERTEX:
         ilo_gpe_init_vs_cso(state->info.dev, state, &sh->cso);
         break;
      case PIPE_SHADER_GEOMETRY:
         ilo_gpe_init_gs_cso(state->info.dev, state, &sh->cso);
         break;
      case PIPE_SHADER_FRAGMENT:
         ilo_gpe_init_fs_cso(state->info.dev, state, &sh->cso);
         break;
      default:
         break;
      }
   }

   return true;
}

struct ilo_shader_state *
ilo_shader_create_vs(const struct ilo_dev_info *dev,
                     const struct pipe_shader_state *state,
                     const struct ilo_state_vector *precompile)
{
   struct ilo_shader_state *shader;

   shader = ilo_shader_state_create(dev, precompile,
         PIPE_SHADER_VERTEX, state);

   /* states used in ilo_shader_variant_init() */
   shader->info.non_orthogonal_states = ILO_DIRTY_VIEW_VS |
                                        ILO_DIRTY_RASTERIZER |
                                        ILO_DIRTY_CBUF;

   return shader;
}

struct ilo_shader_state *
ilo_shader_create_gs(const struct ilo_dev_info *dev,
                     const struct pipe_shader_state *state,
                     const struct ilo_state_vector *precompile)
{
   struct ilo_shader_state *shader;

   shader = ilo_shader_state_create(dev, precompile,
         PIPE_SHADER_GEOMETRY, state);

   /* states used in ilo_shader_variant_init() */
   shader->info.non_orthogonal_states = ILO_DIRTY_VIEW_GS |
                                        ILO_DIRTY_VS |
                                        ILO_DIRTY_RASTERIZER |
                                        ILO_DIRTY_CBUF;

   return shader;
}

struct ilo_shader_state *
ilo_shader_create_fs(const struct ilo_dev_info *dev,
                     const struct pipe_shader_state *state,
                     const struct ilo_state_vector *precompile)
{
   struct ilo_shader_state *shader;

   shader = ilo_shader_state_create(dev, precompile,
         PIPE_SHADER_FRAGMENT, state);

   /* states used in ilo_shader_variant_init() */
   shader->info.non_orthogonal_states = ILO_DIRTY_VIEW_FS |
                                        ILO_DIRTY_RASTERIZER |
                                        ILO_DIRTY_FB |
                                        ILO_DIRTY_CBUF;

   return shader;
}

struct ilo_shader_state *
ilo_shader_create_cs(const struct ilo_dev_info *dev,
                     const struct pipe_compute_state *state,
                     const struct ilo_state_vector *precompile)
{
   struct ilo_shader_state *shader;

   shader = ilo_shader_state_create(dev, precompile,
         PIPE_SHADER_COMPUTE, state);

   shader->info.non_orthogonal_states = 0;

   return shader;
}

/**
 * Destroy a shader state.
 */
void
ilo_shader_destroy(struct ilo_shader_state *shader)
{
   struct ilo_shader *sh, *next;

   LIST_FOR_EACH_ENTRY_SAFE(sh, next, &shader->variants, list)
      ilo_shader_destroy_kernel(sh);

   FREE((struct tgsi_token *) shader->info.tokens);
   FREE(shader);
}

/**
 * Return the type (PIPE_SHADER_x) of the shader.
 */
int
ilo_shader_get_type(const struct ilo_shader_state *shader)
{
   return shader->info.type;
}

/**
 * Select a kernel for the given context.  This will compile a new kernel if
 * none of the existing kernels work with the context.
 *
 * \param ilo the context
 * \param dirty states of the context that are considered changed
 * \return true if a different kernel is selected
 */
bool
ilo_shader_select_kernel(struct ilo_shader_state *shader,
                         const struct ilo_state_vector *vec,
                         uint32_t dirty)
{
   const struct ilo_shader * const cur = shader->shader;
   struct ilo_shader_variant variant;

   if (!(shader->info.non_orthogonal_states & dirty))
      return false;

   ilo_shader_variant_init(&variant, &shader->info, vec);
   ilo_shader_state_use_variant(shader, &variant);

   return (shader->shader != cur);
}

static int
route_attr(const int *semantics, const int *indices, int len,
           int semantic, int index)
{
   int i;

   for (i = 0; i < len; i++) {
      if (semantics[i] == semantic && indices[i] == index)
         return i;
   }

   /* failed to match for COLOR, try BCOLOR */
   if (semantic == TGSI_SEMANTIC_COLOR) {
      for (i = 0; i < len; i++) {
         if (semantics[i] == TGSI_SEMANTIC_BCOLOR && indices[i] == index)
            return i;
      }
   }

   return -1;
}

/**
 * Select a routing for the given source shader and rasterizer state.
 *
 * \return true if a different routing is selected
 */
bool
ilo_shader_select_kernel_routing(struct ilo_shader_state *shader,
                                 const struct ilo_shader_state *source,
                                 const struct ilo_rasterizer_state *rasterizer)
{
   const uint32_t sprite_coord_enable = rasterizer->state.sprite_coord_enable;
   const bool light_twoside = rasterizer->state.light_twoside;
   struct ilo_shader *kernel = shader->shader;
   struct ilo_kernel_routing *routing = &kernel->routing;
   const int *src_semantics, *src_indices;
   int src_len, max_src_slot;
   int dst_len, dst_slot;

   /* we are constructing 3DSTATE_SBE here */
   assert(ilo_dev_gen(shader->info.dev) >= ILO_GEN(6) &&
          ilo_dev_gen(shader->info.dev) <= ILO_GEN(7.5));

   assert(kernel);

   if (source) {
      assert(source->shader);
      src_semantics = source->shader->out.semantic_names;
      src_indices = source->shader->out.semantic_indices;
      src_len = source->shader->out.count;
   }
   else {
      src_semantics = kernel->in.semantic_names;
      src_indices = kernel->in.semantic_indices;
      src_len = kernel->in.count;
   }

   /* no change */
   if (kernel->routing_initialized &&
       routing->source_skip + routing->source_len <= src_len &&
       kernel->routing_sprite_coord_enable == sprite_coord_enable &&
       !memcmp(kernel->routing_src_semantics,
          &src_semantics[routing->source_skip],
          sizeof(kernel->routing_src_semantics[0]) * routing->source_len) &&
       !memcmp(kernel->routing_src_indices,
          &src_indices[routing->source_skip],
          sizeof(kernel->routing_src_indices[0]) * routing->source_len))
      return false;

   if (source) {
      /* skip PSIZE and POSITION (how about the optional CLIPDISTs?) */
      assert(src_semantics[0] == TGSI_SEMANTIC_PSIZE);
      assert(src_semantics[1] == TGSI_SEMANTIC_POSITION);
      routing->source_skip = 2;

      routing->source_len = src_len - routing->source_skip;
      src_semantics += routing->source_skip;
      src_indices += routing->source_skip;
   }
   else {
      routing->source_skip = 0;
      routing->source_len = src_len;
   }

   routing->const_interp_enable = kernel->in.const_interp_enable;
   routing->point_sprite_enable = 0;
   routing->swizzle_enable = false;

   assert(kernel->in.count <= Elements(routing->swizzles));
   dst_len = MIN2(kernel->in.count, Elements(routing->swizzles));
   max_src_slot = -1;

   for (dst_slot = 0; dst_slot < dst_len; dst_slot++) {
      const int semantic = kernel->in.semantic_names[dst_slot];
      const int index = kernel->in.semantic_indices[dst_slot];
      int src_slot;

      if (semantic == TGSI_SEMANTIC_GENERIC &&
          (sprite_coord_enable & (1 << index)))
         routing->point_sprite_enable |= 1 << dst_slot;

      if (source) {
         src_slot = route_attr(src_semantics, src_indices,
               routing->source_len, semantic, index);

         /*
          * The source shader stage does not output this attribute.  The value
          * is supposed to be undefined, unless the attribute goes through
          * point sprite replacement or the attribute is
          * TGSI_SEMANTIC_POSITION.  In all cases, we do not care which source
          * attribute is picked.
          *
          * We should update the kernel code and omit the output of
          * TGSI_SEMANTIC_POSITION here.
          */
         if (src_slot < 0)
            src_slot = 0;
      }
      else {
         src_slot = dst_slot;
      }

      routing->swizzles[dst_slot] = src_slot;

      /* use the following slot for two-sided lighting */
      if (semantic == TGSI_SEMANTIC_COLOR && light_twoside &&
          src_slot + 1 < routing->source_len &&
          src_semantics[src_slot + 1] == TGSI_SEMANTIC_BCOLOR &&
          src_indices[src_slot + 1] == index) {
         routing->swizzles[dst_slot] |= GEN7_SBE_ATTR_INPUTATTR_FACING;
         src_slot++;
      }

      if (routing->swizzles[dst_slot] != dst_slot)
         routing->swizzle_enable = true;

      if (max_src_slot < src_slot)
         max_src_slot = src_slot;
   }

   memset(&routing->swizzles[dst_slot], 0, sizeof(routing->swizzles) -
         sizeof(routing->swizzles[0]) * dst_slot);

   /*
    * From the Sandy Bridge PRM, volume 2 part 1, page 248:
    *
    *     "It is UNDEFINED to set this field (Vertex URB Entry Read Length) to
    *      0 indicating no Vertex URB data to be read.
    *
    *      This field should be set to the minimum length required to read the
    *      maximum source attribute. The maximum source attribute is indicated
    *      by the maximum value of the enabled Attribute # Source Attribute if
    *      Attribute Swizzle Enable is set, Number of Output Attributes-1 if
    *      enable is not set.
    *
    *        read_length = ceiling((max_source_attr+1)/2)
    *
    *      [errata] Corruption/Hang possible if length programmed larger than
    *      recommended"
    */
   routing->source_len = max_src_slot + 1;

   /* remember the states of the source */
   kernel->routing_initialized = true;
   kernel->routing_sprite_coord_enable = sprite_coord_enable;
   memcpy(kernel->routing_src_semantics, src_semantics,
         sizeof(kernel->routing_src_semantics[0]) * routing->source_len);
   memcpy(kernel->routing_src_indices, src_indices,
         sizeof(kernel->routing_src_indices[0]) * routing->source_len);

   return true;
}

/**
 * Return the cache offset of the selected kernel.  This must be called after
 * ilo_shader_select_kernel() and ilo_shader_cache_upload().
 */
uint32_t
ilo_shader_get_kernel_offset(const struct ilo_shader_state *shader)
{
   const struct ilo_shader *kernel = shader->shader;

   assert(kernel && kernel->uploaded);

   return kernel->cache_offset;
}

/**
 * Query a kernel parameter for the selected kernel.
 */
int
ilo_shader_get_kernel_param(const struct ilo_shader_state *shader,
                            enum ilo_kernel_param param)
{
   const struct ilo_shader *kernel = shader->shader;
   int val;

   assert(kernel);

   switch (param) {
   case ILO_KERNEL_INPUT_COUNT:
      val = kernel->in.count;
      break;
   case ILO_KERNEL_OUTPUT_COUNT:
      val = kernel->out.count;
      break;
   case ILO_KERNEL_SAMPLER_COUNT:
      val = shader->info.num_samplers;
      break;
   case ILO_KERNEL_URB_DATA_START_REG:
      val = kernel->in.start_grf;
      break;
   case ILO_KERNEL_SKIP_CBUF0_UPLOAD:
      val = kernel->skip_cbuf0_upload;
      break;
   case ILO_KERNEL_PCB_CBUF0_SIZE:
      val = kernel->pcb.cbuf0_size;
      break;

   case ILO_KERNEL_SURFACE_TOTAL_COUNT:
      val = kernel->bt.total_count;
      break;
   case ILO_KERNEL_SURFACE_TEX_BASE:
      val = kernel->bt.tex_base;
      break;
   case ILO_KERNEL_SURFACE_TEX_COUNT:
      val = kernel->bt.tex_count;
      break;
   case ILO_KERNEL_SURFACE_CONST_BASE:
      val = kernel->bt.const_base;
      break;
   case ILO_KERNEL_SURFACE_CONST_COUNT:
      val = kernel->bt.const_count;
      break;
   case ILO_KERNEL_SURFACE_RES_BASE:
      val = kernel->bt.res_base;
      break;
   case ILO_KERNEL_SURFACE_RES_COUNT:
      val = kernel->bt.res_count;
      break;

   case ILO_KERNEL_VS_INPUT_INSTANCEID:
      val = shader->info.has_instanceid;
      break;
   case ILO_KERNEL_VS_INPUT_VERTEXID:
      val = shader->info.has_vertexid;
      break;
   case ILO_KERNEL_VS_INPUT_EDGEFLAG:
      if (shader->info.edgeflag_in >= 0) {
         /* we rely on the state tracker here */
         assert(shader->info.edgeflag_in == kernel->in.count - 1);
         val = true;
      }
      else {
         val = false;
      }
      break;
   case ILO_KERNEL_VS_PCB_UCP_SIZE:
      val = kernel->pcb.clip_state_size;
      break;
   case ILO_KERNEL_VS_GEN6_SO:
      val = kernel->stream_output;
      break;
   case ILO_KERNEL_VS_GEN6_SO_START_REG:
      val = kernel->gs_start_grf;
      break;
   case ILO_KERNEL_VS_GEN6_SO_POINT_OFFSET:
      val = kernel->gs_offsets[0];
      break;
   case ILO_KERNEL_VS_GEN6_SO_LINE_OFFSET:
      val = kernel->gs_offsets[1];
      break;
   case ILO_KERNEL_VS_GEN6_SO_TRI_OFFSET:
      val = kernel->gs_offsets[2];
      break;
   case ILO_KERNEL_VS_GEN6_SO_SURFACE_COUNT:
      val = kernel->gs_bt_so_count;
      break;

   case ILO_KERNEL_GS_DISCARD_ADJACENCY:
      val = kernel->in.discard_adj;
      break;
   case ILO_KERNEL_GS_GEN6_SVBI_POST_INC:
      val = kernel->svbi_post_inc;
      break;
   case ILO_KERNEL_GS_GEN6_SURFACE_SO_BASE:
      val = kernel->bt.gen6_so_base;
      break;
   case ILO_KERNEL_GS_GEN6_SURFACE_SO_COUNT:
      val = kernel->bt.gen6_so_count;
      break;

   case ILO_KERNEL_FS_INPUT_Z:
   case ILO_KERNEL_FS_INPUT_W:
      val = kernel->in.has_pos;
      break;
   case ILO_KERNEL_FS_OUTPUT_Z:
      val = kernel->out.has_pos;
      break;
   case ILO_KERNEL_FS_USE_KILL:
      val = kernel->has_kill;
      break;
   case ILO_KERNEL_FS_BARYCENTRIC_INTERPOLATIONS:
      val = kernel->in.barycentric_interpolation_mode;
      break;
   case ILO_KERNEL_FS_DISPATCH_16_OFFSET:
      val = 0;
      break;
   case ILO_KERNEL_FS_SURFACE_RT_BASE:
      val = kernel->bt.rt_base;
      break;
   case ILO_KERNEL_FS_SURFACE_RT_COUNT:
      val = kernel->bt.rt_count;
      break;

   case ILO_KERNEL_CS_LOCAL_SIZE:
      val = shader->info.compute.req_local_mem;
      break;
   case ILO_KERNEL_CS_PRIVATE_SIZE:
      val = shader->info.compute.req_private_mem;
      break;
   case ILO_KERNEL_CS_INPUT_SIZE:
      val = shader->info.compute.req_input_mem;
      break;
   case ILO_KERNEL_CS_SIMD_SIZE:
      val = 16;
      break;
   case ILO_KERNEL_CS_SURFACE_GLOBAL_BASE:
      val = kernel->bt.global_base;
      break;
   case ILO_KERNEL_CS_SURFACE_GLOBAL_COUNT:
      val = kernel->bt.global_count;
      break;

   default:
      assert(!"unknown kernel parameter");
      val = 0;
      break;
   }

   return val;
}

/**
 * Return the CSO of the selected kernel.
 */
const struct ilo_shader_cso *
ilo_shader_get_kernel_cso(const struct ilo_shader_state *shader)
{
   const struct ilo_shader *kernel = shader->shader;

   assert(kernel);

   return &kernel->cso;
}

/**
 * Return the SO info of the selected kernel.
 */
const struct pipe_stream_output_info *
ilo_shader_get_kernel_so_info(const struct ilo_shader_state *shader)
{
   const struct ilo_shader *kernel = shader->shader;

   assert(kernel);

   return &kernel->so_info;
}

/**
 * Return the routing info of the selected kernel.
 */
const struct ilo_kernel_routing *
ilo_shader_get_kernel_routing(const struct ilo_shader_state *shader)
{
   const struct ilo_shader *kernel = shader->shader;

   assert(kernel);

   return &kernel->routing;
}
