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 fragment shader to support anti-aliasing points.
     29  */
     30 
     31 #include "util/u_debug.h"
     32 #include "util/u_math.h"
     33 #include "tgsi_info.h"
     34 #include "tgsi_aa_point.h"
     35 #include "tgsi_transform.h"
     36 
     37 #define INVALID_INDEX 9999
     38 
     39 struct aa_transform_context
     40 {
     41    struct tgsi_transform_context base;
     42 
     43    unsigned tmp;           // temp register
     44    unsigned color_out;     // frag color out register
     45    unsigned color_tmp;     // frag color temp register
     46    unsigned num_tmp;       // number of temp registers
     47    unsigned num_imm;       // number of immediates
     48    unsigned num_input;     // number of inputs
     49    unsigned aa_point_coord_index;
     50 };
     51 
     52 static inline struct aa_transform_context *
     53 aa_transform_context(struct tgsi_transform_context *ctx)
     54 {
     55    return (struct aa_transform_context *) ctx;
     56 }
     57 
     58 /**
     59  * TGSI declaration transform callback.
     60  */
     61 static void
     62 aa_decl(struct tgsi_transform_context *ctx,
     63               struct tgsi_full_declaration *decl)
     64 {
     65    struct aa_transform_context *ts = aa_transform_context(ctx);
     66 
     67    if (decl->Declaration.File == TGSI_FILE_OUTPUT &&
     68        decl->Semantic.Name == TGSI_SEMANTIC_COLOR &&
     69        decl->Semantic.Index == 0) {
     70          ts->color_out = decl->Range.First;
     71    }
     72    else if (decl->Declaration.File == TGSI_FILE_INPUT) {
     73       ts->num_input++;
     74    }
     75    else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
     76       ts->num_tmp = MAX2(ts->num_tmp, decl->Range.Last + 1);
     77    }
     78 
     79    ctx->emit_declaration(ctx, decl);
     80 }
     81 
     82 /**
     83  * TGSI immediate declaration transform callback.
     84  */
     85 static void
     86 aa_immediate(struct tgsi_transform_context *ctx,
     87                   struct tgsi_full_immediate *imm)
     88 {
     89    struct aa_transform_context *ts = aa_transform_context(ctx);
     90 
     91    ctx->emit_immediate(ctx, imm);
     92    ts->num_imm++;
     93 }
     94 
     95 /**
     96  * TGSI transform prolog callback.
     97  */
     98 static void
     99 aa_prolog(struct tgsi_transform_context *ctx)
    100 {
    101    struct aa_transform_context *ts = aa_transform_context(ctx);
    102    unsigned tmp0;
    103    unsigned texIn;
    104    unsigned imm;
    105 
    106    /* Declare two temporary registers, one for temporary and
    107     * one for color.
    108     */
    109    ts->tmp = ts->num_tmp++;
    110    ts->color_tmp = ts->num_tmp++;
    111 
    112    tgsi_transform_temps_decl(ctx, ts->tmp, ts->color_tmp);
    113 
    114    /* Declare new generic input/texcoord */
    115    texIn = ts->num_input++;
    116    tgsi_transform_input_decl(ctx, texIn, TGSI_SEMANTIC_GENERIC,
    117                              ts->aa_point_coord_index, TGSI_INTERPOLATE_LINEAR);
    118 
    119    /* Declare extra immediates */
    120    imm = ts->num_imm++;
    121    tgsi_transform_immediate_decl(ctx, 0.5, 0.5, 0.45, 1.0);
    122 
    123    /*
    124     * Emit code to compute fragment coverage.
    125     * The point always has radius 0.5.  The threshold value will be a
    126     * value less than, but close to 0.5, such as 0.45.
    127     * We compute a coverage factor from the distance and threshold.
    128     * If the coverage is negative, the fragment is outside the circle and
    129     * it's discarded.
    130     * If the coverage is >= 1, the fragment is fully inside the threshold
    131     * distance.  We limit/clamp the coverage to 1.
    132     * Otherwise, the fragment is between the threshold value and 0.5 and we
    133     * compute a coverage value in [0,1].
    134     *
    135     * Input reg (texIn) usage:
    136     *  texIn.x = x point coord in [0,1]
    137     *  texIn.y = y point coord in [0,1]
    138     *  texIn.z = "k" the smoothing threshold distance
    139     *  texIn.w = unused
    140     *
    141     * Temp reg (t0) usage:
    142     *  t0.x = distance of fragment from center point
    143     *  t0.y = boolean, is t0.x > 0.5, also misc temp usage
    144     *  t0.z = temporary for computing 1/(0.5-k) value
    145     *  t0.w = final coverage value
    146     */
    147 
    148    tmp0 = ts->tmp;
    149 
    150    /* SUB t0.xy, texIn, (0.5, 0,5) */
    151    tgsi_transform_op2_inst(ctx, TGSI_OPCODE_ADD,
    152                            TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_XY,
    153                            TGSI_FILE_INPUT, texIn,
    154                            TGSI_FILE_IMMEDIATE, imm, true);
    155 
    156    /* DP2 t0.x, t0.xy, t0.xy;  # t0.x = x^2 + y^2 */
    157    tgsi_transform_op2_inst(ctx, TGSI_OPCODE_DP2,
    158                            TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_X,
    159                            TGSI_FILE_TEMPORARY, tmp0,
    160                            TGSI_FILE_TEMPORARY, tmp0, false);
    161 
    162    /* SQRT t0.x, t0.x */
    163    tgsi_transform_op1_inst(ctx, TGSI_OPCODE_SQRT,
    164                            TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_X,
    165                            TGSI_FILE_TEMPORARY, tmp0);
    166 
    167    /* compute coverage factor = (0.5-d)/(0.5-k) */
    168 
    169    /* SUB t0.w, 0.5, texIn.z;  # t0.w = 0.5-k */
    170    tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_ADD,
    171                                TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_W,
    172                                TGSI_FILE_IMMEDIATE, imm, TGSI_SWIZZLE_X,
    173                                TGSI_FILE_INPUT, texIn, TGSI_SWIZZLE_Z, true);
    174 
    175    /* SUB t0.y, 0.5, t0.x;  # t0.y = 0.5-d */
    176    tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_ADD,
    177                                TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_Y,
    178                                TGSI_FILE_IMMEDIATE, imm, TGSI_SWIZZLE_X,
    179                                TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_X, true);
    180 
    181    /* DIV t0.w, t0.y, t0.w;  # coverage = (0.5-d)/(0.5-k) */
    182    tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_DIV,
    183                                TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_W,
    184                                TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_Y,
    185                                TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_W, false);
    186 
    187    /* If the coverage value is negative, it means the fragment is outside
    188     * the point's circular boundary.  Kill it.
    189     */
    190    /* KILL_IF tmp0.w;  # if tmp0.w < 0 KILL */
    191    tgsi_transform_kill_inst(ctx, TGSI_FILE_TEMPORARY, tmp0,
    192                             TGSI_SWIZZLE_W, FALSE);
    193 
    194    /* If the distance is less than the threshold, the coverage/alpha value
    195     * will be greater than one.  Clamp to one here.
    196     */
    197    /* MIN tmp0.w, tmp0.w, 1.0 */
    198    tgsi_transform_op2_swz_inst(ctx, TGSI_OPCODE_MIN,
    199                                TGSI_FILE_TEMPORARY, tmp0, TGSI_WRITEMASK_W,
    200                                TGSI_FILE_TEMPORARY, tmp0, TGSI_SWIZZLE_W,
    201                                TGSI_FILE_IMMEDIATE, imm, TGSI_SWIZZLE_W, false);
    202 }
    203 
    204 /**
    205  * TGSI instruction transform callback.
    206  */
    207 static void
    208 aa_inst(struct tgsi_transform_context *ctx,
    209         struct tgsi_full_instruction *inst)
    210 {
    211    struct aa_transform_context *ts = aa_transform_context(ctx);
    212    unsigned i;
    213 
    214    /* Look for writes to color output reg and replace it with
    215     * color temp reg.
    216     */
    217    for (i = 0; i < inst->Instruction.NumDstRegs; i++) {
    218       struct tgsi_full_dst_register *dst = &inst->Dst[i];
    219       if (dst->Register.File == TGSI_FILE_OUTPUT &&
    220           dst->Register.Index == ts->color_out) {
    221          dst->Register.File = TGSI_FILE_TEMPORARY;
    222          dst->Register.Index = ts->color_tmp;
    223       }
    224    }
    225 
    226    ctx->emit_instruction(ctx, inst);
    227 }
    228 
    229 /**
    230  * TGSI transform epilog callback.
    231  */
    232 static void
    233 aa_epilog(struct tgsi_transform_context *ctx)
    234 {
    235    struct aa_transform_context *ts = aa_transform_context(ctx);
    236 
    237    /* add alpha modulation code at tail of program */
    238    assert(ts->color_out != INVALID_INDEX);
    239    assert(ts->color_tmp != INVALID_INDEX);
    240 
    241    /* MOV output.color.xyz colorTmp */
    242    tgsi_transform_op1_inst(ctx, TGSI_OPCODE_MOV,
    243                            TGSI_FILE_OUTPUT, ts->color_out,
    244                            TGSI_WRITEMASK_XYZ,
    245                            TGSI_FILE_TEMPORARY, ts->color_tmp);
    246 
    247    /* MUL output.color.w colorTmp.w tmp0.w */
    248    tgsi_transform_op2_inst(ctx, TGSI_OPCODE_MUL,
    249                            TGSI_FILE_OUTPUT, ts->color_out,
    250                            TGSI_WRITEMASK_W,
    251                            TGSI_FILE_TEMPORARY, ts->color_tmp,
    252                            TGSI_FILE_TEMPORARY, ts->tmp, false);
    253 }
    254 
    255 /**
    256  * TGSI utility to transform a fragment shader to support antialiasing point.
    257  *
    258  * This utility accepts two inputs:
    259  *\param tokens_in  -- the original token string of the shader
    260  *\param aa_point_coord_index -- the semantic index of the generic register
    261  *                            that contains the point sprite texture coord
    262  *
    263  * For each fragment in the point, we compute the distance of the fragment
    264  * from the point center using the point sprite texture coordinates.
    265  * If the distance is greater than 0.5, we'll discard the fragment.
    266  * Otherwise, we'll compute a coverage value which approximates how much
    267  * of the fragment is inside the bounding circle of the point. If the distance
    268  * is less than 'k', the coverage is 1. Else, the coverage is between 0 and 1.
    269  * The final fragment color's alpha channel is then modulated by the coverage
    270  * value.
    271  */
    272 struct tgsi_token *
    273 tgsi_add_aa_point(const struct tgsi_token *tokens_in,
    274                   const int aa_point_coord_index)
    275 {
    276    struct aa_transform_context transform;
    277    const uint num_new_tokens = 200; /* should be enough */
    278    const uint new_len = tgsi_num_tokens(tokens_in) + num_new_tokens;
    279    struct tgsi_token *new_tokens;
    280 
    281    /* allocate new tokens buffer */
    282    new_tokens = tgsi_alloc_tokens(new_len);
    283    if (!new_tokens)
    284       return NULL;
    285 
    286    /* setup transformation context */
    287    memset(&transform, 0, sizeof(transform));
    288    transform.base.transform_declaration = aa_decl;
    289    transform.base.transform_instruction = aa_inst;
    290    transform.base.transform_immediate = aa_immediate;
    291    transform.base.prolog = aa_prolog;
    292    transform.base.epilog = aa_epilog;
    293 
    294    transform.tmp = INVALID_INDEX;
    295    transform.color_out = INVALID_INDEX;
    296    transform.color_tmp = INVALID_INDEX;
    297 
    298    assert(aa_point_coord_index != -1);
    299    transform.aa_point_coord_index = (unsigned)aa_point_coord_index;
    300 
    301    transform.num_tmp = 0;
    302    transform.num_imm = 0;
    303    transform.num_input = 0;
    304 
    305    /* transform the shader */
    306    tgsi_transform_shader(tokens_in, new_tokens, new_len, &transform.base);
    307 
    308    return new_tokens;
    309 }
    310