gallium: Replace gl_rasterization_rules with lower_left_origin and half_pixel_center.
[mesa.git] / src / gallium / state_trackers / vega / renderer.c
index 2bb4c8bc756651149cc4d7fa06a4227370f90c6d..b8232787eba7064a729eeb3234b044a484049f12 100644 (file)
@@ -1,6 +1,7 @@
 /**************************************************************************
  *
  * Copyright 2009 VMware, Inc.  All Rights Reserved.
+ * Copyright 2010 LunarG, Inc.  All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the
 #include "pipe/p_shader_tokens.h"
 
 #include "util/u_draw_quad.h"
-#include "util/u_format.h"
 #include "util/u_simple_shaders.h"
 #include "util/u_memory.h"
-#include "util/u_rect.h"
 #include "util/u_sampler.h"
+#include "util/u_surface.h"
+#include "util/u_math.h"
+#include "util/u_format.h"
 
 #include "cso_cache/cso_context.h"
+#include "tgsi/tgsi_ureg.h"
+
+typedef enum {
+   RENDERER_STATE_INIT,
+   RENDERER_STATE_COPY,
+   RENDERER_STATE_DRAWTEX,
+   RENDERER_STATE_SCISSOR,
+   RENDERER_STATE_CLEAR,
+   RENDERER_STATE_FILTER,
+   RENDERER_STATE_POLYGON_STENCIL,
+   RENDERER_STATE_POLYGON_FILL,
+   NUM_RENDERER_STATES
+} RendererState;
+
+typedef enum {
+   RENDERER_VS_PLAIN,
+   RENDERER_VS_COLOR,
+   RENDERER_VS_TEXTURE,
+   NUM_RENDERER_VS
+} RendererVs;
+
+typedef enum {
+   RENDERER_FS_COLOR,
+   RENDERER_FS_TEXTURE,
+   RENDERER_FS_SCISSOR,
+   RENDERER_FS_WHITE,
+   NUM_RENDERER_FS
+} RendererFs;
 
 struct renderer {
    struct pipe_context *pipe;
-   struct vg_context *owner;
-
    struct cso_context *cso;
 
-   void *fs;
+   VGbitfield dirty;
+   struct {
+      struct pipe_rasterizer_state rasterizer;
+      struct pipe_depth_stencil_alpha_state dsa;
+      struct pipe_framebuffer_state fb;
+   } g3d;
+   struct matrix projection;
 
+   struct matrix mvp;
+   struct pipe_resource *vs_cbuf;
+
+   struct pipe_resource *fs_cbuf;
+   VGfloat fs_cbuf_data[32];
+   VGint fs_cbuf_len;
+
+   struct pipe_vertex_element velems[2];
    VGfloat vertices[4][2][4];
+
+   void *cached_vs[NUM_RENDERER_VS];
+   void *cached_fs[NUM_RENDERER_FS];
+
+   RendererState state;
+
+   /* state data */
+   union {
+      struct {
+         VGint tex_width;
+         VGint tex_height;
+      } copy;
+
+      struct {
+         VGint tex_width;
+         VGint tex_height;
+      } drawtex;
+
+      struct {
+         VGboolean restore_dsa;
+      } scissor;
+
+      struct {
+         VGboolean use_sampler;
+         VGint tex_width, tex_height;
+      } filter;
+
+      struct {
+         struct pipe_depth_stencil_alpha_state dsa;
+         VGboolean manual_two_sides;
+         VGboolean restore_dsa;
+      } polygon_stencil;
+   } u;
 };
 
-static void setup_shaders(struct renderer *ctx)
+/**
+ * Return VG_TRUE if the renderer can use the resource as the asked bindings.
+ */
+static VGboolean renderer_can_support(struct renderer *renderer,
+                                      struct pipe_resource *res,
+                                      unsigned bindings)
 {
-   struct pipe_context *pipe = ctx->pipe;
-   /* fragment shader */
-   ctx->fs = util_make_fragment_tex_shader(pipe, TGSI_TEXTURE_2D);
-}
-
-static struct pipe_buffer *
-setup_vertex_data(struct renderer *ctx,
-                  float x0, float y0, float x1, float y1, float z)
-{
-   ctx->vertices[0][0][0] = x0;
-   ctx->vertices[0][0][1] = y0;
-   ctx->vertices[0][0][2] = z;
-   ctx->vertices[0][1][0] = 0.0f; /*s*/
-   ctx->vertices[0][1][1] = 0.0f; /*t*/
-
-   ctx->vertices[1][0][0] = x1;
-   ctx->vertices[1][0][1] = y0;
-   ctx->vertices[1][0][2] = z;
-   ctx->vertices[1][1][0] = 1.0f; /*s*/
-   ctx->vertices[1][1][1] = 0.0f; /*t*/
-
-   ctx->vertices[2][0][0] = x1;
-   ctx->vertices[2][0][1] = y1;
-   ctx->vertices[2][0][2] = z;
-   ctx->vertices[2][1][0] = 1.0f;
-   ctx->vertices[2][1][1] = 1.0f;
-
-   ctx->vertices[3][0][0] = x0;
-   ctx->vertices[3][0][1] = y1;
-   ctx->vertices[3][0][2] = z;
-   ctx->vertices[3][1][0] = 0.0f;
-   ctx->vertices[3][1][1] = 1.0f;
-
-   return pipe_user_buffer_create( ctx->pipe->screen,
-                                   ctx->vertices,
-                                   sizeof(ctx->vertices) );
-}
-
-static struct pipe_buffer *
-setup_vertex_data_tex(struct renderer *ctx,
-                      float x0, float y0, float x1, float y1,
-                      float s0, float t0, float s1, float t1,
-                      float z)
-{
-   ctx->vertices[0][0][0] = x0;
-   ctx->vertices[0][0][1] = y0;
-   ctx->vertices[0][0][2] = z;
-   ctx->vertices[0][1][0] = s0; /*s*/
-   ctx->vertices[0][1][1] = t0; /*t*/
-
-   ctx->vertices[1][0][0] = x1;
-   ctx->vertices[1][0][1] = y0;
-   ctx->vertices[1][0][2] = z;
-   ctx->vertices[1][1][0] = s1; /*s*/
-   ctx->vertices[1][1][1] = t0; /*t*/
-
-   ctx->vertices[2][0][0] = x1;
-   ctx->vertices[2][0][1] = y1;
-   ctx->vertices[2][0][2] = z;
-   ctx->vertices[2][1][0] = s1;
-   ctx->vertices[2][1][1] = t1;
-
-   ctx->vertices[3][0][0] = x0;
-   ctx->vertices[3][0][1] = y1;
-   ctx->vertices[3][0][2] = z;
-   ctx->vertices[3][1][0] = s0;
-   ctx->vertices[3][1][1] = t1;
-
-   return pipe_user_buffer_create( ctx->pipe->screen,
-                                   ctx->vertices,
-                                   sizeof(ctx->vertices) );
-}
-
-
-static struct pipe_buffer *
-setup_vertex_data_qtex(struct renderer *ctx,
-                       float x0, float y0, float x1, float y1,
-                       float x2, float y2, float x3, float y3,
-                       float s0, float t0, float s1, float t1,
-                       float z)
-{
-   ctx->vertices[0][0][0] = x0;
-   ctx->vertices[0][0][1] = y0;
-   ctx->vertices[0][0][2] = z;
-   ctx->vertices[0][1][0] = s0; /*s*/
-   ctx->vertices[0][1][1] = t0; /*t*/
-
-   ctx->vertices[1][0][0] = x1;
-   ctx->vertices[1][0][1] = y1;
-   ctx->vertices[1][0][2] = z;
-   ctx->vertices[1][1][0] = s1; /*s*/
-   ctx->vertices[1][1][1] = t0; /*t*/
-
-   ctx->vertices[2][0][0] = x2;
-   ctx->vertices[2][0][1] = y2;
-   ctx->vertices[2][0][2] = z;
-   ctx->vertices[2][1][0] = s1;
-   ctx->vertices[2][1][1] = t1;
-
-   ctx->vertices[3][0][0] = x3;
-   ctx->vertices[3][0][1] = y3;
-   ctx->vertices[3][0][2] = z;
-   ctx->vertices[3][1][0] = s0;
-   ctx->vertices[3][1][1] = t1;
-
-   return pipe_user_buffer_create( ctx->pipe->screen,
-                                   ctx->vertices,
-                                   sizeof(ctx->vertices) );
+   struct pipe_screen *screen = renderer->pipe->screen;
+
+   return screen->is_format_supported(screen,
+         res->format, res->target, 0, bindings);
 }
 
-struct renderer * renderer_create(struct vg_context *owner)
+/**
+ * Set the model-view-projection matrix used by vertex shaders.
+ */
+static void renderer_set_mvp(struct renderer *renderer,
+                             const struct matrix *mvp)
 {
+   struct matrix *cur = &renderer->mvp;
+   struct pipe_resource *cbuf;
+   VGfloat consts[3][4];
    VGint i;
-   struct renderer *renderer = CALLOC_STRUCT(renderer);
 
-   if (!renderer)
+   /* projection only */
+   if (!mvp)
+      mvp = &renderer->projection;
+
+   /* re-upload only if necessary */
+   if (memcmp(cur, mvp, sizeof(*mvp)) == 0)
+      return;
+
+   /* 3x3 matrix to 3 constant vectors (no Z) */
+   for (i = 0; i < 3; i++) {
+      consts[i][0] = mvp->m[i + 0];
+      consts[i][1] = mvp->m[i + 3];
+      consts[i][2] = 0.0f;
+      consts[i][3] = mvp->m[i + 6];
+   }
+
+   cbuf = renderer->vs_cbuf;
+   pipe_resource_reference(&cbuf, NULL);
+   cbuf = pipe_buffer_create(renderer->pipe->screen,
+                             PIPE_BIND_CONSTANT_BUFFER,
+                             PIPE_USAGE_STATIC,
+                             sizeof(consts));
+   if (cbuf) {
+      pipe_buffer_write(renderer->pipe, cbuf,
+            0, sizeof(consts), consts);
+   }
+   pipe_set_constant_buffer(renderer->pipe,
+         PIPE_SHADER_VERTEX, 0, cbuf);
+
+   memcpy(cur, mvp, sizeof(*mvp));
+   renderer->vs_cbuf = cbuf;
+}
+
+/**
+ * Create a simple vertex shader that passes through position and the given
+ * attribute.
+ */
+static void *create_passthrough_vs(struct pipe_context *pipe, int semantic_name)
+{
+   struct ureg_program *ureg;
+   struct ureg_src src[2], constants[3];
+   struct ureg_dst dst[2], tmp;
+   int i;
+
+   ureg = ureg_create(TGSI_PROCESSOR_VERTEX);
+   if (!ureg)
       return NULL;
 
-   renderer->owner = owner;
-   renderer->pipe = owner->pipe;
-   renderer->cso = owner->cso_context;
+   /* position is in user coordinates */
+   src[0] = ureg_DECL_vs_input(ureg, 0);
+   dst[0] = ureg_DECL_output(ureg, TGSI_SEMANTIC_POSITION, 0);
+   tmp = ureg_DECL_temporary(ureg);
+   for (i = 0; i < Elements(constants); i++)
+      constants[i] = ureg_DECL_constant(ureg, i);
+
+   /* transform to clipped coordinates */
+   ureg_DP4(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_X), src[0], constants[0]);
+   ureg_DP4(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_Y), src[0], constants[1]);
+   ureg_MOV(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_Z), src[0]);
+   ureg_DP4(ureg, ureg_writemask(tmp, TGSI_WRITEMASK_W), src[0], constants[2]);
+   ureg_MOV(ureg, dst[0], ureg_src(tmp));
+
+   if (semantic_name >= 0) {
+      src[1] = ureg_DECL_vs_input(ureg, 1);
+      dst[1] = ureg_DECL_output(ureg, semantic_name, 0);
+      ureg_MOV(ureg, dst[1], src[1]);
+   }
 
-   setup_shaders(renderer);
+   ureg_END(ureg);
+
+   return ureg_create_shader_and_destroy(ureg, pipe);
+}
+
+/**
+ * Set renderer vertex shader.
+ *
+ * This function modifies vertex_shader state.
+ */
+static void renderer_set_vs(struct renderer *r, RendererVs id)
+{
+   /* create as needed */
+   if (!r->cached_vs[id]) {
+      int semantic_name = -1;
+
+      switch (id) {
+      case RENDERER_VS_PLAIN:
+         break;
+      case RENDERER_VS_COLOR:
+         semantic_name = TGSI_SEMANTIC_COLOR;
+         break;
+      case RENDERER_VS_TEXTURE:
+         semantic_name = TGSI_SEMANTIC_GENERIC;
+         break;
+      default:
+         assert(!"Unknown renderer vs id");
+         break;
+      }
+
+      r->cached_vs[id] = create_passthrough_vs(r->pipe, semantic_name);
+   }
+
+   cso_set_vertex_shader_handle(r->cso, r->cached_vs[id]);
+}
+
+/**
+ * Create a simple fragment shader that sets the depth to 0.0f.
+ */
+static void *create_scissor_fs(struct pipe_context *pipe)
+{
+   struct ureg_program *ureg;
+   struct ureg_dst out;
+   struct ureg_src imm;
+
+   ureg = ureg_create(TGSI_PROCESSOR_FRAGMENT);
+   out = ureg_DECL_output(ureg, TGSI_SEMANTIC_POSITION, 0);
+   imm = ureg_imm4f(ureg, 0.0f, 0.0f, 0.0f, 0.0f);
+
+   ureg_MOV(ureg, ureg_writemask(out, TGSI_WRITEMASK_Z), imm);
+   ureg_END(ureg);
+
+   return ureg_create_shader_and_destroy(ureg, pipe);
+}
+
+/**
+ * Create a simple fragment shader that sets the color to white.
+ */
+static void *create_white_fs(struct pipe_context *pipe)
+{
+   struct ureg_program *ureg;
+   struct ureg_dst out;
+   struct ureg_src imm;
+
+   ureg = ureg_create(TGSI_PROCESSOR_FRAGMENT);
+   out = ureg_DECL_output(ureg, TGSI_SEMANTIC_COLOR, 0);
+   imm = ureg_imm4f(ureg, 1.0f, 1.0f, 1.0f, 1.0f);
+
+   ureg_MOV(ureg, out, imm);
+   ureg_END(ureg);
+
+   return ureg_create_shader_and_destroy(ureg, pipe);
+}
+
+/**
+ * Set renderer fragment shader.
+ *
+ * This function modifies fragment_shader state.
+ */
+static void renderer_set_fs(struct renderer *r, RendererFs id)
+{
+   /* create as needed */
+   if (!r->cached_fs[id]) {
+      void *fs = NULL;
+
+      switch (id) {
+      case RENDERER_FS_COLOR:
+         fs = util_make_fragment_passthrough_shader(r->pipe,
+                          TGSI_SEMANTIC_COLOR, TGSI_INTERPOLATE_PERSPECTIVE);
+         break;
+      case RENDERER_FS_TEXTURE:
+         fs = util_make_fragment_tex_shader(r->pipe,
+               TGSI_TEXTURE_2D, TGSI_INTERPOLATE_LINEAR);
+         break;
+      case RENDERER_FS_SCISSOR:
+         fs = create_scissor_fs(r->pipe);
+         break;
+      case RENDERER_FS_WHITE:
+         fs = create_white_fs(r->pipe);
+         break;
+      default:
+         assert(!"Unknown renderer fs id");
+         break;
+      }
+
+      r->cached_fs[id] = fs;
+   }
+
+   cso_set_fragment_shader_handle(r->cso, r->cached_fs[id]);
+}
+
+typedef enum {
+   VEGA_Y0_TOP,
+   VEGA_Y0_BOTTOM
+} VegaOrientation;
+
+static void vg_set_viewport(struct renderer *r,
+                            VegaOrientation orientation)
+{
+   const struct pipe_framebuffer_state *fb = &r->g3d.fb;
+   struct pipe_viewport_state viewport;
+   VGfloat y_scale = (orientation == VEGA_Y0_BOTTOM) ? -2.f : 2.f;
+
+   viewport.scale[0] =  fb->width / 2.f;
+   viewport.scale[1] =  fb->height / y_scale;
+   viewport.scale[2] =  1.0;
+   viewport.scale[3] =  1.0;
+   viewport.translate[0] = fb->width / 2.f;
+   viewport.translate[1] = fb->height / 2.f;
+   viewport.translate[2] = 0.0;
+   viewport.translate[3] = 0.0;
+
+   cso_set_viewport(r->cso, &viewport);
+}
+
+/**
+ * Set renderer target.
+ *
+ * This function modifies framebuffer and viewport states.
+ */
+static void renderer_set_target(struct renderer *r,
+                                struct pipe_surface *cbuf,
+                                struct pipe_surface *zsbuf,
+                                VGboolean y0_top)
+{
+   struct pipe_framebuffer_state fb;
+
+   memset(&fb, 0, sizeof(fb));
+   fb.width = cbuf->width;
+   fb.height = cbuf->height;
+   fb.cbufs[0] = cbuf;
+   fb.nr_cbufs = 1;
+   fb.zsbuf = zsbuf;
+   cso_set_framebuffer(r->cso, &fb);
+
+   vg_set_viewport(r, (y0_top) ? VEGA_Y0_TOP : VEGA_Y0_BOTTOM);
+}
+
+/**
+ * Set renderer blend state.  Blending is disabled.
+ *
+ * This function modifies blend state.
+ */
+static void renderer_set_blend(struct renderer *r,
+                               VGbitfield channel_mask)
+{
+   struct pipe_blend_state blend;
+
+   memset(&blend, 0, sizeof(blend));
+
+   blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE;
+   blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
+   blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO;
+   blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
+
+   if (channel_mask & VG_RED)
+      blend.rt[0].colormask |= PIPE_MASK_R;
+   if (channel_mask & VG_GREEN)
+      blend.rt[0].colormask |= PIPE_MASK_G;
+   if (channel_mask & VG_BLUE)
+      blend.rt[0].colormask |= PIPE_MASK_B;
+   if (channel_mask & VG_ALPHA)
+      blend.rt[0].colormask |= PIPE_MASK_A;
+
+   cso_set_blend(r->cso, &blend);
+}
+
+/**
+ * Set renderer sampler and view states.
+ *
+ * This function modifies samplers and fragment_sampler_views states.
+ */
+static void renderer_set_samplers(struct renderer *r,
+                                  uint num_views,
+                                  struct pipe_sampler_view **views)
+{
+   struct pipe_sampler_state sampler;
+   unsigned tex_filter = PIPE_TEX_FILTER_NEAREST;
+   unsigned tex_wrap = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
+   uint i;
+
+   memset(&sampler, 0, sizeof(sampler));
+
+   sampler.min_img_filter = tex_filter;
+   sampler.mag_img_filter = tex_filter;
+   sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
+
+   sampler.wrap_s = tex_wrap;
+   sampler.wrap_t = tex_wrap;
+   sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
+
+   sampler.normalized_coords = 1;
+
+   /* set samplers */
+   for (i = 0; i < num_views; i++)
+      cso_single_sampler(r->cso, PIPE_SHADER_FRAGMENT, i, &sampler);
+   cso_single_sampler_done(r->cso, PIPE_SHADER_FRAGMENT);
+
+   /* set views */
+   cso_set_sampler_views(r->cso, PIPE_SHADER_FRAGMENT, num_views, views);
+}
+
+/**
+ * Set custom renderer fragment shader, and optionally set samplers and views
+ * and upload the fragment constant buffer.
+ *
+ * This function modifies fragment_shader, samplers and fragment_sampler_views
+ * states.
+ */
+static void renderer_set_custom_fs(struct renderer *renderer,
+                                   void *fs,
+                                   const struct pipe_sampler_state **samplers,
+                                   struct pipe_sampler_view **views,
+                                   VGint num_samplers,
+                                   const void *const_buffer,
+                                   VGint const_buffer_len)
+{
+   cso_set_fragment_shader_handle(renderer->cso, fs);
+
+   /* set samplers and views */
+   if (num_samplers) {
+      cso_set_samplers(renderer->cso, PIPE_SHADER_FRAGMENT, num_samplers, samplers);
+      cso_set_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT, num_samplers, views);
+   }
+
+   /* upload fs constant buffer */
+   if (const_buffer_len) {
+      struct pipe_resource *cbuf = renderer->fs_cbuf;
+
+      if (!cbuf || renderer->fs_cbuf_len != const_buffer_len ||
+          memcmp(renderer->fs_cbuf_data, const_buffer, const_buffer_len)) {
+         pipe_resource_reference(&cbuf, NULL);
+
+         cbuf = pipe_buffer_create(renderer->pipe->screen,
+               PIPE_BIND_CONSTANT_BUFFER, PIPE_USAGE_STATIC,
+               const_buffer_len);
+         pipe_buffer_write(renderer->pipe, cbuf, 0,
+               const_buffer_len, const_buffer);
+         pipe_set_constant_buffer(renderer->pipe,
+               PIPE_SHADER_FRAGMENT, 0, cbuf);
+
+         renderer->fs_cbuf = cbuf;
+         if (const_buffer_len <= sizeof(renderer->fs_cbuf_data)) {
+            memcpy(renderer->fs_cbuf_data, const_buffer, const_buffer_len);
+            renderer->fs_cbuf_len = const_buffer_len;
+         }
+         else {
+            renderer->fs_cbuf_len = 0;
+         }
+      }
+   }
+}
+
+/**
+ * Setup renderer quad position.
+ */
+static void renderer_quad_pos(struct renderer *r,
+                              VGfloat x0, VGfloat y0,
+                              VGfloat x1, VGfloat y1,
+                              VGboolean scissor)
+{
+   VGfloat z;
+
+   /* the depth test is used for scissoring */
+   z = (scissor) ? 0.0f : 1.0f;
+
+   /* positions */
+   r->vertices[0][0][0] = x0;
+   r->vertices[0][0][1] = y0;
+   r->vertices[0][0][2] = z;
+
+   r->vertices[1][0][0] = x1;
+   r->vertices[1][0][1] = y0;
+   r->vertices[1][0][2] = z;
+
+   r->vertices[2][0][0] = x1;
+   r->vertices[2][0][1] = y1;
+   r->vertices[2][0][2] = z;
+
+   r->vertices[3][0][0] = x0;
+   r->vertices[3][0][1] = y1;
+   r->vertices[3][0][2] = z;
+}
+
+/**
+ * Setup renderer quad texture coordinates.
+ */
+static void renderer_quad_texcoord(struct renderer *r,
+                                   VGfloat x0, VGfloat y0,
+                                   VGfloat x1, VGfloat y1,
+                                   VGint tex_width, VGint tex_height)
+{
+   VGfloat s0, t0, s1, t1, r0, q0;
+   VGint i;
+
+   s0 = x0 / tex_width;
+   s1 = x1 / tex_width;
+   t0 = y0 / tex_height;
+   t1 = y1 / tex_height;
+   r0 = 0.0f;
+   q0 = 1.0f;
+
+   /* texcoords */
+   r->vertices[0][1][0] = s0;
+   r->vertices[0][1][1] = t0;
+
+   r->vertices[1][1][0] = s1;
+   r->vertices[1][1][1] = t0;
+
+   r->vertices[2][1][0] = s1;
+   r->vertices[2][1][1] = t1;
+
+   r->vertices[3][1][0] = s0;
+   r->vertices[3][1][1] = t1;
 
-   /* init vertex data that doesn't change */
    for (i = 0; i < 4; i++) {
-      renderer->vertices[i][0][3] = 1.0f; /* w */
-      renderer->vertices[i][1][2] = 0.0f; /* r */
-      renderer->vertices[i][1][3] = 1.0f; /* q */
+      r->vertices[i][1][2] = r0;
+      r->vertices[i][1][3] = q0;
    }
+}
 
-   return renderer;
+/**
+ * Draw renderer quad.
+ */
+static void renderer_quad_draw(struct renderer *r)
+{
+   util_draw_user_vertex_buffer(r->cso, r->vertices, PIPE_PRIM_TRIANGLE_FAN,
+                                Elements(r->vertices),     /* verts */
+                                Elements(r->vertices[0])); /* attribs/vert */
 }
 
-void renderer_destroy(struct renderer *ctx)
+/**
+ * Prepare the renderer for copying.
+ */
+VGboolean renderer_copy_begin(struct renderer *renderer,
+                              struct pipe_surface *dst,
+                              VGboolean y0_top,
+                              struct pipe_sampler_view *src)
+{
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   /* sanity check */
+   if (!renderer_can_support(renderer,
+            dst->texture, PIPE_BIND_RENDER_TARGET) ||
+       !renderer_can_support(renderer,
+          src->texture, PIPE_BIND_SAMPLER_VIEW))
+      return VG_FALSE;
+
+   cso_save_framebuffer(renderer->cso);
+   cso_save_viewport(renderer->cso);
+   cso_save_blend(renderer->cso);
+   cso_save_samplers(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_save_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_save_fragment_shader(renderer->cso);
+   cso_save_vertex_shader(renderer->cso);
+
+   renderer_set_target(renderer, dst, NULL, y0_top);
+
+   renderer_set_blend(renderer, ~0);
+   renderer_set_samplers(renderer, 1, &src);
+
+   renderer_set_fs(renderer, RENDERER_FS_TEXTURE);
+   renderer_set_vs(renderer, RENDERER_VS_TEXTURE);
+
+   renderer_set_mvp(renderer, NULL);
+
+   /* remember the texture size */
+   renderer->u.copy.tex_width = src->texture->width0;
+   renderer->u.copy.tex_height = src->texture->height0;
+   renderer->state = RENDERER_STATE_COPY;
+
+   return VG_TRUE;
+}
+
+/**
+ * Draw into the destination rectangle given by (x, y, w, h).  The texture is
+ * sampled from within the rectangle given by (sx, sy, sw, sh).
+ *
+ * The coordinates are in surface coordinates.
+ */
+void renderer_copy(struct renderer *renderer,
+                   VGint x, VGint y, VGint w, VGint h,
+                   VGint sx, VGint sy, VGint sw, VGint sh)
+{
+   assert(renderer->state == RENDERER_STATE_COPY);
+
+   /* there is no depth buffer for scissoring anyway */
+   renderer_quad_pos(renderer, x, y, x + w, y + h, VG_FALSE);
+   renderer_quad_texcoord(renderer, sx, sy, sx + sw, sy + sh,
+         renderer->u.copy.tex_width,
+         renderer->u.copy.tex_height);
+
+   renderer_quad_draw(renderer);
+}
+
+/**
+ * End copying and restore the states.
+ */
+void renderer_copy_end(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_COPY);
+
+   cso_restore_framebuffer(renderer->cso);
+   cso_restore_viewport(renderer->cso);
+   cso_restore_blend(renderer->cso);
+   cso_restore_samplers(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_restore_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_restore_fragment_shader(renderer->cso);
+   cso_restore_vertex_shader(renderer->cso);
+
+   renderer->state = RENDERER_STATE_INIT;
+}
+
+/**
+ * Prepare the renderer for textured drawing.
+ */
+VGboolean renderer_drawtex_begin(struct renderer *renderer,
+                                 struct pipe_sampler_view *src)
+{
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   if (!renderer_can_support(renderer, src->texture, PIPE_BIND_SAMPLER_VIEW))
+      return VG_FALSE;
+
+   cso_save_blend(renderer->cso);
+   cso_save_samplers(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_save_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_save_fragment_shader(renderer->cso);
+   cso_save_vertex_shader(renderer->cso);
+
+   renderer_set_blend(renderer, ~0);
+
+   renderer_set_samplers(renderer, 1, &src);
+
+   renderer_set_fs(renderer, RENDERER_FS_TEXTURE);
+   renderer_set_vs(renderer, RENDERER_VS_TEXTURE);
+
+   renderer_set_mvp(renderer, NULL);
+
+   /* remember the texture size */
+   renderer->u.drawtex.tex_width = src->texture->width0;
+   renderer->u.drawtex.tex_height = src->texture->height0;
+   renderer->state = RENDERER_STATE_DRAWTEX;
+
+   return VG_TRUE;
+}
+
+/**
+ * Draw into the destination rectangle given by (x, y, w, h).  The texture is
+ * sampled from within the rectangle given by (sx, sy, sw, sh).
+ *
+ * The coordinates are in surface coordinates.
+ */
+void renderer_drawtex(struct renderer *renderer,
+                      VGint x, VGint y, VGint w, VGint h,
+                      VGint sx, VGint sy, VGint sw, VGint sh)
+{
+   assert(renderer->state == RENDERER_STATE_DRAWTEX);
+
+   /* with scissoring */
+   renderer_quad_pos(renderer, x, y, x + w, y + h, VG_TRUE);
+   renderer_quad_texcoord(renderer, sx, sy, sx + sw, sy + sh,
+         renderer->u.drawtex.tex_width,
+         renderer->u.drawtex.tex_height);
+
+   renderer_quad_draw(renderer);
+}
+
+/**
+ * End textured drawing and restore the states.
+ */
+void renderer_drawtex_end(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_DRAWTEX);
+
+   cso_restore_blend(renderer->cso);
+   cso_restore_samplers(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_restore_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT);
+   cso_restore_fragment_shader(renderer->cso);
+   cso_restore_vertex_shader(renderer->cso);
+
+   renderer->state = RENDERER_STATE_INIT;
+}
+
+/**
+ * Prepare the renderer for scissor update.  This will reset the depth buffer
+ * to 1.0f.
+ */
+VGboolean renderer_scissor_begin(struct renderer *renderer,
+                                 VGboolean restore_dsa)
+{
+   struct pipe_depth_stencil_alpha_state dsa;
+
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   if (restore_dsa)
+      cso_save_depth_stencil_alpha(renderer->cso);
+   cso_save_blend(renderer->cso);
+   cso_save_fragment_shader(renderer->cso);
+
+   /* enable depth writes */
+   memset(&dsa, 0, sizeof(dsa));
+   dsa.depth.enabled = 1;
+   dsa.depth.writemask = 1;
+   dsa.depth.func = PIPE_FUNC_ALWAYS;
+   cso_set_depth_stencil_alpha(renderer->cso, &dsa);
+
+   /* disable color writes */
+   renderer_set_blend(renderer, 0);
+   renderer_set_fs(renderer, RENDERER_FS_SCISSOR);
+
+   renderer_set_mvp(renderer, NULL);
+
+   renderer->u.scissor.restore_dsa = restore_dsa;
+   renderer->state = RENDERER_STATE_SCISSOR;
+
+   /* clear the depth buffer to 1.0f */
+   renderer->pipe->clear(renderer->pipe,
+         PIPE_CLEAR_DEPTHSTENCIL, NULL, 1.0f, 0);
+
+   return VG_TRUE;
+}
+
+/**
+ * Add a scissor rectangle.  Depth values inside the rectangle will be set to
+ * 0.0f.
+ */
+void renderer_scissor(struct renderer *renderer,
+                      VGint x, VGint y, VGint width, VGint height)
+{
+   assert(renderer->state == RENDERER_STATE_SCISSOR);
+
+   renderer_quad_pos(renderer, x, y, x + width, y + height, VG_FALSE);
+   renderer_quad_draw(renderer);
+}
+
+/**
+ * End scissor update and restore the states.
+ */
+void renderer_scissor_end(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_SCISSOR);
+
+   if (renderer->u.scissor.restore_dsa)
+      cso_restore_depth_stencil_alpha(renderer->cso);
+   cso_restore_blend(renderer->cso);
+   cso_restore_fragment_shader(renderer->cso);
+
+   renderer->state = RENDERER_STATE_INIT;
+}
+
+/**
+ * Prepare the renderer for clearing.
+ */
+VGboolean renderer_clear_begin(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   cso_save_blend(renderer->cso);
+   cso_save_fragment_shader(renderer->cso);
+   cso_save_vertex_shader(renderer->cso);
+
+   renderer_set_blend(renderer, ~0);
+   renderer_set_fs(renderer, RENDERER_FS_COLOR);
+   renderer_set_vs(renderer, RENDERER_VS_COLOR);
+
+   renderer_set_mvp(renderer, NULL);
+
+   renderer->state = RENDERER_STATE_CLEAR;
+
+   return VG_TRUE;
+}
+
+/**
+ * Clear the framebuffer with the specified region and color.
+ *
+ * The coordinates are in surface coordinates.
+ */
+void renderer_clear(struct renderer *renderer,
+                    VGint x, VGint y, VGint width, VGint height,
+                    const VGfloat color[4])
+{
+   VGuint i;
+
+   assert(renderer->state == RENDERER_STATE_CLEAR);
+
+   renderer_quad_pos(renderer, x, y, x + width, y + height, VG_TRUE);
+   for (i = 0; i < 4; i++)
+      memcpy(renderer->vertices[i][1], color, sizeof(VGfloat) * 4);
+
+   renderer_quad_draw(renderer);
+}
+
+/**
+ * End clearing and retore the states.
+ */
+void renderer_clear_end(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_CLEAR);
+
+   cso_restore_blend(renderer->cso);
+   cso_restore_fragment_shader(renderer->cso);
+   cso_restore_vertex_shader(renderer->cso);
+
+   renderer->state = RENDERER_STATE_INIT;
+}
+
+/**
+ * Prepare the renderer for image filtering.
+ */
+VGboolean renderer_filter_begin(struct renderer *renderer,
+                                struct pipe_resource *dst,
+                                VGboolean y0_top,
+                                VGbitfield channel_mask,
+                                const struct pipe_sampler_state **samplers,
+                                struct pipe_sampler_view **views,
+                                VGint num_samplers,
+                                void *fs,
+                                const void *const_buffer,
+                                VGint const_buffer_len)
+{
+   struct pipe_surface *surf, surf_tmpl;
+
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   if (!fs)
+      return VG_FALSE;
+   if (!renderer_can_support(renderer, dst, PIPE_BIND_RENDER_TARGET))
+      return VG_FALSE;
+
+   u_surface_default_template(&surf_tmpl, dst);
+   surf = renderer->pipe->create_surface(renderer->pipe, dst, &surf_tmpl);
+   if (!surf)
+      return VG_FALSE;
+
+   cso_save_framebuffer(renderer->cso);
+   cso_save_viewport(renderer->cso);
+   cso_save_blend(renderer->cso);
+
+   /* set the image as the target */
+   renderer_set_target(renderer, surf, NULL, y0_top);
+   pipe_surface_reference(&surf, NULL);
+
+   renderer_set_blend(renderer, channel_mask);
+
+   if (num_samplers) {
+      struct pipe_resource *tex;
+
+      cso_save_samplers(renderer->cso, PIPE_SHADER_FRAGMENT);
+      cso_save_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT);
+      cso_save_fragment_shader(renderer->cso);
+      cso_save_vertex_shader(renderer->cso);
+
+      renderer_set_custom_fs(renderer, fs,
+                             samplers, views, num_samplers,
+                             const_buffer, const_buffer_len);
+      renderer_set_vs(renderer, RENDERER_VS_TEXTURE);
+
+      tex = views[0]->texture;
+      renderer->u.filter.tex_width = tex->width0;
+      renderer->u.filter.tex_height = tex->height0;
+      renderer->u.filter.use_sampler = VG_TRUE;
+   }
+   else {
+      cso_save_fragment_shader(renderer->cso);
+
+      renderer_set_custom_fs(renderer, fs, NULL, NULL, 0,
+                             const_buffer, const_buffer_len);
+
+      renderer->u.filter.use_sampler = VG_FALSE;
+   }
+
+   renderer_set_mvp(renderer, NULL);
+
+   renderer->state = RENDERER_STATE_FILTER;
+
+   return VG_TRUE;
+}
+
+/**
+ * Draw into a rectangle of the destination with the specified region of the
+ * texture(s).
+ *
+ * The coordinates are in surface coordinates.
+ */
+void renderer_filter(struct renderer *renderer,
+                    VGint x, VGint y, VGint w, VGint h,
+                    VGint sx, VGint sy, VGint sw, VGint sh)
 {
-#if 0
-   if (ctx->fs) {
-      cso_delete_fragment_shader(ctx->cso, ctx->fs);
-      ctx->fs = NULL;
+   assert(renderer->state == RENDERER_STATE_FILTER);
+
+   renderer_quad_pos(renderer, x, y, x + w, y + h, VG_FALSE);
+   if (renderer->u.filter.use_sampler) {
+      renderer_quad_texcoord(renderer, sx, sy, sx + sw, sy + sh,
+            renderer->u.filter.tex_width,
+            renderer->u.filter.tex_height);
    }
-#endif
-   free(ctx);
+
+   renderer_quad_draw(renderer);
 }
 
-void renderer_draw_quad(struct renderer *r,
-                        VGfloat x1, VGfloat y1,
-                        VGfloat x2, VGfloat y2,
-                        VGfloat depth)
+/**
+ * End image filtering and restore the states.
+ */
+void renderer_filter_end(struct renderer *renderer)
 {
-   struct pipe_buffer *buf;
+   assert(renderer->state == RENDERER_STATE_FILTER);
 
-   buf = setup_vertex_data(r, x1, y1, x2, y2, depth);
+   if (renderer->u.filter.use_sampler) {
+      cso_restore_samplers(renderer->cso, PIPE_SHADER_FRAGMENT);
+      cso_restore_sampler_views(renderer->cso, PIPE_SHADER_FRAGMENT);
+      cso_restore_vertex_shader(renderer->cso);
+   }
 
-   if (buf) {
-      cso_set_vertex_elements(r->cso, 2, r->owner->velems);
-      util_draw_vertex_buffer(r->pipe, buf, 0,
-                              PIPE_PRIM_TRIANGLE_FAN,
-                              4,  /* verts */
-                              2); /* attribs/vert */
+   cso_restore_framebuffer(renderer->cso);
+   cso_restore_viewport(renderer->cso);
+   cso_restore_blend(renderer->cso);
+   cso_restore_fragment_shader(renderer->cso);
 
-      pipe_buffer_reference( &buf,
-                             NULL );
+   renderer->state = RENDERER_STATE_INIT;
+}
+
+/**
+ * Prepare the renderer for polygon silhouette rendering.
+ */
+VGboolean renderer_polygon_stencil_begin(struct renderer *renderer,
+                                         struct pipe_vertex_element *velem,
+                                         VGFillRule rule,
+                                         VGboolean restore_dsa)
+{
+   struct pipe_depth_stencil_alpha_state *dsa;
+   VGboolean manual_two_sides;
+
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   cso_save_vertex_elements(renderer->cso);
+   cso_save_blend(renderer->cso);
+   cso_save_depth_stencil_alpha(renderer->cso);
+
+   cso_set_vertex_elements(renderer->cso, 1, velem);
+
+   /* disable color writes */
+   renderer_set_blend(renderer, 0);
+
+   manual_two_sides = VG_FALSE;
+   dsa = &renderer->u.polygon_stencil.dsa;
+   memset(dsa, 0, sizeof(*dsa));
+   if (rule == VG_EVEN_ODD) {
+      dsa->stencil[0].enabled = 1;
+      dsa->stencil[0].writemask = 1;
+      dsa->stencil[0].fail_op = PIPE_STENCIL_OP_KEEP;
+      dsa->stencil[0].zfail_op = PIPE_STENCIL_OP_KEEP;
+      dsa->stencil[0].zpass_op = PIPE_STENCIL_OP_INVERT;
+      dsa->stencil[0].func = PIPE_FUNC_ALWAYS;
+      dsa->stencil[0].valuemask = ~0;
+   }
+   else {
+      assert(rule == VG_NON_ZERO);
+
+      /* front face */
+      dsa->stencil[0].enabled = 1;
+      dsa->stencil[0].writemask = ~0;
+      dsa->stencil[0].fail_op = PIPE_STENCIL_OP_KEEP;
+      dsa->stencil[0].zfail_op = PIPE_STENCIL_OP_KEEP;
+      dsa->stencil[0].zpass_op = PIPE_STENCIL_OP_INCR_WRAP;
+      dsa->stencil[0].func = PIPE_FUNC_ALWAYS;
+      dsa->stencil[0].valuemask = ~0;
+
+      if (renderer->pipe->screen->get_param(renderer->pipe->screen,
+                                            PIPE_CAP_TWO_SIDED_STENCIL)) {
+         /* back face */
+         dsa->stencil[1] = dsa->stencil[0];
+         dsa->stencil[1].zpass_op = PIPE_STENCIL_OP_DECR_WRAP;
+      }
+      else {
+         manual_two_sides = VG_TRUE;
+      }
    }
+   cso_set_depth_stencil_alpha(renderer->cso, dsa);
+
+   if (manual_two_sides)
+      cso_save_rasterizer(renderer->cso);
+
+   renderer->u.polygon_stencil.manual_two_sides = manual_two_sides;
+   renderer->u.polygon_stencil.restore_dsa = restore_dsa;
+   renderer->state = RENDERER_STATE_POLYGON_STENCIL;
+
+   return VG_TRUE;
 }
 
-void renderer_draw_texture(struct renderer *r,
-                           struct pipe_texture *tex,
-                           VGfloat x1offset, VGfloat y1offset,
-                           VGfloat x2offset, VGfloat y2offset,
-                           VGfloat x1, VGfloat y1,
-                           VGfloat x2, VGfloat y2)
+/**
+ * Render a polygon silhouette to stencil buffer.
+ */
+void renderer_polygon_stencil(struct renderer *renderer,
+                              struct pipe_vertex_buffer *vbuf,
+                              VGuint mode, VGuint start, VGuint count)
 {
-   struct pipe_context *pipe = r->pipe;
-   struct pipe_buffer *buf;
-   VGfloat s0, t0, s1, t1;
+   assert(renderer->state == RENDERER_STATE_POLYGON_STENCIL);
 
-   assert(tex->width0 != 0);
-   assert(tex->height0 != 0);
+   cso_set_vertex_buffers(renderer->cso, 0, 1, vbuf);
 
-   s0 = x1offset / tex->width0;
-   s1 = x2offset / tex->width0;
-   t0 = y1offset / tex->height0;
-   t1 = y2offset / tex->height0;
+   if (!renderer->u.polygon_stencil.manual_two_sides) {
+      cso_draw_arrays(renderer->cso, mode, start, count);
+   }
+   else {
+      struct pipe_rasterizer_state raster;
+      struct pipe_depth_stencil_alpha_state dsa;
 
-   cso_save_vertex_shader(r->cso);
-   /* shaders */
-   cso_set_vertex_shader_handle(r->cso, vg_texture_vs(r->owner));
-
-   /* draw quad */
-   buf = setup_vertex_data_tex(r, x1, y1, x2, y2,
-                               s0, t0, s1, t1, 0.0f);
-
-   if (buf) {
-      cso_set_vertex_elements(r->cso, 2, r->owner->velems);
-      util_draw_vertex_buffer(pipe, buf, 0,
-                           PIPE_PRIM_TRIANGLE_FAN,
-                           4,  /* verts */
-                           2); /* attribs/vert */
-
-      pipe_buffer_reference( &buf,
-                             NULL );
+      raster = renderer->g3d.rasterizer;
+      dsa = renderer->u.polygon_stencil.dsa;
+
+      /* front */
+      raster.cull_face = PIPE_FACE_BACK;
+      dsa.stencil[0].zpass_op = PIPE_STENCIL_OP_INCR_WRAP;
+
+      cso_set_rasterizer(renderer->cso, &raster);
+      cso_set_depth_stencil_alpha(renderer->cso, &dsa);
+      cso_draw_arrays(renderer->cso, mode, start, count);
+
+      /* back */
+      raster.cull_face = PIPE_FACE_FRONT;
+      dsa.stencil[0].zpass_op = PIPE_STENCIL_OP_DECR_WRAP;
+
+      cso_set_rasterizer(renderer->cso, &raster);
+      cso_set_depth_stencil_alpha(renderer->cso, &dsa);
+      cso_draw_arrays(renderer->cso, mode, start, count);
    }
+}
 
-   cso_restore_vertex_shader(r->cso);
+/**
+ * End polygon silhouette rendering.
+ */
+void renderer_polygon_stencil_end(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_POLYGON_STENCIL);
+
+   if (renderer->u.polygon_stencil.manual_two_sides)
+      cso_restore_rasterizer(renderer->cso);
+
+   cso_restore_vertex_elements(renderer->cso);
+
+   /* restore color writes */
+   cso_restore_blend(renderer->cso);
+
+   if (renderer->u.polygon_stencil.restore_dsa)
+      cso_restore_depth_stencil_alpha(renderer->cso);
+
+   renderer->state = RENDERER_STATE_INIT;
 }
 
-void renderer_copy_texture(struct renderer *ctx,
-                           struct pipe_sampler_view *src,
-                           VGfloat sx1, VGfloat sy1,
-                           VGfloat sx2, VGfloat sy2,
-                           struct pipe_texture *dst,
-                           VGfloat dx1, VGfloat dy1,
-                           VGfloat dx2, VGfloat dy2)
+/**
+ * Prepare the renderer for polygon filling.
+ */
+VGboolean renderer_polygon_fill_begin(struct renderer *renderer,
+                                      VGboolean save_dsa)
 {
-   struct pipe_context *pipe = ctx->pipe;
-   struct pipe_screen *screen = pipe->screen;
-   struct pipe_texture *tex = src->texture;
-   struct pipe_buffer *buf;
-   struct pipe_surface *dst_surf = screen->get_tex_surface(
-      screen, dst, 0, 0, 0,
-      PIPE_BUFFER_USAGE_GPU_WRITE);
-   struct pipe_framebuffer_state fb;
-   float s0, t0, s1, t1;
+   struct pipe_depth_stencil_alpha_state dsa;
 
-   assert(tex->width0 != 0);
-   assert(tex->height0 != 0);
-   assert(dst->width0 != 0);
-   assert(dst->height0 != 0);
-
-#if 0
-   debug_printf("copy texture [%f, %f, %f, %f], [%f, %f, %f, %f]\n",
-                sx1, sy1, sx2, sy2, dx1, dy1, dx2, dy2);
-#endif
-
-#if 1
-   s0 = sx1 / tex->width0;
-   s1 = sx2 / tex->width0;
-   t0 = sy1 / tex->height0;
-   t1 = sy2 / tex->height0;
-#else
-   s0 = 0;
-   s1 = 1;
-   t0 = 0;
-   t1 = 1;
-#endif
-
-   assert(screen->is_format_supported(screen, dst_surf->format, PIPE_TEXTURE_2D,
-                                      PIPE_TEXTURE_USAGE_RENDER_TARGET, 0));
-
-   /* save state (restored below) */
-   cso_save_blend(ctx->cso);
-   cso_save_samplers(ctx->cso);
-   cso_save_fragment_sampler_views(ctx->cso);
-   cso_save_framebuffer(ctx->cso);
-   cso_save_fragment_shader(ctx->cso);
-   cso_save_vertex_shader(ctx->cso);
-
-   cso_save_viewport(ctx->cso);
-
-
-   /* set misc state we care about */
-   {
-      struct pipe_blend_state blend;
-      memset(&blend, 0, sizeof(blend));
-      blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE;
-      blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
-      blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO;
-      blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
-      blend.rt[0].colormask = PIPE_MASK_RGBA;
-      cso_set_blend(ctx->cso, &blend);
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   if (save_dsa)
+      cso_save_depth_stencil_alpha(renderer->cso);
+
+   /* setup stencil ops */
+   memset(&dsa, 0, sizeof(dsa));
+   dsa.stencil[0].enabled = 1;
+   dsa.stencil[0].func = PIPE_FUNC_NOTEQUAL;
+   dsa.stencil[0].fail_op = PIPE_STENCIL_OP_REPLACE;
+   dsa.stencil[0].zfail_op = PIPE_STENCIL_OP_REPLACE;
+   dsa.stencil[0].zpass_op = PIPE_STENCIL_OP_REPLACE;
+   dsa.stencil[0].valuemask = ~0;
+   dsa.stencil[0].writemask = ~0;
+   dsa.depth = renderer->g3d.dsa.depth;
+   cso_set_depth_stencil_alpha(renderer->cso, &dsa);
+
+   renderer->state = RENDERER_STATE_POLYGON_FILL;
+
+   return VG_TRUE;
+}
+
+/**
+ * Fill a polygon.
+ */
+void renderer_polygon_fill(struct renderer *renderer,
+                           VGfloat min_x, VGfloat min_y,
+                           VGfloat max_x, VGfloat max_y)
+{
+   assert(renderer->state == RENDERER_STATE_POLYGON_FILL);
+
+   renderer_quad_pos(renderer, min_x, min_y, max_x, max_y, VG_TRUE);
+   renderer_quad_draw(renderer);
+}
+
+/**
+ * End polygon filling.
+ */
+void renderer_polygon_fill_end(struct renderer *renderer)
+{
+   assert(renderer->state == RENDERER_STATE_POLYGON_FILL);
+
+   cso_restore_depth_stencil_alpha(renderer->cso);
+
+   renderer->state = RENDERER_STATE_INIT;
+}
+
+struct renderer * renderer_create(struct vg_context *owner)
+{
+   struct renderer *renderer;
+   struct pipe_rasterizer_state *raster;
+   struct pipe_stencil_ref sr;
+   VGint i;
+
+   renderer = CALLOC_STRUCT(renderer);
+   if (!renderer)
+      return NULL;
+
+   renderer->pipe = owner->pipe;
+   renderer->cso = owner->cso_context;
+
+   /* init vertex data that doesn't change */
+   for (i = 0; i < 4; i++)
+      renderer->vertices[i][0][3] = 1.0f; /* w */
+
+   for (i = 0; i < 2; i++) {
+      renderer->velems[i].src_offset = i * 4 * sizeof(float);
+      renderer->velems[i].instance_divisor = 0;
+      renderer->velems[i].vertex_buffer_index = 0;
+      renderer->velems[i].src_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
    }
+   cso_set_vertex_elements(renderer->cso, 2, renderer->velems);
+
+   /* GL rasterization rules */
+   raster = &renderer->g3d.rasterizer;
+   memset(raster, 0, sizeof(*raster));
+   raster->half_pixel_center = 1;
+   raster->bottom_edge_rule = 1;
+   raster->depth_clip = 1;
+   cso_set_rasterizer(renderer->cso, raster);
+
+   /* fixed at 0 */
+   memset(&sr, 0, sizeof(sr));
+   cso_set_stencil_ref(renderer->cso, &sr);
+
+   renderer_set_vs(renderer, RENDERER_VS_PLAIN);
+
+   renderer->state = RENDERER_STATE_INIT;
+
+   return renderer;
+}
+
+void renderer_destroy(struct renderer *ctx)
+{
+   int i;
 
-   /* sampler */
-   {
-      struct pipe_sampler_state sampler;
-      memset(&sampler, 0, sizeof(sampler));
-      sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
-      sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
-      sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
-      sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
-      sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST;
-      sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
-      sampler.normalized_coords = 1;
-      cso_single_sampler(ctx->cso, 0, &sampler);
-      cso_single_sampler_done(ctx->cso);
+   for (i = 0; i < NUM_RENDERER_VS; i++) {
+      if (ctx->cached_vs[i])
+         cso_delete_vertex_shader(ctx->cso, ctx->cached_vs[i]);
    }
+   for (i = 0; i < NUM_RENDERER_FS; i++) {
+      if (ctx->cached_fs[i])
+         cso_delete_fragment_shader(ctx->cso, ctx->cached_fs[i]);
+   }
+
+   pipe_resource_reference(&ctx->vs_cbuf, NULL);
+   pipe_resource_reference(&ctx->fs_cbuf, NULL);
 
-   vg_set_viewport(ctx->owner, VEGA_Y0_TOP);
+   FREE(ctx);
+}
+
+static void update_clip_state(struct renderer *renderer,
+                              const struct vg_state *state)
+{
+   struct pipe_depth_stencil_alpha_state *dsa = &renderer->g3d.dsa;
+
+   memset(dsa, 0, sizeof(struct pipe_depth_stencil_alpha_state));
+
+   if (state->scissoring) {
+      struct pipe_framebuffer_state *fb = &renderer->g3d.fb;
+      int i;
+
+      renderer_scissor_begin(renderer, VG_FALSE);
+
+      for (i = 0; i < state->scissor_rects_num; ++i) {
+         const float x      = state->scissor_rects[i * 4 + 0].f;
+         const float y      = state->scissor_rects[i * 4 + 1].f;
+         const float width  = state->scissor_rects[i * 4 + 2].f;
+         const float height = state->scissor_rects[i * 4 + 3].f;
+         VGint x0, y0, x1, y1, iw, ih;
+
+         x0 = (VGint) x;
+         y0 = (VGint) y;
+         if (x0 < 0)
+            x0 = 0;
+         if (y0 < 0)
+            y0 = 0;
+
+         /* note that x1 and y1 are exclusive */
+         x1 = (VGint) ceilf(x + width);
+         y1 = (VGint) ceilf(y + height);
+         if (x1 > fb->width)
+            x1 = fb->width;
+         if (y1 > fb->height)
+            y1 = fb->height;
+
+         iw = x1 - x0;
+         ih = y1 - y0;
+         if (iw > 0 && ih> 0 )
+            renderer_scissor(renderer, x0, y0, iw, ih);
+      }
+
+      renderer_scissor_end(renderer);
+
+      dsa->depth.enabled = 1; /* glEnable(GL_DEPTH_TEST); */
+      dsa->depth.writemask = 0;/*glDepthMask(FALSE);*/
+      dsa->depth.func = PIPE_FUNC_GEQUAL;
+   }
+}
 
-   /* texture */
-   cso_set_fragment_sampler_views(ctx->cso, 1, &src);
+static void renderer_validate_blend(struct renderer *renderer,
+                                     const struct vg_state *state,
+                                     enum pipe_format fb_format)
+{
+   struct pipe_blend_state blend;
 
-   /* shaders */
-   cso_set_vertex_shader_handle(ctx->cso, vg_texture_vs(ctx->owner));
-   cso_set_fragment_shader_handle(ctx->cso, ctx->fs);
+   memset(&blend, 0, sizeof(blend));
+   blend.rt[0].colormask = PIPE_MASK_RGBA;
+   blend.rt[0].rgb_src_factor   = PIPE_BLENDFACTOR_ONE;
+   blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
+   blend.rt[0].rgb_dst_factor   = PIPE_BLENDFACTOR_ZERO;
+   blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
 
-   /* drawing dest */
-   memset(&fb, 0, sizeof(fb));
-   fb.width = dst_surf->width;
-   fb.height = dst_surf->height;
-   fb.nr_cbufs = 1;
-   fb.cbufs[0] = dst_surf;
-   {
-      VGint i;
-      for (i = 1; i < PIPE_MAX_COLOR_BUFS; ++i)
-         fb.cbufs[i] = 0;
+   /* TODO alpha masking happens after blending? */
+
+   switch (state->blend_mode) {
+   case VG_BLEND_SRC:
+      blend.rt[0].rgb_src_factor   = PIPE_BLENDFACTOR_ONE;
+      blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
+      blend.rt[0].rgb_dst_factor   = PIPE_BLENDFACTOR_ZERO;
+      blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
+      break;
+   case VG_BLEND_SRC_OVER:
+      /* use the blend state only when there is no alpha channel */
+      if (!util_format_has_alpha(fb_format)) {
+         blend.rt[0].rgb_src_factor   = PIPE_BLENDFACTOR_SRC_ALPHA;
+         blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
+         blend.rt[0].rgb_dst_factor   = PIPE_BLENDFACTOR_INV_SRC_ALPHA;
+         blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_INV_SRC_ALPHA;
+         blend.rt[0].blend_enable = 1;
+      }
+      break;
+   case VG_BLEND_SRC_IN:
+      blend.rt[0].rgb_src_factor   = PIPE_BLENDFACTOR_ONE;
+      blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_DST_ALPHA;
+      blend.rt[0].rgb_dst_factor   = PIPE_BLENDFACTOR_ZERO;
+      blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
+      blend.rt[0].blend_enable = 1;
+      break;
+   case VG_BLEND_DST_IN:
+      blend.rt[0].rgb_src_factor   = PIPE_BLENDFACTOR_ZERO;
+      blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ZERO;
+      blend.rt[0].rgb_dst_factor   = PIPE_BLENDFACTOR_ONE;
+      blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_SRC_ALPHA;
+      blend.rt[0].blend_enable = 1;
+      break;
+   case VG_BLEND_DST_OVER:
+   case VG_BLEND_MULTIPLY:
+   case VG_BLEND_SCREEN:
+   case VG_BLEND_DARKEN:
+   case VG_BLEND_LIGHTEN:
+   case VG_BLEND_ADDITIVE:
+      /* need a shader */
+      break;
+   default:
+      assert(!"not implemented blend mode");
+      break;
    }
-   cso_set_framebuffer(ctx->cso, &fb);
-
-   /* draw quad */
-   buf = setup_vertex_data_tex(ctx,
-                         dx1, dy1,
-                         dx2, dy2,
-                         s0, t0, s1, t1,
-                         0.0f);
-
-   if (buf) {
-      cso_set_vertex_elements(ctx->cso, 2, ctx->owner->velems);
-      util_draw_vertex_buffer(ctx->pipe, buf, 0,
-                              PIPE_PRIM_TRIANGLE_FAN,
-                              4,  /* verts */
-                              2); /* attribs/vert */
-
-      pipe_buffer_reference( &buf,
-                             NULL );
+
+   cso_set_blend(renderer->cso, &blend);
+}
+
+/**
+ * Propogate OpenVG state changes to the renderer.  Only framebuffer, blending
+ * and scissoring states are relevant here.
+ */
+void renderer_validate(struct renderer *renderer,
+                       VGbitfield dirty,
+                       const struct st_framebuffer *stfb,
+                       const struct vg_state *state)
+{
+   assert(renderer->state == RENDERER_STATE_INIT);
+
+   dirty |= renderer->dirty;
+   renderer->dirty = 0;
+
+   if (dirty & FRAMEBUFFER_DIRTY) {
+      struct pipe_framebuffer_state *fb = &renderer->g3d.fb;
+      struct matrix *proj = &renderer->projection;
+
+      memset(fb, 0, sizeof(struct pipe_framebuffer_state));
+      fb->width  = stfb->width;
+      fb->height = stfb->height;
+      fb->nr_cbufs = 1;
+      fb->cbufs[0] = stfb->strb->surface;
+      fb->zsbuf = stfb->dsrb->surface;
+
+      cso_set_framebuffer(renderer->cso, fb);
+      vg_set_viewport(renderer, VEGA_Y0_BOTTOM);
+
+      matrix_load_identity(proj);
+      matrix_translate(proj, -1.0f, -1.0f);
+      matrix_scale(proj, 2.0f / fb->width, 2.0f / fb->height);
+
+      /* we also got a new depth buffer */
+      if (dirty & DEPTH_STENCIL_DIRTY) {
+         renderer->pipe->clear(renderer->pipe,
+               PIPE_CLEAR_DEPTHSTENCIL, NULL, 0.0, 0);
+      }
    }
 
-   /* restore state we changed */
-   cso_restore_blend(ctx->cso);
-   cso_restore_samplers(ctx->cso);
-   cso_restore_fragment_sampler_views(ctx->cso);
-   cso_restore_framebuffer(ctx->cso);
-   cso_restore_vertex_shader(ctx->cso);
-   cso_restore_fragment_shader(ctx->cso);
-   cso_restore_viewport(ctx->cso);
+   /* must be last because it renders to the depth buffer*/
+   if (dirty & DEPTH_STENCIL_DIRTY) {
+      update_clip_state(renderer, state);
+      cso_set_depth_stencil_alpha(renderer->cso, &renderer->g3d.dsa);
+   }
 
-   pipe_surface_reference(&dst_surf, NULL);
+   if (dirty & BLEND_DIRTY)
+      renderer_validate_blend(renderer, state, stfb->strb->format);
+}
+
+/**
+ * Prepare the renderer for OpenVG pipeline.
+ */
+void renderer_validate_for_shader(struct renderer *renderer,
+                                  const struct pipe_sampler_state **samplers,
+                                  struct pipe_sampler_view **views,
+                                  VGint num_samplers,
+                                  const struct matrix *modelview,
+                                  void *fs,
+                                  const void *const_buffer,
+                                  VGint const_buffer_len)
+{
+   struct matrix mvp = renderer->projection;
+
+   /* will be used in POLYGON_STENCIL and POLYGON_FILL */
+   matrix_mult(&mvp, modelview);
+   renderer_set_mvp(renderer, &mvp);
+
+   renderer_set_custom_fs(renderer, fs,
+                          samplers, views, num_samplers,
+                          const_buffer, const_buffer_len);
+}
+
+void renderer_validate_for_mask_rendering(struct renderer *renderer,
+                                          struct pipe_surface *dst,
+                                          const struct matrix *modelview)
+{
+   struct matrix mvp = renderer->projection;
+
+   /* will be used in POLYGON_STENCIL and POLYGON_FILL */
+   matrix_mult(&mvp, modelview);
+   renderer_set_mvp(renderer, &mvp);
+
+   renderer_set_target(renderer, dst, renderer->g3d.fb.zsbuf, VG_FALSE);
+   renderer_set_blend(renderer, ~0);
+   renderer_set_fs(renderer, RENDERER_FS_WHITE);
+
+   /* set internal dirty flags (hacky!) */
+   renderer->dirty = FRAMEBUFFER_DIRTY | BLEND_DIRTY;
 }
 
 void renderer_copy_surface(struct renderer *ctx,
@@ -407,13 +1422,11 @@ void renderer_copy_surface(struct renderer *ctx,
 {
    struct pipe_context *pipe = ctx->pipe;
    struct pipe_screen *screen = pipe->screen;
-   struct pipe_buffer *buf;
    struct pipe_sampler_view view_templ;
    struct pipe_sampler_view *view;
-   struct pipe_texture texTemp, *tex;
-   struct pipe_surface *texSurf;
-   struct pipe_framebuffer_state fb;
-   struct st_framebuffer *stfb = ctx->owner->draw_buffer;
+   struct pipe_box src_box;
+   struct pipe_resource texTemp, *tex;
+   const struct pipe_framebuffer_state *fb = &ctx->g3d.fb;
    const int srcW = abs(srcX1 - srcX0);
    const int srcH = abs(srcY1 - srcY0);
    const int srcLeft = MIN2(srcX0, srcX1);
@@ -437,11 +1450,11 @@ void renderer_copy_surface(struct renderer *ctx,
    }
 
    assert(screen->is_format_supported(screen, src->format, PIPE_TEXTURE_2D,
-                                      PIPE_TEXTURE_USAGE_SAMPLER, 0));
+                                      0, PIPE_BIND_SAMPLER_VIEW));
    assert(screen->is_format_supported(screen, dst->format, PIPE_TEXTURE_2D,
-                                      PIPE_TEXTURE_USAGE_SAMPLER, 0));
+                                      0, PIPE_BIND_SAMPLER_VIEW));
    assert(screen->is_format_supported(screen, dst->format, PIPE_TEXTURE_2D,
-                                      PIPE_TEXTURE_USAGE_RENDER_TARGET, 0));
+                                      0, PIPE_BIND_RENDER_TARGET));
 
    /*
     * XXX for now we're always creating a temporary texture.
@@ -456,8 +1469,10 @@ void renderer_copy_surface(struct renderer *ctx,
    texTemp.width0 = srcW;
    texTemp.height0 = srcH;
    texTemp.depth0 = 1;
+   texTemp.array_size = 1;
+   texTemp.bind = PIPE_BIND_SAMPLER_VIEW;
 
-   tex = screen->texture_create(screen, &texTemp);
+   tex = screen->resource_create(screen, &texTemp);
    if (!tex)
       return;
 
@@ -467,113 +1482,39 @@ void renderer_copy_surface(struct renderer *ctx,
    if (!view)
       return;
 
-   texSurf = screen->get_tex_surface(screen, tex, 0, 0, 0,
-                                     PIPE_BUFFER_USAGE_GPU_WRITE);
-
-   /* load temp texture */
-   if (pipe->surface_copy) {
-      pipe->surface_copy(pipe,
-                         texSurf, 0, 0,   /* dest */
-                         src, srcLeft, srcTop, /* src */
-                         srcW, srcH);     /* size */
-   } else {
-      util_surface_copy(pipe, FALSE,
-                        texSurf, 0, 0,   /* dest */
-                        src, srcLeft, srcTop, /* src */
-                        srcW, srcH);     /* size */
-   }
+   u_box_2d_zslice(srcLeft, srcTop, src->u.tex.first_layer, srcW, srcH, &src_box);
 
-   /* free the surface, update the texture if necessary.*/
-   screen->tex_surface_destroy(texSurf);
-
-   /* save state (restored below) */
-   cso_save_blend(ctx->cso);
-   cso_save_samplers(ctx->cso);
-   cso_save_fragment_sampler_views(ctx->cso);
-   cso_save_framebuffer(ctx->cso);
-   cso_save_fragment_shader(ctx->cso);
-   cso_save_vertex_shader(ctx->cso);
-   cso_save_viewport(ctx->cso);
-
-   /* set misc state we care about */
-   {
-      struct pipe_blend_state blend;
-      memset(&blend, 0, sizeof(blend));
-      blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE;
-      blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
-      blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ZERO;
-      blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ZERO;
-      blend.rt[0].colormask = PIPE_MASK_RGBA;
-      cso_set_blend(ctx->cso, &blend);
-   }
+   pipe->resource_copy_region(pipe,
+                              tex, 0, 0, 0, 0,  /* dest */
+                              src->texture, 0, &src_box);
 
-   vg_set_viewport(ctx->owner, VEGA_Y0_TOP);
-
-   /* sampler */
-   {
-      struct pipe_sampler_state sampler;
-      memset(&sampler, 0, sizeof(sampler));
-      sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
-      sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
-      sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
-      sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
-      sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST;
-      sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
-      sampler.normalized_coords = 1;
-      cso_single_sampler(ctx->cso, 0, &sampler);
-      cso_single_sampler_done(ctx->cso);
-   }
+   assert(floatsEqual(z, 0.0f));
 
-   /* texture */
-   cso_set_fragment_sampler_views(ctx->cso, 1, &view);
-
-   /* shaders */
-   cso_set_fragment_shader_handle(ctx->cso, ctx->fs);
-   cso_set_vertex_shader_handle(ctx->cso, vg_texture_vs(ctx->owner));
-
-   /* drawing dest */
-   if (stfb->strb->surface != dst) {
-      memset(&fb, 0, sizeof(fb));
-      fb.width = dst->width;
-      fb.height = dst->height;
-      fb.nr_cbufs = 1;
-      fb.cbufs[0] = dst;
-      fb.zsbuf = stfb->dsrb->surface;
-      cso_set_framebuffer(ctx->cso, &fb);
-   }
+   /* draw */
+   if (fb->cbufs[0] == dst) {
+      /* transform back to surface coordinates */
+      dstY0 = dst->height - dstY0;
+      dstY1 = dst->height - dstY1;
 
-   /* draw quad */
-   buf = setup_vertex_data(ctx,
-                           (float) dstX0, (float) dstY0,
-                           (float) dstX1, (float) dstY1, z);
-
-   if (buf) {
-      cso_set_vertex_elements(ctx->cso, 2, ctx->owner->velems);
-      util_draw_vertex_buffer(ctx->pipe, buf, 0,
-                              PIPE_PRIM_TRIANGLE_FAN,
-                              4,  /* verts */
-                              2); /* attribs/vert */
-
-      pipe_buffer_reference( &buf,
-                             NULL );
+      if (renderer_drawtex_begin(ctx, view)) {
+         renderer_drawtex(ctx,
+               dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0,
+               0, 0, view->texture->width0, view->texture->height0);
+         renderer_drawtex_end(ctx);
+      }
+   }
+   else {
+      if (renderer_copy_begin(ctx, dst, VG_TRUE, view)) {
+         renderer_copy(ctx,
+               dstX0, dstY0, dstX1 - dstX0, dstY1 - dstY0,
+               0, 0, view->texture->width0, view->texture->height0);
+         renderer_copy_end(ctx);
+      }
    }
-
-
-   /* restore state we changed */
-   cso_restore_blend(ctx->cso);
-   cso_restore_samplers(ctx->cso);
-   cso_restore_fragment_sampler_views(ctx->cso);
-   cso_restore_framebuffer(ctx->cso);
-   cso_restore_fragment_shader(ctx->cso);
-   cso_restore_vertex_shader(ctx->cso);
-   cso_restore_viewport(ctx->cso);
-
-   pipe_texture_reference(&tex, NULL);
-   pipe_sampler_view_reference(&view, NULL);
 }
 
 void renderer_texture_quad(struct renderer *r,
-                           struct pipe_texture *tex,
+                           struct pipe_resource *tex,
                            VGfloat x1offset, VGfloat y1offset,
                            VGfloat x2offset, VGfloat y2offset,
                            VGfloat x1, VGfloat y1,
@@ -581,36 +1522,38 @@ void renderer_texture_quad(struct renderer *r,
                            VGfloat x3, VGfloat y3,
                            VGfloat x4, VGfloat y4)
 {
-   struct pipe_context *pipe = r->pipe;
-   struct pipe_buffer *buf;
-   VGfloat s0, t0, s1, t1;
+   const VGfloat z = 0.0f;
 
+   assert(r->state == RENDERER_STATE_INIT);
    assert(tex->width0 != 0);
    assert(tex->height0 != 0);
 
-   s0 = x1offset / tex->width0;
-   s1 = x2offset / tex->width0;
-   t0 = y1offset / tex->height0;
-   t1 = y2offset / tex->height0;
-
    cso_save_vertex_shader(r->cso);
-   /* shaders */
-   cso_set_vertex_shader_handle(r->cso, vg_texture_vs(r->owner));
-
-   /* draw quad */
-   buf = setup_vertex_data_qtex(r, x1, y1, x2, y2, x3, y3, x4, y4,
-                          s0, t0, s1, t1, 0.0f);
-
-   if (buf) {
-      cso_set_vertex_elements(r->cso, 2, r->owner->velems);
-      util_draw_vertex_buffer(pipe, buf, 0,
-                              PIPE_PRIM_TRIANGLE_FAN,
-                              4,  /* verts */
-                              2); /* attribs/vert */
-
-      pipe_buffer_reference(&buf,
-                            NULL);
-   }
+
+   renderer_set_vs(r, RENDERER_VS_TEXTURE);
+
+   /* manually set up positions */
+   r->vertices[0][0][0] = x1;
+   r->vertices[0][0][1] = y1;
+   r->vertices[0][0][2] = z;
+
+   r->vertices[1][0][0] = x2;
+   r->vertices[1][0][1] = y2;
+   r->vertices[1][0][2] = z;
+
+   r->vertices[2][0][0] = x3;
+   r->vertices[2][0][1] = y3;
+   r->vertices[2][0][2] = z;
+
+   r->vertices[3][0][0] = x4;
+   r->vertices[3][0][1] = y4;
+   r->vertices[3][0][2] = z;
+
+   /* texcoords */
+   renderer_quad_texcoord(r, x1offset, y1offset,
+         x2offset, y2offset, tex->width0, tex->height0);
+
+   renderer_quad_draw(r);
 
    cso_restore_vertex_shader(r->cso);
 }