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