util: use standard name for vsnprintf()
[mesa.git] / src / gallium / auxiliary / gallivm / lp_bld_tgsi_info.c
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
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
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
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
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
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 continue;
310 } else if (dst->File == TGSI_FILE_BUFFER) {
311 continue;
312 } else {
313 assert(0);
314 continue;
315 }
316
317 /*
318 * Detect direct TEX instructions
319 */
320
321 switch (inst->Instruction.Opcode) {
322 case TGSI_OPCODE_TEX:
323 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_NONE);
324 break;
325 case TGSI_OPCODE_TXD:
326 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV);
327 break;
328 case TGSI_OPCODE_TXB:
329 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS);
330 break;
331 case TGSI_OPCODE_TXL:
332 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD);
333 break;
334 case TGSI_OPCODE_TXP:
335 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_PROJECTED);
336 break;
337 case TGSI_OPCODE_TEX2:
338 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_NONE);
339 break;
340 case TGSI_OPCODE_TXB2:
341 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS);
342 break;
343 case TGSI_OPCODE_TXL2:
344 analyse_tex(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD);
345 break;
346 case TGSI_OPCODE_SAMPLE:
347 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_NONE, FALSE);
348 break;
349 case TGSI_OPCODE_SAMPLE_C:
350 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_NONE, TRUE);
351 break;
352 case TGSI_OPCODE_SAMPLE_C_LZ:
353 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_ZERO, TRUE);
354 break;
355 case TGSI_OPCODE_SAMPLE_D:
356 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_DERIV, FALSE);
357 break;
358 case TGSI_OPCODE_SAMPLE_B:
359 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_LOD_BIAS, FALSE);
360 break;
361 case TGSI_OPCODE_SAMPLE_L:
362 analyse_sample(ctx, inst, LP_BLD_TEX_MODIFIER_EXPLICIT_LOD, FALSE);
363 break;
364 default:
365 break;
366 }
367
368 /*
369 * Keep track of assignments and writes
370 */
371
372 if (dst->Indirect) {
373 /*
374 * It could be any register index so clear all register indices.
375 */
376
377 for (chan = 0; chan < 4; ++chan) {
378 if (dst->WriteMask & (1 << chan)) {
379 for (index = 0; index < max_regs; ++index) {
380 regs[index][chan].file = TGSI_FILE_NULL;
381 }
382 }
383 }
384 } else if (dst->Index < max_regs) {
385 /*
386 * Update this destination register value.
387 */
388
389 struct lp_tgsi_channel_info res[4];
390
391 memset(res, 0, sizeof res);
392
393 if (!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_CONT:
444 case TGSI_OPCODE_ENDLOOP:
445 case TGSI_OPCODE_CAL:
446 case TGSI_OPCODE_BGNSUB:
447 case TGSI_OPCODE_ENDSUB:
448 case TGSI_OPCODE_SWITCH:
449 case TGSI_OPCODE_CASE:
450 case TGSI_OPCODE_DEFAULT:
451 case TGSI_OPCODE_ENDSWITCH:
452 case TGSI_OPCODE_RET:
453 case TGSI_OPCODE_END:
454 /* XXX: Are there more cases? */
455 memset(&ctx->temp, 0, sizeof ctx->temp);
456 memset(&info->output, 0, sizeof info->output);
457 default:
458 break;
459 }
460 }
461
462
463 static inline void
464 dump_info(const struct tgsi_token *tokens,
465 struct lp_tgsi_info *info)
466 {
467 unsigned index;
468 unsigned chan;
469
470 tgsi_dump(tokens, 0);
471
472 for (index = 0; index < info->num_texs; ++index) {
473 const struct lp_tgsi_texture_info *tex_info = &info->tex[index];
474 debug_printf("TEX[%u] =", index);
475 for (chan = 0; chan < 4; ++chan) {
476 const struct lp_tgsi_channel_info *chan_info =
477 &tex_info->coord[chan];
478 if (chan_info->file != TGSI_FILE_NULL) {
479 debug_printf(" %s[%u].%c",
480 tgsi_file_name(chan_info->file),
481 chan_info->u.index,
482 "xyzw01"[chan_info->swizzle]);
483 } else {
484 debug_printf(" _");
485 }
486 }
487 debug_printf(", RES[%u], SAMP[%u], %s\n",
488 tex_info->texture_unit,
489 tex_info->sampler_unit,
490 tgsi_texture_names[tex_info->target]);
491 }
492
493 for (index = 0; index < PIPE_MAX_SHADER_OUTPUTS; ++index) {
494 for (chan = 0; chan < 4; ++chan) {
495 const struct lp_tgsi_channel_info *chan_info =
496 &info->output[index][chan];
497 if (chan_info->file != TGSI_FILE_NULL) {
498 debug_printf("OUT[%u].%c = ", index, "xyzw"[chan]);
499 if (chan_info->file == TGSI_FILE_IMMEDIATE) {
500 debug_printf("%f", chan_info->u.value);
501 } else {
502 const char *file_name;
503 switch (chan_info->file) {
504 case TGSI_FILE_CONSTANT:
505 file_name = "CONST";
506 break;
507 case TGSI_FILE_INPUT:
508 file_name = "IN";
509 break;
510 default:
511 file_name = "???";
512 break;
513 }
514 debug_printf("%s[%u].%c",
515 file_name,
516 chan_info->u.index,
517 "xyzw01"[chan_info->swizzle]);
518 }
519 debug_printf("\n");
520 }
521 }
522 }
523 }
524
525
526 /**
527 * Detect any direct relationship between the output color
528 */
529 void
530 lp_build_tgsi_info(const struct tgsi_token *tokens,
531 struct lp_tgsi_info *info)
532 {
533 struct tgsi_parse_context parse;
534 struct analysis_context *ctx;
535 unsigned index;
536 unsigned chan;
537
538 memset(info, 0, sizeof *info);
539
540 tgsi_scan_shader(tokens, &info->base);
541
542 ctx = CALLOC(1, sizeof(struct analysis_context));
543 ctx->info = info;
544
545 tgsi_parse_init(&parse, tokens);
546
547 while (!tgsi_parse_end_of_tokens(&parse)) {
548 tgsi_parse_token(&parse);
549
550 switch (parse.FullToken.Token.Type) {
551 case TGSI_TOKEN_TYPE_DECLARATION: {
552 struct tgsi_full_declaration *decl = &parse.FullToken.FullDeclaration;
553 if (decl->Declaration.File == TGSI_FILE_SAMPLER_VIEW) {
554 for (index = decl->Range.First; index <= decl->Range.Last; index++) {
555 ctx->sample_target[index] = decl->SamplerView.Resource;
556 }
557 }
558 }
559 break;
560
561 case TGSI_TOKEN_TYPE_INSTRUCTION:
562 {
563 struct tgsi_full_instruction *inst =
564 &parse.FullToken.FullInstruction;
565
566 if (inst->Instruction.Opcode == TGSI_OPCODE_END ||
567 inst->Instruction.Opcode == TGSI_OPCODE_BGNSUB) {
568 /* We reached the end of main function body. */
569 goto finished;
570 }
571
572 analyse_instruction(ctx, inst);
573 }
574 break;
575
576 case TGSI_TOKEN_TYPE_IMMEDIATE:
577 {
578 const unsigned size =
579 parse.FullToken.FullImmediate.Immediate.NrTokens - 1;
580 assert(size <= 4);
581 if (ctx->num_imms < ARRAY_SIZE(ctx->imm)) {
582 for (chan = 0; chan < size; ++chan) {
583 float value = parse.FullToken.FullImmediate.u[chan].Float;
584 ctx->imm[ctx->num_imms][chan] = value;
585
586 if (value < 0.0f || value > 1.0f) {
587 info->unclamped_immediates = TRUE;
588 }
589 }
590 ++ctx->num_imms;
591 }
592 }
593 break;
594
595 case TGSI_TOKEN_TYPE_PROPERTY:
596 break;
597
598 default:
599 assert(0);
600 }
601 }
602 finished:
603
604 tgsi_parse_free(&parse);
605 FREE(ctx);
606
607
608 /*
609 * Link the output color values.
610 */
611
612 for (index = 0; index < PIPE_MAX_COLOR_BUFS; ++index) {
613 static const struct lp_tgsi_channel_info null_output[4];
614 info->cbuf[index] = null_output;
615 }
616
617 for (index = 0; index < info->base.num_outputs; ++index) {
618 unsigned semantic_name = info->base.output_semantic_name[index];
619 unsigned semantic_index = info->base.output_semantic_index[index];
620 if (semantic_name == TGSI_SEMANTIC_COLOR &&
621 semantic_index < PIPE_MAX_COLOR_BUFS) {
622 info->cbuf[semantic_index] = info->output[index];
623 }
624 }
625
626 if (gallivm_debug & GALLIVM_DEBUG_TGSI) {
627 dump_info(tokens, info);
628 }
629 }