diff options
author | Timothy Arceri <[email protected]> | 2016-10-27 12:21:52 +1100 |
---|---|---|
committer | Timothy Arceri <[email protected]> | 2016-11-11 09:17:07 +1100 |
commit | 7372d2153ab68eefec792a8cb094a6557bdb8b4a (patch) | |
tree | 905454e19632d625d65c2703646f3a0a492dc82d /src/compiler/nir | |
parent | 53d1f4251f58bd858a4fdf3a7564dd16f90165d4 (diff) |
nir: update nir_gather_info to only mark used array/matrix elements
This is based on the code from the GLSL IR pass however unlike the GLSL IR
pass it also supports arrays of arrays.
As well as implementing the logic from the GLSL IR pass we add some
additional intrinsic cases to catch more system values.
Reviewed-by: Kenneth Graunke <[email protected]>
Diffstat (limited to 'src/compiler/nir')
-rw-r--r-- | src/compiler/nir/nir_gather_info.c | 260 |
1 files changed, 207 insertions, 53 deletions
diff --git a/src/compiler/nir/nir_gather_info.c b/src/compiler/nir/nir_gather_info.c index 380140ad5ce..63c8a420fdf 100644 --- a/src/compiler/nir/nir_gather_info.c +++ b/src/compiler/nir/nir_gather_info.c @@ -21,9 +21,189 @@ * IN THE SOFTWARE. */ +#include "main/mtypes.h" #include "nir.h" static void +set_io_mask(nir_shader *shader, nir_variable *var, int offset, int len) +{ + for (int i = 0; i < len; i++) { + assert(var->data.location != -1); + + int idx = var->data.location + offset + i; + bool is_patch_generic = var->data.patch && + idx != VARYING_SLOT_TESS_LEVEL_INNER && + idx != VARYING_SLOT_TESS_LEVEL_OUTER && + idx != VARYING_SLOT_BOUNDING_BOX0 && + idx != VARYING_SLOT_BOUNDING_BOX1; + uint64_t bitfield; + + if (is_patch_generic) { + assert(idx >= VARYING_SLOT_PATCH0 && idx < VARYING_SLOT_TESS_MAX); + bitfield = BITFIELD64_BIT(idx - VARYING_SLOT_PATCH0); + } + else { + assert(idx < VARYING_SLOT_MAX); + bitfield = BITFIELD64_BIT(idx); + } + + if (var->data.mode == nir_var_shader_in) { + if (is_patch_generic) + shader->info->patch_inputs_read |= bitfield; + else + shader->info->inputs_read |= bitfield; + + /* double inputs read is only for vertex inputs */ + if (shader->stage == MESA_SHADER_VERTEX && + glsl_type_is_dual_slot(glsl_without_array(var->type))) + shader->info->double_inputs_read |= bitfield; + + if (shader->stage == MESA_SHADER_FRAGMENT) { + shader->info->fs.uses_sample_qualifier |= var->data.sample; + } + } else { + assert(var->data.mode == nir_var_shader_out); + if (is_patch_generic) { + shader->info->patch_outputs_written |= bitfield; + } else if (!var->data.read_only) { + shader->info->outputs_written |= bitfield; + } + + if (var->data.fb_fetch_output) + shader->info->outputs_read |= bitfield; + } + } +} + +/** + * Mark an entire variable as used. Caller must ensure that the variable + * represents a shader input or output. + */ +static void +mark_whole_variable(nir_shader *shader, nir_variable *var) +{ + const struct glsl_type *type = var->type; + bool is_vertex_input = false; + + if (nir_is_per_vertex_io(var, shader->stage)) { + assert(glsl_type_is_array(type)); + type = glsl_get_array_element(type); + } + + if (shader->stage == MESA_SHADER_VERTEX && + var->data.mode == nir_var_shader_in) + is_vertex_input = true; + + set_io_mask(shader, var, 0, + glsl_count_attribute_slots(type, is_vertex_input)); +} + +static unsigned +get_io_offset(nir_deref_var *deref, bool is_vertex_input) +{ + unsigned offset = 0; + + nir_deref *tail = &deref->deref; + while (tail->child != NULL) { + tail = tail->child; + + if (tail->deref_type == nir_deref_type_array) { + nir_deref_array *deref_array = nir_deref_as_array(tail); + + if (deref_array->deref_array_type == nir_deref_array_type_indirect) { + return -1; + } + + offset += glsl_count_attribute_slots(tail->type, is_vertex_input) * + deref_array->base_offset; + } + /* TODO: we can get the offset for structs here see nir_lower_io() */ + } + + return offset; +} + +/** + * Try to mark a portion of the given varying as used. Caller must ensure + * that the variable represents a shader input or output. + * + * If the index can't be interpreted as a constant, or some other problem + * occurs, then nothing will be marked and false will be returned. + */ +static bool +try_mask_partial_io(nir_shader *shader, nir_deref_var *deref) +{ + nir_variable *var = deref->var; + const struct glsl_type *type = var->type; + + if (nir_is_per_vertex_io(var, shader->stage)) { + assert(glsl_type_is_array(type)); + type = glsl_get_array_element(type); + } + + /* The code below only handles: + * + * - Indexing into matrices + * - Indexing into arrays of (arrays, matrices, vectors, or scalars) + * + * For now, we just give up if we see varying structs and arrays of structs + * here marking the entire variable as used. + */ + if (!(glsl_type_is_matrix(type) || + (glsl_type_is_array(type) && + (glsl_type_is_numeric(glsl_without_array(type)) || + glsl_type_is_boolean(glsl_without_array(type)))))) { + + /* If we don't know how to handle this case, give up and let the + * caller mark the whole variable as used. + */ + return false; + } + + bool is_vertex_input = false; + if (shader->stage == MESA_SHADER_VERTEX && + var->data.mode == nir_var_shader_in) + is_vertex_input = true; + + unsigned offset = get_io_offset(deref, is_vertex_input); + if (offset == -1) + return false; + + unsigned num_elems; + unsigned elem_width = 1; + unsigned mat_cols = 1; + if (glsl_type_is_array(type)) { + num_elems = glsl_get_aoa_size(type); + if (glsl_type_is_matrix(glsl_without_array(type))) + mat_cols = glsl_get_matrix_columns(glsl_without_array(type)); + } else { + num_elems = glsl_get_matrix_columns(type); + } + + /* double element width for double types that takes two slots */ + if (!is_vertex_input && + glsl_type_is_dual_slot(glsl_without_array(type))) { + elem_width *= 2; + } + + if (offset >= num_elems * elem_width * mat_cols) { + /* Constant index outside the bounds of the matrix/array. This could + * arise as a result of constant folding of a legal GLSL program. + * + * Even though the spec says that indexing outside the bounds of a + * matrix/array results in undefined behaviour, we don't want to pass + * out-of-range values to set_io_mask() (since this could result in + * slots that don't exist being marked as used), so just let the caller + * mark the whole variable as used. + */ + return false; + } + + set_io_mask(shader, var, offset, elem_width); + return true; +} + +static void gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader) { switch (instr->intrinsic) { @@ -33,10 +213,24 @@ gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader) shader->info->fs.uses_discard = true; break; + case nir_intrinsic_interp_var_at_centroid: + case nir_intrinsic_interp_var_at_sample: + case nir_intrinsic_interp_var_at_offset: + case nir_intrinsic_load_var: + case nir_intrinsic_store_var: + if (instr->variables[0]->var->data.mode == nir_var_shader_in || + instr->variables[0]->var->data.mode == nir_var_shader_out) { + if (!try_mask_partial_io(shader, instr->variables[0])) + mark_whole_variable(shader, instr->variables[0]->var); + } + break; + + case nir_intrinsic_load_draw_id: case nir_intrinsic_load_front_face: case nir_intrinsic_load_vertex_id: case nir_intrinsic_load_vertex_id_zero_base: case nir_intrinsic_load_base_vertex: + case nir_intrinsic_load_base_instance: case nir_intrinsic_load_instance_id: case nir_intrinsic_load_sample_id: case nir_intrinsic_load_sample_pos: @@ -47,6 +241,9 @@ gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader) case nir_intrinsic_load_local_invocation_index: case nir_intrinsic_load_work_group_id: case nir_intrinsic_load_num_work_groups: + case nir_intrinsic_load_tess_coord: + case nir_intrinsic_load_tess_level_outer: + case nir_intrinsic_load_tess_level_inner: shader->info->system_values_read |= (1 << nir_system_value_from_intrinsic(instr->intrinsic)); break; @@ -89,62 +286,9 @@ gather_info_block(nir_block *block, nir_shader *shader) } } -/** - * Returns the bits in the inputs_read, outputs_written, or - * system_values_read bitfield corresponding to this variable. - */ -static inline uint64_t -get_io_mask(nir_variable *var, gl_shader_stage stage) -{ - assert(var->data.mode == nir_var_shader_in || - var->data.mode == nir_var_shader_out || - var->data.mode == nir_var_system_value); - assert(var->data.location >= 0); - - const struct glsl_type *var_type = var->type; - if (stage == MESA_SHADER_GEOMETRY && var->data.mode == nir_var_shader_in) { - /* Most geometry shader inputs are per-vertex arrays */ - if (var->data.location >= VARYING_SLOT_VAR0) - assert(glsl_type_is_array(var_type)); - - if (glsl_type_is_array(var_type)) - var_type = glsl_get_array_element(var_type); - } - - bool is_vertex_input = (var->data.mode == nir_var_shader_in && - stage == MESA_SHADER_VERTEX); - unsigned slots = glsl_count_attribute_slots(var_type, is_vertex_input); - return ((1ull << slots) - 1) << var->data.location; -} - void nir_shader_gather_info(nir_shader *shader, nir_function_impl *entrypoint) { - /* This pass does not yet support tessellation shaders */ - assert(shader->stage == MESA_SHADER_VERTEX || - shader->stage == MESA_SHADER_GEOMETRY || - shader->stage == MESA_SHADER_FRAGMENT || - shader->stage == MESA_SHADER_COMPUTE); - - bool uses_sample_qualifier = false; - shader->info->inputs_read = 0; - foreach_list_typed(nir_variable, var, node, &shader->inputs) { - shader->info->inputs_read |= get_io_mask(var, shader->stage); - uses_sample_qualifier |= var->data.sample; - } - - if (shader->stage == MESA_SHADER_FRAGMENT) - shader->info->fs.uses_sample_qualifier = uses_sample_qualifier; - - /* TODO: Some day we may need to add stream support to NIR */ - shader->info->outputs_written = 0; - foreach_list_typed(nir_variable, var, node, &shader->outputs) - shader->info->outputs_written |= get_io_mask(var, shader->stage); - - shader->info->system_values_read = 0; - foreach_list_typed(nir_variable, var, node, &shader->system_values) - shader->info->system_values_read |= get_io_mask(var, shader->stage); - shader->info->num_textures = 0; shader->info->num_images = 0; nir_foreach_variable(var, &shader->uniforms) { @@ -162,6 +306,16 @@ nir_shader_gather_info(nir_shader *shader, nir_function_impl *entrypoint) } } + shader->info->inputs_read = 0; + shader->info->outputs_written = 0; + shader->info->outputs_read = 0; + shader->info->double_inputs_read = 0; + shader->info->patch_inputs_read = 0; + shader->info->patch_outputs_written = 0; + shader->info->system_values_read = 0; + if (shader->stage == MESA_SHADER_FRAGMENT) { + shader->info->fs.uses_sample_qualifier = false; + } nir_foreach_block(block, entrypoint) { gather_info_block(block, shader); } |