1 /************************************************************************** 2 * 3 * Copyright 2008 VMware, Inc. 4 * All Rights Reserved. 5 * Copyright 2008 VMware, Inc. All rights Reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the 9 * "Software"), to deal in the Software without restriction, including 10 * without limitation the rights to use, copy, modify, merge, publish, 11 * distribute, sub license, and/or sell copies of the Software, and to 12 * permit persons to whom the Software is furnished to do so, subject to 13 * the following conditions: 14 * 15 * The above copyright notice and this permission notice (including the 16 * next paragraph) shall be included in all copies or substantial portions 17 * of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR 23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 **************************************************************************/ 28 29 /** 30 * TGSI program scan utility. 31 * Used to determine which registers and instructions are used by a shader. 32 * 33 * Authors: Brian Paul 34 */ 35 36 37 #include "util/u_debug.h" 38 #include "util/u_math.h" 39 #include "util/u_memory.h" 40 #include "util/u_prim.h" 41 #include "tgsi/tgsi_parse.h" 42 #include "tgsi/tgsi_util.h" 43 #include "tgsi/tgsi_scan.h" 44 45 46 47 48 /** 49 * Scan the given TGSI shader to collect information such as number of 50 * registers used, special instructions used, etc. 51 * \return info the result of the scan 52 */ 53 void 54 tgsi_scan_shader(const struct tgsi_token *tokens, 55 struct tgsi_shader_info *info) 56 { 57 uint procType, i; 58 struct tgsi_parse_context parse; 59 unsigned current_depth = 0; 60 61 memset(info, 0, sizeof(*info)); 62 for (i = 0; i < TGSI_FILE_COUNT; i++) 63 info->file_max[i] = -1; 64 for (i = 0; i < Elements(info->const_file_max); i++) 65 info->const_file_max[i] = -1; 66 info->properties[TGSI_PROPERTY_GS_INVOCATIONS] = 1; 67 68 /** 69 ** Setup to begin parsing input shader 70 **/ 71 if (tgsi_parse_init( &parse, tokens ) != TGSI_PARSE_OK) { 72 debug_printf("tgsi_parse_init() failed in tgsi_scan_shader()!\n"); 73 return; 74 } 75 procType = parse.FullHeader.Processor.Processor; 76 assert(procType == TGSI_PROCESSOR_FRAGMENT || 77 procType == TGSI_PROCESSOR_VERTEX || 78 procType == TGSI_PROCESSOR_GEOMETRY || 79 procType == TGSI_PROCESSOR_TESS_CTRL || 80 procType == TGSI_PROCESSOR_TESS_EVAL || 81 procType == TGSI_PROCESSOR_COMPUTE); 82 info->processor = procType; 83 84 85 /** 86 ** Loop over incoming program tokens/instructions 87 */ 88 while( !tgsi_parse_end_of_tokens( &parse ) ) { 89 90 info->num_tokens++; 91 92 tgsi_parse_token( &parse ); 93 94 switch( parse.FullToken.Token.Type ) { 95 case TGSI_TOKEN_TYPE_INSTRUCTION: 96 { 97 const struct tgsi_full_instruction *fullinst 98 = &parse.FullToken.FullInstruction; 99 uint i; 100 101 assert(fullinst->Instruction.Opcode < TGSI_OPCODE_LAST); 102 info->opcode_count[fullinst->Instruction.Opcode]++; 103 104 switch (fullinst->Instruction.Opcode) { 105 case TGSI_OPCODE_IF: 106 case TGSI_OPCODE_UIF: 107 case TGSI_OPCODE_BGNLOOP: 108 current_depth++; 109 info->max_depth = MAX2(info->max_depth, current_depth); 110 break; 111 case TGSI_OPCODE_ENDIF: 112 case TGSI_OPCODE_ENDLOOP: 113 current_depth--; 114 break; 115 default: 116 break; 117 } 118 119 if (fullinst->Instruction.Opcode == TGSI_OPCODE_INTERP_CENTROID || 120 fullinst->Instruction.Opcode == TGSI_OPCODE_INTERP_OFFSET || 121 fullinst->Instruction.Opcode == TGSI_OPCODE_INTERP_SAMPLE) { 122 const struct tgsi_full_src_register *src0 = &fullinst->Src[0]; 123 unsigned input; 124 125 if (src0->Register.Indirect && src0->Indirect.ArrayID) 126 input = info->input_array_first[src0->Indirect.ArrayID]; 127 else 128 input = src0->Register.Index; 129 130 /* For the INTERP opcodes, the interpolation is always 131 * PERSPECTIVE unless LINEAR is specified. 132 */ 133 switch (info->input_interpolate[input]) { 134 case TGSI_INTERPOLATE_COLOR: 135 case TGSI_INTERPOLATE_CONSTANT: 136 case TGSI_INTERPOLATE_PERSPECTIVE: 137 switch (fullinst->Instruction.Opcode) { 138 case TGSI_OPCODE_INTERP_CENTROID: 139 info->uses_persp_opcode_interp_centroid = true; 140 break; 141 case TGSI_OPCODE_INTERP_OFFSET: 142 info->uses_persp_opcode_interp_offset = true; 143 break; 144 case TGSI_OPCODE_INTERP_SAMPLE: 145 info->uses_persp_opcode_interp_sample = true; 146 break; 147 } 148 break; 149 150 case TGSI_INTERPOLATE_LINEAR: 151 switch (fullinst->Instruction.Opcode) { 152 case TGSI_OPCODE_INTERP_CENTROID: 153 info->uses_linear_opcode_interp_centroid = true; 154 break; 155 case TGSI_OPCODE_INTERP_OFFSET: 156 info->uses_linear_opcode_interp_offset = true; 157 break; 158 case TGSI_OPCODE_INTERP_SAMPLE: 159 info->uses_linear_opcode_interp_sample = true; 160 break; 161 } 162 break; 163 } 164 } 165 166 if (fullinst->Instruction.Opcode >= TGSI_OPCODE_F2D && 167 fullinst->Instruction.Opcode <= TGSI_OPCODE_DSSG) 168 info->uses_doubles = true; 169 170 for (i = 0; i < fullinst->Instruction.NumSrcRegs; i++) { 171 const struct tgsi_full_src_register *src = 172 &fullinst->Src[i]; 173 int ind = src->Register.Index; 174 175 /* Mark which inputs are effectively used */ 176 if (src->Register.File == TGSI_FILE_INPUT) { 177 unsigned usage_mask; 178 usage_mask = tgsi_util_get_inst_usage_mask(fullinst, i); 179 if (src->Register.Indirect) { 180 for (ind = 0; ind < info->num_inputs; ++ind) { 181 info->input_usage_mask[ind] |= usage_mask; 182 } 183 } else { 184 assert(ind >= 0); 185 assert(ind < PIPE_MAX_SHADER_INPUTS); 186 info->input_usage_mask[ind] |= usage_mask; 187 } 188 189 if (procType == TGSI_PROCESSOR_FRAGMENT && 190 info->reads_position && 191 src->Register.Index == 0 && 192 (src->Register.SwizzleX == TGSI_SWIZZLE_Z || 193 src->Register.SwizzleY == TGSI_SWIZZLE_Z || 194 src->Register.SwizzleZ == TGSI_SWIZZLE_Z || 195 src->Register.SwizzleW == TGSI_SWIZZLE_Z)) { 196 info->reads_z = TRUE; 197 } 198 } 199 200 /* check for indirect register reads */ 201 if (src->Register.Indirect) { 202 info->indirect_files |= (1 << src->Register.File); 203 info->indirect_files_read |= (1 << src->Register.File); 204 } 205 206 if (src->Register.Dimension && src->Dimension.Indirect) { 207 info->dimension_indirect_files |= (1 << src->Register.File); 208 } 209 /* MSAA samplers */ 210 if (src->Register.File == TGSI_FILE_SAMPLER) { 211 assert(fullinst->Instruction.Texture); 212 assert((unsigned)src->Register.Index < Elements(info->is_msaa_sampler)); 213 214 if (fullinst->Instruction.Texture && 215 (fullinst->Texture.Texture == TGSI_TEXTURE_2D_MSAA || 216 fullinst->Texture.Texture == TGSI_TEXTURE_2D_ARRAY_MSAA)) { 217 info->is_msaa_sampler[src->Register.Index] = TRUE; 218 } 219 } 220 } 221 222 /* check for indirect register writes */ 223 for (i = 0; i < fullinst->Instruction.NumDstRegs; i++) { 224 const struct tgsi_full_dst_register *dst = &fullinst->Dst[i]; 225 if (dst->Register.Indirect) { 226 info->indirect_files |= (1 << dst->Register.File); 227 info->indirect_files_written |= (1 << dst->Register.File); 228 } 229 if (dst->Register.Dimension && dst->Dimension.Indirect) 230 info->dimension_indirect_files |= (1 << dst->Register.File); 231 } 232 233 info->num_instructions++; 234 } 235 break; 236 237 case TGSI_TOKEN_TYPE_DECLARATION: 238 { 239 const struct tgsi_full_declaration *fulldecl 240 = &parse.FullToken.FullDeclaration; 241 const uint file = fulldecl->Declaration.File; 242 uint reg; 243 244 if (fulldecl->Declaration.Array) { 245 unsigned array_id = fulldecl->Array.ArrayID; 246 247 switch (file) { 248 case TGSI_FILE_INPUT: 249 assert(array_id < ARRAY_SIZE(info->input_array_first)); 250 info->input_array_first[array_id] = fulldecl->Range.First; 251 info->input_array_last[array_id] = fulldecl->Range.Last; 252 break; 253 case TGSI_FILE_OUTPUT: 254 assert(array_id < ARRAY_SIZE(info->output_array_first)); 255 info->output_array_first[array_id] = fulldecl->Range.First; 256 info->output_array_last[array_id] = fulldecl->Range.Last; 257 break; 258 } 259 info->array_max[file] = MAX2(info->array_max[file], array_id); 260 } 261 262 for (reg = fulldecl->Range.First; 263 reg <= fulldecl->Range.Last; 264 reg++) { 265 unsigned semName = fulldecl->Semantic.Name; 266 unsigned semIndex = 267 fulldecl->Semantic.Index + (reg - fulldecl->Range.First); 268 269 /* only first 32 regs will appear in this bitfield */ 270 info->file_mask[file] |= (1 << reg); 271 info->file_count[file]++; 272 info->file_max[file] = MAX2(info->file_max[file], (int)reg); 273 274 if (file == TGSI_FILE_CONSTANT) { 275 int buffer = 0; 276 277 if (fulldecl->Declaration.Dimension) 278 buffer = fulldecl->Dim.Index2D; 279 280 info->const_file_max[buffer] = 281 MAX2(info->const_file_max[buffer], (int)reg); 282 } 283 else if (file == TGSI_FILE_INPUT) { 284 info->input_semantic_name[reg] = (ubyte) semName; 285 info->input_semantic_index[reg] = (ubyte) semIndex; 286 info->input_interpolate[reg] = (ubyte)fulldecl->Interp.Interpolate; 287 info->input_interpolate_loc[reg] = (ubyte)fulldecl->Interp.Location; 288 info->input_cylindrical_wrap[reg] = (ubyte)fulldecl->Interp.CylindricalWrap; 289 info->num_inputs++; 290 291 /* Only interpolated varyings. Don't include POSITION. 292 * Don't include integer varyings, because they are not 293 * interpolated. 294 */ 295 if (semName == TGSI_SEMANTIC_GENERIC || 296 semName == TGSI_SEMANTIC_TEXCOORD || 297 semName == TGSI_SEMANTIC_COLOR || 298 semName == TGSI_SEMANTIC_BCOLOR || 299 semName == TGSI_SEMANTIC_FOG || 300 semName == TGSI_SEMANTIC_CLIPDIST || 301 semName == TGSI_SEMANTIC_CULLDIST) { 302 switch (fulldecl->Interp.Interpolate) { 303 case TGSI_INTERPOLATE_COLOR: 304 case TGSI_INTERPOLATE_PERSPECTIVE: 305 switch (fulldecl->Interp.Location) { 306 case TGSI_INTERPOLATE_LOC_CENTER: 307 info->uses_persp_center = true; 308 break; 309 case TGSI_INTERPOLATE_LOC_CENTROID: 310 info->uses_persp_centroid = true; 311 break; 312 case TGSI_INTERPOLATE_LOC_SAMPLE: 313 info->uses_persp_sample = true; 314 break; 315 } 316 break; 317 case TGSI_INTERPOLATE_LINEAR: 318 switch (fulldecl->Interp.Location) { 319 case TGSI_INTERPOLATE_LOC_CENTER: 320 info->uses_linear_center = true; 321 break; 322 case TGSI_INTERPOLATE_LOC_CENTROID: 323 info->uses_linear_centroid = true; 324 break; 325 case TGSI_INTERPOLATE_LOC_SAMPLE: 326 info->uses_linear_sample = true; 327 break; 328 } 329 break; 330 /* TGSI_INTERPOLATE_CONSTANT doesn't do any interpolation. */ 331 } 332 } 333 334 if (semName == TGSI_SEMANTIC_PRIMID) 335 info->uses_primid = TRUE; 336 else if (procType == TGSI_PROCESSOR_FRAGMENT) { 337 if (semName == TGSI_SEMANTIC_POSITION) 338 info->reads_position = TRUE; 339 else if (semName == TGSI_SEMANTIC_FACE) 340 info->uses_frontface = TRUE; 341 } 342 } 343 else if (file == TGSI_FILE_SYSTEM_VALUE) { 344 unsigned index = fulldecl->Range.First; 345 346 info->system_value_semantic_name[index] = semName; 347 info->num_system_values = MAX2(info->num_system_values, 348 index + 1); 349 350 if (semName == TGSI_SEMANTIC_INSTANCEID) { 351 info->uses_instanceid = TRUE; 352 } 353 else if (semName == TGSI_SEMANTIC_VERTEXID) { 354 info->uses_vertexid = TRUE; 355 } 356 else if (semName == TGSI_SEMANTIC_VERTEXID_NOBASE) { 357 info->uses_vertexid_nobase = TRUE; 358 } 359 else if (semName == TGSI_SEMANTIC_BASEVERTEX) { 360 info->uses_basevertex = TRUE; 361 } 362 else if (semName == TGSI_SEMANTIC_PRIMID) { 363 info->uses_primid = TRUE; 364 } else if (semName == TGSI_SEMANTIC_INVOCATIONID) { 365 info->uses_invocationid = TRUE; 366 } 367 } 368 else if (file == TGSI_FILE_OUTPUT) { 369 info->output_semantic_name[reg] = (ubyte) semName; 370 info->output_semantic_index[reg] = (ubyte) semIndex; 371 info->num_outputs++; 372 373 if (semName == TGSI_SEMANTIC_COLOR) 374 info->colors_written |= 1 << semIndex; 375 376 if (procType == TGSI_PROCESSOR_VERTEX || 377 procType == TGSI_PROCESSOR_GEOMETRY || 378 procType == TGSI_PROCESSOR_TESS_CTRL || 379 procType == TGSI_PROCESSOR_TESS_EVAL) { 380 if (semName == TGSI_SEMANTIC_VIEWPORT_INDEX) { 381 info->writes_viewport_index = TRUE; 382 } 383 else if (semName == TGSI_SEMANTIC_LAYER) { 384 info->writes_layer = TRUE; 385 } 386 else if (semName == TGSI_SEMANTIC_PSIZE) { 387 info->writes_psize = TRUE; 388 } 389 else if (semName == TGSI_SEMANTIC_CLIPVERTEX) { 390 info->writes_clipvertex = TRUE; 391 } 392 } 393 394 if (procType == TGSI_PROCESSOR_FRAGMENT) { 395 if (semName == TGSI_SEMANTIC_POSITION) { 396 info->writes_z = TRUE; 397 } 398 else if (semName == TGSI_SEMANTIC_STENCIL) { 399 info->writes_stencil = TRUE; 400 } 401 } 402 403 if (procType == TGSI_PROCESSOR_VERTEX) { 404 if (semName == TGSI_SEMANTIC_EDGEFLAG) { 405 info->writes_edgeflag = TRUE; 406 } 407 } 408 } else if (file == TGSI_FILE_SAMPLER) { 409 info->samplers_declared |= 1 << reg; 410 } 411 } 412 } 413 break; 414 415 case TGSI_TOKEN_TYPE_IMMEDIATE: 416 { 417 uint reg = info->immediate_count++; 418 uint file = TGSI_FILE_IMMEDIATE; 419 420 info->file_mask[file] |= (1 << reg); 421 info->file_count[file]++; 422 info->file_max[file] = MAX2(info->file_max[file], (int)reg); 423 } 424 break; 425 426 case TGSI_TOKEN_TYPE_PROPERTY: 427 { 428 const struct tgsi_full_property *fullprop 429 = &parse.FullToken.FullProperty; 430 unsigned name = fullprop->Property.PropertyName; 431 unsigned value = fullprop->u[0].Data; 432 433 assert(name < Elements(info->properties)); 434 info->properties[name] = value; 435 436 switch (name) { 437 case TGSI_PROPERTY_NUM_CLIPDIST_ENABLED: 438 info->num_written_clipdistance = value; 439 info->clipdist_writemask |= (1 << value) - 1; 440 break; 441 case TGSI_PROPERTY_NUM_CULLDIST_ENABLED: 442 info->num_written_culldistance = value; 443 info->culldist_writemask |= (1 << value) - 1; 444 break; 445 } 446 } 447 break; 448 449 default: 450 assert( 0 ); 451 } 452 } 453 454 info->uses_kill = (info->opcode_count[TGSI_OPCODE_KILL_IF] || 455 info->opcode_count[TGSI_OPCODE_KILL]); 456 457 /* The dimensions of the IN decleration in geometry shader have 458 * to be deduced from the type of the input primitive. 459 */ 460 if (procType == TGSI_PROCESSOR_GEOMETRY) { 461 unsigned input_primitive = 462 info->properties[TGSI_PROPERTY_GS_INPUT_PRIM]; 463 int num_verts = u_vertices_per_prim(input_primitive); 464 int j; 465 info->file_count[TGSI_FILE_INPUT] = num_verts; 466 info->file_max[TGSI_FILE_INPUT] = 467 MAX2(info->file_max[TGSI_FILE_INPUT], num_verts - 1); 468 for (j = 0; j < num_verts; ++j) { 469 info->file_mask[TGSI_FILE_INPUT] |= (1 << j); 470 } 471 } 472 473 tgsi_parse_free (&parse); 474 } 475 476 477 478 /** 479 * Check if the given shader is a "passthrough" shader consisting of only 480 * MOV instructions of the form: MOV OUT[n], IN[n] 481 * 482 */ 483 boolean 484 tgsi_is_passthrough_shader(const struct tgsi_token *tokens) 485 { 486 struct tgsi_parse_context parse; 487 488 /** 489 ** Setup to begin parsing input shader 490 **/ 491 if (tgsi_parse_init(&parse, tokens) != TGSI_PARSE_OK) { 492 debug_printf("tgsi_parse_init() failed in tgsi_is_passthrough_shader()!\n"); 493 return FALSE; 494 } 495 496 /** 497 ** Loop over incoming program tokens/instructions 498 */ 499 while (!tgsi_parse_end_of_tokens(&parse)) { 500 501 tgsi_parse_token(&parse); 502 503 switch (parse.FullToken.Token.Type) { 504 case TGSI_TOKEN_TYPE_INSTRUCTION: 505 { 506 struct tgsi_full_instruction *fullinst = 507 &parse.FullToken.FullInstruction; 508 const struct tgsi_full_src_register *src = 509 &fullinst->Src[0]; 510 const struct tgsi_full_dst_register *dst = 511 &fullinst->Dst[0]; 512 513 /* Do a whole bunch of checks for a simple move */ 514 if (fullinst->Instruction.Opcode != TGSI_OPCODE_MOV || 515 (src->Register.File != TGSI_FILE_INPUT && 516 src->Register.File != TGSI_FILE_SYSTEM_VALUE) || 517 dst->Register.File != TGSI_FILE_OUTPUT || 518 src->Register.Index != dst->Register.Index || 519 520 src->Register.Negate || 521 src->Register.Absolute || 522 523 src->Register.SwizzleX != TGSI_SWIZZLE_X || 524 src->Register.SwizzleY != TGSI_SWIZZLE_Y || 525 src->Register.SwizzleZ != TGSI_SWIZZLE_Z || 526 src->Register.SwizzleW != TGSI_SWIZZLE_W || 527 528 dst->Register.WriteMask != TGSI_WRITEMASK_XYZW) 529 { 530 tgsi_parse_free(&parse); 531 return FALSE; 532 } 533 } 534 break; 535 536 case TGSI_TOKEN_TYPE_DECLARATION: 537 /* fall-through */ 538 case TGSI_TOKEN_TYPE_IMMEDIATE: 539 /* fall-through */ 540 case TGSI_TOKEN_TYPE_PROPERTY: 541 /* fall-through */ 542 default: 543 ; /* no-op */ 544 } 545 } 546 547 tgsi_parse_free(&parse); 548 549 /* if we get here, it's a pass-through shader */ 550 return TRUE; 551 } 552