/**************************************************************************
 *
 * 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
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

#include "renderer.h"

#include "vg_context.h"

#include "pipe/p_context.h"
#include "pipe/p_state.h"
#include "util/u_inlines.h"
#include "pipe/p_screen.h"
#include "pipe/p_shader_tokens.h"

#include "util/u_draw_quad.h"
#include "util/u_simple_shaders.h"
#include "util/u_memory.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 cso_context *cso;

   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;
};

/**
 * 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_screen *screen = renderer->pipe->screen;

   return screen->is_format_supported(screen,
         res->format, res->target, 0, bindings);
}

/**
 * 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;

   /* 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_DEFAULT,
                             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;

   /* 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]);
   }

   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,
                          TRUE);
         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_DEFAULT,
               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;

   for (i = 0; i < 4; i++) {
      r->vertices[i][1][2] = r0;
      r->vertices[i][1][3] = q0;
   }
}

/**
 * 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 */
}

/**
 * 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)
{
   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);
   }

   renderer_quad_draw(renderer);
}

/**
 * End image filtering and restore the states.
 */
void renderer_filter_end(struct renderer *renderer)
{
   assert(renderer->state == RENDERER_STATE_FILTER);

   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);
   }

   cso_restore_framebuffer(renderer->cso);
   cso_restore_viewport(renderer->cso);
   cso_restore_blend(renderer->cso);
   cso_restore_fragment_shader(renderer->cso);

   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;
}

/**
 * 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)
{
   assert(renderer->state == RENDERER_STATE_POLYGON_STENCIL);

   cso_set_vertex_buffers(renderer->cso, 0, 1, vbuf);

   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;

      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);
   }
}

/**
 * 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;
}

/**
 * Prepare the renderer for polygon filling.
 */
VGboolean renderer_polygon_fill_begin(struct renderer *renderer,
                                      VGboolean save_dsa)
{
   struct pipe_depth_stencil_alpha_state dsa;

   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;

   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);

   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;
   }
}

static void renderer_validate_blend(struct renderer *renderer,
                                     const struct vg_state *state,
                                     enum pipe_format fb_format)
{
   struct pipe_blend_state blend;

   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;

   /* 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_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);
      }
   }

   /* 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);
   }

   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,
                           struct pipe_surface *src,
                           int srcX0, int srcY0,
                           int srcX1, int srcY1,
                           struct pipe_surface *dst,
                           int dstX0, int dstY0,
                           int dstX1, int dstY1,
                           float z, unsigned filter)
{
   struct pipe_context *pipe = ctx->pipe;
   struct pipe_screen *screen = pipe->screen;
   struct pipe_sampler_view view_templ;
   struct pipe_sampler_view *view;
   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);
   const int srcTop = MIN2(srcY0, srcY1);

   assert(filter == PIPE_TEX_MIPFILTER_NEAREST ||
          filter == PIPE_TEX_MIPFILTER_LINEAR);

   if (srcLeft != srcX0) {
      /* left-right flip */
      int tmp = dstX0;
      dstX0 = dstX1;
      dstX1 = tmp;
   }

   if (srcTop != srcY0) {
      /* up-down flip */
      int tmp = dstY0;
      dstY0 = dstY1;
      dstY1 = tmp;
   }

   assert(screen->is_format_supported(screen, src->format, PIPE_TEXTURE_2D,
                                      0, PIPE_BIND_SAMPLER_VIEW));
   assert(screen->is_format_supported(screen, dst->format, PIPE_TEXTURE_2D,
                                      0, PIPE_BIND_SAMPLER_VIEW));
   assert(screen->is_format_supported(screen, dst->format, PIPE_TEXTURE_2D,
                                      0, PIPE_BIND_RENDER_TARGET));

   /*
    * XXX for now we're always creating a temporary texture.
    * Strictly speaking that's not always needed.
    */

   /* create temp texture */
   memset(&texTemp, 0, sizeof(texTemp));
   texTemp.target = PIPE_TEXTURE_2D;
   texTemp.format = src->format;
   texTemp.last_level = 0;
   texTemp.width0 = srcW;
   texTemp.height0 = srcH;
   texTemp.depth0 = 1;
   texTemp.array_size = 1;
   texTemp.bind = PIPE_BIND_SAMPLER_VIEW;

   tex = screen->resource_create(screen, &texTemp);
   if (!tex)
      return;

   u_sampler_view_default_template(&view_templ, tex, tex->format);
   view = pipe->create_sampler_view(pipe, tex, &view_templ);

   if (!view)
      return;

   u_box_2d_zslice(srcLeft, srcTop, src->u.tex.first_layer, srcW, srcH, &src_box);

   pipe->resource_copy_region(pipe,
                              tex, 0, 0, 0, 0,  /* dest */
                              src->texture, 0, &src_box);

   assert(floatsEqual(z, 0.0f));

   /* draw */
   if (fb->cbufs[0] == dst) {
      /* transform back to surface coordinates */
      dstY0 = dst->height - dstY0;
      dstY1 = dst->height - dstY1;

      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);
      }
   }
}

void renderer_texture_quad(struct renderer *r,
                           struct pipe_resource *tex,
                           VGfloat x1offset, VGfloat y1offset,
                           VGfloat x2offset, VGfloat y2offset,
                           VGfloat x1, VGfloat y1,
                           VGfloat x2, VGfloat y2,
                           VGfloat x3, VGfloat y3,
                           VGfloat x4, VGfloat y4)
{
   const VGfloat z = 0.0f;

   assert(r->state == RENDERER_STATE_INIT);
   assert(tex->width0 != 0);
   assert(tex->height0 != 0);

   cso_save_vertex_shader(r->cso);

   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);
}
