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