Home | History | Annotate | Download | only in tgsi
      1 /*
      2  * Copyright 2014 VMware, Inc.
      3  * All Rights Reserved.
      4  *
      5  * Permission is hereby granted, free of charge, to any person obtaining a
      6  * copy of this software and associated documentation files (the
      7  * "Software"), to deal in the Software without restriction, including
      8  * without limitation the rights to use, copy, modify, merge, publish,
      9  * distribute, sub license, and/or sell copies of the Software, and to
     10  * permit persons to whom the Software is furnished to do so, subject to
     11  * the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the
     14  * next paragraph) shall be included in all copies or substantial portions
     15  * of the Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     20  * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
     21  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     24  */
     25 
     26 
     27 /**
     28  * This utility transforms the geometry shader to emulate point sprite by
     29  * drawing a quad. It also adds an extra output for the original point position
     30  * if the point position is to be written to a stream output buffer.
     31  * Note: It assumes the driver will add a constant for the inverse viewport
     32  *       after the user defined constants.
     33  */
     34 
     35 #include "util/u_debug.h"
     36 #include "util/u_math.h"
     37 #include "tgsi_info.h"
     38 #include "tgsi_point_sprite.h"
     39 #include "tgsi_transform.h"
     40 #include "pipe/p_state.h"
     41 
     42 #define INVALID_INDEX 9999
     43 
     44 /* Set swizzle based on the immediates (0, 1, 0, -1) */
     45 static inline unsigned
     46 set_swizzle(int x, int y, int z, int w)
     47 {
     48    static const unsigned map[3] = {TGSI_SWIZZLE_W, TGSI_SWIZZLE_X,
     49                                    TGSI_SWIZZLE_Y};
     50    assert(x >= -1);
     51    assert(x <= 1);
     52    assert(y >= -1);
     53    assert(y <= 1);
     54    assert(z >= -1);
     55    assert(z <= 1);
     56    assert(w >= -1);
     57    assert(w <= 1);
     58 
     59    return map[x+1] | (map[y+1] << 2) | (map[z+1] << 4) | (map[w+1] << 6);
     60 }
     61 
     62 static inline unsigned
     63 get_swizzle(unsigned swizzle, unsigned component)
     64 {
     65    assert(component < 4);
     66    return (swizzle >> (component * 2)) & 0x3;
     67 }
     68 
     69 struct psprite_transform_context
     70 {
     71    struct tgsi_transform_context base;
     72    unsigned num_tmp;
     73    unsigned num_out;
     74    unsigned num_orig_out;
     75    unsigned num_const;
     76    unsigned num_imm;
     77    unsigned point_size_in;          // point size input
     78    unsigned point_size_out;         // point size output
     79    unsigned point_size_tmp;         // point size temp
     80    unsigned point_pos_in;           // point pos input
     81    unsigned point_pos_out;          // point pos output
     82    unsigned point_pos_sout;         // original point pos for streamout
     83    unsigned point_pos_tmp;          // point pos temp
     84    unsigned point_scale_tmp;        // point scale temp
     85    unsigned point_color_out;        // point color output
     86    unsigned point_color_tmp;        // point color temp
     87    unsigned point_imm;              // point immediates
     88    unsigned point_ivp;              // point inverseViewport constant
     89    unsigned point_dir_swz[4];       // point direction swizzle
     90    unsigned point_coord_swz[4];     // point coord swizzle
     91    unsigned point_coord_enable;     // point coord enable mask
     92    unsigned point_coord_decl;       // point coord output declared mask
     93    unsigned point_coord_out;        // point coord output starting index
     94    unsigned point_coord_aa;         // aa point coord semantic index
     95    unsigned point_coord_k;          // aa point coord threshold distance
     96    unsigned stream_out_point_pos:1; // set if to stream out original point pos
     97    unsigned aa_point:1;             // set if doing aa point
     98    unsigned out_tmp_index[PIPE_MAX_SHADER_OUTPUTS];
     99    int max_generic;                 // max generic semantic index
    100 };
    101 
    102 static inline struct psprite_transform_context *
    103 psprite_transform_context(struct tgsi_transform_context *ctx)
    104 {
    105    return (struct psprite_transform_context *) ctx;
    106 }
    107 
    108 
    109 /**
    110  * TGSI declaration transform callback.
    111  */
    112 static void
    113 psprite_decl(struct tgsi_transform_context *ctx,
    114              struct tgsi_full_declaration *decl)
    115 {
    116    struct psprite_transform_context *ts = psprite_transform_context(ctx);
    117 
    118    if (decl->Declaration.File == TGSI_FILE_INPUT) {
    119       if (decl->Semantic.Name == TGSI_SEMANTIC_PSIZE) {
    120          ts->point_size_in = decl->Range.First;
    121       }
    122       else if (decl->Semantic.Name == TGSI_SEMANTIC_POSITION) {
    123          ts->point_pos_in = decl->Range.First;
    124       }
    125    }
    126    else if (decl->Declaration.File == TGSI_FILE_OUTPUT) {
    127       if (decl->Semantic.Name == TGSI_SEMANTIC_PSIZE) {
    128          ts->point_size_out = decl->Range.First;
    129       }
    130       else if (decl->Semantic.Name == TGSI_SEMANTIC_POSITION) {
    131          ts->point_pos_out = decl->Range.First;
    132       }
    133       else if (decl->Semantic.Name == TGSI_SEMANTIC_GENERIC &&
    134                decl->Semantic.Index < 32) {
    135          ts->point_coord_decl |= 1 << decl->Semantic.Index;
    136          ts->max_generic = MAX2(ts->max_generic, (int)decl->Semantic.Index);
    137       }
    138       ts->num_out = MAX2(ts->num_out, decl->Range.Last + 1);
    139    }
    140    else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
    141       ts->num_tmp = MAX2(ts->num_tmp, decl->Range.Last + 1);
    142    }
    143    else if (decl->Declaration.File == TGSI_FILE_CONSTANT) {
    144       ts->num_const = MAX2(ts->num_const, decl->Range.Last + 1);
    145    }
    146 
    147    ctx->emit_declaration(ctx, decl);
    148 }
    149 
    150 /**
    151  * TGSI immediate declaration transform callback.
    152  */
    153 static void
    154 psprite_immediate(struct tgsi_transform_context *ctx,
    155                   struct tgsi_full_immediate *imm)
    156 {
    157    struct psprite_transform_context *ts = psprite_transform_context(ctx);
    158 
    159    ctx->emit_immediate(ctx, imm);
    160    ts->num_imm++;
    161 }
    162 
    163 
    164 /**
    165  * TGSI transform prolog callback.
    166  */
    167 static void
    168 psprite_prolog(struct tgsi_transform_context *ctx)
    169 {
    170    struct psprite_transform_context *ts = psprite_transform_context(ctx);
    171    unsigned point_coord_enable, en;
    172    int i;
    173 
    174    /* Replace output registers with temporary registers */
    175    for (i = 0; i < ts->num_out; i++) {
    176       ts->out_tmp_index[i] = ts->num_tmp++;
    177    }
    178    ts->num_orig_out = ts->num_out;
    179 
    180    /* Declare a tmp register for point scale */
    181    ts->point_scale_tmp = ts->num_tmp++;
    182 
    183    if (ts->point_size_out != INVALID_INDEX)
    184       ts->point_size_tmp = ts->out_tmp_index[ts->point_size_out];
    185    else
    186       ts->point_size_tmp = ts->num_tmp++;
    187 
    188    assert(ts->point_pos_out != INVALID_INDEX);
    189    ts->point_pos_tmp = ts->out_tmp_index[ts->point_pos_out];
    190    ts->out_tmp_index[ts->point_pos_out] = INVALID_INDEX;
    191 
    192    /* Declare one more tmp register for point coord threshold distance
    193     * if we are generating anti-aliased point.
    194     */
    195    if (ts->aa_point)
    196       ts->point_coord_k = ts->num_tmp++;
    197 
    198    tgsi_transform_temps_decl(ctx, ts->point_size_tmp, ts->num_tmp-1);
    199 
    200    /* Declare an extra output for the original point position for stream out */
    201    if (ts->stream_out_point_pos) {
    202       ts->point_pos_sout = ts->num_out++;
    203       tgsi_transform_output_decl(ctx, ts->point_pos_sout,
    204                                  TGSI_SEMANTIC_GENERIC, 0, 0);
    205    }
    206 
    207    /* point coord outputs to be declared */
    208    point_coord_enable = ts->point_coord_enable & ~ts->point_coord_decl;
    209 
    210    /* Declare outputs for those point coord that are enabled but are not
    211     * already declared in this shader.
    212     */
    213    ts->point_coord_out = ts->num_out;
    214    if (point_coord_enable) {
    215       for (i = 0, en = point_coord_enable; en; en>>=1, i++) {
    216          if (en & 0x1) {
    217             tgsi_transform_output_decl(ctx, ts->num_out++,
    218                                        TGSI_SEMANTIC_GENERIC, i, 0);
    219             ts->max_generic = MAX2(ts->max_generic, (int)i);
    220          }
    221       }
    222    }
    223 
    224    /* add an extra generic output for aa point texcoord */
    225    if (ts->aa_point) {
    226       ts->point_coord_aa = ts->max_generic + 1;
    227       assert((ts->point_coord_enable & (1 << ts->point_coord_aa)) == 0);
    228       ts->point_coord_enable |= 1 << (ts->point_coord_aa);
    229       tgsi_transform_output_decl(ctx, ts->num_out++, TGSI_SEMANTIC_GENERIC,
    230                                  ts->point_coord_aa, 0);
    231    }
    232 
    233    /* Declare extra immediates */
    234    ts->point_imm = ts->num_imm;
    235    tgsi_transform_immediate_decl(ctx, 0, 1, 0.5, -1);
    236 
    237    /* Declare point constant -
    238     * constant.xy -- inverseViewport
    239     * constant.z -- current point size
    240     * constant.w -- max point size
    241     * The driver needs to add this constant to the constant buffer
    242     */
    243    ts->point_ivp = ts->num_const++;
    244    tgsi_transform_const_decl(ctx, ts->point_ivp, ts->point_ivp);
    245 
    246    /* If this geometry shader does not specify point size,
    247     * get the current point size from the point constant.
    248     */
    249    if (ts->point_size_out == INVALID_INDEX) {
    250       struct tgsi_full_instruction inst;
    251 
    252       inst = tgsi_default_full_instruction();
    253       inst.Instruction.Opcode = TGSI_OPCODE_MOV;
    254       inst.Instruction.NumDstRegs = 1;
    255       tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
    256                              ts->point_size_tmp, TGSI_WRITEMASK_XYZW);
    257       inst.Instruction.NumSrcRegs = 1;
    258       tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_CONSTANT,
    259                              ts->point_ivp, TGSI_SWIZZLE_Z,
    260                              TGSI_SWIZZLE_Z, TGSI_SWIZZLE_Z, TGSI_SWIZZLE_Z);
    261       ctx->emit_instruction(ctx, &inst);
    262    }
    263 }
    264 
    265 
    266 /**
    267  * Add the point sprite emulation instructions at the emit vertex instruction
    268  */
    269 static void
    270 psprite_emit_vertex_inst(struct tgsi_transform_context *ctx,
    271                          struct tgsi_full_instruction *vert_inst)
    272 {
    273    struct psprite_transform_context *ts = psprite_transform_context(ctx);
    274    struct tgsi_full_instruction inst;
    275    unsigned point_coord_enable, en;
    276    unsigned i, j, s;
    277 
    278    /* new point coord outputs */
    279    point_coord_enable = ts->point_coord_enable & ~ts->point_coord_decl;
    280 
    281    /* OUTPUT[pos_sout] = TEMP[pos] */
    282    if (ts->point_pos_sout != INVALID_INDEX) {
    283       tgsi_transform_op1_inst(ctx, TGSI_OPCODE_MOV,
    284                               TGSI_FILE_OUTPUT, ts->point_pos_sout,
    285                               TGSI_WRITEMASK_XYZW,
    286                               TGSI_FILE_TEMPORARY, ts->point_pos_tmp);
    287    }
    288 
    289    /**
    290     * Set up the point scale vector
    291     * scale = pointSize * pos.w * inverseViewport
    292     */
    293 
    294    /* MUL point_scale.x, point_size.x, point_pos.w */
    295    tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MUL,
    296                   TGSI_FILE_TEMPORARY, ts->point_scale_tmp, TGSI_WRITEMASK_X,
    297                   TGSI_FILE_TEMPORARY, ts->point_size_tmp, TGSI_SWIZZLE_X,
    298                   TGSI_FILE_TEMPORARY, ts->point_pos_tmp, TGSI_SWIZZLE_W, false);
    299 
    300    /* MUL point_scale.xy, point_scale.xx, inverseViewport.xy */
    301    inst = tgsi_default_full_instruction();
    302    inst.Instruction.Opcode = TGSI_OPCODE_MUL;
    303    inst.Instruction.NumDstRegs = 1;
    304    tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
    305                           ts->point_scale_tmp, TGSI_WRITEMASK_XY);
    306    inst.Instruction.NumSrcRegs = 2;
    307    tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,
    308                           ts->point_scale_tmp, TGSI_SWIZZLE_X,
    309                           TGSI_SWIZZLE_X, TGSI_SWIZZLE_X, TGSI_SWIZZLE_X);
    310    tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_CONSTANT,
    311                           ts->point_ivp, TGSI_SWIZZLE_X,
    312                           TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Z, TGSI_SWIZZLE_Z);
    313    ctx->emit_instruction(ctx, &inst);
    314 
    315    /**
    316     * Set up the point coord threshold distance
    317     * k = 0.5 - 1 / pointsize
    318     */
    319    if (ts->aa_point) {
    320       tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_DIV,
    321                                   TGSI_FILE_TEMPORARY, ts->point_coord_k,
    322                                   TGSI_WRITEMASK_X,
    323                                   TGSI_FILE_IMMEDIATE, ts->point_imm,
    324                                   TGSI_SWIZZLE_Y,
    325                                   TGSI_FILE_TEMPORARY, ts->point_size_tmp,
    326                                   TGSI_SWIZZLE_X, false);
    327 
    328       tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_ADD,
    329                                   TGSI_FILE_TEMPORARY, ts->point_coord_k,
    330                                   TGSI_WRITEMASK_X,
    331                                   TGSI_FILE_IMMEDIATE, ts->point_imm,
    332                                   TGSI_SWIZZLE_Z,
    333                                   TGSI_FILE_TEMPORARY, ts->point_coord_k,
    334                                   TGSI_SWIZZLE_X, true);
    335    }
    336 
    337 
    338    for (i = 0; i < 4; i++) {
    339       unsigned point_dir_swz = ts->point_dir_swz[i];
    340       unsigned point_coord_swz = ts->point_coord_swz[i];
    341 
    342       /* All outputs need to be emitted for each vertex */
    343       for (j = 0; j < ts->num_orig_out; j++) {
    344          if (ts->out_tmp_index[j] != INVALID_INDEX) {
    345             tgsi_transform_op1_inst(ctx, TGSI_OPCODE_MOV,
    346                                     TGSI_FILE_OUTPUT, j,
    347                                     TGSI_WRITEMASK_XYZW,
    348                                     TGSI_FILE_TEMPORARY, ts->out_tmp_index[j]);
    349          }
    350       }
    351 
    352       /* pos = point_scale * point_dir + point_pos */
    353       inst = tgsi_default_full_instruction();
    354       inst.Instruction.Opcode = TGSI_OPCODE_MAD;
    355       inst.Instruction.NumDstRegs = 1;
    356       tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_OUTPUT, ts->point_pos_out,
    357                              TGSI_WRITEMASK_XYZW);
    358       inst.Instruction.NumSrcRegs = 3;
    359       tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY, ts->point_scale_tmp,
    360                              TGSI_SWIZZLE_X, TGSI_SWIZZLE_Y, TGSI_SWIZZLE_X,
    361                              TGSI_SWIZZLE_X);
    362       tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_IMMEDIATE, ts->point_imm,
    363                              get_swizzle(point_dir_swz, 0),
    364                              get_swizzle(point_dir_swz, 1),
    365                              get_swizzle(point_dir_swz, 2),
    366                              get_swizzle(point_dir_swz, 3));
    367       tgsi_transform_src_reg(&inst.Src[2], TGSI_FILE_TEMPORARY, ts->point_pos_tmp,
    368                              TGSI_SWIZZLE_X, TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Z,
    369                              TGSI_SWIZZLE_W);
    370       ctx->emit_instruction(ctx, &inst);
    371 
    372       /* point coord */
    373       for (j = 0, s = 0, en = point_coord_enable; en; en>>=1, s++) {
    374          unsigned dstReg;
    375 
    376          if (en & 0x1) {
    377             dstReg = ts->point_coord_out + j;
    378 
    379             inst = tgsi_default_full_instruction();
    380             inst.Instruction.Opcode = TGSI_OPCODE_MOV;
    381             inst.Instruction.NumDstRegs = 1;
    382             tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_OUTPUT,
    383                                    dstReg, TGSI_WRITEMASK_XYZW);
    384             inst.Instruction.NumSrcRegs = 1;
    385             tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_IMMEDIATE, ts->point_imm,
    386                                    get_swizzle(point_coord_swz, 0),
    387                                    get_swizzle(point_coord_swz, 1),
    388                                    get_swizzle(point_coord_swz, 2),
    389                                    get_swizzle(point_coord_swz, 3));
    390             ctx->emit_instruction(ctx, &inst);
    391 
    392             /* MOV point_coord.z  point_coord_k.x */
    393             if (s == ts->point_coord_aa) {
    394                tgsi_transform_op1_swz_inst(ctx, TGSI_OPCODE_MOV,
    395                                            TGSI_FILE_OUTPUT, dstReg, TGSI_WRITEMASK_Z,
    396                                            TGSI_FILE_TEMPORARY, ts->point_coord_k,
    397                                            TGSI_SWIZZLE_X);
    398             }
    399             j++;  /* the next point coord output offset */
    400          }
    401       }
    402 
    403       /* Emit the EMIT instruction for each vertex of the quad */
    404       ctx->emit_instruction(ctx, vert_inst);
    405    }
    406 
    407    /* Emit the ENDPRIM instruction for the quad */
    408    inst = tgsi_default_full_instruction();
    409    inst.Instruction.Opcode = TGSI_OPCODE_ENDPRIM;
    410    inst.Instruction.NumDstRegs = 0;
    411    inst.Instruction.NumSrcRegs = 1;
    412    inst.Src[0] = vert_inst->Src[0];
    413    ctx->emit_instruction(ctx, &inst);
    414 }
    415 
    416 
    417 /**
    418  * TGSI instruction transform callback.
    419  */
    420 static void
    421 psprite_inst(struct tgsi_transform_context *ctx,
    422              struct tgsi_full_instruction *inst)
    423 {
    424    struct psprite_transform_context *ts = psprite_transform_context(ctx);
    425 
    426    if (inst->Instruction.Opcode == TGSI_OPCODE_EMIT) {
    427       psprite_emit_vertex_inst(ctx, inst);
    428    }
    429    else if (inst->Dst[0].Register.File == TGSI_FILE_OUTPUT &&
    430             inst->Dst[0].Register.Index == ts->point_size_out) {
    431       /**
    432        * Replace point size output reg with tmp reg.
    433        * The tmp reg will be later used as a src reg for computing
    434        * the point scale factor.
    435        */
    436       inst->Dst[0].Register.File = TGSI_FILE_TEMPORARY;
    437       inst->Dst[0].Register.Index = ts->point_size_tmp;
    438       ctx->emit_instruction(ctx, inst);
    439 
    440       /* Clamp the point size */
    441       /* MAX point_size_tmp.x, point_size_tmp.x, point_imm.y */
    442       tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MAX,
    443                  TGSI_FILE_TEMPORARY, ts->point_size_tmp, TGSI_WRITEMASK_X,
    444                  TGSI_FILE_TEMPORARY, ts->point_size_tmp, TGSI_SWIZZLE_X,
    445                  TGSI_FILE_IMMEDIATE, ts->point_imm, TGSI_SWIZZLE_Y, false);
    446 
    447       /* MIN point_size_tmp.x, point_size_tmp.x, point_ivp.w */
    448       tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MIN,
    449                  TGSI_FILE_TEMPORARY, ts->point_size_tmp, TGSI_WRITEMASK_X,
    450                  TGSI_FILE_TEMPORARY, ts->point_size_tmp, TGSI_SWIZZLE_X,
    451                  TGSI_FILE_CONSTANT, ts->point_ivp, TGSI_SWIZZLE_W, false);
    452    }
    453    else if (inst->Dst[0].Register.File == TGSI_FILE_OUTPUT &&
    454             inst->Dst[0].Register.Index == ts->point_pos_out) {
    455       /**
    456        * Replace point pos output reg with tmp reg.
    457        */
    458       inst->Dst[0].Register.File = TGSI_FILE_TEMPORARY;
    459       inst->Dst[0].Register.Index = ts->point_pos_tmp;
    460       ctx->emit_instruction(ctx, inst);
    461    }
    462    else if (inst->Dst[0].Register.File == TGSI_FILE_OUTPUT) {
    463       /**
    464        * Replace output reg with tmp reg.
    465        */
    466       inst->Dst[0].Register.File = TGSI_FILE_TEMPORARY;
    467       inst->Dst[0].Register.Index = ts->out_tmp_index[inst->Dst[0].Register.Index];
    468       ctx->emit_instruction(ctx, inst);
    469    }
    470    else {
    471       ctx->emit_instruction(ctx, inst);
    472    }
    473 }
    474 
    475 
    476 /**
    477  * TGSI property instruction transform callback.
    478  * Transforms a point into a 4-vertex triangle strip.
    479  */
    480 static void
    481 psprite_property(struct tgsi_transform_context *ctx,
    482                  struct tgsi_full_property *prop)
    483 {
    484    switch (prop->Property.PropertyName) {
    485    case TGSI_PROPERTY_GS_OUTPUT_PRIM:
    486        prop->u[0].Data = PIPE_PRIM_TRIANGLE_STRIP;
    487        break;
    488    case TGSI_PROPERTY_GS_MAX_OUTPUT_VERTICES:
    489        prop->u[0].Data *= 4;
    490        break;
    491    default:
    492        break;
    493    }
    494    ctx->emit_property(ctx, prop);
    495 }
    496 
    497 /**
    498  * TGSI utility to transform a geometry shader to support point sprite.
    499  */
    500 struct tgsi_token *
    501 tgsi_add_point_sprite(const struct tgsi_token *tokens_in,
    502                       const unsigned point_coord_enable,
    503                       const bool sprite_origin_lower_left,
    504                       const bool stream_out_point_pos,
    505                       int *aa_point_coord_index)
    506 {
    507    struct psprite_transform_context transform;
    508    const uint num_new_tokens = 200; /* should be enough */
    509    const uint new_len = tgsi_num_tokens(tokens_in) + num_new_tokens;
    510    struct tgsi_token *new_tokens;
    511 
    512    /* setup transformation context */
    513    memset(&transform, 0, sizeof(transform));
    514    transform.base.transform_declaration = psprite_decl;
    515    transform.base.transform_instruction = psprite_inst;
    516    transform.base.transform_property = psprite_property;
    517    transform.base.transform_immediate = psprite_immediate;
    518    transform.base.prolog = psprite_prolog;
    519 
    520    transform.point_size_in = INVALID_INDEX;
    521    transform.point_size_out = INVALID_INDEX;
    522    transform.point_size_tmp = INVALID_INDEX;
    523    transform.point_pos_in = INVALID_INDEX;
    524    transform.point_pos_out = INVALID_INDEX;
    525    transform.point_pos_sout = INVALID_INDEX;
    526    transform.point_pos_tmp = INVALID_INDEX;
    527    transform.point_scale_tmp = INVALID_INDEX;
    528    transform.point_imm = INVALID_INDEX;
    529    transform.point_coord_aa = INVALID_INDEX;
    530    transform.point_coord_k = INVALID_INDEX;
    531 
    532    transform.stream_out_point_pos = stream_out_point_pos;
    533    transform.point_coord_enable = point_coord_enable;
    534    transform.aa_point = aa_point_coord_index != NULL;
    535    transform.max_generic = -1;
    536 
    537    /* point sprite directions based on the immediates (0, 1, 0.5, -1) */
    538    /* (-1, -1, 0, 0) */
    539    transform.point_dir_swz[0] = set_swizzle(-1, -1, 0, 0);
    540    /* (-1, 1, 0, 0) */
    541    transform.point_dir_swz[1] = set_swizzle(-1, 1, 0, 0);
    542    /* (1, -1, 0, 0) */
    543    transform.point_dir_swz[2] = set_swizzle(1, -1, 0, 0);
    544    /* (1, 1, 0, 0) */
    545    transform.point_dir_swz[3] = set_swizzle(1, 1, 0, 0);
    546 
    547    /* point coord based on the immediates (0, 1, 0, -1) */
    548    if (sprite_origin_lower_left) {
    549       /* (0, 0, 0, 1) */
    550       transform.point_coord_swz[0] = set_swizzle(0, 0, 0, 1);
    551       /* (0, 1, 0, 1) */
    552       transform.point_coord_swz[1] = set_swizzle(0, 1, 0, 1);
    553       /* (1, 0, 0, 1) */
    554       transform.point_coord_swz[2] = set_swizzle(1, 0, 0, 1);
    555       /* (1, 1, 0, 1) */
    556       transform.point_coord_swz[3] = set_swizzle(1, 1, 0, 1);
    557    }
    558    else {
    559       /* (0, 1, 0, 1) */
    560       transform.point_coord_swz[0] = set_swizzle(0, 1, 0, 1);
    561       /* (0, 0, 0, 1) */
    562       transform.point_coord_swz[1] = set_swizzle(0, 0, 0, 1);
    563       /* (1, 1, 0, 1) */
    564       transform.point_coord_swz[2] = set_swizzle(1, 1, 0, 1);
    565       /* (1, 0, 0, 1) */
    566       transform.point_coord_swz[3] = set_swizzle(1, 0, 0, 1);
    567    }
    568 
    569 
    570    /* allocate new tokens buffer */
    571    new_tokens = tgsi_alloc_tokens(new_len);
    572    if (!new_tokens)
    573       return NULL;
    574 
    575    /* transform the shader */
    576    tgsi_transform_shader(tokens_in, new_tokens, new_len, &transform.base);
    577 
    578    if (aa_point_coord_index)
    579       *aa_point_coord_index = transform.point_coord_aa;
    580 
    581    return new_tokens;
    582 }
    583