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;

   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) {
         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){
         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(ctx);
   if (ctx->sub->stencil_state_dirty)
      vrend_update_stencil_state(ctx);
   if (ctx->sub->scissor_state_dirty)
      vrend_update_scissor_state(ctx);

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

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

   if (ctx->sub->shader_dirty || ctx->sub->swizzle_output_rgb_to_bgr) {
      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(&ctx->sub->blend_state, 0);
      bool same_prog;

      ctx->sub->shader_dirty = false;

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

      vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_VERTEX], &vs_dirty);

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

         vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_VERTEX], &vs_dirty);
      }

      if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL])
         vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_TESS_EVAL], &tes_dirty);
      if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY])
         vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_GEOMETRY], &gs_dirty);
      vrend_shader_select(ctx, ctx->sub->shaders[PIPE_SHADER_FRAGMENT], &fs_dirty);

      if (!ctx->sub->shaders[PIPE_SHADER_VERTEX]->current ||
          !ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current ||
          (ctx->sub->shaders[PIPE_SHADER_GEOMETRY] && !ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current) ||
          (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] && !ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current) ||
          (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] && !ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current)) {
         vrend_printf( "failure to compile shader variants: %s\n", ctx->debug_name);
         return 0;
      }
      same_prog = true;
      if (ctx->sub->shaders[PIPE_SHADER_VERTEX]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_VERTEX])
         same_prog = false;
      if (ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_FRAGMENT])
         same_prog = false;
      if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY] && ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_GEOMETRY])
         same_prog = false;
      if (ctx->sub->prog && ctx->sub->prog->dual_src_linked != dual_src)
         same_prog = false;
      if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] && ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_TESS_CTRL])
         same_prog = false;
      if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] && ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current->id != (GLuint)ctx->sub->prog_ids[PIPE_SHADER_TESS_EVAL])
         same_prog = false;

      if (!same_prog) {
         prog = lookup_shader_program(ctx,
                                      ctx->sub->shaders[PIPE_SHADER_VERTEX]->current->id,
                                      ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current->id,
                                      ctx->sub->shaders[PIPE_SHADER_GEOMETRY] ? ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current->id : 0,
                                      ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] ? ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current->id : 0,
                                      ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] ? ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current->id : 0,
                                      dual_src);
         if (!prog) {
            prog = add_shader_program(ctx,
                                      ctx->sub->shaders[PIPE_SHADER_VERTEX]->current,
                                      ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current,
                                      ctx->sub->shaders[PIPE_SHADER_GEOMETRY] ? ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current : NULL,
                                      ctx->sub->shaders[PIPE_SHADER_TESS_CTRL] ? ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current : NULL,
                                      ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] ? ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current : NULL);
            if (!prog)
               return 0;
         }

         ctx->sub->last_shader_idx = ctx->sub->shaders[PIPE_SHADER_TESS_EVAL] ? PIPE_SHADER_TESS_EVAL : (ctx->sub->shaders[PIPE_SHADER_GEOMETRY] ? PIPE_SHADER_GEOMETRY : PIPE_SHADER_FRAGMENT);
      } else
         prog = ctx->sub->prog;
      if (ctx->sub->prog != prog) {
         new_program = true;
         ctx->sub->prog_ids[PIPE_SHADER_VERTEX] = ctx->sub->shaders[PIPE_SHADER_VERTEX]->current->id;
         ctx->sub->prog_ids[PIPE_SHADER_FRAGMENT] = ctx->sub->shaders[PIPE_SHADER_FRAGMENT]->current->id;
         if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY])
            ctx->sub->prog_ids[PIPE_SHADER_GEOMETRY] = ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->current->id;
         if (ctx->sub->shaders[PIPE_SHADER_TESS_CTRL])
            ctx->sub->prog_ids[PIPE_SHADER_TESS_CTRL] = ctx->sub->shaders[PIPE_SHADER_TESS_CTRL]->current->id;
         if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL])
            ctx->sub->prog_ids[PIPE_SHADER_TESS_EVAL] = ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->current->id;
         ctx->sub->prog_ids[PIPE_SHADER_COMPUTE] = -1;
         ctx->sub->prog = prog;

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

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

   vrend_use_program(ctx, ctx->sub->prog->id);

   vrend_draw_bind_objects(ctx, new_program);

   if (!ctx->sub->ve) {
      vrend_printf("illegal VE setup - skipping renderering\n");
      return 0;
   }
   float viewport_neg_val = ctx->sub->viewport_is_negative ? -1.0 : 1.0;
   if (ctx->sub->prog->viewport_neg_val != viewport_neg_val) {
      glUniform1f(ctx->sub->prog->vs_ws_adjust_loc, viewport_neg_val);
      ctx->sub->prog->viewport_neg_val = viewport_neg_val;
   }

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

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

   for (i = 0 ; i < ctx->sub->prog->ss[PIPE_SHADER_VERTEX]->sel->sinfo.num_inputs; i++) {
      struct vrend_vertex_element_array *va = ctx->sub->ve;
      struct vrend_vertex_element *ve = &va->elements[i];
      int vbo_index = ve->base.vertex_buffer_index;
      if (!ctx->sub->vbo[vbo_index].buffer) {
         vrend_printf( "VBO missing vertex buffer\n");
         return 0;
      }
   }

   if (info->indexed) {
      struct vrend_resource *res = (struct vrend_resource *)ctx->sub->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 (ctx->sub->current_so) {
      if (ctx->sub->current_so->xfb_state == XFB_STATE_STARTED_NEED_BEGIN) {
         if (ctx->sub->shaders[PIPE_SHADER_GEOMETRY])
            glBeginTransformFeedback(get_gs_xfb_mode(ctx->sub->shaders[PIPE_SHADER_GEOMETRY]->sinfo.gs_out_prim));
	 else if (ctx->sub->shaders[PIPE_SHADER_TESS_EVAL])
            glBeginTransformFeedback(get_tess_xfb_mode(ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_prim,
						       ctx->sub->shaders[PIPE_SHADER_TESS_EVAL]->sinfo.tes_point_mode));
         else
            glBeginTransformFeedback(get_xfb_mode(info->mode));
         ctx->sub->current_so->xfb_state = XFB_STATE_STARTED;
      } else if (ctx->sub->current_so->xfb_state == XFB_STATE_PAUSED) {
         glResumeTransformFeedback();
         ctx->sub->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 (ctx->sub->draw_indirect_buffer != buf) {
         glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buf);
         ctx->sub->draw_indirect_buffer = buf;
      }

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

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

   /* 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 <= 1)
         glDrawArrays(mode, start, count);
      else if (info->start_instance)
         glDrawArraysInstancedBaseInstance(mode, start, count, info->instance_count, info->start_instance);
      else
         glDrawArraysInstancedARB(mode, start, count, info->instance_count);
   } else {
      GLenum elsz;
      GLenum mode = info->mode;
      switch (ctx->sub->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 > 1)
            glDrawElementsInstancedBaseVertex(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->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)ctx->sub->ib.offset, info->index_bias);
         else
            glDrawElementsBaseVertex(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset, info->index_bias);
      } else if (info->instance_count > 1) {
         glDrawElementsInstancedARB(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->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)ctx->sub->ib.offset);
      else
         glDrawElements(mode, info->count, elsz, (void *)(unsigned long)ctx->sub->ib.offset);
   }

   if (info->primitive_restart) {
      if (vrend_state.use_gles) {
         glEnable(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 (ctx->sub->current_so && has_feature(feat_transform_feedback2)) {
      if (ctx->sub->current_so->xfb_state == XFB_STATE_STARTED) {
         glPauseTransformFeedback();
         ctx->sub->current_so->xfb_state = XFB_STATE_PAUSED;
      }
   }
   return 0;
}