1 /* 2 * Copyright 2010 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 /** 25 * \file ir_set_program_inouts.cpp 26 * 27 * Sets the InputsRead and OutputsWritten of Mesa programs. 28 * 29 * Additionally, for fragment shaders, sets the InterpQualifier array, the 30 * IsCentroid bitfield, and the UsesDFdy flag. 31 * 32 * Mesa programs (gl_program, not gl_shader_program) have a set of 33 * flags indicating which varyings are read and written. Computing 34 * which are actually read from some sort of backend code can be 35 * tricky when variable array indexing involved. So this pass 36 * provides support for setting InputsRead and OutputsWritten right 37 * from the GLSL IR. 38 */ 39 40 #include "main/core.h" /* for struct gl_program */ 41 #include "program/hash_table.h" 42 #include "ir.h" 43 #include "ir_visitor.h" 44 #include "glsl_types.h" 45 46 class ir_set_program_inouts_visitor : public ir_hierarchical_visitor { 47 public: 48 ir_set_program_inouts_visitor(struct gl_program *prog, 49 bool is_fragment_shader) 50 { 51 this->prog = prog; 52 this->is_fragment_shader = is_fragment_shader; 53 this->ht = hash_table_ctor(0, 54 hash_table_pointer_hash, 55 hash_table_pointer_compare); 56 } 57 ~ir_set_program_inouts_visitor() 58 { 59 hash_table_dtor(this->ht); 60 } 61 62 virtual ir_visitor_status visit_enter(ir_dereference_array *); 63 virtual ir_visitor_status visit_enter(ir_function_signature *); 64 virtual ir_visitor_status visit_enter(ir_expression *); 65 virtual ir_visitor_status visit_enter(ir_discard *); 66 virtual ir_visitor_status visit(ir_dereference_variable *); 67 virtual ir_visitor_status visit(ir_variable *); 68 69 struct gl_program *prog; 70 struct hash_table *ht; 71 bool is_fragment_shader; 72 }; 73 74 static void 75 mark(struct gl_program *prog, ir_variable *var, int offset, int len, 76 bool is_fragment_shader) 77 { 78 /* As of GLSL 1.20, varyings can only be floats, floating-point 79 * vectors or matrices, or arrays of them. For Mesa programs using 80 * InputsRead/OutputsWritten, everything but matrices uses one 81 * slot, while matrices use a slot per column. Presumably 82 * something doing a more clever packing would use something other 83 * than InputsRead/OutputsWritten. 84 */ 85 86 for (int i = 0; i < len; i++) { 87 GLbitfield64 bitfield = BITFIELD64_BIT(var->location + var->index + offset + i); 88 if (var->mode == ir_var_in) { 89 prog->InputsRead |= bitfield; 90 if (is_fragment_shader) { 91 gl_fragment_program *fprog = (gl_fragment_program *) prog; 92 fprog->InterpQualifier[var->location + var->index + offset + i] = 93 (glsl_interp_qualifier) var->interpolation; 94 if (var->centroid) 95 fprog->IsCentroid |= bitfield; 96 } 97 } else if (var->mode == ir_var_system_value) { 98 prog->SystemValuesRead |= bitfield; 99 } else { 100 prog->OutputsWritten |= bitfield; 101 } 102 } 103 } 104 105 /* Default handler: Mark all the locations in the variable as used. */ 106 ir_visitor_status 107 ir_set_program_inouts_visitor::visit(ir_dereference_variable *ir) 108 { 109 if (hash_table_find(this->ht, ir->var) == NULL) 110 return visit_continue; 111 112 if (ir->type->is_array()) { 113 mark(this->prog, ir->var, 0, 114 ir->type->length * ir->type->fields.array->matrix_columns, 115 this->is_fragment_shader); 116 } else { 117 mark(this->prog, ir->var, 0, ir->type->matrix_columns, 118 this->is_fragment_shader); 119 } 120 121 return visit_continue; 122 } 123 124 ir_visitor_status 125 ir_set_program_inouts_visitor::visit_enter(ir_dereference_array *ir) 126 { 127 ir_dereference_variable *deref_var; 128 ir_constant *index = ir->array_index->as_constant(); 129 deref_var = ir->array->as_dereference_variable(); 130 ir_variable *var = NULL; 131 132 /* Check that we're dereferencing a shader in or out */ 133 if (deref_var) 134 var = (ir_variable *)hash_table_find(this->ht, deref_var->var); 135 136 if (index && var) { 137 int width = 1; 138 139 if (deref_var->type->is_array() && 140 deref_var->type->fields.array->is_matrix()) { 141 width = deref_var->type->fields.array->matrix_columns; 142 } 143 144 mark(this->prog, var, index->value.i[0] * width, width, 145 this->is_fragment_shader); 146 return visit_continue_with_parent; 147 } 148 149 return visit_continue; 150 } 151 152 ir_visitor_status 153 ir_set_program_inouts_visitor::visit(ir_variable *ir) 154 { 155 if (ir->mode == ir_var_in || 156 ir->mode == ir_var_out || 157 ir->mode == ir_var_system_value) { 158 hash_table_insert(this->ht, ir, ir); 159 } 160 161 return visit_continue; 162 } 163 164 ir_visitor_status 165 ir_set_program_inouts_visitor::visit_enter(ir_function_signature *ir) 166 { 167 /* We don't want to descend into the function parameters and 168 * consider them as shader inputs or outputs. 169 */ 170 visit_list_elements(this, &ir->body); 171 return visit_continue_with_parent; 172 } 173 174 ir_visitor_status 175 ir_set_program_inouts_visitor::visit_enter(ir_expression *ir) 176 { 177 if (is_fragment_shader && ir->operation == ir_unop_dFdy) { 178 gl_fragment_program *fprog = (gl_fragment_program *) prog; 179 fprog->UsesDFdy = true; 180 } 181 return visit_continue; 182 } 183 184 ir_visitor_status 185 ir_set_program_inouts_visitor::visit_enter(ir_discard *) 186 { 187 /* discards are only allowed in fragment shaders. */ 188 assert(is_fragment_shader); 189 190 gl_fragment_program *fprog = (gl_fragment_program *) prog; 191 fprog->UsesKill = true; 192 193 return visit_continue; 194 } 195 196 void 197 do_set_program_inouts(exec_list *instructions, struct gl_program *prog, 198 bool is_fragment_shader) 199 { 200 ir_set_program_inouts_visitor v(prog, is_fragment_shader); 201 202 prog->InputsRead = 0; 203 prog->OutputsWritten = 0; 204 prog->SystemValuesRead = 0; 205 if (is_fragment_shader) { 206 gl_fragment_program *fprog = (gl_fragment_program *) prog; 207 memset(fprog->InterpQualifier, 0, sizeof(fprog->InterpQualifier)); 208 fprog->IsCentroid = 0; 209 fprog->UsesDFdy = false; 210 fprog->UsesKill = false; 211 } 212 visit_list_elements(&v, instructions); 213 } 214