int vrend_draw_vbo(struct vrend_context *ctx,
                   const struct pipe_draw_info *info,
                   uint32_t cso, uint32_t indirect_handle,
                   uint32_t indirect_draw_count_handle)
{
   int i;
   bool new_program = false;
   struct vrend_resource *indirect_res = NULL;
   struct vrend_resource *indirect_params_res = NULL;
   struct vrend_sub_context *sub_ctx = ctx->sub;

   if (ctx->in_error)
      return 0;

   if (info->instance_count && !has_feature(feat_draw_instance))
      return EINVAL;

   if (info->start_instance && !has_feature(feat_base_instance))
      return EINVAL;

   if (info->indirect.draw_count > 1 && !has_feature(feat_multi_draw_indirect))
      return EINVAL;

   if (indirect_handle) {
      if (!has_feature(feat_indirect_draw))
         return EINVAL;
      indirect_res = vrend_renderer_ctx_res_lookup(ctx, indirect_handle);
      if (!indirect_res) {
         vrend_report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, indirect_handle);
         return 0;
      }
   }

   /* this must be zero until we support the feature */
   if (indirect_draw_count_handle) {
      if (!has_feature(feat_indirect_params))
         return EINVAL;

      indirect_params_res = vrend_renderer_ctx_res_lookup(ctx, indirect_draw_count_handle);
      if (!indirect_params_res){
         vrend_report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, indirect_draw_count_handle);
         return 0;
      }
   }

   if (ctx->ctx_switch_pending)
      vrend_finish_context_switch(ctx);

   vrend_update_frontface_state(sub_ctx);
   if (ctx->sub->stencil_state_dirty)
      vrend_update_stencil_state(sub_ctx);
   if (ctx->sub->scissor_state_dirty)
      vrend_update_scissor_state(sub_ctx);

   if (ctx->sub->viewport_state_dirty)
      vrend_update_viewport_state(sub_ctx);

   if (ctx->sub->blend_state_dirty)
      vrend_patch_blend_state(sub_ctx);

   // enable primitive-mode-dependent shader variants
   if (sub_ctx->prim_mode != (int)info->mode) {
      // Only refresh shader program when switching in/out of GL_POINTS primitive mode
      if (sub_ctx->prim_mode == PIPE_PRIM_POINTS
          || (int)info->mode == PIPE_PRIM_POINTS)
         sub_ctx->shader_dirty = true;

      sub_ctx->prim_mode = (int)info->mode;
   }

   if (!sub_ctx->ve) {
      vrend_printf("illegal VE setup - skipping renderering\n");
      return 0;
   }

   if (sub_ctx->shader_dirty || sub_ctx->swizzle_output_rgb_to_bgr ||
       sub_ctx->convert_linear_to_srgb_on_write)
      new_program = vrend_select_program(sub_ctx, info->vertices_per_patch);

   if (!sub_ctx->prog) {
      vrend_printf("dropping rendering due to missing shaders: %s\n", ctx->debug_name);
      return 0;
   }

   vrend_use_program(sub_ctx, sub_ctx->prog->id);

   if (vrend_state.use_gles) {
      /* PIPE_SHADER and TGSI_SHADER have different ordering, so use two
       * different prefix arrays */
      for (unsigned i = PIPE_SHADER_VERTEX; i < PIPE_SHADER_COMPUTE; ++i) {
         if (sub_ctx->prog->gles_use_query_texturelevel_mask & (1 << i)) {
            char loc_name[32];
            snprintf(loc_name, 32, "%s_texlod", pipe_shader_to_prefix(i));
            sub_ctx->prog->tex_levels_uniform_id[i] = glGetUniformLocation(sub_ctx->prog->id, loc_name);
         } else {
            sub_ctx->prog->tex_levels_uniform_id[i] = -1;
         }

      }
   }

   vrend_draw_bind_objects(sub_ctx, new_program);


   float viewport_neg_val = sub_ctx->viewport_is_negative ? -1.0 : 1.0;
   if (sub_ctx->prog->viewport_neg_val != viewport_neg_val) {
      glUniform1f(sub_ctx->prog->vs_ws_adjust_loc, viewport_neg_val);
      sub_ctx->prog->viewport_neg_val = viewport_neg_val;
   }

   if (sub_ctx->rs_state.clip_plane_enable) {
      glUniform1i(sub_ctx->prog->clip_enabled_loc, 1);
      for (i = 0 ; i < 8; i++) {
         glUniform4fv(sub_ctx->prog->clip_locs[i], 1, (const GLfloat *)&sub_ctx->ucp_state.ucp[i]);
      }
   } else {
      glUniform1i(sub_ctx->prog->clip_enabled_loc, 0);
   }

   if (has_feature(feat_gles31_vertex_attrib_binding))
      vrend_draw_bind_vertex_binding(ctx, sub_ctx->ve);
   else
      vrend_draw_bind_vertex_legacy(ctx, sub_ctx->ve);

   if (info->indexed) {
      struct vrend_resource *res = (struct vrend_resource *)sub_ctx->ib.buffer;
      if (!res) {
         vrend_printf( "VBO missing indexed array buffer\n");
         return 0;
      }
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, res->id);
   } else
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

   if (sub_ctx->current_so) {
      if (sub_ctx->current_so->xfb_state == XFB_STATE_STARTED_NEED_BEGIN) {
         if (sub_ctx->shaders[PIPE_SHADER_GEOMETRY])
            glBeginTransformFeedback(get_gs_xfb_mode(sub_ctx->shaders[PIPE_SHADER_GEOMETRY]->sinfo.gs_out_prim));
     else if (sub_ctx->shaders[PIPE_SHADER_TESS_EVAL])
            glBeginTransformFeedback(get_tess_xfb_mode(sub_ctx->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_prim,
                               sub_ctx->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_point_mode));
         else
            glBeginTransformFeedback(get_xfb_mode(info->mode));
         sub_ctx->current_so->xfb_state = XFB_STATE_STARTED;
      } else if (sub_ctx->current_so->xfb_state == XFB_STATE_PAUSED) {
         glResumeTransformFeedback();
         sub_ctx->current_so->xfb_state = XFB_STATE_STARTED;
      }
   }

   if (info->primitive_restart) {
      if (vrend_state.use_gles) {
         glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
      } else if (has_feature(feat_nv_prim_restart)) {
         glEnableClientState(GL_PRIMITIVE_RESTART_NV);
         glPrimitiveRestartIndexNV(info->restart_index);
      } else if (has_feature(feat_gl_prim_restart)) {
         glEnable(GL_PRIMITIVE_RESTART);
         glPrimitiveRestartIndex(info->restart_index);
      }
   }

   if (has_feature(feat_indirect_draw)) {
      GLint buf = indirect_res ? indirect_res->id : 0;
      if (sub_ctx->draw_indirect_buffer != buf) {
         glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buf);
         sub_ctx->draw_indirect_buffer = buf;
      }

      if (has_feature(feat_indirect_params)) {
         GLint buf = indirect_params_res ? indirect_params_res->id : 0;
         if (sub_ctx->draw_indirect_params_buffer != buf) {
            glBindBuffer(GL_PARAMETER_BUFFER_ARB, buf);
            sub_ctx->draw_indirect_params_buffer = buf;
         }
      }
   }

   if (info->vertices_per_patch && has_feature(feat_tessellation))
      glPatchParameteri(GL_PATCH_VERTICES, info->vertices_per_patch);

   /* If the host support blend_equation_advanced but not fbfetch,
    * the guest driver will not lower the equation to fbfetch so we need to set up the renderer to
    * accept those blend equations.
    * When we transmit the blend mode through alpha_src_factor, alpha_dst_factor is always 0.
    */
   uint32_t blend_mask_shader = sub_ctx->shaders[PIPE_SHADER_FRAGMENT]->sinfo.fs_blend_equation_advanced;
   uint32_t blend_mode = sub_ctx->blend_state.rt[0].alpha_src_factor;
   uint32_t alpha_dst_factor = sub_ctx->blend_state.rt[0].alpha_dst_factor;
   bool use_advanced_blending = !has_feature(feat_framebuffer_fetch) &&
                                 has_feature(feat_blend_equation_advanced) &&
                                 blend_mask_shader != 0 &&
                                 blend_mode != 0 &&
                                 alpha_dst_factor == 0;
   if(use_advanced_blending) {
      GLenum blend = translate_blend_func_advanced(blend_mode);
      glBlendEquation(blend);
      glEnable(GL_BLEND);
   }

   /* set the vertex state up now on a delay */
   if (!info->indexed) {
      GLenum mode = info->mode;
      int count = cso ? cso : info->count;
      int start = cso ? 0 : info->start;

      if (indirect_handle) {
         if (indirect_params_res)
            glMultiDrawArraysIndirectCountARB(mode, (GLvoid const *)(unsigned long)info->indirect.offset,
                                              info->indirect.indirect_draw_count_offset, info->indirect.draw_count, info->indirect.stride);
         else if (info->indirect.draw_count > 1)
            glMultiDrawArraysIndirect(mode, (GLvoid const *)(unsigned long)info->indirect.offset, info->indirect.draw_count, info->indirect.stride);
         else
            glDrawArraysIndirect(mode, (GLvoid const *)(unsigned long)info->indirect.offset);
      } else if (info->instance_count > 0) {
         if (info->start_instance > 0)
            glDrawArraysInstancedBaseInstance(mode, start, count, info->instance_count, info->start_instance);
         else
            glDrawArraysInstancedARB(mode, start, count, info->instance_count);
      } else
         glDrawArrays(mode, start, count);
   } else {
      GLenum elsz;
      GLenum mode = info->mode;
      switch (sub_ctx->ib.index_size) {
      case 1:
         elsz = GL_UNSIGNED_BYTE;
         break;
      case 2:
         elsz = GL_UNSIGNED_SHORT;
         break;
      case 4:
      default:
         elsz = GL_UNSIGNED_INT;
         break;
      }

      if (indirect_handle) {
         if (indirect_params_res)
            glMultiDrawElementsIndirectCountARB(mode, elsz, (GLvoid const *)(unsigned long)info->indirect.offset,
                                                info->indirect.indirect_draw_count_offset, info->indirect.draw_count, info->indirect.stride);
         else if (info->indirect.draw_count > 1)
            glMultiDrawElementsIndirect(mode, elsz, (GLvoid const *)(unsigned long)info->indirect.offset, info->indirect.draw_count, info->indirect.stride);
         else
            glDrawElementsIndirect(mode, elsz, (GLvoid const *)(unsigned long)info->indirect.offset);
      } else if (info->index_bias) {
         if (info->instance_count > 0) {
            if (info->start_instance > 0)
               glDrawElementsInstancedBaseVertexBaseInstance(mode, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset,
                                                             info->instance_count, info->index_bias, info->start_instance);
            else
               glDrawElementsInstancedBaseVertex(mode, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset, info->instance_count, info->index_bias);


         } else if (info->min_index != 0 || info->max_index != (unsigned)-1)
            glDrawRangeElementsBaseVertex(mode, info->min_index, info->max_index, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset, info->index_bias);
         else
            glDrawElementsBaseVertex(mode, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset, info->index_bias);
      } else if (info->instance_count > 1) {
         glDrawElementsInstancedARB(mode, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset, info->instance_count);
      } else if (info->min_index != 0 || info->max_index != (unsigned)-1)
         glDrawRangeElements(mode, info->min_index, info->max_index, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset);
      else
         glDrawElements(mode, info->count, elsz, (void *)(unsigned long)sub_ctx->ib.offset);
   }

   if (info->primitive_restart) {
      if (vrend_state.use_gles) {
         glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
      } else if (has_feature(feat_nv_prim_restart)) {
         glDisableClientState(GL_PRIMITIVE_RESTART_NV);
      } else if (has_feature(feat_gl_prim_restart)) {
         glDisable(GL_PRIMITIVE_RESTART);
      }
   }

   if (sub_ctx->current_so && has_feature(feat_transform_feedback2)) {
      if (sub_ctx->current_so->xfb_state == XFB_STATE_STARTED) {
         glPauseTransformFeedback();
         sub_ctx->current_so->xfb_state = XFB_STATE_PAUSED;
      }
   }
   return 0;
}