summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/Makefile.sources1
-rw-r--r--src/compiler/glsl/ir_optimization.h3
-rw-r--r--src/compiler/glsl/link_varyings.cpp50
-rw-r--r--src/compiler/glsl/link_varyings.h6
-rw-r--r--src/compiler/glsl/lower_xfb_varying.cpp222
-rw-r--r--src/compiler/glsl/meson.build1
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',