1 /**************************************************************************
2 *
3 * Copyright 2010 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
18 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 * USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * The above copyright notice and this permission notice (including the
23 * next paragraph) shall be included in all copies or substantial portions
24 * of the Software.
25 *
26 **************************************************************************/
27
28
29 #include "util/u_memory.h"
30 #include "util/u_math.h"
31 #include "tgsi/tgsi_parse.h"
32 #include "tgsi/tgsi_util.h"
33 #include "tgsi/tgsi_dump.h"
34 #include "tgsi/tgsi_strings.h"
35 #include "lp_bld_debug.h"
36 #include "lp_bld_tgsi.h"
37
38
39 /**
40 * Analysis context.
41 *
42 * This is where we keep store the value of each channel of the IMM/TEMP/OUT
43 * register values, as we walk the shader.
44 */
45 struct analysis_context
46 {
47 struct lp_tgsi_info *info;
48
49 unsigned num_imms;
50 float imm[LP_MAX_TGSI_IMMEDIATES][4];
51 unsigned sample_target[PIPE_MAX_SHADER_SAMPLER_VIEWS];
52
53 struct lp_tgsi_channel_info temp[32][4];
54 };
55
56
57 /**
58 * Describe the specified channel of the src register.
59 */
60 static void
analyse_src(struct analysis_context * ctx,struct lp_tgsi_channel_info * chan_info,const struct tgsi_src_register * src,unsigned chan)61 analyse_src(struct analysis_context *ctx,
62 struct lp_tgsi_channel_info *chan_info,
63 const struct tgsi_src_register *src,
64 unsigned chan)
65 {
66 chan_info->file = TGSI_FILE_NULL;
67 if (!src->Indirect && !src->Absolute && !src->Negate) {
68 unsigned swizzle = tgsi_util_get_src_register_swizzle(src, chan);
69 if (src->File == TGSI_FILE_TEMPORARY) {
70 if (src->Index < ARRAY_SIZE(ctx->temp)) {
71 *chan_info = ctx->temp[src->Index][swizzle];
72 }
73 } else {
74 chan_info->file = src->File;
75 if (src->File == TGSI_FILE_IMMEDIATE) {
76 assert(src->Index < ARRAY_SIZE(ctx->imm));
77 if (src->Index < ARRAY_SIZE(ctx->imm)) {
78 chan_info->u.value = ctx->imm[src->Index][swizzle];
79 }
80 } else {
81 chan_info->u.index = src->Index;
82 chan_info->swizzle = swizzle;
83 }
84 }
85 }
86 }
87
88
89 /**
90 * Whether this register channel refers to a specific immediate value.
91 */
92 static boolean
is_immediate(const struct lp_tgsi_channel_info * chan_info,float value)93 is_immediate(const struct lp_tgsi_channel_info *chan_info, float value)
94 {
95 return chan_info->file == TGSI_FILE_IMMEDIATE &&
96 chan_info->u.value == value;
97 }
98
99
100 /**
101 * Analyse properties of tex instructions, in particular used
102 * to figure out if a texture is considered indirect.
103 * Not actually used by much except the tgsi dumping code.
104 */
105 static void
analyse_tex(struct analysis_context * ctx,const struct tgsi_full_instruction * inst,enum lp_build_tex_modifier modifier)106 analyse_tex(struct analysis_context *ctx,
107 const struct tgsi_full_instruction *inst,
108 enum lp_build_tex_modifier modifier)
109 {
110 struct lp_tgsi_info *info = ctx->info;
111 unsigned chan;
112
113 if (info->num_texs < ARRAY_SIZE(info->tex)) {
114 struct lp_tgsi_texture_info *tex_info = &info->tex[info->num_texs];
115 boolean indirect = FALSE;
116 unsigned readmask = 0;
117
118 tex_info->target = inst->Texture.Texture;
119 switch (inst->Texture.Texture) {
120 case TGSI_TEXTURE_1D:
121 readmask = TGSI_WRITEMASK_X;
122 break;
123 case TGSI_TEXTURE_1D_ARRAY:
124 case TGSI_TEXTURE_2D:
125 case TGSI_TEXTURE_RECT:
126 readmask = TGSI_WRITEMASK_XY;
127 break;
128 case TGSI_TEXTURE_SHADOW1D:
129 case TGSI_TEXTURE_SHADOW1D_ARRAY:
130 case TGSI_TEXTURE_SHADOW2D:
131 case TGSI_TEXTURE_SHADOWRECT:
132 case TGSI_TEXTURE_2D_ARRAY:
133 case TGSI_TEXTURE_2D_MSAA:
134 case TGSI_TEXTURE_3D:
135 case TGSI_TEXTURE_CUBE:
136 readmask = TGSI_WRITEMASK_XYZ;
137 break;
138 case TGSI_TEXTURE_SHADOW2D_ARRAY:
139 case TGSI_TEXTURE_SHADOWCUBE:
140 case TGSI_TEXTURE_2D_ARRAY_MSAA:
141 case TGSI_TEXTURE_CUBE_ARRAY:
142 readmask = TGSI_WRITEMASK_XYZW;
143 /* modifier would be in another not analyzed reg so just say indirect */
144 if (modifier != LP_BLD_TEX_MODIFIER_NONE) {
145 indirect = TRUE;
146 }
147 break;
148 case TGSI_TEXTURE_SHADOWCUBE_ARRAY:
149 readmask = TGSI_WRITEMASK_XYZW;
150 indirect = TRUE;
151 break;
152 default:
153 assert(0);
154 return;
155 }
156
157 if (modifier == LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV) {
158 /* We don't track explicit derivatives, although we could */
159 indirect = TRUE;
160 tex_info->sampler_unit = inst->Src[3].Register.Index;
161 tex_info->texture_unit = inst->Src[3].Register.Index;
162 } else {
163 if (modifier == LP_BLD_TEX_MODIFIER_PROJECTED ||
164 modifier == LP_BLD_TEX_MODIFIER_LOD_BIAS ||
165 modifier == LP_BLD_TEX_MODIFIER_EXPLICIT_LOD) {
166 readmask |= TGSI_WRITEMASK_W;
167 }
168 tex_info->sampler_unit = inst->Src[1].Register.Index;
169 tex_info->texture_unit = inst->Src[1].Register.Index;
170 }
171
172 for (chan = 0; chan < 4; ++chan) {
173 struct lp_tgsi_channel_info *chan_info = &tex_info->coord[chan];
174 if (readmask & (1 << chan)) {
175 analyse_src(ctx, chan_info, &inst->Src[0].Register, chan);
176 if (chan_info->file != TGSI_FILE_INPUT) {
177 indirect = TRUE;
178 }
179 } else {
180 memset(chan_info, 0, sizeof *chan_info);
181 }
182 }
183
184 if (indirect) {
185 info->indirect_textures = TRUE;
186 }
187
188 ++info->num_texs;
189 } else {
190 info->indirect_textures = TRUE;
191 }
192 }
193
194
195 /**
196 * Analyse properties of sample instructions, in particular used
197 * to figure out if a texture is considered indirect.
198 * Not actually used by much except the tgsi dumping code.
199 */
200 static void
analyse_sample(struct analysis_context * ctx,const struct tgsi_full_instruction * inst,enum lp_build_tex_modifier modifier,boolean shadow)201 analyse_sample(struct analysis_context *ctx,
202 const struct tgsi_full_instruction *inst,
203 enum lp_build_tex_modifier modifier,
204 boolean shadow)
205 {
206 struct lp_tgsi_info *info = ctx->info;
207 unsigned chan;
208
209 if (info->num_texs < ARRAY_SIZE(info->tex)) {
210 struct lp_tgsi_texture_info *tex_info = &info->tex[info->num_texs];
211 unsigned target = ctx->sample_target[inst->Src[1].Register.Index];
212 boolean indirect = FALSE;
213 boolean shadow = FALSE;
214 unsigned readmask;
215
216 switch (target) {
217 /* note no shadow targets here */
218 case TGSI_TEXTURE_BUFFER:
219 case TGSI_TEXTURE_1D:
220 readmask = TGSI_WRITEMASK_X;
221 break;
222 case TGSI_TEXTURE_1D_ARRAY:
223 case TGSI_TEXTURE_2D:
224 case TGSI_TEXTURE_RECT:
225 readmask = TGSI_WRITEMASK_XY;
226 break;
227 case TGSI_TEXTURE_2D_ARRAY:
228 case TGSI_TEXTURE_2D_MSAA:
229 case TGSI_TEXTURE_3D:
230 case TGSI_TEXTURE_CUBE:
231 readmask = TGSI_WRITEMASK_XYZ;
232 break;
233 case TGSI_TEXTURE_CUBE_ARRAY:
234 case TGSI_TEXTURE_2D_ARRAY_MSAA:
235 readmask = TGSI_WRITEMASK_XYZW;
236 break;
237 default:
238 assert(0);
239 return;
240 }
241
242 tex_info->target = target;
243 tex_info->texture_unit = inst->Src[1].Register.Index;
244 tex_info->sampler_unit = inst->Src[2].Register.Index;
245
246 if (tex_info->texture_unit != tex_info->sampler_unit) {
247 info->sampler_texture_units_different = TRUE;
248 }
249
250 if (modifier == LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV ||
251 modifier == LP_BLD_TEX_MODIFIER_EXPLICIT_LOD ||
252 modifier == LP_BLD_TEX_MODIFIER_LOD_BIAS || shadow) {
253 /* We don't track insts with additional regs, although we could */
254 indirect = TRUE;
255 }
256
257 for (chan = 0; chan < 4; ++chan) {
258 struct lp_tgsi_channel_info *chan_info = &tex_info->coord[chan];
259 if (readmask & (1 << chan)) {
260 analyse_src(ctx, chan_info, &inst->Src[0].Register, chan);
261 if (chan_info->file != TGSI_FILE_INPUT) {
262 indirect = TRUE;
263 }
264 } else {
265 memset(chan_info, 0, sizeof *chan_info);
266 }
267 }
268
269 if (indirect) {
270 info->indirect_textures = TRUE;
271 }
272
273 ++info->num_texs;
274 } else {
275 info->indirect_textures = TRUE;
276 }
277 }
278
279
280 /**
281 * Process an instruction, and update the register values accordingly.
282 */
283 static void
analyse_instruction(struct analysis_context * ctx,struct tgsi_full_instruction * inst)284 analyse_instruction(struct analysis_context *ctx,
285 struct tgsi_full_instruction *inst)
286 {
287 struct lp_tgsi_info *info = ctx->info;
288 struct lp_tgsi_channel_info (*regs)[4];
289 unsigned max_regs;
290 unsigned i;
291 unsigned index;
292 unsigned chan;
293
294 for (i = 0; i < inst->Instruction.NumDstRegs; ++i) {
295 const struct tgsi_dst_register *dst = &inst->Dst[i].Register;
296
297 /*
298 * Get the lp_tgsi_channel_info array corresponding to the destination
299 * register file.
300 */
301
302 if (dst->File == TGSI_FILE_TEMPORARY) {
303 regs = ctx->temp;
304 max_regs = ARRAY_SIZE(ctx->temp);
305 } else if (dst->File == TGSI_FILE_OUTPUT) {
306 regs = info->output;
307 max_regs = ARRAY_SIZE(info->output);
308 } else if (dst->File == TGSI_FILE_ADDRESS ||
309 dst->File == TGSI_FILE_PREDICATE) {
310 continue;
311 } else {
312 assert(0);
313 continue;
314 }
315
316 /*
317 * Detect direct TEX instructions
318 */
319
320 switch (inst->Instruction.Opcode) {
321 case TGSI_OPCODE_TEX:
322 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_NONE);
323 break;
324 case TGSI_OPCODE_TXD:
325 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV);
326 break;
327 case TGSI_OPCODE_TXB:
328 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS);
329 break;
330 case TGSI_OPCODE_TXL:
331 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD);
332 break;
333 case TGSI_OPCODE_TXP:
334 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_PROJECTED);
335 break;
336 case TGSI_OPCODE_TEX2:
337 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_NONE);
338 break;
339 case TGSI_OPCODE_TXB2:
340 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS);
341 break;
342 case TGSI_OPCODE_TXL2:
343 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD);
344 break;
345 case TGSI_OPCODE_SAMPLE:
346 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_NONE, FALSE);
347 break;
348 case TGSI_OPCODE_SAMPLE_C:
349 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_NONE, TRUE);
350 break;
351 case TGSI_OPCODE_SAMPLE_C_LZ:
352 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_ZERO, TRUE);
353 break;
354 case TGSI_OPCODE_SAMPLE_D:
355 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV, FALSE);
356 break;
357 case TGSI_OPCODE_SAMPLE_B:
358 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS, FALSE);
359 break;
360 case TGSI_OPCODE_SAMPLE_L:
361 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD, FALSE);
362 break;
363 default:
364 break;
365 }
366
367 /*
368 * Keep track of assignments and writes
369 */
370
371 if (dst->Indirect) {
372 /*
373 * It could be any register index so clear all register indices.
374 */
375
376 for (chan = 0; chan < 4; ++chan) {
377 if (dst->WriteMask & (1 << chan)) {
378 for (index = 0; index < max_regs; ++index) {
379 regs[index][chan].file = TGSI_FILE_NULL;
380 }
381 }
382 }
383 } else if (dst->Index < max_regs) {
384 /*
385 * Update this destination register value.
386 */
387
388 struct lp_tgsi_channel_info res[4];
389
390 memset(res, 0, sizeof res);
391
392 if (!inst->Instruction.Predicate &&
393 !inst->Instruction.Saturate) {
394 for (chan = 0; chan < 4; ++chan) {
395 if (dst->WriteMask & (1 << chan)) {
396 if (inst->Instruction.Opcode == TGSI_OPCODE_MOV) {
397 analyse_src(ctx, &res[chan],
398 &inst->Src[0].Register, chan);
399 } else if (inst->Instruction.Opcode == TGSI_OPCODE_MUL) {
400 /*
401 * Propagate values across 1.0 and 0.0 multiplications.
402 */
403
404 struct lp_tgsi_channel_info src0;
405 struct lp_tgsi_channel_info src1;
406
407 analyse_src(ctx, &src0, &inst->Src[0].Register, chan);
408 analyse_src(ctx, &src1, &inst->Src[1].Register, chan);
409
410 if (is_immediate(&src0, 0.0f)) {
411 res[chan] = src0;
412 } else if (is_immediate(&src1, 0.0f)) {
413 res[chan] = src1;
414 } else if (is_immediate(&src0, 1.0f)) {
415 res[chan] = src1;
416 } else if (is_immediate(&src1, 1.0f)) {
417 res[chan] = src0;
418 }
419 }
420 }
421 }
422 }
423
424 for (chan = 0; chan < 4; ++chan) {
425 if (dst->WriteMask & (1 << chan)) {
426 regs[dst->Index][chan] = res[chan];
427 }
428 }
429 }
430 }
431
432 /*
433 * Clear all temporaries information in presence of a control flow opcode.
434 */
435
436 switch (inst->Instruction.Opcode) {
437 case TGSI_OPCODE_IF:
438 case TGSI_OPCODE_UIF:
439 case TGSI_OPCODE_ELSE:
440 case TGSI_OPCODE_ENDIF:
441 case TGSI_OPCODE_BGNLOOP:
442 case TGSI_OPCODE_BRK:
443 case TGSI_OPCODE_BREAKC:
444 case TGSI_OPCODE_CONT:
445 case TGSI_OPCODE_ENDLOOP:
446 case TGSI_OPCODE_CALLNZ:
447 case TGSI_OPCODE_CAL:
448 case TGSI_OPCODE_BGNSUB:
449 case TGSI_OPCODE_ENDSUB:
450 case TGSI_OPCODE_SWITCH:
451 case TGSI_OPCODE_CASE:
452 case TGSI_OPCODE_DEFAULT:
453 case TGSI_OPCODE_ENDSWITCH:
454 case TGSI_OPCODE_RET:
455 case TGSI_OPCODE_END:
456 /* XXX: Are there more cases? */
457 memset(&ctx->temp, 0, sizeof ctx->temp);
458 memset(&info->output, 0, sizeof info->output);
459 default:
460 break;
461 }
462 }
463
464
465 static inline void
dump_info(const struct tgsi_token * tokens,struct lp_tgsi_info * info)466 dump_info(const struct tgsi_token *tokens,
467 struct lp_tgsi_info *info)
468 {
469 unsigned index;
470 unsigned chan;
471
472 tgsi_dump(tokens, 0);
473
474 for (index = 0; index < info->num_texs; ++index) {
475 const struct lp_tgsi_texture_info *tex_info = &info->tex[index];
476 debug_printf("TEX[%u] =", index);
477 for (chan = 0; chan < 4; ++chan) {
478 const struct lp_tgsi_channel_info *chan_info =
479 &tex_info->coord[chan];
480 if (chan_info->file != TGSI_FILE_NULL) {
481 debug_printf(" %s[%u].%c",
482 tgsi_file_name(chan_info->file),
483 chan_info->u.index,
484 "xyzw01"[chan_info->swizzle]);
485 } else {
486 debug_printf(" _");
487 }
488 }
489 debug_printf(", RES[%u], SAMP[%u], %s\n",
490 tex_info->texture_unit,
491 tex_info->sampler_unit,
492 tgsi_texture_names[tex_info->target]);
493 }
494
495 for (index = 0; index < PIPE_MAX_SHADER_OUTPUTS; ++index) {
496 for (chan = 0; chan < 4; ++chan) {
497 const struct lp_tgsi_channel_info *chan_info =
498 &info->output[index][chan];
499 if (chan_info->file != TGSI_FILE_NULL) {
500 debug_printf("OUT[%u].%c = ", index, "xyzw"[chan]);
501 if (chan_info->file == TGSI_FILE_IMMEDIATE) {
502 debug_printf("%f", chan_info->u.value);
503 } else {
504 const char *file_name;
505 switch (chan_info->file) {
506 case TGSI_FILE_CONSTANT:
507 file_name = "CONST";
508 break;
509 case TGSI_FILE_INPUT:
510 file_name = "IN";
511 break;
512 default:
513 file_name = "???";
514 break;
515 }
516 debug_printf("%s[%u].%c",
517 file_name,
518 chan_info->u.index,
519 "xyzw01"[chan_info->swizzle]);
520 }
521 debug_printf("\n");
522 }
523 }
524 }
525 }
526
527
528 /**
529 * Detect any direct relationship between the output color
530 */
531 void
lp_build_tgsi_info(const struct tgsi_token * tokens,struct lp_tgsi_info * info)532 lp_build_tgsi_info(const struct tgsi_token *tokens,
533 struct lp_tgsi_info *info)
534 {
535 struct tgsi_parse_context parse;
536 struct analysis_context *ctx;
537 unsigned index;
538 unsigned chan;
539
540 memset(info, 0, sizeof *info);
541
542 tgsi_scan_shader(tokens, &info->base);
543
544 ctx = CALLOC(1, sizeof(struct analysis_context));
545 ctx->info = info;
546
547 tgsi_parse_init(&parse, tokens);
548
549 while (!tgsi_parse_end_of_tokens(&parse)) {
550 tgsi_parse_token(&parse);
551
552 switch (parse.FullToken.Token.Type) {
553 case TGSI_TOKEN_TYPE_DECLARATION: {
554 struct tgsi_full_declaration *decl = &parse.FullToken.FullDeclaration;
555 if (decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) {
556 for (index = decl->Range.First; index <= decl->Range.Last; index++) {
557 ctx->sample_target[index] = decl->SamplerView.Resource;
558 }
559 }
560 }
561 break;
562
563 case TGSI_TOKEN_TYPE_INSTRUCTION:
564 {
565 struct tgsi_full_instruction *inst =
566 &parse.FullToken.FullInstruction;
567
568 if (inst->Instruction.Opcode == TGSI_OPCODE_END ||
569 inst->Instruction.Opcode == TGSI_OPCODE_BGNSUB) {
570 /* We reached the end of main function body. */
571 goto finished;
572 }
573
574 analyse_instruction(ctx, inst);
575 }
576 break;
577
578 case TGSI_TOKEN_TYPE_IMMEDIATE:
579 {
580 const unsigned size =
581 parse.FullToken.FullImmediate.Immediate.NrTokens - 1;
582 assert(size <= 4);
583 if (ctx->num_imms < ARRAY_SIZE(ctx->imm)) {
584 for (chan = 0; chan < size; ++chan) {
585 float value = parse.FullToken.FullImmediate.u[chan].Float;
586 ctx->imm[ctx->num_imms][chan] = value;
587
588 if (value < 0.0f || value > 1.0f) {
589 info->unclamped_immediates = TRUE;
590 }
591 }
592 ++ctx->num_imms;
593 }
594 }
595 break;
596
597 case TGSI_TOKEN_TYPE_PROPERTY:
598 break;
599
600 default:
601 assert(0);
602 }
603 }
604 finished:
605
606 tgsi_parse_free(&parse);
607 FREE(ctx);
608
609
610 /*
611 * Link the output color values.
612 */
613
614 for (index = 0; index < PIPE_MAX_COLOR_BUFS; ++index) {
615 const struct lp_tgsi_channel_info null_output[4];
616 info->cbuf[index] = null_output;
617 }
618
619 for (index = 0; index < info->base.num_outputs; ++index) {
620 unsigned semantic_name = info->base.output_semantic_name[index];
621 unsigned semantic_index = info->base.output_semantic_index[index];
622 if (semantic_name == TGSI_SEMANTIC_COLOR &&
623 semantic_index < PIPE_MAX_COLOR_BUFS) {
624 info->cbuf[semantic_index] = info->output[index];
625 }
626 }
627
628 if (gallivm_debug & GALLIVM_DEBUG_TGSI) {
629 dump_info(tokens, info);
630 }
631 }
632