diff options
-rw-r--r-- | src/compiler/Makefile.sources | 1 | ||||
-rw-r--r-- | src/compiler/glsl/ir_optimization.h | 3 | ||||
-rw-r--r-- | src/compiler/glsl/link_varyings.cpp | 50 | ||||
-rw-r--r-- | src/compiler/glsl/link_varyings.h | 6 | ||||
-rw-r--r-- | src/compiler/glsl/lower_xfb_varying.cpp | 222 | ||||
-rw-r--r-- | src/compiler/glsl/meson.build | 1 |
6 files changed, 283 insertions, 0 deletions
diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources index 2482de2a799..48c5d6e7cf2 100644 --- a/src/compiler/Makefile.sources +++ b/src/compiler/Makefile.sources @@ -125,6 +125,7 @@ LIBGLSL_FILES = \ glsl/lower_output_reads.cpp \ glsl/lower_shared_reference.cpp \ glsl/lower_ubo_reference.cpp \ + glsl/lower_xfb_varying.cpp \ glsl/opt_algebraic.cpp \ glsl/opt_array_splitting.cpp \ glsl/opt_conditional_discard.cpp \ diff --git a/src/compiler/glsl/ir_optimization.h b/src/compiler/glsl/ir_optimization.h index 11695c5aba2..20766db3354 100644 --- a/src/compiler/glsl/ir_optimization.h +++ b/src/compiler/glsl/ir_optimization.h @@ -143,6 +143,9 @@ bool lower_quadop_vector(exec_list *instructions, bool dont_lower_swz); bool lower_const_arrays_to_uniforms(exec_list *instructions, unsigned stage, unsigned max_uniform_components); bool lower_clip_cull_distance(struct gl_shader_program *prog, gl_linked_shader *shader); +ir_variable * lower_xfb_varying(void *mem_ctx, + gl_linked_shader *shader, + const char *old_var_name); void lower_output_reads(unsigned stage, exec_list *instructions); bool lower_packing_builtins(exec_list *instructions, int op_mask); void lower_shared_reference(struct gl_context *ctx, diff --git a/src/compiler/glsl/link_varyings.cpp b/src/compiler/glsl/link_varyings.cpp index 4b63f7693f6..4757825c244 100644 --- a/src/compiler/glsl/link_varyings.cpp +++ b/src/compiler/glsl/link_varyings.cpp @@ -1373,6 +1373,21 @@ tfeedback_decl::find_candidate(gl_shader_program *prog, return this->matched_candidate; } +/** + * Force a candidate over the previously matched one. It happens when a new + * varying needs to be created to match the xfb declaration, for example, + * to fullfil an alignment criteria. + */ +void +tfeedback_decl::set_lowered_candidate(const tfeedback_candidate *candidate) +{ + this->matched_candidate = candidate; + + /* The subscript part is no longer relevant */ + this->is_subscripted = false; + this->array_subscript = 0; +} + /** * Parse all the transform feedback declarations that were passed to @@ -2779,6 +2794,41 @@ assign_varying_locations(struct gl_context *ctx, return false; } + /* A new output varying is needed, and the matched candidate should be + * replaced, if varying packing is disabled for xfb and the current + * declaration is not aligned within the top level varying + * (e.g. vec3_arr[1]). + */ + const unsigned dmul = + matched_candidate->type->without_array()->is_64bit() ? 2 : 1; + if (disable_xfb_packing && + !tfeedback_decls[i].is_aligned(dmul, matched_candidate->offset)) { + ir_variable *new_var; + tfeedback_candidate *new_candidate = NULL; + + new_var = lower_xfb_varying(mem_ctx, producer, tfeedback_decls[i].name()); + if (new_var == NULL) { + ralloc_free(hash_table_ctx); + return false; + } + + /* Create new candidate and replace matched_candidate */ + new_candidate = rzalloc(mem_ctx, tfeedback_candidate); + new_candidate->toplevel_var = new_var; + new_candidate->toplevel_var->data.is_unmatched_generic_inout = 1; + new_candidate->type = new_var->type; + new_candidate->offset = 0; + _mesa_hash_table_insert(tfeedback_candidates, + ralloc_strdup(mem_ctx, new_var->name), + new_candidate); + + tfeedback_decls[i].set_lowered_candidate(new_candidate); + matched_candidate = new_candidate; + } + + /* Mark as xfb varying */ + matched_candidate->toplevel_var->data.is_xfb = 1; + /* Mark xfb varyings as always active */ matched_candidate->toplevel_var->data.always_active_io = 1; diff --git a/src/compiler/glsl/link_varyings.h b/src/compiler/glsl/link_varyings.h index b802250819e..6f4bcdc79c5 100644 --- a/src/compiler/glsl/link_varyings.h +++ b/src/compiler/glsl/link_varyings.h @@ -104,6 +104,7 @@ public: const void *mem_ctx) const; const tfeedback_candidate *find_candidate(gl_shader_program *prog, hash_table *tfeedback_candidates); + void set_lowered_candidate(const tfeedback_candidate *candidate); bool is_next_buffer_separator() const { @@ -123,6 +124,11 @@ public: return !this->next_buffer_separator && !this->skip_components; } + bool is_aligned(unsigned dmul, unsigned offset) const + { + return (dmul * (this->array_subscript + offset)) % 4 == 0; + } + const char *name() const { return this->orig_name; diff --git a/src/compiler/glsl/lower_xfb_varying.cpp b/src/compiler/glsl/lower_xfb_varying.cpp new file mode 100644 index 00000000000..d460bbd5cae --- /dev/null +++ b/src/compiler/glsl/lower_xfb_varying.cpp @@ -0,0 +1,222 @@ +/* + * 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; +} diff --git a/src/compiler/glsl/meson.build b/src/compiler/glsl/meson.build index 1e2b3da1b4b..ea3e7beaa0c 100644 --- a/src/compiler/glsl/meson.build +++ b/src/compiler/glsl/meson.build @@ -175,6 +175,7 @@ files_libglsl = files( 'lower_output_reads.cpp', 'lower_shared_reference.cpp', 'lower_ubo_reference.cpp', + 'lower_xfb_varying.cpp', 'opt_algebraic.cpp', 'opt_array_splitting.cpp', 'opt_conditional_discard.cpp', |