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