• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 shader to support dynamic array indexing
29  * for samplers and constant buffers.
30  * It calculates dynamic array index first and then compare it with each
31  * index and operation will be performed with matching index
32  */
33 
34 #include "util/u_debug.h"
35 #include "util/u_math.h"
36 #include "tgsi_info.h"
37 #include "tgsi_dynamic_indexing.h"
38 #include "tgsi_transform.h"
39 #include "tgsi_dump.h"
40 #include "pipe/p_state.h"
41 
42 
43 struct dIndexing_transform_context
44 {
45    struct tgsi_transform_context base;
46    unsigned orig_num_tmp;
47    unsigned orig_num_imm;
48    unsigned num_const_bufs;
49    unsigned num_samplers;
50    unsigned num_iterations;
51    unsigned const_buf_range[PIPE_MAX_CONSTANT_BUFFERS];
52 };
53 
54 
55 static inline struct dIndexing_transform_context *
dIndexing_transform_context(struct tgsi_transform_context * ctx)56 dIndexing_transform_context(struct tgsi_transform_context *ctx)
57 {
58    return (struct dIndexing_transform_context *) ctx;
59 }
60 
61 
62 /**
63  * TGSI declaration transform callback.
64  */
65 static void
dIndexing_decl(struct tgsi_transform_context * ctx,struct tgsi_full_declaration * decl)66 dIndexing_decl(struct tgsi_transform_context *ctx,
67                struct tgsi_full_declaration *decl)
68 {
69    struct dIndexing_transform_context *dc = dIndexing_transform_context(ctx);
70 
71    if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
72       /**
73        * Emit some extra temporary register to use in keeping track of
74        * dynamic index.
75        */
76       dc->orig_num_tmp = decl->Range.Last;
77       decl->Range.Last = decl->Range.Last + 3;
78    }
79    else if (decl->Declaration.File == TGSI_FILE_CONSTANT) {
80       /* Keep track of number of constants in each buffer */
81       dc->const_buf_range[decl->Dim.Index2D] = decl->Range.Last;
82    }
83    ctx->emit_declaration(ctx, decl);
84 }
85 
86 
87 /**
88  * TGSI transform prolog callback.
89  */
90 static void
dIndexing_prolog(struct tgsi_transform_context * ctx)91 dIndexing_prolog(struct tgsi_transform_context *ctx)
92 {
93    tgsi_transform_immediate_int_decl(ctx, 0, 1, 2, 3);
94    tgsi_transform_immediate_int_decl(ctx, 4, 5, 6, 7);
95 }
96 
97 
98 /**
99  * This function emits some extra instruction to remove dynamic array
100  * indexing of constant buffers / samplers from the shader.
101  * It calculates dynamic array index first and compare it with each index for
102  * declared constants/samplers.
103  */
104 static void
remove_dynamic_indexes(struct tgsi_transform_context * ctx,struct tgsi_full_instruction * orig_inst,const struct tgsi_full_src_register * reg)105 remove_dynamic_indexes(struct tgsi_transform_context *ctx,
106                        struct tgsi_full_instruction *orig_inst,
107                        const struct tgsi_full_src_register *reg)
108 {
109    struct dIndexing_transform_context *dc = dIndexing_transform_context(ctx);
110    int i, j;
111    int tmp_loopIdx = dc->orig_num_tmp + 1;
112    int tmp_cond = dc->orig_num_tmp + 2;
113    int tmp_arrayIdx = dc->orig_num_tmp + 3;
114    int imm_index = dc->orig_num_imm;
115    struct tgsi_full_instruction inst;
116    unsigned INVALID_INDEX = 99999;
117    unsigned file = TGSI_FILE_NULL, index = INVALID_INDEX;
118    unsigned imm_swz_index = INVALID_INDEX;
119 
120    /* calculate dynamic array index store it in tmp_arrayIdx.x */
121    inst = tgsi_default_full_instruction();
122    inst.Instruction.Opcode = TGSI_OPCODE_UADD;
123    inst.Instruction.NumDstRegs = 1;
124    tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
125                           tmp_arrayIdx, TGSI_WRITEMASK_X);
126    inst.Instruction.NumSrcRegs = 2;
127    if (reg->Register.File == TGSI_FILE_CONSTANT) {
128       file = reg->DimIndirect.File;
129       index = reg->DimIndirect.Index;
130       imm_swz_index = reg->Dimension.Index;
131    }
132    else if (reg->Register.File == TGSI_FILE_SAMPLER) {
133       file = reg->Indirect.File;
134       index = reg->Indirect.Index;
135       imm_swz_index = reg->Register.Index;
136    }
137    tgsi_transform_src_reg(&inst.Src[0], file,
138                           index, TGSI_SWIZZLE_X,
139                           TGSI_SWIZZLE_X, TGSI_SWIZZLE_X, TGSI_SWIZZLE_X);
140    tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_IMMEDIATE,
141                           imm_index + (imm_swz_index / 4),
142                           imm_swz_index % 4,
143                           imm_swz_index % 4,
144                           imm_swz_index % 4,
145                           imm_swz_index % 4);
146    ctx->emit_instruction(ctx, &inst);
147 
148    /* initialize counter to zero: tmp_loopIdx = 0 */
149    inst = tgsi_default_full_instruction();
150    inst.Instruction.Opcode = TGSI_OPCODE_MOV;
151    inst.Instruction.NumDstRegs = 1;
152    tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
153                           tmp_loopIdx, TGSI_WRITEMASK_X);
154    inst.Instruction.NumSrcRegs = 1;
155    tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_IMMEDIATE,
156                           imm_index, TGSI_SWIZZLE_X,
157                           TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,
158                           TGSI_SWIZZLE_X);
159    ctx->emit_instruction(ctx, &inst);
160 
161    for (i = 0; i < dc->num_iterations; i++) {
162       boolean out_of_bound_index = FALSE;
163       /**
164        * Make sure we are not exceeding index limit of constant buffer
165        *
166        * For example, In declaration, We have
167        *
168        * DCL CONST[0][0..1]
169        * DCL CONST[1][0..2]
170        * DCL CONST[2][0]
171        *
172        * and our dynamic index instruction is
173        * MOV TEMP[0], CONST[ADDR[0].x][1]
174        *
175        * We have to make sure to skip unrolling for CONST[2] because
176        * it has only one constant in the buffer
177        */
178       if ((reg->Register.File == TGSI_FILE_CONSTANT) &&
179           (!reg->Register.Indirect &&
180            (reg->Register.Index > dc->const_buf_range[i]))) {
181          out_of_bound_index = TRUE;
182       }
183 
184       if (!out_of_bound_index) {
185          /**
186           * If we have an instruction of the format:
187           * OPCODE dst, src..., CONST[K][foo], src...
188           * where K is dynamic and tmp_loopIdx = i (loopcount),
189           * replace it with:
190           *
191           * if (K == tmp_loopIdx)
192           *    OPCODE dst, src... where src is CONST[i][foo] and i is constant
193           * }
194           *
195           * Similarly, If instruction uses dynamic array index for samplers
196           * e.g. OPCODE dst, src, SAMPL[k] ..
197           * replace it with:
198           * if (K == tmp_loopIdx)
199           *    OPCODE dst, src, SAMPL[i][foo]... where i is constant.
200           * }
201           */
202          inst = tgsi_default_full_instruction();
203          inst.Instruction.Opcode = TGSI_OPCODE_USEQ;
204          inst.Instruction.NumDstRegs = 1;
205          tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
206                                 tmp_cond, TGSI_WRITEMASK_X);
207          inst.Instruction.NumSrcRegs = 2;
208          tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,
209                                 tmp_arrayIdx, TGSI_SWIZZLE_X,
210                                 TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,
211                                 TGSI_SWIZZLE_X);
212          tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_TEMPORARY,
213                                 tmp_loopIdx, TGSI_SWIZZLE_X,
214                                 TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,
215                                 TGSI_SWIZZLE_X);
216          ctx->emit_instruction(ctx, &inst);
217 
218          inst = tgsi_default_full_instruction();
219          inst.Instruction.Opcode = TGSI_OPCODE_UIF;
220          inst.Instruction.NumDstRegs = 0;
221          inst.Instruction.NumSrcRegs = 1;
222          tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,
223                                 tmp_cond, TGSI_SWIZZLE_X,
224                                 TGSI_SWIZZLE_X, TGSI_SWIZZLE_X,
225                                 TGSI_SWIZZLE_X);
226          ctx->emit_instruction(ctx, &inst);
227 
228          /* emit instruction with new, non-dynamic source registers */
229          inst = *orig_inst;
230          for (j = 0; j < inst.Instruction.NumSrcRegs; j++) {
231             if (inst.Src[j].Dimension.Indirect &&
232                 inst.Src[j].Register.File == TGSI_FILE_CONSTANT) {
233                inst.Src[j].Register.Dimension = 1;
234                inst.Src[j].Dimension.Index = i;
235                inst.Src[j].Dimension.Indirect = 0;
236             }
237             else if (inst.Src[j].Register.Indirect &&
238                      inst.Src[j].Register.File == TGSI_FILE_SAMPLER) {
239                inst.Src[j].Register.Indirect = 0;
240                inst.Src[j].Register.Index = i;
241             }
242          }
243          ctx->emit_instruction(ctx, &inst);
244 
245          inst = tgsi_default_full_instruction();
246          inst.Instruction.Opcode = TGSI_OPCODE_ENDIF;
247          inst.Instruction.NumDstRegs = 0;
248          inst.Instruction.NumSrcRegs = 0;
249          ctx->emit_instruction(ctx, &inst);
250       }
251 
252       /**
253        * Increment counter
254        * UADD tmp_loopIdx.x tmp_loopIdx.x imm(1)
255        */
256       inst = tgsi_default_full_instruction();
257       inst.Instruction.Opcode = TGSI_OPCODE_UADD;
258       inst.Instruction.NumDstRegs = 1;
259       tgsi_transform_dst_reg(&inst.Dst[0], TGSI_FILE_TEMPORARY,
260                              tmp_loopIdx, TGSI_WRITEMASK_X);
261       inst.Instruction.NumSrcRegs = 2;
262       tgsi_transform_src_reg(&inst.Src[0], TGSI_FILE_TEMPORARY,
263                               tmp_loopIdx, TGSI_SWIZZLE_X,
264                               TGSI_SWIZZLE_X, TGSI_SWIZZLE_X, TGSI_SWIZZLE_X);
265       tgsi_transform_src_reg(&inst.Src[1], TGSI_FILE_IMMEDIATE, imm_index,
266                              TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Y,
267                              TGSI_SWIZZLE_Y, TGSI_SWIZZLE_Y);
268 
269       ctx->emit_instruction(ctx, &inst);
270    }
271 }
272 
273 
274 /**
275  * TGSI instruction transform callback.
276  */
277 static void
dIndexing_inst(struct tgsi_transform_context * ctx,struct tgsi_full_instruction * inst)278 dIndexing_inst(struct tgsi_transform_context *ctx,
279                struct tgsi_full_instruction *inst)
280 {
281    int i;
282    boolean indexing = FALSE;
283    struct dIndexing_transform_context *dc = dIndexing_transform_context(ctx);
284 
285    for (i = 0; i < inst->Instruction.NumSrcRegs; i++) {
286       struct tgsi_full_src_register *src;
287       src = &inst->Src[i];
288       /* check if constant buffer/sampler is using dynamic index */
289       if ((src->Dimension.Indirect &&
290            src->Register.File == TGSI_FILE_CONSTANT) ||
291           (src->Register.Indirect &&
292            src->Register.File == TGSI_FILE_SAMPLER)) {
293 
294          if (indexing)
295             assert("More than one src has dynamic indexing");
296 
297          if (src->Register.File == TGSI_FILE_CONSTANT)
298             dc->num_iterations = dc->num_const_bufs;
299          else
300             dc->num_iterations = dc->num_samplers;
301 
302          remove_dynamic_indexes(ctx, inst, src);
303          indexing = TRUE;
304       }
305    }
306 
307    if (!indexing) {
308       ctx->emit_instruction(ctx, inst);
309    }
310 }
311 
312 /**
313  * TGSI utility to remove dynamic array indexing for constant buffers and
314  * samplers.
315  *
316  * This utility accepts bitmask of declared constant buffers and samplers,
317  * number of immediates used in shader.
318  *
319  * If dynamic array index is used for constant buffers and samplers, this
320  * utility removes those dynamic indexes from shader. It also makes sure
321  * that it has same output as per original shader.
322  * This is achieved by calculating dynamic array index first and then compare
323  * it with each constant buffer/ sampler index and replace that dynamic index
324  * with static index.
325  */
326 struct tgsi_token *
tgsi_remove_dynamic_indexing(const struct tgsi_token * tokens_in,unsigned const_buffers_declared_bitmask,unsigned samplers_declared_bitmask,unsigned imm_count)327 tgsi_remove_dynamic_indexing(const struct tgsi_token *tokens_in,
328                              unsigned const_buffers_declared_bitmask,
329                              unsigned samplers_declared_bitmask,
330                              unsigned imm_count)
331 {
332    struct dIndexing_transform_context transform;
333    const uint num_new_tokens = 1000; /* should be enough */
334    const uint new_len = tgsi_num_tokens(tokens_in) + num_new_tokens;
335    struct tgsi_token *new_tokens;
336 
337    /* setup transformation context */
338    memset(&transform, 0, sizeof(transform));
339    transform.base.transform_declaration = dIndexing_decl;
340    transform.base.transform_instruction = dIndexing_inst;
341    transform.base.prolog = dIndexing_prolog;
342 
343    transform.orig_num_tmp = 0;
344    transform.orig_num_imm = imm_count;
345    /* get count of declared const buffers and sampler from their bitmasks*/
346    transform.num_const_bufs = log2(const_buffers_declared_bitmask + 1);
347    transform.num_samplers = log2(samplers_declared_bitmask + 1);
348    transform.num_iterations = 0;
349 
350    /* allocate new tokens buffer */
351    new_tokens = tgsi_alloc_tokens(new_len);
352    if (!new_tokens)
353       return NULL;
354 
355    /* transform the shader */
356    tgsi_transform_shader(tokens_in, new_tokens, new_len, &transform.base);
357 
358    return new_tokens;
359 }
360 
361 
362