X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fgallium%2Fauxiliary%2Ftgsi%2Ftgsi_scan.c;h=4cb1a3152c3b8faeb9d784a8d3bba647e4d80b00;hb=c9af7701d1be791752fafcbe19169851bcdf1336;hp=0c810053fe5a3581c30410753fd109ae6452426d;hpb=965a5f181047846b081bf1d510ddc4e5424f6b32;p=mesa.git diff --git a/src/gallium/auxiliary/tgsi/tgsi_scan.c b/src/gallium/auxiliary/tgsi/tgsi_scan.c index 0c810053fe5..4cb1a3152c3 100644 --- a/src/gallium/auxiliary/tgsi/tgsi_scan.c +++ b/src/gallium/auxiliary/tgsi/tgsi_scan.c @@ -50,21 +50,28 @@ is_memory_file(unsigned file) return file == TGSI_FILE_SAMPLER || file == TGSI_FILE_SAMPLER_VIEW || file == TGSI_FILE_IMAGE || - file == TGSI_FILE_BUFFER; + file == TGSI_FILE_BUFFER || + file == TGSI_FILE_HW_ATOMIC; } +static bool +is_mem_query_inst(enum tgsi_opcode opcode) +{ + return opcode == TGSI_OPCODE_RESQ || + opcode == TGSI_OPCODE_TXQ || + opcode == TGSI_OPCODE_TXQS || + opcode == TGSI_OPCODE_LODQ; +} + /** * Is the opcode a "true" texture instruction which samples from a * texture map? */ static bool -is_texture_inst(unsigned opcode) +is_texture_inst(enum tgsi_opcode opcode) { - return (opcode != TGSI_OPCODE_TXQ && - opcode != TGSI_OPCODE_TXQS && - opcode != TGSI_OPCODE_TXQ_LZ && - opcode != TGSI_OPCODE_LODQ && + return (!is_mem_query_inst(opcode) && tgsi_get_opcode_info(opcode)->is_tex); } @@ -74,16 +81,17 @@ is_texture_inst(unsigned opcode) * implicitly? */ static bool -computes_derivative(unsigned opcode) +computes_derivative(enum tgsi_opcode opcode) { if (tgsi_get_opcode_info(opcode)->is_tex) { return opcode != TGSI_OPCODE_TG4 && opcode != TGSI_OPCODE_TXD && opcode != TGSI_OPCODE_TXF && + opcode != TGSI_OPCODE_TXF_LZ && + opcode != TGSI_OPCODE_TEX_LZ && opcode != TGSI_OPCODE_TXL && opcode != TGSI_OPCODE_TXL2 && opcode != TGSI_OPCODE_TXQ && - opcode != TGSI_OPCODE_TXQ_LZ && opcode != TGSI_OPCODE_TXQS; } @@ -95,6 +103,227 @@ computes_derivative(unsigned opcode) } +static void +scan_src_operand(struct tgsi_shader_info *info, + const struct tgsi_full_instruction *fullinst, + const struct tgsi_full_src_register *src, + unsigned src_index, + unsigned usage_mask_after_swizzle, + bool is_interp_instruction, + bool *is_mem_inst) +{ + int ind = src->Register.Index; + + if (info->processor == PIPE_SHADER_COMPUTE && + src->Register.File == TGSI_FILE_SYSTEM_VALUE) { + unsigned name, mask; + + name = info->system_value_semantic_name[src->Register.Index]; + + switch (name) { + case TGSI_SEMANTIC_THREAD_ID: + case TGSI_SEMANTIC_BLOCK_ID: + mask = usage_mask_after_swizzle & TGSI_WRITEMASK_XYZ; + while (mask) { + unsigned i = u_bit_scan(&mask); + + if (name == TGSI_SEMANTIC_THREAD_ID) + info->uses_thread_id[i] = true; + else + info->uses_block_id[i] = true; + } + break; + case TGSI_SEMANTIC_BLOCK_SIZE: + /* The block size is translated to IMM with a fixed block size. */ + if (info->properties[TGSI_PROPERTY_CS_FIXED_BLOCK_WIDTH] == 0) + info->uses_block_size = true; + break; + case TGSI_SEMANTIC_GRID_SIZE: + info->uses_grid_size = true; + break; + } + } + + /* Mark which inputs are effectively used */ + if (src->Register.File == TGSI_FILE_INPUT) { + if (src->Register.Indirect) { + for (ind = 0; ind < info->num_inputs; ++ind) { + info->input_usage_mask[ind] |= usage_mask_after_swizzle; + } + } else { + assert(ind >= 0); + assert(ind < PIPE_MAX_SHADER_INPUTS); + info->input_usage_mask[ind] |= usage_mask_after_swizzle; + } + + if (info->processor == PIPE_SHADER_FRAGMENT) { + unsigned name, index, input; + + if (src->Register.Indirect && src->Indirect.ArrayID) + input = info->input_array_first[src->Indirect.ArrayID]; + else + input = src->Register.Index; + + name = info->input_semantic_name[input]; + index = info->input_semantic_index[input]; + + if (name == TGSI_SEMANTIC_POSITION && + usage_mask_after_swizzle & TGSI_WRITEMASK_Z) + info->reads_z = true; + + if (name == TGSI_SEMANTIC_COLOR) + info->colors_read |= usage_mask_after_swizzle << (index * 4); + + /* Process only interpolated varyings. Don't include POSITION. + * Don't include integer varyings, because they are not + * interpolated. Don't process inputs interpolated by INTERP + * opcodes. Those are tracked separately. + */ + if ((!is_interp_instruction || src_index != 0) && + (name == TGSI_SEMANTIC_GENERIC || + name == TGSI_SEMANTIC_TEXCOORD || + name == TGSI_SEMANTIC_COLOR || + name == TGSI_SEMANTIC_BCOLOR || + name == TGSI_SEMANTIC_FOG || + name == TGSI_SEMANTIC_CLIPDIST)) { + switch (info->input_interpolate[input]) { + case TGSI_INTERPOLATE_COLOR: + case TGSI_INTERPOLATE_PERSPECTIVE: + switch (info->input_interpolate_loc[input]) { + case TGSI_INTERPOLATE_LOC_CENTER: + info->uses_persp_center = TRUE; + break; + case TGSI_INTERPOLATE_LOC_CENTROID: + info->uses_persp_centroid = TRUE; + break; + case TGSI_INTERPOLATE_LOC_SAMPLE: + info->uses_persp_sample = TRUE; + break; + } + break; + case TGSI_INTERPOLATE_LINEAR: + switch (info->input_interpolate_loc[input]) { + case TGSI_INTERPOLATE_LOC_CENTER: + info->uses_linear_center = TRUE; + break; + case TGSI_INTERPOLATE_LOC_CENTROID: + info->uses_linear_centroid = TRUE; + break; + case TGSI_INTERPOLATE_LOC_SAMPLE: + info->uses_linear_sample = TRUE; + break; + } + break; + /* TGSI_INTERPOLATE_CONSTANT doesn't do any interpolation. */ + } + } + } + } + + if (info->processor == PIPE_SHADER_TESS_CTRL && + src->Register.File == TGSI_FILE_OUTPUT) { + unsigned input; + + if (src->Register.Indirect && src->Indirect.ArrayID) + input = info->output_array_first[src->Indirect.ArrayID]; + else + input = src->Register.Index; + + switch (info->output_semantic_name[input]) { + case TGSI_SEMANTIC_PATCH: + info->reads_perpatch_outputs = true; + break; + case TGSI_SEMANTIC_TESSINNER: + case TGSI_SEMANTIC_TESSOUTER: + info->reads_tessfactor_outputs = true; + break; + default: + info->reads_pervertex_outputs = true; + } + } + + /* check for indirect register reads */ + if (src->Register.Indirect) { + info->indirect_files |= (1 << src->Register.File); + info->indirect_files_read |= (1 << src->Register.File); + + /* record indirect constant buffer indexing */ + if (src->Register.File == TGSI_FILE_CONSTANT) { + if (src->Register.Dimension) { + if (src->Dimension.Indirect) + info->const_buffers_indirect = info->const_buffers_declared; + else + info->const_buffers_indirect |= 1u << src->Dimension.Index; + } else { + info->const_buffers_indirect |= 1; + } + } + } + + if (src->Register.Dimension && src->Dimension.Indirect) + info->dim_indirect_files |= 1u << src->Register.File; + + /* Texture samplers */ + if (src->Register.File == TGSI_FILE_SAMPLER) { + const unsigned index = src->Register.Index; + + assert(fullinst->Instruction.Texture); + assert(index < PIPE_MAX_SAMPLERS); + + if (is_texture_inst(fullinst->Instruction.Opcode)) { + const unsigned target = fullinst->Texture.Texture; + assert(target < TGSI_TEXTURE_UNKNOWN); + /* for texture instructions, check that the texture instruction + * target matches the previous sampler view declaration (if there + * was one.) + */ + if (info->sampler_targets[index] == TGSI_TEXTURE_UNKNOWN) { + /* probably no sampler view declaration */ + info->sampler_targets[index] = target; + } else { + /* Make sure the texture instruction's sampler/target info + * agrees with the sampler view declaration. + */ + assert(info->sampler_targets[index] == target); + } + } + } + + if (is_memory_file(src->Register.File) && + !is_mem_query_inst(fullinst->Instruction.Opcode)) { + *is_mem_inst = true; + + if (tgsi_get_opcode_info(fullinst->Instruction.Opcode)->is_store) { + info->writes_memory = TRUE; + + if (src->Register.File == TGSI_FILE_IMAGE) { + if (src->Register.Indirect) + info->images_atomic = info->images_declared; + else + info->images_atomic |= 1 << src->Register.Index; + } else if (src->Register.File == TGSI_FILE_BUFFER) { + if (src->Register.Indirect) + info->shader_buffers_atomic = info->shader_buffers_declared; + else + info->shader_buffers_atomic |= 1 << src->Register.Index; + } + } else { + if (src->Register.File == TGSI_FILE_IMAGE) { + if (src->Register.Indirect) + info->images_load = info->images_declared; + else + info->images_load |= 1 << src->Register.Index; + } else if (src->Register.File == TGSI_FILE_BUFFER) { + if (src->Register.Indirect) + info->shader_buffers_load = info->shader_buffers_declared; + else + info->shader_buffers_load |= 1 << src->Register.Index; + } + } + } +} + + static void scan_instruction(struct tgsi_shader_info *info, const struct tgsi_full_instruction *fullinst, @@ -103,6 +332,7 @@ scan_instruction(struct tgsi_shader_info *info, unsigned i; bool is_mem_inst = false; bool is_interp_instruction = false; + unsigned sampler_src; assert(fullinst->Instruction.Opcode < TGSI_OPCODE_LAST); info->opcode_count[fullinst->Instruction.Opcode]++; @@ -118,6 +348,69 @@ scan_instruction(struct tgsi_shader_info *info, case TGSI_OPCODE_ENDLOOP: (*current_depth)--; break; + case TGSI_OPCODE_TEX: + case TGSI_OPCODE_TEX_LZ: + case TGSI_OPCODE_TXB: + case TGSI_OPCODE_TXD: + case TGSI_OPCODE_TXL: + case TGSI_OPCODE_TXP: + case TGSI_OPCODE_TXQ: + case TGSI_OPCODE_TXQS: + case TGSI_OPCODE_TXF: + case TGSI_OPCODE_TXF_LZ: + case TGSI_OPCODE_TEX2: + case TGSI_OPCODE_TXB2: + case TGSI_OPCODE_TXL2: + case TGSI_OPCODE_TG4: + case TGSI_OPCODE_LODQ: + sampler_src = fullinst->Instruction.NumSrcRegs - 1; + if (fullinst->Src[sampler_src].Register.File != TGSI_FILE_SAMPLER) + info->uses_bindless_samplers = true; + break; + case TGSI_OPCODE_RESQ: + if (tgsi_is_bindless_image_file(fullinst->Src[0].Register.File)) + info->uses_bindless_images = true; + break; + case TGSI_OPCODE_LOAD: + if (tgsi_is_bindless_image_file(fullinst->Src[0].Register.File)) { + info->uses_bindless_images = true; + + if (fullinst->Memory.Texture == TGSI_TEXTURE_BUFFER) + info->uses_bindless_buffer_load = true; + else + info->uses_bindless_image_load = true; + } + break; + case TGSI_OPCODE_ATOMUADD: + case TGSI_OPCODE_ATOMXCHG: + case TGSI_OPCODE_ATOMCAS: + case TGSI_OPCODE_ATOMAND: + case TGSI_OPCODE_ATOMOR: + case TGSI_OPCODE_ATOMXOR: + case TGSI_OPCODE_ATOMUMIN: + case TGSI_OPCODE_ATOMUMAX: + case TGSI_OPCODE_ATOMIMIN: + case TGSI_OPCODE_ATOMIMAX: + case TGSI_OPCODE_ATOMFADD: + if (tgsi_is_bindless_image_file(fullinst->Src[0].Register.File)) { + info->uses_bindless_images = true; + + if (fullinst->Memory.Texture == TGSI_TEXTURE_BUFFER) + info->uses_bindless_buffer_atomic = true; + else + info->uses_bindless_image_atomic = true; + } + break; + case TGSI_OPCODE_STORE: + if (tgsi_is_bindless_image_file(fullinst->Dst[0].Register.File)) { + info->uses_bindless_images = true; + + if (fullinst->Memory.Texture == TGSI_TEXTURE_BUFFER) + info->uses_bindless_buffer_store = true; + else + info->uses_bindless_image_store = true; + } + break; default: break; } @@ -171,182 +464,107 @@ scan_instruction(struct tgsi_shader_info *info, } } - if (fullinst->Instruction.Opcode >= TGSI_OPCODE_F2D && - fullinst->Instruction.Opcode <= TGSI_OPCODE_DSSG) + if ((fullinst->Instruction.Opcode >= TGSI_OPCODE_F2D && + fullinst->Instruction.Opcode <= TGSI_OPCODE_DSSG) || + fullinst->Instruction.Opcode == TGSI_OPCODE_DFMA || + fullinst->Instruction.Opcode == TGSI_OPCODE_DDIV || + fullinst->Instruction.Opcode == TGSI_OPCODE_D2U64 || + fullinst->Instruction.Opcode == TGSI_OPCODE_D2I64 || + fullinst->Instruction.Opcode == TGSI_OPCODE_U642D || + fullinst->Instruction.Opcode == TGSI_OPCODE_I642D) info->uses_doubles = TRUE; for (i = 0; i < fullinst->Instruction.NumSrcRegs; i++) { - const struct tgsi_full_src_register *src = &fullinst->Src[i]; - int ind = src->Register.Index; - - /* Mark which inputs are effectively used */ - if (src->Register.File == TGSI_FILE_INPUT) { - unsigned usage_mask; - usage_mask = tgsi_util_get_inst_usage_mask(fullinst, i); - if (src->Register.Indirect) { - for (ind = 0; ind < info->num_inputs; ++ind) { - info->input_usage_mask[ind] |= usage_mask; - } - } else { - assert(ind >= 0); - assert(ind < PIPE_MAX_SHADER_INPUTS); - info->input_usage_mask[ind] |= usage_mask; - } + scan_src_operand(info, fullinst, &fullinst->Src[i], i, + tgsi_util_get_inst_usage_mask(fullinst, i), + is_interp_instruction, &is_mem_inst); - if (info->processor == PIPE_SHADER_FRAGMENT) { - unsigned name, index, input; + if (fullinst->Src[i].Register.Indirect) { + struct tgsi_full_src_register src = {{0}}; - if (src->Register.Indirect && src->Indirect.ArrayID) - input = info->input_array_first[src->Indirect.ArrayID]; - else - input = src->Register.Index; - - name = info->input_semantic_name[input]; - index = info->input_semantic_index[input]; - - if (name == TGSI_SEMANTIC_POSITION && - (src->Register.SwizzleX == TGSI_SWIZZLE_Z || - src->Register.SwizzleY == TGSI_SWIZZLE_Z || - src->Register.SwizzleZ == TGSI_SWIZZLE_Z || - src->Register.SwizzleW == TGSI_SWIZZLE_Z)) - info->reads_z = TRUE; - - if (name == TGSI_SEMANTIC_COLOR) { - unsigned mask = - (1 << src->Register.SwizzleX) | - (1 << src->Register.SwizzleY) | - (1 << src->Register.SwizzleZ) | - (1 << src->Register.SwizzleW); - - info->colors_read |= mask << (index * 4); - } + src.Register.File = fullinst->Src[i].Indirect.File; + src.Register.Index = fullinst->Src[i].Indirect.Index; - /* Process only interpolated varyings. Don't include POSITION. - * Don't include integer varyings, because they are not - * interpolated. Don't process inputs interpolated by INTERP - * opcodes. Those are tracked separately. - */ - if ((!is_interp_instruction || i != 0) && - (name == TGSI_SEMANTIC_GENERIC || - name == TGSI_SEMANTIC_TEXCOORD || - name == TGSI_SEMANTIC_COLOR || - name == TGSI_SEMANTIC_BCOLOR || - name == TGSI_SEMANTIC_FOG || - name == TGSI_SEMANTIC_CLIPDIST)) { - switch (info->input_interpolate[input]) { - case TGSI_INTERPOLATE_COLOR: - case TGSI_INTERPOLATE_PERSPECTIVE: - switch (info->input_interpolate_loc[input]) { - case TGSI_INTERPOLATE_LOC_CENTER: - info->uses_persp_center = TRUE; - break; - case TGSI_INTERPOLATE_LOC_CENTROID: - info->uses_persp_centroid = TRUE; - break; - case TGSI_INTERPOLATE_LOC_SAMPLE: - info->uses_persp_sample = TRUE; - break; - } - break; - case TGSI_INTERPOLATE_LINEAR: - switch (info->input_interpolate_loc[input]) { - case TGSI_INTERPOLATE_LOC_CENTER: - info->uses_linear_center = TRUE; - break; - case TGSI_INTERPOLATE_LOC_CENTROID: - info->uses_linear_centroid = TRUE; - break; - case TGSI_INTERPOLATE_LOC_SAMPLE: - info->uses_linear_sample = TRUE; - break; - } - break; - /* TGSI_INTERPOLATE_CONSTANT doesn't do any interpolation. */ - } - } - } - } - - /* check for indirect register reads */ - if (src->Register.Indirect) { - info->indirect_files |= (1 << src->Register.File); - info->indirect_files_read |= (1 << src->Register.File); - - /* record indirect constant buffer indexing */ - if (src->Register.File == TGSI_FILE_CONSTANT) { - if (src->Register.Dimension) { - if (src->Dimension.Indirect) - info->const_buffers_indirect = info->const_buffers_declared; - else - info->const_buffers_indirect |= 1u << src->Dimension.Index; - } else { - info->const_buffers_indirect |= 1; - } - } + scan_src_operand(info, fullinst, &src, -1, + 1 << fullinst->Src[i].Indirect.Swizzle, + false, NULL); } - /* Texture samplers */ - if (src->Register.File == TGSI_FILE_SAMPLER) { - const unsigned index = src->Register.Index; + if (fullinst->Src[i].Register.Dimension && + fullinst->Src[i].Dimension.Indirect) { + struct tgsi_full_src_register src = {{0}}; - assert(fullinst->Instruction.Texture); - assert(index < ARRAY_SIZE(info->is_msaa_sampler)); - assert(index < PIPE_MAX_SAMPLERS); + src.Register.File = fullinst->Src[i].DimIndirect.File; + src.Register.Index = fullinst->Src[i].DimIndirect.Index; - if (is_texture_inst(fullinst->Instruction.Opcode)) { - const unsigned target = fullinst->Texture.Texture; - assert(target < TGSI_TEXTURE_UNKNOWN); - /* for texture instructions, check that the texture instruction - * target matches the previous sampler view declaration (if there - * was one.) - */ - if (info->sampler_targets[index] == TGSI_TEXTURE_UNKNOWN) { - /* probably no sampler view declaration */ - info->sampler_targets[index] = target; - } else { - /* Make sure the texture instruction's sampler/target info - * agrees with the sampler view declaration. - */ - assert(info->sampler_targets[index] == target); - } - /* MSAA samplers */ - if (target == TGSI_TEXTURE_2D_MSAA || - target == TGSI_TEXTURE_2D_ARRAY_MSAA) { - info->is_msaa_sampler[src->Register.Index] = TRUE; - } - } + scan_src_operand(info, fullinst, &src, -1, + 1 << fullinst->Src[i].DimIndirect.Swizzle, + false, NULL); } + } - if (is_memory_file(src->Register.File)) { - is_mem_inst = true; + if (fullinst->Instruction.Texture) { + for (i = 0; i < fullinst->Texture.NumOffsets; i++) { + struct tgsi_full_src_register src = {{0}}; - if (tgsi_get_opcode_info(fullinst->Instruction.Opcode)->is_store) { - info->writes_memory = TRUE; + src.Register.File = fullinst->TexOffsets[i].File; + src.Register.Index = fullinst->TexOffsets[i].Index; - if (src->Register.File == TGSI_FILE_IMAGE && - !src->Register.Indirect) - info->images_writemask |= 1 << src->Register.Index; - } + /* The usage mask is suboptimal but should be safe. */ + scan_src_operand(info, fullinst, &src, -1, + (1 << fullinst->TexOffsets[i].SwizzleX) | + (1 << fullinst->TexOffsets[i].SwizzleY) | + (1 << fullinst->TexOffsets[i].SwizzleZ), + false, &is_mem_inst); } } /* check for indirect register writes */ for (i = 0; i < fullinst->Instruction.NumDstRegs; i++) { const struct tgsi_full_dst_register *dst = &fullinst->Dst[i]; + if (dst->Register.Indirect) { + struct tgsi_full_src_register src = {{0}}; + + src.Register.File = dst->Indirect.File; + src.Register.Index = dst->Indirect.Index; + + scan_src_operand(info, fullinst, &src, -1, + 1 << dst->Indirect.Swizzle, false, NULL); + info->indirect_files |= (1 << dst->Register.File); info->indirect_files_written |= (1 << dst->Register.File); } + if (dst->Register.Dimension && dst->Dimension.Indirect) { + struct tgsi_full_src_register src = {{0}}; + + src.Register.File = dst->DimIndirect.File; + src.Register.Index = dst->DimIndirect.Index; + + scan_src_operand(info, fullinst, &src, -1, + 1 << dst->DimIndirect.Swizzle, false, NULL); + + info->dim_indirect_files |= 1u << dst->Register.File; + } + if (is_memory_file(dst->Register.File)) { assert(fullinst->Instruction.Opcode == TGSI_OPCODE_STORE); is_mem_inst = true; info->writes_memory = TRUE; - if (dst->Register.File == TGSI_FILE_IMAGE && - !dst->Register.Indirect) - info->images_writemask |= 1 << dst->Register.Index; + if (dst->Register.File == TGSI_FILE_IMAGE) { + if (dst->Register.Indirect) + info->images_store = info->images_declared; + else + info->images_store |= 1 << dst->Register.Index; + } else if (dst->Register.File == TGSI_FILE_BUFFER) { + if (dst->Register.Indirect) + info->shader_buffers_store = info->shader_buffers_declared; + else + info->shader_buffers_store |= 1 << dst->Register.Index; + } } } @@ -390,14 +608,20 @@ scan_declaration(struct tgsi_shader_info *info, unsigned semName = fulldecl->Semantic.Name; unsigned semIndex = fulldecl->Semantic.Index + (reg - fulldecl->Range.First); + int buffer; + unsigned index, target, type; - /* only first 32 regs will appear in this bitfield */ - info->file_mask[file] |= (1 << reg); + /* + * only first 32 regs will appear in this bitfield, if larger + * bits will wrap around. + */ + info->file_mask[file] |= (1u << (reg & 31)); info->file_count[file]++; info->file_max[file] = MAX2(info->file_max[file], (int)reg); - if (file == TGSI_FILE_CONSTANT) { - int buffer = 0; + switch (file) { + case TGSI_FILE_CONSTANT: + buffer = 0; if (fulldecl->Declaration.Dimension) buffer = fulldecl->Dim.Index2D; @@ -405,8 +629,19 @@ scan_declaration(struct tgsi_shader_info *info, info->const_file_max[buffer] = MAX2(info->const_file_max[buffer], (int)reg); info->const_buffers_declared |= 1u << buffer; - } - else if (file == TGSI_FILE_INPUT) { + break; + + case TGSI_FILE_IMAGE: + info->images_declared |= 1u << reg; + if (fulldecl->Image.Resource == TGSI_TEXTURE_BUFFER) + info->images_buffers |= 1 << reg; + break; + + case TGSI_FILE_BUFFER: + info->shader_buffers_declared |= 1u << reg; + break; + + case TGSI_FILE_INPUT: info->input_semantic_name[reg] = (ubyte) semName; info->input_semantic_index[reg] = (ubyte) semIndex; info->input_interpolate[reg] = (ubyte)fulldecl->Interp.Interpolate; @@ -416,17 +651,21 @@ scan_declaration(struct tgsi_shader_info *info, /* Vertex shaders can have inputs with holes between them. */ info->num_inputs = MAX2(info->num_inputs, reg + 1); - if (semName == TGSI_SEMANTIC_PRIMID) - info->uses_primid = TRUE; - else if (procType == PIPE_SHADER_FRAGMENT) { - if (semName == TGSI_SEMANTIC_POSITION) - info->reads_position = TRUE; - else if (semName == TGSI_SEMANTIC_FACE) - info->uses_frontface = TRUE; + switch (semName) { + case TGSI_SEMANTIC_PRIMID: + info->uses_primid = true; + break; + case TGSI_SEMANTIC_POSITION: + info->reads_position = true; + break; + case TGSI_SEMANTIC_FACE: + info->uses_frontface = true; + break; } - } - else if (file == TGSI_FILE_SYSTEM_VALUE) { - unsigned index = fulldecl->Range.First; + break; + + case TGSI_FILE_SYSTEM_VALUE: + index = fulldecl->Range.First; info->system_value_semantic_name[index] = semName; info->num_system_values = MAX2(info->num_system_values, index + 1); @@ -444,6 +683,9 @@ scan_declaration(struct tgsi_shader_info *info, case TGSI_SEMANTIC_BASEVERTEX: info->uses_basevertex = TRUE; break; + case TGSI_SEMANTIC_DRAWID: + info->uses_drawid = TRUE; + break; case TGSI_SEMANTIC_PRIMID: info->uses_primid = TRUE; break; @@ -459,61 +701,81 @@ scan_declaration(struct tgsi_shader_info *info, case TGSI_SEMANTIC_SAMPLEMASK: info->reads_samplemask = TRUE; break; + case TGSI_SEMANTIC_TESSINNER: + case TGSI_SEMANTIC_TESSOUTER: + info->reads_tess_factors = true; + break; } - } - else if (file == TGSI_FILE_OUTPUT) { + break; + + case TGSI_FILE_OUTPUT: info->output_semantic_name[reg] = (ubyte) semName; info->output_semantic_index[reg] = (ubyte) semIndex; + info->output_usagemask[reg] |= fulldecl->Declaration.UsageMask; info->num_outputs = MAX2(info->num_outputs, reg + 1); - if (semName == TGSI_SEMANTIC_COLOR) - info->colors_written |= 1 << semIndex; - - if (procType == PIPE_SHADER_VERTEX || - procType == PIPE_SHADER_GEOMETRY || - procType == PIPE_SHADER_TESS_CTRL || - procType == PIPE_SHADER_TESS_EVAL) { - switch (semName) { - case TGSI_SEMANTIC_VIEWPORT_INDEX: - info->writes_viewport_index = TRUE; - break; - case TGSI_SEMANTIC_LAYER: - info->writes_layer = TRUE; - break; - case TGSI_SEMANTIC_PSIZE: - info->writes_psize = TRUE; - break; - case TGSI_SEMANTIC_CLIPVERTEX: - info->writes_clipvertex = TRUE; - break; - } + if (fulldecl->Declaration.UsageMask & TGSI_WRITEMASK_X) { + info->output_streams[reg] |= (ubyte)fulldecl->Semantic.StreamX; + info->num_stream_output_components[fulldecl->Semantic.StreamX]++; } - - if (procType == PIPE_SHADER_FRAGMENT) { - switch (semName) { - case TGSI_SEMANTIC_POSITION: - info->writes_z = TRUE; - break; - case TGSI_SEMANTIC_STENCIL: - info->writes_stencil = TRUE; - break; - case TGSI_SEMANTIC_SAMPLEMASK: - info->writes_samplemask = TRUE; - break; - } + if (fulldecl->Declaration.UsageMask & TGSI_WRITEMASK_Y) { + info->output_streams[reg] |= (ubyte)fulldecl->Semantic.StreamY << 2; + info->num_stream_output_components[fulldecl->Semantic.StreamY]++; + } + if (fulldecl->Declaration.UsageMask & TGSI_WRITEMASK_Z) { + info->output_streams[reg] |= (ubyte)fulldecl->Semantic.StreamZ << 4; + info->num_stream_output_components[fulldecl->Semantic.StreamZ]++; + } + if (fulldecl->Declaration.UsageMask & TGSI_WRITEMASK_W) { + info->output_streams[reg] |= (ubyte)fulldecl->Semantic.StreamW << 6; + info->num_stream_output_components[fulldecl->Semantic.StreamW]++; } - if (procType == PIPE_SHADER_VERTEX) { - if (semName == TGSI_SEMANTIC_EDGEFLAG) { - info->writes_edgeflag = TRUE; - } + switch (semName) { + case TGSI_SEMANTIC_PRIMID: + info->writes_primid = true; + break; + case TGSI_SEMANTIC_VIEWPORT_INDEX: + info->writes_viewport_index = true; + break; + case TGSI_SEMANTIC_LAYER: + info->writes_layer = true; + break; + case TGSI_SEMANTIC_PSIZE: + info->writes_psize = true; + break; + case TGSI_SEMANTIC_CLIPVERTEX: + info->writes_clipvertex = true; + break; + case TGSI_SEMANTIC_COLOR: + info->colors_written |= 1 << semIndex; + break; + case TGSI_SEMANTIC_STENCIL: + info->writes_stencil = true; + break; + case TGSI_SEMANTIC_SAMPLEMASK: + info->writes_samplemask = true; + break; + case TGSI_SEMANTIC_EDGEFLAG: + info->writes_edgeflag = true; + break; + case TGSI_SEMANTIC_POSITION: + if (procType == PIPE_SHADER_FRAGMENT) + info->writes_z = true; + else + info->writes_position = true; + break; } - } else if (file == TGSI_FILE_SAMPLER) { + break; + + case TGSI_FILE_SAMPLER: STATIC_ASSERT(sizeof(info->samplers_declared) * 8 >= PIPE_MAX_SAMPLERS); info->samplers_declared |= 1u << reg; - } else if (file == TGSI_FILE_SAMPLER_VIEW) { - unsigned target = fulldecl->SamplerView.Resource; - unsigned type = fulldecl->SamplerView.ReturnTypeX; + break; + + case TGSI_FILE_SAMPLER_VIEW: + target = fulldecl->SamplerView.Resource; + type = fulldecl->SamplerView.ReturnTypeX; assert(target < TGSI_TEXTURE_UNKNOWN); if (info->sampler_targets[reg] == TGSI_TEXTURE_UNKNOWN) { @@ -525,9 +787,7 @@ scan_declaration(struct tgsi_shader_info *info, assert(info->sampler_targets[reg] == target); assert(info->sampler_type[reg] == type); } - } else if (file == TGSI_FILE_IMAGE) { - if (fulldecl->Image.Resource == TGSI_TEXTURE_BUFFER) - info->images_buffers |= 1 << reg; + break; } } } @@ -605,13 +865,12 @@ tgsi_scan_shader(const struct tgsi_token *tokens, procType == PIPE_SHADER_TESS_EVAL || procType == PIPE_SHADER_COMPUTE); info->processor = procType; + info->num_tokens = tgsi_num_tokens(parse.Tokens); /** ** Loop over incoming program tokens/instructions */ while (!tgsi_parse_end_of_tokens(&parse)) { - info->num_tokens++; - tgsi_parse_token( &parse ); switch( parse.FullToken.Token.Type ) { @@ -732,78 +991,238 @@ tgsi_scan_arrays(const struct tgsi_token *tokens, return; } +static void +check_no_subroutines(const struct tgsi_full_instruction *inst) +{ + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_BGNSUB: + case TGSI_OPCODE_ENDSUB: + case TGSI_OPCODE_CAL: + unreachable("subroutines unhandled"); + } +} -/** - * Check if the given shader is a "passthrough" shader consisting of only - * MOV instructions of the form: MOV OUT[n], IN[n] - * - */ -boolean -tgsi_is_passthrough_shader(const struct tgsi_token *tokens) +static unsigned +get_inst_tessfactor_writemask(const struct tgsi_shader_info *info, + const struct tgsi_full_instruction *inst) { - struct tgsi_parse_context parse; + unsigned writemask = 0; - /** - ** Setup to begin parsing input shader - **/ + for (unsigned i = 0; i < inst->Instruction.NumDstRegs; i++) { + const struct tgsi_full_dst_register *dst = &inst->Dst[i]; + + if (dst->Register.File == TGSI_FILE_OUTPUT && + !dst->Register.Indirect) { + unsigned name = info->output_semantic_name[dst->Register.Index]; + + if (name == TGSI_SEMANTIC_TESSINNER) + writemask |= dst->Register.WriteMask; + else if (name == TGSI_SEMANTIC_TESSOUTER) + writemask |= dst->Register.WriteMask << 4; + } + } + return writemask; +} + +static unsigned +get_block_tessfactor_writemask(const struct tgsi_shader_info *info, + struct tgsi_parse_context *parse, + unsigned end_opcode) +{ + struct tgsi_full_instruction *inst; + unsigned writemask = 0; + + tgsi_parse_token(parse); + assert(parse->FullToken.Token.Type == TGSI_TOKEN_TYPE_INSTRUCTION); + inst = &parse->FullToken.FullInstruction; + check_no_subroutines(inst); + + while (inst->Instruction.Opcode != end_opcode) { + + /* Recursively process nested blocks. */ + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_IF: + case TGSI_OPCODE_UIF: + writemask |= + get_block_tessfactor_writemask(info, parse, TGSI_OPCODE_ENDIF); + break; + + case TGSI_OPCODE_BGNLOOP: + writemask |= + get_block_tessfactor_writemask(info, parse, TGSI_OPCODE_ENDLOOP); + break; + + case TGSI_OPCODE_BARRIER: + unreachable("nested BARRIER is illegal"); + break; + + default: + writemask |= get_inst_tessfactor_writemask(info, inst); + } + + tgsi_parse_token(parse); + assert(parse->FullToken.Token.Type == TGSI_TOKEN_TYPE_INSTRUCTION); + inst = &parse->FullToken.FullInstruction; + check_no_subroutines(inst); + } + + return writemask; +} + +static void +get_if_block_tessfactor_writemask(const struct tgsi_shader_info *info, + struct tgsi_parse_context *parse, + unsigned *upper_block_tf_writemask, + unsigned *cond_block_tf_writemask) +{ + struct tgsi_full_instruction *inst; + unsigned then_tessfactor_writemask = 0; + unsigned else_tessfactor_writemask = 0; + unsigned writemask; + bool is_then = true; + + tgsi_parse_token(parse); + assert(parse->FullToken.Token.Type == TGSI_TOKEN_TYPE_INSTRUCTION); + inst = &parse->FullToken.FullInstruction; + check_no_subroutines(inst); + + while (inst->Instruction.Opcode != TGSI_OPCODE_ENDIF) { + + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_ELSE: + is_then = false; + break; + + /* Recursively process nested blocks. */ + case TGSI_OPCODE_IF: + case TGSI_OPCODE_UIF: + get_if_block_tessfactor_writemask(info, parse, + is_then ? &then_tessfactor_writemask : + &else_tessfactor_writemask, + cond_block_tf_writemask); + break; + + case TGSI_OPCODE_BGNLOOP: + *cond_block_tf_writemask |= + get_block_tessfactor_writemask(info, parse, TGSI_OPCODE_ENDLOOP); + break; + + case TGSI_OPCODE_BARRIER: + unreachable("nested BARRIER is illegal"); + break; + default: + /* Process an instruction in the current block. */ + writemask = get_inst_tessfactor_writemask(info, inst); + + if (writemask) { + if (is_then) + then_tessfactor_writemask |= writemask; + else + else_tessfactor_writemask |= writemask; + } + } + + tgsi_parse_token(parse); + assert(parse->FullToken.Token.Type == TGSI_TOKEN_TYPE_INSTRUCTION); + inst = &parse->FullToken.FullInstruction; + check_no_subroutines(inst); + } + + if (then_tessfactor_writemask || else_tessfactor_writemask) { + /* If both statements write the same tess factor channels, + * we can say that the upper block writes them too. */ + *upper_block_tf_writemask |= then_tessfactor_writemask & + else_tessfactor_writemask; + *cond_block_tf_writemask |= then_tessfactor_writemask | + else_tessfactor_writemask; + } +} + +void +tgsi_scan_tess_ctrl(const struct tgsi_token *tokens, + const struct tgsi_shader_info *info, + struct tgsi_tessctrl_info *out) +{ + memset(out, 0, sizeof(*out)); + + if (info->processor != PIPE_SHADER_TESS_CTRL) + return; + + struct tgsi_parse_context parse; if (tgsi_parse_init(&parse, tokens) != TGSI_PARSE_OK) { - debug_printf("tgsi_parse_init() failed in tgsi_is_passthrough_shader()!\n"); - return FALSE; + debug_printf("tgsi_parse_init() failed in tgsi_scan_arrays()!\n"); + return; } - /** - ** Loop over incoming program tokens/instructions + /* The pass works as follows: + * If all codepaths write tess factors, we can say that all invocations + * define tess factors. + * + * Each tess factor channel is tracked separately. */ - while (!tgsi_parse_end_of_tokens(&parse)) { + unsigned main_block_tf_writemask = 0; /* if main block writes tess factors */ + unsigned cond_block_tf_writemask = 0; /* if cond block writes tess factors */ + + /* Initial value = true. Here the pass will accumulate results from multiple + * segments surrounded by barriers. If tess factors aren't written at all, + * it's a shader bug and we don't care if this will be true. + */ + out->tessfactors_are_def_in_all_invocs = true; + while (!tgsi_parse_end_of_tokens(&parse)) { tgsi_parse_token(&parse); - switch (parse.FullToken.Token.Type) { - case TGSI_TOKEN_TYPE_INSTRUCTION: - { - struct tgsi_full_instruction *fullinst = - &parse.FullToken.FullInstruction; - const struct tgsi_full_src_register *src = - &fullinst->Src[0]; - const struct tgsi_full_dst_register *dst = - &fullinst->Dst[0]; - - /* Do a whole bunch of checks for a simple move */ - if (fullinst->Instruction.Opcode != TGSI_OPCODE_MOV || - (src->Register.File != TGSI_FILE_INPUT && - src->Register.File != TGSI_FILE_SYSTEM_VALUE) || - dst->Register.File != TGSI_FILE_OUTPUT || - src->Register.Index != dst->Register.Index || - - src->Register.Negate || - src->Register.Absolute || - - src->Register.SwizzleX != TGSI_SWIZZLE_X || - src->Register.SwizzleY != TGSI_SWIZZLE_Y || - src->Register.SwizzleZ != TGSI_SWIZZLE_Z || - src->Register.SwizzleW != TGSI_SWIZZLE_W || - - dst->Register.WriteMask != TGSI_WRITEMASK_XYZW) - { - tgsi_parse_free(&parse); - return FALSE; - } - } - break; + if (parse.FullToken.Token.Type != TGSI_TOKEN_TYPE_INSTRUCTION) + continue; - case TGSI_TOKEN_TYPE_DECLARATION: - /* fall-through */ - case TGSI_TOKEN_TYPE_IMMEDIATE: - /* fall-through */ - case TGSI_TOKEN_TYPE_PROPERTY: - /* fall-through */ - default: - ; /* no-op */ + struct tgsi_full_instruction *inst = &parse.FullToken.FullInstruction; + check_no_subroutines(inst); + + /* Process nested blocks. */ + switch (inst->Instruction.Opcode) { + case TGSI_OPCODE_IF: + case TGSI_OPCODE_UIF: + get_if_block_tessfactor_writemask(info, &parse, + &main_block_tf_writemask, + &cond_block_tf_writemask); + continue; + + case TGSI_OPCODE_BGNLOOP: + cond_block_tf_writemask |= + get_block_tessfactor_writemask(info, &parse, TGSI_OPCODE_ENDLOOP); + continue; + + case TGSI_OPCODE_BARRIER: + /* The following case must be prevented: + * gl_TessLevelInner = ...; + * barrier(); + * if (gl_InvocationID == 1) + * gl_TessLevelInner = ...; + * + * If you consider disjoint code segments separated by barriers, each + * such segment that writes tess factor channels should write the same + * channels in all codepaths within that segment. + */ + if (main_block_tf_writemask || cond_block_tf_writemask) { + /* Accumulate the result: */ + out->tessfactors_are_def_in_all_invocs &= + !(cond_block_tf_writemask & ~main_block_tf_writemask); + + /* Analyze the next code segment from scratch. */ + main_block_tf_writemask = 0; + cond_block_tf_writemask = 0; + } + continue; } + + main_block_tf_writemask |= get_inst_tessfactor_writemask(info, inst); } - tgsi_parse_free(&parse); + /* Accumulate the result for the last code segment separated by a barrier. */ + if (main_block_tf_writemask || cond_block_tf_writemask) { + out->tessfactors_are_def_in_all_invocs &= + !(cond_block_tf_writemask & ~main_block_tf_writemask); + } - /* if we get here, it's a pass-through shader */ - return TRUE; + tgsi_parse_free(&parse); }