vrend_select_program(struct vrend_sub_context *sub_ctx, ubyte vertices_per_patch)
{
   struct vrend_linked_shader_program *prog;
   bool fs_dirty, vs_dirty, gs_dirty, tcs_dirty, tes_dirty;
   bool dual_src = util_blend_state_is_dual(&sub_ctx->blend_state, 0);
   bool new_program = false;

   struct vrend_shader_selector **shaders = sub_ctx->shaders;

   sub_ctx->shader_dirty = false;

   if (!shaders[PIPE_SHADER_VERTEX] || !shaders[PIPE_SHADER_FRAGMENT]) {
      vrend_printf("dropping rendering due to missing shaders: %s\n", sub_ctx->parent->debug_name);
      return false;
   }

   // For some GPU, we'd like to use integer variable in generated GLSL if
   // the input buffers are integer formats. But we actually don't know the
   // buffer formats when the shader is created, we only know it here.
   // Set it to true so the underlying code knows to use the buffer formats
   // now.
   sub_ctx->drawing = true;
   vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_VERTEX], &vs_dirty);
   sub_ctx->drawing = false;

   if (shaders[PIPE_SHADER_TESS_CTRL] && shaders[PIPE_SHADER_TESS_CTRL]->tokens)
      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_TESS_CTRL], &tcs_dirty);
   else if (vrend_state.use_gles && shaders[PIPE_SHADER_TESS_EVAL]) {
      VREND_DEBUG(dbg_shader, sub_ctx->parent, "Need to inject a TCS\n");
      vrend_inject_tcs(sub_ctx, vertices_per_patch);

      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_VERTEX], &vs_dirty);
   }

   if (shaders[PIPE_SHADER_TESS_EVAL])
      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_TESS_EVAL], &tes_dirty);
   if (shaders[PIPE_SHADER_GEOMETRY])
      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_GEOMETRY], &gs_dirty);
   vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_FRAGMENT], &fs_dirty);

   // NOTE: run shader selection again as a workaround to #180 - "duplicated shader compilation"
   if (shaders[PIPE_SHADER_GEOMETRY])
      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_GEOMETRY], &gs_dirty);
   if (shaders[PIPE_SHADER_TESS_EVAL])
      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_TESS_EVAL], &tes_dirty);
   if (shaders[PIPE_SHADER_TESS_CTRL] && shaders[PIPE_SHADER_TESS_CTRL]->tokens)
      vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_TESS_CTRL], &tcs_dirty);
   else if (vrend_state.use_gles && shaders[PIPE_SHADER_TESS_EVAL]) {
      VREND_DEBUG(dbg_shader, sub_ctx->parent, "Need to inject a TCS\n");
      vrend_inject_tcs(sub_ctx, vertices_per_patch);
   }
   sub_ctx->drawing = true;
   vrend_shader_select(sub_ctx, shaders[PIPE_SHADER_VERTEX], &vs_dirty);
   sub_ctx->drawing = false;

   uint8_t gles_emulate_query_texture_levels_mask = 0;

   for (uint i = 0; i < PIPE_SHADER_TYPES; i++) {
      struct vrend_shader_selector *sel = shaders[i];
      if (!sel)
         continue;

      struct vrend_shader *shader = sel->current;
      if (shader && !shader->is_compiled) {//shader->sel->type == PIPE_SHADER_FRAGMENT || shader->sel->type == PIPE_SHADER_GEOMETRY) {
         if (!vrend_compile_shader(sub_ctx, shader))
            return false;
      }
      if (vrend_state.use_gles && sel->sinfo.gles_use_tex_query_level)
         gles_emulate_query_texture_levels_mask |= 1 << i;
   }

   if (!shaders[PIPE_SHADER_VERTEX]->current ||
       !shaders[PIPE_SHADER_FRAGMENT]->current ||
       (shaders[PIPE_SHADER_GEOMETRY] && !shaders[PIPE_SHADER_GEOMETRY]->current) ||
       (shaders[PIPE_SHADER_TESS_CTRL] && !shaders[PIPE_SHADER_TESS_CTRL]->current) ||
       (shaders[PIPE_SHADER_TESS_EVAL] && !shaders[PIPE_SHADER_TESS_EVAL]->current)) {
      vrend_printf( "failure to compile shader variants: %s\n", sub_ctx->parent->debug_name);
      return false;
   }

   GLuint vs_id = shaders[PIPE_SHADER_VERTEX]->current->id;
   GLuint fs_id = shaders[PIPE_SHADER_FRAGMENT]->current->id;
   GLuint gs_id = shaders[PIPE_SHADER_GEOMETRY] ? shaders[PIPE_SHADER_GEOMETRY]->current->id : 0;
   GLuint tcs_id = shaders[PIPE_SHADER_TESS_CTRL] ? shaders[PIPE_SHADER_TESS_CTRL]->current->id : 0;
   GLuint tes_id = shaders[PIPE_SHADER_TESS_EVAL] ? shaders[PIPE_SHADER_TESS_EVAL]->current->id : 0;

   if (shaders[PIPE_SHADER_FRAGMENT]->current->sel->sinfo.num_outputs <= 1)
      dual_src = false;

   bool same_prog = sub_ctx->prog &&
                    vs_id == sub_ctx->prog_ids[PIPE_SHADER_VERTEX] &&
                    fs_id == sub_ctx->prog_ids[PIPE_SHADER_FRAGMENT] &&
                    gs_id == sub_ctx->prog_ids[PIPE_SHADER_GEOMETRY] &&
                    tcs_id == sub_ctx->prog_ids[PIPE_SHADER_TESS_CTRL] &&
                    tes_id == sub_ctx->prog_ids[PIPE_SHADER_TESS_EVAL] &&
                    sub_ctx->prog->dual_src_linked == dual_src;

   if (!same_prog) {
      prog = lookup_shader_program(sub_ctx, vs_id, fs_id, gs_id, tcs_id, tes_id, dual_src);
      if (!prog) {
         prog = add_shader_program(sub_ctx,
                                   sub_ctx->shaders[PIPE_SHADER_VERTEX]->current,
                                   sub_ctx->shaders[PIPE_SHADER_FRAGMENT]->current,
                                   gs_id ? sub_ctx->shaders[PIPE_SHADER_GEOMETRY]->current : NULL,
                                   tcs_id ? sub_ctx->shaders[PIPE_SHADER_TESS_CTRL]->current : NULL,
                                   tes_id ? sub_ctx->shaders[PIPE_SHADER_TESS_EVAL]->current : NULL);
         if (!prog)
            return false;
         prog->gles_use_query_texturelevel_mask = gles_emulate_query_texture_levels_mask;
      }

      sub_ctx->last_shader_idx = sub_ctx->shaders[PIPE_SHADER_TESS_EVAL] ? PIPE_SHADER_TESS_EVAL : (sub_ctx->shaders[PIPE_SHADER_GEOMETRY] ? PIPE_SHADER_GEOMETRY : PIPE_SHADER_FRAGMENT);
   } else
      prog = sub_ctx->prog;
   if (sub_ctx->prog != prog) {
      new_program = true;
      sub_ctx->prog_ids[PIPE_SHADER_VERTEX] = vs_id;
      sub_ctx->prog_ids[PIPE_SHADER_FRAGMENT] = fs_id;
      sub_ctx->prog_ids[PIPE_SHADER_GEOMETRY] = gs_id;
      sub_ctx->prog_ids[PIPE_SHADER_TESS_CTRL] = tcs_id;
      sub_ctx->prog_ids[PIPE_SHADER_TESS_EVAL] = tes_id;
      sub_ctx->prog_ids[PIPE_SHADER_COMPUTE] = 0;
      sub_ctx->prog = prog;

      /* mark all constbufs and sampler views as dirty */
      for (int stage = PIPE_SHADER_VERTEX; stage <= PIPE_SHADER_FRAGMENT; stage++) {
         sub_ctx->const_bufs_dirty[stage] = ~0;
         sub_ctx->sampler_views_dirty[stage] = ~0;
      }

      prog->ref_context = sub_ctx;
   }
   sub_ctx->cs_shader_dirty = true;
   return new_program;
}