static inline void vrend_fill_shader_key(struct vrend_context *ctx,
                                         struct vrend_shader_selector *sel,
                                         struct vrend_shader_key *key)
{
   unsigned type = sel->type;

   if (vrend_state.use_core_profile == true) {
      int i;
      bool add_alpha_test = true;
      key->cbufs_are_a8_bitmask = 0;
      for (i = 0; i < ctx->sub->nr_cbufs; i++) {
         if (!ctx->sub->surf[i])
            continue;
         if (vrend_format_is_emulated_alpha(ctx->sub->surf[i]->format))
            key->cbufs_are_a8_bitmask |= (1 << i);
         if (util_format_is_pure_integer(ctx->sub->surf[i]->format))
            add_alpha_test = false;
         key->surface_component_bits[i] = util_format_get_component_bits(ctx->sub->surf[i]->format, UTIL_FORMAT_COLORSPACE_RGB, 0);
      }
      if (add_alpha_test) {
         key->add_alpha_test = ctx->sub->dsa_state.alpha.enabled;
         key->alpha_test = ctx->sub->dsa_state.alpha.func;
         key->alpha_ref_val = ctx->sub->dsa_state.alpha.ref_value;
      }

      key->pstipple_tex = ctx->sub->rs_state.poly_stipple_enable;
      key->color_two_side = ctx->sub->rs_state.light_twoside;

      key->clip_plane_enable = ctx->sub->rs_state.clip_plane_enable;
      key->flatshade = ctx->sub->rs_state.flatshade ? true : false;
   } else {
      key->add_alpha_test = 0;
      key->pstipple_tex = 0;
   }

   if (type == PIPE_SHADER_FRAGMENT && vrend_state.use_gles && can_emulate_logicop(ctx->sub->blend_state.logicop_func)) {
      key->fs_logicop_enabled = ctx->sub->blend_state.logicop_enable;
      key->fs_logicop_func = ctx->sub->blend_state.logicop_func;
      key->fs_logicop_emulate_coherent = !has_feature(feat_framebuffer_fetch_non_coherent);
   }

   key->invert_fs_origin = !ctx->sub->inverted_fbo_content;
   key->coord_replace = ctx->sub->rs_state.point_quad_rasterization ? ctx->sub->rs_state.sprite_coord_enable : 0;

   if (type == PIPE_SHADER_FRAGMENT)
      key->fs_swizzle_output_rgb_to_bgr = ctx->sub->swizzle_output_rgb_to_bgr;

   if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY])
      key->gs_present = true;
   if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL])
      key->tcs_present = true;
   if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL])
      key->tes_present = true;

   int prev_type = -1;

   /* Gallium sends and binds the shaders in the reverse order, so if an
    * old shader is still bound we should ignore the "previous" (as in
    * execution order) shader when the key is evaluated, unless the currently
    * bound shader selector is actually refers to the current shader. */
   if (ctx->sub->shaders[type] == sel) {
      switch (type) {
      case PIPE_SHADER_GEOMETRY:
         if (key->tcs_present || key->tes_present)
            prev_type = PIPE_SHADER_TESS_EVAL;
         else
            prev_type = PIPE_SHADER_VERTEX;
         break;
      case PIPE_SHADER_FRAGMENT:
         if (key->gs_present)
            prev_type = PIPE_SHADER_GEOMETRY;
         else if (key->tcs_present || key->tes_present)
            prev_type = PIPE_SHADER_TESS_EVAL;
         else
            prev_type = PIPE_SHADER_VERTEX;
         break;
      case PIPE_SHADER_TESS_EVAL:
         if (key->tcs_present)
            prev_type = PIPE_SHADER_TESS_CTRL;
         else
            prev_type = PIPE_SHADER_VERTEX;
         break;
      case PIPE_SHADER_TESS_CTRL:
         prev_type = PIPE_SHADER_VERTEX;
         break;
      default:
         break;
      }
   }

   if (prev_type != -1 && ctx->sub->shaders[prev_type]) {
      key->prev_stage_pervertex_out = ctx->sub->shaders[prev_type]->sinfo.has_pervertex_out;
      key->prev_stage_num_clip_out = ctx->sub->shaders[prev_type]->sinfo.num_clip_out;
      key->prev_stage_num_cull_out = ctx->sub->shaders[prev_type]->sinfo.num_cull_out;
      key->num_indirect_generic_inputs = ctx->sub->shaders[prev_type]->sinfo.num_indirect_generic_outputs;
      key->num_indirect_patch_inputs = ctx->sub->shaders[prev_type]->sinfo.num_indirect_patch_outputs;
      key->num_prev_generic_and_patch_outputs = ctx->sub->shaders[prev_type]->sinfo.num_generic_and_patch_outputs;
      key->guest_sent_io_arrays = ctx->sub->shaders[prev_type]->sinfo.guest_sent_io_arrays;

      memcpy(key->prev_stage_generic_and_patch_outputs_layout,
             ctx->sub->shaders[prev_type]->sinfo.generic_outputs_layout,
             64 * sizeof (struct vrend_layout_info));
      key->force_invariant_inputs = ctx->sub->shaders[prev_type]->sinfo.invariant_outputs;
   }

   int next_type = -1;
   switch (type) {
   case PIPE_SHADER_VERTEX:
     if (key->tcs_present)
       next_type = PIPE_SHADER_TESS_CTRL;
     else if (key->gs_present)
       next_type = PIPE_SHADER_GEOMETRY;
     else if (key->tes_present) {
        if (!ctx->shader_cfg.use_gles)
           next_type = PIPE_SHADER_TESS_EVAL;
        else
           next_type = PIPE_SHADER_TESS_CTRL;
     } else
        next_type = PIPE_SHADER_FRAGMENT;
     break;
   case PIPE_SHADER_TESS_CTRL:
      next_type = PIPE_SHADER_TESS_EVAL;
     break;
   case PIPE_SHADER_GEOMETRY:
     next_type = PIPE_SHADER_FRAGMENT;
     break;
   case PIPE_SHADER_TESS_EVAL:
     if (key->gs_present)
       next_type = PIPE_SHADER_GEOMETRY;
     else
       next_type = PIPE_SHADER_FRAGMENT;
   default:
     break;
   }

   if (next_type != -1 && ctx->sub->shaders[next_type]) {
      key->num_indirect_generic_outputs = ctx->sub->shaders[next_type]->sinfo.num_indirect_generic_inputs;
      key->num_indirect_patch_outputs = ctx->sub->shaders[next_type]->sinfo.num_indirect_patch_inputs;
      key->generic_outputs_expected_mask = ctx->sub->shaders[next_type]->sinfo.generic_inputs_emitted_mask;
   }
}