static inline void vrend_sync_shader_io(struct vrend_sub_context *sub_ctx,
                                        struct vrend_shader_selector *sel,
                                        struct vrend_shader_key *key)
{
   unsigned type = sel->type;

   int prev_type = (type != PIPE_SHADER_VERTEX) ?
            PIPE_SHADER_VERTEX : -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 (sub_ctx->shaders[type] == sel) {
      switch (type) {
      case PIPE_SHADER_GEOMETRY:
         if (key->tcs_present || key->tes_present)
            prev_type = PIPE_SHADER_TESS_EVAL;
         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;
         break;
      case PIPE_SHADER_TESS_EVAL:
         if (key->tcs_present)
            prev_type = PIPE_SHADER_TESS_CTRL;
         break;
      default:
         break;
      }
   }

   struct vrend_shader_selector *prev = sub_ctx->shaders[prev_type];
   if (prev_type != -1 && prev) {
      key->input = prev->sinfo.out;
      key->force_invariant_inputs = prev->sinfo.invariant_outputs;

      memcpy(key->prev_stage_generic_and_patch_outputs_layout,
             prev->sinfo.generic_outputs_layout,
             prev->sinfo.out.num_generic_and_patch * sizeof (struct vrend_layout_info));

      key->num_in_clip = sub_ctx->shaders[prev_type]->current->var_sinfo.num_out_clip;
      key->num_in_cull = sub_ctx->shaders[prev_type]->current->var_sinfo.num_out_cull;

      if (vrend_state.use_gles && type == PIPE_SHADER_FRAGMENT)
         key->fs.available_color_in_bits = sub_ctx->shaders[prev_type]->current->var_sinfo.legacy_color_bits;
   }

   int next_type = -1;

   if (type == PIPE_SHADER_FRAGMENT) {
      key->fs.invert_origin = !sub_ctx->inverted_fbo_content;
      key->fs.swizzle_output_rgb_to_bgr = sub_ctx->swizzle_output_rgb_to_bgr;
      key->fs.convert_linear_to_srgb_on_write = sub_ctx->convert_linear_to_srgb_on_write;
      if (vrend_state.use_gles && can_emulate_logicop(sub_ctx->blend_state.logicop_func)) {
         key->fs.logicop_enabled = sub_ctx->blend_state.logicop_enable;
         key->fs.logicop_func = sub_ctx->blend_state.logicop_func;
      }
      int fs_prim_mode = sub_ctx->prim_mode; // inherit draw-call's mode

      // Only use coord_replace if frag shader receives GL_POINTS
      switch (prev_type) {
         case PIPE_SHADER_TESS_EVAL:
            if (sub_ctx->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_point_mode)
               fs_prim_mode = PIPE_PRIM_POINTS;
            break;
         case PIPE_SHADER_GEOMETRY:
            fs_prim_mode = sub_ctx->shaders[PIPE_SHADER_GEOMETRY]->sinfo.gs_out_prim;
            break;
      }
      key->fs.prim_is_points = (fs_prim_mode == PIPE_PRIM_POINTS);
      key->fs.coord_replace = sub_ctx->rs_state.point_quad_rasterization
         && key->fs.prim_is_points
         ? sub_ctx->rs_state.sprite_coord_enable
         : 0x0;

   } else {
      if (sub_ctx->shaders[PIPE_SHADER_FRAGMENT]) {
         struct vrend_shader *fs =
               sub_ctx->shaders[PIPE_SHADER_FRAGMENT]->current;
         key->fs_info = &fs->var_sinfo.fs_info;
         next_type = PIPE_SHADER_FRAGMENT;
      }
  }

   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 (!vrend_state.use_gles)
           next_type = PIPE_SHADER_TESS_EVAL;
        else
           next_type = PIPE_SHADER_TESS_CTRL;
     }
     break;
   case PIPE_SHADER_TESS_CTRL:
      next_type = PIPE_SHADER_TESS_EVAL;
     break;
   case PIPE_SHADER_TESS_EVAL:
     if (key->gs_present)
       next_type = PIPE_SHADER_GEOMETRY;
   default:
     break;
   }

   if (next_type != -1 && sub_ctx->shaders[next_type]) {
      key->output = sub_ctx->shaders[next_type]->sinfo.in;

      /* FS gets the clip/cull info in the key from this shader, so
       * we can avoid re-translating this shader by not updating the
       * info in the key */
      if (next_type != PIPE_SHADER_FRAGMENT) {
         key->num_out_clip = sub_ctx->shaders[next_type]->current->var_sinfo.num_in_clip;
         key->num_out_cull = sub_ctx->shaders[next_type]->current->var_sinfo.num_in_cull;
      }

      if (type == PIPE_SHADER_VERTEX && next_type == PIPE_SHADER_FRAGMENT) {
         if (sub_ctx->shaders[type]) {
            uint32_t fog_input = sub_ctx->shaders[next_type]->sinfo.fog_input_mask;
            uint32_t fog_output = sub_ctx->shaders[type]->sinfo.fog_output_mask;

            //We only want to issue the fixup for inputs not fed by the outputs of the
            //previous stage
            key->vs.fog_fixup_mask = (fog_input ^ fog_output) & fog_input;
         }
      }
   }
}