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