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
39struct 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
52static inline struct aa_transform_context *
53aa_transform_context(struct tgsi_transform_context *ctx)
54{
55   return (struct aa_transform_context *) ctx;
56}
57
58/**
59 * TGSI declaration transform callback.
60 */
61static void
62aa_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 */
85static void
86aa_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 */
98static void
99aa_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 */
207static void
208aa_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 */
232static void
233aa_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 */
272struct tgsi_token *
273tgsi_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