glsl: implement lower_xfb_varying() as a NIR pass
This just converts the GLSL IR pass to NIR. Acked-by: Marek Olšák <marek.olsak@amd.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15731>
This commit is contained in:

committed by
Marge Bot

parent
4600108ddf
commit
fa9cee4247
@@ -2490,6 +2490,48 @@ assign_initial_varying_locations(const struct gl_constants *consts,
|
||||
if (matched_candidate == NULL)
|
||||
return false;
|
||||
|
||||
/* There are two situations where a new output varying is needed:
|
||||
*
|
||||
* - If varying packing is disabled for xfb and the current declaration
|
||||
* is subscripting an array, whether the subscript is aligned or not.
|
||||
* to preserve the rest of the array for the consumer.
|
||||
*
|
||||
* - If a builtin variable needs to be copied to a new variable
|
||||
* before its content is modified by another lowering pass (e.g.
|
||||
* \c gl_Position is transformed by \c nir_lower_viewport_transform).
|
||||
*/
|
||||
const bool lowered =
|
||||
(vm->disable_xfb_packing && xfb_decls[i].is_subscripted) ||
|
||||
(matched_candidate->toplevel_var->data.explicit_location &&
|
||||
matched_candidate->toplevel_var->data.location < VARYING_SLOT_VAR0 &&
|
||||
(!consumer || consumer->Stage == MESA_SHADER_FRAGMENT) &&
|
||||
(consts->ShaderCompilerOptions[producer->Stage].LowerBuiltinVariablesXfb &
|
||||
BITFIELD_BIT(matched_candidate->toplevel_var->data.location)));
|
||||
|
||||
if (lowered) {
|
||||
nir_variable *new_var;
|
||||
struct tfeedback_candidate *new_candidate = NULL;
|
||||
|
||||
new_var = gl_nir_lower_xfb_varying(producer->Program->nir,
|
||||
xfb_decls[i].orig_name,
|
||||
matched_candidate->toplevel_var);
|
||||
if (new_var == NULL)
|
||||
return false;
|
||||
|
||||
/* Create new candidate and replace matched_candidate */
|
||||
new_candidate = rzalloc(mem_ctx, struct tfeedback_candidate);
|
||||
new_candidate->toplevel_var = new_var;
|
||||
new_candidate->type = new_var->type;
|
||||
new_candidate->struct_offset_floats = 0;
|
||||
new_candidate->xfb_offset_floats = 0;
|
||||
_mesa_hash_table_insert(tfeedback_candidates,
|
||||
ralloc_strdup(mem_ctx, new_var->name),
|
||||
new_candidate);
|
||||
|
||||
xfb_decl_set_lowered_candidate(&xfb_decls[i], new_candidate);
|
||||
matched_candidate = new_candidate;
|
||||
}
|
||||
|
||||
/* Mark as xfb varying */
|
||||
matched_candidate->toplevel_var->data.is_xfb = 1;
|
||||
|
||||
@@ -2514,9 +2556,9 @@ assign_initial_varying_locations(const struct gl_constants *consts,
|
||||
}
|
||||
|
||||
/* Add the xfb varying to varying matches if it wasn't already added */
|
||||
if (!should_add_varying_match_record(input_var, prog, producer,
|
||||
consumer) &&
|
||||
!matched_candidate->toplevel_var->data.is_xfb_only) {
|
||||
if ((!should_add_varying_match_record(input_var, prog, producer,
|
||||
consumer) &&
|
||||
!matched_candidate->toplevel_var->data.is_xfb_only) || lowered) {
|
||||
matched_candidate->toplevel_var->data.is_xfb_only = 1;
|
||||
varying_matches_record(mem_ctx, vm, matched_candidate->toplevel_var,
|
||||
NULL);
|
||||
|
@@ -68,6 +68,10 @@ bool gl_nir_link_varyings(const struct gl_constants *consts,
|
||||
const struct gl_extensions *exts,
|
||||
gl_api api, struct gl_shader_program *prog);
|
||||
|
||||
nir_variable * gl_nir_lower_xfb_varying(nir_shader *shader,
|
||||
const char *old_var_name,
|
||||
nir_variable *toplevel_var);
|
||||
|
||||
void gl_nir_opt_dead_builtin_varyings(const struct gl_constants *consts,
|
||||
gl_api api,
|
||||
struct gl_shader_program *prog,
|
||||
|
209
src/compiler/glsl/gl_nir_lower_xfb_varying.c
Normal file
209
src/compiler/glsl/gl_nir_lower_xfb_varying.c
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2022 Valve Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "nir.h"
|
||||
#include "nir_builder.h"
|
||||
#include "gl_nir_linker.h"
|
||||
#include "main/shader_types.h"
|
||||
#include "util/strndup.h"
|
||||
|
||||
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(nir_builder *b, const char *name, nir_variable *toplevel_var,
|
||||
nir_deref_instr **deref, const struct glsl_type **type)
|
||||
{
|
||||
if (name[0] == '\0') {
|
||||
/* End */
|
||||
return (*deref != NULL);
|
||||
} else if (name[0] == '[') {
|
||||
/* Array index */
|
||||
char *endptr = NULL;
|
||||
unsigned index = strtol(name + 1, &endptr, 10);
|
||||
assert(*type != NULL && glsl_type_is_array(*type) && endptr[0] == ']');
|
||||
|
||||
nir_load_const_instr *c = nir_load_const_instr_create(b->shader, 1, 32);
|
||||
c->value[0].u32 = index;
|
||||
nir_builder_instr_insert(b, &c->instr);
|
||||
|
||||
*deref = nir_build_deref_array(b, *deref, &c->def);
|
||||
*type = glsl_without_array(*type);
|
||||
return get_deref(b, endptr + 1, NULL, deref, type);
|
||||
} else if (name[0] == '.') {
|
||||
/* Struct member */
|
||||
char *field = get_field_name(name + 1);
|
||||
|
||||
assert(*type != NULL && glsl_type_is_struct(*type) && field != NULL);
|
||||
|
||||
int idx = glsl_get_field_index(*type, field);
|
||||
*deref = nir_build_deref_struct(b, *deref, idx);
|
||||
*type = glsl_get_struct_field(*type, idx);
|
||||
name += 1 + strlen(field);
|
||||
free(field);
|
||||
return get_deref(b, name, NULL, deref, type);
|
||||
} else {
|
||||
/* Top level variable */
|
||||
char *field = get_field_name(name);
|
||||
name += strlen(field);
|
||||
free(field);
|
||||
if (toplevel_var == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*deref = nir_build_deref_var(b, toplevel_var);
|
||||
*type = toplevel_var->type;
|
||||
return get_deref(b, name, NULL, deref, type);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_to_new_var(nir_builder *b, nir_deref_instr *deref,
|
||||
nir_deref_instr *new_var_deref, const struct glsl_type *type)
|
||||
{
|
||||
bool is_matrix = glsl_type_is_matrix(type);
|
||||
unsigned components = glsl_get_vector_elements(type);
|
||||
unsigned writemask = (1 << components) - 1;
|
||||
|
||||
if (is_matrix) {
|
||||
unsigned array_size = glsl_get_length(type);
|
||||
for (unsigned i = 0; i < array_size; i++) {
|
||||
nir_load_const_instr *c = nir_load_const_instr_create(b->shader, 1, 32);
|
||||
c->value[0].u32 = i;
|
||||
nir_builder_instr_insert(b, &c->instr);
|
||||
|
||||
nir_deref_instr *m_deref = nir_build_deref_array(b, deref, &c->def);
|
||||
nir_deref_instr *new_var_m_deref =
|
||||
nir_build_deref_array(b, new_var_deref, &c->def);
|
||||
|
||||
nir_ssa_def *value = nir_load_deref(b, m_deref);
|
||||
nir_store_deref(b, new_var_m_deref, value, writemask);
|
||||
}
|
||||
} else {
|
||||
nir_ssa_def *value = nir_load_deref(b, deref);
|
||||
nir_store_deref(b, new_var_deref, value, writemask);
|
||||
}
|
||||
}
|
||||
|
||||
nir_variable *
|
||||
gl_nir_lower_xfb_varying(nir_shader *shader, const char *old_var_name,
|
||||
nir_variable *toplevel_var)
|
||||
{
|
||||
nir_function_impl *impl = nir_shader_get_entrypoint(shader);
|
||||
|
||||
nir_builder b;
|
||||
nir_builder_init(&b, impl);
|
||||
b.cursor = nir_before_block(nir_start_block(impl));
|
||||
|
||||
nir_deref_instr *deref = NULL;
|
||||
const struct glsl_type *type = NULL;
|
||||
if (!get_deref(&b, old_var_name, toplevel_var, &deref, &type))
|
||||
return NULL;
|
||||
|
||||
nir_variable *new_variable = rzalloc(shader, nir_variable);
|
||||
new_variable->name = generate_new_name(new_variable, old_var_name);
|
||||
new_variable->type = type;
|
||||
new_variable->data.mode = nir_var_shader_out;
|
||||
new_variable->data.location = -1;
|
||||
new_variable->data.xfb.buffer = -1;
|
||||
new_variable->data.xfb.stride = -1;
|
||||
new_variable->data.assigned = true;
|
||||
nir_shader_add_variable(shader, new_variable);
|
||||
nir_deref_instr *new_var_deref = nir_build_deref_var(&b, new_variable);
|
||||
|
||||
nir_foreach_block(block, impl) {
|
||||
if (shader->info.stage != MESA_SHADER_GEOMETRY) {
|
||||
/* For shaders other than geometry, outputs need to be lowered before
|
||||
* each return statement and at the end of main()
|
||||
*/
|
||||
if (nir_block_ends_in_return_or_halt(block)) {
|
||||
b.cursor = nir_before_instr(nir_block_last_instr(block));
|
||||
copy_to_new_var(&b, deref, new_var_deref, type);
|
||||
} else if (block == nir_impl_last_block(impl)) {
|
||||
b.cursor = nir_after_instr(nir_block_last_instr(block));
|
||||
copy_to_new_var(&b, deref, new_var_deref, type);
|
||||
}
|
||||
} else {
|
||||
/* For geometry shaders, outputs need to be lowered before each call
|
||||
* to EmitVertex()
|
||||
*/
|
||||
nir_foreach_instr_safe(instr, block) {
|
||||
if (instr->type != nir_instr_type_intrinsic)
|
||||
continue;
|
||||
|
||||
nir_intrinsic_instr *intrin = nir_instr_as_intrinsic(instr);
|
||||
if (intrin->intrinsic != nir_intrinsic_emit_vertex)
|
||||
continue;
|
||||
|
||||
b.cursor = nir_before_instr(instr);
|
||||
copy_to_new_var(&b, deref, new_var_deref, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new_variable;
|
||||
}
|
@@ -89,6 +89,7 @@ files_libglsl = files(
|
||||
'gl_nir_lower_packed_varyings.c',
|
||||
'gl_nir_lower_samplers.c',
|
||||
'gl_nir_lower_samplers_as_deref.c',
|
||||
'gl_nir_lower_xfb_varying.c',
|
||||
'gl_nir_link_atomics.c',
|
||||
'gl_nir_link_uniform_blocks.c',
|
||||
'gl_nir_link_uniform_initializers.c',
|
||||
|
Reference in New Issue
Block a user