#include "xorg_exa_tgsi.h"

/*### stupidity defined in X11/extensions/XI.h */
#undef Absolute

#include "pipe/p_format.h"
#include "pipe/p_context.h"
#include "pipe/p_state.h"
#include "pipe/p_shader_tokens.h"

#include "util/u_memory.h"

#include "tgsi/tgsi_ureg.h"

#include "cso_cache/cso_context.h"
#include "cso_cache/cso_hash.h"

/* Vertex shader:
 * IN[0]    = vertex pos
 * IN[1]    = src tex coord | solid fill color
 * IN[2]    = mask tex coord
 * IN[3]    = dst tex coord
 * CONST[0] = (2/dst_width, 2/dst_height, 1, 1)
 * CONST[1] = (-1, -1, 0, 0)
 *
 * OUT[0]   = vertex pos
 * OUT[1]   = src tex coord | solid fill color
 * OUT[2]   = mask tex coord
 * OUT[3]   = dst tex coord
 */

/* Fragment shader:
 * SAMP[0]  = src
 * SAMP[1]  = mask
 * SAMP[2]  = dst
 * IN[0]    = pos src | solid fill color
 * IN[1]    = pos mask
 * IN[2]    = pos dst
 * CONST[0] = (0, 0, 0, 1)
 *
 * OUT[0] = color
 */

static void
print_fs_traits(int fs_traits)
{
   const char *strings[] = {
      "FS_COMPOSITE",       /* = 1 << 0, */
      "FS_MASK",            /* = 1 << 1, */
      "FS_SOLID_FILL",      /* = 1 << 2, */
      "FS_LINGRAD_FILL",    /* = 1 << 3, */
      "FS_RADGRAD_FILL",    /* = 1 << 4, */
      "FS_CA_FULL",         /* = 1 << 5, */ /* src.rgba * mask.rgba */
      "FS_CA_SRCALPHA",     /* = 1 << 6, */ /* src.aaaa * mask.rgba */
      "FS_YUV",             /* = 1 << 7, */
      "FS_SRC_REPEAT_NONE", /* = 1 << 8, */
      "FS_MASK_REPEAT_NONE",/* = 1 << 9, */
      "FS_SRC_SWIZZLE_RGB", /* = 1 << 10, */
      "FS_MASK_SWIZZLE_RGB",/* = 1 << 11, */
      "FS_SRC_SET_ALPHA",   /* = 1 << 12, */
      "FS_MASK_SET_ALPHA",  /* = 1 << 13, */
      "FS_SRC_LUMINANCE",   /* = 1 << 14, */
      "FS_MASK_LUMINANCE",  /* = 1 << 15, */
   };
   int i, k;
   debug_printf("%s: ", __func__);

   for (i = 0, k = 1; k < (1 << 16); i++, k <<= 1) {
      if (fs_traits & k)
         debug_printf("%s, ", strings[i]);
   }

   debug_printf("\n");
}

struct xorg_shaders {
   struct xorg_renderer *r;

   struct cso_hash *vs_hash;
   struct cso_hash *fs_hash;
};

static INLINE void
src_in_mask(struct ureg_program *ureg,
            struct ureg_dst dst,
            struct ureg_src src,
            struct ureg_src mask,
            unsigned component_alpha,
            unsigned mask_luminance)
{
   if (component_alpha == FS_CA_FULL) {
      ureg_MUL(ureg, dst, src, mask);
   } else if (component_alpha == FS_CA_SRCALPHA) {
      ureg_MUL(ureg, dst,
               ureg_scalar(src, TGSI_SWIZZLE_W), mask);
   }
   else {
      if (mask_luminance)
         ureg_MUL(ureg, dst, src,
                  ureg_scalar(mask, TGSI_SWIZZLE_X));
      else
         ureg_MUL(ureg, dst, src,
                  ureg_scalar(mask, TGSI_SWIZZLE_W));
   }
}

static struct ureg_src
vs_normalize_coords(struct ureg_program *ureg, struct ureg_src coords,
                    struct ureg_src const0, struct ureg_src const1)
{
   struct ureg_dst tmp = ureg_DECL_temporary(ureg);
   struct ureg_src ret;
   ureg_MAD(ureg, tmp, coords, const0, const1);
   ret = ureg_src(tmp);
   ureg_release_temporary(ureg, tmp);
   return ret;
}

static void
linear_gradient(struct ureg_program *ureg,
                struct ureg_dst out,
                struct ureg_src pos,
                struct ureg_src sampler,
                struct ureg_src coords,
                struct ureg_src const0124,
                struct ureg_src matrow0,
                struct ureg_src matrow1,
                struct ureg_src matrow2)
{
   struct ureg_dst temp0 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp1 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp2 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp3 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp4 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp5 = ureg_DECL_temporary(ureg);

   ureg_MOV(ureg,
            ureg_writemask(temp0, TGSI_WRITEMASK_XY), pos);
   ureg_MOV(ureg,
            ureg_writemask(temp0, TGSI_WRITEMASK_Z),
            ureg_scalar(const0124, TGSI_SWIZZLE_Y));

   ureg_DP3(ureg, temp1, matrow0, ureg_src(temp0));
   ureg_DP3(ureg, temp2, matrow1, ureg_src(temp0));
   ureg_DP3(ureg, temp3, matrow2, ureg_src(temp0));
   ureg_RCP(ureg, temp3, ureg_src(temp3));
   ureg_MUL(ureg, temp1, ureg_src(temp1), ureg_src(temp3));
   ureg_MUL(ureg, temp2, ureg_src(temp2), ureg_src(temp3));

   ureg_MOV(ureg, ureg_writemask(temp4, TGSI_WRITEMASK_X),
            ureg_src(temp1));
   ureg_MOV(ureg, ureg_writemask(temp4, TGSI_WRITEMASK_Y),
            ureg_src(temp2));

   ureg_MUL(ureg, temp0,
            ureg_scalar(coords, TGSI_SWIZZLE_Y),
            ureg_scalar(ureg_src(temp4), TGSI_SWIZZLE_Y));
   ureg_MAD(ureg, temp1,
            ureg_scalar(coords, TGSI_SWIZZLE_X),
            ureg_scalar(ureg_src(temp4), TGSI_SWIZZLE_X),
            ureg_src(temp0));

   ureg_MUL(ureg, temp2,
            ureg_src(temp1),
            ureg_scalar(coords, TGSI_SWIZZLE_Z));

   ureg_TEX(ureg, out,
            TGSI_TEXTURE_1D, ureg_src(temp2), sampler);

   ureg_release_temporary(ureg, temp0);
   ureg_release_temporary(ureg, temp1);
   ureg_release_temporary(ureg, temp2);
   ureg_release_temporary(ureg, temp3);
   ureg_release_temporary(ureg, temp4);
   ureg_release_temporary(ureg, temp5);
}


static void
radial_gradient(struct ureg_program *ureg,
                struct ureg_dst out,
                struct ureg_src pos,
                struct ureg_src sampler,
                struct ureg_src coords,
                struct ureg_src const0124,
                struct ureg_src matrow0,
                struct ureg_src matrow1,
                struct ureg_src matrow2)
{
   struct ureg_dst temp0 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp1 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp2 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp3 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp4 = ureg_DECL_temporary(ureg);
   struct ureg_dst temp5 = ureg_DECL_temporary(ureg);

   ureg_MOV(ureg,
            ureg_writemask(temp0, TGSI_WRITEMASK_XY),
            pos);
   ureg_MOV(ureg,
            ureg_writemask(temp0, TGSI_WRITEMASK_Z),
            ureg_scalar(const0124, TGSI_SWIZZLE_Y));

   ureg_DP3(ureg, temp1, matrow0, ureg_src(temp0));
   ureg_DP3(ureg, temp2, matrow1, ureg_src(temp0));
   ureg_DP3(ureg, temp3, matrow2, ureg_src(temp0));
   ureg_RCP(ureg, temp3, ureg_src(temp3));
   ureg_MUL(ureg, temp1, ureg_src(temp1), ureg_src(temp3));
   ureg_MUL(ureg, temp2, ureg_src(temp2), ureg_src(temp3));

   ureg_MOV(ureg, ureg_writemask(temp5, TGSI_WRITEMASK_X),
            ureg_src(temp1));
   ureg_MOV(ureg, ureg_writemask(temp5, TGSI_WRITEMASK_Y),
            ureg_src(temp2));

   ureg_MUL(ureg, temp0, ureg_scalar(coords, TGSI_SWIZZLE_Y),
            ureg_scalar(ureg_src(temp5), TGSI_SWIZZLE_Y));
   ureg_MAD(ureg, temp1,
            ureg_scalar(coords, TGSI_SWIZZLE_X),
            ureg_scalar(ureg_src(temp5), TGSI_SWIZZLE_X),
            ureg_src(temp0));
   ureg_ADD(ureg, temp1,
            ureg_src(temp1), ureg_src(temp1));
   ureg_MUL(ureg, temp3,
            ureg_scalar(ureg_src(temp5), TGSI_SWIZZLE_Y),
            ureg_scalar(ureg_src(temp5), TGSI_SWIZZLE_Y));
   ureg_MAD(ureg, temp4,
            ureg_scalar(ureg_src(temp5), TGSI_SWIZZLE_X),
            ureg_scalar(ureg_src(temp5), TGSI_SWIZZLE_X),
            ureg_src(temp3));
   ureg_MOV(ureg, temp4, ureg_negate(ureg_src(temp4)));
   ureg_MUL(ureg, temp2,
            ureg_scalar(coords, TGSI_SWIZZLE_Z),
            ureg_src(temp4));
   ureg_MUL(ureg, temp0,
            ureg_scalar(const0124, TGSI_SWIZZLE_W),
            ureg_src(temp2));
   ureg_MUL(ureg, temp3,
            ureg_src(temp1), ureg_src(temp1));
   ureg_SUB(ureg, temp2,
            ureg_src(temp3), ureg_src(temp0));
   ureg_RSQ(ureg, temp2, ureg_abs(ureg_src(temp2)));
   ureg_RCP(ureg, temp2, ureg_src(temp2));
   ureg_SUB(ureg, temp1,
            ureg_src(temp2), ureg_src(temp1));
   ureg_ADD(ureg, temp0,
            ureg_scalar(coords, TGSI_SWIZZLE_Z),
            ureg_scalar(coords, TGSI_SWIZZLE_Z));
   ureg_RCP(ureg, temp0, ureg_src(temp0));
   ureg_MUL(ureg, temp2,
            ureg_src(temp1), ureg_src(temp0));
   ureg_TEX(ureg, out, TGSI_TEXTURE_1D,
            ureg_src(temp2), sampler);

   ureg_release_temporary(ureg, temp0);
   ureg_release_temporary(ureg, temp1);
   ureg_release_temporary(ureg, temp2);
   ureg_release_temporary(ureg, temp3);
   ureg_release_temporary(ureg, temp4);
   ureg_release_temporary(ureg, temp5);
}

static void *
create_vs(struct pipe_context *pipe,
          unsigned vs_traits)
{
   struct ureg_program *ureg;
   struct ureg_src src;
   struct ureg_dst dst;
   struct ureg_src const0, const1;
   boolean is_fill = (vs_traits & VS_FILL) != 0;
   boolean is_composite = (vs_traits & VS_COMPOSITE) != 0;
   boolean has_mask = (vs_traits & VS_MASK) != 0;
   boolean is_yuv = (vs_traits & VS_YUV) != 0;
   unsigned input_slot = 0;

   ureg = ureg_create(TGSI_PROCESSOR_VERTEX);
   if (ureg == NULL)
      return 0;

   const0 = ureg_DECL_constant(ureg, 0);
   const1 = ureg_DECL_constant(ureg, 1);

   /* it has to be either a fill or a composite op */
   debug_assert((is_fill ^ is_composite) ^ is_yuv);

   src = ureg_DECL_vs_input(ureg, input_slot++);
   dst = ureg_DECL_output(ureg, TGSI_SEMANTIC_POSITION, 0);
   src = vs_normalize_coords(ureg, src,
                             const0, const1);
   ureg_MOV(ureg, dst, src);

   if (is_yuv) {
      src = ureg_DECL_vs_input(ureg, input_slot++);
      dst = ureg_DECL_output(ureg, TGSI_SEMANTIC_GENERIC, 0);
      ureg_MOV(ureg, dst, src);
   }

   if (is_composite) {
      src = ureg_DECL_vs_input(ureg, input_slot++);
      dst = ureg_DECL_output(ureg, TGSI_SEMANTIC_GENERIC, 0);
      ureg_MOV(ureg, dst, src);
   }

   if (is_fill) {
      src = ureg_DECL_vs_input(ureg, input_slot++);
      dst = ureg_DECL_output(ureg, TGSI_SEMANTIC_COLOR, 0);
      ureg_MOV(ureg, dst, src);
   }

   if (has_mask) {
      src = ureg_DECL_vs_input(ureg, input_slot++);
      dst = ureg_DECL_output(ureg, TGSI_SEMANTIC_GENERIC, 1);
      ureg_MOV(ureg, dst, src);
   }

   ureg_END(ureg);

   return ureg_create_shader_and_destroy(ureg, pipe);
}

static void *
create_yuv_shader(struct pipe_context *pipe, struct ureg_program *ureg)
{
   struct ureg_src y_sampler, u_sampler, v_sampler;
   struct ureg_src pos;
   struct ureg_src matrow0, matrow1, matrow2;
   struct ureg_dst y, u, v, rgb;
   struct ureg_dst out = ureg_DECL_output(ureg,
                                          TGSI_SEMANTIC_COLOR,
                                          0);

   pos = ureg_DECL_fs_input(ureg,
                            TGSI_SEMANTIC_GENERIC,
                            0,
                            TGSI_INTERPOLATE_PERSPECTIVE);

   rgb = ureg_DECL_temporary(ureg);
   y = ureg_DECL_temporary(ureg);
   u = ureg_DECL_temporary(ureg);
   v = ureg_DECL_temporary(ureg);

   y_sampler = ureg_DECL_sampler(ureg, 0);
   u_sampler = ureg_DECL_sampler(ureg, 1);
   v_sampler = ureg_DECL_sampler(ureg, 2);

   matrow0 = ureg_DECL_constant(ureg, 0);
   matrow1 = ureg_DECL_constant(ureg, 1);
   matrow2 = ureg_DECL_constant(ureg, 2);

   ureg_TEX(ureg, y,
            TGSI_TEXTURE_2D, pos, y_sampler);
   ureg_TEX(ureg, u,
            TGSI_TEXTURE_2D, pos, u_sampler);
   ureg_TEX(ureg, v,
            TGSI_TEXTURE_2D, pos, v_sampler);

   ureg_SUB(ureg, u, ureg_src(u),
            ureg_scalar(matrow0, TGSI_SWIZZLE_W));
   ureg_SUB(ureg, v, ureg_src(v),
            ureg_scalar(matrow0, TGSI_SWIZZLE_W));

   ureg_MUL(ureg, rgb,
            ureg_scalar(ureg_src(y), TGSI_SWIZZLE_X),
            matrow0);
   ureg_MAD(ureg, rgb,
            ureg_scalar(ureg_src(u), TGSI_SWIZZLE_X),
            matrow1,
            ureg_src(rgb));
   ureg_MAD(ureg, rgb,
            ureg_scalar(ureg_src(v), TGSI_SWIZZLE_X),
            matrow2,
            ureg_src(rgb));

   /* rgb.a = 1; */
   ureg_MOV(ureg, ureg_writemask(rgb, TGSI_WRITEMASK_W),
            ureg_scalar(matrow0, TGSI_SWIZZLE_X));

   ureg_MOV(ureg, out, ureg_src(rgb));

   ureg_release_temporary(ureg, rgb);
   ureg_release_temporary(ureg, y);
   ureg_release_temporary(ureg, u);
   ureg_release_temporary(ureg, v);

   ureg_END(ureg);

   return ureg_create_shader_and_destroy(ureg, pipe);
}


static INLINE void
xrender_tex(struct ureg_program *ureg,
            struct ureg_dst dst,
            struct ureg_src coords,
            struct ureg_src sampler,
            struct ureg_src imm0,
            boolean repeat_none,
            boolean swizzle,
            boolean set_alpha)
{
   if (repeat_none) {
      struct ureg_dst tmp0 = ureg_DECL_temporary(ureg);
      struct ureg_dst tmp1 = ureg_DECL_temporary(ureg);
      ureg_SGT(ureg, tmp1, ureg_swizzle(coords,
                                        TGSI_SWIZZLE_X,
                                        TGSI_SWIZZLE_Y,
                                        TGSI_SWIZZLE_X,
                                        TGSI_SWIZZLE_Y),
               ureg_scalar(imm0, TGSI_SWIZZLE_X));
      ureg_SLT(ureg, tmp0, ureg_swizzle(coords,
                                        TGSI_SWIZZLE_X,
                                        TGSI_SWIZZLE_Y,
                                        TGSI_SWIZZLE_X,
                                        TGSI_SWIZZLE_Y),
               ureg_scalar(imm0, TGSI_SWIZZLE_W));
      ureg_MIN(ureg, tmp0, ureg_src(tmp0), ureg_src(tmp1));
      ureg_MIN(ureg, tmp0, ureg_scalar(ureg_src(tmp0), TGSI_SWIZZLE_X),
               ureg_scalar(ureg_src(tmp0), TGSI_SWIZZLE_Y));
      ureg_TEX(ureg, tmp1, TGSI_TEXTURE_2D, coords, sampler);
      if (swizzle)
         ureg_MOV(ureg, tmp1, ureg_swizzle(ureg_src(tmp1),
                                           TGSI_SWIZZLE_Z,
                                           TGSI_SWIZZLE_Y,
                                           TGSI_SWIZZLE_X,
                                           TGSI_SWIZZLE_W));
      if (set_alpha)
         ureg_MOV(ureg,
                  ureg_writemask(tmp1, TGSI_WRITEMASK_W),
                  ureg_scalar(imm0, TGSI_SWIZZLE_W));
      ureg_MUL(ureg, dst, ureg_src(tmp1), ureg_src(tmp0));
      ureg_release_temporary(ureg, tmp0);
      ureg_release_temporary(ureg, tmp1);
   } else {
      if (swizzle) {
         struct ureg_dst tmp = ureg_DECL_temporary(ureg);
         ureg_TEX(ureg, tmp, TGSI_TEXTURE_2D, coords, sampler);
         ureg_MOV(ureg, dst, ureg_swizzle(ureg_src(tmp),
                                          TGSI_SWIZZLE_Z,
                                          TGSI_SWIZZLE_Y,
                                          TGSI_SWIZZLE_X,
                                          TGSI_SWIZZLE_W));
         ureg_release_temporary(ureg, tmp);
      } else {
         ureg_TEX(ureg, dst, TGSI_TEXTURE_2D, coords, sampler);
      }
      if (set_alpha)
         ureg_MOV(ureg,
                  ureg_writemask(dst, TGSI_WRITEMASK_W),
                  ureg_scalar(imm0, TGSI_SWIZZLE_W));
   }
}

static void *
create_fs(struct pipe_context *pipe,
          unsigned fs_traits)
{
   struct ureg_program *ureg;
   struct ureg_src /*dst_sampler,*/ src_sampler, mask_sampler;
   struct ureg_src /*dst_pos,*/ src_input, mask_pos;
   struct ureg_dst src, mask;
   struct ureg_dst out;
   struct ureg_src imm0 = { 0 };
   unsigned has_mask = (fs_traits & FS_MASK) != 0;
   unsigned is_fill = (fs_traits & FS_FILL) != 0;
   unsigned is_composite = (fs_traits & FS_COMPOSITE) != 0;
   unsigned is_solid   = (fs_traits & FS_SOLID_FILL) != 0;
   unsigned is_lingrad = (fs_traits & FS_LINGRAD_FILL) != 0;
   unsigned is_radgrad = (fs_traits & FS_RADGRAD_FILL) != 0;
   unsigned comp_alpha_mask = fs_traits & FS_COMPONENT_ALPHA;
   unsigned is_yuv = (fs_traits & FS_YUV) != 0;
   unsigned src_repeat_none = (fs_traits & FS_SRC_REPEAT_NONE) != 0;
   unsigned mask_repeat_none = (fs_traits & FS_MASK_REPEAT_NONE) != 0;
   unsigned src_swizzle = (fs_traits & FS_SRC_SWIZZLE_RGB) != 0;
   unsigned mask_swizzle = (fs_traits & FS_MASK_SWIZZLE_RGB) != 0;
   unsigned src_set_alpha = (fs_traits & FS_SRC_SET_ALPHA) != 0;
   unsigned mask_set_alpha = (fs_traits & FS_MASK_SET_ALPHA) != 0;
   unsigned src_luminance = (fs_traits & FS_SRC_LUMINANCE) != 0;
   unsigned mask_luminance = (fs_traits & FS_MASK_LUMINANCE) != 0;

#if 0
   print_fs_traits(fs_traits);
#else
   (void)print_fs_traits;
#endif

   ureg = ureg_create(TGSI_PROCESSOR_FRAGMENT);
   if (ureg == NULL)
      return 0;

   /* it has to be either a fill, a composite op or a yuv conversion */
   debug_assert((is_fill ^ is_composite) ^ is_yuv);
   (void) is_yuv;

   out = ureg_DECL_output(ureg,
                          TGSI_SEMANTIC_COLOR,
                          0);

   if (src_repeat_none || mask_repeat_none ||
       src_set_alpha || mask_set_alpha ||
       src_luminance) {
      imm0 = ureg_imm4f(ureg, 0, 0, 0, 1);
   }
   if (is_composite) {
      src_sampler = ureg_DECL_sampler(ureg, 0);
      src_input = ureg_DECL_fs_input(ureg,
                                     TGSI_SEMANTIC_GENERIC,
                                     0,
                                     TGSI_INTERPOLATE_PERSPECTIVE);
   } else if (is_fill) {
      if (is_solid)
         src_input = ureg_DECL_fs_input(ureg,
                                        TGSI_SEMANTIC_COLOR,
                                        0,
                                        TGSI_INTERPOLATE_PERSPECTIVE);
      else
         src_input = ureg_DECL_fs_input(ureg,
                                        TGSI_SEMANTIC_POSITION,
                                        0,
                                        TGSI_INTERPOLATE_PERSPECTIVE);
   } else {
      debug_assert(is_yuv);
      return create_yuv_shader(pipe, ureg);
   }

   if (has_mask) {
      mask_sampler = ureg_DECL_sampler(ureg, 1);
      mask_pos = ureg_DECL_fs_input(ureg,
                                    TGSI_SEMANTIC_GENERIC,
                                    1,
                                    TGSI_INTERPOLATE_PERSPECTIVE);
   }

#if 0  /* unused right now */
   dst_sampler = ureg_DECL_sampler(ureg, 2);
   dst_pos = ureg_DECL_fs_input(ureg,
                                TGSI_SEMANTIC_POSITION,
                                2,
                                TGSI_INTERPOLATE_PERSPECTIVE);
#endif


   if (is_composite) {
      if (has_mask || src_luminance)
         src = ureg_DECL_temporary(ureg);
      else
         src = out;
      xrender_tex(ureg, src, src_input, src_sampler, imm0,
                  src_repeat_none, src_swizzle, src_set_alpha);
   } else if (is_fill) {
      if (is_solid) {
         if (has_mask || src_luminance)
            src = ureg_dst(src_input);
         else
            ureg_MOV(ureg, out, src_input);
      } else if (is_lingrad || is_radgrad) {
         struct ureg_src coords, const0124,
            matrow0, matrow1, matrow2;

         if (has_mask || src_luminance)
            src = ureg_DECL_temporary(ureg);
         else
            src = out;

         coords = ureg_DECL_constant(ureg, 0);
         const0124 = ureg_DECL_constant(ureg, 1);
         matrow0 = ureg_DECL_constant(ureg, 2);
         matrow1 = ureg_DECL_constant(ureg, 3);
         matrow2 = ureg_DECL_constant(ureg, 4);

         if (is_lingrad) {
            linear_gradient(ureg, src,
                            src_input, src_sampler,
                            coords, const0124,
                            matrow0, matrow1, matrow2);
         } else if (is_radgrad) {
            radial_gradient(ureg, src,
                            src_input, src_sampler,
                            coords, const0124,
                            matrow0, matrow1, matrow2);
         }
      } else
         debug_assert(!"Unknown fill type!");
   }
   if (src_luminance) {
      ureg_MOV(ureg, src,
               ureg_scalar(ureg_src(src), TGSI_SWIZZLE_X));
      ureg_MOV(ureg, ureg_writemask(src, TGSI_WRITEMASK_XYZ),
               ureg_scalar(imm0, TGSI_SWIZZLE_X));
      if (!has_mask)
         ureg_MOV(ureg, out, ureg_src(src));
   }

   if (has_mask) {
      mask = ureg_DECL_temporary(ureg);
      xrender_tex(ureg, mask, mask_pos, mask_sampler, imm0,
                  mask_repeat_none, mask_swizzle, mask_set_alpha);
      /* src IN mask */
      src_in_mask(ureg, out, ureg_src(src), ureg_src(mask),
                  comp_alpha_mask, mask_luminance);
      ureg_release_temporary(ureg, mask);
   }

   ureg_END(ureg);

   return ureg_create_shader_and_destroy(ureg, pipe);
}

struct xorg_shaders * xorg_shaders_create(struct xorg_renderer *r)
{
   struct xorg_shaders *sc = CALLOC_STRUCT(xorg_shaders);

   sc->r = r;
   sc->vs_hash = cso_hash_create();
   sc->fs_hash = cso_hash_create();

   return sc;
}

static void
cache_destroy(struct cso_context *cso,
              struct cso_hash *hash,
              unsigned processor)
{
   struct cso_hash_iter iter = cso_hash_first_node(hash);
   while (!cso_hash_iter_is_null(iter)) {
      void *shader = (void *)cso_hash_iter_data(iter);
      if (processor == PIPE_SHADER_FRAGMENT) {
         cso_delete_fragment_shader(cso, shader);
      } else if (processor == PIPE_SHADER_VERTEX) {
         cso_delete_vertex_shader(cso, shader);
      }
      iter = cso_hash_erase(hash, iter);
   }
   cso_hash_delete(hash);
}

void xorg_shaders_destroy(struct xorg_shaders *sc)
{
   cache_destroy(sc->r->cso, sc->vs_hash,
                 PIPE_SHADER_VERTEX);
   cache_destroy(sc->r->cso, sc->fs_hash,
                 PIPE_SHADER_FRAGMENT);

   FREE(sc);
}

static INLINE void *
shader_from_cache(struct pipe_context *pipe,
                  unsigned type,
                  struct cso_hash *hash,
                  unsigned key)
{
   void *shader = 0;

   struct cso_hash_iter iter = cso_hash_find(hash, key);

   if (cso_hash_iter_is_null(iter)) {
      if (type == PIPE_SHADER_VERTEX)
         shader = create_vs(pipe, key);
      else
         shader = create_fs(pipe, key);
      cso_hash_insert(hash, key, shader);
   } else
      shader = (void *)cso_hash_iter_data(iter);

   return shader;
}

struct xorg_shader xorg_shaders_get(struct xorg_shaders *sc,
                                    unsigned vs_traits,
                                    unsigned fs_traits)
{
   struct xorg_shader shader = { NULL, NULL };
   void *vs, *fs;

   vs = shader_from_cache(sc->r->pipe, PIPE_SHADER_VERTEX,
                          sc->vs_hash, vs_traits);
   fs = shader_from_cache(sc->r->pipe, PIPE_SHADER_FRAGMENT,
                          sc->fs_hash, fs_traits);

   debug_assert(vs && fs);
   if (!vs || !fs)
      return shader;

   shader.vs = vs;
   shader.fs = fs;

   return shader;
}
