1/* 2 * Copyright ©2019 Collabora Ltd. 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 lower_xfb_varying.cpp 26 * 27 */ 28 29#include "ir.h" 30#include "main/mtypes.h" 31#include "glsl_symbol_table.h" 32#include "util/strndup.h" 33 34namespace { 35 36/** 37 * Visitor that splices varying packing code before every return. 38 */ 39class lower_xfb_var_splicer : public ir_hierarchical_visitor 40{ 41public: 42 explicit lower_xfb_var_splicer(void *mem_ctx, 43 gl_shader_stage stage, 44 const exec_list *instructions); 45 46 ir_visitor_status append_instructions(exec_node *node); 47 virtual ir_visitor_status visit_leave(ir_return *ret); 48 virtual ir_visitor_status visit_leave(ir_function_signature *sig); 49 virtual ir_visitor_status visit_leave(ir_emit_vertex *emit); 50 51private: 52 /** 53 * Memory context used to allocate new instructions for the shader. 54 */ 55 void * const mem_ctx; 56 57 gl_shader_stage stage; 58 59 /** 60 * Instructions that should be spliced into place before each return and EmitVertex(). 61 */ 62 const exec_list *instructions; 63}; 64 65} /* anonymous namespace */ 66 67 68lower_xfb_var_splicer::lower_xfb_var_splicer(void *mem_ctx, gl_shader_stage stage, 69 const exec_list *instructions) 70 : mem_ctx(mem_ctx), stage(stage), instructions(instructions) 71{ 72} 73 74ir_visitor_status 75lower_xfb_var_splicer::append_instructions(exec_node *node) 76{ 77 foreach_in_list(ir_instruction, ir, this->instructions) { 78 node->insert_before(ir->clone(this->mem_ctx, NULL)); 79 } 80 return visit_continue; 81} 82 83ir_visitor_status 84lower_xfb_var_splicer::visit_leave(ir_return *ret) 85{ 86 if (stage != MESA_SHADER_VERTEX) 87 return visit_continue; 88 return append_instructions(ret); 89} 90 91ir_visitor_status 92lower_xfb_var_splicer::visit_leave(ir_emit_vertex *emit) 93{ 94 return append_instructions(emit); 95} 96 97/** Insert a copy-back assignment at the end of the main() function */ 98ir_visitor_status 99lower_xfb_var_splicer::visit_leave(ir_function_signature *sig) 100{ 101 if (strcmp(sig->function_name(), "main") != 0) 102 return visit_continue; 103 104 if (this->stage == MESA_SHADER_VERTEX) { 105 if (((ir_instruction*)sig->body.get_tail())->ir_type == ir_type_return) 106 return visit_continue; 107 108 foreach_in_list(ir_instruction, ir, this->instructions) { 109 sig->body.push_tail(ir->clone(this->mem_ctx, NULL)); 110 } 111 } 112 113 return visit_continue; 114} 115 116static char* 117get_field_name(const char *name) 118{ 119 const char *first_dot = strchr(name, '.'); 120 const char *first_square_bracket = strchr(name, '['); 121 int name_size = 0; 122 123 if (!first_square_bracket && !first_dot) 124 name_size = strlen(name); 125 else if ((!first_square_bracket || 126 (first_dot && first_dot < first_square_bracket))) 127 name_size = first_dot - name; 128 else 129 name_size = first_square_bracket - name; 130 131 return strndup(name, name_size); 132} 133 134/* Generate a new name given the old xfb declaration string by replacing dots 135 * with '_', brackets with '@' and appending "-xfb" */ 136static char * 137generate_new_name(void *mem_ctx, const char *name) 138{ 139 char *new_name; 140 unsigned i = 0; 141 142 new_name = ralloc_strdup(mem_ctx, name); 143 while (new_name[i]) { 144 if (new_name[i] == '.') { 145 new_name[i] = '_'; 146 } else if (new_name[i] == '[' || new_name[i] == ']') { 147 new_name[i] = '@'; 148 } 149 i++; 150 } 151 152 if (!ralloc_strcat(&new_name, "-xfb")) { 153 ralloc_free(new_name); 154 return NULL; 155 } 156 157 return new_name; 158} 159 160/* Get the dereference for the given variable name. The method is called 161 * recursively to parse array indices and struct members. */ 162static bool 163get_deref(void *ctx, 164 const char *name, 165 struct gl_linked_shader *shader, 166 ir_dereference **deref, 167 const glsl_type **type) 168{ 169 if (name[0] == '\0') { 170 /* End */ 171 return (*deref != NULL); 172 } else if (name[0] == '[') { 173 /* Array index */ 174 char *endptr = NULL; 175 unsigned index; 176 177 index = strtol(name + 1, &endptr, 10); 178 assert(*type != NULL && (*type)->is_array() && endptr[0] == ']'); 179 *deref = new(ctx) ir_dereference_array(*deref, new(ctx) ir_constant(index)); 180 *type = (*type)->without_array(); 181 return get_deref(ctx, endptr + 1, shader, deref, type); 182 } else if (name[0] == '.') { 183 /* Struct member */ 184 char *field = get_field_name(name + 1); 185 186 assert(*type != NULL && (*type)->is_struct() && field != NULL); 187 *deref = new(ctx) ir_dereference_record(*deref, field); 188 *type = (*type)->field_type(field); 189 assert(*type != glsl_type::error_type); 190 name += 1 + strlen(field); 191 free(field); 192 return get_deref(ctx, name, shader, deref, type); 193 } else { 194 /* Top level variable */ 195 char *field = get_field_name(name); 196 ir_variable *toplevel_var; 197 198 toplevel_var = shader->symbols->get_variable(field); 199 name += strlen(field); 200 free(field); 201 if (toplevel_var == NULL) { 202 return false; 203 } 204 205 *deref = new (ctx) ir_dereference_variable(toplevel_var); 206 *type = toplevel_var->type; 207 return get_deref(ctx, name, shader, deref, type); 208 } 209} 210 211ir_variable * 212lower_xfb_varying(void *mem_ctx, 213 struct gl_linked_shader *shader, 214 const char *old_var_name) 215{ 216 exec_list new_instructions; 217 char *new_var_name; 218 ir_dereference *deref = NULL; 219 const glsl_type *type = NULL; 220 221 if (!get_deref(mem_ctx, old_var_name, shader, &deref, &type)) { 222 if (deref) { 223 delete deref; 224 } 225 return NULL; 226 } 227 228 new_var_name = generate_new_name(mem_ctx, old_var_name); 229 ir_variable *new_variable 230 = new(mem_ctx) ir_variable(type, new_var_name, ir_var_shader_out); 231 new_variable->data.assigned = true; 232 new_variable->data.used = true; 233 shader->ir->push_head(new_variable); 234 ralloc_free(new_var_name); 235 236 ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(new_variable); 237 ir_assignment *new_assignment = new(mem_ctx) ir_assignment(lhs, deref); 238 new_instructions.push_tail(new_assignment); 239 240 lower_xfb_var_splicer splicer(mem_ctx, shader->Stage, &new_instructions); 241 visit_list_elements(&splicer, shader->ir); 242 243 return new_variable; 244} 245