| /* |
| * Copyright ©2019 Collabora Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| */ |
| |
| /** |
| * \file lower_xfb_varying.cpp |
| * |
| */ |
| |
| #include "ir.h" |
| #include "main/mtypes.h" |
| #include "glsl_symbol_table.h" |
| #include "util/strndup.h" |
| |
| namespace { |
| |
| /** |
| * Visitor that splices varying packing code before every return. |
| */ |
| class lower_xfb_var_splicer : public ir_hierarchical_visitor |
| { |
| public: |
| explicit lower_xfb_var_splicer(void *mem_ctx, |
| const exec_list *instructions); |
| |
| virtual ir_visitor_status visit_leave(ir_return *ret); |
| virtual ir_visitor_status visit_leave(ir_function_signature *sig); |
| |
| private: |
| /** |
| * Memory context used to allocate new instructions for the shader. |
| */ |
| void * const mem_ctx; |
| |
| /** |
| * Instructions that should be spliced into place before each return. |
| */ |
| const exec_list *instructions; |
| }; |
| |
| } /* anonymous namespace */ |
| |
| |
| lower_xfb_var_splicer::lower_xfb_var_splicer(void *mem_ctx, const exec_list *instructions) |
| : mem_ctx(mem_ctx), instructions(instructions) |
| { |
| } |
| |
| ir_visitor_status |
| lower_xfb_var_splicer::visit_leave(ir_return *ret) |
| { |
| foreach_in_list(ir_instruction, ir, this->instructions) { |
| ret->insert_before(ir->clone(this->mem_ctx, NULL)); |
| } |
| return visit_continue; |
| } |
| |
| /** Insert a copy-back assignment at the end of the main() function */ |
| ir_visitor_status |
| lower_xfb_var_splicer::visit_leave(ir_function_signature *sig) |
| { |
| if (strcmp(sig->function_name(), "main") != 0) |
| return visit_continue; |
| |
| if (((ir_instruction*)sig->body.get_tail())->ir_type == ir_type_return) |
| return visit_continue; |
| |
| foreach_in_list(ir_instruction, ir, this->instructions) { |
| sig->body.push_tail(ir->clone(this->mem_ctx, NULL)); |
| } |
| |
| return visit_continue; |
| } |
| |
| static char* |
| get_field_name(const char *name) |
| { |
| const char *first_dot = strchr(name, '.'); |
| const char *first_square_bracket = strchr(name, '['); |
| int name_size = 0; |
| |
| if (!first_square_bracket && !first_dot) |
| name_size = strlen(name); |
| else if ((!first_square_bracket || |
| (first_dot && first_dot < first_square_bracket))) |
| name_size = first_dot - name; |
| else |
| name_size = first_square_bracket - name; |
| |
| return strndup(name, name_size); |
| } |
| |
| /* Generate a new name given the old xfb declaration string by replacing dots |
| * with '_', brackets with '@' and appending "-xfb" */ |
| static char * |
| generate_new_name(void *mem_ctx, const char *name) |
| { |
| char *new_name; |
| unsigned i = 0; |
| |
| new_name = ralloc_strdup(mem_ctx, name); |
| while (new_name[i]) { |
| if (new_name[i] == '.') { |
| new_name[i] = '_'; |
| } else if (new_name[i] == '[' || new_name[i] == ']') { |
| new_name[i] = '@'; |
| } |
| i++; |
| } |
| |
| if (!ralloc_strcat(&new_name, "-xfb")) { |
| ralloc_free(new_name); |
| return NULL; |
| } |
| |
| return new_name; |
| } |
| |
| /* Get the dereference for the given variable name. The method is called |
| * recursively to parse array indices and struct members. */ |
| static bool |
| get_deref(void *ctx, |
| const char *name, |
| struct gl_linked_shader *shader, |
| ir_dereference **deref, |
| const glsl_type **type) |
| { |
| if (name[0] == '\0') { |
| /* End */ |
| return (*deref != NULL); |
| } else if (name[0] == '[') { |
| /* Array index */ |
| char *endptr = NULL; |
| unsigned index; |
| |
| index = strtol(name + 1, &endptr, 10); |
| assert(*type != NULL && (*type)->is_array() && endptr[0] == ']'); |
| *deref = new(ctx) ir_dereference_array(*deref, new(ctx) ir_constant(index)); |
| *type = (*type)->without_array(); |
| return get_deref(ctx, endptr + 1, shader, deref, type); |
| } else if (name[0] == '.') { |
| /* Struct member */ |
| char *field = get_field_name(name + 1); |
| |
| assert(*type != NULL && (*type)->is_struct() && field != NULL); |
| *deref = new(ctx) ir_dereference_record(*deref, field); |
| *type = (*type)->field_type(field); |
| assert(*type != glsl_type::error_type); |
| name += 1 + strlen(field); |
| free(field); |
| return get_deref(ctx, name, shader, deref, type); |
| } else { |
| /* Top level variable */ |
| char *field = get_field_name(name); |
| ir_variable *toplevel_var; |
| |
| toplevel_var = shader->symbols->get_variable(field); |
| name += strlen(field); |
| free(field); |
| if (toplevel_var == NULL) { |
| return false; |
| } |
| |
| *deref = new (ctx) ir_dereference_variable(toplevel_var); |
| *type = toplevel_var->type; |
| return get_deref(ctx, name, shader, deref, type); |
| } |
| } |
| |
| ir_variable * |
| lower_xfb_varying(void *mem_ctx, |
| struct gl_linked_shader *shader, |
| const char *old_var_name) |
| { |
| exec_list new_instructions; |
| char *new_var_name; |
| ir_dereference *deref = NULL; |
| const glsl_type *type = NULL; |
| |
| if (!get_deref(mem_ctx, old_var_name, shader, &deref, &type)) { |
| if (deref) { |
| delete deref; |
| } |
| return NULL; |
| } |
| |
| new_var_name = generate_new_name(mem_ctx, old_var_name); |
| ir_variable *new_variable |
| = new(mem_ctx) ir_variable(type, new_var_name, ir_var_shader_out); |
| new_variable->data.assigned = true; |
| new_variable->data.used = true; |
| shader->ir->push_head(new_variable); |
| ralloc_free(new_var_name); |
| |
| ir_dereference *lhs = new(mem_ctx) ir_dereference_variable(new_variable); |
| ir_assignment *new_assignment = new(mem_ctx) ir_assignment(lhs, deref); |
| new_instructions.push_tail(new_assignment); |
| |
| lower_xfb_var_splicer splicer(mem_ctx, &new_instructions); |
| visit_list_elements(&splicer, shader->ir); |
| |
| return new_variable; |
| } |